refactor(cli): align mcp add args help

This commit is contained in:
opencode-agent[bot] 2026-06-01 04:34:11 +00:00
commit ebb101bd54
4 changed files with 81 additions and 55 deletions

View file

@ -59,7 +59,7 @@ type McpAddArgs = {
_?: Array<string | number> _?: Array<string | number>
"--"?: string[] "--"?: string[]
name?: string name?: string
urlOrCommand?: string[] args?: string[]
type?: "local" | "remote" type?: "local" | "remote"
env?: string[] env?: string[]
header?: string[] header?: string[]
@ -456,7 +456,7 @@ async function addMcpToConfig(name: string, mcpConfig: ConfigMCP.Info, configPat
} }
export const McpAddCommand = effectCmd({ export const McpAddCommand = effectCmd({
command: "add [name] [urlOrCommand..]", command: "add [name] [args..]",
describe: "add an MCP server", describe: "add an MCP server",
builder: (yargs) => builder: (yargs) =>
yargs yargs
@ -465,8 +465,8 @@ export const McpAddCommand = effectCmd({
describe: "name of the MCP server", describe: "name of the MCP server",
type: "string", type: "string",
}) })
.positional("urlOrCommand", { .positional("args", {
describe: "URL for remote servers or command for local servers", describe: "URL for remote servers or command and arguments for local servers",
type: "string", type: "string",
array: true, array: true,
default: [], default: [],
@ -527,15 +527,27 @@ export const McpAddCommand = effectCmd({
.option("oauth-redirect-uri", { .option("oauth-redirect-uri", {
describe: "OAuth redirect URI for remote servers", describe: "OAuth redirect URI for remote servers",
type: "string", type: "string",
}), }).epilogue(`Usage:
handler: Effect.fn("Cli.mcp.add")(function* (args: McpAddArgs) { opencode mcp add <name> -- <command> [args...] (local MCP server)
opencode mcp add <name> --env KEY=VALUE -- <command> [args...] (local MCP server with env vars)
opencode mcp add <name> <url> (remote MCP server)
opencode mcp add <name> --header KEY=VALUE <url> (remote MCP server with headers)
opencode mcp add <name> --scope project -- <command> [args...] (save to project config)
Examples:
opencode mcp add context7 -- npx -y @upstash/context7-mcp
opencode mcp add local-env --env FOO=bar -- node server.js
opencode mcp add sg --header Authorization=token https://sg.example/mcp
opencode mcp add hugging-face https://huggingface.co/mcp`),
handler: Effect.fn("Cli.mcp.add")(function* (input: McpAddArgs) {
const maybeCtx = yield* InstanceRef const maybeCtx = yield* InstanceRef
if (!maybeCtx) return yield* Effect.die("InstanceRef not provided") if (!maybeCtx) return yield* Effect.die("InstanceRef not provided")
const ctx = maybeCtx const ctx = maybeCtx
const urlOrCommand = mcpAddUrlOrCommand(args) const inlineArgs = mcpAddArgs(input)
const inlineConfig = parseInlineMcpAdd(args, urlOrCommand) const inlineConfig = parseInlineMcpAdd(input, inlineArgs)
if (inlineConfig && "error" in inlineConfig) return yield* fail(inlineConfig.error) if (inlineConfig && "error" in inlineConfig) return yield* fail(inlineConfig.error)
if (args.global && args.scope === "project") return yield* fail("--global cannot be combined with --scope project") if (input.global && input.scope === "project")
return yield* fail("--global cannot be combined with --scope project")
yield* Effect.promise(async () => { yield* Effect.promise(async () => {
UI.empty() UI.empty()
prompts.intro("Add MCP server") prompts.intro("Add MCP server")
@ -548,8 +560,8 @@ export const McpAddCommand = effectCmd({
]) ])
const configPath = await (async () => { const configPath = await (async () => {
if (args.global || args.scope === "global") return globalConfigPath if (input.global || input.scope === "global") return globalConfigPath
if (args.scope === "project") return projectConfigPath if (input.scope === "project") return projectConfigPath
if (inlineConfig) return project.vcs === "git" ? projectConfigPath : globalConfigPath if (inlineConfig) return project.vcs === "git" ? projectConfigPath : globalConfigPath
if (project.vcs !== "git") return globalConfigPath if (project.vcs !== "git") return globalConfigPath
const scopeResult = await prompts.select({ const scopeResult = await prompts.select({
@ -572,8 +584,8 @@ export const McpAddCommand = effectCmd({
})() })()
if (inlineConfig) { if (inlineConfig) {
await addMcpToConfig(args.name!.trim(), inlineConfig.config, configPath) await addMcpToConfig(input.name!.trim(), inlineConfig.config, configPath)
prompts.log.success(`MCP server "${args.name!.trim()}" added to ${configPath}`) prompts.log.success(`MCP server "${input.name!.trim()}" added to ${configPath}`)
prompts.outro("MCP server added successfully") prompts.outro("MCP server added successfully")
return return
} }
@ -701,47 +713,47 @@ export const McpAddCommand = effectCmd({
}), }),
}) })
function mcpAddUrlOrCommand(args: McpAddArgs) { function mcpAddArgs(input: McpAddArgs) {
const addIndex = args._?.lastIndexOf("add") ?? -1 const addIndex = input._?.lastIndexOf("add") ?? -1
return [ return [
...(args.urlOrCommand ?? []), ...(input.args ?? []),
...(addIndex === -1 || !args._ ? [] : args._.slice(addIndex + 1).map(String)), ...(addIndex === -1 || !input._ ? [] : input._.slice(addIndex + 1).map(String)),
...(args["--"] ?? []), ...(input["--"] ?? []),
] ]
} }
function parseInlineMcpAdd( function parseInlineMcpAdd(
args: McpAddArgs, input: McpAddArgs,
urlOrCommand: string[], inlineArgs: string[],
): { config: ConfigMCP.Info } | { error: string } | undefined { ): { config: ConfigMCP.Info } | { error: string } | undefined {
if (!hasInlineMcpAdd(args, urlOrCommand)) return undefined if (!hasInlineMcpAdd(input, inlineArgs)) return undefined
const name = args.name?.trim() const name = input.name?.trim()
if (!name) return { error: "MCP server name is required" } if (!name) return { error: "MCP server name is required" }
if (urlOrCommand.length === 0) return { error: "URL or command is required" } if (inlineArgs.length === 0) return { error: "URL or command is required" }
if (args.timeout !== undefined && (!Number.isInteger(args.timeout) || args.timeout <= 0)) { if (input.timeout !== undefined && (!Number.isInteger(input.timeout) || input.timeout <= 0)) {
return { error: "--timeout must be a positive integer" } return { error: "--timeout must be a positive integer" }
} }
const type = args.type ?? (urlOrCommand.length === 1 && URL.canParse(urlOrCommand[0]) ? "remote" : "local") const type = input.type ?? (inlineArgs.length === 1 && URL.canParse(inlineArgs[0]) ? "remote" : "local")
if (type === "local") return parseInlineLocalMcp(args, urlOrCommand) if (type === "local") return parseInlineLocalMcp(input, inlineArgs)
return parseInlineRemoteMcp(args, urlOrCommand) return parseInlineRemoteMcp(input, inlineArgs)
} }
function hasInlineMcpAdd(args: McpAddArgs, urlOrCommand: string[]) { function hasInlineMcpAdd(input: McpAddArgs, inlineArgs: string[]) {
return !!( return !!(
args.name || input.name ||
urlOrCommand.length > 0 || inlineArgs.length > 0 ||
args.type || input.type ||
args.env?.length || input.env?.length ||
args.header?.length || input.header?.length ||
args.enabled !== undefined || input.enabled !== undefined ||
args.timeout !== undefined || input.timeout !== undefined ||
args.oauth !== undefined || input.oauth !== undefined ||
args.oauthClientId || input.oauthClientId ||
args.oauthClientSecret || input.oauthClientSecret ||
args.oauthScope || input.oauthScope ||
args.oauthCallbackPort !== undefined || input.oauthCallbackPort !== undefined ||
args.oauthRedirectUri input.oauthRedirectUri
) )
} }

View file

@ -27,11 +27,11 @@ exports[`opencode CLI help-text snapshots every documented command emits stable
manage MCP (Model Context Protocol) servers manage MCP (Model Context Protocol) servers
Commands: Commands:
opencode mcp add [name] [urlOrCommand..] add an MCP server opencode mcp add [name] [args..] add an MCP server
opencode mcp list list MCP servers and their status [aliases: ls] opencode mcp list list MCP servers and their status [aliases: ls]
opencode mcp auth [name] authenticate with an OAuth-enabled MCP server opencode mcp auth [name] authenticate with an OAuth-enabled MCP server
opencode mcp logout [name] remove OAuth credentials for an MCP server opencode mcp logout [name] remove OAuth credentials for an MCP server
opencode mcp debug <name> debug OAuth connection for an MCP server opencode mcp debug <name> debug OAuth connection for an MCP server
Options: Options:
-h, --help show help [boolean] -h, --help show help [boolean]
@ -425,13 +425,13 @@ Options:
`; `;
exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode mcp add --help 1`] = ` exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode mcp add --help 1`] = `
"opencode mcp add [name] [urlOrCommand..] "opencode mcp add [name] [args..]
add an MCP server add an MCP server
Positionals: Positionals:
name name of the MCP server [string] name name of the MCP server [string]
urlOrCommand URL for remote servers or command for local servers [array] [default: []] args URL for remote servers or command and arguments for local servers [array] [default: []]
Options: Options:
-h, --help show help [boolean] -h, --help show help [boolean]
@ -452,7 +452,20 @@ Options:
--oauth-client-secret OAuth client secret for remote servers [string] --oauth-client-secret OAuth client secret for remote servers [string]
--oauth-scope OAuth scopes to request for remote servers [string] --oauth-scope OAuth scopes to request for remote servers [string]
--oauth-callback-port OAuth local callback port for remote servers [number] --oauth-callback-port OAuth local callback port for remote servers [number]
--oauth-redirect-uri OAuth redirect URI for remote servers [string]" --oauth-redirect-uri OAuth redirect URI for remote servers [string]
Usage:
opencode mcp add <name> -- <command> [args...] (local MCP server)
opencode mcp add <name> --env KEY=VALUE -- <command> [args...] (local MCP server with env vars)
opencode mcp add <name> <url> (remote MCP server)
opencode mcp add <name> --header KEY=VALUE <url> (remote MCP server with headers)
opencode mcp add <name> --scope project -- <command> [args...] (save to project config)
Examples:
opencode mcp add context7 -- npx -y @upstash/context7-mcp
opencode mcp add local-env --env FOO=bar -- node server.js
opencode mcp add sg --header Authorization=token https://sg.example/mcp
opencode mcp add hugging-face https://huggingface.co/mcp"
`; `;
exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode mcp auth --help 1`] = ` exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode mcp auth --help 1`] = `

View file

@ -39,12 +39,13 @@ describe("opencode mcp", () => {
"mcp", "mcp",
"add", "add",
"everything", "everything",
"npx",
"-y",
"@modelcontextprotocol/server-everything",
"--env", "--env",
"FOO=bar", "FOO=bar",
"--global", "--global",
"--",
"npx",
"-y",
"@modelcontextprotocol/server-everything",
]) ])
opencode.expectExit(local, 0, "opencode mcp add local") opencode.expectExit(local, 0, "opencode mcp add local")

View file

@ -50,7 +50,7 @@ You can add MCP servers from the command line with `opencode mcp add`.
```bash ```bash
# Local server # Local server
opencode mcp add mcp_everything npx -y @modelcontextprotocol/server-everything opencode mcp add mcp_everything -- npx -y @modelcontextprotocol/server-everything
# Remote server # Remote server
opencode mcp add github https://api.githubcopilot.com/mcp \ opencode mcp add github https://api.githubcopilot.com/mcp \
@ -58,7 +58,7 @@ opencode mcp add github https://api.githubcopilot.com/mcp \
--header "Authorization: Bearer YOUR_GITHUB_PAT" --header "Authorization: Bearer YOUR_GITHUB_PAT"
``` ```
Use `--env KEY=VALUE` for local server environment variables and repeat it for multiple values. Use `--global` to save to global config; otherwise the command saves to the current project when run inside a git repository. Use `--env KEY=VALUE` for local server environment variables and repeat it for multiple values. Use `--scope project` or `--global` to choose where the server is saved.
--- ---