@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.
Files changed (152) hide show
  1. package/dist/admin-blueprints-transport.d.ts.map +1 -1
  2. package/dist/admin-blueprints-transport.js +2 -1
  3. package/dist/admin-oauth-providers-transport.d.ts.map +1 -1
  4. package/dist/admin-oauth-providers-transport.js +7 -5
  5. package/dist/api-renders-routes.d.ts +85 -0
  6. package/dist/api-renders-routes.d.ts.map +1 -0
  7. package/dist/api-renders-routes.js +372 -0
  8. package/dist/build-mcp.d.ts +2 -2
  9. package/dist/build-mcp.d.ts.map +1 -1
  10. package/dist/build-mcp.js +64 -5
  11. package/dist/code-routes.d.ts +47 -0
  12. package/dist/code-routes.d.ts.map +1 -0
  13. package/dist/code-routes.js +81 -0
  14. package/dist/code-store-fs.js +2 -2
  15. package/dist/console-auth.d.ts +10 -10
  16. package/dist/console-auth.d.ts.map +1 -1
  17. package/dist/console-auth.js +5 -5
  18. package/dist/console-blueprint-routes.d.ts +71 -0
  19. package/dist/console-blueprint-routes.d.ts.map +1 -0
  20. package/dist/console-blueprint-routes.js +348 -0
  21. package/dist/console-chat-routes.d.ts +80 -0
  22. package/dist/console-chat-routes.d.ts.map +1 -0
  23. package/dist/console-chat-routes.js +204 -0
  24. package/dist/console-config-routes.d.ts +37 -0
  25. package/dist/console-config-routes.d.ts.map +1 -0
  26. package/dist/console-config-routes.js +91 -0
  27. package/dist/console-headers.d.ts +1 -1
  28. package/dist/console-info-routes.d.ts +84 -0
  29. package/dist/console-info-routes.d.ts.map +1 -0
  30. package/dist/console-info-routes.js +135 -0
  31. package/dist/console-keys-routes.d.ts +50 -0
  32. package/dist/console-keys-routes.d.ts.map +1 -0
  33. package/dist/console-keys-routes.js +222 -0
  34. package/dist/console-llm-keys-routes.d.ts +47 -0
  35. package/dist/console-llm-keys-routes.d.ts.map +1 -0
  36. package/dist/console-llm-keys-routes.js +443 -0
  37. package/dist/console-mcp-tools-routes.d.ts +41 -0
  38. package/dist/console-mcp-tools-routes.d.ts.map +1 -0
  39. package/dist/console-mcp-tools-routes.js +60 -0
  40. package/dist/console-registry-routes.d.ts +66 -0
  41. package/dist/console-registry-routes.d.ts.map +1 -0
  42. package/dist/console-registry-routes.js +276 -0
  43. package/dist/console-session-routes.d.ts +89 -0
  44. package/dist/console-session-routes.d.ts.map +1 -0
  45. package/dist/console-session-routes.js +385 -0
  46. package/dist/console-sessions-routes.d.ts +52 -0
  47. package/dist/console-sessions-routes.d.ts.map +1 -0
  48. package/dist/console-sessions-routes.js +106 -0
  49. package/dist/console-static-routes.d.ts +54 -0
  50. package/dist/console-static-routes.d.ts.map +1 -0
  51. package/dist/console-static-routes.js +190 -0
  52. package/dist/console-theme-routes.d.ts +3 -3
  53. package/dist/console-theme-routes.js +1 -1
  54. package/dist/console-timeline.d.ts +5 -5
  55. package/dist/console-timeline.d.ts.map +1 -1
  56. package/dist/console-timeline.js +27 -26
  57. package/dist/console-welcome.js +2 -2
  58. package/dist/email-login.d.ts.map +1 -1
  59. package/dist/email-login.js +2 -3
  60. package/dist/ggui-session-channel/action-ingress.d.ts +54 -0
  61. package/dist/ggui-session-channel/action-ingress.d.ts.map +1 -0
  62. package/dist/ggui-session-channel/action-ingress.js +228 -0
  63. package/dist/ggui-session-channel/channel-subscriptions.d.ts +97 -0
  64. package/dist/ggui-session-channel/channel-subscriptions.d.ts.map +1 -0
  65. package/dist/ggui-session-channel/channel-subscriptions.js +224 -0
  66. package/dist/ggui-session-channel/internal-types.d.ts +102 -0
  67. package/dist/ggui-session-channel/internal-types.d.ts.map +1 -0
  68. package/dist/ggui-session-channel/internal-types.js +6 -0
  69. package/dist/ggui-session-channel/outbound.d.ts +81 -0
  70. package/dist/ggui-session-channel/outbound.d.ts.map +1 -0
  71. package/dist/ggui-session-channel/outbound.js +174 -0
  72. package/dist/ggui-session-channel/socket-router.d.ts +38 -0
  73. package/dist/ggui-session-channel/socket-router.d.ts.map +1 -0
  74. package/dist/ggui-session-channel/socket-router.js +213 -0
  75. package/dist/ggui-session-channel/subscribe.d.ts +165 -0
  76. package/dist/ggui-session-channel/subscribe.d.ts.map +1 -0
  77. package/dist/ggui-session-channel/subscribe.js +370 -0
  78. package/dist/ggui-session-channel/subscriber-lifecycle.d.ts +40 -0
  79. package/dist/ggui-session-channel/subscriber-lifecycle.d.ts.map +1 -0
  80. package/dist/ggui-session-channel/subscriber-lifecycle.js +123 -0
  81. package/dist/ggui-session-channel.d.ts +425 -0
  82. package/dist/ggui-session-channel.d.ts.map +1 -0
  83. package/dist/ggui-session-channel.js +262 -0
  84. package/dist/health-routes.d.ts +76 -0
  85. package/dist/health-routes.d.ts.map +1 -0
  86. package/dist/health-routes.js +145 -0
  87. package/dist/index.d.ts +10 -11
  88. package/dist/index.d.ts.map +1 -1
  89. package/dist/index.js +8 -9
  90. package/dist/instructions-presets.d.ts +3 -3
  91. package/dist/instructions-presets.js +24 -24
  92. package/dist/llm-backed-negotiator.d.ts +68 -67
  93. package/dist/llm-backed-negotiator.d.ts.map +1 -1
  94. package/dist/llm-backed-negotiator.js +82 -221
  95. package/dist/mcp-apps-outbound.d.ts +47 -48
  96. package/dist/mcp-apps-outbound.d.ts.map +1 -1
  97. package/dist/mcp-apps-outbound.js +154 -177
  98. package/dist/mcp-endpoint-routes.d.ts +88 -0
  99. package/dist/mcp-endpoint-routes.d.ts.map +1 -0
  100. package/dist/mcp-endpoint-routes.js +359 -0
  101. package/dist/mcp-mounts.d.ts +2 -76
  102. package/dist/mcp-mounts.d.ts.map +1 -1
  103. package/dist/mcp-mounts.js +0 -76
  104. package/dist/oauth-as-routes.d.ts +60 -0
  105. package/dist/oauth-as-routes.d.ts.map +1 -0
  106. package/dist/oauth-as-routes.js +82 -0
  107. package/dist/oauth-clients-routes.d.ts +39 -0
  108. package/dist/oauth-clients-routes.d.ts.map +1 -0
  109. package/dist/oauth-clients-routes.js +87 -0
  110. package/dist/oauth-login-types.d.ts +1 -20
  111. package/dist/oauth-login-types.d.ts.map +1 -1
  112. package/dist/oauth-login-types.js +30 -7
  113. package/dist/oauth-login.d.ts.map +1 -1
  114. package/dist/oauth-login.js +3 -2
  115. package/dist/oauth-providers-store.d.ts.map +1 -1
  116. package/dist/oauth-providers-store.js +5 -5
  117. package/dist/oauth.d.ts +9 -8
  118. package/dist/oauth.d.ts.map +1 -1
  119. package/dist/oauth.js +41 -19
  120. package/dist/pairing-transport.d.ts.map +1 -1
  121. package/dist/pairing-transport.js +2 -1
  122. package/dist/request-context.d.ts +2 -2
  123. package/dist/request-context.js +2 -2
  124. package/dist/reserved-validators.d.ts.map +1 -1
  125. package/dist/reserved-validators.js +9 -1
  126. package/dist/route-param.d.ts +9 -0
  127. package/dist/route-param.d.ts.map +1 -0
  128. package/dist/route-param.js +10 -0
  129. package/dist/runtime-bundle-route.d.ts +43 -0
  130. package/dist/runtime-bundle-route.d.ts.map +1 -0
  131. package/dist/runtime-bundle-route.js +80 -0
  132. package/dist/schema-compat.d.ts +64 -62
  133. package/dist/schema-compat.d.ts.map +1 -1
  134. package/dist/schema-compat.js +23 -51
  135. package/dist/server.d.ts +179 -193
  136. package/dist/server.d.ts.map +1 -1
  137. package/dist/server.js +644 -3759
  138. package/dist/storage.d.ts +5 -5
  139. package/dist/storage.d.ts.map +1 -1
  140. package/dist/storage.js +5 -5
  141. package/dist/thread-transport.d.ts.map +1 -1
  142. package/dist/thread-transport.js +4 -3
  143. package/dist/user-session-auth.d.ts +7 -21
  144. package/dist/user-session-auth.d.ts.map +1 -1
  145. package/dist/user-session-auth.js +7 -28
  146. package/package.json +16 -15
  147. package/dist/mcp-apps-inbound.d.ts +0 -86
  148. package/dist/mcp-apps-inbound.d.ts.map +0 -1
  149. package/dist/mcp-apps-inbound.js +0 -283
  150. package/dist/render-channel.d.ts +0 -694
  151. package/dist/render-channel.d.ts.map +0 -1
  152. package/dist/render-channel.js +0 -1775
