@plurnk/plurnk-mcp 3.2.1 → 3.3.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.
Files changed (53) hide show
  1. package/README.md +30 -15
  2. package/SPEC.md +193 -97
  3. package/dist/McpExecutor.d.ts +10 -1
  4. package/dist/McpExecutor.d.ts.map +1 -1
  5. package/dist/McpExecutor.js +45 -11
  6. package/dist/McpExecutor.js.map +1 -1
  7. package/dist/McpResources.d.ts +2 -1
  8. package/dist/McpResources.d.ts.map +1 -1
  9. package/dist/McpResources.js +7 -3
  10. package/dist/McpResources.js.map +1 -1
  11. package/dist/Module.d.ts.map +1 -1
  12. package/dist/Module.js +142 -43
  13. package/dist/Module.js.map +1 -1
  14. package/dist/PluginConfiguration.d.ts +8 -10
  15. package/dist/PluginConfiguration.d.ts.map +1 -1
  16. package/dist/PluginConfiguration.js +4 -4
  17. package/dist/PluginConfiguration.js.map +1 -1
  18. package/dist/capabilityMatrix.d.ts.map +1 -1
  19. package/dist/capabilityMatrix.js +1 -0
  20. package/dist/capabilityMatrix.js.map +1 -1
  21. package/dist/client.d.ts +7 -0
  22. package/dist/client.d.ts.map +1 -1
  23. package/dist/client.js +180 -83
  24. package/dist/client.js.map +1 -1
  25. package/dist/config.d.ts.map +1 -1
  26. package/dist/config.js +2 -2
  27. package/dist/config.js.map +1 -1
  28. package/dist/definition.js.map +1 -1
  29. package/dist/extensionChannel.d.ts.map +1 -1
  30. package/dist/extensionChannel.js +2 -3
  31. package/dist/extensionChannel.js.map +1 -1
  32. package/dist/inputRequired.d.ts.map +1 -1
  33. package/dist/inputRequired.js +11 -4
  34. package/dist/inputRequired.js.map +1 -1
  35. package/dist/oauth.d.ts +28 -4
  36. package/dist/oauth.d.ts.map +1 -1
  37. package/dist/oauth.js +83 -36
  38. package/dist/oauth.js.map +1 -1
  39. package/dist/protocol.d.ts +1 -1
  40. package/dist/protocol.js +1 -1
  41. package/dist/protocolHeaders.d.ts.map +1 -1
  42. package/dist/protocolHeaders.js +3 -2
  43. package/dist/protocolHeaders.js.map +1 -1
  44. package/dist/registry.d.ts.map +1 -1
  45. package/dist/registry.js +8 -3
  46. package/dist/registry.js.map +1 -1
  47. package/dist/subscriptions.js.map +1 -1
  48. package/dist/tasks.d.ts +2 -2
  49. package/dist/tasks.d.ts.map +1 -1
  50. package/dist/tasks.js +23 -26
  51. package/dist/tasks.js.map +1 -1
  52. package/docs/mcp.md +9 -9
  53. package/package.json +9 -8
package/README.md CHANGED
@@ -132,7 +132,8 @@ PLURNK_MCP_forge_ENABLED=0
132
132
 
133
133
  HTTP authorization belongs in the complete definition. Bearer tokens and
134
134
  OAuth client secrets are symbolic `${NAME}` environment references, resolved
135
- only when preparing a connection; interactive grants remain in memory.
135
+ only when preparing a connection; an interactive sign-in stays in memory until
136
+ the daemon stops.
136
137
  See [`.env.defaults`](./.env.defaults) for all controls and
