🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@forcedream/mcp-server

Package Overview
Dependencies
Maintainers
1
Versions
22
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@forcedream/mcp-server - npm Package Compare versions

Comparing version
0.6.2
to
0.7.0
+100
dist/extract_data.js
import { z } from 'zod';
const FD_API = process.env.FD_API_BASE || 'https://api.forcedream.ai';
const SLUG = 'data-extract-v1';
/**
* Zod input schema for the forcedream_extract_data tool. fields and document are required;
* max_wait_seconds bounds how long this call polls before returning a pollable task_id.
* A dedicated, named tool for ForceDream's data-extract-v1 agent, rather than requiring
* a caller to know the generic forcedream_invoke_agent + agent_slug pattern.
*/
export const extractDataSchema = {
fields: z.array(z.string()).describe('The field names to extract, e.g. ["company_name", "ceo_name"].'),
document: z.string().describe('The unstructured document text to extract from.'),
max_wait_seconds: z.number().optional().describe('Max seconds to poll (default 60, agent typically takes ~30s). On timeout, returns task_id to poll later.'),
};
function authHeader() {
const key = process.env.FD_API_KEY || '';
return key ? { Authorization: `Bearer ${key}` } : {};
}
async function postJson(url, body) {
const res = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', ...authHeader() }, body: JSON.stringify(body) });
let json = null;
try {
json = await res.json();
}
catch { }
return { status: res.status, json };
}
async function getJson(url) {
const res = await fetch(url, { headers: authHeader() });
let json = null;
try {
json = await res.json();
}
catch { }
return { status: res.status, json };
}
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
// Invoke data-extract-v1 and wait (bounded) for the result. SPENDS balance -- needs FD_API_KEY.
// Invokes ONCE; on timeout does NOT re-invoke (would double-charge), returns task_id instead.
// Same real invoke/poll pattern as forcedream_invoke_agent / forcedream_security_scan, fixed
// to this one, specific agent. fields + document are combined into the agent's real,
// established "Fields to extract: ...\n\nDocument: ..." task format internally.
/**
* Invokes ForceDream's real data-extract-v1 agent and polls (bounded) for the result.
* SPENDS your balance -- requires FD_API_KEY. Invokes once; never re-invokes on timeout
* (would double-charge) -- returns a pollable task_id instead. Set FD_MOCK_MODE=true to
* test without spending real balance.
* @param args.fields - The field names to extract.
* @param args.document - The document text to extract from.
* @param args.max_wait_seconds - Max seconds to poll before returning a pollable task_id (default 60, max 120).
*/
export async function extractData(args) {
// Mock mode: explicit opt-in only, never a default. Intercepts BEFORE any real network
// call -- no real balance touched, no real agent invoked. Same discipline as invoke_agent.ts
// and security_scan.ts.
if (process.env.FD_MOCK_MODE === 'true') {
return {
status: 'completed',
agent: SLUG,
task_id: 'mock_' + Date.now(),
output: { rows: [], extracted_fields: [], missing_fields: args.fields, entity_verification: [], note: 'Synthetic mock output. This is not a real extraction result.' },
charged_pence: 0,
mock: true,
message: 'MOCK MODE ACTIVE (FD_MOCK_MODE=true): no real extraction was run, no balance was spent, and this response has no real proof_id -- forcedream_verify_proof will correctly reject it if you try. Unset FD_MOCK_MODE to run real extractions.',
};
}
if (!process.env.FD_API_KEY) {
return { status: 'error', agent: SLUG, message: 'FD_API_KEY is required to extract (extracting spends your balance). Set it in the MCP server env. forcedream_search_agents and forcedream_verify_proof need no key.' };
}
const maxWaitMs = Math.max(5, Math.min(120, args.max_wait_seconds ?? 60)) * 1000;
const task = `Fields to extract: ${args.fields.join(', ')}. Document: ${args.document}`;
const inv = await postJson(`${FD_API}/v1/agents/${SLUG}/invoke`, { task });
if (inv.status === 401)
return { status: 'error', agent: SLUG, message: 'Invalid FD_API_KEY (401). Check the key in the MCP server env.' };
if (!inv.json?.task_id)
return { status: 'error', agent: SLUG, message: `Invoke failed (HTTP ${inv.status}): ${inv.json?.error || inv.json?.note || 'no task_id'}` };
const taskId = inv.json.task_id;
const start = Date.now();
let intervalMs = 2500;
while (Date.now() - start < maxWaitMs) {
await sleep(intervalMs);
const poll = await getJson(`${FD_API}/v1/agents/${SLUG}/result/${encodeURIComponent(taskId)}`);
const d = poll.json || {};
const status = d.status || d.outcome;
if (status === 'completed' || status === 'succeeded' || d.ok === true) {
return {
status: 'completed', agent: SLUG, task_id: taskId, output: d.output, charged_pence: d.charged_pence,
proof_id: d.proof_id || taskId,
verify_proof_hint: `Verify trustlessly: call forcedream_verify_proof with task_id "${d.proof_id || taskId}". The signature proves authenticity without trusting ForceDream.`,
message: `Completed. Charged ${d.charged_pence}p. Cryptographically proven (proof_id ${d.proof_id || taskId}).`,
};
}
if (status === 'charge_failed')
return { status: 'charge_failed', agent: SLUG, task_id: taskId, charged_pence: 0, message: `Charge failed: ${d.reason || 'insufficient_balance'}. Nothing charged or delivered. Top up and retry.` };
if (status === 'failed' || status === 'dead_letter')
return { status: 'error', agent: SLUG, task_id: taskId, message: `Task ${status}: ${d.reason || d.last_error || 'unknown'}` };
intervalMs = Math.min(intervalMs + 1000, 6000);
}
return { status: 'pending', agent: SLUG, task_id: taskId, message: `Still processing after ${maxWaitMs / 1000}s. Not re-invoked (would double-charge). Poll the result later with this task_id.` };
}
+21
-0

