Skip to content

proposal: serialize mcp_config as sanitized deprecated projection#2877

Closed
enyst wants to merge 2 commits intofix/mcp-secrets-expansionfrom
proposal/sanitized-mcp-config-serialization
Closed

proposal: serialize mcp_config as sanitized deprecated projection#2877
enyst wants to merge 2 commits intofix/mcp-secrets-expansionfrom
proposal/sanitized-mcp-config-serialization

Conversation

@enyst
Copy link
Copy Markdown
Collaborator

@enyst enyst commented Apr 18, 2026

Summary

This proposal keeps the serialized mcp_config field present, but changes it from a runnable config dump into a sanitized projection:

  • keep the field in serialized payloads to avoid the current contract break
  • redact string leaf values recursively so expanded secrets cannot leak
  • mark the serialized field as deprecated in schema metadata with a 5-minor-release runway (v1.17.0v1.22.0)
  • preserve the raw runtime config only for explicit internal transport paths via model_dump(context={"raw_mcp_config": True})

Why this version

I aimed for the smallest change that addresses both concerns:

  1. Security: public/default serialization no longer exposes expanded MCP secrets
  2. Compatibility: clients still see mcp_config in the response shape instead of the field disappearing outright

This keeps the public contract additive while making it clear that serialized mcp_config is no longer the runnable source of truth.

Changes

  • AgentBase.mcp_config
    • remove exclude=True
    • add a serializer that preserves nested shape but redacts string values
    • add JSON-schema deprecation metadata and explicit removal target
  • internal transport
    • pass raw_mcp_config=True for remote conversation startup / in-memory server handoff so the live runtime still gets the real config where explicitly intended
  • tests
    • update serialization leak tests to assert sanitized shape
    • add coverage for the raw transport override and schema deprecation metadata

Validation

Ran:

uv run pytest tests/sdk/agent/test_agent_serialization.py tests/sdk/conversation/test_mcp_secrets_serialization_leak.py tests/sdk/conversation/test_local_conversation_plugins.py tests/sdk/skills/test_mcp_config_expansion.py tests/sdk/plugin/test_plugin_loading.py tests/sdk/plugin/test_plugin_loader.py tests/sdk/conversation/test_secrets_manager.py -k 'mcp or secret or serialization'
uv run pytest tests/agent_server/test_conversation_service.py::TestConversationServiceStartConversation::test_start_conversation_with_secrets tests/agent_server/test_conversation_service.py::TestConversationServiceStartConversation::test_start_conversation_without_secrets -q
uv run pre-commit run --files openhands-sdk/openhands/sdk/agent/base.py openhands-sdk/openhands/sdk/conversation/impl/remote_conversation.py openhands-agent-server/openhands/agent_server/conversation_service.py openhands-agent-server/openhands/agent_server/event_service.py tests/sdk/agent/test_agent_serialization.py tests/sdk/conversation/test_mcp_secrets_serialization_leak.py

Notes

This proposal intentionally stays minimal. It does not try to reconstruct provenance inside an already-expanded MCP config. Instead it treats serialized mcp_config as a safe public projection and keeps raw config transport opt-in.

This PR was created by an AI assistant (OpenHands) on behalf of the user.

Serialize Agent.mcp_config as a sanitized projection instead of dropping the field entirely, and mark the serialized surface deprecated for removal in v1.22.0. Preserve raw runtime config only for explicit internal transport paths via raw_mcp_config context.

Co-authored-by: openhands <openhands@all-hands.dev>
Resolve the proposal branch conflicts by keeping the base branch's selective MCP redaction, while preserving the proposal's deprecation metadata and raw_mcp_config transport override.

Co-authored-by: openhands <openhands@all-hands.dev>
@github-actions
Copy link
Copy Markdown
Contributor

Coverage

Coverage Report •
FileStmtsMissCoverMissing
openhands-agent-server/openhands/agent_server
   conversation_service.py4549579%141–142, 169, 172, 174, 181–187, 215, 222, 243, 342, 348, 353, 359, 367–368, 377–380, 389, 403–405, 412, 445–446, 488, 491, 508–512, 514–515, 518–519, 522–527, 624, 631–635, 638–639, 643–647, 650–651, 655–659, 662–663, 669–674, 681–682, 686, 688–689, 694–695, 701–702, 709–710, 714–716, 734, 758, 1007, 1010
   event_service.py3498974%56–57, 75–77, 86–90, 93–96, 116, 220, 237, 278, 288, 312–313, 317, 325, 328, 368–369, 385, 387, 391–393, 397, 406–407, 409, 413, 419, 421, 451–456, 603, 605–606, 610, 624–626, 628, 632–635, 639–642, 650–653, 673–674, 676–683, 685–686, 695–696, 698–699, 706–707, 709–710, 730, 736, 742, 751–752
openhands-sdk/openhands/sdk/agent
   base.py2101791%217, 283, 351–353, 404–406, 430, 440, 448–449, 553, 590–591, 601–602
openhands-sdk/openhands/sdk/conversation/impl
   remote_conversation.py62310183%78, 80, 147, 174, 187, 189–192, 202, 224–225, 230–233, 316, 326–328, 334, 375, 522–525, 527, 553–557, 562–565, 568, 584, 746–747, 751–752, 766, 790–791, 810, 821–822, 842–845, 847–848, 872–874, 877–881, 883–884, 888, 890–898, 900, 937, 1068, 1140–1141, 1145, 1150–1154, 1160–1166, 1179, 1184, 1219, 1275, 1282, 1288–1289, 1367–1368
TOTAL23467567975% 

@enyst enyst closed this Apr 18, 2026
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