137
138
  [`SPEC.md`](./SPEC.md#mcp-configuration-configuration) for the contract.
138
139
 
@@ -186,26 +187,40 @@ client.
186
187
  HTTP servers support bearer references, client credentials, and interactive
187
188
  OAuth; stdio servers read their credentials from the environment. A server that
188
189
  challenges an anonymous connection comes up `authorization-required`, without
189
- publishing partial tools. A URL-only definition is sufficient; no callback
190
- port needs to be configured. The terminal client's `/mcp oauth <alias>` binds
191
- an available loopback port and calls `workspace.mcp.oauth.begin` before opening
192
- the returned authorization URL. A fixed OAuth redirect, if configured, is
193
- respected. Supplying the
194
- callback URL explicitly also works for remote/headless use. The client submits
195
- the complete URL through `workspace.mcp.oauth.complete`. Its `202` acknowledges
196
- sign-in; tools activate at the next safe workspace boundary, without another
197
- callback submission. `list` shows their current readiness. PKCE,
190
+ publishing partial tools. One that answers its catalog anonymously and refuses a
191
+ request instead goes `authorization-required` at that refusal, and the request
192
+ fails with that code ({§mcp-request-unauthorized}); either way its alias stays
193
+ registered, out of service ({§mcp-unavailable-runtime}). A URL-only definition
194
+ is sufficient; no callback port needs to be configured. The terminal client's
195
+ `/mcp oauth <alias>` binds an available loopback port and calls
196
+ `workspace.mcp.oauth.begin` before opening the returned authorization URL. A
197
+ fixed OAuth redirect, if configured, is respected. A server that publishes no
198
+ OAuth metadata needs its authorization server named as `authorization.issuer`,
199
+ and `authorization.authorizationParams` adds provider request parameters such as
200
+ `access_type=offline` where a refresh token is issued only on request
201
+ ({§oauth-issuer}). Supplying the callback URL explicitly also works for
202
+ remote/headless use. The client submits the complete URL through
203
+ `workspace.mcp.oauth.complete`. Its `202` acknowledges sign-in; tools activate
204
+ at the next safe workspace boundary, between turns, without another callback
205
+ submission. Until then `list` and a call to the alias report the published
206
+ state, so a turn in flight still sees the alias awaiting sign-in. A sign-in
207
+ outlives its connection: the alias's later connections with the same definition
208
+ present it, refreshing it as it lapses — when an idle workspace returns, on
209
+ `enable`, or on `disable` then `enable` — until its authorization server rejects
210
+ it, another sign-in replaces it, or the daemon stops ({§oauth-grant}). `list`
211
+ shows an active alias's grant as its `detail.grant`: its issuer, the scope its
212
+ authorization server stated, and whether it refreshes, never a token. PKCE,
198
213
  issuer and resource validation, refresh, scope escalation, and credentials
199
- remain inside the host connection. Callback addresses and credentials never
200
- replace the saved definition. `enable` retries a connection; it does not replace
201
- user sign-in. Rejected configured credentials are reported as authentication
202
- failures rather than retryable server downtime.
214
+ remain inside the host. Callback addresses and credentials never replace the
215
+ saved definition. `enable` retries a connection; it does not replace user
216
+ sign-in. Rejected configured credentials are reported as authentication failures
217
+ rather than retryable server downtime.
203
218
 
204
219
  ## Verification
205
220
 
206
221
  ```sh
207
222
  npm test -w @plurnk/plurnk-mcp
208
- PLURNK_MODEL=<selector> npm run test:live:specimen -w @plurnk/plurnk-service -- "live mcp: current third-party stdio and HTTP servers compose through the assembled product"
223
+ PLURNK_MODEL=<selector> npm run test:live -- --specimen "live mcp: current third-party stdio and HTTP servers compose through the assembled product"
209
224
  ```
210
225
 
211
226
  The package gate runs the exact current SDK and official conformance
package/SPEC.md CHANGED
@@ -11,7 +11,7 @@ executor, resource, proposal, entry, Problem, and lifecycle contracts.
11
11
 
12
12
  The host's own wire authority is revision `2026-07-28`, specification commit
13
13
  `5f5440bb26a62e2cf3440b92da5a667efa03b267`. The implementation exact-pins
14
- `@modelcontextprotocol/client@2.3.1`. SDK exports are not protocol authority:
14
+ `@modelcontextprotocol/client@2.4.0`. SDK exports are not protocol authority:
15
15
  that package deliberately retains legacy and deprecated API shapes. It owns
16
16
  core negotiation and transport; this package owns only exact-pinned extension
17
17
  wire that the SDK does not yet implement.
@@ -33,6 +33,15 @@ per-connection and absent from a legacy peer.
33
33
 
34
34
  ## §mcp-core-matrix Core capability matrix
35
35
 
36
+ <!-- map -->
37
+ | Tag | Promise |
38
+ | --- | --- |
39
+ | {§mcp-catalog-convergence} | A catalog is complete or it is an error. |
40
+ | {§mcp-catalog-deadline} | `PLURNK_MCP_CONNECT_TIMEOUT` bounds connection setup and, separately, each complete catalog/list walk,… |
41
+ | {§mcp-retry-pacing} | Every retry the adapter schedules on its own — reopening a dropped subscription, refreshing a catalog a… |
42
+ | {§mcp-catalog-list-absence} | An unsupported list method does not disable the server. |
43
+ <!-- /map -->
44
+
36
45
  The accountable capability matrix lives in `capabilityMatrix.ts`
37
46
  ({§mcp-capability-matrix}). This table describes the pinned revision;
38
47
  {§mcp-authority} owns the SDK-negotiated older-peer surface.
@@ -167,6 +176,15 @@ conformance stays a separate named gate, never folded into a matrix row.
167
176
 
168
177
  ## §mcp-transports Transport bindings
169
178
 
179
+ <!-- map -->
180
+ | Tag | Promise |
181
+ | --- | --- |
182
+ | {§mcp-endpoint-security} | Configured MCP and registry endpoints accept HTTP or HTTPS, including private-network hosts. |
183
+ | {§mcp-stdio-process-ownership} | A stdio connection owns the complete process group created for its server. |
184
+ | {§mcp-redirect-refused} | An HTTP endpoint is never redirected. |
185
+ | {§mcp-header-declarations} | A tool with an invalid `x-mcp-header` declaration is never called. |
186
+ <!-- /map -->
187
+
170
188
  | Binding | Contract |
171
189
  |---|---|
172
190
  | stdio | Spawn one exact executable with an explicit argument array and no shell; newline-delimited JSON-RPC is the only stdout/stdin traffic; stderr is diagnostic; shutdown closes stdin, waits, then terminates if necessary |
@@ -194,17 +212,15 @@ case-insensitively, and body/header disagreement fails instead of guessing.
194
212
  For `tasks/get`, `tasks/update`, and `tasks/cancel`, `Mcp-Name` is the encoded
195
213
  `taskId` required by the Tasks extension.
196
214
 
197
- §mcp-header-declarations **A tool with an invalid `x-mcp-header` declaration is never called.** Before
198
- either request leg of a tool call, Tasks or direct, the tool's `inputSchema` is scanned for
199
- `x-mcp-header` declarations: each sits on a property reached through `properties` alone, never under
200
- `items`, `additionalProperties`, a combinator, a conditional, `$defs` or a `$ref` target; names a
201
- non-empty RFC 9110 token that is unique case-insensitively; and types `string`, `integer` or `boolean`,
202
- never `number` (SEP-2243). The definition the caller supplies is the one scanned; otherwise the server's
203
- listing supplies it, and a Tasks call whose listing lacks the tool fails as unlisted. A definition that
204
- breaks any of these refuses the call with `tool-header-invalid` ({§problems-mcp}) naming the tool and
205
- the declaration, and no request is sent. A direct call never waits on a failed listing
206
- ({§mcp-catalog-deadline}): with no definition at hand there is nothing to scan and no routing header to
207
- derive, so it proceeds as a plain `tools/call`.
215
+ §mcp-header-declarations **A tool with an invalid `x-mcp-header` declaration is never called.** Before either request leg of a tool call, Tasks or direct, the tool's `inputSchema` is scanned for `x-mcp-header` declarations. A definition that breaks any rule below refuses the call with `tool-header-invalid` ({§problems-mcp}) naming the tool and the declaration, and no request is sent.
216
+
217
+ | Rule | What |
218
+ | --- | --- |
219
+ | Placement | each sits on a property reached through `properties` alone, never under `items`, `additionalProperties`, a combinator, a conditional, `$defs` or a `$ref` target |
220
+ | Name | a non-empty RFC 9110 token that is unique case-insensitively |
221
+ | Type | `string`, `integer` or `boolean`, never `number` (SEP-2243) |
222
+ | The definition scanned | the one the caller supplies; otherwise the server's listing supplies it, and a Tasks call whose listing lacks the tool fails as unlisted |
223
+ | A direct call | never waits on a failed listing ({§mcp-catalog-deadline}): with no definition at hand there is nothing to scan and no routing header to derive, so it proceeds as a plain `tools/call` |
208
224
 
209
225
  ## §mcp-errors Error allocation
210
226
 
@@ -226,6 +242,27 @@ originating distinction in its canonical Problem/result path.
226
242
 
227
243
  ## §mcp-configuration Configuration
228
244
 
245
+ <!-- map -->
246
+ | Tag | Promise |
247
+ | --- | --- |
248
+ | {§mcp-file-configuration} | Standalone `mcp.json` files are read-only configuration inputs, not plugins. |
249
+ | {§mcp-plugin-configuration} | Plugin MCP components use their format's interpretation, not the native file dialect. |
250
+ | {§mcp-activation-isolation} | Cold endpoint failure is capability-local. |
251
+ | {§mcp-unavailable-runtime} | An enabled server that cannot run keeps its alias. |
252
+ | {§mcp-definitions} | A server is not a plugin installation. |
253
+ | {§mcp-launch-directory} | An implicit working directory is workspace-owned state, not the project. |
254
+ | {§mcp-summary-derivation} | Orientation prefers the server's own purpose over display labels; no capabilities are inferred. |
255
+ | {§mcp-server-settings} | Independent behavior settings do not change connection definitions. |
256
+ | {§mcp-module} | The MCP family beneath the coordinator |
257
+ | {§mcp-launch-environment} | Ambient inheritance and explicit bindings are distinct. |
258
+ | {§mcp-management-actions} | MCP is one family of workspace Functionality |
259
+ | {§mcp-registry-discovery} | `discover` searches the MCP Registry. |
260
+ | {§mcp-preparation-diagnostics} | Unavailable preparation reports observations. |
261
+ | {§oauth-continuation} | A URL-only HTTP definition is sufficient to reach the client sign-in handoff. |
262
+ | {§oauth-issuer} | A configured issuer is where a sign-in starts and the one server it trusts. |
263
+ | {§oauth-begin-unchallenged} | A sign-in starts where no connection is challenged. |
264
+ <!-- /map -->
265
+
229
266
  MCP servers are complete connection definitions ({§mcp-server-definition}). Standalone files, plugin components, and environment declarations
230
267
  supply the service baseline; live additions belong to the workspace. All use the common resolution
231
268
  and lifecycle ({§configuration-definition-resolution}, {§functionality-coordinator}). Disabled definitions remain
@@ -288,13 +325,23 @@ source repair replaces them, rather than retaining a historical warning.
288
325
 
289
326
  §mcp-activation-isolation **Cold endpoint failure is capability-local.** An enabled server
290
327
  that cannot connect or complete discovery during workspace activation remains
291
- enabled and client-visible as `unavailable`. It publishes no runtime, tools,
292
- resources, or documentation and cannot prevent other capabilities or the daemon
293
- from starting or serving dormant workers. Enabling that already-enabled alias is an explicit reconnect
328
+ enabled and client-visible as `unavailable`. It publishes no tools, resources, or
329
+ documentation, only its alias out of service ({§mcp-unavailable-runtime}), and cannot
330
+ prevent other capabilities or the daemon from starting or serving dormant workers. Enabling that already-enabled alias is an explicit reconnect
294
331
  attempt; failure preserves the unavailable snapshot, while success atomically
295
332
  replaces it. An interactive enable continues to reject an unavailable candidate
296
333
  without changing durable state.
297
334
 
335
+ §mcp-unavailable-runtime **An enabled server that cannot run keeps its alias.** Preparation registers
336
+ every enabled server: an active one with its tools and resources, any other as a runtime out of
337
+ service whose availability states what keeps it from running, so a call to the alias is refused with
338
+ that state ({§exec-registry-resolves}) and never as a name nothing registered.
339
+
340
+ | State | The refusal names |
341
+ | --- | --- |
342
+ | `authorization-required` | the operator's sign-in it awaits |
343
+ | `unavailable` | what preparation observed: its Problem's bounded diagnostic, else its detail |
344
+
298
345
  | Variable | Contract |
299
346
  |---|---|
300
347
  | `PLURNK_MCP_<alias>` | Complete `McpServerDefinition` JSON; transport and authentication replace together |
@@ -395,30 +442,19 @@ environment change; there is no automatic restart or stale-configuration state.
395
442
  have no local process environment; their native header and authorization references use the
396
443
  same workspace-composed operator environment.
397
444
 
398
- §mcp-management-actions MCP is one family of workspace Functionality ({§functionality-coordinator}). The
399
- coordinator publishes `workspace.mcp.list | add | replace | enable | disable | remove`, with `discover`
400
- while a registry is configured, and the model's
401
- `mcp` runtime with the common semantics, durable state, and publication; this module
402
- registers the family adapter and owns protocol truth beneath it. `available` is the configured service baseline; `add`, `replace` ({§functionality-replacement}), and `remove` change workspace state ({§mcp-definitions}), and
403
- `discover` searches the MCP Registry ({§mcp-registry-discovery}). `prepare` connects the enabled set, reusing
404
- unchanged live attachments, and returns one runtime and resource facet per connected server,
405
- one outcome per alias (`active` with catalog detail — negotiated protocol version, server identity,
406
- capabilities, tool names, resource and prompt counts — `unavailable` with its exact Problem, or
407
- `authorization-required` with its URL), and a snapshot in three phases: `publish` adopts the connected
408
- set and consumes or records pending authorizations, synchronously; `commit` closes connections the new
409
- set no longer uses; `abort` closes only what the attempt opened.
410
-
411
- §mcp-registry-discovery **`discover` searches the MCP Registry.** The family serves discovery
412
- while `PLURNK_MCP_REGISTRY_URL`, read when the module is constructed, names a registry; empty,
413
- the family has no `discover` ({§functionality-discover-advertisement}). Its one input, `query`,
414
- asks that registry (API v0.1) for one page of at most `PLURNK_MCP_REGISTRY_LIMIT` servers, each at
415
- its latest version, whose names match. Each npm, PyPI, NuGet or OCI package with a stdio transport
416
- becomes a stdio entry run as the registry's own examples run it (`npx -y`, `uvx`, `dnx`, or
417
- `docker run -i --rm` passing each declared variable through), and each Streamable HTTP remote becomes a
418
- URL entry with its non-secret literal headers. An entry that needs a person's input first, such as a
419
- template variable or a required argument with no value, has none. A candidate is a complete definition;
420
- its summary names the environment variables and headers the
421
- server needs, and its provenance names the registry and the server's `name@version`.
445
+ §mcp-management-actions **MCP is one family of workspace Functionality** ({§functionality-coordinator}). The coordinator publishes `workspace.mcp.list | add | replace | enable | disable | remove`, with `discover` while a registry is configured, and the model's `mcp` runtime with the common semantics, durable state, and publication; this module registers the family adapter and owns protocol truth beneath it.
446
+
447
+ | Verb | What |
448
+ | --- | --- |
449
+ | `available` | the configured service baseline |
450
+ | `add`, `replace` ({§functionality-replacement}), `remove` | change workspace state ({§mcp-definitions}) |
451
+ | `discover` | searches the MCP Registry ({§mcp-registry-discovery}) |
452
+ | `prepare` | connects the enabled set, reusing unchanged live attachments, and returns one runtime per enabled server, with its resource facet when active ({§mcp-unavailable-runtime}), one outcome per alias (`active` with catalog detail — negotiated protocol version, server identity, capabilities, tool names, resource and prompt counts — and any grant it presents ({§oauth-grant}), `unavailable` with its exact Problem, or `authorization-required` with its URL), and a snapshot in three phases |
453
+ | `publish` | adopts the connected set and consumes or records pending authorizations, synchronously |
454
+ | `commit` | closes connections the new set no longer uses |
455
+ | `abort` | closes only what the attempt opened |
456
+
457
+ §mcp-registry-discovery **`discover` searches the MCP Registry.** The family serves discovery while `PLURNK_MCP_REGISTRY_URL`, read when the module is constructed, names a registry; empty, the family has no `discover` ({§functionality-discover-advertisement}). Its one input, `query`, asks that registry (API v0.1) for one page of at most `PLURNK_MCP_REGISTRY_LIMIT` servers, each at its latest version, whose names match.
422
458
 
