@ggui-ai/mcp-server 0.1.0-rc.1
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/LICENSE +201 -0
- package/README.md +48 -0
- package/dist/admin-blueprints-transport.d.ts +114 -0
- package/dist/admin-blueprints-transport.d.ts.map +1 -0
- package/dist/admin-blueprints-transport.js +118 -0
- package/dist/admin-oauth-providers-transport.d.ts +40 -0
- package/dist/admin-oauth-providers-transport.d.ts.map +1 -0
- package/dist/admin-oauth-providers-transport.js +263 -0
- package/dist/auth.d.ts +39 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +75 -0
- package/dist/build-mcp.d.ts +128 -0
- package/dist/build-mcp.d.ts.map +1 -0
- package/dist/build-mcp.js +113 -0
- package/dist/code-store-fs.d.ts +19 -0
- package/dist/code-store-fs.d.ts.map +1 -0
- package/dist/code-store-fs.js +98 -0
- package/dist/console-auth.d.ts +139 -0
- package/dist/console-auth.d.ts.map +1 -0
- package/dist/console-auth.js +102 -0
- package/dist/console-cache.d.ts +78 -0
- package/dist/console-cache.d.ts.map +1 -0
- package/dist/console-cache.js +105 -0
- package/dist/console-headers.d.ts +124 -0
- package/dist/console-headers.d.ts.map +1 -0
- package/dist/console-headers.js +49 -0
- package/dist/console-llm-trace.d.ts +66 -0
- package/dist/console-llm-trace.d.ts.map +1 -0
- package/dist/console-llm-trace.js +105 -0
- package/dist/console-payloads.d.ts +67 -0
- package/dist/console-payloads.d.ts.map +1 -0
- package/dist/console-payloads.js +105 -0
- package/dist/console-theme-routes.d.ts +111 -0
- package/dist/console-theme-routes.d.ts.map +1 -0
- package/dist/console-theme-routes.js +202 -0
- package/dist/console-timeline.d.ts +45 -0
- package/dist/console-timeline.d.ts.map +1 -0
- package/dist/console-timeline.js +169 -0
- package/dist/console-validator.d.ts +67 -0
- package/dist/console-validator.d.ts.map +1 -0
- package/dist/console-validator.js +105 -0
- package/dist/console-welcome.d.ts +7 -0
- package/dist/console-welcome.d.ts.map +1 -0
- package/dist/console-welcome.js +221 -0
- package/dist/csrf-middleware.d.ts +55 -0
- package/dist/csrf-middleware.d.ts.map +1 -0
- package/dist/csrf-middleware.js +138 -0
- package/dist/email-login.d.ts +174 -0
- package/dist/email-login.d.ts.map +1 -0
- package/dist/email-login.js +254 -0
- package/dist/email-resend.d.ts +29 -0
- package/dist/email-resend.d.ts.map +1 -0
- package/dist/email-resend.js +71 -0
- package/dist/email-sender-from-env.d.ts +34 -0
- package/dist/email-sender-from-env.d.ts.map +1 -0
- package/dist/email-sender-from-env.js +112 -0
- package/dist/email-smtp.d.ts +42 -0
- package/dist/email-smtp.d.ts.map +1 -0
- package/dist/email-smtp.js +81 -0
- package/dist/index.d.ts +102 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +122 -0
- package/dist/instructions-presets.d.ts +112 -0
- package/dist/instructions-presets.d.ts.map +1 -0
- package/dist/instructions-presets.js +195 -0
- package/dist/llm-backed-negotiator.d.ts +178 -0
- package/dist/llm-backed-negotiator.d.ts.map +1 -0
- package/dist/llm-backed-negotiator.js +579 -0
- package/dist/logger.d.ts +23 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +41 -0
- package/dist/mcp-apps-inbound.d.ts +86 -0
- package/dist/mcp-apps-inbound.d.ts.map +1 -0
- package/dist/mcp-apps-inbound.js +278 -0
- package/dist/mcp-apps-outbound.d.ts +448 -0
- package/dist/mcp-apps-outbound.d.ts.map +1 -0
- package/dist/mcp-apps-outbound.js +1163 -0
- package/dist/mcp-mounts.d.ts +239 -0
- package/dist/mcp-mounts.d.ts.map +1 -0
- package/dist/mcp-mounts.js +222 -0
- package/dist/oauth-login-types.d.ts +160 -0
- package/dist/oauth-login-types.d.ts.map +1 -0
- package/dist/oauth-login-types.js +9 -0
- package/dist/oauth-login.d.ts +77 -0
- package/dist/oauth-login.d.ts.map +1 -0
- package/dist/oauth-login.js +455 -0
- package/dist/oauth-providers/github.d.ts +17 -0
- package/dist/oauth-providers/github.d.ts.map +1 -0
- package/dist/oauth-providers/github.js +89 -0
- package/dist/oauth-providers/google.d.ts +18 -0
- package/dist/oauth-providers/google.d.ts.map +1 -0
- package/dist/oauth-providers/google.js +59 -0
- package/dist/oauth-providers-store.d.ts +32 -0
- package/dist/oauth-providers-store.d.ts.map +1 -0
- package/dist/oauth-providers-store.js +291 -0
- package/dist/oauth.d.ts +347 -0
- package/dist/oauth.d.ts.map +1 -0
- package/dist/oauth.js +686 -0
- package/dist/pairing-transport.d.ts +99 -0
- package/dist/pairing-transport.d.ts.map +1 -0
- package/dist/pairing-transport.js +223 -0
- package/dist/rate-limit-middleware.d.ts +36 -0
- package/dist/rate-limit-middleware.d.ts.map +1 -0
- package/dist/rate-limit-middleware.js +57 -0
- package/dist/render-gate.d.ts +87 -0
- package/dist/render-gate.d.ts.map +1 -0
- package/dist/render-gate.js +77 -0
- package/dist/render-rate-limit.d.ts +59 -0
- package/dist/render-rate-limit.d.ts.map +1 -0
- package/dist/render-rate-limit.js +73 -0
- package/dist/render-signing.d.ts +98 -0
- package/dist/render-signing.d.ts.map +1 -0
- package/dist/render-signing.js +113 -0
- package/dist/request-context.d.ts +113 -0
- package/dist/request-context.d.ts.map +1 -0
- package/dist/request-context.js +154 -0
- package/dist/reserved-validators.d.ts +22 -0
- package/dist/reserved-validators.d.ts.map +1 -0
- package/dist/reserved-validators.js +101 -0
- package/dist/schema-compat.d.ts +167 -0
- package/dist/schema-compat.d.ts.map +1 -0
- package/dist/schema-compat.js +187 -0
- package/dist/security-headers-middleware.d.ts +38 -0
- package/dist/security-headers-middleware.d.ts.map +1 -0
- package/dist/security-headers-middleware.js +30 -0
- package/dist/server.d.ts +2060 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +6338 -0
- package/dist/session-channel.d.ts +651 -0
- package/dist/session-channel.d.ts.map +1 -0
- package/dist/session-channel.js +1756 -0
- package/dist/storage.d.ts +89 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +171 -0
- package/dist/thread-transport.d.ts +118 -0
- package/dist/thread-transport.d.ts.map +1 -0
- package/dist/thread-transport.js +478 -0
- package/dist/user-session-auth.d.ts +167 -0
- package/dist/user-session-auth.d.ts.map +1 -0
- package/dist/user-session-auth.js +148 -0
- package/package.json +76 -0
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,2060 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createGguiServer — build a runnable open MCP server.
|
|
3
|
+
*
|
|
4
|
+
* Composition:
|
|
5
|
+
*
|
|
6
|
+
* - `@ggui-ai/mcp-server-handlers/blueprints` — the three blueprint-read
|
|
7
|
+
* handlers (search / list_featured / render), shared with hosted
|
|
8
|
+
* closed-runtime servers. If you want more tools, extract the next
|
|
9
|
+
* family into `@ggui-ai/mcp-server-handlers` and pass it via
|
|
10
|
+
* `handlers:`.
|
|
11
|
+
*
|
|
12
|
+
* - `@ggui-ai/mcp-server-core/in-memory` — default backing adapters
|
|
13
|
+
* (vectors, embedding, auth). Real persistence bindings ship in
|
|
14
|
+
* later packages (sqlite / postgres / redis) and plug into the
|
|
15
|
+
* same interfaces.
|
|
16
|
+
*
|
|
17
|
+
* - `@modelcontextprotocol/sdk` — `McpServer` + `StreamableHTTPServerTransport`
|
|
18
|
+
* matching the MCP wire spec. Fresh transport + fresh server per
|
|
19
|
+
* request (stateless); response close tears both down.
|
|
20
|
+
*
|
|
21
|
+
* Transport:
|
|
22
|
+
*
|
|
23
|
+
* POST /mcp — MCP Streamable HTTP wire protocol (JSON-RPC).
|
|
24
|
+
* GET /ggui/health — unauthenticated liveness, returns
|
|
25
|
+
* `{status, server, version, tools, ...}`.
|
|
26
|
+
* GET /ggui/auth-check — authenticated liveness. 204 when the bearer
|
|
27
|
+
* token resolves via the configured AuthAdapter,
|
|
28
|
+
* 401 otherwise. Pairs with `/ggui/health` so
|
|
29
|
+
* clients (e.g. Portal settings) can distinguish
|
|
30
|
+
* `reachable` from `token-invalid` without
|
|
31
|
+
* opening a full MCP session just to probe.
|
|
32
|
+
* GET/DELETE /mcp — 405 (stateless server doesn't support the
|
|
33
|
+
* streaming continuation / session-terminate verbs).
|
|
34
|
+
*
|
|
35
|
+
* Zero-config boot: omit every option and you get an in-memory server
|
|
36
|
+
* accepting any bearer token (dev mode) with the blueprint-read
|
|
37
|
+
* handlers wired up. The `Logger.warn('dev_mode_auth_enabled')` fires
|
|
38
|
+
* once at boot so operators see the shape they're running.
|
|
39
|
+
*/
|
|
40
|
+
import { type Express, type Request } from 'express';
|
|
41
|
+
import { Server as NodeHttpServer } from 'node:http';
|
|
42
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
43
|
+
import { type ThemeWriter, type ThemeFileUploader } from './console-theme-routes.js';
|
|
44
|
+
import { type ZodRawShape } from 'zod';
|
|
45
|
+
import type { AppMetadataStore, AuditSink, AuthAdapter, AuthResult, BlueprintProvider, BlueprintSearch, BlueprintSelector, BlueprintStore, ConnectorRegistry, CodeStore, EmbeddingProvider, GeneratorRegistry, KeyValueStore, PairingService, PendingEventConsumer, ProviderKeyStore, RateLimiter, SessionStore, SessionStreamBuffer, ShortCodeIndex, TelemetrySink, ThreadStore, VectorStore } from '@ggui-ai/mcp-server-core';
|
|
46
|
+
import type { Blueprint } from '@ggui-ai/protocol';
|
|
47
|
+
import type { DiscoveredPrimitiveCatalog, LoadedTheme } from '@ggui-ai/project-config/node';
|
|
48
|
+
import type { OperatorConfig } from '@ggui-ai/project-config';
|
|
49
|
+
import type { SharedHandler } from '@ggui-ai/mcp-server-handlers';
|
|
50
|
+
import { type ThemeCatalogEntry } from '@ggui-ai/mcp-server-handlers/app-discovery';
|
|
51
|
+
import { type AppsSource, type UserDefaultAppSource } from '@ggui-ai/mcp-server-handlers/ops-apps';
|
|
52
|
+
import { type OrgsSource, type OrgInvitesSource } from '@ggui-ai/mcp-server-handlers/ops-orgs';
|
|
53
|
+
import { type ConnectorKeysSource } from '@ggui-ai/mcp-server-handlers/ops-connector-keys';
|
|
54
|
+
import { type CouponRedeemSource } from '@ggui-ai/mcp-server-handlers/ops-coupon';
|
|
55
|
+
import type { UiRegistry } from '@ggui-ai/ui-registry';
|
|
56
|
+
import { type ChannelNotifier, type GenerationCredentials, type GenerationDeps, type HandshakeNegotiator, type PropsUpdateNotifier, type ProvisionalPreviewDeps, type ProvisionalPreviewEmitter, type ProvisionalPreviewConfig, type ProvisionalPreviewOutcome } from '@ggui-ai/mcp-server-handlers/session-mutations';
|
|
57
|
+
import { type ServerInfo } from './build-mcp.js';
|
|
58
|
+
import { type OAuthConfig } from './oauth.js';
|
|
59
|
+
import { type Logger } from './logger.js';
|
|
60
|
+
import { type EmailSender, type MagicLinkStore } from './email-login.js';
|
|
61
|
+
import { type McpInstructionsValue } from './instructions-presets.js';
|
|
62
|
+
import { type ThreadOwnerResolver } from './thread-transport.js';
|
|
63
|
+
import { type SessionChannelServer, type WiredActionRouter } from './session-channel.js';
|
|
64
|
+
import { type McpServerMount, type McpService } from './mcp-mounts.js';
|
|
65
|
+
import { type SchemaCompatMode } from './schema-compat.js';
|
|
66
|
+
export declare function defaultHandlers(deps: {
|
|
67
|
+
readonly embedding: EmbeddingProvider;
|
|
68
|
+
readonly vectors: VectorStore;
|
|
69
|
+
/**
|
|
70
|
+
* Optional blueprint catalog source. When bound,
|
|
71
|
+
* `ggui_list_featured_blueprints` enumerates the provider's
|
|
72
|
+
* catalog; absent = the handler returns an empty list (the
|
|
73
|
+
* zero-config OSS default).
|
|
74
|
+
*
|
|
75
|
+
* `createGguiServer` constructs a `ManifestBlueprintProvider`
|
|
76
|
+
* from `ggui.json#blueprints.include` at boot and threads it in
|
|
77
|
+
* here — that's how manifest-declared UIs surface through the MCP
|
|
78
|
+
* tool.
|
|
79
|
+
*/
|
|
80
|
+
readonly blueprints?: BlueprintProvider;
|
|
81
|
+
/**
|
|
82
|
+
* UI registry consulted by `ggui_render_blueprint`. When bound,
|
|
83
|
+
* the render handler is registered and resolves every call through
|
|
84
|
+
* this registry's `get(id)` + `getBundle(id)` pair. Absent = the
|
|
85
|
+
* render handler is omitted from the handler array (no deprecation
|
|
86
|
+
* shim, no throwing stub — operator sees "tool unavailable" only
|
|
87
|
+
* if a caller tries to invoke it).
|
|
88
|
+
*
|
|
89
|
+
* `createGguiServer` threads `opts.uiRegistry` through when
|
|
90
|
+
* present. OSS `ggui serve` binds
|
|
91
|
+
* `@ggui-ai/dev-stack::LocalUiRegistry`.
|
|
92
|
+
*/
|
|
93
|
+
readonly uiRegistry?: UiRegistry;
|
|
94
|
+
/**
|
|
95
|
+
* Handshake-state KV store. When bound, `ggui_handshake` is
|
|
96
|
+
* registered and the paired `ggui_push({handshakeId})` consume
|
|
97
|
+
* path is enabled. Both handlers share the same instance so the
|
|
98
|
+
* write + read sit on one source of truth.
|
|
99
|
+
*
|
|
100
|
+
* Absent = handshake handler is NOT registered AND
|
|
101
|
+
* `ggui_push({handshakeId})` falls back to a rejection shape.
|
|
102
|
+
*/
|
|
103
|
+
readonly handshake?: {
|
|
104
|
+
readonly kvStore: KeyValueStore;
|
|
105
|
+
/**
|
|
106
|
+
* Optional session store. When bound, `ggui_handshake` validates
|
|
107
|
+
* the wire `sessionId` against this store (existence + tenant
|
|
108
|
+
* ownership) before negotiating. OSS sets this to the same store
|
|
109
|
+
* push uses so the handshake catches unknown / cross-tenant ids
|
|
110
|
+
* at the earliest boundary; cloud pods omit and validate at push
|
|
111
|
+
* time via their own DDB-backed path.
|
|
112
|
+
*/
|
|
113
|
+
readonly sessionStore?: SessionStore;
|
|
114
|
+
/**
|
|
115
|
+
* Optional negotiator binding. Absent = `ggui_handshake` stamps
|
|
116
|
+
* `action: 'create'` + honest no-negotiator reason on the record
|
|
117
|
+
* (the seam is still real; persistence + consumption still
|
|
118
|
+
* anchor the round-trip).
|
|
119
|
+
*/
|
|
120
|
+
readonly negotiator?: HandshakeNegotiator;
|
|
121
|
+
/**
|
|
122
|
+
* Optional per-app metadata resolver. When bound, the handshake
|
|
123
|
+
* handler reads `app.gadgets` and threads the catalog to
|
|
124
|
+
* the negotiator so synth knows which gadget bindings the app
|
|
125
|
+
* exposes. Defaults to the same `appMetadataStore` the
|
|
126
|
+
* `ggui_list_gadgets` tool uses.
|
|
127
|
+
*/
|
|
128
|
+
readonly appMetadataStore?: AppMetadataStore;
|
|
129
|
+
/**
|
|
130
|
+
* Optional resolver for the `serverCapabilities` field on
|
|
131
|
+
* every handshake response. Composition wires this so iframes
|
|
132
|
+
* learn which `streamSpec[ch].source.tool` channels they can WS-
|
|
133
|
+
* subscribe-for vs. must iframe-poll directly. Returning
|
|
134
|
+
* `undefined` omits the field (universal iframe-polling fallback).
|
|
135
|
+
*/
|
|
136
|
+
readonly serverCapabilities?: () => import('@ggui-ai/protocol').ServerCapabilities | undefined;
|
|
137
|
+
};
|
|
138
|
+
readonly push?: {
|
|
139
|
+
readonly sessionStore: SessionStore;
|
|
140
|
+
readonly renderBaseUrl: string;
|
|
141
|
+
/**
|
|
142
|
+
* Optional render-URL signer. Returns the query suffix to append
|
|
143
|
+
* to a minted URL (`sig=...&exp=...`, no leading `?` or `&`).
|
|
144
|
+
* Absent when `renderSigning: false` is set on boot.
|
|
145
|
+
* Mirrors the handler-side dep with the same name.
|
|
146
|
+
*/
|
|
147
|
+
readonly signRenderUrl?: (shortCode: string) => string;
|
|
148
|
+
/**
|
|
149
|
+
* Optional bootstrap-credential minter. When present, `ggui_push`
|
|
150
|
+
* results carry `_meta.ggui.bootstrap`. When absent, they don't —
|
|
151
|
+
* non-MCP-Apps hosts still get `structuredContent.url` as the
|
|
152
|
+
* fallback link.
|
|
153
|
+
*/
|
|
154
|
+
readonly mintBootstrap?: (sessionId: string, appId: string) => {
|
|
155
|
+
wsUrl: string;
|
|
156
|
+
token: string;
|
|
157
|
+
expiresAt: string;
|
|
158
|
+
};
|
|
159
|
+
/**
|
|
160
|
+
* URL of the renderer bundle the thin-shell HTML should fetch
|
|
161
|
+
* (C8 — plan §C8). Padded onto
|
|
162
|
+
* {@link GguiBootstrapMeta.runtimeUrl} at `resultMeta` time.
|
|
163
|
+
* Same-origin default is `/_ggui/iframe-runtime.js`; hosted cloud
|
|
164
|
+
* operators override to a CDN URL. Required when `mintBootstrap`
|
|
165
|
+
* is set (the thin shell depends on it); otherwise ignored.
|
|
166
|
+
*
|
|
167
|
+
* Function form: callers passing a getter let the handler
|
|
168
|
+
* resolve the URL per request — auto-derive from
|
|
169
|
+
* `X-Forwarded-Host` when the TCP peer is loopback so tunnel/
|
|
170
|
+
* reverse-proxy setups produce absolute URLs that work under
|
|
171
|
+
* srcdoc iframes (claude.ai). Static `publicBaseUrl` config
|
|
172
|
+
* still wins.
|
|
173
|
+
*/
|
|
174
|
+
readonly runtimeUrl?: string | (() => string | undefined);
|
|
175
|
+
/**
|
|
176
|
+
* Theme preset id resolved from `ggui.json#theme`. Forwarded onto
|
|
177
|
+
* `_meta.ggui.bootstrap.themeId` in the `ggui_push` resultMeta so
|
|
178
|
+
* MCP Apps hosts (claude.ai, Claude Desktop) propagate the
|
|
179
|
+
* operator's theme into the iframe.
|
|
180
|
+
*/
|
|
181
|
+
readonly themeId?: string;
|
|
182
|
+
/** Theme color mode resolved from `ggui.json#theme.mode`. */
|
|
183
|
+
readonly themeMode?: 'light' | 'dark';
|
|
184
|
+
/**
|
|
185
|
+
* Live theme getter — resolved per-push. When set, supersedes
|
|
186
|
+
* the static `themeId` / `themeMode` for every push's bootstrap.
|
|
187
|
+
* Pair with the same getter passed into `createGguiServer({
|
|
188
|
+
* themeProvider })` and a closure that reads from the shared
|
|
189
|
+
* mutable cell `mountDevtoolThemeRoutes`'s POST handler updates.
|
|
190
|
+
* Forwarded onto `deps.push.themeProvider` so the handler reads
|
|
191
|
+
* the live theme each call.
|
|
192
|
+
*/
|
|
193
|
+
readonly themeProvider?: () => {
|
|
194
|
+
readonly id?: string;
|
|
195
|
+
readonly mode?: 'light' | 'dark';
|
|
196
|
+
} | undefined;
|
|
197
|
+
/**
|
|
198
|
+
* Optional connector registry — required for accepting
|
|
199
|
+
* `shortcuts.mcpApps` push payloads (inbound MCP Apps hosting).
|
|
200
|
+
* Omitted = inbound path is rejected with a clear error.
|
|
201
|
+
*/
|
|
202
|
+
readonly connectors?: ConnectorRegistry;
|
|
203
|
+
/**
|
|
204
|
+
* Optional admission-control limiter. When present, `ggui_push`
|
|
205
|
+
* gates every call through `rateLimiter.check({key:
|
|
206
|
+
* 'ggui_push:<appId>', cost:1})` before doing any work; denial
|
|
207
|
+
* surfaces as a `RateLimitedError`. Omitted = unlimited (the
|
|
208
|
+
* `NoopRateLimiter` server default).
|
|
209
|
+
*/
|
|
210
|
+
readonly rateLimiter?: RateLimiter;
|
|
211
|
+
/**
|
|
212
|
+
* Optional shortCode → session binding index. When present,
|
|
213
|
+
* `ggui_push` records every minted `shortCode` so console's
|
|
214
|
+
* `/s/<shortCode>` viewer (via the session-cookie endpoint) can
|
|
215
|
+
* resolve it back to the right session. Absent = hosted cloud
|
|
216
|
+
* flow (DynamoDB side-table owns lookups), or console not
|
|
217
|
+
* enabled.
|
|
218
|
+
*/
|
|
219
|
+
readonly shortCodeIndex?: ShortCodeIndex;
|
|
220
|
+
/**
|
|
221
|
+
* Optional provisional-preview wiring. When present, `ggui_push`
|
|
222
|
+
* kicks off the configured emitter on every qualifying push (the
|
|
223
|
+
* `evaluateProvisionalPreviewGate` predicate filters MCP Apps
|
|
224
|
+
* pushes + storyless calls automatically). Absent = no preview
|
|
225
|
+
* channel traffic.
|
|
226
|
+
*
|
|
227
|
+
* Constructed by `createGguiServer` from `opts.provisionalPreview`
|
|
228
|
+
* plus the late-bound `SessionChannelServer.sendToSession`
|
|
229
|
+
* closure; callers threading their own handler set can build
|
|
230
|
+
* `ProvisionalPreviewDeps` directly.
|
|
231
|
+
*/
|
|
232
|
+
readonly provisionalPreview?: ProvisionalPreviewDeps;
|
|
233
|
+
/**
|
|
234
|
+
* Optional generation wiring. When present, `ggui_push` invokes
|
|
235
|
+
* the supplied {@link UiGenerator} on every story-path call and
|
|
236
|
+
* appends the generated `StackItem` to the session before
|
|
237
|
+
* returning `codeReady: true`. Absent = push stays in
|
|
238
|
+
* placeholder mode (session + shortCode + preview still work,
|
|
239
|
+
* but no componentCode is produced).
|
|
240
|
+
*
|
|
241
|
+
* Callers compose the `GenerationDeps` directly via the
|
|
242
|
+
* `@ggui-ai/mcp-server-handlers` export — `defaultHandlers`
|
|
243
|
+
* simply threads the bundle through to `createGguiPushHandler`.
|
|
244
|
+
*/
|
|
245
|
+
readonly generation?: GenerationDeps;
|
|
246
|
+
/**
|
|
247
|
+
* Optional live-subscriber stack-push notifier.
|
|
248
|
+
* When present, every successful `appendStackItem` inside `ggui_push`
|
|
249
|
+
* fan-outs a `{type:'push', payload:{stackItem}}` live-channel frame to
|
|
250
|
+
* every live subscriber on the affected session. Forwarded as-is
|
|
251
|
+
* to `createGguiPushHandler`.
|
|
252
|
+
*
|
|
253
|
+
* Hosts without a session channel (programmatic embedding, Lambda
|
|
254
|
+
* one-shot) leave this absent — there are no live subscribers to
|
|
255
|
+
* notify, and the push handler's own no-op-on-absent posture
|
|
256
|
+
* keeps the path intact.
|
|
257
|
+
*/
|
|
258
|
+
readonly channelNotifier?: ChannelNotifier;
|
|
259
|
+
/**
|
|
260
|
+
* Optional F4 schema compat check hook. When present,
|
|
261
|
+
* `ggui_push` invokes it immediately
|
|
262
|
+
* before every `appendStackItem` — if the pending StackItem's
|
|
263
|
+
* `actionSpec` / `streamSpec` references a tool whose schemas
|
|
264
|
+
* disagree, the hook throws `SchemaCompatError` and the handler
|
|
265
|
+
* converts the rejection into an error stack-item + `codeReady:
|
|
266
|
+
* false`. Forwarded as-is to `createGguiPushHandler`.
|
|
267
|
+
*
|
|
268
|
+
* `createGguiServer` binds this closure against the composed
|
|
269
|
+
* `handlers` list + `opts.schemaCompatCheck` (default `'reject'`)
|
|
270
|
+
* automatically; callers composing their own push handler via
|
|
271
|
+
* `defaultHandlers` wire the hook themselves.
|
|
272
|
+
*/
|
|
273
|
+
readonly checkStackItemContracts?: (shape: {
|
|
274
|
+
readonly actionSpec?: import('@ggui-ai/protocol').ActionSpec;
|
|
275
|
+
readonly streamSpec?: import('@ggui-ai/protocol').StreamSpec;
|
|
276
|
+
}) => void;
|
|
277
|
+
/**
|
|
278
|
+
* Optional content-addressable code store. When present together
|
|
279
|
+
* with {@link codeBaseUrl}, `ggui_push` writes generated
|
|
280
|
+
* componentCode to the store and surfaces `codeUrl` + `codeHash`
|
|
281
|
+
* on the response — the sole static-component delivery channel
|
|
282
|
+
* post-T3-1 (2026-05-13).
|
|
283
|
+
*
|
|
284
|
+
* Absent: `ggui_push.resultMeta` omits `codeUrl`. The iframe boots
|
|
285
|
+
* via live-mode (wsUrl+token) and receives the stack item via the
|
|
286
|
+
* live-channel WS subscribe. `/r/<shortCode>` + `/api/bootstrap/<shortCode>`
|
|
287
|
+
* routes ALSO mint `codeUrl` when `codeStore` is set — they derive
|
|
288
|
+
* the base URL from `req.protocol + req.host` when `codeBaseUrl`
|
|
289
|
+
* isn't explicit (works for local dev + tunnel deployments).
|
|
290
|
+
* Forwarded as-is to `createGguiPushHandler`.
|
|
291
|
+
*/
|
|
292
|
+
readonly codeStore?: CodeStore;
|
|
293
|
+
/**
|
|
294
|
+
* Base URL the code-blob route resolves to. Required when
|
|
295
|
+
* `codeStore` is present so the handler can compose
|
|
296
|
+
* `<base>/code/<hash>.js`. Forwarded as-is to
|
|
297
|
+
* `createGguiPushHandler`.
|
|
298
|
+
*/
|
|
299
|
+
readonly codeBaseUrl?: string;
|
|
300
|
+
/**
|
|
301
|
+
* Resolver for the bootstrap field
|
|
302
|
+
* `streamWebSocketLocalTools`. Mirrors the handshake's
|
|
303
|
+
* `serverCapabilities.streamWebSocketLocalTools` so iframe-runtime
|
|
304
|
+
* can pick WS-subscribe vs iframe-poll per channel. Composing
|
|
305
|
+
* `createGguiServer` wires both from the SAME
|
|
306
|
+
* `streamWebSocketLocalTools` option — so a server that advertises
|
|
307
|
+
* a tool on the handshake also surfaces it on the bootstrap.
|
|
308
|
+
*
|
|
309
|
+
* Returns undefined ⇒ field omitted from bootstrap ⇒ legacy
|
|
310
|
+
* "iframe polls everything" path. Returns an empty array ⇒
|
|
311
|
+
* "WS transport supported but no tool is local" (still useful —
|
|
312
|
+
* lets the iframe know the server is transport-aware).
|
|
313
|
+
*/
|
|
314
|
+
readonly streamWebSocketLocalTools?: () => readonly string[] | undefined;
|
|
315
|
+
/**
|
|
316
|
+
* Optional bootstrap-refresh seam for the
|
|
317
|
+
* `ggui_runtime_refresh_bootstrap` tool (G14, 2026-05-23). When
|
|
318
|
+
* supplied, the tool registers and validates each refresh request
|
|
319
|
+
* via this seam's HMAC check + refresh-window arithmetic. Typically
|
|
320
|
+
* wired against the SAME `channelBootstrap.refresh` the
|
|
321
|
+
* session-channel server uses for WS upgrade validation, so both
|
|
322
|
+
* paths share one HMAC secret and one refresh-window policy.
|
|
323
|
+
*
|
|
324
|
+
* Absent: the tool is NOT registered on this deployment. iframes
|
|
325
|
+
* fall back to the historical "fresh handshake on every reconnect"
|
|
326
|
+
* posture — fast via the matcher cache, but more wire traffic than
|
|
327
|
+
* a stateless refresh.
|
|
328
|
+
*
|
|
329
|
+
* `createGguiServer` wires this from the `mcpAppsEnabled` branch's
|
|
330
|
+
* `channelBootstrap.refresh` so the OSS factory's behavior matches
|
|
331
|
+
* the cloud pod's tool-side composition.
|
|
332
|
+
*/
|
|
333
|
+
readonly bootstrapRefresh?: import('@ggui-ai/mcp-server-handlers/session-mutations').BootstrapRefreshSeam;
|
|
334
|
+
};
|
|
335
|
+
/**
|
|
336
|
+
* `ggui_update` wiring. When present, register the OSS update
|
|
337
|
+
* handler against the supplied SessionStore + optional live-channel
|
|
338
|
+
* props_update notifier. The handler reads sessionId / stackItemId
|
|
339
|
+
* from wire input today, but a future in-process dispatcher can
|
|
340
|
+
* populate them on the canonical context.
|
|
341
|
+
*
|
|
342
|
+
* Absent = `ggui_update` is NOT registered on this server. Hosts
|
|
343
|
+
* that don't expose props mutation keep the smaller surface (e.g.,
|
|
344
|
+
* static-blueprint demos, MCP-Apps-only deployments).
|
|
345
|
+
*/
|
|
346
|
+
readonly update?: {
|
|
347
|
+
readonly sessionStore: SessionStore;
|
|
348
|
+
/**
|
|
349
|
+
* Optional live-subscriber `props_update` notifier — typically a
|
|
350
|
+
* thin closure over `SessionChannelServer.sendPropsUpdate`.
|
|
351
|
+
* Forwarded as-is to `createGguiUpdateHandler`. Hosts without a
|
|
352
|
+
* session channel leave this absent; the handler still persists
|
|
353
|
+
* via `sessionStore.appendStackItem` on every successful patch.
|
|
354
|
+
*/
|
|
355
|
+
readonly propsUpdateNotifier?: PropsUpdateNotifier;
|
|
356
|
+
/**
|
|
357
|
+
* Bootstrap-credential minter (live trio). When wired, the
|
|
358
|
+
* `ggui_update` resultMeta emits `_meta.ggui.bootstrap` so MCP Apps
|
|
359
|
+
* hosts that re-post `ui/notifications/tool-result` via postMessage
|
|
360
|
+
* can re-apply patched props on the live mount without re-subscribing.
|
|
361
|
+
* Mirrors the same field on `push` deps; composing hosts wire both
|
|
362
|
+
* from the same minter.
|
|
363
|
+
*/
|
|
364
|
+
readonly mintBootstrap?: (sessionId: string, appId: string) => {
|
|
365
|
+
wsUrl: string;
|
|
366
|
+
token: string;
|
|
367
|
+
expiresAt: string;
|
|
368
|
+
};
|
|
369
|
+
/** Iframe-runtime bundle URL forwarded onto bootstrap.runtimeUrl.
|
|
370
|
+
* Function form mirrors push deps — see {@link BuildMcpDeps.push}. */
|
|
371
|
+
readonly runtimeUrl?: string | (() => string | undefined);
|
|
372
|
+
/** Theme preset id forwarded onto bootstrap.themeId. */
|
|
373
|
+
readonly themeId?: string;
|
|
374
|
+
/** Theme color mode forwarded onto bootstrap.themeMode. */
|
|
375
|
+
readonly themeMode?: 'light' | 'dark';
|
|
376
|
+
/** Live theme getter — overrides static themeId/themeMode per-update. */
|
|
377
|
+
readonly themeProvider?: () => {
|
|
378
|
+
readonly id?: string;
|
|
379
|
+
readonly mode?: 'light' | 'dark';
|
|
380
|
+
} | undefined;
|
|
381
|
+
/** Returns names of app-visible tools for bootstrap.appCallableTools. */
|
|
382
|
+
readonly appCallableTools?: () => readonly string[];
|
|
383
|
+
/** Resolver for bootstrap.streamWebSocketLocalTools. */
|
|
384
|
+
readonly streamWebSocketLocalTools?: () => readonly string[] | undefined;
|
|
385
|
+
};
|
|
386
|
+
/**
|
|
387
|
+
* Pending-events consumer wiring for `ggui_consume`. When `push`
|
|
388
|
+
* is bound, the handler registers automatically with an in-memory
|
|
389
|
+
* default; pass `consume.pendingEventConsumer` to override (e.g.,
|
|
390
|
+
* SQLite-backed for persistent dev or a Dynamo adapter on cloud).
|
|
391
|
+
*
|
|
392
|
+
* `defaultSessionTtlSeconds` controls the activity-bump TTL the
|
|
393
|
+
* handler forwards to `consumeAndClear` on every read. Falls back
|
|
394
|
+
* to 1 day when omitted.
|
|
395
|
+
*/
|
|
396
|
+
readonly consume?: {
|
|
397
|
+
readonly pendingEventConsumer?: PendingEventConsumer;
|
|
398
|
+
readonly defaultSessionTtlSeconds?: number;
|
|
399
|
+
};
|
|
400
|
+
/**
|
|
401
|
+
* Stream channel wiring for `ggui_emit`. When `push` is bound, the
|
|
402
|
+
* handler registers automatically; its `sendEnvelope` closes over
|
|
403
|
+
* `stream.channelProvider`, a lazy getter that resolves the
|
|
404
|
+
* `SessionChannelServer` at emit time (the channel is constructed
|
|
405
|
+
* AFTER `defaultHandlers` runs, so a static reference would always
|
|
406
|
+
* be null on the OSS in-process boot).
|
|
407
|
+
*
|
|
408
|
+
* Absent / returns null = no live receiver. Emit still succeeds at
|
|
409
|
+
* the protocol level; the envelope just isn't fanned out. Matches
|
|
410
|
+
* cloud's `ggui_emit_accepted_no_receiver` posture.
|
|
411
|
+
*
|
|
412
|
+
* The getter pattern lets the OSS server bind once at boot, then
|
|
413
|
+
* mutate the cell after `createSessionChannelServer` runs.
|
|
414
|
+
*/
|
|
415
|
+
readonly stream?: {
|
|
416
|
+
readonly channelProvider?: () => SessionChannelServer | null;
|
|
417
|
+
};
|
|
418
|
+
/**
|
|
419
|
+
* Structured-event logger threaded into handlers that emit
|
|
420
|
+
* protocol-adherence telemetry — `ggui_consume` fires the yellow-
|
|
421
|
+
* flag `action_consume_slow` info-event when an event sat in the
|
|
422
|
+
* pipe past the latency threshold. Absent = silent; the handler's
|
|
423
|
+
* drain semantics are unaffected.
|
|
424
|
+
*/
|
|
425
|
+
readonly logger?: Logger;
|
|
426
|
+
/**
|
|
427
|
+
* Operational-signal sink. Threaded into handlers that emit named
|
|
428
|
+
* events (`ggui_new_session` emits `session.created` /
|
|
429
|
+
* `session.create_failed`). Absent = NoopTelemetrySink semantic.
|
|
430
|
+
* Lossy + non-throwing per the {@link TelemetrySink} contract.
|
|
431
|
+
*/
|
|
432
|
+
readonly telemetry?: TelemetrySink;
|
|
433
|
+
/**
|
|
434
|
+
* Per-app metadata store backing `ggui_list_gadgets`.
|
|
435
|
+
* Absent = `createGguiServer` constructs a fresh
|
|
436
|
+
* `InMemoryAppMetadataStore` seeded with `STDLIB_GADGETS` per app
|
|
437
|
+
* on first access (sandbox-app permitted-error path inside the
|
|
438
|
+
* handler also falls back to stdlib, so omitting the store still
|
|
439
|
+
* yields a working tool).
|
|
440
|
+
*
|
|
441
|
+
* Hosted deployments inject an `AppMetadataStore` backed by their per-app
|
|
442
|
+
* metadata table (cloud's DDB-backed adapter applies the
|
|
443
|
+
* default-on-read pattern inside `getApp` directly).
|
|
444
|
+
*/
|
|
445
|
+
readonly appMetadataStore?: AppMetadataStore;
|
|
446
|
+
/**
|
|
447
|
+
* Global theme-catalog resolver consumed by `ggui_list_themes` AND by
|
|
448
|
+
* `ggui_new_session`'s opt-in `requestThemeList` projection. Returns
|
|
449
|
+
* the full registry every call (kept as a function so additions to
|
|
450
|
+
* the catalog at runtime — e.g. operator-defined themes in a future
|
|
451
|
+
* slice — surface without a server restart). When BOTH this and
|
|
452
|
+
* `appMetadataStore` are bound, `ggui_list_themes` registers; either
|
|
453
|
+
* absent ⇒ the tool is omitted from the handler array (zero-config
|
|
454
|
+
* OSS behavior: a deployment that hasn't wired themes simply doesn't
|
|
455
|
+
* advertise theme picking).
|
|
456
|
+
*
|
|
457
|
+
* The CLI binds this to `@ggui-ai/design`'s `listThemes()`; hosted
|
|
458
|
+
* deployments may project a different shape so this stays
|
|
459
|
+
* design-package-agnostic at the handler layer.
|
|
460
|
+
*/
|
|
461
|
+
readonly themes?: () => readonly ThemeCatalogEntry[];
|
|
462
|
+
/**
|
|
463
|
+
* Operator-class blueprint tool wiring. When
|
|
464
|
+
* `generators` + `blueprintStore` + `blueprintSearch` are all
|
|
465
|
+
* bound, `defaultHandlers` registers the four `ggui_ops_*`
|
|
466
|
+
* blueprint tools on `/ops`:
|
|
467
|
+
*
|
|
468
|
+
* - `ggui_ops_generate_blueprint` (requires `resolveLlm` +
|
|
469
|
+
* `blueprints` too — same deps the push generation path
|
|
470
|
+
* reads).
|
|
471
|
+
* - `ggui_ops_list_blueprints`
|
|
472
|
+
* - `ggui_ops_update_blueprint`
|
|
473
|
+
* - `ggui_ops_delete_blueprint`
|
|
474
|
+
*
|
|
475
|
+
* Absent = the ops tools are not registered (operator UX falls
|
|
476
|
+
* back to whatever surface the cloud pod exposes, or the deployment
|
|
477
|
+
* runs without operator authorship). The list/update/delete trio
|
|
478
|
+
* registers even when `generate` deps are absent — read-only
|
|
479
|
+
* operations on an existing store can be useful for inspection.
|
|
480
|
+
*/
|
|
481
|
+
readonly opsBlueprint?: {
|
|
482
|
+
readonly registry: GeneratorRegistry;
|
|
483
|
+
readonly blueprintStore: BlueprintStore;
|
|
484
|
+
readonly blueprintSearch: BlueprintSearch;
|
|
485
|
+
/**
|
|
486
|
+
* Hook into the store's code-body path. When the bound
|
|
487
|
+
* `blueprintStore` is an `InMemoryBlueprintStore`, pass
|
|
488
|
+
* `(codeHash, body) => store.putCode(codeHash, body)` so the
|
|
489
|
+
* generated body is reachable via `getCode(codeHash)`. Cloud
|
|
490
|
+
* adapters that persist code inside `BlueprintStore.put` omit
|
|
491
|
+
* this.
|
|
492
|
+
*/
|
|
493
|
+
readonly putCode?: (codeHash: string, body: string) => void | Promise<void>;
|
|
494
|
+
/**
|
|
495
|
+
* Per-app blueprint enumerator — used for the persona near-dup
|
|
496
|
+
* warning on the generate path. Optional; when omitted the
|
|
497
|
+
* check is skipped.
|
|
498
|
+
*/
|
|
499
|
+
readonly listAllForApp?: (appId: string) => Promise<readonly Blueprint[]>;
|
|
500
|
+
/**
|
|
501
|
+
* Resolver for LLM credentials on the generate path. Same shape
|
|
502
|
+
* as `push.generation.resolveLlm` — typically wired to the same
|
|
503
|
+
* closure. When absent, the generate handler is NOT registered
|
|
504
|
+
* (list/update/delete still register).
|
|
505
|
+
*/
|
|
506
|
+
readonly resolveLlm?: (ctx: import('@ggui-ai/mcp-server-handlers').HandlerContext) => Promise<GenerationCredentials | null> | GenerationCredentials | null;
|
|
507
|
+
/**
|
|
508
|
+
* BlueprintProvider passed to the generator (same instance
|
|
509
|
+
* `defaultHandlers`'s blueprint search reads). Required when
|
|
510
|
+
* `resolveLlm` is set — generate needs this on its
|
|
511
|
+
* UiGenerateInput.
|
|
512
|
+
*/
|
|
513
|
+
readonly blueprints?: BlueprintProvider;
|
|
514
|
+
/**
|
|
515
|
+
* Cache-registry mirror for `ggui_ops_generate_blueprint`. When
|
|
516
|
+
* bound, operator-authored blueprints are dual-written to the
|
|
517
|
+
* cache vectorStore via `registerBlueprint` so the agent-facing
|
|
518
|
+
* matchBlueprint exact-key probe (handshake + push) finds them.
|
|
519
|
+
* Same bundle the push handler reads/writes.
|
|
520
|
+
*/
|
|
521
|
+
readonly cacheRegistry?: {
|
|
522
|
+
readonly embedding: EmbeddingProvider;
|
|
523
|
+
readonly vectorStore: VectorStore;
|
|
524
|
+
};
|
|
525
|
+
};
|
|
526
|
+
/**
|
|
527
|
+
* Per-domain dep bundles for the twelve operator-class `ggui_ops_*`
|
|
528
|
+
* handlers covering apps + orgs + connector-keys + coupons. Each
|
|
529
|
+
* domain is independently optional:
|
|
530
|
+
* deployments that don't wire the seam simply don't register that
|
|
531
|
+
* domain's tools (matching `ggui_ops_get_credit_balance`'s pattern).
|
|
532
|
+
*
|
|
533
|
+
* OSS deployments (no AppSync, no Cognito) leave these all
|
|
534
|
+
* undefined and the surface stays narrow. Cloud pods bind
|
|
535
|
+
* AppSync-backed adapters in a follow-up slice; the handlers ship
|
|
536
|
+
* here with deps seams only.
|
|
537
|
+
*/
|
|
538
|
+
readonly opsApps?: {
|
|
539
|
+
readonly apps: AppsSource;
|
|
540
|
+
readonly userDefaultApp: UserDefaultAppSource;
|
|
541
|
+
};
|
|
542
|
+
readonly opsOrgs?: {
|
|
543
|
+
readonly orgs: OrgsSource;
|
|
544
|
+
readonly invites: OrgInvitesSource;
|
|
545
|
+
};
|
|
546
|
+
readonly opsConnectorKeys?: {
|
|
547
|
+
readonly connectorKeys: ConnectorKeysSource;
|
|
548
|
+
};
|
|
549
|
+
readonly opsCoupon?: {
|
|
550
|
+
readonly coupons: CouponRedeemSource;
|
|
551
|
+
};
|
|
552
|
+
}): ReadonlyArray<SharedHandler<ZodRawShape, ZodRawShape>>;
|
|
553
|
+
export interface CreateGguiServerOptions {
|
|
554
|
+
/**
|
|
555
|
+
* Server identity broadcast to MCP clients. Defaults to
|
|
556
|
+
* `{name: 'ggui-mcp-server', version: '0.0.1', ...}`.
|
|
557
|
+
*/
|
|
558
|
+
readonly info?: Partial<ServerInfo>;
|
|
559
|
+
/**
|
|
560
|
+
* Operator-mode hint that gates the `/devtools/*` namespace.
|
|
561
|
+
*
|
|
562
|
+
* - `'prod'` (default for `ggui serve`): only `/admin/*` mounts.
|
|
563
|
+
* - `'dev'` (default for `ggui dev`): `/devtools/*` mounts in
|
|
564
|
+
* addition to `/admin/*` and the SPA shows the dev-mode link
|
|
565
|
+
* in the TopNav. The dev surfaces are admin-cookie gated, same
|
|
566
|
+
* as `/admin/*` — `mode: 'dev'` only changes WHAT mounts, not
|
|
567
|
+
* who can reach it.
|
|
568
|
+
*
|
|
569
|
+
* When omitted, resolves from `process.env.GGUI_MODE` (`'dev'` →
|
|
570
|
+
* dev, anything else including unset → prod). Pass an explicit
|
|
571
|
+
* value to override the env in test fixtures and embedders.
|
|
572
|
+
*/
|
|
573
|
+
readonly mode?: 'dev' | 'prod';
|
|
574
|
+
/**
|
|
575
|
+
* Shared handler set to expose. Defaults to the blueprint-read family
|
|
576
|
+
* (search + list_featured + render). Pass your own list to add / remove
|
|
577
|
+
* tools — handlers MUST be `SharedHandler` instances from
|
|
578
|
+
* `@ggui-ai/mcp-server-handlers` (or shape-compatible custom ones).
|
|
579
|
+
*/
|
|
580
|
+
readonly handlers?: ReadonlyArray<SharedHandler<ZodRawShape, ZodRawShape>>;
|
|
581
|
+
/**
|
|
582
|
+
* Identity-kind allowlist for tool registration. When set, handlers
|
|
583
|
+
* whose `allowedFor` field declares a non-overlapping audience are
|
|
584
|
+
* skipped at registration time. Handlers without `allowedFor` register
|
|
585
|
+
* unconditionally.
|
|
586
|
+
*
|
|
587
|
+
* - agent-builder posture sets `['app']` — skips hypothetical
|
|
588
|
+
* `['user']`-only handlers without affecting the existing toolset
|
|
589
|
+
* (which is all `['app', 'builder']`).
|
|
590
|
+
* - end-user / Connector posture sets `['user']` — skips all
|
|
591
|
+
* agent-builder writes (push / handshake / update) while keeping
|
|
592
|
+
* the read-only blueprint surface visible.
|
|
593
|
+
* - OSS local omits this option — every handler registers; OSS
|
|
594
|
+
* callers resolve to `kind: 'builder'` and the filter never fires.
|
|
595
|
+
*
|
|
596
|
+
* See `packages/mcp-server-handlers/src/types.ts`
|
|
597
|
+
* (`SharedHandler.allowedFor`).
|
|
598
|
+
*/
|
|
599
|
+
readonly allowedKinds?: ReadonlyArray<'app' | 'user' | 'builder'>;
|
|
600
|
+
/**
|
|
601
|
+
* Auth adapter. Defaults to `InMemoryAuthAdapter({devAllowAll: true})`
|
|
602
|
+
* — accepts any non-empty bearer token as the `builder` identity.
|
|
603
|
+
* Every real deployment SHOULD override this (e.g. with a
|
|
604
|
+
* `PairingService`-backed adapter).
|
|
605
|
+
*/
|
|
606
|
+
readonly auth?: AuthAdapter;
|
|
607
|
+
/**
|
|
608
|
+
* Vector store for blueprint search. Defaults to `InMemoryVectorStore`.
|
|
609
|
+
*/
|
|
610
|
+
readonly vectors?: VectorStore;
|
|
611
|
+
/**
|
|
612
|
+
* Embedding provider for blueprint search. Defaults to `MockEmbeddingProvider`
|
|
613
|
+
* — produces deterministic but NOT semantically meaningful vectors.
|
|
614
|
+
* Swap for a real provider (OpenAI / Bedrock / Voyage / local) in
|
|
615
|
+
* production.
|
|
616
|
+
*/
|
|
617
|
+
readonly embedding?: EmbeddingProvider;
|
|
618
|
+
/**
|
|
619
|
+
* Per-app metadata source. When bound, threaded into
|
|
620
|
+
* `defaultHandlers` so `ggui_list_gadgets`,
|
|
621
|
+
* `ggui_list_themes`, `ggui_new_session({themeId?})`, and the
|
|
622
|
+
* handshake's `app.gadgets` lookup all read from the same
|
|
623
|
+
* store. The CLI binds an `InMemoryAppMetadataStore` seeded from
|
|
624
|
+
* `ggui.json#theme.preset` (so every appId picks up the operator's
|
|
625
|
+
* chosen default theme without an explicit `register()`); hosted
|
|
626
|
+
* deployments bind a multi-tenant adapter.
|
|
627
|
+
*
|
|
628
|
+
* Absent ⇒ `defaultHandlers` constructs a fresh
|
|
629
|
+
* `InMemoryAppMetadataStore` per request site that needs one (no
|
|
630
|
+
* cross-handler sharing) and `ggui_list_themes` is NOT registered.
|
|
631
|
+
*/
|
|
632
|
+
readonly appMetadataStore?: AppMetadataStore;
|
|
633
|
+
/**
|
|
634
|
+
* Global theme-catalog resolver. When bound alongside
|
|
635
|
+
* `appMetadataStore`, registers `ggui_list_themes` and projects the
|
|
636
|
+
* same catalog into `ggui_new_session({requestThemeList: true})`
|
|
637
|
+
* outputs. Read each call so additions to the registry at runtime
|
|
638
|
+
* (operator-defined themes in a future slice) surface without a
|
|
639
|
+
* restart.
|
|
640
|
+
*
|
|
641
|
+
* The OSS CLI binds this to `@ggui-ai/design`'s `listThemes()`;
|
|
642
|
+
* hosted deployments may project a different shape. Kept as a
|
|
643
|
+
* resolver function so this surface stays design-package-agnostic.
|
|
644
|
+
*/
|
|
645
|
+
readonly themes?: () => readonly ThemeCatalogEntry[];
|
|
646
|
+
/**
|
|
647
|
+
* Blueprint catalog source consulted by
|
|
648
|
+
* `ggui_list_featured_blueprints`. Omitted = the handler returns
|
|
649
|
+
* an empty list (zero-config default). The `ggui-cli` binding
|
|
650
|
+
* constructs a `ManifestBlueprintProvider` from the declared
|
|
651
|
+
* `ggui.json#blueprints.include` manifests and passes it here,
|
|
652
|
+
* so `ggui serve` surfaces every authored UI through the MCP tool
|
|
653
|
+
* without any code change per deployment.
|
|
654
|
+
*
|
|
655
|
+
* Passing `blueprintProvider:` is what makes the `ggui.json`
|
|
656
|
+
* manifest's blueprint declarations take effect — the manifest
|
|
657
|
+
* shape stops being inert once a provider is wired.
|
|
658
|
+
*/
|
|
659
|
+
readonly blueprintProvider?: BlueprintProvider;
|
|
660
|
+
/**
|
|
661
|
+
* Admin-blueprints transport (`POST /admin/blueprints`) — runtime
|
|
662
|
+
* manifest registration into the active {@link blueprintProvider}.
|
|
663
|
+
*
|
|
664
|
+
* - `undefined` (default) or an object `{path?}` — mount the
|
|
665
|
+
* route at `/admin/blueprints` (override via `{path: '/x'}`).
|
|
666
|
+
* Only actually mounted when {@link blueprintProvider} is also
|
|
667
|
+
* wired; without a caller-supplied provider the default
|
|
668
|
+
* provider built inside `defaultHandlers` is unreachable from
|
|
669
|
+
* this scope and silently mounting would surprise operators.
|
|
670
|
+
* - `{path: null}` — disable the route explicitly while leaving
|
|
671
|
+
* the provider wired (operators who want the provider but not
|
|
672
|
+
* the HTTP admin surface).
|
|
673
|
+
* - `false` — same as `{path: null}`, but shorter.
|
|
674
|
+
*
|
|
675
|
+
* In-memory only: runtime-registered manifests survive until the
|
|
676
|
+
* server process exits. Plan explicitly permits this choice; a
|
|
677
|
+
* follow-up slice may add disk persistence if operators ask.
|
|
678
|
+
*
|
|
679
|
+
* Auth: builder-bearer, same gate as `/admin/pair/init`.
|
|
680
|
+
*/
|
|
681
|
+
readonly adminBlueprints?: false | {
|
|
682
|
+
readonly path?: string | null;
|
|
683
|
+
};
|
|
684
|
+
/**
|
|
685
|
+
* UI registry consulted by `ggui_render_blueprint`. When present, the
|
|
686
|
+
* render handler is registered on the MCP wire and resolves every
|
|
687
|
+
* call through this registry's `get(id)` + `getBundle(id)` pair.
|
|
688
|
+
* When absent, `ggui_render_blueprint` is NOT registered at all —
|
|
689
|
+
* callers that attempted to invoke it get a clean "tool not
|
|
690
|
+
* available" MCP response instead of a throwing shim.
|
|
691
|
+
*
|
|
692
|
+
* OSS `ggui serve` binds `@ggui-ai/dev-stack::LocalUiRegistry` here
|
|
693
|
+
* (manifest-backed, compile-on-demand via esbuild). Hosted or
|
|
694
|
+
* programmatic embedders plug in their own implementation (cloud
|
|
695
|
+
* origin, S3-backed, etc.) — the handler is pure over the
|
|
696
|
+
* `UiRegistry` interface.
|
|
697
|
+
*/
|
|
698
|
+
readonly uiRegistry?: UiRegistry;
|
|
699
|
+
/**
|
|
700
|
+
* Primitive catalogs declared in `ggui.json#primitives.{packages,local}`
|
|
701
|
+
* and resolved at boot by `discoverPrimitives()` in
|
|
702
|
+
* `@ggui-ai/project-config/node`. Threaded through here so future
|
|
703
|
+
* consumers (generator wiring, capability introspection) can
|
|
704
|
+
* enumerate every declared primitive source without re-reading the
|
|
705
|
+
* manifest.
|
|
706
|
+
*
|
|
707
|
+
* The server itself does NOT yet thread these into the generation
|
|
708
|
+
* pipeline. Making the catalogs visible at boot is the minimum
|
|
709
|
+
* honest "capability is consumed" signal: the declaration is no
|
|
710
|
+
* longer inert; any in-process host can read
|
|
711
|
+
* `server.primitiveCatalogs`.
|
|
712
|
+
*
|
|
713
|
+
* Omitted = the zero-config default (the CLI passes the single
|
|
714
|
+
* shipped `@ggui-ai/design/primitives` catalog; programmatic hosts
|
|
715
|
+
* may pass whatever they discovered themselves).
|
|
716
|
+
*/
|
|
717
|
+
readonly primitiveCatalogs?: readonly DiscoveredPrimitiveCatalog[];
|
|
718
|
+
/**
|
|
719
|
+
* Theme resolved at boot from `ggui.json#theme` by
|
|
720
|
+
* `loadTheme()` in `@ggui-ai/project-config/node`. Threaded through
|
|
721
|
+
* here so future consumers (console bootstrap, render endpoint,
|
|
722
|
+
* MCP apps iframe) can pull the token tree + pre-rendered CSS block
|
|
723
|
+
* without re-reading the manifest.
|
|
724
|
+
*
|
|
725
|
+
* The server itself does NOT yet inject theme CSS into console
|
|
726
|
+
* or bake it into MCP responses. Making the theme visible at boot
|
|
727
|
+
* is the minimum honest "capability is consumed" signal: the
|
|
728
|
+
* declaration stops being inert; any in-process host can read
|
|
729
|
+
* `server.theme`.
|
|
730
|
+
*
|
|
731
|
+
* Omitted = the zero-config default (the server builds a
|
|
732
|
+
* `LoadedTheme` backed by `@ggui-ai/design`'s shipped `lightTheme`
|
|
733
|
+
* internally; `server.theme.source === 'default'`).
|
|
734
|
+
*/
|
|
735
|
+
readonly theme?: LoadedTheme;
|
|
736
|
+
/**
|
|
737
|
+
* Optional callback that persists a theme selection back to disk.
|
|
738
|
+
*
|
|
739
|
+
* The console `/theme` route mutates `ggui.json#theme` via this
|
|
740
|
+
* callback. The server never touches the filesystem itself — the
|
|
741
|
+
* caller (typically `@ggui-ai/cli`'s serve command) provides a
|
|
742
|
+
* writer that knows where `ggui.json` lives and how to rewrite
|
|
743
|
+
* just the `theme` field while preserving formatting + comments.
|
|
744
|
+
*
|
|
745
|
+
* Signature: receives the new {@link ThemeConfig} (or `null` to
|
|
746
|
+
* clear the field and fall back to defaults). Returns a promise
|
|
747
|
+
* that resolves once the write is durable. Throwing surfaces as
|
|
748
|
+
* a 500 from `POST /ggui/console/theme` with the error message
|
|
749
|
+
* forwarded to the operator.
|
|
750
|
+
*
|
|
751
|
+
* Omitted = `POST /ggui/console/theme` returns 501
|
|
752
|
+
* (`writer_not_configured`). The picker stays browsable; clicking
|
|
753
|
+
* "save" surfaces the missing-writer error instead of silently
|
|
754
|
+
* writing nowhere.
|
|
755
|
+
*/
|
|
756
|
+
readonly themeWriter?: ThemeWriter;
|
|
757
|
+
/**
|
|
758
|
+
* Optional callback that writes an uploaded DTCG theme document to a
|
|
759
|
+
* file alongside `ggui.json`. Pairs with {@link themeWriter}: the
|
|
760
|
+
* `POST /ggui/console/theme/upload` route invokes this first, then
|
|
761
|
+
* calls `themeWriter` with `{ file: './<filename>', mode }`.
|
|
762
|
+
*
|
|
763
|
+
* Omitted = the upload route returns 501 and the picker hides its
|
|
764
|
+
* "Upload theme.json" button. Provided alone (without a writer) is
|
|
765
|
+
* also treated as "not configured" — both side-effects must land for
|
|
766
|
+
* an upload to be meaningful.
|
|
767
|
+
*/
|
|
768
|
+
readonly themeFileUploader?: ThemeFileUploader;
|
|
769
|
+
/**
|
|
770
|
+
* Live-theme getter. When set, the `ggui_push` handler reads this
|
|
771
|
+
* on every result-meta computation and embeds the returned `id` /
|
|
772
|
+
* `mode` into `_meta.ggui.bootstrap`. Pair with
|
|
773
|
+
* {@link onThemeConfigChange} so a console save updates the
|
|
774
|
+
* shared state cell the closure reads from. The cell pattern
|
|
775
|
+
* closes the parallel-state-stores bug where the push handler
|
|
776
|
+
* captured `themeId` at boot from the static `theme` opt and
|
|
777
|
+
* silently ignored every subsequent ggui.json edit until restart.
|
|
778
|
+
*
|
|
779
|
+
* Returning `undefined` means "no theme override" — the static
|
|
780
|
+
* `theme` opt's resolved id/mode (if any) take effect. Returning
|
|
781
|
+
* `{ id, mode? }` always wins over the static path.
|
|
782
|
+
*/
|
|
783
|
+
readonly themeProvider?: () => {
|
|
784
|
+
readonly id?: string;
|
|
785
|
+
readonly mode?: 'light' | 'dark';
|
|
786
|
+
} | undefined;
|
|
787
|
+
/**
|
|
788
|
+
* Optional change notifier — fires when the operator's theme
|
|
789
|
+
* selection changes via `POST /ggui/console/theme` (or the
|
|
790
|
+
* `/upload` variant). Forwarded onto
|
|
791
|
+
* `mountDevtoolThemeRoutes({onConfigChange})`. Pair with a
|
|
792
|
+
* `themeProvider` closure that reads from the same shared cell
|
|
793
|
+
* the callback writes to so a console save reaches the next push
|
|
794
|
+
* without restarting the server.
|
|
795
|
+
*
|
|
796
|
+
* `next` matches `ThemeConfig` from `@ggui-ai/project-config` —
|
|
797
|
+
* one of: a string shorthand (`'indigo'`), a preset object
|
|
798
|
+
* (`{ preset, mode?, overrides? }`), a file object
|
|
799
|
+
* (`{ file, mode? }`), or `null` (cleared).
|
|
800
|
+
*/
|
|
801
|
+
readonly onThemeConfigChange?: (next: string | {
|
|
802
|
+
preset: string;
|
|
803
|
+
mode?: 'light' | 'dark';
|
|
804
|
+
overrides?: Record<string, string>;
|
|
805
|
+
} | {
|
|
806
|
+
file: string;
|
|
807
|
+
mode?: 'light' | 'dark';
|
|
808
|
+
} | null) => void;
|
|
809
|
+
/**
|
|
810
|
+
* Map a resolved identity to the `appId` used by handlers for tenant
|
|
811
|
+
* scoping. Defaults to `defaultAppIdFromIdentity` — single-user
|
|
812
|
+
* `'builder'` for builder-kind identities, `userId`/`workspaceId`
|
|
813
|
+
* for user-kind.
|
|
814
|
+
*/
|
|
815
|
+
readonly appIdFromIdentity?: (result: AuthResult) => string;
|
|
816
|
+
/**
|
|
817
|
+
* Path the universal MCP endpoint mounts at. Defaults to `/mcp` per
|
|
818
|
+
* Streamable HTTP convention. Cloud `mcp.ggui.ai` overrides to `/`
|
|
819
|
+
* (bare root) so URLs are short — the domain already says "mcp",
|
|
820
|
+
* no need to repeat it in the path.
|
|
821
|
+
*
|
|
822
|
+
* Threaded into the well-known protected-resource metadata so OAuth
|
|
823
|
+
* clients discover the right resource URL, and into the route table
|
|
824
|
+
* so `app.post(${path})` / `app.get/delete(${path})` mount on it.
|
|
825
|
+
*/
|
|
826
|
+
readonly universalMcpPath?: string;
|
|
827
|
+
/**
|
|
828
|
+
* Per-tenant URL routing. When set, the factory
|
|
829
|
+
* additionally mounts `${pathPrefix}/:${paramName}(${paramPattern})`
|
|
830
|
+
* alongside the universal path. The shared handler reads
|
|
831
|
+
* `req.params[paramName]` and uses it as `ctx.appId`, overriding
|
|
832
|
+
* `appIdFromIdentity` for that request.
|
|
833
|
+
*
|
|
834
|
+
* Cloud `mcp.ggui.ai` deployments pass `{paramName: 'appId',
|
|
835
|
+
* paramPattern: '[A-Za-z0-9]{8}', pathPrefix: '/apps'}` so URLs
|
|
836
|
+
* like `mcp.ggui.ai/apps/aB3kP9xY` route to a session scoped to
|
|
837
|
+
* that specific GguiApp. The `/apps/` prefix segments the
|
|
838
|
+
* namespace cleanly — no risk of an 8-char appId colliding with a
|
|
839
|
+
* bare-root system route like `/health` or `/settings`.
|
|
840
|
+
*
|
|
841
|
+
* Without `pathPrefix`, the route mounts at the bare
|
|
842
|
+
* `/:${paramName}(${paramPattern})` — useful only when the
|
|
843
|
+
* deployment owns the entire URL space and the pattern guarantees
|
|
844
|
+
* no collision (e.g. UUIDs).
|
|
845
|
+
*
|
|
846
|
+
* Pattern is an Express-compatible regex (no slashes, no flags).
|
|
847
|
+
* Express only matches the URL when it satisfies the pattern; a
|
|
848
|
+
* malformed appId 404s instead of reaching the handler.
|
|
849
|
+
*
|
|
850
|
+
* `authorize` is the deployment-specific access check. After auth
|
|
851
|
+
* resolves but before session work begins, the handler invokes it
|
|
852
|
+
* with the URL-supplied appId + identity. Throw to deny — the
|
|
853
|
+
* handler converts to a 403 response and skips MCP processing.
|
|
854
|
+
* Cloud uses this to verify `GguiApp.userId === identity.userId`
|
|
855
|
+
* (raw-DDB readers in pod tools bypass AppSync owner-auth, so
|
|
856
|
+
* this is the boundary that prevents cross-user blueprint reads).
|
|
857
|
+
* OSS deployments that opt in to per-app routing without an
|
|
858
|
+
* authorize callback are TRUSTED — every authenticated caller can
|
|
859
|
+
* scope to any URL appId.
|
|
860
|
+
*/
|
|
861
|
+
readonly perAppRouting?: {
|
|
862
|
+
readonly paramName: string;
|
|
863
|
+
readonly paramPattern: string;
|
|
864
|
+
/**
|
|
865
|
+
* Optional path prefix prepended to the per-app route. Cloud
|
|
866
|
+
* `mcp.ggui.ai` uses `'/apps'` so URLs are
|
|
867
|
+
* `mcp.ggui.ai/apps/<appId>` — leaves the bare root for system
|
|
868
|
+
* routes (`/health`, `/oauth/*`, `/.well-known/*`) without
|
|
869
|
+
* collision concerns. Omit when the pattern alone guarantees
|
|
870
|
+
* non-collision (e.g. UUIDs, opaque hex of fixed length).
|
|
871
|
+
*/
|
|
872
|
+
readonly pathPrefix?: string;
|
|
873
|
+
readonly authorize?: (urlAppId: string, identity: AuthResult) => Promise<void>;
|
|
874
|
+
};
|
|
875
|
+
/** Structured logger. Defaults to `createConsoleLogger()`. */
|
|
876
|
+
readonly logger?: Logger;
|
|
877
|
+
/**
|
|
878
|
+
* Optional error-to-HTTP mapper invoked on any handler / transport
|
|
879
|
+
* exception that surfaces past the MCP SDK. When the mapper returns
|
|
880
|
+
* a `{status, code, message}` triple the factory writes that JSON-RPC
|
|
881
|
+
* error response instead of the default `500 / -32603 'Internal
|
|
882
|
+
* server error'`. Returning `undefined` (or omitting the option)
|
|
883
|
+
* preserves the default.
|
|
884
|
+
*
|
|
885
|
+
* Use case: hosted closed-runtime deployments throw domain errors from
|
|
886
|
+
* tool handlers (e.g. `SessionAccessError` "this session doesn't belong
|
|
887
|
+
* to you") that should map to HTTP 404 so callers can distinguish
|
|
888
|
+
* tenancy violations from real server bugs. OSS deployments don't
|
|
889
|
+
* need this seam — every domain error is a 500 unless they say
|
|
890
|
+
* otherwise.
|
|
891
|
+
*
|
|
892
|
+
* The mapper MUST NOT throw. It runs inside the factory's outer
|
|
893
|
+
* `catch (err)` block; any throw from the mapper itself is treated
|
|
894
|
+
* as if it returned `undefined` (default 500).
|
|
895
|
+
*/
|
|
896
|
+
readonly errorMapper?: (err: unknown) => {
|
|
897
|
+
readonly status: number;
|
|
898
|
+
readonly code: number;
|
|
899
|
+
readonly message: string;
|
|
900
|
+
} | undefined;
|
|
901
|
+
/**
|
|
902
|
+
* BYOK provider-key store consumed by the operator-facing
|
|
903
|
+
* `/ggui/console/llm-keys` admin API and (today, transparently
|
|
904
|
+
* via `ggui-cli`'s `ByokResolver`) by the generation pipeline.
|
|
905
|
+
*
|
|
906
|
+
* When set, the gated route block mounts:
|
|
907
|
+
* - `GET /ggui/console/llm-keys` — list providers + presence
|
|
908
|
+
* - `POST /ggui/console/llm-keys` — set a provider's key
|
|
909
|
+
* - `DELETE /ggui/console/llm-keys/:provider` — clear (idempotent)
|
|
910
|
+
*
|
|
911
|
+
* The store is the SAME instance backing the CLI's `ByokResolver`
|
|
912
|
+
* second-step lookup (`~/.ggui/credentials.json` by default) — writing
|
|
913
|
+
* via this API has immediate effect on subsequent generations because
|
|
914
|
+
* `PlaintextFileProviderKeyStore` re-reads the file on every `get()`.
|
|
915
|
+
*
|
|
916
|
+
* Omitted (default): the route block is NOT mounted. Operators
|
|
917
|
+
* who want the /settings UI to work pass a store explicitly. The
|
|
918
|
+
* CLI binding does so for personal-mode `ggui serve`; programmatic
|
|
919
|
+
* embedders (test suites, custom hosts) can omit it.
|
|
920
|
+
*/
|
|
921
|
+
readonly providerKeys?: ProviderKeyStore;
|
|
922
|
+
/**
|
|
923
|
+
* Map an authenticated request to the BYOK scope key the
|
|
924
|
+
* `/ggui/console/llm-keys` endpoints write under (and that the
|
|
925
|
+
* generation pipeline reads via `ProviderKeyStore.get(scope, provider)`).
|
|
926
|
+
*
|
|
927
|
+
* Default depends on {@link providerKeysGate}:
|
|
928
|
+
* - `'admin-token'` (default): scope is always `'global'` — the
|
|
929
|
+
* OSS-personal posture: every caller who clears the admin gate
|
|
930
|
+
* operates on the single global keyset stored in
|
|
931
|
+
* `~/.ggui/credentials.json`.
|
|
932
|
+
* - `'auth-adapter'`: scope is derived from the authenticated
|
|
933
|
+
* identity — `userId` for `kind: 'user'`, `appId` for `kind: 'app'`,
|
|
934
|
+
* and `'global'` for `kind: 'builder'` (which under multi-tenant
|
|
935
|
+
* is rejected at the gate before this fires anyway).
|
|
936
|
+
*
|
|
937
|
+
* Operators with composite scopes (`${appId}:${userId}` for per-app-
|
|
938
|
+
* per-user keysets) override this; the underlying store treats the
|
|
939
|
+
* value as opaque. The `identity` arg is `null` under the
|
|
940
|
+
* `'admin-token'` gate (no auth-adapter call happens) and the
|
|
941
|
+
* resolved `AuthResult` under the `'auth-adapter'` gate.
|
|
942
|
+
*/
|
|
943
|
+
readonly providerKeyScope?: (req: Request, identity: AuthResult | null) => string;
|
|
944
|
+
/**
|
|
945
|
+
* Which gate guards the `/ggui/console/llm-keys` plane.
|
|
946
|
+
*
|
|
947
|
+
* - `'admin-token'` (default — OSS-personal posture): the gate
|
|
948
|
+
* accepts the admin bearer (Authorization header or
|
|
949
|
+
* `ggui_console_admin` cookie). Single global keyset; the
|
|
950
|
+
* /settings UI lets the operator paste keys that everyone uses.
|
|
951
|
+
* - `'auth-adapter'` (multi-tenant posture): the gate calls the
|
|
952
|
+
* server's configured `AuthAdapter` (same path as `/mcp`). Each
|
|
953
|
+
* authenticated end-user manages their OWN keys, scoped by
|
|
954
|
+
* {@link providerKeyScope} (default: `userId` / `appId`).
|
|
955
|
+
* `kind: 'builder'` identities are rejected at the gate — the
|
|
956
|
+
* posture is meaningless without a real per-caller identifier.
|
|
957
|
+
*
|
|
958
|
+
* Both gates require `providerKeys` to be set; route block is
|
|
959
|
+
* unmounted otherwise. Under `'admin-token'`, the route block is
|
|
960
|
+
* additionally unmounted when the server has no admin token wired
|
|
961
|
+
* (e.g. embedding hosts that don't surface console routes). Under
|
|
962
|
+
* `'auth-adapter'`, the admin token is irrelevant — the route is
|
|
963
|
+
* mounted whenever `providerKeys` is set.
|
|
964
|
+
*/
|
|
965
|
+
readonly providerKeysGate?: 'admin-token' | 'auth-adapter';
|
|
966
|
+
/** Express body size limit. Defaults to `'4mb'`. */
|
|
967
|
+
readonly bodyLimit?: string;
|
|
968
|
+
/**
|
|
969
|
+
* Session store — backing plane for the live-channel session endpoint
|
|
970
|
+
* (and future OSS session-reading MCP tools). Defaults to
|
|
971
|
+
* `InMemorySessionStore`, which is fine for OSS zero-config / dev.
|
|
972
|
+
* SQLite / Postgres / Redis adapters bind via the same interface
|
|
973
|
+
* when they land.
|
|
974
|
+
*/
|
|
975
|
+
readonly sessionStore?: SessionStore;
|
|
976
|
+
/**
|
|
977
|
+
* Outbound stream replay buffer for the live-channel endpoint. Defaults
|
|
978
|
+
* to a fresh `InMemorySessionStreamBuffer` — fine for OSS zero-config
|
|
979
|
+
* / dev. Operators who need durability layer a different
|
|
980
|
+
* `SessionStreamBuffer` implementation behind this seam.
|
|
981
|
+
*
|
|
982
|
+
* Only used when `sessionChannel` is enabled. Ignored otherwise.
|
|
983
|
+
*/
|
|
984
|
+
readonly streamBuffer?: SessionStreamBuffer;
|
|
985
|
+
/**
|
|
986
|
+
* Enable the OSS live-channel session endpoint at `/ws` (configurable).
|
|
987
|
+
*
|
|
988
|
+
* - `false` (default): no session channel. `/mcp` is the only
|
|
989
|
+
* HTTP surface. Callers who only need the tool plane get the
|
|
990
|
+
* smallest shape.
|
|
991
|
+
* - `true`: mount the channel at the default path (`/ws`) with
|
|
992
|
+
* the default session store.
|
|
993
|
+
* - `{ path?: string }`: override the mount path.
|
|
994
|
+
*
|
|
995
|
+
* The live channel is where the live-contract enforcement point
|
|
996
|
+
* lives. Enabling this makes the OSS server a second real consumer
|
|
997
|
+
* of the shared `@ggui-ai/mcp-server-handlers/session-mutations`
|
|
998
|
+
* helpers.
|
|
999
|
+
*/
|
|
1000
|
+
readonly sessionChannel?: boolean | {
|
|
1001
|
+
readonly path?: string;
|
|
1002
|
+
};
|
|
1003
|
+
/**
|
|
1004
|
+
* Opt-in WS-direct action dispatcher for agent-less deployments.
|
|
1005
|
+
* When present AND `sessionChannel: true`, the channel server
|
|
1006
|
+
* fires the tool named by an incoming action's `payload.tool` hint
|
|
1007
|
+
* (falling back to `actionSpec[name].nextStep` when the client
|
|
1008
|
+
* omitted the hint) in-process after inbound validation, and emits
|
|
1009
|
+
* every declared `streamSpec[name].source.tool` refresh on the
|
|
1010
|
+
* session. See {@link WiredActionRouter} + `session-channel.ts` for
|
|
1011
|
+
* the full router contract.
|
|
1012
|
+
*
|
|
1013
|
+
* Absent = agent-mediated behavior (canonical for MCP Apps hosts and
|
|
1014
|
+
* Claude Agent SDK consumers). Inbound actions land on the
|
|
1015
|
+
* stackItemId-keyed pending-events pipe via `ggui_runtime_submit_action`
|
|
1016
|
+
* and the agent's `ggui_consume` long-poll drains them. CLI
|
|
1017
|
+
* composition in `ggui serve` wires a router by default over the
|
|
1018
|
+
* same handler bundle `/mcp` uses, since that command runs WITHOUT
|
|
1019
|
+
* an agent (raw WS clients hitting the OSS server directly). Library
|
|
1020
|
+
* consumers MAY pass their own router; library consumers running
|
|
1021
|
+
* behind an agent typically pass `undefined` so actions flow through
|
|
1022
|
+
* the agent's reasoning loop.
|
|
1023
|
+
*/
|
|
1024
|
+
readonly wiredActionRouter?: WiredActionRouter;
|
|
1025
|
+
/**
|
|
1026
|
+
* Per-call timeout for wired-tool invocations, in ms. Defaults to
|
|
1027
|
+
* `DEFAULT_WIRED_TOOL_TIMEOUT_MS` (30 s) when omitted. Forwarded
|
|
1028
|
+
* verbatim to `createSessionChannelServer`.
|
|
1029
|
+
*/
|
|
1030
|
+
readonly wiredActionTimeoutMs?: number;
|
|
1031
|
+
/**
|
|
1032
|
+
* Opt-in plumbing for `channel_subscribe` polling — the WS fan-out
|
|
1033
|
+
* path for `streamSpec[*].source.tool`. When present, the
|
|
1034
|
+
* session channel accepts `channel_subscribe` frames whose
|
|
1035
|
+
* `source.tool` is in `allowlist` and begins polling. When absent
|
|
1036
|
+
* (the OSS first-run zero-config posture), every `channel_subscribe`
|
|
1037
|
+
* rejects with `CHANNEL_NOT_LOCAL` so the iframe falls back to
|
|
1038
|
+
* direct polling via the MCP host proxy.
|
|
1039
|
+
*
|
|
1040
|
+
* The `allowlist` is also advertised on every successful
|
|
1041
|
+
* `ggui_handshake` response as
|
|
1042
|
+
* `serverCapabilities.streamWebSocketLocalTools` so `@ggui-ai/wire`
|
|
1043
|
+
* agrees with the server on which channels use WS fan-out.
|
|
1044
|
+
*
|
|
1045
|
+
* Only consulted when `sessionChannel` is enabled. Forwarded
|
|
1046
|
+
* verbatim to `createSessionChannelServer` (see
|
|
1047
|
+
* `SessionChannelOptions.streamWebSocketLocalTools`).
|
|
1048
|
+
*/
|
|
1049
|
+
readonly streamWebSocketLocalTools?: import('./session-channel.js').SessionChannelLocalToolsOptions;
|
|
1050
|
+
/**
|
|
1051
|
+
* Override the sanitizer applied to the stringified original error
|
|
1052
|
+
* written into `ContractErrorPayload.error.causedBy`. Defaults to
|
|
1053
|
+
* `@ggui-ai/protocol::sanitizeCausedBy` when omitted — redacts
|
|
1054
|
+
* Bearer tokens, query-param secrets, common env-var dumps, and
|
|
1055
|
+
* truncates at 2 KB. Forwarded verbatim to
|
|
1056
|
+
* `createSessionChannelServer`. See `SessionChannelOptions
|
|
1057
|
+
* .sanitizeCausedBy` for the contract.
|
|
1058
|
+
*/
|
|
1059
|
+
readonly sanitizeCausedBy?: import('@ggui-ai/protocol').SanitizeCausedBy;
|
|
1060
|
+
/**
|
|
1061
|
+
* Extra reserved-channel payload validators merged with the
|
|
1062
|
+
* server's default A2UI preview validator before being passed to
|
|
1063
|
+
* the session channel (Item 4 injection pattern). Caller-provided
|
|
1064
|
+
* entries WIN on key conflict — the pattern is "server supplies
|
|
1065
|
+
* defaults, operator may replace by key".
|
|
1066
|
+
*
|
|
1067
|
+
* Absent = the server binds only the A2UI validator for
|
|
1068
|
+
* `_ggui:preview` by default. `_ggui:contract-error` is validated
|
|
1069
|
+
* via the protocol-shipped builtin regardless of this option.
|
|
1070
|
+
*
|
|
1071
|
+
* Pass `new Map()` (explicitly empty) to DISABLE the A2UI default —
|
|
1072
|
+
* useful in tests that want to assert `validateStreamData`'s
|
|
1073
|
+
* fall-through behavior on `_ggui:preview` without the adapter
|
|
1074
|
+
* running.
|
|
1075
|
+
*/
|
|
1076
|
+
readonly extraReservedValidators?: ReadonlyMap<string, import('@ggui-ai/protocol').ReservedChannelValidator>;
|
|
1077
|
+
/**
|
|
1078
|
+
* Protocol-version handshake policy for the session channel. Forwarded
|
|
1079
|
+
* verbatim to `createSessionChannelServer` (see
|
|
1080
|
+
* `SessionChannelOptions.versionPolicy`). Defaults to `'reject'` —
|
|
1081
|
+
* mismatched `SubscribePayload.supportedVersions` emits
|
|
1082
|
+
* UPGRADE_REQUIRED and closes the connection. Legacy opt-out
|
|
1083
|
+
* `'advisory'` keeps the connection open after the error frame for
|
|
1084
|
+
* controlled migration windows.
|
|
1085
|
+
*
|
|
1086
|
+
* Only consulted when `sessionChannel` is enabled.
|
|
1087
|
+
*/
|
|
1088
|
+
readonly versionPolicy?: 'advisory' | 'reject';
|
|
1089
|
+
/**
|
|
1090
|
+
* Policy for the schema compat check. Checks that every
|
|
1091
|
+
* `actionSpec[name]` tool ref points at a tool whose
|
|
1092
|
+
* `inputSchema` is a superset of the
|
|
1093
|
+
* action's declared `schema`, and that every
|
|
1094
|
+
* `streamSpec[channel].tool` ref points at a tool whose return
|
|
1095
|
+
* schema fits inside the channel's declared `schema`.
|
|
1096
|
+
*
|
|
1097
|
+
* - `'reject'` (default) — violations throw before the stack
|
|
1098
|
+
* item commits (or before blueprint registration completes).
|
|
1099
|
+
* Canonical enforcement posture for launch.
|
|
1100
|
+
* - `'warn'` — violations log through the server's structured
|
|
1101
|
+
* logger (`schema_compat_warn` event with the full report
|
|
1102
|
+
* attached). Caller's flow continues. Used for controlled
|
|
1103
|
+
* migration windows.
|
|
1104
|
+
* - `'off'` — check is skipped entirely. Test / opt-out
|
|
1105
|
+
* convenience.
|
|
1106
|
+
*
|
|
1107
|
+
* Applies to both check points wired by this server:
|
|
1108
|
+
*
|
|
1109
|
+
* 1. The console `POST /ggui/console/blueprint/:id/try`
|
|
1110
|
+
* endpoint (blueprint registration — fires when a manifest
|
|
1111
|
+
* blueprint's pre-declared `actionSpec` / `streamSpec`
|
|
1112
|
+
* references a tool mounted on this server).
|
|
1113
|
+
* 2. The `ggui_push` generation path (push-time — defensive
|
|
1114
|
+
* wiring for when the generator starts emitting
|
|
1115
|
+
* actionSpec / streamSpec on its `UIGenerationResponse`;
|
|
1116
|
+
* current generators emit only componentCode, but the
|
|
1117
|
+
* hook is in place so the check fires automatically when
|
|
1118
|
+
* generator outputs widen).
|
|
1119
|
+
*
|
|
1120
|
+
* See `./schema-compat.ts` for the check helper contract, and
|
|
1121
|
+
* `@ggui-ai/protocol/validation/{schema-subset,zod-to-json-schema}`
|
|
1122
|
+
* for the underlying primitives.
|
|
1123
|
+
*/
|
|
1124
|
+
readonly schemaCompatCheck?: SchemaCompatMode;
|
|
1125
|
+
/**
|
|
1126
|
+
* Enable OAuth 2.1 + PKCE + Dynamic Client Registration on this
|
|
1127
|
+
* server (per MCP spec 2025-06-18+).
|
|
1128
|
+
*
|
|
1129
|
+
* - `false` / omitted (default): OAuth routes NOT mounted;
|
|
1130
|
+
* `WWW-Authenticate` header NOT set on 401 responses. Pure-bearer
|
|
1131
|
+
* clients (CLI tools shipping `Authorization: Bearer ggui_user_*`)
|
|
1132
|
+
* still work; OAuth-discovery clients (Claude Desktop, claude.ai,
|
|
1133
|
+
* Goose, etc.) bail with "couldn't reach" / "couldn't authenticate".
|
|
1134
|
+
* - `true`: enable with defaults. Mounts:
|
|
1135
|
+
* GET /.well-known/oauth-protected-resource (RFC 9728)
|
|
1136
|
+
* GET /.well-known/oauth-authorization-server (RFC 8414)
|
|
1137
|
+
* POST /oauth/register (RFC 7591 DCR)
|
|
1138
|
+
* GET /oauth/authorize (paste-key form)
|
|
1139
|
+
* POST /oauth/authorize (form submit)
|
|
1140
|
+
* POST /oauth/token (code → access_token)
|
|
1141
|
+
* Adds `WWW-Authenticate: Bearer realm=mcp, resource_metadata=…`
|
|
1142
|
+
* header to 401 responses on `/mcp`. Storage defaults to
|
|
1143
|
+
* {@link InMemoryOAuthStorage}.
|
|
1144
|
+
* - `{ issuerUrl?, storage? }`: explicit config. `issuerUrl` is the
|
|
1145
|
+
* public origin the server advertises in metadata + redirects
|
|
1146
|
+
* (defaults to derivation from `X-Forwarded-Proto`/`Host`);
|
|
1147
|
+
* `storage` swaps the in-memory map for a Redis/DDB-backed
|
|
1148
|
+
* implementation when multi-replica deployments need stateless
|
|
1149
|
+
* token exchange.
|
|
1150
|
+
*
|
|
1151
|
+
* The OAuth flow is a one-time ceremony per Claude Desktop install:
|
|
1152
|
+
* the user pastes their `ggui_user_*` API key once, the server hands
|
|
1153
|
+
* it back as the `access_token`. Subsequent `/mcp` calls hit the
|
|
1154
|
+
* existing {@link AuthAdapter} (e.g. `ApiKeyAuthAdapter`) unchanged.
|
|
1155
|
+
* See `./oauth.ts` for the full flow.
|
|
1156
|
+
*/
|
|
1157
|
+
readonly oauth?: boolean | OAuthConfig;
|
|
1158
|
+
/**
|
|
1159
|
+
* Enable the MCP Apps outbound delivery path on this server.
|
|
1160
|
+
*
|
|
1161
|
+
* - `false` / omitted (default): `ggui_push` is NOT registered;
|
|
1162
|
+
* `ui://ggui/session` is NOT served; `io.modelcontextprotocol/ui`
|
|
1163
|
+
* is NOT advertised. Server looks identical to the pre-MCP-Apps
|
|
1164
|
+
* surface.
|
|
1165
|
+
* - `true`: enable with sensible defaults. Requires
|
|
1166
|
+
* `sessionChannel: true` so the iframe has a WebSocket to open;
|
|
1167
|
+
* throws at construction otherwise. `renderBaseUrl` defaults to
|
|
1168
|
+
* `"http://localhost/r/"` (dev-sensible; production operators
|
|
1169
|
+
* override).
|
|
1170
|
+
* - `{ renderBaseUrl?, shellHtml?, wsUrl? }`: explicit config.
|
|
1171
|
+
*
|
|
1172
|
+
* When enabled, FOUR things happen on every fresh per-request
|
|
1173
|
+
* `McpServer`:
|
|
1174
|
+
* 1. `ggui_push` tool is registered, carrying `_meta.ui.resourceUri:
|
|
1175
|
+
* "ui://ggui/session"` and `_meta.ui.visibility: ["model"]` on
|
|
1176
|
+
* its declaration.
|
|
1177
|
+
* 2. `ui://ggui/session` is served via `resources/read`.
|
|
1178
|
+
* 3. `io.modelcontextprotocol/ui` is advertised in the server's
|
|
1179
|
+
* `initialize` capabilities (under `experimental`).
|
|
1180
|
+
* 4. Each `ggui_push` result carries `_meta.ggui.bootstrap` (wsUrl
|
|
1181
|
+
* + short-TTL token + expiresAt). The session-channel server
|
|
1182
|
+
* accepts that token on `subscribe` and issues a longer-TTL
|
|
1183
|
+
* `sessionToken` in the ack for iframe reconnects.
|
|
1184
|
+
*/
|
|
1185
|
+
readonly mcpApps?: boolean | {
|
|
1186
|
+
readonly renderBaseUrl?: string;
|
|
1187
|
+
readonly shellHtml?: string;
|
|
1188
|
+
/**
|
|
1189
|
+
* External WebSocket URL the iframe should open, visible to
|
|
1190
|
+
* MCP Apps hosts. Defaults to `"ws://localhost:<port>/ws"`
|
|
1191
|
+
* — only sensible for local dev. Production operators pass
|
|
1192
|
+
* their public URL (`wss://mcp.example.com/ws`).
|
|
1193
|
+
*/
|
|
1194
|
+
readonly wsUrl?: string;
|
|
1195
|
+
};
|
|
1196
|
+
/**
|
|
1197
|
+
* Iframe-runtime bundle mount (C8 — plan §C8).
|
|
1198
|
+
*
|
|
1199
|
+
* The thin-shell HTML served from `ui://ggui/session` dynamic-
|
|
1200
|
+
* script-loads the renderer bundle from this URL. The server needs
|
|
1201
|
+
* to either (a) serve the bundle itself (default), or (b) publish
|
|
1202
|
+
* the operator-owned URL on `_meta.ggui.bootstrap.runtimeUrl`
|
|
1203
|
+
* so the shell knows where to look.
|
|
1204
|
+
*
|
|
1205
|
+
* - `true` / omitted (default when `mcpApps` is on): serve the
|
|
1206
|
+
* bundle via `express.static` at `/_ggui/iframe-runtime.js` from the
|
|
1207
|
+
* `@ggui-ai/iframe-runtime` package's built `dist/iframe-runtime.js`.
|
|
1208
|
+
* `runtimeUrl` on the bootstrap becomes `/_ggui/iframe-runtime.js`.
|
|
1209
|
+
* - `false`: no static mount. Operator MUST supply
|
|
1210
|
+
* `runtime.url` so the bootstrap still carries a valid URL
|
|
1211
|
+
* (or the shell will fail `MALFORMED_BOOTSTRAP`).
|
|
1212
|
+
* - `{ path?, distDir?, url? }`: explicit config. `path` is the
|
|
1213
|
+
* HTTP route under which the bundle is served (default
|
|
1214
|
+
* `/_ggui/iframe-runtime.js`); `distDir` overrides the
|
|
1215
|
+
* {@link @ggui-ai/iframe-runtime/server!RUNTIME_BUNDLE_FILE} auto-
|
|
1216
|
+
* resolution for advanced embeddings; `url` overrides the URL
|
|
1217
|
+
* written onto the bootstrap (useful when a CDN / proxy fronts
|
|
1218
|
+
* the bundle — mount here for local verification, publish the
|
|
1219
|
+
* external URL to clients).
|
|
1220
|
+
*
|
|
1221
|
+
* Ignored entirely when `mcpApps` is disabled.
|
|
1222
|
+
*/
|
|
1223
|
+
readonly runtime?: boolean | {
|
|
1224
|
+
readonly path?: string;
|
|
1225
|
+
readonly distDir?: string;
|
|
1226
|
+
readonly url?: string;
|
|
1227
|
+
};
|
|
1228
|
+
/**
|
|
1229
|
+
* Connector registry for external MCP servers. Required to accept
|
|
1230
|
+
* inbound MCP Apps push payloads (`shortcuts.mcpApps`) and for the
|
|
1231
|
+
* `/mcp-apps/resource` proxy route to resolve source-server
|
|
1232
|
+
* endpoints. Absent = inbound MCP Apps hosting disabled.
|
|
1233
|
+
*/
|
|
1234
|
+
readonly connectors?: ConnectorRegistry;
|
|
1235
|
+
/**
|
|
1236
|
+
* HMAC secret used to sign bootstrap + session tokens. When the MCP
|
|
1237
|
+
* Apps outbound path is enabled and no secret is passed, the server
|
|
1238
|
+
* mints a random 32-byte secret at boot — fine for dev + a single
|
|
1239
|
+
* long-running process, wrong for multi-host deployments (each host
|
|
1240
|
+
* would reject the others' tokens). Production operators MUST pass
|
|
1241
|
+
* a deterministic secret (typically from env / secrets manager).
|
|
1242
|
+
*
|
|
1243
|
+
* Ignored entirely when MCP Apps is disabled.
|
|
1244
|
+
*/
|
|
1245
|
+
readonly bootstrapSecret?: string;
|
|
1246
|
+
/**
|
|
1247
|
+
* Enable the pairing transport. Adds `POST /pair` (public, completes a
|
|
1248
|
+
* pairing handshake) and by default `POST /admin/pair/init` (builder-
|
|
1249
|
+
* authenticated, mints a one-shot code).
|
|
1250
|
+
*
|
|
1251
|
+
* - `false` / omitted (default): pairing routes are NOT mounted.
|
|
1252
|
+
* `GguiServer.pairingService` is `null`. The server is still a
|
|
1253
|
+
* valid MCP server; pairing is simply not part of its surface.
|
|
1254
|
+
* - `true`: enable with defaults. Constructs an
|
|
1255
|
+
* `InMemoryPairingService` and bridges its `onTokenIssued` /
|
|
1256
|
+
* `onTokenRevoked` callbacks into the configured {@link auth}
|
|
1257
|
+
* adapter. Requires the adapter to implement
|
|
1258
|
+
* `registerToken`/`unregisterToken` (see
|
|
1259
|
+
* `isTokenRegisteringAuthAdapter`); throws at construction
|
|
1260
|
+
* otherwise.
|
|
1261
|
+
* - `{ service?, serverName?, path?, adminInitPath? }`: explicit
|
|
1262
|
+
* config. When `service` is omitted, the default
|
|
1263
|
+
* `InMemoryPairingService` + auth-adapter bridge is constructed
|
|
1264
|
+
* as above. When `service` is provided, the caller owns the
|
|
1265
|
+
* service's lifecycle AND the auth bridge — `createGguiServer`
|
|
1266
|
+
* mounts the HTTP routes only.
|
|
1267
|
+
*
|
|
1268
|
+
* Pairing-minted tokens authenticate subsequent `/mcp` and live-channel
|
|
1269
|
+
* requests through the normal bearer path — the bridge registers them
|
|
1270
|
+
* into the active AuthAdapter, NOT a parallel pairing-only store.
|
|
1271
|
+
*/
|
|
1272
|
+
/**
|
|
1273
|
+
* Enable the persistent-chat HTTP transport. Mounts six routes under
|
|
1274
|
+
* the configured path (defaults to `/threads`):
|
|
1275
|
+
*
|
|
1276
|
+
* POST /threads — createThread
|
|
1277
|
+
* GET /threads — listThreads
|
|
1278
|
+
* GET /threads/:id — getThread
|
|
1279
|
+
* PATCH /threads/:id — applyThreadAction
|
|
1280
|
+
* GET /threads/:id/messages — listMessages
|
|
1281
|
+
* POST /threads/:id/messages — appendMessage
|
|
1282
|
+
*
|
|
1283
|
+
* - Omitted / undefined (default): no thread routes. The server is
|
|
1284
|
+
* still a valid MCP server + optional live-channel host; persistent
|
|
1285
|
+
* chat simply isn't part of its surface.
|
|
1286
|
+
* - `{ store: ThreadStore }`: enable with the supplied store. OSS
|
|
1287
|
+
* dev callers pass `new InMemoryThreadStore()`. SQLite binding
|
|
1288
|
+
* (Step 6 of the slice) plugs in the same way.
|
|
1289
|
+
* - Extra fields (`path`, `ownerFromIdentity`) are power-user
|
|
1290
|
+
* overrides — sensible defaults otherwise.
|
|
1291
|
+
*
|
|
1292
|
+
* SSE observe endpoint (`GET /threads/:id/stream`) is Step 5 of the
|
|
1293
|
+
* same slice; it lands behind this option but in a separate route.
|
|
1294
|
+
*/
|
|
1295
|
+
readonly threads?: {
|
|
1296
|
+
readonly store: ThreadStore;
|
|
1297
|
+
/**
|
|
1298
|
+
* URL prefix. Defaults to `/threads`. Operators who already have
|
|
1299
|
+
* another server mounted under `/threads` override here.
|
|
1300
|
+
*/
|
|
1301
|
+
readonly path?: string;
|
|
1302
|
+
/**
|
|
1303
|
+
* Identity → ownerId mapping override. Defaults to
|
|
1304
|
+
* `defaultThreadOwnerFromIdentity` from `thread-transport.ts`:
|
|
1305
|
+
* pairing metadata → `paired_<pairingId>`; cognito → `cognito_<sub>`;
|
|
1306
|
+
* kind=user → `user_<workspaceId ?? userId>`; everything else →
|
|
1307
|
+
* `DEFAULT_BUILDER_OWNER_ID` ("builder").
|
|
1308
|
+
*/
|
|
1309
|
+
readonly ownerFromIdentity?: ThreadOwnerResolver;
|
|
1310
|
+
/**
|
|
1311
|
+
* Durability advertisement for the thread store. Surfaced on
|
|
1312
|
+
* `GET /ggui/health` under `threads.durability` so Portal + other
|
|
1313
|
+
* clients can decide whether to display a non-durable caveat.
|
|
1314
|
+
*
|
|
1315
|
+
* - `'durable'`: data survives server restart. `ggui serve`
|
|
1316
|
+
* resolves this automatically when `storage.threads.driver ===
|
|
1317
|
+
* 'sqlite'` is declared in `ggui.json`.
|
|
1318
|
+
* - `'ephemeral'` (default): in-memory or otherwise lost on
|
|
1319
|
+
* restart. Safe default — overclaiming durability would mislead
|
|
1320
|
+
* Portal into hiding its caveat.
|
|
1321
|
+
*
|
|
1322
|
+
* Embedded hosts that supply a custom `store` also supply the
|
|
1323
|
+
* right durability claim; the server doesn't inspect the store
|
|
1324
|
+
* instance to guess.
|
|
1325
|
+
*/
|
|
1326
|
+
readonly durability?: 'durable' | 'ephemeral';
|
|
1327
|
+
};
|
|
1328
|
+
readonly pairing?: boolean | {
|
|
1329
|
+
/**
|
|
1330
|
+
* Custom PairingService implementation. When present, the
|
|
1331
|
+
* caller owns the service's full lifecycle including any
|
|
1332
|
+
* `onTokenIssued` / `onTokenRevoked` bridging into their auth
|
|
1333
|
+
* adapter. When absent, a default `InMemoryPairingService`
|
|
1334
|
+
* is constructed and bridged automatically.
|
|
1335
|
+
*/
|
|
1336
|
+
readonly service?: PairingService;
|
|
1337
|
+
/**
|
|
1338
|
+
* Server display name the default `InMemoryPairingService`
|
|
1339
|
+
* surfaces in `PairingInit.serverName` and `PairingCompletion
|
|
1340
|
+
* .serverName`. Defaults to `info.name`. Ignored when
|
|
1341
|
+
* {@link service} is provided.
|
|
1342
|
+
*/
|
|
1343
|
+
readonly serverName?: string;
|
|
1344
|
+
/**
|
|
1345
|
+
* When set, the default `InMemoryPairingService` persists
|
|
1346
|
+
* its pairings + idCounter to this JSON file (atomic write,
|
|
1347
|
+
* `0600` perms) and restores them on subsequent boots —
|
|
1348
|
+
* tokens survive a `ggui serve` restart. Tokens are stored in
|
|
1349
|
+
* **plaintext**: assume the file lives on operator-controlled
|
|
1350
|
+
* disk (e.g. `~/.ggui/keys.json`). For multi-operator or
|
|
1351
|
+
* untrusted-host deployments swap to a hashed adapter.
|
|
1352
|
+
* Ignored when {@link service} is provided.
|
|
1353
|
+
*/
|
|
1354
|
+
readonly persistencePath?: string;
|
|
1355
|
+
/**
|
|
1356
|
+
* URL path the `POST /pair` route is mounted at. Defaults to
|
|
1357
|
+
* `/pair`.
|
|
1358
|
+
*/
|
|
1359
|
+
readonly path?: string;
|
|
1360
|
+
/**
|
|
1361
|
+
* URL path the `POST /admin/pair/init` route is mounted at.
|
|
1362
|
+
* Defaults to `/admin/pair/init`. Pass `null` to disable the
|
|
1363
|
+
* HTTP-triggered mint path — embedded hosts that call
|
|
1364
|
+
* `GguiServer.pairingService.initPairing()` programmatically
|
|
1365
|
+
* may not want the route.
|
|
1366
|
+
*/
|
|
1367
|
+
readonly adminInitPath?: string | null;
|
|
1368
|
+
/**
|
|
1369
|
+
* URL-template the `POST /admin/pair/:pairingId/revoke` route
|
|
1370
|
+
* is mounted at. Defaults to `/admin/pair/:pairingId/revoke`.
|
|
1371
|
+
* Pass `null` to disable the HTTP-triggered revoke path —
|
|
1372
|
+
* embedded hosts that call `GguiServer.pairingService
|
|
1373
|
+
* .revokePairing()` programmatically may not want the route.
|
|
1374
|
+
*/
|
|
1375
|
+
readonly adminRevokePath?: string | null;
|
|
1376
|
+
};
|
|
1377
|
+
/**
|
|
1378
|
+
* Operational / product-signal sink. Bound once at composition;
|
|
1379
|
+
* transports + handlers call `emit` for lossy counts / durations.
|
|
1380
|
+
* Defaults to {@link NoopTelemetrySink} — an OSS deployment that
|
|
1381
|
+
* doesn't care about metrics sees zero-cost no-op. Real adapters
|
|
1382
|
+
* (OTLP, CloudWatch, Datadog) plug in here.
|
|
1383
|
+
*
|
|
1384
|
+
* Sync, fire-and-forget, MUST NOT throw — see `TelemetrySink`.
|
|
1385
|
+
*/
|
|
1386
|
+
readonly telemetry?: TelemetrySink;
|
|
1387
|
+
/**
|
|
1388
|
+
* Durable audit-log sink for privileged actions (pairing-token
|
|
1389
|
+
* lifecycle today; API-key lifecycle + admin mutations follow in
|
|
1390
|
+
* later slices). Bound once at composition; ingress points await
|
|
1391
|
+
* `record` and surface failure.
|
|
1392
|
+
*
|
|
1393
|
+
* Defaults to {@link NoopAuditSink} with a boot-time `warn` log —
|
|
1394
|
+
* same pattern as the missing-auth-adapter warning. Production
|
|
1395
|
+
* deployments MUST bind a durable implementation (DynamoDB /
|
|
1396
|
+
* Postgres journal / Kafka topic) because privileged actions
|
|
1397
|
+
* leaving no record is a compliance breach.
|
|
1398
|
+
*/
|
|
1399
|
+
readonly audit?: AuditSink;
|
|
1400
|
+
/**
|
|
1401
|
+
* Admission-control limiter applied at the highest-cost handler
|
|
1402
|
+
* ingress — today just `ggui_push`. Defaults to
|
|
1403
|
+
* {@link NoopRateLimiter} (always allows). Per-handler wiring maps
|
|
1404
|
+
* denials from a `RateLimitedError` to HTTP 429 + `Retry-After` /
|
|
1405
|
+
* `X-RateLimit-*` headers at the transport boundary.
|
|
1406
|
+
*
|
|
1407
|
+
* For real policy (per-app or per-identity windows), bind a
|
|
1408
|
+
* `FixedWindowRateLimiter` over a durable
|
|
1409
|
+
* {@link import('@ggui-ai/mcp-server-core').QuotaStore} — or any
|
|
1410
|
+
* adapter that implements the same contract. Handlers never see the
|
|
1411
|
+
* policy shape; they just call `check` and honor the decision.
|
|
1412
|
+
*
|
|
1413
|
+
* Wiring only the highest-cost handler is intentional. Other
|
|
1414
|
+
* handlers (blueprint search, thread reads, pairing) follow as
|
|
1415
|
+
* individual slices when real policy signal demands it.
|
|
1416
|
+
*/
|
|
1417
|
+
readonly rateLimiter?: RateLimiter;
|
|
1418
|
+
/**
|
|
1419
|
+
* CSRF secret used to HMAC-sign double-submit tokens for browser
|
|
1420
|
+
* POST/PUT/DELETE/PATCH endpoints. Production deployments pass a
|
|
1421
|
+
* stable value (so tokens survive a deploy); OSS dev defaults to a
|
|
1422
|
+
* fresh per-process random — pre-restart tokens won't validate
|
|
1423
|
+
* after restart, which is acceptable for dev where sessions don't
|
|
1424
|
+
* survive a restart anyway.
|
|
1425
|
+
*/
|
|
1426
|
+
readonly csrfSecret?: string;
|
|
1427
|
+
/**
|
|
1428
|
+
* Trust the `X-Forwarded-For` header for per-IP rate limiting on
|
|
1429
|
+
* `/pair`. Operators behind a reverse proxy / load
|
|
1430
|
+
* balancer that strips and re-attaches a trusted client-IP header
|
|
1431
|
+
* pass `true`; localhost-only dev paths leave it falsy so requests
|
|
1432
|
+
* key on `req.socket.remoteAddress` instead.
|
|
1433
|
+
*/
|
|
1434
|
+
readonly trustProxy?: boolean;
|
|
1435
|
+
/**
|
|
1436
|
+
* Public base URL the server is reachable at — REQUIRED for OAuth
|
|
1437
|
+
* login routes. Composes the `redirect_uri` registered
|
|
1438
|
+
* with each OAuth provider's console as
|
|
1439
|
+
* `${publicBaseUrl}/ggui/oauth-login/<providerId>/callback`. If
|
|
1440
|
+
* absent, OAuth login routes are NOT mounted (admin transport
|
|
1441
|
+
* still mounts so operators can paste credentials in advance).
|
|
1442
|
+
*/
|
|
1443
|
+
readonly publicBaseUrl?: string;
|
|
1444
|
+
/**
|
|
1445
|
+
* Render-URL signing config — capability-URL hardening.
|
|
1446
|
+
*
|
|
1447
|
+
* When set (default), every minted `/r/<code>` URL carries
|
|
1448
|
+
* `?sig=<hmac>&exp=<unix>`. The gate on `/r/` + `/api/bootstrap/`
|
|
1449
|
+
* verifies the sig + freshness before lookup; tampered or expired
|
|
1450
|
+
* URLs return 410.
|
|
1451
|
+
*
|
|
1452
|
+
* - `secret`: 32-byte hex key. When omitted, a fresh random key
|
|
1453
|
+
* is minted at boot — restart = every outstanding URL dies
|
|
1454
|
+
* (good for incident response, bad if you don't want it).
|
|
1455
|
+
* Pin a stable secret to survive restarts.
|
|
1456
|
+
* - `ttlSeconds`: URL lifetime. Defaults to 24h (86400s).
|
|
1457
|
+
* - Set `renderSigning: false` to disable the layer entirely
|
|
1458
|
+
* (`--no-render-signing`). URLs revert to the plain unsigned
|
|
1459
|
+
* form. Use for legacy hosts that strip query strings or
|
|
1460
|
+
* tooling that pre-records URLs.
|
|
1461
|
+
*/
|
|
1462
|
+
readonly renderSigning?: false | {
|
|
1463
|
+
readonly secret?: string;
|
|
1464
|
+
readonly ttlSeconds?: number;
|
|
1465
|
+
};
|
|
1466
|
+
/**
|
|
1467
|
+
* Per-shortCode rate-limit config. Defends `/r/` and
|
|
1468
|
+
* `/api/bootstrap/` against brute-force scans.
|
|
1469
|
+
*
|
|
1470
|
+
* - `windowSeconds` + `limit`: bucket size. Default 60s / 30 hits.
|
|
1471
|
+
* - `false`: disable rate limiting entirely (CI/test convenience).
|
|
1472
|
+
*
|
|
1473
|
+
* Rate-limit decision is per-shortCode (not per-peer) — cross-origin
|
|
1474
|
+
* iframe hosts NAT every user through one proxy; per-peer limits
|
|
1475
|
+
* would either rate-limit the whole host or accept every peer.
|
|
1476
|
+
* See render-rate-limit.ts for the rationale.
|
|
1477
|
+
*/
|
|
1478
|
+
readonly renderRateLimit?: false | {
|
|
1479
|
+
readonly windowSeconds?: number;
|
|
1480
|
+
readonly limit?: number;
|
|
1481
|
+
};
|
|
1482
|
+
/**
|
|
1483
|
+
* Override path for `~/.ggui/oauth-providers.json`. Mainly for
|
|
1484
|
+
* tests + per-deployment isolation. Defaults to the home-relative
|
|
1485
|
+
* path resolved by `createOAuthProvidersStore`.
|
|
1486
|
+
*/
|
|
1487
|
+
readonly oauthProvidersPath?: string;
|
|
1488
|
+
/**
|
|
1489
|
+
* Server-level instructions surfaced on the MCP `InitializeResult.
|
|
1490
|
+
* instructions` field. MCP hosts (Claude.ai web, Claude Desktop,
|
|
1491
|
+
* the MCP Inspector) inject this into the LLM's system prompt as a
|
|
1492
|
+
* top-level block, ABOVE per-tool descriptions — influencing
|
|
1493
|
+
* "how should I behave with this server's tools generally?"
|
|
1494
|
+
*
|
|
1495
|
+
* - Omit (`undefined`): use the package default (`'default'`
|
|
1496
|
+
* preset — sensible "ggui first when UI fits" nudge).
|
|
1497
|
+
* - Preset name: `'default' | 'aggressive' | 'minimal' | 'off'`.
|
|
1498
|
+
* `'aggressive'` matches a manual "always use ggui_*" custom
|
|
1499
|
+
* instruction. `'off'` omits the field entirely.
|
|
1500
|
+
* - Arbitrary string: used verbatim. Lets operators write
|
|
1501
|
+
* deployment-specific copy without forking the package.
|
|
1502
|
+
*
|
|
1503
|
+
* Since OSS forks can edit `instructions-presets.ts` directly, the
|
|
1504
|
+
* preset enum is a convenience dial, not a contract — devs are
|
|
1505
|
+
* welcome to ship custom strings or tweak the presets to match
|
|
1506
|
+
* their fleet's voice.
|
|
1507
|
+
*/
|
|
1508
|
+
readonly mcpInstructions?: McpInstructionsValue;
|
|
1509
|
+
/**
|
|
1510
|
+
* Email magic-link login config. When set, mounts
|
|
1511
|
+
* `POST /ggui/email-login/start`, `GET /ggui/email-login/verify`,
|
|
1512
|
+
* and `GET /ggui/email-login/config` so the `/login` UI can offer
|
|
1513
|
+
* a passwordless email path. Requires `publicBaseUrl` so the magic
|
|
1514
|
+
* link the user clicks resolves back to this server.
|
|
1515
|
+
*
|
|
1516
|
+
* - `false` / omitted: no email login routes mounted. `/login`
|
|
1517
|
+
* fetches `/ggui/email-login/config` → 404 → hides the form.
|
|
1518
|
+
* - `{ sender, fromAddress, ... }`: opt-in. The `sender` is the
|
|
1519
|
+
* transport (use `ConsoleEmailSender` for dev; SMTP / Resend /
|
|
1520
|
+
* SES adapters for production). `fromAddress` is stamped on
|
|
1521
|
+
* every outgoing message.
|
|
1522
|
+
*
|
|
1523
|
+
* Authentication: callbacks mint
|
|
1524
|
+
* `{ kind: 'user', userId: 'email:<lowercased-email>', roles: [] }`
|
|
1525
|
+
* via `auth.registerToken`. The configured `auth` adapter MUST
|
|
1526
|
+
* support `registerToken` — pairing-incompatible adapters
|
|
1527
|
+
* (Cognito/OIDC) can't accept email login.
|
|
1528
|
+
*/
|
|
1529
|
+
readonly emailLogin?: {
|
|
1530
|
+
readonly sender: EmailSender;
|
|
1531
|
+
readonly fromAddress: string;
|
|
1532
|
+
readonly store?: MagicLinkStore;
|
|
1533
|
+
readonly subject?: string;
|
|
1534
|
+
readonly bodyText?: string;
|
|
1535
|
+
readonly bodyHtml?: string;
|
|
1536
|
+
};
|
|
1537
|
+
/**
|
|
1538
|
+
* Enable the `@ggui-ai/console` operator landing page. When
|
|
1539
|
+
* enabled, the server:
|
|
1540
|
+
*
|
|
1541
|
+
* - Mounts `GET /ggui/console/info` — returns
|
|
1542
|
+
* `{ server, version, description?, pairing: { enabled, pending } }`
|
|
1543
|
+
* as JSON. Consumed by the landing-page SPA on first load.
|
|
1544
|
+
* - Mounts `express.static` at the configured `path` (default `/`)
|
|
1545
|
+
* pointing at the console's built `dist/`.
|
|
1546
|
+
*
|
|
1547
|
+
* Boundary lock:
|
|
1548
|
+
*
|
|
1549
|
+
* - Same-origin ONLY — not a Portal replacement, not an MCP Apps
|
|
1550
|
+
* iframe shell.
|
|
1551
|
+
* - No same-origin cookie, no session viewer, no WebSocket
|
|
1552
|
+
* wiring yet.
|
|
1553
|
+
*
|
|
1554
|
+
* Options:
|
|
1555
|
+
*
|
|
1556
|
+
* - `false` / omitted (default): no console mount. The server
|
|
1557
|
+
* is identical to the pre-console surface.
|
|
1558
|
+
* - `true`: mount at `/` with the package's built-in `dist/`.
|
|
1559
|
+
* - `{ path?, distDir? }`: override the URL path (`path`) and/or
|
|
1560
|
+
* the filesystem dir that Express serves (`distDir`). `distDir`
|
|
1561
|
+
* is primarily a test-fixture seam — production should leave it
|
|
1562
|
+
* unset so the package-shipped bundle is served.
|
|
1563
|
+
*
|
|
1564
|
+
* If the resolved `distDir` does not exist on disk when the route is
|
|
1565
|
+
* hit, the server responds with 503 + a clear hint pointing operators
|
|
1566
|
+
* at `pnpm --filter @ggui-ai/console build`. Silent 404 would be
|
|
1567
|
+
* a worse failure mode — operators would think console was
|
|
1568
|
+
* broken rather than unbuilt.
|
|
1569
|
+
*/
|
|
1570
|
+
readonly console?: boolean | {
|
|
1571
|
+
/** URL path to mount at. Defaults to `/`. */
|
|
1572
|
+
readonly path?: string;
|
|
1573
|
+
/**
|
|
1574
|
+
* Override the filesystem dir Express serves. Defaults to the
|
|
1575
|
+
* package-shipped `dist/` (`CONSOLE_DIST_DIR`). Primarily a
|
|
1576
|
+
* test-fixture seam.
|
|
1577
|
+
*/
|
|
1578
|
+
readonly distDir?: string;
|
|
1579
|
+
/**
|
|
1580
|
+
* Enable the Slice-2 same-origin session-cookie flow
|
|
1581
|
+
* (`POST /ggui/console/session-cookie` + session-channel
|
|
1582
|
+
* cookie-auth wiring). Defaults to OFF — the landing-page
|
|
1583
|
+
* static surface is useful on its own (pair-code display,
|
|
1584
|
+
* server identity); turning on the cookie flow is an
|
|
1585
|
+
* explicit step that pulls in additional deps.
|
|
1586
|
+
*
|
|
1587
|
+
* Enabling REQUIRES `sessionChannel: true` — the cookie only
|
|
1588
|
+
* authenticates the live-channel WebSocket upgrade, so a cookie
|
|
1589
|
+
* flow without a channel to use it on would be pointless +
|
|
1590
|
+
* confusing. Throws at construction if that invariant fails.
|
|
1591
|
+
*
|
|
1592
|
+
* Enabling REQUIRES a configured {@link shortCodeIndex} — the
|
|
1593
|
+
* cookie endpoint resolves shortCode → sessionId by reading
|
|
1594
|
+
* it. Throws at construction if the index is absent.
|
|
1595
|
+
*
|
|
1596
|
+
* The cookie signing secret is the same {@link bootstrapSecret}
|
|
1597
|
+
* used by the MCP Apps bootstrap/session tokens — different
|
|
1598
|
+
* token `kind` claims make cross-kind confusion impossible
|
|
1599
|
+
* (see `console-auth.ts` isolation comment).
|
|
1600
|
+
*/
|
|
1601
|
+
readonly sessionCookie?: boolean | {
|
|
1602
|
+
/**
|
|
1603
|
+
* Cookie TTL in seconds. Defaults to 8 hours
|
|
1604
|
+
* (`DEFAULT_DEVTOOL_SESSION_TTL_SEC`).
|
|
1605
|
+
*/
|
|
1606
|
+
readonly ttlSec?: number;
|
|
1607
|
+
/**
|
|
1608
|
+
* Add `Secure` to the Set-Cookie attributes. Explicit
|
|
1609
|
+
* because auto-detecting TLS through a reverse proxy
|
|
1610
|
+
* is unreliable — operators passing `true` when their
|
|
1611
|
+
* public URL is HTTPS is the safe contract.
|
|
1612
|
+
*/
|
|
1613
|
+
readonly secure?: boolean;
|
|
1614
|
+
};
|
|
1615
|
+
/**
|
|
1616
|
+
* Admin bearer that gates the operator-only console routes
|
|
1617
|
+
* (`/ggui/console/keys*`, `/ggui/console/admin-login`). When
|
|
1618
|
+
* absent, `createGguiServer` mints `ggui_admin_<base64url(9)>`
|
|
1619
|
+
* at boot — surfaced on {@link GguiServer.adminToken} so the
|
|
1620
|
+
* CLI banner can print it. Operator passes `--admin-token <t>`
|
|
1621
|
+
* to pin a stable value across restarts.
|
|
1622
|
+
*
|
|
1623
|
+
* The gate accepts either an `Authorization: Bearer <token>`
|
|
1624
|
+
* header OR the `ggui_console_admin` cookie set by the
|
|
1625
|
+
* admin-login route. Other console routes (registry, sessions,
|
|
1626
|
+
* cached blueprints, …) are NOT gated by this token — that's
|
|
1627
|
+
* a separate audit slice. The keys plane is the immediate
|
|
1628
|
+
* threat: plaintext bearer rendering + mint + revoke must not
|
|
1629
|
+
* be reachable to anyone who finds the URL over a tunnel.
|
|
1630
|
+
*/
|
|
1631
|
+
readonly adminToken?: string;
|
|
1632
|
+
/**
|
|
1633
|
+
* Onboarding-redirect probe. Called per `GET /` request; if
|
|
1634
|
+
* it returns a non-null path, the server responds 302 to that
|
|
1635
|
+
* path instead of the SPA index. Use this to send first-run
|
|
1636
|
+
* operators (no LLM credentials configured) to the assistant-
|
|
1637
|
+
* connection flow before the chat playground is meaningful.
|
|
1638
|
+
*
|
|
1639
|
+
* The probe is recomputed every request — once the operator
|
|
1640
|
+
* sets a key, the next visit serves the SPA normally. Scoped
|
|
1641
|
+
* to the root path only; deep links bypass the redirect so
|
|
1642
|
+
* `/preview/<id>` / `/blueprints` / etc. continue to work.
|
|
1643
|
+
*
|
|
1644
|
+
* Return `null` to fall through to the SPA. Returning the
|
|
1645
|
+
* same path the request is already on is a no-op (the server
|
|
1646
|
+
* compares before redirecting to avoid loops).
|
|
1647
|
+
*/
|
|
1648
|
+
readonly landingRedirect?: () => string | null;
|
|
1649
|
+
};
|
|
1650
|
+
/**
|
|
1651
|
+
* Public welcome page served at the console root (`/`) when the
|
|
1652
|
+
* console is mounted there.
|
|
1653
|
+
*
|
|
1654
|
+
* Resolved from `ggui.json#operator` + `ggui.json#app.name` by the
|
|
1655
|
+
* `ggui serve` CLI; programmatic embedders pass whatever values
|
|
1656
|
+
* fit their context. The page identifies who runs the server
|
|
1657
|
+
* (operator block — hidden entirely when nothing is configured)
|
|
1658
|
+
* and links to the public deep-link surfaces (`/preview/<id>`,
|
|
1659
|
+
* `/s/<shortCode>`) plus an "Operator login →" affordance pointing
|
|
1660
|
+
* at `/admin-login`.
|
|
1661
|
+
*
|
|
1662
|
+
* Posture: this page is the ONLY unauthenticated SPA-mount HTML
|
|
1663
|
+
* surface alongside `/admin-login`. Every other client-side route
|
|
1664
|
+
* (`/admin/*`, `/devtools/*`) requires the admin cookie/bearer.
|
|
1665
|
+
*
|
|
1666
|
+
* Omitted = the legacy SPA index handler runs at `/` (no welcome
|
|
1667
|
+
* page; the SPA handles its own root-route render).
|
|
1668
|
+
*
|
|
1669
|
+
* Ignored when `console.path !== '/'` — operators mounting console
|
|
1670
|
+
* on a non-root prefix already opted out of the welcome page surface.
|
|
1671
|
+
*/
|
|
1672
|
+
readonly welcomePage?: {
|
|
1673
|
+
/** Operator-block input. Hidden entirely when omitted/empty. */
|
|
1674
|
+
readonly operator?: OperatorConfig;
|
|
1675
|
+
/**
|
|
1676
|
+
* Display name for the running app (typically `ggui.json#app.name`).
|
|
1677
|
+
* Falls back to the server identity name when omitted.
|
|
1678
|
+
*/
|
|
1679
|
+
readonly appName?: string;
|
|
1680
|
+
};
|
|
1681
|
+
/**
|
|
1682
|
+
* Index for resolving `shortCode → { sessionId, appId }`. Required
|
|
1683
|
+
* when `console.sessionCookie` is enabled (the cookie endpoint
|
|
1684
|
+
* looks up the posted shortCode to find the session to bind).
|
|
1685
|
+
*
|
|
1686
|
+
* Pair this with a `push` handler so the agent's `ggui_push` writes
|
|
1687
|
+
* the shortCode into the same index that console later reads.
|
|
1688
|
+
* See `defaultHandlers` for the wiring seam.
|
|
1689
|
+
*/
|
|
1690
|
+
readonly shortCodeIndex?: ShortCodeIndex;
|
|
1691
|
+
/**
|
|
1692
|
+
* Content-addressable code blob storage. When wired, this server
|
|
1693
|
+
* mounts `GET /code/<hash>.js` for the iframe runtime to fetch
|
|
1694
|
+
* compiled componentCode by content hash. The push handler writes
|
|
1695
|
+
* to the store before emitting `_meta.ggui.bootstrap.codeUrl`.
|
|
1696
|
+
*
|
|
1697
|
+
* Defaults: when omitted the route is NOT mounted; the push
|
|
1698
|
+
* handler falls back to inline base64 `componentCode` on
|
|
1699
|
+
* `_meta.ggui.bootstrap` (legacy delivery channel).
|
|
1700
|
+
*
|
|
1701
|
+
* OSS dev wires `FileSystemCodeStore` (rooted at `~/.ggui/code-cache/`)
|
|
1702
|
+
* via `ggui-cli/buildMcpServerBackend`. Tests wire
|
|
1703
|
+
* `InMemoryCodeStore` from `@ggui-ai/mcp-server-core/in-memory`.
|
|
1704
|
+
* A hosted closed runtime wires a durable adapter (e.g. S3-backed)
|
|
1705
|
+
* from its own closed-source package. The wire format is identical
|
|
1706
|
+
* across deployments — only the storage adapter changes.
|
|
1707
|
+
*/
|
|
1708
|
+
readonly codeStore?: CodeStore;
|
|
1709
|
+
/**
|
|
1710
|
+
* Provisional A2UI preview wiring for `ggui_push`. When the config
|
|
1711
|
+
* flag is on, every qualifying component push kicks off the
|
|
1712
|
+
* supplied emitter; frames land on the reserved `_ggui:preview`
|
|
1713
|
+
* channel of the push's session.
|
|
1714
|
+
*
|
|
1715
|
+
* The server owns the `sendEnvelope` + registry plumbing — only
|
|
1716
|
+
* the emitter + flag + optional observers are caller-facing.
|
|
1717
|
+
*
|
|
1718
|
+
* Requires `sessionChannel: true` + `mcpApps` enabled (preview
|
|
1719
|
+
* needs a channel to emit on AND a push handler to attach to).
|
|
1720
|
+
* When the flag is on without those, `createGguiServer` throws —
|
|
1721
|
+
* silent drop would make "I enabled preview and nothing fires"
|
|
1722
|
+
* look like a generation bug instead of a wiring bug.
|
|
1723
|
+
*
|
|
1724
|
+
* `ggui-cli`'s `buildMcpServerBackend` passes the deterministic
|
|
1725
|
+
* emitter from `@ggui-ai/preview-a2ui/emitters` as the OSS
|
|
1726
|
+
* default; hosted + programmatic hosts inject their own.
|
|
1727
|
+
*/
|
|
1728
|
+
readonly provisionalPreview?: {
|
|
1729
|
+
/** Global kill-switch. Default `false` (no preview fan-out). */
|
|
1730
|
+
readonly enabled: boolean;
|
|
1731
|
+
/**
|
|
1732
|
+
* Caller-supplied producer. Absent = no preview even when
|
|
1733
|
+
* `enabled` is true (guardrail: hosts opting in must be
|
|
1734
|
+
* explicit about the producer).
|
|
1735
|
+
*/
|
|
1736
|
+
readonly emitter: ProvisionalPreviewEmitter;
|
|
1737
|
+
/** Per-push predicate. See {@link ProvisionalPreviewConfig}. */
|
|
1738
|
+
readonly isEnabledFor?: ProvisionalPreviewConfig['isEnabledFor'];
|
|
1739
|
+
/** Lifecycle observer. Fires sync — must not throw. */
|
|
1740
|
+
readonly onOutcome?: (outcome: ProvisionalPreviewOutcome) => void;
|
|
1741
|
+
/** Clock override for tests. Defaults to `Date.now`. */
|
|
1742
|
+
readonly now?: () => number;
|
|
1743
|
+
};
|
|
1744
|
+
/**
|
|
1745
|
+
* Handshake preflight wiring. When `mcpApps` is enabled, the
|
|
1746
|
+
* server defaults to an `InMemoryKeyValueStore` + no negotiator:
|
|
1747
|
+
* `ggui_handshake` is registered, `ggui_push({handshakeId})` is
|
|
1748
|
+
* consumable, and handshake records persist for 10 minutes before
|
|
1749
|
+
* single-use consumption.
|
|
1750
|
+
*
|
|
1751
|
+
* Explicit overrides:
|
|
1752
|
+
*
|
|
1753
|
+
* - `{kvStore}` — swap the persistence backend (e.g., SQLite
|
|
1754
|
+
* when it lands) while keeping the default "no negotiator"
|
|
1755
|
+
* shape.
|
|
1756
|
+
* - `{kvStore, negotiator}` — wire a real negotiator (e.g. RAG
|
|
1757
|
+
* in a hosted closed runtime) so handshake records carry a
|
|
1758
|
+
* decision the paired push echoes as `structuredContent.decision`.
|
|
1759
|
+
* - `false` — explicitly disable: `ggui_handshake` is NOT
|
|
1760
|
+
* registered and `ggui_push({handshakeId})` falls back to
|
|
1761
|
+
* the rejection shape.
|
|
1762
|
+
*
|
|
1763
|
+
* Omitted entirely means "use the default in-memory store when
|
|
1764
|
+
* `mcpApps` is on". That matches how `sessionStore` and
|
|
1765
|
+
* `streamBuffer` default — no opt-in required for the OSS
|
|
1766
|
+
* first-run path.
|
|
1767
|
+
*
|
|
1768
|
+
* Requires `mcpApps` to be enabled — handshake is paired with
|
|
1769
|
+
* `ggui_push`, which is only registered under MCP Apps. Throws
|
|
1770
|
+
* at construction when handshake is explicitly enabled without
|
|
1771
|
+
* MCP Apps.
|
|
1772
|
+
*/
|
|
1773
|
+
readonly handshake?: false | {
|
|
1774
|
+
/**
|
|
1775
|
+
* Persistence plane. Omit to accept the default
|
|
1776
|
+
* `InMemoryKeyValueStore`.
|
|
1777
|
+
*/
|
|
1778
|
+
readonly kvStore?: KeyValueStore;
|
|
1779
|
+
/**
|
|
1780
|
+
* Optional negotiator. Omit = handshake records stamp
|
|
1781
|
+
* `action: 'create'` + no-negotiator-bound reason.
|
|
1782
|
+
*/
|
|
1783
|
+
readonly negotiator?: HandshakeNegotiator;
|
|
1784
|
+
};
|
|
1785
|
+
/**
|
|
1786
|
+
* Generation wiring for the `ggui_push` story path. When present,
|
|
1787
|
+
* every component push invokes the bound `UiGenerator` and
|
|
1788
|
+
* appends the result as a real `StackItem` on the session.
|
|
1789
|
+
* Absent = placeholder mode: `ggui_push` on the story path
|
|
1790
|
+
* returns `codeReady: false` without writing componentCode.
|
|
1791
|
+
*
|
|
1792
|
+
* Requires `mcpApps` to be enabled — generation attaches to
|
|
1793
|
+
* `ggui_push`, which is only registered when MCP Apps is on.
|
|
1794
|
+
* Throws at construction otherwise so a misconfigured server
|
|
1795
|
+
* doesn't silently drop the generator binding.
|
|
1796
|
+
*
|
|
1797
|
+
* The `@ggui-ai/ui-gen` package ships the OSS default
|
|
1798
|
+
* implementation (`createUiGenerator({adapter})`); a hosted closed
|
|
1799
|
+
* runtime supplies its own generator binding through the same seam.
|
|
1800
|
+
* BYOK resolution (env → credentials file) is the CLI layer's
|
|
1801
|
+
* concern — at this boundary the caller hands in a closure that
|
|
1802
|
+
* returns resolved credentials per push.
|
|
1803
|
+
*/
|
|
1804
|
+
readonly generation?: GenerationDeps;
|
|
1805
|
+
/**
|
|
1806
|
+
* Optional multi-generator registry. When present, exposes named
|
|
1807
|
+
* generators (e.g. `ui-gen-default-haiku-4-5`,
|
|
1808
|
+
* `ui-gen-advanced-opus-4-7`) for consumers such as the blueprint
|
|
1809
|
+
* matcher, `ggui_ops_generate_blueprint`, the LLM-driven variant
|
|
1810
|
+
* selector, the console blueprint UI, and the benchmark framework.
|
|
1811
|
+
*
|
|
1812
|
+
* When omitted, `createGguiServer` auto-seeds a registry containing
|
|
1813
|
+
* `generation.uiGenerator` (when `generation` is supplied) so
|
|
1814
|
+
* consumers observe a non-empty registry by default. When supplied,
|
|
1815
|
+
* the caller's registry is used as-is; the caller is responsible
|
|
1816
|
+
* for including their `generation.uiGenerator` if they want it
|
|
1817
|
+
* discoverable.
|
|
1818
|
+
*/
|
|
1819
|
+
readonly generators?: GeneratorRegistry;
|
|
1820
|
+
/**
|
|
1821
|
+
* Optional multi-variant blueprint store. When present, `Blueprint`
|
|
1822
|
+
* rows persist via this seam so `ggui_ops_generate_blueprint` and
|
|
1823
|
+
* push-on-cache-miss can read + write through it. When omitted,
|
|
1824
|
+
* `createGguiServer` auto-seeds an {@link InMemoryBlueprintStore}.
|
|
1825
|
+
*/
|
|
1826
|
+
readonly blueprintStore?: BlueprintStore;
|
|
1827
|
+
/**
|
|
1828
|
+
* Optional variant selector. When present, the handshake handler
|
|
1829
|
+
* calls `selectVariant(candidates)` against the candidate list
|
|
1830
|
+
* returned by `blueprintStore.list((appId, contractHash))`. When
|
|
1831
|
+
* omitted, `createGguiServer` defaults to
|
|
1832
|
+
* {@link createDeterministicBlueprintSelector} — a deterministic
|
|
1833
|
+
* fallback ladder.
|
|
1834
|
+
*
|
|
1835
|
+
* Operators MAY swap in an LLM-driven selector without touching the
|
|
1836
|
+
* handler composition.
|
|
1837
|
+
*/
|
|
1838
|
+
readonly blueprintSelector?: BlueprintSelector;
|
|
1839
|
+
/**
|
|
1840
|
+
* Optional multi-axis blueprint search. When present, the
|
|
1841
|
+
* three-step handshake reads through this seam for the
|
|
1842
|
+
* parallel-search half of step 2 (cache vs agent vs synth
|
|
1843
|
+
* routing). When omitted, `createGguiServer` auto-seeds an
|
|
1844
|
+
* `createInMemoryBlueprintSearch` against the resolved
|
|
1845
|
+
* `blueprintStore`. The auto-seeded search wires the optional
|
|
1846
|
+
* `embedding` provider when set, so cached `contractEmbedding`
|
|
1847
|
+
* fields on Blueprint rows surface in the embed axis without
|
|
1848
|
+
* additional caller wiring.
|
|
1849
|
+
*
|
|
1850
|
+
* Operators MAY swap in a vector-DB-backed search (Pinecone,
|
|
1851
|
+
* pgvector, OpenSearch) without touching downstream handlers —
|
|
1852
|
+
* the seam is the contract; the implementation is fungible.
|
|
1853
|
+
*/
|
|
1854
|
+
readonly blueprintSearch?: BlueprintSearch;
|
|
1855
|
+
/**
|
|
1856
|
+
* External tool-handler bundles aggregated onto this server's
|
|
1857
|
+
* `/mcp` surface. Every mount's handlers register alongside
|
|
1858
|
+
* ggui's native tools, so one MCP session sees both — `tools/list`
|
|
1859
|
+
* enumerates ggui-native tools plus every mount's tools, and
|
|
1860
|
+
* `tools/call` dispatches uniformly.
|
|
1861
|
+
*
|
|
1862
|
+
* Each mount is a `{ name, handlers }` bundle where `handlers` is
|
|
1863
|
+
* `SharedHandler[]` — the exact shape ggui-native handlers use.
|
|
1864
|
+
* A fixture, hosted adapter, or programmatic host builds handlers
|
|
1865
|
+
* against the same `@ggui-ai/mcp-server-handlers` seams + zod
|
|
1866
|
+
* shapes they'd use for any ggui-native tool, then passes them
|
|
1867
|
+
* here.
|
|
1868
|
+
*
|
|
1869
|
+
* Collision rules: mount tool names MUST NOT collide with a
|
|
1870
|
+
* ggui-native tool name OR with any other mount's tool name.
|
|
1871
|
+
* Composition throws on collision so misconfiguration surfaces
|
|
1872
|
+
* at server-construction time rather than as a surprising
|
|
1873
|
+
* "tools/call dispatched to the wrong handler" at runtime.
|
|
1874
|
+
*
|
|
1875
|
+
* Ignored when {@link handlers} is set — callers who pass a
|
|
1876
|
+
* custom handler list compose the final list themselves.
|
|
1877
|
+
*/
|
|
1878
|
+
readonly mcpMounts?: ReadonlyArray<McpServerMount>;
|
|
1879
|
+
/**
|
|
1880
|
+
* Isolated MCP services — each mounted at its own HTTP path with
|
|
1881
|
+
* its own tool namespace. Unlike {@link mcpMounts} (which aggregates
|
|
1882
|
+
* tools onto the shared audience-filtered routes), every entry here
|
|
1883
|
+
* becomes a self-contained MCP server reachable at `app.post(path)`.
|
|
1884
|
+
*
|
|
1885
|
+
* Use a service when the handler set is conceptually a distinct MCP
|
|
1886
|
+
* server (`mcp.ggui.ai/docs`, `mcp.ggui.ai/playground/todos`). Use a
|
|
1887
|
+
* mount when the handlers should appear alongside ggui-native tools
|
|
1888
|
+
* on the shared `/mcp` surface.
|
|
1889
|
+
*
|
|
1890
|
+
* Compose-time invariants are enforced by `validateMcpServices`:
|
|
1891
|
+
* unique non-reserved paths, non-empty handler `outputSchema`, no
|
|
1892
|
+
* `audience` tags on service handlers, no within-service tool-name
|
|
1893
|
+
* collisions. Cross-service tool-name collisions ARE allowed.
|
|
1894
|
+
*
|
|
1895
|
+
* Empty / absent → no service routes mounted, no behavior change.
|
|
1896
|
+
*/
|
|
1897
|
+
readonly mcpServices?: ReadonlyArray<McpService>;
|
|
1898
|
+
/**
|
|
1899
|
+
* Per-request resource registrars run against every fresh
|
|
1900
|
+
* `McpServer` instance, after the MCP-Apps outbound install (when
|
|
1901
|
+
* enabled) and before tool registration. The hook is the canonical
|
|
1902
|
+
* extension seam for hosts that mount cross-cutting MCP App UI
|
|
1903
|
+
* bundles (e.g. a `ui://`-scheme resource for system-level cards)
|
|
1904
|
+
* without baking the bundle's wiring into this OSS factory.
|
|
1905
|
+
*
|
|
1906
|
+
* Each registrar receives the per-request `McpServer`; misuse
|
|
1907
|
+
* (duplicate URI, malformed declaration) throws synchronously and
|
|
1908
|
+
* fails the request before tool dispatch, surfacing the
|
|
1909
|
+
* misconfiguration immediately. Idempotent in spirit — the
|
|
1910
|
+
* underlying SDK rejects duplicate registrations.
|
|
1911
|
+
*/
|
|
1912
|
+
readonly extraResources?: ReadonlyArray<(server: McpServer) => void>;
|
|
1913
|
+
/**
|
|
1914
|
+
* Per-domain dep seams for the twelve operator-class `ggui_ops_*`
|
|
1915
|
+
* handlers covering the console's apps + orgs + connector-keys +
|
|
1916
|
+
* coupon surfaces. Each domain is independently optional —
|
|
1917
|
+
* `defaultHandlers` registers a domain's tools only when its seam
|
|
1918
|
+
* is bound here. OSS deployments leave these undefined (the smaller
|
|
1919
|
+
* surface); cloud pods bind AppSync-backed adapters.
|
|
1920
|
+
*
|
|
1921
|
+
* Mirrors `creditBalance` + `creditTransactions`'s pattern — the
|
|
1922
|
+
* shared-handler layer is the same code path everywhere, and the
|
|
1923
|
+
* deps interface is the boundary between the open handler and the
|
|
1924
|
+
* deployment-specific implementation.
|
|
1925
|
+
*/
|
|
1926
|
+
readonly opsApps?: {
|
|
1927
|
+
readonly apps: AppsSource;
|
|
1928
|
+
readonly userDefaultApp: UserDefaultAppSource;
|
|
1929
|
+
};
|
|
1930
|
+
readonly opsOrgs?: {
|
|
1931
|
+
readonly orgs: OrgsSource;
|
|
1932
|
+
readonly invites: OrgInvitesSource;
|
|
1933
|
+
};
|
|
1934
|
+
readonly opsConnectorKeys?: {
|
|
1935
|
+
readonly connectorKeys: ConnectorKeysSource;
|
|
1936
|
+
};
|
|
1937
|
+
readonly opsCoupon?: {
|
|
1938
|
+
readonly coupons: CouponRedeemSource;
|
|
1939
|
+
};
|
|
1940
|
+
}
|
|
1941
|
+
export interface GguiServer {
|
|
1942
|
+
/**
|
|
1943
|
+
* The Express app. Mount it under your own parent router if you want
|
|
1944
|
+
* to add middleware, or call {@link listen} for the zero-config path.
|
|
1945
|
+
*/
|
|
1946
|
+
readonly app: Express;
|
|
1947
|
+
/**
|
|
1948
|
+
* Bind the app to a port and return the underlying `node:http` server.
|
|
1949
|
+
* Resolves once the listener is accepting connections.
|
|
1950
|
+
*/
|
|
1951
|
+
listen(port?: number, host?: string): Promise<NodeHttpServer>;
|
|
1952
|
+
/** Close every outstanding HTTP connection. Idempotent. */
|
|
1953
|
+
close(): Promise<void>;
|
|
1954
|
+
/**
|
|
1955
|
+
* Number of MCP tools registered on this server. Same value the
|
|
1956
|
+
* `GET /ggui/health` endpoint echoes. Useful for hosts (CLIs,
|
|
1957
|
+
* dashboards, tests) that want to surface a real count without
|
|
1958
|
+
* round-tripping over HTTP.
|
|
1959
|
+
*/
|
|
1960
|
+
readonly toolCount: number;
|
|
1961
|
+
/**
|
|
1962
|
+
* The OSS live-channel session endpoint, when `sessionChannel` was
|
|
1963
|
+
* enabled. `null` when disabled. Hosts can use this for
|
|
1964
|
+
* introspection (`.sessionCount`, `.subscriberCount`) or for
|
|
1965
|
+
* composition with future mutation handlers that want to fan out
|
|
1966
|
+
* via `sessionChannel.sendToSession(sessionId, data)`.
|
|
1967
|
+
*/
|
|
1968
|
+
readonly sessionChannel: SessionChannelServer | null;
|
|
1969
|
+
/**
|
|
1970
|
+
* The pairing service bound to this server, when the `pairing` option
|
|
1971
|
+
* was enabled. `null` when pairing is disabled. In-process hosts
|
|
1972
|
+
* (CLIs, embedded viewers) use this to call `initPairing()` directly
|
|
1973
|
+
* instead of POSTing to `/admin/pair/init`, and to list / revoke
|
|
1974
|
+
* pairings over a programmatic path.
|
|
1975
|
+
*/
|
|
1976
|
+
readonly pairingService: PairingService | null;
|
|
1977
|
+
/**
|
|
1978
|
+
* Primitive catalogs resolved at boot from
|
|
1979
|
+
* `ggui.json#primitives.{packages,local}`. Empty array when the
|
|
1980
|
+
* operator passed nothing (programmatic hosts) or when the
|
|
1981
|
+
* declaration resolved to an empty set. Read-only; callers that
|
|
1982
|
+
* need to mutate the catalog should rebuild the server.
|
|
1983
|
+
*
|
|
1984
|
+
* After the CLI `discoverPrimitives()` walk, every declared source
|
|
1985
|
+
* surfaces here in boot-time order (packages first, locals after).
|
|
1986
|
+
* Generator integration (threading the catalog into
|
|
1987
|
+
* `buildSystemPrompt`) consumes this field.
|
|
1988
|
+
*/
|
|
1989
|
+
readonly primitiveCatalogs: readonly DiscoveredPrimitiveCatalog[];
|
|
1990
|
+
/**
|
|
1991
|
+
* Theme resolved at boot from `ggui.json#theme`. Always populated
|
|
1992
|
+
* — the server falls back to `@ggui-ai/design`'s shipped
|
|
1993
|
+
* `lightTheme` when the caller omits `opts.theme`, so consumers
|
|
1994
|
+
* never have to null-check.
|
|
1995
|
+
*
|
|
1996
|
+
* Carries the parsed DTCG document + a pre-rendered
|
|
1997
|
+
* `:root { --ggui-*: value; }` CSS block. The server does not yet
|
|
1998
|
+
* inject this CSS into any HTTP response or console bootstrap;
|
|
1999
|
+
* downstream consumers read `server.theme.document` or
|
|
2000
|
+
* `server.theme.cssVariables` as they layer on.
|
|
2001
|
+
*/
|
|
2002
|
+
readonly theme: LoadedTheme;
|
|
2003
|
+
/**
|
|
2004
|
+
* Admin bearer that gates the operator-only console routes
|
|
2005
|
+
* (`/ggui/console/keys*` + `/ggui/console/admin-login`). Either the
|
|
2006
|
+
* value the caller supplied via {@link CreateGguiServerOptions.console}
|
|
2007
|
+
* `.adminToken`, or a freshly minted `ggui_admin_*` token when the
|
|
2008
|
+
* caller didn't pass one. `null` when console is disabled (the gate
|
|
2009
|
+
* has no consumer).
|
|
2010
|
+
*
|
|
2011
|
+
* The CLI banner reads this and prints it next to PAIR_CODE so the
|
|
2012
|
+
* operator can paste it into the admin-login page on their first
|
|
2013
|
+
* visit to `/keys`.
|
|
2014
|
+
*/
|
|
2015
|
+
readonly adminToken: string | null;
|
|
2016
|
+
/**
|
|
2017
|
+
* The composed generator registry. When the caller passed
|
|
2018
|
+
* `opts.generators`, this is that exact registry. Otherwise, when
|
|
2019
|
+
* `opts.generation` was supplied, this is an auto-seeded registry
|
|
2020
|
+
* containing `generation.uiGenerator` under its declared slug.
|
|
2021
|
+
* `null` when neither was supplied (no generators to expose).
|
|
2022
|
+
*
|
|
2023
|
+
* This field is a seam — consumers read it for blueprint matcher
|
|
2024
|
+
* dispatch, `ggui_ops_generate_blueprint`, the LLM-driven variant
|
|
2025
|
+
* selector, the console blueprint UI, and the benchmark framework.
|
|
2026
|
+
*/
|
|
2027
|
+
readonly generators: GeneratorRegistry | null;
|
|
2028
|
+
/**
|
|
2029
|
+
* The composed multi-variant blueprint store. When the caller
|
|
2030
|
+
* passed `opts.blueprintStore`, this is that exact instance.
|
|
2031
|
+
* Otherwise an auto-seeded {@link InMemoryBlueprintStore}.
|
|
2032
|
+
*/
|
|
2033
|
+
readonly blueprintStore: BlueprintStore;
|
|
2034
|
+
/**
|
|
2035
|
+
* The composed variant selector. When the caller passed
|
|
2036
|
+
* `opts.blueprintSelector`, this is that exact instance. Otherwise
|
|
2037
|
+
* {@link createDeterministicBlueprintSelector} — a deterministic
|
|
2038
|
+
* fallback ladder. Operators MAY swap in an LLM-driven selector.
|
|
2039
|
+
*/
|
|
2040
|
+
readonly blueprintSelector: BlueprintSelector;
|
|
2041
|
+
/**
|
|
2042
|
+
* The composed multi-axis blueprint search. When the caller passed
|
|
2043
|
+
* `opts.blueprintSearch`, this is that exact instance. Otherwise
|
|
2044
|
+
* `createInMemoryBlueprintSearch` against the resolved
|
|
2045
|
+
* `blueprintStore`, wiring the optional `embedding` provider if one
|
|
2046
|
+
* was supplied.
|
|
2047
|
+
*
|
|
2048
|
+
* The three-step handshake reads this for its parallel search +
|
|
2049
|
+
* validate step. Hosts MAY introspect it for cache-warm runbooks
|
|
2050
|
+
* or observability.
|
|
2051
|
+
*/
|
|
2052
|
+
readonly blueprintSearch: BlueprintSearch;
|
|
2053
|
+
}
|
|
2054
|
+
/**
|
|
2055
|
+
* Build a runnable OSS MCP server. Every option has a sensible default
|
|
2056
|
+
* so `createGguiServer()` with no arguments boots a working in-memory
|
|
2057
|
+
* server on demand.
|
|
2058
|
+
*/
|
|
2059
|
+
export declare function createGguiServer(opts?: CreateGguiServerOptions): GguiServer;
|
|
2060
|
+
//# sourceMappingURL=server.d.ts.map
|