Skip to content

MCP, auth, and one-off commands

Not everything is a conversation. These run the CLI, read what it printed, and exit — a question with an answer rather than a process you talk to over time.

await command.version(); // '2.1.170', or null when undeterminable
await command.usage(); // the usage report as the CLI prints it
await command.update(); // CommandResult — success differs by install method
await command.describe(); // models, agents, account, settings in force here
await command.supportsModel('haiku'); // boolean
await command.exec(['mcp', 'list']); // the escape hatch
Method Worth knowing
version() null rather than throwing. “Which version” is a question a host asks to decide what to offer, and a CLI that will not answer is a fact to handle.
describe() Runs in its own ephemeral session, so asking a CLI to describe itself does not disturb what it was doing or land in the user’s transcript. null on timeout — an older CLI may not support the request.
supportsModel(m) Asked by sending the cheapest possible turn and seeing whether it comes back. Entitlement depends on the account and org, so the honest test is to try. Costs a request — cache the answer.
exec(args) A CLI gains subcommands faster than this library wraps them. Arguments are passed without a shell, so quoting and metacharacters stay literal.
const status = await command.auth.status();
status.loggedIn; // boolean
status.raw; // the account's own description of itself, unedited
status.text; // the human-readable output, for when JSON is absent

Signing in is a conversation with the user rather than a question with an answer, so it returns a live session:

const login = command.auth.login('console'); // or 'claudeai', the default
login.onUrl((url) => showToUser(url));
login.onFinished((code) => console.log('sign-in exited', code));
login.submitCode(pastedCode);
login.cancel();

Each of these runs the documented CLI command and parses what it printed.

await command.mcp.list(); // McpListEntry[] — names and statuses
await command.mcp.get('playwright'); // McpServer | null — transport details too
await command.mcp.all(); // every server in full: list, then describe each
await command.mcp.add('playwright', {
command: 'npx',
args: ['@executeautomation/playwright-mcp-server'],
}, { scope: 'user' });
await command.mcp.remove('playwright', { scope: 'user' });
Method Worth knowing
list() Names and statuses only — that is all claude mcp list reports. Returns [] when the command fails, because no MCP configured is the ordinary case and is indistinguishable from failure in the output.
all() Two commands per server, so noticeably slower. Worth it only when transport details are actually needed.
add() “Already exists” is accepted as a state rather than raised as an error. The CLI reports failure in its output rather than by exit code.
remove() Pass the scope for project- and local-scoped servers, or the CLI looks in the wrong configuration file and finds nothing.

The config travels as a single positional argument, which is why none of this goes through a shell — on win32 cmd.exe would tokenise the JSON’s quotes, &, | and spaces, corrupting the config and opening an injection surface.

  • Cross-platform — the caret escaping that keeps mcp add-json intact on Windows.