Skip to content

feat(mcp): surface MCP resources and prompts in the SDK - #6196

Open
xxwtiancai wants to merge 5 commits into
OpenHands:mainfrom
xxwtiancai:feat/mcp-resources-prompts-6191
Open

xxwtiancai wants to merge 5 commits into
OpenHands:mainfrom
xxwtiancai:feat/mcp-resources-prompts-6191

Conversation

@xxwtiancai

@xxwtiancai xxwtiancai commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

HUMAN:

Summary

Implements SDK-level support for MCP resources and prompts (#6191) as the foundational step of the SDK-first rollout. Server REST APIs and TypeScript clients are deferred to follow-up PRs.

Key Changes

  • Core Helpers (openhands.sdk.mcp.resources_prompts):
    • list_mcp_resources: Returns resources, templates, and parsed RFC 6570 parameters.
    • read_mcp_resource: Reads text/binary payloads with original URI and MIME types preserved.
    • list_mcp_prompts: Returns prompts with parameter schemas and required flags.
    • render_mcp_prompt: Preserves message roles and block content types.
  • Connection Architecture:
    • Establishes single-server connections per call via _prepare_mcp_config, inheriting existing OAuth/credential expansions.
    • Avoids fastmcp composite client URI rewrites (protocol://{server}/path) to guarantee exact URI attribution across single and multi-server setups. Leaves create_mcp_tools untouched.
  • Error Handling:
    • Method-not-found errors (-32601, inspected via cause chain) map to catchable MCPCapabilityNotSupportedError.
    • Failures and timeouts surface server-attributed MCPError and MCPTimeoutError.
  • Session Integration:
    • Explicit user-driven path: Resource/prompt outputs format into annotated user messages ([MCP resource server=... uri=...]) without LLM overhead (EXAMPLE_COST: 0).

Verification

  • New Tests: 8/8 passed against real stdio servers (binary roundtrips, dual-server attribution, capability error mapping).
  • Regression Suite: tests/sdk/mcp passed (164 passed, 1 pre-existing sandbox 502).
  • Lint & Quality: Pre-commit passed (resolved circular imports and getattr hook rules).

AGENT:

Why

openhands.sdk.mcp is tools-only today. MCP servers that also advertise resources (static or templated content, e.g. server-provided usage documentation) and prompts (server-rendered, parameterized message templates) expose none of that to OpenHands users, so the only options are duplicating the content into AGENTS.md or wrapping it in nonstandard tools.

Summary

  • New openhands.sdk.mcp.resources_prompts module with four functions over the existing native dict[str, MCPServer] config:
    • list_mcp_resources(config, server=None) → {server: MCPResourceListing} with resources plus resource templates, each template's RFC 6570 parameters parsed out;
    • read_mcp_resource(config, server, uri) → content blocks carrying the server key, URI, MIME type, and either text or base64 blob_b64;
    • list_mcp_prompts(config, server=None) → prompts with declared arguments and required flags;
    • render_mcp_prompt(config, server, name, arguments) → messages preserving each message's role and content-block type (text / image / audio / embedded resource).
  • Each call opens one single-server client through the existing _prepare_mcp_config boundary (OAuth, secret expansion included) and closes it. This is deliberate: the composite client rewrites resource URIs to protocol://{server}/path, which would lose the server's original URIs, and per-server connections make attribution exact and identical for single- and multi-server configs. create_mcp_tools and tools-only configs are untouched — same tools, same system prompt behavior, no new connections.
  • Errors: a method-not-found answer (MCP error code -32601, walking the cause chain) maps to a new catchable MCPCapabilityNotSupportedError(server, capability); fastmcp-based servers that implement resources/list with an empty result surface as an ordinary empty listing. Any other failure raises an MCPError naming the server and the resource URI or prompt; timeouts raise MCPTimeoutError with the server name, matching create_mcp_tools.
  • Conversation integration is explicit-selection only: user code reads/renders, composes a Message with a provenance header, and sends it via conversation.send_message. Nothing is loaded implicitly.
  • Example examples/01_standalone_sdk/60_mcp_resources_prompts/ (registered in _TARGET_DIRECTORIES) demonstrates the full flow — list, static read, template resolution, prompt listing and rendering, and message composition — against a local stdio server, with no LLM (EXAMPLE_COST: 0).

Issue Number

Closes #6191

How to Test

uv run pytest tests/sdk/mcp/test_mcp_resources_prompts.py -q
uv run python examples/01_standalone_sdk/60_mcp_resources_prompts/main.py

The tests run a real stdio FastMCP server (resources, a template, a binary blob, a prompt, plus the existing tools-only server for the multi-server attribution case) and cover: listing with parsed template parameters, static and resolved-template reads, binary blob round-trip, prompt listing/rendering with preserved roles, per-server attribution across two servers, unknown server key, failed read naming server and URI, and the method-not-found → MCPCapabilityNotSupportedError mapping. The full tests/sdk/mcp suite passes (164 passed; the one failure is the pre-existing localhost-502 environment failure that also fails on unmodified main in this sandbox).

Video/Screenshots

Not a visual change; example output is shown in How to Test.

Design Doc

.pr/design.md — covers the per-server-connection decision and the error mapping.

Type

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

Notes

  • Scoped to the SDK surface per the issue triage; downstream agent-server REST and TypeScript client surfaces follow the SDK-first flow in later PRs.
  • No new serialized events or persisted-settings changes; the new models are return types only.

xxwtiancai and others added 2 commits October 24, 2026 02:02
A chat-completions stream that yields no chunks reassembles to None in
_transport_call and _atransport_call, tripping the ModelResponse
assertion. That AssertionError is not in LLM_RETRY_EXCEPTIONS, so an
empty stream from the provider was neither retried nor classified as
the retryable no-response failure.

Raise LLMNoResponseError from the streaming branch when the reassembly
returns None, so the existing num_retries/backoff policy applies, and
add sync/async retry and exhaustion tests.

Addresses item 2 of the streaming audit in issue 4077, superseding the
stale draft PR 4095.

Co-authored-by: openhands <[email protected]>
openhands.sdk.mcp was tools-only: servers that also advertise
resources (static or templated content) or prompts (server-rendered
message templates) exposed none of that to OpenHands users, forcing
duplication into AGENTS.md or nonstandard wrapper tools.

Add resources_prompts with four functions over the existing native
dict[str, MCPServer] config: list_mcp_resources (resources plus
templates with parsed RFC 6570 parameters), read_mcp_resource (text or
base64 blob with URI and MIME type), list_mcp_prompts (declared
arguments with required flags), and render_mcp_prompt (messages
preserving role and content type). Each call opens one single-server
client through the existing _prepare_mcp_config boundary, so every
result carries the true server key and the server's original URIs —
the composite client's URI rewriting would lose them. A
method-not-found answer maps to a catchable
MCPCapabilityNotSupportedError; other failures name the server and the
resource or prompt involved. Content flows only through explicit
selection: the new example composes a provenance-carrying user message
from a resource read and a rendered prompt and needs no LLM.

Closes OpenHands#6191

Co-authored-by: openhands <[email protected]>
@github-actions

github-actions Bot commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

📁 PR Artifacts Notice

This PR adds or changes temporary PR-specific documents under .pr/. They stay available on this branch for review; after merge, the next release PR removes .pr/ from main.

@xxwtiancai
xxwtiancai marked this pull request as ready for review October 24, 2026 10:07
xxwtiancai and others added 3 commits November 11, 2026 22:05
Merge of upstream main brings the fastmcp 4 / mcp 2 migration (OpenHands#6540):
mcp.shared.exceptions.McpError no longer exists. Import the preserved
fastmcp.exceptions.McpError instead and build it with the new
positional (code, message) constructor in the capability test.

Co-authored-by: openhands <[email protected]>
…prompts-6191

# Conflicts:
#	openhands-sdk/openhands/sdk/llm/llm.py
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.

[Feature]: Add MCP Resources and Prompts integration to the SDK

1 participant