| name | manage-mcp |
| description | Add, remove, or debug MCP servers in an mcporter-serve setup. Use when wiring a new MCP tool, removing one, checking status, or debugging why tools aren't showing.
Triggers: - "add X as an MCP" - "register X in mcporter" - "wire X as a tool" - "remove X from mcporter" - "why isn't X showing as a tool" - "debug MCP" - "check MCP status"
|
Manage MCPs
Architecture
Server definitions live only in ~/.mcporter/mcporter.json and run under the daemon,
bridged by mcporter serve on :4748. Your client config (e.g. ~/.claude.json) holds only
routing entries that point at the bridge.
With per-server endpoints, each server is its own client entry pointing at
http://127.0.0.1:4748/mcp/<server>, with unprefixed tool names mcp__<server>__<tool>. The
alternative is one aggregate entry (/mcp, tools mcp__mcporter__<server>__<tool>). See
references/per-server-serve.md.
Client (Claude, Codex, Cursor, ...)
├── <server-a>, <server-b>, ... → :4748/mcp/<server> (per-server routing entries)
└── mcporter serve (launchd: com.mcporter.serve)
└── daemon
└── <server-a>, <server-b>, ...
Adding a server
1. Find the right command
npx -y my-package@latest --help
npx -y my-package@latest mcp
A missing subcommand causes "Connection closed": the binary starts but exits immediately.
2. Register in mcporter.json
You can use mcporter config add, or edit the file directly.
npm/npx package with API key:
python3 - << 'EOF'
import json, os
path = os.path.expanduser('~/.mcporter/mcporter.json')
d = json.load(open(path))
d.setdefault('mcpServers', {})['my-server'] = {
'description': 'What it does',
'command': 'npx',
'args': ['-y', 'my-package@latest', 'mcp'],
'env': {'API_KEY': '...'},
'lifecycle': 'keep-alive'
}
json.dump(d, open(path, 'w'), indent=2)
print('registered')
EOF
HTTP/remote endpoint:
d['mcpServers']['my-server'] = {
'description': 'What it does',
'url': 'https://api.example.com/mcp',
'headers': {'Authorization': 'Bearer TOKEN'}
}
Shell-script wrapper (for binaries needing env vars or a working dir): write a script that
exports what it needs and execs the binary, then register it with
"command": "/abs/path/to/wrapper.sh" and no env block.
Per-server mode also needs a matching client routing entry. See
references/per-server-serve.md.
3. Reload
mcporter daemon restart && sleep 2 && mcporter daemon status
launchctl kickstart -k "gui/$(id -u)/com.mcporter.serve"
curl -s http://127.0.0.1:4748/mcp | head -1
Then reload your client (Claude: /reload-plugins then /mcp).
4. Verify
mcporter list my-server
mcporter call my-server.tool_name key=value
Removing a server
python3 - << 'EOF'
import json, os
path = os.path.expanduser('~/.mcporter/mcporter.json')
d = json.load(open(path))
d.get('mcpServers', {}).pop('my-server', None)
json.dump(d, open(path, 'w'), indent=2)
print('removed')
EOF
In per-server mode, also remove the routing entry from your client config. Then run the
reload sequence above.
Debugging
| Symptom | Fix |
|---|
| "Connection closed" | Missing subcommand in args: run npx package --help |
| Server not in daemon status | mcporter daemon restart |
| "Server X not managed by daemon" | serve bridge stale: kickstart the launchd agent |
| Tools not in client after restart | Reload the client (Claude: /reload-plugins) |
| Port 4748 not responding | Check ~/Library/Logs/mcporter-serve.log: usually a PATH issue (node bin dir missing from the plist PATH) |
| Env vars missing | Use a shell-script wrapper and put vars in the script, not the JSON env block |
Key files
| File | Purpose |
|---|
~/.mcporter/mcporter.json | All server definitions |
~/.claude.json | Client routing entries (/mcp/<server>). Pointers only |
~/Library/LaunchAgents/com.mcporter.serve.plist | Keeps the serve bridge alive on port 4748 |
~/Library/Logs/mcporter-serve.log | Serve bridge logs |