@plurnk/plurnk-contracts 1.7.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/SPEC.md +106 -53
  2. package/dist/plurnk.gemma.gbnf +26 -22
  3. package/dist/plurnk.qwen.gbnf +26 -22
  4. package/dist/schema/ClientStatement.json +9 -4
  5. package/dist/schema/McpConfigurationOverlay.json +25 -0
  6. package/dist/schema/McpServerDefinition.json +2 -1
  7. package/dist/schema/McpServerOptions.json +36 -0
  8. package/dist/schema/ModelCatalogPage.json +122 -0
  9. package/dist/schema/ModelCatalogQuery.json +32 -0
  10. package/dist/schema/ModelReadiness.json +47 -0
  11. package/dist/schema/ModelRoute.json +23 -0
  12. package/dist/schema/PlurnkStatement.json +48 -31
  13. package/dist/schema/ReasoningPolicy.json +7 -0
  14. package/dist/src/AstBuilder.d.ts.map +1 -1
  15. package/dist/src/AstBuilder.js +42 -21
  16. package/dist/src/AstBuilder.js.map +1 -1
  17. package/dist/src/PlurnkErrorStrategy.js +16 -16
  18. package/dist/src/PlurnkErrorStrategy.js.map +1 -1
  19. package/dist/src/PlurnkParser.js +3 -3
  20. package/dist/src/PlurnkParser.js.map +1 -1
  21. package/dist/src/Validator.d.ts +29 -1
  22. package/dist/src/Validator.d.ts.map +1 -1
  23. package/dist/src/Validator.js +137 -0
  24. package/dist/src/Validator.js.map +1 -1
  25. package/dist/src/generated/plurnkLexer.d.ts +30 -28
  26. package/dist/src/generated/plurnkLexer.d.ts.map +1 -1
  27. package/dist/src/generated/plurnkLexer.js +398 -381
  28. package/dist/src/generated/plurnkLexer.js.map +1 -1
  29. package/dist/src/generated/plurnkParser.d.ts +56 -39
  30. package/dist/src/generated/plurnkParser.d.ts.map +1 -1
  31. package/dist/src/generated/plurnkParser.js +644 -450
  32. package/dist/src/generated/plurnkParser.js.map +1 -1
  33. package/dist/src/generated/plurnkParserVisitor.d.ts +7 -7
  34. package/dist/src/generated/plurnkParserVisitor.d.ts.map +1 -1
  35. package/dist/src/generated/plurnkParserVisitor.js +6 -6
  36. package/dist/src/generated/plurnkParserVisitor.js.map +1 -1
  37. package/dist/src/index.d.ts +2 -2
  38. package/dist/src/index.d.ts.map +1 -1
  39. package/dist/src/index.js +2 -2
  40. package/dist/src/index.js.map +1 -1
  41. package/dist/src/types.d.ts +2 -1
  42. package/dist/src/types.d.ts.map +1 -1
  43. package/dist/src/types.generated.d.ts +145 -19
  44. package/dist/src/types.generated.d.ts.map +1 -1
  45. package/dist/src/types.js +4 -0
  46. package/dist/src/types.js.map +1 -1
  47. package/package.json +2 -3
  48. package/plurnk.md +91 -110
package/SPEC.md CHANGED
@@ -10,10 +10,12 @@ is the single code API for those contracts.
10
10
  | ------------------------------------------------------------------------------- | --------------------------------------------------- |
11
11
  | Parser, AST, validators, Problems, results, Notices, text regions and extents | `@plurnk/plurnk-contracts` |
12
12
  | Effective loop policy and its default | `LoopFlags`, `DEFAULT_LOOP_FLAGS` |
13
+ | Durable reasoning intent | `ReasoningPolicy`, `REASONING_POLICIES` |
14
+ | Model route and catalog discovery | `ModelRoute`, `ModelCatalogQuery`, `ModelCatalogPage`, `ModelReadiness` |
13
15
  | Stopped-world client contract | `ProposalDisposition`, `ProposalProjection` |
14
16
  | Client-owned interaction contract | `ClientInteractionRequest`, `ClientInteractionProjection`, `ClientInteractionResolution` |
15
17
  | Client capability presentation | `ClientDisplayCapabilities` |
16
- | Workspace MCP server attachment | `McpServerDefinition` |
18
+ | Workspace MCP configuration | `McpServerDefinition`, `McpServerOptions`, `McpConfigurationOverlay` |
17
19
  | JSON Schemas | `@plurnk/plurnk-contracts/schema/*.json` |
18
20
  | Generated JSON result rendering | `renderJsonResult` |
19
21
  | Local-model rails | `@plurnk/plurnk-contracts/plurnk.{gemma,qwen}.gbnf` |
@@ -63,7 +65,7 @@ flowchart LR
63
65
  | Shared wire data | `schema/*.json` | Defines runtime-neutral data shapes projected into generated TypeScript |
64
66
  | Stateful behavior | consuming runtime | Resolves addresses, permissions, selection arithmetic, effects, and lifecycle |
65
67
 
66
- ANTLR owns statement structure, suffix matching, slot multiplicity, accepted
68
+ ANTLR owns statement structure, delimiter matching, slot multiplicity, accepted
67
69
  slot permutations, scope-number syntax, and interstatement text recognition.
68
70
  AstBuilder owns URL decomposition and deterministic matcher validation through
69
71
  WHATWG `URL`, ECMAScript `RegExp`, XPath 1.0, and RFC 9535 JSONPath parsers.
@@ -89,6 +91,20 @@ The schemas own the runtime-neutral shapes; core owns their stateful values.
89
91
 
90
92
  `DEFAULT_LOOP_FLAGS` is the contracts-owned effective default value. A consumer may persist a partial object as an implementation detail, but it never exposes or acts on that partial representation as though it were the complete contract.
91
93
 
94
+ §reasoning-policy-wire `ReasoningPolicy` is exactly `off | adaptive | low |
95
+ medium | high`. The schema owns this shared wire vocabulary. Providers own the
96
+ supported subset and native projection for a selected route; core owns the
97
+ durable worker value.
98
+
99
+ §model-catalog-wire `ModelRoute` is one exact client-visible provider/model
100
+ identity with optional alias provenance. Provider credentials, endpoints, and
101
+ tuning never enter this wire shape. Catalog discovery uses a
102
+ closed bounded query and page: entries carry exact selectors, display facts,
103
+ physical limits, capabilities, and local `ModelReadiness`. A readiness cause
104
+ contains alternative environment-variable sets—every name within a set is
105
+ required and any set may satisfy the cause. It carries names only, never values,
106
+ and asserts neither credential validity nor endpoint reachability.
107
+
92
108
  ### §client-interaction-wire Client-owned interaction wire
