@ggui-ai/mcp-server 0.2.0-alpha.4 → 0.4.0-rc.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/dist/admin-blueprints-transport.d.ts.map +1 -1
- package/dist/admin-blueprints-transport.js +2 -1
- package/dist/admin-oauth-providers-transport.d.ts.map +1 -1
- package/dist/admin-oauth-providers-transport.js +7 -5
- package/dist/api-renders-routes.d.ts +85 -0
- package/dist/api-renders-routes.d.ts.map +1 -0
- package/dist/api-renders-routes.js +372 -0
- package/dist/build-mcp.d.ts +2 -2
- package/dist/build-mcp.d.ts.map +1 -1
- package/dist/build-mcp.js +64 -5
- package/dist/code-routes.d.ts +47 -0
- package/dist/code-routes.d.ts.map +1 -0
- package/dist/code-routes.js +81 -0
- package/dist/code-store-fs.js +2 -2
- package/dist/console-auth.d.ts +10 -10
- package/dist/console-auth.d.ts.map +1 -1
- package/dist/console-auth.js +5 -5
- package/dist/console-blueprint-routes.d.ts +71 -0
- package/dist/console-blueprint-routes.d.ts.map +1 -0
- package/dist/console-blueprint-routes.js +348 -0
- package/dist/console-chat-routes.d.ts +80 -0
- package/dist/console-chat-routes.d.ts.map +1 -0
- package/dist/console-chat-routes.js +204 -0
- package/dist/console-config-routes.d.ts +37 -0
- package/dist/console-config-routes.d.ts.map +1 -0
- package/dist/console-config-routes.js +91 -0
- package/dist/console-headers.d.ts +1 -1
- package/dist/console-info-routes.d.ts +84 -0
- package/dist/console-info-routes.d.ts.map +1 -0
- package/dist/console-info-routes.js +135 -0
- package/dist/console-keys-routes.d.ts +50 -0
- package/dist/console-keys-routes.d.ts.map +1 -0
- package/dist/console-keys-routes.js +222 -0
- package/dist/console-llm-keys-routes.d.ts +47 -0
- package/dist/console-llm-keys-routes.d.ts.map +1 -0
- package/dist/console-llm-keys-routes.js +443 -0
- package/dist/console-mcp-tools-routes.d.ts +41 -0
- package/dist/console-mcp-tools-routes.d.ts.map +1 -0
- package/dist/console-mcp-tools-routes.js +60 -0
- package/dist/console-registry-routes.d.ts +66 -0
- package/dist/console-registry-routes.d.ts.map +1 -0
- package/dist/console-registry-routes.js +276 -0
- package/dist/console-session-routes.d.ts +89 -0
- package/dist/console-session-routes.d.ts.map +1 -0
- package/dist/console-session-routes.js +385 -0
- package/dist/console-sessions-routes.d.ts +52 -0
- package/dist/console-sessions-routes.d.ts.map +1 -0
- package/dist/console-sessions-routes.js +106 -0
- package/dist/console-static-routes.d.ts +54 -0
- package/dist/console-static-routes.d.ts.map +1 -0
- package/dist/console-static-routes.js +190 -0
- package/dist/console-theme-routes.d.ts +3 -3
- package/dist/console-theme-routes.js +1 -1
- package/dist/console-timeline.d.ts +5 -5
- package/dist/console-timeline.d.ts.map +1 -1
- package/dist/console-timeline.js +27 -26
- package/dist/console-welcome.js +2 -2
- package/dist/email-login.d.ts.map +1 -1
- package/dist/email-login.js +2 -3
- package/dist/ggui-session-channel/action-ingress.d.ts +54 -0
- package/dist/ggui-session-channel/action-ingress.d.ts.map +1 -0
- package/dist/ggui-session-channel/action-ingress.js +228 -0
- package/dist/ggui-session-channel/channel-subscriptions.d.ts +97 -0
- package/dist/ggui-session-channel/channel-subscriptions.d.ts.map +1 -0
- package/dist/ggui-session-channel/channel-subscriptions.js +224 -0
- package/dist/ggui-session-channel/internal-types.d.ts +102 -0
- package/dist/ggui-session-channel/internal-types.d.ts.map +1 -0
- package/dist/ggui-session-channel/internal-types.js +6 -0
- package/dist/ggui-session-channel/outbound.d.ts +81 -0
- package/dist/ggui-session-channel/outbound.d.ts.map +1 -0
- package/dist/ggui-session-channel/outbound.js +174 -0
- package/dist/ggui-session-channel/socket-router.d.ts +38 -0
- package/dist/ggui-session-channel/socket-router.d.ts.map +1 -0
- package/dist/ggui-session-channel/socket-router.js +213 -0
- package/dist/ggui-session-channel/subscribe.d.ts +165 -0
- package/dist/ggui-session-channel/subscribe.d.ts.map +1 -0
- package/dist/ggui-session-channel/subscribe.js +370 -0
- package/dist/ggui-session-channel/subscriber-lifecycle.d.ts +40 -0
- package/dist/ggui-session-channel/subscriber-lifecycle.d.ts.map +1 -0
- package/dist/ggui-session-channel/subscriber-lifecycle.js +123 -0
- package/dist/ggui-session-channel.d.ts +425 -0
- package/dist/ggui-session-channel.d.ts.map +1 -0
- package/dist/ggui-session-channel.js +262 -0
- package/dist/health-routes.d.ts +76 -0
- package/dist/health-routes.d.ts.map +1 -0
- package/dist/health-routes.js +145 -0
- package/dist/index.d.ts +10 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -9
- package/dist/instructions-presets.d.ts +3 -3
- package/dist/instructions-presets.js +24 -24
- package/dist/llm-backed-negotiator.d.ts +68 -67
- package/dist/llm-backed-negotiator.d.ts.map +1 -1
- package/dist/llm-backed-negotiator.js +82 -221
- package/dist/mcp-apps-outbound.d.ts +47 -48
- package/dist/mcp-apps-outbound.d.ts.map +1 -1
- package/dist/mcp-apps-outbound.js +154 -177
- package/dist/mcp-endpoint-routes.d.ts +88 -0
- package/dist/mcp-endpoint-routes.d.ts.map +1 -0
- package/dist/mcp-endpoint-routes.js +359 -0
- package/dist/mcp-mounts.d.ts +2 -76
- package/dist/mcp-mounts.d.ts.map +1 -1
- package/dist/mcp-mounts.js +0 -76
- package/dist/oauth-as-routes.d.ts +60 -0
- package/dist/oauth-as-routes.d.ts.map +1 -0
- package/dist/oauth-as-routes.js +82 -0
- package/dist/oauth-clients-routes.d.ts +39 -0
- package/dist/oauth-clients-routes.d.ts.map +1 -0
- package/dist/oauth-clients-routes.js +87 -0
- package/dist/oauth-login-types.d.ts +1 -20
- package/dist/oauth-login-types.d.ts.map +1 -1
- package/dist/oauth-login-types.js +30 -7
- package/dist/oauth-login.d.ts.map +1 -1
- package/dist/oauth-login.js +3 -2
- package/dist/oauth-providers-store.d.ts.map +1 -1
- package/dist/oauth-providers-store.js +5 -5
- package/dist/oauth.d.ts +9 -8
- package/dist/oauth.d.ts.map +1 -1
- package/dist/oauth.js +41 -19
- package/dist/pairing-transport.d.ts.map +1 -1
- package/dist/pairing-transport.js +2 -1
- package/dist/request-context.d.ts +2 -2
- package/dist/request-context.js +2 -2
- package/dist/reserved-validators.d.ts.map +1 -1
- package/dist/reserved-validators.js +9 -1
- package/dist/route-param.d.ts +9 -0
- package/dist/route-param.d.ts.map +1 -0
- package/dist/route-param.js +10 -0
- package/dist/runtime-bundle-route.d.ts +43 -0
- package/dist/runtime-bundle-route.d.ts.map +1 -0
- package/dist/runtime-bundle-route.js +80 -0
- package/dist/schema-compat.d.ts +64 -62
- package/dist/schema-compat.d.ts.map +1 -1
- package/dist/schema-compat.js +23 -51
- package/dist/server.d.ts +179 -193
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +644 -3759
- package/dist/storage.d.ts +5 -5
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +5 -5
- package/dist/thread-transport.d.ts.map +1 -1
- package/dist/thread-transport.js +4 -3
- package/dist/user-session-auth.d.ts +7 -21
- package/dist/user-session-auth.d.ts.map +1 -1
- package/dist/user-session-auth.js +7 -28
- package/package.json +16 -15
- package/dist/mcp-apps-inbound.d.ts +0 -86
- package/dist/mcp-apps-inbound.d.ts.map +0 -1
- package/dist/mcp-apps-inbound.js +0 -283
- package/dist/render-channel.d.ts +0 -694
- package/dist/render-channel.d.ts.map +0 -1
- package/dist/render-channel.js +0 -1775
package/dist/build-mcp.js
CHANGED
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
12
12
|
import { registerAppTool } from '@modelcontextprotocol/ext-apps/server';
|
|
13
|
+
import { isRecord } from '@ggui-ai/protocol';
|
|
13
14
|
import { z } from 'zod';
|
|
15
|
+
import { isHandlerFailure, } from '@ggui-ai/mcp-server-handlers';
|
|
14
16
|
import { installMcpAppsOutbound, } from './mcp-apps-outbound.js';
|
|
15
17
|
/**
|
|
16
18
|
* Build a fresh MCP server with every handler registered.
|
|
@@ -74,16 +76,42 @@ export function buildMcpServer(info, handlers, getContext, logger, opts = {}) {
|
|
|
74
76
|
// deadline. Both ride the canonical context without leaking the
|
|
75
77
|
// SDK type into the handlers package.
|
|
76
78
|
const baseCtx = getContext();
|
|
79
|
+
// `_meta` is `unknown` at the SDK seam; per JSON-RPC it MUST be
|
|
80
|
+
// an object, so narrow with the validating predicate and DROP
|
|
81
|
+
// anything else rather than asserting.
|
|
82
|
+
const requestMeta = extra?._meta;
|
|
77
83
|
const ctx = {
|
|
78
84
|
...baseCtx,
|
|
79
|
-
...(
|
|
80
|
-
? { requestMeta: extra._meta }
|
|
81
|
-
: {}),
|
|
85
|
+
...(isRecord(requestMeta) ? { requestMeta } : {}),
|
|
82
86
|
...(extra?.signal !== undefined ? { signal: extra.signal } : {}),
|
|
83
87
|
};
|
|
84
88
|
const start = Date.now();
|
|
85
89
|
try {
|
|
86
90
|
const data = await handler.handler(input, ctx);
|
|
91
|
+
// First-class in-result failure channel. A handler that
|
|
92
|
+
// returns the `HandlerFailure` marker gets an `isError: true`
|
|
93
|
+
// TOOL RESULT (never a thrown/JSON-RPC error): the marker's
|
|
94
|
+
// `errorText` is the model-visible content, and its `data` is
|
|
95
|
+
// validated against the SAME outputSchema as a success — MCP
|
|
96
|
+
// SDK clients validate structuredContent against outputSchema
|
|
97
|
+
// even when isError is set, so the envelope stays
|
|
98
|
+
// schema-conformant. NO `_meta` on failures: `resultMeta` is
|
|
99
|
+
// not invoked, so no mount affordance / bootstrap slice is
|
|
100
|
+
// emitted for a failed call.
|
|
101
|
+
if (isHandlerFailure(data)) {
|
|
102
|
+
const validated = z.object(handler.outputSchema).parse(data.data);
|
|
103
|
+
logger.warn('tool_invoked', {
|
|
104
|
+
tool: handler.name,
|
|
105
|
+
appId: ctx.appId,
|
|
106
|
+
outcome: 'tool_error',
|
|
107
|
+
elapsedMs: Date.now() - start,
|
|
108
|
+
});
|
|
109
|
+
return {
|
|
110
|
+
isError: true,
|
|
111
|
+
structuredContent: validated,
|
|
112
|
+
content: [{ type: 'text', text: data.errorText }],
|
|
113
|
+
};
|
|
114
|
+
}
|
|
87
115
|
const validated = z.object(handler.outputSchema).parse(data);
|
|
88
116
|
// Per-result `_meta` — NOT merged into structuredContent, so
|
|
89
117
|
// agents that typecheck against the tool signature never see
|
|
@@ -128,11 +156,18 @@ export function buildMcpServer(info, handlers, getContext, logger, opts = {}) {
|
|
|
128
156
|
// single canonical key only; the helper owns the back-compat
|
|
129
157
|
// shape. Handlers without `_meta.ui` fall through to plain
|
|
130
158
|
// `registerTool` — the ext-apps helper requires `_meta.ui` to be
|
|
131
|
-
// typed.
|
|
159
|
+
// typed. A declared-but-malformed `_meta.ui` is a programming
|
|
160
|
+
// error on the handler author's side — fail loud rather than
|
|
161
|
+
// silently registering the tool without its UI surface.
|
|
132
162
|
if (handler._meta && 'ui' in handler._meta) {
|
|
163
|
+
const ui = handler._meta['ui'];
|
|
164
|
+
if (!isMcpUiToolMeta(ui)) {
|
|
165
|
+
throw new Error(`Tool ${handler.name} declares _meta.ui with an invalid shape — ` +
|
|
166
|
+
`expected { resourceUri?: string; visibility?: ('model' | 'app')[] }.`);
|
|
167
|
+
}
|
|
133
168
|
registerAppTool(server, handler.name, {
|
|
134
169
|
...baseConfig,
|
|
135
|
-
_meta: handler._meta,
|
|
170
|
+
_meta: { ...handler._meta, ui },
|
|
136
171
|
}, cb);
|
|
137
172
|
}
|
|
138
173
|
else {
|
|
@@ -144,6 +179,30 @@ export function buildMcpServer(info, handlers, getContext, logger, opts = {}) {
|
|
|
144
179
|
}
|
|
145
180
|
return server;
|
|
146
181
|
}
|
|
182
|
+
/**
|
|
183
|
+
* Validating narrower for declaration-level MCP-Apps UI meta. The
|
|
184
|
+
* `SharedHandler` seam types `_meta` as `Record<string, unknown>`;
|
|
185
|
+
* `registerAppTool` requires `_meta.ui` typed as `McpUiToolMeta`.
|
|
186
|
+
* Validates the two fields the ext-apps helper actually reads —
|
|
187
|
+
* `resourceUri` (string when present) and `visibility`
|
|
188
|
+
* (`"model"`/`"app"` array when present) — instead of asserting
|
|
189
|
+
* blindly across the SDK seam.
|
|
190
|
+
*/
|
|
191
|
+
function isMcpUiToolMeta(value) {
|
|
192
|
+
if (!isRecord(value))
|
|
193
|
+
return false;
|
|
194
|
+
if (value.resourceUri !== undefined && typeof value.resourceUri !== 'string') {
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
if (value.visibility !== undefined) {
|
|
198
|
+
if (!Array.isArray(value.visibility))
|
|
199
|
+
return false;
|
|
200
|
+
if (!value.visibility.every((v) => v === 'model' || v === 'app')) {
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
147
206
|
function errorClassName(err) {
|
|
148
207
|
if (err instanceof Error) {
|
|
149
208
|
if (err.name && err.name !== 'Error')
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content-addressable code delivery routes.
|
|
3
|
+
*
|
|
4
|
+
* GET /code/:hash.js — content-addressable componentCode delivery.
|
|
5
|
+
* GET /contract/:hash.js — content-addressable contract-validator-bundle
|
|
6
|
+
* delivery (#109).
|
|
7
|
+
*
|
|
8
|
+
* Both routes serve from the same {@link CodeStore} keyed by
|
|
9
|
+
* `sha256(bytes)`. The same store is safe to share — content-addressable
|
|
10
|
+
* hashes can't collide across kinds unless the bytes are identical
|
|
11
|
+
* (in which case the cached value is equally valid for either path).
|
|
12
|
+
* Two separate URLs exist for debuggability + protocol clarity: a
|
|
13
|
+
* request to `/code/<hash>.js` is unambiguously a component fetch;
|
|
14
|
+
* `/contract/<hash>.js` is unambiguously a validator-bundle fetch.
|
|
15
|
+
*
|
|
16
|
+
* Cache posture: `Cache-Control: public, max-age=31536000, immutable` —
|
|
17
|
+
* hash is content-derived, the bytes can NEVER change for a given URL,
|
|
18
|
+
* so browsers + CDNs cache forever (immutable means "don't even
|
|
19
|
+
* revalidate"). A second render with the same componentCode / contract
|
|
20
|
+
* hits browser cache for free.
|
|
21
|
+
*
|
|
22
|
+
* CORS: same `*` posture as `/r/:shortCode` (JSON branch) — bytes are
|
|
23
|
+
* public-by-shortCode anyway (the agent already shared the URL with
|
|
24
|
+
* the host), and the bytes carry no credentials.
|
|
25
|
+
*
|
|
26
|
+
* Validation: hash MUST match `[a-f0-9]{64}` — strict charset gate
|
|
27
|
+
* closes path-traversal (`..`, `/`) and other shenanigans before the
|
|
28
|
+
* store sees the parameter.
|
|
29
|
+
*/
|
|
30
|
+
import type { CodeStore } from "@ggui-ai/mcp-server-core";
|
|
31
|
+
import type { Express } from "express";
|
|
32
|
+
import type { Logger } from "./logger.js";
|
|
33
|
+
interface MountOptions {
|
|
34
|
+
/** Express app to mount onto. */
|
|
35
|
+
readonly app: Express;
|
|
36
|
+
/** Content-addressable store both routes serve from. */
|
|
37
|
+
readonly codeStore: CodeStore;
|
|
38
|
+
/** Structured logger for fetch-failure warnings. */
|
|
39
|
+
readonly logger: Logger;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Mount `GET /code/:hash.js` + `GET /contract/:hash.js` onto the
|
|
43
|
+
* express app. Returns nothing — the routes self-register.
|
|
44
|
+
*/
|
|
45
|
+
export declare function mountCodeRoutes(opts: MountOptions): void;
|
|
46
|
+
export {};
|
|
47
|
+
//# sourceMappingURL=code-routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-routes.d.ts","sourceRoot":"","sources":["../src/code-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAE1D,OAAO,KAAK,EAAE,OAAO,EAAqB,MAAM,SAAS,CAAC;AAC1D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,UAAU,YAAY;IACpB,iCAAiC;IACjC,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,wDAAwD;IACxD,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,oDAAoD;IACpD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CA6CxD"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content-addressable code delivery routes.
|
|
3
|
+
*
|
|
4
|
+
* GET /code/:hash.js — content-addressable componentCode delivery.
|
|
5
|
+
* GET /contract/:hash.js — content-addressable contract-validator-bundle
|
|
6
|
+
* delivery (#109).
|
|
7
|
+
*
|
|
8
|
+
* Both routes serve from the same {@link CodeStore} keyed by
|
|
9
|
+
* `sha256(bytes)`. The same store is safe to share — content-addressable
|
|
10
|
+
* hashes can't collide across kinds unless the bytes are identical
|
|
11
|
+
* (in which case the cached value is equally valid for either path).
|
|
12
|
+
* Two separate URLs exist for debuggability + protocol clarity: a
|
|
13
|
+
* request to `/code/<hash>.js` is unambiguously a component fetch;
|
|
14
|
+
* `/contract/<hash>.js` is unambiguously a validator-bundle fetch.
|
|
15
|
+
*
|
|
16
|
+
* Cache posture: `Cache-Control: public, max-age=31536000, immutable` —
|
|
17
|
+
* hash is content-derived, the bytes can NEVER change for a given URL,
|
|
18
|
+
* so browsers + CDNs cache forever (immutable means "don't even
|
|
19
|
+
* revalidate"). A second render with the same componentCode / contract
|
|
20
|
+
* hits browser cache for free.
|
|
21
|
+
*
|
|
22
|
+
* CORS: same `*` posture as `/r/:shortCode` (JSON branch) — bytes are
|
|
23
|
+
* public-by-shortCode anyway (the agent already shared the URL with
|
|
24
|
+
* the host), and the bytes carry no credentials.
|
|
25
|
+
*
|
|
26
|
+
* Validation: hash MUST match `[a-f0-9]{64}` — strict charset gate
|
|
27
|
+
* closes path-traversal (`..`, `/`) and other shenanigans before the
|
|
28
|
+
* store sees the parameter.
|
|
29
|
+
*/
|
|
30
|
+
import { CODE_HASH_REGEX } from "@ggui-ai/mcp-server-core";
|
|
31
|
+
/**
|
|
32
|
+
* Mount `GET /code/:hash.js` + `GET /contract/:hash.js` onto the
|
|
33
|
+
* express app. Returns nothing — the routes self-register.
|
|
34
|
+
*/
|
|
35
|
+
export function mountCodeRoutes(opts) {
|
|
36
|
+
const { app, codeStore, logger } = opts;
|
|
37
|
+
const mountContentAddressableRoute = (mountPath, label) => {
|
|
38
|
+
app.get(mountPath, async (req, res) => {
|
|
39
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
40
|
+
const hash = req.params["hash"];
|
|
41
|
+
if (typeof hash !== "string" || !CODE_HASH_REGEX.test(hash)) {
|
|
42
|
+
res.setHeader("Cache-Control", "no-store");
|
|
43
|
+
res.status(400).json({
|
|
44
|
+
error: {
|
|
45
|
+
code: "invalid_request",
|
|
46
|
+
message: "hash path parameter must be 64-char lowercase hex",
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
const code = await codeStore.get(hash);
|
|
53
|
+
if (code === null) {
|
|
54
|
+
res.setHeader("Cache-Control", "no-store");
|
|
55
|
+
res.status(404).json({
|
|
56
|
+
error: {
|
|
57
|
+
code: "not_found",
|
|
58
|
+
message: `unknown ${label} hash`,
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
res.setHeader("Content-Type", "application/javascript; charset=utf-8");
|
|
64
|
+
res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
|
|
65
|
+
res.status(200).send(code);
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
logger.warn(`${label}_route_failed`, { hash, error: String(err) });
|
|
69
|
+
res.setHeader("Cache-Control", "no-store");
|
|
70
|
+
res.status(500).json({
|
|
71
|
+
error: {
|
|
72
|
+
code: "internal",
|
|
73
|
+
message: `${label} fetch failed`,
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
mountContentAddressableRoute("/code/:hash.js", "code");
|
|
80
|
+
mountContentAddressableRoute("/contract/:hash.js", "contract");
|
|
81
|
+
}
|
package/dist/code-store-fs.js
CHANGED
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
* The store grows unbounded. Operators who care can periodically
|
|
30
30
|
* `rm -rf ~/.ggui/code-cache/` — every entry is content-addressable
|
|
31
31
|
* and immutable, so a re-fetch will repopulate from upstream code on
|
|
32
|
-
* next
|
|
32
|
+
* next render. There is no eviction policy in the seam itself.
|
|
33
33
|
*
|
|
34
34
|
* ## Permissions
|
|
35
35
|
*
|
|
@@ -61,7 +61,7 @@ export class FileSystemCodeStore {
|
|
|
61
61
|
const absPath = this.absPath(hash);
|
|
62
62
|
// Idempotent: if the file already exists, skip rewrite. Cheap fast
|
|
63
63
|
// path that avoids unnecessary disk churn when the same blueprint
|
|
64
|
-
// is
|
|
64
|
+
// is rendered repeatedly.
|
|
65
65
|
if (await fileExists(absPath))
|
|
66
66
|
return;
|
|
67
67
|
await mkdir(dirname(absPath), { recursive: true });
|
package/dist/console-auth.d.ts
CHANGED
|
@@ -33,23 +33,23 @@
|
|
|
33
33
|
* so the cookie never leaves the console's origin in the first
|
|
34
34
|
* place.
|
|
35
35
|
*
|
|
36
|
-
* Lifecycle. Cookie is minted at `POST /ggui/console/
|
|
36
|
+
* Lifecycle. Cookie is minted at `POST /ggui/console/session-cookie`
|
|
37
37
|
* (operator posts a shortCode → server resolves → sets Set-Cookie).
|
|
38
38
|
* Cookie is consumed at `GET /ws` upgrade (if the server composition
|
|
39
|
-
* wires `cookieAuth` to the
|
|
39
|
+
* wires `cookieAuth` to the render-channel). Never mutated in
|
|
40
40
|
* between.
|
|
41
41
|
*/
|
|
42
42
|
import type { IncomingHttpHeaders } from 'node:http';
|
|
43
43
|
/**
|
|
44
44
|
* Cookie name. Chosen deliberately to NOT conflict with common framework
|
|
45
45
|
* cookies (Express session, CSRF, etc.). Change carries a compat
|
|
46
|
-
* concern — the console SPA + the
|
|
46
|
+
* concern — the console SPA + the render-channel upgrade read by
|
|
47
47
|
* this exact name. Keep this export as the single source of truth.
|
|
48
48
|
*/
|
|
49
49
|
export declare const CONSOLE_COOKIE_NAME = "ggui_console_session";
|
|
50
50
|
/**
|
|
51
51
|
* Result of minting a cookie. Callers set the cookie on their response
|
|
52
|
-
* (the cookie endpoint) and echo the bound `
|
|
52
|
+
* (the cookie endpoint) and echo the bound `sessionId`/`appId` back in
|
|
53
53
|
* the response body so the client SPA can bootstrap the viewer without
|
|
54
54
|
* a follow-up round-trip.
|
|
55
55
|
*/
|
|
@@ -57,11 +57,11 @@ export interface DevtoolCookieMint {
|
|
|
57
57
|
readonly cookieValue: string;
|
|
58
58
|
readonly setCookieHeader: string;
|
|
59
59
|
readonly expiresAt: number;
|
|
60
|
-
readonly
|
|
60
|
+
readonly sessionId: string;
|
|
61
61
|
readonly appId: string;
|
|
62
62
|
}
|
|
63
63
|
export interface MintDevtoolCookieInput {
|
|
64
|
-
readonly
|
|
64
|
+
readonly sessionId: string;
|
|
65
65
|
readonly appId: string;
|
|
66
66
|
/** HMAC secret. Shared with bootstrap + session tokens by design. */
|
|
67
67
|
readonly secret: string;
|
|
@@ -84,7 +84,7 @@ export interface MintDevtoolCookieInput {
|
|
|
84
84
|
readonly sameSite?: 'Strict' | 'Lax' | 'None';
|
|
85
85
|
}
|
|
86
86
|
/**
|
|
87
|
-
* Mint a cookie value bound to `{
|
|
87
|
+
* Mint a cookie value bound to `{ sessionId, appId }` and the formatted
|
|
88
88
|
* `Set-Cookie` header to send on the response. Caller does
|
|
89
89
|
* `res.setHeader('Set-Cookie', result.setCookieHeader)`.
|
|
90
90
|
*/
|
|
@@ -117,11 +117,11 @@ export declare function extractDevtoolCookie(cookieHeader: string | undefined):
|
|
|
117
117
|
export declare function readDevtoolCookieFromHeaders(headers: IncomingHttpHeaders): string | null;
|
|
118
118
|
/**
|
|
119
119
|
* Verified claims extracted from an console cookie. Scope is
|
|
120
|
-
* deliberately narrow: just the binding the
|
|
121
|
-
* needs to enforce `subscribe.
|
|
120
|
+
* deliberately narrow: just the binding the render-channel upgrade
|
|
121
|
+
* needs to enforce `subscribe.sessionId === cookie.sessionId`.
|
|
122
122
|
*/
|
|
123
123
|
export interface DevtoolCookieClaims {
|
|
124
|
-
readonly
|
|
124
|
+
readonly sessionId: string;
|
|
125
125
|
readonly appId: string;
|
|
126
126
|
}
|
|
127
127
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"console-auth.d.ts","sourceRoot":"","sources":["../src/console-auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAOrD;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,yBAAyB,CAAC;AAE1D;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,
|
|
1
|
+
{"version":3,"file":"console-auth.d.ts","sourceRoot":"","sources":["../src/console-auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAOrD;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,yBAAyB,CAAC;AAE1D;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,qEAAqE;IACrE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,kDAAkD;IAClD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,oCAAoC;IACpC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;CAC/C;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,sBAAsB,GAC5B,iBAAiB,CAqBnB;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,YAAY,EAAE,MAAM,GAAG,SAAS,GAC/B,MAAM,GAAG,IAAI,CAcf;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,mBAAmB,GAC3B,MAAM,GAAG,IAAI,CAIf;AAED;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,GACb,mBAAmB,GAAG,IAAI,CAO5B"}
|
package/dist/console-auth.js
CHANGED
|
@@ -2,18 +2,18 @@ import { DEFAULT_DEVTOOL_SESSION_TTL_SEC, mintDevtoolSessionToken, verifyToken,
|
|
|
2
2
|
/**
|
|
3
3
|
* Cookie name. Chosen deliberately to NOT conflict with common framework
|
|
4
4
|
* cookies (Express session, CSRF, etc.). Change carries a compat
|
|
5
|
-
* concern — the console SPA + the
|
|
5
|
+
* concern — the console SPA + the render-channel upgrade read by
|
|
6
6
|
* this exact name. Keep this export as the single source of truth.
|
|
7
7
|
*/
|
|
8
8
|
export const CONSOLE_COOKIE_NAME = 'ggui_console_session';
|
|
9
9
|
/**
|
|
10
|
-
* Mint a cookie value bound to `{
|
|
10
|
+
* Mint a cookie value bound to `{ sessionId, appId }` and the formatted
|
|
11
11
|
* `Set-Cookie` header to send on the response. Caller does
|
|
12
12
|
* `res.setHeader('Set-Cookie', result.setCookieHeader)`.
|
|
13
13
|
*/
|
|
14
14
|
export function mintDevtoolCookie(input) {
|
|
15
15
|
const ttlSec = input.ttlSec ?? DEFAULT_DEVTOOL_SESSION_TTL_SEC;
|
|
16
|
-
const { token, claims } = mintDevtoolSessionToken({
|
|
16
|
+
const { token, claims } = mintDevtoolSessionToken({ sessionId: input.sessionId, appId: input.appId, ttlSec }, input.secret);
|
|
17
17
|
const attrs = [
|
|
18
18
|
`${CONSOLE_COOKIE_NAME}=${token}`,
|
|
19
19
|
`Path=${input.path ?? '/'}`,
|
|
@@ -27,7 +27,7 @@ export function mintDevtoolCookie(input) {
|
|
|
27
27
|
cookieValue: token,
|
|
28
28
|
setCookieHeader: attrs.join('; '),
|
|
29
29
|
expiresAt: claims.exp * 1000,
|
|
30
|
-
|
|
30
|
+
sessionId: input.sessionId,
|
|
31
31
|
appId: input.appId,
|
|
32
32
|
};
|
|
33
33
|
}
|
|
@@ -96,7 +96,7 @@ export function verifyDevtoolCookie(cookieValue, secret) {
|
|
|
96
96
|
if (!result.ok)
|
|
97
97
|
return null;
|
|
98
98
|
return {
|
|
99
|
-
|
|
99
|
+
sessionId: result.claims.sessionId,
|
|
100
100
|
appId: result.claims.appId,
|
|
101
101
|
};
|
|
102
102
|
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Console blueprint resolution + try-live routes.
|
|
3
|
+
*
|
|
4
|
+
* GET /ggui/console/blueprint/:id — same-origin HTTP mirror of
|
|
5
|
+
* the `ggui_render_blueprint` MCP tool. Resolves a
|
|
6
|
+
* manifest-declared blueprint id to its compiled bundle +
|
|
7
|
+
* metadata via the wired `UiRegistry`; lets the SPA's
|
|
8
|
+
* `/preview/<id>` route mount the blueprint with a single fetch
|
|
9
|
+
* instead of negotiating a full MCP round-trip from the browser.
|
|
10
|
+
* POST /ggui/console/blueprint/:id/try — create a render, compile
|
|
11
|
+
* the blueprint's componentCode, commit a render with its full
|
|
12
|
+
* contract (actionSpec/streamSpec/propsSpec from the manifest),
|
|
13
|
+
* mint a shortCode, and return `{sessionId, shortCode, url}`.
|
|
14
|
+
* The returned `/s/<shortCode>` lands on the console's render
|
|
15
|
+
* viewer + subscribes to the render over `/ws`.
|
|
16
|
+
*
|
|
17
|
+
* Scope: registered only when a `UiRegistry` is present (same gate
|
|
18
|
+
* as the MCP render handler — no registry = no render path, no
|
|
19
|
+
* endpoint). No bearer auth: console routes are same-origin
|
|
20
|
+
* operator-facing; the operator already has OS access to the TSX
|
|
21
|
+
* sources this endpoint serves back.
|
|
22
|
+
*
|
|
23
|
+
* GET failure shape: { error, message } with a matching HTTP code.
|
|
24
|
+
* - 404 for unknown id OR known-id-no-bundle (source-only /
|
|
25
|
+
* compile-failed). The operator's remediation is the same in
|
|
26
|
+
* both cases (fix the manifest / fix the entry / fix the
|
|
27
|
+
* compile); splitting codes would add noise without signal.
|
|
28
|
+
* - 400 for malformed id parameters (empty, oversized).
|
|
29
|
+
*
|
|
30
|
+
* Shape symmetry: GET matches `GguiRenderBlueprintOutput` exactly, so
|
|
31
|
+
* the browser-side fetch can share a type import with MCP-tool
|
|
32
|
+
* callers without a translation layer.
|
|
33
|
+
*
|
|
34
|
+
* Try-live gates (all three required):
|
|
35
|
+
* - `uiRegistry` — blueprint resolution
|
|
36
|
+
* - `renderStore` — render persistence
|
|
37
|
+
* - `shortCodeIndex` — shortCode → render binding
|
|
38
|
+
*
|
|
39
|
+
* Partial gate (uiRegistry alone) → 503 with a remediation hint.
|
|
40
|
+
*/
|
|
41
|
+
import type { GguiSessionStore, ShortCodeIndex } from "@ggui-ai/mcp-server-core";
|
|
42
|
+
import type { SharedHandler } from "@ggui-ai/mcp-server-handlers";
|
|
43
|
+
import type { UiRegistry } from "@ggui-ai/ui-registry";
|
|
44
|
+
import type { Express } from "express";
|
|
45
|
+
import type { ZodRawShape } from "zod";
|
|
46
|
+
import type { Logger } from "./logger.js";
|
|
47
|
+
import { type SchemaCompatMode } from "./schema-compat.js";
|
|
48
|
+
interface MountOptions {
|
|
49
|
+
/** Express app to mount onto. */
|
|
50
|
+
readonly app: Express;
|
|
51
|
+
/** Blueprint registry both routes resolve against. */
|
|
52
|
+
readonly uiRegistry: UiRegistry;
|
|
53
|
+
/** GguiSession store the try route commits into (gate 2 of 3). */
|
|
54
|
+
readonly renderStore?: GguiSessionStore;
|
|
55
|
+
/** shortCode → render binding for the try route (gate 3 of 3). */
|
|
56
|
+
readonly shortCodeIndex?: ShortCodeIndex;
|
|
57
|
+
/** Composed handler list the schema-compat check validates against. */
|
|
58
|
+
readonly handlers: ReadonlyArray<SharedHandler<ZodRawShape, ZodRawShape>>;
|
|
59
|
+
/** Schema-compat posture (`reject` default / `warn` / `off`). */
|
|
60
|
+
readonly schemaCompatMode: SchemaCompatMode;
|
|
61
|
+
/** Structured logger. */
|
|
62
|
+
readonly logger: Logger;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Mount `GET /ggui/console/blueprint/:id` +
|
|
66
|
+
* `POST /ggui/console/blueprint/:id/try` onto the express app.
|
|
67
|
+
* Returns nothing — the routes self-register.
|
|
68
|
+
*/
|
|
69
|
+
export declare function mountConsoleBlueprintRoutes(opts: MountOptions): void;
|
|
70
|
+
export {};
|
|
71
|
+
//# sourceMappingURL=console-blueprint-routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"console-blueprint-routes.d.ts","sourceRoot":"","sources":["../src/console-blueprint-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AACjF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAElE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAEvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,KAAK,CAAC;AAGvC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,oBAAoB,CAAC;AAqB5B,UAAU,YAAY;IACpB,iCAAiC;IACjC,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,sDAAsD;IACtD,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,kEAAkE;IAClE,QAAQ,CAAC,WAAW,CAAC,EAAE,gBAAgB,CAAC;IACxC,kEAAkE;IAClE,QAAQ,CAAC,cAAc,CAAC,EAAE,cAAc,CAAC;IACzC,uEAAuE;IACvE,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,aAAa,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC,CAAC;IAC1E,iEAAiE;IACjE,QAAQ,CAAC,gBAAgB,EAAE,gBAAgB,CAAC;IAC5C,yBAAyB;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAwRpE"}
|