Open-World Write Exposure
Scenario: Verify that write and admin operations are absent from public-facing MCP paths, and that path separation prevents a read-tier caller from discovering or invoking write surfaces.
What You Are Testing
frisian-mcp's security architecture recommends separating read and write surfaces at the URL routing level. This is not a package default — it is an architecture you configure. This test verifies that if you have implemented path separation, the boundary holds: callers on the read path cannot reach write tools by any means.
The threat model is an agent — compromised, misconfigured, or adversarial — that:
- Enumerates tools via
tools/list - Identifies write or admin tool names
- Attempts to invoke them directly, bypassing the listing
Path separation defeats this at the routing level: write routes are not mounted on the read path. There is nothing to invoke.
See Security Architecture for the full design rationale.
Prerequisites
- Read path mounted at
/mcp/(public, no auth required or read-tier auth) - Write path mounted at a separate URL (e.g.
/mcp-write/or/mcp-protected/) - Write tools registered only on the write path's McpView instance
Test 1: Read Path tools/list Contains No Write Tools
Setup: Call /mcp/tools/list with a read-tier token (or no token if the path is public).
Pass condition: Response contains no tools with write or admin semantics. Create, update, destroy tools are absent.
How to verify: Cross-check the listing against /mcp-write/tools/list (with an authorized write token). Tools present on the write path but absent on the read path confirm correct separation.
Test 2: Direct Invocation of Write Tool on Read Path Returns 404
Setup: From the write-path tools/list, take the name of a write tool. Attempt to invoke it via /mcp/ directly:
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "devices.create",
"arguments": {"name": "probe-device"}
},
"id": 1
}
Pass condition: Response is a tool-not-found error (not a permission error). The tool does not exist on this path — the router never registered it here.
Fail condition: Response is a 403 permission error. This means the write tool IS registered on the read path but blocked by permission class. Path separation has not been achieved — a permission misconfiguration would expose the tool.
Test 3: Error Responses on Read Path Are Uninformative
Setup: On the read path (/mcp/), call a tool name that exists on the write path but not on the read path.
Pass condition: The error response for a non-existent tool is identical in shape and timing to the response for any other non-existent tool name. No timing side-channel, no schema leakage.
What to avoid: A read-path 404 that takes 15ms (route found, permission check ran) vs a truly missing route that takes 2ms tells a probing agent the write route exists. Route separation eliminates this timing signal entirely.
Test 4: Admin Tools Absent From Both Read and Write Paths (If Using Three-Tier Architecture)
Setup: If you have deployed a three-tier architecture (public read / authenticated write / admin), confirm that admin tools (admin=True on @mcp_tool) are absent from both /mcp/ and /mcp-write/.
Pass condition: Admin tools appear only at the protected admin path. Neither the read nor write tools/list includes them.
Test 5: Enumeration Attack Simulation
Setup: Using a read-token, systematically call tools/list, then attempt to invoke each write tool name observed during prior access (before path separation was deployed, or from source code knowledge).
Pass condition: All write tool names return tool-not-found on the read path. Zero write operations succeed.
This test is most useful as a regression check after deploying path separation for the first time.
Common Failures
| Symptom | Likely cause |
|---|---|
| Write tools visible on read path | Single McpView instance serving both paths — use separate view instances with separate URL mounts |
| Write tool returns 403 instead of 404 | Write tool is registered on the read path but permission-gated; path separation is not achieved |
| Admin tools appear in write-path listing | Tools registered with admin=True but no separate admin path configured — they default to the main tool registry |
| Timing difference between 404s | Route lookup cost differs — investigate middleware execution order |
Architecture Note
If you are not using path separation and instead rely on permission_classes alone, this test suite will find write tools on the read path (failing Test 1 and Test 2). That is an architectural finding, not a frisian-mcp bug. The package supports both configurations; path separation is a recommendation documented in the security guide. Decide deliberately which model fits your deployment before accepting the test results.