93
109
 
94
110
  The closed client-interaction schemas describe one operation asking its client
@@ -203,14 +219,14 @@ response roots plus the parser/AstBuilder result.
203
219
 
204
220
  §rail-heading-boundaries On the GBNF rail, PLAN and every operation use lane `0`.
205
221
  Every reserved PLAN or operation heading stem is structural, regardless of the
206
- suffix a model attempts next, so a non-`0` pseudo-heading cannot be swallowed as
222
+ delimiter a model attempts next, so a non-`0` pseudo-heading cannot be swallowed as
207
223
  literal body text. Rail bodies therefore cannot quote reserved headings from any
208
- lane. This makes both the canonical suffix and section boundary structurally
224
+ lane. This makes both the canonical delimiter and section boundary structurally
209
225
  available during constrained generation; ANTLR remains the wider language and
210
226
  accepts intentional alternate-lane literals during ingestion.
211
227
 
212
228
  §gbnf-curation-shaping The rail admits OPEN/FOLD curation terms, a canonical
213
- `log:` target, and a matcher independently. It accepts any ordered mixture of
229
+ `log:` target, an optional log-body line scope, and a matcher independently. It accepts any ordered mixture of
214
230
  unsigned, `+`, and `-` terms without proving that the combination selects a log
215
231
  item; ANTLR and AstBuilder own that condition, while runtime owns wider ingested
216
232
  target resolution.
@@ -218,32 +234,33 @@ target resolution.
218
234
  ## §canonical-statement 2. Canonical statement form
219
235
 
220
236
  ```text
221
- # PLANsuffix
237
+ # PLANdelimiter
222
238
  body
223
239
 
224
- ## OPsuffix [signal]? (path)? <scope>?
240
+ ## OPdelimiter [signal]? (path)? <scope>? <!-- annotation -->?
225
241
  body?
226
242
  ```
227
243
 
228
244
  §section-boundary A statement is one Markdown section. PLAN alone uses a level-one
229
245
  heading; every other operation uses a level-two heading. Its body is the
230
246
  character-perfect section content before the next structural heading or EOF.
231
- Canonical adjacent sections have one blank separator line. That final separator
232
- line is syntax and is not body content; any additional preceding blank lines
233
- remain body content. The tolerant ingester also admits a structural heading on
234
- the immediately following line.
247
+ Canonical adjacent sections place the next structural heading on the immediately
248
+ following line. The tolerant ingester also admits one empty separator line; that
249
+ separator is syntax rather than body content, while any additional preceding
250
+ blank lines remain body content.
235
251
 
236
- §empty-section An empty section has no body lines between its heading and the
237
- section separator (or EOF) and normalizes to a null body.
252
+ §empty-section An empty section has no body lines between its heading and the next
253
+ structural heading, tolerated separator, or EOF and normalizes to a null body.
238
254
 
239
255
  | Element | Canonical contract |
240
256
  |--------------|---------------------------------------------------------------------------|
241
257
  | `# PLAN` | Required level-one turn anchor |
242
258
  | `## OP` | Level-two protocol operation |
243
- | `suffix` | Heading lane, joined directly to PLAN or OP |
259
+ | `delimiter` | Heading lane, joined directly to PLAN or OP |
244
260
  | `[signal]` | Optional operation-specific signal, preceded by one space |
245
261
  | `(path)` | Optional target slot, preceded by one space |
246
262
  | `<scope>` | Optional numeric scope, preceded by one space |
263
+ | `<!-- … -->` | Optional trailing operation annotation, preceded by one space |
247
264
  | line ending | Ends the single-line heading |
248
265
  | `body` | Zero or more characters of operation-specific, character-perfect content |
249
266
  | blank line | Canonical section separator; excluded from the preceding body |
@@ -251,21 +268,31 @@ section separator (or EOF) and normalizes to a null body.
251
268
  The following constraints are structural:
252
269
 
253
270
  - §lane-match PLAN establishes one lane for the turn. A heading is structural
254
- only when its suffix character-matches that lane; a different suffix remains
271
+ only when its delimiter character-matches that lane; a different delimiter remains
255
272
  ordinary body text.
256
273
  - PLAN is the only H1 operation and every non-PLAN operation is H2.
257
274
  - A header occupies one physical line.
258
275
  - Each admitted signal, target, and scope slot appears at most once.
259
- - OPEN, FOLD, BARE, WORK, FORK, and KILL do not admit a scope slot.
260
- - An ingested suffix is `[A-Za-z0-9_]*`; canonical teaching and the GBNF use `0`.
276
+ - An annotation follows every present modifier and appears at most once.
277
+ - BARE, WORK, FORK, and KILL do not admit a scope slot.
278
+ - An ingested delimiter is `[A-Za-z0-9_]*`; canonical teaching and the GBNF use `0`.
261
279
 
262
280
  §slot-order Canonical producers and the GBNF rail emit signal, then target, then
263
- scope, with one ASCII space before every present slot. Slot delimiters make
281
+ scope, then annotation, with one ASCII space before every present slot. Slot delimiters make
264
282
  their boundaries unambiguous, so the tolerant ANTLR ingester accepts zero or
265
283
  more horizontal whitespace characters before each slot and any permutation of
266
284
  the slots admitted by that operation, at most once each. Accepted spacing and
267
285
  permutation are not second canonical spellings.
268
286
 
287
+ §operation-annotation A heading may end with one single-line Markdown HTML
288
+ comment. AstBuilder strips the delimiters and surrounding horizontal whitespace
289
+ into the statement's fixed `annotation: string | null` field. The annotation is
290
+ durable, model- and client-facing descriptive text but semantically inert: it
291
+ does not alter operation identity, signal, target, scope, dispatch, effect,
292
+ authorization, status, or body. An empty comment normalizes to the empty string.
293
+ Text containing a newline or lacking the closing `-->` is not an annotation;
294
+ `<!--` elsewhere remains ordinary body text.
295
+
269
296
  The ingester also accepts several bounded noncanonical forms so it can explain
270
297
  or safely execute understandable input:
271
298
 
@@ -283,10 +310,11 @@ or safely execute understandable input:
283
310
  | Element | Accepted shape or role |
284
311
  |-------------|--------------------------------------------------------------------|
