@plitzi/sdk-shared 0.32.10 → 0.32.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @plitzi/sdk-shared
2
2
 
3
+ ## 0.32.12
4
+
5
+ ### Patch Changes
6
+
7
+ - v0.32.12
8
+ - Updated dependencies
9
+ - @plitzi/nexus@0.32.12
10
+
11
+ ## 0.32.11
12
+
13
+ ### Patch Changes
14
+
15
+ - v0.32.11
16
+ - Updated dependencies
17
+ - @plitzi/nexus@0.32.11
18
+
3
19
  ## 0.32.10
4
20
 
5
21
  ### Patch Changes
@@ -193,27 +193,61 @@ export type SSRRscConfig = {
193
193
  /** Server-side cache TTL for RSC responses in milliseconds. Defaults to 30 000. Set to 0 to disable. */
194
194
  cacheTtlMs?: number;
195
195
  };
196
- /** A structured log event the MCP server emits for each tool call and resource read, so a CONSUMER can render its
197
- * own request log (dev tooling, a dashboard, structured logging). Wire a sink via `SSRServerConfig.mcpLogger`;
198
- * without one the server prints to the console only when `MCP_DEBUG=1`. */
199
- export type McpLogEvent = {
200
- /** 'tool' for a plitzi_* tool call, 'resource' for a plitzi:// resource read. */
201
- kind: 'tool' | 'resource';
202
- /** The tool name (e.g. 'plitzi_apply') or the resource URI that was read. */
203
- name: string;
204
- /** Wall-clock duration of the handler, in milliseconds. */
196
+ /** What every log event carries, whatever layer it came from. */
197
+ type ServerLogEventBase = {
198
+ /** Wall-clock duration of the work the event describes, in milliseconds. */
205
199
  durationMs: number;
206
- /** Whether the handler completed without throwing. */
200
+ /** False when the work threw or answered an error status. */
207
201
  ok: boolean;
208
- /** The error message when `ok` is false. */
202
+ /** The error message when `ok` is false — never the request payload. */
209
203
  error?: string;
210
- /** A compact, truncated JSON summary of the tool arguments (tool events only). */
211
- argsSummary?: string;
212
204
  /** ISO-8601 timestamp of when the event was emitted. */
213
205
  timestamp: string;
214
206
  };
215
- /** A sink the consumer provides to receive every MCP {@link McpLogEvent} (see `SSRServerConfig.mcpLogger`). */
216
- export type McpLogger = (event: McpLogEvent) => void;
207
+ /** One HTTP request served by ANY Plitzi server SSR pages, RSC, plugin assets, the preview endpoint, MCP —
208
+ * emitted by the dispatcher once the request is answered, including the ones that failed or were rejected. */
209
+ export type ServerRequestLogEvent = ServerLogEventBase & {
210
+ kind: 'request';
211
+ /** Which server answered — the dispatcher label, e.g. 'SSR' or 'MCP'. */
212
+ server: string;
213
+ method: string;
214
+ /** Request path with query VALUES stripped and only the keys kept, e.g. '/search?q&page'. */
215
+ path: string;
216
+ /** The operation inside the request, when the answering stage names one — e.g. the MCP JSON-RPC method. */
217
+ operation?: string;
218
+ /** The HTTP status the server answered with. */
219
+ status: number;
220
+ /** The client the request came from, resolved through the proxies the deployment sits behind. Empty when no
221
+ * address could be determined. It IS personal data — see the note on {@link ServerLogEvent}. */
222
+ clientIp?: string;
223
+ };
224
+ /** One plitzi_* tool call inside an MCP request. Reported separately from the request because a failing tool
225
+ * still answers HTTP 200 — the failure lives in the JSON-RPC payload, so only this event exposes it. */
226
+ export type McpToolLogEvent = ServerLogEventBase & {
227
+ kind: 'tool';
228
+ /** The tool name, e.g. 'plitzi_apply'. */
229
+ name: string;
230
+ /** A compact SHAPE summary of the arguments — keys, value types and array lengths, never values. Set
231
+ * `MCP_LOG_ARGS=1` to dump the real arguments instead; they may carry user content, so it stays opt-in. */
232
+ argsSummary?: string;
233
+ };
234
+ /** One plitzi:// resource read inside an MCP request, named by the URI the request asked for. */
235
+ export type McpResourceLogEvent = ServerLogEventBase & {
236
+ kind: 'resource';
237
+ /** The resource URI that was read, e.g. 'plitzi://element/hero_1'. */
238
+ name: string;
239
+ };
240
+ /** Everything a Plitzi server reports about the work it does, as ONE stream: the HTTP requests it answers, plus
241
+ * the MCP tool calls and resource reads that happen inside them. Wire a single sink via `SSRServerConfig.logger`
242
+ * and switch on `kind` — a consumer can render it, ship it to a dashboard or drop the kinds it does not want.
243
+ *
244
+ * Payload-free by construction: no headers, cookies, tokens nor request body ever reach an event, query values
245
+ * are stripped from paths and tool arguments are reduced to their shape. Two fields are NOT anonymous and a
246
+ * consumer shipping these events must handle them accordingly: `clientIp` on a request event, and the request
247
+ * path, which is kept verbatim because it is what makes the log usable. */
248
+ export type ServerLogEvent = ServerRequestLogEvent | McpToolLogEvent | McpResourceLogEvent;
249
+ /** The sink a consumer provides to receive every {@link ServerLogEvent} (see `SSRServerConfig.logger`). */
250
+ export type ServerLogger = (event: ServerLogEvent) => void;
217
251
  export type SSRServerConfig = {
218
252
  port?: number;
219
253
  host?: string;
@@ -257,10 +291,10 @@ export type SSRServerConfig = {
257
291
  enabled?: boolean;
258
292
  path?: string;
259
293
  };
260
- /** Receives a structured {@link McpLogEvent} for every MCP tool call and resource read, so the consumer can
261
- * render its own request log (e.g. in dev mode). Without it, the MCP server logs to the console only when
262
- * `MCP_DEBUG=1`. */
263
- mcpLogger?: McpLogger;
294
+ /** Receives a {@link ServerLogEvent} for every HTTP request this server answers whatever stage answered it
295
+ * and whatever the outcome plus every MCP tool call and resource read inside those requests. Without it the
296
+ * server reports nothing per request (the MCP events still reach the console when `MCP_DEBUG=1`). */
297
+ logger?: ServerLogger;
264
298
  adapters: SSRAdapters;
265
299
  /** Draft-preview endpoint for the MCP visual-preview tools (the RENDERER side). Off unless `enabled`. */
266
300
  preview?: SSRPreviewConfig;
@@ -300,6 +334,8 @@ export type SSRServerConfig = {
300
334
  };
301
335
  /** Cache-buster appended as ?v=<assetVersion> to all default SDK asset URLs (jsPath, cssPath, react vendor). Compute from file mtime or package version at startup. */
302
336
  assetVersion?: string;
337
+ /** OAuth 2.1 authorization for the MCP server. Omit to keep the server anonymous — see {@link OAuthConfig}. */
338
+ oauth?: OAuthConfig;
303
339
  };
304
340
  export type ServerServices = {
305
341
  ssr?: boolean;
@@ -333,6 +369,96 @@ export type SSRServer = {
333
369
  readonly cache: CacheManager | null;
334
370
  readonly plugins: PluginRegistry;
335
371
  };
372
+ /** A key/value store with per-entry expiry, backing the OAuth layer's short-lived protocol state (registered
373
+ * clients, authorization codes, refresh grants). Values are opaque strings the SDK serialises itself. A
374
+ * multi-replica deployment MUST inject a shared implementation — a code minted on one replica is redeemed on
375
+ * whichever replica the token request lands on. */
376
+ export type OAuthStore = {
377
+ put: (key: string, value: string, ttlSeconds: number) => void | Promise<void>;
378
+ get: (key: string) => (string | undefined) | Promise<string | undefined>;
379
+ drop: (key: string) => void | Promise<void>;
380
+ };
381
+ /** Someone who got through {@link OAuthAdapters.authenticate}. `id` is what the other adapters key off; `label`
382
+ * is shown back on the consent screen so the user can see who they are about to grant access as. */
383
+ export type OAuthUser = {
384
+ id: string;
385
+ label: string;
386
+ };
387
+ /** One thing the user may grant the client access to — a Plitzi space, or whatever else a deployment scopes its
388
+ * tokens by. `value` is the opaque handle the SDK round-trips back to {@link OAuthAdapters.issueToken}; only the
389
+ * consumer interprets it. A deployment whose public surface is useful on its own (plitzi_render needs no space)
390
+ * can offer a target that grants nothing, so the user is never forced to pick one. */
391
+ export type OAuthGrantTarget = {
392
+ value: string;
393
+ label: string;
394
+ description?: string;
395
+ };
396
+ /** What the OAuth layer cannot resolve on its own: who the user is, what they may grant, and the bearer to mint
397
+ * for them. The SDK owns the protocol (discovery, registration, PKCE, code exchange); the consumer owns identity
398
+ * and issues a token its own `adapters.getSpaceId` will accept back. */
399
+ export type OAuthAdapters = {
400
+ /** Verify the credentials typed into the consent screen. Return undefined to re-show the form with an error —
401
+ * never throw for a wrong password. */
402
+ authenticate: (credentials: {
403
+ username: string;
404
+ password: string;
405
+ }) => Promise<OAuthUser | undefined>;
406
+ /** What this user may grant access to. An empty list ends the flow with `access_denied`. */
407
+ grantTargets: (user: OAuthUser) => Promise<OAuthGrantTarget[]>;
408
+ /** Mint the bearer the client will send on every MCP request. Return undefined to deny the grant. */
409
+ issueToken: (user: OAuthUser, target: OAuthGrantTarget) => Promise<{
410
+ token: string;
411
+ expiresInSeconds?: number;
412
+ } | undefined>;
413
+ store: OAuthStore;
414
+ };
415
+ /** What the built-in consent screen shows around the form. Ignored when `renderConsent` replaces the page. */
416
+ export type OAuthBranding = {
417
+ /** Shown as the heading, e.g. 'Plitzi'. Defaults to 'Plitzi'. */
418
+ productName?: string;
419
+ /** Absolute or same-origin URL of a logo to show above the heading. */
420
+ logoUrl?: string;
421
+ /** Extra CSS appended to the page's own, for a deployment that wants its own look without replacing the page. */
422
+ css?: string;
423
+ };
424
+ /** Everything the consent screen needs to render itself, for a deployment that replaces the built-in page. Return
425
+ * a full HTML document; the SDK serves it as-is and reads the same form fields back. */
426
+ export type OAuthConsentView = {
427
+ /** 'credentials' asks for username + password; 'target' asks which space to grant, after a successful login. */
428
+ step: 'credentials' | 'target';
429
+ /** Where the form must POST to (the authorize endpoint). */
430
+ action: string;
431
+ /** Hidden fields the form MUST round-trip verbatim, or the flow cannot be resumed. */
432
+ hidden: Record<string, string>;
433
+ /** Offered on the 'target' step only. */
434
+ targets: OAuthGrantTarget[];
435
+ /** Who logged in, on the 'target' step. */
436
+ user?: OAuthUser;
437
+ /** A message to show the user, e.g. after a failed login. */
438
+ error?: string;
439
+ branding: OAuthBranding;
440
+ };
441
+ /** OAuth 2.1 authorization for the MCP server (RFC 9728 discovery + RFC 7591 dynamic client registration +
442
+ * authorization code with PKCE). ENTIRELY OPTIONAL: without this config no endpoint is mounted, discovery keeps
443
+ * answering 404 and the server stays anonymous, which is a working setup — the public surface (handshake, tool
444
+ * and resource listing, the guide, plitzi_render) never needed a token. Configure it only to let a remote host
445
+ * that cannot send custom headers — Claude Desktop, ChatGPT — obtain a space-scoped one. */
446
+ export type OAuthConfig = {
447
+ adapters: OAuthAdapters;
448
+ /** The issuer/resource identifier published in the metadata documents. Defaults to the origin the request came
449
+ * in on, which is correct whenever the server owns its sub-domain; set it when a proxy rewrites the host. */
450
+ issuer?: string;
451
+ /** Scope names advertised and echoed back on the token. Defaults to ['plitzi']. */
452
+ scopes?: string[];
453
+ /** How long an authorization code stays redeemable, in seconds. Default 60 — codes are one-shot and redeemed
454
+ * immediately. */
455
+ codeTtlSeconds?: number;
456
+ /** How long a refresh grant lives, in seconds. Default 30 days. Set 0 to issue no refresh tokens. */
457
+ refreshTtlSeconds?: number;
458
+ branding?: OAuthBranding;
459
+ /** Replaces the built-in consent screen — return a full HTML document for the given step. */
460
+ renderConsent?: (view: OAuthConsentView) => string | Promise<string>;
461
+ };
336
462
  /** A short-TTL, one-shot store for unsaved draft offline-data behind a preview token. The SDK ships an
337
463
  * in-memory default (fine for a single replica); a multi-replica deployment injects a shared (e.g. Redis)
338
464
  * implementation so a preview URL resolves on whichever replica the browser lands on. `take` consumes the
@@ -353,3 +479,4 @@ export type SSRPreviewConfig = {
353
479
  /** Token time-to-live in milliseconds. Default 60000. */
354
480
  ttlMs?: number;
355
481
  };
482
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plitzi/sdk-shared",
3
- "version": "0.32.10",
3
+ "version": "0.32.12",
4
4
  "license": "AGPL-3.0",
5
5
  "files": [
6
6
  "dist",
@@ -1184,9 +1184,6 @@
1184
1184
  "./types/SegmentTypes": {
1185
1185
  "types": "./dist/types/SegmentTypes.d.ts"
1186
1186
  },
1187
- "./types/ServerTypes": {
1188
- "types": "./dist/types/ServerTypes.d.ts"
1189
- },
1190
1187
  "./types/SpaceTypes": {
1191
1188
  "types": "./dist/types/SpaceTypes.d.ts"
1192
1189
  },
@@ -1233,7 +1230,7 @@
1233
1230
  },
1234
1231
  "dependencies": {
1235
1232
  "@modelcontextprotocol/sdk": "^1.29.0",
1236
- "@plitzi/nexus": "0.32.10",
1233
+ "@plitzi/nexus": "0.32.12",
1237
1234
  "@plitzi/plitzi-ui": "^1.6.18",
1238
1235
  "date-fns": "^4.4.0",
1239
1236
  "date-fns-tz": "^3.2.0",