Skip to content

Build built-in MCP groundwork and on-demand server injection bridge #155

Closed

Description

@Juliusolsson05

Problem

Agent Code is going to rely more heavily on built-in MCP servers as a core advantage: the app can inject domain-specific tools into an agent only when that agent needs them, instead of carrying every tool surface in every session forever.

Today there is no first-class Agent Code MCP subsystem. The codebase can render/provider-handle MCP activity from Claude/Codex, but Agent Code does not yet have a structured home for built-in MCP servers, nor a runtime bridge for enabling a selected MCP domain on a single live agent.

We need the groundwork before building any specific domain server such as Orchestration Agents, task management, project TODOs, issue/PR workflows, or repo-intelligence tools.

Goal

Create the app-level MCP foundation:

  • a structured src/mcp/ subsystem for built-in MCP servers
  • a registry of built-in MCP domains
  • a provider-neutral way to inject selected built-in MCP servers into a specific agent session on demand
  • a secure bridge that lets those MCP servers call back into Agent Code without directly mutating renderer state
  • clear lifecycle and permission rules so one agent only gets the MCP tools explicitly enabled for it

Proposed Shape

Suggested layout:

src/mcp/
  shared/
    types.ts
    schemas.ts
    toolResult.ts
  runtime/
    builtInServerRegistry.ts
    mcpInjection.ts
    controlBridge.ts
  orchestration/
    README.md
    server.ts      # later issue owns implementation details

The exact file layout can change during implementation, but the important invariant is that MCP becomes a first-class internal subsystem. Do not bury the first server under a feature folder in a way that makes future MCP domains look like one-off hacks.

Runtime Model

Built-in MCP servers should be enabled per session, not globally.

Conceptual flow:

Focused Agent Code session
  -> user runs command to enable a built-in MCP domain
  -> Agent Code reloads/respawns that provider session with a temporary MCP config
  -> Claude/Codex launches the Agent Code MCP stdio server
  -> MCP server calls back to Agent Code through a scoped control bridge
  -> Agent Code main/renderer perform approved app actions

Important: the MCP server process should not directly own workspace state. It should be a capability-limited bridge client. Workspace mutation stays in the existing app state/session action paths.

Design Questions

  • What is the cleanest provider-neutral abstraction for “reload this session with these built-in MCP servers enabled”?
  • Can Claude and Codex both support per-session temporary MCP config without modifying global user config?
  • If provider behavior differs, do we ship one provider first behind the same abstraction?
  • Where should per-session MCP enablement state live and persist, if anywhere?
  • What should happen on app restart: do orchestration/MCP-enabled sessions restore with the same built-in MCP servers, or require explicit re-enable?
  • What confirmation UX is needed when enabling an MCP domain that can create/send work to other agents?

Tasks

  • Research current Claude Code and Codex MCP configuration/injection paths and document the supported per-session strategy.
  • Add a src/mcp/ subsystem with shared types and a built-in server registry.
  • Add a main-process MCP runtime/injection service that can compute provider-specific MCP config for a session.
  • Add a scoped control bridge for built-in MCP servers to call back into Agent Code.
  • Add per-session capability tokens or equivalent scoping so a server can only act for the session that enabled it.
  • Add renderer/main API for enabling a built-in MCP domain on the focused session.
  • Add a command-palette entry for enabling MCP domains, initially enough to support the later Orchestration Agent domain.
  • Add thick WHY comments explaining why MCP servers are grouped under src/mcp/, why injection is per-session, and why workspace mutation must go through the bridge instead of direct state access.
  • Add tests for:
    • registry shape and enabled-domain resolution
    • scoped bridge rejects missing/invalid tokens
    • bridge rejects actions outside the owning session scope
    • provider injection config generation does not mutate global config

Acceptance Criteria

  • The repo has a clear first-class MCP subsystem under src/mcp/.
  • Agent Code can enable a built-in MCP domain for one selected agent session without globally enabling it for all sessions.
  • The injection bridge is scoped to the owning session and rejects unauthorized calls.
  • The implementation is structured so future MCP domains can be added without changing provider/session internals each time.
  • The groundwork is sufficient for the Orchestration Agent issue to implement its domain-specific server and tools.

Follow-up

This is the prerequisite for the Orchestration Agent feature. The follow-up issue should implement the actual orchestration MCP server and tools on top of this groundwork.

