Skip to content

Package boundaries

The module is five packages with a strict dependency direction, and a test that keeps it so.

flowchart LR
    cli --> cobra
    cli --> server
    cobra --> root
    server --> root
    grpc --> root
    cli --> controls[go/controls, go/transport]
    server --> sdk[modelcontextprotocol/go-sdk]
    cobra --> spf13[spf13/cobra, pflag]
    grpc --> google[google.golang.org/grpc, protobuf, go/authn, go/transit]

The root is framework-free

gitlab.com/phpboyscout/go/mcp owns the operation model: definitions, the immutable registry, owned results and content, failures, form and URL continuations, and resources. It imports the estate's errors module and a JSON Schema compiler, and nothing else of consequence. No protocol SDK, no Cobra, no HTTP, no gRPC, no lifecycle framework. depfootprint_test.go runs go list -deps and fails if any of those enter the root graph.

The reason is the two consumers the module was designed for. A CLI built on Cobra and an HTTP service built on the estate's transport stack share the operation model and nothing else; the root is what they share, so it must be importable by either without dragging in the other's framework.

The adapters are opt-in

server is the protocol adapter over the official Go SDK. It translates the registry into compact or direct tools, resources, interactions and an Apps shell, over stdio or Streamable HTTP. It knows nothing about where operations came from.

cobra is a producer of operations. It snapshots a Cobra tree into definitions and handlers that run the commands as subprocesses. It knows nothing about the protocol.

grpc is the other producer. It binds a gRPC service's unary methods, one call each, into operations that run the generated server method in-process through the service's own interceptors, with the host's verified identity established as the transport's auth interceptor would establish it. It too knows nothing about the protocol, and it owns no listener: the service keeps its gRPC server exactly as it was (spec 0002).

cli is the convenience that composes the two for a command-line tool: the mcp command tree with start, stream, tools and the editor helpers. It is the only package that imports go/controls and go/transport, because stream is the one place this module hosts a listener of its own, and a binding should not carry a lifecycle framework it does not use. A tool that wants to own the composition itself imports cobra and server directly and never pays for cli.

What a framework adds on top

A framework built over this module has conventions of its own: which flags are the process's rather than the command's, how commands are grouped, where the publication mode is configured, whose logger to use. Those belong in the framework, applied once. go-tool-base does this in its pkg/mcp, which passes WithExposure, WithFlagFilter, WithGroup and the server mode into cli.Command from the framework's own types (its spec 0201). The module offers the seams; it does not know the framework exists, and a gtb subpackage here was rejected for exactly that reason.

Internal packages

internal/serving (server lifetime epochs), internal/resourceexec (request scopes), internal/inputprofile and internal/progressdiag are shared between the root and server and are not API. internal/fixturetree is the real Cobra tree the binding and command-tree tests execute as a built binary. internal/grpcfixture is the notes service the gRPC binding's integration tests run: a real gRPC server behind the transport's auth interceptor, and the same implementation bound behind the HTTP chain, with the generated contract under internal/grpcfixture/notesv1 (source in proto/). internal/servicefixture is the HTTP service the service-integration tests run: an application route beside the MCP handler, behind go/transport, a go/transit chain and go/authn API keys, under a go/controls controller. It is the second place those modules are imported, and being a fixture rather than API it does not change the picture above: a consumer that mounts the handler brings its own.