cyberdyne-mcp 0.6.4 → 0.6.5
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/dist/server.js +7 -10
- package/package.json +2 -4
- package/src/server.ts +7 -17
- package/dist/founder-check.js +0 -91
- package/dist/smoke.js +0 -90
- package/src/founder-check.ts +0 -103
- package/src/smoke.ts +0 -108
package/dist/server.js
CHANGED
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
* list_categories — the static task taxonomy (no network)
|
|
10
10
|
* search_humans — POST /api/a2a {search_humans} → capability index
|
|
11
11
|
* get_treasury — GET /api/treasury → the agent's balance
|
|
12
|
-
* fund_treasury — POST /api/treasury/fund → demo top-up (testnet only)
|
|
13
12
|
* get_deposit_address — GET /api/treasury/deposit → where to send real USDC (live)
|
|
14
13
|
* deposit — POST /api/treasury/deposit → credit treasury from a real USDC tx
|
|
15
14
|
* withdraw_treasury — POST /api/treasury/withdraw → pull unspent treasury back to your wallet (live)
|
|
@@ -158,7 +157,7 @@ async function guard(fn) {
|
|
|
158
157
|
}
|
|
159
158
|
}
|
|
160
159
|
// ---- Server ---------------------------------------------------------------
|
|
161
|
-
const server = new McpServer({ name: "cyberdyne", version: "0.6.
|
|
160
|
+
const server = new McpServer({ name: "cyberdyne", version: "0.6.5" });
|
|
162
161
|
server.tool("list_categories", "List the kinds of real-world work CYBERDYNE humans can do. Static (no network). Use this to learn the valid `category` values before posting a task.", {}, async () => json(Object.entries(CATEGORIES).map(([id, blurb]) => ({ id, blurb }))));
|
|
163
162
|
server.tool("onboard", "BOOTSTRAP (works WITHOUT an existing key — the one tool that self-onboards). Zero-browser: generates a fresh wallet if you don't have one, signs in to CYBERDYNE with it (SIWE), mints your `cyb_` agent API key, and saves both to ~/.cyberdyne/config.json (0600) so every other tool here authenticates automatically. No web dashboard, no env vars. Returns your wallet address, the cyb_ key (shown once), and the next steps (fund via get_deposit_address+deposit → post_task → authorize_task → review_submission → close_task). The same generated wallet auto-signs pool budgets. To bring your OWN wallet instead, use the CLI: `npx cyberdyne-mcp onboard --import <0xKEY | mnemonic>` (or --create for a fresh one). Idempotent-ish: re-running with a saved wallet reuses it and mints a fresh key.", {}, async () => guard(async () => {
|
|
164
163
|
const r = await onboard();
|
|
@@ -183,17 +182,16 @@ server.tool("search_humans", "Find verified humans by capability via the live ca
|
|
|
183
182
|
...(min_reputation != null ? { min_reputation } : {}),
|
|
184
183
|
...(location ? { location } : {}),
|
|
185
184
|
})));
|
|
186
|
-
server.tool("get_treasury", "Get the agent's own treasury
|
|
187
|
-
server.tool("fund_treasury", "Demo top-up (TESTNET/DEMO ONLY): add USD to the treasury balance. DISABLED when the platform is live (returns 403 funding_disabled) — on the live rail fund with REAL USDC via get_deposit_address + deposit instead.", { amount_usd: z.number().positive().describe("USD to add to the treasury balance.") }, async ({ amount_usd }) => guard(() => client.rest("POST", "/api/treasury/fund", { body: { amount_usd } })));
|
|
185
|
+
server.tool("get_treasury", "Get the agent's own treasury — residual on-chain balance available to cover deploy fees and to withdraw. Fund it with REAL USDC via get_deposit_address + deposit. Returns null if the agent has no treasury yet.", {}, async () => guard(() => client.rest("GET", "/api/treasury")));
|
|
188
186
|
server.tool("get_deposit_address", "Get the on-chain address to fund your treasury with REAL USDC (live rail). Returns { deposit_address, chain_id, usdc_address, decimals }. Send USDC from your VERIFIED wallet to deposit_address on Base, then call `deposit` with the tx hash to credit your treasury.", {}, async () => guard(() => client.rest("GET", "/api/treasury/deposit")));
|
|
189
|
-
server.tool("deposit", "Credit your treasury from a REAL on-chain USDC deposit
|
|
187
|
+
server.tool("deposit", "Credit your treasury from a REAL on-chain USDC deposit. First send USDC to the address from get_deposit_address (from your verified wallet), then call this with the transaction hash. The transfer is verified on-chain (to = platform wallet, from = your wallet) and credited exactly once — resubmitting the same tx never double-credits.", {
|
|
190
188
|
tx_hash: z
|
|
191
189
|
.string()
|
|
192
190
|
.regex(/^0x[0-9a-fA-F]{64}$/)
|
|
193
191
|
.describe("The Base tx hash of your USDC transfer to the deposit address."),
|
|
194
192
|
}, async ({ tx_hash }) => guard(() => client.rest("POST", "/api/treasury/deposit", { body: { tx_hash } })));
|
|
195
193
|
server.tool("withdraw_treasury", "Recover UNSPENT treasury to your wallet (live rail): pull USDC out of your treasury back to your own VERIFIED deposit wallet on Base — no browser. Available balance is your treasury balance net of any open escrow holds. Funds can ONLY go to your verified wallet (no destination param), so a leaked key can't redirect them. Returns { ok, tx_hash, amount_usd, to }. 400 insufficient_treasury if the balance can't cover it; 403 withdraws_disabled on the demo rail.", { amount_usd: z.number().positive().describe("USD to withdraw from your treasury to your verified wallet.") }, async ({ amount_usd }) => guard(() => client.rest("POST", "/api/treasury/withdraw", { body: { amount_usd } })));
|
|
196
|
-
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)
|
|
194
|
+
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.", {
|
|
197
195
|
title: z.string().min(2).max(160).describe("Short task title."),
|
|
198
196
|
category: z.enum(TASK_CATEGORIES),
|
|
199
197
|
description: z.string().max(4000).optional().describe("What you need the human to do."),
|
|
@@ -205,7 +203,7 @@ server.tool("post_task", "Open an FCFS pool bounty on the marketplace. There is
|
|
|
205
203
|
pay_token: z.enum(["USDC", "BNKR", "CYOS"]).optional().describe("Settlement token (default USDC)."),
|
|
206
204
|
deadline_hours: z.number().int().positive().optional(),
|
|
207
205
|
}, async (args) => guard(() => client.rest("POST", "/api/tasks", { body: args })));
|
|
208
|
-
server.tool("authorize_task", "Freeze the bounty budget on-chain (the second step of the FCFS flow). REAL-TOKEN POOL rail: pass BOTH `auth_intent` (the authIntent from post_task) AND `deploy_fee` (the deployFee object from post_task) — with CYBERDYNE_EVM_PRIVATE_KEY set, the MCP signs the whole-budget authorization AND pays the separate 2.5% USDC / 5% other-token deploy fee tx from its wallet, then freezes the budget on the audited escrow; or pass a pre-signed `signed_payment` and a pre-paid `fee_tx_hash`. After this, any eligible human submits FCFS and you review_submission each.
|
|
206
|
+
server.tool("authorize_task", "Freeze the bounty budget on-chain (the second step of the FCFS flow). REAL-TOKEN POOL rail: pass BOTH `auth_intent` (the authIntent from post_task) AND `deploy_fee` (the deployFee object from post_task) — with CYBERDYNE_EVM_PRIVATE_KEY set, the MCP signs the whole-budget authorization AND pays the separate 2.5% USDC / 5% other-token deploy fee tx from its wallet, then freezes the budget on the audited escrow; or pass a pre-signed `signed_payment` and a pre-paid `fee_tx_hash`. After this, any eligible human submits FCFS and you review_submission each. The non-custodial POOL escrow is the only rail; a non-real token / non-live config returns 409 settlement_unavailable. Idempotent once frozen.", {
|
|
209
207
|
task_id: z.string().uuid(),
|
|
210
208
|
signed_payment: z.string().optional().describe("Pre-signed base64 auth-capture payload (external/Bankr signer)."),
|
|
211
209
|
auth_intent: z.unknown().optional().describe("The authIntent from post_task — required for MCP wallet auto-signing."),
|
|
@@ -283,8 +281,7 @@ server.registerPrompt("quickstart", {
|
|
|
283
281
|
"",
|
|
284
282
|
"FUND (optional — the pool freezes from your wallet at deploy, but a treasury can cover fees):",
|
|
285
283
|
"1. get_deposit_address -> the platform deposit address on Base.",
|
|
286
|
-
"2. Send USDC to it FROM your own verified wallet, then deposit({ tx_hash }) -> credits your treasury (idempotent).",
|
|
287
|
-
" (fund_treasury is demo/testnet only and is disabled on the live rail.) Check get_treasury anytime.",
|
|
284
|
+
"2. Send USDC to it FROM your own verified wallet, then deposit({ tx_hash }) -> credits your treasury (idempotent). Check get_treasury anytime.",
|
|
288
285
|
"",
|
|
289
286
|
"POST + PAY (the single FCFS flow):",
|
|
290
287
|
"3. post_task({ title, category, reward_usd, quantity, duration_min, difficulty }) -> returns { task, authIntent, deployFee }. reward_usd is the TOTAL budget; quantity is how many humans it pays (each unit must be >= $0.01). authIntent is the whole-budget authorization; deployFee is a SEPARATE non-refundable fee tx (2.5% USDC / 5% other token).",
|
|
@@ -305,5 +302,5 @@ const transport = new StdioServerTransport();
|
|
|
305
302
|
await server.connect(transport);
|
|
306
303
|
console.error(`CYBERDYNE MCP server running on stdio → ${config.apiUrl}` +
|
|
307
304
|
(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)") +
|
|
308
|
-
". Tools (
|
|
305
|
+
". Tools (13): onboard, list_categories, search_humans, get_treasury, get_deposit_address, deposit, withdraw_treasury, post_task, authorize_task, get_task, review_submission, close_task, reclaim." +
|
|
309
306
|
" CLI: onboard, login, treasury (alias balance/fees), post, tasks.");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cyberdyne-mcp",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.5",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -45,9 +45,7 @@
|
|
|
45
45
|
"build": "tsc",
|
|
46
46
|
"prepublishOnly": "npm run build",
|
|
47
47
|
"start": "node dist/server.js",
|
|
48
|
-
"dev": "tsx src/server.ts"
|
|
49
|
-
"smoke": "tsx src/smoke.ts",
|
|
50
|
-
"founder-check": "tsx src/founder-check.ts"
|
|
48
|
+
"dev": "tsx src/server.ts"
|
|
51
49
|
},
|
|
52
50
|
"engines": {
|
|
53
51
|
"node": ">=18"
|
package/src/server.ts
CHANGED
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
* list_categories — the static task taxonomy (no network)
|
|
10
10
|
* search_humans — POST /api/a2a {search_humans} → capability index
|
|
11
11
|
* get_treasury — GET /api/treasury → the agent's balance
|
|
12
|
-
* fund_treasury — POST /api/treasury/fund → demo top-up (testnet only)
|
|
13
12
|
* get_deposit_address — GET /api/treasury/deposit → where to send real USDC (live)
|
|
14
13
|
* deposit — POST /api/treasury/deposit → credit treasury from a real USDC tx
|
|
15
14
|
* withdraw_treasury — POST /api/treasury/withdraw → pull unspent treasury back to your wallet (live)
|
|
@@ -170,7 +169,7 @@ async function guard<T>(fn: () => Promise<T>) {
|
|
|
170
169
|
|
|
171
170
|
// ---- Server ---------------------------------------------------------------
|
|
172
171
|
|
|
173
|
-
const server = new McpServer({ name: "cyberdyne", version: "0.6.
|
|
172
|
+
const server = new McpServer({ name: "cyberdyne", version: "0.6.5" });
|
|
174
173
|
|
|
175
174
|
server.tool(
|
|
176
175
|
"list_categories",
|
|
@@ -220,19 +219,11 @@ server.tool(
|
|
|
220
219
|
|
|
221
220
|
server.tool(
|
|
222
221
|
"get_treasury",
|
|
223
|
-
"Get the agent's own treasury
|
|
222
|
+
"Get the agent's own treasury — residual on-chain balance available to cover deploy fees and to withdraw. Fund it with REAL USDC via get_deposit_address + deposit. Returns null if the agent has no treasury yet.",
|
|
224
223
|
{},
|
|
225
224
|
async () => guard(() => client.rest("GET", "/api/treasury")),
|
|
226
225
|
);
|
|
227
226
|
|
|
228
|
-
server.tool(
|
|
229
|
-
"fund_treasury",
|
|
230
|
-
"Demo top-up (TESTNET/DEMO ONLY): add USD to the treasury balance. DISABLED when the platform is live (returns 403 funding_disabled) — on the live rail fund with REAL USDC via get_deposit_address + deposit instead.",
|
|
231
|
-
{ amount_usd: z.number().positive().describe("USD to add to the treasury balance.") },
|
|
232
|
-
async ({ amount_usd }) =>
|
|
233
|
-
guard(() => client.rest("POST", "/api/treasury/fund", { body: { amount_usd } })),
|
|
234
|
-
);
|
|
235
|
-
|
|
236
227
|
server.tool(
|
|
237
228
|
"get_deposit_address",
|
|
238
229
|
"Get the on-chain address to fund your treasury with REAL USDC (live rail). Returns { deposit_address, chain_id, usdc_address, decimals }. Send USDC from your VERIFIED wallet to deposit_address on Base, then call `deposit` with the tx hash to credit your treasury.",
|
|
@@ -242,7 +233,7 @@ server.tool(
|
|
|
242
233
|
|
|
243
234
|
server.tool(
|
|
244
235
|
"deposit",
|
|
245
|
-
"Credit your treasury from a REAL on-chain USDC deposit
|
|
236
|
+
"Credit your treasury from a REAL on-chain USDC deposit. First send USDC to the address from get_deposit_address (from your verified wallet), then call this with the transaction hash. The transfer is verified on-chain (to = platform wallet, from = your wallet) and credited exactly once — resubmitting the same tx never double-credits.",
|
|
246
237
|
{
|
|
247
238
|
tx_hash: z
|
|
248
239
|
.string()
|
|
@@ -263,7 +254,7 @@ server.tool(
|
|
|
263
254
|
|
|
264
255
|
server.tool(
|
|
265
256
|
"post_task",
|
|
266
|
-
"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)
|
|
257
|
+
"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.",
|
|
267
258
|
{
|
|
268
259
|
title: z.string().min(2).max(160).describe("Short task title."),
|
|
269
260
|
category: z.enum(TASK_CATEGORIES),
|
|
@@ -281,7 +272,7 @@ server.tool(
|
|
|
281
272
|
|
|
282
273
|
server.tool(
|
|
283
274
|
"authorize_task",
|
|
284
|
-
"Freeze the bounty budget on-chain (the second step of the FCFS flow). REAL-TOKEN POOL rail: pass BOTH `auth_intent` (the authIntent from post_task) AND `deploy_fee` (the deployFee object from post_task) — with CYBERDYNE_EVM_PRIVATE_KEY set, the MCP signs the whole-budget authorization AND pays the separate 2.5% USDC / 5% other-token deploy fee tx from its wallet, then freezes the budget on the audited escrow; or pass a pre-signed `signed_payment` and a pre-paid `fee_tx_hash`. After this, any eligible human submits FCFS and you review_submission each.
|
|
275
|
+
"Freeze the bounty budget on-chain (the second step of the FCFS flow). REAL-TOKEN POOL rail: pass BOTH `auth_intent` (the authIntent from post_task) AND `deploy_fee` (the deployFee object from post_task) — with CYBERDYNE_EVM_PRIVATE_KEY set, the MCP signs the whole-budget authorization AND pays the separate 2.5% USDC / 5% other-token deploy fee tx from its wallet, then freezes the budget on the audited escrow; or pass a pre-signed `signed_payment` and a pre-paid `fee_tx_hash`. After this, any eligible human submits FCFS and you review_submission each. The non-custodial POOL escrow is the only rail; a non-real token / non-live config returns 409 settlement_unavailable. Idempotent once frozen.",
|
|
285
276
|
{
|
|
286
277
|
task_id: z.string().uuid(),
|
|
287
278
|
signed_payment: z.string().optional().describe("Pre-signed base64 auth-capture payload (external/Bankr signer)."),
|
|
@@ -405,8 +396,7 @@ server.registerPrompt(
|
|
|
405
396
|
"",
|
|
406
397
|
"FUND (optional — the pool freezes from your wallet at deploy, but a treasury can cover fees):",
|
|
407
398
|
"1. get_deposit_address -> the platform deposit address on Base.",
|
|
408
|
-
"2. Send USDC to it FROM your own verified wallet, then deposit({ tx_hash }) -> credits your treasury (idempotent).",
|
|
409
|
-
" (fund_treasury is demo/testnet only and is disabled on the live rail.) Check get_treasury anytime.",
|
|
399
|
+
"2. Send USDC to it FROM your own verified wallet, then deposit({ tx_hash }) -> credits your treasury (idempotent). Check get_treasury anytime.",
|
|
410
400
|
"",
|
|
411
401
|
"POST + PAY (the single FCFS flow):",
|
|
412
402
|
"3. post_task({ title, category, reward_usd, quantity, duration_min, difficulty }) -> returns { task, authIntent, deployFee }. reward_usd is the TOTAL budget; quantity is how many humans it pays (each unit must be >= $0.01). authIntent is the whole-budget authorization; deployFee is a SEPARATE non-refundable fee tx (2.5% USDC / 5% other token).",
|
|
@@ -431,6 +421,6 @@ await server.connect(transport);
|
|
|
431
421
|
console.error(
|
|
432
422
|
`CYBERDYNE MCP server running on stdio → ${config.apiUrl}` +
|
|
433
423
|
(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)") +
|
|
434
|
-
". Tools (
|
|
424
|
+
". Tools (13): onboard, list_categories, search_humans, get_treasury, get_deposit_address, deposit, withdraw_treasury, post_task, authorize_task, get_task, review_submission, close_task, reclaim." +
|
|
435
425
|
" CLI: onboard, login, treasury (alias balance/fees), post, tasks.",
|
|
436
426
|
);
|
package/dist/founder-check.js
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Example: a trading agent hires a human for a founder liveness check before it buys.
|
|
3
|
-
*
|
|
4
|
-
* A trading agent can run every on-chain check itself — but it can't tell whether
|
|
5
|
-
* a real person is behind a token. Fake / impersonated / deepfaked teams are the
|
|
6
|
-
* #1 token scam, and defeating a deepfake on a live call is something only a human
|
|
7
|
-
* can do. So before a risky buy, the agent hires a human through CYBERDYNE to
|
|
8
|
-
* video-verify the founder, then settles on a passing verify. This is the pattern
|
|
9
|
-
* behind e.g. Bankr (https://bankr.bot) and any x402 trader.
|
|
10
|
-
*
|
|
11
|
-
* This drives the LIVE platform. Requires CYBERDYNE_IDENTITY_TOKEN (a cyb_ key);
|
|
12
|
-
* optionally CYBERDYNE_API_URL. No key is hardcoded — it no-ops without one.
|
|
13
|
-
*
|
|
14
|
-
* CYBERDYNE_API_URL=http://localhost:3000 CYBERDYNE_IDENTITY_TOKEN=cyb_… \
|
|
15
|
-
* npm run build && npm run founder-check
|
|
16
|
-
*/
|
|
17
|
-
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
18
|
-
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
19
|
-
const token = process.env.CYBERDYNE_IDENTITY_TOKEN;
|
|
20
|
-
if (!token) {
|
|
21
|
-
console.log("founder-check: no-op. Set CYBERDYNE_IDENTITY_TOKEN (a cyb_ key) and optionally " +
|
|
22
|
-
"CYBERDYNE_API_URL to run this example against the live platform.");
|
|
23
|
-
process.exit(0);
|
|
24
|
-
}
|
|
25
|
-
const transport = new StdioClientTransport({
|
|
26
|
-
command: "node",
|
|
27
|
-
args: ["dist/server.js"],
|
|
28
|
-
env: { ...process.env },
|
|
29
|
-
});
|
|
30
|
-
const client = new Client({ name: "trading-agent", version: "0" });
|
|
31
|
-
await client.connect(transport);
|
|
32
|
-
const call = async (name, args = {}) => {
|
|
33
|
-
const r = await client.callTool({ name, arguments: args });
|
|
34
|
-
const data = JSON.parse(r.content[0].text);
|
|
35
|
-
if (r.isError)
|
|
36
|
-
throw new Error(`${name} → ${data.error}`);
|
|
37
|
-
return data;
|
|
38
|
-
};
|
|
39
|
-
console.log("[agent] connected to CYBERDYNE over MCP\n");
|
|
40
|
-
// 0. Make sure the agent treasury can cover the bounty (demo top-up).
|
|
41
|
-
const t = await call("get_treasury");
|
|
42
|
-
if (Number(t.treasury?.balance_usd ?? 0) < 50) {
|
|
43
|
-
await call("fund_treasury", { amount_usd: 100 });
|
|
44
|
-
}
|
|
45
|
-
// 1. The agent can't verify a real human is behind the token — find one who can.
|
|
46
|
-
const found = await call("search_humans", { skills: ["groundtruth"], min_reputation: 4.8 });
|
|
47
|
-
const human = found.humans[0];
|
|
48
|
-
console.log(`search_humans(groundtruth, min_rep 4.8) -> ${found.humans.length} match`);
|
|
49
|
-
if (human)
|
|
50
|
-
console.log(` hiring ${human.handle} ${human.location} rep ${human.reputation}\n`);
|
|
51
|
-
// 2. Post the founder liveness check. Not charged until authorize.
|
|
52
|
-
const posted = await call("post_task", {
|
|
53
|
-
title: "Founder liveness check on $PEPE2",
|
|
54
|
-
category: "groundtruth",
|
|
55
|
-
description: "Get the claimed founder on a short video call and confirm they're a real, specific " +
|
|
56
|
-
"person — not an impersonator or deepfake — matched to a known reference. Return verified / not + notes.",
|
|
57
|
-
steps: [
|
|
58
|
-
"Live video matches a known reference",
|
|
59
|
-
"Passes liveness / deepfake probes => verified; otherwise not-verified + reasons",
|
|
60
|
-
],
|
|
61
|
-
reward_usd: 50,
|
|
62
|
-
duration_min: 30,
|
|
63
|
-
difficulty: "hard",
|
|
64
|
-
deadline_hours: 2,
|
|
65
|
-
});
|
|
66
|
-
const taskId = posted.task.id;
|
|
67
|
-
console.log(`post_task -> ${taskId} reward $${posted.task.reward_usd}`);
|
|
68
|
-
// (search_humans is discovery only — there is NO direct hire. The task is an open
|
|
69
|
-
// FCFS pool bounty: ANY eligible human submits first-come-first-served.)
|
|
70
|
-
void human;
|
|
71
|
-
// 3. Authorize — freeze the budget (POOL: authIntent + deployFee; manual: just task_id).
|
|
72
|
-
const authd = await call("authorize_task", {
|
|
73
|
-
task_id: taskId,
|
|
74
|
-
...(posted.authIntent ? { auth_intent: posted.authIntent } : {}),
|
|
75
|
-
...(posted.deployFee ? { deploy_fee: posted.deployFee } : {}),
|
|
76
|
-
});
|
|
77
|
-
console.log(`authorize_task -> escrow ${authd.task?.escrow_status}`);
|
|
78
|
-
// 4. Poll until the human's proof is in (they submit in the app — human-only).
|
|
79
|
-
const got = await call("get_task", { task_id: taskId });
|
|
80
|
-
console.log(`get_task -> ${got.task.status} submissions ${got.submissions.length}`);
|
|
81
|
-
// 5. If the proof is in, verify it and capture one unit to the human.
|
|
82
|
-
const pending = (got.submissions ?? []).find((s) => s.status === "pending");
|
|
83
|
-
if (pending) {
|
|
84
|
-
const settled = await call("review_submission", { submission_id: pending.id, approve: true, score: 5 });
|
|
85
|
-
console.log(`review_submission -> captured ${JSON.stringify(settled).slice(0, 80)}`);
|
|
86
|
-
console.log("\n[agent] has a human verification it could not produce itself, and the contributor is paid.");
|
|
87
|
-
}
|
|
88
|
-
else {
|
|
89
|
-
console.log("\n[agent] bounty is live and the budget is frozen; humans submit proof in the app FCFS, then the agent reviews each.");
|
|
90
|
-
}
|
|
91
|
-
await client.close();
|
package/dist/smoke.js
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Live smoke test: drive the MCP server against the REAL platform API like an
|
|
3
|
-
* agent would. Not shipped.
|
|
4
|
-
*
|
|
5
|
-
* Requires both env vars to run for real:
|
|
6
|
-
* CYBERDYNE_API_URL e.g. http://localhost:3000 or https://app.cyberdyne-os.xyz
|
|
7
|
-
* CYBERDYNE_IDENTITY_TOKEN the agent's cyb_ key
|
|
8
|
-
* If either is missing it no-ops with a clear message (no key is ever hardcoded).
|
|
9
|
-
*
|
|
10
|
-
* The server inherits this process's env over stdio, so the same creds drive it.
|
|
11
|
-
*/
|
|
12
|
-
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
13
|
-
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
14
|
-
const apiUrl = process.env.CYBERDYNE_API_URL;
|
|
15
|
-
const token = process.env.CYBERDYNE_IDENTITY_TOKEN;
|
|
16
|
-
if (!token) {
|
|
17
|
-
console.log("smoke: no-op. Set CYBERDYNE_IDENTITY_TOKEN (a cyb_ key) and optionally " +
|
|
18
|
-
"CYBERDYNE_API_URL to run the live flow against the platform.\n" +
|
|
19
|
-
" e.g. CYBERDYNE_API_URL=http://localhost:3000 CYBERDYNE_IDENTITY_TOKEN=cyb_… npm run smoke");
|
|
20
|
-
process.exit(0);
|
|
21
|
-
}
|
|
22
|
-
const transport = new StdioClientTransport({
|
|
23
|
-
command: "node",
|
|
24
|
-
args: ["dist/server.js"],
|
|
25
|
-
env: { ...process.env },
|
|
26
|
-
});
|
|
27
|
-
const client = new Client({ name: "smoke", version: "0" });
|
|
28
|
-
await client.connect(transport);
|
|
29
|
-
const call = async (name, args = {}) => {
|
|
30
|
-
const r = await client.callTool({ name, arguments: args });
|
|
31
|
-
const data = JSON.parse(r.content[0].text);
|
|
32
|
-
if (r.isError)
|
|
33
|
-
throw new Error(`${name} → ${data.error}`);
|
|
34
|
-
return data;
|
|
35
|
-
};
|
|
36
|
-
console.log(`smoke → ${apiUrl ?? "https://app.cyberdyne-os.xyz"}`);
|
|
37
|
-
console.log("tools:", (await client.listTools()).tools.map((t) => t.name).join(", "));
|
|
38
|
-
// 1. Categories (static, no network).
|
|
39
|
-
const cats = await call("list_categories");
|
|
40
|
-
console.log(`list_categories → ${cats.length} categories`);
|
|
41
|
-
// 2. Treasury — ensure it can cover a small task; top up if needed.
|
|
42
|
-
let treasury = await call("get_treasury");
|
|
43
|
-
const balance = Number(treasury.treasury?.balance_usd ?? 0);
|
|
44
|
-
console.log(`get_treasury → balance $${balance}`);
|
|
45
|
-
if (balance < 5) {
|
|
46
|
-
treasury = await call("fund_treasury", { amount_usd: 25 });
|
|
47
|
-
console.log(`fund_treasury → balance $${treasury.treasury?.balance_usd}`);
|
|
48
|
-
}
|
|
49
|
-
// 3. Discover a human via the live capability index.
|
|
50
|
-
const found = await call("search_humans", { skills: ["capture"] });
|
|
51
|
-
console.log(`search_humans(capture) → ${found.humans.length} match`);
|
|
52
|
-
const human = found.humans[0];
|
|
53
|
-
// (search_humans is for discovery only — there is NO direct hire/assign. Every task
|
|
54
|
-
// is an open FCFS pool bounty: post a budget, humans submit, you review each.)
|
|
55
|
-
void human;
|
|
56
|
-
// 4. Post an FCFS pool bounty. reward_usd is the budget; not charged until authorize.
|
|
57
|
-
const posted = await call("post_task", {
|
|
58
|
-
title: "Read 10 phrases (smoke)",
|
|
59
|
-
category: "capture",
|
|
60
|
-
description: "Record 10 short phrases clearly in a quiet room.",
|
|
61
|
-
reward_usd: 3.5,
|
|
62
|
-
duration_min: 10,
|
|
63
|
-
difficulty: "easy",
|
|
64
|
-
});
|
|
65
|
-
const taskId = posted.task.id;
|
|
66
|
-
console.log(`post_task → ${taskId} (status ${posted.task.status})`);
|
|
67
|
-
// 5. Authorize — freeze the budget (POOL: pass authIntent + deployFee; manual: just task_id).
|
|
68
|
-
const authd = await call("authorize_task", {
|
|
69
|
-
task_id: taskId,
|
|
70
|
-
...(posted.authIntent ? { auth_intent: posted.authIntent } : {}),
|
|
71
|
-
...(posted.deployFee ? { deploy_fee: posted.deployFee } : {}),
|
|
72
|
-
});
|
|
73
|
-
console.log(`authorize_task → escrow_status ${authd.task?.escrow_status}`);
|
|
74
|
-
// 6. Poll the live task. The human submits proof in the app (human-only), so on a
|
|
75
|
-
// fresh task there is typically no submission yet — that is expected here.
|
|
76
|
-
const got = await call("get_task", { task_id: taskId });
|
|
77
|
-
console.log(`get_task → status ${got.task.status}, submissions ${got.submissions.length}, claims ${got.claims.length}`);
|
|
78
|
-
const pending = (got.submissions ?? []).find((s) => s.status === "pending");
|
|
79
|
-
if (pending) {
|
|
80
|
-
const settled = await call("review_submission", { submission_id: pending.id, approve: true, score: 5 });
|
|
81
|
-
console.log(`review_submission → captured one unit ${JSON.stringify(settled).slice(0, 80)}`);
|
|
82
|
-
}
|
|
83
|
-
else {
|
|
84
|
-
console.log("review_submission → skipped: no pending submission yet (a human submits proof in the app). " +
|
|
85
|
-
"Closing the task to refund the unfilled budget.");
|
|
86
|
-
const closed = await call("close_task", { task_id: taskId });
|
|
87
|
-
console.log(`close_task → status ${closed.task?.status ?? "closed"}`);
|
|
88
|
-
}
|
|
89
|
-
await client.close();
|
|
90
|
-
console.log("OK");
|
package/src/founder-check.ts
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Example: a trading agent hires a human for a founder liveness check before it buys.
|
|
3
|
-
*
|
|
4
|
-
* A trading agent can run every on-chain check itself — but it can't tell whether
|
|
5
|
-
* a real person is behind a token. Fake / impersonated / deepfaked teams are the
|
|
6
|
-
* #1 token scam, and defeating a deepfake on a live call is something only a human
|
|
7
|
-
* can do. So before a risky buy, the agent hires a human through CYBERDYNE to
|
|
8
|
-
* video-verify the founder, then settles on a passing verify. This is the pattern
|
|
9
|
-
* behind e.g. Bankr (https://bankr.bot) and any x402 trader.
|
|
10
|
-
*
|
|
11
|
-
* This drives the LIVE platform. Requires CYBERDYNE_IDENTITY_TOKEN (a cyb_ key);
|
|
12
|
-
* optionally CYBERDYNE_API_URL. No key is hardcoded — it no-ops without one.
|
|
13
|
-
*
|
|
14
|
-
* CYBERDYNE_API_URL=http://localhost:3000 CYBERDYNE_IDENTITY_TOKEN=cyb_… \
|
|
15
|
-
* npm run build && npm run founder-check
|
|
16
|
-
*/
|
|
17
|
-
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
18
|
-
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
19
|
-
|
|
20
|
-
const token = process.env.CYBERDYNE_IDENTITY_TOKEN;
|
|
21
|
-
if (!token) {
|
|
22
|
-
console.log(
|
|
23
|
-
"founder-check: no-op. Set CYBERDYNE_IDENTITY_TOKEN (a cyb_ key) and optionally " +
|
|
24
|
-
"CYBERDYNE_API_URL to run this example against the live platform.",
|
|
25
|
-
);
|
|
26
|
-
process.exit(0);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const transport = new StdioClientTransport({
|
|
30
|
-
command: "node",
|
|
31
|
-
args: ["dist/server.js"],
|
|
32
|
-
env: { ...process.env } as Record<string, string>,
|
|
33
|
-
});
|
|
34
|
-
const client = new Client({ name: "trading-agent", version: "0" });
|
|
35
|
-
await client.connect(transport);
|
|
36
|
-
|
|
37
|
-
const call = async (name: string, args: Record<string, unknown> = {}) => {
|
|
38
|
-
const r: any = await client.callTool({ name, arguments: args });
|
|
39
|
-
const data = JSON.parse(r.content[0].text);
|
|
40
|
-
if (r.isError) throw new Error(`${name} → ${data.error}`);
|
|
41
|
-
return data;
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
console.log("[agent] connected to CYBERDYNE over MCP\n");
|
|
45
|
-
|
|
46
|
-
// 0. Make sure the agent treasury can cover the bounty (demo top-up).
|
|
47
|
-
const t = await call("get_treasury");
|
|
48
|
-
if (Number(t.treasury?.balance_usd ?? 0) < 50) {
|
|
49
|
-
await call("fund_treasury", { amount_usd: 100 });
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// 1. The agent can't verify a real human is behind the token — find one who can.
|
|
53
|
-
const found = await call("search_humans", { skills: ["groundtruth"], min_reputation: 4.8 });
|
|
54
|
-
const human = found.humans[0];
|
|
55
|
-
console.log(`search_humans(groundtruth, min_rep 4.8) -> ${found.humans.length} match`);
|
|
56
|
-
if (human) console.log(` hiring ${human.handle} ${human.location} rep ${human.reputation}\n`);
|
|
57
|
-
|
|
58
|
-
// 2. Post the founder liveness check. Not charged until authorize.
|
|
59
|
-
const posted = await call("post_task", {
|
|
60
|
-
title: "Founder liveness check on $PEPE2",
|
|
61
|
-
category: "groundtruth",
|
|
62
|
-
description:
|
|
63
|
-
"Get the claimed founder on a short video call and confirm they're a real, specific " +
|
|
64
|
-
"person — not an impersonator or deepfake — matched to a known reference. Return verified / not + notes.",
|
|
65
|
-
steps: [
|
|
66
|
-
"Live video matches a known reference",
|
|
67
|
-
"Passes liveness / deepfake probes => verified; otherwise not-verified + reasons",
|
|
68
|
-
],
|
|
69
|
-
reward_usd: 50,
|
|
70
|
-
duration_min: 30,
|
|
71
|
-
difficulty: "hard",
|
|
72
|
-
deadline_hours: 2,
|
|
73
|
-
});
|
|
74
|
-
const taskId = posted.task.id;
|
|
75
|
-
console.log(`post_task -> ${taskId} reward $${posted.task.reward_usd}`);
|
|
76
|
-
|
|
77
|
-
// (search_humans is discovery only — there is NO direct hire. The task is an open
|
|
78
|
-
// FCFS pool bounty: ANY eligible human submits first-come-first-served.)
|
|
79
|
-
void human;
|
|
80
|
-
|
|
81
|
-
// 3. Authorize — freeze the budget (POOL: authIntent + deployFee; manual: just task_id).
|
|
82
|
-
const authd = await call("authorize_task", {
|
|
83
|
-
task_id: taskId,
|
|
84
|
-
...(posted.authIntent ? { auth_intent: posted.authIntent } : {}),
|
|
85
|
-
...(posted.deployFee ? { deploy_fee: posted.deployFee } : {}),
|
|
86
|
-
});
|
|
87
|
-
console.log(`authorize_task -> escrow ${authd.task?.escrow_status}`);
|
|
88
|
-
|
|
89
|
-
// 4. Poll until the human's proof is in (they submit in the app — human-only).
|
|
90
|
-
const got = await call("get_task", { task_id: taskId });
|
|
91
|
-
console.log(`get_task -> ${got.task.status} submissions ${got.submissions.length}`);
|
|
92
|
-
|
|
93
|
-
// 5. If the proof is in, verify it and capture one unit to the human.
|
|
94
|
-
const pending = (got.submissions ?? []).find((s: any) => s.status === "pending");
|
|
95
|
-
if (pending) {
|
|
96
|
-
const settled = await call("review_submission", { submission_id: pending.id, approve: true, score: 5 });
|
|
97
|
-
console.log(`review_submission -> captured ${JSON.stringify(settled).slice(0, 80)}`);
|
|
98
|
-
console.log("\n[agent] has a human verification it could not produce itself, and the contributor is paid.");
|
|
99
|
-
} else {
|
|
100
|
-
console.log("\n[agent] bounty is live and the budget is frozen; humans submit proof in the app FCFS, then the agent reviews each.");
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
await client.close();
|
package/src/smoke.ts
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Live smoke test: drive the MCP server against the REAL platform API like an
|
|
3
|
-
* agent would. Not shipped.
|
|
4
|
-
*
|
|
5
|
-
* Requires both env vars to run for real:
|
|
6
|
-
* CYBERDYNE_API_URL e.g. http://localhost:3000 or https://app.cyberdyne-os.xyz
|
|
7
|
-
* CYBERDYNE_IDENTITY_TOKEN the agent's cyb_ key
|
|
8
|
-
* If either is missing it no-ops with a clear message (no key is ever hardcoded).
|
|
9
|
-
*
|
|
10
|
-
* The server inherits this process's env over stdio, so the same creds drive it.
|
|
11
|
-
*/
|
|
12
|
-
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
13
|
-
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
14
|
-
|
|
15
|
-
const apiUrl = process.env.CYBERDYNE_API_URL;
|
|
16
|
-
const token = process.env.CYBERDYNE_IDENTITY_TOKEN;
|
|
17
|
-
|
|
18
|
-
if (!token) {
|
|
19
|
-
console.log(
|
|
20
|
-
"smoke: no-op. Set CYBERDYNE_IDENTITY_TOKEN (a cyb_ key) and optionally " +
|
|
21
|
-
"CYBERDYNE_API_URL to run the live flow against the platform.\n" +
|
|
22
|
-
" e.g. CYBERDYNE_API_URL=http://localhost:3000 CYBERDYNE_IDENTITY_TOKEN=cyb_… npm run smoke",
|
|
23
|
-
);
|
|
24
|
-
process.exit(0);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const transport = new StdioClientTransport({
|
|
28
|
-
command: "node",
|
|
29
|
-
args: ["dist/server.js"],
|
|
30
|
-
env: { ...process.env } as Record<string, string>,
|
|
31
|
-
});
|
|
32
|
-
const client = new Client({ name: "smoke", version: "0" });
|
|
33
|
-
await client.connect(transport);
|
|
34
|
-
|
|
35
|
-
const call = async (name: string, args: Record<string, unknown> = {}) => {
|
|
36
|
-
const r: any = await client.callTool({ name, arguments: args });
|
|
37
|
-
const data = JSON.parse(r.content[0].text);
|
|
38
|
-
if (r.isError) throw new Error(`${name} → ${data.error}`);
|
|
39
|
-
return data;
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
console.log(`smoke → ${apiUrl ?? "https://app.cyberdyne-os.xyz"}`);
|
|
43
|
-
console.log("tools:", (await client.listTools()).tools.map((t) => t.name).join(", "));
|
|
44
|
-
|
|
45
|
-
// 1. Categories (static, no network).
|
|
46
|
-
const cats = await call("list_categories");
|
|
47
|
-
console.log(`list_categories → ${cats.length} categories`);
|
|
48
|
-
|
|
49
|
-
// 2. Treasury — ensure it can cover a small task; top up if needed.
|
|
50
|
-
let treasury = await call("get_treasury");
|
|
51
|
-
const balance = Number(treasury.treasury?.balance_usd ?? 0);
|
|
52
|
-
console.log(`get_treasury → balance $${balance}`);
|
|
53
|
-
if (balance < 5) {
|
|
54
|
-
treasury = await call("fund_treasury", { amount_usd: 25 });
|
|
55
|
-
console.log(`fund_treasury → balance $${treasury.treasury?.balance_usd}`);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// 3. Discover a human via the live capability index.
|
|
59
|
-
const found = await call("search_humans", { skills: ["capture"] });
|
|
60
|
-
console.log(`search_humans(capture) → ${found.humans.length} match`);
|
|
61
|
-
const human = found.humans[0];
|
|
62
|
-
|
|
63
|
-
// (search_humans is for discovery only — there is NO direct hire/assign. Every task
|
|
64
|
-
// is an open FCFS pool bounty: post a budget, humans submit, you review each.)
|
|
65
|
-
void human;
|
|
66
|
-
|
|
67
|
-
// 4. Post an FCFS pool bounty. reward_usd is the budget; not charged until authorize.
|
|
68
|
-
const posted = await call("post_task", {
|
|
69
|
-
title: "Read 10 phrases (smoke)",
|
|
70
|
-
category: "capture",
|
|
71
|
-
description: "Record 10 short phrases clearly in a quiet room.",
|
|
72
|
-
reward_usd: 3.5,
|
|
73
|
-
duration_min: 10,
|
|
74
|
-
difficulty: "easy",
|
|
75
|
-
});
|
|
76
|
-
const taskId = posted.task.id;
|
|
77
|
-
console.log(`post_task → ${taskId} (status ${posted.task.status})`);
|
|
78
|
-
|
|
79
|
-
// 5. Authorize — freeze the budget (POOL: pass authIntent + deployFee; manual: just task_id).
|
|
80
|
-
const authd = await call("authorize_task", {
|
|
81
|
-
task_id: taskId,
|
|
82
|
-
...(posted.authIntent ? { auth_intent: posted.authIntent } : {}),
|
|
83
|
-
...(posted.deployFee ? { deploy_fee: posted.deployFee } : {}),
|
|
84
|
-
});
|
|
85
|
-
console.log(`authorize_task → escrow_status ${authd.task?.escrow_status}`);
|
|
86
|
-
|
|
87
|
-
// 6. Poll the live task. The human submits proof in the app (human-only), so on a
|
|
88
|
-
// fresh task there is typically no submission yet — that is expected here.
|
|
89
|
-
const got = await call("get_task", { task_id: taskId });
|
|
90
|
-
console.log(
|
|
91
|
-
`get_task → status ${got.task.status}, submissions ${got.submissions.length}, claims ${got.claims.length}`,
|
|
92
|
-
);
|
|
93
|
-
|
|
94
|
-
const pending = (got.submissions ?? []).find((s: any) => s.status === "pending");
|
|
95
|
-
if (pending) {
|
|
96
|
-
const settled = await call("review_submission", { submission_id: pending.id, approve: true, score: 5 });
|
|
97
|
-
console.log(`review_submission → captured one unit ${JSON.stringify(settled).slice(0, 80)}`);
|
|
98
|
-
} else {
|
|
99
|
-
console.log(
|
|
100
|
-
"review_submission → skipped: no pending submission yet (a human submits proof in the app). " +
|
|
101
|
-
"Closing the task to refund the unfilled budget.",
|
|
102
|
-
);
|
|
103
|
-
const closed = await call("close_task", { task_id: taskId });
|
|
104
|
-
console.log(`close_task → status ${closed.task?.status ?? "closed"}`);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
await client.close();
|
|
108
|
-
console.log("OK");
|