Skip to content

Commit e82c170

Browse files
committed
feat(s059): complete slice 059 (MCP capability gap and seam probe)
PROOF.md with every criterion's evidence, the probe diff as a record under proof/, NOTES findings, the coverage row (80.54 at 53c9091), the README's related-projects paragraph brought to the dependency, and the ROADMAP row to done. Signed-off-by: Ayla Croft <aylacroft@proton.me>
1 parent 53c9091 commit e82c170

5 files changed

Lines changed: 177 additions & 6 deletions

File tree

README.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ later without renumbering anything.
141141
| `VERSIONS.md` | The verified dependency versions, generated from `lib/trinity/versions.ex` |
142142
| `CLAUDE.md` | The engineering contract: slice rules, definition of done, proof standard |
143143
| `docs/` | Vision, architecture, tech stack, conventions, slice process, data model, risks, security model, standards; packaging, the FIPS leg, backup and restore, performance measurements |
144+
| `slices/059-mcp-library-spike/FINDINGS.md` | What the MCP server core (`beam_mcp`) ships, carries, refuses or leaves open against the 2026-07-28 checklist; the reference for the MCP phase |
144145
| `docs/adr/` | Architecture decision records. One is added whenever a decision changes |
145146
| `slices/` | One folder per slice: specification, notes and proof |
146147
| `templates/` | The templates a new slice, proof or decision record starts from |
@@ -175,11 +176,12 @@ every project space.
175176
## Related projects
176177

