@nifrajs/mcp 1.0.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.
@@ -0,0 +1,40 @@
1
+ /**
2
+ * The tiny, dependency-free client bridge injected into every `ui://` widget's HTML (MCP Apps / SEP-1865).
3
+ * It speaks the MCP-JSON-RPC-over-`postMessage` subset a host exposes to an embedded UI:
4
+ *
5
+ * host → widget : `ui/notifications/tool-result` (the tools/call result incl. `structuredContent`),
6
+ * `ui/notifications/tool-input` (the call arguments), and `ui/notifications/theme`
7
+ * (the host's design tokens) — pushed as JSON-RPC notifications.
8
+ * widget → host : `tools/call` requests (id-matched) so a button can re-invoke a tool through the host.
9
+ *
10
+ * Theming follows the shadcn/Tailwind semantic-token convention (`--background`, `--foreground`,
11
+ * `--primary`, `--card`, `--border`, `--radius`, …). The host pushes its resolved tokens; the bridge
12
+ * applies them to the document root, so a widget that styles with `var(--primary)` etc. matches the host
13
+ * app (e.g. a ShipNow-generated app injecting its theme preset) with zero per-widget work.
14
+ *
15
+ * It exposes a small global the widget author scripts against, so authoring a widget is just
16
+ * `mcpApp.onData(render)` + (optionally) `mcpApp.callTool(name, args)` — no postMessage plumbing.
17
+ *
18
+ * Returned as a string so {@link ./widget.ts} can inline it in the resource HTML (a widget ships as ONE
19
+ * self-contained document; the host renders it in a sandboxed iframe with no network of its own).
20
+ */
21
+ /** The author-facing global injected into a widget. Kept minimal and stable. */
22
+ export interface McpAppBridge {
23
+ /** Latest `structuredContent` the host pushed (or `null` before the first result). */
24
+ data: unknown;
25
+ /** Register a callback for `structuredContent` — fired immediately if data already arrived, then on
26
+ * every later `ui/notifications/tool-result`. The common case: `mcpApp.onData(render)`. */
27
+ onData(cb: (data: unknown) => void): void;
28
+ /** Register a callback for the raw tool-call arguments (`ui/notifications/tool-input`). */
29
+ onInput(cb: (args: unknown) => void): void;
30
+ /** Latest theme the host pushed (`{ mode, tokens }`), or `null` before the first push. */
31
+ theme: unknown;
32
+ /** Register a callback for host theme pushes. The bridge ALSO auto-applies the tokens to the document
33
+ * root (shadcn semantic vars + `color-scheme`/`data-theme`), so most widgets need no theme code. */
34
+ onTheme(cb: (theme: unknown) => void): void;
35
+ /** Invoke a tool back through the host; resolves with the tools/call result. */
36
+ callTool(name: string, args?: Record<string, unknown>): Promise<unknown>;
37
+ }
38
+ /** The bridge source, as a string for inlining in a `<script>`. Self-contained, no imports. */
39
+ export declare function bridgeScript(): string;
40
+ //# sourceMappingURL=bridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,gFAAgF;AAChF,MAAM,WAAW,YAAY;IAC3B,sFAAsF;IACtF,IAAI,EAAE,OAAO,CAAA;IACb;+FAC2F;IAC3F,MAAM,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI,CAAA;IACzC,2FAA2F;IAC3F,OAAO,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI,CAAA;IAC1C,0FAA0F;IAC1F,KAAK,EAAE,OAAO,CAAA;IACd;wGACoG;IACpG,OAAO,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI,CAAA;IAC3C,gFAAgF;IAChF,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CACzE;AAED,+FAA+F;AAC/F,wBAAgB,YAAY,IAAI,MAAM,CAErC"}
package/dist/bridge.js ADDED
@@ -0,0 +1,107 @@
1
+ /**
2
+ * The tiny, dependency-free client bridge injected into every `ui://` widget's HTML (MCP Apps / SEP-1865).
3
+ * It speaks the MCP-JSON-RPC-over-`postMessage` subset a host exposes to an embedded UI:
4
+ *
5
+ * host → widget : `ui/notifications/tool-result` (the tools/call result incl. `structuredContent`),
6
+ * `ui/notifications/tool-input` (the call arguments), and `ui/notifications/theme`
7
+ * (the host's design tokens) — pushed as JSON-RPC notifications.
8
+ * widget → host : `tools/call` requests (id-matched) so a button can re-invoke a tool through the host.
9
+ *
10
+ * Theming follows the shadcn/Tailwind semantic-token convention (`--background`, `--foreground`,
11
+ * `--primary`, `--card`, `--border`, `--radius`, …). The host pushes its resolved tokens; the bridge
12
+ * applies them to the document root, so a widget that styles with `var(--primary)` etc. matches the host
13
+ * app (e.g. a ShipNow-generated app injecting its theme preset) with zero per-widget work.
14
+ *
15
+ * It exposes a small global the widget author scripts against, so authoring a widget is just
16
+ * `mcpApp.onData(render)` + (optionally) `mcpApp.callTool(name, args)` — no postMessage plumbing.
17
+ *
18
+ * Returned as a string so {@link ./widget.ts} can inline it in the resource HTML (a widget ships as ONE
19
+ * self-contained document; the host renders it in a sandboxed iframe with no network of its own).
20
+ */
21
+ /** The bridge source, as a string for inlining in a `<script>`. Self-contained, no imports. */
22
+ export function bridgeScript() {
23
+ return BRIDGE_SOURCE;
24
+ }
25
+ // Authored as a plain string (not a function we `.toString()`) so a bundler/minifier can't rename the
26
+ // global or strip it, and so it has zero dependency on this module's runtime. Intentionally ES5-ish for
27
+ // maximal host/iframe compatibility.
28
+ const BRIDGE_SOURCE = `(function () {
29
+ var pending = {};
30
+ var nextId = 1;
31
+ var dataCbs = [];
32
+ var inputCbs = [];
33
+ var themeCbs = [];
34
+ function applyTheme(theme) {
35
+ if (!theme || typeof theme !== "object") return;
36
+ var root = document.documentElement;
37
+ if (theme.mode === "light" || theme.mode === "dark") {
38
+ root.setAttribute("data-theme", theme.mode);
39
+ root.style.colorScheme = theme.mode;
40
+ }
41
+ var tokens = theme.tokens;
42
+ if (tokens && typeof tokens === "object") {
43
+ for (var k in tokens) {
44
+ if (Object.prototype.hasOwnProperty.call(tokens, k)) {
45
+ root.style.setProperty(k.charAt(0) === "-" ? k : "--" + k, String(tokens[k]));
46
+ }
47
+ }
48
+ }
49
+ }
50
+ var api = {
51
+ data: null,
52
+ theme: null,
53
+ onData: function (cb) {
54
+ if (typeof cb !== "function") return;
55
+ dataCbs.push(cb);
56
+ if (api.data !== null) { try { cb(api.data); } catch (e) {} }
57
+ },
58
+ onInput: function (cb) { if (typeof cb === "function") inputCbs.push(cb); },
59
+ onTheme: function (cb) {
60
+ if (typeof cb !== "function") return;
61
+ themeCbs.push(cb);
62
+ if (api.theme !== null) { try { cb(api.theme); } catch (e) {} }
63
+ },
64
+ callTool: function (name, args) {
65
+ var id = "w" + nextId++;
66
+ return new Promise(function (resolve, reject) {
67
+ pending[id] = { resolve: resolve, reject: reject };
68
+ post({ jsonrpc: "2.0", id: id, method: "tools/call", params: { name: name, arguments: args || {} } });
69
+ });
70
+ }
71
+ };
72
+ function post(msg) { try { (window.parent || window).postMessage(msg, "*"); } catch (e) {} }
73
+ function emit(list, value) { for (var i = 0; i < list.length; i++) { try { list[i](value); } catch (e) {} } }
74
+ window.addEventListener("message", function (event) {
75
+ // Only trust the embedding host (the parent window). A widget renders in a sandboxed iframe whose host
76
+ // posts via the parent (or a sandbox-proxy parent), so event.source === window.parent. Reject anything
77
+ // else — including a null source (synthetic/dispatched events) — so a sibling/opener frame or in-page
78
+ // script can't spoof tool-results, tool-call responses, or theme. (A top-level widget has no host.)
79
+ if (event.source !== window.parent || window.parent === window) return;
80
+ var msg = event.data;
81
+ if (!msg || typeof msg !== "object") return;
82
+ // A response to one of our tools/call requests (id-matched).
83
+ if (msg.id != null && pending[msg.id]) {
84
+ var p = pending[msg.id]; delete pending[msg.id];
85
+ if (msg.error) p.reject(msg.error); else p.resolve(msg.result);
86
+ return;
87
+ }
88
+ // A host → widget notification.
89
+ if (msg.method === "ui/notifications/tool-result") {
90
+ var params = msg.params || {};
91
+ var structured = params.structuredContent != null ? params.structuredContent : params;
92
+ api.data = structured;
93
+ emit(dataCbs, structured);
94
+ } else if (msg.method === "ui/notifications/tool-input") {
95
+ emit(inputCbs, (msg.params || {}).arguments);
96
+ } else if (msg.method === "ui/notifications/theme") {
97
+ var theme = msg.params || {};
98
+ api.theme = theme;
99
+ applyTheme(theme);
100
+ emit(themeCbs, theme);
101
+ }
102
+ });
103
+ window.mcpApp = api;
104
+ // Some hosts wait for the iframe to signal readiness before pushing data; harmless if ignored.
105
+ post({ jsonrpc: "2.0", method: "ui/notifications/ready" });
106
+ })();`;
107
+ //# sourceMappingURL=bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge.js","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAoBH,+FAA+F;AAC/F,MAAM,UAAU,YAAY;IAC1B,OAAO,aAAa,CAAA;AACtB,CAAC;AAED,sGAAsG;AACtG,wGAAwG;AACxG,qCAAqC;AACrC,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MA8EhB,CAAA"}
package/dist/http.d.ts ADDED
@@ -0,0 +1,27 @@
1
+ /**
2
+ * The transport core shared by every HTTP host of an MCP server — the dev/docs MCP in `@nifrajs/cli`
3
+ * (`nifra docs-mcp`), the site's edge worker, and a nifra app mounting `POST /mcp` via
4
+ * {@link ./server.ts}. {@link respondMcpHttp} is a plain Web `fetch` handler: POST a JSON-RPC body →
5
+ * JSON-RPC response; GET is a health page; OPTIONS is the CORS preflight. It never throws — a bad body
6
+ * becomes a JSON-RPC parse error. Pass the tools (and optional {@link McpServerFeatures} for resources /
7
+ * prompts / the MCP Apps `ui://` widgets) so each host supplies its own corpus/source behind one core.
8
+ */
9
+ import { type McpServerFeatures, type McpTool } from "./protocol.js";
10
+ export interface McpHttpOptions {
11
+ /** Maximum JSON-RPC request body size in bytes. Default 1 MB. */
12
+ readonly maxBodyBytes?: number;
13
+ /** Resources / prompts / the MCP Apps UI extension served alongside the tools. */
14
+ readonly features?: McpServerFeatures;
15
+ /** Shown on the GET health page so each host can describe itself. */
16
+ readonly health?: string;
17
+ }
18
+ /**
19
+ * Handle one MCP request over HTTP against the given `tools`/`features`. POST a JSON-RPC body → JSON-RPC
20
+ * response; GET → a plain-text health page; OPTIONS → CORS preflight. Never throws — a bad body becomes a
21
+ * JSON-RPC parse error. The dispatch is the shared, transport-agnostic {@link handleRpc}.
22
+ */
23
+ export declare function respondMcpHttp(request: Request, tools: McpTool[], serverInfo: {
24
+ name: string;
25
+ version: string;
26
+ }, options?: McpHttpOptions): Promise<Response>;
27
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAGL,KAAK,iBAAiB,EACtB,KAAK,OAAO,EAEb,MAAM,eAAe,CAAA;AAWtB,MAAM,WAAW,cAAc;IAC7B,iEAAiE;IACjE,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;IAC9B,kFAAkF;IAClF,QAAQ,CAAC,QAAQ,CAAC,EAAE,iBAAiB,CAAA;IACrC,qEAAqE;IACrE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CACzB;AAuDD;;;;GAIG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,OAAO,EAAE,EAChB,UAAU,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EAC7C,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,QAAQ,CAAC,CAgCnB"}
package/dist/http.js ADDED
@@ -0,0 +1,107 @@
1
+ /**
2
+ * The transport core shared by every HTTP host of an MCP server — the dev/docs MCP in `@nifrajs/cli`
3
+ * (`nifra docs-mcp`), the site's edge worker, and a nifra app mounting `POST /mcp` via
4
+ * {@link ./server.ts}. {@link respondMcpHttp} is a plain Web `fetch` handler: POST a JSON-RPC body →
5
+ * JSON-RPC response; GET is a health page; OPTIONS is the CORS preflight. It never throws — a bad body
6
+ * becomes a JSON-RPC parse error. Pass the tools (and optional {@link McpServerFeatures} for resources /
7
+ * prompts / the MCP Apps `ui://` widgets) so each host supplies its own corpus/source behind one core.
8
+ */
9
+ import { handleRpc, rpcError, } from "./protocol.js";
10
+ const CORS = {
11
+ "access-control-allow-origin": "*",
12
+ "access-control-allow-methods": "POST, GET, OPTIONS",
13
+ "access-control-allow-headers": "content-type",
14
+ };
15
+ const DEFAULT_MAX_BODY_BYTES = 1_000_000;
16
+ const TEXT_DECODER = new TextDecoder();
17
+ function parseContentLength(value) {
18
+ if (value.length === 0)
19
+ return undefined;
20
+ let length = 0;
21
+ for (let i = 0; i < value.length; i++) {
22
+ const digit = value.charCodeAt(i) - 48;
23
+ if (digit < 0 || digit > 9)
24
+ return undefined;
25
+ length = length * 10 + digit;
26
+ if (length > Number.MAX_SAFE_INTEGER)
27
+ return Number.POSITIVE_INFINITY;
28
+ }
29
+ return length;
30
+ }
31
+ async function readJsonBounded(request, maxBytes) {
32
+ const declared = request.headers.get("content-length");
33
+ if (declared !== null) {
34
+ const length = parseContentLength(declared);
35
+ if (length === undefined)
36
+ return { ok: false, status: 400 };
37
+ if (length > maxBytes)
38
+ return { ok: false, status: 413 };
39
+ }
40
+ const body = request.body;
41
+ if (body === null)
42
+ return { ok: false, status: 400 };
43
+ const reader = body.getReader();
44
+ const chunks = [];
45
+ let total = 0;
46
+ for (;;) {
47
+ const { done, value } = await reader.read();
48
+ if (done)
49
+ break;
50
+ total += value.byteLength;
51
+ if (total > maxBytes) {
52
+ await reader.cancel();
53
+ return { ok: false, status: 413 };
54
+ }
55
+ chunks.push(value);
56
+ }
57
+ const bytes = new Uint8Array(total);
58
+ let offset = 0;
59
+ for (const chunk of chunks) {
60
+ bytes.set(chunk, offset);
61
+ offset += chunk.byteLength;
62
+ }
63
+ try {
64
+ return { ok: true, value: JSON.parse(TEXT_DECODER.decode(bytes)) };
65
+ }
66
+ catch {
67
+ return { ok: false, status: 400 };
68
+ }
69
+ }
70
+ /**
71
+ * Handle one MCP request over HTTP against the given `tools`/`features`. POST a JSON-RPC body → JSON-RPC
72
+ * response; GET → a plain-text health page; OPTIONS → CORS preflight. Never throws — a bad body becomes a
73
+ * JSON-RPC parse error. The dispatch is the shared, transport-agnostic {@link handleRpc}.
74
+ */
75
+ export async function respondMcpHttp(request, tools, serverInfo, options = {}) {
76
+ if (request.method === "OPTIONS")
77
+ return new Response(null, { status: 204, headers: CORS });
78
+ if (request.method === "GET") {
79
+ return new Response(options.health ??
80
+ "MCP server — POST JSON-RPC 2.0 here (methods: initialize, tools/list, tools/call).", { headers: { "content-type": "text/plain; charset=utf-8", ...CORS } });
81
+ }
82
+ if (request.method !== "POST") {
83
+ return new Response("method not allowed", {
84
+ status: 405,
85
+ headers: { allow: "POST, GET", ...CORS },
86
+ });
87
+ }
88
+ const parsed = await readJsonBounded(request, options.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES);
89
+ if (!parsed.ok) {
90
+ if (parsed.status === 413) {
91
+ return Response.json(rpcError(null, -32000, "payload too large"), {
92
+ status: 413,
93
+ headers: CORS,
94
+ });
95
+ }
96
+ return Response.json(rpcError(null, -32700, "parse error"), { status: 400, headers: CORS });
97
+ }
98
+ const message = parsed.value;
99
+ const response = await handleRpc(message, tools, serverInfo, options.features ?? {}, {
100
+ signal: request.signal,
101
+ });
102
+ // A notification (no id) yields null — acknowledge with 204, no body.
103
+ if (response === null)
104
+ return new Response(null, { status: 204, headers: CORS });
105
+ return Response.json(response, { headers: CORS });
106
+ }
107
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * `@nifrajs/mcp` — build MCP servers (and MCP Apps) for a nifra app.
3
+ *
4
+ * - {@link ./protocol.ts} — the transport-agnostic JSON-RPC dispatch ({@link handleRpc}) + types, plus
5
+ * the MCP Apps (SEP-1865) extension (`structuredContent`, `_meta.ui.resourceUri`, the UI capability).
6
+ * - {@link ./http.ts} — {@link respondMcpHttp}, a Web `fetch` handler you mount at `POST /mcp`.
7
+ *
8
+ * Higher-level authoring helpers — {@link defineMcpWidget}, {@link defineMcpTool}, {@link createMcpServer}
9
+ * — build MCP Apps servers whose tool results render real UI.
10
+ */
11
+ export { bridgeScript, type McpAppBridge } from "./bridge.js";
12
+ export { type McpHttpOptions, respondMcpHttp } from "./http.js";
13
+ export * from "./protocol.js";
14
+ export { type CreateMcpServerOptions, createMcpServer, type McpServer } from "./server.js";
15
+ export { type DefineMcpToolOptions, defineMcpTool, type McpToolHandlerResult, type McpUiIntent, } from "./tool.js";
16
+ export { type DefineMcpWidgetOptions, defineMcpWidget, type McpWidget, uiResourceMeta, widgetDocument, } from "./widget.js";
17
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,YAAY,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAA;AAC7D,OAAO,EAAE,KAAK,cAAc,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAC/D,cAAc,eAAe,CAAA;AAC7B,OAAO,EAAE,KAAK,sBAAsB,EAAE,eAAe,EAAE,KAAK,SAAS,EAAE,MAAM,aAAa,CAAA;AAC1F,OAAO,EACL,KAAK,oBAAoB,EACzB,aAAa,EACb,KAAK,oBAAoB,EACzB,KAAK,WAAW,GACjB,MAAM,WAAW,CAAA;AAClB,OAAO,EACL,KAAK,sBAAsB,EAC3B,eAAe,EACf,KAAK,SAAS,EACd,cAAc,EACd,cAAc,GACf,MAAM,aAAa,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,17 @@
1
+ /**
2
+ * `@nifrajs/mcp` — build MCP servers (and MCP Apps) for a nifra app.
3
+ *
4
+ * - {@link ./protocol.ts} — the transport-agnostic JSON-RPC dispatch ({@link handleRpc}) + types, plus
5
+ * the MCP Apps (SEP-1865) extension (`structuredContent`, `_meta.ui.resourceUri`, the UI capability).
6
+ * - {@link ./http.ts} — {@link respondMcpHttp}, a Web `fetch` handler you mount at `POST /mcp`.
7
+ *
8
+ * Higher-level authoring helpers — {@link defineMcpWidget}, {@link defineMcpTool}, {@link createMcpServer}
9
+ * — build MCP Apps servers whose tool results render real UI.
10
+ */
11
+ export { bridgeScript } from "./bridge.js";
12
+ export { respondMcpHttp } from "./http.js";
13
+ export * from "./protocol.js";
14
+ export { createMcpServer } from "./server.js";
15
+ export { defineMcpTool, } from "./tool.js";
16
+ export { defineMcpWidget, uiResourceMeta, widgetDocument, } from "./widget.js";
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,YAAY,EAAqB,MAAM,aAAa,CAAA;AAC7D,OAAO,EAAuB,cAAc,EAAE,MAAM,WAAW,CAAA;AAC/D,cAAc,eAAe,CAAA;AAC7B,OAAO,EAA+B,eAAe,EAAkB,MAAM,aAAa,CAAA;AAC1F,OAAO,EAEL,aAAa,GAGd,MAAM,WAAW,CAAA;AAClB,OAAO,EAEL,eAAe,EAEf,cAAc,EACd,cAAc,GACf,MAAM,aAAa,CAAA"}
@@ -0,0 +1,134 @@
1
+ /**
2
+ * The pure MCP (Model Context Protocol) JSON-RPC dispatch — no I/O, no `Bun.*`, no side effects, so it
3
+ * unit-tests cleanly. A transport (stdio in `@nifrajs/cli`'s `mcp.ts`, Streamable-HTTP in {@link ./http.ts})
4
+ * wires this to a byte stream; the tools/resources are injected, so the protocol logic is exercised
5
+ * without spawning or reading streams.
6
+ *
7
+ * Beyond base MCP this also implements the server half of **MCP Apps (SEP-1865)** — interactive `ui://`
8
+ * widgets: a tool links a UI resource via `_meta.ui.resourceUri`, `tools/call` returns `structuredContent`
9
+ * for the widget to render, and `initialize` advertises the `io.modelcontextprotocol/ui` extension. All of
10
+ * it is additive — a tool whose handler returns a plain `string` behaves exactly as before.
11
+ */
12
+ export declare const PROTOCOL_VERSION = "2024-11-05";
13
+ /** The MIME type a UI resource MUST use so a host recognizes it as an MCP App widget (SEP-1865). */
14
+ export declare const UI_MIME = "text/html;profile=mcp-app";
15
+ /** The `capabilities.extensions` key advertising UI support in the `initialize` result (SEP-1865). */
16
+ export declare const UI_EXTENSION_KEY = "io.modelcontextprotocol/ui";
17
+ type RequestId = string | number;
18
+ type JsonRpcId = RequestId | null;
19
+ type ProgressToken = string | number;
20
+ export interface McpToolContext {
21
+ readonly signal: AbortSignal;
22
+ readonly requestId: JsonRpcId;
23
+ readonly progressToken?: ProgressToken;
24
+ readonly reportProgress: (progress: number, total?: number) => void;
25
+ }
26
+ /** A single content block in a tool result. Today only text — the model-facing representation. */
27
+ export interface McpContentBlock {
28
+ readonly type: "text";
29
+ readonly text: string;
30
+ }
31
+ /**
32
+ * The rich result a tool handler may return instead of a bare string (MCP Apps). `content` is the
33
+ * model-facing text (also shown by text-only hosts); `structuredContent` is the data a linked `ui://`
34
+ * widget renders and is deliberately NOT added to the model's context; `_meta` carries the
35
+ * `ui.resourceUri` link and any render hints. Returning a `string` is shorthand for one text block.
36
+ */
37
+ export interface McpToolResult {
38
+ readonly content?: readonly McpContentBlock[];
39
+ readonly structuredContent?: Record<string, unknown>;
40
+ readonly _meta?: Record<string, unknown>;
41
+ readonly isError?: boolean;
42
+ }
43
+ /** A tool the agent can call. `handler` returns the text shown to the agent, or a rich {@link McpToolResult}
44
+ * (for MCP Apps: structured data + a `ui://` widget link). `_meta` is the per-tool descriptor metadata
45
+ * surfaced in `tools/list` — for a widget tool it carries `ui.resourceUri`. */
46
+ export interface McpTool {
47
+ readonly name: string;
48
+ readonly description: string;
49
+ readonly inputSchema: Record<string, unknown>;
50
+ readonly _meta?: Record<string, unknown>;
51
+ readonly handler: (args: Record<string, unknown>, context: McpToolContext) => Promise<string | McpToolResult>;
52
+ }
53
+ export interface McpResource {
54
+ readonly uri: string;
55
+ readonly name: string;
56
+ readonly description?: string;
57
+ readonly mimeType?: string;
58
+ /** Descriptor `_meta` (MCP Apps: CSP / domain / border render hints under `ui`). */
59
+ readonly _meta?: Record<string, unknown>;
60
+ readonly read: () => Promise<{
61
+ readonly text: string;
62
+ readonly mimeType?: string;
63
+ }>;
64
+ }
65
+ export interface McpPromptMessage {
66
+ readonly role: "user" | "assistant";
67
+ readonly content: {
68
+ readonly type: "text";
69
+ readonly text: string;
70
+ };
71
+ }
72
+ export interface McpPrompt {
73
+ readonly name: string;
74
+ readonly description: string;
75
+ readonly arguments?: readonly {
76
+ readonly name: string;
77
+ readonly description?: string;
78
+ readonly required?: boolean;
79
+ }[];
80
+ readonly handler: (args: Record<string, unknown>) => Promise<readonly McpPromptMessage[]>;
81
+ }
82
+ export interface McpServerFeatures {
83
+ readonly resources?: readonly McpResource[];
84
+ readonly prompts?: readonly McpPrompt[];
85
+ /** Advertise the MCP Apps UI extension in `initialize`. Present when the server serves `ui://` widgets;
86
+ * `mimeTypes` defaults to `[UI_MIME]`. */
87
+ readonly ui?: {
88
+ readonly mimeTypes?: readonly string[];
89
+ };
90
+ }
91
+ export interface JsonRpcRequest {
92
+ readonly jsonrpc?: string;
93
+ readonly id?: JsonRpcId;
94
+ readonly method?: string;
95
+ readonly params?: Record<string, unknown>;
96
+ }
97
+ export interface JsonRpcNotification {
98
+ readonly jsonrpc: "2.0";
99
+ readonly method: string;
100
+ readonly params?: Record<string, unknown>;
101
+ }
102
+ export type JsonRpcResponse = {
103
+ jsonrpc: "2.0";
104
+ id: JsonRpcId;
105
+ result: unknown;
106
+ } | {
107
+ jsonrpc: "2.0";
108
+ id: JsonRpcId;
109
+ error: {
110
+ code: number;
111
+ message: string;
112
+ };
113
+ };
114
+ export declare const rpcResult: (id: JsonRpcId, value: unknown) => JsonRpcResponse;
115
+ export declare const rpcError: (id: JsonRpcId, code: number, message: string) => JsonRpcResponse;
116
+ export interface McpProtocolState {
117
+ readonly activeRequests: Map<string, AbortController>;
118
+ }
119
+ export interface McpProtocolOptions {
120
+ readonly state?: McpProtocolState;
121
+ readonly signal?: AbortSignal;
122
+ readonly sendNotification?: (notification: JsonRpcNotification) => void;
123
+ }
124
+ export declare function createMcpProtocolState(): McpProtocolState;
125
+ /**
126
+ * Dispatch one JSON-RPC message against the given tools. Returns the response, or `null` for a
127
+ * notification (no reply). Tool errors are reported in-band (`isError`) so the agent can react to them.
128
+ */
129
+ export declare function handleRpc(message: JsonRpcRequest, tools: readonly McpTool[], serverInfo: {
130
+ name: string;
131
+ version: string;
132
+ }, features?: McpServerFeatures, options?: McpProtocolOptions): Promise<JsonRpcResponse | null>;
133
+ export {};
134
+ //# sourceMappingURL=protocol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,eAAO,MAAM,gBAAgB,eAAe,CAAA;AAE5C,oGAAoG;AACpG,eAAO,MAAM,OAAO,8BAA8B,CAAA;AAClD,sGAAsG;AACtG,eAAO,MAAM,gBAAgB,+BAA+B,CAAA;AAE5D,KAAK,SAAS,GAAG,MAAM,GAAG,MAAM,CAAA;AAChC,KAAK,SAAS,GAAG,SAAS,GAAG,IAAI,CAAA;AACjC,KAAK,aAAa,GAAG,MAAM,GAAG,MAAM,CAAA;AAEpC,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;IAC5B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAA;IAC7B,QAAQ,CAAC,aAAa,CAAC,EAAE,aAAa,CAAA;IACtC,QAAQ,CAAC,cAAc,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;CACpE;AAED,kGAAkG;AAClG,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;AAED;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,eAAe,EAAE,CAAA;IAC7C,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACpD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACxC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAC3B;AAED;;+EAE+E;AAC/E,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC7C,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACxC,QAAQ,CAAC,OAAO,EAAE,CAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,EAAE,cAAc,KACpB,OAAO,CAAC,MAAM,GAAG,aAAa,CAAC,CAAA;CACrC;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,oFAAoF;IACpF,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACxC,QAAQ,CAAC,IAAI,EAAE,MAAM,OAAO,CAAC;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CACpF;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAA;IACnC,QAAQ,CAAC,OAAO,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CACnE;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS;QAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;QACrB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;QAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAC5B,EAAE,CAAA;IACH,QAAQ,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,SAAS,gBAAgB,EAAE,CAAC,CAAA;CAC1F;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,WAAW,EAAE,CAAA;IAC3C,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,SAAS,EAAE,CAAA;IACvC;8CAC0C;IAC1C,QAAQ,CAAC,EAAE,CAAC,EAAE;QAAE,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,CAAA;CACzD;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,EAAE,CAAC,EAAE,SAAS,CAAA;IACvB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC1C;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC1C;AAED,MAAM,MAAM,eAAe,GACvB;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,EAAE,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,GAClD;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,EAAE,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAA;AAE/E,eAAO,MAAM,SAAS,GAAI,IAAI,SAAS,EAAE,OAAO,OAAO,KAAG,eAIxD,CAAA;AACF,eAAO,MAAM,QAAQ,GAAI,IAAI,SAAS,EAAE,MAAM,MAAM,EAAE,SAAS,MAAM,KAAG,eAItE,CAAA;AAEF,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAA;CACtD;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAA;IACjC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;IAC7B,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC,YAAY,EAAE,mBAAmB,KAAK,IAAI,CAAA;CACxE;AAED,wBAAgB,sBAAsB,IAAI,gBAAgB,CAEzD;AAiED;;;GAGG;AACH,wBAAsB,SAAS,CAC7B,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,SAAS,OAAO,EAAE,EACzB,UAAU,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EAC7C,QAAQ,GAAE,iBAAsB,EAChC,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAwLjC"}