sygnal 7.0.0 → 7.2.0-preview.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/CHANGELOG.md +632 -0
- package/dist/agent-ui.cjs.js +592 -0
- package/dist/agent-ui.cjs.js.map +1 -0
- package/dist/agent-ui.esm.js +582 -0
- package/dist/agent-ui.esm.js.map +1 -0
- package/dist/api.cjs.js +63 -0
- package/dist/api.cjs.js.map +1 -0
- package/dist/api.esm.js +2 -0
- package/dist/api.esm.js.map +1 -0
- package/dist/astro/index.cjs.js +968 -4
- package/dist/astro/index.cjs.js.map +1 -1
- package/dist/astro/index.mjs +968 -4
- package/dist/astro/index.mjs.map +1 -1
- package/dist/astro/server.cjs.js +73 -45
- package/dist/astro/server.cjs.js.map +1 -1
- package/dist/astro/server.mjs +73 -45
- package/dist/astro/server.mjs.map +1 -1
- package/dist/canvas.cjs.js +2919 -0
- package/dist/canvas.cjs.js.map +1 -0
- package/dist/canvas.esm.js +2866 -0
- package/dist/canvas.esm.js.map +1 -0
- package/dist/devtools.cjs.js +38 -6
- package/dist/devtools.cjs.js.map +1 -1
- package/dist/devtools.esm.js +38 -6
- package/dist/devtools.esm.js.map +1 -1
- package/dist/diagnostics.cjs.js +224 -20
- package/dist/diagnostics.cjs.js.map +1 -1
- package/dist/diagnostics.esm.js +224 -20
- package/dist/diagnostics.esm.js.map +1 -1
- package/dist/guide/accessibility.md +1 -1
- package/dist/guide/ai-compose.md +150 -0
- package/dist/guide/behaviors.md +2 -1
- package/dist/guide/collaborative-state.md +265 -0
- package/dist/guide/element-commands.md +36 -2
- package/dist/guide/forms-reference.md +5 -5
- package/dist/guide/forms.md +3 -2
- package/dist/guide/http.md +28 -1
- package/dist/guide/islands.md +240 -0
- package/dist/guide/recipes/code-editor.md +1 -1
- package/dist/guide/renderers/agent-ui.md +138 -0
- package/dist/guide/renderers/custom-targets.md +126 -0
- package/dist/guide/renderers/overview.md +120 -0
- package/dist/guide/renderers/react-native.md +128 -0
- package/dist/guide/renderers/terminal.md +306 -0
- package/dist/guide/renderers/three.md +332 -0
- package/dist/guide/renderers/write-once.md +137 -0
- package/dist/guide/resources.md +2 -0
- package/dist/guide/sandboxed-components.md +223 -0
- package/dist/guide/timers.md +32 -5
- package/dist/guide/typed-apis.md +442 -0
- package/dist/guide/ui/accordion.md +11 -0
- package/dist/guide/ui/combobox.md +112 -8
- package/dist/guide/ui/dialog.md +96 -20
- package/dist/guide/ui/menu.md +57 -5
- package/dist/guide/ui/overview.md +8 -4
- package/dist/guide/ui/popover.md +69 -9
- package/dist/guide/ui/select.md +54 -4
- package/dist/guide/ui/tabs.md +1 -1
- package/dist/guide/ui/tooltip.md +7 -5
- package/dist/guide/virtual-collections.md +3 -0
- package/dist/guide/widgets.md +90 -1
- package/dist/host.cjs.js +2181 -0
- package/dist/host.cjs.js.map +1 -0
- package/dist/host.esm.js +2146 -0
- package/dist/host.esm.js.map +1 -0
- package/dist/index.cjs.js +3948 -586
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +584 -44
- package/dist/index.esm.js +3918 -586
- package/dist/index.esm.js.map +1 -1
- package/dist/primitives.cjs.js +143 -0
- package/dist/primitives.cjs.js.map +1 -0
- package/dist/primitives.esm.js +133 -0
- package/dist/primitives.esm.js.map +1 -0
- package/dist/react-native.cjs.js +248 -0
- package/dist/react-native.cjs.js.map +1 -0
- package/dist/react-native.esm.js +244 -0
- package/dist/react-native.esm.js.map +1 -0
- package/dist/replay.cjs.js +39 -0
- package/dist/replay.cjs.js.map +1 -0
- package/dist/replay.esm.js +2 -0
- package/dist/replay.esm.js.map +1 -0
- package/dist/sandbox-build.cjs.js +140 -0
- package/dist/sandbox-build.cjs.js.map +1 -0
- package/dist/sandbox-build.esm.js +137 -0
- package/dist/sandbox-build.esm.js.map +1 -0
- package/dist/sandbox-kernel.esm.js +212 -0
- package/dist/sandbox-kernel.esm.js.map +1 -0
- package/dist/sandbox-worker.cjs.js +857 -0
- package/dist/sandbox-worker.cjs.js.map +1 -0
- package/dist/sandbox-worker.esm.js +854 -0
- package/dist/sandbox-worker.esm.js.map +1 -0
- package/dist/sandbox.cjs.js +1425 -0
- package/dist/sandbox.cjs.js.map +1 -0
- package/dist/sandbox.esm.js +1406 -0
- package/dist/sandbox.esm.js.map +1 -0
- package/dist/sygnal.min.js +1 -1
- package/dist/sygnal.min.js.map +1 -1
- package/dist/terminal.cjs.js +1314 -0
- package/dist/terminal.cjs.js.map +1 -0
- package/dist/terminal.esm.js +1304 -0
- package/dist/terminal.esm.js.map +1 -0
- package/dist/three.cjs.js +1111 -0
- package/dist/three.cjs.js.map +1 -0
- package/dist/three.esm.js +1079 -0
- package/dist/three.esm.js.map +1 -0
- package/dist/ui-combobox.cjs.js +256 -18
- package/dist/ui-combobox.cjs.js.map +1 -1
- package/dist/ui-combobox.esm.js +256 -18
- package/dist/ui-combobox.esm.js.map +1 -1
- package/dist/ui-menu.cjs.js +150 -16
- package/dist/ui-menu.cjs.js.map +1 -1
- package/dist/ui-menu.esm.js +151 -17
- package/dist/ui-menu.esm.js.map +1 -1
- package/dist/ui-select.cjs.js +95 -9
- package/dist/ui-select.cjs.js.map +1 -1
- package/dist/ui-select.esm.js +95 -9
- package/dist/ui-select.esm.js.map +1 -1
- package/dist/ui.cjs.js +679 -173
- package/dist/ui.cjs.js.map +1 -1
- package/dist/ui.esm.js +679 -175
- package/dist/ui.esm.js.map +1 -1
- package/dist/vike/config/package.json +1 -1
- package/dist/vite/plugin.cjs.js +968 -4
- package/dist/vite/plugin.cjs.js.map +1 -1
- package/dist/vite/plugin.mjs +968 -4
- package/dist/vite/plugin.mjs.map +1 -1
- package/dist/worker-page.cjs.js +669 -0
- package/dist/worker-page.cjs.js.map +1 -0
- package/dist/worker-page.esm.js +665 -0
- package/dist/worker-page.esm.js.map +1 -0
- package/dist/worker.cjs.js +377 -0
- package/dist/worker.cjs.js.map +1 -0
- package/dist/worker.esm.js +374 -0
- package/dist/worker.esm.js.map +1 -0
- package/dist/yjs-testing.cjs.js +98 -0
- package/dist/yjs-testing.cjs.js.map +1 -0
- package/dist/yjs-testing.esm.js +77 -0
- package/dist/yjs-testing.esm.js.map +1 -0
- package/dist/yjs.cjs.js +923 -0
- package/dist/yjs.cjs.js.map +1 -0
- package/dist/yjs.esm.js +900 -0
- package/dist/yjs.esm.js.map +1 -0
- package/dist/zag.cjs.js +11 -1
- package/dist/zag.cjs.js.map +1 -1
- package/dist/zag.esm.js +11 -1
- package/dist/zag.esm.js.map +1 -1
- package/llms/ai.md +65 -0
- package/llms/collaboration.md +5 -0
- package/llms/graphics.md +10 -0
- package/llms/renderers.md +7 -0
- package/llms/replay.md +5 -0
- package/llms/sandbox.md +5 -0
- package/llms/typed-apis.md +5 -0
- package/llms/ui.md +10 -0
- package/llms-full.txt +444 -0
- package/llms.txt +37 -35
- package/package.json +139 -3
- package/src/agent-ui/mcp.ts +152 -0
- package/src/agent-ui/snapshot.ts +212 -0
- package/src/agent-ui.d.ts +106 -0
- package/src/agent-ui.ts +249 -0
- package/src/ai.d.ts +95 -2
- package/src/api.d.ts +303 -0
- package/src/api.ts +9 -0
- package/src/canvas/Canvas.ts +585 -0
- package/src/canvas/backend.ts +627 -0
- package/src/canvas/geometry.ts +187 -0
- package/src/canvas/gesture.ts +23 -0
- package/src/canvas/image.ts +197 -0
- package/src/canvas/kit.ts +130 -0
- package/src/canvas/paint.ts +260 -0
- package/src/canvas/pointer.ts +25 -0
- package/src/canvas/snap.ts +68 -0
- package/src/canvas/svg.ts +99 -0
- package/src/canvas.d.ts +373 -0
- package/src/canvas.ts +46 -0
- package/src/core/actions.ts +14 -3
- package/src/core/cell.ts +12 -4
- package/src/core/debug.ts +8 -3
- package/src/core/hooks.ts +16 -2
- package/src/core/hosts/collection.ts +1 -1
- package/src/core/hosts/switchable.ts +26 -8
- package/src/core/instance.ts +98 -45
- package/src/core/markers/portal.ts +4 -0
- package/src/core/registry.ts +4 -0
- package/src/core/runtime.ts +34 -25
- package/src/core/statics.ts +5 -5
- package/src/cycle/dom/EventDelegator.ts +37 -33
- package/src/cycle/dom/MainDOMSource.ts +16 -14
- package/src/cycle/dom/enrichEventStream.ts +13 -9
- package/src/cycle/dom/fromEvent.ts +6 -10
- package/src/cycle/dom/index.ts +11 -7
- package/src/cycle/dom/isolate.ts +13 -6
- package/src/cycle/dom/makeDOMDriver.ts +6 -2
- package/src/cycle/dom/utils.ts +9 -2
- package/src/cycle/run/adapt.ts +4 -12
- package/src/devtools.d.ts +9 -0
- package/src/devtools.ts +2 -1
- package/src/extra/ai/compose/catalog.ts +2 -1
- package/src/extra/ai/compose/decisions.ts +1 -2
- package/src/extra/ai/compose/generated.ts +234 -0
- package/src/extra/ai/compose/index.ts +6 -2
- package/src/extra/ai/compose/layout.ts +3 -1
- package/src/extra/ai/compose/llm.ts +7 -3
- package/src/extra/ai/compose/types.ts +2 -2
- package/src/extra/ai/transports/shared.ts +3 -1
- package/src/extra/api/contract.ts +485 -0
- package/src/extra/api/index.ts +26 -0
- package/src/extra/behaviors.ts +3 -3
- package/src/extra/controlled.ts +44 -0
- package/src/extra/copyAsTest.ts +22 -0
- package/src/extra/devtools.ts +5 -3
- package/src/extra/devtoolsActions.ts +8 -0
- package/src/extra/devtoolsNext.ts +1 -1
- package/src/extra/diagnostics/checks/actionLog.ts +8 -3
- package/src/extra/diagnostics/checks/behaviors.ts +25 -0
- package/src/extra/diagnostics/checks/elementCommands.ts +17 -5
- package/src/extra/diagnostics/checks/index.ts +1 -1
- package/src/extra/diagnostics/checks/next.ts +78 -4
- package/src/extra/diagnostics/checks/props.ts +6 -1
- package/src/extra/diagnostics/checks/replies.ts +11 -7
- package/src/extra/diagnostics/checks/timers.ts +8 -2
- package/src/extra/diagnostics/checks/virtual.ts +11 -0
- package/src/extra/diagnostics/codes.ts +51 -1
- package/src/extra/elementCommands.ts +20 -4
- package/src/extra/fetchDriver.ts +4 -1
- package/src/extra/fetchEventSource.ts +6 -2
- package/src/extra/field.ts +29 -0
- package/src/extra/form.ts +42 -16
- package/src/extra/frame.ts +54 -0
- package/src/extra/keymap.ts +41 -0
- package/src/extra/pager.ts +2 -0
- package/src/extra/persist.ts +2 -1
- package/src/extra/pointer.ts +262 -0
- package/src/extra/processForm.ts +34 -1
- package/src/extra/replay/address.ts +49 -0
- package/src/extra/replay/entropy.ts +99 -0
- package/src/extra/replay/format.ts +263 -0
- package/src/extra/replay/index.ts +8 -0
- package/src/extra/replay/inert.ts +22 -0
- package/src/extra/replay/recorder.ts +247 -0
- package/src/extra/replay/replayer.ts +234 -0
- package/src/extra/replay/report.ts +126 -0
- package/src/extra/replay/schema.ts +52 -0
- package/src/extra/replay/version.ts +3 -0
- package/src/extra/router.ts +7 -3
- package/src/extra/selection.ts +2 -0
- package/src/extra/socketDriver.ts +51 -6
- package/src/extra/ssr.ts +128 -42
- package/src/extra/testing.ts +295 -39
- package/src/extra/timers.ts +96 -7
- package/src/extra/undo.ts +2 -0
- package/src/extra/virtual.ts +25 -2
- package/src/extra/widget.ts +106 -17
- package/src/host/actions.ts +383 -0
- package/src/host/delegation.ts +101 -0
- package/src/host/dom.ts +553 -0
- package/src/host/driver.ts +185 -0
- package/src/host/selector.ts +119 -0
- package/src/host/style.ts +51 -0
- package/src/host.d.ts +298 -0
- package/src/host.ts +46 -0
- package/src/index.d.ts +302 -38
- package/src/index.ts +15 -3
- package/src/pragma/blocks.ts +211 -0
- package/src/pragma/index.ts +44 -10
- package/src/primitives.d.ts +54 -0
- package/src/primitives.ts +136 -0
- package/src/react-native.d.ts +57 -0
- package/src/react-native.ts +285 -0
- package/src/react-peers.d.ts +21 -3
- package/src/replay.d.ts +185 -0
- package/src/replay.ts +6 -0
- package/src/sandbox/build.ts +119 -0
- package/src/sandbox/create.ts +187 -0
- package/src/sandbox/engine.ts +267 -0
- package/src/sandbox/generated.ts +195 -0
- package/src/sandbox/kernel.ts +180 -0
- package/src/sandbox/mirror.ts +269 -0
- package/src/sandbox/protocol.ts +128 -0
- package/src/sandbox/quickjs.ts +74 -0
- package/src/sandbox/ssr.ts +41 -0
- package/src/sandbox/widget.ts +60 -0
- package/src/sandbox/worker-entry.ts +59 -0
- package/src/sandbox/worker.ts +137 -0
- package/src/sandbox-build.d.ts +12 -0
- package/src/sandbox-build.ts +6 -0
- package/src/sandbox.d.ts +299 -0
- package/src/sandbox.ts +25 -0
- package/src/shared.ts +12 -0
- package/src/terminal/paint.ts +712 -0
- package/src/terminal.d.ts +167 -0
- package/src/terminal.ts +530 -0
- package/src/three/assets.ts +170 -0
- package/src/three/host.ts +36 -0
- package/src/three/scene.ts +217 -0
- package/src/three/target.ts +252 -0
- package/src/three.d.ts +221 -0
- package/src/three.ts +30 -0
- package/src/ui/accordion.ts +21 -7
- package/src/ui/dialog.ts +192 -90
- package/src/ui/disclosure.ts +2 -0
- package/src/ui/dom.ts +30 -0
- package/src/ui/layers.ts +132 -0
- package/src/ui/popover.ts +120 -21
- package/src/ui/shared.ts +98 -12
- package/src/ui/stack.ts +18 -0
- package/src/ui/tabs.ts +2 -0
- package/src/ui/toaster.ts +16 -15
- package/src/ui/tooltip.ts +24 -8
- package/src/ui/tooltips.ts +26 -14
- package/src/ui/zag/combobox.ts +180 -13
- package/src/ui/zag/menu.ts +97 -17
- package/src/ui/zag/select.ts +15 -4
- package/src/ui/zag/shared.ts +60 -8
- package/src/ui-combobox.d.ts +16 -1
- package/src/ui-menu.d.ts +22 -7
- package/src/ui-zag-types.d.ts +13 -5
- package/src/ui.d.ts +62 -7
- package/src/ui.ts +2 -2
- package/src/vite/api.ts +186 -0
- package/src/vite/compile.ts +280 -0
- package/src/vite/plugin.d.ts +34 -0
- package/src/vite/plugin.ts +78 -4
- package/src/vite/sandbox.ts +16 -0
- package/src/worker/page.ts +396 -0
- package/src/worker/protocol.ts +48 -0
- package/src/worker/target.ts +282 -0
- package/src/worker-page.d.ts +42 -0
- package/src/worker-page.ts +12 -0
- package/src/worker.d.ts +65 -0
- package/src/worker.ts +16 -0
- package/src/yjs/driver.ts +253 -0
- package/src/yjs/mapping.ts +389 -0
- package/src/yjs/shared.ts +138 -0
- package/src/yjs/testing.ts +91 -0
- package/src/yjs-testing.d.ts +34 -0
- package/src/yjs-testing.ts +7 -0
- package/src/yjs.d.ts +109 -0
- package/src/yjs.ts +18 -0
- package/src/zag.d.ts +2 -0
- package/src/zag.ts +8 -1
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sygnal/agent-ui: a minimal Model Context Protocol server (tools only), as sygnal-check's
|
|
3
|
+
* (sygnal-check/src/mcp.js) and sygnal/vite's dev endpoint (src/vite/mcp.ts): hand-rolled, no
|
|
4
|
+
* dependency, both protocol eras answered per request.
|
|
5
|
+
*
|
|
6
|
+
* - legacy, the `initialize` revisions (2024-11-05 … 2025-11-25): initialize, ping, tools/list,
|
|
7
|
+
* tools/call; JSON-RPC batches answered. tools.listChanged is true: the tool set can change
|
|
8
|
+
* when extra tools (sygnal/ai agentTools) do, and notifications/tools/list_changed is sent.
|
|
9
|
+
* - modern, the stateless 2026-07-28 revision (`_meta['io.modelcontextprotocol/protocolVersion']`
|
|
10
|
+
* with client capabilities): server/discover, tools/list, tools/call; results carry
|
|
11
|
+
* `resultType: 'complete'` and the server info in `_meta`.
|
|
12
|
+
* Transport: newline-delimited JSON over any stream pair with `on('data')` and `write()` (Node's
|
|
13
|
+
* stdin / stdout); messages are handled one at a time, in order.
|
|
14
|
+
*/
|
|
15
|
+
export const MODERN_VERSIONS = ['2026-07-28']
|
|
16
|
+
export const LEGACY_VERSIONS = ['2025-11-25', '2025-06-18', '2025-03-26', '2024-11-05']
|
|
17
|
+
const SUPPORTED = [...MODERN_VERSIONS, ...LEGACY_VERSIONS]
|
|
18
|
+
const META = 'io.modelcontextprotocol/'
|
|
19
|
+
|
|
20
|
+
export interface McpTool {
|
|
21
|
+
name: string
|
|
22
|
+
description: string
|
|
23
|
+
inputSchema: Record<string, any>
|
|
24
|
+
annotations?: Record<string, any>
|
|
25
|
+
}
|
|
26
|
+
export interface McpToolResult {
|
|
27
|
+
/** the text the model reads */
|
|
28
|
+
text: string
|
|
29
|
+
/** structured content (an object) */
|
|
30
|
+
value?: Record<string, any>
|
|
31
|
+
isError?: boolean
|
|
32
|
+
}
|
|
33
|
+
export interface McpServerSpec {
|
|
34
|
+
name: string
|
|
35
|
+
version: string
|
|
36
|
+
instructions?: string
|
|
37
|
+
tools(): McpTool[]
|
|
38
|
+
call(name: string, args: Record<string, any>): Promise<McpToolResult>
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const isObj = (v: any): v is Record<string, any> => !!v && typeof v == 'object' && !Array.isArray(v)
|
|
42
|
+
|
|
43
|
+
export function createMcpHandler(spec: McpServerSpec) {
|
|
44
|
+
const serverInfo = {name: spec.name, version: spec.version}
|
|
45
|
+
const result = (id: any, value: any) => ({jsonrpc: '2.0', id, result: value})
|
|
46
|
+
const error = (id: any, code: number, message: string, data?: any) => ({jsonrpc: '2.0', id, error: {code, message, ...(data !== undefined ? {data} : {})}})
|
|
47
|
+
|
|
48
|
+
async function route(id: any, method: string, params: Record<string, any>, modern: boolean): Promise<any> {
|
|
49
|
+
if (modern && (method == 'initialize' || method == 'ping')) return error(id, -32601, `Method not found: ${method} is not in 2026-07-28`)
|
|
50
|
+
switch (method) {
|
|
51
|
+
case 'initialize':
|
|
52
|
+
return result(id, {
|
|
53
|
+
protocolVersion: LEGACY_VERSIONS.includes(params.protocolVersion) ? params.protocolVersion : LEGACY_VERSIONS[0],
|
|
54
|
+
capabilities: {tools: {listChanged: true}},
|
|
55
|
+
serverInfo,
|
|
56
|
+
...(spec.instructions ? {instructions: spec.instructions} : {}),
|
|
57
|
+
})
|
|
58
|
+
case 'server/discover':
|
|
59
|
+
if (!modern) return error(id, -32602, 'Invalid params: server/discover is a 2026-07-28 request')
|
|
60
|
+
return result(id, {supportedVersions: SUPPORTED, capabilities: {tools: {}}, ...(spec.instructions ? {instructions: spec.instructions} : {})})
|
|
61
|
+
case 'ping':
|
|
62
|
+
return result(id, {})
|
|
63
|
+
case 'tools/list':
|
|
64
|
+
return result(id, {tools: spec.tools()})
|
|
65
|
+
case 'tools/call': {
|
|
66
|
+
const name = params.name
|
|
67
|
+
if (!spec.tools().some(t => t.name == name)) return error(id, -32602, `Unknown tool: ${name}`)
|
|
68
|
+
try {
|
|
69
|
+
const r = await spec.call(name, isObj(params.arguments) ? params.arguments : {})
|
|
70
|
+
return result(id, {content: [{type: 'text', text: r.text}], ...(r.value ? {structuredContent: r.value} : {}), isError: !!r.isError})
|
|
71
|
+
} catch (e: any) {
|
|
72
|
+
return result(id, {content: [{type: 'text', text: `${name} failed: ${e?.message || e}`}], isError: true})
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
default:
|
|
76
|
+
return error(id, -32601, `Method not found: ${method}`)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** one message → its response (null for a notification); never throws */
|
|
81
|
+
async function handle(msg: any, inBatch = false): Promise<any> {
|
|
82
|
+
try {
|
|
83
|
+
if (!isObj(msg) || msg.jsonrpc !== '2.0' || typeof msg.method != 'string') {
|
|
84
|
+
return isObj(msg) && 'id' in msg && !('result' in msg || 'error' in msg) ? error(msg.id ?? null, -32600, 'Invalid Request') : null
|
|
85
|
+
}
|
|
86
|
+
if (!('id' in msg) || msg.id === null) return null
|
|
87
|
+
const {id, method} = msg
|
|
88
|
+
const params = isObj(msg.params) ? msg.params : {}
|
|
89
|
+
const meta = isObj(params._meta) ? params._meta : {}
|
|
90
|
+
let modern = false
|
|
91
|
+
if (META + 'protocolVersion' in meta) {
|
|
92
|
+
const v = meta[META + 'protocolVersion']
|
|
93
|
+
if (!SUPPORTED.includes(v)) return error(id, -32022, 'Unsupported protocol version', {supported: SUPPORTED, requested: v})
|
|
94
|
+
if (MODERN_VERSIONS.includes(v)) {
|
|
95
|
+
if (!isObj(meta[META + 'clientCapabilities'])) return error(id, -32602, `Invalid params: _meta["${META}clientCapabilities"] is required`)
|
|
96
|
+
if (inBatch) return error(id, -32600, 'Invalid Request: 2026-07-28 messages are sent one per line, not in a batch')
|
|
97
|
+
modern = true
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const out = await route(id, method, params, modern)
|
|
101
|
+
if (modern && isObj(out.result)) out.result = {resultType: 'complete', ...out.result, _meta: {...(out.result._meta || {}), [META + 'serverInfo']: serverInfo}}
|
|
102
|
+
return out
|
|
103
|
+
} catch (e: any) {
|
|
104
|
+
return error(isObj(msg) ? msg.id ?? null : null, -32603, `Internal error: ${e?.message || e}`)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return {handle}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export interface McpStdio {
|
|
111
|
+
/** notifications/tools/list_changed to the client */
|
|
112
|
+
listChanged(): void
|
|
113
|
+
/** stop reading */
|
|
114
|
+
stop(): void
|
|
115
|
+
/** resolves when the input ends */
|
|
116
|
+
done: Promise<void>
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** serve over a newline-delimited JSON stream pair (default: process.stdin / process.stdout) */
|
|
120
|
+
export function serveMcpStream(spec: McpServerSpec, input?: any, output?: any): McpStdio {
|
|
121
|
+
const proc: any = (globalThis as any).process
|
|
122
|
+
input ||= proc?.stdin
|
|
123
|
+
output ||= proc?.stdout
|
|
124
|
+
const {handle} = createMcpHandler(spec)
|
|
125
|
+
const send = (m: any) => { if (m && !(Array.isArray(m) && !m.length)) output.write(JSON.stringify(m) + '\n') }
|
|
126
|
+
let buf = '', queue = Promise.resolve(), end!: () => void
|
|
127
|
+
const done = new Promise<void>(r => end = r)
|
|
128
|
+
const line = (l: string) => {
|
|
129
|
+
if (!l.trim()) return
|
|
130
|
+
queue = queue.then(async () => {
|
|
131
|
+
let msg: any
|
|
132
|
+
try { msg = JSON.parse(l) } catch { return send({jsonrpc: '2.0', id: null, error: {code: -32700, message: 'Parse error'}}) }
|
|
133
|
+
if (Array.isArray(msg)) send((await Promise.all(msg.map(m => handle(m, true)))).filter(Boolean))
|
|
134
|
+
else send(await handle(msg))
|
|
135
|
+
})
|
|
136
|
+
}
|
|
137
|
+
const onData = (chunk: any) => {
|
|
138
|
+
buf += String(chunk)
|
|
139
|
+
let i
|
|
140
|
+
while ((i = buf.indexOf('\n')) >= 0) { line(buf.slice(0, i)); buf = buf.slice(i + 1) }
|
|
141
|
+
}
|
|
142
|
+
const onEnd = () => { if (buf) line(buf); buf = ''; queue.then(end) }
|
|
143
|
+
input.setEncoding?.('utf8')
|
|
144
|
+
input.on('data', onData)
|
|
145
|
+
input.on('end', onEnd)
|
|
146
|
+
input.resume?.()
|
|
147
|
+
return {
|
|
148
|
+
listChanged: () => send({jsonrpc: '2.0', method: 'notifications/tools/list_changed'}),
|
|
149
|
+
stop: () => { input.off?.('data', onData); input.off?.('end', onEnd); end() },
|
|
150
|
+
done,
|
|
151
|
+
}
|
|
152
|
+
}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sygnal/agent-ui: the accessibility snapshot. The host tree as an LLM agent reads a page: roles,
|
|
3
|
+
* accessible names, states and values, with a ref on each element it can act on. The format is
|
|
4
|
+
* the YAML-like aria snapshot browser agents already know (Playwright's, Claude in Chrome's
|
|
5
|
+
* read_page):
|
|
6
|
+
*
|
|
7
|
+
* - heading "todos" [level=1]
|
|
8
|
+
* - textbox "What needs to be done?" [ref=e4]
|
|
9
|
+
* - list:
|
|
10
|
+
* - listitem:
|
|
11
|
+
* - checkbox "Toggle" [checked] [ref=e9]
|
|
12
|
+
* - generic "Write the spike" [ref=e10] [on=dblclick]
|
|
13
|
+
* - button "Delete" [ref=e11]
|
|
14
|
+
* - text: 1 item left
|
|
15
|
+
*
|
|
16
|
+
* What a browser can't give an agent: `on=…` lists the events the app's intents listen for on an
|
|
17
|
+
* element that isn't a native control (Sygnal's intents are selectors, so the renderer knows
|
|
18
|
+
* exactly which elements are interactive).
|
|
19
|
+
*/
|
|
20
|
+
import type {HostDocument, HostElement, HostNode, HostStyleRule} from '../host'
|
|
21
|
+
import {computeStyle, isInteractive, isTextField, isCheckable, labelTarget, nodeId, optionValue} from '../host'
|
|
22
|
+
|
|
23
|
+
export interface AgentNode {
|
|
24
|
+
role: string
|
|
25
|
+
name?: string
|
|
26
|
+
/** `e<id>`: the element tools act on */
|
|
27
|
+
ref?: string
|
|
28
|
+
value?: string
|
|
29
|
+
/** checked, disabled, expanded, pressed, selected, focused, required, level=n */
|
|
30
|
+
states?: string[]
|
|
31
|
+
/** events the app listens for on a non-native element */
|
|
32
|
+
on?: string[]
|
|
33
|
+
/** a text node's text */
|
|
34
|
+
text?: string
|
|
35
|
+
children?: AgentNode[]
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const BLOCK = /^(root|div|section|header|footer|main|nav|article|aside|ul|ol|li|form|fieldset|h[1-6]|p|pre|table|tr|td|th|details|summary|dialog|blockquote|figure|figcaption|dl|dt|dd|hr|br|menu|search|label)$/
|
|
39
|
+
const NATIVE = /^(input|textarea|select|button|a|summary|option)$/
|
|
40
|
+
const LANDMARKS: Record<string, string> = {nav: 'navigation', main: 'main', aside: 'complementary', search: 'search', dialog: 'dialog'}
|
|
41
|
+
|
|
42
|
+
export function roleOf(el: HostElement): string | null {
|
|
43
|
+
const r = el.getAttribute('role')
|
|
44
|
+
if (r) return r.split(/\s+/)[0]
|
|
45
|
+
const t = el.localName
|
|
46
|
+
// a target's own field tags (registerFieldTags)
|
|
47
|
+
if (t != 'input' && t != 'textarea' && isCheckable(el)) return 'checkbox'
|
|
48
|
+
if (t != 'input' && t != 'textarea' && isTextField(el)) return 'textbox'
|
|
49
|
+
if (t == 'a') return el.hasAttribute('href') ? 'link' : null
|
|
50
|
+
if (t == 'button' || t == 'summary') return 'button'
|
|
51
|
+
if (t == 'input') {
|
|
52
|
+
const ty = el.type || 'text'
|
|
53
|
+
if (/^(button|submit|reset|image)$/.test(ty)) return 'button'
|
|
54
|
+
if (ty == 'checkbox' || ty == 'radio') return ty
|
|
55
|
+
if (ty == 'range') return 'slider'
|
|
56
|
+
if (ty == 'number') return 'spinbutton'
|
|
57
|
+
if (ty == 'search') return 'searchbox'
|
|
58
|
+
if (ty == 'hidden' || ty == 'file') return null
|
|
59
|
+
return 'textbox'
|
|
60
|
+
}
|
|
61
|
+
if (t == 'textarea') return 'textbox'
|
|
62
|
+
if (t == 'select') return el.multiple ? 'listbox' : 'combobox'
|
|
63
|
+
if (t == 'option') return 'option'
|
|
64
|
+
if (/^h[1-6]$/.test(t)) return 'heading'
|
|
65
|
+
if (t == 'ul' || t == 'ol' || t == 'menu') return 'list'
|
|
66
|
+
if (t == 'li') return 'listitem'
|
|
67
|
+
if (t == 'img') return el.getAttribute('alt') === '' ? null : 'img'
|
|
68
|
+
if (t == 'table') return 'table'
|
|
69
|
+
if (t == 'tr') return 'row'
|
|
70
|
+
if (t == 'td') return 'cell'
|
|
71
|
+
if (t == 'th') return 'columnheader'
|
|
72
|
+
if (t == 'progress') return 'progressbar'
|
|
73
|
+
if (t == 'fieldset' || t == 'details') return 'group'
|
|
74
|
+
if (t == 'form') return el.getAttribute('aria-label') || el.getAttribute('name') ? 'form' : null
|
|
75
|
+
if (t == 'section') return el.getAttribute('aria-label') ? 'region' : null
|
|
76
|
+
return LANDMARKS[t] || null
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const squash = (s: string) => s.replace(/\s+/g, ' ').trim()
|
|
80
|
+
/** visible text, without hidden subtrees */
|
|
81
|
+
function textOf(n: HostNode, rules: readonly HostStyleRule[], skip?: HostElement): string {
|
|
82
|
+
if (n.nodeType === 3) return (n as any).data
|
|
83
|
+
const el = n as HostElement
|
|
84
|
+
if (el === skip || computeStyle(el, rules).display == 'none' || el.getAttribute('aria-hidden') == 'true') return ''
|
|
85
|
+
if (el.localName == 'input' || el.localName == 'select' || el.localName == 'textarea') return ''
|
|
86
|
+
return el.childNodes.map(c => textOf(c, rules, skip)).join(BLOCK.test(el.localName) ? ' ' : '')
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** the accessible name: aria-label, aria-labelledby, a label, contents, alt / value / placeholder / title */
|
|
90
|
+
export function nameOf(el: HostElement, rules: readonly HostStyleRule[] = []): string {
|
|
91
|
+
const a = el.getAttribute('aria-label')
|
|
92
|
+
if (a) return squash(a)
|
|
93
|
+
const by = el.getAttribute('aria-labelledby')
|
|
94
|
+
if (by) return squash(by.split(/\s+/).map(id => { const t = el.ownerDocument.getElementById(id); return t ? textOf(t, rules) : '' }).join(' '))
|
|
95
|
+
const t = el.localName
|
|
96
|
+
if (t == 'input' || t == 'textarea' || t == 'select') {
|
|
97
|
+
const id = el.id
|
|
98
|
+
const lab = (id && el.ownerDocument.querySelector(`label[for="${id}"]`)) || el.closest('label')
|
|
99
|
+
// (a label names its control even when it's visually hidden, as the browser's accessible name)
|
|
100
|
+
const l = lab ? squash(textOf(lab, [], el)) : ''
|
|
101
|
+
if (l) return l
|
|
102
|
+
if (t == 'input' && /^(button|submit|reset)$/.test(el.type)) return el.getAttribute('value') || (el.type == 'reset' ? 'Reset' : 'Submit')
|
|
103
|
+
return squash(el.placeholder || el.title || '')
|
|
104
|
+
}
|
|
105
|
+
if (t == 'img') return squash(el.getAttribute('alt') || el.title || '')
|
|
106
|
+
return squash(textOf(el, rules)) || squash(el.title || '')
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const CONTENT_NAMED = /^(button|link|heading|checkbox|radio|switch|textbox|searchbox|combobox|listbox|slider|spinbutton|option|img|columnheader|cell|tab|menuitem|treeitem|tooltip|progressbar)$/
|
|
110
|
+
const labelOnly = (el: HostElement, rules: readonly HostStyleRule[]) => {
|
|
111
|
+
const a = el.getAttribute('aria-label')
|
|
112
|
+
if (a) return squash(a)
|
|
113
|
+
const by = el.getAttribute('aria-labelledby')
|
|
114
|
+
if (by) return squash(by.split(/\s+/).map(id => { const t = el.ownerDocument.getElementById(id); return t ? textOf(t, rules) : '' }).join(' '))
|
|
115
|
+
return squash(el.title || '')
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface SnapshotOptions {
|
|
119
|
+
rules?: readonly HostStyleRule[]
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** the host tree as agent nodes (generic containers flattened, their text merged into text nodes) */
|
|
123
|
+
export function snapshotTree(doc: HostDocument, options: SnapshotOptions = {}): AgentNode[] {
|
|
124
|
+
const rules = options.rules || []
|
|
125
|
+
// labels that name a control are read as that control's name, not again as text
|
|
126
|
+
const naming = new Set<HostElement>()
|
|
127
|
+
for (const l of doc.body.querySelectorAll('label')) {
|
|
128
|
+
const t = labelTarget(l)
|
|
129
|
+
if (t && t.localName != 'button') naming.add(l)
|
|
130
|
+
}
|
|
131
|
+
const kids = (el: HostElement): AgentNode[] => {
|
|
132
|
+
const out: AgentNode[] = []
|
|
133
|
+
let text = ''
|
|
134
|
+
const flushText = () => { const t = squash(text); if (t) out.push({role: 'text', text: t}); text = '' }
|
|
135
|
+
// a flex container's children are separate items (a column's lines, a row's gap): their text doesn't run together
|
|
136
|
+
const visit = (n: HostNode, inFlex = false) => {
|
|
137
|
+
if (n.nodeType === 3) { text += (n as any).data; return }
|
|
138
|
+
const c = n as HostElement
|
|
139
|
+
const st = computeStyle(c, rules)
|
|
140
|
+
if (st.display == 'none' || c.getAttribute('aria-hidden') == 'true') return
|
|
141
|
+
const node = nodeFor(c)
|
|
142
|
+
if (node) { flushText(); out.push(node); return }
|
|
143
|
+
if (naming.has(c)) {
|
|
144
|
+
// the label's own text names its control; any other content stays
|
|
145
|
+
for (const x of c.childNodes) if (x.nodeType === 1) visit(x)
|
|
146
|
+
return
|
|
147
|
+
}
|
|
148
|
+
const block = inFlex || BLOCK.test(c.localName)
|
|
149
|
+
const flex = /flex$/.test(st.display || '')
|
|
150
|
+
if (block) flushText()
|
|
151
|
+
for (const x of c.childNodes) visit(x, flex)
|
|
152
|
+
if (block) flushText()
|
|
153
|
+
}
|
|
154
|
+
for (const c of el.childNodes) visit(c, /flex$/.test(computeStyle(el, rules).display || ''))
|
|
155
|
+
flushText()
|
|
156
|
+
return out
|
|
157
|
+
}
|
|
158
|
+
const nodeFor = (el: HostElement): AgentNode | null => {
|
|
159
|
+
const role = roleOf(el)
|
|
160
|
+
const interactive = isInteractive(el, rules)
|
|
161
|
+
const listened = NATIVE.test(el.localName) ? [] : el.ownerDocument.listenedTypes(el).filter(t => !/^(focus|blur|focusin|focusout)$/.test(t))
|
|
162
|
+
if (!role && !(interactive && listened.length)) return null
|
|
163
|
+
const node: AgentNode = {role: role || 'generic'}
|
|
164
|
+
// a role-less element the app listens to is one control (its contents its name), unless
|
|
165
|
+
// interactive elements are inside it: a scene-graph group with a click intent (canvas-three F1)
|
|
166
|
+
// keeps its children
|
|
167
|
+
const leaf = /^(textbox|searchbox|checkbox|radio|switch|slider|spinbutton|combobox|listbox|button|link|img|heading|option|progressbar)$/.test(node.role) || !role && !el.querySelectorAll('*').some(d => isInteractive(d, rules))
|
|
168
|
+
// a name from contents only for the roles that take one (ARIA "name from content"); a
|
|
169
|
+
// landmark, list or group (or such a container) is named by aria-label / aria-labelledby / title only
|
|
170
|
+
const name = !role && leaf || CONTENT_NAMED.test(node.role) ? nameOf(el, rules) : labelOnly(el, rules)
|
|
171
|
+
if (name) node.name = name
|
|
172
|
+
if (interactive) node.ref = 'e' + nodeId(el)
|
|
173
|
+
if (listened.length) node.on = listened
|
|
174
|
+
const states: string[] = []
|
|
175
|
+
if (/^(checkbox|radio|switch)$/.test(node.role)) { if (isCheckable(el) ? el.checked : el.getAttribute('aria-checked') == 'true') states.push('checked') }
|
|
176
|
+
if (el.disabled || el.getAttribute('aria-disabled') == 'true') states.push('disabled')
|
|
177
|
+
for (const s of ['expanded', 'pressed', 'selected']) { const v = el.getAttribute('aria-' + s); if (v) states.push(v == 'true' ? s : `${s}=${v}`) }
|
|
178
|
+
if (node.role == 'option' && el.selected) states.push('selected')
|
|
179
|
+
if (el.required) states.push('required')
|
|
180
|
+
if (node.role == 'heading') states.push('level=' + (+(el.getAttribute('aria-level') || el.localName.slice(1)) || 2))
|
|
181
|
+
if (el.ownerDocument.activeElement === el) states.push('focused')
|
|
182
|
+
if (states.length) node.states = states
|
|
183
|
+
if (isTextField(el)) node.value = el.type == 'password' ? '•'.repeat([...el.value].length) : el.value
|
|
184
|
+
if (el.localName == 'select') {
|
|
185
|
+
const o = el.options.find(x => optionValue(x) == el.value)
|
|
186
|
+
if (o) node.value = squash(o.textContent)
|
|
187
|
+
node.children = el.options.map(x => ({role: 'option', name: squash(x.textContent), ...(x === o ? {states: ['selected']} : {})}))
|
|
188
|
+
return node
|
|
189
|
+
}
|
|
190
|
+
if (!leaf) { const c = kids(el); if (c.length) node.children = c }
|
|
191
|
+
return node
|
|
192
|
+
}
|
|
193
|
+
return kids(doc.body)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const q = (s: string) => JSON.stringify(s)
|
|
197
|
+
/** agent nodes as the YAML-like text */
|
|
198
|
+
export function formatSnapshot(nodes: AgentNode[], indent = ''): string {
|
|
199
|
+
const lines: string[] = []
|
|
200
|
+
for (const n of nodes) {
|
|
201
|
+
if (n.role == 'text') { lines.push(`${indent}- text: ${n.text}`); continue }
|
|
202
|
+
let line = `${indent}- ${n.role}`
|
|
203
|
+
if (n.name) line += ' ' + q(n.name)
|
|
204
|
+
for (const s of n.states || []) line += ` [${s}]`
|
|
205
|
+
if (n.ref) line += ` [ref=${n.ref}]`
|
|
206
|
+
if (n.on?.length) line += ` [on=${n.on.join(',')}]`
|
|
207
|
+
if (n.value !== undefined && n.value !== '') line += ': ' + q(n.value)
|
|
208
|
+
if (n.children?.length) lines.push(line + ':', formatSnapshot(n.children, indent + ' '))
|
|
209
|
+
else lines.push(line)
|
|
210
|
+
}
|
|
211
|
+
return lines.join('\n')
|
|
212
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// 'sygnal/agent-ui': a Sygnal app rendered for an LLM agent, on 'sygnal/host'. Guide: https://sygnal.js.org/renderers/agent-ui
|
|
2
|
+
import type { HostDocument, HostElement, HostSource, HostStyleRule } from 'sygnal/host'
|
|
3
|
+
|
|
4
|
+
/** A snapshot node */
|
|
5
|
+
export interface AgentNode {
|
|
6
|
+
role: string
|
|
7
|
+
name?: string
|
|
8
|
+
/** `e<id>`: what tools act on */
|
|
9
|
+
ref?: string
|
|
10
|
+
value?: string
|
|
11
|
+
/** checked, disabled, expanded, pressed, selected, focused, required, level=n */
|
|
12
|
+
states?: string[]
|
|
13
|
+
/** events the app's intents listen for on a non-native element */
|
|
14
|
+
on?: string[]
|
|
15
|
+
text?: string
|
|
16
|
+
children?: AgentNode[]
|
|
17
|
+
}
|
|
18
|
+
export function snapshotTree(doc: HostDocument, options?: { rules?: readonly HostStyleRule[] }): AgentNode[]
|
|
19
|
+
export function formatSnapshot(nodes: AgentNode[], indent?: string): string
|
|
20
|
+
export function roleOf(el: HostElement): string | null
|
|
21
|
+
export function nameOf(el: HostElement, rules?: readonly HostStyleRule[]): string
|
|
22
|
+
|
|
23
|
+
export interface AgentUIOptions {
|
|
24
|
+
/** what is shown: `['.todo .edit', { display: 'none' }]` */
|
|
25
|
+
styles?: readonly HostStyleRule[]
|
|
26
|
+
/** a link's default action (a router driver's go) */
|
|
27
|
+
onNavigate?: (href: string, el: HostElement) => void
|
|
28
|
+
/** quiet time before a tool call answers (ms, default 20) */
|
|
29
|
+
quietMs?: number
|
|
30
|
+
/** longest wait for the app to settle (ms, default 2000) */
|
|
31
|
+
maxWaitMs?: number
|
|
32
|
+
document?: HostDocument
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface McpTool {
|
|
36
|
+
name: string
|
|
37
|
+
description: string
|
|
38
|
+
inputSchema: Record<string, any>
|
|
39
|
+
annotations?: Record<string, any>
|
|
40
|
+
}
|
|
41
|
+
export interface AgentUITool extends McpTool {
|
|
42
|
+
annotations: { readOnlyHint?: boolean, destructiveHint?: boolean }
|
|
43
|
+
}
|
|
44
|
+
export type AgentUIResult =
|
|
45
|
+
| { ok: true, snapshot: string }
|
|
46
|
+
| { ok: false, error: string, snapshot?: string }
|
|
47
|
+
|
|
48
|
+
export interface AgentUI {
|
|
49
|
+
readonly doc: HostDocument
|
|
50
|
+
/** run(App, { DOM: ui.driver }) */
|
|
51
|
+
readonly driver: (vnode$: any, name?: string) => HostSource
|
|
52
|
+
snapshot(): string
|
|
53
|
+
tree(): AgentNode[]
|
|
54
|
+
element(ref: string): HostElement | null
|
|
55
|
+
/** ui_snapshot, ui_click, ui_type, ui_press, ui_select, ui_check */
|
|
56
|
+
tools(): AgentUITool[]
|
|
57
|
+
/** the tools as ChatRequest.tools (sygnal/ai) */
|
|
58
|
+
chatTools(): Record<string, { description: string, inputSchema: Record<string, any> }>
|
|
59
|
+
/** run a tool; answers after the app settles, with the new snapshot */
|
|
60
|
+
call(name: string, args?: Record<string, any>): Promise<AgentUIResult>
|
|
61
|
+
/** run()'s result, to wait on its flushes (runAgentUI does this) */
|
|
62
|
+
attach(app: any): void
|
|
63
|
+
settle(): Promise<void>
|
|
64
|
+
subscribe(listener: () => void): () => void
|
|
65
|
+
}
|
|
66
|
+
export function createAgentUI(options?: AgentUIOptions): AgentUI
|
|
67
|
+
|
|
68
|
+
export interface RunAgentUIOptions extends AgentUIOptions {
|
|
69
|
+
drivers?: Record<string, any>
|
|
70
|
+
runOptions?: Record<string, any>
|
|
71
|
+
}
|
|
72
|
+
export function runAgentUI(App: any, options?: RunAgentUIOptions): { app: any, ui: AgentUI }
|
|
73
|
+
|
|
74
|
+
/** a tool set served alongside: sygnal/ai's agentTools(app), or your own */
|
|
75
|
+
export interface ExtraTools {
|
|
76
|
+
list(): { name: string, description: string, inputSchema: Record<string, any>, annotations?: Record<string, any> }[]
|
|
77
|
+
call(name: string, args?: unknown): Promise<unknown>
|
|
78
|
+
subscribe?(listener: (change: any) => void): () => void
|
|
79
|
+
}
|
|
80
|
+
export interface ServeMcpOptions {
|
|
81
|
+
name?: string
|
|
82
|
+
version?: string
|
|
83
|
+
instructions?: string
|
|
84
|
+
tools?: ExtraTools
|
|
85
|
+
/** default process.stdin / process.stdout */
|
|
86
|
+
input?: any
|
|
87
|
+
output?: any
|
|
88
|
+
}
|
|
89
|
+
export interface McpStdio {
|
|
90
|
+
listChanged(): void
|
|
91
|
+
stop(): void
|
|
92
|
+
done: Promise<void>
|
|
93
|
+
}
|
|
94
|
+
/** the UI tools (and extra tools) as an MCP server over newline-delimited JSON (default stdio) */
|
|
95
|
+
export function serveMcp(ui: AgentUI, options?: ServeMcpOptions): McpStdio
|
|
96
|
+
|
|
97
|
+
export interface McpToolResult { text: string, value?: Record<string, any>, isError?: boolean }
|
|
98
|
+
export interface McpServerSpec {
|
|
99
|
+
name: string
|
|
100
|
+
version: string
|
|
101
|
+
instructions?: string
|
|
102
|
+
tools(): McpTool[]
|
|
103
|
+
call(name: string, args: Record<string, any>): Promise<McpToolResult>
|
|
104
|
+
}
|
|
105
|
+
export function createMcpHandler(spec: McpServerSpec): { handle(message: unknown, inBatch?: boolean): Promise<unknown> }
|
|
106
|
+
export function serveMcpStream(spec: McpServerSpec, input?: any, output?: any): McpStdio
|