423
459
  | Discovery, admission, or installation condition | Problem | Status |
424
460
  |---|---|---|
@@ -426,6 +462,13 @@ server needs, and its provenance names the registry and the server's `name@versi
426
462
  | The complete connection definition is invalid | `definition-invalid` | 400 |
427
463
  | The alias differs from the definition's name | `alias-mismatch` | 400 |
428
464
 
465
+ | Registry server | Candidate |
466
+ | --- | --- |
467
+ | Each npm, PyPI, NuGet or OCI package with a stdio transport | a stdio entry run as the registry's own examples run it (`npx -y`, `uvx`, `dnx`, or `docker run -i --rm` passing each declared variable through) |
468
+ | Each Streamable HTTP remote | a URL entry with its non-secret literal headers |
469
+ | One that needs a person's input first, such as a template variable or a required argument with no value | none |
470
+ | Every candidate | a complete definition; its summary names the environment variables and headers the server needs, and its provenance names the registry and the server's `name@version` |
471
+
429
472
  Protocol continuations remain MCP-registered workspace actions beneath the
430
473
  common grammar:
431
474
 
@@ -443,7 +486,7 @@ Worker's own accepted mutation publishes them as unavailable
443
486
  | Endpoint condition | Problem |
444
487
  |---|---|
445
488
  | Cannot connect or complete discovery/catalog preparation at the negotiated revision | `502 server-unavailable`, retryable; names the server and its `type` without exposing credentials |
