@forcedream/mcp-server 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,20 +1,41 @@
1
1
  # @forcedream/mcp-server
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/@forcedream/mcp-server.svg)](https://www.npmjs.com/package/@forcedream/mcp-server)
4
+ [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
5
+ [![Node >=18](https://img.shields.io/badge/node-%3E%3D18-brightgreen.svg)](https://nodejs.org)
6
+
3
7
  An [MCP](https://modelcontextprotocol.io) server for **ForceDream** — discover AI agents, invoke them to do real work, and **verify the result cryptographically in your own process**. No trust in ForceDream required: every agent task produces an Ed25519-signed proof that this server checks locally.
4
8
 
9
+ Listed on the [official MCP Registry](https://registry.modelcontextprotocol.io) as `io.github.forcedreamai/mcp-server`.
10
+
11
+ ## Two ways to connect
12
+
13
+ | | Local (npm) | Remote (hosted) |
14
+ |---|---|---|
15
+ | **Transport** | stdio, runs on your machine | Streamable HTTP, hosted by ForceDream |
16
+ | **Setup** | `npx -y @forcedream/mcp-server` | Point your client at `https://api.forcedream.ai/v1/mcp` |
17
+ | **Auth for invoking** | `FD_API_KEY` env var | OAuth 2.1 + PKCE (standard MCP auth flow) |
18
+ | **Tools available** | `search_agents`, `verify_proof`, `invoke_agent` | All of the above, plus `check_fraud`, `generate_embedding`, `market_quote` |
19
+ | **Best for** | Claude Desktop, local dev | Any client with native remote-MCP + OAuth support |
20
+
21
+ Both talk to the same real ForceDream API and the same real settlement system. Pick whichever fits your client.
22
+
5
23
  ## What it does
6
24
 
7
- Three tools, exposed to any MCP client (Claude Desktop, Cursor, Cline, …):
25
+ `search_agents` and `verify_proof` need no account. Tools that spend your balance need authentication.
8
26
 
9
27
  | Tool | Auth | What it does |
10
28
  |------|------|--------------|
11
- | `search_agents` | none | Discover ForceDream agents and their honest, system-derived metrics (proof count, success rate). |
12
- | `verify_proof` | none | Independently verify a ForceDream proof by task ID. The Ed25519 signature is checked locally against the published public key — ForceDream is never asked whether the proof is valid. |
13
- | `invoke_agent` | key | Invoke an agent to do real work. Spends your balance. Returns the output plus a `proof_id` you can verify. |
29
+ | `search_agents` | none | Discover ForceDream agents, their real capabilities, and honest, system-derived metrics. |
30
+ | `verify_proof` | none | Independently verify a ForceDream proof by task ID. Checked locally against the published public key. |
31
+ | `invoke_agent` | key/OAuth | Invoke an agent to do real work. Spends your balance. Honest declines and failed charges cost nothing. |
32
+ | `check_fraud`\* | OAuth | Real-time fraud risk scoring using IP reputation and behavioural signals. |
33
+ | `generate_embedding`\* | OAuth | Real 1024-dim text embeddings via Voyage voyage-3.5. |
34
+ | `market_quote`\* | OAuth | Live stock quotes via Alpha Vantage, cached, WORM-sealed. |
14
35
 
15
- `search_agents` and `verify_proof` need no account. `invoke_agent` spends a balance, so it needs your key.
36
+ \* remote server only.
16
37
 
17
- ## Quick start
38
+ ## Quick start (local, npm)
18
39
 
19
40
  ### 1. Get a key
20
41
 
@@ -41,10 +62,30 @@ Edit your `claude_desktop_config.json`:
41
62
  }
42
63
  ```
43
64
 
44
- Restart Claude Desktop. You should see the three ForceDream tools available.
65
+ Restart Claude Desktop. You should see the ForceDream tools available.
45
66
 
46
67
  > Omit `FD_API_KEY` to run discovery + verification only (no spending). Add it to enable `invoke_agent`.
47
68
 
69
+ ### 2b. Add to Cursor
70
+
71
+ Open Cursor Settings -> MCP -> Add new MCP Server, or edit your MCP config directly:
72
+
73
+ ```json
74
+ {
75
+ "mcpServers": {
76
+ "forcedream": {
77
+ "command": "npx",
78
+ "args": ["-y", "@forcedream/mcp-server"],
79
+ "env": { "FD_API_KEY": "fd_live_your_key_here" }
80
+ }
81
+ }
82
+ }
83
+ ```
84
+
85
+ ### 2c. Add to Windsurf
86
+
87
+ In Windsurf, go to Settings -> Cascade -> MCP Servers -> Add Server, and use the same config block as above.
88
+
48
89
  ### 3. Try it
49
90
 
50
91
  In a new chat:
@@ -53,25 +94,173 @@ In a new chat:
53
94
 
54
95
  You'll watch discovery → invocation → trustless verification, end to end.
55
96
 
56
- ## Configuration
97
+ ## Quick start (remote, OAuth)
57
98
 
58
- | Env var | Required | Default | Purpose |
59
- |---------|----------|---------|---------|
60
- | `FD_API_KEY` | only for `invoke_agent` | — | Your `fd_live_` billing key. Spending happens against its balance. |
61
- | `FD_API_BASE` | no | `https://api.forcedream.ai` | Override the API base (for testing). |
99
+ For MCP clients with native remote-server support, add:
100
+
101
+ ```json
102
+ {
103
+ "mcpServers": {
104
+ "forcedream": {
105
+ "url": "https://api.forcedream.ai/v1/mcp"
106
+ }
107
+ }
108
+ }
109
+ ```
110
+
111
+ Your client will handle the OAuth 2.1 + PKCE flow automatically the first time you invoke a billed tool.
112
+
113
+ ## Examples
114
+
115
+ Real agents you can try, see search_agents for the full current list.
116
+
117
+ ```
118
+ Invoke data-extract-v1 to pull structured fields from raw text.
119
+ Invoke translation-v1 to translate a passage.
120
+ Invoke summarization-v1 to summarise a document.
121
+ Invoke forecast-generation-v1 to generate a forecast from a data series.
122
+ ```
123
+
124
+ ## Architecture
125
+
126
+ ```mermaid
127
+ graph TD
128
+ A[ForceDream API] --> B[Agent marketplace]
129
+ A --> C[Invocation API]
130
+ A --> D[Settlement]
131
+ A --> E[Proof signing]
132
+ A --> F["This MCP server (stdio, local)"]
133
+ A --> G["Remote MCP endpoint (OAuth)"]
134
+ F --> H[Claude Desktop]
135
+ F --> I[Cursor]
136
+ F --> J[Cline]
137
+ G --> K["Any MCP client with remote support"]
138
+ ```
139
+
140
+ This repository is a thin client. It calls the public API and speaks MCP -- it does not contain ForceDream's agent orchestration, routing, or settlement logic, which remain part of the private platform.
141
+
142
+ ## Platform capabilities
143
+
144
+ What visitors get, not how it works internally:
145
+
146
+ - Agent marketplace
147
+ - Multi-agent workflows
148
+ - Adaptive routing
149
+ - Provider intelligence
150
+ - Confidence scoring
151
+ - Cryptographic proofs
152
+ - Developer payouts
153
+ - MCP integration
154
+
155
+ ## Why ForceDream
156
+
157
+ Unlike a documentation-lookup or local-automation MCP server, ForceDream is a paid, verifiable agent marketplace reachable over MCP:
62
158
 
63
- ## What a proof proves and what it doesn't
159
+ - Real settlement -- every successful call is billed and split with the agent's developer; nothing self-reported.
160
+ - Cryptographic proof -- every result is Ed25519-signed and independently verifiable, not just trusted.
161
+ - Honest declines -- an agent that cannot answer confidently declines rather than fabricates, and charges nothing.
162
+ - No double-charging -- timeouts and retries never bill you twice for the same task.
64
163
 
65
- A valid proof attests **provenance and integrity**: that ForceDream produced this exact output for this exact input, at this cost, and that nothing has been altered since. The signature is checked in your process, so you don't have to trust ForceDream's word.
164
+ ## Example workflows
66
165
 
67
- A proof does **not** attest factual correctness. An agent's answer can still be wrong; the proof only guarantees it is the agent's genuine, unmodified work. Verify cited sources yourself.
166
+ Real prompts you can adapt, covering different real ways to use the tools together.
68
167
 
69
- You can also verify any proof in a browser at [forcedream.com/proof](https://www.forcedream.com/proof).
168
+ **Discover, then invoke, then verify**
169
+ > Search ForceDream for agents that do data extraction, invoke the best one on this text, then verify the proof it returns.
170
+
171
+ **Multi-step pipeline: extract, then translate**
172
+ > Extract the key fields from this document with data-extract-v1, then translate the result into Spanish with translation-v1.
173
+
174
+ **Summarize, then confirm authenticity**
175
+ > Summarize this report with summarization-v1, then verify the proof so I know it is genuinely ForceDream's unaltered output.
176
+
177
+ **Forecast from real data**
178
+ > Feed this sales history to forecast-generation-v1 and ask for a 3-month forecast.
179
+
180
+ **Fraud check before a sensitive action** (remote only)
181
+ > Before processing this withdrawal, run check_fraud on this user ID and IP address.
182
+
183
+ **Market-aware research** (remote only)
184
+ > Get a live quote for AAPL, then summarize what today's price move might mean for a tech-sector report.
185
+
186
+ **Embeddings for downstream search** (remote only)
187
+ > Generate an embedding for this paragraph so I can compare it against my existing document vectors.
188
+
189
+ **Chained verification across multiple tasks**
190
+ > Invoke summarization-v1 on these three documents one at a time, and after each one, verify its proof before moving to the next.
191
+
192
+ ## What a proof proves -- and what it does not
193
+
194
+ A valid proof attests provenance and integrity: that ForceDream produced this exact output for this exact input, at this cost, and that nothing has been altered since. The signature is checked in your process, so you do not have to trust ForceDream's word.
195
+
196
+ A proof does not attest factual correctness. An agent's answer can still be wrong; the proof only guarantees it is the agent's genuine, unmodified work. Verify cited sources yourself.
197
+
198
+ You can also verify any proof in a browser at forcedream.com/proof.
199
+
200
+ ## Error responses
201
+
202
+ Every error is a real, structured shape, not a generic failure message -- useful for building automated retry logic.
203
+
204
+ **Insufficient balance:**
205
+
206
+ ```json
207
+ {
208
+ "status": "error",
209
+ "error": "insufficient_balance",
210
+ "balance_pence": 0,
211
+ "required_pence": 10
212
+ }
213
+ ```
214
+
215
+ **Honest decline** (agent could not answer confidently -- not charged):
216
+
217
+ ```json
218
+ {
219
+ "status": "insufficient",
220
+ "charged_pence": 0,
221
+ "message": "Insufficient retrieved evidence. No charge."
222
+ }
223
+ ```
224
+
225
+ **Charge failed** (balance check passed, charge itself failed):
226
+
227
+ ```json
228
+ {
229
+ "status": "charge_failed",
230
+ "reason": "insufficient_balance"
231
+ }
232
+ ```
233
+
234
+ **Still processing** (poll again with the same task_id):
235
+
236
+ ```json
237
+ {
238
+ "status": "pending",
239
+ "task_id": "wtask_...",
240
+ "message": "Still processing. Not re-invoked (would double-charge)."
241
+ }
242
+ ```
243
+
244
+ **Authentication required** (remote server, invoking without a valid OAuth token):
245
+
246
+ ```
247
+ HTTP 401, WWW-Authenticate: Bearer realm="mcp"
248
+ ```
249
+
250
+ None of these ever result in a double charge. A failed or pending task is never billed twice on retry.
251
+
252
+ ## Configuration (local)
253
+
254
+ | Env var | Required | Default | Purpose |
255
+ |---------|----------|---------|---------|
256
+ | FD_API_KEY | only for invoke_agent | none | Your fd_live_ billing key. Spending happens against its balance. |
257
+ | FD_API_BASE | no | https://api.forcedream.ai | Override the API base (for testing). |
258
+ | FD_MOCK_MODE | no | unset | Set to "true" to test invoke_agent with synthetic, clearly-labeled fake results -- no real network call, no real balance spent. Never affects search_agents or verify_proof. |
70
259
 
71
260
  ## Run it directly
72
261
 
73
262
  ```bash
74
- npx -y @forcedream/mcp-server # starts the stdio MCP server
263
+ npx -y @forcedream/mcp-server
75
264
  ```
76
265
 
77
266
  It speaks MCP over stdio; point any MCP client at it.
@@ -80,6 +269,7 @@ It speaks MCP over stdio; point any MCP client at it.
80
269
 
81
270
  - ForceDream: https://www.forcedream.com
82
271
  - Verify a proof: https://www.forcedream.com/proof
272
+ - Official MCP Registry entry: https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.forcedreamai/mcp-server
83
273
  - MCP: https://modelcontextprotocol.io
84
274
 
85
275
  ## License
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env node
2
+ // Real CLI verification tool: npx @forcedream/mcp-server verify <task_id> [--json] [--quiet]
3
+ // Wraps the exact same verifyProof() function the MCP tool itself uses --
4
+ // no separate verification logic, no hardcoded example proof.
5
+ import { verifyProof } from './verify_proof.js';
6
+ function printUsage() {
7
+ console.error('Usage: fd-verify <task_id> [options]');
8
+ console.error('');
9
+ console.error('Options:');
10
+ console.error(' --json, -j Compact, single-line JSON output (for piping into jq, grep, etc.)');
11
+ console.error(' --quiet, -q No output at all; rely on the exit code only');
12
+ console.error('');
13
+ console.error('Exit codes: 0 = verified, 1 = verification failed, 2 = usage or fetch error');
14
+ console.error('');
15
+ console.error('Example: fd-verify wtask_a3ff3b4f3a1f6bf7df1f');
16
+ console.error('Example: fd-verify wtask_a3ff3b4f3a1f6bf7df1f --json | jq .verified');
17
+ console.error('Example: fd-verify wtask_a3ff3b4f3a1f6bf7df1f --quiet && echo "genuine"');
18
+ }
19
+ async function main() {
20
+ const args = process.argv.slice(2);
21
+ const flags = new Set(args.filter((a) => a.startsWith('-')));
22
+ const taskId = args.find((a) => !a.startsWith('-'));
23
+ const compact = flags.has('--json') || flags.has('-j');
24
+ const quiet = flags.has('--quiet') || flags.has('-q');
25
+ if (!taskId) {
26
+ printUsage();
27
+ process.exit(2);
28
+ }
29
+ try {
30
+ const result = await verifyProof({ task_id: taskId });
31
+ if (!quiet) {
32
+ console.log(compact ? JSON.stringify(result) : JSON.stringify(result, null, 2));
33
+ }
34
+ process.exit(result.verified ? 0 : 1);
35
+ }
36
+ catch (e) {
37
+ if (!quiet)
38
+ console.error('Error:', e?.message || String(e));
39
+ process.exit(2);
40
+ }
41
+ }
42
+ main();
package/dist/index.js CHANGED
@@ -5,7 +5,10 @@ import { z } from 'zod';
5
5
  import { verifyProof } from './verify_proof.js';
6
6
  import { searchAgents, searchAgentsSchema } from './search_agents.js';
7
7
  import { invokeAgent, invokeAgentSchema } from './invoke_agent.js';
8
- const server = new McpServer({ name: 'forcedream', version: '0.1.0' });
8
+ import { searchReliability, searchReliabilitySchema } from './search_reliability.js';
9
+ import { searchCosts, searchCostsSchema } from './search_costs.js';
10
+ import { searchProviders } from './search_providers.js';
11
+ const server = new McpServer({ name: 'forcedream', version: '0.3.0' });
9
12
  // verify_proof — trustless, keyless. Verify a ForceDream proof's Ed25519 signature client-side.
10
13
  server.registerTool('verify_proof', {
11
14
  title: 'Verify a ForceDream proof',
@@ -57,5 +60,50 @@ server.registerTool('invoke_agent', {
57
60
  return { content: [{ type: 'text', text: JSON.stringify({ status: 'error', error: e.message }, null, 2) }], isError: true };
58
61
  }
59
62
  });
63
+ // search_reliability — keyless. Real, system-measured reliability per agent.
64
+ server.registerTool('search_reliability', {
65
+ title: 'Search agent reliability data',
66
+ description: 'Real, system-measured reliability per agent: success_rate, avg_latency_ms, sample_size. No key needed. ' +
67
+ 'Same real data as search_agents\' health field, exposed standalone for reliability-focused queries.',
68
+ inputSchema: searchReliabilitySchema,
69
+ }, async ({ agent_slug }) => {
70
+ try {
71
+ const result = await searchReliability({ agent_slug });
72
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
73
+ }
74
+ catch (e) {
75
+ return { content: [{ type: 'text', text: JSON.stringify({ error: e.message }, null, 2) }], isError: true };
76
+ }
77
+ });
78
+ // search_costs — keyless. Real price_per_call_pence per agent.
79
+ server.registerTool('search_costs', {
80
+ title: 'Search agent pricing',
81
+ description: 'Real price_per_call_pence for every registered agent. No key needed. ' +
82
+ 'Useful for budget-aware agent selection before invoking.',
83
+ inputSchema: searchCostsSchema,
84
+ }, async ({ max_price_pence }) => {
85
+ try {
86
+ const result = await searchCosts({ max_price_pence });
87
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
88
+ }
89
+ catch (e) {
90
+ return { content: [{ type: 'text', text: JSON.stringify({ error: e.message }, null, 2) }], isError: true };
91
+ }
92
+ });
93
+ // search_providers — keyless. Real, live inference-provider health.
94
+ server.registerTool('search_providers', {
95
+ title: 'Search provider health',
96
+ description: 'Real, live inference-provider health: health_score, breaker_state, uptime_ratio, recent successes/failures. ' +
97
+ 'The same real intelligence the platform\'s own adaptive routing uses internally. No key needed.',
98
+ inputSchema: {},
99
+ }, async () => {
100
+ try {
101
+ const result = await searchProviders();
102
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
103
+ }
104
+ catch (e) {
105
+ return { content: [{ type: 'text', text: JSON.stringify({ error: e.message }, null, 2) }], isError: true };
106
+ }
107
+ });
60
108
  const transport = new StdioServerTransport();
61
109
  await server.connect(transport);
@@ -1,5 +1,9 @@
1
1
  import { z } from 'zod';
2
2
  const FD_API = process.env.FD_API_BASE || 'https://api.forcedream.ai';
3
+ /**
4
+ * Zod input schema for the invoke_agent tool. agent_slug and task are required;
5
+ * max_wait_seconds bounds how long this call polls before returning a pollable task_id.
6
+ */
3
7
  export const invokeAgentSchema = {
4
8
  agent_slug: z.string().describe('The agent to invoke, e.g. "atlas-research-v1". Use search_agents to discover.'),
5
9
  task: z.string().describe('The task/query for the agent (Atlas: a research question).'),
@@ -30,7 +34,30 @@ async function getJson(url) {
30
34
  const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
31
35
  // Invoke a ForceDream agent and wait (bounded) for the result. SPENDS balance — needs FD_API_KEY.
32
36
  // Invokes ONCE; on timeout does NOT re-invoke (would double-charge), returns task_id instead.
37
+ /**
38
+ * Invokes a real ForceDream agent and polls (bounded) for the result. SPENDS your balance --
39
+ * requires FD_API_KEY. Invokes once; never re-invokes on timeout (would double-charge) --
40
+ * returns a pollable task_id instead. Set FD_MOCK_MODE=true to test without spending real balance.
41
+ * @param args.agent_slug - The agent to invoke, e.g. "atlas-research-v1".
42
+ * @param args.task - The task/query for the agent.
43
+ * @param args.max_wait_seconds - Max seconds to poll before returning a pollable task_id (default 60, max 120).
44
+ */
33
45
  export async function invokeAgent(args) {
46
+ // Mock mode: explicit opt-in only, never a default. Intercepts BEFORE any real network call --
47
+ // no real balance touched, no real agent invoked. Every field is unmistakably labeled so a mock
48
+ // result can never be confused for a real one. Scoped to invoke_agent only: search_agents and
49
+ // verify_proof are already free and keyless, so mocking them would only make testing worse.
50
+ if (process.env.FD_MOCK_MODE === 'true') {
51
+ return {
52
+ status: 'completed',
53
+ agent: args.agent_slug,
54
+ task_id: 'mock_' + Date.now(),
55
+ output: { mock: true, note: 'Synthetic mock output. This is not real ForceDream data.' },
56
+ charged_pence: 0,
57
+ mock: true,
58
+ message: 'MOCK MODE ACTIVE (FD_MOCK_MODE=true): no real agent was invoked, no balance was spent, and this response has no real proof_id -- verify_proof will correctly reject it if you try. Unset FD_MOCK_MODE to invoke real agents.',
59
+ };
60
+ }
34
61
  if (!process.env.FD_API_KEY) {
35
62
  return { status: 'error', agent: args.agent_slug, message: 'FD_API_KEY is required to invoke (invoking spends your balance). Set it in the MCP server env. search_agents and verify_proof need no key.' };
36
63
  }
@@ -1,5 +1,9 @@
1
1
  import { z } from 'zod';
2
2
  const FD_API = process.env.FD_API_BASE || 'https://api.forcedream.ai';
3
+ /**
4
+ * Zod input schema for the search_agents tool. Both fields optional -- omitting both
5
+ * returns the full real agent registry, merged with live reliability data.
6
+ */
3
7
  export const searchAgentsSchema = {
4
8
  capability: z.string().optional().describe('Optional capability filter (e.g. "research:citation"). Omit to return all.'),
5
9
  query: z.string().optional().describe('Optional free-text match against agent slug/name/capability.'),
@@ -16,6 +20,13 @@ async function fetchJson(url) {
16
20
  // endpoint) rather than inventing any new health computation -- matching the identical fix
17
21
  // already made to the remote MCP server's own mcpSearchAgents. A reliability-fetch failure never
18
22
  // blocks the core listing; health is honestly null where no real reliability data exists yet.
23
+ /**
24
+ * Discovers real ForceDream agents from /v1/agents/list, merges in real reliability data from
25
+ * /v1/agents/reliability, and applies client-side capability/query filters (the server has no
26
+ * working server-side capability filter). Keyless -- no account needed.
27
+ * @param args.capability - Optional exact capability match, e.g. "research:citation".
28
+ * @param args.query - Optional free-text match against slug, name, or capabilities.
29
+ */
19
30
  export async function searchAgents(args) {
20
31
  const [data, relData] = await Promise.all([
21
32
  fetchJson(`${FD_API}/v1/agents/list`),
@@ -0,0 +1,32 @@
1
+ import { z } from 'zod';
2
+ const FD_API = process.env.FD_API_BASE || 'https://api.forcedream.ai';
3
+ /**
4
+ * Zod input schema for search_costs. Optional max_price_pence filter for budget-aware selection.
5
+ */
6
+ export const searchCostsSchema = {
7
+ max_price_pence: z.number().optional().describe('Optional: only return agents at or under this price.'),
8
+ };
9
+ async function fetchJson(url) {
10
+ const res = await fetch(url);
11
+ if (!res.ok)
12
+ throw new Error(`fetch ${url} -> HTTP ${res.status}`);
13
+ return res.json();
14
+ }
15
+ /**
16
+ * Fetches real price_per_call_pence for every registered agent from /v1/agents/list.
17
+ * @param args.max_price_pence - Optional upper bound; agents priced above this are excluded.
18
+ */
19
+ export async function searchCosts(args) {
20
+ const data = await fetchJson(`${FD_API}/v1/agents/list`);
21
+ const agents = Array.isArray(data?.agents) ? data.agents : [];
22
+ let priced = agents.map((a) => ({ slug: a.slug, name: a.name, price_per_call_pence: a.price_per_call_pence }));
23
+ if (typeof args.max_price_pence === 'number') {
24
+ const cap = args.max_price_pence;
25
+ priced = priced.filter((a) => (a.price_per_call_pence ?? Infinity) <= cap);
26
+ }
27
+ return {
28
+ count: priced.length,
29
+ agents: priced,
30
+ note: 'Real price_per_call_pence per agent, useful for budget-aware selection before invoking.',
31
+ };
32
+ }
@@ -0,0 +1,20 @@
1
+ const FD_API = process.env.FD_API_BASE || 'https://api.forcedream.ai';
2
+ async function fetchJson(url) {
3
+ const res = await fetch(url);
4
+ if (!res.ok)
5
+ throw new Error(`fetch ${url} -> HTTP ${res.status}`);
6
+ return res.json();
7
+ }
8
+ /**
9
+ * Fetches real, live inference-provider health from /v1/intelligence/status --
10
+ * the same intelligence the platform's own adaptive routing uses internally.
11
+ */
12
+ export async function searchProviders() {
13
+ const data = await fetchJson(`${FD_API}/v1/intelligence/status`);
14
+ const providers = Array.isArray(data?.providers) ? data.providers : [];
15
+ return {
16
+ count: providers.length,
17
+ providers,
18
+ note: 'Real, live provider health -- the same intelligence the platform\'s own adaptive routing uses internally.',
19
+ };
20
+ }
@@ -0,0 +1,30 @@
1
+ import { z } from 'zod';
2
+ const FD_API = process.env.FD_API_BASE || 'https://api.forcedream.ai';
3
+ /**
4
+ * Zod input schema for search_reliability. Optional agent_slug filter; omit for all agents.
5
+ */
6
+ export const searchReliabilitySchema = {
7
+ agent_slug: z.string().optional().describe('Optional: filter to one agent slug. Omit to return all.'),
8
+ };
9
+ async function fetchJson(url) {
10
+ const res = await fetch(url);
11
+ if (!res.ok)
12
+ throw new Error(`fetch ${url} -> HTTP ${res.status}`);
13
+ return res.json();
14
+ }
15
+ /**
16
+ * Fetches real, system-measured reliability per agent from /v1/agents/reliability --
17
+ * the same real endpoint the remote MCP server's identical tool uses. No new computation.
18
+ * @param args.agent_slug - Optional filter to a single agent.
19
+ */
20
+ export async function searchReliability(args) {
21
+ const data = await fetchJson(`${FD_API}/v1/agents/reliability`);
22
+ let agents = Array.isArray(data?.agents) ? data.agents : [];
23
+ if (args.agent_slug)
24
+ agents = agents.filter((a) => a.agent_slug === args.agent_slug);
25
+ return {
26
+ count: agents.length,
27
+ agents,
28
+ note: 'Real, system-measured reliability -- success_rate, avg_latency_ms, sample_size. Never self-reported.',
29
+ };
30
+ }
@@ -33,6 +33,13 @@ async function fetchJson(url) {
33
33
  }
34
34
  // Trustless verification: fetch public key (+ optionally proof), reconstruct
35
35
  // signable, verify Ed25519 locally. Server never asked "is this valid?".
36
+ /**
37
+ * Trustlessly verifies a ForceDream proof's Ed25519 signature entirely client-side. Fetches the
38
+ * public key and (if only a task_id is given) the proof itself from public, keyless endpoints,
39
+ * then verifies locally -- ForceDream is never asked whether the proof is valid.
40
+ * @param args.task_id - Fetches the proof from the public endpoint if proof is not given directly.
41
+ * @param args.proof - A full proof object to verify directly, skipping the fetch.
42
+ */
36
43
  export async function verifyProof(args) {
37
44
  let proof = args.proof;
38
45
  if (!proof) {
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@forcedream/mcp-server",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "mcpName": "io.github.forcedreamai/mcp-server",
5
5
  "description": "MCP server for ForceDream \u2014 discover, invoke, and trustlessly verify AI agents with cryptographic proofs.",
6
6
  "type": "module",
7
7
  "bin": {
8
- "fd-mcp": "dist/index.js"
8
+ "fd-mcp": "dist/index.js",
9
+ "fd-verify": "dist/cli-verify.js"
9
10
  },
10
11
  "main": "dist/index.js",
11
12
  "scripts": {