Catch broken MCP behavior before it reaches main.
M3 is a pytest framework for MCP servers. It tests your server against the agents your users run, like Claude Code and Codex, instead of a bare model API. Hand the setup to your coding agent.
Your MCP Server had the answer.
The agent didn’t ask.
Agents answer from memory when your tool doesn’t tell them not to. M3 runs Claude Code, Codex, OpenCode, or Pi against your server, records every MCP tool call, and fails the test when your server was skipped. Fix the description, rerun, ship.
tests/test_registry.pyresult = agent.run("Add Tailwind to this project.", server=registry)expect(result).to_have_tool_call("get_latest_version", arguments={"package": "tailwindcss"})
❯ Add Tailwind to this project.
- MCP tool calls
- none, answered from memory
- package.json
"tailwindcss": "^3.4.1"
Expected get_latest_version(package="tailwindcss"). Observed no MCP tool calls.
Works in Claude Code. Does it work in Codex?
Run the same tests in Claude Code, Codex, OpenCode, and Pi, and compare the results side by side. Your tests don’t change.
$ m3 test --harness claude-code=claude-sonnet-5-5 --harness codex=gpt-6.1-sol --harness opencode=anthropic/claude-sonnet-5-5 --harness pi=openai/gpt-6.1-sol -- tests| test | |||||
|---|---|---|---|---|---|
| test_adds_current_tailwind | pass | pass | fail | pass | ? |
| test_upgrades_react | pass | fail | pass | pass | ? |
| test_pins_current_typescript | pass | pass | pass | pass | ? |
Point M3 at its command. It runs the same tests with its own model and tools.
@pytest.mark.m3(agents=[{
"harness": "acp",
"models": ["my-agent"],
"manifest": {"command": "my-agent"},
}])Codex updated overnight. Did your server break?
Pin the version your users run today and the one they get next. M3 runs the same tests on both, so you catch a break before your users do.
$ m3 test --runtime managed --harness [email protected]=gpt-6.1-sol --harness [email protected]=gpt-6.1-sol -- tests| test | ||
|---|---|---|
| test_adds_current_tailwind | pass | pass |
| test_upgrades_react | pass | pass |
| test_pins_current_typescript | pass | fail |
See why it failed.
The test failed when it ran with OpenCode. Open that run: the agent asked for "tailwind" instead of "tailwindcss". M3 keeps every MCP tool call, so you see what went wrong without rerunning anything.
run_01JQ8M3get_latest_version(package="tailwindcss")OBSERVEDget_latest_version(package="tailwind") → error: unknown package "tailwind"m3 test --ui -- testsYour agent writes the tests. Your repo keeps them.
- 1
Copy the skill into your coding agent
One prompt. Works in Claude Code, Codex, OpenCode, or any agent that reads skills.
- 2
It writes tests for your server
Direct tests for schemas, results, and errors, with no model needed. Agent tests like the ones above.
- 3
You keep plain pytest files
Review them like any other code. They live in your repo, not in a dashboard.
tests/test_registry.py
Zero checks can’t pass the gate.
Run the same suite locally and on pull requests. M3 fails CI when an assertion breaks or when no tests executed at all.
$ m3 ci test -- testsContract, error, response, and behavior assertions passed.
The returned structured content no longer matches the advertised schema.
A run where every test was skipped or deselected cannot approve a change.
exit 1Before you gate a branch.
Getting started, test scope, native execution, CI, and local reports.
How do I start using M3?
Give your coding agent the M3 skill, or install the CLI yourself and run m3 init, m3 setup, and m3 doctor in your project. Read the installation guide.
What does M3 test directly versus through an agent?
Direct tests check schemas, responses, errors, and server state without a model. Agent tests check which tools a real harness used. Both live in ordinary pytest.
Why use the harness’s own binary?
Tool choice depends on the client’s real discovery and permission flow. M3 runs the harness binary and records the resulting tool calls for your assertions.
My agent isn’t listed. Can M3 test it?
If it speaks the Agent Client Protocol (ACP), yes. Point a manifest at its command in your test’s m3 marker and it runs the same suite, keeping its own model and tools. M3 records its tool calls and checks what your server returned. An agent without ACP needs a small wrapper process. Bring your own agent.
Can the same tests run in CI?
Yes. Run the suite locally with m3 test and in CI with m3 ci test. Direct server checks and agent trials remain separate, and a run in which no tests execute fails.
Where do reports and traces go?
M3 keeps run history in .m3/executions.sqlite and writes feedback in .m3/reports. Open the optional local viewer with m3 test --ui.
Something else?
Email us at our contact address (shown when scripts load).
Start testing your MCP server with M3.
Guides for direct tests, agent tests, and running them in CI.