Skip to content

[spark-compete] fix(spawner-ui): return 400 for empty/malformed JSON body on MCP endpoints - #887

Open
cryptopecx wants to merge 1 commit into
vibeforge1111:mainfrom
cryptopecx:fix/mcp-endpoints-empty-body-400
Open

[spark-compete] fix(spawner-ui): return 400 for empty/malformed JSON body on MCP endpoints#887
cryptopecx wants to merge 1 commit into
vibeforge1111:mainfrom
cryptopecx:fix/mcp-endpoints-empty-body-400

Conversation

@cryptopecx

@cryptopecx cryptopecx commented Jul 8, 2026

Copy link
Copy Markdown
{
  "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

…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.
@cryptopecx

Copy link
Copy Markdown
Author

Verified the fix locally on this branch (cryptopecx/vibeship-spawner-ui:fix/mcp-endpoints-empty-body-400):

npm run test:run -- src/routes/api/mcp/mcp.integration.test.ts
✓ src/routes/api/mcp/mcp.integration.test.ts (8 tests) 21ms
Test Files  1 passed (1)
     Tests  8 passed (8)

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant