[spark-compete] fix(spawner-ui): return 400 for empty/malformed JSON body on MCP endpoints - #887
Conversation
…oints POST /api/mcp/call, POST /api/mcp, and DELETE /api/mcp threw 'Unexpected end of JSON input' (HTTP 500) when the request body was empty or malformed, because request.json() was called inside the broad try/catch that maps all errors to 500. Wrap request.json() in a dedicated guard that returns 400 with a clear 'Invalid or missing JSON body' message. No behavior change for valid requests. Adds regression tests for empty and malformed bodies.
|
Verified the fix locally on this branch (cryptopecx/vibeship-spawner-ui:fix/mcp-endpoints-empty-body-400): CI has not run on this PR. Because it is a fork PR from a contributor without write access to the upstream repo, GitHub blocks workflow runs until a maintainer approves them (Settings > Actions > "Approve and run workflows" on the PR). Without a green CI run, the PR cannot advance through the review gates. The packet in the PR body validates PASS via /api/packet/validate (ok:true, 0 errors, 0 warnings, review_required:true). The fix is minimal: it wraps request.json() in a dedicated try/catch on /api/mcp, /api/mcp/call, and DELETE /api/mcp, returning HTTP 400 with a clear client-facing message instead of HTTP 500 with a raw SyntaxError. Request: please approve the CI workflows for this fork PR so the test-and-audit + secret-scan checks can run and the review pipeline can proceed. Happy to repair the same PR if any gate needs changes. |
{ "schema": "spark-compete-hotfix-v1", "event": "spark-compete-first-event", "submission_mode": "public_repo_pr", "submission_target_url": "https://github.com/vibeforge1111/vibeship-spawner-ui/pull/887", "team": { "name": "Cryptopecx", "members": ["kolegxs", "acumalakas", "cryptopecx"], "llm_device_holder": "kolegxs", "device_holder_github": "https://github.com/kolegxs", "github_accounts": ["cryptopecx"] }, "target_repo": { "id": "vibeforge1111/vibeship-spawner-ui", "source": "https://github.com/vibeforge1111/vibeship-spawner-ui", "owner_surface": "spawner-ui" }, "issue": { "type": "bug", "severity": "low", "title": "fix(spawner-ui): MCP endpoints return 500 on empty/malformed JSON body", "actual_behavior": "POST /api/mcp/call, POST /api/mcp, and DELETE /api/mcp return HTTP 500 with raw {\"error\":\"Unexpected end of JSON input\"} when the request body is empty or malformed JSON. The 500 misleads callers into thinking it is a server fault, and the raw SyntaxError leaks an internal detail.", "expected_behavior": "These endpoints should return HTTP 400 with a clear client-facing message ('Invalid or missing JSON body') when the body cannot be parsed, consistent with the existing 400 handling for missing fields (instanceId/toolName) on the same routes.", "repro_steps": [ "spark start spawner-ui", "curl -X POST http://127.0.0.1:3333/api/mcp/call -H 'Content-Type: application/json' -d ''", "curl -X POST http://127.0.0.1:3333/api/mcp -H 'Content-Type: application/json' -d ''", "curl -X DELETE http://127.0.0.1:3333/api/mcp -H 'Content-Type: application/json' -d ''" ], "affected_workflow": "Operator reliability / MCP connection API" }, "evidence": { "safe_links_only": true, "before_after_proof": "BEFORE: POST /api/mcp/call (empty) -> HTTP 500 {\"error\":\"Unexpected end of JSON input\"}. POST /api/mcp (empty) -> 500. DELETE /api/mcp (empty) -> 500. AFTER (patched + running spawner-ui): all three return HTTP 400 {\"error\":\"Invalid or missing JSON body\"}.", "links": ["https://github.com/cryptopecx/vibeship-spawner-ui/blob/fix/mcp-endpoints-empty-body-400/src/routes/api/mcp/call/+server.ts"], "forbidden": ["tokens", "logs", "cookies", "private chats"] }, "proposed_fix": { "approach": "[spark-compete] fix(spawner-ui): return 400 for empty/malformed JSON body on MCP endpoints", "files_expected": [ "src/routes/api/mcp/call/+server.ts", "src/routes/api/mcp/+server.ts", "src/routes/api/mcp/mcp.integration.test.ts" ], "tests_or_smoke": "Added vitest regression tests in src/routes/api/mcp/mcp.integration.test.ts ('returns 400 for empty request body instead of 500', 'returns 400 for malformed JSON body instead of 500'). Live curl repro on running spawner-ui confirms 400 on all three endpoints; valid requests unchanged (connect 200, missing instanceId 400)." }, "pr": { "branch": "fix/mcp-endpoints-empty-body-400", "title_prefix": "[spark-compete]", "author_github": "cryptopecx", "url": "https://github.com/vibeforge1111/vibeship-spawner-ui/pull/887", "body_must_include": ["packet","team","pr_author","repo","actual_behavior","expected_behavior","repro_steps","before_after_proof","tests_or_smoke","duplicate_notes","risk_notes","review_claim"] }, "review_claim": { "impact_claim": "low", "evidence_types": ["redacted_terminal_excerpt", "passing_test"], "duplicate_notes": "Existing PRs #812, #255, #145 add safe-JSON helpers but target OTHER files (provider-artifacts.ts, mission/active, analyze/prd-bridge/scan/verify). None touch /api/mcp/* route handlers. Searched org:vibeforge1111 repo:vibeship-spawner-ui for mcp/json/400/empty body/request.json; no open or merged PR fixes the mcp endpoint empty-body 500.", "risk_notes": "Minimal localized change: only wraps request.json() in a dedicated try/catch returning 400. No behavior change for valid requests. No new dependencies, no auth/origin-surface change, no new files beyond the two route handlers and test additions.", "review_state_requested": "pr_review" } }packet: spark-compete-hotfix-v1
pr_author: cryptopecx
repo: vibeforge1111/vibeship-spawner-ui
Team: Cryptopecx | @cryptopecx
Testing
npm run test:run -- src/routes/api/mcp/mcp.integration.test.ts