Activity

  1. Juliusolsson05 commented on May 16, 2026

    @Juliusolsson05
    OwnerAuthor

    Implementation Plan After MCP + Repo Audit

    Relevant MCP facts

    • MCP tools are model-controlled; Agent Code must enforce scoping, limits, and user-visible lifecycle rules in code, not only in tool descriptions.
    • Stdio is the right first transport for built-in local servers.
    • Tool schemas should be tight and token-efficient. Prefer small structured results over transcript dumps.
    • Claude Code supports dynamic MCP injection through --mcp-config <json-or-file> and --strict-mcp-config.
    • Codex supports MCP servers under mcp_servers config. Its config type accepts stdio fields: command, args, env, env_vars, cwd, plus startup_timeout_sec, tool_timeout_sec, enabled_tools, and disabled_tools.
    • Agent Code already spawns Claude/Codex through provider wrappers, so per-session MCP config belongs in SessionManager.spawn -> provider options -> provider argv/env.

    Repo integration points

    • src/main/sessionManager.ts

      • Extend SpawnOptions with something like mcpDomains?: BuiltInMcpDomain[] or mcp?: BuiltInMcpSessionConfig.
      • Pass provider-neutral MCP runtime config into provider.createSession(...).
    • src/providers/claude/runtime/claudeSession.ts

      • Add mcpConfigArgs?: string[] or a typed provider injection result.
      • Append --mcp-config <json> to Claude argv.
      • Consider --strict-mcp-config only if we explicitly want to suppress user/project MCP servers. For orchestration, probably do not use strict by default because users may already rely on their own MCP servers.
    • src/providers/codex/runtime/codexSession.ts

      • Add generated --config overrides for the built-in MCP server.
      • Need a spike to confirm CLI override syntax for nested tables. If nested mcp_servers.agent_code_orchestration.command=... works, use that. If not, use temporary CODEX_HOME overlay with a generated config.toml copied/merged from the real config.
    • src/renderer/src/workspace/hook/actions/session.ts

      • Extend spawn(...) options with enabled MCP domains.
      • Persist enough session metadata only if we decide MCP enablement survives reload.
    • src/preload/api/session.ts and src/main/ipc/session.ts

      • Thread MCP spawn options through session:spawn.

    Proposed src/mcp/ structure

    src/mcp/
      shared/
        domains.ts
        protocol.ts
        results.ts
      runtime/
        builtInServerRegistry.ts
        injectionConfig.ts
        controlBridge.ts
        controlBridgeTypes.ts
        sessionMcpState.ts
      servers/
        orchestration/
          server.ts
          tools/
    

    Use servers/ under src/mcp/ so future domains land beside orchestration rather than underneath it.

    Control bridge design

    MCP stdio server process should not import renderer/workspace code. It should call back to Agent Code through a local control bridge owned by main.

    Minimum bridge shape:

    • Main starts a local loopback HTTP server or IPC-like endpoint during app boot.
    • For each MCP-enabled session, main mints a short-lived capability token and records:
      • parent sessionId
      • enabled domains
      • allowed child/session scope
      • created timestamp
    • The MCP server is launched with env:
      • AGENT_CODE_MCP_CONTROL_URL
      • AGENT_CODE_MCP_TOKEN
      • AGENT_CODE_MCP_PARENT_SESSION_ID
      • AGENT_CODE_MCP_DOMAIN
    • Every bridge request validates token + domain + parent session scope.

    HTTP loopback is likely the most practical first bridge because stdio is already owned by the provider/MCP protocol, and the server process is external to Electron renderer. Bind to 127.0.0.1, random port, bearer token, no remote interface.

    Main-to-renderer mutation path

    The control bridge cannot directly update workspace state because linked-agent placement and Dispatch metadata live in renderer actions today.

    Add a small request/response bridge from main to renderer:

    • Main receives MCP control request.
    • Main validates token/scope.
    • Main sends a request to the focused BrowserWindow with a unique request id.
    • Renderer handles the request through workspace actions and replies on a paired IPC channel.
    • Main returns the result to the MCP server.

    This keeps the existing invariants intact: renderer owns tabs, detached sessions, linked-agent placement, and runtime drafts.

    Groundwork acceptance path

    The #155 implementation should finish when this is possible, even with only a dummy/test domain:

    1. Registry declares a built-in MCP domain.
    2. User/session spawn can enable that domain for one agent.
    3. Provider receives generated MCP config for that session only.
    4. Built-in MCP stdio server can start and call ping through the bridge.
    5. Bridge rejects missing/invalid token and wrong-session requests.
    6. No global Claude/Codex user config is mutated.

    Open decisions for first PR

    • Persist MCP enablement across app restart? Recommendation: no for MVP. Make enablement explicit per run until behavior is proven.
    • Use --strict-mcp-config for Claude? Recommendation: no for MVP. Inject built-in servers alongside user config.
    • Support both providers in first implementation? Recommendation: build provider-neutral API, implement Claude first if Codex nested override syntax is not immediately reliable.
    • Add @modelcontextprotocol/sdk dependency? Recommendation: yes, unless bundle policy objects. Use official SDK for server protocol and tool registration.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions