Add MCP (Model Context Protocol) server support as an integration - #250
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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)
MCPIntegrationProvider(lib/integrations/mcp.py): discovers serversfrom
mcp.json/.mcp.yamlconfig files (conventionalmcpServers/serverslayout) across project, user, and context search roots.ClientSessionlifecycle (connect, every request, disconnect) is confined toone dedicated asyncio task (
_ServerConnection). Callers on any task submitclosures over an
asyncio.Queueand await per-request futures; requests areserialized. 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.
initialize()are isolated so a failedhandshake 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.
MCPToolResource/MCPResourceResource/MCPPromptResource); the agent gets generic dispatchtools (
connect_to_mcp_server,list_mcp_tools,call_mcp_tool,read_mcp_resource).user-writable config (
~/.beaker/mcp.jsonby default, or an existingwritable config the provider already reads).
Integrations UI (frontend)
MCPIntegrationViewer(read-only),MCPIntegrationEditor(full add/editform: transport, command/args/env or URL/headers, display name, description),
and
MCPToolsPanel(read-only tool catalog with argument schemas).context-<slug>corpus) areread-only ("View"); locally-configured servers are editable ("Edit").
and "MCP Server".
persisted to config).
Integration framework refactors (enabling MCP, shared with skills)
BaseIntegrationProvider: dropped thedisplay_nameconstructor arg (now aclass attribute), added an async
cleanup()hook, afrom_context()hook,and an optional
iconclassvar. Documented the one-instance-per-class /mergefold invariant.IntegrationProviderRegistry: documented the folding invariant;system_preamblenow renders as per-provider markdown sections.SkillIntegrationProvider: discovery refactored to share adiscover_integration_paths/from_contextstructure with MCP; promptoutput moved to YAML. Context skills now load via
from_contextrather thanbespoke 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 renderedas YAML.
loop via
call_soon(loop.stop)after cleanup completes.Other
call_in_contextREST bridge returns structured{status, ename, evalue}error payloads; integration-id route regex accepts
:(formcp:<corpus>:<name>uuids).handle_message.__aexit__matches onexc_value(instance) rather thanexc_type(class) — theCancelledErrorbranch never fired before.mcpandpyyamldependencies;.gitignorefor personal.mcp.*/.skills.jsonconfig.Testing
tests/integrations/mcp/test_mcp_provider.py(73 tests): pure helpers,config parsing/discovery, the
_ServerConnectionactor lifecycle (reuse,disconnect, startup failure, init error, run-after-close), catalog
population, prompt rendering, and dispatch tools. No live servers contacted.