446
- | HTTP 401 without configured credentials | Enabled `authorization-required`, no runtime and no URL until the client begins sign-in |
489
+ | HTTP 401 without configured credentials | Enabled `authorization-required`, its alias out of service ({§mcp-unavailable-runtime}), and no URL until the client begins sign-in |
447
490
  | HTTP 401 with configured credentials | `502 server-authentication-failed`, non-retryable, with `upstreamStatus: 401`; credentials are not replaced by interactive OAuth |
448
491
  | Sign-in discovery lacks validated authorization metadata or a usable client-registration method | `502 oauth-metadata-unavailable` or `oauth-registration-unavailable`, non-retryable; retain the definition and surface the setup boundary, not retryable downtime |
449
492
  | The endpoint answers with a redirect | `502 server-redirected`, non-retryable ({§mcp-redirect-refused}) |
@@ -483,7 +526,8 @@ client is registered, or workspace lease retained merely for that challenge.
483
526
  The client binds its callback before `oauth.begin`; absent an explicitly
484
527
  configured redirect, it obtains an ephemeral loopback port (RFC 8252 §7.3).
485
528
  The MCP SDK owns metadata discovery, registration, PKCE and token exchange.
486
- An explicit OAuth definition may supply scope and client-registration metadata;
529
+ An explicit OAuth definition may supply scope, client-registration metadata, an issuer and
530
+ authorization parameters ({§oauth-issuer});
487
531
  its optional fixed redirect is respected, never silently rewritten. Bearer,
488
532
  client-credentials and Authorization-header configurations cannot be replaced
489
533
  by `oauth.begin`. Callback addresses and acquired credentials are session state,
@@ -492,7 +536,7 @@ not changes to the persisted definition.
492
536
  ```mermaid
493
537
  sequenceDiagram
494
538
  Model->>MCP: add URL-only definition
495
- MCP-->>Model: authorization-required
539
+ MCP-->>Client: authorization-required notice
496
540
  User->>Client: sign in to alias
497
541
  Client->>Client: bind callback
498
542
  Client->>MCP: oauth.begin(alias, redirectUrl)
@@ -501,32 +545,18 @@ sequenceDiagram
501
545
  User->>Client: browser callback
502
546
  Client->>MCP: oauth.complete(alias, callbackUrl)
503
547
  MCP-->>Client: 202 sign-in accepted; activation pending
504
- MCP-->>Model: active tools through normal publication
548
+ MCP-->>Model: settled active outcome and tools
505
549
  ```
506
550
 
