Skip to content

feat: register MCP author tools with registerMcpTool() + ellmer::tool()#4412

Merged
schloerke merged 11 commits into
mcpfrom
schloerke/shinymcp-tool-registration
Jul 15, 2026
Merged

feat: register MCP author tools with registerMcpTool() + ellmer::tool()#4412
schloerke merged 11 commits into
mcpfrom
schloerke/shinymcp-tool-registration

Conversation

@schloerke

Copy link
Copy Markdown
Collaborator

Summary

Replaces the experimental options(shiny.mcp.tools = list(...)) MCP author-tool API with an exported registerMcpTool() that accepts ellmer::tool() objects — the same tool-definition style used by mcptools and James Wade's shinymcp.

options(shiny.mcp = TRUE)

registerMcpTool(
  ellmer::tool(
    function(n) { x <- rnorm(n); list(n = n, mean = mean(x), sd = stats::sd(x)) },
    name = "get_sample_stats",
    description = "Summary statistics for a normal sample of size n.",
    arguments = list(n = ellmer::type_integer("Sample size", required = TRUE))
  )
)

Before: hand-written JSON Schema + handler, configured via a process option.
After: typed ellmer::tool() objects, registered via a function.

What changed

  • New export registerMcpTool(...) — accepts one or more ellmer::ToolDef objects, stored in .globals$mcpAuthorTools (keyed by name, last-write-wins). Validates eagerly: a non-ellmer::tool() argument or a name colliding with a reserved tool (the app-opening tool or the internal _shiny_* tunnel tools) is a hard error at registration time. Name mirrors registerInputHandler().
  • Schema conversiontools/list derives each tool's inputSchema from the ToolDef's typed @arguments via ellmer's provider-agnostic as_json(), the same path as mcptools::tool_as_json() (keeps us aligned for future upstreaming).
  • Invocationtools/call runs the tool with rlang::exec(tool, !!!args). Return-value handling is unchanged (character → text, list → structuredContent, promises supported, errors → isError).
  • Dependencyellmer added to Suggests (>= 0.4.0), gated by rlang::check_installed(). Nothing on the core MCP path touches ellmer unless a tool is registered.
  • options(shiny.mcp.tools) is removed — no fallback. This API was experimental and unreleased (only reached mcp, not main), so no deprecation.

Scope

Author tools only. The app-opening tool (options(shiny.mcp.tool), singular) and every other shiny.mcp.* option are intentionally unchanged — a separate follow-up.

Docs & demos

  • New ?registerMcpTool help; mcp-session "Additional tools" section now points at it; pkgdown + NEWS updated.
  • Demo apps (mcp/demo-app, mcp/demo-app2) and mcp/ architecture/README/QUESTIONS migrated to the new API.
  • Design + implementation plan live in mcp/design-register-mcp-tool.md and mcp/implementation-plan-register-mcp-tool.md.

Testing

Full MCP test suite green (author-tool tests gated with skip_if_not_installed("ellmer")); the single skip is the CRAN-only stdio subprocess test. Covers registration/validation, tools/list schema (including required array shape), tools/call execution, error propagation, and async/promise handlers.

@schloerke
schloerke merged commit 57a7485 into mcp Jul 15, 2026
12 checks passed
@schloerke
schloerke deleted the schloerke/shinymcp-tool-registration branch July 15, 2026 19:36
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