Skip to content

Reject non-json MCP content types#857

Open
xiefuzheng713-alt wants to merge 1 commit into
ramimbo:mainfrom
xiefuzheng713-alt:codex/mcp-content-type
Open

Reject non-json MCP content types#857
xiefuzheng713-alt wants to merge 1 commit into
ramimbo:mainfrom
xiefuzheng713-alt:codex/mcp-content-type

Conversation

@xiefuzheng713-alt
Copy link
Copy Markdown
Contributor

@xiefuzheng713-alt xiefuzheng713-alt commented Jun 4, 2026

Summary:

  • rejects /mcp requests whose Content-Type is missing or not application/json before parsing the JSON-RPC body;
  • keeps application/json; charset=utf-8 accepted and preserves the existing JSON-RPC parse-error response for malformed JSON;
  • adds regressions for text/plain, text/html, application/xml, multipart/form-data, and missing Content-Type.

Bounty #799

Source report: #798 (comment)

Production evidence before fix:

  • POST https://mcp.mrwk.online/mcp with a valid tools/list JSON-RPC body and Content-Type: application/json -> HTTP 200, returns the tools list.
  • The same valid JSON body with Content-Type: text/plain -> HTTP 200, returns the tools list.
  • The same valid JSON body with Content-Type: text/html -> HTTP 200, returns the tools list.
  • The same valid JSON body with Content-Type: application/xml -> HTTP 200, returns the tools list.
  • The same valid JSON body with no explicit Content-Type -> HTTP 200, returns the tools list.

Duplicate/current check:

Scope:

  • This PR intentionally stays on the public MCP /mcp request media-type gate.
  • It does not change MCP tool semantics, wallet behavior, ledger mutation, bounty creation, payout execution, treasury mutation, private data, secrets, bridge/exchange/cash-out behavior, or MRWK price behavior.

Validation:

  • uv run --extra dev pytest tests/test_api_mcp.py::test_mcp_rejects_malformed_requests_without_500 tests/test_api_mcp.py::test_mcp_rejects_non_json_content_types tests/test_api_mcp.py::test_mcp_tools_list_and_call -q -> 7 passed, 1 warning.
  • uv run --extra dev pytest tests/test_api_mcp.py tests/test_mcp_tools.py -q -> 117 passed, 1 warning.
  • uv run --extra dev ruff check app/mcp.py tests/test_api_mcp.py -> passed.
  • uv run --extra dev ruff format --check app/mcp.py tests/test_api_mcp.py -> 2 files already formatted.
  • uv run --extra dev mypy app/mcp.py -> success.
  • uv run --extra dev python scripts/docs_smoke.py -> docs smoke ok.
  • git diff --check origin/main...HEAD -> clean.
  • git merge-tree --write-tree origin/main HEAD -> clean tree abda6f4c7b8cc6d19c1ef4bc0fd2711b121cfce1.

Summary by CodeRabbit

  • Bug Fixes

    • Improved request validation by detecting and rejecting non-JSON Content-Type headers early, returning HTTP 415 status with appropriate error messages instead of attempting to parse invalid requests
  • Tests

    • Added comprehensive test coverage for Content-Type validation across various media types and edge cases
    • Enhanced existing malformed request tests to verify proper handling of JSON requests with charset specifications

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 4, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: d4277645-d29b-48b4-93bc-d27034e38b5e

📥 Commits

Reviewing files that changed from the base of the PR and between d4d0e48 and ce11ee6.

📒 Files selected for processing (2)
  • app/mcp.py
  • tests/test_api_mcp.py

📝 Walkthrough

Walkthrough

The PR adds Content-Type header validation to MCP request handling. A helper function normalizes and checks whether the header indicates JSON, while the request handler uses it to reject non-JSON requests early with HTTP 415 and a JSON-RPC error. Tests verify both accepted JSON variants and multiple rejected non-JSON media types.

Changes

MCP Content-Type Validation

Layer / File(s) Summary
Content-Type validation helper and handler guard
app/mcp.py
_is_json_content_type() normalizes the Content-Type header and checks for application/json, while handle_mcp_request() uses it as an early guard to reject non-JSON requests with HTTP 415 and a JSON-RPC error response.
Content-Type validation test coverage
tests/test_api_mcp.py
Added test cases verifying JSON requests with charset parameters succeed (200), and parametrized test confirming non-JSON and missing Content-Type requests are rejected with HTTP 415 and standardized unsupported media type error payload.

Possibly related PRs

  • ramimbo/mergework#329: Overlaps at the MCP HTTP request handler layer in app/mcp.py, with this PR adding early Content-Type validation to the existing handler logic.
🚥 Pre-merge checks | ✅ 6
✅ Passed checks (6 passed)
Check name Status Explanation
Title check ✅ Passed Title clearly names the changed surface—rejecting non-JSON content types in MCP requests—and directly reflects the main changeset.
Description check ✅ Passed Description includes all required template sections with substantive content: summary, evidence of confusion, bounty reference, scope boundaries, and comprehensive validation results.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Mergework Public Artifact Hygiene ✅ Passed PR contains no prohibited artifacts: no investment/price/cash-out claims, fabricated payouts, or security details. MRWK correctly described as native coin.
Bounty Pr Focus ✅ Passed PR diff matches stated scope: app/mcp.py (+13) and tests/test_api_mcp.py (+36) only. Adds media-type gate with HTTP 415 response. Test coverage for non-JSON types confirmed.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@NiXouuuu NiXouuuu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed current head ce11ee6cd671918cc06418fed6acce5b21925797 as a non-author for Bounty #838 review evidence.

