@toon-protocol/client-mcp 0.20.2 → 0.20.4
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/{chunk-LN2OF264.js → chunk-6GXZ4KRO.js} +257 -1552
- package/dist/chunk-6GXZ4KRO.js.map +1 -0
- package/dist/{chunk-ZKSFO7M3.js → chunk-F3XAIOGQ.js} +2 -2
- package/dist/{chunk-IAOKAQLA.js → chunk-FDUYHYB2.js} +2 -2
- package/dist/{chunk-IAOKAQLA.js.map → chunk-FDUYHYB2.js.map} +1 -1
- package/dist/chunk-IYPIOSEF.js +3856 -0
- package/dist/chunk-IYPIOSEF.js.map +1 -0
- package/dist/{chunk-UPIAVE44.js → chunk-JFDVE4IA.js} +544 -4115
- package/dist/chunk-JFDVE4IA.js.map +1 -0
- package/dist/{chunk-WS3GVRQJ.js → chunk-TGIKIMXO.js} +9 -7
- package/dist/{chunk-WS3GVRQJ.js.map → chunk-TGIKIMXO.js.map} +1 -1
- package/dist/chunk-V7D5HJBT.js +1363 -0
- package/dist/chunk-V7D5HJBT.js.map +1 -0
- package/dist/daemon.js +6 -4
- package/dist/daemon.js.map +1 -1
- package/dist/{ed25519-VBPL32VX.js → ed25519-U6LNJH4K.js} +3 -2
- package/dist/index.js +7 -5
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +6 -4
- package/dist/mcp.js.map +1 -1
- package/dist/mina-channel-deploy-CO2LMPPB-H2N5FX4G.js +12 -0
- package/dist/mina-channel-deploy-CO2LMPPB-H2N5FX4G.js.map +1 -0
- package/dist/{node-WPA2UDEH-RCLJ66AU.js → node-WPA2UDEH-HZP3B4FH.js} +3 -3
- package/dist/{node-WPA2UDEH-RCLJ66AU.js.map → node-WPA2UDEH-HZP3B4FH.js.map} +1 -1
- package/package.json +5 -5
- package/dist/chunk-LN2OF264.js.map +0 -1
- package/dist/chunk-UPIAVE44.js.map +0 -1
- /package/dist/{chunk-ZKSFO7M3.js.map → chunk-F3XAIOGQ.js.map} +0 -0
- /package/dist/{ed25519-VBPL32VX.js.map → ed25519-U6LNJH4K.js.map} +0 -0
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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 * → Store 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: Store 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: 'store-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. swap peer). */\n destination: string;\n /** Total source-asset amount to swap, in source micro-units. */\n amount: string;\n /** Swap peer's 64-char lowercase hex Nostr pubkey (gift-wrap recipient). */\n swapPubkey: 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 swapPubkey: opts.swapPubkey,\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 API base URL (default http://127.0.0.1:8787)\n * TOON_SWAP_DEST swap ILP destination (e.g. g.proxy.swap)\n * TOON_SWAP_AMOUNT source-asset amount, micro-units (e.g. 1000000)\n * TOON_SWAP_PUBKEY swap peer 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 swapPubkey = env['TOON_SWAP_PUBKEY'];\n const chainRecipient = env['TOON_CHAIN_RECIPIENT'];\n const pairRaw = env['TOON_SWAP_PAIR'];\n\n if (!destination || !amount || !swapPubkey || !chainRecipient || !pairRaw) {\n console.error(\n '[capstone] missing required env: TOON_SWAP_DEST, TOON_SWAP_AMOUNT, ' +\n 'TOON_SWAP_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, swapPubkey, 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":"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","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 * → Store 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: Store 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: 'store-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. swap peer). */\n destination: string;\n /** Total source-asset amount to swap, in source micro-units. */\n amount: string;\n /** Swap peer's 64-char lowercase hex Nostr pubkey (gift-wrap recipient). */\n swapPubkey: 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 swapPubkey: opts.swapPubkey,\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 API base URL (default http://127.0.0.1:8787)\n * TOON_SWAP_DEST swap ILP destination (e.g. g.proxy.swap)\n * TOON_SWAP_AMOUNT source-asset amount, micro-units (e.g. 1000000)\n * TOON_SWAP_PUBKEY swap peer 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 swapPubkey = env['TOON_SWAP_PUBKEY'];\n const chainRecipient = env['TOON_CHAIN_RECIPIENT'];\n const pairRaw = env['TOON_SWAP_PAIR'];\n\n if (!destination || !amount || !swapPubkey || !chainRecipient || !pairRaw) {\n console.error(\n '[capstone] missing required env: TOON_SWAP_DEST, TOON_SWAP_AMOUNT, ' +\n 'TOON_SWAP_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, swapPubkey, 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":"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","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-
|
|
7
|
+
} from "./chunk-F3XAIOGQ.js";
|
|
8
8
|
import {
|
|
9
9
|
ARWEAVE_GATEWAYS,
|
|
10
10
|
ControlClient,
|
|
@@ -13,12 +13,14 @@ import {
|
|
|
13
13
|
readConfigFile,
|
|
14
14
|
spawnDaemonDetached,
|
|
15
15
|
waitForReady
|
|
16
|
-
} from "./chunk-
|
|
17
|
-
import "./chunk-
|
|
16
|
+
} from "./chunk-JFDVE4IA.js";
|
|
17
|
+
import "./chunk-FDUYHYB2.js";
|
|
18
18
|
import "./chunk-HMD5EVQI.js";
|
|
19
|
+
import "./chunk-IYPIOSEF.js";
|
|
19
20
|
import "./chunk-T3WCTWRP.js";
|
|
20
21
|
import "./chunk-3DDN4CJQ.js";
|
|
21
|
-
import "./chunk-
|
|
22
|
+
import "./chunk-V7D5HJBT.js";
|
|
23
|
+
import "./chunk-6GXZ4KRO.js";
|
|
22
24
|
import "./chunk-NL7NYWPC.js";
|
|
23
25
|
import "./chunk-I7TGLGN6.js";
|
|
24
26
|
|
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 { createHash } from 'node:crypto';\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 { ARWEAVE_GATEWAYS } from '@toon-protocol/arweave';\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 API 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 API 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 API`);\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 {\n capabilities: { tools: {}, resources: {} },\n // Travels to EVERY host in the `initialize` result (incl. claude.ai chat,\n // which never loads the Claude Code skill). Keep the render-first policy\n // here so it governs all surfaces, not just Claude Code.\n instructions:\n 'TOON is pay-to-write Nostr over Interledger; reads are free, writes ' +\n 'are paid. RENDERING IS THE DEFAULT SURFACE: when the user asks to ' +\n 'see/show/open/view/browse/render TOON events, profiles, feeds, ' +\n 'threads, channels, or balances, call toon_atoms then toon_render ' +\n '(ui://toon/app) — do NOT route TOON data to a generic ' +\n 'HTML/SVG/chart/widget tool or dump it as text. Always call toon_atoms ' +\n 'before toon_render. The rendered card IS the response: for read-only ' +\n 'views (feeds, profiles, threads, channels, balances, wallet) go ' +\n 'STRAIGHT toon_atoms → toon_render — do NOT precede a render with ' +\n 'daemon-health (toon_status), identity (toon_identity), or balance ' +\n 'preflight, and do NOT narrate the tool calls or write a status ' +\n 'report; at most a one-line caption after. On ANY intent to upload ' +\n 'media or post/publish a ' +\n 'picture/video/image (e.g. \"I want to upload\", \"publish an image\", ' +\n '\"post a photo\"), your FIRST action is toon_atoms then toon_render with ' +\n 'the media view (media-uploader atom). The uploader has an in-app FILE ' +\n 'PICKER, so do NOT ask whether a file is attached, where the image is, ' +\n 'or for a URL, and do NOT recount upload-path history or known issues — ' +\n 'just render the uploader and let the user pick a file. Writes ' +\n '(post/like/follow/upload/swap) spend a ' +\n 'payment-channel claim; surface the fee and confirm before paying. ' +\n 'When rendering, the trusted host shows the pay/consent surface. When ' +\n 'you CANNOT render (a text-only host), you MUST first call toon_status ' +\n 'to quote the exact fee + asset, tell the user the fee and that the ' +\n 'write is permanent and non-refundable (events cannot be unpublished), ' +\n 'and get explicit confirmation before calling any paid write ' +\n '(toon_publish, toon_publish_unsigned, toon_upload, toon_swap, and ' +\n 'the toon_git_* writes). toon_git_push is two-step everywhere: ' +\n 'dry_run:true first (free fee estimate), quote estimate.totalFee and ' +\n 'get explicit user confirmation, then push with confirm:true. ' +\n 'Fall back to text only on explicit request or render failure.',\n }\n );\n\n const appHtml = loadAppHtml();\n\n // CACHE-BUST the UI resource. Hosts (Claude Desktop) PREFETCH and CACHE the\n // `ui://` template keyed by its URI and do not re-fetch it across server\n // restarts — so a rebuilt bundle is never picked up while the URI is constant\n // (toon-client iframe stays stale until a reinstall). Version the URI by a hash\n // of the bundle: every rebuild yields a new URI the host has never cached, so\n // it fetches fresh; an unchanged bundle keeps the same URI (no needless churn).\n const appVersion = createHash('sha256').update(appHtml).digest('hex').slice(0, 12);\n const versionedUri = `${APP_RESOURCE_URI}?v=${appVersion}`;\n log(`serving ui resource ${versionedUri} (${appHtml.length} bytes)`);\n\n // Point toon_render's `_meta.ui.resourceUri` at the versioned URI so the host\n // fetches THIS build's bundle, not a cached one. Other tools pass through.\n const toolsWithVersionedUi = TOOL_DEFINITIONS.map((t) => {\n const ui = (t._meta?.['ui'] ?? undefined) as { resourceUri?: string } | undefined;\n if (!ui?.resourceUri) return t;\n return { ...t, _meta: { ...t._meta, ui: { ...ui, resourceUri: versionedUri } } };\n });\n\n server.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: toolsWithVersionedUi,\n }));\n\n // The MCP-app UI resource the host renders for toon_render results.\n //\n // CSP: the rendered feed/uploader shows media stored on Arweave. Without an\n // explicit `resourceDomains`, the host iframe's default `img-src`/`media-src`\n // blocks those gateways and images never load (toon-client#127). Advertise the\n // Arweave gateways as both resource (img/media/static) and connect origins.\n // Per the ext-apps spec the host reads `_meta.ui.csp` from the `resources/read`\n // content item, with the `resources/list` entry as fallback — so set it on both.\n //\n // CRUCIAL: ar.io / arweave.net gateways serve a 302 from the apex\n // (`https://arweave.net/<txId>`) to a per-tx SANDBOX SUBDOMAIN\n // (`https://<base32>.arweave.net/<txId>`). CSP `img-src` is checked against the\n // REDIRECT TARGET, so an apex-only allowlist still blocks the image. Allow both\n // the apex (initial request) and a wildcard subdomain (where the bytes load).\n const arweaveCspDomains = ARWEAVE_GATEWAYS.flatMap((gateway) => {\n try {\n const host = new URL(gateway).host;\n return [gateway, `https://*.${host}`];\n } catch {\n return [gateway];\n }\n });\n const APP_CSP = {\n csp: {\n resourceDomains: arweaveCspDomains,\n connectDomains: arweaveCspDomains,\n },\n };\n\n server.setRequestHandler(ListResourcesRequestSchema, async () => ({\n resources: [\n { uri: versionedUri, name: 'TOON', mimeType: APP_MIME, _meta: { ui: APP_CSP } },\n ],\n }));\n\n server.setRequestHandler(ReadResourceRequestSchema, async (request) => {\n // Accept the versioned URI and the bare base (in case a host strips the\n // `?v=` query before re-reading) — both resolve to this build's bundle.\n if (!request.params.uri.startsWith(APP_RESOURCE_URI)) {\n throw new Error(`Unknown resource: ${request.params.uri}`);\n }\n return {\n contents: [\n { uri: request.params.uri, mimeType: APP_MIME, text: appHtml, _meta: { ui: APP_CSP } },\n ],\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 { createHash } from 'node:crypto';\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 { ARWEAVE_GATEWAYS } from '@toon-protocol/arweave';\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 API 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 API 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 API`);\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 {\n capabilities: { tools: {}, resources: {} },\n // Travels to EVERY host in the `initialize` result (incl. claude.ai chat,\n // which never loads the Claude Code skill). Keep the render-first policy\n // here so it governs all surfaces, not just Claude Code.\n instructions:\n 'TOON is pay-to-write Nostr over Interledger; reads are free, writes ' +\n 'are paid. RENDERING IS THE DEFAULT SURFACE: when the user asks to ' +\n 'see/show/open/view/browse/render TOON events, profiles, feeds, ' +\n 'threads, channels, or balances, call toon_atoms then toon_render ' +\n '(ui://toon/app) — do NOT route TOON data to a generic ' +\n 'HTML/SVG/chart/widget tool or dump it as text. Always call toon_atoms ' +\n 'before toon_render. The rendered card IS the response: for read-only ' +\n 'views (feeds, profiles, threads, channels, balances, wallet) go ' +\n 'STRAIGHT toon_atoms → toon_render — do NOT precede a render with ' +\n 'daemon-health (toon_status), identity (toon_identity), or balance ' +\n 'preflight, and do NOT narrate the tool calls or write a status ' +\n 'report; at most a one-line caption after. On ANY intent to upload ' +\n 'media or post/publish a ' +\n 'picture/video/image (e.g. \"I want to upload\", \"publish an image\", ' +\n '\"post a photo\"), your FIRST action is toon_atoms then toon_render with ' +\n 'the media view (media-uploader atom). The uploader has an in-app FILE ' +\n 'PICKER, so do NOT ask whether a file is attached, where the image is, ' +\n 'or for a URL, and do NOT recount upload-path history or known issues — ' +\n 'just render the uploader and let the user pick a file. Writes ' +\n '(post/like/follow/upload/swap) spend a ' +\n 'payment-channel claim; surface the fee and confirm before paying. ' +\n 'When rendering, the trusted host shows the pay/consent surface. When ' +\n 'you CANNOT render (a text-only host), you MUST first call toon_status ' +\n 'to quote the exact fee + asset, tell the user the fee and that the ' +\n 'write is permanent and non-refundable (events cannot be unpublished), ' +\n 'and get explicit confirmation before calling any paid write ' +\n '(toon_publish, toon_publish_unsigned, toon_upload, toon_swap, and ' +\n 'the toon_git_* writes). toon_git_push is two-step everywhere: ' +\n 'dry_run:true first (free fee estimate), quote estimate.totalFee and ' +\n 'get explicit user confirmation, then push with confirm:true. ' +\n 'Fall back to text only on explicit request or render failure.',\n }\n );\n\n const appHtml = loadAppHtml();\n\n // CACHE-BUST the UI resource. Hosts (Claude Desktop) PREFETCH and CACHE the\n // `ui://` template keyed by its URI and do not re-fetch it across server\n // restarts — so a rebuilt bundle is never picked up while the URI is constant\n // (toon-client iframe stays stale until a reinstall). Version the URI by a hash\n // of the bundle: every rebuild yields a new URI the host has never cached, so\n // it fetches fresh; an unchanged bundle keeps the same URI (no needless churn).\n const appVersion = createHash('sha256').update(appHtml).digest('hex').slice(0, 12);\n const versionedUri = `${APP_RESOURCE_URI}?v=${appVersion}`;\n log(`serving ui resource ${versionedUri} (${appHtml.length} bytes)`);\n\n // Point toon_render's `_meta.ui.resourceUri` at the versioned URI so the host\n // fetches THIS build's bundle, not a cached one. Other tools pass through.\n const toolsWithVersionedUi = TOOL_DEFINITIONS.map((t) => {\n const ui = (t._meta?.['ui'] ?? undefined) as { resourceUri?: string } | undefined;\n if (!ui?.resourceUri) return t;\n return { ...t, _meta: { ...t._meta, ui: { ...ui, resourceUri: versionedUri } } };\n });\n\n server.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: toolsWithVersionedUi,\n }));\n\n // The MCP-app UI resource the host renders for toon_render results.\n //\n // CSP: the rendered feed/uploader shows media stored on Arweave. Without an\n // explicit `resourceDomains`, the host iframe's default `img-src`/`media-src`\n // blocks those gateways and images never load (toon-client#127). Advertise the\n // Arweave gateways as both resource (img/media/static) and connect origins.\n // Per the ext-apps spec the host reads `_meta.ui.csp` from the `resources/read`\n // content item, with the `resources/list` entry as fallback — so set it on both.\n //\n // CRUCIAL: ar.io / arweave.net gateways serve a 302 from the apex\n // (`https://arweave.net/<txId>`) to a per-tx SANDBOX SUBDOMAIN\n // (`https://<base32>.arweave.net/<txId>`). CSP `img-src` is checked against the\n // REDIRECT TARGET, so an apex-only allowlist still blocks the image. Allow both\n // the apex (initial request) and a wildcard subdomain (where the bytes load).\n const arweaveCspDomains = ARWEAVE_GATEWAYS.flatMap((gateway) => {\n try {\n const host = new URL(gateway).host;\n return [gateway, `https://*.${host}`];\n } catch {\n return [gateway];\n }\n });\n const APP_CSP = {\n csp: {\n resourceDomains: arweaveCspDomains,\n connectDomains: arweaveCspDomains,\n },\n };\n\n server.setRequestHandler(ListResourcesRequestSchema, async () => ({\n resources: [\n { uri: versionedUri, name: 'TOON', mimeType: APP_MIME, _meta: { ui: APP_CSP } },\n ],\n }));\n\n server.setRequestHandler(ReadResourceRequestSchema, async (request) => {\n // Accept the versioned URI and the bare base (in case a host strips the\n // `?v=` query before re-reading) — both resolve to this build's bundle.\n if (!request.params.uri.startsWith(APP_RESOURCE_URI)) {\n throw new Error(`Unknown resource: ${request.params.uri}`);\n }\n return {\n contents: [\n { uri: request.params.uri, mimeType: APP_MIME, text: appHtml, _meta: { ui: APP_CSP } },\n ],\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":[]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createRequire as __cr } from 'module'; const require = __cr(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
deployMinaChannelZkApp,
|
|
4
|
+
ensureOwnedMinaZkApp
|
|
5
|
+
} from "./chunk-IYPIOSEF.js";
|
|
6
|
+
import "./chunk-6GXZ4KRO.js";
|
|
7
|
+
import "./chunk-I7TGLGN6.js";
|
|
8
|
+
export {
|
|
9
|
+
deployMinaChannelZkApp,
|
|
10
|
+
ensureOwnedMinaZkApp
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=mina-channel-deploy-CO2LMPPB-H2N5FX4G.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -4,10 +4,10 @@ import {
|
|
|
4
4
|
__esm,
|
|
5
5
|
__export,
|
|
6
6
|
__require
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-FDUYHYB2.js";
|
|
8
8
|
import "./chunk-I7TGLGN6.js";
|
|
9
9
|
|
|
10
|
-
// ../../node_modules/.pnpm/@toon-protocol+sdk@3.1.
|
|
10
|
+
// ../../node_modules/.pnpm/@toon-protocol+sdk@3.1.2_mina-signer@3.1.0_typescript@5.9.3/node_modules/@toon-protocol/sdk/dist/node-WPA2UDEH.js
|
|
11
11
|
var require_bignumber = __commonJS({
|
|
12
12
|
"../../node_modules/.pnpm/bignumber.js@9.3.1/node_modules/bignumber.js/bignumber.js"(exports, module) {
|
|
13
13
|
"use strict";
|
|
@@ -8144,4 +8144,4 @@ var node_WPA2UDEH_default = require_node2();
|
|
|
8144
8144
|
export {
|
|
8145
8145
|
node_WPA2UDEH_default as default
|
|
8146
8146
|
};
|
|
8147
|
-
//# sourceMappingURL=node-WPA2UDEH-
|
|
8147
|
+
//# sourceMappingURL=node-WPA2UDEH-HZP3B4FH.js.map
|