507
- `oauth.begin` prepares an authorization attempt without enabling the alias or
508
- waiting for workspace quiescence. It returns `202` with an `authorization.url`
509
- when consent is required, `202` without that field when an accepted connection
510
- awaits publication, or `200` when tools are already active. These results name
511
- the alias, not a capability snapshot. The adapter retains one pending candidate
512
- per `(workspace, alias)` holding the connection and one workspace residency
513
- lease. An unchanged publication preserves that independent attempt; a new
514
- challenge supersedes it ({§oauth-lifetime}).
515
- Concurrent begin requests for one alias are refused; a later begun attempt
516
- supersedes the prior one unless its grant is already accepted and awaiting
517
- publication. An in-flight begin cannot attach after disable,
518
- remove or definition replacement. `oauth.complete` accepts the complete callback URL so state, `code`, and
519
- `iss` remain one parsing unit. Grant acceptance returns without waiting for
520
- workspace quiescence; it is not a configuration mutation and cannot re-enable
521
- an alias. The existing catalog-refresh path prepares and publishes the current
522
- enabled definitions, then consumes the candidate and releases its lease.
523
- The accepted result does not claim tool readiness: `list` reports the current
524
- published snapshot, including any subsequent preparation failure. A callback
525
- for a superseded attempt fails as invalid; a
526
- committed attachment that no longer matches the pending definition fails
527
- with a conflict instead of replaying a stale snapshot. A missing, expired,
528
- mismatched, or replayed callback fails without exposing attacker-owned OAuth
529
- error text.
551
+ `oauth.begin` prepares an authorization attempt without enabling the alias or waiting for workspace quiescence; `oauth.complete` accepts the complete callback URL so state, `code`, and `iss` remain one parsing unit.
552
+
553
+ | Fact | Rule |
554
+ | --- | --- |
555
+ | `oauth.begin` returns | `202` with an `authorization.url` when consent is required, `202` without that field when an accepted connection awaits publication, or `200` when tools are already active; these results name the alias, not a capability snapshot |
556
+ | The pending candidate | the adapter retains one per `(workspace, alias)` holding the connection and one workspace residency lease; an unchanged publication preserves that independent attempt, and a new challenge supersedes it ({§oauth-lifetime}) |
557
+ | Concurrent begins | concurrent begin requests for one alias are refused; a later begun attempt supersedes the prior one unless its grant is already accepted and awaiting publication; an in-flight begin cannot attach after disable, remove or definition replacement |
558
+ | Grant acceptance | returns without waiting for workspace quiescence; it is not a configuration mutation and cannot re-enable an alias; the existing catalog-refresh path prepares and publishes the current enabled definitions, then consumes the candidate and releases its lease; the accepted result does not claim tool readiness: `list` and a call to the alias report the current published snapshot, including any subsequent preparation failure, so a turn holding the workspace when the sign-in is accepted still sees the alias awaiting it, and the next turn sees its publication ({§module-workspace-quiescence}) |
559
+ | A bad callback | one for a superseded attempt fails as invalid; a committed attachment that no longer matches the pending definition fails with a conflict instead of replaying a stale snapshot; a missing, expired, mismatched, or replayed callback fails without exposing attacker-owned OAuth error text |
530
560
 
531
561
  | Callback boundary | Result |
532
562
  |---|---|
@@ -538,19 +568,40 @@ error text.
538
568
  | Preparation failure published as unavailable | Preserve the preparation diagnostic; release the pending lease and close its unused connection. |
539
569
  | Callback after publication | The attempt has been consumed; `404 oauth-not-pending`. |
540
570
 
571
+ §oauth-issuer **A configured issuer is where a sign-in starts and the one server it trusts.** An
572
+ interactive definition's `authorization.issuer` names the authorization server: discovery reads its
573
+ metadata (RFC 8414, then OpenID Connect Discovery) whether or not the MCP server publishes
574
+ protected-resource metadata, and the client registration, tokens and refresh are bound to it. With
575
+ none named, the SDK discovers the server from the resource's protected-resource metadata, falling
576
+ back to the resource's origin; where neither publishes metadata, a sign-in fails
577
+ `502 oauth-metadata-unavailable` and its recovery names the issuer field. `authorization.authorizationParams`
578
+ join every authorization request the definition makes, such as `access_type=offline` for a server
579
+ that issues a refresh token only when asked; the parameters the flow sets itself cannot be named
580
+ ({§mcp-oauth}).
581
+
582
+ §oauth-begin-unchallenged **A sign-in starts where no connection is challenged.** A server may answer
583
+ its catalog to anyone and refuse only requests ({§mcp-request-unauthorized}), so the connection
584
+ `oauth.begin` prepares meets no challenge.
585
+
586
+ | The alias awaits a sign-in because | and begin's connection meets no challenge |
587
+ | --- | --- |
588
+ | a request was refused | begin starts the SDK's authorization on that connection and returns its URL as for a challenged one; a refusal of that start is a preparation failure ({§mcp-preparation-diagnostics}) and leaves no pending attempt |
589
+ | its connection was challenged | the connection is accepted as it is: `202` without a URL, publishing at the next boundary |
590
+
541
591
  ## §oauth-lifetime Interactive OAuth lifetime and reauthorization
542
592
 
543
- Interactive OAuth state is deliberately ephemeral and process-memory: client
544
- registration data, access and refresh tokens, the PKCE verifier, and pending
545
- state live only in the owning connection or pending candidate. Nothing
546
- OAuth-secret is written to SQLite; the durable workspace state holds only the
547
- unexpanded definition ({§mcp-configuration}). There is no daemon callback HTTP
548
- listener, authority-root resource, or daemon-side browser side channel: an
549
- interactive client may receive its configured loopback redirect, or accept a
550
- pasted callback for remote/headless use. It returns the complete URL through `workspace.mcp.oauth.complete`
551
- so `state`, `code`, and `iss` remain one parsing unit. Reauthorization after a
552
- daemon restart is the intended journey, documented here rather than presented
553
- as an accidental failure.
593
+ Interactive OAuth state is process memory: an accepted sign-in's client
594
+ registration and access and refresh tokens are its alias's grant
595
+ ({§oauth-grant}), and the PKCE verifier and pending state live only in the
596
+ pending candidate. Nothing OAuth-secret is written to SQLite; the durable
597
+ workspace state holds only the unexpanded definition ({§mcp-configuration}).
598
+ There is no daemon callback HTTP listener, authority-root resource, or
599
+ daemon-side browser side channel: an interactive client may receive its
600
+ configured loopback redirect, or accept a pasted callback for remote/headless
601
+ use. It returns the complete URL through `workspace.mcp.oauth.complete` so
602
+ `state`, `code`, and `iss` remain one parsing unit. Reauthorization after a
603
+ daemon restart is the intended journey, documented here rather than presented as
604
+ an accidental failure.
554
605
 
