Documentation
¶
Overview ¶
Package mcptest serves in-process MCP servers to tests in other packages.
It exists so that the official go-sdk stays a dependency of internal/mcp and its own test support alone (CLAUDE.md): a package whose tests need something to speak MCP to imports this rather than building a server out of SDK types itself. The servers are real ones built from that SDK and served over real HTTP by httptest — not hand-rolled fakes, which would encode this platform's understanding of the protocol and pass whether or not it is right.
Index ¶
Constants ¶
const ( ServerURLEnv = "MCP_LIVE_SERVER_URL" ServerTokenEnv = "MCP_LIVE_SERVER_TOKEN" )
The live server's coordinates, read from the environment or the repo-root .env. Exactly these two — nothing else ever reaches the file.
const LiveEnv = "RUN_LIVE_MCP_TESTS"
LiveEnv opts into the live MCP tier: one real handshake and one real listing against a server this repository does not run. Any non-empty value opts in.
It exists because every other MCP test in this repository speaks to a fixture built from the same SDK the client uses, so both ends share an understanding of the protocol and agree even where that understanding is wrong. A third party's server is the only thing that can find that out.
Variables ¶
This section is empty.
Functions ¶
func Client ¶
Client is an HTTP client a fixture server can be reached through. The production client carries a dial-address guard that refuses loopback — which is exactly where httptest listens, and the reason a caller cannot simply let the MCP config default.
func LiveServer ¶
LiveServer gates a live test and returns the server's endpoint and bearer token. Not opted in: the test skips and the credential file is never opened. Opted in with no endpoint: the test FAILS, because a live tier that skips itself when its configuration rots is not a live tier.
The token is optional, and empty means an anonymous dial — which the protocol admits and the platform implements, so requiring one would fence off the public servers this tier is most likely to be pointed at. Nothing is lost by it: a server that wants a credential answers 401 when none arrives, and the test fails on that exactly as it would on a rotted one.
func ServeAt ¶
ServeAt is Server listening somewhere other than loopback, and returns the same thing: the endpoint to point an MCP client at.
It exists for the callers the platform reaches with its *own* dialer rather than a test client. The eval suite runs a real executor, whose MCP dials go through internal/dialguard — and that guard refuses loopback, which is exactly where httptest listens. Such a caller has nowhere to pass Client() in, so the fixture moves instead.
hosts are addresses of this machine the guard admits, in preference order, and the first that binds wins. A list rather than one address because enumerating interfaces yields addresses that cannot be bound — a stopped VM bridge or a docker0 whose interface is down still appears — and picking one of those would abort the caller's test on a machine where nothing is wrong.
The port is chosen free, and the URL names the host that bound, so a caller that resolved an interface gets back a URL the executor can dial.
Types ¶
type Block ¶
Block is one content block a fixture tool answers with, in this package's own shape so the go-sdk stays inside internal/mcp (CLAUDE.md). It mirrors what the protocol admits in a tool result: Type is "text", "image", "audio", "resource" or "resource_link", and each type fills only the fields that mean something for it.
type Tool ¶
type Tool struct {
Name string
Description string
Result string
Blocks []Block
IsError bool
Fail string
While func()
}
Tool is one tool a fixture server offers. Result is the text every call to it answers with — the common case, equivalent to a single Block of type "text". Blocks answers with those blocks instead when set, IsError marks the answer as a tool that ran and failed, and Fail makes the call itself fail (a JSON-RPC error rather than a result), which is a different thing entirely. While runs on the server's goroutine before the answer is built, for a test that needs the world to change during a call rather than around it.