add-mcp 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +128 -94
- package/dist/index.js +242 -94
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,133 +1,167 @@
|
|
|
1
1
|
# add-mcp
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Add MCP servers to your favorite coding agents with a single command.
|
|
4
|
+
|
|
5
|
+
Supports **OpenCode**, **Claude Code**, **Codex**, **Cursor**, and [5 more](#supported-agents).
|
|
6
|
+
|
|
7
|
+
## Install an MCP Server
|
|
4
8
|
|
|
5
9
|
```bash
|
|
6
|
-
npx add-mcp
|
|
10
|
+
npx add-mcp https://mcp.example.com/sse
|
|
7
11
|
```
|
|
8
12
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
## Features
|
|
13
|
+
### Source Formats
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
- Supports project-level and global installation
|
|
15
|
+
```bash
|
|
16
|
+
# Remote MCP server (HTTP streamable - default)
|
|
17
|
+
npx add-mcp https://mcp.example.com/mcp
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
# Remote MCP server (SSE transport)
|
|
20
|
+
npx add-mcp https://mcp.example.com/sse --transport sse
|
|
19
21
|
|
|
20
|
-
|
|
22
|
+
# npm package (runs via npx)
|
|
23
|
+
npx add-mcp @modelcontextprotocol/server-postgres
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
npx add-mcp https://mcp.example.com/api
|
|
25
|
+
# Full command with arguments
|
|
26
|
+
npx add-mcp "npx -y @org/mcp-server --flag value"
|
|
25
27
|
|
|
26
|
-
#
|
|
27
|
-
npx add-mcp
|
|
28
|
+
# Node.js script
|
|
29
|
+
npx add-mcp "node /path/to/server.js --port 3000"
|
|
28
30
|
```
|
|
29
31
|
|
|
30
|
-
###
|
|
32
|
+
### Options
|
|
33
|
+
|
|
34
|
+
| Option | Description |
|
|
35
|
+
| ------------------------ | ------------------------------------------------------------------------ |
|
|
36
|
+
| `-g, --global` | Install to user directory instead of project |
|
|
37
|
+
| `-a, --agent <agent>` | Target specific agents (e.g., `cursor`, `claude-code`). Can be repeated. |
|
|
38
|
+
| `-t, --transport <type>` | Transport type for remote servers: `http` (default), `sse` |
|
|
39
|
+
| `--type <type>` | Alias for `--transport` |
|
|
40
|
+
| `-n, --name <name>` | Server name (auto-inferred if not provided) |
|
|
41
|
+
| `-y, --yes` | Skip all confirmation prompts |
|
|
42
|
+
| `--all` | Install to all agents |
|
|
43
|
+
|
|
44
|
+
### Examples
|
|
31
45
|
|
|
32
46
|
```bash
|
|
33
|
-
# Install
|
|
34
|
-
npx add-mcp
|
|
47
|
+
# Install to specific agents
|
|
48
|
+
npx add-mcp https://mcp.example.com/mcp -a cursor -a claude-code
|
|
35
49
|
|
|
36
|
-
# Install with
|
|
37
|
-
npx add-mcp mcp
|
|
38
|
-
```
|
|
50
|
+
# Install with SSE transport
|
|
51
|
+
npx add-mcp https://mcp.neon.tech/sse --transport sse
|
|
39
52
|
|
|
40
|
-
|
|
53
|
+
# Install with custom server name
|
|
54
|
+
npx add-mcp @modelcontextprotocol/server-postgres --name postgres
|
|
41
55
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
npx add-mcp "npx -y @org/mcp-server --flag value"
|
|
56
|
+
# Non-interactive installation (CI/CD friendly)
|
|
57
|
+
npx add-mcp https://mcp.example.com/mcp -g -a claude-code -y
|
|
45
58
|
|
|
46
|
-
#
|
|
47
|
-
npx add-mcp
|
|
59
|
+
# Install to all agents
|
|
60
|
+
npx add-mcp mcp-server-github --all
|
|
61
|
+
|
|
62
|
+
# Install to all agents, globally, without prompts
|
|
63
|
+
npx add-mcp mcp-server-github --all -g -y
|
|
48
64
|
```
|
|
49
65
|
|
|
50
|
-
|
|
66
|
+
### Installation Scope
|
|
51
67
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
target MCP server URL (remote) or package name (local stdio)
|
|
57
|
-
|
|
58
|
-
Options:
|
|
59
|
-
-V, --version output the version number
|
|
60
|
-
-g, --global Install globally (user-level) instead of project-level
|
|
61
|
-
-a, --agent <agents...> Specify agents to install to
|
|
62
|
-
-n, --name <name> Server name (auto-inferred from target if not provided)
|
|
63
|
-
-y, --yes Skip confirmation prompts
|
|
64
|
-
-l, --list List supported agents
|
|
65
|
-
--all Install to all agents without prompts (implies -y -g)
|
|
66
|
-
-h, --help display help for command
|
|
67
|
-
```
|
|
68
|
+
| Scope | Flag | Location | Use Case |
|
|
69
|
+
| ----------- | --------- | ----------------------- | --------------------------------------------- |
|
|
70
|
+
| **Project** | (default) | `.cursor/mcp.json` etc. | Committed with your project, shared with team |
|
|
71
|
+
| **Global** | `-g` | `~/.cursor/mcp.json` | Available across all projects |
|
|
68
72
|
|
|
69
|
-
|
|
73
|
+
### Smart Detection
|
|
74
|
+
|
|
75
|
+
The CLI automatically detects agents based on your environment:
|
|
70
76
|
|
|
71
|
-
|
|
77
|
+
**Default (project mode):**
|
|
72
78
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
| Claude Desktop | `claude-desktop` | JSON | No |
|
|
77
|
-
| Codex | `codex` | TOML | No |
|
|
78
|
-
| Cursor | `cursor` | JSON | Yes |
|
|
79
|
-
| Gemini CLI | `gemini-cli` | JSON | Yes |
|
|
80
|
-
| Goose | `goose` | YAML | No |
|
|
81
|
-
| OpenCode | `opencode` | JSON | Yes |
|
|
82
|
-
| VS Code | `vscode` | JSON | Yes |
|
|
83
|
-
| Zed | `zed` | JSON | No |
|
|
79
|
+
- Detects project-level config files (`.cursor/`, `.vscode/`, `.mcp.json`, etc.)
|
|
80
|
+
- Also detects globally-installed agents that only support global config (Claude Desktop, Codex, Zed)
|
|
81
|
+
- Agents are routed appropriately: project-capable agents use project config, global-only agents use global config
|
|
84
82
|
|
|
85
|
-
|
|
83
|
+
**With `-g` (global mode):**
|
|
84
|
+
|
|
85
|
+
- Detects all globally-installed agents
|
|
86
|
+
- All agents use global config
|
|
87
|
+
|
|
88
|
+
**No agents detected:**
|
|
89
|
+
|
|
90
|
+
- Interactive mode: Shows error with guidance to use `--global` or run in a project
|
|
91
|
+
- With `--yes`: Installs to all project-capable agents
|
|
92
|
+
|
|
93
|
+
## Transport Types
|
|
94
|
+
|
|
95
|
+
MCP supports different transport mechanisms for remote servers:
|
|
96
|
+
|
|
97
|
+
| Transport | Flag | Description |
|
|
98
|
+
| --------- | ------------------ | ---------------------------------------------- |
|
|
99
|
+
| **HTTP** | `--transport http` | Streamable HTTP (default, modern standard) |
|
|
100
|
+
| **SSE** | `--transport sse` | Server-Sent Events (legacy, still widely used) |
|
|
101
|
+
|
|
102
|
+
Local servers (npm packages, commands) always use **stdio** transport.
|
|
103
|
+
|
|
104
|
+
## Supported Agents
|
|
105
|
+
|
|
106
|
+
MCP servers can be installed to any of these agents:
|
|
107
|
+
|
|
108
|
+
| Agent | `--agent` | Project Path | Global Path |
|
|
109
|
+
| -------------- | ---------------- | ----------------------- | ----------------------------------------------------------------- |
|
|
110
|
+
| Claude Code | `claude-code` | `.mcp.json` | `~/.claude.json` |
|
|
111
|
+
| Claude Desktop | `claude-desktop` | - | `~/Library/Application Support/Claude/claude_desktop_config.json` |
|
|
112
|
+
| Codex | `codex` | - | `~/.codex/config.toml` |
|
|
113
|
+
| Cursor | `cursor` | `.cursor/mcp.json` | `~/.cursor/mcp.json` |
|
|
114
|
+
| Gemini CLI | `gemini-cli` | `.gemini/settings.json` | `~/.gemini/settings.json` |
|
|
115
|
+
| Goose | `goose` | `.goose/config.yaml` | `~/.config/goose/config.yaml` |
|
|
116
|
+
| OpenCode | `opencode` | `.opencode.json` | `~/.config/opencode/opencode.json` |
|
|
117
|
+
| VS Code | `vscode` | `.vscode/mcp.json` | `~/Library/Application Support/Code/User/mcp.json` |
|
|
118
|
+
| Zed | `zed` | - | `~/.config/zed/settings.json` |
|
|
86
119
|
|
|
87
120
|
**Aliases:** `github-copilot` → `vscode`
|
|
88
121
|
|
|
89
|
-
|
|
122
|
+
The CLI uses smart detection to find agents in your project directory and globally installed agents. See [Smart Detection](#smart-detection) for details.
|
|
90
123
|
|
|
91
|
-
###
|
|
124
|
+
### Transport Support
|
|
92
125
|
|
|
93
|
-
|
|
94
|
-
# Install to Cursor and Claude Code only
|
|
95
|
-
npx add-mcp https://mcp.example.com/api -a cursor claude-code
|
|
126
|
+
Not all agents support all transport types:
|
|
96
127
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
128
|
+
| Agent | stdio | http | sse |
|
|
129
|
+
| -------------- | ----- | ---- | --- |
|
|
130
|
+
| Claude Code | ✓ | ✓ | ✓ |
|
|
131
|
+
| Claude Desktop | ✓ | ✓ | ✓ |
|
|
132
|
+
| Codex | ✓ | ✓ | ✓ |
|
|
133
|
+
| Cursor | ✓ | ✓ | ✓ |
|
|
134
|
+
| Gemini CLI | ✓ | ✓ | ✓ |
|
|
135
|
+
| Goose | ✓ | ✓ | ✗ |
|
|
136
|
+
| OpenCode | ✓ | ✓ | ✓ |
|
|
137
|
+
| VS Code | ✓ | ✓ | ✓ |
|
|
138
|
+
| Zed | ✓ | ✓ | ✓ |
|
|
100
139
|
|
|
101
|
-
|
|
140
|
+
## What are MCP Servers?
|
|
102
141
|
|
|
103
|
-
|
|
104
|
-
# Project-level (creates .cursor/mcp.json, .mcp.json, etc.)
|
|
105
|
-
npx add-mcp mcp-server
|
|
142
|
+
[Model Context Protocol (MCP)](https://modelcontextprotocol.io/) servers extend your coding agent's capabilities by providing tools, resources, and context. MCP servers can:
|
|
106
143
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
144
|
+
- Connect to databases (PostgreSQL, MySQL, etc.)
|
|
145
|
+
- Integrate with external services (GitHub, Linear, Notion)
|
|
146
|
+
- Provide file system access
|
|
147
|
+
- Offer specialized tools for your workflow
|
|
110
148
|
|
|
111
|
-
|
|
149
|
+
## Troubleshooting
|
|
112
150
|
|
|
113
|
-
|
|
114
|
-
# Skip all prompts, install globally to all detected agents
|
|
115
|
-
npx add-mcp https://mcp.example.com/api -y -g
|
|
151
|
+
### Transport mismatch error
|
|
116
152
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
153
|
+
If you get an error about transport not being supported, check that the agent supports your chosen transport type. For example, Goose doesn't support SSE transport.
|
|
154
|
+
|
|
155
|
+
### Server not loading
|
|
156
|
+
|
|
157
|
+
- Verify the server URL is correct and accessible
|
|
158
|
+
- Check the agent's MCP configuration file for syntax errors
|
|
159
|
+
- Ensure the server name doesn't conflict with existing servers
|
|
160
|
+
|
|
161
|
+
### Permission errors
|
|
162
|
+
|
|
163
|
+
Ensure you have write access to the target configuration directory.
|
|
164
|
+
|
|
165
|
+
## License
|
|
120
166
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
| Agent | Global | Local |
|
|
124
|
-
| -------------- | ----------------------------------------------------------------- | ----------------------- |
|
|
125
|
-
| Claude Code | `~/.claude.json` | `.mcp.json` |
|
|
126
|
-
| Claude Desktop | `~/Library/Application Support/Claude/claude_desktop_config.json` | - |
|
|
127
|
-
| Codex | `~/.codex/config.toml` | - |
|
|
128
|
-
| Cursor | `~/.cursor/mcp.json` | `.cursor/mcp.json` |
|
|
129
|
-
| Gemini CLI | `~/.gemini/settings.json` | `.gemini/settings.json` |
|
|
130
|
-
| Goose | `~/.config/goose/config.yaml` | - |
|
|
131
|
-
| OpenCode | `~/.config/opencode/opencode.json` | `.opencode.json` |
|
|
132
|
-
| VS Code | `~/Library/Application Support/Code/User/mcp.json` | `.vscode/mcp.json` |
|
|
133
|
-
| Zed | `~/.config/zed/settings.json` | - |
|
|
167
|
+
Apache 2.0
|
package/dist/index.js
CHANGED
|
@@ -42,11 +42,9 @@ function transformGooseConfig(serverName, config) {
|
|
|
42
42
|
if (config.url) {
|
|
43
43
|
return {
|
|
44
44
|
name: serverName,
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
enabled:
|
|
48
|
-
envs: {},
|
|
49
|
-
type: "stdio",
|
|
45
|
+
type: "streamable_http",
|
|
46
|
+
url: config.url,
|
|
47
|
+
enabled: true,
|
|
50
48
|
timeout: 300
|
|
51
49
|
};
|
|
52
50
|
}
|
|
@@ -113,9 +111,11 @@ var agents = {
|
|
|
113
111
|
displayName: "Claude Code",
|
|
114
112
|
configPath: join(home, ".claude.json"),
|
|
115
113
|
localConfigPath: ".mcp.json",
|
|
114
|
+
projectDetectPaths: [".mcp.json", ".claude"],
|
|
116
115
|
configKey: "mcpServers",
|
|
117
116
|
format: "json",
|
|
118
|
-
|
|
117
|
+
supportedTransports: ["stdio", "http", "sse"],
|
|
118
|
+
detectGlobalInstall: async () => {
|
|
119
119
|
return existsSync(join(home, ".claude"));
|
|
120
120
|
}
|
|
121
121
|
},
|
|
@@ -123,9 +123,12 @@ var agents = {
|
|
|
123
123
|
name: "claude-desktop",
|
|
124
124
|
displayName: "Claude Desktop",
|
|
125
125
|
configPath: join(appSupport, "Claude", "claude_desktop_config.json"),
|
|
126
|
+
projectDetectPaths: [],
|
|
127
|
+
// Global only - no project support
|
|
126
128
|
configKey: "mcpServers",
|
|
127
129
|
format: "json",
|
|
128
|
-
|
|
130
|
+
supportedTransports: ["stdio", "http", "sse"],
|
|
131
|
+
detectGlobalInstall: async () => {
|
|
129
132
|
return existsSync(join(appSupport, "Claude"));
|
|
130
133
|
}
|
|
131
134
|
},
|
|
@@ -136,9 +139,12 @@ var agents = {
|
|
|
136
139
|
process.env.CODEX_HOME || join(home, ".codex"),
|
|
137
140
|
"config.toml"
|
|
138
141
|
),
|
|
142
|
+
projectDetectPaths: [],
|
|
143
|
+
// Global only - no project support
|
|
139
144
|
configKey: "mcp_servers",
|
|
140
145
|
format: "toml",
|
|
141
|
-
|
|
146
|
+
supportedTransports: ["stdio", "http", "sse"],
|
|
147
|
+
detectGlobalInstall: async () => {
|
|
142
148
|
return existsSync(join(home, ".codex"));
|
|
143
149
|
},
|
|
144
150
|
transformConfig: transformCodexConfig
|
|
@@ -148,9 +154,11 @@ var agents = {
|
|
|
148
154
|
displayName: "Cursor",
|
|
149
155
|
configPath: join(home, ".cursor", "mcp.json"),
|
|
150
156
|
localConfigPath: ".cursor/mcp.json",
|
|
157
|
+
projectDetectPaths: [".cursor"],
|
|
151
158
|
configKey: "mcpServers",
|
|
152
159
|
format: "json",
|
|
153
|
-
|
|
160
|
+
supportedTransports: ["stdio", "http", "sse"],
|
|
161
|
+
detectGlobalInstall: async () => {
|
|
154
162
|
return existsSync(join(home, ".cursor"));
|
|
155
163
|
}
|
|
156
164
|
},
|
|
@@ -159,9 +167,11 @@ var agents = {
|
|
|
159
167
|
displayName: "Gemini CLI",
|
|
160
168
|
configPath: join(home, ".gemini", "settings.json"),
|
|
161
169
|
localConfigPath: ".gemini/settings.json",
|
|
170
|
+
projectDetectPaths: [".gemini"],
|
|
162
171
|
configKey: "mcpServers",
|
|
163
172
|
format: "json",
|
|
164
|
-
|
|
173
|
+
supportedTransports: ["stdio", "http", "sse"],
|
|
174
|
+
detectGlobalInstall: async () => {
|
|
165
175
|
return existsSync(join(home, ".gemini"));
|
|
166
176
|
}
|
|
167
177
|
},
|
|
@@ -169,9 +179,13 @@ var agents = {
|
|
|
169
179
|
name: "goose",
|
|
170
180
|
displayName: "Goose",
|
|
171
181
|
configPath: join(home, ".config", "goose", "config.yaml"),
|
|
182
|
+
localConfigPath: ".goose/config.yaml",
|
|
183
|
+
projectDetectPaths: [".goose"],
|
|
172
184
|
configKey: "extensions",
|
|
173
185
|
format: "yaml",
|
|
174
|
-
|
|
186
|
+
supportedTransports: ["stdio", "http"],
|
|
187
|
+
// Goose does not support SSE
|
|
188
|
+
detectGlobalInstall: async () => {
|
|
175
189
|
return existsSync(join(home, ".config", "goose"));
|
|
176
190
|
},
|
|
177
191
|
transformConfig: transformGooseConfig
|
|
@@ -181,9 +195,11 @@ var agents = {
|
|
|
181
195
|
displayName: "OpenCode",
|
|
182
196
|
configPath: join(home, ".config", "opencode", "opencode.json"),
|
|
183
197
|
localConfigPath: ".opencode.json",
|
|
198
|
+
projectDetectPaths: [".opencode.json", ".opencode"],
|
|
184
199
|
configKey: "mcp",
|
|
185
200
|
format: "json",
|
|
186
|
-
|
|
201
|
+
supportedTransports: ["stdio", "http", "sse"],
|
|
202
|
+
detectGlobalInstall: async () => {
|
|
187
203
|
return existsSync(join(home, ".config", "opencode"));
|
|
188
204
|
},
|
|
189
205
|
transformConfig: transformOpenCodeConfig
|
|
@@ -193,9 +209,11 @@ var agents = {
|
|
|
193
209
|
displayName: "VS Code",
|
|
194
210
|
configPath: join(vscodePath, "mcp.json"),
|
|
195
211
|
localConfigPath: ".vscode/mcp.json",
|
|
212
|
+
projectDetectPaths: [".vscode"],
|
|
196
213
|
configKey: "mcpServers",
|
|
197
214
|
format: "json",
|
|
198
|
-
|
|
215
|
+
supportedTransports: ["stdio", "http", "sse"],
|
|
216
|
+
detectGlobalInstall: async () => {
|
|
199
217
|
return existsSync(vscodePath);
|
|
200
218
|
}
|
|
201
219
|
},
|
|
@@ -207,9 +225,12 @@ var agents = {
|
|
|
207
225
|
"Zed",
|
|
208
226
|
"settings.json"
|
|
209
227
|
) : join(home, ".config", "zed", "settings.json"),
|
|
228
|
+
projectDetectPaths: [],
|
|
229
|
+
// Global only - no project support
|
|
210
230
|
configKey: "context_servers",
|
|
211
231
|
format: "json",
|
|
212
|
-
|
|
232
|
+
supportedTransports: ["stdio", "http", "sse"],
|
|
233
|
+
detectGlobalInstall: async () => {
|
|
213
234
|
return existsSync(join(home, ".config", "zed")) || existsSync(join(process.env.APPDATA || "", "Zed"));
|
|
214
235
|
},
|
|
215
236
|
transformConfig: transformZedConfig
|
|
@@ -218,14 +239,49 @@ var agents = {
|
|
|
218
239
|
function getAgentTypes() {
|
|
219
240
|
return Object.keys(agents);
|
|
220
241
|
}
|
|
221
|
-
|
|
222
|
-
|
|
242
|
+
function supportsProjectConfig(agentType) {
|
|
243
|
+
return agents[agentType].localConfigPath !== void 0;
|
|
244
|
+
}
|
|
245
|
+
function getProjectCapableAgents() {
|
|
246
|
+
return Object.keys(agents).filter(
|
|
247
|
+
(type) => supportsProjectConfig(type)
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
function detectProjectAgents(cwd) {
|
|
251
|
+
const dir = cwd || process.cwd();
|
|
252
|
+
const detected = [];
|
|
223
253
|
for (const [type, config] of Object.entries(agents)) {
|
|
224
|
-
if (
|
|
225
|
-
|
|
254
|
+
if (!config.localConfigPath) continue;
|
|
255
|
+
for (const detectPath of config.projectDetectPaths) {
|
|
256
|
+
if (existsSync(join(dir, detectPath))) {
|
|
257
|
+
detected.push(type);
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
226
260
|
}
|
|
227
261
|
}
|
|
228
|
-
return
|
|
262
|
+
return detected;
|
|
263
|
+
}
|
|
264
|
+
async function detectGlobalOnlyAgents() {
|
|
265
|
+
const detected = [];
|
|
266
|
+
for (const [type, config] of Object.entries(agents)) {
|
|
267
|
+
if (config.localConfigPath) continue;
|
|
268
|
+
if (await config.detectGlobalInstall()) {
|
|
269
|
+
detected.push(type);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return detected;
|
|
273
|
+
}
|
|
274
|
+
async function detectAllGlobalAgents() {
|
|
275
|
+
const detected = [];
|
|
276
|
+
for (const [type, config] of Object.entries(agents)) {
|
|
277
|
+
if (await config.detectGlobalInstall()) {
|
|
278
|
+
detected.push(type);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return detected;
|
|
282
|
+
}
|
|
283
|
+
function isTransportSupported(agentType, transport) {
|
|
284
|
+
return agents[agentType].supportedTransports.includes(transport);
|
|
229
285
|
}
|
|
230
286
|
|
|
231
287
|
// src/source-parser.ts
|
|
@@ -532,10 +588,10 @@ function buildConfigWithKey(configKey, serverName, serverConfig) {
|
|
|
532
588
|
}
|
|
533
589
|
|
|
534
590
|
// src/installer.ts
|
|
535
|
-
function buildServerConfig(parsed) {
|
|
591
|
+
function buildServerConfig(parsed, options = {}) {
|
|
536
592
|
if (parsed.type === "remote") {
|
|
537
593
|
return {
|
|
538
|
-
type: "http",
|
|
594
|
+
type: options.transport ?? "http",
|
|
539
595
|
url: parsed.value
|
|
540
596
|
};
|
|
541
597
|
}
|
|
@@ -590,24 +646,26 @@ function installServerForAgent(serverName, serverConfig, agentType, options = {}
|
|
|
590
646
|
function installServer(serverName, serverConfig, agentTypes, options = {}) {
|
|
591
647
|
const results = /* @__PURE__ */ new Map();
|
|
592
648
|
for (const agentType of agentTypes) {
|
|
649
|
+
const routing = options.routing?.get(agentType);
|
|
650
|
+
const installOptions = {
|
|
651
|
+
local: routing === "local",
|
|
652
|
+
cwd: options.cwd
|
|
653
|
+
};
|
|
593
654
|
const result = installServerForAgent(
|
|
594
655
|
serverName,
|
|
595
656
|
serverConfig,
|
|
596
657
|
agentType,
|
|
597
|
-
|
|
658
|
+
installOptions
|
|
598
659
|
);
|
|
599
660
|
results.set(agentType, result);
|
|
600
661
|
}
|
|
601
662
|
return results;
|
|
602
663
|
}
|
|
603
|
-
function getAgentsWithLocalSupport() {
|
|
604
|
-
return Object.entries(agents).filter(([_, config]) => config.localConfigPath !== void 0).map(([type, _]) => type);
|
|
605
|
-
}
|
|
606
664
|
|
|
607
665
|
// package.json
|
|
608
666
|
var package_default = {
|
|
609
667
|
name: "add-mcp",
|
|
610
|
-
version: "0.
|
|
668
|
+
version: "0.2.0",
|
|
611
669
|
description: "Install MCP servers onto coding agents (Claude Code, Cursor, VS Code, OpenCode, Codex)",
|
|
612
670
|
author: "Andre Landgraf <andre@neon.tech>",
|
|
613
671
|
license: "Apache-2.0",
|
|
@@ -623,8 +681,8 @@ var package_default = {
|
|
|
623
681
|
fmt: "prettier --write .",
|
|
624
682
|
build: "tsup src/index.ts --format esm --dts --clean",
|
|
625
683
|
dev: "tsx src/index.ts",
|
|
626
|
-
test: "tsx tests/source-parser.test.ts && tsx tests/installer.test.ts && tsx tests/e2e/install.test.ts",
|
|
627
|
-
"test:unit": "tsx tests/source-parser.test.ts && tsx tests/installer.test.ts",
|
|
684
|
+
test: "tsx tests/source-parser.test.ts && tsx tests/agents.test.ts && tsx tests/installer.test.ts && tsx tests/e2e/install.test.ts",
|
|
685
|
+
"test:unit": "tsx tests/source-parser.test.ts && tsx tests/agents.test.ts && tsx tests/installer.test.ts",
|
|
628
686
|
"test:e2e": "tsx tests/e2e/install.test.ts",
|
|
629
687
|
typecheck: "tsc --noEmit",
|
|
630
688
|
prepublishOnly: "npm run build"
|
|
@@ -692,40 +750,27 @@ function resolveAgentType(input) {
|
|
|
692
750
|
}
|
|
693
751
|
return null;
|
|
694
752
|
}
|
|
753
|
+
function collect(value, previous) {
|
|
754
|
+
return previous.concat([value]);
|
|
755
|
+
}
|
|
695
756
|
program.name("add-mcp").description(
|
|
696
757
|
"Install MCP servers onto coding agents (Claude Code, Cursor, VS Code, OpenCode, Codex)"
|
|
697
758
|
).version(version).argument("[target]", "MCP server URL (remote) or package name (local stdio)").option(
|
|
698
759
|
"-g, --global",
|
|
699
760
|
"Install globally (user-level) instead of project-level"
|
|
700
|
-
).option("-a, --agent <
|
|
761
|
+
).option("-a, --agent <agent>", "Specify agents to install to", collect, []).option(
|
|
701
762
|
"-n, --name <name>",
|
|
702
763
|
"Server name (auto-inferred from target if not provided)"
|
|
703
|
-
).option(
|
|
764
|
+
).option(
|
|
765
|
+
"-t, --transport <type>",
|
|
766
|
+
"Transport type for remote servers (http, sse)"
|
|
767
|
+
).option("--type <type>", "Alias for --transport").option("-y, --yes", "Skip confirmation prompts").option("--all", "Install to all agents").action(async (target, options) => {
|
|
704
768
|
await main(target, options);
|
|
705
769
|
});
|
|
706
770
|
program.parse();
|
|
707
771
|
async function main(target, options) {
|
|
708
|
-
if (options.all) {
|
|
709
|
-
options.yes = true;
|
|
710
|
-
options.global = true;
|
|
711
|
-
}
|
|
712
772
|
console.log();
|
|
713
773
|
p.intro(chalk.bgCyan.black(" add-mcp "));
|
|
714
|
-
if (options.list) {
|
|
715
|
-
console.log();
|
|
716
|
-
p.log.step(chalk.bold("Supported Agents"));
|
|
717
|
-
const allTypes = getAgentTypes();
|
|
718
|
-
for (const type of allTypes) {
|
|
719
|
-
const agent = agents[type];
|
|
720
|
-
const hasLocal = agent.localConfigPath ? chalk.dim(" (supports local)") : "";
|
|
721
|
-
p.log.message(` ${chalk.cyan(type)} - ${agent.displayName}${hasLocal}`);
|
|
722
|
-
}
|
|
723
|
-
console.log();
|
|
724
|
-
p.log.info(chalk.dim("Aliases: github-copilot \u2192 vscode"));
|
|
725
|
-
console.log();
|
|
726
|
-
p.outro("Use -a/--agent to specify agents");
|
|
727
|
-
return;
|
|
728
|
-
}
|
|
729
774
|
if (!target) {
|
|
730
775
|
p.log.error("Missing required argument: target");
|
|
731
776
|
console.log();
|
|
@@ -752,9 +797,27 @@ async function main(target, options) {
|
|
|
752
797
|
spinner2.stop(`Source: ${chalk.cyan(parsed.value)} (${sourceType})`);
|
|
753
798
|
const serverName = options.name || parsed.inferredName;
|
|
754
799
|
p.log.info(`Server name: ${chalk.cyan(serverName)}`);
|
|
755
|
-
const
|
|
800
|
+
const transportValue = options.transport || options.type;
|
|
801
|
+
let resolvedTransport;
|
|
802
|
+
if (transportValue) {
|
|
803
|
+
const validTransports = ["http", "sse"];
|
|
804
|
+
if (!validTransports.includes(transportValue)) {
|
|
805
|
+
p.log.error(
|
|
806
|
+
`Invalid transport: ${transportValue}. Valid options: ${validTransports.join(", ")}`
|
|
807
|
+
);
|
|
808
|
+
process.exit(1);
|
|
809
|
+
}
|
|
810
|
+
resolvedTransport = transportValue;
|
|
811
|
+
if (!isRemoteSource(parsed)) {
|
|
812
|
+
p.log.warn("--transport is only used for remote URLs, ignoring");
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
const serverConfig = buildServerConfig(parsed, {
|
|
816
|
+
transport: resolvedTransport
|
|
817
|
+
});
|
|
756
818
|
let targetAgents;
|
|
757
819
|
const allAgentTypes = getAgentTypes();
|
|
820
|
+
let agentRouting = /* @__PURE__ */ new Map();
|
|
758
821
|
if (options.agent && options.agent.length > 0) {
|
|
759
822
|
const resolved = [];
|
|
760
823
|
const invalid = [];
|
|
@@ -776,16 +839,40 @@ async function main(target, options) {
|
|
|
776
839
|
targetAgents = allAgentTypes;
|
|
777
840
|
p.log.info(`Installing to all ${targetAgents.length} agents`);
|
|
778
841
|
} else {
|
|
779
|
-
spinner2.start("Detecting
|
|
780
|
-
|
|
842
|
+
spinner2.start("Detecting agents...");
|
|
843
|
+
let detectedAgents;
|
|
844
|
+
if (options.global) {
|
|
845
|
+
detectedAgents = await detectAllGlobalAgents();
|
|
846
|
+
} else {
|
|
847
|
+
const projectAgents = detectProjectAgents();
|
|
848
|
+
const globalOnlyAgents = await detectGlobalOnlyAgents();
|
|
849
|
+
detectedAgents = [...projectAgents, ...globalOnlyAgents];
|
|
850
|
+
for (const agent of projectAgents) {
|
|
851
|
+
agentRouting.set(agent, "local");
|
|
852
|
+
}
|
|
853
|
+
for (const agent of globalOnlyAgents) {
|
|
854
|
+
agentRouting.set(agent, "global");
|
|
855
|
+
}
|
|
856
|
+
}
|
|
781
857
|
spinner2.stop(
|
|
782
|
-
`Detected ${
|
|
858
|
+
`Detected ${detectedAgents.length} agent${detectedAgents.length !== 1 ? "s" : ""}`
|
|
783
859
|
);
|
|
784
|
-
if (
|
|
860
|
+
if (detectedAgents.length === 0) {
|
|
785
861
|
if (options.yes) {
|
|
786
|
-
targetAgents =
|
|
787
|
-
|
|
862
|
+
targetAgents = getProjectCapableAgents();
|
|
863
|
+
for (const agent of targetAgents) {
|
|
864
|
+
agentRouting.set(agent, "local");
|
|
865
|
+
}
|
|
866
|
+
p.log.info(
|
|
867
|
+
`Installing to ${targetAgents.length} project-capable agents (none detected)`
|
|
868
|
+
);
|
|
788
869
|
} else {
|
|
870
|
+
if (!options.global) {
|
|
871
|
+
p.log.error(
|
|
872
|
+
"No agents detected in this project. Use --global to install globally, or run in a project with agent config files."
|
|
873
|
+
);
|
|
874
|
+
process.exit(1);
|
|
875
|
+
}
|
|
789
876
|
p.log.warn(
|
|
790
877
|
"No coding agents detected. You can still install MCP servers."
|
|
791
878
|
);
|
|
@@ -804,21 +891,25 @@ async function main(target, options) {
|
|
|
804
891
|
}
|
|
805
892
|
targetAgents = selected;
|
|
806
893
|
}
|
|
807
|
-
} else if (
|
|
808
|
-
targetAgents =
|
|
809
|
-
const agentNames =
|
|
894
|
+
} else if (detectedAgents.length === 1 || options.yes) {
|
|
895
|
+
targetAgents = detectedAgents;
|
|
896
|
+
const agentNames = detectedAgents.map((a) => chalk.cyan(agents[a].displayName)).join(", ");
|
|
810
897
|
p.log.info(`Installing to: ${agentNames}`);
|
|
811
898
|
} else {
|
|
812
|
-
const agentChoices =
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
899
|
+
const agentChoices = detectedAgents.map((a) => {
|
|
900
|
+
const routing = agentRouting.get(a);
|
|
901
|
+
const hint = routing === "local" ? "project" : routing === "global" ? "global" : shortenPath(agents[a].configPath);
|
|
902
|
+
return {
|
|
903
|
+
value: a,
|
|
904
|
+
label: agents[a].displayName,
|
|
905
|
+
hint
|
|
906
|
+
};
|
|
907
|
+
});
|
|
817
908
|
const selected = await p.multiselect({
|
|
818
909
|
message: "Select agents to install to",
|
|
819
910
|
options: agentChoices,
|
|
820
911
|
required: true,
|
|
821
|
-
initialValues:
|
|
912
|
+
initialValues: detectedAgents
|
|
822
913
|
});
|
|
823
914
|
if (p.isCancel(selected)) {
|
|
824
915
|
p.cancel("Installation cancelled");
|
|
@@ -827,47 +918,104 @@ async function main(target, options) {
|
|
|
827
918
|
targetAgents = selected;
|
|
828
919
|
}
|
|
829
920
|
}
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
921
|
+
const requiredTransport = isRemoteSource(parsed) ? resolvedTransport ?? "http" : "stdio";
|
|
922
|
+
const unsupportedAgents = targetAgents.filter(
|
|
923
|
+
(a) => !isTransportSupported(a, requiredTransport)
|
|
924
|
+
);
|
|
925
|
+
if (unsupportedAgents.length > 0) {
|
|
926
|
+
const unsupportedNames = unsupportedAgents.map((a) => agents[a].displayName).join(", ");
|
|
927
|
+
if (options.all) {
|
|
928
|
+
p.log.warn(
|
|
929
|
+
`Skipping agents that don't support ${requiredTransport} transport: ${unsupportedNames}`
|
|
930
|
+
);
|
|
931
|
+
targetAgents = targetAgents.filter(
|
|
932
|
+
(a) => isTransportSupported(a, requiredTransport)
|
|
933
|
+
);
|
|
934
|
+
if (targetAgents.length === 0) {
|
|
935
|
+
p.log.error("No agents support this transport type");
|
|
936
|
+
process.exit(1);
|
|
937
|
+
}
|
|
938
|
+
} else {
|
|
939
|
+
p.log.error(
|
|
940
|
+
`The following agents don't support ${requiredTransport} transport: ${unsupportedNames}`
|
|
941
|
+
);
|
|
942
|
+
process.exit(1);
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
const hasSmartRouting = agentRouting.size > 0;
|
|
946
|
+
if (options.global) {
|
|
947
|
+
for (const agent of targetAgents) {
|
|
948
|
+
agentRouting.set(agent, "global");
|
|
949
|
+
}
|
|
950
|
+
} else if (!hasSmartRouting) {
|
|
833
951
|
const selectedWithLocal = targetAgents.filter(
|
|
834
|
-
(a) =>
|
|
952
|
+
(a) => supportsProjectConfig(a)
|
|
835
953
|
);
|
|
954
|
+
const globalOnlySelected = targetAgents.filter(
|
|
955
|
+
(a) => !supportsProjectConfig(a)
|
|
956
|
+
);
|
|
957
|
+
for (const agent of globalOnlySelected) {
|
|
958
|
+
agentRouting.set(agent, "global");
|
|
959
|
+
}
|
|
836
960
|
if (selectedWithLocal.length > 0) {
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
961
|
+
let installLocally = true;
|
|
962
|
+
if (!options.yes) {
|
|
963
|
+
const scope = await p.select({
|
|
964
|
+
message: "Installation scope",
|
|
965
|
+
options: [
|
|
966
|
+
{
|
|
967
|
+
value: true,
|
|
968
|
+
label: "Project",
|
|
969
|
+
hint: "Install in current directory (committed with your project)"
|
|
970
|
+
},
|
|
971
|
+
{
|
|
972
|
+
value: false,
|
|
973
|
+
label: "Global",
|
|
974
|
+
hint: "Install in home directory (available across all projects)"
|
|
975
|
+
}
|
|
976
|
+
]
|
|
977
|
+
});
|
|
978
|
+
if (p.isCancel(scope)) {
|
|
979
|
+
p.cancel("Installation cancelled");
|
|
980
|
+
process.exit(0);
|
|
981
|
+
}
|
|
982
|
+
installLocally = scope;
|
|
983
|
+
}
|
|
984
|
+
for (const agent of selectedWithLocal) {
|
|
985
|
+
agentRouting.set(agent, installLocally ? "local" : "global");
|
|
855
986
|
}
|
|
856
|
-
installGlobally = scope;
|
|
857
987
|
} else {
|
|
858
|
-
installGlobally = true;
|
|
859
988
|
p.log.info("Selected agents only support global installation");
|
|
860
989
|
}
|
|
861
990
|
}
|
|
862
991
|
const summaryLines = [];
|
|
863
992
|
summaryLines.push(`${chalk.cyan("Server:")} ${serverName}`);
|
|
864
993
|
summaryLines.push(`${chalk.cyan("Type:")} ${sourceType}`);
|
|
865
|
-
|
|
866
|
-
|
|
994
|
+
const localAgents = targetAgents.filter(
|
|
995
|
+
(a) => agentRouting.get(a) === "local"
|
|
867
996
|
);
|
|
868
|
-
|
|
869
|
-
|
|
997
|
+
const globalAgents = targetAgents.filter(
|
|
998
|
+
(a) => agentRouting.get(a) === "global"
|
|
870
999
|
);
|
|
1000
|
+
if (localAgents.length > 0 && globalAgents.length > 0) {
|
|
1001
|
+
summaryLines.push(`${chalk.cyan("Scope:")} Mixed (project + global)`);
|
|
1002
|
+
summaryLines.push(
|
|
1003
|
+
`${chalk.cyan(" Project:")} ${localAgents.map((a) => agents[a].displayName).join(", ")}`
|
|
1004
|
+
);
|
|
1005
|
+
summaryLines.push(
|
|
1006
|
+
`${chalk.cyan(" Global:")} ${globalAgents.map((a) => agents[a].displayName).join(", ")}`
|
|
1007
|
+
);
|
|
1008
|
+
} else if (localAgents.length > 0) {
|
|
1009
|
+
summaryLines.push(`${chalk.cyan("Scope:")} Project`);
|
|
1010
|
+
summaryLines.push(
|
|
1011
|
+
`${chalk.cyan("Agents:")} ${localAgents.map((a) => agents[a].displayName).join(", ")}`
|
|
1012
|
+
);
|
|
1013
|
+
} else {
|
|
1014
|
+
summaryLines.push(`${chalk.cyan("Scope:")} Global`);
|
|
1015
|
+
summaryLines.push(
|
|
1016
|
+
`${chalk.cyan("Agents:")} ${globalAgents.map((a) => agents[a].displayName).join(", ")}`
|
|
1017
|
+
);
|
|
1018
|
+
}
|
|
871
1019
|
console.log();
|
|
872
1020
|
p.note(summaryLines.join("\n"), "Installation Summary");
|
|
873
1021
|
if (!options.yes) {
|
|
@@ -881,7 +1029,7 @@ async function main(target, options) {
|
|
|
881
1029
|
}
|
|
882
1030
|
spinner2.start("Installing MCP server...");
|
|
883
1031
|
const results = installServer(serverName, serverConfig, targetAgents, {
|
|
884
|
-
|
|
1032
|
+
routing: agentRouting
|
|
885
1033
|
});
|
|
886
1034
|
spinner2.stop("Installation complete");
|
|
887
1035
|
console.log();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "add-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Install MCP servers onto coding agents (Claude Code, Cursor, VS Code, OpenCode, Codex)",
|
|
5
5
|
"author": "Andre Landgraf <andre@neon.tech>",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
"fmt": "prettier --write .",
|
|
17
17
|
"build": "tsup src/index.ts --format esm --dts --clean",
|
|
18
18
|
"dev": "tsx src/index.ts",
|
|
19
|
-
"test": "tsx tests/source-parser.test.ts && tsx tests/installer.test.ts && tsx tests/e2e/install.test.ts",
|
|
20
|
-
"test:unit": "tsx tests/source-parser.test.ts && tsx tests/installer.test.ts",
|
|
19
|
+
"test": "tsx tests/source-parser.test.ts && tsx tests/agents.test.ts && tsx tests/installer.test.ts && tsx tests/e2e/install.test.ts",
|
|
20
|
+
"test:unit": "tsx tests/source-parser.test.ts && tsx tests/agents.test.ts && tsx tests/installer.test.ts",
|
|
21
21
|
"test:e2e": "tsx tests/e2e/install.test.ts",
|
|
22
22
|
"typecheck": "tsc --noEmit",
|
|
23
23
|
"prepublishOnly": "npm run build"
|