Add a guarded-CLI example over the published MCP reference server #343

Merged
coilysiren merged 1 commit from examples-mcp-cli into main 2026-08-30 05:43:31 +00:00
Owner

Makes the demo from the #337 walkthrough durable, as an examples/ entry pointing at a real upstream.

Why a second MCP example

examples/mcpverb/ imports the engine and starts its own server in-process. That makes it run with nothing installed, and it never exercises stdio.

This one is the product path: KDL policy plus a committed lock, no Go at all, against a server this repository does not control.

The upstream

@modelcontextprotocol/server-everything, the protocol's own reference implementation, over npx stdio. Public, maintained by the MCP project, and reproducible by any contributor.

Deliberately not the node-stats server on kai-server that the original walkthrough used. umbra is the generic engine at the base of the stack, and a guardfile naming one operator's host would leak an estate into a repo that should know nothing about it.

What the example demonstrates

Three leaves from fourteen upstream tools:

COMMANDS:
   echo                    call echo
   get-sum                 call get-sum
   get-structured-content  call get-structured-content

get-env is denied by name, since a tool that reads the process environment is the obvious thing to close. The other ten are named by no sentence at all. Both come back identically absent - that is deny-by-absence in one --help, and a reader cannot tell which tools exist upstream.

Flags are typed from the server's own JSON Schema: -a and -b are floats on get-sum, and --location carries (one of: New York, Chicago, Los Angeles) because an enum is the constraint a caller cannot infer from a type.

What is committed, and what is not

The tool lock is committed, so the granted surface is readable without running anything. specverb.lock is not: it pins an umbra module version that is the reader's rather than this repository's, and a lock made with --umbra-replace additionally carries an absolute local path. So the README is a two-step specgen lock && specgen build.

Verification

Every command in the README was run against the live server rather than written from the schema. That caught one error: get-structured-content's location is an enum of three cities, so the value I first wrote was rejected upstream. Fixed, and the enum-in-help behaviour it exposed is now called out in the README.

Also confirmed against the real server: the contradiction check (can call get-env beside the never fails the lock closed rather than resolving it), --dry-run, and the JMESPath projection.

go test ./..., golangci-lint run ./..., and pre-commit run --all-files pass.

This is the first end-to-end exercise of the stdio transport against a real published server. The mcpverb tests cover stdio's argv gate but drive HTTP.

Makes the demo from the #337 walkthrough durable, as an `examples/` entry pointing at a real upstream. ## Why a second MCP example [`examples/mcpverb/`](https://forgejo.coilysiren.me/coilyco-flight-deck/umbra/src/branch/main/examples/mcpverb/main.go) imports the engine and starts its own server in-process. That makes it run with nothing installed, and it never exercises **stdio**. This one is the product path: **KDL policy plus a committed lock, no Go at all**, against a server this repository does not control. ## The upstream `@modelcontextprotocol/server-everything`, the protocol's own reference implementation, over `npx` stdio. Public, maintained by the MCP project, and reproducible by any contributor. Deliberately **not** the node-stats server on kai-server that the original walkthrough used. umbra is the generic engine at the base of the stack, and a guardfile naming one operator's host would leak an estate into a repo that should know nothing about it. ## What the example demonstrates Three leaves from fourteen upstream tools: ``` COMMANDS: echo call echo get-sum call get-sum get-structured-content call get-structured-content ``` `get-env` is denied by name, since a tool that reads the process environment is the obvious thing to close. The other ten are named by no sentence at all. **Both come back identically absent** - that is deny-by-absence in one `--help`, and a reader cannot tell which tools exist upstream. Flags are typed from the server's own JSON Schema: `-a` and `-b` are floats on `get-sum`, and `--location` carries `(one of: New York, Chicago, Los Angeles)` because an enum is the constraint a caller cannot infer from a type. ## What is committed, and what is not The **tool lock** is committed, so the granted surface is readable without running anything. `specverb.lock` is not: it pins an umbra module version that is the reader's rather than this repository's, and a lock made with `--umbra-replace` additionally carries an absolute local path. So the README is a two-step `specgen lock && specgen build`. ## Verification Every command in the README was run against the live server rather than written from the schema. That caught one error: `get-structured-content`'s `location` is an enum of three cities, so the value I first wrote was rejected upstream. Fixed, and the enum-in-help behaviour it exposed is now called out in the README. Also confirmed against the real server: the contradiction check (`can call get-env` beside the `never` fails the lock closed rather than resolving it), `--dry-run`, and the JMESPath projection. `go test ./...`, `golangci-lint run ./...`, and `pre-commit run --all-files` pass. This is the first end-to-end exercise of the **stdio** transport against a real published server. The mcpverb tests cover stdio's argv gate but drive HTTP.
docs(examples): add a guarded CLI over a real, published MCP server
All checks were successful
ci / secrets (pull_request) Successful in 9s
ci / lint (pull_request) Successful in 37s
ci / test (pull_request) Successful in 50s
271720c58e
examples/mcpverb starts its own server in-process, so it runs with nothing
installed and never exercises stdio. This one is the product path instead:
KDL policy plus a committed tool lock, no Go at all, against a server this
repository does not control.

The upstream is @modelcontextprotocol/server-everything, the protocol's own
reference implementation, over npx stdio. Public and reproducible by any
contributor, which pointing it at anything in one operator's estate would
not be.

The guardfile grants 3 of its 14 tools and denies get-env by name, because
a tool that reads the process environment is the obvious thing to close.
The other ten are named by no sentence at all. Both kinds come back
identically absent, which is the whole of deny-by-absence in one --help.

The tool lock is committed so the surface is readable without running
anything. specverb.lock is not: it pins an umbra version that is the
reader's rather than this repository's, and a dev lock additionally
carries an absolute --umbra-replace path.

Every command in the README was run against the live server rather than
written from the schema. That caught one: the get-structured-content
location is an enum of three cities, so the value I first wrote was
rejected upstream. The enum reaches --help on its own, which is the point
of carrying it there.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Kai Siren <coilysiren@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Agent-Role: platform
coilysiren deleted branch examples-mcp-cli 2026-08-30 05:43:31 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
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!343
No description provided.