@@ -1,283 +0,0 @@
1
- /**
2
- * MCP Apps inbound hosting — resource proxy + tools/call visibility gate.
3
- *
4
- * When the ggui server hosts a third-party MCP App iframe, the browser
5
- * CANNOT resolve `ui://` URIs directly (browser URL scheme set is
6
- * limited to http/https/etc). The server owns the only path to the
7
- * resource bytes:
8
- *
9
- * browser iframe
10
- * ↓ https://<ggui-server>/mcp-apps/resource?session=…&item=…
11
- * ggui-server proxy route
12
- * ↓ MCP resources/read (via @modelcontextprotocol/sdk/client)
13
- * source MCP server
14
- * ↓ ui:// resource HTML
15
- * ggui-server returns the HTML bytes to the iframe.
16
- *
17
- * Similarly, iframe-originated `tools/call` flows:
18
- *
19
- * iframe ----postMessage---> ggui client (in parent)
20
- * ggui client -----fetch----> ggui-server /mcp-apps/tools-call
21
- * ggui-server enforces visibility, proxies via MCP client to source.
22
- *
23
- * Visibility enforcement lives at the proxy boundary (this file), NOT
24
- * in the client. Source `tools/list` is fetched + cached per connector;
25
- * `_meta.ui.visibility` gates which tools iframe-originated calls can
26
- * invoke. Cross-connector calls are rejected — the iframe for connector
27
- * A can NEVER call tools on connector B.
28
- */
29
- import { Client } from '@modelcontextprotocol/sdk/client/index.js';
30
- import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
31
- import { isMcpAppsRender } from '@ggui-ai/protocol/integrations/mcp-apps';
32
- /** Default mount path for the MCP Apps inbound routes. */
33
- export const DEFAULT_MCP_APPS_INBOUND_PATH = '/mcp-apps';
34
- /**
35
- * Tools-list cache keyed by connectorId. Short TTL — stale visibility
36
- * decisions have small but real security impact, so cache invalidation
37
- * is time-bounded (default 60s) with on-demand refresh.
38
- */
39
- export class ToolsListCache {
40
- ttlMs;
41
- map = new Map();
42
- constructor(ttlMs = 60_000) {
43
- this.ttlMs = ttlMs;
44
- }
45
- get(connectorId) {
46
- const entry = this.map.get(connectorId);
47
- if (!entry)
48
- return null;
49
- if (Date.now() - entry.fetchedAt > this.ttlMs) {
50
- this.map.delete(connectorId);
51
- return null;
52
- }
53
- return entry;
54
- }
55
- set(connectorId, tools) {
56
- const byName = new Map();
57
- for (const t of tools)
58
- byName.set(t.name, t);
59
- const entry = { byName, fetchedAt: Date.now() };
60
- this.map.set(connectorId, entry);
61
- return entry;
62
- }
63
- invalidate(connectorId) {
64
- this.map.delete(connectorId);
65
- }
66
- }
67
- async function connectSourceClient(connector) {
68
- const transport = new StreamableHTTPClientTransport(new URL(connector.serverUrl), connector.auth?.bearer
69
- ? { requestInit: { headers: { Authorization: `Bearer ${connector.auth.bearer}` } } }
70
- : {});
71
- const client = new Client({ name: 'ggui-mcp-apps-proxy', version: '0.0.1' }, { capabilities: {} });
72
- await client.connect(transport);
73
- return client;
74
- }
75
- /**
76
- * Parse the `_meta.ui.visibility` off an MCP tool declaration.
77
- * Missing / empty defaults to `['model']` per spec convention (tools
78
- * without explicit visibility are model-callable).
79
- */
80
- function readVisibility(toolDecl) {
81
- if (toolDecl === null || typeof toolDecl !== 'object')
82
- return ['model'];
83
- const meta = toolDecl._meta;
84
- if (meta === null || typeof meta !== 'object')
85
- return ['model'];
86
- const ui = meta.ui;
87
- if (ui === null || typeof ui !== 'object')
88
- return ['model'];
89
- const v = ui.visibility;
90
- if (!Array.isArray(v) || v.length === 0)
91
- return ['model'];
92
- const out = [];
93
- for (const x of v) {
94
- if (x === 'model' || x === 'app')
95
- out.push(x);
96
- }
97
- return out.length > 0 ? out : ['model'];
98
- }
99
- /**
100
- * Resolve `{render}` from the ggui render store to a
101
- * {@link McpAppsRender}. Returns null if the render is missing or is
102
- * not an mcpApps variant.
103
- *
104
- * Phase B: a render IS the addressable unit — no per-item lookup inside
105
- * a stack vessel — so the render id alone resolves the McpAppsRender
106
- * directly. The `itemId` parameter is preserved on the wire-facing
107
- * query string for callsite stability but ignored when it matches the
108
- * renderId (which is the only valid value post-collapse).
109
- */
110
- async function resolveMcpAppsItem(store, renderId, _itemId) {
111
- const stored = await store.get(renderId);
112
- if (!stored)
113
- return null;
114
- if (!isMcpAppsRender(stored.render))
115
- return null;
116
- return stored.render;
117
- }
118
- /**
119
- * Register the MCP Apps inbound proxy routes on an Express app.
120
- *
121
- * GET /mcp-apps/resource?session=<id>&item=<id>
122
- * Fetches the referenced McpAppsRender's `source.resourceUri`
123
- * via resources/read on the source MCP server. Returns the HTML
124
- * with the MIME declared by the source (typically
125
- * `text/html;profile=mcp-app`) and spec-canonical CSP headers.
126
- *
127
- * POST /mcp-apps/tools-call
128
- * Body: { session, item, tool, arguments? }.
129
- * Resolves the stack item → connector → cached tools/list, gates
130
- * on `_meta.ui.visibility` (must include 'app'), and proxies the
131
- * call via MCP client. Cross-connector / unknown tool / model-
132
- * only tool are all rejected.
133
- */
134
- export function installMcpAppsInbound(app, opts) {
135
- const path = opts.path ?? DEFAULT_MCP_APPS_INBOUND_PATH;
136
- const cache = new ToolsListCache();
137
- app.get(`${path}/resource`, async (req, res) => {
138
- const renderId = typeof req.query.render === 'string' ? req.query.render : '';
139
- const itemId = typeof req.query.item === 'string' ? req.query.item : renderId;
140
- if (!renderId) {
141
- res.status(400).type('text/plain').send('Missing ?render');
142
- return;
143
- }
144
- const item = await resolveMcpAppsItem(opts.renderStore, renderId, itemId);
145
- if (!item) {
146
- res.status(404).type('text/plain').send('Render not found');
147
- return;
148
- }
149
- const connector = await opts.connectors.get(item.source.connectorId);
150
- if (!connector) {
151
- res.status(404).type('text/plain').send('Unknown connector');
152
- return;
153
- }
154
- // Dev/cache optimization: inline resourceContent bypasses source fetch.
155
- if (typeof item.resourceContent === 'string' && item.resourceContent.length > 0) {
156
- res.status(200).type('text/html;profile=mcp-app').send(item.resourceContent);
157
- return;
158
- }
159
- let client = null;
160
- try {
161
- client = await connectSourceClient(connector);
162
- const result = await client.readResource({ uri: item.source.resourceUri });
163
- const first = result.contents[0];
164
- if (!first || !('text' in first) || typeof first.text !== 'string') {
165
- res.status(502).type('text/plain').send('Source returned non-text resource');
166
- return;
167
- }
168
- // CSP composition — use the source-declared field names verbatim;
169
- // a stricter ggui baseline can be layered in a later slice.
170
- const csp = composeCsp(item);
171
- if (csp)
172
- res.setHeader('Content-Security-Policy', csp);
173
- res.status(200).type(first.mimeType ?? 'text/html;profile=mcp-app').send(first.text);
174
- }
175
- catch (err) {
176
- opts.logger.error('mcp_apps_resource_proxy_failed', {
177
- renderId,
178
- itemId,
179
- connectorId: item.source.connectorId,
180
- error: String(err),
181
- });
182
- res.status(502).type('text/plain').send('Failed to fetch source resource');
183
- }
184
- finally {
185
- if (client)
186
- await client.close().catch(() => undefined);
187
- }
188
- });
189
- app.post(`${path}/tools-call`, async (req, res) => {
190
- const body = (req.body ?? {});
191
- const renderId = typeof body.render === 'string' ? body.render : '';
192
- const itemId = typeof body.item === 'string' ? body.item : renderId;
193
- const toolName = typeof body.tool === 'string' ? body.tool : '';
194
- if (!renderId || !toolName) {
195
- res.status(400).json({ error: 'missing_fields' });
196
- return;
197
- }
198
- const item = await resolveMcpAppsItem(opts.renderStore, renderId, itemId);
199
- if (!item) {
200
- res.status(404).json({ error: 'item_not_found' });
201
- return;
202
- }
203
- const connector = await opts.connectors.get(item.source.connectorId);
204
- if (!connector) {
205
- res.status(404).json({ error: 'unknown_connector' });
206
- return;
207
- }
208
- let client = null;
209
- try {
210
- client = await connectSourceClient(connector);
211
- // Cache tools/list per-connector. Visibility decisions read from
212
- // the cache; a cold cache fetches via tools/list on the source.
213
- let entry = cache.get(item.source.connectorId);
214
- if (!entry) {
215
- const list = await client.listTools();
216
- entry = cache.set(item.source.connectorId, list.tools.map((t) => ({
217
- name: t.name,
218
- visibility: readVisibility(t),
219
- })));
220
- }
221
- const meta = entry.byName.get(toolName);
222
- if (!meta) {
223
- res.status(404).json({ error: 'tool_not_found' });
224
- return;
225
- }
226
- if (!meta.visibility.includes('app')) {
227
- // Model-only tool — not callable from an iframe view per
228
- // MCP Apps visibility semantics.
229
- res.status(403).json({ error: 'visibility_denied' });
230
- return;
231
- }
232
- // Cross-connector check: the iframe for item A.connector can
233
- // only call tools on A.connector. Since we resolved connector
234
- // from `item.source.connectorId`, any other connector would
235
- // require a different proxy path; this handler only serves
236
- // `item.source.connectorId`. No-op enforcement here; see the
237
- // test that exercises a forged cross-connector `tool` input
238
- // (the tool name won't exist in THIS connector's tools-list).
239
- const result = await client.callTool({
240
- name: toolName,
241
- arguments: body.arguments ?? {},
242
- });
243
- res.status(200).json(result);
244
- }
245
- catch (err) {
246
- opts.logger.error('mcp_apps_tools_call_proxy_failed', {
247
- renderId,
248
- itemId,
249
- connectorId: item.source.connectorId,
250
- toolName,
251
- error: String(err),
252
- });
253
- res.status(502).json({ error: 'proxy_failed' });
254
- }
255
- finally {
256
- if (client)
257
- await client.close().catch(() => undefined);
258
- }
259
- });
260
- return cache;
261
- }
262
- /**
263
- * Compose a Content-Security-Policy header from an MCP App's declared
264
- * CSP metadata. Minimal — uses spec-canonical field names verbatim.
265
- * Returns null when no CSP was declared (caller leaves the default
266
- * header in place).
267
- */
268
- function composeCsp(item) {
269
- const csp = item.csp;
270
- if (!csp)
271
- return null;
272
- const parts = [];
273
- if (csp.connectDomains && csp.connectDomains.length > 0) {
274
- parts.push(`connect-src 'self' ${csp.connectDomains.join(' ')}`);
275
- }
276
- if (csp.resourceDomains && csp.resourceDomains.length > 0) {
277
- parts.push(`default-src 'self' ${csp.resourceDomains.join(' ')}`, `script-src 'self' 'unsafe-inline' ${csp.resourceDomains.join(' ')}`, `style-src 'self' 'unsafe-inline' ${csp.resourceDomains.join(' ')}`);
278
- }
279
- if (csp.frameDomains && csp.frameDomains.length > 0) {
280
- parts.push(`frame-src 'self' ${csp.frameDomains.join(' ')}`);
281
- }
282
- return parts.length > 0 ? parts.join('; ') : null;
283
- }