555
606
  | Journey point | Behaviour |
556
607
  |---|---|
@@ -558,11 +609,25 @@ as an accidental failure.
558
609
  | Client disconnect | Does not touch the pending candidate; it can still be completed, or replaced by a fresh request. |
559
610
  | Daemon restart during pending | The candidate is lost: nothing was durable, no attachment publishes, and `oauth.complete` answers `404 oauth-not-pending`. Start authorization again. |
560
611
  | Daemon restart after authorization | The durable definition rehydrates but tokens are gone; a challenged attachment publishes `authorization-required` and enable returns `202`. A URL is present only when a callback is already configured. The operator reauthorizes. |
561
- | Token expiry | An expired access token surfaces as one unauthorized response; the SDK re-acquires via `refresh_token` when one was issued, otherwise re-enters interactive authorization. |
562
- | Refresh | Happens only against the issuer bound during the original authorization; the refreshed token replaces the in-memory token. |
563
- | Workspace disable/remove | Closes the attachment and clears its pending candidate; no durable secret deletion is needed because nothing secret is durable. |
612
+ | Token expiry | An expired access token surfaces as one unauthorized response; the SDK re-acquires via `refresh_token` when one was issued; otherwise the request fails and the alias awaits a sign-in ({§mcp-request-unauthorized}). |
613
+ | Refresh | Happens only against the issuer bound during the original authorization; the refreshed token replaces the grant's, for every later connection ({§oauth-grant}). |
614
+ | Workspace cooling | Closes the alias's connection; the grant outlives it, and the alias's next connection presents it ({§oauth-grant}). |
615
+ | Workspace disable/remove | Closes the attachment and clears its pending candidate; the grant stays for the alias's return with the definition it was accepted for ({§oauth-grant}). No durable secret deletion is needed because nothing secret is durable. |
564
616
  | Server replacement | Publication discards any superseded pending connection and releases its residency, including replacement by a definition without OAuth. Completion of an unpublished configuration drift fails `409 oauth-target-conflict` instead of replaying a stale snapshot. |
565
- | Cross-authorization protection | Candidates are keyed by `(workspace, alias)`; callback state, PKCE, and issuer are validated by the SDK against the attempt that created them, so no other workspace, alias, or attempt can complete this authorization. |
617
+ | Cross-authorization protection | Candidates and grants are keyed by `(workspace, alias)`; callback state, PKCE, and issuer are validated by the SDK against the attempt that created them, so no other workspace, alias, or attempt can complete this authorization. |
618
+
619
+ §oauth-grant **A grant outlives its connection.** An accepted sign-in is its alias's grant, held per
620
+ `(workspace, alias)` with the definition it was accepted for. Only a redirect the definition
621
+ configures still has a client waiting at it once the sign-in is accepted.
622
+
623
+ | When | The grant |
624
+ | --- | --- |
625
+ | A connection the module opens for the alias with that definition, after cooling, an explicit enable, or disable and enable | is presented, and refreshed as it lapses |
626
+ | An active alias's connection presents it | is listed in the alias's `detail.grant`: the `issuer` its tokens came from, the `scope` that authorization server stated, and whether it is `refreshable`; never a token or a time. An active alias presenting no grant lists none |
627
+ | A connection serves another definition, or another workspace | is never presented |
628
+ | Its server refuses it, at connection or on a request ({§mcp-request-unauthorized}) | starts a sign-in only at a redirect the definition configures; otherwise the alias awaits the sign-in a client begins ({§oauth-continuation}) |
629
+ | A sign-in for the alias is accepted | is replaced by the new one |
630
+ | Its authorization server rejects it, or the daemon stops | ends |
566
631
 
567
632
  ## §oauth-client-credentials Client-credentials grant adoption
568
633
 
@@ -638,19 +703,20 @@ boundaries; neither closes or replaces the committed connection.
638
703
  | Aborted publication | Does not acknowledge the unpublished catalog. |
639
704
  | Disable/remove, workspace cooling, or shutdown | Retires obsolete refresh timers and invalidations. |
640
705
 
641
- MCP participates in core Functionality residency ({§module-workspace-residency}).
642
- Preparation reports the current server alias through the coordinator's activity
643
- contract ({§functionality-preparation-visibility}); inspection never starts a
644
- connection ({§functionality-inspection}).
645
- Every tool call and Task retains the workspace from executor entry through its
646
- terminal result; an interactive OAuth candidate retains it until completion,
647
- replacement, cancellation, or module shutdown. Catalog refresh timers are
648
- infrastructure, not residency owners: cooling serializes behind a refresh
649
- already running and cancels any timer not yet begun. At a lease-free quiescent
650
- boundary, deactivation removes the workspace snapshot and closes all of its
651
- connections. Core separately withdraws the executor/scheme publication while
652
- preserving durable MCP state and generated reference entries for transparent
653
- reactivation.
706
+ MCP participates in core Functionality residency
707
+ ({§module-workspace-residency}). Preparation reports the current server alias
708
+ through the coordinator's activity contract
709
+ ({§functionality-preparation-visibility}); inspection never starts a connection
710
+ ({§functionality-inspection}). Every tool call and Task retains the workspace
711
+ from executor entry through its terminal result; an interactive OAuth candidate
712
+ retains it until completion, replacement, cancellation, or module shutdown.
713
+ Catalog refresh timers are infrastructure, not residency owners: cooling
714
+ serializes behind a refresh already running and cancels any timer not yet begun.
715
+ At a lease-free quiescent boundary, deactivation removes the workspace snapshot
716
+ and closes all of its connections; an accepted sign-in outlives them
717
+ ({§oauth-grant}). Core separately withdraws the executor/scheme publication
718
+ while preserving durable MCP state and generated reference entries for
719
+ transparent reactivation.
654
720
 
655
721
  Add and enable prepare the candidate while the old snapshot remains
656
722
  authoritative, then publish only at {§module-workspace-quiescence}. Disable and
@@ -682,6 +748,16 @@ failures are reported. Core's shutdown deadline remains the outer bound
682
748
 
683
749
  ## §mcp-host-composition Protocol-to-Plurnk composition
684
750
 