@@ -9,2 +9,3 @@ #!/usr/bin/env node

import { securityScan, securityScanSchema } from './security_scan.js';
import { extractData, extractDataSchema } from './extract_data.js';
import { searchReliability, searchReliabilitySchema } from './search_reliability.js';

@@ -92,2 +93,22 @@ import { searchCosts, searchCostsSchema } from './search_costs.js';

});
// forcedream_extract_data — dedicated, named tool for data-extract-v1 specifically.
// Spends balance (needs FD_API_KEY). Same real invoke/poll pattern as forcedream_invoke_agent,
// fixed to this one agent so a caller doesn't need to know the generic agent_slug pattern.
server.registerTool('forcedream_extract_data', {
title: 'Extract structured fields from a document',
description: 'Structured JSON extraction from unstructured text -- grounded in real, live verification, not just ' +
'pattern-matching. Pulls requested fields, nulls anything missing, never guesses. Cross-references detected ' +
'proper-noun entities (companies, people, places) against Wikidata to confirm which extracted values are ' +
'independently verified vs. unconfirmed. SPENDS your balance — requires FD_API_KEY. Returns the extracted ' +
'rows, what you were charged, and a proof_id.',
inputSchema: extractDataSchema,
}, async ({ fields, document, max_wait_seconds }) => {
try {
const result = await extractData({ fields, document, max_wait_seconds });
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
}
catch (e) {
return { content: [{ type: 'text', text: JSON.stringify({ status: 'error', error: e.message }, null, 2) }], isError: true };
}
});
// forcedream_search_reliability — keyless. Real, system-measured reliability per agent.

@@ -94,0 +115,0 @@ server.registerTool('forcedream_search_reliability', {

+1
-1
{
"name": "@forcedream/mcp-server",
"version": "0.6.2",
"version": "0.7.0",
"mcpName": "io.github.forcedreamai/mcp-server",

@@ -5,0 +5,0 @@ "description": "Discover, invoke, and trustlessly verify ForceDream AI agents with cryptographic proofs. ForceDream is a paid, verifiable agent marketplace reachable over MCP, every successful call is billed and split with the agent's developer, and every result is Ed25519-signed and independently verifiable in your own process.",