gesso-framework 0.4.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +114 -0
- package/README.md +2 -0
- package/dist/ChannelProtocol-ByNoHujM.d.ts +247 -0
- package/dist/{FunctionComponent-CgwLKE5d.d.ts → FunctionComponent-fYMdePtH.d.ts} +3 -247
- package/dist/agent/index.d.ts +317 -0
- package/dist/agent/index.js +184 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/app-BvlIO1G9.js +326 -0
- package/dist/app-BvlIO1G9.js.map +1 -0
- package/dist/{index-C9FAI_Kt.d.ts → index-BDM_gzzZ.d.ts} +377 -268
- package/dist/index.d.ts +5 -3
- package/dist/index.js +974 -92
- package/dist/index.js.map +1 -1
- package/dist/jsx/jsx-runtime.d.ts +1 -1
- package/dist/{persisted-CsTPnjkc.js → persisted-pix1fS1D.js} +6 -1
- package/dist/{persisted-CsTPnjkc.js.map → persisted-pix1fS1D.js.map} +1 -1
- package/dist/remote-xxij8zXe.js +600 -0
- package/dist/remote-xxij8zXe.js.map +1 -0
- package/dist/rolldown-runtime-D7D4PA-g.js +13 -0
- package/dist/ui-U39HjNFA.d.ts +517 -0
- package/dist/webmcp-CJVFvAHe.js +77 -0
- package/dist/webmcp-CJVFvAHe.js.map +1 -0
- package/dist/worker/index.d.ts +4 -2
- package/dist/worker/index.js +1 -1
- package/package.json +12 -4
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { E as WorkerHandle, F as JsonSchema, a as resolveTarget, c as AgentResource, d as AgentSurfaceOptions, f as AgentTool, g as ServedChannel, h as resourceUri, i as outline, l as AgentSurface, m as agentSurface, n as UiRefs, o as uiSurface, p as AgentToolResult, r as UiSurfaceOptions, s as AgentConfirmation, t as UiHost, u as AgentSurfaceLike } from "../ui-U39HjNFA.js";
|
|
2
|
+
//#region src/agent/mcp.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* An agent surface, served over the Model Context Protocol.
|
|
5
|
+
*
|
|
6
|
+
* Two layers. `handleMcpMessage` answers one JSON-RPC message and knows
|
|
7
|
+
* nothing about how it arrived, which is all a stdio transport or a
|
|
8
|
+
* relay needs. `mcpHandler` wraps it in MCP's Streamable HTTP
|
|
9
|
+
* transport as a `fetch`-style function, `(Request) => Response`,
|
|
10
|
+
* which is what `Bun.serve`, Deno, a service worker and every modern
|
|
11
|
+
* Node server framework take, so the framework imports no server.
|
|
12
|
+
*
|
|
13
|
+
* What it implements is the part a channel needs: `initialize`, `ping`,
|
|
14
|
+
* `tools/list`, `tools/call`, `resources/list` and `resources/read`.
|
|
15
|
+
* Responses are plain JSON rather than an event stream, which the
|
|
16
|
+
* transport allows, and the GET stream for server-initiated messages
|
|
17
|
+
* is declined with 405, which it also allows: nothing here sends one,
|
|
18
|
+
* so the capabilities say neither `listChanged` nor `subscribe`.
|
|
19
|
+
*/
|
|
20
|
+
/** The protocol revisions this server speaks, newest first. */
|
|
21
|
+
declare const MCP_PROTOCOL_VERSIONS: readonly ['2025-11-25', '2025-06-18', '2025-03-26'];
|
|
22
|
+
interface McpServerInfo {
|
|
23
|
+
/** The name a client shows for this server. Defaults to `gesso`. */
|
|
24
|
+
name?: string;
|
|
25
|
+
version?: string;
|
|
26
|
+
/** Told to the agent on connecting: what this application is, in a sentence or two. */
|
|
27
|
+
instructions?: string;
|
|
28
|
+
}
|
|
29
|
+
type JsonRpcId = string | number;
|
|
30
|
+
type JsonRpcResponse = {
|
|
31
|
+
jsonrpc: '2.0';
|
|
32
|
+
id: JsonRpcId | null;
|
|
33
|
+
result: unknown;
|
|
34
|
+
} | {
|
|
35
|
+
jsonrpc: '2.0';
|
|
36
|
+
id: JsonRpcId | null;
|
|
37
|
+
error: {
|
|
38
|
+
code: number;
|
|
39
|
+
message: string;
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Answers one JSON-RPC message. Returns null for a notification, which
|
|
44
|
+
* takes no answer, and never throws: a malformed message is answered
|
|
45
|
+
* with the JSON-RPC error that says what was wrong with it.
|
|
46
|
+
*/
|
|
47
|
+
declare function handleMcpMessage(surface: AgentSurfaceLike, message: unknown, info?: McpServerInfo): Promise<JsonRpcResponse | null>;
|
|
48
|
+
interface McpHandlerOptions extends McpServerInfo {
|
|
49
|
+
/**
|
|
50
|
+
* Origins a browser may call from. A request carrying any other
|
|
51
|
+
* `Origin` is refused, as the transport requires: without it, a web
|
|
52
|
+
* page the person happens to have open could reach a server bound to
|
|
53
|
+
* their own machine and send commands to their application. Agents
|
|
54
|
+
* that are not browsers send no `Origin` and are unaffected.
|
|
55
|
+
*/
|
|
56
|
+
allowedOrigins?: readonly string[];
|
|
57
|
+
/**
|
|
58
|
+
* A secret every request must carry as `Authorization: Bearer
|
|
59
|
+
* <token>`. Worth setting whenever the port is reachable by anything
|
|
60
|
+
* other than the person's own agents.
|
|
61
|
+
*/
|
|
62
|
+
token?: string;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The Streamable HTTP transport, as a `fetch` handler.
|
|
66
|
+
*
|
|
67
|
+
* Bun.serve({ hostname: '127.0.0.1', port: 7310, fetch: mcpHandler(surface, { name: 'notes' }) });
|
|
68
|
+
*
|
|
69
|
+
* It answers every path; mount it where the server routes `/mcp`.
|
|
70
|
+
*/
|
|
71
|
+
declare function mcpHandler(surface: AgentSurfaceLike, options?: McpHandlerOptions): (request: Request) => Promise<Response>;
|
|
72
|
+
//#endregion
|
|
73
|
+
//#region src/agent/validate.d.ts
|
|
74
|
+
/**
|
|
75
|
+
* Checks a value against the JSON Schema a channel description uses.
|
|
76
|
+
*
|
|
77
|
+
* Not a general validator. It understands the keywords
|
|
78
|
+
* `gesso-vite-plugin` writes (`type`, `enum`, `const`, `anyOf`,
|
|
79
|
+
* `items`, `prefixItems`, `minItems`, `properties`, `required`,
|
|
80
|
+
* `additionalProperties`, `not` and `$ref` into `$defs`), and passes
|
|
81
|
+
* anything else. That is enough to stop the case that matters: an
|
|
82
|
+
* agent sending `"3"` where a command takes a number, which the
|
|
83
|
+
* command would accept, store, and fail on much later and somewhere
|
|
84
|
+
* else.
|
|
85
|
+
*
|
|
86
|
+
* Returns the first problem as a sentence naming where it is, written
|
|
87
|
+
* for the agent that sent the value so it can correct itself, or null.
|
|
88
|
+
*/
|
|
89
|
+
declare function validate(schema: JsonSchema, value: unknown, root?: JsonSchema, at?: string): string | null;
|
|
90
|
+
//#endregion
|
|
91
|
+
//#region src/agent/remote.d.ts
|
|
92
|
+
/**
|
|
93
|
+
* An agent surface across a thread.
|
|
94
|
+
*
|
|
95
|
+
* A web application's channels are served in workers, and an agent
|
|
96
|
+
* reaches the page, so the surface has to cross from one to the other.
|
|
97
|
+
* The worker that serves channels answers a `gesso:agent` port with its
|
|
98
|
+
* own surface (`serveAgentPort`); the page holds the other end as a
|
|
99
|
+
* surface of its own (`remoteSurface`); and a thread that knows several
|
|
100
|
+
* such ports, the render worker with its channel workers behind it,
|
|
101
|
+
* offers them as one (`combineSurfaces`).
|
|
102
|
+
*
|
|
103
|
+
* The four operations cross as they are. MCP itself is spoken only at
|
|
104
|
+
* the end that faces the agent, so nothing in a worker parses JSON-RPC.
|
|
105
|
+
*/
|
|
106
|
+
/** The port key a thread answers with its agent surface. */
|
|
107
|
+
declare const AGENT_PORT = "gesso:agent";
|
|
108
|
+
/** What crosses an agent port, page to worker. */
|
|
109
|
+
type AgentPortRequest = {
|
|
110
|
+
id: number;
|
|
111
|
+
op: 'tools';
|
|
112
|
+
} | {
|
|
113
|
+
id: number;
|
|
114
|
+
op: 'resources';
|
|
115
|
+
} | {
|
|
116
|
+
id: number;
|
|
117
|
+
op: 'call';
|
|
118
|
+
name: string;
|
|
119
|
+
args?: Readonly<Record<string, unknown>>;
|
|
120
|
+
} | {
|
|
121
|
+
id: number;
|
|
122
|
+
op: 'read';
|
|
123
|
+
uri: string;
|
|
124
|
+
};
|
|
125
|
+
/** What crosses back. */
|
|
126
|
+
type AgentPortResponse = {
|
|
127
|
+
id: number;
|
|
128
|
+
result: unknown;
|
|
129
|
+
} | {
|
|
130
|
+
id: number;
|
|
131
|
+
error: string;
|
|
132
|
+
};
|
|
133
|
+
/** How a thread asks the person, wherever the person is. */
|
|
134
|
+
type AgentConfirm = (request: AgentConfirmation) => boolean | Promise<boolean>;
|
|
135
|
+
/** A `MessagePort`, or anything that posts and receives like one. */
|
|
136
|
+
type AgentPort = Pick<MessagePort, 'postMessage' | 'onmessage'>;
|
|
137
|
+
/**
|
|
138
|
+
* Answers a port with a surface.
|
|
139
|
+
*
|
|
140
|
+
* The surface is made on the first request rather than when the port
|
|
141
|
+
* arrives: a port is opened by a page that may never ask anything, and
|
|
142
|
+
* a surface subscribes to every view key once it is used. It is given
|
|
143
|
+
* a `confirm` that asks the far end of this port, so a command marked
|
|
144
|
+
* `@confirm` reaches the person wherever they are; the far end answers
|
|
145
|
+
* no when it has no way to ask.
|
|
146
|
+
*/
|
|
147
|
+
declare function serveAgentPort(port: AgentPort, makeSurface: (confirm: AgentConfirm) => AgentSurfaceLike): () => void;
|
|
148
|
+
/**
|
|
149
|
+
* The surface at the other end of a port.
|
|
150
|
+
*
|
|
151
|
+
* A request nobody answers within `timeoutMs` (default 2000) is taken
|
|
152
|
+
* as a thread with nothing to offer: a worker that serves no channels
|
|
153
|
+
* never installs the answering side, and an agent asking the whole
|
|
154
|
+
* application should hear about the threads that do rather than wait
|
|
155
|
+
* on one that does not.
|
|
156
|
+
*/
|
|
157
|
+
declare function remoteSurface(port: AgentPort, options?: {
|
|
158
|
+
timeoutMs?: number;
|
|
159
|
+
confirm?: AgentConfirm;
|
|
160
|
+
}): AgentSurfaceLike;
|
|
161
|
+
/**
|
|
162
|
+
* Several surfaces as one: tools and resources listed together, a call
|
|
163
|
+
* or a read sent to whichever surface listed it. When two list the same
|
|
164
|
+
* name, the first keeps it, as the order of the threads is the order
|
|
165
|
+
* the application registered them in.
|
|
166
|
+
*/
|
|
167
|
+
declare function combineSurfaces(surfaces: readonly AgentSurfaceLike[]): AgentSurfaceLike;
|
|
168
|
+
//#endregion
|
|
169
|
+
//#region src/agent/dev.d.ts
|
|
170
|
+
/**
|
|
171
|
+
* The page's half of the development agent bridge.
|
|
172
|
+
*
|
|
173
|
+
* `gesso-vite-plugin` serves MCP at `/__gesso/mcp` on the dev server,
|
|
174
|
+
* but the channels are in the page's workers, and a server cannot
|
|
175
|
+
* reach a worker. The page can, and it already holds a socket to the
|
|
176
|
+
* server: Vite's HMR connection. So the server relays each JSON-RPC
|
|
177
|
+
* message down that socket, this answers it against the render
|
|
178
|
+
* worker's agent port, and the answer goes back up the same way.
|
|
179
|
+
*
|
|
180
|
+
* MCP is spoken here, in the page, rather than on the server, so the
|
|
181
|
+
* server needs nothing from the framework and the workers need nothing
|
|
182
|
+
* from MCP. The plugin injects the call in a dev server only; a build
|
|
183
|
+
* carries no reference to this module.
|
|
184
|
+
*/
|
|
185
|
+
/** The part of `import.meta.hot` this uses. */
|
|
186
|
+
interface DevAgentHot {
|
|
187
|
+
send(event: string, data?: unknown): void;
|
|
188
|
+
on(event: string, listener: (data: never) => void): void;
|
|
189
|
+
}
|
|
190
|
+
/** What the bridge needs of an application: a way to reach its render worker. */
|
|
191
|
+
interface DevAgentApp {
|
|
192
|
+
openRenderPort(key: string): MessagePort | undefined;
|
|
193
|
+
}
|
|
194
|
+
/** Server to page. */
|
|
195
|
+
interface DevAgentRequest {
|
|
196
|
+
readonly id: number;
|
|
197
|
+
readonly message: unknown;
|
|
198
|
+
}
|
|
199
|
+
/** Page to server. */
|
|
200
|
+
interface DevAgentResponse {
|
|
201
|
+
readonly id: number;
|
|
202
|
+
readonly response: JsonRpcResponse | null;
|
|
203
|
+
}
|
|
204
|
+
/** The HMR events the bridge speaks. The plugin's half uses the same names. */
|
|
205
|
+
declare const DEV_AGENT_EVENTS: {
|
|
206
|
+
readonly ready: 'gesso:agent:ready';
|
|
207
|
+
readonly request: 'gesso:agent:request';
|
|
208
|
+
readonly response: 'gesso:agent:response';
|
|
209
|
+
};
|
|
210
|
+
declare function connectDevAgent(app: DevAgentApp, hot: DevAgentHot, info?: McpServerInfo): void;
|
|
211
|
+
//#endregion
|
|
212
|
+
//#region src/agent/webmcp.d.ts
|
|
213
|
+
/**
|
|
214
|
+
* An application's channels, offered to an agent in the browser.
|
|
215
|
+
*
|
|
216
|
+
* WebMCP (https://webmachinelearning.github.io/webmcp/) is a page
|
|
217
|
+
* registering tools with the browser, `document.modelContext.registerTool`,
|
|
218
|
+
* for an agent the browser runs or hosts to call. It is MCP without
|
|
219
|
+
* the server: no port, no process, and the tools live exactly as long
|
|
220
|
+
* as the page does. The channel surface is already a list of tools
|
|
221
|
+
* with JSON Schema inputs and an answer per call, so this registers
|
|
222
|
+
* each one and routes its calls back through the render worker.
|
|
223
|
+
*
|
|
224
|
+
* What the spec gives a tool that a channel does not, and the reverse:
|
|
225
|
+
*
|
|
226
|
+
* - `readOnlyHint` is a channel's view tool; `consequentialHint`, the
|
|
227
|
+
* spec's "significant, real-world, or non-reversible", is a command
|
|
228
|
+
* marked `@destructive`. `untrustedContentHint` is never set: a
|
|
229
|
+
* channel's view is the application's own data.
|
|
230
|
+
* - There is no output schema. The result of a call is serialized to
|
|
231
|
+
* JSON for the agent, so a call answers with the view it left.
|
|
232
|
+
* - There is no way to ask the person. A command marked `@confirm`
|
|
233
|
+
* asks through `confirm`, `window.confirm` unless told otherwise.
|
|
234
|
+
* - A failed call rejects, which the spec turns into a failed
|
|
235
|
+
* `executeTool` the agent reads, with the sentence that says why.
|
|
236
|
+
*
|
|
237
|
+
* The API is behind an origin trial in Chrome 149 to 156, and was
|
|
238
|
+
* `navigator.modelContext` until Chrome 150; the old name is read when
|
|
239
|
+
* the new one is missing. Where neither exists, nothing is registered
|
|
240
|
+
* and nothing fails: a page offering tools to a browser that takes
|
|
241
|
+
* none is an ordinary page.
|
|
242
|
+
*/
|
|
243
|
+
/** The part of `ModelContext` this uses. */
|
|
244
|
+
interface ModelContextLike {
|
|
245
|
+
registerTool(tool: {
|
|
246
|
+
name: string;
|
|
247
|
+
title?: string;
|
|
248
|
+
description: string;
|
|
249
|
+
inputSchema?: object;
|
|
250
|
+
annotations?: {
|
|
251
|
+
readOnlyHint?: boolean;
|
|
252
|
+
consequentialHint?: boolean;
|
|
253
|
+
};
|
|
254
|
+
execute: (input: Record<string, unknown>, options?: {
|
|
255
|
+
signal?: AbortSignal;
|
|
256
|
+
}) => Promise<unknown>;
|
|
257
|
+
}, options?: {
|
|
258
|
+
signal?: AbortSignal;
|
|
259
|
+
}): Promise<void>;
|
|
260
|
+
}
|
|
261
|
+
interface WebMcpOptions {
|
|
262
|
+
/**
|
|
263
|
+
* Asks the person whether a `@confirm` command may be sent. Defaults to
|
|
264
|
+
* the browser's own `window.confirm`, which an application with its
|
|
265
|
+
* own dialogs will want to replace.
|
|
266
|
+
*/
|
|
267
|
+
confirm?: AgentConfirm;
|
|
268
|
+
/** Where to register. Defaults to the page's own `modelContext`. */
|
|
269
|
+
modelContext?: ModelContextLike;
|
|
270
|
+
}
|
|
271
|
+
/** The page's model context, if the browser has one. */
|
|
272
|
+
declare function pageModelContext(): ModelContextLike | undefined;
|
|
273
|
+
/**
|
|
274
|
+
* Registers every tool a surface offers, and returns what removes them.
|
|
275
|
+
*
|
|
276
|
+
* A tool the browser refuses, a name already taken by something else on
|
|
277
|
+
* the page, is reported on the console and skipped rather than taking
|
|
278
|
+
* the rest down with it. Resolves to a no-op when there is no model
|
|
279
|
+
* context to register with.
|
|
280
|
+
*/
|
|
281
|
+
declare function registerWebMcpTools(surface: AgentSurfaceLike, modelContext?: ModelContextLike | undefined): Promise<() => void>;
|
|
282
|
+
/**
|
|
283
|
+
* Registers an application's channels once it is mounted.
|
|
284
|
+
*
|
|
285
|
+
* Reaches them the way the development bridge does, through an agent
|
|
286
|
+
* port to the render worker, so every channel the page can reach is
|
|
287
|
+
* offered, wherever it is served.
|
|
288
|
+
*/
|
|
289
|
+
declare function connectWebMcp(app: {
|
|
290
|
+
openRenderPort(key: string): MessagePort | undefined;
|
|
291
|
+
}, options?: WebMcpOptions): Promise<() => void>;
|
|
292
|
+
/** The browser's own dialog, worded for a person who did not ask for anything. */
|
|
293
|
+
declare function confirmInWindow(request: AgentConfirmation): boolean;
|
|
294
|
+
//#endregion
|
|
295
|
+
//#region src/agent/app.d.ts
|
|
296
|
+
/** What a thread that draws the application can offer an agent. */
|
|
297
|
+
interface ApplicationAgentParts {
|
|
298
|
+
/** The channels fed from this thread. */
|
|
299
|
+
readonly channels: () => readonly ServedChannel[];
|
|
300
|
+
/** The workers behind it, each asked over an agent port of its own. */
|
|
301
|
+
readonly workers: () => Iterable<WorkerHandle>;
|
|
302
|
+
/** The screen, once the application has started. */
|
|
303
|
+
readonly ui: () => UiHost | undefined;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Answers an agent port with the whole application: the channels this
|
|
307
|
+
* thread feeds, whatever each worker behind it serves, and the screen.
|
|
308
|
+
*
|
|
309
|
+
* The thread that draws is the one that knows all three, which is the
|
|
310
|
+
* render worker in the worker configuration and the page in the single
|
|
311
|
+
* thread one; both answer with this. The screen comes last, so a
|
|
312
|
+
* channel tool keeps its name.
|
|
313
|
+
*/
|
|
314
|
+
declare function serveApplicationAgent(port: MessagePort, parts: ApplicationAgentParts): () => void;
|
|
315
|
+
//#endregion
|
|
316
|
+
export { AGENT_PORT, type AgentConfirmation, type AgentPortRequest, type AgentPortResponse, type AgentResource, type AgentSurface, type AgentSurfaceLike, type AgentSurfaceOptions, type AgentTool, type AgentToolResult, type ApplicationAgentParts, DEV_AGENT_EVENTS, type DevAgentApp, type DevAgentHot, type DevAgentRequest, type DevAgentResponse, type JsonRpcResponse, MCP_PROTOCOL_VERSIONS, type McpHandlerOptions, type McpServerInfo, type ModelContextLike, type UiHost, UiRefs, type UiSurfaceOptions, type WebMcpOptions, agentSurface, combineSurfaces, confirmInWindow, connectDevAgent, connectWebMcp, handleMcpMessage, mcpHandler, outline, pageModelContext, registerWebMcpTools, remoteSurface, resolveTarget, resourceUri, serveAgentPort, serveApplicationAgent, uiSurface, validate };
|
|
317
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { a as agentSurface, i as serveAgentPort, n as combineSurfaces, o as resourceUri, r as remoteSurface, s as validate, t as AGENT_PORT } from "../remote-xxij8zXe.js";
|
|
2
|
+
import { a as resolveTarget, i as outline, n as serveApplicationAgent, o as uiSurface, r as UiRefs } from "../app-BvlIO1G9.js";
|
|
3
|
+
import { i as registerWebMcpTools, n as connectWebMcp, r as pageModelContext, t as confirmInWindow } from "../webmcp-CJVFvAHe.js";
|
|
4
|
+
//#region src/agent/mcp.ts
|
|
5
|
+
/**
|
|
6
|
+
* An agent surface, served over the Model Context Protocol.
|
|
7
|
+
*
|
|
8
|
+
* Two layers. `handleMcpMessage` answers one JSON-RPC message and knows
|
|
9
|
+
* nothing about how it arrived, which is all a stdio transport or a
|
|
10
|
+
* relay needs. `mcpHandler` wraps it in MCP's Streamable HTTP
|
|
11
|
+
* transport as a `fetch`-style function, `(Request) => Response`,
|
|
12
|
+
* which is what `Bun.serve`, Deno, a service worker and every modern
|
|
13
|
+
* Node server framework take, so the framework imports no server.
|
|
14
|
+
*
|
|
15
|
+
* What it implements is the part a channel needs: `initialize`, `ping`,
|
|
16
|
+
* `tools/list`, `tools/call`, `resources/list` and `resources/read`.
|
|
17
|
+
* Responses are plain JSON rather than an event stream, which the
|
|
18
|
+
* transport allows, and the GET stream for server-initiated messages
|
|
19
|
+
* is declined with 405, which it also allows: nothing here sends one,
|
|
20
|
+
* so the capabilities say neither `listChanged` nor `subscribe`.
|
|
21
|
+
*/
|
|
22
|
+
/** The protocol revisions this server speaks, newest first. */
|
|
23
|
+
const MCP_PROTOCOL_VERSIONS = [
|
|
24
|
+
"2025-11-25",
|
|
25
|
+
"2025-06-18",
|
|
26
|
+
"2025-03-26"
|
|
27
|
+
];
|
|
28
|
+
const PARSE_ERROR = -32700;
|
|
29
|
+
const INVALID_REQUEST = -32600;
|
|
30
|
+
const METHOD_NOT_FOUND = -32601;
|
|
31
|
+
const INVALID_PARAMS = -32602;
|
|
32
|
+
/** MCP's code for a resource that does not exist. */
|
|
33
|
+
const RESOURCE_NOT_FOUND = -32002;
|
|
34
|
+
/**
|
|
35
|
+
* Answers one JSON-RPC message. Returns null for a notification, which
|
|
36
|
+
* takes no answer, and never throws: a malformed message is answered
|
|
37
|
+
* with the JSON-RPC error that says what was wrong with it.
|
|
38
|
+
*/
|
|
39
|
+
async function handleMcpMessage(surface, message, info = {}) {
|
|
40
|
+
if (!isRequest(message)) return error(null, INVALID_REQUEST, "Expected a JSON-RPC 2.0 request with a method.");
|
|
41
|
+
if (message.id === void 0) return null;
|
|
42
|
+
const { id } = message;
|
|
43
|
+
const params = message.params ?? {};
|
|
44
|
+
switch (message.method) {
|
|
45
|
+
case "initialize": {
|
|
46
|
+
const asked = params.protocolVersion;
|
|
47
|
+
return ok(id, {
|
|
48
|
+
protocolVersion: MCP_PROTOCOL_VERSIONS.find((supported) => supported === asked) ?? MCP_PROTOCOL_VERSIONS[0],
|
|
49
|
+
capabilities: {
|
|
50
|
+
tools: {},
|
|
51
|
+
resources: {}
|
|
52
|
+
},
|
|
53
|
+
serverInfo: {
|
|
54
|
+
name: info.name ?? "gesso",
|
|
55
|
+
version: info.version ?? "0.0.0"
|
|
56
|
+
},
|
|
57
|
+
...info.instructions === void 0 ? {} : { instructions: info.instructions }
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
case "ping": return ok(id, {});
|
|
61
|
+
case "tools/list": return ok(id, { tools: await surface.tools() });
|
|
62
|
+
case "tools/call": {
|
|
63
|
+
if (typeof params.name !== "string") return error(id, INVALID_PARAMS, "tools/call needs the name of a tool.");
|
|
64
|
+
const args = params.arguments;
|
|
65
|
+
if (args !== void 0 && (args === null || typeof args !== "object" || Array.isArray(args))) return error(id, INVALID_PARAMS, "tools/call arguments must be an object.");
|
|
66
|
+
return ok(id, await surface.call(params.name, args));
|
|
67
|
+
}
|
|
68
|
+
case "resources/list": return ok(id, { resources: await surface.resources() });
|
|
69
|
+
case "resources/templates/list": return ok(id, { resourceTemplates: [] });
|
|
70
|
+
case "resources/read": {
|
|
71
|
+
const uri = params.uri;
|
|
72
|
+
const view = typeof uri === "string" ? await surface.read(uri) : void 0;
|
|
73
|
+
if (typeof uri !== "string" || view === void 0) return error(id, RESOURCE_NOT_FOUND, `No resource at ${String(uri)}.`);
|
|
74
|
+
return ok(id, { contents: [{
|
|
75
|
+
uri,
|
|
76
|
+
mimeType: "application/json",
|
|
77
|
+
text: JSON.stringify(view)
|
|
78
|
+
}] });
|
|
79
|
+
}
|
|
80
|
+
default: return error(id, METHOD_NOT_FOUND, `This server does not implement ${message.method}.`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* The Streamable HTTP transport, as a `fetch` handler.
|
|
85
|
+
*
|
|
86
|
+
* Bun.serve({ hostname: '127.0.0.1', port: 7310, fetch: mcpHandler(surface, { name: 'notes' }) });
|
|
87
|
+
*
|
|
88
|
+
* It answers every path; mount it where the server routes `/mcp`.
|
|
89
|
+
*/
|
|
90
|
+
function mcpHandler(surface, options = {}) {
|
|
91
|
+
return async (request) => {
|
|
92
|
+
const origin = request.headers.get("origin");
|
|
93
|
+
if (origin !== null && !(options.allowedOrigins ?? []).includes(origin)) return text(403, `Requests from ${origin} are not allowed.`);
|
|
94
|
+
if (options.token !== void 0 && request.headers.get("authorization") !== `Bearer ${options.token}`) return text(401, "This server needs a bearer token.");
|
|
95
|
+
const version = request.headers.get("mcp-protocol-version");
|
|
96
|
+
if (version !== null && !MCP_PROTOCOL_VERSIONS.includes(version)) return text(400, `Unsupported MCP protocol version ${version}.`);
|
|
97
|
+
if (request.method !== "POST") return new Response(null, {
|
|
98
|
+
status: 405,
|
|
99
|
+
headers: { allow: "POST" }
|
|
100
|
+
});
|
|
101
|
+
let message;
|
|
102
|
+
try {
|
|
103
|
+
message = await request.json();
|
|
104
|
+
} catch {
|
|
105
|
+
return json(400, error(null, PARSE_ERROR, "The body is not JSON."));
|
|
106
|
+
}
|
|
107
|
+
const response = await handleMcpMessage(surface, message, options);
|
|
108
|
+
return response === null ? new Response(null, { status: 202 }) : json(200, response);
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function isRequest(message) {
|
|
112
|
+
const candidate = message;
|
|
113
|
+
return typeof candidate === "object" && candidate !== null && !Array.isArray(candidate) && candidate.jsonrpc === "2.0" && typeof candidate.method === "string";
|
|
114
|
+
}
|
|
115
|
+
function ok(id, result) {
|
|
116
|
+
return {
|
|
117
|
+
jsonrpc: "2.0",
|
|
118
|
+
id,
|
|
119
|
+
result
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function error(id, code, message) {
|
|
123
|
+
return {
|
|
124
|
+
jsonrpc: "2.0",
|
|
125
|
+
id,
|
|
126
|
+
error: {
|
|
127
|
+
code,
|
|
128
|
+
message
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function json(status, body) {
|
|
133
|
+
return new Response(JSON.stringify(body), {
|
|
134
|
+
status,
|
|
135
|
+
headers: { "content-type": "application/json" }
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
function text(status, body) {
|
|
139
|
+
return new Response(body, {
|
|
140
|
+
status,
|
|
141
|
+
headers: { "content-type": "text/plain; charset=utf-8" }
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
//#endregion
|
|
145
|
+
//#region src/agent/dev.ts
|
|
146
|
+
/** The HMR events the bridge speaks. The plugin's half uses the same names. */
|
|
147
|
+
const DEV_AGENT_EVENTS = {
|
|
148
|
+
ready: "gesso:agent:ready",
|
|
149
|
+
request: "gesso:agent:request",
|
|
150
|
+
response: "gesso:agent:response"
|
|
151
|
+
};
|
|
152
|
+
function connectDevAgent(app, hot, info = {}) {
|
|
153
|
+
let surface;
|
|
154
|
+
/**
|
|
155
|
+
* Opened on the first request, because the render worker does not
|
|
156
|
+
* exist until the app mounts, and an agent may well ask first.
|
|
157
|
+
*/
|
|
158
|
+
const reach = () => {
|
|
159
|
+
if (surface !== void 0) return surface;
|
|
160
|
+
const port = app.openRenderPort(AGENT_PORT);
|
|
161
|
+
if (port === void 0) return combineSurfaces([]);
|
|
162
|
+
surface = remoteSurface(port, { confirm: confirmInWindow });
|
|
163
|
+
return surface;
|
|
164
|
+
};
|
|
165
|
+
hot.on(DEV_AGENT_EVENTS.request, (request) => {
|
|
166
|
+
handleMcpMessage(reach(), request.message, {
|
|
167
|
+
name: info.name ?? (document.title || "gesso"),
|
|
168
|
+
...info
|
|
169
|
+
}).then((response) => hot.send(DEV_AGENT_EVENTS.response, {
|
|
170
|
+
id: request.id,
|
|
171
|
+
response
|
|
172
|
+
}));
|
|
173
|
+
});
|
|
174
|
+
const announce = () => hot.send(DEV_AGENT_EVENTS.ready, {
|
|
175
|
+
title: document.title,
|
|
176
|
+
url: location.href
|
|
177
|
+
});
|
|
178
|
+
announce();
|
|
179
|
+
hot.on("vite:ws:connect", announce);
|
|
180
|
+
}
|
|
181
|
+
//#endregion
|
|
182
|
+
export { AGENT_PORT, DEV_AGENT_EVENTS, MCP_PROTOCOL_VERSIONS, UiRefs, agentSurface, combineSurfaces, confirmInWindow, connectDevAgent, connectWebMcp, handleMcpMessage, mcpHandler, outline, pageModelContext, registerWebMcpTools, remoteSurface, resolveTarget, resourceUri, serveAgentPort, serveApplicationAgent, uiSurface, validate };
|
|
183
|
+
|
|
184
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/agent/mcp.ts","../../src/agent/dev.ts"],"sourcesContent":["import type { AgentSurfaceLike } from './AgentSurface';\n\n/**\n * An agent surface, served over the Model Context Protocol.\n *\n * Two layers. `handleMcpMessage` answers one JSON-RPC message and knows\n * nothing about how it arrived, which is all a stdio transport or a\n * relay needs. `mcpHandler` wraps it in MCP's Streamable HTTP\n * transport as a `fetch`-style function, `(Request) => Response`,\n * which is what `Bun.serve`, Deno, a service worker and every modern\n * Node server framework take, so the framework imports no server.\n *\n * What it implements is the part a channel needs: `initialize`, `ping`,\n * `tools/list`, `tools/call`, `resources/list` and `resources/read`.\n * Responses are plain JSON rather than an event stream, which the\n * transport allows, and the GET stream for server-initiated messages\n * is declined with 405, which it also allows: nothing here sends one,\n * so the capabilities say neither `listChanged` nor `subscribe`.\n */\n\n/** The protocol revisions this server speaks, newest first. */\nexport const MCP_PROTOCOL_VERSIONS = ['2025-11-25', '2025-06-18', '2025-03-26'] as const;\n\nexport interface McpServerInfo {\n /** The name a client shows for this server. Defaults to `gesso`. */\n name?: string;\n version?: string;\n /** Told to the agent on connecting: what this application is, in a sentence or two. */\n instructions?: string;\n}\n\ntype JsonRpcId = string | number;\n\ninterface JsonRpcRequest {\n readonly jsonrpc: '2.0';\n readonly id?: JsonRpcId;\n readonly method: string;\n readonly params?: Record<string, unknown>;\n}\n\nexport type JsonRpcResponse =\n | { jsonrpc: '2.0'; id: JsonRpcId | null; result: unknown }\n | { jsonrpc: '2.0'; id: JsonRpcId | null; error: { code: number; message: string } };\n\nconst PARSE_ERROR = -32700;\nconst INVALID_REQUEST = -32600;\nconst METHOD_NOT_FOUND = -32601;\nconst INVALID_PARAMS = -32602;\n/** MCP's code for a resource that does not exist. */\nconst RESOURCE_NOT_FOUND = -32002;\n\n/**\n * Answers one JSON-RPC message. Returns null for a notification, which\n * takes no answer, and never throws: a malformed message is answered\n * with the JSON-RPC error that says what was wrong with it.\n */\nexport async function handleMcpMessage(\n surface: AgentSurfaceLike,\n message: unknown,\n info: McpServerInfo = {}\n): Promise<JsonRpcResponse | null> {\n if (!isRequest(message)) {\n return error(null, INVALID_REQUEST, 'Expected a JSON-RPC 2.0 request with a method.');\n }\n if (message.id === undefined) {\n // `notifications/initialized` and `notifications/cancelled` are the\n // ones a client sends. Neither needs anything from a server whose\n // every call answers before the next is read.\n return null;\n }\n const { id } = message;\n const params = message.params ?? {};\n switch (message.method) {\n case 'initialize': {\n const asked = params.protocolVersion;\n const version = MCP_PROTOCOL_VERSIONS.find(supported => supported === asked) ?? MCP_PROTOCOL_VERSIONS[0];\n return ok(id, {\n protocolVersion: version,\n capabilities: { tools: {}, resources: {} },\n serverInfo: { name: info.name ?? 'gesso', version: info.version ?? '0.0.0' },\n ...(info.instructions === undefined ? {} : { instructions: info.instructions })\n });\n }\n case 'ping':\n return ok(id, {});\n case 'tools/list':\n return ok(id, { tools: await surface.tools() });\n case 'tools/call': {\n if (typeof params.name !== 'string') {\n return error(id, INVALID_PARAMS, 'tools/call needs the name of a tool.');\n }\n const args = params.arguments;\n if (args !== undefined && (args === null || typeof args !== 'object' || Array.isArray(args))) {\n return error(id, INVALID_PARAMS, 'tools/call arguments must be an object.');\n }\n return ok(id, await surface.call(params.name, args as Record<string, unknown> | undefined));\n }\n case 'resources/list':\n return ok(id, { resources: await surface.resources() });\n case 'resources/templates/list':\n return ok(id, { resourceTemplates: [] });\n case 'resources/read': {\n const uri = params.uri;\n const view = typeof uri === 'string' ? await surface.read(uri) : undefined;\n if (typeof uri !== 'string' || view === undefined) {\n return error(id, RESOURCE_NOT_FOUND, `No resource at ${String(uri)}.`);\n }\n return ok(id, { contents: [{ uri, mimeType: 'application/json', text: JSON.stringify(view) }] });\n }\n default:\n return error(id, METHOD_NOT_FOUND, `This server does not implement ${message.method}.`);\n }\n}\n\nexport interface McpHandlerOptions extends McpServerInfo {\n /**\n * Origins a browser may call from. A request carrying any other\n * `Origin` is refused, as the transport requires: without it, a web\n * page the person happens to have open could reach a server bound to\n * their own machine and send commands to their application. Agents\n * that are not browsers send no `Origin` and are unaffected.\n */\n allowedOrigins?: readonly string[];\n /**\n * A secret every request must carry as `Authorization: Bearer\n * <token>`. Worth setting whenever the port is reachable by anything\n * other than the person's own agents.\n */\n token?: string;\n}\n\n/**\n * The Streamable HTTP transport, as a `fetch` handler.\n *\n * Bun.serve({ hostname: '127.0.0.1', port: 7310, fetch: mcpHandler(surface, { name: 'notes' }) });\n *\n * It answers every path; mount it where the server routes `/mcp`.\n */\nexport function mcpHandler(\n surface: AgentSurfaceLike,\n options: McpHandlerOptions = {}\n): (request: Request) => Promise<Response> {\n return async request => {\n const origin = request.headers.get('origin');\n if (origin !== null && !(options.allowedOrigins ?? []).includes(origin)) {\n return text(403, `Requests from ${origin} are not allowed.`);\n }\n if (options.token !== undefined && request.headers.get('authorization') !== `Bearer ${options.token}`) {\n return text(401, 'This server needs a bearer token.');\n }\n const version = request.headers.get('mcp-protocol-version');\n if (version !== null && !(MCP_PROTOCOL_VERSIONS as readonly string[]).includes(version)) {\n return text(400, `Unsupported MCP protocol version ${version}.`);\n }\n if (request.method !== 'POST') {\n // GET is the optional stream for messages the server starts, and\n // DELETE ends a session. There are no such messages and no\n // sessions, and 405 is how the transport says so.\n return new Response(null, { status: 405, headers: { allow: 'POST' } });\n }\n let message: unknown;\n try {\n message = await request.json();\n } catch {\n return json(400, error(null, PARSE_ERROR, 'The body is not JSON.'));\n }\n const response = await handleMcpMessage(surface, message, options);\n return response === null ? new Response(null, { status: 202 }) : json(200, response);\n };\n}\n\nfunction isRequest(message: unknown): message is JsonRpcRequest {\n const candidate = message as Partial<JsonRpcRequest> | null;\n return (\n typeof candidate === 'object' &&\n candidate !== null &&\n !Array.isArray(candidate) &&\n candidate.jsonrpc === '2.0' &&\n typeof candidate.method === 'string'\n );\n}\n\nfunction ok(id: JsonRpcId, result: unknown): JsonRpcResponse {\n return { jsonrpc: '2.0', id, result };\n}\n\nfunction error(id: JsonRpcId | null, code: number, message: string): JsonRpcResponse {\n return { jsonrpc: '2.0', id, error: { code, message } };\n}\n\nfunction json(status: number, body: unknown): Response {\n return new Response(JSON.stringify(body), { status, headers: { 'content-type': 'application/json' } });\n}\n\nfunction text(status: number, body: string): Response {\n return new Response(body, { status, headers: { 'content-type': 'text/plain; charset=utf-8' } });\n}\n","import { handleMcpMessage, type JsonRpcResponse, type McpServerInfo } from './mcp';\nimport { AGENT_PORT, combineSurfaces, remoteSurface } from './remote';\nimport { confirmInWindow } from './webmcp';\nimport type { AgentSurfaceLike } from './AgentSurface';\n\n/**\n * The page's half of the development agent bridge.\n *\n * `gesso-vite-plugin` serves MCP at `/__gesso/mcp` on the dev server,\n * but the channels are in the page's workers, and a server cannot\n * reach a worker. The page can, and it already holds a socket to the\n * server: Vite's HMR connection. So the server relays each JSON-RPC\n * message down that socket, this answers it against the render\n * worker's agent port, and the answer goes back up the same way.\n *\n * MCP is spoken here, in the page, rather than on the server, so the\n * server needs nothing from the framework and the workers need nothing\n * from MCP. The plugin injects the call in a dev server only; a build\n * carries no reference to this module.\n */\n\n/** The part of `import.meta.hot` this uses. */\nexport interface DevAgentHot {\n send(event: string, data?: unknown): void;\n on(event: string, listener: (data: never) => void): void;\n}\n\n/** What the bridge needs of an application: a way to reach its render worker. */\nexport interface DevAgentApp {\n openRenderPort(key: string): MessagePort | undefined;\n}\n\n/** Server to page. */\nexport interface DevAgentRequest {\n readonly id: number;\n readonly message: unknown;\n}\n\n/** Page to server. */\nexport interface DevAgentResponse {\n readonly id: number;\n readonly response: JsonRpcResponse | null;\n}\n\n/** The HMR events the bridge speaks. The plugin's half uses the same names. */\nexport const DEV_AGENT_EVENTS = {\n ready: 'gesso:agent:ready',\n request: 'gesso:agent:request',\n response: 'gesso:agent:response'\n} as const;\n\nexport function connectDevAgent(app: DevAgentApp, hot: DevAgentHot, info: McpServerInfo = {}): void {\n let surface: AgentSurfaceLike | undefined;\n /**\n * Opened on the first request, because the render worker does not\n * exist until the app mounts, and an agent may well ask first.\n */\n const reach = (): AgentSurfaceLike => {\n if (surface !== undefined) {\n return surface;\n }\n const port = app.openRenderPort(AGENT_PORT);\n if (port === undefined) {\n return combineSurfaces([]);\n }\n // A command marked `@confirm` is put to the person here, in the\n // page they are looking at. A browser's own dialog: in development\n // that is enough, and it cannot be mistaken for the application's.\n surface = remoteSurface(port, { confirm: confirmInWindow });\n return surface;\n };\n hot.on(DEV_AGENT_EVENTS.request, (request: DevAgentRequest) => {\n void handleMcpMessage(reach(), request.message, {\n name: info.name ?? (document.title || 'gesso'),\n ...info\n }).then(response => hot.send(DEV_AGENT_EVENTS.response, { id: request.id, response } satisfies DevAgentResponse));\n });\n const announce = () => hot.send(DEV_AGENT_EVENTS.ready, { title: document.title, url: location.href });\n announce();\n // A restarted dev server is a new one that has never heard of this\n // page, and the page reconnects to it without reloading. Saying so\n // again on every connection is what keeps an agent from being told\n // no page is open while one plainly is.\n hot.on('vite:ws:connect', announce);\n}\n"],"mappings":"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"}
|