Skip to content

fix(mcp): reconcile live agent tool snapshots - #4367

Merged
neubig merged 4 commits into
OpenHands:mainfrom
Shimada666:fix/mcp-tool-reconciliation
Aug 4, 2026
Merged

fix(mcp): reconcile live agent tool snapshots#4367
neubig merged 4 commits into
OpenHands:mainfrom
Shimada666:fix/mcp-tool-reconciliation

Conversation

@Shimada666

@Shimada666 Shimada666 commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

HUMAN:

This is a capability I need in practice. Our work requires tools to be updated dynamically, and OpenCode already supports this behavior.

Agent-assisted validation: I reviewed the output from the real FastMCP streamable-HTTP integration tests.
They verified dynamic tool addition, invocation, removal, re-advertisement, and same-name schema replacement.


AGENT:

Why

notifications/tools/list_changed currently updates MCPClient.tools, but a running agent only receives newly added definitions. Removed tools remain exposed by the agent, and same-name schema changes keep the stale definition and validation model. This completes the stale-runtime behavior identified in the review of #3894 and tracked by #4059.

Summary

  • Atomically replace only the tools owned by the notifying MCP client, including additions, schema updates, and removals.
  • Keep the public additions-only callback while binding full-snapshot reconciliation on MCPClient, preserving the existing MCPToolProvider contract.
  • Reconcile against the current Agent instance, including MCP tools loaded by runtime plugins.
  • Serialize Agent tool-map reads and writes across concurrent MCP clients.
  • Key dynamically generated MCP validation models by both tool name and input schema.
  • Document the behavior in docs(sdk): describe live MCP tool reconciliation docs#685.

Issue Number

Follow-up to #4059 and #3894.

How to Test

Run:

uv run pytest -q tests/sdk/mcp/ tests/sdk/conversation/test_local_conversation_plugins.py tests/sdk/conversation/test_local_conversation_mcp.py tests/sdk/agent/test_filter_tools_regex.py

Observed on the current PR head: 178 passed in 9.93s. This includes a real FastMCP streamable-HTTP server that emits notifications/tools/list_changed; the test verifies that a dynamically added tool is callable, disappears from both client and agent after removal, and is callable again after re-advertisement. A focused unit test also warms the old validation model, changes the same tool name to a new input schema, and verifies the new schema accepts new arguments and rejects the old arguments.

Run the repository checks on all changed files:

PIP_INDEX_URL=https://pypi.org/simple uv run pre-commit run --files openhands-sdk/openhands/sdk/mcp/utils.py openhands-sdk/openhands/sdk/mcp/client.py openhands-sdk/openhands/sdk/mcp/tool.py openhands-sdk/openhands/sdk/agent/base.py openhands-sdk/openhands/sdk/conversation/impl/local_conversation.py openhands-agent-server/openhands/agent_server/mcp_oauth_store.py tests/sdk/mcp/test_mcp_tool_list_changed.py tests/sdk/agent/test_filter_tools_regex.py tests/sdk/conversation/test_local_conversation_mcp.py tests/sdk/conversation/test_local_conversation_plugins.py

Observed: Ruff format/lint, pycodestyle, Pyright, import rules, and tool registration checks all passed.

Video/Screenshots

Not applicable; this is SDK runtime behavior covered by the real FastMCP integration test.

Type

  • Bug fix
  • Feature
  • Refactor
  • Breaking change
  • Docs / chore

Notes

The public on_tools_changed callback keeps its additions-only contract. The MCPToolProvider.create_tools() signature is unchanged, so existing custom providers remain compatible. Reconciliation is attached to the returned MCPClient; historical action and observation events are untouched.

Co-authored-by: openhands <openhands@all-hands.dev>
@all-hands-bot

Copy link
Copy Markdown
Collaborator

👋 This PR needs a couple of things fixed before OpenHands can review it:

  • the PR description's HUMAN: section needs at least 20 characters describing what you tested, not just the template placeholder

Push an update once this is addressed and this check re-runs automatically.

This is an automated check - no AI was used to generate this comment.

1 similar comment
@all-hands-bot

Copy link
Copy Markdown
Collaborator

👋 This PR needs a couple of things fixed before OpenHands can review it:

  • the PR description's HUMAN: section needs at least 20 characters describing what you tested, not just the template placeholder

Push an update once this is addressed and this check re-runs automatically.

This is an automated check - no AI was used to generate this comment.

@all-hands-bot

Copy link
Copy Markdown
Collaborator

🚦 CI is currently failing on this PR's latest commit.

Please fix the failing checks before OpenHands reviews it - this is re-checked automatically once you push a new commit. (A maintainer can also request @all-hands-bot as a reviewer to have it reviewed regardless of CI status.)

This is an automated check - no AI was used to generate this comment.

Shimada666 and others added 3 commits August 4, 2026 20:20
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: openhands <openhands@all-hands.dev>
@Shimada666

Copy link
Copy Markdown
Contributor Author

@VascoSch92, would you be willing to help review this PR?

Our current production use case requires MCP tools to be added, updated, and removed dynamically while an agent conversation is running. In particular, when an MCP server emits notifications/tools/list_changed, the live agent needs to reconcile its exposed tool set rather than retaining stale tools or schemas.

Please feel completely free to change any part of this PR, push an alternative implementation, or reimplement the approach from scratch if you see a cleaner design. We care more about getting the SDK behavior and extension boundary right than preserving the current code.

Thank you very much for your time and help!

@neubig neubig left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Approved: live MCP tool reconciliation is sound, with focused and full CI coverage verified.

@neubig
neubig merged commit 4d0b53c into OpenHands:main Aug 4, 2026
37 checks passed
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.

3 participants