751
+ <!-- map -->
752
+ | Tag | Promise |
753
+ | --- | --- |
754
+ | {§mcp-input-deadline} | Client-input waits consume the remaining owning operation budget (`PLURNK_MCP_REQUEST_TIMEOUT`); each MRTR… |
755
+ | {§mcp-tool-replay} | A tool call is effectful unless host policy proves otherwise. |
756
+ | {§mcp-tool-problem-detail} | Tool-call Problems keep runtime, tool, and any bounded remote diagnostic as structured extensions. |
757
+ | {§mcp-request-unauthorized} | A request refused for want of a sign-in says so, and its alias then awaits one. |
758
+ | {§mcp-trailing-aside} | A tool call's body is one JSON object. |
759
+ <!-- /map -->
760
+
685
761
  One `ServerConnection` owns negotiation, SDK caches, authorization partition,
686
762
  subscriptions, active request controllers, MRTR rounds, and Tasks for one
687
763
  workspace attachment. The host does not reproduce SDK protocol machinery.
@@ -729,6 +805,21 @@ Nontext parts and structured data are not interpreted as explanations. With no
729
805
  text explanation the extension is absent; successful results do not acquire one.
730
806
  The complete result and passive output remain unchanged ({§mcp-result-content}).
731
807
 
808
+ §mcp-request-unauthorized **A request refused for want of a sign-in says so, and its alias then awaits one.**
809
+ A server may answer its catalog to anyone and challenge only a request, so preparation can publish it
810
+ `active`. A later request — a tool call, a resource or prompt read, or a completion — is refused for
811
+ want of a sign-in when it is answered with HTTP 401, or challenged in a way the SDK cannot meet without
812
+ the operator's consent (an interactive grant it cannot refresh). The refusal depends on the definition:
813
+
814
+ | Definition | A request refused for want of a sign-in |
815
+ | --- | --- |
816
+ | URL-only, or interactive OAuth | fails `401 authorization-required`, non-retryable, and republishes the alias `authorization-required`, out of service ({§mcp-unavailable-runtime}), with no URL until the client begins sign-in ({§oauth-continuation}) |
817
+ | A bearer token, a client-credentials grant, or an Authorization header | fails as any failed request of its kind; no state changes and no sign-in is offered |
818
+
819
+ Only a fresh connection answers the awaited sign-in — an accepted sign-in, an explicit enable, a changed
820
+ definition, or the alias's return after disable, removal, cooling or a restart — and it publishes what
821
+ its preparation observes, presenting the alias's grant where its definition has one ({§oauth-grant}).
822
+
732
823
  §mcp-trailing-aside A tool call's body is one JSON object. HTML comments after
733
824
  that object are the writer's aside, not arguments: when the body does not parse
734
825
  as written, trailing `<!-- … -->` comments are removed and the object is read.
@@ -889,11 +980,16 @@ servers are composition evidence only.
889
980
  |---|---:|---|
890
981
  | `tool-required` | 400 | An MCP tool target is required. Recovery: Select a target documented under worker:///_plurnk/tools/*runtime*/. |
891
982
  | `tool-not-enabled` | 404 | The MCP tool is not enabled. Recovery: Select a target documented under worker:///_plurnk/tools/*runtime*/. |
892
- | `tool-reported-error` | 502 | The MCP tool reported an error (the tool call failed). |
893
- | `invalid-tool-arguments` | 400 | MCP tool arguments must be one JSON object. Recovery: One JSON object per MCP tool call. |
983
+ | `tool-reported-error` | 502 | The MCP tool reported an error (the tool call failed). Recovery: the tool's working invocation fence with its schema note and schema link (#1228). |
984
+ | `invalid-tool-arguments` | 400 | MCP tool arguments must be one JSON object. Recovery: One JSON object per MCP tool call, followed by the tool's working invocation fence (#1228). |
985
+ | `resource-address-invalid` | 400 | The MCP resource address is invalid (a malformed encoded address); not retryable. |
986
+ | `resource-part-not-found` | 404 | The resource response does not contain this part. |
987
+ | `resource-read-failed` | 502 | MCP resource preparation failed; retryable, with the bounded upstream diagnostic. |
988
+ | `resource-find-failed` | 502 | MCP resource listing failed; retryable, with the bounded upstream diagnostic. |
894
989
  | `oauth-client-credentials-failed` | 502 | MCP server '*name*' rejected the client-credentials grant. |
895
990
  | `server-authentication-failed` | 502 | MCP server '*name*' rejected authentication (HTTP 401). |
896
- | `oauth-metadata-unavailable` | 502 | MCP OAuth requires validated authorization-server metadata, and the server provided none. |
991
+ | `authorization-required` | 401 | MCP server '*name*' awaits the operator's sign-in; the request was not completed. |
992
+ | `oauth-metadata-unavailable` | 502 | MCP OAuth requires validated authorization-server metadata, and the server provided none. Recovery, when the definition names no issuer: Name the authorization server as the definition's authorization.issuer. |
897
993
  | `oauth-registration-unavailable` | 502 | No client is registered with the authorization server: none is configured for it, it advertises no Dynamic Client Registration endpoint, and no client metadata document it accepts is configured. |
898
994
  | `oauth-redirect-invalid` | 400 | The OAuth callback must use HTTPS or HTTP loopback with a usable port. |
899
995
  | `oauth-configuration-conflict` | 409 | MCP server '*alias*' is not configured for this interactive OAuth callback. |
@@ -1,6 +1,7 @@
1
1
  import { BaseExecutor } from "@plurnk/plurnk-execs";
2
2
  import type { ChannelDecl, Effect, ExecArgs, ExecResult, RuntimeAvailability, RuntimeDecl, RuntimeSummaryDecl, RuntimeToolRegistry } from "@plurnk/plurnk-execs";
3
- import ServerConnection, { type ServerCatalog } from "./client.ts";
3
+ import type ServerConnection from "./client.ts";
4
+ import { type ServerCatalog } from "./client.ts";
4
5
  import type { ContentBlock } from "@modelcontextprotocol/client";
5
6
  import type { ToolPolicy } from "./config.ts";
6
7
  export declare const serverSummary: (name: string, catalog: ServerCatalog | undefined) => string;
@@ -14,6 +15,14 @@ export declare const toolResultBody: (result: ToolResultShape, runtime: string,
14
15
  content: string;
15
16
  mimetype: string;
16
17
  }>;
