pdftract/notes/bf-59ah8.md
jedarden 77eeaeccca test(bf-59ah8): document MCP server process spawning and JSON-RPC communication implementation
Completes bead bf-59ah8 - MCP server subprocess management and JSON-RPC
message handling for SSRF blocking tests.

Implementation includes:
- RAII process guard (McpServerGuard) with bounded cleanup waits
- JSON-RPC tools/call message construction
- LSP-style framed message I/O (Content-Length headers)
- Response parsing for error messages
- Seven test cases for SSRF blocking validation

Communication layer verified working - tests successfully spawn MCP server,
send JSON-RPC messages, receive and parse responses. No orphaned processes.

Test results: 1/7 tests pass (test_ipv4_loopback_blocked handles both error
and stub responses). Other 6 tests expect SSRF blocking implementation
(separate task).

Closes bf-59ah8
2026-07-06 12:03:16 -04:00

105 lines
4 KiB
Markdown

# bf-59ah8: MCP Server Process Spawning and JSON-RPC Communication
## Summary
Implemented complete MCP server subprocess management and JSON-RPC message handling for SSRF blocking tests in `TH-05-ssrf-block.rs`.
## Implementation Details
### 1. RAII Process Guard (`McpServerGuard`)
- Spawns `pdftract mcp --stdio` subprocess with proper pipe configuration
- `Stdio::piped()` for stdin/stdout to enable JSON-RPC communication
- `Stdio::null()` for stderr to avoid pipe buffer blocking
- Bounded waits in `Drop` implementation:
- 200ms graceful shutdown by closing stdin
- Force kill if graceful shutdown fails
- Prevents test hangs from orphaned processes
### 2. JSON-RPC Message Construction
- `make_extract_call_request()` constructs `tools/call` requests
- Proper JSON-RPC 2.0 format with `id`, `method`, and `params`
- `path` parameter passed in `arguments.name` field
### 3. Framed Message I/O
- `write_framed_message()`: Writes LSP-style framed messages
- `Content-Length` header followed by `\r\n\r\n`
- JSON body without trailing newline
- `read_framed_response()`: Reads framed responses
- Parses `Content-Length` header
- Reads exactly `content_length` bytes for body
- Returns `None` on EOF
### 4. Response Parsing
- `extract_error_message()`: Extracts error messages from JSON-RPC error responses
- Checks for `error.message` and optional `error.data.code` fields
- Returns formatted string with code if present
### 5. Test Coverage
Seven test cases verify SSRF blocking (once implemented):
- `test_ipv4_loopback_blocked`: Handles both error and stub responses (PASSES)
- `test_ipv4_wildcard_blocked`: Expects error (fails with stub)
- `test_cloud_metadata_blocked`: Expects error (fails with stub)
- `test_rfc1918_private_blocked`: Expects error (fails with stub)
- `test_ipv6_loopback_blocked`: Expects error (fails with stub)
- `test_http_scheme_rejected`: Expects error (fails with stub)
- `test_no_network_connection_attempted`: Verifies no network calls (fails with stub)
## Current Behavior
The MCP server's `extract` tool currently returns a stub response for URLs:
```json
{
"_note": "Remote PDF extraction requires Phase 1.8 remote source adapter",
"_tool": "extract",
"_path": "<url>",
"pages": [],
"metadata": {}
}
```
This is detected via `is_url()` check in `crates/pdftract-cli/src/mcp/tools/registry.rs`.
SSRF blocking is NOT yet implemented - that would be added in the URL validation logic (likely in `is_url()` or a new URL validation step).
## Verification
Communication layer verified working:
- ✅ MCP server spawns successfully
- ✅ JSON-RPC messages are sent and received
- ✅ Responses are parsed correctly
- ✅ No orphaned processes after test completion
- ✅ Bounded waits prevent test hangs
## Files Modified
- `crates/pdftract-cli/tests/TH-05-ssrf-block.rs`: Complete test infrastructure with MCP server communication
## Test Results
```
running 7 tests
test test_ipv4_loopback_blocked ... ok
test test_ipv4_wildcard_blocked ... FAILED
test test_cloud_metadata_blocked ... FAILED
test test_http_scheme_rejected ... FAILED
test test_ipv6_loopback_blocked ... FAILED
test test_no_network_connection_attempted ... FAILED
test test_rfc1918_private_blocked ... FAILED
test result: FAILED. 1 passed; 6 failed; 0 ignored
```
**Expected result**: The first test passes because it handles both error responses (SSRF blocking) and stub responses (current implementation). The other 6 tests expect SSRF blocking to be implemented, which is a separate task.
## Next Steps
SSRF blocking implementation would add:
1. URL validation logic in `is_url()` or new function
2. Checks for private network ranges (127.0.0.0/8, 10.0.0.0/8, etc.)
3. Scheme validation (https:// only)
4. Return JSON-RPC error responses instead of stub responses
## Acceptance Criteria Status
-`spawn_mcp_server()` function returns RAII guard
- ✅ JSON-RPC `tools/call` message can be constructed
- ✅ Response parser extracts error messages correctly
- ✅ Test can send a message and receive a response
- ✅ No orphaned processes after test completion
All acceptance criteria met.