diff --git a/ROADMAP.md b/ROADMAP.md index 8c769fa..934b0c6 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -45,10 +45,10 @@ A size given as `M or L` is conditional on a decision named in that slice's file | 040 | Skills registry + agentskills.io format + progressive disclosure | 4 Skills | M | 020 | planned | | 041 | Skill self-management with staged approval + scanner | 4 Skills | M | 040, 021 | planned | | 050 | Scheduler: Oban cron agent tasks with delivery targets | 5 Automation | M | 012 | planned | -| 059 | MCP library spike (finalises ADR-0007) | 6 MCP | M | 020 | planned | -| 060 | MCP client (2026-07-28 preferred, 2025-11-25 compat, MRTR, Tasks, OAuth client) | 6 MCP | M/L | 059, 021 | planned | +| 059 | MCP capability gap against beam_mcp, and the server seam probe | 6 MCP | S/M | 020 | planned | +| 060 | MCP client: Trinity's thin driver (2026-07-28 preferred, 2025-11-25 compat, MRTR, Tasks) | 6 MCP | L | 059, 021 | planned | | 061 | MCP server (stateless 2026-07-28 + compat, MRTR approvals, headless profile) | 6 MCP | M | 060, 024 | planned | -| 062 | MCP authorization: RS + embedded AS + Enterprise Managed Authorization (ID-JAG) | 6 MCP | L | 061 | planned | +| 062 | MCP authorization: OAuth client role, RS, embedded AS, Enterprise Managed Authorization (ID-JAG) | 6 MCP | L | 061 | planned | | 070 | Gateway core: adapter behaviour, routing, PubSub fan-out | 7 Gateways | M | 012 | planned | | 071 | Gateway: Telegram | 7 Gateways | S | 070 | planned | | 072 | Gateway: Discord (Nostrum) | 7 Gateways | S | 070 | planned | @@ -110,3 +110,4 @@ After 012, multiple branches are independent. If running more than one coding ag | 2026-09-05 | Authority made an adapter behind a behaviour rather than a mode (ADR-0008, ADR-0010). ADR-0009 opened for the Jido question, decided at the 012 checkpoint. Slice 024 added: effect catalog, `TRINITY_AUTHORITY`, local receipts. Alignment appendices on 012, 020, 021, 022, 023, 030, 032, 040, 041. | | 2026-09-05 | Review pass before commit 1. Counts in the three entries above were typed, not derived, and none matches the tree; a decreasing count is impossible under insert-never-renumber. Derived this date: `find slices -name SLICE.md | wc -l` → **38**. Milestones are derived from the Milestones table, not from memory. From here, any count in this log names the command that produced it. Entries above are not rewritten. | | 2026-09-06 | Plan corrections, round 2. **Supersedes the slice count in the entry above:** that entry derived **38** on 2026-09-05, before slices 082, 083 and 084 were withdrawn and slice 034 was added. Re-derived this date, not adjusted by arithmetic: `find slices -name SLICE.md | wc -l` → **36**. The entry above is not rewritten. `scripts/plan_check.sh` now enforces this count, the acceptance-criteria numbering, the Definition-of-Done ranges, ROADMAP/SLICE.md agreement, and the absence of references to paths not in `git ls-files`. | +| 2026-09-20 | The MCP phase replanned under owner decisions of 2026-09-08, recorded in ADR-0007 decisions 5 to 8: beam_mcp 0.8.0 is the server core; 059 measures the capability gap and probes the `:server` seam (S/M); 060 is Trinity's thin driver (L, the M/L condition decided); the OAuth client role moves from 060 to 062; 061 carries a named blocker on the MRTR wrapper; the four `M5 Always-on` headers on 059 to 062 read M5a Automates, as this file has since 2026-09-08. R14 re-scoped, R15 closed. Re-derived this date: `find slices -name SLICE.md \| wc -l` → **36**. | diff --git a/VERSIONS.md b/VERSIONS.md index c2e76c7..93e9fff 100644 --- a/VERSIONS.md +++ b/VERSIONS.md @@ -89,7 +89,7 @@ never pin a version hex marks as retired or vulnerable. | `oban` | ~> 2.24 | 🔍 not yet a dependency | Uses `Oban.Engines.Lite` on SQLite. ⚠️ Oban Pro Workflows/Smart engine are Postgres-only. Added at Slice 050. | | `req` | ~> 0.5 | ✅ in `mix.lock` | HTTP client. | | `req_llm` | ~> 1.22 | 🔍 not yet a dependency | Provider layer (streaming, tools, structured output, usage). ⚠️ The pin was `~> 1.10` against a recorded latest of 1.10.0; the real latest was twelve minors ahead. Check event shapes against the current version at Slice 011, not against this file's prose. Added at Slice 011. | -| `MCP library` | **decided by Slice 059** | 🔍 not a single package | Candidates verified 2026-09-05: **anubis_mcp** 2.0.x (hex updated 2026-08-07, **LGPL-3.0**, spec ≤ 2025-11-25); **fastest_mcp** 0.3.2 (2026-08-28, Apache-2.0, very new, ~400 total downloads); **gen_mcp** 2.0.0 (2026-07-30, server-only stateless + compat plug, MIT). ⚠️ None speaks 2024-11-05, which is obsolete and not a target. Undecided, so no lock key. | +| `beam_mcp` | ~> 0.8 | 🔍 not yet a dependency | MCP server core, Apache-2.0, ADR-0007 decision 5 (owner decision 2026-09-08, recorded 2026-09-20). 0.8.0 on hex.pm, standing before 1.0.0. Server side only: the client, MRTR and OAuth are Trinity's, above it. Added at Slice 059. The earlier candidate list (anubis_mcp, fastest_mcp, gen_mcp) is history. | | `jido` | ~> 2.3 (pending ADR-0009) | 🔍 not yet a dependency | Actions, directives and the effect boundary, if the Slice 012 checkpoint adopts it. | | `jason` | ~> 1.2 | ✅ in `mix.lock` | | | `boundary` | ~> 0.10 | ✅ in `mix.lock` | Compile-time module dependency enforcement. Measured at Slice 000: it compiles and enforces on Elixir 1.20.4 / OTP 28, and it reports violations as **warnings**, so it enforces only while `--warnings-as-errors` is on the compile step. ⚠️ No release since 2024-09-25. | diff --git a/docs/06-risk-register.md b/docs/06-risk-register.md index c3fe09f..e0e2240 100644 --- a/docs/06-risk-register.md +++ b/docs/06-risk-register.md @@ -15,8 +15,8 @@ | R11 | Stale or single-maintainer libs (**boundary**, hnswlib, ex_tauri, sqlite_vec, nostrum, telegex) | High | Med–High | No release in 6 months. Measured 2026-09-05: boundary 2024-09-25, sqlite_vec 2024-11-19, telegex 1.9.0-rc.0 2024-09-18, nostrum 2025-03-02, the trigger already fires for four of them | All behind behaviours; vendor if needed. **boundary is the highest-consequence one**: ADR-0001, docs/01, CLAUDE.md §5 and Slice 000 AC4 all rest on it, and it is unverified on Elixir 1.20. Probe it before anything is built on it | 000, 032, 072 | | R12 | Secrets leak into logs/DB/commits | Low | High | grep hits in CI | `mix gate` includes a secret scan (gitleaks-style regex) from 000; Secrets module from 100 | 000, 100 | | R13 | Scope drifts toward matching other agents' breadth instead of shipping depth | Med | Low | Slice scope grows during a phase | Non-goals in docs/00 are binding; breadth is a later decision, not a default | none | -| R14 | Elixir MCP libraries lag the 2026-07-28 spec; the one that claims it is weeks old | High | Med | 059 probes fail | Behaviour boundaries; own minimal stateless server as fallback; fastest_mcp/gen_mcp/anubis compared by measurement | 059 | -| R15 | anubis_mcp is LGPL-3.0 | Med | Med | It wins the 059 spike | Legal review before adoption in a distributed binary; prefer Apache or MIT candidates | 059 | +| R14 | beam_mcp's will-not-implement page keeps MRTR, the client role and OAuth out of the core; each is Trinity's to build above it (re-scoped 2026-09-20, was library lag) | High | Med | 059 FINDINGS names a gap 060 to 062 did not plan for | The layering rule in ADR-0007 decision 6; the `:server` seam asked for, with the fork as recorded fallback; boundaries around `Trinity.MCP.*` | 059 | +| R15 | Closed 2026-09-20: the server core is beam_mcp, Apache-2.0 (ADR-0007 decision 5); no LGPL candidate remains | none | none | none | none | 059 | | R20 | Foundation donation may require transferring assets or marks the project intends to keep | Med | Med | Proposal drafting (122) | Unverified: the requirement is asserted from an announcement, not from the charter text. Read the charter, then decide what is offered and what is retained. Legal review before any proposal leaves the tree | 122 | | R21 | The tree carries IP that is not this project's to publish | Med | High | Any design in Trinity that reproduces a third party's protected mechanism | Trinity's receipt and policy design is its own. Policy identifiers stay out of `signed_payload` until legal review clears them; 024's field set is reviewed before that slice starts | 024, 120, 122 | | R22 | Single maintainer; Growth needs two unaffiliated production users and commits from two orgs | High | Med | 6-month Sandbox checkpoint | Sandbox tolerates it; GOVERNANCE.md documents intent to grow; recruit co-maintainers via the Elixir community once public | 120, 122 | diff --git a/docs/adr/0007-mcp-2026-07-28-target-and-library.md b/docs/adr/0007-mcp-2026-07-28-target-and-library.md index 749bcbb..e8a6f14 100644 --- a/docs/adr/0007-mcp-2026-07-28-target-and-library.md +++ b/docs/adr/0007-mcp-2026-07-28-target-and-library.md @@ -1,5 +1,5 @@ # ADR-0007 — Target MCP 2026-07-28; choose the Elixir MCP library by spike -Status: proposed → finalised by Slice 059 · Date: 2026-09-05 +Status: accepted · Date: 2026-09-05 · Decision 5 recorded 2026-09-20, superseding decision 3 ## Context The plan originally targeted anubis_mcp (spec ≤ 2025-11-25). On 2026-07-28 MCP shipped its largest revision: @@ -21,3 +21,38 @@ stateless core, MRTR, extensions (Tasks, Apps, EMA), auth hardening (CIMD over D - Slices 060–062 are rewritten against 2026-07-28 semantics. - `VERSIONS.md` MCP rows are provisional until 059. - Risk register gains R14 (library lag) and R15 (LGPL). + +## Decision, appended 2026-09-20 + +Owner decision of 2026-09-08, recorded here on 2026-09-20. Nothing above is rewritten; decision 3 is +superseded by decision 5 and its candidate list is history, not an input. + +5. **The server core is `beam_mcp`**, pinned at 0.8.0 (`v0.8.0` = `cfa706b` on its `main`, on hex.pm, + Apache-2.0). Slice 059 no longer chooses a library; it measures beam_mcp's capability gap against the + 2026-07-28 checklist and probes one seam. Decision 4 stands unchanged: `Trinity.MCP.Client` and + `Trinity.MCP.Server` are the only modules that import `BeamMCP.*`, enforced by `boundary`. +6. **The layering rule.** beam_mcp holds no authority: no risk tiers, approvals, receipts, masking or + authority in the package, by its own plan and by its will-not-implement page, each entry with an + enforcing test. An approval-shaped `input_required` is an authority act, so multi-round-trip requests + (MRTR) live above the core, in a sibling package that wraps `BeamMCP.Server` and decodes nothing, and + the envelope inside `requestState` is minted and validated by Trinity. The core needs one seam for that: + a `:server` module option on `BeamMCP.Transport.HTTP`, default `BeamMCP.Server`. That option is beam_mcp's + to add; Trinity asks for it and does not fork the transport. If the seam is refused and the refusal is + recorded, the fallback is a fork of the dispatch function into Trinity, recorded as the worse option. +7. **The client is Trinity's own thin driver** (slice 060). beam_mcp does not build a client and its + will-not-implement page pins that. The driver builds the outbound JSON-RPC request and nothing else of the + protocol; decoding and validation call beam_mcp's public functions. If the driver grows a second + protocol core, the slice stops and the question of a shared client package is raised instead of forking. +8. **OAuth in every role is Trinity's** (slice 062): the resource server and the embedded authorization + server live above the core, and the driver consumes tokens the 062 client role obtains. + +| Layer | Package | Status at this record | +|---|---|---| +| Server core | beam_mcp 0.8.0 | shipped | +| MRTR wrapper | sibling package; name and tree are the owner's call | proposed | +| Client driver | Trinity slice 060 | planned | +| Authorization, all roles | Trinity slice 062, extractable at slice 123 | planned | + +Consequences: `VERSIONS.md` gains a `beam_mcp ~> 0.8` row, marked not yet a dependency until 059 adds it; +R14 in the risk register is re-scoped from library lag to the will-not-implement gap; R15 (LGPL) is closed; +slices 059 to 062 are amended in the same change as this record. diff --git a/lib/trinity/versions.ex b/lib/trinity/versions.ex index af1d1b9..e4e48df 100644 --- a/lib/trinity/versions.ex +++ b/lib/trinity/versions.ex @@ -145,11 +145,11 @@ defmodule Trinity.Versions do "Provider layer (streaming, tools, structured output, usage). ⚠️ The pin was `~> 1.10` against a recorded latest of 1.10.0; the real latest was twelve minors ahead. Check event shapes against the current version at Slice 011, not against this file's prose. Added at Slice 011." }, %{ - name: "MCP library", - pin: "**decided by Slice 059**", - lock: nil, + name: "beam_mcp", + pin: "~> 0.8", + lock: "beam_mcp", note: - "Candidates verified 2026-09-05: **anubis_mcp** 2.0.x (hex updated 2026-08-07, **LGPL-3.0**, spec ≤ 2025-11-25); **fastest_mcp** 0.3.2 (2026-08-28, Apache-2.0, very new, ~400 total downloads); **gen_mcp** 2.0.0 (2026-07-30, server-only stateless + compat plug, MIT). ⚠️ None speaks 2024-11-05, which is obsolete and not a target. Undecided, so no lock key." + "MCP server core, Apache-2.0, ADR-0007 decision 5 (owner decision 2026-09-08, recorded 2026-09-20). 0.8.0 on hex.pm, standing before 1.0.0. Server side only: the client, MRTR and OAuth are Trinity's, above it. Added at Slice 059. The earlier candidate list (anubis_mcp, fastest_mcp, gen_mcp) is history." }, %{ name: "jido", diff --git a/slices/059-mcp-library-spike/SLICE.md b/slices/059-mcp-library-spike/SLICE.md index c026947..8a7c3cf 100644 --- a/slices/059-mcp-library-spike/SLICE.md +++ b/slices/059-mcp-library-spike/SLICE.md @@ -1,56 +1,90 @@ -# Slice 059: MCP library spike (finalises ADR-0007) +# Slice 059: MCP capability gap against beam_mcp, and the server seam probe | Field | Value | |---|---| | Phase | 6 MCP | -| Milestone | M5 Always-on | -| Size | M | +| Milestone | M5a Automates | +| Size | S/M | | Depends on | 020 | +Supersedes the 2026-09-05 draft, which was a library selection spike. ADR-0007 decision 5 (owner decision +2026-09-08, recorded 2026-09-20) names beam_mcp the server core; this slice measures, it does not choose. + ## Goal -Choose the Elixir MCP implementation for client and server against a fixed checklist, by running each candidate, -not by reading READMEs. Output: ADR-0007 finalised with measurements; `VERSIONS.md` MCP rows flipped to ✅. - -## Candidates -`fastest_mcp` (Apache-2.0, claims 2026-07-28 + 2025-11-25 client+server), `gen_mcp` 2.x (server only, 2026-07-28 + -compat plug), `anubis_mcp` 2.x (LGPL-3.0, ≤ 2025-11-25), own minimal stateless server + a candidate client. - -## Checklist (each item is a runnable probe with pasted output) -1. Server: stateless 2026-07-28 over Streamable HTTP as a Plug in Phoenix; `server/discover`; `Mcp-Method`/`Mcp-Name` - headers; `ttlMs`/`cacheScope` on lists; deterministic tool order; MRTR `input_required` round trip. -2. Server: 2025-11-25 client served from the same endpoint (compat). -3. Client: connect to a 2026-07-28 server and a 2025-11-25 server; MRTR retry loop; Tasks extension poll; stdio + HTTP. -4. Client: behaviour against a server advertising a revision the candidate does not speak. The failure must be - legible and recoverable, not a hang or a silent downgrade. Record the exact failure. -5. OAuth RS hooks (PRM endpoint, bearer validation hook) present or feasible. -6. License, maintainer count, release cadence, download counts (from hex.pm, dated). -7. Binary size delta in a Burrito build. +`FINDINGS.md` with one row per item of the 2026-07-28 checklist, each row stating what beam_mcp 0.8.0 ships, +carries but Trinity does not yet read, refuses on its will-not-implement page, or leaves open, with a path and +line at the pinned commit; the two conflicts between that page and Trinity's own slices stated against the slice +lines they collide with; a probe, on a throwaway branch of beam_mcp that is never merged, of whether +`BeamMCP.Transport.HTTP` can take a `:server` module option without a fork; and `beam_mcp` added to the tree as a +dependency behind the `Trinity.MCP` boundary. -## Acceptance criteria -1. [auto] A table with every checklist item per candidate, each cell a measurement with the command. -2. [auto] ADR-0007 status → accepted, naming the choice and the fallback. -3. [auto] `VERSIONS.md` updated with exact versions and dates. +## Why +Slices 060 to 062 are written against what the core does. A gap discovered mid-build is a scope change; a gap +in a table is a plan. The seam probe turns a question to another project into a diff size. + +## Checklist (one FINDINGS row each) +1. Revision negotiation: `server/discover` for 2026-07-28, `initialize` for 2025-11-25, from one route. +2. Stateless Streamable HTTP as a Plug; `Mcp-Method` and `Mcp-Name` headers; no session identifier. +3. stdio transport. +4. tools: `tools/list`, `tools/call`, deterministic order, schema validation. +5. resources: `resources/list`, `resources/read`, templates, subscriptions. +6. prompts: `prompts/list`, `prompts/get`. +7. `ttlMs` and `cacheScope` on every cacheable result. +8. `resultType` on every result, including `server/discover`. +9. MRTR: `input_required`, `requestState`, `inputResponses`. +10. Tasks extension. +11. OAuth resource server hooks: what the `:authorize` and `:authorize_body` hooks give a host and what they do + not. +12. Client role. +13. `connectome://` resources and the `:observe` tool: shipped by the core; whether Trinity exports them is a + 061 G1 decision, default off. +14. Telemetry events the core emits around dispatch, by name and metadata shape, for slice 090's catalogue. +15. The signer seam: `BeamMCP.Signer`, `Canonical.signature/3`, and whether a verifier of exported bytes can + read the signature algorithm without the signer module. ## Scope **In:** -- Run each candidate against the checklist below. Probes, not README reading. -- A comparison table where every cell is a measurement with the command that produced it. -- ADR-0007 finalised, naming the choice and the fallback. +- The FINDINGS table, every cell derived from `docs/will-not-implement.md`, `docs/public-api.txt`, the README + and the source of beam_mcp at the pinned commit, with the deriving command pasted. +- The seam probe: a throwaway branch on a local clone of beam_mcp adding the `:server` option; the diff size in + lines; which of its census tests change; pasted output. The branch is deleted at the end of the slice. +- `{:beam_mcp, "~> 0.8"}` in `mix.exs`; the `VERSIONS.md` row flipped by `mix versions.gen`; a `Trinity.MCP` + boundary declared with `BeamMCP` as its only permitted external, so nothing else in Trinity can import it. +- A proposed amendment to ADR-0007 if any finding changes the shape of 060 to 062. **Out:** -- Building anything on the winner; that is 060 and 061. +- Building anything on the core: that is 060 and 061. +- Any change to beam_mcp itself. A change it needs is a question to that project's board, routed through the + owner, never a patch from here. ## Deliverables -- `docs/adr/0007-*.md` finalised, the comparison table in PROOF.md, throwaway probe code under a scratch dir (not merged), `VERSIONS.md` MCP rows resolved. +- `slices/059-mcp-library-spike/FINDINGS.md`, the pasted probe output in `PROOF.md`, the dependency and the + boundary in `mix.exs` and `lib/trinity/mcp/`, the `VERSIONS.md` row. + +## Acceptance criteria +1. [auto] `FINDINGS.md` carries one row per checklist item, each with a path and line at the pinned beam_mcp + commit and the command that derived it. +2. [auto] The two conflicts are stated against slice lines: will-not-implement entry 12 (no MRTR) against 061's + `input_required` criteria; entries 9 (no client) and 8 (no OAuth) against 060 and 062. +3. [auto] The seam probe reports the diff size and the census tests it touches, with output; no code from it is + merged anywhere. +4. [auto] `beam_mcp` is in `mix.lock`, its `VERSIONS.md` row reads in `mix.lock`, and `mix compile + --warnings-as-errors` fails on a planted `BeamMCP` import outside `Trinity.MCP` (test). +5. [auto] Gate green; coverage line reported. ## Proof required -- For each acceptance criterion: the command and its output, or a test name and its result, or a screenshot under `proof/`. A sentence is not proof. +- For each acceptance criterion: the command and its output, or a test name and its result. A sentence is not + proof. ## Manual verification queue None. Every acceptance criterion in this slice is `[auto]` and is proven by a command or a test. If that changes during the slice, the criterion is retagged and this section is filled at G1. ## Definition of Done -- [ ] `mix gate` green · [ ] AC1–3 proven · [ ] docs/ADR/VERSIONS updated if affected · [ ] ROADMAP status → done · [ ] final commit + tag +- [ ] `mix gate` green · [ ] AC1–5 proven · [ ] docs/ADR/VERSIONS updated if affected · [ ] ROADMAP status → done · [ ] final commit + tag ## Commit & tag -`feat(s059): complete slice 059 (MCP library spike)` · tag `slice/059` +`feat(s059): complete slice 059 (MCP capability gap and seam probe)` · tag `slice/059` + +## Risks / open questions +- The seam is another project's decision. If it is refused, 061 takes the recorded fallback in ADR-0007 + decision 6. diff --git a/slices/060-mcp-client/SLICE.md b/slices/060-mcp-client/SLICE.md index 306df8b..879c683 100644 --- a/slices/060-mcp-client/SLICE.md +++ b/slices/060-mcp-client/SLICE.md @@ -3,22 +3,25 @@ | Field | Value | |---|---| | Phase | 6 MCP | -| Milestone | M5 Always-on | -| Size | M/L | +| Milestone | M5a Automates | +| Size | L | | Depends on | 059, 021 | Supersedes the 2026-09-05 first draft of this slice, which targeted anubis_mcp at 2025-11-25 and planned sampling/elicitation callbacks (both deprecated in 2026-07-28). -**Sized `M/L`, conditionally, and the condition is decided by slice 059.** M if 059 selects a library that ships a -working client. L otherwise: a server-only library, or the own-minimal-server fallback, leaves this slice to build -the MRTR retry loop, Tasks polling and the whole OAuth client role: PKCE, resource indicators, client metadata -with dynamic-registration fallback, issuer checking and per-issuer credential storage. `docs/08-standards.md` says -as much in its own words: the stateless server side is small and the client side is more work. A single number -here would be a guess wearing an estimate's clothes. +**Amended 2026-09-20 under ADR-0007 decision 7.** The size condition is decided: beam_mcp ships no client and +will not, so this slice is L. The client is Trinity's own thin driver. The OAuth client role moves to slice 062, +which owns authorization in every role; this driver consumes the tokens 062 obtains. + +**The thin-driver rule, which is a test (AC7).** The driver builds the outbound JSON-RPC request object and +nothing else of the protocol. Decoding and validation call beam_mcp's public functions. Revision handling is +limited to sending `server/discover` and reading `supportedVersions`, with the `initialize` path for 2025-11-25. +If the driver needs its own revision negotiation, envelope vocabulary or schema validator, the slice stops and +the question of a shared client package is raised on the beam_mcp side through the owner, instead of forking. ## Goal -Connect to MCP servers (stdio and Streamable HTTP) with the library chosen in 059, one supervised client per +Connect to MCP servers (stdio and Streamable HTTP) with Trinity's own driver over beam_mcp's public functions, one supervised client per configured server, preferring 2026-07-28 (`server/discover`, request-scoped `_meta`, MRTR, cacheable lists, Tasks extension) and falling back to 2025-11-25; expose their tools as runtime-registered tools that can never enter the effect catalog (M4); health, reconnect, UI. @@ -38,10 +41,12 @@ enter the effect catalog (M4); health, reconnect, UI. - Tasks extension: long-running tools return a task handle; poll `tasks/get` under the Session's Task with timeout; `tasks/update` for client-to-server input when the server asks. - Results mapped to provenance-tagged content parts (M1), `untrusted` taint. -- OAuth client role for protected servers: PRM discovery, AS metadata (RFC 8414 / OIDC), PKCE, RFC 8707 resource - indicator, CIMD first with DCR fallback, RFC 9207 `iss` check, per-issuer credential storage via `Trinity.Secrets`. +- Token use for protected servers: the driver presents the bearer token that slice 062's client role obtained + and handles `401` with `WWW-Authenticate` by asking 062 for a token; it performs no OAuth flow of its own. +- Order of work: stdio first (Port driven), Streamable HTTP second, both against beam_mcp's shipped transports + as the server under test. - Reconnect with backoff; health + tool list in `/mcp` UI. -- Test servers in `test/support/`: a 2026-07-28 server and a 2025-11-25 server built with the chosen library. +- Test servers in `test/support/`: a 2026-07-28 server and a 2025-11-25 server built with beam_mcp. **Out:** sampling, roots, logging (all deprecated); SSE transport; revisions older than 2025-11-25. ## Acceptance criteria @@ -51,8 +56,10 @@ enter the effect catalog (M4); health, reconnect, UI. 4. [auto] MRTR: test server returns `input_required` with a `requestState`; the Session surfaces the request; answering resumes; the retried call carries the `requestState` back byte-for-byte and completes. A retry with the `requestState` altered or omitted is rejected by the server (test). 5. [auto] Tasks: a slow tool returns a handle; polling completes; cancel from the UI cancels the task (test). 6. [auto] Server dies → tools unregistered → reconnect → re-registered (test with short backoff). -7. [auto] OAuth: against a test AS (in-repo fake supporting CIMD + PKCE + resource indicator), the client obtains an - audience-bound token and the RS accepts it; wrong `iss` is rejected (tests). +7. [auto] The thin-driver rule: a census over `lib/trinity/mcp/client/` finds no revision negotiation beyond + `server/discover` and `initialize`, no protocol object built other than the outbound request, and no schema + validator; decode and validation are calls into beam_mcp's public functions (test, with the population + command pasted). 8. [manual] Manual: one real public 2026-07-28 server used end-to-end (GIF). ## Manual verification queue diff --git a/slices/061-mcp-server/SLICE.md b/slices/061-mcp-server/SLICE.md index 39cb17e..45cc9ef 100644 --- a/slices/061-mcp-server/SLICE.md +++ b/slices/061-mcp-server/SLICE.md @@ -3,12 +3,27 @@ | Field | Value | |---|---| | Phase | 6 MCP | -| Milestone | M5 Always-on | +| Milestone | M5a Automates | | Size | M | | Depends on | 060, 024 | Supersedes the 2026-09-05 first draft (bearer token from settings, anubis server). +**Dependencies and blockers, added 2026-09-20 under ADR-0007 decision 6.** The design below is unchanged. +- Blocked, for AC4 and AC7, on the MRTR wrapper (a sibling package wrapping `BeamMCP.Server` through the + `:server` option on `BeamMCP.Transport.HTTP`) or on the recorded refusal of that seam and the fallback it names. + beam_mcp's will-not-implement entry 12 keeps MRTR out of the core; the wrapper carries `requestState` verbatim + and decodes nothing; Trinity mints and validates the envelope. +- Depends on `resultType` being stamped on `server/discover` by the core; at 0.8.0 only `tools/call` results + carry it. Recorded as a gap on the beam_mcp board; 059's FINDINGS names its status at the time. +- `ttlMs` and `cacheScope` on every cacheable result shipped in beam_mcp 0.5.0 and are not a dependency. +- Replay defence is Trinity's: a partition-local nonce cache keyed by expiry window, in the wrapper's host + callback, and the 024 membrane's idempotency key as the cross-partition backstop. The property is at-most-once + per partition plus idempotent effects, stated as such, with reds for replay inside the window, replay across + partitions, an expired envelope and one tampered byte. +- `connectome://` resources and the `:observe` tool are shipped by the core. Whether Trinity exports them is a + G1 decision here, default off. + ## Goal Trinity as an MCP server: a stateless Plug mounted at `/mcp` serving 2026-07-28 (`server/discover`, `_meta` protocol version per request, `Mcp-Method`/`Mcp-Name`, `ttlMs`/`cacheScope`, deterministic tool order, MRTR for diff --git a/slices/062-mcp-server-auth/SLICE.md b/slices/062-mcp-server-auth/SLICE.md index 652d888..c9b6154 100644 --- a/slices/062-mcp-server-auth/SLICE.md +++ b/slices/062-mcp-server-auth/SLICE.md @@ -3,12 +3,24 @@ | Field | Value | |---|---| | Phase | 6 MCP | -| Milestone | M5 Always-on | +| Milestone | M5a Automates | | Size | L | | Depends on | 061 | Supersedes the 2026-09-05 first draft (RS only, AS "owner decision"). +**Amended 2026-09-20 under ADR-0007 decision 8.** This slice owns authorization in every role. The OAuth client +role moves here from slice 060: PRM discovery, AS metadata (RFC 8414 and OIDC), PKCE, RFC 8707 resource +indicators, CIMD first with DCR as the legacy fallback, RFC 9207 `iss` checking, per-issuer credential storage via +`Trinity.Secrets`. The 060 driver consumes the tokens this role obtains. The resource server half lives above +beam_mcp, whose will-not-implement entry 8 keeps OAuth out of the core; the `:authorize` and `:authorize_body` +hooks are where the RS attaches. + +**Non-person identity, noted for a later amendment.** A deployment that requires it gives each Trinity instance +an X.509 credential from the deployment's own PKI, and the boot receipt carries a sponsor field naming the +accountable person. SPIFFE is an issuance path for that credential, not an identity model of its own. No +acceptance criterion is added for it until the standards register carries the row that asks for it. + ## Goal Make Trinity's MCP server enterprise-connectable per MCP 2026-07-28: (1) the **resource server** profile (RFC 9728 Protected Resource Metadata, `401` + `WWW-Authenticate`, audience-bound bearer validation, RFC 8707); @@ -25,7 +37,7 @@ Identity is not authority: OAuth/EMA answers *who is calling and with what scope the selected authority adapter answer *may this effect happen*. Every token decision is receipted. ## Scope -**In:** the three components above as `Trinity.MCP.Auth.*` behind the `Trinity.MCP.Auth` behaviour (`Local` +**In:** the OAuth client role described in the amendment above, as `Trinity.MCP.Auth.Client`; the three components above as `Trinity.MCP.Auth.*` behind the `Trinity.MCP.Auth` behaviour (`Local` loopback default unchanged; `Embedded` = RS + AS + EMA; `JWT`/`Introspection`/`TrustedHeaders` for external AS or gateway deployments); key management via `Trinity.Secrets` with JWKS publication and rotation; admin UI for IdPs, clients (CIMD URLs), scopes; conformance tests modelled on the spec's flows; docs page. @@ -45,6 +57,9 @@ clients (CIMD URLs), scopes; conformance tests modelled on the spec's flows; doc flow; screenshots. If none is available on the developer machine, recorded as not measured. 7. [auto] The library boundary: `Trinity.MCP.Auth.*` has no dependency on `Trinity.Sessions`/`Trinity.Tools` (boundary check), so it can be extracted as its own package (slice 123). +8. [auto] Client role: against a test AS (in-repo fake supporting CIMD, PKCE and resource indicators), the client + obtains an audience-bound token and the RS accepts it; wrong `iss` is rejected; the 060 driver presents that + token without performing any flow of its own (tests). ## Manual verification queue Every `[manual]` criterion below needs a person. Listed here so the owner sees the queue at G1 rather @@ -52,7 +67,7 @@ than at review time. - **AC6**: Manual: one real MCP client that supports EMA (per the MCP client matrix at the time) connects through the fake IdP. ## Definition of Done -- [ ] gate green · [ ] AC1–7 proven · [ ] docs/08 synced · [ ] ROADMAP → done · [ ] commit + tag +- [ ] gate green · [ ] AC1–8 proven · [ ] docs/08 synced · [ ] ROADMAP → done · [ ] commit + tag ## Commit & tag `feat(s062): complete slice 062 (MCP authorization: RS, embedded AS, EMA)` · tag `slice/062`