Watch
2
upgrade specgen into a mcporter replacement #336
Open
opened 2026-08-29 20:57:35 +00:00 by coilysiren
·
2 comments
No Branch/Tag specified
main
release
aos/1105-optional-args
aos/claude/sb46-typed-mapped-leaves
aos/claude/sb46-mapped-body-limit
aos/1047-untyped-array-union
chore/regenerate-repo-pointer-skill
task/mapped-body-pins
aos/claude/bk79-comment-debt
aos/claude/bk79-raw-media-negotiation
aos/claude/bk79-raw-response
aos/claude/ck78-mark
feat/remove-sandbox
feat/auto-degrade-mode2
feat/pluggable-value-providers
chore/rename-module-path
chore/remove-dead-packages
claude/agents-temp-clone-note
feat/honor-response-media-type
aos/claude/gm46-can-describe
aos/codex/cli-guard-embedded-files
issue-244
recovery/2026-07-27-local-work
ward-salvage/cli-guard-a8e13cf8
ward-salvage/cli-guard-7f69a8f5
ward-salvage/cli-guard-9b23a268
ward-salvage/cli-guard-e8175b16
v0.194.0
draft-9db1341137d928b58c3f1f214d4d86b6fe3702a0
v0.193.0
draft-0a873c9abd6bc97f7c94bfea1ef86ced54f418ea
v0.192.0
draft-cb89700d1327dc069eccadc9296b2e82216cded4
v0.191.0
draft-ec1211d4b820a8ef9b779211147a60d6eb0cf6ec
v0.190.0
draft-6a576fa83020781a7fdd038e4234658373cfee3b
v0.189.0
draft-c06a946677603664b6febdeb16b5ab6914104c74
v0.188.0
draft-f7877dfbd9beaed1a18419300133ba08ca898f53
v0.187.0
draft-9c837c66e06b92f13f63d00a58c73f8273ff2285
v0.186.0
draft-f410b589aa5bad1b20976cbec6fb3e807f3a5b06
v0.185.0
draft-eea7b26348d1ec8a6829f7f731bcf7baab63bbef
v0.184.0
draft-2f51a6f54d508bff614e87887994ce5e2d49ace1
v0.183.0
draft-0530f5b4fd9d21c89b1df4997d80011bfd9eacf5
v0.182.0
draft-27eebb1de0efeca292280f337f5e3f41dadf7a2f
v0.181.0
draft-663dc8dc7c89a0d6638b3132b4d85ad070d41022
v0.180.0
draft-91cec77078764211f6a4748476a1eb370beb2768
v0.179.0
draft-5c292bc8616e70dcae6a4817ea1eda4377a3d5b3
v0.178.0
draft-8e46ec6c5676dc6a468920e160d125f13a01bec9
v0.177.0
draft-19352914e2c51bc8a5f97b5cc3d7c9d005dd3855
v0.176.0
draft-228538037f7c5caf7b8eb260906816af73cfeda2
v0.175.0
draft-5e27572f02806f17c52ffd24253b08fb44e8f408
v0.174.0
draft-39b76ec95adca5f11232e1e64a1c99ab83508af8
v0.173.0
draft-3d0c33dd6c80fdd5b7f9898805517e6fa58a4488
v0.172.0
draft-cbff67cb8a6550eca355ad0308f4658d17fd5282
v0.171.0
draft-9ed606291f8f38012324fae9896efba7800f1bd8
v0.170.0
draft-6883083b8297c342b3f6462b7740cf76814e8201
v0.169.0
draft-4b14e54dd90c8c4ffbc30c367e9e0931b16bfe71
v0.168.0
draft-72173c4084ce08da59675207d0a3073924eed225
v0.167.0
draft-99131202d035c198e86f8b49d785c3a0718ff06b
v0.166.0
draft-3f2452d6561b02cb58821143381a5398697e2eb5
v0.165.0
draft-af4fda054d686aa7b63439cad8bf8ee324aab319
v0.164.0
draft-524a809272cbb57f15e4e84f3fda4f491060f905
v0.163.0
draft-5c0974e7c6798657bc7d9b26584579fd9547de4b
v0.162.0
draft-0ba4e9d71f3371e1f8604335fc399fa689aec5d6
v0.161.0
draft-59a230c7c9ee6869b35df44794983014c60fab32
v0.160.0
draft-66a5898dc8699b210fae629cc87a48389a6dee3e
v0.159.0
draft-8b098f789b88142851ff02b9d44cf8b91690bb94
v0.158.0
draft-47eed471b0f4e84166983e9c5c1e10b409944f16
v0.157.0
draft-0e14103cf6c59bf8d8590714c0c067da38507ab0
v0.156.0
draft-599429f8aa32cef94fd95116db9b22cf7c8f0747
v0.155.0
draft-ebc6cb80edd535e26e54bdbd78687f9634959f9b
v0.154.0
draft-ac86bf1ab0121a82e64de9325e31745d2a40a04e
v0.153.0
draft-c7c4bc16511a326bb4e0c36789661244b83c574a
v0.152.0
draft-dafac75068ecbb2f4f97a977da227a4f3bbc72a1
v0.151.0
draft-916c6a73fddd15e6f1eaf9fba4f8444cf6813ea0
v0.150.0
draft-df519769467955e0e9ff848b12c73757ce4ab619
v0.149.0
draft-7b22f1df41f6ea6fb795127168009e8ad192c730
v0.148.0
draft-7849611ff749a7e77b5497f311f5f6fab9d4bfad
v0.147.0
draft-3893db50211c8d8fc3b16b647d9da1a2b06c8b2e
v0.146.0
draft-8ec7a0cac0be34c2b075ac2df9f7981b809d1744
v0.145.0
draft-d41a2095ab92763c540bdffc32ac611d37e64bd0
v0.144.0
draft-be9487c4b9a499e634b8104ed25ed97eb6aa84a0
v0.143.0
draft-99744ccc4b1c531ec556492e609947fdaf7c4d8f
v0.142.0
draft-f19f2272ad214de5cd3c0097fec2df2ab8110272
v0.141.0
draft-50c56897c8fbf2abe7ed32fb8acc4bc04d63fe1e
v0.140.0
draft-0afc06265c46b3046db7c90e154b25d1377a24e5
v0.139.0
draft-32f278b23ccb6bdc31bdadbe0c8234b4b2ba2bb0
v0.138.0
draft-2d01d26376a9b954caac6871493b15bb27e93005
v0.137.0
draft-28dda03318260279ade890b2db2f47fba3da3733
v0.136.0
draft-de6749808aff6cc9a51d7a047a788adc7d0b6f9e
v0.135.0
draft-d9426dc3aeb39ef9fedcb9ff99b50194a25e1d9a
v0.134.0
draft-f9dd5dcc16f3f1dc1054683c3f6ef725a9e18a77
v0.133.0
draft-529f234654d799a6deecf08464ced4e5d13e0e8d
v0.132.0
draft-94204c070157d3c32c797f7c5003fa74727cd286
v0.131.0
draft-8ef1a1703a8ef8f907e3bab468818f6fcdc851c0
v0.130.0
draft-794ec95725ec6a8a2ff47ce2ac5fda287fdcf200
v0.129.0
draft-c05efef093167513d1c0cfacd5904eda13653f1a
v0.128.0
draft-4329ee77e55c7bcb9fbef5338d036894b60c3f25
v0.127.0
draft-530d4f474c46c8f842654ddf9a67c0709e919250
v0.126.0
draft-e76edeb5352bf0aad4d4b95ed93602056a461a7f
v0.125.0
draft-01f45023816ab158da0ad16c967c4364463459ac
v0.124.0
v0.123.0
draft-0ea86b6d50dc66a5e4a3e29504159263f44b69b5
draft-a4bbfaa2b3a1e9932abb333cf96085b08dc893f4
v0.122.0
draft-f2e33339b65d8fdc133e214ec49c37d2c8f671ab
v0.121.0
draft-bf2bd0e5fceb55cc6f4983d883b548d65fe43c5c
v0.120.0
draft-a1fd0de01e8694a804646901465846f9c829964e
v0.119.0
draft-d6a59219d86e7432deee0ef332be0c3512451932
v0.118.0
draft-1424a8b2696390464c59c48b9d3a0c2987e941c4
v0.117.0
draft-b46e95adc5d331530a8676333c8a4c4331b941a2
v0.116.0
draft-188ac86e27d361b5dc2a7116e19333fb9259f384
v0.115.0
draft-b764efcc94a6748a3ba4c0ed6174b0ed71414cdb
v0.114.0
draft-2a571af9b416723565249d41e05cc53e87774e28
v0.113.0
draft-15810837dd6b1226643f4bb78a5879c4a2ec0dfc
v0.112.0
draft-aa876f776ffc560160959f095f9390c24984c31a
v0.111.0
draft-c41011c1888e0f1d8dbf582dbac4bfbd82f18938
v0.110.0
draft-1e3d3c0ab52723e75361fe0fff12a8f63c37155f
v0.109.0
draft-ffb44199e39f4bbfcf0d3a9e0288f37d268e87d7
v0.108.0
draft-907b707e7f09151d5ef7f1b3bc865d011291f5bc
v0.107.0
draft-7c179ec30cbf2e7311def42e1501f761c4ec69ea
v0.106.0
draft-17a362a65e439d06f0f90cd00f293943dcd2f62f
v0.105.0
draft-781edc923dfe2ba8b17d3aa2bc7e056825748983
v0.104.0
draft-143732937a354bb4c30e1aebb11c810d24006e50
v0.103.0
draft-9de614246e23e7ef711276384d66985ff864cc48
v0.102.0
draft-6f0ee0740b537405539619de75b0e61df91bced1
v0.101.0
draft-16b0b2affc80b689710f3a470137449f4d898a6b
v0.100.0
v0.99.0
v0.98.0
v0.97.0
v0.96.0
v0.95.0
v0.94.0
v0.93.0
v0.92.0
v0.91.0
v0.90.0
v0.89.0
v0.88.0
v0.87.0
v0.86.0
v0.85.0
v0.84.0
v0.83.0
v0.82.0
v0.81.0
v0.80.0
v0.79.0
v0.78.0
v0.77.0
v0.76.0
v0.75.0
v0.74.0
v0.73.0
v0.72.0
v0.71.0
v0.70.0
v0.69.0
v0.68.0
v0.67.0
v0.66.0
v0.65.0
v0.64.0
v0.63.0
v0.62.0
v0.61.0
v0.60.0
v0.59.0
v0.58.0
v0.57.0
v0.56.0
v0.55.0
v0.54.0
v0.53.0
v0.52.0
v0.51.0
v0.50.0
v0.49.0
v0.48.0
v0.47.0
v0.46.0
v0.45.0
v0.44.0
v0.43.0
v0.42.0
v0.41.0
v0.40.0
v0.39.0
v0.38.0
v0.37.0
v0.36.0
v0.35.0
v0.34.0
v0.33.0
v0.32.0
v0.31.0
v0.30.0
v0.29.0
v0.28.0
v0.27.0
v0.26.0
v0.25.0
v0.24.0
v0.23.0
v0.22.0
v0.21.0
v0.20.0
v0.19.0
v0.18.0
v0.17.0
v0.16.0
v0.15.0
v0.14.0
v0.13.0
v0.12.0
v0.11.0
v0.10.0
v0.9.0
v0.8.0
v0.7.0
v0.6.0
v0.5.0
v0.4.0
v0.3.0
v0.2.0
v0.1.0
Labels
Clear labels
burndown-2026-06
Backlog burndown June 2026
burndown-2026-08
Closed in the 2026-08-26 backlog burn-down. Reopen freely: state:closed label:burndown-2026-08 recovers the whole set.
sunday-sprint
Burn-down by Sunday 2026-06-07
autonomy
async-consult
A human needs to consult on the issue to upgrade it to headless
autonomy
epic
This issue has many units of sub work - its size makes it meaningfully exclusive with other autonomy types
autonomy
headless
The agent can perform the work on its own
autonomy
live-collab
The agent and the human need to work together in realtime
coherence-core
Core review set for the warded control plane coherence milestone. These issues form the release spine; adjacent milestone issues are stretch or supporting work.
priority
P0
priority tier
priority
P1
priority tier
priority
P2
priority tier
priority
P3
priority tier
priority
P4
priority tier
qa-fixture
Disposable issue admitted to the bounded Ward QA verification lane.
role/advocate
requires work from the Developer Advocate seat
role/director
requires work from the Portfolio Director seat
role/exec
requires work from the exec role
role/frontend
requires work from the Frontend Engineer seat
role/gamedev
requires work from the Game Developer seat
role/human
requires a person, and specifically not an agent seat
role/platform
requires work from the Platform Engineer seat
role/qa
requires work from the QA role
role/science
requires work from the Applied Scientist seat
role/sysadmin
requires work from the Systems Administrator seat
state
ambient
ambient and ephemeral work, held as a maintained document rather than a queue
No labels
burndown-2026-06
burndown-2026-08
sunday-sprint
autonomy
async-consult
autonomy
epic
autonomy
headless
autonomy
live-collab
coherence-core
priority
P0
priority
P1
priority
P2
priority
P3
priority
P4
qa-fixture
role/advocate
role/director
role/exec
role/frontend
role/gamedev
role/human
role/platform
role/qa
role/science
role/sysadmin
state
ambient
Milestone
Clear milestone
No items
No milestone
Projects
Clear projects
No items
No project
Assignees
Clear assignees
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".
No due date set.
Dependencies
No dependencies set
Reference
coilyco-flight-deck/umbra#336
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Design filed by the platform seat, 2026-08-29, at Kai's direction. Issue was a stub with an empty body. Downstream consumer: coilysiren/inbox#505, which blocks on this and asks one question this body answers.
Everything below was read at
mainof this repo, cloned to a temporary path because no checkout of umbra exists on this mac. Versions and greps are measured on that clone and on the installed binaries, not recalled.The claim
MCP is a third transport dialect at a seam that already exists. It is not a new subsystem, and specgen does not become an MCP tool. It gains a third answer to a question it already asks once per guardfile.
http/specgen/codegen/codegen.go:16states the dialect as two constants:specgen.go:135picks between them by looking at one thing:memberatspecgen.go:50carriesGF *guardfile.Guardfilefor spec andExecGF *execverb.Guardfilefor exec, one nil at a time. The generatedmain.godispatches throughspecverb.Mountorexecverb.Mount, spec-only imports gated behind a spec member, so a binary compiles with either dialect alone or both. AddingTransportMCPis an addition at each of those four places rather than a rewrite of any of them.The whole lifecycle above the transport already exists and is transport-agnostic: discovery, member merge, deterministic ordering, the cache stamp, the module lock,
--skills-out. None of it knows what a request is.Precedent on disk: the sql grant
The strongest evidence that this fits is that the same move already landed once.
http/opcore/operation.go, inExecute:A sql grant reaches a database rather than a URL, branches out before
Resolve, and rejoins atcheckResponsesofail-whenstill applies. An MCP grant is the same shape:executeMCPfirestools/call, then rejoinscheckResponse.RawResponseis a second branch already in that function, so MCP is the third, not the first.This is what specgen has that mcporter structurally cannot. Every guard sits above the transport.
restrictgates,fail-whenJMESPath postconditions, the destructive marking, the audit row, the exit-code taxonomy, andrespfmtoutput all apply to an MCP call the moment the transport lands, because none of them touch HTTP.mcporter call linear.list_issues limit:5has no policy, no audit row, no postcondition, and no deny-by-absence. That gap is not a feature mcporter is missing, it is the thing umbra is.The grant already exists too, and is half-built
http/opcore/descriptor.go:54:proxy <tool> { upstream <server> <tool>; allow|deny <field> matches <regex>; post-call ... }parses today inParseInline. What it lacks is a schema source, and the comment says so out loud: the consumer resolves the upstream schema at runtime. So the tool's input shape is either hand-restated in KDL or discovered live and trusted.docs/specverb-descriptors.mdalready names that failure for the HTTP case:lockis the missing schema source. That is the design in one sentence.Grep result: umbra speaks no MCP today
40 occurrences of
mcpacross.gofiles. Every one is a test fixture (wrap ward mcp forgejo, which is the command path, not a transport), a doc comment naming ward-mcp as a downstream consumer, or theProxyparse above.http/opcore/schema.go:106is explicit:No client, no transport, no
tools/list, notools/call. Greenfield inside umbra, with the policy grammar already sitting there waiting for it.The dialect
and the remote transport:
sniffTransportiterateswrap.Children().Nodes.wrap ward mcp forgejoputsmcpin the positional args, never in the children, so a child node namedmcpdoes not collide with the existing command path. It reads badly on the page and parses cleanly. Takingmcpfor symmetry withexecrather than inventingmcp-upstream, and flagging it below as reversible before anything ships.can call <tool>names an upstream tool exactly, no verb-plus-resource resolution, because MCP tool names are a flat namespace with no spec to resolve against.neverandoverride cancarry over unchanged fromspecverb-policy.docs/specverb-descriptors.mdalready rules this: a denied leaf returns nothing rather than a refusing handler, because a denied tool that exists still costs context and still invites the call. That rule was written for MCP consumers and now binds a dialect that generates them.allow/deny/post-callfrom the existingProxygrammar move in as-is.lock and skew
lockis the deliberate online step. For a spec member it fetches Swagger and prunes to the granted surface. For an MCP member it connects, runsinitializeplustools/list, prunes to the granted tools, and writes<member>.tools.gz.Reused unchanged:
encodeSpecLock,decodeSpecLock,writeSpecLock(all byte-generic inspeclock.go), the.stamp.jsoninput hashes, and the cache key. The only new code on the lock path is the fetcher.fetchSpec(specURL)does an HTTP GET,fetchTools(member)runs a session and disconnects.skewprunes live upstream to the granted surface and diffs against the lock, exit 3 on drift, never writes. Pointed at MCP that becomes schema drift detection for MCP tools, which nothing else on the market does. mcporter haslist --schemaand no lock, so it can print today's schema and cannot tell you it moved. This is the single most defensible reason for #336 to exist rather than shipping a wrapper aroundmcporter call.Determinism note:
tools/listordering is not guaranteed by the protocol, so the lock sorts by tool name and canonicalises JSON before gzip, the wayorderedSpecsandcanonicalalready do for specs.Runtime shape: pick the client, get the server later
Two binaries fall out of the same descriptors, and the title picks one.
aosguard ops forgejo list-issue --owner xfirestools/callunder the full guard floor. This is the mcporter replacement the title asks for.opcore.DescriptorplusProxyalready describes a guarded served surface, and ward-mcp already drivesOperation.Executeas a non-CLI consumer. Once the client exists, the upstream half of a proxy stops being hand-restated.Do not build 2 first. It is the shape that already half-exists, which makes it look closer than it is.
The
_metaquestion from inbox#505inbox#505 asks this to be answered before anything else, so answering it here rather than making a reader chase it.
Read: the descriptor model is schema-only.
opcore.Descriptorcarries 21 fields and not one is a metadata map.opcore.Fieldlikewise. There is nowhere to put_metatoday, and no generic extension bag anywhere in the model. So on the literal question, MCP Apps support is a model change rather than a field addition.But the model change is not on the critical path. The tool lock is pruned JSON, not the descriptor model, so
_metasurvives into<member>.tools.gzfor free as long as pruning keeps it rather than projecting to a descriptor and back. Aui://consumer reads the lock.Descriptoronly needs aMetafield on the day the generated runtime has to act on_meta.ui.resourceUriitself, which is phase 2 of the MCP Apps work and not this issue.Two consequences for #505:
_meta.ui.resourceUrisurvive aggregation" was a question about a third party's proxy. Here it is a pruning rule in our ownlock, so the answer is "it does, because we write the pruner."skewover a locked_metais what catches aui://resource whose backing tool schema moved. #505 claims that capability and this is the mechanism under it.One requirement lands on this issue from #505: the tool-lock pruner must preserve
_metaverbatim rather than dropping unknown keys. Cheap now, expensive to retrofit once locks are committed across the fleet.Dependency: the official Go SDK
umbra's
go.modis lean:go 1.25.5, eight direct requires. Adding a ninth is a real decision, so I checked the candidate rather than assuming it.github.com/modelcontextprotocol/go-sdk- measured 2026-08-29 - v1.7.0 latest on the module proxy, twelve releases in the v1.x line, last push 2026-08-28, 5,035 stars, not archived, 92 open issues, described as "the official Go SDK for Model Context Protocol servers and clients. Maintained in collaboration with Google."tools/list, and it stops being cheap at session lifecycle, notifications, cancellation, and content-block decoding. Take the SDK, and keep it behind the new package so it does not leak intoopcore.v1.x on a spec still moving is worth naming as exposure. The mitigation is that
lockfreezes what we consume, so a protocol change shows up as skew rather than as a runtime surprise.Where the code lives, and the one real architectural cost
docs/architecture.mdstates the import rule as downward-only,cli/andhttp/both depending onpkg/and never on each other, with the surviving cross-surface reaches described as legacy being unwound.MCP spans both. A stdio server is an
execvethat umbra would now perform itself, and a Streamable HTTP server is an outbound request. Putting the dialect underhttp/meanshttp/mcpverbreaching intocli/execverbto spawn, which is a new instance of exactly the reach that doc is unwinding.Recommendation: a third top-level surface,
mcp/, depending downward onpkg/only.pkg/policyalready validates argv beforeexecve, so the stdio spawn goes through the same validation as any other, and the http/ egress proxy stays available for the remote transport. The directory keeps telling a reader which surface they are in.The cost, stated plainly: umbra's README and architecture doc both say "two surfaces" and this makes it three. That is an identity change to the framing, not just a new folder, and it needs the README,
docs/architecture.md, anddocs/FEATURES.mdedited in the same commit rather than after.The alternative is
http/mcpverbwith one accepted cross-surface reach, which is a smaller diff and adds to the pile that doc wants shrinking. My call is the third surface. Flagged below in case Kai wants the smaller diff.What does not come into umbra
mcporter's value is not its CLI verbs. Four things carry it, and they do not all belong here.
pkg/valuesourceandpkg/tokenmintalready mintclient_credentialstokens. MCP's browserauthorization_codeflow with refresh storage is a different grant and its own issue. Phase 1 guardfiles name a value provider.serveas an aggregating proxy - out. That is server-side and belongs with mcp-beaver and ward-mcp, per the read already recorded in inbox#505.emit-ts- out. No consumer.So this issue replaces mcporter's
list,list --schema,call,resource, andgenerate-cli, and deliberately does not replacedaemon,serve,vault,config import, oremit-ts. A cold reader should not expectbrew uninstall mcporterat the end of phase 1. Say so in the FEATURES entry.Dispatchable units
mcp/surface with a client - transport plus session against the Go SDK, stdio argv validated throughpkg/policy,tools/listandtools/callandresources/read. No specgen wiring yet.TransportMCP, themcpchild insniffTransport,MCPGFonmember,can callgrants reusing theProxyguards,codegenmount arm.fetchTools, the pruner that keeps_metaverbatim, deterministic ordering,<member>.tools.gz, skew diff and exit 3.executeMCPbranch inopcore.Operation.Executealongside the sql one, rejoiningcheckResponsesofail-whenbinds.docs/architecture.md,docs/FEATURES.md, a newdocs/mcpverb.md,examples/<name>/runnable, andgodoc-current.txtregenerated.1 through 4 are sequential. 5 lands with 2 and 4 rather than after.
Acceptance
mcp stdioblock and twocan callgrants builds a binary that calls both tools and refuses a third that is not granted, with the refusal absent from--helprather than present and refusing.specgen lockwrites a committed tool lock, andspecgen runafterwards works with the network down.specgen skewexits 3 when an upstream tool's input schema changes, and 0 when it has not._metaround-trips it through lock byte-identically.fail-whenrejects atools/callthat returned successfully but failed its postcondition, using the same expression syntax an HTTP grant uses.make lint,make vet,make test, and the godoc pin all pass.Risks
pkg/policy, the http side quietly gains an unvalidated exec path. This is the one item where getting it wrong is a security regression rather than a design wart.umbra-shroudand renames the sixdocs/specgen-*.mdpages with it. A newdocs/mcpverb.mdand a new transport arm collide with that diff. Sequencing is a choice rather than an accident: land #336 first and let #295 sweep it, or land #295 first and write the new pages under the new name. Either works, doing them concurrently does not.Open questions
Batched rather than blocking. Defaults are picked and stated, so implementation can start on any of them.
mcp/, orhttp/mcpverbwith one cross-surface reach? Default taken: third surface.mcp, or something that does not read strangely next towrap ward mcp forgejo? Default taken:mcp, for symmetry withexec..specgen/discovery need anything for MCP members, or is a.kdlwith a top-levelwrapenough? Default taken: enough, no discovery change.Sources
All read at
main, 2026-08-29.http/specgen/codegen/codegen.go- the two transport constants and the mount templatehttp/specgen/specgen.go-member,sniffTransport,readMember,Lock,Skew,fetchSpechttp/specgen/speclock.go- the byte-generic gzip lock helpershttp/opcore/descriptor.go-Descriptor,Proxy,UpstreamTool, and the runtime-resolution commenthttp/opcore/operation.go-Executeand the sql branch out of the HTTP floorhttp/opcore/schema.go- the "never an MCP tool type" boundarydocs/specgen.md,docs/specgen-materialization.md,docs/opcore-inline.md,docs/specverb-descriptors.md,docs/architecture.mdAGENTS.md,README.md,go.modspecgen versionon this mac - v0.185.0 (umbra ref v0.185.0)mcporter --helpandmcporter0.11.3 dist - the command surface, and zero occurrences ofui://go list -m -versions github.com/modelcontextprotocol/go-sdkand the GitHub API for that repo_metaquestionImplemented in #337. Not merged: the push to
mainwas refused by the harness, not by policy.Platform seat, 2026-08-29. All five dispatchable units in the body are built, tested, and pushed to
mcp-dialect. The work is not landed. This repo runsmerge-remote-main, and the direct push tomainwas blocked by the session's permission layer, so the branch is the only thing carrying it until #337 merges. That is the blocking wall, and it needs a human.Verification, all green on the branch:
go build,go vet,go test ./...,golangci-lint run ./..., andpre-commit run --all-files.Two design decisions in this body turned out wrong against the code
The third surface was not needed, and the cost I named for it does not exist. This body recommended a top-level
mcp/on the reasoning thathttp/mcpverbwould have to reach intocli/execverbto spawn a stdio child, a new instance of exactly the cross-surface reachdocs/architecture.mdis unwinding. Readingpkg/policyshowed the spawn needs the core, notcli/: argv validation already lives inpkg/. So the reach never arises,mcpverbsits inhttp/,mcpclientsits inpkg/, and umbra stays two surfaces. The identity change this body called "the one real architectural cost" was a cost of my own framing rather than of the feature.docs/architecture.mdnow states the one thing a reader would otherwise trip on, that an mcp stdio transport starts a subprocess inside the request surface, and why that is still the request surface.Open question 1 is therefore answered by the code rather than by Kai.
Reusing the inline grammar's proxy rules was wrong. This body said the
allow/deny/post-callguards "move in as-is". They do not:opcore's selector vocabulary is a fixed list (url,target,element,text,key,state) because the inline grammar has no schema behind it. The mcp dialect has the lock, so it checks a selector against the tool's real arguments at build time. A misspelled selector now fails the build instead of compiling into a rule that matches nothing and reads like a guard that passed. That is strictly stronger than what this body proposed, and it is the reason the export I first added toopcorewas reverted.The
_metarequirement from inbox#505 is met and testedThe tool-lock pruner preserves
_metaverbatim. Two tests hold it: one asserts the map survives a real session byte-for-byte, and one moves a live server's_metamid-test and assertsskewreports it as drift. So an MCP Apps widget address that silently repoints is drift rather than a swap nobody sees.What the other open questions resolved to
mcpas the child node name - kept, for symmetry withexec. The collision I flagged is genuinely absent:sniffTransportreads children, and themcpinwrap ward mcp forgejois a positional argument. There is a test asserting an mcp-named spec member still sniffs as spec..specgen/discovery - unchanged, as predicted. A.kdlwith a top-levelwrapwas enough.Deferrals, each with an issue rather than a sentence here
authorization_codeis unreachable; only a pre-resolved token works. Filed because the gap is silent from the guardfile's side.One unrelated repair carried in
_typos.tomllearnsser8. That hook fails onmaintoday, before this branch: typos reads theserinside the real hostname as a misspeltset. Fixed here because this change has to pass the same gate, not because it belongs to this work.What is left
Merging #337. Everything else in this issue is done.
Correction: the exit-code taxonomy does not survive the generated binary
Merged and released as v0.186.0. Demonstrated end to end from the released binary against the node-stats MCP server on kai-server, and one claim I made here and on #337 does not hold up.
What I wrote: "
restrict,fail-when, the destructive marking, the audit row, and the exit-code taxonomy all bind to atools/callunchanged."What is true: the first four bind. The taxonomy does not reach either consumer, for any dialect.
opcorebuilds the coded error correctly, and the generatedmain.goprints it and callsos.Exit(1)without ever importingpkg/exitcode. Measured on the release: a policy refusal exits 1 wherePolicyDeniedis 2, and a missing required input exits 1 whereUserErroris 5. The audit row records the same collapsed 1.This is pre-existing and not caused by #337 (the template's
os.Exit(1)predates it), so the claim was wrong rather than the code being broken by this work. Filed as #341 with the measurements and the two-consumer scope.The correction matters because the taxonomy is exactly what an orchestrator would act on, and I put the claim in two durable artifacts.
What the demo did confirm
Against the real upstream, on the released v0.186.0:
specgen lockreported2 tools of 23 upstream, so pruning to the granted surface works against a server nobody wrote for this.get-memory-infoandstat-pathand nothing else.read_text_head(denied) and the other 20 tools (unnamed) are equally absent, which is the deny-by-absence rule holding in practice.allow path matches "^/var/log"permitted/var/logand refused/etc/shadowbefore any network call.--query 'virtual.percent'projected the result,--dry-runprinted the resolved tool and arguments without firing, andspecgen skewreported no drift against the live server.So the dialect works as documented. The taxonomy sentence was the one overclaim, and it is now #341.