ngx-json-render 0.9.2 → 0.9.5
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 +146 -23
- package/fesm2022/ngx-json-render-ag-ui.mjs +308 -0
- package/fesm2022/ngx-json-render-ag-ui.mjs.map +1 -0
- package/fesm2022/ngx-json-render-mcp-server.mjs +424 -0
- package/fesm2022/ngx-json-render-mcp-server.mjs.map +1 -0
- package/package.json +9 -1
- package/types/ngx-json-render-ag-ui.d.ts +223 -0
- package/types/ngx-json-render-mcp-server.d.ts +107 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { Spec } from '@json-render/core';
|
|
2
|
+
import * as _angular_core from '@angular/core';
|
|
3
|
+
import { Signal, Type } from '@angular/core';
|
|
4
|
+
import { SpecValidationMode, RenderLimits, SpecCatalog, SpecCheckIssue } from 'ngx-json-render';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The `activityType` a json-render spec travels under.
|
|
8
|
+
*
|
|
9
|
+
* An agent sends the spec as an AG-UI activity: `ACTIVITY_SNAPSHOT` carries a
|
|
10
|
+
* whole spec in `content`, and `ACTIVITY_DELTA` carries RFC 6902 patches
|
|
11
|
+
* against it in `patch` — the same patches a json-render JSONL stream is made
|
|
12
|
+
* of. CopilotKit carries A2UI the same way, under `"a2ui-surface"`.
|
|
13
|
+
*
|
|
14
|
+
* The name says both the format and what `content` holds — a `Spec`, the
|
|
15
|
+
* object upstream calls a spec in its AI SDK part type `data-spec` — so a
|
|
16
|
+
* renderer for another framework can match it without reading these docs.
|
|
17
|
+
*/
|
|
18
|
+
declare const JSON_RENDER_ACTIVITY_TYPE = "json-render-spec";
|
|
19
|
+
/**
|
|
20
|
+
* The part of an AG-UI event this entry point reads.
|
|
21
|
+
*
|
|
22
|
+
* Structural on purpose: `@ag-ui/core`'s `EventType` is a string enum, so its
|
|
23
|
+
* events fit a `string` here, and nothing below needs the package at runtime
|
|
24
|
+
* or pins its version.
|
|
25
|
+
*/
|
|
26
|
+
interface AgUiEvent {
|
|
27
|
+
readonly type: string;
|
|
28
|
+
}
|
|
29
|
+
/** The part of an AG-UI message this entry point reads. */
|
|
30
|
+
interface AgUiMessage {
|
|
31
|
+
readonly id: string;
|
|
32
|
+
readonly role: string;
|
|
33
|
+
}
|
|
34
|
+
/** One spec an agent is building, keyed by its activity message. */
|
|
35
|
+
interface AgUiSurface {
|
|
36
|
+
/** The activity's `messageId`; one run can build several surfaces. */
|
|
37
|
+
readonly messageId: string;
|
|
38
|
+
readonly spec: Spec;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Whether activity content has the shape of a spec. Only the shape: what the
|
|
42
|
+
* elements say is the renderer's and the finished-spec check's business.
|
|
43
|
+
*/
|
|
44
|
+
declare function isJsonRenderSpec(content: unknown): content is Spec;
|
|
45
|
+
/**
|
|
46
|
+
* The surfaces an AG-UI message list holds, in message order.
|
|
47
|
+
*
|
|
48
|
+
* Used for `MESSAGES_SNAPSHOT` and to pick up the history an agent already
|
|
49
|
+
* has when a hook attaches to it.
|
|
50
|
+
*/
|
|
51
|
+
declare function surfacesFromMessages(messages: readonly AgUiMessage[], activityType?: string): readonly AgUiSurface[];
|
|
52
|
+
/**
|
|
53
|
+
* Fold one AG-UI event into the surfaces built so far.
|
|
54
|
+
*
|
|
55
|
+
* Returns the same array when the event does not concern json-render, so a
|
|
56
|
+
* signal holding the result only notifies on a real change. A delta applies
|
|
57
|
+
* through the same immutable patch engine as a JSONL stream: elements it does
|
|
58
|
+
* not touch keep their identity, and the renderer skips them.
|
|
59
|
+
*
|
|
60
|
+
* Two leniencies, both in the direction of rendering what arrived:
|
|
61
|
+
* - a delta for a surface no snapshot has opened starts from an empty spec,
|
|
62
|
+
* so an agent can stream patches the way a JSONL endpoint does;
|
|
63
|
+
* - a malformed patch op is dropped rather than failing the surface.
|
|
64
|
+
*/
|
|
65
|
+
declare function applyAgUiEvent(surfaces: readonly AgUiSurface[], event: AgUiEvent, activityType?: string): readonly AgUiSurface[];
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* The callbacks of an AG-UI subscriber this entry point uses — a subset of
|
|
69
|
+
* `@ag-ui/client`'s `AgentSubscriber`.
|
|
70
|
+
*/
|
|
71
|
+
interface AgUiSubscriber {
|
|
72
|
+
onRunInitialized?(): void;
|
|
73
|
+
onEvent?(params: {
|
|
74
|
+
event: AgUiEvent;
|
|
75
|
+
}): void;
|
|
76
|
+
onRunErrorEvent?(params: {
|
|
77
|
+
event: {
|
|
78
|
+
message: string;
|
|
79
|
+
};
|
|
80
|
+
}): void;
|
|
81
|
+
onRunFailed?(params: {
|
|
82
|
+
error: Error;
|
|
83
|
+
}): void;
|
|
84
|
+
onRunFinalized?(): void;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* What {@link injectAgentUI} needs of an agent. Any `@ag-ui/client` agent —
|
|
88
|
+
* `HttpAgent`, a subclass of `AbstractAgent`, the agent CopilotKit hands out —
|
|
89
|
+
* already has this shape.
|
|
90
|
+
*/
|
|
91
|
+
interface AgUiAgent {
|
|
92
|
+
readonly messages?: readonly AgUiMessage[];
|
|
93
|
+
subscribe(subscriber: AgUiSubscriber): {
|
|
94
|
+
unsubscribe(): void;
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/** Options for {@link injectAgentUI}. */
|
|
98
|
+
interface AgentUIOptions {
|
|
99
|
+
/** The agent whose runs build the UI. The app runs it; the hook listens. */
|
|
100
|
+
agent: AgUiAgent;
|
|
101
|
+
/** Defaults to {@link JSON_RENDER_ACTIVITY_TYPE}. */
|
|
102
|
+
activityType?: string;
|
|
103
|
+
/**
|
|
104
|
+
* Called once per surface a run touched, after the finished-spec check.
|
|
105
|
+
* Skipped for a run that failed, and for a surface the check rejected.
|
|
106
|
+
*/
|
|
107
|
+
onComplete?: (spec: Spec, messageId: string) => void;
|
|
108
|
+
/** Called when a run fails or a finished surface fails the check. */
|
|
109
|
+
onError?: (error: Error) => void;
|
|
110
|
+
/** As for `injectUIStream`: checked once per surface, when the run ends. */
|
|
111
|
+
validate?: SpecValidationMode;
|
|
112
|
+
/** As for `injectUIStream`; enforced whatever `validate` is. */
|
|
113
|
+
renderLimits?: RenderLimits | null;
|
|
114
|
+
/** As for `injectUIStream`: lets `validate` check types and props. */
|
|
115
|
+
catalog?: SpecCatalog | null;
|
|
116
|
+
}
|
|
117
|
+
/** Return type of {@link injectAgentUI}. */
|
|
118
|
+
interface AgentUIReturn {
|
|
119
|
+
/** Every json-render surface the agent has built, in order of appearance. */
|
|
120
|
+
readonly surfaces: Signal<readonly AgUiSurface[]>;
|
|
121
|
+
/** The most recently opened surface's spec — the one most apps render. */
|
|
122
|
+
readonly spec: Signal<Spec | null>;
|
|
123
|
+
/** Whether a run of the agent is in progress. */
|
|
124
|
+
readonly isStreaming: Signal<boolean>;
|
|
125
|
+
/** The last run's error, if any. */
|
|
126
|
+
readonly error: Signal<Error | null>;
|
|
127
|
+
/** What the check found in the surfaces the last run finished. */
|
|
128
|
+
readonly issues: Signal<readonly SpecCheckIssue[]>;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Render what an AG-UI agent streams. Subscribes to the agent, folds every
|
|
132
|
+
* json-render activity it emits into signals, and checks each surface when
|
|
133
|
+
* the run ends — the same contract as `injectUIStream`, with the agent in
|
|
134
|
+
* place of the endpoint.
|
|
135
|
+
*
|
|
136
|
+
* The app keeps running the agent itself (`agent.runAgent()`, CopilotKit, a
|
|
137
|
+
* reconnect); the hook only listens, so it sees every run, whoever starts it.
|
|
138
|
+
* It starts from whatever surfaces are already in the agent's messages and
|
|
139
|
+
* unsubscribes on destroy.
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```ts
|
|
143
|
+
* export class AgentPage {
|
|
144
|
+
* readonly agent = new HttpAgent({ url: '/api/agent' });
|
|
145
|
+
* readonly ui = injectAgentUI({ agent: this.agent, catalog });
|
|
146
|
+
* ask(text: string) {
|
|
147
|
+
* this.agent.addMessage({ id: crypto.randomUUID(), role: 'user', content: text });
|
|
148
|
+
* this.agent.runAgent();
|
|
149
|
+
* }
|
|
150
|
+
* }
|
|
151
|
+
* // template:
|
|
152
|
+
* // <json-render [spec]="ui.spec()" [registry]="registry" [loading]="ui.isStreaming()" />
|
|
153
|
+
* ```
|
|
154
|
+
*/
|
|
155
|
+
declare function injectAgentUI(options: AgentUIOptions): AgentUIReturn;
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Renders one json-render activity message. It takes the inputs every AG-UI
|
|
159
|
+
* activity renderer is given — CopilotKit Angular's `ActivityRenderer`
|
|
160
|
+
* contract — and draws `content` with `<json-render>`, which takes its
|
|
161
|
+
* registry, catalog and handlers from the nearest `provideJsonRender`.
|
|
162
|
+
*
|
|
163
|
+
* Register it with {@link jsonRenderActivityRenderer} rather than by hand.
|
|
164
|
+
*/
|
|
165
|
+
declare class JsonRenderActivity {
|
|
166
|
+
readonly activityType: _angular_core.InputSignal<string>;
|
|
167
|
+
readonly content: _angular_core.InputSignal<unknown>;
|
|
168
|
+
/** The AG-UI activity message; unused, accepted for the contract. */
|
|
169
|
+
readonly message: _angular_core.InputSignal<unknown>;
|
|
170
|
+
/** The agent that produced it; unused, accepted for the contract. */
|
|
171
|
+
readonly agent: _angular_core.InputSignal<unknown>;
|
|
172
|
+
protected readonly spec: _angular_core.Signal<Spec | null>;
|
|
173
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<JsonRenderActivity, never>;
|
|
174
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<JsonRenderActivity, "json-render-activity", never, { "activityType": { "alias": "activityType"; "required": false; "isSignal": true; }; "content": { "alias": "content"; "required": true; "isSignal": true; }; "message": { "alias": "message"; "required": false; "isSignal": true; }; "agent": { "alias": "agent"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
175
|
+
}
|
|
176
|
+
/** Options for {@link jsonRenderActivityRenderer}. */
|
|
177
|
+
interface JsonRenderActivityRendererOptions {
|
|
178
|
+
/** Defaults to {@link JSON_RENDER_ACTIVITY_TYPE}. */
|
|
179
|
+
activityType?: string;
|
|
180
|
+
/** Limit the renderer to one agent's activities. */
|
|
181
|
+
agentId?: string;
|
|
182
|
+
/** A component of your own in place of {@link JsonRenderActivity}. */
|
|
183
|
+
component?: Type<unknown>;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* An activity renderer config in the shape CopilotKit Angular's
|
|
187
|
+
* `renderActivityMessages` takes, matched structurally so this package does
|
|
188
|
+
* not depend on CopilotKit.
|
|
189
|
+
*/
|
|
190
|
+
interface JsonRenderActivityRendererConfig {
|
|
191
|
+
activityType: string;
|
|
192
|
+
agentId?: string;
|
|
193
|
+
content: {
|
|
194
|
+
safeParse(content: unknown): {
|
|
195
|
+
success: true;
|
|
196
|
+
data: Spec;
|
|
197
|
+
} | {
|
|
198
|
+
success: false;
|
|
199
|
+
error?: unknown;
|
|
200
|
+
};
|
|
201
|
+
};
|
|
202
|
+
component: Type<any>;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Teach a CopilotKit Angular app to render json-render activities:
|
|
206
|
+
*
|
|
207
|
+
* ```ts
|
|
208
|
+
* providers: [
|
|
209
|
+
* provideJsonRender({ registry }),
|
|
210
|
+
* provideCopilotKit({
|
|
211
|
+
* runtimeUrl: '/api/copilotkit',
|
|
212
|
+
* renderActivityMessages: [jsonRenderActivityRenderer()],
|
|
213
|
+
* }),
|
|
214
|
+
* ]
|
|
215
|
+
* ```
|
|
216
|
+
*
|
|
217
|
+
* Content that is not a spec is rejected by the config's schema, so CopilotKit
|
|
218
|
+
* skips the message with a warning instead of mounting an empty renderer.
|
|
219
|
+
*/
|
|
220
|
+
declare function jsonRenderActivityRenderer(options?: JsonRenderActivityRendererOptions): JsonRenderActivityRendererConfig;
|
|
221
|
+
|
|
222
|
+
export { JSON_RENDER_ACTIVITY_TYPE, JsonRenderActivity, applyAgUiEvent, injectAgentUI, isJsonRenderSpec, jsonRenderActivityRenderer, surfacesFromMessages };
|
|
223
|
+
export type { AgUiAgent, AgUiEvent, AgUiMessage, AgUiSubscriber, AgUiSurface, AgentUIOptions, AgentUIReturn, JsonRenderActivityRendererConfig, JsonRenderActivityRendererOptions };
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { Catalog } from '@json-render/core';
|
|
2
|
+
import { McpUiResourceCsp } from '@modelcontextprotocol/ext-apps';
|
|
3
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
|
|
6
|
+
/** Options for {@link createRenderUiServer}. */
|
|
7
|
+
interface RenderUiServerOptions {
|
|
8
|
+
/** The components and actions the model may use. */
|
|
9
|
+
catalog: Catalog;
|
|
10
|
+
/** The view: one self-contained HTML document that renders the spec. */
|
|
11
|
+
html: string;
|
|
12
|
+
/** The server's name, as hosts list it. Default `'ngx-json-render'`. */
|
|
13
|
+
name?: string;
|
|
14
|
+
/** The server's version. Default `'0.0.0'`. */
|
|
15
|
+
version?: string;
|
|
16
|
+
/** The tool's name; the view resource is `ui://<toolName>/view.html`. Default `'render-ui'`. */
|
|
17
|
+
toolName?: string;
|
|
18
|
+
/** The tool's title, as hosts show it. Default `'Render UI'`. */
|
|
19
|
+
title?: string;
|
|
20
|
+
/** The tool's description. Default: {@link renderUiDescription} for the catalog. */
|
|
21
|
+
description?: string;
|
|
22
|
+
/**
|
|
23
|
+
* The origins the view may load from (`resourceDomains`) and connect to
|
|
24
|
+
* (`connectDomains`). Left out, the host's default applies, which the MCP
|
|
25
|
+
* Apps spec defines as none.
|
|
26
|
+
*/
|
|
27
|
+
csp?: McpUiResourceCsp;
|
|
28
|
+
/**
|
|
29
|
+
* The origin ChatGPT derives the view's sandbox origin from, which its
|
|
30
|
+
* plugin directory requires, unique per plugin. Set through the OpenAI
|
|
31
|
+
* alias rather than `_meta.ui.domain`, because that key's format is up to
|
|
32
|
+
* each host and Claude expects a `{hash}.claudemcpcontent.com` value there.
|
|
33
|
+
*/
|
|
34
|
+
widgetDomain?: string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* An MCP server with a `render-ui` tool for `catalog` and its `ui://` view.
|
|
38
|
+
* Connect it to any transport: `StdioServerTransport` for a local host,
|
|
39
|
+
* or {@link handleRenderUiRequest} for Streamable HTTP.
|
|
40
|
+
*
|
|
41
|
+
* The tool checks a spec against the catalog before it echoes it back. A
|
|
42
|
+
* rejected spec goes back to the model as an error it can fix.
|
|
43
|
+
*/
|
|
44
|
+
declare function createRenderUiServer(options: RenderUiServerOptions): McpServer;
|
|
45
|
+
/**
|
|
46
|
+
* Serve one Streamable HTTP request, statelessly: a fresh server and
|
|
47
|
+
* transport per request, so it runs unchanged on a serverless or edge
|
|
48
|
+
* function (anything with web `Request` and `Response`). Answers CORS,
|
|
49
|
+
* because browser-based clients such as MCP Inspector call cross-origin.
|
|
50
|
+
*/
|
|
51
|
+
declare function handleRenderUiRequest(options: RenderUiServerOptions, request: Request): Promise<Response>;
|
|
52
|
+
|
|
53
|
+
/** Claude cuts a tool description off after roughly this many characters. */
|
|
54
|
+
declare const DESCRIPTION_LIMIT = 2000;
|
|
55
|
+
/** Options for {@link renderUiDescription}. */
|
|
56
|
+
interface RenderUiDescriptionOptions {
|
|
57
|
+
/** What the tool draws, as the first sentence names it. Default `'UI'`. */
|
|
58
|
+
ui?: string;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The `render-ui` tool description for `catalog`: how a spec is put
|
|
62
|
+
* together, in under {@link DESCRIPTION_LIMIT} characters. The components,
|
|
63
|
+
* props and actions are left to the input schema. The paragraph on
|
|
64
|
+
* `sendMessage` is included only when the catalog defines that action, since
|
|
65
|
+
* the view has to handle it.
|
|
66
|
+
*/
|
|
67
|
+
declare function renderUiDescription(catalog: Catalog, { ui }?: RenderUiDescriptionOptions): string;
|
|
68
|
+
/**
|
|
69
|
+
* The tool's input schema: a spec whose elements are typed per component.
|
|
70
|
+
*
|
|
71
|
+
* `catalog.zodSchema()` types an element's `type` but not its props, and it
|
|
72
|
+
* has no `state`, `on` or `watch`, so the MCP SDK, which parses tool
|
|
73
|
+
* arguments with the input schema, stripped those and let any prop through.
|
|
74
|
+
* Here each component's props are its catalog schema (with dynamic values
|
|
75
|
+
* allowed and unknown props rejected), and each action's params are checked.
|
|
76
|
+
*/
|
|
77
|
+
declare function specInputSchema(catalog: Catalog): z.ZodObject<{
|
|
78
|
+
root: z.ZodString;
|
|
79
|
+
elements: z.ZodRecord<z.ZodString, z.ZodDiscriminatedUnion<[z.ZodObject<z.core.$ZodLooseShape, z.core.$strip>, ...z.ZodObject<z.core.$ZodLooseShape, z.core.$strip>[]], "type">>;
|
|
80
|
+
state: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
81
|
+
}, z.core.$strict>;
|
|
82
|
+
/**
|
|
83
|
+
* What `render-ui` returns as `structuredContent`: the spec it was given,
|
|
84
|
+
* after the input schema filled in missing `children`. Only its outline,
|
|
85
|
+
* since the input schema already carries the catalog and a second copy would
|
|
86
|
+
* double what every `tools/list` sends the model. ChatGPT flags a tool
|
|
87
|
+
* without an output schema ("Output schema recommended").
|
|
88
|
+
*/
|
|
89
|
+
declare const specOutputSchema: z.ZodObject<{
|
|
90
|
+
root: z.ZodString;
|
|
91
|
+
elements: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
92
|
+
type: z.ZodString;
|
|
93
|
+
props: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
94
|
+
children: z.ZodArray<z.ZodString>;
|
|
95
|
+
}, z.core.$loose>>;
|
|
96
|
+
state: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
97
|
+
}, z.core.$loose>;
|
|
98
|
+
/**
|
|
99
|
+
* What the input schema cannot express: a root and children that exist,
|
|
100
|
+
* repeat with a template child, valid visibility conditions, and `$template`
|
|
101
|
+
* placeholders that read a repeat item. Returns the problems as text for the
|
|
102
|
+
* model, or `undefined` when there are none.
|
|
103
|
+
*/
|
|
104
|
+
declare function specProblems(spec: object): string | undefined;
|
|
105
|
+
|
|
106
|
+
export { DESCRIPTION_LIMIT, createRenderUiServer, handleRenderUiRequest, renderUiDescription, specInputSchema, specOutputSchema, specProblems };
|
|
107
|
+
export type { RenderUiDescriptionOptions, RenderUiServerOptions };
|