Skip to content

Add corgea mcp install for agent MCP configs - #163

Open
Ibrahimrahhal wants to merge 5 commits into
mainfrom
cursor/mcp-install-c919
Open

Add corgea mcp install for agent MCP configs#163
Ibrahimrahhal wants to merge 5 commits into
mainfrom
cursor/mcp-install-c919

Conversation

@Ibrahimrahhal

@Ibrahimrahhal Ibrahimrahhal commented Aug 26, 2026

Copy link
Copy Markdown
Member

Adds corgea mcp install --agent <name> so a logged-in CLI can write the Corgea MCP server into an agent's JSON config using the URL and token already stored by corgea login.

Version bumped 1.11.1 → 1.12.0 (minor: new command). Cargo.toml is the release source of truth.

Behavior

  • Requires login, same as scan / skill / ls. An empty or missing token exits with the existing "No token set" message.
  • Writes the client-specific JSON documented at https://docs.corgea.app/modelcontextprotocol:
    • Cursor / Windsurf: mcp-remote with --transport http-only and the token in the header (Cursor does not interpolate ${env:NAME} from the server env block).
    • Claude Desktop (--agent claude): mcp-remote plus an env block, matching the docs.
    • Claude Code: typed HTTP ("type": "http") with url + CORGEA-TOKEN header.
    • VS Code / Gemini CLI / Continue / OpenCode: their native HTTP / provider shapes.
  • If Corgea is already present (the corgea key, or any server whose URL still points at a *.corgea.*/mcp endpoint), that entry is removed and rewritten so a reinstall refreshes URL and token. Neighboring servers and unrelated keys are left intact.
  • --scope user (default) or project; --dir writes a custom path; --set-default persists the agent like corgea skill install.
  • Project-scope installs warn that the file now contains a token.

Tests

  • Unit tests cover path resolution, JSON merge/replace, malformed files, and each client shape.
  • Integration tests drive the real binary: unauthenticated refusal, unknown agent, Cursor install from CORGEA_URL/CORGEA_TOKEN, reinstall refresh, and --agent claude.

This branch is now rebased/merged with main, including the #164 ETXTBSY image-test fix. No further MCP-branch changes.

Open in Web Open in Cursor 

Install (or reinstall) the Corgea MCP server into an agent's JSON config
using the URL and token already stored by `corgea login`. Re-running
removes any existing Corgea entry first so the endpoint and token stay
current. Unauthenticated runs fail through the same login gate as other
commands.

Co-authored-by: Ibrahim Rahhal <ibrahim.rahhal3636@gmail.com>
@Ibrahimrahhal
Ibrahimrahhal marked this pull request as ready for review August 26, 2026 11:36
cursoragent and others added 3 commits August 26, 2026 11:40
CI under cargo-llvm-cov failed save_images_separates_references_differing_only_in_case
with "Text file busy" when execing a stub script that had just been written
in place. Write the stub via fsync+rename, wait until it is executable, and
give each test a unique temp dir.

Co-authored-by: Ibrahim Rahhal <ibrahim.rahhal3636@gmail.com>
New `corgea mcp install` command is a minor SemVer bump. Cargo.toml is
the release source of truth; keep Cargo.lock in sync.

Co-authored-by: Ibrahim Rahhal <ibrahim.rahhal3636@gmail.com>
Comment thread src/mcp.rs
} else {
// Cursor interpolates ${env:NAME} from its own process environment,
// which a Dock/Start-menu launch usually lacks. Write the token.
args.push(format!("CORGEA-TOKEN:{token}"));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high: Project installation writes credentials into repository files

Project scope writes the raw Corgea token into files such as .cursor/mcp.json and .vscode/mcp.json, which are commonly committed. Printing a warning only after writing the secret does not prevent accidental disclosure. Project installs should use supported environment-variable or secret mechanisms, or reject project scope where secure indirection is unavailable.

Proof or reproduction:

corgea mcp install --agent cursor --scope project
git add .cursor/mcp.json
git diff --cached
# The diff contains CORGEA-TOKEN:<raw token> because the entry is built with format!("CORGEA-TOKEN:{token}").

Comment thread src/mcp.rs
}

