ngx-json-render 0.9.5 → 0.9.7
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 +35 -6
- package/fesm2022/ngx-json-render-mcp-server.mjs +46 -4
- package/fesm2022/ngx-json-render-mcp-server.mjs.map +1 -1
- package/fesm2022/ngx-json-render-mcp.mjs +35 -12
- package/fesm2022/ngx-json-render-mcp.mjs.map +1 -1
- package/package.json +2 -1
- package/schematics/builders.json +10 -0
- package/schematics/collection.json +5 -0
- package/schematics/mcp-app/index.cjs +552 -0
- package/schematics/mcp-app/schema.json +27 -0
- package/schematics/mcp-app-builder/index.cjs +98 -0
- package/schematics/mcp-app-builder/inline.cjs +117 -0
- package/schematics/mcp-app-builder/schema.json +31 -0
- package/types/ngx-json-render-mcp-server.d.ts +25 -2
- package/types/ngx-json-render-mcp.d.ts +25 -3
package/README.md
CHANGED
|
@@ -732,7 +732,7 @@ export class App {
|
|
|
732
732
|
}
|
|
733
733
|
```
|
|
734
734
|
|
|
735
|
-
The spec renders while the model is still writing the tool call, from the host's `toolinputpartial` notifications; pass `streamPartialInput: false` to wait for the result. `mcp.sendMessage(text, data)` posts a user message to the chat, so a button can continue the conversation, and `mcp.callServerTool(name, args)` replaces the spec with another tool's result. The connection closes with the injector that created it.
|
|
735
|
+
The spec renders while the model is still writing the tool call, from the host's `toolinputpartial` notifications; pass `streamPartialInput: false` to wait for the result. `mcp.sendMessage(text, data)` posts a user message to the chat, so a button can continue the conversation, and `mcp.callServerTool(name, args)` replaces the spec with another tool's result. `mcp.handlers` holds a ready `sendMessage` action handler for `<json-render [handlers]>`, the one the server's `sendMessage: true` describes to the model (below), and `mcp.lastMessage` is a signal with how its last call went, so the view can say when a host declined the message. The connection closes with the injector that created it.
|
|
736
736
|
|
|
737
737
|
### The server
|
|
738
738
|
|
|
@@ -746,7 +746,7 @@ import { catalog } from './catalog'; // defineCatalog(schema, …) from ngx-json
|
|
|
746
746
|
|
|
747
747
|
const server = createRenderUiServer({
|
|
748
748
|
catalog,
|
|
749
|
-
html: readFileSync('
|
|
749
|
+
html: readFileSync(new URL('./view.html', import.meta.url), 'utf8'), // the view above, built into one page
|
|
750
750
|
name: 'my-app',
|
|
751
751
|
version: '1.0.0',
|
|
752
752
|
});
|
|
@@ -762,9 +762,36 @@ What it does on top of upstream's `createMcpApp` from `@json-render/mcp`, whose
|
|
|
762
762
|
- **Errors the model can fix.** A spec with a missing child or root, or a `$template` that reads a repeat item as `${$item/field}`, goes back as a tool error that says what to write instead (`specProblems`).
|
|
763
763
|
- **The structured result ChatGPT asks for:** the spec as `structuredContent`, with an output schema (`specOutputSchema`).
|
|
764
764
|
|
|
765
|
-
Options: `name`, `version`, `toolName` (default `render-ui`), `title`, `description`, `csp` (the origins the view may load from and connect to; left out, the host allows none)
|
|
765
|
+
Options: `name`, `version`, `toolName` (default `render-ui`), `title`, `description`, `csp` (the origins the view may load from and connect to; left out, the host allows none) `widgetDomain` (the view's origin for ChatGPT's plugin directory) and `sendMessage: true`, which adds a `sendMessage` action to the catalog the model sees, so a button in the view can post a message to the chat as the user; hand the view `mcp.handlers` to handle it. Your catalog stays host-neutral; `withSendMessage(catalog)` gives you the extended catalog if you need it elsewhere.
|
|
766
766
|
|
|
767
|
-
|
|
767
|
+
### In one command
|
|
768
|
+
|
|
769
|
+
```bash
|
|
770
|
+
ng generate ngx-json-render:mcp-app --catalog src/app/catalog.ts#catalog --registry src/app/registry.ts#registry
|
|
771
|
+
ng run mcp-app:mcp
|
|
772
|
+
node dist/mcp-app/server.mjs
|
|
773
|
+
```
|
|
774
|
+
|
|
775
|
+
The schematic adds an application project for the view (`--name`, default `mcp-app`): `src/app/app.ts` renders the model's spec with your registry, handles `sendMessage` with `mcp.handlers`, says under the UI whether the host took the message, and follows the host's light or dark theme. Next to it, `server.ts` serves your catalog with `createRenderUiServer({ sendMessage: true })` over stdio, or over HTTP on port 3001 with `--http`, and an `mcp` target builds both with the builder below. It also adds the MCP peers to `package.json`. The server loads the catalog file in Node, so that file must not import Angular; the schematic warns when it does. Without `--catalog` and `--registry` it uses the Angular Material catalog when `ngx-json-render-material` is installed, with its theme, fonts and CSP, and otherwise writes a starter catalog and registry into the new project.
|
|
776
|
+
|
|
777
|
+
### Building it
|
|
778
|
+
|
|
779
|
+
A host loads the view as one HTML document with nowhere to fetch chunks from, so the Angular build has to be folded into a single page. The `ngx-json-render:mcp-app` builder does that, and bundles the server next to it. The schematic above sets it up; by hand, give the view its own application project (`ng generate application mcp-view`), then add a target to it in `angular.json`:
|
|
780
|
+
|
|
781
|
+
```json
|
|
782
|
+
"mcp": {
|
|
783
|
+
"builder": "ngx-json-render:mcp-app",
|
|
784
|
+
"options": {
|
|
785
|
+
"buildTarget": "mcp-view:build:production",
|
|
786
|
+
"outputPath": "dist/mcp-app",
|
|
787
|
+
"server": "projects/mcp-view/server.ts"
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
```
|
|
791
|
+
|
|
792
|
+
`ng run mcp-view:mcp` runs the application build, then writes `dist/mcp-app/view.html` with every script and stylesheet inlined, and the fonts and images the stylesheets use as data URLs, and `dist/mcp-app/server.mjs` with its dependencies bundled in, so `node dist/mcp-app/server.mjs` runs anywhere without `node_modules`. Files the page cannot reach, such as an image referenced only from a template, are listed as a warning: point at them from a stylesheet or from an `https:` origin in `csp`. The server bundle is not type-checked; run `tsc --noEmit` on it first if you want that. Options: `server` (leave it out to build the view only), `tsConfig` for the server, and `externalPackages: true` to keep `node_modules` packages out of `server.mjs`.
|
|
793
|
+
|
|
794
|
+
[`projects/mcp-app`](https://github.com/shteynu/ngx-json-render/tree/main/projects/mcp-app) is a complete example with the Material catalog: the view, built with this builder, and this server, the one behind `https://ngx-json-render.vercel.app/mcp`.
|
|
768
795
|
|
|
769
796
|
## Devtools
|
|
770
797
|
|
|
@@ -1204,9 +1231,11 @@ Registry & schema: `defineRegistry`, `createStoreSetState`, `schema`, and the ca
|
|
|
1204
1231
|
|
|
1205
1232
|
Schema alone (`ngx-json-render/schema`): `schema`, `AngularSchema`, `AngularSpec`, with no Angular behind them, for a server that defines a catalog.
|
|
1206
1233
|
|
|
1207
|
-
MCP Apps (`ngx-json-render/mcp`): `injectJsonRenderApp`, `parseSpecFromToolResult`, `messageText`, and the types `JsonRenderApp`, `JsonRenderAppOptions`. Needs the optional peers `@modelcontextprotocol/ext-apps` and `@modelcontextprotocol/sdk`.
|
|
1234
|
+
MCP Apps (`ngx-json-render/mcp`): `injectJsonRenderApp`, `parseSpecFromToolResult`, `messageText`, and the types `JsonRenderApp`, `JsonRenderAppOptions`, `MessageOutcome`. Needs the optional peers `@modelcontextprotocol/ext-apps` and `@modelcontextprotocol/sdk`.
|
|
1235
|
+
|
|
1236
|
+
MCP App server (`ngx-json-render/mcp/server`): `createRenderUiServer`, `handleRenderUiRequest`, `renderUiDescription`, `specInputSchema`, `specOutputSchema`, `specProblems`, `DESCRIPTION_LIMIT`, `withSendMessage`, `sendMessageAction`, and the types `RenderUiServerOptions`, `RenderUiDescriptionOptions`. No Angular; the same optional peers.
|
|
1208
1237
|
|
|
1209
|
-
MCP App
|
|
1238
|
+
MCP App builder (`ngx-json-render:mcp-app`): the view's application build as one `view.html`, and the server bundled into `server.mjs`. Schematic `ng generate ngx-json-render:mcp-app` (`--name`, `--catalog`, `--registry`): the view project, its server and the `mcp` target.
|
|
1210
1239
|
|
|
1211
1240
|
Router (`ngx-json-render/router`): `injectRouterNavigate`, and the type `RouterNavigateOptions`. Needs the optional peer `@angular/router`, which an Angular app on the router already has.
|
|
1212
1241
|
|
|
@@ -1,8 +1,47 @@
|
|
|
1
|
+
import { defineCatalog, validateSpec } from '@json-render/core';
|
|
2
|
+
import { z } from 'zod';
|
|
1
3
|
import { registerAppTool, registerAppResource, RESOURCE_MIME_TYPE } from '@modelcontextprotocol/ext-apps/server';
|
|
2
4
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
5
|
import { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
+
|
|
7
|
+
// Actions that only make sense inside an MCP Apps host. A catalog stays
|
|
8
|
+
// host-neutral; the server adds these to the catalog it describes to the
|
|
9
|
+
// model, and the view handles them (`injectJsonRenderApp().handlers` in
|
|
10
|
+
// `ngx-json-render/mcp`).
|
|
11
|
+
/**
|
|
12
|
+
* `sendMessage`: post a message to the chat as the user, so the conversation
|
|
13
|
+
* carries on from what was clicked in the view.
|
|
14
|
+
*/
|
|
15
|
+
const sendMessageAction = {
|
|
16
|
+
params: z.object({
|
|
17
|
+
text: z.string(),
|
|
18
|
+
data: z.record(z.string(), z.unknown()).optional(),
|
|
19
|
+
}),
|
|
20
|
+
description: 'Send a message to you, the assistant, as the user, so the conversation continues from the UI. ' +
|
|
21
|
+
'Params: { "text": "what the user chose or asks for", "data"?: { "$state": "/path" } }. `data` is optional and is attached as JSON; ' +
|
|
22
|
+
'make it a single reference to a state object, because references nested inside `data` are not resolved. ' +
|
|
23
|
+
'Use it for choices and follow-ups ("Approve", "Show more", a picked option). ' +
|
|
24
|
+
'To send a form, gate it with submitForm: ' +
|
|
25
|
+
'{ "action": "submitForm", "params": { "action": "sendMessage", "params": { "text": "Submit the sign-up form", "data": { "$state": "/form" } } } }. ' +
|
|
26
|
+
"The host puts the message in the chat as the user's (Claude lets the user review it and send it), so write `text` in their voice.",
|
|
27
|
+
};
|
|
28
|
+
const withSendMessageCache = new WeakMap();
|
|
29
|
+
/**
|
|
30
|
+
* `catalog` plus the {@link sendMessageAction}, the same object for the same
|
|
31
|
+
* catalog, so the input schema built from it is built once.
|
|
32
|
+
*/
|
|
33
|
+
function withSendMessage(catalog) {
|
|
34
|
+
let extended = withSendMessageCache.get(catalog);
|
|
35
|
+
if (!extended) {
|
|
36
|
+
const data = catalog.data;
|
|
37
|
+
extended = defineCatalog(catalog.schema, {
|
|
38
|
+
...data,
|
|
39
|
+
actions: { ...data.actions, sendMessage: sendMessageAction },
|
|
40
|
+
});
|
|
41
|
+
withSendMessageCache.set(catalog, extended);
|
|
42
|
+
}
|
|
43
|
+
return extended;
|
|
44
|
+
}
|
|
6
45
|
|
|
7
46
|
// What the model sees of the `render-ui` tool: a short description and an
|
|
8
47
|
// input schema that carries the catalog.
|
|
@@ -317,7 +356,10 @@ function walk(value, path, visit) {
|
|
|
317
356
|
* rejected spec goes back to the model as an error it can fix.
|
|
318
357
|
*/
|
|
319
358
|
function createRenderUiServer(options) {
|
|
320
|
-
const
|
|
359
|
+
const catalog = options.sendMessage
|
|
360
|
+
? withSendMessage(options.catalog)
|
|
361
|
+
: options.catalog;
|
|
362
|
+
const { html, name = 'ngx-json-render', version = '0.0.0', toolName = 'render-ui', title = 'Render UI', description = renderUiDescription(catalog), csp, widgetDomain, } = options;
|
|
321
363
|
const resourceUri = `ui://${toolName}/view.html`;
|
|
322
364
|
const server = new McpServer({ name, version });
|
|
323
365
|
registerAppTool(server, toolName, {
|
|
@@ -420,5 +462,5 @@ const CORS_HEADERS = {
|
|
|
420
462
|
* Generated bundle index. Do not edit.
|
|
421
463
|
*/
|
|
422
464
|
|
|
423
|
-
export { DESCRIPTION_LIMIT, createRenderUiServer, handleRenderUiRequest, renderUiDescription, specInputSchema, specOutputSchema, specProblems };
|
|
465
|
+
export { DESCRIPTION_LIMIT, createRenderUiServer, handleRenderUiRequest, renderUiDescription, sendMessageAction, specInputSchema, specOutputSchema, specProblems, withSendMessage };
|
|
424
466
|
//# sourceMappingURL=ngx-json-render-mcp-server.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngx-json-render-mcp-server.mjs","sources":["../../../projects/ngx-json-render/mcp/server/src/tool.ts","../../../projects/ngx-json-render/mcp/server/src/server.ts","../../../projects/ngx-json-render/mcp/server/src/public-api.ts","../../../projects/ngx-json-render/mcp/server/src/ngx-json-render-mcp-server.ts"],"sourcesContent":["// What the model sees of the `render-ui` tool: a short description and an\n// input schema that carries the catalog.\n//\n// `catalog.prompt()` is written to be a system prompt, and it is about 26 000\n// characters long. Claude shows a tool's description to the model cut off\n// after roughly 2 000 characters, which `catalog.prompt()` spends on its\n// JSON Patch streaming instructions: the model never saw a component, a prop\n// or an action. So the description here stays short, and each component's\n// props and each action's params go into the input schema, where they are\n// also enforced.\nimport { type Catalog, type Spec, validateSpec } from '@json-render/core';\nimport { z } from 'zod';\n\n/** Claude cuts a tool description off after roughly this many characters. */\nexport const DESCRIPTION_LIMIT = 2000;\n\n/** Options for {@link renderUiDescription}. */\nexport interface RenderUiDescriptionOptions {\n /** What the tool draws, as the first sentence names it. Default `'UI'`. */\n ui?: string;\n}\n\n/**\n * The `render-ui` tool description for `catalog`: how a spec is put\n * together, in under {@link DESCRIPTION_LIMIT} characters. The components,\n * props and actions are left to the input schema. The paragraph on\n * `sendMessage` is included only when the catalog defines that action, since\n * the view has to handle it.\n */\nexport function renderUiDescription(\n catalog: Catalog,\n { ui = 'UI' }: RenderUiDescriptionOptions = {},\n): string {\n const actions = (catalog.data as CatalogData).actions ?? {};\n const lines = [\n `Render an interactive ${ui} inline in the chat. Pass \\`spec\\`: one complete json-render spec object (not JSON Patch lines). The input schema lists every component, its props and allowed values, and every action; use only those.`,\n '',\n '- `root` is the key of the top element. `elements` is a flat map of key → { type, props, children: [child keys] }; every child key must exist in `elements`.',\n '- `state` holds the data. Read it with {\"$state\":\"/path\"} rather than repeating values in props. Data-backed UI always needs `state`; use realistic sample data.',\n '- A prop can be dynamic: {\"$state\":\"/p\"} reads, {\"$bindState\":\"/p\"} binds a form field both ways, {\"$template\":\"Hi ${/name}\"} interpolates, {\"$cond\":{\"$state\":\"/p\"},\"$then\":a,\"$else\":b} picks.',\n '- Lists: on a container, \"repeat\":{\"statePath\":\"/items\",\"key\":\"id\"}; its children render once per item and read {\"$item\":\"field\"}, or ${field} inside a $template.',\n '- `visible` and `on` sit next to `props`, never inside: \"visible\":{\"$state\":\"/tab\",\"eq\":\"home\"}, \"on\":{\"press\":{\"action\":\"setState\",\"params\":{\"statePath\":\"/tab\",\"value\":\"home\"}}}.',\n '- Forms: give inputs props.validation {\"checks\":[{\"type\":\"required\",\"message\":\"...\"}]} and submit with submitForm, which runs its inner action only when every field is valid.',\n ];\n if ('sendMessage' in actions) {\n lines.push(\n '- To continue the conversation from a button, use sendMessage: {\"action\":\"sendMessage\",\"params\":{\"text\":\"Approve release 2.4.0\",\"data\":{\"$state\":\"/release\"}}}. The host puts `text` (and `data` as JSON) in the chat as the user\\'s message, so write it in their voice. For a form: {\"action\":\"submitForm\",\"params\":{\"action\":\"sendMessage\",\"params\":{\"text\":\"Sign me up\",\"data\":{\"$state\":\"/form\"}}}}.',\n );\n }\n return lines.join('\\n');\n}\n\n/** The parts of a catalog's data this file reads. */\ninterface CatalogData {\n components: Record<string, { props: z.ZodType; description: string }>;\n actions?: Record<string, { params?: z.ZodType; description: string }>;\n}\n\n/** A prop value computed from state, such as `{ \"$state\": \"/path\" }`. */\nconst dynamic = z\n .looseObject({})\n .refine((value) => Object.keys(value).some((key) => key.startsWith('$')), {\n message:\n 'Expected a dynamic value such as {\"$state\": \"/path\"} or {\"$bindState\": \"/path\"}',\n })\n .meta({\n // An id makes the JSON Schema define it once and refer to it.\n id: 'DynamicValue',\n description:\n 'Dynamic value: {\"$state\"}, {\"$bindState\"}, {\"$bindItem\"}, {\"$item\"}, {\"$index\"}, {\"$template\"} or {\"$cond\",\"$then\",\"$else\"}.',\n });\n\n/**\n * A union's error that says why its first branch failed (say, the allowed\n * enum values), rather than just that no branch matched.\n */\nfunction firstBranchError(suffix = '') {\n return (issue: z.core.$ZodRawIssue) => {\n if (issue.code !== 'invalid_union') return undefined;\n const reasons = issue.errors[0]?.map((error) => error.message);\n return reasons?.length ? reasons.join('; ') + suffix : undefined;\n };\n}\n\n/** `shape`'s fields, each of which may also be a dynamic value; nothing else. */\nfunction allowDynamic(schema: z.ZodType) {\n const shape = (schema as z.ZodObject).shape ?? {};\n return z.strictObject(\n Object.fromEntries(\n Object.entries(shape).map(([key, field]) => {\n const optional = field instanceof z.ZodOptional;\n const value = z.union([optional ? field.unwrap() : field, dynamic], {\n error: firstBranchError(\n ', or a dynamic value such as {\"$state\": \"/path\"}',\n ),\n });\n return [key, optional ? value.optional() : value];\n }),\n ),\n );\n}\n\n/**\n * An action binding, or several, with one branch per action so the params\n * of the catalog's own actions are checked.\n */\nfunction actionBinding(catalog: Catalog) {\n const common = {\n confirm: z.unknown().optional(),\n onSuccess: z.unknown().optional(),\n onError: z.unknown().optional(),\n preventDefault: z.boolean().optional(),\n };\n const builtIn = catalog.schema.builtInActions ?? [];\n const actions = (catalog.data as CatalogData).actions ?? {};\n const bindings: z.ZodObject[] = Object.entries(actions).map(\n ([name, action]) =>\n z\n .strictObject({\n action: z.literal(name),\n params: allowDynamic(action.params ?? z.object({})),\n ...common,\n })\n .describe(`${name}: ${action.description}`),\n );\n if (builtIn.length > 0) {\n const names = builtIn.map((action) => action.name) as [string, ...string[]];\n bindings.push(\n z\n .strictObject({\n action: z.enum(names),\n params: z.record(z.string(), z.unknown()).optional(),\n ...common,\n })\n .describe(\n builtIn\n .map((action) => `${action.name}: ${action.description}`)\n .join('\\n'),\n ),\n );\n }\n const binding = z\n .discriminatedUnion('action', bindings as [z.ZodObject, ...z.ZodObject[]], {\n error: `Unknown action. Use one of: ${[...Object.keys(actions), ...builtIn.map((action) => action.name)].join(', ')}.`,\n })\n .meta({ id: 'ActionBinding' });\n return z.union([binding, z.array(binding)], { error: firstBranchError() });\n}\n\n/**\n * The tool's input schema: a spec whose elements are typed per component.\n *\n * `catalog.zodSchema()` types an element's `type` but not its props, and it\n * has no `state`, `on` or `watch`, so the MCP SDK, which parses tool\n * arguments with the input schema, stripped those and let any prop through.\n * Here each component's props are its catalog schema (with dynamic values\n * allowed and unknown props rejected), and each action's params are checked.\n */\nexport function specInputSchema(catalog: Catalog) {\n let schema = inputSchemas.get(catalog);\n if (!schema) {\n schema = buildSpecInputSchema(catalog);\n inputSchemas.set(catalog, schema);\n }\n return schema;\n}\n\n/**\n * The sentences every validatable component's description ends with, or \"\"\n * when there are none worth sharing.\n *\n * The catalog repeats its validation hint on each form field because\n * `catalog.prompt()` lists components one by one and shows no prop-level\n * descriptions. In the input schema the same ~600 characters would arrive\n * once per field component, so the schema says them once, on `elements`, and\n * each field points there. Found as the descriptions' common tail, cut back\n * to a sentence start, so the catalog does not have to export it.\n */\nfunction sharedValidationHint(components: CatalogData['components']): string {\n const descriptions = Object.values(components)\n .filter(\n (component) =>\n (component.props as Partial<z.ZodObject>).shape?.['validation'] !==\n undefined,\n )\n .map((component) => component.description);\n if (descriptions.length < 2) return '';\n\n let tail = descriptions[0];\n for (const description of descriptions.slice(1)) {\n let n = 0;\n while (\n n < tail.length &&\n n < description.length &&\n tail[tail.length - 1 - n] === description[description.length - 1 - n]\n ) {\n n++;\n }\n tail = tail.slice(tail.length - n);\n }\n // The shared tail usually starts part-way through the sentence before it.\n const before = descriptions[0].slice(0, descriptions[0].length - tail.length);\n if (before !== '' && !/[.!?] $/.test(before)) {\n const next = tail.indexOf('. ');\n tail = next === -1 ? '' : tail.slice(next + 2);\n }\n return tail.length >= 200 ? tail : '';\n}\n\n/**\n * What `render-ui` returns as `structuredContent`: the spec it was given,\n * after the input schema filled in missing `children`. Only its outline,\n * since the input schema already carries the catalog and a second copy would\n * double what every `tools/list` sends the model. ChatGPT flags a tool\n * without an output schema (\"Output schema recommended\").\n */\nexport const specOutputSchema = z\n .looseObject({\n root: z.string(),\n elements: z.record(\n z.string(),\n z.looseObject({\n type: z.string(),\n props: z.record(z.string(), z.unknown()),\n children: z.array(z.string()),\n }),\n ),\n state: z.record(z.string(), z.unknown()).optional(),\n })\n .describe('The json-render spec that was rendered.');\n\n// The server is stateless, so it is created for every request; build each\n// catalog's schema once.\nconst inputSchemas = new WeakMap<\n Catalog,\n ReturnType<typeof buildSpecInputSchema>\n>();\n\nfunction buildSpecInputSchema(catalog: Catalog) {\n const bindings = actionBinding(catalog);\n // The fields every element shares, defined once in the JSON Schema.\n const shared = {\n visible: z\n .unknown()\n .meta({\n id: 'Visibility',\n description:\n 'Show the element only when this holds: {\"$state\":\"/p\",\"eq\":v} (or neq, gt, gte, lt, lte, not), an array of conditions (all), {\"$or\":[...]}, true or false.',\n })\n .optional(),\n repeat: z\n .strictObject({\n statePath: z.union([z.string(), dynamic]),\n key: z.string().optional(),\n })\n .meta({\n id: 'Repeat',\n description:\n 'Render the children once per item of the state array at statePath; they read {\"$item\":\"field\"} and {\"$index\":true}.',\n })\n .optional(),\n on: z\n .record(z.string(), bindings)\n .meta({\n id: 'Events',\n description:\n 'Event name (such as \"press\", \"change\" or \"submit\") → action binding.',\n })\n .optional(),\n watch: z\n .record(z.string(), bindings)\n .meta({\n id: 'Watchers',\n description:\n 'State path → action binding, run when the value at that path changes.',\n })\n .optional(),\n };\n const { components } = catalog.data as CatalogData;\n const hint = sharedValidationHint(components);\n const elements = Object.entries(components).map(([type, component]) => {\n const description =\n hint && component.description.endsWith(hint)\n ? `${component.description.slice(0, -hint.length)}Validation: see \\`elements\\`.`\n : component.description;\n return z\n .strictObject({\n type: z.literal(type),\n props: allowDynamic(component.props),\n children: z.array(z.string()).default([]),\n ...shared,\n })\n .describe(`${type}: ${description}`);\n });\n return z.strictObject({\n root: z.string().describe('Key of the top element in `elements`.'),\n elements: z\n .record(\n z.string(),\n z.discriminatedUnion(\n 'type',\n elements as unknown as [z.ZodObject, ...z.ZodObject[]],\n ),\n )\n .describe(\n hint\n ? `Elements by key. On every component that takes \\`validation\\`: ${hint}`\n : 'Elements by key.',\n ),\n state: z\n .record(z.string(), z.unknown())\n .optional()\n .describe(\n 'Initial state model; bindings read it with {\"$state\": \"/path\"}.',\n ),\n });\n}\n\n/**\n * What the input schema cannot express: a root and children that exist,\n * repeat with a template child, valid visibility conditions, and `$template`\n * placeholders that read a repeat item. Returns the problems as text for the\n * model, or `undefined` when there are none.\n */\nexport function specProblems(spec: object): string | undefined {\n // The input schema has already checked the shape `Spec` describes.\n const { issues } = validateSpec(spec as Spec);\n const errors = issues.filter((issue) => issue.severity === 'error');\n const messages = [\n ...errors.map((issue) => issue.message),\n ...itemTemplateProblems(spec as Spec),\n ];\n return messages.length > 0\n ? [\n 'The generated UI spec has the following errors:',\n ...messages.map((message) => `- ${message}`),\n ].join('\\n')\n : undefined;\n}\n\n/**\n * `${$item/field}` placeholders in a `$template`. A `$template` reads an\n * absolute `${/path}` from state and a bare `${field}` from the repeat item\n * first, so `$item/field` is looked up as the state path `/$item/field`,\n * finds nothing and renders as an empty string, without an error anywhere.\n */\nfunction itemTemplateProblems(spec: Spec): string[] {\n const problems: string[] = [];\n for (const [key, { props, on, watch }] of Object.entries(spec.elements)) {\n walk({ props, on, watch }, '', (template, path) => {\n for (const [, placeholder, field] of template.matchAll(\n ITEM_PLACEHOLDER,\n )) {\n problems.push(\n `Element \"${key}\" ${path}: inside $template write \\${${field.replaceAll('.', '/')}}, not \\${${placeholder}}`,\n );\n }\n });\n }\n return problems;\n}\n\n/** `${$item/field}` or `${$item.field}`; the groups are the placeholder and the field. */\nconst ITEM_PLACEHOLDER = /\\$\\{(\\$item[/.]([^}]+))\\}/g;\n\n/** Calls `visit` with every `$template` string under `value` and where it sits. */\nfunction walk(\n value: unknown,\n path: string,\n visit: (template: string, path: string) => void,\n): void {\n if (Array.isArray(value)) {\n value.forEach((item, index) => walk(item, `${path}[${index}]`, visit));\n } else if (value !== null && typeof value === 'object') {\n for (const [key, child] of Object.entries(value)) {\n if (key === '$template' && typeof child === 'string') {\n visit(child, path);\n } else {\n walk(child, path ? `${path}.${key}` : key, visit);\n }\n }\n }\n}\n","// The MCP server half of an MCP App: one `render-ui` tool that takes a\n// json-render spec, and the `ui://` view a host renders it in. Registered the\n// way upstream's `createMcpApp` (`@json-render/mcp`) does it, same tool name\n// and same view resource, with a short description and a typed input schema\n// (`tool.ts`) in place of `catalog.prompt()` and `catalog.zodSchema()`.\n//\n// Nothing here is Angular-specific: the Angular part is the view, passed in\n// as one HTML document.\nimport type { Catalog } from '@json-render/core';\nimport {\n RESOURCE_MIME_TYPE,\n registerAppResource,\n registerAppTool,\n} from '@modelcontextprotocol/ext-apps/server';\nimport type { McpUiResourceCsp } from '@modelcontextprotocol/ext-apps';\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js';\nimport {\n renderUiDescription,\n specInputSchema,\n specOutputSchema,\n specProblems,\n} from './tool';\n\n/** Options for {@link createRenderUiServer}. */\nexport interface RenderUiServerOptions {\n /** The components and actions the model may use. */\n catalog: Catalog;\n /** The view: one self-contained HTML document that renders the spec. */\n html: string;\n /** The server's name, as hosts list it. Default `'ngx-json-render'`. */\n name?: string;\n /** The server's version. Default `'0.0.0'`. */\n version?: string;\n /** The tool's name; the view resource is `ui://<toolName>/view.html`. Default `'render-ui'`. */\n toolName?: string;\n /** The tool's title, as hosts show it. Default `'Render UI'`. */\n title?: string;\n /** The tool's description. Default: {@link renderUiDescription} for the catalog. */\n description?: string;\n /**\n * The origins the view may load from (`resourceDomains`) and connect to\n * (`connectDomains`). Left out, the host's default applies, which the MCP\n * Apps spec defines as none.\n */\n csp?: McpUiResourceCsp;\n /**\n * The origin ChatGPT derives the view's sandbox origin from, which its\n * plugin directory requires, unique per plugin. Set through the OpenAI\n * alias rather than `_meta.ui.domain`, because that key's format is up to\n * each host and Claude expects a `{hash}.claudemcpcontent.com` value there.\n */\n widgetDomain?: string;\n}\n\n/**\n * An MCP server with a `render-ui` tool for `catalog` and its `ui://` view.\n * Connect it to any transport: `StdioServerTransport` for a local host,\n * or {@link handleRenderUiRequest} for Streamable HTTP.\n *\n * The tool checks a spec against the catalog before it echoes it back. A\n * rejected spec goes back to the model as an error it can fix.\n */\nexport function createRenderUiServer(options: RenderUiServerOptions) {\n const {\n catalog,\n html,\n name = 'ngx-json-render',\n version = '0.0.0',\n toolName = 'render-ui',\n title = 'Render UI',\n description = renderUiDescription(catalog),\n csp,\n widgetDomain,\n } = options;\n const resourceUri = `ui://${toolName}/view.html`;\n const server = new McpServer({ name, version });\n registerAppTool(\n server,\n toolName,\n {\n title,\n description,\n inputSchema: { spec: specInputSchema(catalog) },\n outputSchema: { spec: specOutputSchema },\n // It only echoes the spec back for the view to draw: nothing is read\n // from or written to any system.\n annotations: {\n title,\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: false,\n },\n _meta: { ui: { resourceUri } },\n },\n // The SDK has already parsed `spec` against the input schema; what is\n // left is the structure between elements. The text keeps upstream's\n // shape, the bare spec, which is what views built on\n // `@json-render/mcp/app` read.\n async ({ spec }) => {\n const problems = specProblems(spec);\n return problems\n ? { isError: true, content: [{ type: 'text', text: problems }] }\n : {\n content: [{ type: 'text', text: JSON.stringify(spec) }],\n structuredContent: { spec },\n };\n },\n );\n const meta: Record<string, unknown> = {};\n if (csp) meta['ui'] = { csp };\n if (widgetDomain) meta['openai/widgetDomain'] = widgetDomain;\n registerAppResource(\n server,\n resourceUri,\n resourceUri,\n { mimeType: RESOURCE_MIME_TYPE },\n async () => ({\n contents: [\n {\n uri: resourceUri,\n mimeType: RESOURCE_MIME_TYPE,\n text: html,\n ...(Object.keys(meta).length > 0 && { _meta: meta }),\n },\n ],\n }),\n );\n return server;\n}\n\n/**\n * Serve one Streamable HTTP request, statelessly: a fresh server and\n * transport per request, so it runs unchanged on a serverless or edge\n * function (anything with web `Request` and `Response`). Answers CORS,\n * because browser-based clients such as MCP Inspector call cross-origin.\n */\nexport async function handleRenderUiRequest(\n options: RenderUiServerOptions,\n request: Request,\n): Promise<Response> {\n if (request.method === 'OPTIONS') {\n return new Response(null, { status: 204, headers: CORS_HEADERS });\n }\n const server = createRenderUiServer(options);\n const transport = new WebStandardStreamableHTTPServerTransport({\n sessionIdGenerator: undefined,\n // One JSON body per request: nothing here streams, and a serverless\n // function returns as soon as the response is complete.\n enableJsonResponse: true,\n });\n await server.connect(transport);\n try {\n const response = await transport.handleRequest(request);\n const headers = new Headers(response.headers);\n for (const [key, value] of Object.entries(CORS_HEADERS)) {\n headers.set(key, value);\n }\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n } finally {\n await transport.close();\n await server.close();\n }\n}\n\nconst CORS_HEADERS: Record<string, string> = {\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, DELETE, OPTIONS',\n 'Access-Control-Allow-Headers':\n 'Content-Type, Accept, Authorization, Mcp-Session-Id, Mcp-Protocol-Version, Last-Event-ID',\n 'Access-Control-Expose-Headers': 'Mcp-Session-Id',\n};\n","/*\n * Public API Surface of ngx-json-render/mcp/server\n *\n * The server half of an MCP App for a json-render catalog: the `render-ui`\n * tool and the `ui://` view resource. Angular-free, so it runs in Node or on\n * an edge function; the view it serves is built with `ngx-json-render/mcp`.\n */\n\nexport {\n createRenderUiServer,\n handleRenderUiRequest,\n type RenderUiServerOptions,\n} from './server';\nexport {\n DESCRIPTION_LIMIT,\n renderUiDescription,\n specInputSchema,\n specOutputSchema,\n specProblems,\n type RenderUiDescriptionOptions,\n} from './tool';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ngx-json-render-mcp-server.mjs","sources":["../../../projects/ngx-json-render/mcp/server/src/actions.ts","../../../projects/ngx-json-render/mcp/server/src/tool.ts","../../../projects/ngx-json-render/mcp/server/src/server.ts","../../../projects/ngx-json-render/mcp/server/src/public-api.ts","../../../projects/ngx-json-render/mcp/server/src/ngx-json-render-mcp-server.ts"],"sourcesContent":["// Actions that only make sense inside an MCP Apps host. A catalog stays\n// host-neutral; the server adds these to the catalog it describes to the\n// model, and the view handles them (`injectJsonRenderApp().handlers` in\n// `ngx-json-render/mcp`).\nimport { type Catalog, defineCatalog } from '@json-render/core';\nimport { z } from 'zod';\n\n/**\n * `sendMessage`: post a message to the chat as the user, so the conversation\n * carries on from what was clicked in the view.\n */\nexport const sendMessageAction = {\n params: z.object({\n text: z.string(),\n data: z.record(z.string(), z.unknown()).optional(),\n }),\n description:\n 'Send a message to you, the assistant, as the user, so the conversation continues from the UI. ' +\n 'Params: { \"text\": \"what the user chose or asks for\", \"data\"?: { \"$state\": \"/path\" } }. `data` is optional and is attached as JSON; ' +\n 'make it a single reference to a state object, because references nested inside `data` are not resolved. ' +\n 'Use it for choices and follow-ups (\"Approve\", \"Show more\", a picked option). ' +\n 'To send a form, gate it with submitForm: ' +\n '{ \"action\": \"submitForm\", \"params\": { \"action\": \"sendMessage\", \"params\": { \"text\": \"Submit the sign-up form\", \"data\": { \"$state\": \"/form\" } } } }. ' +\n \"The host puts the message in the chat as the user's (Claude lets the user review it and send it), so write `text` in their voice.\",\n};\n\nconst withSendMessageCache = new WeakMap<Catalog, Catalog>();\n\n/**\n * `catalog` plus the {@link sendMessageAction}, the same object for the same\n * catalog, so the input schema built from it is built once.\n */\nexport function withSendMessage<C extends Catalog>(catalog: C): C {\n let extended = withSendMessageCache.get(catalog);\n if (!extended) {\n const data = catalog.data as {\n actions?: Record<string, unknown>;\n } & Record<string, unknown>;\n extended = defineCatalog(catalog.schema, {\n ...data,\n actions: { ...data.actions, sendMessage: sendMessageAction },\n } as Parameters<typeof defineCatalog>[1]);\n withSendMessageCache.set(catalog, extended);\n }\n return extended as C;\n}\n","// What the model sees of the `render-ui` tool: a short description and an\n// input schema that carries the catalog.\n//\n// `catalog.prompt()` is written to be a system prompt, and it is about 26 000\n// characters long. Claude shows a tool's description to the model cut off\n// after roughly 2 000 characters, which `catalog.prompt()` spends on its\n// JSON Patch streaming instructions: the model never saw a component, a prop\n// or an action. So the description here stays short, and each component's\n// props and each action's params go into the input schema, where they are\n// also enforced.\nimport { type Catalog, type Spec, validateSpec } from '@json-render/core';\nimport { z } from 'zod';\n\n/** Claude cuts a tool description off after roughly this many characters. */\nexport const DESCRIPTION_LIMIT = 2000;\n\n/** Options for {@link renderUiDescription}. */\nexport interface RenderUiDescriptionOptions {\n /** What the tool draws, as the first sentence names it. Default `'UI'`. */\n ui?: string;\n}\n\n/**\n * The `render-ui` tool description for `catalog`: how a spec is put\n * together, in under {@link DESCRIPTION_LIMIT} characters. The components,\n * props and actions are left to the input schema. The paragraph on\n * `sendMessage` is included only when the catalog defines that action, since\n * the view has to handle it.\n */\nexport function renderUiDescription(\n catalog: Catalog,\n { ui = 'UI' }: RenderUiDescriptionOptions = {},\n): string {\n const actions = (catalog.data as CatalogData).actions ?? {};\n const lines = [\n `Render an interactive ${ui} inline in the chat. Pass \\`spec\\`: one complete json-render spec object (not JSON Patch lines). The input schema lists every component, its props and allowed values, and every action; use only those.`,\n '',\n '- `root` is the key of the top element. `elements` is a flat map of key → { type, props, children: [child keys] }; every child key must exist in `elements`.',\n '- `state` holds the data. Read it with {\"$state\":\"/path\"} rather than repeating values in props. Data-backed UI always needs `state`; use realistic sample data.',\n '- A prop can be dynamic: {\"$state\":\"/p\"} reads, {\"$bindState\":\"/p\"} binds a form field both ways, {\"$template\":\"Hi ${/name}\"} interpolates, {\"$cond\":{\"$state\":\"/p\"},\"$then\":a,\"$else\":b} picks.',\n '- Lists: on a container, \"repeat\":{\"statePath\":\"/items\",\"key\":\"id\"}; its children render once per item and read {\"$item\":\"field\"}, or ${field} inside a $template.',\n '- `visible` and `on` sit next to `props`, never inside: \"visible\":{\"$state\":\"/tab\",\"eq\":\"home\"}, \"on\":{\"press\":{\"action\":\"setState\",\"params\":{\"statePath\":\"/tab\",\"value\":\"home\"}}}.',\n '- Forms: give inputs props.validation {\"checks\":[{\"type\":\"required\",\"message\":\"...\"}]} and submit with submitForm, which runs its inner action only when every field is valid.',\n ];\n if ('sendMessage' in actions) {\n lines.push(\n '- To continue the conversation from a button, use sendMessage: {\"action\":\"sendMessage\",\"params\":{\"text\":\"Approve release 2.4.0\",\"data\":{\"$state\":\"/release\"}}}. The host puts `text` (and `data` as JSON) in the chat as the user\\'s message, so write it in their voice. For a form: {\"action\":\"submitForm\",\"params\":{\"action\":\"sendMessage\",\"params\":{\"text\":\"Sign me up\",\"data\":{\"$state\":\"/form\"}}}}.',\n );\n }\n return lines.join('\\n');\n}\n\n/** The parts of a catalog's data this file reads. */\ninterface CatalogData {\n components: Record<string, { props: z.ZodType; description: string }>;\n actions?: Record<string, { params?: z.ZodType; description: string }>;\n}\n\n/** A prop value computed from state, such as `{ \"$state\": \"/path\" }`. */\nconst dynamic = z\n .looseObject({})\n .refine((value) => Object.keys(value).some((key) => key.startsWith('$')), {\n message:\n 'Expected a dynamic value such as {\"$state\": \"/path\"} or {\"$bindState\": \"/path\"}',\n })\n .meta({\n // An id makes the JSON Schema define it once and refer to it.\n id: 'DynamicValue',\n description:\n 'Dynamic value: {\"$state\"}, {\"$bindState\"}, {\"$bindItem\"}, {\"$item\"}, {\"$index\"}, {\"$template\"} or {\"$cond\",\"$then\",\"$else\"}.',\n });\n\n/**\n * A union's error that says why its first branch failed (say, the allowed\n * enum values), rather than just that no branch matched.\n */\nfunction firstBranchError(suffix = '') {\n return (issue: z.core.$ZodRawIssue) => {\n if (issue.code !== 'invalid_union') return undefined;\n const reasons = issue.errors[0]?.map((error) => error.message);\n return reasons?.length ? reasons.join('; ') + suffix : undefined;\n };\n}\n\n/** `shape`'s fields, each of which may also be a dynamic value; nothing else. */\nfunction allowDynamic(schema: z.ZodType) {\n const shape = (schema as z.ZodObject).shape ?? {};\n return z.strictObject(\n Object.fromEntries(\n Object.entries(shape).map(([key, field]) => {\n const optional = field instanceof z.ZodOptional;\n const value = z.union([optional ? field.unwrap() : field, dynamic], {\n error: firstBranchError(\n ', or a dynamic value such as {\"$state\": \"/path\"}',\n ),\n });\n return [key, optional ? value.optional() : value];\n }),\n ),\n );\n}\n\n/**\n * An action binding, or several, with one branch per action so the params\n * of the catalog's own actions are checked.\n */\nfunction actionBinding(catalog: Catalog) {\n const common = {\n confirm: z.unknown().optional(),\n onSuccess: z.unknown().optional(),\n onError: z.unknown().optional(),\n preventDefault: z.boolean().optional(),\n };\n const builtIn = catalog.schema.builtInActions ?? [];\n const actions = (catalog.data as CatalogData).actions ?? {};\n const bindings: z.ZodObject[] = Object.entries(actions).map(\n ([name, action]) =>\n z\n .strictObject({\n action: z.literal(name),\n params: allowDynamic(action.params ?? z.object({})),\n ...common,\n })\n .describe(`${name}: ${action.description}`),\n );\n if (builtIn.length > 0) {\n const names = builtIn.map((action) => action.name) as [string, ...string[]];\n bindings.push(\n z\n .strictObject({\n action: z.enum(names),\n params: z.record(z.string(), z.unknown()).optional(),\n ...common,\n })\n .describe(\n builtIn\n .map((action) => `${action.name}: ${action.description}`)\n .join('\\n'),\n ),\n );\n }\n const binding = z\n .discriminatedUnion('action', bindings as [z.ZodObject, ...z.ZodObject[]], {\n error: `Unknown action. Use one of: ${[...Object.keys(actions), ...builtIn.map((action) => action.name)].join(', ')}.`,\n })\n .meta({ id: 'ActionBinding' });\n return z.union([binding, z.array(binding)], { error: firstBranchError() });\n}\n\n/**\n * The tool's input schema: a spec whose elements are typed per component.\n *\n * `catalog.zodSchema()` types an element's `type` but not its props, and it\n * has no `state`, `on` or `watch`, so the MCP SDK, which parses tool\n * arguments with the input schema, stripped those and let any prop through.\n * Here each component's props are its catalog schema (with dynamic values\n * allowed and unknown props rejected), and each action's params are checked.\n */\nexport function specInputSchema(catalog: Catalog) {\n let schema = inputSchemas.get(catalog);\n if (!schema) {\n schema = buildSpecInputSchema(catalog);\n inputSchemas.set(catalog, schema);\n }\n return schema;\n}\n\n/**\n * The sentences every validatable component's description ends with, or \"\"\n * when there are none worth sharing.\n *\n * The catalog repeats its validation hint on each form field because\n * `catalog.prompt()` lists components one by one and shows no prop-level\n * descriptions. In the input schema the same ~600 characters would arrive\n * once per field component, so the schema says them once, on `elements`, and\n * each field points there. Found as the descriptions' common tail, cut back\n * to a sentence start, so the catalog does not have to export it.\n */\nfunction sharedValidationHint(components: CatalogData['components']): string {\n const descriptions = Object.values(components)\n .filter(\n (component) =>\n (component.props as Partial<z.ZodObject>).shape?.['validation'] !==\n undefined,\n )\n .map((component) => component.description);\n if (descriptions.length < 2) return '';\n\n let tail = descriptions[0];\n for (const description of descriptions.slice(1)) {\n let n = 0;\n while (\n n < tail.length &&\n n < description.length &&\n tail[tail.length - 1 - n] === description[description.length - 1 - n]\n ) {\n n++;\n }\n tail = tail.slice(tail.length - n);\n }\n // The shared tail usually starts part-way through the sentence before it.\n const before = descriptions[0].slice(0, descriptions[0].length - tail.length);\n if (before !== '' && !/[.!?] $/.test(before)) {\n const next = tail.indexOf('. ');\n tail = next === -1 ? '' : tail.slice(next + 2);\n }\n return tail.length >= 200 ? tail : '';\n}\n\n/**\n * What `render-ui` returns as `structuredContent`: the spec it was given,\n * after the input schema filled in missing `children`. Only its outline,\n * since the input schema already carries the catalog and a second copy would\n * double what every `tools/list` sends the model. ChatGPT flags a tool\n * without an output schema (\"Output schema recommended\").\n */\nexport const specOutputSchema = z\n .looseObject({\n root: z.string(),\n elements: z.record(\n z.string(),\n z.looseObject({\n type: z.string(),\n props: z.record(z.string(), z.unknown()),\n children: z.array(z.string()),\n }),\n ),\n state: z.record(z.string(), z.unknown()).optional(),\n })\n .describe('The json-render spec that was rendered.');\n\n// The server is stateless, so it is created for every request; build each\n// catalog's schema once.\nconst inputSchemas = new WeakMap<\n Catalog,\n ReturnType<typeof buildSpecInputSchema>\n>();\n\nfunction buildSpecInputSchema(catalog: Catalog) {\n const bindings = actionBinding(catalog);\n // The fields every element shares, defined once in the JSON Schema.\n const shared = {\n visible: z\n .unknown()\n .meta({\n id: 'Visibility',\n description:\n 'Show the element only when this holds: {\"$state\":\"/p\",\"eq\":v} (or neq, gt, gte, lt, lte, not), an array of conditions (all), {\"$or\":[...]}, true or false.',\n })\n .optional(),\n repeat: z\n .strictObject({\n statePath: z.union([z.string(), dynamic]),\n key: z.string().optional(),\n })\n .meta({\n id: 'Repeat',\n description:\n 'Render the children once per item of the state array at statePath; they read {\"$item\":\"field\"} and {\"$index\":true}.',\n })\n .optional(),\n on: z\n .record(z.string(), bindings)\n .meta({\n id: 'Events',\n description:\n 'Event name (such as \"press\", \"change\" or \"submit\") → action binding.',\n })\n .optional(),\n watch: z\n .record(z.string(), bindings)\n .meta({\n id: 'Watchers',\n description:\n 'State path → action binding, run when the value at that path changes.',\n })\n .optional(),\n };\n const { components } = catalog.data as CatalogData;\n const hint = sharedValidationHint(components);\n const elements = Object.entries(components).map(([type, component]) => {\n const description =\n hint && component.description.endsWith(hint)\n ? `${component.description.slice(0, -hint.length)}Validation: see \\`elements\\`.`\n : component.description;\n return z\n .strictObject({\n type: z.literal(type),\n props: allowDynamic(component.props),\n children: z.array(z.string()).default([]),\n ...shared,\n })\n .describe(`${type}: ${description}`);\n });\n return z.strictObject({\n root: z.string().describe('Key of the top element in `elements`.'),\n elements: z\n .record(\n z.string(),\n z.discriminatedUnion(\n 'type',\n elements as unknown as [z.ZodObject, ...z.ZodObject[]],\n ),\n )\n .describe(\n hint\n ? `Elements by key. On every component that takes \\`validation\\`: ${hint}`\n : 'Elements by key.',\n ),\n state: z\n .record(z.string(), z.unknown())\n .optional()\n .describe(\n 'Initial state model; bindings read it with {\"$state\": \"/path\"}.',\n ),\n });\n}\n\n/**\n * What the input schema cannot express: a root and children that exist,\n * repeat with a template child, valid visibility conditions, and `$template`\n * placeholders that read a repeat item. Returns the problems as text for the\n * model, or `undefined` when there are none.\n */\nexport function specProblems(spec: object): string | undefined {\n // The input schema has already checked the shape `Spec` describes.\n const { issues } = validateSpec(spec as Spec);\n const errors = issues.filter((issue) => issue.severity === 'error');\n const messages = [\n ...errors.map((issue) => issue.message),\n ...itemTemplateProblems(spec as Spec),\n ];\n return messages.length > 0\n ? [\n 'The generated UI spec has the following errors:',\n ...messages.map((message) => `- ${message}`),\n ].join('\\n')\n : undefined;\n}\n\n/**\n * `${$item/field}` placeholders in a `$template`. A `$template` reads an\n * absolute `${/path}` from state and a bare `${field}` from the repeat item\n * first, so `$item/field` is looked up as the state path `/$item/field`,\n * finds nothing and renders as an empty string, without an error anywhere.\n */\nfunction itemTemplateProblems(spec: Spec): string[] {\n const problems: string[] = [];\n for (const [key, { props, on, watch }] of Object.entries(spec.elements)) {\n walk({ props, on, watch }, '', (template, path) => {\n for (const [, placeholder, field] of template.matchAll(\n ITEM_PLACEHOLDER,\n )) {\n problems.push(\n `Element \"${key}\" ${path}: inside $template write \\${${field.replaceAll('.', '/')}}, not \\${${placeholder}}`,\n );\n }\n });\n }\n return problems;\n}\n\n/** `${$item/field}` or `${$item.field}`; the groups are the placeholder and the field. */\nconst ITEM_PLACEHOLDER = /\\$\\{(\\$item[/.]([^}]+))\\}/g;\n\n/** Calls `visit` with every `$template` string under `value` and where it sits. */\nfunction walk(\n value: unknown,\n path: string,\n visit: (template: string, path: string) => void,\n): void {\n if (Array.isArray(value)) {\n value.forEach((item, index) => walk(item, `${path}[${index}]`, visit));\n } else if (value !== null && typeof value === 'object') {\n for (const [key, child] of Object.entries(value)) {\n if (key === '$template' && typeof child === 'string') {\n visit(child, path);\n } else {\n walk(child, path ? `${path}.${key}` : key, visit);\n }\n }\n }\n}\n","// The MCP server half of an MCP App: one `render-ui` tool that takes a\n// json-render spec, and the `ui://` view a host renders it in. Registered the\n// way upstream's `createMcpApp` (`@json-render/mcp`) does it, same tool name\n// and same view resource, with a short description and a typed input schema\n// (`tool.ts`) in place of `catalog.prompt()` and `catalog.zodSchema()`.\n//\n// Nothing here is Angular-specific: the Angular part is the view, passed in\n// as one HTML document.\nimport type { Catalog } from '@json-render/core';\nimport {\n RESOURCE_MIME_TYPE,\n registerAppResource,\n registerAppTool,\n} from '@modelcontextprotocol/ext-apps/server';\nimport type { McpUiResourceCsp } from '@modelcontextprotocol/ext-apps';\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js';\nimport { withSendMessage } from './actions';\nimport {\n renderUiDescription,\n specInputSchema,\n specOutputSchema,\n specProblems,\n} from './tool';\n\n/** Options for {@link createRenderUiServer}. */\nexport interface RenderUiServerOptions {\n /** The components and actions the model may use. */\n catalog: Catalog;\n /** The view: one self-contained HTML document that renders the spec. */\n html: string;\n /** The server's name, as hosts list it. Default `'ngx-json-render'`. */\n name?: string;\n /** The server's version. Default `'0.0.0'`. */\n version?: string;\n /** The tool's name; the view resource is `ui://<toolName>/view.html`. Default `'render-ui'`. */\n toolName?: string;\n /** The tool's title, as hosts show it. Default `'Render UI'`. */\n title?: string;\n /** The tool's description. Default: {@link renderUiDescription} for the catalog. */\n description?: string;\n /**\n * Add the `sendMessage` action to the catalog the model sees, so a button\n * in the view can post a message to the chat as the user. The view handles\n * it with `injectJsonRenderApp().handlers` from `ngx-json-render/mcp`.\n */\n sendMessage?: boolean;\n /**\n * The origins the view may load from (`resourceDomains`) and connect to\n * (`connectDomains`). Left out, the host's default applies, which the MCP\n * Apps spec defines as none.\n */\n csp?: McpUiResourceCsp;\n /**\n * The origin ChatGPT derives the view's sandbox origin from, which its\n * plugin directory requires, unique per plugin. Set through the OpenAI\n * alias rather than `_meta.ui.domain`, because that key's format is up to\n * each host and Claude expects a `{hash}.claudemcpcontent.com` value there.\n */\n widgetDomain?: string;\n}\n\n/**\n * An MCP server with a `render-ui` tool for `catalog` and its `ui://` view.\n * Connect it to any transport: `StdioServerTransport` for a local host,\n * or {@link handleRenderUiRequest} for Streamable HTTP.\n *\n * The tool checks a spec against the catalog before it echoes it back. A\n * rejected spec goes back to the model as an error it can fix.\n */\nexport function createRenderUiServer(options: RenderUiServerOptions) {\n const catalog = options.sendMessage\n ? withSendMessage(options.catalog)\n : options.catalog;\n const {\n html,\n name = 'ngx-json-render',\n version = '0.0.0',\n toolName = 'render-ui',\n title = 'Render UI',\n description = renderUiDescription(catalog),\n csp,\n widgetDomain,\n } = options;\n const resourceUri = `ui://${toolName}/view.html`;\n const server = new McpServer({ name, version });\n registerAppTool(\n server,\n toolName,\n {\n title,\n description,\n inputSchema: { spec: specInputSchema(catalog) },\n outputSchema: { spec: specOutputSchema },\n // It only echoes the spec back for the view to draw: nothing is read\n // from or written to any system.\n annotations: {\n title,\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: false,\n },\n _meta: { ui: { resourceUri } },\n },\n // The SDK has already parsed `spec` against the input schema; what is\n // left is the structure between elements. The text keeps upstream's\n // shape, the bare spec, which is what views built on\n // `@json-render/mcp/app` read.\n async ({ spec }) => {\n const problems = specProblems(spec);\n return problems\n ? { isError: true, content: [{ type: 'text', text: problems }] }\n : {\n content: [{ type: 'text', text: JSON.stringify(spec) }],\n structuredContent: { spec },\n };\n },\n );\n const meta: Record<string, unknown> = {};\n if (csp) meta['ui'] = { csp };\n if (widgetDomain) meta['openai/widgetDomain'] = widgetDomain;\n registerAppResource(\n server,\n resourceUri,\n resourceUri,\n { mimeType: RESOURCE_MIME_TYPE },\n async () => ({\n contents: [\n {\n uri: resourceUri,\n mimeType: RESOURCE_MIME_TYPE,\n text: html,\n ...(Object.keys(meta).length > 0 && { _meta: meta }),\n },\n ],\n }),\n );\n return server;\n}\n\n/**\n * Serve one Streamable HTTP request, statelessly: a fresh server and\n * transport per request, so it runs unchanged on a serverless or edge\n * function (anything with web `Request` and `Response`). Answers CORS,\n * because browser-based clients such as MCP Inspector call cross-origin.\n */\nexport async function handleRenderUiRequest(\n options: RenderUiServerOptions,\n request: Request,\n): Promise<Response> {\n if (request.method === 'OPTIONS') {\n return new Response(null, { status: 204, headers: CORS_HEADERS });\n }\n const server = createRenderUiServer(options);\n const transport = new WebStandardStreamableHTTPServerTransport({\n sessionIdGenerator: undefined,\n // One JSON body per request: nothing here streams, and a serverless\n // function returns as soon as the response is complete.\n enableJsonResponse: true,\n });\n await server.connect(transport);\n try {\n const response = await transport.handleRequest(request);\n const headers = new Headers(response.headers);\n for (const [key, value] of Object.entries(CORS_HEADERS)) {\n headers.set(key, value);\n }\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n } finally {\n await transport.close();\n await server.close();\n }\n}\n\nconst CORS_HEADERS: Record<string, string> = {\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, DELETE, OPTIONS',\n 'Access-Control-Allow-Headers':\n 'Content-Type, Accept, Authorization, Mcp-Session-Id, Mcp-Protocol-Version, Last-Event-ID',\n 'Access-Control-Expose-Headers': 'Mcp-Session-Id',\n};\n","/*\n * Public API Surface of ngx-json-render/mcp/server\n *\n * The server half of an MCP App for a json-render catalog: the `render-ui`\n * tool and the `ui://` view resource. Angular-free, so it runs in Node or on\n * an edge function; the view it serves is built with `ngx-json-render/mcp`.\n */\n\nexport { sendMessageAction, withSendMessage } from './actions';\nexport {\n createRenderUiServer,\n handleRenderUiRequest,\n type RenderUiServerOptions,\n} from './server';\nexport {\n DESCRIPTION_LIMIT,\n renderUiDescription,\n specInputSchema,\n specOutputSchema,\n specProblems,\n type RenderUiDescriptionOptions,\n} from './tool';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
@@ -35,7 +35,8 @@ function specFrom(value) {
|
|
|
35
35
|
* Connect an Angular app running inside an MCP Apps iframe to its host and
|
|
36
36
|
* keep the json-render spec the model sent in a signal. The Angular
|
|
37
37
|
* counterpart of `useJsonRenderApp` from `@json-render/mcp/app`; the server
|
|
38
|
-
* side is `
|
|
38
|
+
* side is `createRenderUiServer` from `ngx-json-render/mcp/server`, or
|
|
39
|
+
* `createMcpApp` from `@json-render/mcp`.
|
|
39
40
|
*
|
|
40
41
|
* Call it in an injection context. The connection closes when the injector
|
|
41
42
|
* that created it is destroyed.
|
|
@@ -51,6 +52,7 @@ function injectJsonRenderApp(options = {}) {
|
|
|
51
52
|
const loading = signal(true, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
|
|
52
53
|
const connected = signal(false, ...(ngDevMode ? [{ debugName: "connected" }] : /* istanbul ignore next */ []));
|
|
53
54
|
const error = signal(null, ...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
|
|
55
|
+
const lastMessage = signal(null, ...(ngDevMode ? [{ debugName: "lastMessage" }] : /* istanbul ignore next */ []));
|
|
54
56
|
const app = new App({ name, version }, {}, { autoResize });
|
|
55
57
|
// Listeners go on before connect(), so a notification the host sends right
|
|
56
58
|
// after the handshake is not lost.
|
|
@@ -80,6 +82,35 @@ function injectJsonRenderApp(options = {}) {
|
|
|
80
82
|
inject(DestroyRef).onDestroy(() => {
|
|
81
83
|
app.close().catch(() => undefined);
|
|
82
84
|
});
|
|
85
|
+
async function sendMessage(text, data) {
|
|
86
|
+
if (!app.getHostCapabilities()?.message) {
|
|
87
|
+
throw new Error('The host does not accept messages from the view.');
|
|
88
|
+
}
|
|
89
|
+
const result = await app.sendMessage({
|
|
90
|
+
role: 'user',
|
|
91
|
+
content: [{ type: 'text', text: messageText(text, data) }],
|
|
92
|
+
});
|
|
93
|
+
if (result.isError)
|
|
94
|
+
throw new Error('The host declined the message.');
|
|
95
|
+
}
|
|
96
|
+
const handlers = {
|
|
97
|
+
sendMessage: async ({ text, data }) => {
|
|
98
|
+
try {
|
|
99
|
+
if (typeof text !== 'string' || !text.trim()) {
|
|
100
|
+
throw new Error('sendMessage needs a non-empty "text" param.');
|
|
101
|
+
}
|
|
102
|
+
const isObject = data !== null && typeof data === 'object' && !Array.isArray(data);
|
|
103
|
+
await sendMessage(text, isObject ? data : undefined);
|
|
104
|
+
lastMessage.set({ ok: true });
|
|
105
|
+
}
|
|
106
|
+
catch (err) {
|
|
107
|
+
const reason = err instanceof Error ? err : new Error(String(err));
|
|
108
|
+
lastMessage.set({ ok: false, error: reason });
|
|
109
|
+
// Rethrown, so a binding's `onError` still runs.
|
|
110
|
+
throw reason;
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
};
|
|
83
114
|
return {
|
|
84
115
|
spec: spec.asReadonly(),
|
|
85
116
|
connecting: computed(() => !connected() && !error()),
|
|
@@ -102,17 +133,9 @@ function injectJsonRenderApp(options = {}) {
|
|
|
102
133
|
loading.set(false);
|
|
103
134
|
}
|
|
104
135
|
},
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
const result = await app.sendMessage({
|
|
110
|
-
role: 'user',
|
|
111
|
-
content: [{ type: 'text', text: messageText(text, data) }],
|
|
112
|
-
});
|
|
113
|
-
if (result.isError)
|
|
114
|
-
throw new Error('The host declined the message.');
|
|
115
|
-
},
|
|
136
|
+
sendMessage,
|
|
137
|
+
handlers,
|
|
138
|
+
lastMessage: lastMessage.asReadonly(),
|
|
116
139
|
};
|
|
117
140
|
}
|
|
118
141
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngx-json-render-mcp.mjs","sources":["../../../projects/ngx-json-render/mcp/src/json-render-app.ts","../../../projects/ngx-json-render/mcp/src/public-api.ts","../../../projects/ngx-json-render/mcp/src/ngx-json-render-mcp.ts"],"sourcesContent":["import {\n DestroyRef,\n type Signal,\n computed,\n inject,\n signal,\n} from '@angular/core';\nimport type { Spec } from '@json-render/core';\nimport { App } from '@modelcontextprotocol/ext-apps';\nimport type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\n\n/** Options for {@link injectJsonRenderApp}. */\nexport interface JsonRenderAppOptions {\n /** App name sent to the host during initialization. Defaults to `\"json-render\"`. */\n name?: string;\n /** App version sent to the host. Defaults to `\"1.0.0\"`. */\n version?: string;\n /**\n * Render the spec while the model is still writing the tool call, from the\n * host's `toolinputpartial` notifications. The host \"heals\" the truncated\n * JSON, so each partial spec is valid but may end in a half-written\n * element; the renderer skips children that do not exist yet. Defaults to\n * `true`. `useJsonRenderApp` in `@json-render/mcp/app` has no equivalent —\n * it waits for the tool result.\n */\n streamPartialInput?: boolean;\n /**\n * Transport to the host. Omit it inside a real MCP Apps iframe: `App`\n * then talks to `window.parent` over `postMessage`. Tests pass one end of\n * an in-memory pair here and an `AppBridge` on the other.\n */\n transport?: Transport;\n /**\n * Report the page's size to the host as it changes, so the iframe grows\n * with the rendered UI. Defaults to `true`; needs `ResizeObserver`.\n */\n autoResize?: boolean;\n}\n\n/**\n * What {@link injectJsonRenderApp} returns — the fields of\n * `UseJsonRenderAppReturn` from `@json-render/mcp/app`, as signals.\n */\nexport interface JsonRenderApp {\n /** The current spec; `null` until the first tool input or result. */\n readonly spec: Signal<Spec | null>;\n /** Whether the app is still connecting to the host. */\n readonly connecting: Signal<boolean>;\n /** Whether the app is connected to the host. */\n readonly connected: Signal<boolean>;\n /** Connection error, if any. */\n readonly error: Signal<Error | null>;\n /** Whether the spec is still arriving: pass it to `<json-render [loading]>`. */\n readonly loading: Signal<boolean>;\n /** The underlying MCP Apps `App`. */\n readonly app: App;\n /**\n * Call a tool on the MCP server and replace the spec with its result —\n * for refresh and drill-down interactions.\n */\n callServerTool(name: string, args?: Record<string, unknown>): Promise<void>;\n /**\n * Post a user message to the host's chat, so the model answers what the\n * user did in the view. `data`, if given, follows the text as a JSON block.\n * Rejects when the host does not accept messages from views or turns this\n * one down, so an action's `onError` runs.\n */\n sendMessage(text: string, data?: Record<string, unknown>): Promise<void>;\n}\n\n/** The text of a {@link JsonRenderApp.sendMessage} message. */\nexport function messageText(\n text: string,\n data?: Record<string, unknown>,\n): string {\n if (!data) return text;\n return `${text}\\n\\n\\`\\`\\`json\\n${JSON.stringify(data, null, 2)}\\n\\`\\`\\``;\n}\n\ninterface ToolResultLike {\n content?: { type: string; text?: string }[];\n}\n\n/**\n * Read a spec out of a tool result. Same rule as `@json-render/mcp/app`: the\n * first text block, parsed as JSON, either a bare spec or `{ spec }`.\n */\nexport function parseSpecFromToolResult(result: ToolResultLike): Spec | null {\n const text = result.content?.find((c) => c.type === 'text')?.text;\n if (!text) return null;\n try {\n return specFrom(JSON.parse(text));\n } catch {\n return null;\n }\n}\n\n/** A bare spec, or the `spec` field of a tool's arguments or result. */\nfunction specFrom(value: unknown): Spec | null {\n if (!value || typeof value !== 'object') return null;\n const spec = 'spec' in value ? (value as { spec: unknown }).spec : value;\n if (!spec || typeof spec !== 'object' || !('root' in spec)) return null;\n return spec as Spec;\n}\n\n/**\n * Connect an Angular app running inside an MCP Apps iframe to its host and\n * keep the json-render spec the model sent in a signal. The Angular\n * counterpart of `useJsonRenderApp` from `@json-render/mcp/app`; the server\n * side is `createMcpApp` from `@json-render/mcp`, unchanged.\n *\n * Call it in an injection context. The connection closes when the injector\n * that created it is destroyed.\n *\n * ```ts\n * readonly mcp = injectJsonRenderApp();\n * // <json-render [spec]=\"mcp.spec()\" [loading]=\"mcp.loading()\" [registry]=\"registry\" />\n * ```\n */\nexport function injectJsonRenderApp(\n options: JsonRenderAppOptions = {},\n): JsonRenderApp {\n const {\n name = 'json-render',\n version = '1.0.0',\n streamPartialInput = true,\n transport,\n autoResize = true,\n } = options;\n\n const spec = signal<Spec | null>(null);\n const loading = signal(true);\n const connected = signal(false);\n const error = signal<Error | null>(null);\n\n const app = new App({ name, version }, {}, { autoResize });\n\n // Listeners go on before connect(), so a notification the host sends right\n // after the handshake is not lost.\n if (streamPartialInput) {\n app.addEventListener('toolinputpartial', ({ arguments: args }) => {\n const partial = specFrom(args);\n if (partial) spec.set(partial);\n });\n }\n app.addEventListener('toolinput', ({ arguments: args }) => {\n const input = specFrom(args);\n if (input) spec.set(input);\n });\n app.addEventListener('toolresult', (result) => {\n const parsed = parseSpecFromToolResult(result);\n if (parsed) spec.set(parsed);\n loading.set(false);\n });\n app.addEventListener('toolcancelled', () => loading.set(false));\n\n app\n .connect(transport)\n .then(() => connected.set(true))\n .catch((err: unknown) =>\n error.set(err instanceof Error ? err : new Error(String(err))),\n );\n\n inject(DestroyRef).onDestroy(() => {\n app.close().catch(() => undefined);\n });\n\n return {\n spec: spec.asReadonly(),\n connecting: computed(() => !connected() && !error()),\n connected: connected.asReadonly(),\n error: error.asReadonly(),\n loading: loading.asReadonly(),\n app,\n async callServerTool(toolName, args = {}) {\n loading.set(true);\n try {\n const result = await app.callServerTool({\n name: toolName,\n arguments: args,\n });\n const parsed = parseSpecFromToolResult(result);\n if (parsed) spec.set(parsed);\n } finally {\n loading.set(false);\n }\n },\n async sendMessage(text, data) {\n if (!app.getHostCapabilities()?.message) {\n throw new Error('The host does not accept messages from the view.');\n }\n const result = await app.sendMessage({\n role: 'user',\n content: [{ type: 'text', text: messageText(text, data) }],\n });\n if (result.isError) throw new Error('The host declined the message.');\n },\n };\n}\n","/*\n * Public API Surface of ngx-json-render/mcp\n *\n * Render json-render specs inside an MCP Apps host (Claude, ChatGPT, VS Code).\n * A separate entry point so `@modelcontextprotocol/ext-apps`, an optional\n * peer, is only needed by apps that import it.\n */\n\nexport {\n injectJsonRenderApp,\n messageText,\n parseSpecFromToolResult,\n type JsonRenderApp,\n type JsonRenderAppOptions,\n} from './json-render-app';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ngx-json-render-mcp.mjs","sources":["../../../projects/ngx-json-render/mcp/src/json-render-app.ts","../../../projects/ngx-json-render/mcp/src/public-api.ts","../../../projects/ngx-json-render/mcp/src/ngx-json-render-mcp.ts"],"sourcesContent":["import {\n DestroyRef,\n type Signal,\n computed,\n inject,\n signal,\n} from '@angular/core';\nimport type { ActionHandler, Spec } from '@json-render/core';\nimport { App } from '@modelcontextprotocol/ext-apps';\nimport type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\n\n/** Options for {@link injectJsonRenderApp}. */\nexport interface JsonRenderAppOptions {\n /** App name sent to the host during initialization. Defaults to `\"json-render\"`. */\n name?: string;\n /** App version sent to the host. Defaults to `\"1.0.0\"`. */\n version?: string;\n /**\n * Render the spec while the model is still writing the tool call, from the\n * host's `toolinputpartial` notifications. The host \"heals\" the truncated\n * JSON, so each partial spec is valid but may end in a half-written\n * element; the renderer skips children that do not exist yet. Defaults to\n * `true`. `useJsonRenderApp` in `@json-render/mcp/app` has no equivalent —\n * it waits for the tool result.\n */\n streamPartialInput?: boolean;\n /**\n * Transport to the host. Omit it inside a real MCP Apps iframe: `App`\n * then talks to `window.parent` over `postMessage`. Tests pass one end of\n * an in-memory pair here and an `AppBridge` on the other.\n */\n transport?: Transport;\n /**\n * Report the page's size to the host as it changes, so the iframe grows\n * with the rendered UI. Defaults to `true`; needs `ResizeObserver`.\n */\n autoResize?: boolean;\n}\n\n/** How the last {@link JsonRenderApp.sendMessage} from an action went. */\nexport type MessageOutcome = { ok: true } | { ok: false; error: Error };\n\n/**\n * What {@link injectJsonRenderApp} returns — the fields of\n * `UseJsonRenderAppReturn` from `@json-render/mcp/app`, as signals.\n */\nexport interface JsonRenderApp {\n /** The current spec; `null` until the first tool input or result. */\n readonly spec: Signal<Spec | null>;\n /** Whether the app is still connecting to the host. */\n readonly connecting: Signal<boolean>;\n /** Whether the app is connected to the host. */\n readonly connected: Signal<boolean>;\n /** Connection error, if any. */\n readonly error: Signal<Error | null>;\n /** Whether the spec is still arriving: pass it to `<json-render [loading]>`. */\n readonly loading: Signal<boolean>;\n /** The underlying MCP Apps `App`. */\n readonly app: App;\n /**\n * Call a tool on the MCP server and replace the spec with its result —\n * for refresh and drill-down interactions.\n */\n callServerTool(name: string, args?: Record<string, unknown>): Promise<void>;\n /**\n * Post a user message to the host's chat, so the model answers what the\n * user did in the view. `data`, if given, follows the text as a JSON block.\n * Rejects when the host does not accept messages from views or turns this\n * one down, so an action's `onError` runs.\n */\n sendMessage(text: string, data?: Record<string, unknown>): Promise<void>;\n /**\n * Handlers for the actions the server adds for MCP Apps hosts\n * (`createRenderUiServer({ sendMessage: true })` in\n * `ngx-json-render/mcp/server`): pass them to `<json-render [handlers]>`.\n * `sendMessage` takes `{ text, data? }` and posts it with\n * {@link JsonRenderApp.sendMessage}.\n */\n readonly handlers: Record<string, ActionHandler>;\n /**\n * How the last `sendMessage` action went, `null` before the first. A host\n * may decline a message or take none at all; show this so a press does not\n * look like it did nothing.\n */\n readonly lastMessage: Signal<MessageOutcome | null>;\n}\n\n/** The text of a {@link JsonRenderApp.sendMessage} message. */\nexport function messageText(\n text: string,\n data?: Record<string, unknown>,\n): string {\n if (!data) return text;\n return `${text}\\n\\n\\`\\`\\`json\\n${JSON.stringify(data, null, 2)}\\n\\`\\`\\``;\n}\n\ninterface ToolResultLike {\n content?: { type: string; text?: string }[];\n}\n\n/**\n * Read a spec out of a tool result. Same rule as `@json-render/mcp/app`: the\n * first text block, parsed as JSON, either a bare spec or `{ spec }`.\n */\nexport function parseSpecFromToolResult(result: ToolResultLike): Spec | null {\n const text = result.content?.find((c) => c.type === 'text')?.text;\n if (!text) return null;\n try {\n return specFrom(JSON.parse(text));\n } catch {\n return null;\n }\n}\n\n/** A bare spec, or the `spec` field of a tool's arguments or result. */\nfunction specFrom(value: unknown): Spec | null {\n if (!value || typeof value !== 'object') return null;\n const spec = 'spec' in value ? (value as { spec: unknown }).spec : value;\n if (!spec || typeof spec !== 'object' || !('root' in spec)) return null;\n return spec as Spec;\n}\n\n/**\n * Connect an Angular app running inside an MCP Apps iframe to its host and\n * keep the json-render spec the model sent in a signal. The Angular\n * counterpart of `useJsonRenderApp` from `@json-render/mcp/app`; the server\n * side is `createRenderUiServer` from `ngx-json-render/mcp/server`, or\n * `createMcpApp` from `@json-render/mcp`.\n *\n * Call it in an injection context. The connection closes when the injector\n * that created it is destroyed.\n *\n * ```ts\n * readonly mcp = injectJsonRenderApp();\n * // <json-render [spec]=\"mcp.spec()\" [loading]=\"mcp.loading()\" [registry]=\"registry\" />\n * ```\n */\nexport function injectJsonRenderApp(\n options: JsonRenderAppOptions = {},\n): JsonRenderApp {\n const {\n name = 'json-render',\n version = '1.0.0',\n streamPartialInput = true,\n transport,\n autoResize = true,\n } = options;\n\n const spec = signal<Spec | null>(null);\n const loading = signal(true);\n const connected = signal(false);\n const error = signal<Error | null>(null);\n const lastMessage = signal<MessageOutcome | null>(null);\n\n const app = new App({ name, version }, {}, { autoResize });\n\n // Listeners go on before connect(), so a notification the host sends right\n // after the handshake is not lost.\n if (streamPartialInput) {\n app.addEventListener('toolinputpartial', ({ arguments: args }) => {\n const partial = specFrom(args);\n if (partial) spec.set(partial);\n });\n }\n app.addEventListener('toolinput', ({ arguments: args }) => {\n const input = specFrom(args);\n if (input) spec.set(input);\n });\n app.addEventListener('toolresult', (result) => {\n const parsed = parseSpecFromToolResult(result);\n if (parsed) spec.set(parsed);\n loading.set(false);\n });\n app.addEventListener('toolcancelled', () => loading.set(false));\n\n app\n .connect(transport)\n .then(() => connected.set(true))\n .catch((err: unknown) =>\n error.set(err instanceof Error ? err : new Error(String(err))),\n );\n\n inject(DestroyRef).onDestroy(() => {\n app.close().catch(() => undefined);\n });\n\n async function sendMessage(text: string, data?: Record<string, unknown>) {\n if (!app.getHostCapabilities()?.message) {\n throw new Error('The host does not accept messages from the view.');\n }\n const result = await app.sendMessage({\n role: 'user',\n content: [{ type: 'text', text: messageText(text, data) }],\n });\n if (result.isError) throw new Error('The host declined the message.');\n }\n\n const handlers: Record<string, ActionHandler> = {\n sendMessage: async ({ text, data }) => {\n try {\n if (typeof text !== 'string' || !text.trim()) {\n throw new Error('sendMessage needs a non-empty \"text\" param.');\n }\n const isObject =\n data !== null && typeof data === 'object' && !Array.isArray(data);\n await sendMessage(\n text,\n isObject ? (data as Record<string, unknown>) : undefined,\n );\n lastMessage.set({ ok: true });\n } catch (err) {\n const reason = err instanceof Error ? err : new Error(String(err));\n lastMessage.set({ ok: false, error: reason });\n // Rethrown, so a binding's `onError` still runs.\n throw reason;\n }\n },\n };\n\n return {\n spec: spec.asReadonly(),\n connecting: computed(() => !connected() && !error()),\n connected: connected.asReadonly(),\n error: error.asReadonly(),\n loading: loading.asReadonly(),\n app,\n async callServerTool(toolName, args = {}) {\n loading.set(true);\n try {\n const result = await app.callServerTool({\n name: toolName,\n arguments: args,\n });\n const parsed = parseSpecFromToolResult(result);\n if (parsed) spec.set(parsed);\n } finally {\n loading.set(false);\n }\n },\n sendMessage,\n handlers,\n lastMessage: lastMessage.asReadonly(),\n };\n}\n","/*\n * Public API Surface of ngx-json-render/mcp\n *\n * Render json-render specs inside an MCP Apps host (Claude, ChatGPT, VS Code).\n * A separate entry point so `@modelcontextprotocol/ext-apps`, an optional\n * peer, is only needed by apps that import it.\n */\n\nexport {\n injectJsonRenderApp,\n messageText,\n parseSpecFromToolResult,\n type JsonRenderApp,\n type JsonRenderAppOptions,\n type MessageOutcome,\n} from './json-render-app';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ngx-json-render",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.7",
|
|
4
4
|
"description": "Generative UI for Angular: a json-render renderer that streams AI-generated JSON specs into real Angular components with signals.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"angular",
|
|
@@ -52,6 +52,7 @@
|
|
|
52
52
|
},
|
|
53
53
|
"sideEffects": false,
|
|
54
54
|
"schematics": "./schematics/collection.json",
|
|
55
|
+
"builders": "./schematics/builders.json",
|
|
55
56
|
"ng-add": {
|
|
56
57
|
"save": "dependencies"
|
|
57
58
|
},
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../../../node_modules/@angular-devkit/architect/src/builders-schema.json",
|
|
3
|
+
"builders": {
|
|
4
|
+
"mcp-app": {
|
|
5
|
+
"implementation": "./mcp-app-builder/index.cjs",
|
|
6
|
+
"schema": "./mcp-app-builder/schema.json",
|
|
7
|
+
"description": "Builds an MCP App: the view as one view.html, the server as server.mjs."
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
"ng-add": {
|
|
5
5
|
"description": "Adds ngx-json-render's peer dependencies (@json-render/core and zod) to the workspace.",
|
|
6
6
|
"factory": "./ng-add/index.cjs#ngAdd"
|
|
7
|
+
},
|
|
8
|
+
"mcp-app": {
|
|
9
|
+
"description": "Generates an MCP App for a catalog: an Angular view project, its MCP server, and an mcp target that builds both into one view.html and server.mjs.",
|
|
10
|
+
"factory": "./mcp-app/index.cjs#mcpApp",
|
|
11
|
+
"schema": "./mcp-app/schema.json"
|
|
7
12
|
}
|
|
8
13
|
}
|
|
9
14
|
}
|