@toon-protocol/client-mcp 0.10.8 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/app/index.html +86 -81
- package/dist/{chunk-3ZBC2HUB.js → chunk-CMGJ3NFT.js} +17 -9
- package/dist/chunk-CMGJ3NFT.js.map +1 -0
- package/dist/{chunk-XFRMAETF.js → chunk-JPQ4VCCF.js} +73 -17
- package/dist/chunk-JPQ4VCCF.js.map +1 -0
- package/dist/{chunk-ADBNZA5O.js → chunk-KVK6OZVD.js} +140 -19
- package/dist/chunk-KVK6OZVD.js.map +1 -0
- package/dist/daemon.js +2 -2
- package/dist/daemon.js.map +1 -1
- package/dist/index.d.ts +81 -18
- package/dist/index.js +3 -3
- package/dist/mcp.js +2 -2
- package/package.json +3 -3
- package/dist/chunk-3ZBC2HUB.js.map +0 -1
- package/dist/chunk-ADBNZA5O.js.map +0 -1
- package/dist/chunk-XFRMAETF.js.map +0 -1
|
@@ -2,7 +2,7 @@ import { createRequire as __cr } from 'module'; const require = __cr(import.meta
|
|
|
2
2
|
import {
|
|
3
3
|
ControlApiError,
|
|
4
4
|
DaemonUnreachableError
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-CMGJ3NFT.js";
|
|
6
6
|
|
|
7
7
|
// ../views/dist/tool-names.js
|
|
8
8
|
var PUBLISH_TOOL = "toon_publish_unsigned";
|
|
@@ -298,9 +298,13 @@ var ATOM_CATALOG = [
|
|
|
298
298
|
},
|
|
299
299
|
{
|
|
300
300
|
id: "composer",
|
|
301
|
-
description: '
|
|
302
|
-
writes: [{ name: "toon_publish_unsigned" }],
|
|
303
|
-
propsSchema: {
|
|
301
|
+
description: 'The DEFAULT post composer: write a kind:1 note and OPTIONALLY attach one media/file (paperclip) \u2014 text-only posts publish via the "post" action (toon_publish_unsigned); a post WITH media publishes via the "upload" action (toon_upload, kind:1) so the file is carried as NIP-92 imeta and renders inline in the feed. Wire BOTH actions. Use this for "make a post" (with or without a picture/video); use media-uploader only when the user just wants to upload a file with no note.',
|
|
302
|
+
writes: [{ name: "toon_publish_unsigned" }, { name: "toon_upload", spendy: true }],
|
|
303
|
+
propsSchema: {
|
|
304
|
+
placeholder: "string",
|
|
305
|
+
label: "string",
|
|
306
|
+
accept: "string (optional MIME filter for the attach picker, e.g. 'image/*')"
|
|
307
|
+
}
|
|
304
308
|
},
|
|
305
309
|
{
|
|
306
310
|
id: "pay-confirm",
|
|
@@ -316,9 +320,13 @@ var ATOM_CATALOG = [
|
|
|
316
320
|
},
|
|
317
321
|
{
|
|
318
322
|
id: "media-uploader",
|
|
319
|
-
description: "
|
|
323
|
+
description: "Compose & publish a media POST: pick a file (image, video, pdf, audio, \u2026) via the in-app picker, preview it, add an OPTIONAL caption/text, then Publish \u2014 uploads to Arweave and publishes a reference event in one paid write. The caption becomes the event content, so an image/video + text is a real post (kind:20 picture, 21 video, 1063 NIP-94 file for everything else \u2014 chosen automatically from the MIME). Spendy. This IS the post-with-media path; use it whenever the user wants to post/upload a picture, video, or file with or without a caption. No URL needed.",
|
|
320
324
|
writes: [{ name: "toon_upload", spendy: true }],
|
|
321
|
-
propsSchema: {
|
|
325
|
+
propsSchema: {
|
|
326
|
+
label: "string",
|
|
327
|
+
accept: "string (optional MIME filter, e.g. 'image/*'; default any)",
|
|
328
|
+
captionPlaceholder: "string (optional placeholder for the caption field)"
|
|
329
|
+
}
|
|
322
330
|
},
|
|
323
331
|
// forge
|
|
324
332
|
{ id: "repo-card", description: "NIP-34 kind:30617 repository card.", kinds: [30617] },
|
|
@@ -727,7 +735,7 @@ var TOOL_DEFINITIONS = [
|
|
|
727
735
|
},
|
|
728
736
|
destination: {
|
|
729
737
|
type: "string",
|
|
730
|
-
description: "Optional ILP destination override (default: the apex/
|
|
738
|
+
description: "Optional ILP destination override (default: the apex/relay)."
|
|
731
739
|
},
|
|
732
740
|
fee: {
|
|
733
741
|
type: "string",
|
|
@@ -829,7 +837,7 @@ var TOOL_DEFINITIONS = [
|
|
|
829
837
|
},
|
|
830
838
|
{
|
|
831
839
|
name: "toon_subscribe",
|
|
832
|
-
description: "Free read: register a persistent
|
|
840
|
+
description: "Free read: register a persistent relay subscription with NIP-01 filter(s). Returns a subscription id to drain with toon_read.",
|
|
833
841
|
inputSchema: {
|
|
834
842
|
type: "object",
|
|
835
843
|
properties: {
|
|
@@ -1025,6 +1033,21 @@ var TOOL_DEFINITIONS = [
|
|
|
1025
1033
|
additionalProperties: false
|
|
1026
1034
|
}
|
|
1027
1035
|
},
|
|
1036
|
+
{
|
|
1037
|
+
name: "toon_fund_status",
|
|
1038
|
+
description: "Check the status of async faucet drips submitted via toon_fund_wallet. Each job reports `status` (`pending` | `success` | `error`), the chain/address, and timestamps \u2014 so an agent can poll for settlement without re-dripping. With no `chain` it returns every tracked job.",
|
|
1039
|
+
inputSchema: {
|
|
1040
|
+
type: "object",
|
|
1041
|
+
properties: {
|
|
1042
|
+
chain: {
|
|
1043
|
+
type: "string",
|
|
1044
|
+
enum: ["evm", "solana", "mina"],
|
|
1045
|
+
description: "Only report the drip job for this chain (default: all)."
|
|
1046
|
+
}
|
|
1047
|
+
},
|
|
1048
|
+
additionalProperties: false
|
|
1049
|
+
}
|
|
1050
|
+
},
|
|
1028
1051
|
{
|
|
1029
1052
|
name: "toon_targets",
|
|
1030
1053
|
description: "List every registered target: relays (read sources, with connection + buffered-event status) and apexes (BTP write targets, with ready/channel status). The TOON client is 1-to-many \u2014 many apexes to write through, many relays to read from. To display these to the user, render them via toon_render \u2014 not a generic widget or plain text.",
|
|
@@ -1190,9 +1213,9 @@ async function dispatchTool(client, name, args) {
|
|
|
1190
1213
|
)
|
|
1191
1214
|
);
|
|
1192
1215
|
case "toon_channels":
|
|
1193
|
-
return ok(await client.channels());
|
|
1216
|
+
return ok(wrapList("channels", await client.channels()));
|
|
1194
1217
|
case "toon_balances":
|
|
1195
|
-
return ok(await client.balances());
|
|
1218
|
+
return ok(wrapList("balances", await client.balances()));
|
|
1196
1219
|
case "toon_channel_deposit":
|
|
1197
1220
|
return ok(
|
|
1198
1221
|
await client.depositToChannel({
|
|
@@ -1225,12 +1248,21 @@ async function dispatchTool(client, name, args) {
|
|
|
1225
1248
|
};
|
|
1226
1249
|
return ok(await client.httpFetchPaid(req));
|
|
1227
1250
|
}
|
|
1228
|
-
case "toon_fund_wallet":
|
|
1251
|
+
case "toon_fund_wallet": {
|
|
1252
|
+
const job = await client.fundWallet({
|
|
1253
|
+
...typeof args["chain"] === "string" ? { chain: args["chain"] } : {},
|
|
1254
|
+
...typeof args["address"] === "string" ? { address: args["address"] } : {}
|
|
1255
|
+
});
|
|
1256
|
+
return okStructured(
|
|
1257
|
+
`Drip submitted for ${job.chain} to ${job.address}. Funds appear in balances once the faucet settles (EVM/Solana ~30s, Mina ~1-2 min). Re-check with toon_balances, or poll toon_fund_status.`,
|
|
1258
|
+
job
|
|
1259
|
+
);
|
|
1260
|
+
}
|
|
1261
|
+
case "toon_fund_status":
|
|
1229
1262
|
return ok(
|
|
1230
|
-
await client.
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
})
|
|
1263
|
+
await client.fundStatus(
|
|
1264
|
+
typeof args["chain"] === "string" ? args["chain"] : void 0
|
|
1265
|
+
)
|
|
1234
1266
|
);
|
|
1235
1267
|
case "toon_targets":
|
|
1236
1268
|
return ok(await client.targets());
|
|
@@ -1259,6 +1291,16 @@ async function dispatchTool(client, name, args) {
|
|
|
1259
1291
|
return err(`Unknown tool: ${name}`);
|
|
1260
1292
|
}
|
|
1261
1293
|
} catch (e) {
|
|
1294
|
+
if (e instanceof ControlApiError && e.status === 504 && name === "toon_balances") {
|
|
1295
|
+
return err(
|
|
1296
|
+
`${e.detail ?? e.message} \u2014 the balances control plane (:8787 GET /balances) stalled reading on-chain balances; the relay and apex are unaffected. Retry shortly.`
|
|
1297
|
+
);
|
|
1298
|
+
}
|
|
1299
|
+
if (e instanceof ControlApiError && e.status === 504 && name === "toon_fund_wallet") {
|
|
1300
|
+
return err(
|
|
1301
|
+
`${e.detail ?? e.message} \u2014 the faucet drip did not return in time (the mina faucet settles slowly); the relay and apex are unaffected. The funds may still land \u2014 re-check balances shortly, or retry.`
|
|
1302
|
+
);
|
|
1303
|
+
}
|
|
1262
1304
|
if (e instanceof ControlApiError && e.status === 504) {
|
|
1263
1305
|
return err(
|
|
1264
1306
|
`${e.detail ?? e.message} \u2014 retry once the relay is reachable and the apex is online.`
|
|
@@ -1280,8 +1322,22 @@ async function dispatchTool(client, name, args) {
|
|
|
1280
1322
|
return err(e instanceof Error ? e.message : String(e));
|
|
1281
1323
|
}
|
|
1282
1324
|
}
|
|
1325
|
+
function wrapList(key, res) {
|
|
1326
|
+
if (Array.isArray(res)) return { [key]: res };
|
|
1327
|
+
if (res !== null && typeof res === "object") {
|
|
1328
|
+
const existing = res[key];
|
|
1329
|
+
if (Array.isArray(existing)) return res;
|
|
1330
|
+
}
|
|
1331
|
+
return { [key]: [] };
|
|
1332
|
+
}
|
|
1283
1333
|
function ok(data) {
|
|
1284
|
-
|
|
1334
|
+
const result = {
|
|
1335
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
1336
|
+
};
|
|
1337
|
+
if (data !== null && typeof data === "object" && !Array.isArray(data)) {
|
|
1338
|
+
result.structuredContent = data;
|
|
1339
|
+
}
|
|
1340
|
+
return result;
|
|
1285
1341
|
}
|
|
1286
1342
|
function okStructured(text, structuredContent) {
|
|
1287
1343
|
return { content: [{ type: "text", text }], structuredContent };
|
|
@@ -1301,4 +1357,4 @@ export {
|
|
|
1301
1357
|
TOOL_DEFINITIONS,
|
|
1302
1358
|
dispatchTool
|
|
1303
1359
|
};
|
|
1304
|
-
//# sourceMappingURL=chunk-
|
|
1360
|
+
//# sourceMappingURL=chunk-JPQ4VCCF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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';\n/** Pre-open a payment channel (daemon signs/settles; UI never holds keys). */\nexport const OPEN_CHANNEL_TOOL = 'toon_open_channel';\n/** Spendy: run a cross-asset swap; daemon signs the source claim, returns a target claim. */\nexport const SWAP_TOOL = 'toon_swap';\n/** Read-only: report the current pay-to-write fee + settlement chain (no payment). */\nexport const STATUS_TOOL = 'toon_status';\n/** Read-only: list tracked payment channels with nonce + cumulative + available balance. */\nexport const CHANNELS_TOOL = 'toon_channels';\n/** Read-only: on-chain wallet token balances per configured chain. */\nexport const BALANCES_TOOL = 'toon_balances';\n/** Devnet: drip faucet test funds to a wallet address (receives funds; not spendy). */\nexport const FUND_WALLET_TOOL = 'toon_fund_wallet';\n/** Spendy: deposit additional collateral into an open payment channel (on-chain). */\nexport const CHANNEL_DEPOSIT_TOOL = 'toon_channel_deposit';\n/** Spendy: close a channel to begin the settlement grace period (withdraw, step 1). */\nexport const CHANNEL_CLOSE_TOOL = 'toon_channel_close';\n/** Spendy: settle a closed channel after its grace period to release collateral (step 2). */\nexport const CHANNEL_SETTLE_TOOL = 'toon_channel_settle';\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([\n PUBLISH_TOOL,\n UPLOAD_TOOL,\n OPEN_CHANNEL_TOOL,\n SWAP_TOOL,\n // Devnet faucet — a write-class action (it mutates wallet funds) but `spendy:\n // false` since it *receives* funds; routed here so the validator permits it.\n FUND_WALLET_TOOL,\n // Channel deposit/close/settle — spendy (move on-chain collateral).\n CHANNEL_DEPOSIT_TOOL,\n CHANNEL_CLOSE_TOOL,\n CHANNEL_SETTLE_TOOL,\n]);\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/**\n * Social feed, optionally scoped to authors: kind:1 text notes plus inline media\n * — NIP-68 pictures (20) / video (21,22) and NIP-94 file metadata (1063). The\n * feed binds with `kindAuto`, so each kind renders via its own atom (note-card\n * for kind:1, media-embed for the media kinds), interleaved newest-first.\n */\nexport function buildFeedFilter(authors?: string[], limit = 100): NostrFilter {\n const f: NostrFilter = { kinds: [1, 20, 21, 22, 1063], 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 * Order events by `created_at` before rendering. Defaults to `'desc'`\n * (newest-first) so feeds are reverse-chronological; threads can opt into\n * `'asc'` (oldest-first). Ties break on `id` for a stable, deterministic\n * order regardless of relay return order or buffered/streamed merge.\n */\n sort?: 'asc' | 'desc';\n}\n\n/** Binds a UI affordance to a write tool call (always `toon_publish_unsigned` / `toon_upload`). */\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\n/* eslint-disable @typescript-eslint/no-non-null-assertion -- bounded array indices in a hot loop */\nfunction editDistance(a: string, b: string): number {\n const n = b.length;\n const row: number[] = Array.from({ length: n + 1 }, (_, j) => j);\n for (let i = 1; i <= a.length; i++) {\n let prev = row[0]!;\n row[0] = i;\n for (let j = 1; j <= n; j++) {\n const tmp = row[j]!;\n row[j] = a[i - 1] === b[j - 1] ? prev : 1 + Math.min(prev, row[j - 1]!, row[j]!);\n prev = tmp;\n }\n }\n return row[n]!;\n}\n/* eslint-enable @typescript-eslint/no-non-null-assertion */\n\nfunction nearestAtom(name: string, allowed: ReadonlySet<string>): string | undefined {\n if (name.length > 64) return undefined; // no real atom name exceeds this; avoid O(m×n) on untrusted input\n const lower = name.toLowerCase();\n let best: string | undefined;\n let bestDist = Infinity;\n for (const id of allowed) {\n const d = editDistance(lower, id.toLowerCase());\n if (d < bestDist) { bestDist = d; best = id; }\n }\n return bestDist <= Math.floor(name.length / 2) ? best : undefined;\n}\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 const atomName = node['atom'] as string;\n const suggestion = nearestAtom(atomName, allowedAtoms);\n const hint = suggestion ? ` Did you mean \"${suggestion}\"?` : '';\n const validList = [...allowedAtoms].join(', ');\n errors.push(\n `${path}.atom: unknown atom \"${atomName}\".${hint}\\n` +\n ` Valid atoms: ${validList}.\\n` +\n ` See toon_atoms for full vocabulary.`\n );\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 const KNOWN_BIND_KEYS = new Set(['query', 'eventId', 'kindAuto', 'sort']);\n for (const key of Object.keys(bind)) {\n if (!KNOWN_BIND_KEYS.has(key)) {\n errors.push(`${path}.bind.${key}: unknown bind key (use \"query\" for a NIP-01 filter)`);\n }\n }\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 if (bind['sort'] !== undefined && bind['sort'] !== 'asc' && bind['sort'] !== 'desc') {\n errors.push(`${path}.bind.sort: must be \"asc\" or \"desc\"`);\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\nimport { FUND_WALLET_TOOL, OPEN_CHANNEL_TOOL, SWAP_TOOL } from './tool-names.js';\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 {\n id: 'note-card',\n description:\n 'NIP-01 kind:1 text note, rendered as an X-style post (author header with ' +\n 'a Follow button, body + inline media, action bar). Optional \"reply\" ' +\n '(kind:1), \"react\"/Like (kind:7 \"+\"), and \"follow\" (kind:3) actions — all ' +\n 'paid writes via toon_publish_unsigned.',\n kinds: [1],\n writes: [{ name: 'toon_publish_unsigned' }],\n },\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:\n 'The DEFAULT post composer: write a kind:1 note and OPTIONALLY attach one ' +\n 'media/file (paperclip) — text-only posts publish via the \"post\" action ' +\n '(toon_publish_unsigned); a post WITH media publishes via the \"upload\" ' +\n 'action (toon_upload, kind:1) so the file is carried as NIP-92 imeta and ' +\n 'renders inline in the feed. Wire BOTH actions. Use this for \"make a post\" ' +\n '(with or without a picture/video); use media-uploader only when the user ' +\n 'just wants to upload a file with no note.',\n writes: [{ name: 'toon_publish_unsigned' }, { name: 'toon_upload', spendy: true }],\n propsSchema: {\n placeholder: 'string',\n label: 'string',\n accept: \"string (optional MIME filter for the attach picker, e.g. 'image/*')\",\n },\n },\n {\n id: 'pay-confirm',\n description:\n 'Pay-to-write moment: compose a note, then a confirm step shows the live ' +\n 'fee + settlement chain (from toon_status) with Confirm/Cancel. Confirm ' +\n 'fires the \"confirm\" action (toon_publish_unsigned) and renders a receipt ' +\n 'with the published eventId — \"the message is the money\".',\n writes: [{ name: 'toon_publish_unsigned' }],\n propsSchema: { placeholder: 'string', label: 'string (compose button label)' },\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:\n 'Compose & publish a media POST: pick a file (image, video, pdf, audio, …) ' +\n 'via the in-app picker, preview it, add an OPTIONAL caption/text, then ' +\n 'Publish — uploads to Arweave and publishes a reference event in one paid ' +\n 'write. The caption becomes the event content, so an image/video + text is ' +\n 'a real post (kind:20 picture, 21 video, 1063 NIP-94 file for everything ' +\n 'else — chosen automatically from the MIME). Spendy. This IS the ' +\n 'post-with-media path; use it whenever the user wants to post/upload a ' +\n 'picture, video, or file with or without a caption. No URL needed.',\n writes: [{ name: 'toon_upload', spendy: true }],\n propsSchema: {\n label: 'string',\n accept: \"string (optional MIME filter, e.g. 'image/*'; default any)\",\n captionPlaceholder: 'string (optional placeholder for the caption field)',\n },\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 // defi\n {\n id: 'channel-card',\n description:\n 'Read-only: shows tracked payment channels (channelId, nonce, cumulative ' +\n 'amount). Optional \"open\" action pre-opens a channel.',\n kinds: [],\n writes: [{ name: OPEN_CHANNEL_TOOL }],\n propsSchema: { destination: 'string (optional ILP destination for \"open\")' },\n },\n {\n id: 'swap-form',\n description:\n 'Interactive: collects swap params and fires a cross-asset swap. Spendy.',\n writes: [{ name: SWAP_TOOL, spendy: true }],\n propsSchema: {\n destination: 'string (swap peer ILP destination)',\n swapPubkey: 'string (swap peer 64-char hex Nostr pubkey)',\n pair: 'SwapPair ({ from, to, rate, … })',\n chainRecipient: 'string (payout address on pair.to.chain)',\n label: 'string (submit button label)',\n },\n },\n {\n id: 'settlement-receipt',\n description:\n 'Read-only render of a SwapResponse / SwapClaim[] (target amount, chain ' +\n 'claim, channelId, tx/claim id, nonce). No writes.',\n },\n\n // onboard\n {\n id: 'onboard-card',\n description:\n 'Onboarding / get-started card: walks a new user through claiming an ' +\n 'identity and opening a payment channel before they pay-to-write. Optional ' +\n '\"publish\" action publishes their initial kind:0 profile.',\n writes: [{ name: 'toon_publish_unsigned' }],\n propsSchema: {\n pubkey: 'string (the user’s Nostr pubkey, hex)',\n steps: 'string[] (optional checklist of get-started steps)',\n label: 'string (optional publish-profile button label)',\n },\n },\n {\n id: 'profile-editor',\n description:\n 'Edit/create a NIP-01 kind:0 profile: input fields for name, display_name, ' +\n 'picture (URL), about, and nip05, serialized into the kind:0 content JSON ' +\n 'and published via toon_publish_unsigned ({ kind: 0, content }) through a ' +\n 'pay-to-write confirm. Bind a kind:0 event to pre-fill the form (unknown ' +\n 'fields are preserved on republish). The \"publish\" action fires the write.',\n writes: [{ name: 'toon_publish_unsigned' }],\n propsSchema: {\n label: 'string (optional save-button label)',\n },\n },\n\n // content — generic, props-driven primitives for ANY structured (non-event)\n // data: daemon status, write targets, balances, identity. Compose these inside\n // layout atoms instead of falling back to plain text. No event kinds.\n {\n id: 'heading',\n description: 'A heading/title for a section of arbitrary content.',\n propsSchema: { text: 'string', level: '1 | 2 | 3 (heading level, default 1)' },\n },\n {\n id: 'text',\n description: 'A paragraph / label of plain text.',\n propsSchema: { text: 'string', muted: 'boolean (render dimmed, optional)' },\n },\n {\n id: 'stat',\n description: 'A labeled metric (KPI) with an optional status colour.',\n propsSchema: {\n label: 'string',\n value: 'string | number',\n tone: \"'default' | 'success' | 'warn' | 'danger' (optional)\",\n },\n },\n {\n id: 'key-value',\n description: 'A definition list of aligned label → value rows (for details / identity / targets).',\n propsSchema: { rows: '{ label: string, value: string | number }[]' },\n },\n {\n id: 'badge',\n description: 'A small status pill.',\n propsSchema: {\n label: 'string',\n tone: \"'default' | 'success' | 'warn' | 'danger' (optional)\",\n },\n },\n\n // status — daemon health dashboard\n {\n id: 'client-status',\n description:\n 'Daemon status dashboard: reads live toon_status and renders the ready/' +\n 'bootstrapping state (badge), uptime, settlement chain + fee, relay (url, ' +\n 'connected, buffered, subscriptions), transport, per-chain readiness, and ' +\n 'identity (npub + chain addresses). No props; use to answer \"show me my status\".',\n },\n\n // wallet — manage wallets + payment channels (live reads from toon_balances /\n // toon_channels; faucet via toon_fund_wallet). No key material in the UI.\n {\n id: 'wallet-overview',\n description:\n 'Wallet dashboard: per-chain address (with copy-to-share) from the live ' +\n 'identity, enriched with on-chain token balance (toon_balances) when ' +\n 'available. Optional devnet \"Fund\" action drips faucet test funds (receives, ' +\n 'not a spend). Use to answer \"show my wallet\" / \"what is my address\".',\n writes: [{ name: FUND_WALLET_TOOL }],\n },\n {\n id: 'channel-list',\n description:\n 'Live list of tracked payment channels (read from toon_channels): channelId, ' +\n 'nonce watermark, and available (spendable) balance / locked deposit. The ' +\n 'read variant of channel-card; use to answer \"show my channels\".',\n },\n {\n id: 'deposit-form',\n description:\n 'Deposit additional on-chain collateral into an open channel: pick a channel ' +\n '(from toon_channels) + an amount, then a spendy signed deposit via ' +\n 'toon_channel_deposit. Shows the new deposit total on success. EVM today.',\n writes: [{ name: 'toon_channel_deposit', spendy: true }],\n },\n {\n id: 'withdraw-flow',\n description:\n 'Withdraw collateral from a channel: close → wait the settlement grace ' +\n 'period → settle. A stepper + live countdown to settleableAt; Settle stays ' +\n 'disabled until the grace period elapses. Two spendy signed on-chain txs ' +\n '(toon_channel_close, toon_channel_settle). EVM today.',\n writes: [\n { name: 'toon_channel_close', spendy: true },\n { name: 'toon_channel_settle', spendy: true },\n ],\n },\n\n // loading — placeholders the agent renders WHILE it works out the real view.\n // Render one of these as a first toon_render, then replace with the finished\n // ViewSpec once the journey is resolved. No data binds, no kinds.\n {\n id: 'skeleton',\n description:\n 'Pulsing placeholder silhouette to render while the real view loads. ' +\n 'Use as an immediate first render, then replace with the finished view.',\n propsSchema: {\n variant: \"'lines' | 'avatar' | 'card' (default 'lines')\",\n lines: 'number (line count for the lines variant, default 3)',\n width: 'string (optional max-width, e.g. \"24rem\")',\n },\n },\n {\n id: 'loading',\n description:\n 'A spinner with an optional status line. Set `message` to narrate what ' +\n 'you are doing (e.g. \"Resolving balances…\") while you compute the real view.',\n propsSchema: {\n message: 'string (status line, optional)',\n size: \"'sm' | 'md' | 'lg' (optional)\",\n },\n },\n {\n id: 'progress-steps',\n description:\n 'A numbered stepper for a multi-step journey (e.g. Close → Wait → Settle). ' +\n 'Steps before `active` show as done, `active` is highlighted, later steps ' +\n 'are pending; mark a failed step with `error`.',\n propsSchema: {\n steps: 'string[] (step labels in order)',\n active: 'number (0-based index of the current step)',\n error: 'number (0-based index of a failed step, optional)',\n },\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 { OPEN_CHANNEL_TOOL, PUBLISH_TOOL, SWAP_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 {\n atom: 'note-card',\n bind: { query: buildFeedFilter(undefined, 50), kindAuto: true },\n // Surface the engagement affordances NoteRow supports. Each is a paid\n // write (kind:7 like, kind:3 follow) routed through the\n // unsigned-publish → pay-to-write flow. Reply is intentionally omitted\n // until there's a reply-composer — firing it bare would publish an\n // empty note.\n actions: {\n react: {\n tool: PUBLISH_TOOL,\n args: { kind: 7 },\n spendy: true,\n confirmLabel: 'Like settles a per-event channel fee. Continue?',\n },\n follow: {\n tool: PUBLISH_TOOL,\n args: { kind: 3 },\n spendy: true,\n confirmLabel: 'Follow settles a per-event channel fee. Continue?',\n },\n },\n },\n ],\n },\n };\n}\n\n/**\n * The headline pay-to-write journey: a `pay-confirm` atom that previews the\n * note, shows the live fee + settlement chain (pulled from `toon_status`), and\n * on Confirm fires `toon_publish_unsigned` and renders the receipt with the real\n * eventId — \"the message is the money\". Below it, the live note feed so the\n * just-posted note shows up.\n */\nexport function payToWriteView(): ViewSpec {\n return {\n title: 'Pay to write',\n root: {\n atom: 'stack',\n children: [\n {\n atom: 'pay-confirm',\n props: { label: 'Pay to post' },\n actions: { confirm: { tool: PUBLISH_TOOL, args: { kind: 1 } } },\n },\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 // Replies read oldest-first so the conversation flows top-to-bottom.\n { atom: 'note-card', bind: { query: buildCommentFilter([rootId]), kindAuto: true, sort: 'asc' } },\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\n/** An onboarding journey: get-started card + the new user's profile header. */\nexport function onboardView(pubkey: string): ViewSpec {\n return {\n title: 'Get started',\n root: {\n atom: 'stack',\n children: [\n {\n atom: 'onboard-card',\n props: { pubkey, label: 'Publish profile' },\n actions: { publish: { tool: PUBLISH_TOOL, args: { kind: 0 } } },\n },\n { atom: 'profile-header', bind: { query: buildProfileFilter([pubkey]) } },\n ],\n },\n };\n}\n\n/**\n * A profile editor: input fields for name/picture/about (pre-filled from the\n * user's existing kind:0, when bound) that publish an updated kind:0 via a\n * pay-to-write confirm, with the live header below so the change shows up.\n */\nexport function profileEditorView(pubkey: string): ViewSpec {\n return {\n title: 'Edit profile',\n root: {\n atom: 'stack',\n children: [\n {\n atom: 'profile-editor',\n props: { label: 'Save profile' },\n bind: { query: buildProfileFilter([pubkey]) },\n actions: { publish: { tool: PUBLISH_TOOL, args: { kind: 0 } } },\n },\n { atom: 'profile-header', bind: { query: buildProfileFilter([pubkey]) } },\n ],\n },\n };\n}\n\n/** A DeFi panel: pre-open a channel, run a swap, show the settlement receipt. */\nexport function swapView(): ViewSpec {\n return {\n title: 'Swap',\n root: {\n atom: 'tabs',\n props: { labels: ['Channel', 'Swap', 'Receipt'] },\n children: [\n { atom: 'channel-card', actions: { open: { tool: OPEN_CHANNEL_TOOL } } },\n {\n atom: 'swap-form',\n props: { label: 'Swap' },\n actions: { swap: { tool: SWAP_TOOL, spendy: true, confirmLabel: 'Confirm swap' } },\n },\n { atom: 'settlement-receipt' },\n ],\n },\n };\n}\n\n/**\n * Daemon status dashboard: a single `client-status` atom. It reads the live\n * `toon_status` itself (no bind/props), so this is the answer to \"show me my\n * status\" — render this instead of falling back to plain text.\n */\nexport function clientStatusView(): ViewSpec {\n return {\n title: 'Client status',\n root: { atom: 'client-status' },\n };\n}\n\n/**\n * A generic info view composed from content primitives — the pattern for\n * rendering ANY non-event structured data (targets, identity, balances). Here:\n * a heading, an aligned label→value list, and a status badge. Swap the rows for\n * whatever the agent has in hand.\n */\nexport function infoView(): ViewSpec {\n return {\n title: 'Identity',\n root: {\n atom: 'card',\n children: [\n {\n atom: 'stack',\n children: [\n { atom: 'heading', props: { text: 'Identity', level: 2 } },\n {\n atom: 'key-value',\n props: {\n rows: [\n { label: 'npub', value: 'npub1<…>' },\n { label: 'EVM', value: '0x<…>' },\n { label: 'Settlement', value: 'evm' },\n ],\n },\n },\n { atom: 'badge', props: { label: 'ready', tone: 'success' } },\n ],\n },\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: 'pay-to-write', description: 'Compose → confirm fee/chain → publish → receipt (the message is the money).', spec: payToWriteView() },\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 { name: 'onboard', description: 'Get-started card + the new user’s profile header.', spec: onboardView('<pubkey-hex>') },\n { name: 'profile-editor', description: 'Edit name/picture/about → publish kind:0 via pay-to-write (pre-fills from the bound profile).', spec: profileEditorView('<pubkey-hex>') },\n { name: 'swap', description: 'Open a channel, run a swap, show the settlement receipt.', spec: swapView() },\n { name: 'client-status', description: 'Daemon health dashboard (ready/relay/chains/identity) from live toon_status — answers \"show me my status\".', spec: clientStatusView() },\n { name: 'info', description: 'Generic info view from content primitives (heading + key-value + badge) — the pattern for any non-event data.', spec: infoView() },\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 'To display this to the user, render it via toon_render — not a generic ' +\n 'widget or plain text.',\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/relay).',\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',\n description:\n 'Pay-to-write upload (SPENDY, two-step): upload the bytes of ANY blob to ' +\n 'Arweave via the kind:5094 store/DVM (POST /store through the connector), then ' +\n 'sign+publish an event referencing the resulting Arweave URL. Supply the bytes ' +\n 'as EXACTLY ONE of `filePath` (an absolute path the daemon reads off disk — ' +\n 'preferred, keeps the payload out of the model context) or `dataBase64` (inline ' +\n 'base64). The reference event kind defaults to 1063 (NIP-94 media; 20=picture, ' +\n '21/22=video, 1=note w/ NIP-92 imeta) — set `kind` to suit any blob type. ' +\n 'Single-packet only.',\n inputSchema: {\n type: 'object',\n properties: {\n filePath: {\n type: 'string',\n description:\n 'Absolute path the daemon reads to source the bytes off disk (preferred; ' +\n 'mutually exclusive with dataBase64). Bounded by an optional configured upload root.',\n },\n dataBase64: {\n type: 'string',\n description: 'Inline base64-encoded media bytes (mutually exclusive with filePath).',\n },\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 // Exactly one of filePath | dataBase64 is required; enforced in the daemon\n // (ClientRunner.uploadMedia) since JSON Schema can't express one-of-required cleanly here.\n additionalProperties: false,\n },\n },\n {\n name: 'toon_atoms',\n description:\n 'REQUIRED first call before any toon_render — returns the atom allowlist ' +\n '(ids + the kinds each renders, props, write actions) plus example ' +\n 'ViewSpecs, for composing a view to pass to toon_render. Never guess atom ' +\n 'ids or kinds; always fetch them here first. This is how you build the ' +\n 'user a generative UI for their journey.',\n inputSchema: { type: 'object', properties: {}, additionalProperties: false },\n },\n {\n name: 'toon_render',\n description:\n 'PRIMARY display surface for ALL TOON data. Use this — not any generic ' +\n 'HTML/SVG/chart/diagram/markdown/widget tool — whenever the user asks to ' +\n 'see, show, open, view, browse, render, or compose TOON events, profiles, ' +\n 'feeds, threads, channels, balances, or a note. Renders a ViewSpec ' +\n '(atoms + data binds + write actions) inline as ui://toon/app. ALWAYS call ' +\n 'toon_atoms first, then build the spec from the returned vocabulary; never ' +\n 'guess atom ids/kinds. Composing/rendering is free; atom-fired writes are ' +\n 'paid and route through the audited write allowlist, with the pay/consent ' +\n 'surface rendered by the trusted host outside the iframe. Fall back to ' +\n 'text only on explicit user request or render failure.',\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 'To display these events to the user, pass them to toon_render — not a ' +\n 'generic widget or plain text.',\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 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 'To display these events to the user, pass them to toon_render — not a ' +\n 'generic widget or plain text.',\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, cumulative ' +\n 'transferred amount, locked deposit, and available (spendable) balance. To ' +\n 'display these to the user, render them via toon_render — not a generic ' +\n 'widget or plain text.',\n inputSchema: {\n type: 'object',\n properties: {},\n additionalProperties: false,\n },\n },\n {\n name: 'toon_balances',\n description:\n 'Read on-chain wallet token balances for each configured chain (EVM token, ' +\n 'Solana SPL, native MINA). Free read, no payment. To display these to the ' +\n 'user, render them via toon_render (the wallet atom) — not plain text.',\n inputSchema: {\n type: 'object',\n properties: {},\n additionalProperties: false,\n },\n },\n {\n name: 'toon_channel_deposit',\n description:\n 'Deposit additional collateral into an open payment channel. `amount` is ' +\n 'the delta to add (base micro-units). Spends on-chain (the client signs its ' +\n 'own tx). EVM is supported today; Solana/Mina are coming. Returns the new ' +\n 'deposit total.',\n inputSchema: {\n type: 'object',\n properties: {\n channelId: { type: 'string', description: 'The channel to deposit into.' },\n amount: { type: 'string', description: 'Delta to add, base micro-units (decimal string).' },\n },\n required: ['channelId', 'amount'],\n additionalProperties: false,\n },\n },\n {\n name: 'toon_channel_close',\n description:\n 'Close a payment channel to begin the settlement grace period (withdraw, ' +\n 'step 1). Spends on-chain. EVM today. Returns closedAt + settleableAt; the ' +\n 'channel can be settled (collateral released) once now ≥ settleableAt.',\n inputSchema: {\n type: 'object',\n properties: { channelId: { type: 'string', description: 'The channel to close.' } },\n required: ['channelId'],\n additionalProperties: false,\n },\n },\n {\n name: 'toon_channel_settle',\n description:\n 'Settle a closed channel after its grace period to release collateral ' +\n '(withdraw, step 2). Fails as RETRYABLE if called before settleableAt — ' +\n 'poll toon_channels for closeState and retry once it is \"settleable\". Spends ' +\n 'on-chain. EVM today.',\n inputSchema: {\n type: 'object',\n properties: { channelId: { type: 'string', description: 'The closed channel to settle.' } },\n required: ['channelId'],\n additionalProperties: false,\n },\n },\n {\n name: 'toon_swap',\n description:\n 'Pay a swap 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 'swap peer 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: 'Swap peer ILP destination (e.g. g.proxy.swap).',\n },\n amount: {\n type: 'string',\n description: 'Total source-asset amount to swap, source micro-units.',\n },\n swapPubkey: {\n type: 'string',\n description:\n \"Swap peer'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 'swapPubkey',\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_fund_wallet',\n description:\n 'Drip devnet test funds to a wallet from the configured faucet (EVM: ' +\n 'ETH + USDC, Solana: SOL + USDC, Mina: native MINA + USDC). With no ' +\n \"arguments it funds THIS client's own address on the active settlement \" +\n 'chain — the usual \"fund me before I open a channel / pay for writes\" ' +\n 'step. Requires a faucet to be configured on the daemon.',\n inputSchema: {\n type: 'object',\n properties: {\n chain: {\n type: 'string',\n enum: ['evm', 'solana', 'mina'],\n description: 'Chain to fund (default: the active settlement chain).',\n },\n address: {\n type: 'string',\n description:\n \"Address to fund (default: this client's own address for the chain).\",\n },\n },\n additionalProperties: false,\n },\n },\n {\n name: 'toon_fund_status',\n description:\n 'Check the status of async faucet drips submitted via toon_fund_wallet. ' +\n 'Each job reports `status` (`pending` | `success` | `error`), the chain/' +\n 'address, and timestamps — so an agent can poll for settlement without ' +\n 're-dripping. With no `chain` it returns every tracked job.',\n inputSchema: {\n type: 'object',\n properties: {\n chain: {\n type: 'string',\n enum: ['evm', 'solana', 'mina'],\n description: 'Only report the drip job for this chain (default: all).',\n },\n },\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. To display these to the user, render ' +\n 'them via toon_render — not a generic widget or plain text.',\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.',\n inputSchema: {\n type: 'object',\n properties: {\n relayUrl: {\n type: 'string',\n description: 'Relay WS URL (ws://host:7100).',\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.proxy).',\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. [\"store\",\"swap\"]).',\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 const s = await client.status();\n return okStructured(JSON.stringify(s, null, 2), s as unknown as Record<string, unknown>);\n }\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':\n return ok(\n await client.uploadMedia(args as unknown as UploadMediaRequest)\n );\n case 'toon_atoms': {\n const atomsPayload = { atoms: ATOM_CATALOG, examples: EXAMPLE_VIEWSPECS };\n return okStructured(JSON.stringify(atomsPayload, null, 2), atomsPayload);\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 // Enforce the wire contract at the daemon boundary: ALWAYS emit a plain\n // `{ channels: [...] }` object so `ok()` mirrors it into\n // `structuredContent`. A bare-array regression would otherwise be\n // silently dropped (Array.isArray guard in `ok`) → blank UI (#200).\n return ok(wrapList('channels', await client.channels()));\n case 'toon_balances':\n // Same wire contract as toon_channels: always `{ balances: [...] }` so\n // `structuredContent.balances` is never dropped, even if\n // `client.balances()` regresses to a bare array (#200).\n return ok(wrapList('balances', await client.balances()));\n case 'toon_channel_deposit':\n return ok(\n await client.depositToChannel({\n channelId: String(args['channelId'] ?? ''),\n amount: String(args['amount'] ?? ''),\n })\n );\n case 'toon_channel_close':\n return ok(await client.closeChannel({ channelId: String(args['channelId'] ?? '') }));\n case 'toon_channel_settle':\n return ok(await client.settleChannel({ channelId: String(args['channelId'] ?? '') }));\n case 'toon_swap':\n return ok(\n await client.swap({\n destination: String(args['destination']),\n amount: String(args['amount']),\n swapPubkey: String(args['swapPubkey']),\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_fund_wallet': {\n // The drip is async: the daemon returns a 'pending' snapshot immediately\n // (the Mina faucet settles in ~75s, past the host's tool-call timeout).\n const job = await client.fundWallet({\n ...(typeof args['chain'] === 'string'\n ? { chain: args['chain'] as SettlementChain }\n : {}),\n ...(typeof args['address'] === 'string'\n ? { address: args['address'] }\n : {}),\n });\n return okStructured(\n `Drip submitted for ${job.chain} to ${job.address}. Funds appear in ` +\n `balances once the faucet settles (EVM/Solana ~30s, Mina ~1-2 min). ` +\n `Re-check with toon_balances, or poll toon_fund_status.`,\n job as unknown as Record<string, unknown>\n );\n }\n case 'toon_fund_status':\n return ok(\n await client.fundStatus(\n typeof args['chain'] === 'string'\n ? (args['chain'] as SettlementChain)\n : undefined\n )\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 on a balance read is the `:8787` balances handler stalling on a\n // chain RPC/provider — NOT the relay/apex (#199). Name the real failing\n // subsystem and do not assert relay/apex is the probable cause; toon_status\n // stays `ready` throughout and the read succeeds on retry.\n if (e instanceof ControlApiError && e.status === 504 && name === 'toon_balances') {\n return err(\n `${e.detail ?? e.message} — the balances control plane (:8787 GET ` +\n `/balances) stalled reading on-chain balances; the relay and apex are ` +\n `unaffected. Retry shortly.`\n );\n }\n // A 504 on a fund request is the faucet being slow (the mina faucet settles\n // in ~75s), NOT the relay/apex. The drip may still land server-side — point\n // at the faucet and tell the user to re-check balances, not to chase the\n // relay (#199-class attribution on the /fund-wallet path).\n if (e instanceof ControlApiError && e.status === 504 && name === 'toon_fund_wallet') {\n return err(\n `${e.detail ?? e.message} — the faucet drip did not return in time (the ` +\n `mina faucet settles slowly); the relay and apex are unaffected. The ` +\n `funds may still land — re-check balances shortly, or retry.`\n );\n }\n // Any other 504 is a retryable apex-discovery timeout — give a\n // discovery-specific 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 BTP session can take a few ` +\n `seconds) — 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\n/**\n * Normalize a list-shaped read into the wire-contract object the MCP-app iframe\n * depends on: `{ [key]: [...] }`. Accepts either the already-wrapped object\n * (pass-through, no double-wrap) or a bare array (defensive — wraps it), so the\n * read seam ALWAYS yields a plain object `ok()` mirrors into `structuredContent`\n * rather than a bare array it would silently drop (#200).\n */\nfunction wrapList(\n key: 'balances' | 'channels',\n res: unknown\n): Record<string, unknown> {\n if (Array.isArray(res)) return { [key]: res };\n if (res !== null && typeof res === 'object') {\n const existing = (res as Record<string, unknown>)[key];\n if (Array.isArray(existing)) return res as Record<string, unknown>;\n }\n return { [key]: [] };\n}\n\nfunction ok(data: unknown): ToolResult {\n const result: ToolResult = {\n content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],\n };\n // Mirror object payloads into `structuredContent`. The MCP-app iframe bridge\n // only surfaces `structuredContent` as `ToolOutcome.data` (a text-only result\n // gives the atoms `undefined`), so the read seams (`toon_balances`,\n // `toon_channels`) and every write receipt (`eventId`, `depositTotal`,\n // `settleableAt`, …) depend on it. Without this, `wallet-overview` renders\n // addresses but no balance/USDC — the headline #186 symptom — and deposit /\n // withdraw / publish receipts come back blank. Arrays/primitives aren't valid\n // `structuredContent` objects, so only plain objects are mirrored.\n if (data !== null && typeof data === 'object' && !Array.isArray(data)) {\n result.structuredContent = data as Record<string, unknown>;\n }\n return result;\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":[]}
|