285
312
  | `OP` | `FIND READ EDIT COPY MOVE OPEN FOLD SEND EXEC BARE WORK FORK KILL PLAN` |
286
- | `suffix` | `[A-Za-z0-9_]*`, adjacent to PLAN or OP |
313
+ | `delimiter` | `[A-Za-z0-9_]*`, adjacent to PLAN or OP |
287
314
  | `[signal]` | Operation-specific tags, identifier, branch, or integer |
288
315
  | `(path)` | Local path or scheme URL target; detailed in §5 |
289
316
  | `<scope>` | One or more signed integers or decimals; detailed in §7 |
317
+ | annotation | Optional trailing `<!-- … -->` descriptive text |
290
318
  | `body` | Opaque section text before the next same-lane heading or EOF |
291
319
 
292
320
  ## §op-shapes 4. Per-operation semantics
@@ -302,8 +330,8 @@ governed by {§canonical-statement}; runtime conditions remain explicit below.
302
330
  | EDIT | optional add log tags | required file or entry | required for an existing target | literal text |
303
331
  | COPY | optional add log tags | required source | optional source region | required destination selection |
304
332
  | MOVE | optional add log tags | required source | optional source region | required destination selection |
305
- | FOLD | optional filter/change tags | optional log selection | none | optional matcher |
306
- | OPEN | optional filter/change tags | optional log selection | none | optional matcher |
333
+ | FOLD | optional filter/change tags | optional log selection | optional log-body line scope | optional matcher |
334
+ | OPEN | optional filter/change tags | optional log selection | optional log-body line scope | optional matcher |
307
335
  | EXEC | optional executor | optional local working path | optional timeout, poll | optional executor input |
308
336
  | BARE | optional add log tags | none | none | required prompt |
309
337
  | WORK | optional Git branch | required fresh `worker://name` | none | required prompt |
@@ -329,11 +357,13 @@ strips any `+`; the signal neither filters nor modifies resources. OPEN and
329
357
  FOLD treat every unsigned `tag` as an ALL-tags selector, then add each `+tag`
330
358
  and remove each `-tag` from the selected log items. Signed terms never select,
331
359
  so either a target, matcher, or unsigned tag is required. Adding and removing
332
- the same tag conflicts. Neither curation operation exposes a positional scope.
360
+ the same tag conflicts. An optional OPEN/FOLD line scope changes visibility
361
+ inside every selected canonical log body; it does not participate in row selection.
333
362
 
334
363
  The `<scope>` slot is optional where admitted and its domain is OP-specific. FIND
335
364
  scopes ordered results. EXEC and SEND scope timing. READ, EDIT, COPY, and
336
- MOVE use one universal text algebra independent of mimetype:
365
+ MOVE use one universal text algebra independent of mimetype; OPEN/FOLD admit
366
+ only its one- and two-line forms for canonical log-body visibility:
337
367
 
338
368
  | Arity | Surface meaning | Endpoint rule |
339
369
  |---------------|---------------------------------------------------------------------|------------------------------------------------------|
@@ -548,6 +578,7 @@ The operation column names the canonical AST operation after
548
578
  | EDIT | 0/1/2/4 text coordinates | Text replacement, deletion, prepend, or append |
549
579
  | COPY/MOVE source | 0/1/2/4 text coordinates | Region copied or moved from the selected source |
550
580
  | COPY/MOVE destination | 0/1/2/4 text coordinates after target | Region replaced or insertion point at the destination |
581
+ | OPEN / FOLD | 0/1/2 body-relative line coordinates | Whole log body when absent; one physical line or inclusive range when present |
551
582
  | EXEC | `timeout[,poll]` | Spawn lifetime bound and poll cadence in seconds |
552
583
  | Terminal SEND `[202]` | `timeout[,poll]` | Bounded or indefinite wait and optional poll cadence |
553
584
 
@@ -556,13 +587,15 @@ whole line, two integers are an inclusive whole-line range, and four integers
556
587
  are an exact start-inclusive/end-exclusive region. Mutation scopes additionally
557
588
  admit `0` as prepend and `-1` as append. A leading decimal on semantic FIND
558
589
  is a similarity threshold; any remaining integers select result positions. READ
559
- does not admit decimal scope components.
590
+ does not admit decimal scope components. OPEN/FOLD intersect a valid body-relative line
591
+ scope with each selected body; an absent line is a successful no-op for that
592
+ body, while unsupported arity is a runtime failure.
560
593
 
561
594
  §text-line-anchor-syntax A text coordinate admits a case-sensitive line anchor
562
595
  spelled `@` followed by exactly five Base62 characters (`0-9A-Za-z`) wherever
563
596
  its `L`, `SL`, or `EL` position denotes a line. Columns, prepend `0`, and append
564
- `-1` remain numeric. Exact READ, EDIT, COPY/MOVE source and destination, and
565
- client LOOK preserve these positions in `TextLineMarker`; core resolves them
597
+ `-1` remain numeric. Exact READ, EDIT, COPY/MOVE source and destination,
598
+ OPEN/FOLD, and client LOOK preserve these positions in `TextLineMarker`; core resolves them
566
599
  against the addressed current text before operation-specific numeric scope
567
600
  semantics run. A matcher-bearing or path-glob READ normalizes to FIND, whose
568
601
  result positions remain numeric and reject anchors. Numeric text scopes remain
@@ -577,27 +610,27 @@ shapes for runtime validation. An anchor-bearing text scope uses commas; ANTLR
577
610
  tolerates one space after each comma.
578
611
 
579
612
  Apart from the unadvertised three-coordinate text-scope tolerance in
580
- {§text-scope-semantics}, the runtime rejects invalid arity, out-of-range or
613
+ {§text-scope-semantics} and OPEN/FOLD's per-body empty intersection, the runtime rejects invalid arity, out-of-range or
581
614
  inverted positions, and decimal text coordinates rather than rounding or
582
615
  reinterpreting them. FIND owns a deterministic result order so the same
583
616
  inclusive range selects the same positions from unchanged state. The parser
584
617
  does not enforce either condition.
585
618
 
586
- ## §suffix-discipline 8. Suffix Discipline
619
+ ## §delimiter-discipline 8. Delimiter Discipline
587
620
 
588
- The suffix is a turn-wide heading lane. A heading carrying the active lane
621
+ The delimiter is a turn-wide heading lane. A heading carrying the active lane
589
622
  is structural; an otherwise valid PLURNK heading carrying another lane is body
590
623
  text. The lane therefore makes literal or nested PLURNK unambiguous.
591
624
 
592
- Suffix rules:
625
+ Delimiter rules:
593
626
 
594
- - `suffix` is `[A-Za-z0-9_]*`, concatenated to PLAN or OP with no separator.
627
+ - `delimiter` is `[A-Za-z0-9_]*`, concatenated to PLAN or OP with no separator.
595
628
  - The H1 PLAN establishes the lane; every real H2 operation heading in that
596
- turn has the exact same suffix.
597
- - An empty suffix is accepted only by ANTLR ingestion. Canonical teaching and
629
+ turn has the exact same delimiter.
630
+ - An empty delimiter is accepted only by ANTLR ingestion. Canonical teaching and
598
631
  the generated rail use `0` on PLAN and every operation.
599
- - A body may contain any heading whose suffix differs from the active lane.
600
- - To carry a nested turn written with lane `0`, choose another suffix for the
632
+ - A body may contain any heading whose delimiter differs from the active lane.
633
+ - To carry a nested turn written with lane `0`, choose another delimiter for the
601
634
  outer turn and repeat it on every outer heading.
602
635
  - The GBNF deliberately emits only lane `0`. It cannot emit body content that
603
636
  contains a same-lane structural heading; unconstrained producers use another
@@ -633,23 +666,22 @@ transfers directly:
633
666
  |-------|-----------------------------------------------------------------|-------------------------------|
634
667
  | `1xx` | Continue after submitted operations | `102 Processing` |
635
668
  | `2xx` | Conclude successfully or wait on live obligations | `200 OK`, `202 Accepted` |
636
- | `3xx` | Ask the interactive user a stop-the-world multiple choice | `300 Multiple Choices` |
637
669
  | `4xx` | Abandon the loop after a model-side inability | `499` |
638
670
  | `5xx` | Runtime or infrastructure failure; never a model terminal claim | none |
639
671
 
640
672
  ### §waitpid-dispositions The terminal contract (waitpid)
641
673
 
642
674
  The model signals one intention per turn — **continue (102)**, **done
643
- (200)**, **wait (202)**, or **give up (499)**, plus the operator-facing
644
- **question (300)** and the engine verifies the claim against the
645
- loop's live obligations (spawned children, open streams, pending
646
- retrievals); the grammar polices *shape* only. The shape rules ARE
647
- structural:
675
+ (200)**, **wait (202)**, or **give up (499)** and the engine verifies
676
+ the claim against the loop's live obligations (spawned children, open
677
+ streams, pending retrievals); the grammar polices *shape* only. Asking
678
+ the human is the native `question` EXEC tool ({§question-tool}), not a
679
+ disposition. The shape rules ARE structural:
648
680
 
649
- - §send-mid-reservation The five disposition codes `{102, 200, 202, 300, 499}` lex as a
681
+ - §send-mid-reservation The four disposition codes `{102, 200, 202, 499}` lex as a
650
682
  distinct `DISPOSITION` token, making a disposition-coded SEND
651
683
  **structurally terminal**: a statement after it is a parse error
652
- (the mid-termination rule), and the GBNF reserves the five from
684
+ (the mid-termination rule), and the GBNF reserves the four from
653
685
  mid-position SENDs (`status-mid` is their complement over `DDD`).
654
686
  This keeps the grammar's last-SEND model and the dispatcher's
655
687
  first-disposition model coincident.
@@ -700,6 +732,7 @@ stateDiagram-v2
700
732
  TARGET --> TARGET: balanced literals / target escapes
701
733
  TARGET --> SLOTS: target close at depth zero
702
734
  SLOTS --> SLOTS: scope token
735
+ SLOTS --> SLOTS: trailing annotation
703
736
  SLOTS --> BODY: heading line end
704
737
  BODY --> DEFAULT: same-lane heading boundary
705
738
  BODY --> [*]: end of input
@@ -707,13 +740,14 @@ stateDiagram-v2
707
740
 
708
741
  The first H1 PLAN establishes the turn lane. DEFAULT recognizes only an H1
709
742
  PLAN or H2 minted operation carrying that exact lane. SLOTS admits
710
- operation-appropriate signal, target, and scope openers in any order; the parser
711
- grammar enforces at-most-once multiplicity. Signal submodes select tags, integer,
743
+ operation-appropriate signal, target, and scope openers in any order, followed
744
+ by an optional annotation; the parser grammar enforces at-most-once multiplicity.
745
+ Signal submodes select tags, integer,
712
746
  or identifier tokens by operation family. TARGET preserves balanced inner
713
747
  parentheses and recognized target escapes. BODY emits opaque text until a
714
748
  same-lane heading boundary or EOF.
715
749
 
716
- A differently suffixed heading stays BODY text. Multi-turn logs are plain
750
+ A differently delimited heading stays BODY text. Multi-turn logs are plain
717
751
  sequences of independently lane-anchored PLAN turns. Complete native reasoning
718
752
  enclosures before PLAN remain one TEXT token so an operation drafted inside
719
753
  provider reasoning cannot become the turn anchor.
@@ -728,13 +762,15 @@ possible. EOF is a valid body boundary. An unfinished signal or target produces
728
762
  | Location | Canonical generation | Tolerant ANTLR ingestion |
729
763
  |-----------------------------|---------------------------------------|-----------------------------------------------------------|
730
764
  | Heading marker | `# PLAN0` or `## OP0` at column zero | The initial PLAN may directly follow leading TEXT; subsequent headings retain exact depth and column |
731
- | Between OP and suffix | Adjacent | Must remain adjacent |
765
+ | Between OP and delimiter | Adjacent | Must remain adjacent |
732
766
  | Before each header slot | One ASCII space | Zero or more horizontal whitespace characters |
733
767
  | Inside signal | Adjacent values | Horizontal whitespace is ignored; newline is invalid |
734
768
  | Inside target | Path alias plus target escapes | Balanced literals tolerated; newline is invalid |
735
769
  | Inside scope | Comma-separated numbers | Dash separator and one post-comma space are also accepted |
770
+ | Before annotation | One ASCII space | Zero or more horizontal whitespace characters |
771
+ | Inside annotation | One-line prose padded by one space | Any single-line text through the first closing `-->` |
736
772
  | Inside body | Character-perfect | Character-perfect |
737
- | Between canonical sections | One empty separator line | A directly following structural heading is also admitted |
773
+ | Between canonical sections | No empty separator line | One empty separator line is also admitted |
738
774
  | Before the first PLAN | Nothing | Whitespace or TEXT may surface as preamble items without requiring a separator before PLAN |
