Skip to content

feat: MCP app sampling support via stock SDK types (alt to #477)#530

Open
ochafik wants to merge 1 commit intomainfrom
claude/review-mcp-sampling-CkbHh
Open

feat: MCP app sampling support via stock SDK types (alt to #477)#530
ochafik wants to merge 1 commit intomainfrom
claude/review-mcp-sampling-CkbHh

Conversation

@ochafik
Copy link
Contributor

@ochafik ochafik commented Mar 6, 2026

Alternative to #477. Instead of forking a stripped-down sampling schema, this reuses the stock CreateMessageRequest / CreateMessageResult / CreateMessageResultWithTools types from @modelcontextprotocol/sdk directly — the same pattern already used for tools/call, resources/read, etc.

Motivation

#477 introduces custom McpUiSampling* types that diverge from core MCP sampling:

Core MCP #477
maxTokens required optional
tools / toolChoice ✅ (SEP-1577)
Message content block | block[] single block only
Content types text, image, audio, tool_use, tool_result text, image only
Result content any type, array ok {type:"text"} only
Capability shape sampling: { tools?: {} } sampling: { supportedModalities?: (...)[] }

The original PR's headline use case ("nested agents") requires tool-calling loops, which that schema can't represent.

Approach

Treat sampling/createMessage as a Standard MCP Message (spec § Standard MCP Messages), exactly like tools/call:

  • Spec (specification/draft/apps.mdx):

    • sampling/createMessage listed under Standard MCP Messages, referencing the upstream CreateMessageRequest shape (incl. SEP-1577 tools).
    • sampling?: { tools?: {} } added to HostCapabilities — mirrors MCP ClientCapabilities.sampling so hosts that are already MCP clients can pass it straight through.
  • Types (src/types.ts, src/spec.types.ts):

    • CreateMessageRequestAppRequest union
    • CreateMessageResult / CreateMessageResultWithToolsAppResult union
    • No new Zod schemas to maintain — imported from the SDK.
  • SDK ergonomics:

    • App.createSamplingMessage(params) — overloaded: returns CreateMessageResult when tools is absent, CreateMessageResultWithTools when present. Picks the correct result schema at runtime.
    • AppBridge.oncreatesamplingmessage = async (params, extra) => {...} — same setter pattern as oncalltool.
    • App.assertCapabilityForMethod now checks hostCapabilities.sampling (effective when enforceStrictCapabilities: true).
  • Tests & docs:

    • Round-trip integration test in app-bridge.test.ts.
    • Type-checked .examples.ts regions for both App and AppBridge JSDoc.

What we get for free

  • maxTokens required (cost-control guard rail)
  • temperature, stopSequences, modelPreferences, metadata
  • tools / toolChoice / tool_use / tool_result content blocks
  • stopReason: "toolUse" + array content for parallel tool calls
  • Audio content blocks
  • _meta on messages
  • Wire-compatible with hosts that already implement MCP sampling — the bridge can literally forward the request to an existing sampling/createMessage handler.

https://claude.ai/code/session_01ENGWTtsfcyP4S6fTWtUMAh

Alternative to #477. Instead of forking a stripped-down sampling schema,
this reuses the stock `CreateMessageRequest` / `CreateMessageResult` /
`CreateMessageResultWithTools` types from `@modelcontextprotocol/sdk`
directly — same pattern already used for `tools/call`.

- Spec: `sampling/createMessage` listed under `### Standard MCP Messages`,
  `sampling?: { tools?: {} }` added to `HostCapabilities` (mirrors MCP
  `ClientCapabilities.sampling` shape for easy pass-through).
- SDK: `App.createSamplingMessage()` with overloads that narrow the return
  type based on whether `params.tools` is set; `AppBridge.oncreatesamplingmessage`
  setter; `CreateMessageRequest`/`CreateMessageResult*` added to the
  `AppRequest`/`AppResult` unions.
- Picks up SEP-1577 tool-calling support (`tools`, `toolChoice`, `tool_use`
  content blocks, `stopReason: "toolUse"`, array content) for free — unblocks
  the "nested agents" use case motivated by the original PR.

https://claude.ai/code/session_01ENGWTtsfcyP4S6fTWtUMAh
/**
* Example: Forward sampling requests to your LLM provider.
*/
function AppBridge_oncreatesamplingmessage_forwardToLlm(
/**
* Example: Simple LLM completion via host sampling.
*/
async function App_createSamplingMessage_simple(app: App) {
/**
* Example: Agentic loop with tools (requires host sampling.tools capability).
*/
async function App_createSamplingMessage_withTools(
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.

2 participants