Scope inspected:

  • app/mcp.py: verified the new media-type gate rejects missing or non-application/json request content types before JSON parsing, while still accepting application/json; charset=utf-8.
  • tests/test_api_mcp.py: verified regressions cover text/plain, text/html, application/xml, multipart/form-data, missing Content-Type, charset-qualified JSON, malformed JSON, non-object requests, and invalid params.

Live production repro before this fix, using a valid tools/list JSON-RPC body against https://mcp.mrwk.online/mcp:

  • Content-Type: application/json -> HTTP 200, tools list returned.
  • Content-Type: application/json; charset=utf-8 -> HTTP 200, tools list returned.
  • Content-Type: text/plain -> HTTP 200, tools list returned.
  • Content-Type: text/html -> HTTP 200, tools list returned.
  • Content-Type: application/xml -> HTTP 200, tools list returned.
  • Content-Type: multipart/form-data -> HTTP 200, tools list returned.
  • no explicit Content-Type with a raw byte body -> HTTP 200, tools list returned.

Validation run locally on this exact head:

  • uv run --extra dev pytest tests/test_api_mcp.py::test_mcp_rejects_malformed_requests_without_500 tests/test_api_mcp.py::test_mcp_rejects_non_json_content_types tests/test_api_mcp.py::test_mcp_tools_list_and_call -q -> 7 passed, 1 warning.
  • uv run --extra dev pytest tests/test_api_mcp.py tests/test_mcp_tools.py -q -> 117 passed, 1 warning.
  • uv run --extra dev ruff check app/mcp.py tests/test_api_mcp.py -> passed.
  • uv run --extra dev ruff format --check app/mcp.py tests/test_api_mcp.py -> 2 files already formatted.
  • uv run --extra dev mypy app/mcp.py -> success.
  • uv run --extra dev python scripts/docs_smoke.py -> docs smoke ok.
  • git diff --check origin/main...HEAD -> clean.
  • git merge-tree --write-tree origin/main HEAD -> clean tree 53079ae854d4657d7a3947244732352d41514e91.

Current GitHub state before review: mergeable=MERGEABLE, mergeStateStatus=UNSTABLE only because CodeRabbit is still pending, hosted Quality, readiness, docs, and image checks is SUCCESS, and no current-head human reviews are present.

No blockers found in the reviewed scope. The change is limited to the public MCP request media-type gate and does not touch MCP tool semantics, wallet behavior, ledger mutation, bounty creation, payout execution, treasury mutation, private data, secrets, bridge/exchange/cash-out behavior, or MRWK price behavior.

Copy link
Copy Markdown
Contributor

@MolhamHamwi MolhamHamwi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed current head ce11ee6cd671918cc06418fed6acce5b21925797.

Checked paths:

  • app/mcp.py
  • tests/test_api_mcp.py

Evidence:

  • The new _is_json_content_type() gate normalizes the media type before parameters, so application/json; charset=utf-8 remains accepted while missing/non-JSON content types are rejected before request.json() is called.
  • The 415 response uses a JSON-RPC error with id: null, which is appropriate because the server intentionally does not parse the body after rejecting the media type.
  • Existing malformed JSON behavior remains covered separately: with JSON content type, invalid JSON still returns the prior parse error path rather than being conflated with unsupported media type.

Validation run:

  • .venv/bin/python -m pytest tests/test_api_mcp.py -q → 110 passed, 1 existing Starlette/httpx warning.
  • .venv/bin/python -m ruff check app/mcp.py tests/test_api_mcp.py → passed.

No blocker found; this is a focused fix for the reported MCP content-type handling issue.

Copy link
Copy Markdown
Contributor

@alan747271363-art alan747271363-art left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed current head ce11ee6cd671918cc06418fed6acce5b21925797 against current origin/main d7e9b530fffec7bd774da7708597648096a37393.

Scope inspected:

  • app/mcp.py
  • tests/test_api_mcp.py
  • supporting MCP tool coverage in tests/test_mcp_tools.py

The branch-local media-type gate remains focused: _is_json_content_type() accepts application/json and application/json; charset=utf-8, rejects missing/non-JSON Content-Type before JSON parsing, and keeps malformed JSON on the existing JSON-RPC parse-error path. It does not alter MCP tool semantics, wallet behavior, ledger mutation, bounty creation, payout execution, treasury mutation, private data, or secret handling.

Validation on this exact head:

  • .\.venv\Scripts\python.exe -m pytest tests\test_api_mcp.py tests\test_mcp_tools.py -q -> 112 passed, 1 existing Starlette/httpx warning.
  • .\.venv\Scripts\python.exe -m ruff check app\mcp.py tests\test_api_mcp.py -> passed.
  • .\.venv\Scripts\python.exe -m ruff format --check app\mcp.py tests\test_api_mcp.py -> 2 files already formatted.
  • .\.venv\Scripts\python.exe -m mypy app\mcp.py -> success.
  • .\.venv\Scripts\python.exe scripts\docs_smoke.py -> docs smoke ok.
  • git diff --check origin/main...HEAD -> clean.

Current blocker: GitHub now reports mergeStateStatus=DIRTY / conflicting. git merge-tree --write-tree origin/main HEAD exits non-zero with a current-main conflict in app/bounty_api.py while auto-merging tests/test_bounty_api_routes.py. That conflict is outside the visible MCP content-type diff, so this appears to be a stale branch-base/rebase issue, but it blocks merging and should be resolved before merge.

No private data, credentials, wallet material, production mutation, payout execution, treasury execution, ledger mutation, exchange, bridge, cash-out, price behavior, or fabricated payout claims were used.

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.

4 participants