177178
[beam_mcp](https://github.com/ScriptKittyOS/beam_mcp) is a Model Context Protocol server core for
178-
the BEAM from the same organisation, on Hex as `beam_mcp`. Trinity does not depend on it today.
179-
Trinity's MCP layer (milestone M5a) is chosen by measurement in slice 059, where beam_mcp is one
180-
of the candidates for the server side; the client side and the authorization server are Trinity's
181-
own work whichever library is chosen. `docs/adr/0007-mcp-2026-07-28-target-and-library.md`
182-
records the protocol target and how the choice is made.
179+
the BEAM from the same organisation, on Hex as `beam_mcp`. Trinity depends on it since slice 059,
180+
pinned at 0.8.0 and reached only through the `Trinity.MCP` boundary; slice 059's `FINDINGS.md`
181+
measures what it ships against the 2026-07-28 checklist, and the MCP phase (milestone M5a) builds
182+
Trinity's client driver, the approval loop and authorization above it. The client side and the
183+
authorization server are Trinity's own work. `docs/adr/0007-mcp-2026-07-28-target-and-library.md`
184+
records the protocol target and the layering.
183185

184186
## License
185187

ROADMAP.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ standards register names the rows that ask for them.
5252
| 040 | Skills registry + agentskills.io format + progressive disclosure | 4 Skills | M | 020 | approved |
5353
| 041 | Skill self-management with staged approval + scanner | 4 Skills | M | 040, 021 | approved |
5454
| 050 | Scheduler: Oban cron agent tasks with delivery targets | 5 Automation | M | 012 | planned |
55-
| 059 | MCP capability gap against beam_mcp, and the server seam probe | 6 MCP | S/M | 020 | in_progress |
55+
| 059 | MCP capability gap against beam_mcp, and the server seam probe | 6 MCP | S/M | 020 | done |
5656
| 060 | MCP client: Trinity's thin driver (2026-07-28 preferred, 2025-11-25 compat, MRTR, Tasks) | 6 MCP | L | 059, 021 | planned |
5757
| 061 | MCP server (stateless 2026-07-28 + compat, MRTR approvals, headless profile) | 6 MCP | M | 060, 024 | planned |
5858
| 062 | MCP authorization: OAuth client role, RS, embedded AS, Enterprise Managed Authorization (ID-JAG) | 6 MCP | L | 061 | planned |

coverage.tsv

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ slice_id percent sha date
1818
032 78.99 193800e 2026-09-21
1919
040 79.86 dc9dc6b 2026-09-21
2020
041 80.55 ae200d7 2026-09-21
21+
059 80.54 53c9091 2026-09-21
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# Proof for slice 059: MCP capability gap against beam_mcp, and the server seam probe
2+
3+
Agent: Trinity · Coding Agent · Date: 2026-09-21 · Branch: slice/059-mcp-gap · Final commit: (the commit carrying this file; named in the closing correction)
4+
5+
## Summary
6+
beam_mcp 0.8.0 (its commit `cfa706b`) measured against the fifteen items of the 2026-07-28 checklist, each
7+
row a path and line at that commit with the command that derived it (`FINDINGS.md`); the two conflicts stated
8+
against 061, 060 and 062's lines; the `:server` seam probed on a throwaway branch of a local clone (five
9+
changed lines, one census test's pinned list widened by two entries, the branch deleted); `beam_mcp ~> 0.8` in
10+
the tree behind `Trinity.MCP`, the one boundary the compiler lets reach it. Nothing was built on the core and
11+
nothing was sent to it; no ADR-0007 amendment is proposed (NOTES finding 5). Six findings in NOTES.md.
12+
13+
## Gate
14+
```
15+
$ mix gate (this machine, OTP 28.5.0.5, Elixir 1.20.4, under a 32 GiB cgroup, tree 53c9091 with this file, coverage.tsv and the probe diff uncommitted on top)
16+
2146 mods/funs, found no issues.
17+
... SCAN COMPLETE ...
18+
No retired or security advisory packages found
19+
No vulnerabilities found.
20+
versions.verify: OK. 103 locked packages, none disagreeing with 53 pins
21+
Result: 454 passed, 18 excluded
22+
plan_check: PASS
23+
exit=0
24+
```
25+
CI: named in the closing correction.
26+
27+
## Tests
28+
```
29+
$ mix test --cover (tree 53c9091)
30+
Result: 454 passed, 18 excluded
31+
| 66.67% | Trinity.MCP | (core_events/0 is a constant read by 090)
32+
| 80.54% | Total |
33+
```
34+
`coverage.tsv` row: `059 80.54 53c9091 2026-09-21` (from 80.55 at 041: one hundredth, the boundary's
35+
constant).
36+
37+
The slice's six tests (`mix test test/trinity/mcp --trace`):
38+
```
39+
test/trinity/mcp/boundary_test.exs
40+
* test beam_mcp is in mix.lock at 0.8.0 and the VERSIONS row reads in mix.lock
41+
* test the boundary compiler checks calls into beam_mcp everywhere, and Trinity.MCP is the boundary that lists it
42+
* test the planted reference is a real reference: the file the proof compiles names BeamMCP outside Trinity.MCP
43+
test/trinity/mcp/findings_test.exs
44+
* test AC1: fifteen rows, numbered 1 to 15, each naming the pinned commit's paths with a line and a deriving command
45+
* test AC2: the two conflicts are stated against the slice lines
46+
* test AC3: the probe reports its diff size and the one census test it touches, and none of it is here
47+
```
48+
49+
## Acceptance criteria evidence
50+
51+
### AC1 [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
52+
`FINDINGS.md`, rows 1 to 15; the pinned commit derived by `git rev-parse --short 'v0.8.0^{commit}'``cfa706b`
53+
on the clone. `findings_test.exs` "AC1" parses the table and holds every row to a path with a line, a status
54+
word and a deriving command. The population the rows read: `find lib -name '*.ex' | wc -l` → 25;
55+
`wc -l docs/public-api.txt` → 142. The baseline suite at the pinned commit on its own toolchain:
56+
```
57+
$ cd <clone> && mix test (Erlang 28.1.1, Elixir 1.18.4-otp-28, .tool-versions of the clone)
58+
11 properties, 705 tests, 0 failures
59+
```
60+
61+
### AC2 [auto]: The two conflicts are stated against slice lines
62+
`FINDINGS.md` "The two conflicts": A, will-not-implement entry 12 (no MRTR) against 061's `input_required`
63+
criteria (`slices/061-*/SLICE.md:13-15,38`, which already carry the blocker); B, entries 9 (no client) and 8
64+
(no OAuth) against 060's thin driver (`slices/060-*/SLICE.md:14-15`) and 062's roles. `findings_test.exs` "AC2".
65+
66+
### AC3 [auto]: The seam probe reports the diff size and the census tests it touches, with output; no code from it is merged anywhere
67+
On the clone, branch `probe/server-option` from `cfa706b`:
68+
```
69+
$ git diff --stat
70+
lib/beam_mcp/transport/http.ex | 5 +++--
71+
1 file changed, 3 insertions(+), 2 deletions(-)
72+
```
73+
The diff is `proof/probe-server-option.diff` (a record, not a patch to apply: SLICE.md's "Out"). Before, at
74+
`cfa706b`, and on the probe:
75+
```
76+
$ mix test test/beam_mcp/boundary test/beam_mcp/transport (cfa706b)
77+
180 tests, 0 failures
78+
$ mix test test/beam_mcp/boundary test/beam_mcp/transport (probe)
79+
180 tests, 1 failure
80+
1) test the catalog is called through three callees: capabilities/0 at five sites, read_resource/1 at one, get_prompt/2 at one (BeamMCP.Boundary.NoCatalogTest)
81+
test/beam_mcp/boundary/no_catalog_test.exs:102
82+
calls through a variable module:
83+
...
84+
{{BeamMCP.Transport.HTTP, :do_dispatch, 3}, :handle_message, 2}
85+
{{BeamMCP.Transport.HTTP, :do_dispatch, 3}, :new, 1}
86+
$ mix test (probe, the whole suite)
87+
11 properties, 705 tests, 1 failure
88+
$ git checkout -- . && git checkout 'v0.8.0^{commit}' && git branch -D probe/server-option
89+
Deleted branch probe/server-option (was cfa706b).
90+
```
91+
`findings_test.exs` "AC3": the report names the numbers and the test; `lib/beam_mcp` does not exist in this
92+
tree and `mix.exs` fetches `beam_mcp` from hex, not a path or a git source.
93+
94+
### AC4 [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`
95+
`boundary_test.exs`: the lock holds `{:hex, :beam_mcp, "0.8.0", …}`, the VERSIONS row reads `✅ in mix.lock`
96+
(`mix versions.gen` flipped it), `Trinity.MCP.core_version/0` is `"0.8.0"`, mix.exs carries
97+
`boundary: [default: [check: [apps: [:beam_mcp]]]]`, and the census over `git ls-files lib/*.ex` finds
98+
`BeamMCP.` in `lib/trinity/mcp.ex` alone. The planted reference (its text kept at
99+
`test/support/mcp/planted_reference.ex.txt`, which the test holds to be a real reference), compiled by the
100+
command:
101+
```
102+
$ cat > lib/trinity/planted_beam_mcp.ex <<'EOF'
103+
defmodule Trinity.PlantedBeamMcp do
104+
@moduledoc false
105+
def depth, do: BeamMCP.JSON.max_depth()
106+
end
107+
EOF
108+
$ mix compile --warnings-as-errors
109+
Compiling 1 file (.ex)
110+
Generated trinity app
111+
warning: forbidden reference to BeamMCP.JSON
112+
(references from Trinity to BeamMCP.JSON are not allowed)
113+
lib/trinity/planted_beam_mcp.ex:3
114+
exit=1
115+
$ rm lib/trinity/planted_beam_mcp.ex
116+
```
117+
Why the refusal is pasted and not a test: NOTES, the deviation stated before code.
118+
119+
### AC5 [auto]: Gate green; coverage line reported
120+
The gate above; the coverage row `059 80.54 53c9091 2026-09-21`.
121+
122+
## Manual verification for the reviewer
123+
None: every criterion is automatic. The reviewer may read `FINDINGS.md` beside the clone at `cfa706b`; every
124+
row's last column is the command to rerun.
125+
126+
## Deviations from SLICE.md
127+
NOTES.md, one stated before code (AC4's refusal pasted from the compiler, the test holding what makes it) and
128+
one found building (`Trinity.MCP` is a top-level boundary, finding 1).
129+
130+
## Versions touched
131+
`VERSIONS.md` updated: yes, by `mix versions.gen`: the `beam_mcp` row from 🔍 to ✅ in `mix.lock` (0.8.0).
132+
`mix versions.verify`: OK, 103 locked packages, none disagreeing with 53 pins.
133+
134+
## Git
135+
```
136+
$ git log --oneline main..HEAD
137+
(named in the closing correction, after the final commit)
138+
```
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
diff --git a/lib/beam_mcp/transport/http.ex b/lib/beam_mcp/transport/http.ex
2+
index 401dd97..41eaff6 100644
3+
--- a/lib/beam_mcp/transport/http.ex
4+
+++ b/lib/beam_mcp/transport/http.ex
5+
@@ -198,7 +198,7 @@ if Code.ensure_loaded?(Plug) do
6+
# This Plug's own options; everything else in the keyword list belongs to Server.new/1.
7+
# Derived by exclusion rather than by naming what to keep: a `Keyword.take` list silently
8+
# dropped `tools_ttl_ms` and `tools_cache_scope` when they were added, and a test caught it.
9+
- @plug_opts [:authorize, :allowed_origins, :authorize_body, :read_timeout, :connection_timeout]
10+
+ @plug_opts [:authorize, :allowed_origins, :authorize_body, :read_timeout, :connection_timeout, :server]
11+
12+
# Extracted from `init/1` rather than inlined, and not for tidiness: adding this check
13+
# inline took `init/1` to a cyclomatic complexity of 11 against a limit of 9, and the gate
14+
@@ -301,6 +301,7 @@ if Code.ensure_loaded?(Plug) do
15+
%{
16+
authorize: authorize,
17+
authorize_body: authorize_body,
18+
+ server: Keyword.get(opts, :server, Server),
19+
allowed_origins: origins,
20+
read_timeout: read_timeout,
21+
connection_timeout: connection_timeout,
22+
@@ -1594,7 +1595,7 @@ if Code.ensure_loaded?(Plug) do
23+
defp do_dispatch(conn, message, opts) do
24+
# Nothing is stamped: `check_headers/3` has held the body's `params._meta` to the header
25+
# and to the schema, so the core reads the era from the message the client actually sent.
26+
- case Server.handle_message(Server.new(opts.server_opts), message) do
27+
+ case opts.server.handle_message(opts.server.new(opts.server_opts), message) do
28+
{_state, nil} ->
29+
send_resp(conn, 202, "")
30+

0 commit comments

Comments
 (0)