# Testing an MCP Server End-to-End Before You Publish It

## Quick answer

Publishing an MCP server without an end-to-end test is how you ship a tool that looks correct in the registry and fails the first time an agent calls it. The generated tool list, input schemas, upstream authentication, and error responses all need to be exercised against a running instance, not just validated as static files. Build a local test client that connects to the server, calls every tool with valid and invalid inputs, and asserts both the result shape and the upstream request. If a tool cannot be tested without hitting a live third party, point it at a mock upstream so the test stays deterministic and runnable in CI.

## What "end-to-end" must cover

An MCP server has several layers, and a unit test of the conversion logic misses the integration between them.

| Layer | What to verify | Failure if skipped |
| --- | --- | --- |
| Tool discovery | Every advertised tool exists and has a description | Agent calls missing or wrong tools |
| Input schema | Valid input accepted, invalid rejected | Bad params reach the upstream |
| Auth handling | Credential attached, missing one rejected | 401 at runtime or leaked secrets |
| Upstream mapping | Correct request built from tool arguments | Correct input, wrong API call |
| Error mapping | Upstream errors become structured tool errors | Agent receives a raw exception |

## A local test client

Spin up the server in the test process and connect a real MCP client over the same transport the deployment uses, so the handshake and message framing are included.

Calling with invalid input is essential; a server that only passes happy cases will let malformed parameters reach the upstream in production.

## Mock the upstream, not the tool

The test should exercise the real MCP tool and its mapping logic, so do not mock the tool itself. Instead, stand up a mock HTTP upstream that records the request it receives and returns a fixed response. Then assert the server translated the tool arguments correctly.

This catches the most common integration bug: the input schema accepts the right fields, but the mapping renames or drops one when building the upstream request.

## Run the same suite in CI

Start the server and mock upstream as part of the pipeline so the tests run on every change, not just on a developer's machine. Keep the configuration externalized and pass it through environment variables, so the test instance uses a disposable token and a local upstream rather than real credentials. A published version should be promoted only after the same suite passes against the exact artifact you intend to release.

## Testing concurrency and reconnect behavior

Beyond single calls, a shared MCP server has to handle conditions that only appear when multiple clients or a flaky network are involved. Add tests that open several clients and call the same tool concurrently, confirming each request is correlated with its own progress token and result and that no client receives another's response. A reconnect test should drop the transport mid-call and then reconnect, asserting the server either resumes an in-flight operation by its correlation id or rejects a duplicate start cleanly rather than running the work twice. These cases are cheap to build with a controllable mock transport and expensive to debug after deployment, where they surface as intermittent swapped responses or duplicated upstream actions. Include a timeout test as well: when the upstream stalls, the server should abort within its deadline and return a structured error instead of holding the tool call open forever. Together these harden the session layer that happy-path per-tool tests never reach, and they give you confidence the hosted server behaves correctly under real concurrent use.

## Post-publish smoke check

Even a thorough CI run does not prove the deployed registry entry is wired correctly. After publishing, run a thin smoke test against the hosted instance that lists tools and performs one safe read-only call, confirming the published configuration resolves. Treat this as a deployment gate, not a replacement for the full local suite.

## FAQ

### Why not just validate the schemas and tool definitions?

Static validation confirms the files are well formed but never executes the handshake, auth, mapping, or error path. Most real failures occur in those integrations, which only a running test can reach.

### What if an upstream cannot be mocked?

Mock the HTTP boundary with a local server that emulates the documented contract, even if the real service is proprietary. If the protocol itself is unusual, use a replay of recorded, sanitized responses rather than calling the live service.

### Should I test every tool or just a representative one?

Every tool should have at least one valid and one invalid call, because each has distinct mapping and error logic. Shared infrastructure can be tested once, but the per-tool behavior cannot be assumed.

### How do I keep secrets out of the test?

Use a disposable token and a mock upstream in CI, passed through environment configuration. Never run the end-to-end suite against real credentials, and do not commit the values even for local runs.

This is part of the [Powerduck vs Smithery: Generating and Hosting MCP Servers](https://www.powerduck.com/blog/powerduck-vs-smithery/) — start there for the full decision on where and how to publish MCP servers.

