Skip to content

feat(xmcp): add ergonomic sampling helper for tool handlers - #542

Open
FiammaMuscari wants to merge 3 commits into
basementstudio:canaryfrom
FiammaMuscari:feat/sampling-helper
Open

feat(xmcp): add ergonomic sampling helper for tool handlers#542
FiammaMuscari wants to merge 3 commits into
basementstudio:canaryfrom
FiammaMuscari:feat/sampling-helper

Conversation

@FiammaMuscari

@FiammaMuscari FiammaMuscari commented Apr 1, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add extra.sample() for tool handlers
  • support sampling with text, image, and audio content
  • support modelPreferences
  • support local tool selection (tools, toolChoice)
  • handle the tool_use -> tool_result -> continue loop
  • export SampleResult

Scope

  • limited to local xmcp tools
  • tools: "all" excludes the current tool
  • tool execution is ordered (not parallel)
  • no support for arbitrary MCP Tool[] definitions
  • no server-side sampling capability claims

Verification

  • pnpm.cmd --filter xmcp build
  • manually verified extra.sample() from a tool context
  • manually verified the tool_use -> tool_result -> continue loop

Closes #541
Resolves XMCP-168

@vercel

vercel Bot commented Apr 1, 2026

Copy link
Copy Markdown
Contributor

@FiammaMuscari is attempting to deploy a commit to the xmcp Team on Vercel.

A member of the Team first needs to authorize it.

@0xKoller

0xKoller commented Apr 1, 2026

Copy link
Copy Markdown
Contributor

Thanks for the PR @FiammaMuscari

Looks promising overall. Before a thorough review, could you please add:

  • A concrete usage example
  • Screenshots
  • Documentation updates

@0xKoller
0xKoller changed the base branch from main to canary April 1, 2026 19:31
@FiammaMuscari

Copy link
Copy Markdown
Contributor Author

@0xKoller hii

I added an documentation and examples in packages/xmcp/README.md
README.md

Reposting examples here

Minimal extra.sample() usage from inside a tool handler:

import { z } from "zod";

export const schema = {
  topic: z.string(),
};

export default async ({ topic }, extra) => {
  const result = await extra.sample({
    messages: [
      {
        role: "user",
        content: {
          type: "text",
          text: `Write a concise summary about ${topic}.`,
        },
      },
    ],
    maxTokens: 300,
  });

  return {
    content: [
      {
        type: "text",
        text:
          result.content.type === "text"
            ? result.content.text
            : "The client did not return text content.",
      },
    ],
  };
};

Tool-enabled sampling flow using local xmcp tools:

const result = await extra.sample({
  messages: [
    {
      role: "user",
      content: {
        type: "text",
        text: "Research the current status and give me a short answer.",
      },
    },
  ],
  tools: ["search_docs", "fetch_page"],
  toolChoice: { mode: "auto" },
  maxTokens: 800,
  maxSteps: 6,
});

Verification:

  • pnpm.cmd --filter xmcp build
  • manually verified extra.sample() from a tool context
  • manually verified the tool_use -> tool_result -> continue loop

Screenshots:

  • Basic extra.sample() flow
image (7)
  • Local tool loop (tool_use -> tool_result -> continue)
image (9) image (10) image (11)

@0xKoller 0xKoller left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Remember to also add this to the docs website, and provide the screenshots/vid with MCPJam

Comment thread packages/xmcp/src/runtime/utils/sampling-tool-registry.ts Outdated
Comment thread packages/xmcp/README.md Outdated

@0xKoller 0xKoller left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why the example is an stdio server? can we make it http?

It looks good overall but some changes are required for this PR move on, remember to merge with canary to avoid merge conflicts too.

PS: remember to resolve convos

Comment thread apps/website/public/docs/sampling-helper/mcpjam-basic-sampling.png Outdated
Comment thread apps/website/content/docs/core-concepts/tools.mdx Outdated
Comment thread apps/website/content/docs/core-concepts/tools.mdx Outdated
Comment thread apps/website/content/docs/core-concepts/tools.mdx Outdated
Comment thread apps/website/content/docs/core-concepts/tools.mdx Outdated
Comment thread examples/stdio-transport/src/tools/research-topic.ts Outdated
Comment thread examples/stdio-transport/src/tools/search-docs.ts Outdated
Comment thread packages/xmcp/src/runtime/utils/__tests__/sampling-tool-registry.test.ts Outdated
Comment thread packages/xmcp/src/utils/__tests__/sample-result.test.ts Outdated
@FiammaMuscari
FiammaMuscari force-pushed the feat/sampling-helper branch from c37d655 to ad5078c Compare April 8, 2026 01:49
@FiammaMuscari

Copy link
Copy Markdown
Contributor Author

Verification

MCPJam (no sampling support → fallback)

  • The MCPJam client does not implement sampling/createMessage
  • The tools fall back to deterministic local responses instead of failing
mcpjam-research-fallback mcpjam-summarize-fallback

Real HTTP sampling (compatible client)

  • Verified end-to-end sampling flow using a compatible HTTP MCP client
  • extra.sample() completes the full tool_use -> tool_result -> continue loop
real-http-sampling-success

This PR now includes explicit verification for both fallback behavior and real sampling execution.

  • MCPJam demonstrates graceful fallback behavior when sampling is unsupported
  • Real sampling was verified separately with a compatible HTTP client
  • The docs and examples now reflect that distinction clearly

I also cleaned the branch history down to the final reviewed state only.

Comment thread apps/website/content/docs/core-concepts/tools.mdx
Comment thread examples/http-transport/src/tools/research-topic.ts Outdated
Comment thread examples/http-transport/src/tools/research-topic.ts Outdated
Comment thread examples/http-transport/src/tools/research-topic.ts Outdated
Comment thread examples/http-transport/README.md
Comment thread packages/xmcp/src/runtime/utils/sampling.ts Outdated
Comment thread packages/xmcp/src/runtime/utils/sampling.ts Outdated
@FiammaMuscari

Copy link
Copy Markdown
Contributor Author

Update

All review feedback has been addressed.

  • Simplified the sampling API and removed unnecessary abstractions
  • Aligned HTTP examples with the intended framework usage (no tool-level fallbacks)
  • Moved error normalization into the runtime (sampling.ts)
  • Removed MCPJam-specific references from docs
  • Kept the diff minimal and focused

Status

  • Rebased with latest canary and resolved conflicts
  • Build passes (pnpm --filter xmcp build)
  • Docs verified locally

Let me know if there's anything else you'd like me to adjust!

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 support for sampling capability

3 participants