cyberdyne-mcp 0.6.18 → 0.6.20

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
@@ -151,7 +151,6 @@ reclaim ({ task_id }) → your MCP wallet reads escrow_payment_info, reconstru
151
151
  |---|---|---|
152
152
  | `onboard` | `siwe/nonce → siwe/verify → agent/key` | **Bootstrap (no key needed).** Generate a wallet if you don't have one, SIWE sign-in, mint your `cyb_` key, save both (`0600`). Zero browser. |
153
153
  | `list_categories` | — (static) | The seven task categories. No network. |
154
- | `search_humans` | `POST /api/a2a` `{search_humans}` | Query the capability index by `skills[]`, `min_reputation`, `location`. Ranked by reputation; public columns only. |
155
154
  | `post_task` | `POST /api/tasks` | Open an FCFS pool bounty. `reward_usd` is the total budget; `quantity` units; not charged until authorize. Response carries `authIntent` + `deployFee`. |
156
155
  | `authorize_task` | `POST /api/tasks/[id]/authorize` | Freeze the whole budget on the audited escrow. With a signing wallet: pass `auth_intent` + `deploy_fee` (the MCP signs + pays the fee); or pre-made `signed_payment` + `fee_tx_hash`. |
157
156
  | `get_task` | `GET /api/tasks/[id]` | Task + the submissions/claims the poster may see. Poll for a `pending` submission. |
@@ -164,8 +163,7 @@ the audited base/commerce-payments `AuthCaptureEscrow`): at `authorize_task` the
164
163
  budget is frozen; `review_submission` captures one unit to the human (full reward,
165
164
  in-token); `close_task` voids the unfilled remainder via the operator, and `reclaim`
166
165
  is your own payer-only on-chain recovery if the operator is ever unavailable.
167
- `search_humans` (discovery only — there is no direct hire) goes through the a2a
168
- JSON-RPC gateway because the REST `GET /api/humans` is session-only.
166
+
169
167
 
170
168
  ## Run it
171
169
 
package/dist/client.js CHANGED
@@ -1,13 +1,9 @@
1
1
  /**
2
2
  * Typed HTTP client for the LIVE CYBERDYNE platform API.
3
3
  *
4
- * Two rails, both keyed by the same agent token (a `cyb_…` API key):
5
- * - REST: `Authorization: Bearer ${token}` on /api/tasks, /api/treasury, … —
6
- * the headless-agent rail. Used for post/assign/authorize/get/release/
7
- * fund/close/claims/treasury.
8
- * - a2a: POST /api/a2a — a JSON-RPC 2.0 gateway that carries the key in the
9
- * body (`identity_token`). Used for `search_humans` (the REST
10
- * GET /api/humans is session-only and rejects Bearer keys).
4
+ * One rail, keyed by the agent token (a `cyb_…` API key):
5
+ * - REST: `Authorization: Bearer ${token}` on /api/tasks, /api/submissions, … —
6
+ * the headless-agent rail (post/authorize/get/review/close).
11
7
  *
12
8
  * The agent key (`cyb_…`) is resolved, in order, from:
13
9
  * 1. env CYBERDYNE_IDENTITY_TOKEN (e.g. `claude mcp add … -e CYBERDYNE_IDENTITY_TOKEN=…`)
@@ -97,6 +93,32 @@ export function saveWallet(walletKey) {
97
93
  export function saveTokenAndWallet(token, walletKey) {
98
94
  return writeConfigFile({ identity_token: token.trim(), walletKey: walletKey.trim() });
99
95
  }
96
+ /**
97
+ * Last resort (config save failed): write the live minted key to a FRESH 0600 recovery
98
+ * file — never to stderr/logs and never into a thrown error (which would reach the LLM
99
+ * via the MCP tool-result channel). Returns the path, or "" if even this fails.
100
+ */
101
+ export function saveKeyRecovery(token) {
102
+ try {
103
+ mkdirSync(join(homedir(), ".cyberdyne"), { recursive: true, mode: 0o700 });
104
+ const p = join(homedir(), ".cyberdyne", `key-recovery-${Date.now()}.txt`);
105
+ let flags = FS.O_WRONLY | FS.O_CREAT | FS.O_EXCL;
106
+ if (typeof FS.O_NOFOLLOW === "number")
107
+ flags |= FS.O_NOFOLLOW;
108
+ const fd = openSync(p, flags, 0o600);
109
+ try {
110
+ fchmodSync(fd, 0o600);
111
+ writeSync(fd, token.trim() + "\n");
112
+ }
113
+ finally {
114
+ closeSync(fd);
115
+ }
116
+ return p;
117
+ }
118
+ catch {
119
+ return "";
120
+ }
121
+ }
100
122
  /** Resolve config: token from env first, then the saved login. URL from env only. */
