Skip to content

feat: add non-interactive mode to mcp add command#53

Merged
suryaiyer95 merged 6 commits intomainfrom
feat/AI-0000-mcp-add-non-interactive
Mar 6, 2026
Merged

feat: add non-interactive mode to mcp add command#53
suryaiyer95 merged 6 commits intomainfrom
feat/AI-0000-mcp-add-non-interactive

Conversation

@suryaiyer95
Copy link
Contributor

@suryaiyer95 suryaiyer95 commented Mar 5, 2026

Summary

  • Add non-interactive mode to mcp add command with flags: --name, --type, --url, --command, --header, --no-oauth, --global
  • Add mcp remove (alias rm) command to remove MCP servers by name
  • Fix --no-oauth by using yargs built-in --no- negation instead of a custom option
  • Fall back to global config when not in a git repo (prevents EROFS errors when running from ~)

Test plan

  • Run altimate-code mcp add --name "test" --type remote --url "https://example.com/sse" --header "Authorization=Bearer token" --no-oauth --global and verify config is written
  • Run altimate-code mcp remove test --global and verify config entry is removed
  • Run mcp add from a non-git directory (e.g. ~) without --global and verify it falls back to global config
  • Run mcp remove for a server that doesn't exist and verify error message

🤖 Generated with Claude Code

.option("url", { type: "string", describe: "Server URL (for remote type)" })
.option("command", { type: "string", describe: "Command to run (for local type)" })
.option("header", { type: "array", string: true, describe: "HTTP headers as key=value (repeatable)" })
.option("no-oauth", { type: "boolean", describe: "Disable OAuth" })

This comment was marked as outdated.

Comment on lines +688 to +692
// Try the other scope
const otherPath = await resolveConfigPath(
useGlobal ? Instance.worktree : Global.Path.config,
!useGlobal,
)

This comment was marked as outdated.

@suryaiyer95
Copy link
Contributor Author

Addressing Sentry review comments:

  1. --no-oauth bug — Fixed in commit 115a550. Changed to .option("oauth", { type: "boolean", default: true }) so yargs built-in --no- negation works naturally.

  2. mcp remove in non-git dir — Fixed in commit 80f08d6. Added const useGlobal = args.global || Instance.project.vcs \!== "git" fallback so it uses global config when not in a git repo.

export async function list() {
return state().then((x) => Object.values(x))
const full = state()
const resolved = await Promise.race([full, Promise.resolve(undefined)])
Copy link

Choose a reason for hiding this comment

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

Bug: Promise.race with Promise.resolve(undefined) causes Command.list() to always return only default commands, excluding custom ones from config, MCP, or skills.
Severity: HIGH

Suggested Fix

The Promise.race implementation is incorrect for its intended purpose. A different approach is needed, such as checking if the full promise is already resolved before the race, or using a timeout-based approach instead of racing against an immediately resolved promise.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: packages/altimate-code/src/command/index.ts#L188

Potential issue: In `Command.list()` and `Command.get()`, the code uses
`Promise.race([full, Promise.resolve(undefined)])`. Because `Promise.resolve(undefined)`
creates an already-resolved promise, it always wins the race immediately. This causes
the `resolved` variable to always be `undefined`. As a result, `Command.list()` only
returns the three default commands (`init`, `discover`, `review`) and never includes MCP
prompts, skills, or user-configured commands. This breaks any UI feature, such as the
`/command` endpoint, that relies on listing all available commands. While
`Command.get()` has a fallback, its optimization path becomes dead code.

@suryaiyer95 suryaiyer95 force-pushed the feat/AI-0000-mcp-add-non-interactive branch from 1cf8025 to 80f08d6 Compare March 5, 2026 22:32
suryaiyer95 and others added 4 commits March 6, 2026 14:21
Add CLI flags (`--name`, `--type`, `--url`, `--command`, `--header`,
`--no-oauth`, `--global`) to `mcp add` for scripted usage.

When `--name` and `--type` are provided, the command skips all
interactive prompts and writes the MCP config directly. This enables
copy-paste onboarding commands from the Altimate web UI.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Define `oauth` as a boolean (default `true`) so `--no-oauth` works via
yargs' built-in prefix negation instead of a custom `no-oauth` option
that conflicts with yargs' `--no-X` handling.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add `altimate-code mcp remove <name>` (alias `rm`) to remove an MCP
server from config. Supports `--global` flag; auto-searches the other
scope if the server isn't found in the specified one.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
`mcp add` and `mcp remove` now auto-detect when `cwd` is not inside a
git project and write to the global config instead of trying to create
`/altimate-code.json`.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@suryaiyer95 suryaiyer95 force-pushed the feat/AI-0000-mcp-add-non-interactive branch from 80f08d6 to 7233f77 Compare March 6, 2026 22:29
Comment on lines +688 to +692
// Try the other scope
const otherPath = await resolveConfigPath(
useGlobal ? Instance.worktree : Global.Path.config,
!useGlobal,
)

This comment was marked as outdated.

- Update `ci.yml` TypeScript test `working-directory` from
  `packages/altimate-code` to `packages/opencode` (post-restructure)
- Fix `mcp remove` fallback logic to avoid accessing filesystem root
  when not in a git repo — only try cross-scope fallback when
  `Instance.worktree` is valid (i.e., in a git repo)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@suryaiyer95 suryaiyer95 force-pushed the feat/AI-0000-mcp-add-non-interactive branch from e242f11 to 8b7ab63 Compare March 6, 2026 22:38
Comment on lines +457 to +460
const configPath = await resolveConfigPath(
useGlobal ? Global.Path.config : Instance.worktree,
useGlobal,
)

This comment was marked as outdated.

- Validate server name is non-empty (reject whitespace-only)
- Use `trim().split(/\s+/).filter(Boolean)` for command parsing
  instead of naive `split(" ")` that breaks on multiple spaces
- Add `URL.canParse()` validation for remote server URLs,
  matching interactive mode's validation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@suryaiyer95 suryaiyer95 merged commit 6f9720e into main Mar 6, 2026
4 of 9 checks passed
@kulvirgit kulvirgit deleted the feat/AI-0000-mcp-add-non-interactive branch March 10, 2026 21:06
anandgupta42 pushed a commit that referenced this pull request Mar 17, 2026
…active

feat: add non-interactive mode to mcp add command
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