Context
PR #21 added a large mcp20 test suite that mostly locks in the sidecar, not the 2026 wire:
tests/test_mcp20_stateless_http.py ~3300 lines: StatelessIngressPipeline, header-only ping, era alias soup
tests/test_mcp20_acceptance.py: protocol_coverage_complete() / “every enum has a row” / ping listed as a deliverable
tests/test_mcp20_blueprint.py: import layout as conformance
nitrostack/runtime/acceptance.py and conformance.py: ticket registries, not spec checks
- Public API exports
build_ping_response, wrap_stateless_transport, StatelessIngressPipeline (nitrostack/__init__.py, nitrostack/protocol/__init__.py)
RuntimeLayer / DispatchStage exist for the sidecar lifecycle
WP0–WP2 will break the 3k-line file on purpose. This issue replaces that suite with a small HTTP matrix and drops ping/sidecar exports.
Handoff: #21 (comment)
Do this after WP0 (unwrap) and in the same PR as or immediately after WP2 (wire assertions). Do not “fix” header-only ping tests to keep them green.
Goal
Few TestClient tests against McpApplication.get_combined_app(). Production no longer exports ping/sidecar helpers. Layout registries are not the definition of done.
Probe
wc -l tests/test_mcp20_*.py
rg -n "build_ping_response|wrap_stateless_transport|StatelessIngressPipeline|is_header_only_ping" nitrostack tests
rg -n "ACCEPTANCE_CRITERIA|PROTOCOL_DELIVERABLES|assert_blueprint_layout" nitrostack tests
Read tests/test_transport_http.py and tests/test_lifecycle_http.py for fixture style to copy. Read nitrostack/__init__.py export lists.
Implementation
- Delete or gut
tests/test_mcp20_stateless_http.py (do not keep ping fast-path cases).
- Delete or gut layout/count tests in
tests/test_mcp20_acceptance.py and tests/test_mcp20_blueprint.py.
- Delete
nitrostack/runtime/acceptance.py and conformance.py if nothing production imports them (only tests). Same for unused RuntimeLayer (protocol/layers.py) and DispatchStage if the pipeline is gone (WP0).
- Replace with one (or two) focused modules, e.g.
tests/test_mcp20_http_wire.py, using Starlette TestClient + a tiny @mcp_app like existing HTTP tests.
Matrix (era via env/config; reset DI in setup like other tests):
| Case |
Era |
Expect |
| discover + required headers |
modern, auto |
200, _meta.serverInfo, no Mcp-Session-Id |
ping |
modern, auto |
-32601 |
initialize |
modern |
-32601 |
initialize |
auto |
mcp 2.x 2025 handshake (or documented auto); not NitroStack sidecar |
tools/call without Mcp-Method |
modern |
-32020 |
unknown tool tools/call |
any |
pick existing behavior (isError vs -32601) and assert it |
GET /mcp/health |
auto |
200, protocolEra present |
tasks/* |
modern, auto |
-32601 if WP3 Hide landed; skip/xfail with issue link if WP3 is later |
| CIMD |
— |
keep tests/test_mcp20_oauth_cimd.py (WP4) |
-
Exports. Remove build_ping_response, wrap_stateless_transport, StatelessIngressPipeline from public __init__ if WP0 deleted them. rg ping nitrostack/ empty of live RPC.
-
Concurrency tests (tests/test_mcp20_concurrency.py): if InFlightRegistry was deleted (WP3), delete tests that poke app._in_flight. Do not keep tests that require an unwired cancel map.
-
Do not bump pyproject.toml version. Do not rewrite README feature marketing unless a sentence is factually wrong (ping advertised).
Keep: tests/test_mcp20_oauth_cimd.py, schema/contracts tests that still match protocol/schema.py, MRTR helper tests if those helpers remain.
Test / verify
pytest tests/test_mcp20_http_wire.py tests/test_transport_http.py tests/test_lifecycle_http.py tests/test_oauth.py tests/test_tasks.py -q
pytest tests/test_mcp20_*.py -q
Second command should not include a 3k-line ping suite. Full pytest -q on the repo should pass or only fail on unrelated pre-existing tests — do not ignore failures in mcp20 files you kept.
Review
- New tests hit
get_combined_app() (or the same Starlette app start() uses), not StatelessIngressPipeline as a unit, unless a non-RPC header-only helper remains.
- No test requires
Mcp-Method: ping + garbage body → 200.
acceptance_criteria_registered() / len(PROTOCOL_DELIVERABLES) >= 14 gone.
Success
Related
Depends on WP0 and WP2. Coordinate WP3 Hide so task rows in the matrix match. WP4 keeps its own CIMD tests.
Context
PR #21 added a large mcp20 test suite that mostly locks in the sidecar, not the 2026 wire:
tests/test_mcp20_stateless_http.py~3300 lines:StatelessIngressPipeline, header-only ping, era alias souptests/test_mcp20_acceptance.py:protocol_coverage_complete()/ “every enum has a row” / ping listed as a deliverabletests/test_mcp20_blueprint.py: import layout as conformancenitrostack/runtime/acceptance.pyandconformance.py: ticket registries, not spec checksbuild_ping_response,wrap_stateless_transport,StatelessIngressPipeline(nitrostack/__init__.py,nitrostack/protocol/__init__.py)RuntimeLayer/DispatchStageexist for the sidecar lifecycleWP0–WP2 will break the 3k-line file on purpose. This issue replaces that suite with a small HTTP matrix and drops ping/sidecar exports.
Handoff: #21 (comment)
Do this after WP0 (unwrap) and in the same PR as or immediately after WP2 (wire assertions). Do not “fix” header-only ping tests to keep them green.
Goal
Few
TestClienttests againstMcpApplication.get_combined_app(). Production no longer exports ping/sidecar helpers. Layout registries are not the definition of done.Probe
Read
tests/test_transport_http.pyandtests/test_lifecycle_http.pyfor fixture style to copy. Readnitrostack/__init__.pyexport lists.Implementation
tests/test_mcp20_stateless_http.py(do not keep ping fast-path cases).tests/test_mcp20_acceptance.pyandtests/test_mcp20_blueprint.py.nitrostack/runtime/acceptance.pyandconformance.pyif nothing production imports them (only tests). Same for unusedRuntimeLayer(protocol/layers.py) andDispatchStageif the pipeline is gone (WP0).tests/test_mcp20_http_wire.py, using StarletteTestClient+ a tiny@mcp_applike existing HTTP tests.Matrix (era via env/config; reset DI in setup like other tests):
_meta.serverInfo, noMcp-Session-Idping-32601initialize-32601initializetools/callwithoutMcp-Method-32020tools/callisErrorvs-32601) and assert itGET /mcp/healthprotocolErapresenttasks/*-32601if WP3 Hide landed; skip/xfail with issue link if WP3 is latertests/test_mcp20_oauth_cimd.py(WP4)Exports. Remove
build_ping_response,wrap_stateless_transport,StatelessIngressPipelinefrom public__init__if WP0 deleted them.rg ping nitrostack/empty of live RPC.Concurrency tests (
tests/test_mcp20_concurrency.py): ifInFlightRegistrywas deleted (WP3), delete tests that pokeapp._in_flight. Do not keep tests that require an unwired cancel map.Do not bump
pyproject.tomlversion. Do not rewrite README feature marketing unless a sentence is factually wrong (pingadvertised).Keep:
tests/test_mcp20_oauth_cimd.py, schema/contracts tests that still matchprotocol/schema.py, MRTR helper tests if those helpers remain.Test / verify
pytest tests/test_mcp20_http_wire.py tests/test_transport_http.py tests/test_lifecycle_http.py tests/test_oauth.py tests/test_tasks.py -q pytest tests/test_mcp20_*.py -qSecond command should not include a 3k-line ping suite. Full
pytest -qon the repo should pass or only fail on unrelated pre-existing tests — do not ignore failures in mcp20 files you kept.Review
get_combined_app()(or the same Starlette appstart()uses), notStatelessIngressPipelineas a unit, unless a non-RPC header-only helper remains.Mcp-Method: ping+ garbage body → 200.acceptance_criteria_registered()/len(PROTOCOL_DELIVERABLES) >= 14gone.Success
pytest tests/test_mcp20_*.pyis maintainable (order of hundreds of lines, not thousands of sidecar cases)Related
Depends on WP0 and WP2. Coordinate WP3 Hide so task rows in the matrix match. WP4 keeps its own CIMD tests.