18
+ export declare class UnavailableServerExecutor extends BaseExecutor {
19
+ #private;
20
+ constructor(runtime: string, detail: string);
21
+ get channels(): Readonly<Record<string, ChannelDecl>>;
22
+ get publishedChannel(): string;
23
+ probe(): Promise<RuntimeAvailability>;
24
+ run(): Promise<ExecResult>;
25
+ }
17
26
  export declare const runtimeDecl: (name: string, summary: RuntimeSummaryDecl, expandTools: boolean, instructions?: string) => RuntimeDecl;
18
27
  export default class McpExecutor extends BaseExecutor {
19
28
  #private;
@@ -1 +1 @@
1
- {"version":3,"file":"McpExecutor.d.ts","sourceRoot":"","sources":["../src/McpExecutor.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,YAAY,EAIf,MAAM,sBAAsB,CAAC;AAE9B,OAAO,KAAK,EACR,WAAW,EACX,MAAM,EACN,QAAQ,EACR,UAAU,EAEV,mBAAmB,EACnB,WAAW,EACX,kBAAkB,EAClB,mBAAmB,EACtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,gBAAgB,EAAE,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAkB,MAAM,8BAA8B,CAAC;AAIjF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAY9C,eAAO,MAAM,aAAa,SAChB,MAAM,WACH,aAAa,GAAG,SAAS,KACnC,MAOF,CAAC;AAGF,eAAO,MAAM,oBAAoB,SACvB,MAAM,WACH,aAAa,GAAG,SAAS,KACnC,kBAKF,CAAC;AAMF,MAAM,MAAM,eAAe,GAAG;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,YAAY,EAAE,CAAC;IAC3C,QAAQ,CAAC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IACrC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC9B,CAAC;AAEF,eAAO,MAAM,cAAc,WAAkB,eAAe,WAAW,MAAM,UAAU,QAAQ,CAAC,OAAO,CAAC,KAAG,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAqBvJ,CAAC;AAwBF,eAAO,MAAM,WAAW,SAAU,MAAM,WAAW,kBAAkB,eAAe,OAAO,iBAAiB,MAAM,KAAG,WAenH,CAAC;AAuBH,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,YAAY;;IAoBjD,YACI,QAAQ,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAC5C,UAAU,EAAE,gBAAgB,EAC5B,eAAe,EAAE,MAAM,MAAM,IAAI,EACjC,MAAM,GAAE,OAAO,CAAC,UAAU,CAAM,EAMnC;IAED,IAAI,QAAQ,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAOpD;IAEQ,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAK7C;IAED,IAAa,gBAAgB,IAAI,MAAM,CAEtC;IAyBD,YAAY,IAAI,mBAAmB,CAKlC;IAED,IAAI,OAAO,IAAI,aAAa,CAK3B;IAEc,KAAK,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,mBAAmB,CAAC,CASvE;IAEK,gBAAgB,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAsBzE;IAEK,GAAG,CAAC,EACN,OAAO,EACP,IAAI,EACJ,MAAM,EACN,MAAM,EACN,KAAK,EACL,QAAQ,EACR,IAAI,EACJ,QAAQ,EACR,KAAK,GACR,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC,CAmIhC;CACJ"}
1
+ {"version":3,"file":"McpExecutor.d.ts","sourceRoot":"","sources":["../src/McpExecutor.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,YAAY,EAKf,MAAM,sBAAsB,CAAC;AAE9B,OAAO,KAAK,EACR,WAAW,EACX,MAAM,EACN,QAAQ,EACR,UAAU,EAEV,mBAAmB,EACnB,WAAW,EACX,kBAAkB,EAClB,mBAAmB,EACtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,gBAAgB,MAAM,aAAa,CAAC;AAChD,OAAO,EAA4C,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAC3F,OAAO,KAAK,EAAE,YAAY,EAAkB,MAAM,8BAA8B,CAAC;AAIjF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAY9C,eAAO,MAAM,aAAa,SAChB,MAAM,WACH,aAAa,GAAG,SAAS,KACnC,MAOF,CAAC;AAGF,eAAO,MAAM,oBAAoB,SACvB,MAAM,WACH,aAAa,GAAG,SAAS,KACnC,kBAKF,CAAC;AAMF,MAAM,MAAM,eAAe,GAAG;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,YAAY,EAAE,CAAC;IAC3C,QAAQ,CAAC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IACrC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC9B,CAAC;AAEF,eAAO,MAAM,cAAc,WAAkB,eAAe,WAAW,MAAM,UAAU,QAAQ,CAAC,OAAO,CAAC,KAAG,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAqBvJ,CAAC;AA2BF,qBAAa,yBAA0B,SAAQ,YAAY;;IAGvD,YAAY,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAG1C;IAED,IAAI,QAAQ,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAEpD;IAED,IAAa,gBAAgB,IAAI,MAAM,CAEtC;IAEc,KAAK,IAAI,OAAO,CAAC,mBAAmB,CAAC,CAEnD;IAEK,GAAG,IAAI,OAAO,CAAC,UAAU,CAAC,CAE/B;CACJ;AAED,eAAO,MAAM,WAAW,SAAU,MAAM,WAAW,kBAAkB,eAAe,OAAO,iBAAiB,MAAM,KAAG,WAenH,CAAC;AAuBH,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,YAAY;;IAoBjD,YACI,QAAQ,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAC5C,UAAU,EAAE,gBAAgB,EAC5B,eAAe,EAAE,MAAM,MAAM,IAAI,EACjC,MAAM,GAAE,OAAO,CAAC,UAAU,CAAM,EAMnC;IAED,IAAI,QAAQ,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAOpD;IAEQ,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAK7C;IAED,IAAa,gBAAgB,IAAI,MAAM,CAEtC;IAyBD,YAAY,IAAI,mBAAmB,CAKlC;IAED,IAAI,OAAO,IAAI,aAAa,CAK3B;IAEc,KAAK,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,mBAAmB,CAAC,CASvE;IAEK,gBAAgB,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAsBzE;IAEK,GAAG,CAAC,EACN,OAAO,EACP,IAAI,EACJ,MAAM,EACN,MAAM,EACN,KAAK,EACL,QAAQ,EACR,IAAI,EACJ,QAAQ,EACR,KAAK,GACR,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC,CAgJhC;CACJ"}