101
123
  export function readConfig(env = process.env) {
102
124
  const apiUrl = (env.CYBERDYNE_API_URL || DEFAULT_API_URL).replace(/\/+$/, "");
@@ -166,27 +188,4 @@ export class CyberdyneClient {
166
188
  }
167
189
  return json;
168
190
  }
169
- /**
170
- * a2a JSON-RPC call. The agent key travels in the params as `identity_token`.
171
- * Returns the JSON-RPC `result`; throws ApiError on a JSON-RPC error or non-2xx.
172
- */
173
- async a2a(method, params = {}) {
174
- const token = this.requireToken();
175
- const res = await fetch(this.config.apiUrl + "/api/a2a", {
176
- method: "POST",
177
- headers: { "content-type": "application/json", accept: "application/json" },
178
- body: JSON.stringify({
179
- jsonrpc: "2.0",
180
- id: 1,
181
- method,
182
- params: { ...params, identity_token: token },
183
- }),
184
- });
185
- const json = (await res.json().catch(() => ({})));
186
- if (!res.ok || json.error) {
187
- const code = json.error ? `${json.error.code}:${json.error.message}` : `http_${res.status}`;
188
- throw new ApiError(res.status, code, `a2a ${method}`);
189
- }
190
- return json.result;
191
- }
192
191
  }
package/dist/onboard.js CHANGED
@@ -24,7 +24,7 @@ import { generatePrivateKey, privateKeyToAccount, mnemonicToAccount } from "viem
24
24
  import { bytesToHex } from "viem";
25
25
  import { validateMnemonic } from "@scure/bip39";
26
26
  import { wordlist } from "@scure/bip39/wordlists/english";
27
- import { readConfig, readSavedWalletKey, saveTokenAndWallet, configPath as configFilePath } from "./client.js";
27
+ import { readConfig, readSavedWalletKey, saveTokenAndWallet, saveKeyRecovery, configPath as configFilePath } from "./client.js";
28
28
  /** Normalise a private key to the 0x-prefixed form viem expects. */
