Testing
Tests run the application with coretest, and serve the MCP servers in memory with mcpclienttest, without network. The client is the real one: instrumentation, logs and timeout included.
Serve a server
mcpclienttest.Serve(name, server) makes the server name resolve to an in-memory *mcp.Server, built with the MCP Go SDK:
func TestSearch(t *testing.T) { t.Parallel()
server := mcp.NewServer(&mcp.Implementation{Name: "docs", Version: "test"}, nil) mcp.AddTool(server, &mcp.Tool{Name: "search"}, func(ctx context.Context, req *mcp.CallToolRequest, in SearchInput) (*mcp.CallToolResult, any, error) { return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: "test_result"}}}, nil, nil })
var ( search *Search spans *tracetest.InMemoryExporter )
coretest.Start(t, core.New(mcpclient.Module, fx.Provide(NewSearch)), mcpclienttest.Serve("docs", server), fx.Populate(&search, &spans), )
result, err := search.Search(t.Context(), "oryn") require.NoError(t, err) assert.Equal(t, "test_result", result.Content[0].(*mcp.TextContent).Text)
spantest.HasSpan(t, spans, spantest.WithName("tools/call search"), spantest.WithKind(trace.SpanKindClient), spantest.WithAttributeValue(mcpclient.GenAIToolNameKey.String("search")), )}No mcpclient.servers.docs.url is needed: the in-memory server wins over the configuration. Its sessions are closed when the application stops.
Serve the application server
mcpclienttest.ServeContainer(name) serves the *mcp.Server of the application, provided by mcpserver, to test an application calling itself, or the client and server instrumentation together:
coretest.Start(t, core.New(mcpclient.Module, mcpserver.Module, mcpserver.AsCapability(NewSearchTool)), mcpclienttest.ServeContainer("self"), fx.Populate(&factory, &spans),)
session, err := factory.Session(t.Context(), "self")require.NoError(t, err)
_, err = session.CallTool(t.Context(), &mcp.CallToolParams{Name: "search", Arguments: map[string]any{"query": "oryn"}})require.NoError(t, err)
client := spantest.Find(spans, spantest.WithName("tools/call search"), spantest.WithKind(trace.SpanKindClient))spantest.HasSpan(t, spans, spantest.WithName("tools/call search"), spantest.WithKind(trace.SpanKindServer), spantest.WithParent(client[0].SpanContext.SpanID()),)Over the network
To test the streamable HTTP transport, start an MCP server on 127.0.0.1:0 with mcpserver, and point the server URL to it:
var server *mcpserver.HTTPServer
coretest.Start(t, serverApp, config.AsOptions(config.WithValues(map[string]any{"mcpserver.address": "127.0.0.1:0"})), mcpserver.Serve(), fx.Populate(&server),)
coretest.Start(t, clientApp, config.AsOptions(config.WithValues(map[string]any{"mcpclient.servers.docs.url": "http://" + server.Addr + "/mcp"})), fx.Populate(&factory),)