Skip to content

feat(core): add mcp support - #34513

Merged
rekram1-node merged 18 commits into
v2from
core-mcp
Jun 30, 2026
Merged

rekram1-node merged 18 commits into
v2from
core-mcp

Conversation

@rekram1-node

Copy link
Copy Markdown
Collaborator

Issue for this PR

Closes #34435

rekram1-node and others added 18 commits June 29, 2026 09:14
Connect configured MCP servers in per-server child scopes at layer boot,
tracking status and a startup deferred per server. Query methods wait for
the relevant server(s) to finish startup before serving.

Drop the in-memory connect/disconnect/add overrides; runtime enable/disable
will come later through config updates. List methods (tools/instructions/
prompts/resourceCatalog) are aggregate across all servers with per-item
provenance; prompt/readResource stay server-scoped for routing.

Add a Connection abstraction in client.ts so the SDK Client never leaks
into the rest of core.
Capture each server's initialize instructions on the Connection at connect
time and expose them via MCP.instructions(). Add McpGuidance, a system-context
source modeled on SkillGuidance, that renders the <mcp_instructions> block and
hides servers whose contributed tools are all denied for the agent.

instructions() returns just { server, instructions }; the guidance layer
fetches tools() separately and does the agent-aware permission correlation,
mirroring how SkillGuidance composes raw data with agent permissions.
Add tools() and onToolsChanged() to the MCPClient.Connection abstraction:
list is capability-gated, paginated with cursor-dedup, and tolerates
unresolvable outputSchemas via a per-page fallback. Tool listing is folded
into connect so a listing failure marks the server failed rather than leaving
it connected with a silently empty list. Cache MCP.Tool values per server,
refresh on tools/list_changed notifications, and publish McpEvent.ToolsChanged.
MCP.tools() aggregates the cached tools across all connected servers.
Add MCP.callTool plus a dynamic jsonSchema mode for Tool.make, and register
each MCP server's tools as canonical Location-scoped tools via an McpTool
producer that reconciles on tools-changed notifications. Content-first tool
results preserve image/audio blocks; remove the arbitrary pagination page cap;
log mcp connection outcomes.

Tool naming keeps v1 parity (server_tool) so existing deny rules apply.
isV1 only triggered on a fixed set of top-level keys, so a config with just
$schema and a v1-shaped mcp block (servers directly under mcp, with enabled)
was parsed as v2 and silently produced zero servers. Detect the v1 mcp shape
(no servers wrapper, entries with a type) so these files migrate correctly.
Thin OAuthClientProvider factory plus a Store contract for persisting an MCP
server's OAuth artifacts (DCR client info, PKCE verifier, tokens). The SDK
drives registration, PKCE, and refresh through these callbacks; storage is
injected so the provider stays decoupled from Credential and Effect.
A successful connect set entry.tools but never published ToolsChanged, so the tool registry only registered servers that were connected during the initial sweep or that emit their own list-changed notification. Servers that finish connecting later stayed invisible to the model. Publish ToolsChanged on connect so their tools get registered.
Register every remote MCP server as an OAuth integration so credentials live in the global store instead of committed config. Identity is keyed on name + url, so same-url servers under different names hold separate accounts. The connect path builds an auth provider from any stored credential (refreshing and persisting back to the same row) and, when none exists, an empty in-memory provider so an auth-gated connect reports needs_auth instead of a generic failure. A ConnectionUpdated subscriber reconnects a server when its credential changes, so login/logout takes effect without a restart. The interactive authorize flow stands up a loopback callback server (port from callback_port, then redirect_uri, then ephemeral), validates a CSRF state on the redirect, supports static client credentials, and exchanges the code for a storable credential. Invalid credentials the SDK rejects are dropped via the raw credential service to avoid racing the reconnect subscriber.
Add a GET /api/mcp route returning each configured server's name and connection status, modeled on the integration group. The status union becomes a canonical wire contract in @opencode-ai/schema/mcp that core re-exports, and a new mcp.status.changed event fires whenever a server settles (connected, failed, needs_auth, closed) so clients can refresh without polling. Regenerates the client SDK.
Wire the new /api/mcp endpoint into the data context: bootstrap fetches the server list and a mcp.status.changed subscription refreshes it live. Re-point the status dialog, footer connected-count, and the /mcps dialog (now read-only) off the dead sync stub onto data.location.mcp, and toast once when a server enters a failed or needs-auth state. Regenerates the legacy js SDK.
MCP.servers() constructed ServerInfo with config: entry.config, which re-validated the stored config against ConfigMCP.Server. Config objects from the document pipeline carry present-but-undefined optional keys (cwd, environment) that the schema rejects, so servers() threw and GET /api/mcp returned 500 with an empty body. Nothing consumes ServerInfo.config, so removing it fixes the endpoint; name, status, integrationID, and connection remain.
# Conflicts:
#	packages/client/src/generated-effect/client.ts
#	packages/client/src/generated/client.ts
#	packages/client/src/generated/types.ts
#	packages/sdk/js/src/v2/gen/types.gen.ts
@rekram1-node rekram1-node changed the title Core mcp feat(core): add mcp support Jun 30, 2026
@rekram1-node
rekram1-node merged commit b1ca070 into v2 Jun 30, 2026
6 checks passed
@rekram1-node
rekram1-node deleted the core-mcp branch June 30, 2026 13:37
jinhuang712 pushed a commit to jinhuang712/opencode that referenced this pull request Sep 26, 2026
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