29
29
  function normalizeKey(pk) {
30
30
  return (pk.startsWith("0x") ? pk : `0x${pk}`);
@@ -233,11 +233,15 @@ export async function onboard(env = process.env, opts = {}) {
233
233
  configPath = saveTokenAndWallet(apiKey, privateKey);
234
234
  }
235
235
  catch (e) {
236
- // Print the live key to STDERR only (the console), NEVER in the thrown error
237
- // the error message travels through the MCP tool-result channel into the calling
238
- // LLM's context/transcripts, which would leak the credential.
239
- console.error(`[cyberdyne-mcp] minted agent key (save failed): ${apiKey}\n save it manually with: echo ${apiKey} | npx cyberdyne-mcp login`);
240
- throw new Error(`minted agent key ${apiKey.slice(0, 10)}… but failed to save ~/.cyberdyne/config.json: ${e instanceof Error ? e.message : String(e)} — the FULL key was printed to the console (stderr); save it with \`npx cyberdyne-mcp login\`.`);
236
+ // The key is ALREADY minted server-side. Write it to a FRESH 0600 recovery file
237
+ // never the raw key to stderr/logs, and never into the thrown error (which travels
238
+ // through the MCP tool-result channel into the calling LLM's context). Reference the
239
+ // file by PATH only, so no transcript or log ever captures the credential. (M1.)
240
+ const recoveryPath = saveKeyRecovery(apiKey);
241
+ console.error(`[cyberdyne-mcp] minted agent key but failed to save config.${recoveryPath
242
+ ? ` The key was written to ${recoveryPath} (chmod 600) — load it with: cat ${recoveryPath} | npx cyberdyne-mcp login then delete that file.`
243
+ : " Could not write a recovery file; re-run onboard for a fresh key and revoke the orphan in the app."}`);
244
+ throw new Error(`minted agent key ${apiKey.slice(0, 10)}… but failed to save ~/.cyberdyne/config.json: ${e instanceof Error ? e.message : String(e)}.${recoveryPath ? " The full key was written to a 0600 recovery file (path shown in the console/stderr)." : " Re-run onboard for a fresh key."}`);
241
245
  }
242
246
  // 6. (optional) auto-link Bankr — zero human interaction. If a bk_ key is supplied
243
247
  // (opts or CYBERDYNE_BANKR_KEY), use it ONCE to connect the agent's Bankr project
package/dist/server.js CHANGED
@@ -7,7 +7,6 @@
7
7
  * every tool is a thin, typed wrapper over an HTTP endpoint on the live backend.
8
8
  *
9
9
  * list_categories — the static task taxonomy (no network)
10
- * search_humans — POST /api/a2a {search_humans} → capability index
11
10
  * post_task — POST /api/tasks → open an FCFS pool bounty
12
11
  * authorize_task — POST /api/tasks/[id]/authorize → sign budget + pay fee + freeze
13
12
  * get_task — GET /api/tasks/[id] → status + submissions/claims
@@ -15,10 +14,8 @@
15
14
  * close_task — POST /api/tasks/[id]/close → refund the unfilled budget (operator voids)
16
15
  * reclaim — on-chain reclaim(paymentInfo) → trustless self-recovery, payer-only, no operator
17
16
  *
18
- * Auth: every networked tool sends the agent's `cyb_…` key. The REST routes take
19
- * it as `Authorization: Bearer …`; search_humans goes through the a2a JSON-RPC
20
- * gateway (the REST GET /api/humans is session-only), which carries the key as
21
- * `identity_token`.
17
+ * Auth: every networked tool sends the agent's `cyb_…` key as
18
+ * `Authorization: Bearer …`.
22
19
  *
23
20
  * The HUMAN submit-proof step happens in the app/UI (human-only — agents cannot
24
21
  * submit on a human's behalf). There is ONE settlement model for real tokens:
@@ -228,18 +225,6 @@ server.tool("onboard", "BOOTSTRAP (works WITHOUT an existing key — the one too
228
225
  note: "API key + wallet saved (0600). This MCP now authenticates automatically; networked tools are ready. The full key is NOT shown here by design — read it from the config file if you must export it.",
229
226
  };
230
227
  }));
