mcptest

package
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 16, 2026 License: Apache-2.0 Imports: 16 Imported by: 0

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

View Source
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.

View Source
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

func Client() *http.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

func LiveServer(t *testing.T) (url, token string)

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

func ServeAt(t *testing.T, hosts []string, tools ...Tool) string

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.

func Server

func Server(t *testing.T, tools ...Tool) string

Server starts an MCP server offering tools and returns its URL. The server is shut down when the test ends.

Types

type Block

type Block struct {
	Type     string
	Text     string
	Data     []byte
	MIMEType string
	URI      string
}

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL