@toon-protocol/client-mcp 0.2.0 → 0.3.1

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/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/journey/runner.ts"],"sourcesContent":["import { dispatchTool, type ToolResult } from '../mcp-tools.js';\nimport type { ControlClient } from '../control-client.js';\nimport type { JourneyPlan, JourneyResult, JourneyState } from './types.js';\n\n/**\n * Run all steps in the plan sequentially against the given ControlClient.\n * Each step's result is threaded forward into the next step's buildInput via\n * JourneyState. Halts on the first tool error and returns a partial result.\n */\nexport async function runJourney(\n plan: JourneyPlan,\n client: ControlClient\n): Promise<JourneyResult> {\n const state: JourneyState = {};\n const completedSteps: JourneyResult['steps'] = [];\n\n for (const step of plan.steps) {\n const toolResult = await dispatchTool(client, step.toolName, step.buildInput(state));\n\n if (toolResult.isError) {\n return {\n completed: false,\n steps: completedSteps,\n error: { stepId: step.id, message: toolResult.content[0]?.text ?? 'Tool error' },\n };\n }\n\n const data = extractData(toolResult);\n state[step.id] = data;\n\n const viewSpec = step.renderPanel(data);\n const panel: ToolResult = {\n content: [{ type: 'text', text: `Journey step: ${step.id}` }],\n structuredContent: { viewSpec },\n };\n\n completedSteps.push({ stepId: step.id, toolResult, panel });\n }\n\n return { completed: true, steps: completedSteps };\n}\n\n/** Extract the data payload from a successful ToolResult for state threading. */\nfunction extractData(result: ToolResult): unknown {\n if (result.structuredContent !== undefined) return result.structuredContent;\n const text = result.content[0]?.text;\n if (!text) return null;\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AASA,eAAsB,WACpB,MACA,QACwB;AACxB,QAAM,QAAsB,CAAC;AAC7B,QAAM,iBAAyC,CAAC;AAEhD,aAAW,QAAQ,KAAK,OAAO;AAC7B,UAAM,aAAa,MAAM,aAAa,QAAQ,KAAK,UAAU,KAAK,WAAW,KAAK,CAAC;AAEnF,QAAI,WAAW,SAAS;AACtB,aAAO;AAAA,QACL,WAAW;AAAA,QACX,OAAO;AAAA,QACP,OAAO,EAAE,QAAQ,KAAK,IAAI,SAAS,WAAW,QAAQ,CAAC,GAAG,QAAQ,aAAa;AAAA,MACjF;AAAA,IACF;AAEA,UAAM,OAAO,YAAY,UAAU;AACnC,UAAM,KAAK,EAAE,IAAI;AAEjB,UAAM,WAAW,KAAK,YAAY,IAAI;AACtC,UAAM,QAAoB;AAAA,MACxB,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,iBAAiB,KAAK,EAAE,GAAG,CAAC;AAAA,MAC5D,mBAAmB,EAAE,SAAS;AAAA,IAChC;AAEA,mBAAe,KAAK,EAAE,QAAQ,KAAK,IAAI,YAAY,MAAM,CAAC;AAAA,EAC5D;AAEA,SAAO,EAAE,WAAW,MAAM,OAAO,eAAe;AAClD;AAGA,SAAS,YAAY,QAA6B;AAChD,MAAI,OAAO,sBAAsB,OAAW,QAAO,OAAO;AAC1D,QAAM,OAAO,OAAO,QAAQ,CAAC,GAAG;AAChC,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI;AACF,WAAO,KAAK,MAAM,IAAI;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/journey/runner.ts","../src/journey/socialfi.ts","../src/journey/defi.ts","../src/journey/demo.ts"],"sourcesContent":["import { dispatchTool, type ToolResult } from '../mcp-tools.js';\nimport type { ControlClient } from '../control-client.js';\nimport type { JourneyPlan, JourneyResult, JourneyState } from './types.js';\n\n/**\n * Run all steps in the plan sequentially against the given ControlClient.\n * Each step's result is threaded forward into the next step's buildInput via\n * JourneyState. Halts on the first tool error and returns a partial result.\n */\nexport async function runJourney(\n plan: JourneyPlan,\n client: ControlClient\n): Promise<JourneyResult> {\n const state: JourneyState = {};\n const completedSteps: JourneyResult['steps'] = [];\n\n for (const step of plan.steps) {\n const toolResult = await dispatchTool(client, step.toolName, step.buildInput(state));\n\n if (toolResult.isError) {\n return {\n completed: false,\n steps: completedSteps,\n error: { stepId: step.id, message: toolResult.content[0]?.text ?? 'Tool error' },\n };\n }\n\n const data = extractData(toolResult);\n state[step.id] = data;\n\n const viewSpec = step.renderPanel(data, state);\n const panel: ToolResult = {\n content: [{ type: 'text', text: `Journey step: ${step.id}` }],\n structuredContent: { viewSpec },\n };\n\n completedSteps.push({ stepId: step.id, toolResult, panel });\n }\n\n return { completed: true, steps: completedSteps };\n}\n\n/** Extract the data payload from a successful ToolResult for state threading. */\nfunction extractData(result: ToolResult): unknown {\n if (result.structuredContent !== undefined) return result.structuredContent;\n const text = result.content[0]?.text;\n if (!text) return null;\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n}\n","import {\n buildProfileFilter,\n buildFeedFilter,\n buildFollowListFilter,\n buildFileMetadataFilter,\n PUBLISH_TOOL,\n UPLOAD_TOOL,\n} from '@toon-protocol/views';\nimport type { JourneyPlan, JourneyState } from './types.js';\n\nfunction pubkeyFromState(state: JourneyState, fallback?: string): string {\n const onboard = state['onboard'] as { identity?: { nostrPubkey?: string } } | undefined;\n return onboard?.identity?.nostrPubkey ?? fallback ?? '';\n}\n\n/**\n * The canonical five-step SocialFi journey:\n * onboard → publish profile (kind:0) → publish note (kind:1) → follow (kind:3)\n * → DVM media upload (kind:1063) with media-embed read-back.\n *\n * Pass `opts.pubkey` to seed the panel bind-queries with the user's pubkey\n * without waiting for the onboard step to surface it; the follow step's\n * buildInput also reads it from `state['onboard'].identity.nostrPubkey` so\n * the correct pubkey is used in the kind:3 tags even when opts is omitted.\n */\nexport function socialFiJourney(opts?: { pubkey?: string }): JourneyPlan {\n return {\n id: 'socialfi',\n title: 'SocialFi Journey',\n steps: [\n // ── Step 1: onboard ─────────────────────────────────────────────────────\n {\n id: 'onboard',\n toolName: 'toon_status',\n buildInput: () => ({}),\n renderPanel: (data) => {\n const s = data as { ready?: boolean } | undefined;\n return {\n title: 'Onboard',\n root: {\n atom: 'section',\n props: { title: s?.ready ? 'Ready to publish' : 'Connecting…' },\n children: [{ atom: 'card', children: [{ atom: 'generic-event' }] }],\n },\n };\n },\n },\n\n // ── Step 2: publish profile (kind:0) ────────────────────────────────────\n {\n id: 'publish-profile',\n toolName: 'toon_publish_unsigned',\n buildInput: () => ({\n kind: 0,\n content: JSON.stringify({ name: 'TOON User', about: 'Published via the SocialFi journey.' }),\n }),\n renderPanel: (_data, state) => ({\n title: 'Profile',\n root: {\n atom: 'profile-header',\n bind: { query: buildProfileFilter([pubkeyFromState(state, opts?.pubkey)]) },\n },\n }),\n },\n\n // ── Step 3: publish note (kind:1) ───────────────────────────────────────\n {\n id: 'publish-note',\n toolName: 'toon_publish_unsigned',\n buildInput: () => ({\n kind: 1,\n content: 'Hello from TOON Protocol!',\n }),\n renderPanel: (_data, state) => ({\n title: 'Note',\n root: {\n atom: 'stack',\n children: [\n {\n atom: 'composer',\n props: { placeholder: \"What's happening?\", label: 'Post' },\n actions: { post: { tool: PUBLISH_TOOL, args: { kind: 1 } } },\n },\n {\n atom: 'note-card',\n bind: { query: buildFeedFilter([pubkeyFromState(state, opts?.pubkey)], 50), kindAuto: true },\n },\n ],\n },\n }),\n },\n\n // ── Step 4: follow (kind:3) ─────────────────────────────────────────────\n {\n id: 'follow',\n toolName: 'toon_publish_unsigned',\n buildInput: (state: JourneyState) => {\n const pubkey = pubkeyFromState(state, opts?.pubkey);\n return { kind: 3, tags: [['p', pubkey]] };\n },\n renderPanel: (_data, state) => {\n const pubkey = pubkeyFromState(state, opts?.pubkey);\n return {\n title: 'Follow',\n root: {\n atom: 'stack',\n children: [\n {\n atom: 'follow-button',\n props: { label: 'Follow' },\n actions: { follow: { tool: PUBLISH_TOOL, args: { kind: 3, tags: [['p', pubkey]] } } },\n },\n {\n atom: 'note-card',\n bind: { query: buildFollowListFilter(pubkey), kindAuto: true },\n },\n ],\n },\n };\n },\n },\n\n // ── Step 5: DVM upload (kind:1063) + media-embed read-back ──────────────\n // Use toon_status (read-only) for the auto-call; the actual upload is\n // user-initiated via the panel's media-uploader action (spendy, confirmed).\n {\n id: 'dvm-upload',\n toolName: 'toon_status',\n buildInput: () => ({}),\n renderPanel: (_data, state) => ({\n title: 'Media Upload',\n root: {\n atom: 'stack',\n children: [\n {\n atom: 'media-uploader',\n props: { label: 'Upload media' },\n actions: {\n upload: {\n tool: UPLOAD_TOOL,\n args: { kind: 1063 },\n spendy: true,\n confirmLabel: 'Upload to Arweave (spendy)',\n },\n },\n },\n {\n atom: 'media-embed',\n bind: { query: buildFileMetadataFilter([pubkeyFromState(state, opts?.pubkey)], 30), kindAuto: true },\n },\n ],\n },\n }),\n },\n ],\n };\n}\n","import type { ViewSpec } from '@toon-protocol/views';\nimport type { ChannelsResponse, SwapRequest, SwapResponse } from '../control-api.js';\nimport type { JourneyPlan } from './types.js';\n\nexport interface DeFiJourneyOpts {\n /** ILP destination used for both channel open and the swap (e.g. mill peer). */\n destination: string;\n /** Total source-asset amount to swap, in source micro-units. */\n amount: string;\n /** Mill's 64-char lowercase hex Nostr pubkey (gift-wrap recipient). */\n millPubkey: string;\n /** Swap pair from kind:10032 discovery or operator-supplied. */\n pair: SwapRequest['pair'];\n /** Sender's payout address on the target chain. */\n chainRecipient: string;\n /** Split the swap into N equal packets (default 1). */\n packetCount?: number;\n}\n\n/**\n * DeFi journey: pre-open payment channel → tiny testnet swap → settlement receipt.\n *\n * Step 3's settlement-receipt panel is built by joining the swap result\n * (captured from step 2's renderPanel via closure) with the toon_channels\n * watermark — no non-existent toon_settle tool is involved.\n */\nexport function deFiJourney(opts: DeFiJourneyOpts): JourneyPlan {\n let capturedSwap: SwapResponse | undefined;\n\n return {\n id: 'defi',\n title: 'DeFi Journey: Open Channel → Swap → Settlement Receipt',\n steps: [\n {\n id: 'open-channel',\n toolName: 'toon_open_channel',\n buildInput: (_state) => ({ destination: opts.destination }),\n renderPanel: (data): ViewSpec => {\n const { channelId } = data as { channelId: string };\n return {\n title: 'Payment Channel',\n root: {\n atom: 'stack',\n children: [{ atom: 'channel-card', props: { channelId } }],\n },\n };\n },\n },\n {\n id: 'swap',\n toolName: 'toon_swap',\n buildInput: (_state): Record<string, unknown> => {\n const args: Record<string, unknown> = {\n destination: opts.destination,\n amount: opts.amount,\n millPubkey: opts.millPubkey,\n pair: opts.pair,\n chainRecipient: opts.chainRecipient,\n };\n if (opts.packetCount !== undefined) {\n args['packetCount'] = opts.packetCount;\n }\n return args;\n },\n renderPanel: (data): ViewSpec => {\n capturedSwap = data as SwapResponse;\n return {\n title: 'Swap',\n root: {\n atom: 'stack',\n children: [\n {\n atom: 'swap-form',\n props: {\n accepted: capturedSwap.accepted,\n packetsAccepted: capturedSwap.packetsAccepted,\n cumulativeSource: capturedSwap.cumulativeSource,\n cumulativeTarget: capturedSwap.cumulativeTarget,\n state: capturedSwap.state,\n claims: capturedSwap.claims,\n },\n },\n ],\n },\n };\n },\n },\n {\n id: 'settlement-receipt',\n toolName: 'toon_channels',\n buildInput: (_state) => ({}),\n renderPanel: (data): ViewSpec => {\n const { channels } = data as ChannelsResponse;\n const swap = capturedSwap;\n return {\n title: 'Settlement Receipt',\n root: {\n atom: 'stack',\n children: [\n {\n atom: 'settlement-receipt',\n props: {\n accepted: swap?.accepted,\n cumulativeSource: swap?.cumulativeSource,\n cumulativeTarget: swap?.cumulativeTarget,\n claims: swap?.claims ?? [],\n channels,\n },\n },\n ],\n },\n };\n },\n },\n ],\n };\n}\n","/**\n * Capstone journey demo (#25): chain the merged SocialFi and DeFi journeys into\n * one ordered plan, run it against a `toon-clientd` daemon, print each step's\n * ViewSpec panel, and print the settlement receipt sourced from the `swap`\n * response joined with the `channels()` watermark.\n *\n * There is intentionally NO `settle()`/`toon_settle` step: the receipt is the\n * swap result (cumulative source/target + signed claims) reconciled against the\n * channel nonce watermark, exactly as `deFiJourney`'s `settlement-receipt` panel\n * already does. This module just chains the two existing journeys and renders\n * their output to stdout for the demo.\n *\n * Run from source (root devDep `tsx`):\n * pnpm --filter @toon-protocol/client-mcp demo:journey\n * or against a built dist:\n * node dist/journey/demo.js\n *\n * The daemon must already be running (toon-clientd). Live config + funding is a\n * human prerequisite (Base Sepolia treasury) and is OUT OF SCOPE for CI; the\n * dry-run unit test (`demo.test.ts`) covers the orchestration with a mocked\n * ControlClient and no network/funds.\n */\n\nimport type { ChannelsResponse, SwapResponse } from '../control-api.js';\nimport { ControlClient } from '../control-client.js';\nimport { runJourney } from './runner.js';\nimport { socialFiJourney } from './socialfi.js';\nimport { deFiJourney, type DeFiJourneyOpts } from './defi.js';\nimport type { JourneyPlan, JourneyResult } from './types.js';\n\n/**\n * Concatenate several journey plans into one ordered plan. Step ids are\n * namespaced with the source plan id (`<planId>:<stepId>`) so the combined\n * `JourneyState` keys never collide when two legs reuse a step id.\n *\n * Note: each leg's `buildInput`/`renderPanel` reads its OWN step ids out of\n * `JourneyState`, so re-keying here would break that threading. We therefore\n * keep the original step ids on the steps themselves and only namespace the\n * combined plan's *reported* ids via a wrapper is unnecessary because the two\n * legs (`socialfi`, `defi`) share no step ids. We assert that invariant.\n */\nexport function chainJourneys(\n id: string,\n title: string,\n ...plans: JourneyPlan[]\n): JourneyPlan {\n const steps = plans.flatMap((p) => p.steps);\n const seen = new Set<string>();\n for (const step of steps) {\n if (seen.has(step.id)) {\n throw new Error(\n `chainJourneys: duplicate step id \"${step.id}\" across chained plans — ` +\n `state threading requires unique step ids`\n );\n }\n seen.add(step.id);\n }\n return { id, title, steps };\n}\n\n/** Build the capstone SocialFi → DeFi plan. */\nexport function capstoneJourney(opts: {\n socialFi?: { pubkey?: string };\n deFi: DeFiJourneyOpts;\n}): JourneyPlan {\n return chainJourneys(\n 'capstone',\n 'Capstone Journey: SocialFi → DeFi',\n socialFiJourney(opts.socialFi),\n deFiJourney(opts.deFi)\n );\n}\n\n/** The settlement receipt, derived from the swap response + channel watermark. */\nexport interface SettlementReceipt {\n accepted: boolean;\n state: SwapResponse['state'];\n cumulativeSource: string;\n cumulativeTarget: string;\n claims: SwapResponse['claims'];\n channels: ChannelsResponse['channels'];\n}\n\n/**\n * Reconstruct the settlement receipt from a completed journey result. Sources\n * the swap payload from the `swap` step's raw ToolResult and the channel\n * watermark from the `settlement-receipt` step's raw ToolResult. Returns\n * undefined if either step is missing (e.g. the run halted before DeFi).\n */\nexport function extractReceipt(result: JourneyResult): SettlementReceipt | undefined {\n const swapStep = result.steps.find((s) => s.stepId === 'swap');\n const channelsStep = result.steps.find((s) => s.stepId === 'settlement-receipt');\n if (!swapStep || !channelsStep) return undefined;\n\n const swap = parseToolText<SwapResponse>(swapStep.toolResult.content[0]?.text);\n const channelsRes = parseToolText<ChannelsResponse>(\n channelsStep.toolResult.content[0]?.text\n );\n if (!swap || !channelsRes) return undefined;\n\n return {\n accepted: swap.accepted,\n state: swap.state,\n cumulativeSource: swap.cumulativeSource,\n cumulativeTarget: swap.cumulativeTarget,\n claims: swap.claims ?? [],\n channels: channelsRes.channels,\n };\n}\n\nfunction parseToolText<T>(text: string | undefined): T | undefined {\n if (!text) return undefined;\n try {\n return JSON.parse(text) as T;\n } catch {\n return undefined;\n }\n}\n\n/** Minimal console surface so the runner is testable with a captured logger. */\nexport interface DemoLogger {\n log: (msg: string) => void;\n error: (msg: string) => void;\n}\n\n/**\n * Run the capstone journey against a ControlClient, printing each step's panel\n * JSON and the final settlement receipt. Returns the process exit code: 0 on a\n * fully-completed journey, 1 if any step errored. Does no network I/O itself —\n * the ControlClient does. No funds move in the dry-run test (mocked client).\n */\nexport async function runCapstoneDemo(\n client: ControlClient,\n opts: { socialFi?: { pubkey?: string }; deFi: DeFiJourneyOpts },\n logger: DemoLogger = console\n): Promise<number> {\n const plan = capstoneJourney(opts);\n logger.log(`\\n=== ${plan.title} (${plan.steps.length} steps) ===\\n`);\n\n const result = await runJourney(plan, client);\n\n for (const step of result.steps) {\n const viewSpec = step.panel.structuredContent?.['viewSpec'];\n logger.log(`--- panel: ${step.stepId} ---`);\n logger.log(JSON.stringify(viewSpec, null, 2));\n }\n\n if (!result.completed) {\n logger.error(\n `\\n[capstone] FAILED at step \"${result.error?.stepId}\": ${result.error?.message}`\n );\n return 1;\n }\n\n const receipt = extractReceipt(result);\n logger.log('\\n=== Settlement Receipt ===');\n logger.log(JSON.stringify(receipt, null, 2));\n logger.log(`\\n[capstone] completed all ${result.steps.length} steps.`);\n return 0;\n}\n\n/**\n * CLI entry. Reads connection + DeFi opts from env and runs the demo against a\n * live daemon. See module header for the live-run prerequisites.\n *\n * Env:\n * TOON_DAEMON_URL daemon control-plane base URL (default http://127.0.0.1:8787)\n * TOON_SWAP_DEST mill ILP destination (e.g. g.townhouse.mill)\n * TOON_SWAP_AMOUNT source-asset amount, micro-units (e.g. 1000000)\n * TOON_MILL_PUBKEY mill 64-char hex Nostr pubkey\n * TOON_CHAIN_RECIPIENT payout address on the target chain\n * TOON_SWAP_PAIR JSON SwapPair ({ from, to, rate, ... })\n * TOON_SOCIALFI_PUBKEY optional: seed panel bind-queries before onboard surfaces it\n */\nexport async function main(env: NodeJS.ProcessEnv = process.env): Promise<number> {\n const baseUrl = env['TOON_DAEMON_URL'] ?? 'http://127.0.0.1:8787';\n\n const destination = env['TOON_SWAP_DEST'];\n const amount = env['TOON_SWAP_AMOUNT'];\n const millPubkey = env['TOON_MILL_PUBKEY'];\n const chainRecipient = env['TOON_CHAIN_RECIPIENT'];\n const pairRaw = env['TOON_SWAP_PAIR'];\n\n if (!destination || !amount || !millPubkey || !chainRecipient || !pairRaw) {\n console.error(\n '[capstone] missing required env: TOON_SWAP_DEST, TOON_SWAP_AMOUNT, ' +\n 'TOON_MILL_PUBKEY, TOON_CHAIN_RECIPIENT, TOON_SWAP_PAIR are all required ' +\n 'for the live DeFi leg. See the module header for the full env contract.'\n );\n return 2;\n }\n\n let pair: DeFiJourneyOpts['pair'];\n try {\n pair = JSON.parse(pairRaw) as DeFiJourneyOpts['pair'];\n } catch (e) {\n console.error(`[capstone] TOON_SWAP_PAIR is not valid JSON: ${String(e)}`);\n return 2;\n }\n\n const client = new ControlClient({ baseUrl });\n const socialFiPubkey = env['TOON_SOCIALFI_PUBKEY'];\n\n return runCapstoneDemo(client, {\n ...(socialFiPubkey ? { socialFi: { pubkey: socialFiPubkey } } : {}),\n deFi: { destination, amount, millPubkey, chainRecipient, pair },\n });\n}\n\n// Run when invoked directly (tsx src/journey/demo.ts or node dist/journey/demo.js).\nconst invokedDirectly =\n typeof process !== 'undefined' &&\n Array.isArray(process.argv) &&\n /[/\\\\]journey[/\\\\]demo\\.(ts|js|mjs)$/.test(process.argv[1] ?? '');\n\nif (invokedDirectly) {\n main()\n .then((code) => process.exit(code))\n .catch((err) => {\n console.error('[capstone] fatal:', err instanceof Error ? err.message : err);\n process.exit(1);\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AASA,eAAsB,WACpB,MACA,QACwB;AACxB,QAAM,QAAsB,CAAC;AAC7B,QAAM,iBAAyC,CAAC;AAEhD,aAAW,QAAQ,KAAK,OAAO;AAC7B,UAAM,aAAa,MAAM,aAAa,QAAQ,KAAK,UAAU,KAAK,WAAW,KAAK,CAAC;AAEnF,QAAI,WAAW,SAAS;AACtB,aAAO;AAAA,QACL,WAAW;AAAA,QACX,OAAO;AAAA,QACP,OAAO,EAAE,QAAQ,KAAK,IAAI,SAAS,WAAW,QAAQ,CAAC,GAAG,QAAQ,aAAa;AAAA,MACjF;AAAA,IACF;AAEA,UAAM,OAAO,YAAY,UAAU;AACnC,UAAM,KAAK,EAAE,IAAI;AAEjB,UAAM,WAAW,KAAK,YAAY,MAAM,KAAK;AAC7C,UAAM,QAAoB;AAAA,MACxB,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,iBAAiB,KAAK,EAAE,GAAG,CAAC;AAAA,MAC5D,mBAAmB,EAAE,SAAS;AAAA,IAChC;AAEA,mBAAe,KAAK,EAAE,QAAQ,KAAK,IAAI,YAAY,MAAM,CAAC;AAAA,EAC5D;AAEA,SAAO,EAAE,WAAW,MAAM,OAAO,eAAe;AAClD;AAGA,SAAS,YAAY,QAA6B;AAChD,MAAI,OAAO,sBAAsB,OAAW,QAAO,OAAO;AAC1D,QAAM,OAAO,OAAO,QAAQ,CAAC,GAAG;AAChC,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI;AACF,WAAO,KAAK,MAAM,IAAI;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;AC1CA,SAAS,gBAAgB,OAAqB,UAA2B;AACvE,QAAM,UAAU,MAAM,SAAS;AAC/B,SAAO,SAAS,UAAU,eAAe,YAAY;AACvD;AAYO,SAAS,gBAAgB,MAAyC;AACvE,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,OAAO;AAAA;AAAA,MAEL;AAAA,QACE,IAAI;AAAA,QACJ,UAAU;AAAA,QACV,YAAY,OAAO,CAAC;AAAA,QACpB,aAAa,CAAC,SAAS;AACrB,gBAAM,IAAI;AACV,iBAAO;AAAA,YACL,OAAO;AAAA,YACP,MAAM;AAAA,cACJ,MAAM;AAAA,cACN,OAAO,EAAE,OAAO,GAAG,QAAQ,qBAAqB,mBAAc;AAAA,cAC9D,UAAU,CAAC,EAAE,MAAM,QAAQ,UAAU,CAAC,EAAE,MAAM,gBAAgB,CAAC,EAAE,CAAC;AAAA,YACpE;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA;AAAA,MAGA;AAAA,QACE,IAAI;AAAA,QACJ,UAAU;AAAA,QACV,YAAY,OAAO;AAAA,UACjB,MAAM;AAAA,UACN,SAAS,KAAK,UAAU,EAAE,MAAM,aAAa,OAAO,sCAAsC,CAAC;AAAA,QAC7F;AAAA,QACA,aAAa,CAAC,OAAO,WAAW;AAAA,UAC9B,OAAO;AAAA,UACP,MAAM;AAAA,YACJ,MAAM;AAAA,YACN,MAAM,EAAE,OAAO,mBAAmB,CAAC,gBAAgB,OAAO,MAAM,MAAM,CAAC,CAAC,EAAE;AAAA,UAC5E;AAAA,QACF;AAAA,MACF;AAAA;AAAA,MAGA;AAAA,QACE,IAAI;AAAA,QACJ,UAAU;AAAA,QACV,YAAY,OAAO;AAAA,UACjB,MAAM;AAAA,UACN,SAAS;AAAA,QACX;AAAA,QACA,aAAa,CAAC,OAAO,WAAW;AAAA,UAC9B,OAAO;AAAA,UACP,MAAM;AAAA,YACJ,MAAM;AAAA,YACN,UAAU;AAAA,cACR;AAAA,gBACE,MAAM;AAAA,gBACN,OAAO,EAAE,aAAa,qBAAqB,OAAO,OAAO;AAAA,gBACzD,SAAS,EAAE,MAAM,EAAE,MAAM,cAAc,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE;AAAA,cAC7D;AAAA,cACA;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM,EAAE,OAAO,gBAAgB,CAAC,gBAAgB,OAAO,MAAM,MAAM,CAAC,GAAG,EAAE,GAAG,UAAU,KAAK;AAAA,cAC7F;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA;AAAA,MAGA;AAAA,QACE,IAAI;AAAA,QACJ,UAAU;AAAA,QACV,YAAY,CAAC,UAAwB;AACnC,gBAAM,SAAS,gBAAgB,OAAO,MAAM,MAAM;AAClD,iBAAO,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,KAAK,MAAM,CAAC,EAAE;AAAA,QAC1C;AAAA,QACA,aAAa,CAAC,OAAO,UAAU;AAC7B,gBAAM,SAAS,gBAAgB,OAAO,MAAM,MAAM;AAClD,iBAAO;AAAA,YACL,OAAO;AAAA,YACP,MAAM;AAAA,cACJ,MAAM;AAAA,cACN,UAAU;AAAA,gBACR;AAAA,kBACE,MAAM;AAAA,kBACN,OAAO,EAAE,OAAO,SAAS;AAAA,kBACzB,SAAS,EAAE,QAAQ,EAAE,MAAM,cAAc,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,KAAK,MAAM,CAAC,EAAE,EAAE,EAAE;AAAA,gBACtF;AAAA,gBACA;AAAA,kBACE,MAAM;AAAA,kBACN,MAAM,EAAE,OAAO,sBAAsB,MAAM,GAAG,UAAU,KAAK;AAAA,gBAC/D;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA;AAAA;AAAA;AAAA,MAKA;AAAA,QACE,IAAI;AAAA,QACJ,UAAU;AAAA,QACV,YAAY,OAAO,CAAC;AAAA,QACpB,aAAa,CAAC,OAAO,WAAW;AAAA,UAC9B,OAAO;AAAA,UACP,MAAM;AAAA,YACJ,MAAM;AAAA,YACN,UAAU;AAAA,cACR;AAAA,gBACE,MAAM;AAAA,gBACN,OAAO,EAAE,OAAO,eAAe;AAAA,gBAC/B,SAAS;AAAA,kBACP,QAAQ;AAAA,oBACN,MAAM;AAAA,oBACN,MAAM,EAAE,MAAM,KAAK;AAAA,oBACnB,QAAQ;AAAA,oBACR,cAAc;AAAA,kBAChB;AAAA,gBACF;AAAA,cACF;AAAA,cACA;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM,EAAE,OAAO,wBAAwB,CAAC,gBAAgB,OAAO,MAAM,MAAM,CAAC,GAAG,EAAE,GAAG,UAAU,KAAK;AAAA,cACrG;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;;;AClIO,SAAS,YAAY,MAAoC;AAC9D,MAAI;AAEJ,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,OAAO;AAAA,MACL;AAAA,QACE,IAAI;AAAA,QACJ,UAAU;AAAA,QACV,YAAY,CAAC,YAAY,EAAE,aAAa,KAAK,YAAY;AAAA,QACzD,aAAa,CAAC,SAAmB;AAC/B,gBAAM,EAAE,UAAU,IAAI;AACtB,iBAAO;AAAA,YACL,OAAO;AAAA,YACP,MAAM;AAAA,cACJ,MAAM;AAAA,cACN,UAAU,CAAC,EAAE,MAAM,gBAAgB,OAAO,EAAE,UAAU,EAAE,CAAC;AAAA,YAC3D;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,MACA;AAAA,QACE,IAAI;AAAA,QACJ,UAAU;AAAA,QACV,YAAY,CAAC,WAAoC;AAC/C,gBAAM,OAAgC;AAAA,YACpC,aAAa,KAAK;AAAA,YAClB,QAAQ,KAAK;AAAA,YACb,YAAY,KAAK;AAAA,YACjB,MAAM,KAAK;AAAA,YACX,gBAAgB,KAAK;AAAA,UACvB;AACA,cAAI,KAAK,gBAAgB,QAAW;AAClC,iBAAK,aAAa,IAAI,KAAK;AAAA,UAC7B;AACA,iBAAO;AAAA,QACT;AAAA,QACA,aAAa,CAAC,SAAmB;AAC/B,yBAAe;AACf,iBAAO;AAAA,YACL,OAAO;AAAA,YACP,MAAM;AAAA,cACJ,MAAM;AAAA,cACN,UAAU;AAAA,gBACR;AAAA,kBACE,MAAM;AAAA,kBACN,OAAO;AAAA,oBACL,UAAU,aAAa;AAAA,oBACvB,iBAAiB,aAAa;AAAA,oBAC9B,kBAAkB,aAAa;AAAA,oBAC/B,kBAAkB,aAAa;AAAA,oBAC/B,OAAO,aAAa;AAAA,oBACpB,QAAQ,aAAa;AAAA,kBACvB;AAAA,gBACF;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,MACA;AAAA,QACE,IAAI;AAAA,QACJ,UAAU;AAAA,QACV,YAAY,CAAC,YAAY,CAAC;AAAA,QAC1B,aAAa,CAAC,SAAmB;AAC/B,gBAAM,EAAE,SAAS,IAAI;AACrB,gBAAM,OAAO;AACb,iBAAO;AAAA,YACL,OAAO;AAAA,YACP,MAAM;AAAA,cACJ,MAAM;AAAA,cACN,UAAU;AAAA,gBACR;AAAA,kBACE,MAAM;AAAA,kBACN,OAAO;AAAA,oBACL,UAAU,MAAM;AAAA,oBAChB,kBAAkB,MAAM;AAAA,oBACxB,kBAAkB,MAAM;AAAA,oBACxB,QAAQ,MAAM,UAAU,CAAC;AAAA,oBACzB;AAAA,kBACF;AAAA,gBACF;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;;;AC3EO,SAAS,cACd,IACA,UACG,OACU;AACb,QAAM,QAAQ,MAAM,QAAQ,CAAC,MAAM,EAAE,KAAK;AAC1C,QAAM,OAAO,oBAAI,IAAY;AAC7B,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,IAAI,KAAK,EAAE,GAAG;AACrB,YAAM,IAAI;AAAA,QACR,qCAAqC,KAAK,EAAE;AAAA,MAE9C;AAAA,IACF;AACA,SAAK,IAAI,KAAK,EAAE;AAAA,EAClB;AACA,SAAO,EAAE,IAAI,OAAO,MAAM;AAC5B;AAGO,SAAS,gBAAgB,MAGhB;AACd,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,gBAAgB,KAAK,QAAQ;AAAA,IAC7B,YAAY,KAAK,IAAI;AAAA,EACvB;AACF;AAkBO,SAAS,eAAe,QAAsD;AACnF,QAAM,WAAW,OAAO,MAAM,KAAK,CAAC,MAAM,EAAE,WAAW,MAAM;AAC7D,QAAM,eAAe,OAAO,MAAM,KAAK,CAAC,MAAM,EAAE,WAAW,oBAAoB;AAC/E,MAAI,CAAC,YAAY,CAAC,aAAc,QAAO;AAEvC,QAAM,OAAO,cAA4B,SAAS,WAAW,QAAQ,CAAC,GAAG,IAAI;AAC7E,QAAM,cAAc;AAAA,IAClB,aAAa,WAAW,QAAQ,CAAC,GAAG;AAAA,EACtC;AACA,MAAI,CAAC,QAAQ,CAAC,YAAa,QAAO;AAElC,SAAO;AAAA,IACL,UAAU,KAAK;AAAA,IACf,OAAO,KAAK;AAAA,IACZ,kBAAkB,KAAK;AAAA,IACvB,kBAAkB,KAAK;AAAA,IACvB,QAAQ,KAAK,UAAU,CAAC;AAAA,IACxB,UAAU,YAAY;AAAA,EACxB;AACF;AAEA,SAAS,cAAiB,MAAyC;AACjE,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI;AACF,WAAO,KAAK,MAAM,IAAI;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAcA,eAAsB,gBACpB,QACA,MACA,SAAqB,SACJ;AACjB,QAAM,OAAO,gBAAgB,IAAI;AACjC,SAAO,IAAI;AAAA,MAAS,KAAK,KAAK,KAAK,KAAK,MAAM,MAAM;AAAA,CAAe;AAEnE,QAAM,SAAS,MAAM,WAAW,MAAM,MAAM;AAE5C,aAAW,QAAQ,OAAO,OAAO;AAC/B,UAAM,WAAW,KAAK,MAAM,oBAAoB,UAAU;AAC1D,WAAO,IAAI,cAAc,KAAK,MAAM,MAAM;AAC1C,WAAO,IAAI,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAAA,EAC9C;AAEA,MAAI,CAAC,OAAO,WAAW;AACrB,WAAO;AAAA,MACL;AAAA,6BAAgC,OAAO,OAAO,MAAM,MAAM,OAAO,OAAO,OAAO;AAAA,IACjF;AACA,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,eAAe,MAAM;AACrC,SAAO,IAAI,8BAA8B;AACzC,SAAO,IAAI,KAAK,UAAU,SAAS,MAAM,CAAC,CAAC;AAC3C,SAAO,IAAI;AAAA,2BAA8B,OAAO,MAAM,MAAM,SAAS;AACrE,SAAO;AACT;AAeA,eAAsB,KAAK,MAAyB,QAAQ,KAAsB;AAChF,QAAM,UAAU,IAAI,iBAAiB,KAAK;AAE1C,QAAM,cAAc,IAAI,gBAAgB;AACxC,QAAM,SAAS,IAAI,kBAAkB;AACrC,QAAM,aAAa,IAAI,kBAAkB;AACzC,QAAM,iBAAiB,IAAI,sBAAsB;AACjD,QAAM,UAAU,IAAI,gBAAgB;AAEpC,MAAI,CAAC,eAAe,CAAC,UAAU,CAAC,cAAc,CAAC,kBAAkB,CAAC,SAAS;AACzE,YAAQ;AAAA,MACN;AAAA,IAGF;AACA,WAAO;AAAA,EACT;AAEA,MAAI;AACJ,MAAI;AACF,WAAO,KAAK,MAAM,OAAO;AAAA,EAC3B,SAAS,GAAG;AACV,YAAQ,MAAM,gDAAgD,OAAO,CAAC,CAAC,EAAE;AACzE,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,IAAI,cAAc,EAAE,QAAQ,CAAC;AAC5C,QAAM,iBAAiB,IAAI,sBAAsB;AAEjD,SAAO,gBAAgB,QAAQ;AAAA,IAC7B,GAAI,iBAAiB,EAAE,UAAU,EAAE,QAAQ,eAAe,EAAE,IAAI,CAAC;AAAA,IACjE,MAAM,EAAE,aAAa,QAAQ,YAAY,gBAAgB,KAAK;AAAA,EAChE,CAAC;AACH;AAGA,IAAM,kBACJ,OAAO,YAAY,eACnB,MAAM,QAAQ,QAAQ,IAAI,KAC1B,sCAAsC,KAAK,QAAQ,KAAK,CAAC,KAAK,EAAE;AAElE,IAAI,iBAAiB;AACnB,OAAK,EACF,KAAK,CAAC,SAAS,QAAQ,KAAK,IAAI,CAAC,EACjC,MAAM,CAAC,QAAQ;AACd,YAAQ,MAAM,qBAAqB,eAAe,QAAQ,IAAI,UAAU,GAAG;AAC3E,YAAQ,KAAK,CAAC;AAAA,EAChB,CAAC;AACL;","names":[]}
package/dist/mcp.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  APP_RESOURCE_URI,
5
5
  TOOL_DEFINITIONS,
6
6
  dispatchTool
7
- } from "./chunk-KNQ2DKNN.js";
7
+ } from "./chunk-4GWMHKPP.js";
8
8
  import {
9
9
  ControlClient,
10
10
  defaultConfigPath,
@@ -12,12 +12,11 @@ import {
12
12
  readConfigFile,
13
13
  spawnDaemonDetached,
14
14
  waitForReady
15
- } from "./chunk-NP35YO5O.js";
15
+ } from "./chunk-CQ2QIZ6Z.js";
16
16
  import "./chunk-32QD72IL.js";
17
17
  import "./chunk-DLYE6U2Z.js";
18
18
  import "./chunk-LR7W2ISE.js";
19
19
  import "./chunk-VA7XC4FD.js";
20
- import "./chunk-SKQTKZIH.js";
21
20
  import "./chunk-F22GNSF6.js";
22
21
 
23
22
  // src/mcp.ts
package/dist/mcp.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/mcp.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * `toon-mcp` — a thin MCP stdio server exposing the TOON client to a Claude\n * agent (Desktop or Code). It holds NO chain keys and NO long-lived\n * connections: every tool maps to an HTTP call against the always-on\n * `toon-clientd` daemon, which it auto-spawns (detached) if it is not running.\n *\n * Works on both surfaces:\n * • Claude Desktop — `claude_desktop_config.json` mcpServers entry.\n * • Claude Code — `claude mcp add toon -- toon-mcp` (or `.mcp.json`).\n */\n\nimport { createRequire } from 'node:module';\nimport { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport {\n CallToolRequestSchema,\n ListResourcesRequestSchema,\n ListToolsRequestSchema,\n ReadResourceRequestSchema,\n type CallToolResult,\n} from '@modelcontextprotocol/sdk/types.js';\nimport { APP_RESOURCE_URI } from '@toon-protocol/views';\nimport { ControlClient } from './control-client.js';\nimport { dispatchTool, TOOL_DEFINITIONS } from './mcp-tools.js';\n\n/** MIME marking the bundle as an MCP-app UI resource (ext-apps profile). */\nconst APP_MIME = 'text/html;profile=mcp-app';\n\n/** Load the prebuilt single-file MCP-app bundle served as `ui://toon/app`. */\nfunction loadAppHtml(): string {\n // 1. Prefer the copy shipped next to the built server (tsup onSuccess copies\n // it into dist/app). This is what a published client-mcp serves — no\n // dependency on the unpublished @toon-protocol/views package at runtime.\n try {\n return readFileSync(new URL('./app/index.html', import.meta.url), 'utf8');\n } catch {\n /* running from source / not yet copied — fall through to the dev resolve */\n }\n // 2. Dev fallback: resolve the bundle from the @toon-protocol/views workspace.\n try {\n const req = createRequire(import.meta.url);\n const entry = req.resolve('@toon-protocol/views'); // …/views/dist/index.js\n return readFileSync(join(dirname(entry), 'app', 'index.html'), 'utf8');\n } catch {\n return '<!doctype html><html><body><div id=\"root\">toon app bundle missing — run `pnpm --filter @toon-protocol/views build`</div></body></html>';\n }\n}\nimport { defaultConfigPath, readConfigFile } from './daemon/config.js';\nimport {\n isDaemonRunning,\n spawnDaemonDetached,\n waitForReady,\n} from './daemon/lifecycle.js';\n\n/** stdout carries the MCP protocol — all logging must go to stderr. */\nfunction log(msg: string): void {\n console.error(`[toon-mcp] ${msg}`);\n}\n\n/** Resolve the daemon control-plane URL without needing the mnemonic. */\nfunction controlPlaneUrl(): string {\n const file = readConfigFile(\n process.env['TOON_CLIENT_CONFIG'] ?? defaultConfigPath()\n );\n const port = Number(\n process.env['TOON_CLIENT_HTTP_PORT'] ?? file.httpPort ?? 8787\n );\n return `http://127.0.0.1:${port}`;\n}\n\n/**\n * Make sure the daemon is up: if the lock shows it running, return; otherwise\n * spawn it detached and wait until the control plane is reachable. Best-effort\n * — failures surface as readable tool errors rather than crashing the server.\n */\nasync function ensureDaemon(url: string): Promise<void> {\n if (isDaemonRunning()) return;\n const client = new ControlClient({ baseUrl: url });\n if (await client.ping()) return;\n log('daemon not running — spawning detached');\n try {\n const pid = spawnDaemonDetached();\n log(`spawned toon-clientd (pid ${pid}); waiting for control plane`);\n await waitForReady(url, 20_000);\n } catch (err) {\n log(\n `failed to spawn daemon: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n}\n\nasync function main(): Promise<void> {\n const url = controlPlaneUrl();\n const control = new ControlClient({ baseUrl: url });\n\n // Kick off daemon startup; don't block server init on it (anon bootstrap is\n // slow). Tools report \"bootstrapping — retry\" until it is ready.\n void ensureDaemon(url);\n\n const server = new Server(\n { name: 'toon-client', version: '0.1.0' },\n { capabilities: { tools: {}, resources: {} } }\n );\n\n const appHtml = loadAppHtml();\n\n server.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: TOOL_DEFINITIONS,\n }));\n\n // The MCP-app UI resource the host renders for toon_render results.\n server.setRequestHandler(ListResourcesRequestSchema, async () => ({\n resources: [\n { uri: APP_RESOURCE_URI, name: 'TOON', mimeType: APP_MIME },\n ],\n }));\n\n server.setRequestHandler(ReadResourceRequestSchema, async (request) => {\n if (request.params.uri !== APP_RESOURCE_URI) {\n throw new Error(`Unknown resource: ${request.params.uri}`);\n }\n return {\n contents: [{ uri: APP_RESOURCE_URI, mimeType: APP_MIME, text: appHtml }],\n };\n });\n\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const name = request.params.name;\n const args = (request.params.arguments ?? {}) as Record<string, unknown>;\n // If the daemon went away, try once to bring it back before dispatching.\n if (!(await control.ping())) await ensureDaemon(url);\n // Our ToolResult is a structural subset of CallToolResult (content + isError);\n // the SDK's handler union also carries a task-augmented variant we never use.\n return (await dispatchTool(control, name, args)) as CallToolResult;\n });\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n log(`ready; proxying to ${url}`);\n}\n\nmain().catch((err) => {\n log(err instanceof Error ? (err.stack ?? err.message) : String(err));\n process.exitCode = 1;\n});\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/mcp.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * `toon-mcp` — a thin MCP stdio server exposing the TOON client to a Claude\n * agent (Desktop or Code). It holds NO chain keys and NO long-lived\n * connections: every tool maps to an HTTP call against the always-on\n * `toon-clientd` daemon, which it auto-spawns (detached) if it is not running.\n *\n * Works on both surfaces:\n * • Claude Desktop — `claude_desktop_config.json` mcpServers entry.\n * • Claude Code — `claude mcp add toon -- toon-mcp` (or `.mcp.json`).\n */\n\nimport { createRequire } from 'node:module';\nimport { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport {\n CallToolRequestSchema,\n ListResourcesRequestSchema,\n ListToolsRequestSchema,\n ReadResourceRequestSchema,\n type CallToolResult,\n} from '@modelcontextprotocol/sdk/types.js';\nimport { APP_RESOURCE_URI } from '@toon-protocol/views';\nimport { ControlClient } from './control-client.js';\nimport { dispatchTool, TOOL_DEFINITIONS } from './mcp-tools.js';\n\n/** MIME marking the bundle as an MCP-app UI resource (ext-apps profile). */\nconst APP_MIME = 'text/html;profile=mcp-app';\n\n/** Load the prebuilt single-file MCP-app bundle served as `ui://toon/app`. */\nfunction loadAppHtml(): string {\n // 1. Prefer the copy shipped next to the built server (tsup onSuccess copies\n // it into dist/app). This is what a published client-mcp serves — no\n // dependency on the unpublished @toon-protocol/views package at runtime.\n try {\n return readFileSync(new URL('./app/index.html', import.meta.url), 'utf8');\n } catch {\n /* running from source / not yet copied — fall through to the dev resolve */\n }\n // 2. Dev fallback: resolve the bundle from the @toon-protocol/views workspace.\n try {\n const req = createRequire(import.meta.url);\n const entry = req.resolve('@toon-protocol/views'); // …/views/dist/index.js\n return readFileSync(join(dirname(entry), 'app', 'index.html'), 'utf8');\n } catch {\n return '<!doctype html><html><body><div id=\"root\">toon app bundle missing — run `pnpm --filter @toon-protocol/views build`</div></body></html>';\n }\n}\nimport { defaultConfigPath, readConfigFile } from './daemon/config.js';\nimport {\n isDaemonRunning,\n spawnDaemonDetached,\n waitForReady,\n} from './daemon/lifecycle.js';\n\n/** stdout carries the MCP protocol — all logging must go to stderr. */\nfunction log(msg: string): void {\n console.error(`[toon-mcp] ${msg}`);\n}\n\n/** Resolve the daemon control-plane URL without needing the mnemonic. */\nfunction controlPlaneUrl(): string {\n const file = readConfigFile(\n process.env['TOON_CLIENT_CONFIG'] ?? defaultConfigPath()\n );\n const port = Number(\n process.env['TOON_CLIENT_HTTP_PORT'] ?? file.httpPort ?? 8787\n );\n return `http://127.0.0.1:${port}`;\n}\n\n/**\n * Make sure the daemon is up: if the lock shows it running, return; otherwise\n * spawn it detached and wait until the control plane is reachable. Best-effort\n * — failures surface as readable tool errors rather than crashing the server.\n */\nasync function ensureDaemon(url: string): Promise<void> {\n if (isDaemonRunning()) return;\n const client = new ControlClient({ baseUrl: url });\n if (await client.ping()) return;\n log('daemon not running — spawning detached');\n try {\n const pid = spawnDaemonDetached();\n log(`spawned toon-clientd (pid ${pid}); waiting for control plane`);\n await waitForReady(url, 20_000);\n } catch (err) {\n log(\n `failed to spawn daemon: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n}\n\nasync function main(): Promise<void> {\n const url = controlPlaneUrl();\n const control = new ControlClient({ baseUrl: url });\n\n // Kick off daemon startup; don't block server init on it (BTP bootstrap can\n // take a moment). Tools report \"bootstrapping — retry\" until it is ready.\n void ensureDaemon(url);\n\n const server = new Server(\n { name: 'toon-client', version: '0.1.0' },\n { capabilities: { tools: {}, resources: {} } }\n );\n\n const appHtml = loadAppHtml();\n\n server.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: TOOL_DEFINITIONS,\n }));\n\n // The MCP-app UI resource the host renders for toon_render results.\n server.setRequestHandler(ListResourcesRequestSchema, async () => ({\n resources: [\n { uri: APP_RESOURCE_URI, name: 'TOON', mimeType: APP_MIME },\n ],\n }));\n\n server.setRequestHandler(ReadResourceRequestSchema, async (request) => {\n if (request.params.uri !== APP_RESOURCE_URI) {\n throw new Error(`Unknown resource: ${request.params.uri}`);\n }\n return {\n contents: [{ uri: APP_RESOURCE_URI, mimeType: APP_MIME, text: appHtml }],\n };\n });\n\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const name = request.params.name;\n const args = (request.params.arguments ?? {}) as Record<string, unknown>;\n // If the daemon went away, try once to bring it back before dispatching.\n if (!(await control.ping())) await ensureDaemon(url);\n // Our ToolResult is a structural subset of CallToolResult (content + isError);\n // the SDK's handler union also carries a task-augmented variant we never use.\n return (await dispatchTool(control, name, args)) as CallToolResult;\n });\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n log(`ready; proxying to ${url}`);\n}\n\nmain().catch((err) => {\n log(err instanceof Error ? (err.stack ?? err.message) : String(err));\n process.exitCode = 1;\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAYA,SAAS,qBAAqB;AAC9B,SAAS,oBAAoB;AAC7B,SAAS,SAAS,YAAY;AAC9B,SAAS,cAAc;AACvB,SAAS,4BAA4B;AACrC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AAMP,IAAM,WAAW;AAGjB,SAAS,cAAsB;AAI7B,MAAI;AACF,WAAO,aAAa,IAAI,IAAI,oBAAoB,YAAY,GAAG,GAAG,MAAM;AAAA,EAC1E,QAAQ;AAAA,EAER;AAEA,MAAI;AACF,UAAM,MAAM,cAAc,YAAY,GAAG;AACzC,UAAM,QAAQ,IAAI,QAAQ,sBAAsB;AAChD,WAAO,aAAa,KAAK,QAAQ,KAAK,GAAG,OAAO,YAAY,GAAG,MAAM;AAAA,EACvE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AASA,SAAS,IAAI,KAAmB;AAC9B,UAAQ,MAAM,cAAc,GAAG,EAAE;AACnC;AAGA,SAAS,kBAA0B;AACjC,QAAM,OAAO;AAAA,IACX,QAAQ,IAAI,oBAAoB,KAAK,kBAAkB;AAAA,EACzD;AACA,QAAM,OAAO;AAAA,IACX,QAAQ,IAAI,uBAAuB,KAAK,KAAK,YAAY;AAAA,EAC3D;AACA,SAAO,oBAAoB,IAAI;AACjC;AAOA,eAAe,aAAa,KAA4B;AACtD,MAAI,gBAAgB,EAAG;AACvB,QAAM,SAAS,IAAI,cAAc,EAAE,SAAS,IAAI,CAAC;AACjD,MAAI,MAAM,OAAO,KAAK,EAAG;AACzB,MAAI,6CAAwC;AAC5C,MAAI;AACF,UAAM,MAAM,oBAAoB;AAChC,QAAI,6BAA6B,GAAG,8BAA8B;AAClE,UAAM,aAAa,KAAK,GAAM;AAAA,EAChC,SAAS,KAAK;AACZ;AAAA,MACE,2BAA2B,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,IAC7E;AAAA,EACF;AACF;AAEA,eAAe,OAAsB;AACnC,QAAM,MAAM,gBAAgB;AAC5B,QAAM,UAAU,IAAI,cAAc,EAAE,SAAS,IAAI,CAAC;AAIlD,OAAK,aAAa,GAAG;AAErB,QAAM,SAAS,IAAI;AAAA,IACjB,EAAE,MAAM,eAAe,SAAS,QAAQ;AAAA,IACxC,EAAE,cAAc,EAAE,OAAO,CAAC,GAAG,WAAW,CAAC,EAAE,EAAE;AAAA,EAC/C;AAEA,QAAM,UAAU,YAAY;AAE5B,SAAO,kBAAkB,wBAAwB,aAAa;AAAA,IAC5D,OAAO;AAAA,EACT,EAAE;AAGF,SAAO,kBAAkB,4BAA4B,aAAa;AAAA,IAChE,WAAW;AAAA,MACT,EAAE,KAAK,kBAAkB,MAAM,QAAQ,UAAU,SAAS;AAAA,IAC5D;AAAA,EACF,EAAE;AAEF,SAAO,kBAAkB,2BAA2B,OAAO,YAAY;AACrE,QAAI,QAAQ,OAAO,QAAQ,kBAAkB;AAC3C,YAAM,IAAI,MAAM,qBAAqB,QAAQ,OAAO,GAAG,EAAE;AAAA,IAC3D;AACA,WAAO;AAAA,MACL,UAAU,CAAC,EAAE,KAAK,kBAAkB,UAAU,UAAU,MAAM,QAAQ,CAAC;AAAA,IACzE;AAAA,EACF,CAAC;AAED,SAAO,kBAAkB,uBAAuB,OAAO,YAAY;AACjE,UAAM,OAAO,QAAQ,OAAO;AAC5B,UAAM,OAAQ,QAAQ,OAAO,aAAa,CAAC;AAE3C,QAAI,CAAE,MAAM,QAAQ,KAAK,EAAI,OAAM,aAAa,GAAG;AAGnD,WAAQ,MAAM,aAAa,SAAS,MAAM,IAAI;AAAA,EAChD,CAAC;AAED,QAAM,YAAY,IAAI,qBAAqB;AAC3C,QAAM,OAAO,QAAQ,SAAS;AAC9B,MAAI,sBAAsB,GAAG,EAAE;AACjC;AAEA,KAAK,EAAE,MAAM,CAAC,QAAQ;AACpB,MAAI,eAAe,QAAS,IAAI,SAAS,IAAI,UAAW,OAAO,GAAG,CAAC;AACnE,UAAQ,WAAW;AACrB,CAAC;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toon-protocol/client-mcp",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "description": "Always-on local daemon + MCP server letting a Claude agent (Desktop or Code) act as a TOON Protocol client: pay-to-write publishing, free reads, channel/balance management, and swaps.",
5
5
  "license": "MIT",
6
6
  "author": "Jonathan Green",
@@ -9,7 +9,8 @@
9
9
  "types": "./dist/index.d.ts",
10
10
  "bin": {
11
11
  "toon-clientd": "./dist/daemon.js",
12
- "toon-mcp": "./dist/mcp.js"
12
+ "toon-mcp": "./dist/mcp.js",
13
+ "toon-journey": "./dist/e2e/run-journey.js"
13
14
  },
14
15
  "files": [
15
16
  "dist",
@@ -35,19 +36,20 @@
35
36
  "optionalDependencies": {
36
37
  "@solana/web3.js": "^1.90.0",
37
38
  "mina-signer": "^3.0.0",
38
- "o1js": "2.14.0",
39
- "socks-proxy-agent": "^8.0.5"
39
+ "o1js": "2.14.0"
40
40
  },
41
41
  "devDependencies": {
42
+ "@anthropic-ai/claude-agent-sdk": "0.3.186",
42
43
  "@toon-protocol/core": "^1.4.1",
43
44
  "@toon-protocol/sdk": "^0.5.0",
44
45
  "@types/node": "^20.0.0",
45
46
  "@types/ws": "^8.5.10",
46
47
  "tsup": "^8.0.0",
48
+ "tsx": "^4.19.2",
47
49
  "typescript": "^5.3.0",
48
50
  "vitest": "^1.0.0",
49
- "@toon-protocol/client": "0.11.0",
50
- "@toon-protocol/views": "0.1.0"
51
+ "@toon-protocol/client": "0.12.0",
52
+ "@toon-protocol/views": "0.1.1"
51
53
  },
52
54
  "engines": {
53
55
  "node": ">=20"
@@ -56,6 +58,7 @@
56
58
  "build": "tsup",
57
59
  "test": "vitest run",
58
60
  "test:integration": "vitest run --config vitest.integration.config.ts",
59
- "test:coverage": "vitest run --coverage"
61
+ "test:coverage": "vitest run --coverage",
62
+ "demo:journey": "tsx src/journey/demo.ts"
60
63
  }
61
64
  }
@@ -1,25 +0,0 @@
1
- import { createRequire as __cr } from 'module'; const require = __cr(import.meta.url);
2
- import {
3
- ANON_ASSETS,
4
- ANON_VERSION,
5
- defaultCacheDir,
6
- ensureAnonBinary,
7
- renderTorrc,
8
- selectAnonAsset,
9
- startManagedAnonProxy,
10
- tcpProbe,
11
- waitForAnonSocks
12
- } from "./chunk-SKQTKZIH.js";
13
- import "./chunk-F22GNSF6.js";
14
- export {
15
- ANON_ASSETS,
16
- ANON_VERSION,
17
- defaultCacheDir,
18
- ensureAnonBinary,
19
- renderTorrc,
20
- selectAnonAsset,
21
- startManagedAnonProxy,
22
- tcpProbe,
23
- waitForAnonSocks
24
- };
25
- //# sourceMappingURL=anon-proxy-W3KMM7GU-FN7ZJY7P.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../views/src/tool-names.ts","../../views/src/filters.ts","../../views/src/spec.ts","../../views/src/catalog.ts","../../views/src/examples.ts","../src/mcp-tools.ts"],"sourcesContent":["/**\n * Shared MCP tool / resource names for the TOON apps surface.\n *\n * Node-safe (no React) so both the iframe runtime and the server can import them\n * and stay in lockstep.\n */\n\n/** Resolves a NIP-01 filter to events (free read). */\nexport const QUERY_TOOL = 'toon_query';\n/** Returns the atom catalog so the agent can compose valid ViewSpecs. */\nexport const ATOMS_TOOL = 'toon_atoms';\n/** Accepts an agent-authored ViewSpec; carries `_meta.ui.resourceUri`. */\nexport const RENDER_TOOL = 'toon_render';\n/** Pay-to-write: daemon signs + publishes the supplied event shell. */\nexport const PUBLISH_TOOL = 'toon_publish_unsigned';\n/** Spendy: upload media to Arweave then publish a referencing event. */\nexport const UPLOAD_TOOL = 'toon_upload_media';\n\n/** The single MCP-app UI resource the host renders. */\nexport const APP_RESOURCE_URI = 'ui://toon/app';\n\n/** Write tools the runtime/validator permit an action to target. */\nexport const WRITE_TOOLS: ReadonlySet<string> = new Set([PUBLISH_TOOL, UPLOAD_TOOL]);\n","/**\n * NIP-01 filter builders for the read side (all reads are free via `toon_read`).\n *\n * The forge builders are promoted from rig's `web/relay-client.ts`; the social\n * and media builders are new. Pure functions returning {@link NostrFilter}.\n */\n\nimport { type NostrFilter } from './types.js';\n\n// ── NIP-34 forge ───────────────────────────────────────────────────────────\n\n/** kind:30617 repository announcements. */\nexport function buildRepoListFilter(): NostrFilter {\n return { kinds: [30617] };\n}\n\n/** kind:30618 repository refs/state for one repo. */\nexport function buildRepoRefsFilter(pubkey: string, repoId: string): NostrFilter {\n return { kinds: [30618], authors: [pubkey], '#d': [repoId] };\n}\n\n/** kind:1621 issues for a repository. */\nexport function buildIssueListFilter(ownerPubkey: string, repoId: string): NostrFilter {\n return { kinds: [1621], '#a': [`30617:${ownerPubkey}:${repoId}`], limit: 100 };\n}\n\n/** kind:1617 patches/PRs for a repository. */\nexport function buildPRListFilter(ownerPubkey: string, repoId: string): NostrFilter {\n return { kinds: [1617], '#a': [`30617:${ownerPubkey}:${repoId}`], limit: 100 };\n}\n\n/** kind:1622 comments by parent event id(s). */\nexport function buildCommentFilter(eventIds: string[]): NostrFilter {\n return { kinds: [1622], '#e': eventIds, limit: 500 };\n}\n\n/** kind:1630-1633 status events by referenced event id(s). */\nexport function buildStatusFilter(eventIds: string[]): NostrFilter {\n return { kinds: [1630, 1631, 1632, 1633], '#e': eventIds, limit: 500 };\n}\n\n/** kind:1632 issue-close events by issue event id(s). */\nexport function buildIssueCloseFilter(eventIds: string[]): NostrFilter {\n return { kinds: [1632], '#e': eventIds, limit: 500 };\n}\n\n/** Fetch specific events by id. */\nexport function buildEventByIdFilter(eventIds: string[]): NostrFilter {\n return { ids: eventIds };\n}\n\n// ── Social (NIP-01/02/10/18/25) ──────────────────────────────────────────────\n\n/** kind:0 profile metadata for the given pubkeys. */\nexport function buildProfileFilter(pubkeys: string[]): NostrFilter {\n return { kinds: [0], authors: pubkeys };\n}\n\n/** kind:1 text-note feed, optionally scoped to authors. */\nexport function buildFeedFilter(authors?: string[], limit = 100): NostrFilter {\n const f: NostrFilter = { kinds: [1], limit };\n if (authors && authors.length > 0) f.authors = authors;\n return f;\n}\n\n/** kind:1 replies referencing the given note id(s) (NIP-10 thread). */\nexport function buildRepliesFilter(eventIds: string[]): NostrFilter {\n return { kinds: [1], '#e': eventIds, limit: 500 };\n}\n\n/** kind:3 follow list for a pubkey. */\nexport function buildFollowListFilter(pubkey: string): NostrFilter {\n return { kinds: [3], authors: [pubkey], limit: 1 };\n}\n\n/** kind:7 reactions targeting the given event id(s). */\nexport function buildReactionFilter(eventIds: string[]): NostrFilter {\n return { kinds: [7], '#e': eventIds, limit: 1000 };\n}\n\n/** kind:6/16 reposts targeting the given event id(s). */\nexport function buildRepostFilter(eventIds: string[]): NostrFilter {\n return { kinds: [6, 16], '#e': eventIds, limit: 500 };\n}\n\n// ── Media (NIP-68/71/94) ─────────────────────────────────────────────────────\n\n/** kind:20/21/22 media posts, optionally scoped to authors. */\nexport function buildMediaFeedFilter(authors?: string[], limit = 100): NostrFilter {\n const f: NostrFilter = { kinds: [20, 21, 22], limit };\n if (authors && authors.length > 0) f.authors = authors;\n return f;\n}\n\n/** kind:1063 NIP-94 file metadata, optionally scoped to authors. */\nexport function buildFileMetadataFilter(authors?: string[], limit = 100): NostrFilter {\n const f: NostrFilter = { kinds: [1063], limit };\n if (authors && authors.length > 0) f.authors = authors;\n return f;\n}\n","/**\n * ViewSpec — the declarative composition language the client-side agent authors\n * to assemble atoms into a user journey. A ViewSpec rides as the *result* of the\n * `toon_render` tool (MCP Apps delivers dynamic data via tool results, not a\n * side-channel); the iframe runtime interprets it.\n *\n * ViewSpecs are MODEL-AUTHORED → UNTRUSTED. `validateViewSpec` is the security\n * boundary: it allowlists atom ids, caps depth/breadth, and rejects anything\n * non-serializable. Invalid specs must degrade to a fallback, never `eval`.\n */\n\nimport { type NostrFilter } from './types.js';\n\n/** Data binding for a node; resolved client-side via free reads (`toon_read`). */\nexport interface ViewBind {\n /** A NIP-01 filter to query and feed into the atom. */\n query?: NostrFilter;\n /** Fetch a single event by id. */\n eventId?: string;\n /** Render the bound event(s) via their kind's default atom. */\n kindAuto?: boolean;\n}\n\n/** Binds a UI affordance to a write tool call (always `toon_publish_unsigned` / `toon_upload_media`). */\nexport interface WriteActionRef {\n /** Tool name to invoke. */\n tool: string;\n /** Static argument template merged with runtime values supplied by the atom. */\n args?: Record<string, unknown>;\n /** Spendy actions require host confirmation (elicitation) before firing. */\n spendy?: boolean;\n /** Human-readable confirmation label for spendy actions. */\n confirmLabel?: string;\n}\n\n/** One node in the composition tree. */\nexport interface ViewNode {\n atom: string;\n props?: Record<string, unknown>;\n bind?: ViewBind;\n actions?: Record<string, WriteActionRef>;\n children?: ViewNode[];\n}\n\n/** A complete view the agent asked the host to render. */\nexport interface ViewSpec {\n title?: string;\n root: ViewNode;\n}\n\n/** Limits applied during validation (defense against malicious/huge specs). */\nexport interface ValidateOptions {\n /** Atom ids the runtime knows how to render. */\n allowedAtoms: ReadonlySet<string> | readonly string[];\n /** Write tool names the runtime is allowed to invoke. */\n allowedTools?: ReadonlySet<string> | readonly string[];\n maxDepth?: number;\n maxNodes?: number;\n}\n\nexport type ValidationResult =\n | { ok: true; spec: ViewSpec }\n | { ok: false; errors: string[] };\n\nconst DEFAULT_MAX_DEPTH = 32;\nconst DEFAULT_MAX_NODES = 500;\n\nconst FILTER_ARRAY_KEYS = new Set([\n 'kinds',\n 'authors',\n 'ids',\n '#d',\n '#e',\n '#a',\n '#p',\n '#t',\n]);\nconst FILTER_NUM_KEYS = new Set(['since', 'until', 'limit']);\n\nfunction isPlainObject(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\nfunction toSet(v: ReadonlySet<string> | readonly string[]): ReadonlySet<string> {\n return v instanceof Set ? v : new Set(v as readonly string[]);\n}\n\nfunction validateFilter(filter: unknown, path: string, errors: string[]): void {\n if (!isPlainObject(filter)) {\n errors.push(`${path}: filter must be an object`);\n return;\n }\n for (const [key, value] of Object.entries(filter)) {\n if (FILTER_ARRAY_KEYS.has(key)) {\n if (\n !Array.isArray(value) ||\n !value.every((x) => typeof x === 'string' || typeof x === 'number')\n ) {\n errors.push(`${path}.${key}: must be an array of string/number`);\n }\n } else if (FILTER_NUM_KEYS.has(key)) {\n if (typeof value !== 'number' || !Number.isFinite(value)) {\n errors.push(`${path}.${key}: must be a finite number`);\n }\n } else {\n errors.push(`${path}.${key}: unsupported filter key`);\n }\n }\n}\n\n/** True if a value is safely JSON-serializable (no functions, no cycles, finite). */\nfunction isJsonSafe(value: unknown, depth = 0): boolean {\n if (depth > 8) return false;\n if (value === null) return true;\n switch (typeof value) {\n case 'string':\n case 'boolean':\n return true;\n case 'number':\n return Number.isFinite(value);\n case 'object':\n if (Array.isArray(value)) return value.every((v) => isJsonSafe(v, depth + 1));\n if (isPlainObject(value))\n return Object.values(value).every((v) => isJsonSafe(v, depth + 1));\n return false;\n default:\n return false;\n }\n}\n\n/**\n * Validate a model-authored ViewSpec against the atom/tool allowlists and the\n * depth/breadth caps. Returns the (structurally identical) spec on success or a\n * list of human-readable errors on failure.\n */\nexport function validateViewSpec(\n input: unknown,\n opts: ValidateOptions\n): ValidationResult {\n const errors: string[] = [];\n const allowedAtoms = toSet(opts.allowedAtoms);\n const allowedTools = opts.allowedTools ? toSet(opts.allowedTools) : undefined;\n const maxDepth = opts.maxDepth ?? DEFAULT_MAX_DEPTH;\n const maxNodes = opts.maxNodes ?? DEFAULT_MAX_NODES;\n\n if (!isPlainObject(input)) {\n return { ok: false, errors: ['spec must be an object'] };\n }\n if (input['title'] !== undefined && typeof input['title'] !== 'string') {\n errors.push('spec.title: must be a string');\n }\n if (!isPlainObject(input['root'])) {\n return { ok: false, errors: ['spec.root: required object'] };\n }\n\n let nodeCount = 0;\n\n const walk = (node: unknown, path: string, depth: number): void => {\n if (depth > maxDepth) {\n errors.push(`${path}: exceeds max depth ${maxDepth}`);\n return;\n }\n if (++nodeCount > maxNodes) {\n errors.push(`spec: exceeds max node count ${maxNodes}`);\n return;\n }\n if (!isPlainObject(node)) {\n errors.push(`${path}: node must be an object`);\n return;\n }\n if (typeof node['atom'] !== 'string') {\n errors.push(`${path}.atom: must be a string`);\n } else if (!allowedAtoms.has(node['atom'])) {\n errors.push(`${path}.atom: unknown atom \"${node['atom']}\"`);\n }\n\n if (node['props'] !== undefined) {\n if (!isPlainObject(node['props']) || !isJsonSafe(node['props'])) {\n errors.push(`${path}.props: must be a JSON-serializable object`);\n }\n }\n\n if (node['bind'] !== undefined) {\n const bind = node['bind'];\n if (!isPlainObject(bind)) {\n errors.push(`${path}.bind: must be an object`);\n } else {\n if (bind['query'] !== undefined) validateFilter(bind['query'], `${path}.bind.query`, errors);\n if (bind['eventId'] !== undefined && typeof bind['eventId'] !== 'string') {\n errors.push(`${path}.bind.eventId: must be a string`);\n }\n if (bind['kindAuto'] !== undefined && typeof bind['kindAuto'] !== 'boolean') {\n errors.push(`${path}.bind.kindAuto: must be a boolean`);\n }\n }\n }\n\n if (node['actions'] !== undefined) {\n const actions = node['actions'];\n if (!isPlainObject(actions)) {\n errors.push(`${path}.actions: must be an object`);\n } else {\n for (const [name, ref] of Object.entries(actions)) {\n const p = `${path}.actions.${name}`;\n if (!isPlainObject(ref)) {\n errors.push(`${p}: must be an object`);\n continue;\n }\n if (typeof ref['tool'] !== 'string') {\n errors.push(`${p}.tool: must be a string`);\n } else if (allowedTools && !allowedTools.has(ref['tool'])) {\n errors.push(`${p}.tool: tool \"${ref['tool']}\" not allowed`);\n }\n if (ref['args'] !== undefined && (!isPlainObject(ref['args']) || !isJsonSafe(ref['args']))) {\n errors.push(`${p}.args: must be a JSON-serializable object`);\n }\n if (ref['spendy'] !== undefined && typeof ref['spendy'] !== 'boolean') {\n errors.push(`${p}.spendy: must be a boolean`);\n }\n if (ref['confirmLabel'] !== undefined && typeof ref['confirmLabel'] !== 'string') {\n errors.push(`${p}.confirmLabel: must be a string`);\n }\n }\n }\n }\n\n if (node['children'] !== undefined) {\n if (!Array.isArray(node['children'])) {\n errors.push(`${path}.children: must be an array`);\n } else {\n node['children'].forEach((child, i) => walk(child, `${path}.children[${i}]`, depth + 1));\n }\n }\n };\n\n walk(input['root'], 'spec.root', 0);\n\n if (errors.length > 0) return { ok: false, errors };\n return { ok: true, spec: input as unknown as ViewSpec };\n}\n","/**\n * Pure atom catalog — Node-safe metadata (no React).\n *\n * This is the single source of truth for which atom ids exist, what kinds they\n * render, and which writes they expose. The client-side React registry\n * ({@link ./atoms/registry}) is asserted to match this list in tests, and the\n * server-side `toon_atoms` tool + ViewSpec validator allowlist read from here so\n * `@toon-protocol/client-mcp` never imports the React bundle.\n */\n\nexport interface AtomWriteMeta {\n name: string;\n spendy?: boolean;\n}\n\nexport interface AtomMeta {\n id: string;\n description: string;\n /** Event kinds this atom is the default renderer for. */\n kinds?: number[];\n /** Write tools this atom can fire. */\n writes?: AtomWriteMeta[];\n /** Human/agent-readable hint of the props the atom accepts. */\n propsSchema?: Record<string, string>;\n}\n\nexport const ATOM_CATALOG: AtomMeta[] = [\n // layout\n {\n id: 'stack',\n description: 'Vertical (default) or horizontal stack of child nodes.',\n propsSchema: { direction: \"'row' | 'col'\", gap: 'number (tailwind gap step)' },\n },\n { id: 'section', description: 'Titled section wrapper.', propsSchema: { title: 'string' } },\n { id: 'card', description: 'Bordered card container.' },\n {\n id: 'tabs',\n description: 'Tabbed container; one child node per tab. Use for multi-section journeys.',\n propsSchema: { labels: 'string[] (tab names, in child order)' },\n },\n\n // social\n { id: 'profile-header', description: 'NIP-01 kind:0 profile header (avatar, name, nip05, bio).', kinds: [0] },\n { id: 'note-card', description: 'NIP-01 kind:1 text note; optional \"reply\" action.', kinds: [1] },\n { id: 'reaction-bar', description: 'NIP-25 kind:7 reaction counts; optional \"react\" action.', kinds: [7] },\n {\n id: 'follow-button',\n description: 'NIP-02 follow/unfollow (publishes kind:3).',\n writes: [{ name: 'toon_publish_unsigned' }],\n propsSchema: { label: 'string' },\n },\n {\n id: 'composer',\n description: 'Text composer that publishes a note (kind:1) via its \"post\" action.',\n writes: [{ name: 'toon_publish_unsigned' }],\n propsSchema: { placeholder: 'string', label: 'string' },\n },\n\n // media\n {\n id: 'media-embed',\n description: 'Render image/video streamed from Arweave (NIP-68/71/94).',\n kinds: [20, 21, 22, 1063],\n },\n {\n id: 'media-uploader',\n description: 'Upload media to Arweave then publish a media event. Spendy.',\n writes: [{ name: 'toon_upload_media', spendy: true }],\n propsSchema: { label: 'string' },\n },\n\n // forge\n { id: 'repo-card', description: 'NIP-34 kind:30617 repository card.', kinds: [30617] },\n {\n id: 'issue-card',\n description: 'NIP-34 kind:1621 issue; optional \"comment\" action.',\n kinds: [1621],\n writes: [{ name: 'toon_publish_unsigned' }],\n },\n { id: 'pr-card', description: 'NIP-34 kind:1617 patch/PR summary (status, commits, base).', kinds: [1617] },\n {\n id: 'comment-thread',\n description: 'NIP-34 kind:1622 comment list; optional \"comment\" action.',\n kinds: [1622],\n writes: [{ name: 'toon_publish_unsigned' }],\n },\n\n // fallback\n { id: 'generic-event', description: 'Fallback: decoded JSON + tags for any kind without a bespoke atom.' },\n];\n\n/** All catalog atom ids (the ViewSpec validator allowlist). */\nexport const CATALOG_ATOM_IDS: ReadonlySet<string> = new Set(ATOM_CATALOG.map((a) => a.id));\n","/**\n * Example ViewSpecs — reference compositions the agent can learn from (surfaced\n * in the `toon_atoms` tool result) and reuse as journey starting points.\n *\n * These are plain data (Node-safe). They demonstrate the grammar: layout atoms\n * arranging domain atoms, `bind` for free reads, `kindAuto` feeds, and `actions`\n * wiring write tools.\n */\n\nimport { type ViewSpec } from './spec.js';\nimport {\n buildFeedFilter,\n buildProfileFilter,\n buildRepoListFilter,\n buildIssueListFilter,\n buildCommentFilter,\n buildEventByIdFilter,\n buildMediaFeedFilter,\n} from './filters.js';\nimport { PUBLISH_TOOL, UPLOAD_TOOL } from './tool-names.js';\n\n/** A social feed: composer to post + a kindAuto note feed. */\nexport function feedView(): ViewSpec {\n return {\n title: 'Feed',\n root: {\n atom: 'stack',\n children: [\n { atom: 'composer', actions: { post: { tool: PUBLISH_TOOL, args: { kind: 1 } } } },\n { atom: 'note-card', bind: { query: buildFeedFilter(undefined, 50), kindAuto: true } },\n ],\n },\n };\n}\n\n/** A profile page: header + follow button + that author's notes. */\nexport function profileView(pubkey: string): ViewSpec {\n return {\n title: 'Profile',\n root: {\n atom: 'stack',\n children: [\n { atom: 'profile-header', bind: { query: buildProfileFilter([pubkey]) } },\n {\n atom: 'follow-button',\n props: { label: 'Follow' },\n actions: { follow: { tool: PUBLISH_TOOL, args: { kind: 3, tags: [['p', pubkey]] } } },\n },\n { atom: 'note-card', bind: { query: buildFeedFilter([pubkey], 50), kindAuto: true } },\n ],\n },\n };\n}\n\n/** A thread: the root note, its replies, and a reply composer. */\nexport function threadView(rootId: string): ViewSpec {\n return {\n title: 'Thread',\n root: {\n atom: 'stack',\n children: [\n { atom: 'note-card', bind: { query: buildEventByIdFilter([rootId]), kindAuto: true } },\n { atom: 'note-card', bind: { query: buildCommentFilter([rootId]), kindAuto: true } },\n {\n atom: 'composer',\n props: { placeholder: 'Reply…', label: 'Reply' },\n actions: { post: { tool: PUBLISH_TOOL, args: { kind: 1, tags: [['e', rootId, '', 'root']] } } },\n },\n ],\n },\n };\n}\n\n/** A forge view: repo list, then issues for a repo. */\nexport function forgeView(ownerPubkey: string, repoId: string): ViewSpec {\n return {\n title: 'Forge',\n root: {\n atom: 'tabs',\n props: { labels: ['Repos', 'Issues'] },\n children: [\n {\n atom: 'section',\n props: { title: 'Repositories' },\n children: [{ atom: 'repo-card', bind: { query: buildRepoListFilter(), kindAuto: true } }],\n },\n {\n atom: 'section',\n props: { title: 'Issues' },\n children: [\n { atom: 'issue-card', bind: { query: buildIssueListFilter(ownerPubkey, repoId), kindAuto: true } },\n ],\n },\n ],\n },\n };\n}\n\n/** A media gallery + uploader. */\nexport function mediaView(): ViewSpec {\n return {\n title: 'Media',\n root: {\n atom: 'stack',\n children: [\n { atom: 'media-uploader', props: { label: 'Post a picture' }, actions: { upload: { tool: UPLOAD_TOOL, args: { kind: 20 }, spendy: true } } },\n { atom: 'media-embed', bind: { query: buildMediaFeedFilter(undefined, 30), kindAuto: true } },\n ],\n },\n };\n}\n\nexport interface ExampleView {\n name: string;\n description: string;\n spec: ViewSpec;\n}\n\n/** Concrete examples (with placeholder ids) for the agent to pattern-match on. */\nexport const EXAMPLE_VIEWSPECS: ExampleView[] = [\n { name: 'feed', description: 'Social feed with a post composer.', spec: feedView() },\n { name: 'profile', description: 'A profile header, follow button, and the author’s notes.', spec: profileView('<pubkey-hex>') },\n { name: 'thread', description: 'A note with its replies and a reply composer.', spec: threadView('<root-event-id>') },\n { name: 'forge', description: 'Tabbed repos + issues (NIP-34).', spec: forgeView('<owner-pubkey>', '<repo-id>') },\n { name: 'media', description: 'Media gallery with an uploader.', spec: mediaView() },\n];\n","/**\n * MCP tool definitions + dispatch. The MCP server is a thin proxy: each tool\n * maps to a `toon-clientd` control-plane call. This module is the testable core\n * (no stdio / SDK transport) so the tool→HTTP mapping and the\n * \"bootstrapping — retry\" handling can be unit-tested directly.\n */\n\nimport {\n ATOM_CATALOG,\n CATALOG_ATOM_IDS,\n EXAMPLE_VIEWSPECS,\n WRITE_TOOLS,\n APP_RESOURCE_URI,\n validateViewSpec,\n} from '@toon-protocol/views';\nimport { ControlApiError, DaemonUnreachableError } from './control-client.js';\nimport type { ControlClient } from './control-client.js';\nimport type {\n AddApexRequest,\n HttpFetchPaidRequest,\n NostrFilter,\n PublishRequest,\n PublishUnsignedRequest,\n SettlementChain,\n SwapRequest,\n UploadMediaRequest,\n} from './control-api.js';\n\n/** A JSON-Schema-described MCP tool. */\nexport interface ToolDefinition {\n name: string;\n description: string;\n inputSchema: Record<string, unknown>;\n /** MCP-apps metadata, e.g. `{ ui: { resourceUri } }` linking a UI resource. */\n _meta?: Record<string, unknown>;\n}\n\n/** MCP tool-call result shape (subset of the SDK's CallToolResult). */\nexport interface ToolResult {\n content: { type: 'text'; text: string }[];\n /** Machine-readable payload the MCP-app iframe reads (events, ViewSpec, …). */\n structuredContent?: Record<string, unknown>;\n isError?: boolean;\n}\n\nexport const TOOL_DEFINITIONS: ToolDefinition[] = [\n {\n name: 'toon_status',\n description:\n 'Report TOON client daemon health: bootstrapping/ready state, transport, ' +\n 'relay connection, buffered-event count, and per-chain settlement status.',\n inputSchema: {\n type: 'object',\n properties: {},\n additionalProperties: false,\n },\n },\n {\n name: 'toon_identity',\n description:\n \"Return this client's public identity (Nostr pubkey + EVM/Solana/Mina \" +\n 'addresses). Never returns private keys.',\n inputSchema: {\n type: 'object',\n properties: {},\n additionalProperties: false,\n },\n },\n {\n name: 'toon_publish',\n description:\n 'Pay-to-write: publish a fully-signed Nostr event to the TOON network. ' +\n 'Signs an off-chain payment-channel claim and forwards it over BTP. ' +\n 'Returns the event id, channel id, and the advanced channel nonce.',\n inputSchema: {\n type: 'object',\n properties: {\n event: {\n type: 'object',\n description:\n 'A fully-signed Nostr event (must include id, pubkey, sig, kind, ' +\n 'created_at, tags, content).',\n },\n destination: {\n type: 'string',\n description:\n 'Optional ILP destination override (default: the apex/town).',\n },\n fee: {\n type: 'string',\n description:\n 'Optional fee override in base units (default: daemon config).',\n },\n btpUrl: {\n type: 'string',\n description:\n 'Which apex (BTP write target) to publish through (default: the ' +\n 'config-seeded apex). Use toon_targets to list registered apexes. ' +\n 'Writes always go through BTP — never a relay directly.',\n },\n },\n required: ['event'],\n additionalProperties: false,\n },\n },\n {\n name: 'toon_publish_unsigned',\n description:\n 'Pay-to-write WITHOUT holding a key: supply only the event shell (kind, ' +\n 'content, tags) and the daemon signs it with the held Nostr key, signs the ' +\n 'payment-channel claim, and forwards over BTP. For replaceable kinds ' +\n '(0 profile, 3 follow list) the daemon merges the latest known tags first. ' +\n 'This is the path MCP-app UI actions use so the iframe never signs.',\n inputSchema: {\n type: 'object',\n properties: {\n kind: { type: 'number', description: 'Event kind (integer 0–65535).' },\n content: { type: 'string', description: 'Event content (default empty).' },\n tags: {\n type: 'array',\n items: { type: 'array', items: { type: 'string' } },\n description: 'Event tags (array of string arrays).',\n },\n destination: { type: 'string', description: 'Optional ILP destination override.' },\n fee: { type: 'string', description: 'Optional fee override (base units).' },\n btpUrl: { type: 'string', description: 'Which apex to publish through (default: config-seeded).' },\n },\n required: ['kind'],\n additionalProperties: false,\n },\n },\n {\n name: 'toon_upload_media',\n description:\n 'Pay-to-write media (SPENDY, two-step): upload base64 bytes to Arweave via ' +\n 'the kind:5094 blob-storage DVM, then sign+publish a media event ' +\n '(default kind:1063 NIP-94; 20=picture, 21/22=video, 1=note w/ NIP-92 imeta) ' +\n 'referencing the resulting Arweave URL. Single-packet only.',\n inputSchema: {\n type: 'object',\n properties: {\n dataBase64: { type: 'string', description: 'Base64-encoded media bytes.' },\n mime: { type: 'string', description: \"MIME type (default 'application/octet-stream').\" },\n kind: { type: 'number', description: 'Media event kind (default 1063).' },\n caption: { type: 'string', description: 'Caption/content for the media event.' },\n tags: {\n type: 'array',\n items: { type: 'array', items: { type: 'string' } },\n description: 'Extra tags merged into the published media event.',\n },\n fee: { type: 'string', description: 'Optional fee override (base units).' },\n btpUrl: { type: 'string', description: 'Which apex to publish through (default: config-seeded).' },\n },\n required: ['dataBase64'],\n additionalProperties: false,\n },\n },\n {\n name: 'toon_atoms',\n description:\n 'List the atom vocabulary (ids, kinds rendered, props, write actions) plus ' +\n 'example ViewSpecs, for composing a view to pass to toon_render. This is how ' +\n 'you build the user a generative UI for their journey.',\n inputSchema: { type: 'object', properties: {}, additionalProperties: false },\n },\n {\n name: 'toon_render',\n description:\n 'Render an agent-authored ViewSpec (a tree of atoms with data binds and ' +\n 'write actions) as the in-host UI. Call toon_atoms first to learn the ' +\n 'vocabulary. The ViewSpec is validated; the host renders the app with it.',\n inputSchema: {\n type: 'object',\n properties: {\n spec: {\n type: 'object',\n description: 'A ViewSpec: { title?, root: ViewNode }. See toon_atoms examples.',\n },\n },\n required: ['spec'],\n additionalProperties: false,\n },\n _meta: { ui: { resourceUri: APP_RESOURCE_URI } },\n },\n {\n name: 'toon_query',\n description:\n 'Free read for the UI: resolve a NIP-01 filter to matching events ' +\n '(subscribes, waits briefly, returns matches). Used to fill ViewSpec binds.',\n inputSchema: {\n type: 'object',\n properties: {\n filter: { description: 'A NIP-01 filter object.' },\n timeoutMs: { type: 'number', description: 'Bounded wait, ms (default 1200).' },\n },\n required: ['filter'],\n additionalProperties: false,\n },\n },\n {\n name: 'toon_subscribe',\n description:\n 'Free read: register a persistent town-relay subscription with NIP-01 ' +\n 'filter(s). Returns a subscription id to drain with toon_read.',\n inputSchema: {\n type: 'object',\n properties: {\n filters: {\n description: 'A NIP-01 filter object or an array of OR-ed filters.',\n },\n subId: { type: 'string', description: 'Optional caller-supplied id.' },\n relayUrl: {\n type: 'string',\n description:\n 'Restrict to one relay. Omit to FAN OUT across every registered ' +\n 'relay (reads merge into one ordered stream).',\n },\n },\n required: ['filters'],\n additionalProperties: false,\n },\n },\n {\n name: 'toon_read',\n description:\n 'Free read: drain buffered events newer than a cursor. Pass back the ' +\n 'returned cursor to fetch only events received since the last read.',\n inputSchema: {\n type: 'object',\n properties: {\n subId: { type: 'string', description: 'Restrict to one subscription.' },\n cursor: {\n type: 'number',\n description: 'Cursor from a prior toon_read.',\n },\n limit: {\n type: 'number',\n description: 'Max events to return (default 200).',\n },\n relayUrl: {\n type: 'string',\n description: 'Restrict the drain to events from a single relay.',\n },\n },\n additionalProperties: false,\n },\n },\n {\n name: 'toon_open_channel',\n description:\n 'Open (or return the existing) payment channel for a destination. ' +\n 'Channels open lazily on first publish; use this to pre-open.',\n inputSchema: {\n type: 'object',\n properties: {\n destination: {\n type: 'string',\n description: 'ILP destination (default: apex).',\n },\n },\n additionalProperties: false,\n },\n },\n {\n name: 'toon_channels',\n description:\n 'List tracked payment channels with their nonce watermark and cumulative ' +\n 'transferred amount.',\n inputSchema: {\n type: 'object',\n properties: {},\n additionalProperties: false,\n },\n },\n {\n name: 'toon_swap',\n description:\n 'Pay a mill peer (asset A) to receive asset B plus a signed target-chain ' +\n 'claim. Builds the NIP-59 gift-wrapped kind:20032 swap rumor and streams ' +\n 'it; the source-asset claim is signed against the open apex channel (the ' +\n 'mill must be routed via apexChildPeers). Returns the accumulated, ' +\n 'decrypted target-chain claim(s) and settlement metadata.',\n inputSchema: {\n type: 'object',\n properties: {\n destination: {\n type: 'string',\n description: 'Mill peer ILP destination (e.g. g.townhouse.mill).',\n },\n amount: {\n type: 'string',\n description: 'Total source-asset amount to swap, source micro-units.',\n },\n millPubkey: {\n type: 'string',\n description:\n \"Mill's 64-char lowercase hex Nostr pubkey (gift-wrap recipient).\",\n },\n pair: {\n type: 'object',\n description:\n 'The swap pair (from kind:10032 discovery or operator-supplied): ' +\n '{ from:{assetCode,assetScale,chain}, to:{...}, rate, minAmount?, maxAmount? }.',\n },\n chainRecipient: {\n type: 'string',\n description:\n \"Sender's payout address on pair.to.chain (EVM 0x-hex / Solana / Mina base58).\",\n },\n packetCount: {\n type: 'number',\n description: 'Split the swap into N equal packets (default 1).',\n },\n },\n required: [\n 'destination',\n 'amount',\n 'millPubkey',\n 'pair',\n 'chainRecipient',\n ],\n additionalProperties: false,\n },\n },\n {\n name: 'toon_http_fetch_paid',\n description:\n 'Fetch a paid HTTP resource: issues the request, and if the server ' +\n 'returns 402 Payment Required, transparently pays over TOON and retries, ' +\n 'returning the settled resource. Settlement happens inside the daemon ' +\n 'against the open apex channel (the caller never holds chain keys). ' +\n 'Returns { status, headers, body } (body decoded as text).',\n inputSchema: {\n type: 'object',\n properties: {\n url: {\n type: 'string',\n description: 'Absolute URL of the resource to fetch.',\n },\n method: {\n type: 'string',\n description: 'HTTP method (default GET).',\n },\n headers: {\n type: 'object',\n description: 'Request headers as a flat string→string map.',\n },\n body: {\n type: 'string',\n description: 'Request body (string, sent verbatim; e.g. with POST).',\n },\n timeout: {\n type: 'number',\n description: 'Per-request timeout, ms.',\n },\n },\n required: ['url'],\n additionalProperties: false,\n },\n },\n {\n name: 'toon_targets',\n description:\n 'List every registered target: relays (read sources, with connection + ' +\n 'buffered-event status) and apexes (BTP write targets, with ready/' +\n 'channel status). The TOON client is 1-to-many — many apexes to write ' +\n 'through, many relays to read from.',\n inputSchema: {\n type: 'object',\n properties: {},\n additionalProperties: false,\n },\n },\n {\n name: 'toon_add_relay',\n description:\n 'Add a relay READ target at runtime (persisted across restarts). It joins ' +\n 'all fan-out reads immediately; `.anyone` hidden-service relays reuse the ' +\n 'managed anon read proxy automatically.',\n inputSchema: {\n type: 'object',\n properties: {\n relayUrl: {\n type: 'string',\n description: 'Relay WS URL (ws://host:7100 or a .anyone service).',\n },\n },\n required: ['relayUrl'],\n additionalProperties: false,\n },\n },\n {\n name: 'toon_remove_relay',\n description:\n 'Remove a relay read target (persisted). The config-seeded default relay ' +\n 'cannot be removed.',\n inputSchema: {\n type: 'object',\n properties: {\n relayUrl: { type: 'string', description: 'Relay WS URL to remove.' },\n },\n required: ['relayUrl'],\n additionalProperties: false,\n },\n },\n {\n name: 'toon_add_apex',\n description:\n 'Add an apex WRITE target at runtime (persisted across restarts). ' +\n 'Settlement params are DISCOVERED by reading the apex’s kind:10032 ' +\n 'announcement off the given relay — you do not supply chain/settlement ' +\n 'details. The relay is added as a read target first if unknown.',\n inputSchema: {\n type: 'object',\n properties: {\n ilpAddress: {\n type: 'string',\n description: 'ILP address of the apex (e.g. g.townhouse.town).',\n },\n relayUrl: {\n type: 'string',\n description: 'Relay to discover the apex’s kind:10032 on.',\n },\n pubkey: {\n type: 'string',\n description:\n 'Optional apex Nostr pubkey (64-char hex) to narrow discovery.',\n },\n chain: {\n type: 'string',\n enum: ['evm', 'solana', 'mina'],\n description: 'Preferred settlement chain (default: apex’s first).',\n },\n childPeers: {\n type: 'array',\n items: { type: 'string' },\n description:\n 'Child peers via this apex’s channel (e.g. [\"dvm\",\"mill\"]).',\n },\n feePerEvent: {\n type: 'string',\n description: 'Per-write fee override for this apex (base units).',\n },\n },\n required: ['ilpAddress', 'relayUrl'],\n additionalProperties: false,\n },\n },\n {\n name: 'toon_remove_apex',\n description:\n 'Remove an apex write target by its BTP URL (persisted). The ' +\n 'config-seeded default apex cannot be removed.',\n inputSchema: {\n type: 'object',\n properties: {\n btpUrl: {\n type: 'string',\n description: 'BTP URL of the apex to remove (from toon_targets).',\n },\n },\n required: ['btpUrl'],\n additionalProperties: false,\n },\n },\n];\n\n/**\n * Dispatch an MCP tool call to the daemon control plane. Always resolves with a\n * `ToolResult` (errors are encoded as `isError: true` text, not thrown, so the\n * agent sees a readable message). A retryable error (daemon still\n * bootstrapping) yields a clear \"retry shortly\" message.\n */\nexport async function dispatchTool(\n client: ControlClient,\n name: string,\n args: Record<string, unknown>\n): Promise<ToolResult> {\n try {\n switch (name) {\n case 'toon_status':\n return ok(await client.status());\n case 'toon_identity': {\n const s = await client.status();\n return ok({\n identity: s.identity,\n ready: s.ready,\n bootstrapping: s.bootstrapping,\n });\n }\n case 'toon_publish':\n return ok(await client.publish(args as unknown as PublishRequest));\n case 'toon_publish_unsigned':\n return ok(\n await client.publishUnsigned(args as unknown as PublishUnsignedRequest)\n );\n case 'toon_upload_media':\n return ok(\n await client.uploadMedia(args as unknown as UploadMediaRequest)\n );\n case 'toon_atoms':\n return okStructured('Atom vocabulary + example ViewSpecs.', {\n atoms: ATOM_CATALOG,\n examples: EXAMPLE_VIEWSPECS,\n });\n case 'toon_render': {\n const check = validateViewSpec(args['spec'], {\n allowedAtoms: CATALOG_ATOM_IDS,\n allowedTools: WRITE_TOOLS,\n });\n if (!check.ok) {\n return err(`Invalid ViewSpec:\\n- ${check.errors.join('\\n- ')}`);\n }\n return okStructured(\n `Rendering view${check.spec.title ? `: ${check.spec.title}` : ''}.`,\n { viewSpec: check.spec }\n );\n }\n case 'toon_query': {\n const res = await client.query({\n filters: args['filter'] as NostrFilter,\n ...(typeof args['timeoutMs'] === 'number'\n ? { timeoutMs: args['timeoutMs'] }\n : {}),\n });\n return okStructured(`${res.events.length} event(s).`, {\n events: res.events,\n });\n }\n case 'toon_subscribe':\n return ok(\n await client.subscribe({\n filters: args['filters'] as NostrFilter | NostrFilter[],\n ...(typeof args['subId'] === 'string'\n ? { subId: args['subId'] }\n : {}),\n ...(typeof args['relayUrl'] === 'string'\n ? { relayUrl: args['relayUrl'] }\n : {}),\n })\n );\n case 'toon_read':\n return ok(\n await client.events({\n ...(typeof args['subId'] === 'string'\n ? { subId: args['subId'] }\n : {}),\n ...(typeof args['cursor'] === 'number'\n ? { cursor: args['cursor'] }\n : {}),\n ...(typeof args['limit'] === 'number'\n ? { limit: args['limit'] }\n : {}),\n ...(typeof args['relayUrl'] === 'string'\n ? { relayUrl: args['relayUrl'] }\n : {}),\n })\n );\n case 'toon_open_channel':\n return ok(\n await client.openChannel(\n typeof args['destination'] === 'string'\n ? { destination: args['destination'] }\n : {}\n )\n );\n case 'toon_channels':\n return ok(await client.channels());\n case 'toon_swap':\n return ok(\n await client.swap({\n destination: String(args['destination']),\n amount: String(args['amount']),\n millPubkey: String(args['millPubkey']),\n pair: args['pair'] as SwapRequest['pair'],\n chainRecipient: String(args['chainRecipient']),\n ...(typeof args['packetCount'] === 'number'\n ? { packetCount: args['packetCount'] }\n : {}),\n })\n );\n case 'toon_http_fetch_paid': {\n const req: HttpFetchPaidRequest = {\n url: String(args['url']),\n ...(typeof args['method'] === 'string'\n ? { method: args['method'] }\n : {}),\n ...(args['headers'] && typeof args['headers'] === 'object'\n ? { headers: args['headers'] as Record<string, string> }\n : {}),\n ...(typeof args['body'] === 'string' ? { body: args['body'] } : {}),\n ...(typeof args['timeout'] === 'number'\n ? { timeout: args['timeout'] }\n : {}),\n };\n return ok(await client.httpFetchPaid(req));\n }\n case 'toon_targets':\n return ok(await client.targets());\n case 'toon_add_relay':\n return ok(\n await client.addRelay({ relayUrl: String(args['relayUrl']) })\n );\n case 'toon_remove_relay':\n return ok(\n await client.removeRelay({ relayUrl: String(args['relayUrl']) })\n );\n case 'toon_add_apex': {\n const req: AddApexRequest = {\n ilpAddress: String(args['ilpAddress']),\n relayUrl: String(args['relayUrl']),\n ...(typeof args['pubkey'] === 'string'\n ? { pubkey: args['pubkey'] }\n : {}),\n ...(typeof args['chain'] === 'string'\n ? { chain: args['chain'] as SettlementChain }\n : {}),\n ...(Array.isArray(args['childPeers'])\n ? { childPeers: (args['childPeers'] as unknown[]).map(String) }\n : {}),\n ...(typeof args['feePerEvent'] === 'string'\n ? { feePerEvent: args['feePerEvent'] }\n : {}),\n };\n return ok(await client.addApex(req));\n }\n case 'toon_remove_apex':\n return ok(await client.removeApex({ btpUrl: String(args['btpUrl']) }));\n default:\n return err(`Unknown tool: ${name}`);\n }\n } catch (e) {\n // A 504 is a retryable apex-discovery timeout — give a discovery-specific\n // hint rather than the daemon-bootstrapping one.\n if (e instanceof ControlApiError && e.status === 504) {\n return err(\n `${e.detail ?? e.message} — retry once the relay is reachable and the apex is online.`\n );\n }\n if (e instanceof ControlApiError && e.retryable) {\n return err(\n `TOON client is still bootstrapping (the anon proxy / BTP session can take ` +\n `30–90s) — retry shortly. (${e.message})`\n );\n }\n if (e instanceof DaemonUnreachableError) {\n return err(\n `TOON client daemon is not reachable at ${e.baseUrl}. It may have failed ` +\n `to start — check ~/.toon-client/daemon.log.`\n );\n }\n if (e instanceof ControlApiError) {\n return err(`${e.message}${e.detail ? `: ${e.detail}` : ''}`);\n }\n return err(e instanceof Error ? e.message : String(e));\n }\n}\n\nfunction ok(data: unknown): ToolResult {\n return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };\n}\n\n/** Result carrying machine-readable `structuredContent` for the MCP-app iframe. */\nfunction okStructured(\n text: string,\n structuredContent: Record<string, unknown>\n): ToolResult {\n return { content: [{ type: 'text', text }], structuredContent };\n}\n\nfunction err(message: string): ToolResult {\n return { content: [{ type: 'text', text: message }], isError: true };\n}\n"],"mappings":"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","names":[]}