231
- server.tool("search_humans", "Find verified humans by capability via the live capability index (a2a gateway). Filters are optional and combine (AND). Results are role='human' profiles ranked by reputation, projected to public columns (no wallets/balances). Note: `skills` is an array.", {
232
- skills: z
233
- .array(z.enum(TASK_CATEGORIES))
234
- .optional()
235
- .describe("Task categories the human must be able to do (all must match)."),
236
- min_reputation: z.number().min(0).max(5).optional().describe("Minimum reputation (0–5)."),
237
- location: z.string().optional().describe("Substring match on location, e.g. 'ES', 'Tokyo'."),
238
- }, async ({ skills, min_reputation, location }) => guardUntrusted(() => client.a2a("search_humans", {
239
- ...(skills ? { skills } : {}),
240
- ...(min_reputation != null ? { min_reputation } : {}),
241
- ...(location ? { location } : {}),
242
- })));
243
228
  server.tool("post_task", "Open an FCFS pool bounty on the marketplace. There is NO direct hire and NO agent-picks-human — every task is an open bounty: you freeze a budget, ANY eligible human submits first-come-first-served, and you approve/reject each submission. Funds are NOT charged at post — the budget is frozen later at authorize_task. `reward_usd` is the total budget; `quantity` is how many identical units (humans) it pays — each unit holds reward_usd/quantity (each unit must be >= $0.01). Returns the created task (with its id) plus `authIntent` (the budget authorization to sign) and `deployFee` { usd, bps, recipient, token } (a SEPARATE non-refundable fee tx) — pass BOTH to authorize_task. The non-custodial POOL escrow (USDC/BNKR/GITLAWB on Base) is the only settlement rail; a non-real token (CYOS) or non-live config has no rail and returns 422 settlement_unavailable.", {
244
229
  title: z.string().min(2).max(160).describe("Short task title."),
245
230
  category: z.enum(TASK_CATEGORIES),
@@ -414,5 +399,5 @@ const transport = new StdioServerTransport();
414
399
  await server.connect(transport);
415
400
  console.error(`CYBERDYNE MCP server running on stdio → ${config.apiUrl}` +
416
401
  (config.token ? "" : " (no key — run `npx cyberdyne-mcp onboard` to self-generate a wallet + key, or `login cyb_…`, or set CYBERDYNE_IDENTITY_TOKEN; networked tools error until then)") +
417
- ". Tools (9): onboard, list_categories, search_humans, post_task, authorize_task, get_task, review_submission, close_task, reclaim." +
402
+ ". Tools (8): onboard, list_categories, post_task, authorize_task, get_task, review_submission, close_task, reclaim." +
418
403
  " CLI: onboard, login, post, tasks.");
package/llms.txt CHANGED
@@ -26,11 +26,10 @@ License: MIT
26
26
  - CYBERDYNE_API_URL — base URL of the platform API. Default
27
27
  https://app.cyberdyne-os.xyz
28
28
 
29
- ## Tools → live endpoints (9)
29
+ ## Tools → live endpoints (8)
30
30
 
31
31
  - onboard — zero-browser bootstrap: generate/import a wallet, SIWE sign-in, mint the cyb_ API key, save both to ~/.cyberdyne/config.json (0600)
32
32
  - list_categories — static; the seven task categories (no network)
33
- - search_humans — POST /api/a2a {search_humans}; discovery only (no direct hire) — query by skills[], min_reputation, location
34
33
  - post_task — POST /api/tasks; open an FCFS pool bounty (reward_usd is the total budget; quantity units; not charged until authorize). Response returns authIntent + deployFee
35
34
  - authorize_task — POST /api/tasks/[id]/authorize; freeze the whole budget on the audited escrow (auth_intent + deploy_fee, or pre-made signed_payment + fee_tx_hash)
36
35
  - get_task — GET /api/tasks/[id]; task + submissions/claims; poll for a pending submission
@@ -62,8 +61,7 @@ audited base/commerce-payments AuthCaptureEscrow at 0xBdEA0D1bcC5966192B070Fdf62
62
61
  on Base). authorize_task freezes the whole budget; review_submission captures one
63
62
  unit to the human (full reward, in-token); close_task voids the unfilled remainder
64
63
  via the operator; reclaim is the agent's own payer-only on-chain recovery if the
65
- operator is unavailable. search_humans uses the a2a JSON-RPC gateway because the
66
- REST GET /api/humans is session-only.
64
+ operator is unavailable.
67
65
 
68
66
  ## For diligence (what is independently verifiable today)
69
67
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cyberdyne-mcp",
3
- "version": "0.6.18",
3
+ "version": "0.6.20",
4
4
  "mcpName": "io.github.Cyberdyne-OS/cyberdyne-mcp",
5
5
  "publishConfig": {
6
6
  "access": "public"
package/src/client.ts CHANGED
@@ -1,13 +1,9 @@
1
1
  /**
2
2
  * Typed HTTP client for the LIVE CYBERDYNE platform API.
3
3
  *
4
- * Two rails, both keyed by the same agent token (a `cyb_…` API key):
5
- * - REST: `Authorization: Bearer ${token}` on /api/tasks, /api/treasury, … —
6
- * the headless-agent rail. Used for post/assign/authorize/get/release/
7
- * fund/close/claims/treasury.
8
- * - a2a: POST /api/a2a — a JSON-RPC 2.0 gateway that carries the key in the
9
- * body (`identity_token`). Used for `search_humans` (the REST
10
- * GET /api/humans is session-only and rejects Bearer keys).
4
+ * One rail, keyed by the agent token (a `cyb_…` API key):
5
+ * - REST: `Authorization: Bearer ${token}` on /api/tasks, /api/submissions, … —
6
+ * the headless-agent rail (post/authorize/get/review/close).
11
7
  *
12
8
  * The agent key (`cyb_…`) is resolved, in order, from:
13
9
  * 1. env CYBERDYNE_IDENTITY_TOKEN (e.g. `claude mcp add … -e CYBERDYNE_IDENTITY_TOKEN=…`)
@@ -114,6 +110,30 @@ export function saveTokenAndWallet(token: string, walletKey: string): string {
114
110
  return writeConfigFile({ identity_token: token.trim(), walletKey: walletKey.trim() });
115
111
  }
116
112
 
113
+ /**
114
+ * Last resort (config save failed): write the live minted key to a FRESH 0600 recovery
115
+ * file — never to stderr/logs and never into a thrown error (which would reach the LLM
116
+ * via the MCP tool-result channel). Returns the path, or "" if even this fails.
117
+ */
118
+ export function saveKeyRecovery(token: string): string {
119
+ try {
120
+ mkdirSync(join(homedir(), ".cyberdyne"), { recursive: true, mode: 0o700 });
121
+ const p = join(homedir(), ".cyberdyne", `key-recovery-${Date.now()}.txt`);
122
+ let flags = FS.O_WRONLY | FS.O_CREAT | FS.O_EXCL;
123
+ if (typeof FS.O_NOFOLLOW === "number") flags |= FS.O_NOFOLLOW;
124
+ const fd = openSync(p, flags, 0o600);
125
+ try {
126
+ fchmodSync(fd, 0o600);
127
+ writeSync(fd, token.trim() + "\n");
128
+ } finally {
129
+ closeSync(fd);
130
+ }
131
+ return p;
132
+ } catch {
133
+ return "";
134
+ }
135
+ }
136
+
117
137
  /** Resolve config: token from env first, then the saved login. URL from env only. */
118
138
  export function readConfig(env: NodeJS.ProcessEnv = process.env): CyberdyneConfig {
119
139
  const apiUrl = (env.CYBERDYNE_API_URL || DEFAULT_API_URL).replace(/\/+$/, "");
@@ -189,30 +209,4 @@ export class CyberdyneClient {
189
209
  return json as T;
190
210
  }
191
211
 
192
- /**
193
- * a2a JSON-RPC call. The agent key travels in the params as `identity_token`.
194
- * Returns the JSON-RPC `result`; throws ApiError on a JSON-RPC error or non-2xx.
195
- */
196
- async a2a<T = unknown>(method: string, params: Record<string, unknown> = {}): Promise<T> {
197
- const token = this.requireToken();
198
- const res = await fetch(this.config.apiUrl + "/api/a2a", {
199
- method: "POST",
200
- headers: { "content-type": "application/json", accept: "application/json" },
201
- body: JSON.stringify({
202
- jsonrpc: "2.0",
203
- id: 1,
204
- method,
205
- params: { ...params, identity_token: token },
206
- }),
207
- });
208
- const json = (await res.json().catch(() => ({}))) as {
209
- result?: T;
210
- error?: { code: number; message: string };
211
- };
212
- if (!res.ok || json.error) {
213
- const code = json.error ? `${json.error.code}:${json.error.message}` : `http_${res.status}`;
214
- throw new ApiError(res.status, code, `a2a ${method}`);
215
- }
216
- return json.result as T;
217
- }
218
212
  }
package/src/onboard.ts CHANGED
@@ -25,7 +25,7 @@ import { bytesToHex } from "viem";
25
25
  import { validateMnemonic } from "@scure/bip39";
26
26
  import { wordlist } from "@scure/bip39/wordlists/english";
27
27
  import type { PrivateKeyAccount } from "viem/accounts";
28
- import { readConfig, readSavedWalletKey, saveTokenAndWallet, configPath as configFilePath } from "./client.js";
28
+ import { readConfig, readSavedWalletKey, saveTokenAndWallet, saveKeyRecovery, configPath as configFilePath } from "./client.js";
29
29
 
30
30
  /** Normalise a private key to the 0x-prefixed form viem expects. */
31
31
  function normalizeKey(pk: string): `0x${string}` {
@@ -278,11 +278,23 @@ export async function onboard(
278
278
  try {
279
279
  configPath = saveTokenAndWallet(apiKey, privateKey);
280
280
  } catch (e) {
281
- // Print the live key to STDERR only (the console), NEVER in the thrown error
282
- // the error message travels through the MCP tool-result channel into the calling
283
- // LLM's context/transcripts, which would leak the credential.
284
- console.error(`[cyberdyne-mcp] minted agent key (save failed): ${apiKey}\n save it manually with: echo ${apiKey} | npx cyberdyne-mcp login`);
285
- throw new Error(`minted agent key ${apiKey.slice(0, 10)}… but failed to save ~/.cyberdyne/config.json: ${e instanceof Error ? e.message : String(e)} — the FULL key was printed to the console (stderr); save it with \`npx cyberdyne-mcp login\`.`);
281
+ // The key is ALREADY minted server-side. Write it to a FRESH 0600 recovery file
282
+ // never the raw key to stderr/logs, and never into the thrown error (which travels
283
+ // through the MCP tool-result channel into the calling LLM's context). Reference the
284
+ // file by PATH only, so no transcript or log ever captures the credential. (M1.)
285
+ const recoveryPath = saveKeyRecovery(apiKey);
286
+ console.error(
287
+ `[cyberdyne-mcp] minted agent key but failed to save config.${
288
+ recoveryPath
289
+ ? ` The key was written to ${recoveryPath} (chmod 600) — load it with: cat ${recoveryPath} | npx cyberdyne-mcp login then delete that file.`
290
+ : " Could not write a recovery file; re-run onboard for a fresh key and revoke the orphan in the app."
291
+ }`,
292
+ );
293
+ throw new Error(
294
+ `minted agent key ${apiKey.slice(0, 10)}… but failed to save ~/.cyberdyne/config.json: ${e instanceof Error ? e.message : String(e)}.${
295
+ recoveryPath ? " The full key was written to a 0600 recovery file (path shown in the console/stderr)." : " Re-run onboard for a fresh key."
296
+ }`,
297
+ );
286
298
  }
287
299
 
288
300
  // 6. (optional) auto-link Bankr — zero human interaction. If a bk_ key is supplied
package/src/server.ts CHANGED
@@ -7,7 +7,6 @@
7
7
  * every tool is a thin, typed wrapper over an HTTP endpoint on the live backend.
8
8
  *
9
9
  * list_categories — the static task taxonomy (no network)
10
- * search_humans — POST /api/a2a {search_humans} → capability index
11
10
  * post_task — POST /api/tasks → open an FCFS pool bounty
12
11
  * authorize_task — POST /api/tasks/[id]/authorize → sign budget + pay fee + freeze
13
12
  * get_task — GET /api/tasks/[id] → status + submissions/claims
@@ -15,10 +14,8 @@
15
14
  * close_task — POST /api/tasks/[id]/close → refund the unfilled budget (operator voids)
16
15
  * reclaim — on-chain reclaim(paymentInfo) → trustless self-recovery, payer-only, no operator
17
16
  *
18
- * Auth: every networked tool sends the agent's `cyb_…` key. The REST routes take
19
- * it as `Authorization: Bearer …`; search_humans goes through the a2a JSON-RPC
20
- * gateway (the REST GET /api/humans is session-only), which carries the key as
21
- * `identity_token`.
17
+ * Auth: every networked tool sends the agent's `cyb_…` key as
18
+ * `Authorization: Bearer …`.
22
19
  *
23
20
  * The HUMAN submit-proof step happens in the app/UI (human-only — agents cannot
24
21
  * submit on a human's behalf). There is ONE settlement model for real tokens:
@@ -253,27 +250,6 @@ server.tool(
253
250
  }),
254
251
  );
255
252
 
256
- server.tool(
257
- "search_humans",
258
- "Find verified humans by capability via the live capability index (a2a gateway). Filters are optional and combine (AND). Results are role='human' profiles ranked by reputation, projected to public columns (no wallets/balances). Note: `skills` is an array.",
259
- {
260
- skills: z
261
- .array(z.enum(TASK_CATEGORIES))
262
- .optional()
263
- .describe("Task categories the human must be able to do (all must match)."),
264
- min_reputation: z.number().min(0).max(5).optional().describe("Minimum reputation (0–5)."),
265
- location: z.string().optional().describe("Substring match on location, e.g. 'ES', 'Tokyo'."),
266
- },
267
- async ({ skills, min_reputation, location }) =>
268
- guardUntrusted(() =>
269
- client.a2a<{ humans: unknown[] }>("search_humans", {
270
- ...(skills ? { skills } : {}),
271
- ...(min_reputation != null ? { min_reputation } : {}),
272
- ...(location ? { location } : {}),
273
- }),
274
- ),
275
- );
276
-
277
253
  server.tool(
278
254
  "post_task",
279
255
  "Open an FCFS pool bounty on the marketplace. There is NO direct hire and NO agent-picks-human — every task is an open bounty: you freeze a budget, ANY eligible human submits first-come-first-served, and you approve/reject each submission. Funds are NOT charged at post — the budget is frozen later at authorize_task. `reward_usd` is the total budget; `quantity` is how many identical units (humans) it pays — each unit holds reward_usd/quantity (each unit must be >= $0.01). Returns the created task (with its id) plus `authIntent` (the budget authorization to sign) and `deployFee` { usd, bps, recipient, token } (a SEPARATE non-refundable fee tx) — pass BOTH to authorize_task. The non-custodial POOL escrow (USDC/BNKR/GITLAWB on Base) is the only settlement rail; a non-real token (CYOS) or non-live config has no rail and returns 422 settlement_unavailable.",
@@ -497,6 +473,6 @@ await server.connect(transport);
497
473
  console.error(
498
474
  `CYBERDYNE MCP server running on stdio → ${config.apiUrl}` +
499
475
  (config.token ? "" : " (no key — run `npx cyberdyne-mcp onboard` to self-generate a wallet + key, or `login cyb_…`, or set CYBERDYNE_IDENTITY_TOKEN; networked tools error until then)") +
500
- ". Tools (9): onboard, list_categories, search_humans, post_task, authorize_task, get_task, review_submission, close_task, reclaim." +
476
+ ". Tools (8): onboard, list_categories, post_task, authorize_task, get_task, review_submission, close_task, reclaim." +
501
477
  " CLI: onboard, login, post, tasks.",
502
478
  );