add-mcp
Version:
Add MCP servers to your favorite coding agents with a single command.
166 lines (159 loc) • 7.08 kB
TypeScript
/**
* Optional, capability-gated fields on {@link McpServerConfig}.
*
* The canonical {@link McpServerConfig} is the single, agent-agnostic schema
* that the CLI and library populate. Not every MCP client understands every
* optional field, so each agent declares which of these it supports via
* `AgentConfig.supportedFields`. Anything an agent does not support is dropped
* before its transform runs, guaranteeing that only known fields are ever
* written to a client config.
*/
type OptionalField = "timeout" | "scopes" | "autoApprove" | "bearerTokenEnv";
type AgentType = "antigravity" | "cline" | "cline-cli" | "claude-code" | "claude-desktop" | "codex" | "cursor" | "factory-droid" | "fx" | "gemini-cli" | "goose" | "github-copilot-cli" | "grok-build" | "hermes" | "junie" | "kilo-code" | "kimi-code" | "kiro-cli" | "mastracode" | "mcporter" | "opencode" | "pi" | "vscode" | "windsurf" | "zed";
/** `yaml` is YAML 1.2; `yaml-1.1` is for agents whose parser reads `on`, `off`, and `0123` the YAML 1.1 way. */
type ConfigFormat = "json" | "yaml" | "yaml-1.1" | "toml";
interface AgentConfig {
/** Internal name */
name: string;
/** Display name for UI */
displayName: string;
/** Global config file path */
configPath: string;
/** Local (project-level) config file path, if supported */
localConfigPath?: string;
/** Paths to check for project-level detection (relative to cwd) */
projectDetectPaths: string[];
/** Key in config file where MCP servers are stored (supports dot notation) */
configKey: string;
/** Optional key for project-level config when different from global configKey */
localConfigKey?: string;
/** Config file format */
format: ConfigFormat;
/** Supported transport types for this agent */
supportedTransports: ("stdio" | "sse" | "http")[];
/**
* Optional, capability-gated server fields this agent understands (e.g.
* `timeout`, `scopes`). Any optional field not listed here is stripped from
* the canonical config before {@link transformConfig} runs, so a client never
* receives a field it cannot interpret. Use an empty array for agents that
* only support the core fields.
*/
supportedFields: OptionalField[];
/** Shown when a user tries to use an unsupported transport */
unsupportedTransportMessage?: string;
/** Function to detect if agent is installed globally */
detectGlobalInstall: () => Promise<boolean>;
/** Optional function to dynamically resolve config path */
resolveConfigPath?: (agent: AgentConfig, options: {
local: boolean;
cwd: string;
}) => string;
/**
* Transform the canonical, field-gated server config into this agent's
* concrete on-disk schema. Required for every agent: transforms must build a
* fresh object with only the keys the client understands, which structurally
* guarantees no unknown fields leak into a written config.
*/
transformConfig: (serverName: string, config: McpServerConfig, context?: {
local: boolean;
configKey?: string;
}) => unknown;
}
type TransportType = "sse" | "http";
interface McpServerConfig {
/** For remote servers */
type?: TransportType;
url?: string;
headers?: Record<string, string>;
/** For local stdio servers */
command?: string;
args?: string[];
env?: Record<string, string>;
/**
* Request timeout in milliseconds for remote servers. Capability-gated:
* only emitted for agents that list `"timeout"` in `supportedFields`.
*/
timeout?: number;
/**
* OAuth scopes to request for remote servers. Capability-gated: only emitted
* for agents that list `"scopes"` in `supportedFields`, each mapping it into
* their own native shape (e.g. Cursor `auth.scopes`, Gemini `oauth.scopes`).
*/
oauthScopes?: string[];
/**
* Tools to auto-approve (skip the agent's per-call approval prompt). An empty
* array means "all tools". This is an install directive rather than a literal
* server-config key: capability-gated via `"autoApprove"` in `supportedFields`
* and each supporting agent applies it in its own way (e.g. Codex emits
* approval modes in its config; Claude Code writes permission allow rules to a
* separate settings file). It is never written into a server entry verbatim.
*/
autoApproveTools?: string[];
bearerTokenEnv?: string;
}
interface InstallOptions {
/** Install to local (project-level) config instead of global */
local?: boolean;
/** Current working directory for local installs */
cwd?: string;
}
interface InstallResult {
success: boolean;
path: string;
error?: string;
/**
* Optional fields that were requested but dropped because the target agent
* does not support them. Empty/undefined when nothing was dropped.
*/
droppedFields?: OptionalField[];
/**
* Additional files written beyond the main config (e.g. a Claude Code
* permissions settings file for auto-approval). Surfaced so the CLI can
* report them and include them in `--gitignore`.
*/
extraPaths?: string[];
}
declare const agents: Record<AgentType, AgentConfig>;
declare function getAgentTypes(): AgentType[];
declare function detectProjectAgents(cwd?: string): AgentType[];
declare function detectGlobalAgents(): Promise<AgentType[]>;
interface InstalledServer {
serverName: string;
config: Record<string, unknown>;
identity: string;
agentType: AgentType;
scope: "local" | "global";
configPath: string;
/** Key in the config file where this server was read from */
configKey: string;
}
interface AgentServers {
agentType: AgentType;
displayName: string;
detected: boolean;
scope: "local" | "global";
configPath: string;
servers: InstalledServer[];
/** Present when this agent's config could not be read */
error?: string;
}
/**
* Gather installed servers across detected (or explicitly specified) agents.
* When `agents` is provided, those agents are included even if not detected
* (with detected=false).
*/
declare function listInstalledServers(options: {
global?: boolean;
agents?: AgentType[];
cwd?: string;
}): Promise<AgentServers[]>;
type AgentInput = AgentType | (string & {});
declare function upsertServer(agentType: AgentInput, serverName: string, serverConfig: McpServerConfig, options?: InstallOptions): InstallResult;
interface RemoveServerResult {
success: boolean;
path: string;
removed: boolean;
error?: string;
}
declare function removeServer(agentType: AgentInput, serverName: string, options?: InstallOptions): RemoveServerResult;
export { type AgentConfig, type AgentInput, type AgentServers, type AgentType, type InstallOptions, type InstallResult, type InstalledServer, type McpServerConfig, type OptionalField, type RemoveServerResult, agents, detectGlobalAgents, detectProjectAgents, getAgentTypes, listInstalledServers, removeServer, upsertServer };