739
775
 
740
776
  PLURNK never escape-decodes body text: `\n` reaches the owning operation as
@@ -746,7 +782,8 @@ content emit an actual newline.
746
782
  break, and returns it as ordered text items without assigning semantics. Once
747
783
  a heading begins, all nonstructural text belongs to that section body.
748
784
  `parseStatements` and `parseClient` admit H2 statements;
749
- `parseLog` admits consecutive H1 PLAN turns. PLURNK defines no comment syntax.
785
+ `parseLog` admits consecutive H1 PLAN turns. PLURNK defines no general comment
786
+ syntax; only the trailing heading position gives `<!-- … -->` annotation meaning.
750
787
 
751
788
  ## §public-api 12. Public API
752
789
 
@@ -967,19 +1004,35 @@ model-language syntax or model packet teaching.
967
1004
 
968
1005
  ### §mcp-server-definition 13.8 MCP server definitions
969
1006
 
970
- `McpServerDefinition` is the transport-neutral, client/daemon-shared definition
971
- of one workspace MCP server. It is a closed `stdio`/`http` union. The schema
1007
+ `McpServerDefinition` is the transport-neutral normalized definition of one
1008
+ workspace MCP server. It is a closed `stdio`/`http` union. The schema
972
1009
  owns transport-specific fields, enabled/read tool sets, supported HTTP
973
1010
  authorization choices, and symbolic credential references; it carries no
974
1011
  workspace identifier, connection state, discovered catalog, or secret value.
975
- `Validator.assertMcpServerDefinition` is the admission boundary used by every
976
- client interface and the MCP host before persistence or connection work.
1012
+ `Validator.assertMcpServerDefinition` is the MCP host's admission boundary
1013
+ before persistence or connection work.
1014
+
1015
+ §mcp-server-options `McpServerOptions` is the closed client/daemon-shared
1016
+ supplement accepted when adding an MCP server by alias and target. It reuses
1017
+ only `McpServerDefinition` option fields and cannot repeat identity, target, or
1018
+ transport. The target determines the transport; normalization through
1019
+ `McpServerDefinition` rejects options belonging to the other transport.
977
1020
 
978
1021
  Interactive OAuth always requires a callback URL. Its structurally exclusive
979
1022
  identity modes are an HTTPS Client ID Metadata Document URL, a pre-registered
980
1023
  client ID plus symbolic secret, or neither for server-advertised Dynamic Client
981
1024
  Registration fallback. A definition cannot combine those identity modes.
982
1025
 
1026
+ §mcp-configuration-overlay `McpConfigurationOverlay` is the bounded raw
1027
+ configuration projection a client may carry to MCP list and enable actions. It
1028
+ contains only string-valued `PLURNK_MCP_*` server declaration variables;
1029
+ service-owned connection/request timeouts and default enabledness are excluded.
1030
+ The client does not interpret this map. The MCP host composes it over the
1031
+ lower normalized definition through the same parser that admits service
1032
+ environment declarations, then validates the resulting
1033
+ `McpServerDefinition`. Carrying the overlay does not connect, persist, or
1034
+ expand credentials by itself.
1035
+
983
1036
  ## 14. Parse diagnostics
984
1037
 
985
1038
  §parse-diagnostics `PlurnkParseError` is a JSON-serializable Error subclass.
@@ -45,29 +45,32 @@ section-body-b40 ::= ("#" section-body-b1 | [ PLANFDRETCOYMVSXBWK] section-body-
45
45
  section-body-b41 ::= ("#" section-body-b1 | [ PANFIDRETCOYMVSXBWK] section-body-b0 | "L" section-body-b42 | [^\x00-\x08\x0B-\x0C\x0E-\x1F\x7F# PLANFIDRETCOYMVSXBWK] section-body-b0)?
46
46
  section-body-b42 ::= ("#" section-body-b1 | [ PANFIDRETCOYMVSXBWK] section-body-b0 | [^\x00-\x08\x0B-\x0C\x0E-\x1F\x7F# PLANFIDRETCOYMVSXBWK] section-body-b0)?
47
47
  section-body-ne ::= "#" section-body-b1 | [ PLANFIDRETCOYMVSXBWK] section-body-b0 | [^\x00-\x08\x0B-\x0C\x0E-\x1F\x7F# PLANFIDRETCOYMVSXBWK] section-body-b0
48
+ annotation-body-b0 ::= ("-" annotation-body-b1 | ">" annotation-body-b0 | [^\x00-\x08\x0B-\x0C\x0E-\x1F\x7F\n\r>-] annotation-body-b0)?
49
+ annotation-body-b1 ::= ("-" annotation-body-b2 | ">" annotation-body-b0 | [^\x00-\x08\x0B-\x0C\x0E-\x1F\x7F\n\r>-] annotation-body-b0)?
50
+ annotation-body-b2 ::= ("-" annotation-body-b2 | [^\x00-\x08\x0B-\x0C\x0E-\x1F\x7F\n\r>-] annotation-body-b0)?
51
+ annotation-body-ne ::= "-" annotation-body-b1 | ">" annotation-body-b0 | [^\x00-\x08\x0B-\x0C\x0E-\x1F\x7F\n\r>-] annotation-body-b0
48
52
  pattern-body-ne ::= [^\x00-\x08\x0B-\x0C\x0E-\x1F\x7F\n\r:#] [^\x00-\x08\x0B-\x0C\x0E-\x1F\x7F\n\r]*
49
53
  log-selection ::= (curation-tags-slot log-target-slot? | log-target-slot)?
50
- plan ::= "# PLAN0" "\n" section-body-ne "\n\n"
51
- find ::= "## FIND0" add-tags-slot? target-slot line-slot? "\n\n" | "## FIND0" add-tags-slot? target-slot line-slot? "\n" pattern-body-ne "\n\n"
52
- read ::= "## READ0" add-tags-slot? target-slot text-line-slot? "\n\n" | "## READ0" add-tags-slot? target-slot text-line-slot? "\n" pattern-body-ne "\n\n"
53
- edit ::= "## EDIT0" add-tags-slot? target-slot text-line-slot? "\n\n" | "## EDIT0" add-tags-slot? target-slot text-line-slot? "\n" section-body-ne "\n\n"
54
- copy ::= "## COPY0" add-tags-slot? target-slot text-line-slot? "\n" section-body-ne "\n\n"
55
- move ::= "## MOVE0" add-tags-slot? target-slot text-line-slot? "\n" section-body-ne "\n\n"
56
- open ::= "## OPEN0" log-selection "\n\n" | "## OPEN0" log-selection "\n" pattern-body-ne "\n\n"
57
- fold ::= "## FOLD0" log-selection "\n\n" | "## FOLD0" log-selection "\n" pattern-body-ne "\n\n"
58
- exec ::= "## EXEC0" exec-slot? target-slot? line-slot? "\n\n" | "## EXEC0" exec-slot? target-slot? line-slot? "\n" section-body-ne "\n\n"
59
- bare ::= "## BARE0" add-tags-slot? "\n" section-body-ne "\n\n"
60
- work ::= "## WORK0" branch-slot? target-slot "\n" section-body-ne "\n\n"
61
- fork ::= "## FORK0" branch-slot? target-slot "\n" section-body-ne "\n\n"
62
- kill ::= "## KILL0" kill-slot? target-slot "\n\n"
63
- send-mid ::= "## SEND0" status-mid-slot target-slot? "\n\n" | "## SEND0" status-mid-slot target-slot? "\n" section-body-ne "\n\n" | "## SEND0" target-slot? "\n\n" | "## SEND0" target-slot? "\n" section-body-ne "\n\n"
64
- send-102 ::= "## SEND0 [102]" target-slot? "\n" section-body-ne
65
- send-200 ::= "## SEND0 [200]" target-slot? "\n" section-body-ne
66
- send-202 ::= "## SEND0 [202]" target-slot? park-slot? "\n" section-body-ne
67
- send-300 ::= "## SEND0 [300]" target-slot? "\n" section-body-ne
68
- send-499 ::= "## SEND0 [499]" target-slot? "\n" section-body-ne
69
- send-final-any ::= send-102 | send-200 | send-202 | send-300 | send-499
70
- send-final-first ::= send-200 | send-202 | send-300 | send-499
54
+ plan ::= "# PLAN0" annotation-slot? "\n" section-body-ne "\n"
55
+ find ::= "## FIND0" add-tags-slot? target-slot line-slot? annotation-slot? "\n" | "## FIND0" add-tags-slot? target-slot line-slot? annotation-slot? "\n" pattern-body-ne "\n"
56
+ read ::= "## READ0" add-tags-slot? target-slot text-line-slot? annotation-slot? "\n" | "## READ0" add-tags-slot? target-slot text-line-slot? annotation-slot? "\n" pattern-body-ne "\n"
57
+ edit ::= "## EDIT0" add-tags-slot? target-slot text-line-slot? annotation-slot? "\n" | "## EDIT0" add-tags-slot? target-slot text-line-slot? annotation-slot? "\n" section-body-ne "\n"
58
+ copy ::= "## COPY0" add-tags-slot? target-slot text-line-slot? annotation-slot? "\n" section-body-ne "\n"
59
+ move ::= "## MOVE0" add-tags-slot? target-slot text-line-slot? annotation-slot? "\n" section-body-ne "\n"
60
+ open ::= "## OPEN0" log-selection text-line-slot? annotation-slot? "\n" | "## OPEN0" log-selection text-line-slot? annotation-slot? "\n" pattern-body-ne "\n"
61
+ fold ::= "## FOLD0" log-selection text-line-slot? annotation-slot? "\n" | "## FOLD0" log-selection text-line-slot? annotation-slot? "\n" pattern-body-ne "\n"
62
+ exec ::= "## EXEC0" exec-slot? target-slot? line-slot? annotation-slot? "\n" | "## EXEC0" exec-slot? target-slot? line-slot? annotation-slot? "\n" section-body-ne "\n"
63
+ bare ::= "## BARE0" add-tags-slot? annotation-slot? "\n" section-body-ne "\n"
64
+ work ::= "## WORK0" branch-slot? target-slot annotation-slot? "\n" section-body-ne "\n"
65
+ fork ::= "## FORK0" branch-slot? target-slot annotation-slot? "\n" section-body-ne "\n"
66
+ kill ::= "## KILL0" kill-slot? target-slot annotation-slot? "\n"
67
+ send-mid ::= "## SEND0" status-mid-slot target-slot? annotation-slot? "\n" | "## SEND0" status-mid-slot target-slot? annotation-slot? "\n" section-body-ne "\n" | "## SEND0" target-slot? annotation-slot? "\n" | "## SEND0" target-slot? annotation-slot? "\n" section-body-ne "\n"
68
+ send-102 ::= "## SEND0 [102]" target-slot? annotation-slot? "\n" section-body-ne
69
+ send-200 ::= "## SEND0 [200]" target-slot? annotation-slot? "\n" section-body-ne
70
+ send-202 ::= "## SEND0 [202]" target-slot? park-slot? annotation-slot? "\n" section-body-ne
71
+ send-499 ::= "## SEND0 [499]" target-slot? annotation-slot? "\n" section-body-ne
72
+ send-final-any ::= send-102 | send-200 | send-202 | send-499
73
+ send-final-first ::= send-200 | send-202 | send-499
71
74
  op-statement ::= find | read | edit | copy | move | open | fold | exec | bare | work | fork | kill
72
75
  tail-0 ::= send-mid tail-1 | op-statement tail-1 | send-final-first
73
76
  tail-1 ::= send-mid tail-2 | op-statement tail-2 | send-final-any
@@ -126,10 +129,11 @@ branch-slot ::= " " branch
126
129
  kill-slot ::= " " kill-sig
127
130
  status-mid-slot ::= " [" status-mid "]"
128
131
  park-slot ::= " " park
132
+ annotation-slot ::= " <!-- " annotation-body-ne " -->"
129
133
  status-mid ::= [05-9] [0-9] [0-9] | "1" status-mid-1 | "2" status-mid-2 | "3" status-mid-3 | "4" status-mid-4
130
134
  status-mid-1 ::= "0" [0-13-9] | [1-9] [0-9]
131
135
  status-mid-2 ::= "0" [13-9] | [1-9] [0-9]
132
- status-mid-3 ::= "0" [1-9] | [1-9] [0-9]
136
+ status-mid-3 ::= "0" [0-9] | [1-9] [0-9]
133
137
  status-mid-4 ::= "9" [0-8] | [0-8] [0-9]
134
138
  add-tags ::= "[" add-tag add-tag-rest* "]"
135
139
  curation-tags ::= "[" curation-term curation-term-rest* "]"
@@ -45,29 +45,32 @@ section-body-b40 ::= ("#" section-body-b1 | [ PLANFDRETCOYMVSXBWK] section-body-
45
45
  section-body-b41 ::= ("#" section-body-b1 | [ PANFIDRETCOYMVSXBWK] section-body-b0 | "L" section-body-b42 | [^\x00-\x08\x0B-\x0C\x0E-\x1F\x7F# PLANFIDRETCOYMVSXBWK] section-body-b0)?
46
46
  section-body-b42 ::= ("#" section-body-b1 | [ PANFIDRETCOYMVSXBWK] section-body-b0 | [^\x00-\x08\x0B-\x0C\x0E-\x1F\x7F# PLANFIDRETCOYMVSXBWK] section-body-b0)?
47
47
  section-body-ne ::= "#" section-body-b1 | [ PLANFIDRETCOYMVSXBWK] section-body-b0 | [^\x00-\x08\x0B-\x0C\x0E-\x1F\x7F# PLANFIDRETCOYMVSXBWK] section-body-b0
48
+ annotation-body-b0 ::= ("-" annotation-body-b1 | ">" annotation-body-b0 | [^\x00-\x08\x0B-\x0C\x0E-\x1F\x7F\n\r>-] annotation-body-b0)?
49
+ annotation-body-b1 ::= ("-" annotation-body-b2 | ">" annotation-body-b0 | [^\x00-\x08\x0B-\x0C\x0E-\x1F\x7F\n\r>-] annotation-body-b0)?
50
+ annotation-body-b2 ::= ("-" annotation-body-b2 | [^\x00-\x08\x0B-\x0C\x0E-\x1F\x7F\n\r>-] annotation-body-b0)?
51
+ annotation-body-ne ::= "-" annotation-body-b1 | ">" annotation-body-b0 | [^\x00-\x08\x0B-\x0C\x0E-\x1F\x7F\n\r>-] annotation-body-b0
48
52
  pattern-body-ne ::= [^\x00-\x08\x0B-\x0C\x0E-\x1F\x7F\n\r:#] [^\x00-\x08\x0B-\x0C\x0E-\x1F\x7F\n\r]*
49
53
  log-selection ::= (curation-tags-slot log-target-slot? | log-target-slot)?
50
- plan ::= "# PLAN0" "\n" section-body-ne "\n\n"
51
- find ::= "## FIND0" add-tags-slot? target-slot line-slot? "\n\n" | "## FIND0" add-tags-slot? target-slot line-slot? "\n" pattern-body-ne "\n\n"
52
- read ::= "## READ0" add-tags-slot? target-slot text-line-slot? "\n\n" | "## READ0" add-tags-slot? target-slot text-line-slot? "\n" pattern-body-ne "\n\n"
53
- edit ::= "## EDIT0" add-tags-slot? target-slot text-line-slot? "\n\n" | "## EDIT0" add-tags-slot? target-slot text-line-slot? "\n" section-body-ne "\n\n"
54
- copy ::= "## COPY0" add-tags-slot? target-slot text-line-slot? "\n" section-body-ne "\n\n"
55
- move ::= "## MOVE0" add-tags-slot? target-slot text-line-slot? "\n" section-body-ne "\n\n"
56
- open ::= "## OPEN0" log-selection "\n\n" | "## OPEN0" log-selection "\n" pattern-body-ne "\n\n"
57
- fold ::= "## FOLD0" log-selection "\n\n" | "## FOLD0" log-selection "\n" pattern-body-ne "\n\n"
58
- exec ::= "## EXEC0" exec-slot? target-slot? line-slot? "\n\n" | "## EXEC0" exec-slot? target-slot? line-slot? "\n" section-body-ne "\n\n"
59
- bare ::= "## BARE0" add-tags-slot? "\n" section-body-ne "\n\n"
60
- work ::= "## WORK0" branch-slot? target-slot "\n" section-body-ne "\n\n"
61
- fork ::= "## FORK0" branch-slot? target-slot "\n" section-body-ne "\n\n"
62
- kill ::= "## KILL0" kill-slot? target-slot "\n\n"
63
- send-mid ::= "## SEND0" status-mid-slot target-slot? "\n\n" | "## SEND0" status-mid-slot target-slot? "\n" section-body-ne "\n\n" | "## SEND0" target-slot? "\n\n" | "## SEND0" target-slot? "\n" section-body-ne "\n\n"
64
- send-102 ::= "## SEND0 [102]" target-slot? "\n" section-body-ne
65
- send-200 ::= "## SEND0 [200]" target-slot? "\n" section-body-ne
66
- send-202 ::= "## SEND0 [202]" target-slot? park-slot? "\n" section-body-ne
67
- send-300 ::= "## SEND0 [300]" target-slot? "\n" section-body-ne
68
- send-499 ::= "## SEND0 [499]" target-slot? "\n" section-body-ne
69
- send-final-any ::= send-102 | send-200 | send-202 | send-300 | send-499
70
- send-final-first ::= send-200 | send-202 | send-300 | send-499
54
+ plan ::= "# PLAN0" annotation-slot? "\n" section-body-ne "\n"
55
+ find ::= "## FIND0" add-tags-slot? target-slot line-slot? annotation-slot? "\n" | "## FIND0" add-tags-slot? target-slot line-slot? annotation-slot? "\n" pattern-body-ne "\n"
56
+ read ::= "## READ0" add-tags-slot? target-slot text-line-slot? annotation-slot? "\n" | "## READ0" add-tags-slot? target-slot text-line-slot? annotation-slot? "\n" pattern-body-ne "\n"
57
+ edit ::= "## EDIT0" add-tags-slot? target-slot text-line-slot? annotation-slot? "\n" | "## EDIT0" add-tags-slot? target-slot text-line-slot? annotation-slot? "\n" section-body-ne "\n"
58
+ copy ::= "## COPY0" add-tags-slot? target-slot text-line-slot? annotation-slot? "\n" section-body-ne "\n"
59
+ move ::= "## MOVE0" add-tags-slot? target-slot text-line-slot? annotation-slot? "\n" section-body-ne "\n"
60
+ open ::= "## OPEN0" log-selection text-line-slot? annotation-slot? "\n" | "## OPEN0" log-selection text-line-slot? annotation-slot? "\n" pattern-body-ne "\n"
61
+ fold ::= "## FOLD0" log-selection text-line-slot? annotation-slot? "\n" | "## FOLD0" log-selection text-line-slot? annotation-slot? "\n" pattern-body-ne "\n"
62
+ exec ::= "## EXEC0" exec-slot? target-slot? line-slot? annotation-slot? "\n" | "## EXEC0" exec-slot? target-slot? line-slot? annotation-slot? "\n" section-body-ne "\n"
63
+ bare ::= "## BARE0" add-tags-slot? annotation-slot? "\n" section-body-ne "\n"
64
+ work ::= "## WORK0" branch-slot? target-slot annotation-slot? "\n" section-body-ne "\n"
65
+ fork ::= "## FORK0" branch-slot? target-slot annotation-slot? "\n" section-body-ne "\n"
66
+ kill ::= "## KILL0" kill-slot? target-slot annotation-slot? "\n"
67
+ send-mid ::= "## SEND0" status-mid-slot target-slot? annotation-slot? "\n" | "## SEND0" status-mid-slot target-slot? annotation-slot? "\n" section-body-ne "\n" | "## SEND0" target-slot? annotation-slot? "\n" | "## SEND0" target-slot? annotation-slot? "\n" section-body-ne "\n"
68
+ send-102 ::= "## SEND0 [102]" target-slot? annotation-slot? "\n" section-body-ne
69
+ send-200 ::= "## SEND0 [200]" target-slot? annotation-slot? "\n" section-body-ne
70
+ send-202 ::= "## SEND0 [202]" target-slot? park-slot? annotation-slot? "\n" section-body-ne
71
+ send-499 ::= "## SEND0 [499]" target-slot? annotation-slot? "\n" section-body-ne
72
+ send-final-any ::= send-102 | send-200 | send-202 | send-499
73
+ send-final-first ::= send-200 | send-202 | send-499
71
74
  op-statement ::= find | read | edit | copy | move | open | fold | exec | bare | work | fork | kill
72
75
  tail-0 ::= send-mid tail-1 | op-statement tail-1 | send-final-first
73
76
  tail-1 ::= send-mid tail-2 | op-statement tail-2 | send-final-any
@@ -115,10 +118,11 @@ branch-slot ::= " " branch
115
118
  kill-slot ::= " " kill-sig
116
119
  status-mid-slot ::= " [" status-mid "]"
117
120
  park-slot ::= " " park
121
+ annotation-slot ::= " <!-- " annotation-body-ne " -->"
118
122
  status-mid ::= [05-9] [0-9] [0-9] | "1" status-mid-1 | "2" status-mid-2 | "3" status-mid-3 | "4" status-mid-4
119
123
  status-mid-1 ::= "0" [0-13-9] | [1-9] [0-9]
120
124
  status-mid-2 ::= "0" [13-9] | [1-9] [0-9]
121
- status-mid-3 ::= "0" [1-9] | [1-9] [0-9]
125
+ status-mid-3 ::= "0" [0-9] | [1-9] [0-9]
122
126
  status-mid-4 ::= "9" [0-8] | [0-8] [0-9]
123
127
  add-tags ::= "[" add-tag add-tag-rest* "]"
124
128
  curation-tags ::= "[" curation-term curation-term-rest* "]"
@@ -15,6 +15,9 @@
15
15
  { "type": "null" }
16
16
  ]
17
17
  },
18
+ "AnnotationOrNull": {
19
+ "type": ["string", "null"]
20
+ },
18
21
  "PathOrNull": {
19
22
  "oneOf": [
20
23
  { "$ref": "https://schemas.plurnk.dev/v0/ParsedPath.json" },
@@ -44,11 +47,12 @@
44
47
  },
45
48
  "LookStatement": {
46
49
  "type": "object",
47
- "required": ["op", "suffix", "signal", "target", "lineMarker", "body", "position"],
50
+ "required": ["op", "delimiter", "annotation", "signal", "target", "lineMarker", "body", "position"],
48
51
  "additionalProperties": false,
49
52
  "properties": {
50
53
  "op": { "const": "LOOK" },
51
- "suffix": { "type": "string" },
54
+ "delimiter": { "type": "string" },
55
+ "annotation": { "$ref": "#/$defs/AnnotationOrNull" },
52
56
  "signal": { "$ref": "#/$defs/TagSignal" },
53
57
  "target": { "$ref": "#/$defs/PathOrNull" },
54
58
  "lineMarker": { "$ref": "#/$defs/TextLineMarkerOrNull" },
@@ -58,11 +62,12 @@
58
62
  },
59
63
  "BuffStatement": {
60
64
  "type": "object",
61
- "required": ["op", "suffix", "signal", "target", "lineMarker", "body", "position"],
65
+ "required": ["op", "delimiter", "annotation", "signal", "target", "lineMarker", "body", "position"],
62
66
  "additionalProperties": false,
63
67
  "properties": {
64
68
  "op": { "const": "BUFF" },
65
- "suffix": { "type": "string" },
69
+ "delimiter": { "type": "string" },
70
+ "annotation": { "$ref": "#/$defs/AnnotationOrNull" },
66
71
  "signal": { "$ref": "#/$defs/TagSignal" },
67
72
  "target": { "$ref": "#/$defs/PathOrNull" },
68
73
  "lineMarker": { "$ref": "#/$defs/LineMarkerOrNull" },
@@ -0,0 +1,25 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://schemas.plurnk.dev/v0/McpConfigurationOverlay.json",
4
+ "title": "McpConfigurationOverlay",
5
+ "type": "object",
6
+ "propertyNames": {
7
+ "allOf": [
8
+ {
9
+ "pattern": "^PLURNK_MCP_[A-Za-z0-9_.+-]+$"
10
+ },
11
+ {
12
+ "not": {
13
+ "enum": [
14
+ "PLURNK_MCP_CONNECT_TIMEOUT",
15
+ "PLURNK_MCP_REQUEST_TIMEOUT",
16
+ "PLURNK_MCP_ENABLED"
17
+ ]
18
+ }
19
+ }
20
+ ]
21
+ },
22
+ "additionalProperties": {
23
+ "type": "string"
24
+ }
25
+ }
@@ -94,7 +94,8 @@
94
94
  "type": { "const": "client-credentials" },
95
95
  "clientId": { "type": "string", "minLength": 1 },
96
96
  "clientSecret": { "$ref": "#/$defs/environmentReference" },
97
- "scope": { "type": "string", "minLength": 1 }
97
+ "scope": { "type": "string", "minLength": 1 },
98
+ "issuer": { "type": "string", "minLength": 1 }
98
99
  }
99
100
  }
100
101
  ]