@nifrajs/mcp 2.1.0 → 2.3.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 CHANGED
@@ -26,7 +26,7 @@ hosts.
26
26
 
27
27
  ## For AI agents
28
28
 
29
- Start with [`LLM.md`](./LLM.md) — this package's contract card (the exports you call + its footguns),
29
+ Start with [`LLM.md`](./LLM.md) - this package's contract card (the exports you call + its footguns),
30
30
  one cheap read instead of the whole corpus. For the wider framework: the repo's
31
31
  [`AGENTS.md`](../../AGENTS.md) is the copy-paste quick reference, and
32
32
  [`llms-full.txt`](../../llms-full.txt) is the full machine-readable corpus. Run `nifra check` as the
package/dist/bridge.d.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * host → widget : `ui/notifications/tool-result` (the tools/call result incl. `structuredContent`),
6
6
  * `ui/notifications/tool-input` (the call arguments), and `ui/notifications/theme`
7
- * (the host's design tokens) — pushed as JSON-RPC notifications.
7
+ * (the host's design tokens) - pushed as JSON-RPC notifications.
8
8
  * widget → host : `tools/call` requests (id-matched) so a button can re-invoke a tool through the host.
9
9
  *
10
10
  * Theming follows the shadcn/Tailwind semantic-token convention (`--background`, `--foreground`,
@@ -13,7 +13,7 @@
13
13
  * app with zero per-widget work.
14
14
  *
15
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.
16
+ * `mcpApp.onData(render)` + (optionally) `mcpApp.callTool(name, args)` - no postMessage plumbing.
17
17
  *
18
18
  * Returned as a string so {@link ./widget.ts} can inline it in the resource HTML (a widget ships as ONE
19
19
  * self-contained document; the host renders it in a sandboxed iframe with no network of its own).
@@ -22,7 +22,7 @@
22
22
  export interface McpAppBridge {
23
23
  /** Latest `structuredContent` the host pushed (or `null` before the first result). */
24
24
  data: unknown;
25
- /** Register a callback for `structuredContent` — fired immediately if data already arrived, then on
25
+ /** Register a callback for `structuredContent` - fired immediately if data already arrived, then on
26
26
  * every later `ui/notifications/tool-result`. The common case: `mcpApp.onData(render)`. */
27
27
  onData(cb: (data: unknown) => void): void;
28
28
  /** Register a callback for the raw tool-call arguments (`ui/notifications/tool-input`). */
package/dist/bridge.js CHANGED
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * host → widget : `ui/notifications/tool-result` (the tools/call result incl. `structuredContent`),
6
6
  * `ui/notifications/tool-input` (the call arguments), and `ui/notifications/theme`
7
- * (the host's design tokens) — pushed as JSON-RPC notifications.
7
+ * (the host's design tokens) - pushed as JSON-RPC notifications.
8
8
  * widget → host : `tools/call` requests (id-matched) so a button can re-invoke a tool through the host.
9
9
  *
10
10
  * Theming follows the shadcn/Tailwind semantic-token convention (`--background`, `--foreground`,
@@ -13,7 +13,7 @@
13
13
  * app with zero per-widget work.
14
14
  *
15
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.
16
+ * `mcpApp.onData(render)` + (optionally) `mcpApp.callTool(name, args)` - no postMessage plumbing.
17
17
  *
18
18
  * Returned as a string so {@link ./widget.ts} can inline it in the resource HTML (a widget ships as ONE
19
19
  * self-contained document; the host renders it in a sandboxed iframe with no network of its own).
@@ -74,7 +74,7 @@ const BRIDGE_SOURCE = `(function () {
74
74
  window.addEventListener("message", function (event) {
75
75
  // Only trust the embedding host (the parent window). A widget renders in a sandboxed iframe whose host
76
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
77
+ // else - including a null source (synthetic/dispatched events) - so a sibling/opener frame or in-page
78
78
  // script can't spoof tool-results, tool-call responses, or theme. (A top-level widget has no host.)
79
79
  if (event.source !== window.parent || window.parent === window) return;
80
80
  var msg = event.data;
package/dist/http.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  /**
2
- * The transport core shared by every HTTP host of an MCP server — the dev/docs MCP in `@nifrajs/cli`
2
+ * The transport core shared by every HTTP host of an MCP server - the dev/docs MCP in `@nifrajs/cli`
3
3
  * (`nifra docs-mcp`), the site's edge worker, and a nifra app mounting `POST /mcp` via
4
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
5
+ * JSON-RPC response; GET is a health page; OPTIONS is the CORS preflight. It never throws - a bad body
6
6
  * becomes a JSON-RPC parse error. Pass the tools (and optional {@link McpServerFeatures} for resources /
7
7
  * prompts / the MCP Apps `ui://` widgets) so each host supplies its own corpus/source behind one core.
8
8
  */
@@ -17,7 +17,7 @@ export interface McpHttpOptions {
17
17
  }
18
18
  /**
19
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
20
+ * response; GET → a plain-text health page; OPTIONS → CORS preflight. Never throws - a bad body becomes a
21
21
  * JSON-RPC parse error. The dispatch is the shared, transport-agnostic {@link handleRpc}.
22
22
  */
23
23
  export declare function respondMcpHttp(request: Request, tools: McpTool[], serverInfo: {
package/dist/http.js CHANGED
@@ -1,8 +1,8 @@
1
1
  /**
2
- * The transport core shared by every HTTP host of an MCP server — the dev/docs MCP in `@nifrajs/cli`
2
+ * The transport core shared by every HTTP host of an MCP server - the dev/docs MCP in `@nifrajs/cli`
3
3
  * (`nifra docs-mcp`), the site's edge worker, and a nifra app mounting `POST /mcp` via
4
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
5
+ * JSON-RPC response; GET is a health page; OPTIONS is the CORS preflight. It never throws - a bad body
6
6
  * becomes a JSON-RPC parse error. Pass the tools (and optional {@link McpServerFeatures} for resources /
7
7
  * prompts / the MCP Apps `ui://` widgets) so each host supplies its own corpus/source behind one core.
8
8
  */
@@ -69,7 +69,7 @@ async function readJsonBounded(request, maxBytes) {
69
69
  }
70
70
  /**
71
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
72
+ * response; GET → a plain-text health page; OPTIONS → CORS preflight. Never throws - a bad body becomes a
73
73
  * JSON-RPC parse error. The dispatch is the shared, transport-agnostic {@link handleRpc}.
74
74
  */
75
75
  export async function respondMcpHttp(request, tools, serverInfo, options = {}) {
@@ -77,7 +77,7 @@ export async function respondMcpHttp(request, tools, serverInfo, options = {}) {
77
77
  return new Response(null, { status: 204, headers: CORS });
78
78
  if (request.method === "GET") {
79
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 } });
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
81
  }
82
82
  if (request.method !== "POST") {
83
83
  return new Response("method not allowed", {
@@ -99,7 +99,7 @@ export async function respondMcpHttp(request, tools, serverInfo, options = {}) {
99
99
  const response = await handleRpc(message, tools, serverInfo, options.features ?? {}, {
100
100
  signal: request.signal,
101
101
  });
102
- // A notification (no id) yields null — acknowledge with 204, no body.
102
+ // A notification (no id) yields null - acknowledge with 204, no body.
103
103
  if (response === null)
104
104
  return new Response(null, { status: 204, headers: CORS });
105
105
  return Response.json(response, { headers: CORS });
package/dist/index.d.ts CHANGED
@@ -1,12 +1,12 @@
1
1
  /**
2
- * `@nifrajs/mcp` — build MCP servers (and MCP Apps) for a nifra app.
2
+ * `@nifrajs/mcp` - build MCP servers (and MCP Apps) for a nifra app.
3
3
  *
4
- * - {@link ./protocol.ts} — the transport-agnostic JSON-RPC dispatch ({@link handleRpc}) + types, plus
4
+ * - {@link ./protocol.ts} - the transport-agnostic JSON-RPC dispatch ({@link handleRpc}) + types, plus
5
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`.
6
+ * - {@link ./http.ts} - {@link respondMcpHttp}, a Web `fetch` handler you mount at `POST /mcp`.
7
7
  *
8
- * Higher-level authoring helpers — {@link defineMcpWidget}, {@link defineMcpTool}, {@link createMcpServer}
9
- * — build MCP Apps servers whose tool results render real UI.
8
+ * Higher-level authoring helpers - {@link defineMcpWidget}, {@link defineMcpTool}, {@link createMcpServer}
9
+ * - build MCP Apps servers whose tool results render real UI.
10
10
  */
11
11
  export { bridgeScript, type McpAppBridge } from "./bridge.js";
12
12
  export { type McpHttpOptions, respondMcpHttp } from "./http.js";
package/dist/index.js CHANGED
@@ -1,12 +1,12 @@
1
1
  /**
2
- * `@nifrajs/mcp` — build MCP servers (and MCP Apps) for a nifra app.
2
+ * `@nifrajs/mcp` - build MCP servers (and MCP Apps) for a nifra app.
3
3
  *
4
- * - {@link ./protocol.ts} — the transport-agnostic JSON-RPC dispatch ({@link handleRpc}) + types, plus
4
+ * - {@link ./protocol.ts} - the transport-agnostic JSON-RPC dispatch ({@link handleRpc}) + types, plus
5
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`.
6
+ * - {@link ./http.ts} - {@link respondMcpHttp}, a Web `fetch` handler you mount at `POST /mcp`.
7
7
  *
8
- * Higher-level authoring helpers — {@link defineMcpWidget}, {@link defineMcpTool}, {@link createMcpServer}
9
- * — build MCP Apps servers whose tool results render real UI.
8
+ * Higher-level authoring helpers - {@link defineMcpWidget}, {@link defineMcpTool}, {@link createMcpServer}
9
+ * - build MCP Apps servers whose tool results render real UI.
10
10
  */
11
11
  export { bridgeScript } from "./bridge.js";
12
12
  export { respondMcpHttp } from "./http.js";
@@ -1,13 +1,13 @@
1
1
  /**
2
- * The pure MCP (Model Context Protocol) JSON-RPC dispatch — no I/O, no `Bun.*`, no side effects, so it
2
+ * The pure MCP (Model Context Protocol) JSON-RPC dispatch - no I/O, no `Bun.*`, no side effects, so it
3
3
  * unit-tests cleanly. A transport (stdio in `@nifrajs/cli`'s `mcp.ts`, Streamable-HTTP in {@link ./http.ts})
4
4
  * wires this to a byte stream; the tools/resources are injected, so the protocol logic is exercised
5
5
  * without spawning or reading streams.
6
6
  *
7
- * Beyond base MCP this also implements the server half of **MCP Apps (SEP-1865)** — interactive `ui://`
7
+ * Beyond base MCP this also implements the server half of **MCP Apps (SEP-1865)** - interactive `ui://`
8
8
  * widgets: a tool links a UI resource via `_meta.ui.resourceUri`, `tools/call` returns `structuredContent`
9
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.
10
+ * it is additive - a tool whose handler returns a plain `string` behaves exactly as before.
11
11
  */
12
12
  export declare const PROTOCOL_VERSION = "2024-11-05";
13
13
  /** The MIME type a UI resource MUST use so a host recognizes it as an MCP App widget (SEP-1865). */
@@ -23,7 +23,7 @@ export interface McpToolContext {
23
23
  readonly progressToken?: ProgressToken;
24
24
  readonly reportProgress: (progress: number, total?: number) => void;
25
25
  }
26
- /** A single content block in a tool result. Today only text — the model-facing representation. */
26
+ /** A single content block in a tool result. Today only text - the model-facing representation. */
27
27
  export interface McpContentBlock {
28
28
  readonly type: "text";
29
29
  readonly text: string;
@@ -42,7 +42,7 @@ export interface McpToolResult {
42
42
  }
43
43
  /** A tool the agent can call. `handler` returns the text shown to the agent, or a rich {@link McpToolResult}
44
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`. */
45
+ * surfaced in `tools/list` - for a widget tool it carries `ui.resourceUri`. */
46
46
  /** MCP tool safety hints (`readOnlyHint`/`destructiveHint`/…) surfaced in `tools/list`, per the MCP spec. */
47
47
  export interface McpToolAnnotations {
48
48
  readonly title?: string;
package/dist/protocol.js CHANGED
@@ -1,13 +1,13 @@
1
1
  /**
2
- * The pure MCP (Model Context Protocol) JSON-RPC dispatch — no I/O, no `Bun.*`, no side effects, so it
2
+ * The pure MCP (Model Context Protocol) JSON-RPC dispatch - no I/O, no `Bun.*`, no side effects, so it
3
3
  * unit-tests cleanly. A transport (stdio in `@nifrajs/cli`'s `mcp.ts`, Streamable-HTTP in {@link ./http.ts})
4
4
  * wires this to a byte stream; the tools/resources are injected, so the protocol logic is exercised
5
5
  * without spawning or reading streams.
6
6
  *
7
- * Beyond base MCP this also implements the server half of **MCP Apps (SEP-1865)** — interactive `ui://`
7
+ * Beyond base MCP this also implements the server half of **MCP Apps (SEP-1865)** - interactive `ui://`
8
8
  * widgets: a tool links a UI resource via `_meta.ui.resourceUri`, `tools/call` returns `structuredContent`
9
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.
10
+ * it is additive - a tool whose handler returns a plain `string` behaves exactly as before.
11
11
  */
12
12
  export const PROTOCOL_VERSION = "2024-11-05";
13
13
  /** The MIME type a UI resource MUST use so a host recognizes it as an MCP App widget (SEP-1865). */
@@ -251,7 +251,7 @@ export async function handleRpc(message, tools, serverInfo, features = {}, optio
251
251
  });
252
252
  }
253
253
  catch {
254
- // Don't surface the raw read error — it can leak a filesystem path. Return a generic message.
254
+ // Don't surface the raw read error - it can leak a filesystem path. Return a generic message.
255
255
  return rpcError(rid, -32000, `failed to read resource: ${String(uri)}`);
256
256
  }
257
257
  }
package/dist/react.d.ts CHANGED
@@ -13,7 +13,7 @@ export interface ReactWidgetOptions {
13
13
  /** Minify the client bundle (default true). */
14
14
  readonly minify?: boolean;
15
15
  }
16
- /** Build a {@link McpWidget} from a React component. Async — it bundles the component at definition time
16
+ /** Build a {@link McpWidget} from a React component. Async - it bundles the component at definition time
17
17
  * (a one-time cost at server start); pass the result to `createMcpServer({ widgets })` / a tool's `widget`. */
18
18
  export declare function reactWidget(opts: ReactWidgetOptions): Promise<McpWidget>;
19
19
  //# sourceMappingURL=react.d.ts.map
package/dist/react.js CHANGED
@@ -1,13 +1,13 @@
1
1
  /**
2
- * `reactWidget` — author an MCP Apps (`ui://`) widget from a React component instead of hand-written HTML.
2
+ * `reactWidget` - author an MCP Apps (`ui://`) widget from a React component instead of hand-written HTML.
3
3
  *
4
4
  * The component is bundled for the browser (via `Bun.build`) together with a bootstrap that mounts it and
5
- * re-renders on every `structuredContent` the host pushes — `mcpApp.onData(data => root.render(<C {...data}/>))`.
5
+ * re-renders on every `structuredContent` the host pushes - `mcpApp.onData(data => root.render(<C {...data}/>))`.
6
6
  * So a widget author writes a normal nifra/React component that takes the tool's structured data as props;
7
7
  * the bridge wiring and bundling are handled here. Client-rendered (the widget shell is the resource the
8
8
  * host fetches once; data arrives over the bridge), which matches how a host renders MCP App widgets.
9
9
  *
10
- * `react` + `react-dom` resolve from the CONSUMER's `node_modules` at bundle time — they are NOT a
10
+ * `react` + `react-dom` resolve from the CONSUMER's `node_modules` at bundle time - they are NOT a
11
11
  * dependency of `@nifrajs/mcp`. This entry is Bun-only (it calls `Bun.build`); import it from
12
12
  * `@nifrajs/mcp/react`, not the package root, so the core stays runtime-agnostic and dependency-free.
13
13
  */
@@ -18,8 +18,8 @@ import { defineMcpWidget } from "./widget.js";
18
18
  /** Bundle `component` + a bootstrap into a self-contained browser IIFE that renders on pushed data. */
19
19
  async function bundleComponent(componentPath, minify) {
20
20
  const dir = dirname(componentPath);
21
- // The bootstrap must sit next to the component so its relative import — and `react`/`react-dom`
22
- // resolution up the tree — work against the consumer's node_modules. Unique name; removed in `finally`.
21
+ // The bootstrap must sit next to the component so its relative import - and `react`/`react-dom`
22
+ // resolution up the tree - work against the consumer's node_modules. Unique name; removed in `finally`.
23
23
  const bootPath = join(dir, `.nifra-mcp-widget.${randomBytes(6).toString("hex")}.tsx`);
24
24
  const boot = `import { createElement } from "react"
25
25
  import { createRoot } from "react-dom/client"
@@ -52,12 +52,12 @@ if (el) {
52
52
  await rm(bootPath, { force: true });
53
53
  }
54
54
  }
55
- /** Build a {@link McpWidget} from a React component. Async — it bundles the component at definition time
55
+ /** Build a {@link McpWidget} from a React component. Async - it bundles the component at definition time
56
56
  * (a one-time cost at server start); pass the result to `createMcpServer({ widgets })` / a tool's `widget`. */
57
57
  export async function reactWidget(opts) {
58
58
  const bundle = await bundleComponent(opts.component, opts.minify ?? true);
59
59
  // Escape any `</script` the bundle may contain (inside string/regex literals) so it can't terminate
60
- // this inline <script> early — `<\/script` is equivalent JS but invisible to the HTML parser.
60
+ // this inline <script> early - `<\/script` is equivalent JS but invisible to the HTML parser.
61
61
  const safe = bundle.replace(/<\/(script)/gi, "<\\/$1");
62
62
  return defineMcpWidget({
63
63
  uri: opts.uri,
package/dist/server.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * `createMcpServer` — assemble a mountable MCP server for a nifra app, with MCP Apps (`ui://`) widgets.
2
+ * `createMcpServer` - assemble a mountable MCP server for a nifra app, with MCP Apps (`ui://`) widgets.
3
3
  *
4
4
  * Pass tools (ideally from {@link ./tool.ts | defineMcpTool}) and widgets (from {@link ./widget.ts |
5
5
  * defineMcpWidget}); the widgets' resources are registered and the `io.modelcontextprotocol/ui` capability
@@ -14,7 +14,7 @@
14
14
  *
15
15
  * {@link McpServer.handle} dispatches a single JSON-RPC message directly (for headless verification / tests).
16
16
  *
17
- * SECURITY: this has NO built-in authentication — once mounted, every tool is callable by anyone who can
17
+ * SECURITY: this has NO built-in authentication - once mounted, every tool is callable by anyone who can
18
18
  * reach the route (the CORS header is `*` and no credentials are used, so it is effectively public). That
19
19
  * is fine for read-only/public tools; if any tool mutates state or returns private data, gate the route
20
20
  * yourself (check an `Authorization` header / session in the nifra handler before calling `mcp.fetch`).
@@ -25,7 +25,7 @@ export interface CreateMcpServerOptions {
25
25
  readonly name: string;
26
26
  readonly version: string;
27
27
  readonly tools?: readonly McpTool[];
28
- /** MCP Apps widgets — their resources are registered and the UI capability is advertised. */
28
+ /** MCP Apps widgets - their resources are registered and the UI capability is advertised. */
29
29
  readonly widgets?: readonly McpWidget[];
30
30
  /** Extra (non-widget) resources to expose. */
31
31
  readonly resources?: readonly McpResource[];
@@ -42,9 +42,9 @@ export interface McpServer {
42
42
  name: string;
43
43
  version: string;
44
44
  };
45
- /** Web `fetch` handler — mount at `POST /mcp` (GET is a health page, OPTIONS the CORS preflight). */
45
+ /** Web `fetch` handler - mount at `POST /mcp` (GET is a health page, OPTIONS the CORS preflight). */
46
46
  fetch(request: Request): Promise<Response>;
47
- /** Dispatch one JSON-RPC message directly (no HTTP) — for headless verification and unit tests. */
47
+ /** Dispatch one JSON-RPC message directly (no HTTP) - for headless verification and unit tests. */
48
48
  handle(message: JsonRpcRequest): Promise<JsonRpcResponse | null>;
49
49
  }
50
50
  export declare function createMcpServer(opts: CreateMcpServerOptions): McpServer;
package/dist/server.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * `createMcpServer` — assemble a mountable MCP server for a nifra app, with MCP Apps (`ui://`) widgets.
2
+ * `createMcpServer` - assemble a mountable MCP server for a nifra app, with MCP Apps (`ui://`) widgets.
3
3
  *
4
4
  * Pass tools (ideally from {@link ./tool.ts | defineMcpTool}) and widgets (from {@link ./widget.ts |
5
5
  * defineMcpWidget}); the widgets' resources are registered and the `io.modelcontextprotocol/ui` capability
@@ -14,7 +14,7 @@
14
14
  *
15
15
  * {@link McpServer.handle} dispatches a single JSON-RPC message directly (for headless verification / tests).
16
16
  *
17
- * SECURITY: this has NO built-in authentication — once mounted, every tool is callable by anyone who can
17
+ * SECURITY: this has NO built-in authentication - once mounted, every tool is callable by anyone who can
18
18
  * reach the route (the CORS header is `*` and no credentials are used, so it is effectively public). That
19
19
  * is fine for read-only/public tools; if any tool mutates state or returns private data, gate the route
20
20
  * yourself (check an `Authorization` header / session in the nifra handler before calling `mcp.fetch`).
package/dist/tool.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  /**
2
- * `defineMcpTool` — author a tool for a nifra MCP server, optionally backed by an MCP Apps widget.
2
+ * `defineMcpTool` - author a tool for a nifra MCP server, optionally backed by an MCP Apps widget.
3
3
  *
4
4
  * The handler returns either a `string` (a plain text result, base MCP) or an object with `structuredContent`
5
5
  * (the data a linked {@link ./widget.ts | widget} renders) and/or `text` (the model-facing summary). When a
6
6
  * `widget` is passed, the tool advertises it via `_meta.ui.resourceUri` on both `tools/list` and each
7
- * `tools/call` result, so the host knows to render the widget — see {@link ../protocol.ts}.
7
+ * `tools/call` result, so the host knows to render the widget - see {@link ../protocol.ts}.
8
8
  */
9
9
  import type { McpContentBlock, McpTool, McpToolContext } from "./protocol.js";
10
10
  import type { InferOutput, StandardSchemaV1 } from "./standard.js";
@@ -15,7 +15,7 @@ export type McpToolHandlerResult = string | {
15
15
  readonly text?: string;
16
16
  /** Explicit content blocks (overrides `text`). */
17
17
  readonly content?: readonly McpContentBlock[];
18
- /** Structured data for the linked widget — NOT added to the model's context. */
18
+ /** Structured data for the linked widget - NOT added to the model's context. */
19
19
  readonly structuredContent?: Record<string, unknown>;
20
20
  readonly _meta?: Record<string, unknown>;
21
21
  readonly isError?: boolean;
@@ -24,7 +24,7 @@ export type McpToolHandlerResult = string | {
24
24
  * A render-intent hint for GENERATIVE hosts: how to present the result's
25
25
  * `structuredContent` when the host renders its OWN themed UI rather than an iframe widget. The host maps
26
26
  * the intent to a component in its design system (a shadcn/Tailwind table, form, metric card, …). Open
27
- * union — pick a known tag or a custom one.
27
+ * union - pick a known tag or a custom one.
28
28
  */
29
29
  export type McpUiIntent = "table" | "list" | "cards" | "form" | "metric" | "detail" | "chart" | (string & {});
30
30
  /** The default when no `input` schema is given: arguments arrive untyped and unvalidated. */
@@ -48,7 +48,7 @@ export interface DefineMcpToolOptions<S extends StandardSchemaV1 = UntypedArgs>
48
48
  /** Link a widget so the tool's result renders as interactive UI in MCP Apps hosts (iframe). */
49
49
  readonly widget?: McpWidget;
50
50
  /** Render-intent for generative hosts that build their own themed UI from `structuredContent`. Lands
51
- * in `_meta.ui.intent`. Independent of `widget` — a tool can offer both (widget for MCP Apps hosts,
51
+ * in `_meta.ui.intent`. Independent of `widget` - a tool can offer both (widget for MCP Apps hosts,
52
52
  * intent for generative builders). */
53
53
  readonly intent?: McpUiIntent;
54
54
  readonly handler: (args: InferOutput<S>, context: McpToolContext) => McpToolHandlerResult | Promise<McpToolHandlerResult>;
package/dist/tool.js CHANGED
@@ -1,10 +1,10 @@
1
1
  /**
2
- * `defineMcpTool` — author a tool for a nifra MCP server, optionally backed by an MCP Apps widget.
2
+ * `defineMcpTool` - author a tool for a nifra MCP server, optionally backed by an MCP Apps widget.
3
3
  *
4
4
  * The handler returns either a `string` (a plain text result, base MCP) or an object with `structuredContent`
5
5
  * (the data a linked {@link ./widget.ts | widget} renders) and/or `text` (the model-facing summary). When a
6
6
  * `widget` is passed, the tool advertises it via `_meta.ui.resourceUri` on both `tools/list` and each
7
- * `tools/call` result, so the host knows to render the widget — see {@link ../protocol.ts}.
7
+ * `tools/call` result, so the host knows to render the widget - see {@link ../protocol.ts}.
8
8
  */
9
9
  import { formatIssues, jsonSchemaOf } from "./standard.js";
10
10
  /** Build a tool's descriptor `_meta` from its widget link and/or render intent (both under `ui`). */
package/dist/widget.d.ts CHANGED
@@ -1,27 +1,27 @@
1
1
  /**
2
- * `defineMcpWidget` — author an MCP Apps (`ui://`) interactive widget for a nifra MCP server.
2
+ * `defineMcpWidget` - author an MCP Apps (`ui://`) interactive widget for a nifra MCP server.
3
3
  *
4
4
  * A widget is ONE self-contained HTML document, served as an MCP resource with MIME {@link UI_MIME}. The
5
5
  * host renders it in a sandboxed iframe and pushes the linked tool's `structuredContent` in over the
6
- * {@link ./bridge.ts} bridge — so the author writes plain markup + `mcpApp.onData(render)`, no transport
6
+ * {@link ./bridge.ts} bridge - so the author writes plain markup + `mcpApp.onData(render)`, no transport
7
7
  * glue. Link it to a tool with {@link ./tool.ts | defineMcpTool({ widget })}, which stamps the tool's
8
8
  * `_meta.ui.resourceUri` from {@link McpWidget.meta}.
9
9
  *
10
- * SECURITY: `structuredContent` (and any theme) arriving over the bridge is UNTRUSTED — escape it before
10
+ * SECURITY: `structuredContent` (and any theme) arriving over the bridge is UNTRUSTED - escape it before
11
11
  * writing to `innerHTML`, or render with a framework that escapes (React/Preact, or `@nifrajs/mcp/react`).
12
12
  * The sandboxed iframe limits blast radius, but a widget that builds markup by string concatenation can
13
13
  * still XSS itself; the bridge passes data through, it does not sanitize.
14
14
  */
15
15
  import { type McpResource } from "./protocol.js";
16
16
  export interface DefineMcpWidgetOptions {
17
- /** The widget's resource URI — MUST use the `ui://` scheme, e.g. `ui://orders/table`. */
17
+ /** The widget's resource URI - MUST use the `ui://` scheme, e.g. `ui://orders/table`. */
18
18
  readonly uri: string;
19
19
  /** Human-readable name (shown by hosts that list resources). */
20
20
  readonly name: string;
21
21
  readonly description?: string;
22
22
  /** Document title (defaults to `name`). */
23
23
  readonly title?: string;
24
- /** Extra `<head>` content — `<style>`, `<meta>`, fonts. The bridge `<script>` is injected for you. */
24
+ /** Extra `<head>` content - `<style>`, `<meta>`, fonts. The bridge `<script>` is injected for you. */
25
25
  readonly head?: string;
26
26
  /** The widget body: markup plus a `<script>` that renders from `mcpApp.onData(...)` and may
27
27
  * `mcpApp.callTool(...)`. The bridge global is defined before this runs. */
package/dist/widget.js CHANGED
@@ -1,13 +1,13 @@
1
1
  /**
2
- * `defineMcpWidget` — author an MCP Apps (`ui://`) interactive widget for a nifra MCP server.
2
+ * `defineMcpWidget` - author an MCP Apps (`ui://`) interactive widget for a nifra MCP server.
3
3
  *
4
4
  * A widget is ONE self-contained HTML document, served as an MCP resource with MIME {@link UI_MIME}. The
5
5
  * host renders it in a sandboxed iframe and pushes the linked tool's `structuredContent` in over the
6
- * {@link ./bridge.ts} bridge — so the author writes plain markup + `mcpApp.onData(render)`, no transport
6
+ * {@link ./bridge.ts} bridge - so the author writes plain markup + `mcpApp.onData(render)`, no transport
7
7
  * glue. Link it to a tool with {@link ./tool.ts | defineMcpTool({ widget })}, which stamps the tool's
8
8
  * `_meta.ui.resourceUri` from {@link McpWidget.meta}.
9
9
  *
10
- * SECURITY: `structuredContent` (and any theme) arriving over the bridge is UNTRUSTED — escape it before
10
+ * SECURITY: `structuredContent` (and any theme) arriving over the bridge is UNTRUSTED - escape it before
11
11
  * writing to `innerHTML`, or render with a framework that escapes (React/Preact, or `@nifrajs/mcp/react`).
12
12
  * The sandboxed iframe limits blast radius, but a widget that builds markup by string concatenation can
13
13
  * still XSS itself; the bridge passes data through, it does not sanitize.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nifrajs/mcp",
3
- "version": "2.1.0",
4
- "description": "Build MCP servers — and MCP Apps (interactive `ui://` widgets, SEP-1865) — for a nifra app. The transport-agnostic JSON-RPC core (also used by `@nifrajs/cli`'s dev MCP), a Streamable-HTTP handler you mount at `POST /mcp`, and `defineMcpWidget`/`defineMcpTool` for tool results that render real UI in MCP hosts.",
3
+ "version": "2.3.0",
4
+ "description": "Build MCP servers - and MCP Apps (interactive `ui://` widgets, SEP-1865) - for a nifra app. The transport-agnostic JSON-RPC core (also used by `@nifrajs/cli`'s dev MCP), a Streamable-HTTP handler you mount at `POST /mcp`, and `defineMcpWidget`/`defineMcpTool` for tool results that render real UI in MCP hosts.",
5
5
  "keywords": [
6
6
  "bun",
7
7
  "typescript",
package/src/bridge.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * host → widget : `ui/notifications/tool-result` (the tools/call result incl. `structuredContent`),
6
6
  * `ui/notifications/tool-input` (the call arguments), and `ui/notifications/theme`
7
- * (the host's design tokens) — pushed as JSON-RPC notifications.
7
+ * (the host's design tokens) - pushed as JSON-RPC notifications.
8
8
  * widget → host : `tools/call` requests (id-matched) so a button can re-invoke a tool through the host.
9
9
  *
10
10
  * Theming follows the shadcn/Tailwind semantic-token convention (`--background`, `--foreground`,
@@ -13,7 +13,7 @@
13
13
  * app with zero per-widget work.
14
14
  *
15
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.
16
+ * `mcpApp.onData(render)` + (optionally) `mcpApp.callTool(name, args)` - no postMessage plumbing.
17
17
  *
18
18
  * Returned as a string so {@link ./widget.ts} can inline it in the resource HTML (a widget ships as ONE
19
19
  * self-contained document; the host renders it in a sandboxed iframe with no network of its own).
@@ -23,7 +23,7 @@
23
23
  export interface McpAppBridge {
24
24
  /** Latest `structuredContent` the host pushed (or `null` before the first result). */
25
25
  data: unknown
26
- /** Register a callback for `structuredContent` — fired immediately if data already arrived, then on
26
+ /** Register a callback for `structuredContent` - fired immediately if data already arrived, then on
27
27
  * every later `ui/notifications/tool-result`. The common case: `mcpApp.onData(render)`. */
28
28
  onData(cb: (data: unknown) => void): void
29
29
  /** Register a callback for the raw tool-call arguments (`ui/notifications/tool-input`). */
@@ -94,7 +94,7 @@ const BRIDGE_SOURCE = `(function () {
94
94
  window.addEventListener("message", function (event) {
95
95
  // Only trust the embedding host (the parent window). A widget renders in a sandboxed iframe whose host
96
96
  // posts via the parent (or a sandbox-proxy parent), so event.source === window.parent. Reject anything
97
- // else — including a null source (synthetic/dispatched events) — so a sibling/opener frame or in-page
97
+ // else - including a null source (synthetic/dispatched events) - so a sibling/opener frame or in-page
98
98
  // script can't spoof tool-results, tool-call responses, or theme. (A top-level widget has no host.)
99
99
  if (event.source !== window.parent || window.parent === window) return;
100
100
  var msg = event.data;
package/src/http.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  /**
2
- * The transport core shared by every HTTP host of an MCP server — the dev/docs MCP in `@nifrajs/cli`
2
+ * The transport core shared by every HTTP host of an MCP server - the dev/docs MCP in `@nifrajs/cli`
3
3
  * (`nifra docs-mcp`), the site's edge worker, and a nifra app mounting `POST /mcp` via
4
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
5
+ * JSON-RPC response; GET is a health page; OPTIONS is the CORS preflight. It never throws - a bad body
6
6
  * becomes a JSON-RPC parse error. Pass the tools (and optional {@link McpServerFeatures} for resources /
7
7
  * prompts / the MCP Apps `ui://` widgets) so each host supplies its own corpus/source behind one core.
8
8
  */
@@ -88,7 +88,7 @@ async function readJsonBounded(
88
88
 
89
89
  /**
90
90
  * Handle one MCP request over HTTP against the given `tools`/`features`. POST a JSON-RPC body → JSON-RPC
91
- * response; GET → a plain-text health page; OPTIONS → CORS preflight. Never throws — a bad body becomes a
91
+ * response; GET → a plain-text health page; OPTIONS → CORS preflight. Never throws - a bad body becomes a
92
92
  * JSON-RPC parse error. The dispatch is the shared, transport-agnostic {@link handleRpc}.
93
93
  */
94
94
  export async function respondMcpHttp(
@@ -101,7 +101,7 @@ export async function respondMcpHttp(
101
101
  if (request.method === "GET") {
102
102
  return new Response(
103
103
  options.health ??
104
- "MCP server — POST JSON-RPC 2.0 here (methods: initialize, tools/list, tools/call).",
104
+ "MCP server - POST JSON-RPC 2.0 here (methods: initialize, tools/list, tools/call).",
105
105
  { headers: { "content-type": "text/plain; charset=utf-8", ...CORS } },
106
106
  )
107
107
  }
@@ -125,7 +125,7 @@ export async function respondMcpHttp(
125
125
  const response = await handleRpc(message, tools, serverInfo, options.features ?? {}, {
126
126
  signal: request.signal,
127
127
  })
128
- // A notification (no id) yields null — acknowledge with 204, no body.
128
+ // A notification (no id) yields null - acknowledge with 204, no body.
129
129
  if (response === null) return new Response(null, { status: 204, headers: CORS })
130
130
  return Response.json(response, { headers: CORS })
131
131
  }
package/src/index.ts CHANGED
@@ -1,12 +1,12 @@
1
1
  /**
2
- * `@nifrajs/mcp` — build MCP servers (and MCP Apps) for a nifra app.
2
+ * `@nifrajs/mcp` - build MCP servers (and MCP Apps) for a nifra app.
3
3
  *
4
- * - {@link ./protocol.ts} — the transport-agnostic JSON-RPC dispatch ({@link handleRpc}) + types, plus
4
+ * - {@link ./protocol.ts} - the transport-agnostic JSON-RPC dispatch ({@link handleRpc}) + types, plus
5
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`.
6
+ * - {@link ./http.ts} - {@link respondMcpHttp}, a Web `fetch` handler you mount at `POST /mcp`.
7
7
  *
8
- * Higher-level authoring helpers — {@link defineMcpWidget}, {@link defineMcpTool}, {@link createMcpServer}
9
- * — build MCP Apps servers whose tool results render real UI.
8
+ * Higher-level authoring helpers - {@link defineMcpWidget}, {@link defineMcpTool}, {@link createMcpServer}
9
+ * - build MCP Apps servers whose tool results render real UI.
10
10
  */
11
11
 
12
12
  export { bridgeScript, type McpAppBridge } from "./bridge.ts"
package/src/protocol.ts CHANGED
@@ -1,13 +1,13 @@
1
1
  /**
2
- * The pure MCP (Model Context Protocol) JSON-RPC dispatch — no I/O, no `Bun.*`, no side effects, so it
2
+ * The pure MCP (Model Context Protocol) JSON-RPC dispatch - no I/O, no `Bun.*`, no side effects, so it
3
3
  * unit-tests cleanly. A transport (stdio in `@nifrajs/cli`'s `mcp.ts`, Streamable-HTTP in {@link ./http.ts})
4
4
  * wires this to a byte stream; the tools/resources are injected, so the protocol logic is exercised
5
5
  * without spawning or reading streams.
6
6
  *
7
- * Beyond base MCP this also implements the server half of **MCP Apps (SEP-1865)** — interactive `ui://`
7
+ * Beyond base MCP this also implements the server half of **MCP Apps (SEP-1865)** - interactive `ui://`
8
8
  * widgets: a tool links a UI resource via `_meta.ui.resourceUri`, `tools/call` returns `structuredContent`
9
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.
10
+ * it is additive - a tool whose handler returns a plain `string` behaves exactly as before.
11
11
  */
12
12
 
13
13
  export const PROTOCOL_VERSION = "2024-11-05"
@@ -28,7 +28,7 @@ export interface McpToolContext {
28
28
  readonly reportProgress: (progress: number, total?: number) => void
29
29
  }
30
30
 
31
- /** A single content block in a tool result. Today only text — the model-facing representation. */
31
+ /** A single content block in a tool result. Today only text - the model-facing representation. */
32
32
  export interface McpContentBlock {
33
33
  readonly type: "text"
34
34
  readonly text: string
@@ -49,7 +49,7 @@ export interface McpToolResult {
49
49
 
50
50
  /** A tool the agent can call. `handler` returns the text shown to the agent, or a rich {@link McpToolResult}
51
51
  * (for MCP Apps: structured data + a `ui://` widget link). `_meta` is the per-tool descriptor metadata
52
- * surfaced in `tools/list` — for a widget tool it carries `ui.resourceUri`. */
52
+ * surfaced in `tools/list` - for a widget tool it carries `ui.resourceUri`. */
53
53
  /** MCP tool safety hints (`readOnlyHint`/`destructiveHint`/…) surfaced in `tools/list`, per the MCP spec. */
54
54
  export interface McpToolAnnotations {
55
55
  readonly title?: string
@@ -375,7 +375,7 @@ export async function handleRpc(
375
375
  ],
376
376
  })
377
377
  } catch {
378
- // Don't surface the raw read error — it can leak a filesystem path. Return a generic message.
378
+ // Don't surface the raw read error - it can leak a filesystem path. Return a generic message.
379
379
  return rpcError(rid, -32000, `failed to read resource: ${String(uri)}`)
380
380
  }
381
381
  }
package/src/react.ts CHANGED
@@ -1,13 +1,13 @@
1
1
  /**
2
- * `reactWidget` — author an MCP Apps (`ui://`) widget from a React component instead of hand-written HTML.
2
+ * `reactWidget` - author an MCP Apps (`ui://`) widget from a React component instead of hand-written HTML.
3
3
  *
4
4
  * The component is bundled for the browser (via `Bun.build`) together with a bootstrap that mounts it and
5
- * re-renders on every `structuredContent` the host pushes — `mcpApp.onData(data => root.render(<C {...data}/>))`.
5
+ * re-renders on every `structuredContent` the host pushes - `mcpApp.onData(data => root.render(<C {...data}/>))`.
6
6
  * So a widget author writes a normal nifra/React component that takes the tool's structured data as props;
7
7
  * the bridge wiring and bundling are handled here. Client-rendered (the widget shell is the resource the
8
8
  * host fetches once; data arrives over the bridge), which matches how a host renders MCP App widgets.
9
9
  *
10
- * `react` + `react-dom` resolve from the CONSUMER's `node_modules` at bundle time — they are NOT a
10
+ * `react` + `react-dom` resolve from the CONSUMER's `node_modules` at bundle time - they are NOT a
11
11
  * dependency of `@nifrajs/mcp`. This entry is Bun-only (it calls `Bun.build`); import it from
12
12
  * `@nifrajs/mcp/react`, not the package root, so the core stays runtime-agnostic and dependency-free.
13
13
  */
@@ -34,8 +34,8 @@ export interface ReactWidgetOptions {
34
34
  /** Bundle `component` + a bootstrap into a self-contained browser IIFE that renders on pushed data. */
35
35
  async function bundleComponent(componentPath: string, minify: boolean): Promise<string> {
36
36
  const dir = dirname(componentPath)
37
- // The bootstrap must sit next to the component so its relative import — and `react`/`react-dom`
38
- // resolution up the tree — work against the consumer's node_modules. Unique name; removed in `finally`.
37
+ // The bootstrap must sit next to the component so its relative import - and `react`/`react-dom`
38
+ // resolution up the tree - work against the consumer's node_modules. Unique name; removed in `finally`.
39
39
  const bootPath = join(dir, `.nifra-mcp-widget.${randomBytes(6).toString("hex")}.tsx`)
40
40
  const boot = `import { createElement } from "react"
41
41
  import { createRoot } from "react-dom/client"
@@ -69,12 +69,12 @@ if (el) {
69
69
  }
70
70
  }
71
71
 
72
- /** Build a {@link McpWidget} from a React component. Async — it bundles the component at definition time
72
+ /** Build a {@link McpWidget} from a React component. Async - it bundles the component at definition time
73
73
  * (a one-time cost at server start); pass the result to `createMcpServer({ widgets })` / a tool's `widget`. */
74
74
  export async function reactWidget(opts: ReactWidgetOptions): Promise<McpWidget> {
75
75
  const bundle = await bundleComponent(opts.component, opts.minify ?? true)
76
76
  // Escape any `</script` the bundle may contain (inside string/regex literals) so it can't terminate
77
- // this inline <script> early — `<\/script` is equivalent JS but invisible to the HTML parser.
77
+ // this inline <script> early - `<\/script` is equivalent JS but invisible to the HTML parser.
78
78
  const safe = bundle.replace(/<\/(script)/gi, "<\\/$1")
79
79
  return defineMcpWidget({
80
80
  uri: opts.uri,
package/src/server.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * `createMcpServer` — assemble a mountable MCP server for a nifra app, with MCP Apps (`ui://`) widgets.
2
+ * `createMcpServer` - assemble a mountable MCP server for a nifra app, with MCP Apps (`ui://`) widgets.
3
3
  *
4
4
  * Pass tools (ideally from {@link ./tool.ts | defineMcpTool}) and widgets (from {@link ./widget.ts |
5
5
  * defineMcpWidget}); the widgets' resources are registered and the `io.modelcontextprotocol/ui` capability
@@ -14,7 +14,7 @@
14
14
  *
15
15
  * {@link McpServer.handle} dispatches a single JSON-RPC message directly (for headless verification / tests).
16
16
  *
17
- * SECURITY: this has NO built-in authentication — once mounted, every tool is callable by anyone who can
17
+ * SECURITY: this has NO built-in authentication - once mounted, every tool is callable by anyone who can
18
18
  * reach the route (the CORS header is `*` and no credentials are used, so it is effectively public). That
19
19
  * is fine for read-only/public tools; if any tool mutates state or returns private data, gate the route
20
20
  * yourself (check an `Authorization` header / session in the nifra handler before calling `mcp.fetch`).
@@ -37,7 +37,7 @@ export interface CreateMcpServerOptions {
37
37
  readonly name: string
38
38
  readonly version: string
39
39
  readonly tools?: readonly McpTool[]
40
- /** MCP Apps widgets — their resources are registered and the UI capability is advertised. */
40
+ /** MCP Apps widgets - their resources are registered and the UI capability is advertised. */
41
41
  readonly widgets?: readonly McpWidget[]
42
42
  /** Extra (non-widget) resources to expose. */
43
43
  readonly resources?: readonly McpResource[]
@@ -52,9 +52,9 @@ export interface McpServer {
52
52
  readonly tools: McpTool[]
53
53
  readonly features: McpServerFeatures
54
54
  readonly serverInfo: { name: string; version: string }
55
- /** Web `fetch` handler — mount at `POST /mcp` (GET is a health page, OPTIONS the CORS preflight). */
55
+ /** Web `fetch` handler - mount at `POST /mcp` (GET is a health page, OPTIONS the CORS preflight). */
56
56
  fetch(request: Request): Promise<Response>
57
- /** Dispatch one JSON-RPC message directly (no HTTP) — for headless verification and unit tests. */
57
+ /** Dispatch one JSON-RPC message directly (no HTTP) - for headless verification and unit tests. */
58
58
  handle(message: JsonRpcRequest): Promise<JsonRpcResponse | null>
59
59
  }
60
60
 
package/src/tool.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  /**
2
- * `defineMcpTool` — author a tool for a nifra MCP server, optionally backed by an MCP Apps widget.
2
+ * `defineMcpTool` - author a tool for a nifra MCP server, optionally backed by an MCP Apps widget.
3
3
  *
4
4
  * The handler returns either a `string` (a plain text result, base MCP) or an object with `structuredContent`
5
5
  * (the data a linked {@link ./widget.ts | widget} renders) and/or `text` (the model-facing summary). When a
6
6
  * `widget` is passed, the tool advertises it via `_meta.ui.resourceUri` on both `tools/list` and each
7
- * `tools/call` result, so the host knows to render the widget — see {@link ../protocol.ts}.
7
+ * `tools/call` result, so the host knows to render the widget - see {@link ../protocol.ts}.
8
8
  */
9
9
 
10
10
  import type { McpContentBlock, McpTool, McpToolContext, McpToolResult } from "./protocol.ts"
@@ -20,7 +20,7 @@ export type McpToolHandlerResult =
20
20
  readonly text?: string
21
21
  /** Explicit content blocks (overrides `text`). */
22
22
  readonly content?: readonly McpContentBlock[]
23
- /** Structured data for the linked widget — NOT added to the model's context. */
23
+ /** Structured data for the linked widget - NOT added to the model's context. */
24
24
  readonly structuredContent?: Record<string, unknown>
25
25
  readonly _meta?: Record<string, unknown>
26
26
  readonly isError?: boolean
@@ -30,7 +30,7 @@ export type McpToolHandlerResult =
30
30
  * A render-intent hint for GENERATIVE hosts: how to present the result's
31
31
  * `structuredContent` when the host renders its OWN themed UI rather than an iframe widget. The host maps
32
32
  * the intent to a component in its design system (a shadcn/Tailwind table, form, metric card, …). Open
33
- * union — pick a known tag or a custom one.
33
+ * union - pick a known tag or a custom one.
34
34
  */
35
35
  export type McpUiIntent =
36
36
  | "table"
@@ -64,7 +64,7 @@ export interface DefineMcpToolOptions<S extends StandardSchemaV1 = UntypedArgs>
64
64
  /** Link a widget so the tool's result renders as interactive UI in MCP Apps hosts (iframe). */
65
65
  readonly widget?: McpWidget
66
66
  /** Render-intent for generative hosts that build their own themed UI from `structuredContent`. Lands
67
- * in `_meta.ui.intent`. Independent of `widget` — a tool can offer both (widget for MCP Apps hosts,
67
+ * in `_meta.ui.intent`. Independent of `widget` - a tool can offer both (widget for MCP Apps hosts,
68
68
  * intent for generative builders). */
69
69
  readonly intent?: McpUiIntent
70
70
  readonly handler: (
package/src/widget.ts CHANGED
@@ -1,13 +1,13 @@
1
1
  /**
2
- * `defineMcpWidget` — author an MCP Apps (`ui://`) interactive widget for a nifra MCP server.
2
+ * `defineMcpWidget` - author an MCP Apps (`ui://`) interactive widget for a nifra MCP server.
3
3
  *
4
4
  * A widget is ONE self-contained HTML document, served as an MCP resource with MIME {@link UI_MIME}. The
5
5
  * host renders it in a sandboxed iframe and pushes the linked tool's `structuredContent` in over the
6
- * {@link ./bridge.ts} bridge — so the author writes plain markup + `mcpApp.onData(render)`, no transport
6
+ * {@link ./bridge.ts} bridge - so the author writes plain markup + `mcpApp.onData(render)`, no transport
7
7
  * glue. Link it to a tool with {@link ./tool.ts | defineMcpTool({ widget })}, which stamps the tool's
8
8
  * `_meta.ui.resourceUri` from {@link McpWidget.meta}.
9
9
  *
10
- * SECURITY: `structuredContent` (and any theme) arriving over the bridge is UNTRUSTED — escape it before
10
+ * SECURITY: `structuredContent` (and any theme) arriving over the bridge is UNTRUSTED - escape it before
11
11
  * writing to `innerHTML`, or render with a framework that escapes (React/Preact, or `@nifrajs/mcp/react`).
12
12
  * The sandboxed iframe limits blast radius, but a widget that builds markup by string concatenation can
13
13
  * still XSS itself; the bridge passes data through, it does not sanitize.
@@ -17,14 +17,14 @@ import { bridgeScript } from "./bridge.ts"
17
17
  import { type McpResource, UI_MIME } from "./protocol.ts"
18
18
 
19
19
  export interface DefineMcpWidgetOptions {
20
- /** The widget's resource URI — MUST use the `ui://` scheme, e.g. `ui://orders/table`. */
20
+ /** The widget's resource URI - MUST use the `ui://` scheme, e.g. `ui://orders/table`. */
21
21
  readonly uri: string
22
22
  /** Human-readable name (shown by hosts that list resources). */
23
23
  readonly name: string
24
24
  readonly description?: string
25
25
  /** Document title (defaults to `name`). */
26
26
  readonly title?: string
27
- /** Extra `<head>` content — `<style>`, `<meta>`, fonts. The bridge `<script>` is injected for you. */
27
+ /** Extra `<head>` content - `<style>`, `<meta>`, fonts. The bridge `<script>` is injected for you. */
28
28
  readonly head?: string
29
29
  /** The widget body: markup plus a `<script>` that renders from `mcpApp.onData(...)` and may
30
30
  * `mcpApp.callTool(...)`. The bridge global is defined before this runs. */