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 +1 -3
- package/dist/client.js +29 -30
- package/dist/onboard.js +10 -6
- package/dist/server.js +3 -18
- package/llms.txt +2 -4
- package/package.json +1 -1
- package/src/client.ts +27 -33
- package/src/onboard.ts +18 -6
- package/src/server.ts +3 -27
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
|
-
|
|
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
|
-
*
|
|
5
|
-
* - REST:
|
|
6
|
-
*
|
|
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
|
-
//
|
|
237
|
-
// the
|
|
238
|
-
// LLM's context
|
|
239
|
-
|
|
240
|
-
|
|
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
|
|
19
|
-
*
|
|
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 (
|
|
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 (
|
|
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.
|
|
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
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
|
-
*
|
|
5
|
-
* - REST:
|
|
6
|
-
*
|
|
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
|
-
//
|
|
282
|
-
// the
|
|
283
|
-
// LLM's context
|
|
284
|
-
|
|
285
|
-
|
|
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
|
|
19
|
-
*
|
|
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 (
|
|
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
|
);
|