Skip to content

Add MCP (Model Context Protocol) server support as an integration - #250

Merged
mattprintz merged 7 commits into
devfrom
mcp-support
Jul 20, 2026
Merged

Add MCP (Model Context Protocol) server support as an integration#250
mattprintz merged 7 commits into
devfrom
mcp-support

Conversation

@mattprintz

Copy link
Copy Markdown
Collaborator

Summary

Adds Model Context Protocol servers as a first-class Beaker integration type,
alongside supporting refactors to the integration-provider framework that MCP
builds on. Users can discover, add, edit, and use MCP servers (stdio or
HTTP/SSE) from the Integrations UI; the agent can list and call their tools,
read their resources, and use their prompts.

What's included

MCP integration provider (backend)

  • New MCPIntegrationProvider (lib/integrations/mcp.py): discovers servers
    from mcp.json / .mcp.yaml config files (conventional mcpServers /
    servers layout) across project, user, and context search roots.
  • Kept-open sessions via a per-server owner task. Each server's
    ClientSession lifecycle (connect, every request, disconnect) is confined to
    one dedicated asyncio task (_ServerConnection). Callers on any task submit
    closures over an asyncio.Queue and await per-request futures; requests are
    serialized. This resolves the anyio "cancel scope in a different task" error
    from opening a session on one task and tearing it down on another, and avoids
    paying subprocess-startup cost on every call.
  • Fail-early startup. Connect + initialize() are isolated so a failed
    handshake surfaces the real exception cleanly (rather than being masked by
    anyio task-group teardown) and never leaves a hung owner task. stdio stderr
    is captured and attached to the error to aid debugging.
  • Tools/resources/prompts exposed as typed resources (MCPToolResource /
    MCPResourceResource / MCPPromptResource); the agent gets generic dispatch
    tools (connect_to_mcp_server, list_mcp_tools, call_mcp_tool,
    read_mcp_resource).
  • Mutation: add / update a locally-configured server, written back to a
    user-writable config (~/.beaker/mcp.json by default, or an existing
    writable config the provider already reads).

Integrations UI (frontend)

  • MCPIntegrationViewer (read-only), MCPIntegrationEditor (full add/edit
    form: transport, command/args/env or URL/headers, display name, description),
    and MCPToolsPanel (read-only tool catalog with argument schemas).
  • Context-provided servers (namespaced under a context-<slug> corpus) are
    read-only ("View"); locally-configured servers are editable ("Edit").
  • "New Integration" is now a split button offering "Specialist Agent" (adhoc)
    and "MCP Server".
  • Instructions are shown read-only (reported by the server on connect; not
    persisted to config).

Integration framework refactors (enabling MCP, shared with skills)

  • BaseIntegrationProvider: dropped the display_name constructor arg (now a
    class attribute), added an async cleanup() hook, a from_context() hook,
    and an optional icon classvar. Documented the one-instance-per-class /
    merge fold invariant.
  • IntegrationProviderRegistry: documented the folding invariant;
    system_preamble now renders as per-provider markdown sections.
  • SkillIntegrationProvider: discovery refactored to share a
    discover_integration_paths / from_context structure with MCP; prompt
    output moved to YAML. Context skills now load via from_context rather than
    bespoke logic in BeakerContext.
  • BeakerContext: DEFAULT_INTEGRATION_PROVIDER_CLASSES (skills + MCP);
    split default_integration_providers / context_integration_providers;
    cleanup() is now async and gathers subkernel + integration cleanups.
  • BeakerSubkernel.cleanup() is now async; subkernel system-preamble rendered
    as YAML.
  • Kernel shutdown path made async end-to-end; the shutdown handler stops the
    loop via call_soon(loop.stop) after cleanup completes.

Other

  • call_in_context REST bridge returns structured {status, ename, evalue}
    error payloads; integration-id route regex accepts : (for
    mcp:<corpus>:<name> uuids).
  • handle_message.__aexit__ matches on exc_value (instance) rather than
    exc_type (class) — the CancelledError branch never fired before.
  • Added mcp and pyyaml dependencies; .gitignore for personal .mcp.* /
    .skills.json config.

Testing

  • New tests/integrations/mcp/test_mcp_provider.py (73 tests): pure helpers,
    config parsing/discovery, the _ServerConnection actor lifecycle (reuse,
    disconnect, startup failure, init error, run-after-close), catalog
    population, prompt rendering, and dispatch tools. No live servers contacted.
  • Full suite green (452 passed).

@mattprintz
mattprintz merged commit e335718 into dev Jul 20, 2026
4 checks passed
@mattprintz
mattprintz deleted the mcp-support branch July 20, 2026 17:25
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