-
Notifications
You must be signed in to change notification settings - Fork 0
feat(cli/mcp): 'zeus mcp' subcommand scaffold #24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
f51c9fb
feat(cli/mcp): add 'zeus mcp' subcommand scaffold
kanywst 8f58585
fix(cli/mcp): propagate cwd error, stderr-consistent docs
kanywst 72e8268
fix(cli/mcp): take CommandContext, add --bind / --allow-non-loopback,…
kanywst e6e8d94
feat(cli/mcp): validate workspace is dir, refuse non-loopback bind wi…
kanywst aa41256
refactor(cli/mcp): use std::net::IpAddr for --bind so clap validates …
kanywst ec322b0
feat(cli/mcp): non-zero exit for unimplemented transport, full synops…
kanywst File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| /*--------------------------------------------------------------------------------------------- | ||
| * Copyright (c) Microsoft Corporation. All rights reserved. | ||
| * Licensed under the MIT License. See License.txt in the project root for license information. | ||
| *--------------------------------------------------------------------------------------------*/ | ||
|
|
||
| // Zeus — Model Context Protocol (MCP) server entry point. | ||
| // Scaffold only; real implementation lands in a follow-up PR. See | ||
| // docs/zeus-mcp-server.md for the tool surface and design. | ||
|
|
||
| use crate::commands::args::{McpArgs, McpTransport}; | ||
| use crate::commands::CommandContext; | ||
| use crate::util::errors::{wrap, AnyError, SetupError}; | ||
|
|
||
| pub async fn mcp(_ctx: CommandContext, args: McpArgs) -> Result<i32, AnyError> { | ||
| let raw_workspace = match args.workspace { | ||
| Some(p) => p, | ||
| None => std::env::current_dir().map_err(|e| wrap(e, "could not resolve workspace from cwd"))?, | ||
| }; | ||
|
|
||
| // Canonicalize so the security check ("refuses operations on paths | ||
| // outside this root") can rely on byte-prefix comparison instead of | ||
| // having to re-resolve relative segments on every request. | ||
| let workspace = std::fs::canonicalize(&raw_workspace) | ||
| .map_err(|e| wrap(e, format!("could not canonicalize workspace path {}", raw_workspace.display())))?; | ||
|
|
||
| if !workspace.is_dir() { | ||
| return Err(SetupError(format!( | ||
| "workspace path is not a directory: {}", | ||
| workspace.display() | ||
| )) | ||
| .into()); | ||
| } | ||
|
|
||
| match args.transport { | ||
|
kanywst marked this conversation as resolved.
|
||
| McpTransport::Stdio => { | ||
| // Scaffold-only: surface this as a SetupError (non-zero exit) | ||
| // rather than Ok(0). A silent exit-0 would let scripts that | ||
| // pipe this command into a real MCP client appear to succeed. | ||
| Err(SetupError(format!( | ||
| "zeus mcp: stdio transport not yet implemented (workspace={})", | ||
| workspace.display() | ||
| )) | ||
| .into()) | ||
| } | ||
| McpTransport::Sse => { | ||
| // Refuse to bind to a non-loopback interface without explicit opt-in, | ||
| // to keep the default posture local-only. clap already parsed | ||
| // `--bind` into `IpAddr`, so no string-level validation needed here. | ||
| // See docs/zeus-mcp-server.md. | ||
| if !args.bind.is_loopback() && !args.allow_non_loopback { | ||
| return Err(SetupError(format!( | ||
| "refusing to bind SSE transport on non-loopback address {} \ | ||
| without --allow-non-loopback (use 127.0.0.1 / ::1 for local-only)", | ||
| args.bind | ||
| )) | ||
| .into()); | ||
| } | ||
|
|
||
| Err(SetupError(format!( | ||
| "zeus mcp: sse transport not yet implemented (port={}, bind={}, workspace={})", | ||
| args.port, | ||
| args.bind, | ||
| workspace.display() | ||
| )) | ||
| .into()) | ||
| } | ||
| } | ||
| } | ||
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| # `zeus mcp` — Zeus as an MCP server | ||
|
|
||
| Zeus exposes its editor surface as an MCP server. Any MCP-aware client (Claude Code CLI, ChatGPT desktop, another editor, a script) can read buffers, apply edits, run commands, and start subagents through one protocol. | ||
|
|
||
| Implementation lives in two places: | ||
|
|
||
| - `cli/src/commands/mcp.rs` (this PR): the entry point — `code mcp` / `zeus mcp` subcommand | ||
| - `src/vs/workbench/contrib/mcpServer/` (later PR): the actual tool implementations, which talk to the workbench | ||
|
|
||
| ## Subcommand surface | ||
|
|
||
| ```text | ||
| zeus mcp [--transport stdio|sse] [--port N] [--bind IP] [--allow-non-loopback] [--workspace PATH] | ||
| ``` | ||
|
|
||
| - `--transport`: defaults to `stdio` (Anthropic / Claude Code CLI standard). `sse` for HTTP clients. | ||
| - `--port`: only meaningful with `--transport sse`. Defaults to a random ephemeral port; the chosen port is printed on stderr (stdout is reserved for protocol traffic on the stdio transport, and we keep stderr consistent across transports). | ||
| - `--bind`: only meaningful with `--transport sse`. Defaults to `127.0.0.1` (loopback). Accepts IP literals only — pass `127.0.0.1` / `::1` for loopback, not `localhost`. Any non-loopback value additionally requires `--allow-non-loopback`. | ||
| - `--allow-non-loopback`: acknowledges that binding the SSE transport to a non-loopback interface (`0.0.0.0`, a LAN IP, etc.) is intentional. Without this flag, non-loopback `--bind` values are refused. | ||
| - `--workspace`: workspace root path. Defaults to `$PWD`. | ||
|
|
||
| The Rust CLI launches a headless workbench process (or attaches to a running one if available) and proxies MCP traffic. | ||
|
|
||
| ## Initial tool surface | ||
|
|
||
| ```text | ||
| buffer_get(path) -> { content, language } | ||
| buffer_set(path, content) -> { ok } | ||
| edit_apply(path, range, text) -> { ok } | ||
| diagnostics_get(path?) -> { diagnostics[] } | ||
| selection_get() -> { path, range, text } | null | ||
| command_run(name, args?) -> { result } | ||
| visible_files() -> { paths[] } | ||
| search_workspace(query, opts?) -> { matches[] } | ||
| agent_start(skill, prompt) -> { agent_id } | ||
| agent_status(agent_id) -> { state, progress? } | ||
| agent_cancel(agent_id) -> { ok } | ||
| git_diff(staged?) -> { diff } | ||
| lsp_definitions(path, pos) -> { locations[] } | ||
| lsp_references(path, pos) -> { locations[] } | ||
| ``` | ||
|
|
||
| Detailed schemas land alongside the implementation PR. | ||
|
|
||
| ## Authentication | ||
|
|
||
| MCP over stdio inherits the calling process's permissions; no extra auth. | ||
|
|
||
| MCP over SSE binds to `127.0.0.1` only by default and requires a bearer token printed on **stderr** on start (`Token: ...`). Stdout is reserved for protocol traffic on the stdio transport, and we keep stderr consistent across transports. Binding to non-loopback (`--bind 0.0.0.0`, a LAN IP, etc.) additionally requires `--allow-non-loopback` as a confirmation flag. | ||
|
|
||
| ## Workspace isolation | ||
|
|
||
| A single Zeus install can host multiple `zeus mcp` instances, one per workspace. Each instance is scoped to its workspace root and refuses operations on paths outside it. | ||
|
|
||
| ## Why headless and not just an extension? | ||
|
|
||
| A VS Code extension only runs inside the editor's UI process. We want this to work when no editor is open — for example, a CI job that wants to call `buffer_get` after applying a refactor PR. The CLI gives us that decoupling. | ||
|
|
||
| ## Acceptance criteria for the implementation PR (not this scaffold PR) | ||
|
|
||
| - `code mcp` boots, prints transport info, and accepts an MCP `initialize` request | ||
| - `buffer_get` and `buffer_set` round-trip a small file in tests | ||
| - Refuses paths outside the workspace | ||
| - Plays nicely as a subprocess of Claude Code CLI | ||
|
|
||
| ## Status | ||
|
|
||
| Scaffold only. The Rust command is registered and prints a stub message; real implementation is a follow-up. |
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.
Uh oh!
There was an error while loading. Please reload this page.