fn mcp_remote_entry(url: &str, token: &str, http_only: bool, use_env_block: bool) -> Value {
let mut args = vec!["-y".to_string(), "mcp-remote".to_string(), url.to_string()];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high: Installer executes an unpinned npm package with credential access

Cursor, Windsurf, and Claude Desktop entries run npx -y mcp-remote without a version. Every launch can therefore download and execute whichever release currently owns the npm tag, while exposing the Corgea token through arguments or environment variables. Pin a reviewed version or ship a controlled transport implementation.

Proof or reproduction:

The generated command is equivalent to:
npx -y mcp-remote <url> --header CORGEA-TOKEN:<token>
There is no `mcp-remote@<version>` constraint.

Comment thread src/mcp.rs

pub fn looks_like_corgea_mcp_url(value: &str) -> bool {
let lower = value.to_ascii_lowercase();
lower.contains("corgea.") && lower.contains("/mcp")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high: Loose URL matching deletes unrelated MCP servers

looks_like_corgea_mcp_url uses substring checks instead of parsing and validating the hostname. Consequently, reinstalling Corgea removes unrelated entries whose URLs merely contain corgea. and /mcp. Match an explicit trusted Corgea hostname or another unambiguous identifier.

Proof or reproduction:

let existing = r#"{"mcpServers":{"unrelated":{"url":"https://notcorgea.app/mcp"}}}"#;
let (out, _) = upsert_corgea(Agent::Cursor, existing, "https://www.corgea.app/mcp", "tok").unwrap();
let value: Value = serde_json::from_str(&out).unwrap();
assert!(value["mcpServers"].get("unrelated").is_some()); // fails: entry was deleted

Comment thread src/mcp.rs
"httpUrl": url,
"headers": token_header(token)
}),
Agent::Continue => json!({

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high: Continue installation creates a context provider instead of an MCP server

The Continue branch inserts an arbitrary provider named corgea under contextProviders. Continue does not interpret that object as an MCP server; MCP configuration belongs in its MCP-server configuration structure. Thus the advertised Continue installation succeeds but does not register a usable server.

Proof or reproduction:

let (out, _) = upsert_corgea(Agent::Continue, "{}", "https://www.corgea.app/mcp", "tok").unwrap();
let value: Value = serde_json::from_str(&out).unwrap();
assert!(value.get("mcpServers").is_some()); // fails
assert!(value["contextProviders"][0]["name"] == "corgea"); // confirms the wrong configuration type

@corgea-security corgea-security left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review risk: 4/5.

The installer risks credential disclosure, executes an unpinned package, can delete unrelated MCP entries, and generates an invalid Continue configuration.

Critical or high-priority changes must be addressed.

Automatic approval was not submitted: checks failed: rust-tests.

@corgea-security corgea-security added the dennis-reviewed Dennis completed an automated review label Aug 26, 2026
Bring in the merged ETXTBSY image-test fix (#164) so this PR's CI can
pass without any further MCP-branch changes.

Co-authored-by: Ibrahim Rahhal <ibrahim.rahhal3636@gmail.com>
Comment thread src/mcp.rs
}
}

fn remove_corgea_from_map(servers: &mut Map<String, Value>) -> bool {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can we utilize .retain() here?

Comment thread src/mcp.rs

fn upsert_continue(root: &mut Map<String, Value>, entry: Value) -> Result<bool, String> {
let providers = root
.entry("contextProviders".to_string())

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you check this works with continue?

it requires mcpServers in yaml

https://docs.continue.dev/reference/continue-mcp

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dennis-reviewed Dennis completed an automated review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants