Project the granted surface into served tool definitions #346
No reviewers
Labels
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
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
coilyco-flight-deck/umbra!346
Loading…
Reference in a new issue
No description provided.
Delete branch "served-surface"
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?
Refs #339. Phase 2 of the mcp dialect, scoped to what belongs in umbra.
Most of it already existed
Descriptor.InputSchema().JSONSchema()was built for ward-mcp and does the schema projection. #339 assumed this was mostly wiring, and that was right. Three things were genuinely missing.1.
Descriptor.MetaCarries upstream
_meta. This is the model change #336 deferred with "only on the day the generated runtime acts on_meta.ui.resourceUri" - and a served surface is that day. Forwarded verbatim, never parsed.2.
EnumonFieldandPropertyThe dialect folded an enum into the flag's help text, which is right for a human reading
--helpand useless to an agent calling a served tool. A served schema that lost it would be callable but not correctly callable, so the enum now survives asenum.Found by writing the round-trip test rather than by reading the code.
3.
ServedToolsReturns the tool definitions plus the runtime that fires them. It advertises the upstream tool name, not the kebab-cased CLI leaf, because a served surface speaks the protocol's namespace rather than a shell one.
What this deliberately does not do
umbra builds the surface and does not serve it. No transport, no session registry, no
servecommand, no process lifetime.#339 asked this outright: "Does the served surface belong in umbra at all, or in ward-mcp with umbra supplying descriptors?" Answer: the latter. Gating a request and hosting a server are different jobs, and the consumer already has the second one. #336 kept an aggregating proxy out of umbra on exactly this reasoning, and shipping a server here would reopen a boundary you already merged.
So #339 stays open for the consumer-side half. This PR is the part umbra owes it.
Two properties that carry over rather than being re-derived
opcore.Operation.Execute, the same entry a CLI leaf uses, so the guards cannot drift into two implementations.Verification
The round-trip test is load-bearing: locked schema → flags → served schema, asserting type, description, enum, numeric bounds, array items, and requiredness all survive. Plus
_metaforwarded byte-for-byte and the descriptor identity.go test ./...,golangci-lint run ./...,pre-commit run --all-filespass.