Skip to content

v0.8.6 feat: codex-mcp parity — agent-style server tool + deepseek mcp add/list/get/remove client surface #398

@Hmbown

Description

@Hmbown

Pitch

Codex parity — the missing two halves of our MCP story.

Today we have half of what codex mcp ships:

  • deepseek mcp-server (in crates/tui/src/mcp_server.rs) exposes a curated set of individual tools (file_read, file_write, search, apply_patch, shell) over stdio. Useful, but it's not an agent. A caller can't say "drive a full DeepSeek turn for me" — they have to hand-orchestrate every step.
  • Client-side, there is no deepseek mcp add / list / get / remove. Users hand-edit ~/.deepseek/mcp.json. Adding a new MCP server takes a doc lookup and a JSON file open every time.

Codex nails both. We should mirror both.

Reference design (codex-main)

Server side — codex-rs/mcp-server/src/:

  • codex_tool_config.rs — defines CodexToolCallParam (prompt, model, profile, cwd, approval-policy, sandbox, base_instructions, developer_instructions, compact_prompt, config overrides) and the codex tool's JSON schema.
  • codex_tool_runner.rs::run_codex_tool_session — spawns a Tokio task that starts a thread via ThreadManager, drives the agent loop to completion, and streams every Event back as MCP notifications/message (with OutgoingNotificationMeta { request_id, thread_id } so the client can correlate). On completion it returns a CallToolResult whose structuredContent = { threadId, content } so the LLM can continue the conversation.
  • message_processor.rs registers two tools: codex (start a session) and codex-reply (continue an existing thread by thread_id). Approval requests (exec / patch) round-trip through exec_approval.rs / patch_approval.rs as MCP elicitations.

Client side — codex-rs/cli/src/mcp_cmd.rs:

  • Subcommands: list (with --json), get <name> (with --json), add <name> (--url <URL> | -- <COMMAND>...), remove <name>, login <name> [--scopes …], logout <name>.
  • add supports both transports: stdio (positional command + --env KEY=VALUE) and streamable-HTTP (--url + --bearer-token-env-var).
  • Persists into ~/.codex/config.toml via ConfigEditsBuilder — we already have a config layer (crates/config) that can host the same edit surface.

Scope: split into phases

Phase A — agent-style server tool (mirror codex / codex-reply)

  • Define DeepseekToolCallParam in crates/tui/src/mcp_server.rs (or a new agent_tool.rs): prompt, model, profile, cwd, approval_mode, mode (Plan/Agent/YOLO — our analogue of Codex's sandbox enum), base_instructions, developer_instructions, optional config overrides. Mirror Codex field names where they map cleanly so existing MCP clients can swap with minimal change.
  • Register two tools in the stdio server's tools/list:
    • deepseek — start a new session, return a thread_id in structuredContent.
    • deepseek-reply — continue by thread_id + prompt.
  • Wire tools/call to spawn a Tokio task that drives core/engine.rs to turn-completion, streaming each Event to the caller as notifications/message with { request_id, thread_id } meta. Reuse runtime_threads.rs for the durable thread store so reply works across server restarts.
  • Approval requests round-trip as MCP elicitations (mirror exec_approval.rs / patch_approval.rs). Default approval mode is suggest; respect approval_mode: never for fully unattended use.
  • Cancellation: when the MCP request is cancelled, propagate via CancellationToken into the engine.
  • Tests: stdio fixture that lists tools, calls deepseek with a no-op prompt, asserts a thread_id comes back and notifications stream.

Phase B — deepseek mcp add/list/get/remove client surface

  • New Mcp(McpArgs) variant on the top-level Commands enum in crates/cli/src/lib.rs (separate from the existing McpServer server-mode variant — keep that for stdio).
  • Subcommands mirroring codex mcp:
    • deepseek mcp list [--json]
    • deepseek mcp get <name> [--json]
    • deepseek mcp add <name> (--url <URL> | -- <COMMAND>...) with --env KEY=VALUE (stdio) and --bearer-token-env-var <ENV> (HTTP)
    • deepseek mcp remove <name>
  • Persist into ~/.deepseek/mcp.json (existing format) via the config layer. Do not silently rewrite unrelated fields — preserve user formatting where feasible.
  • On add, validate the server starts (best-effort: spawn, list-tools, kill) so users get an immediate error instead of a silent broken entry. --no-validate to skip.
  • Surface in deepseek doctor already exists for individual MCP servers (doctor_check_mcp_server); ensure new entries flow through it.
  • Tests in crates/cli/tests/: round-trip add → list → get → remove against a tempdir ~/.deepseek.

Phase C — DX polish

  • deepseek mcp (no subcommand) prints the table of configured servers, like codex mcp does.
  • --help examples that copy/paste cleanly:
    deepseek mcp add github -- npx -y @modelcontextprotocol/server-github
    deepseek mcp add linear --url https://mcp.linear.app/sse --bearer-token-env-var LINEAR_TOKEN
    
  • Document in docs/MCP.md how the agent tool lets another LLM (Claude Code, Cursor, etc.) drive a DeepSeek session — the headline use case.

Phase D — DeepSeek-unique extensions (optional, post-parity)

  • Expose mode (Plan / Agent / YOLO) as a first-class field in the agent tool — Codex collapses this into approval+sandbox, but our mode axis is more legible.
  • Per-call cycle_budget and provider overrides so a calling agent can say "reply with V4-Flash, single cycle" for cheap tool-use turns vs. "V4-Pro, no budget" for hard reasoning.

Acceptance (Phase A + B = the milestone bar)

  • deepseek mcp-server registers deepseek and deepseek-reply tools; an external MCP client (Claude Code) can drive a full DeepSeek session over stdio and receive streamed events.
  • deepseek mcp add github -- npx -y @modelcontextprotocol/server-github writes a valid entry to ~/.deepseek/mcp.json and deepseek mcp list shows it.
  • deepseek mcp remove github removes it cleanly.
  • Existing mcp-server tool surface (file_read/write/search/apply_patch/shell) keeps working — additive change.
  • Standard verification gates pass (cargo fmt, clippy -D warnings, cargo test --workspace --all-features --locked, parity gates).

Open questions

  • Naming: keep the server tool as deepseek / deepseek-reply for codex-mirror clarity, or deepseek_agent / deepseek_agent_reply to match our internal agent_* tool family? Lean toward the former — outside-facing name should match the brand.
  • Should mcp add accept a JSON blob (--json '{...}') for power users / scripting? Codex doesn't, but it'd be cheap to add.
  • Login/logout for OAuth MCP servers — Codex has a full flow (codex mcp login, scopes discovery, RMCP). Worth deferring to a follow-up issue unless someone needs Linear/Notion-style auth in 0.8.6.

References

  • crates/tui/src/mcp_server.rs — current stdio MCP server (tool-only, no agent).
  • crates/tui/src/runtime_threads.rs — durable thread store we'd reuse for deepseek-reply.
  • crates/cli/src/lib.rs:138Commands::McpServer (stdio mode); the new Commands::Mcp(McpArgs) lands next to it.
  • codex-main: codex-rs/mcp-server/src/{codex_tool_config,codex_tool_runner,message_processor}.rs, codex-rs/cli/src/mcp_cmd.rs.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestv0.8.6Targeting v0.8.6

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions