A shell script that converts JSON MCP server configuration into MCP CLI commands for Claude Code or Sourcegraph Amp.
Status: ✅ Production Ready — 45+ tests passing (100% success rate)
This script simplifies adding Model Context Protocol (MCP) servers to your preferred CLI tool by accepting JSON configuration and generating the appropriate MCP command. It handles all three transport types: HTTP, SSE, and stdio. Supports both Claude Code (claude mcp add) and Sourcegraph Amp (amp mcp add).
The tool features:
- Full CLI Support — Generates commands for both Claude Code and Sourcegraph Amp with correct syntax differences
- Automatic Transport Inference — Detects
http,sse, orstdiofrom your configuration - Complete Validation — RFC 3986 URL validation, POSIX environment variable names, field type checking
- Header Format Translation — Automatically converts headers to CLI-specific format (colon-space for Claude, equals for Amp)
- Scope Management — Handles CLI-specific scope options for configuration storage
- Preference Persistence — Saves your CLI choice for subsequent uses
This script generates MCP server configuration commands that conform to official specifications for multiple CLIs. Each CLI has specific requirements and supported features.
- Source: Claude Code Documentation
- Version: Latest (updated Jan 2025)
- Transport Detection: Explicit via
--transportflag - Supported Transports: http, sse, stdio
- Scope Support: Yes (local, project, user)
- Header Format:
"Key: Value"(colon with space)
- Source: Amp Manual - MCP
- Version: Latest (updated Jan 2025)
- Transport Detection: Automatic from server headers (no flag)
- Supported Transports: http, sse, stdio
- Scope Support: No (uses global amp.mcpServers)
- Header Format:
"Key=Value"(equals, no space) - Additional Features: includeTools for tool filtering (stdio only)
- Specification: Model Context Protocol
- Version: 2025-11-25
| Feature | Claude Code | Amp | Notes |
|---|---|---|---|
| Transport: HTTP | ✅ Yes | ✅ Yes | Default for URL-based servers |
| Transport: SSE | ✅ Yes | ✅ Yes | Deprecated, use HTTP |
| Transport: Stdio | ✅ Yes | ✅ Yes | For local processes |
| Transport Flag | ✅ Required | ❌ Auto-detect | Claude requires explicit flag |
| Scope Support | ✅ Yes | ❌ No | Amp uses global location only |
| Scope Options | local, project, user | N/A | Control where config is stored |
| Header Format | "Key: Value" |
"Key=Value" |
Different separator syntax |
| includeTools | ❌ Not supported | ✅ Yes (stdio) | Filters which tools are exposed |
| Command Prefix | claude mcp add |
amp mcp add |
Different CLI commands |
- Headers: Claude uses colon format (
Authorization: token), Amp uses equals format (Authorization=token) - Scope: Claude supports it, Amp doesn't (global only)
- Transport Flag: Claude requires it, Amp auto-detects
- includeTools: Amp-specific for tool filtering
git clone https://github.com/jamiemills/mcp-commandline.git
cd mcp-commandline
chmod +x json-to-mcp-add.shThe script accepts JSON input via stdin or as a command argument.
Use the --cli option to specify your target platform:
# Generate Claude Code command (default if not specified or saved)
json-to-mcp-add.sh --cli claude '{"name":"myserver","url":"https://api.example.com"}'
# Output: claude mcp add --transport http myserver https://api.example.com
# Generate Sourcegraph Amp command
json-to-mcp-add.sh --cli amp '{"name":"myserver","url":"https://api.example.com"}'
# Output: amp mcp add myserver https://api.example.comPreference Persistence: Your CLI choice is automatically saved to ~/.config/mcp-commandline/config (or ~/.mcp-commandline-config as fallback). Once you specify a CLI preference, you don't need to include --cli on subsequent invocations—it will use your saved preference.
# First use - save preference
json-to-mcp-add.sh --cli amp '{"name":"server","url":"https://..."}'
# Subsequent uses - uses saved preference (amp)
json-to-mcp-add.sh '{"name":"server2","url":"https://..."}'
# Override saved preference
json-to-mcp-add.sh --cli claude '{"name":"server3","url":"https://..."}'# Via stdin
echo '{"name":"myserver","type":"http","url":"https://api.example.com"}' | json-to-mcp-add.sh
# Via argument
json-to-mcp-add.sh '{"name":"myserver","type":"http","url":"https://api.example.com"}'
# From file
json-to-mcp-add.sh < config.jsonBy default, the script prints the generated command. Use -x or --execute to run it directly:
json-to-mcp-add.sh -x < config.json
json-to-mcp-add.sh --cli amp -x < config.jsonWhen you configure an MCP server with this script, you're participating in a layered communication system. Understanding these layers helps you know what this tool does and what happens after:
Where and how to connect — The script validates your configuration and generates the appropriate MCP command (claude mcp add or amp mcp add) that tells your CLI tool:
- Which protocol to use (stdio, HTTP, or SSE)
- Where the server is located (command path or URL)
- How to authenticate (headers for HTTP/SSE, environment variables for stdio)
Establishing the connection — The CLI runtime (Claude Code or Amp) handles:
- TCP/TLS socket creation
- Process spawning (for stdio)
- HTTP/HTTPS connections (for remote servers)
Speaking MCP — Once connected, the CLI runtime performs:
- JSON-RPC 2.0 message exchange
InitializeRequesthandshake to negotiate capabilitiesInitializeResponsecontaining what the server offers
What the server actually does — After initialization, the server provides:
- Resources — Data sources and context documents
- Tools — Functions that Claude can call
- Prompts — Reusable message templates
- Sampling — Server-initiated LLM requests (advanced)
Example Flow:
Script: "Connect to https://api.example.com using HTTP"
↓
Runtime: Establishes HTTPS connection
↓
Runtime: Sends: {"jsonrpc": "2.0", "method": "initialize", ...}
↓
Server: Responds: {"result": {"capabilities": {"resources": {}, "tools": {}}}}
↓
Claude: Now has access to the server's resources and tools
The type field is optional and will be automatically inferred from the configuration structure if not provided. This makes configurations simpler and less verbose.
The transport type is determined by the presence of specific fields:
- If
commandis present andurlis absent →stdiotransport - If
urlis present andcommandis absent →httptransport - If both
commandandurlare present, or neither is present →typefield is required
# Inferred as stdio (has command, no url)
json-to-mcp-add.sh '{"name":"my-server","command":"npx","args":["mcp-server"]}'
# Inferred as http (has url, no command)
json-to-mcp-add.sh '{"name":"api-server","url":"https://api.example.com"}'
# Explicit type overrides inference (has both, but type is specified)
json-to-mcp-add.sh '{"name":"mixed","type":"http","command":"echo","url":"https://example.com"}'
# Requires explicit type (ambiguous: has both command and url)
json-to-mcp-add.sh '{"name":"ambiguous","command":"cmd","url":"https://example.com"}'
# Error: 'type' field is required. Provide 'type' explicitly...{
"name": "server-name", // (required) Unique server identifier
"type": "http|sse|stdio", // (optional) Transport type — inferred if omitted
"url": "https://...", // (required for http/sse)
"command": "/path/to/cmd", // (required for stdio)
"args": ["arg1", "arg2"], // (optional for stdio) Command arguments
"headers": { // (optional for http/sse) Auth headers
"Authorization": "Bearer token",
"X-Custom-Header": "value"
},
"env": { // (optional for stdio) Environment variables
"API_KEY": "secret",
"CACHE_DIR": "/tmp/cache"
},
"scope": "local|project|user" // (optional) Config scope (default: local)
}This format matches the mcpServers object from Claude Desktop's configuration:
{
"mcpServers": {
"server-name": {
"type": "http|sse|stdio", // (optional) Transport type — inferred if omitted
"url": "https://...", // (required for http/sse)
"command": "/path/to/cmd", // (required for stdio)
"args": ["arg1", "arg2"], // (optional for stdio)
"headers": { // (optional for http/sse)
"Authorization": "Bearer token"
},
"env": { // (optional for stdio)
"API_KEY": "secret"
}
}
}
}The script automatically detects which format is being used and handles both transparently.
local– User-level configuration stored in~/.claude.json(default) — applies across all projects for the current userproject– Repository-level configuration stored in.mcp.jsonat repository root (shared with team)user– Global user configuration (same aslocal, for compatibility)
Extract servers from a Claude Desktop config file for Claude Code:
json-to-mcp-add.sh --cli claude < ~/.claude/config.jsonOutput:
claude mcp add --transport http notion https://mcp.notion.com/mcp --header "Authorization: Bearer token"
claude mcp add --transport stdio airtable -- npx -y airtable-mcp-server
Or for Sourcegraph Amp:
json-to-mcp-add.sh --cli amp < ~/.claude/config.jsonOutput:
amp mcp add notion https://mcp.notion.com/mcp --header "Authorization=Bearer token"
amp mcp add airtable stdio npx -y airtable-mcp-server
Note the differences:
- Amp omits
--transportflag (auto-detected) - Amp uses
=in headers instead of: - Amp omits
--separator for stdio
json-to-mcp-add.sh --cli claude << 'EOF'
{
"name": "notion",
"type": "http",
"url": "https://mcp.notion.com/mcp",
"headers": {
"Authorization": "Bearer sk_live_..."
},
"scope": "project"
}
EOFOutput:
claude mcp add --transport http notion https://mcp.notion.com/mcp --header "Authorization: Bearer sk_live_..." --scope project
json-to-mcp-add.sh --cli amp << 'EOF'
{
"name": "notion",
"type": "http",
"url": "https://mcp.notion.com/mcp",
"headers": {
"Authorization": "Bearer sk_live_..."
}
}
EOFOutput:
amp mcp add --transport http notion https://mcp.notion.com/mcp --header "Authorization: Bearer sk_live_..."
json-to-mcp-add.sh --cli claude << 'EOF'
{
"name": "airtable",
"type": "stdio",
"command": "npx",
"args": ["-y", "airtable-mcp-server"],
"env": {
"AIRTABLE_API_KEY": "pat_..."
}
}
EOFOutput:
claude mcp add --transport stdio airtable --env AIRTABLE_API_KEY="pat_..." -- npx -y airtable-mcp-server
Or for Amp:
json-to-mcp-add.sh --cli amp << 'EOF'
{
"name": "postgres",
"type": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres"],
"env": {
"PGUSER": "admin"
}
}
EOFOutput:
amp mcp add postgres stdio npx -y @modelcontextprotocol/server-postgres PGUSER="admin"
Note: Amp places environment variables after the command/args (no --env prefix, no -- separator).
Since the configuration has url and no command, the type is automatically inferred as http:
json-to-mcp-add.sh << 'EOF'
{
"name": "github",
"url": "https://mcp.github.com/api",
"headers": {
"Authorization": "Bearer ghp_..."
}
}
EOFOutput:
claude mcp add --transport http github https://mcp.github.com/api --header "Authorization: Bearer ghp_..."
Since the configuration has command and no url, the type is automatically inferred as stdio:
json-to-mcp-add.sh << 'EOF'
{
"name": "local-server",
"command": "node",
"args": ["server.js"],
"env": {
"PORT": "3000"
}
}
EOFOutput:
claude mcp add --transport stdio local-server -- node server.js PORT="3000"
Note: Environment variables are placed after the command and arguments.
json-to-mcp-add.sh '{"name":"asana","type":"sse","url":"https://mcp.asana.com/sse"}'Output:
claude mcp add --transport sse asana https://mcp.asana.com/sse
The script validates all input against the official MCP server configuration schema as defined in the MCP specification and Claude Code documentation.
Schema Sources:
- MCP Specification 2025-11-25
- Claude Code Configuration Documentation
- POSIX Shell Specification (for environment variable naming)
typemust be one of:http,sse,stdiohttp: Remote HTTP/HTTPS endpoint (recommended)sse: Remote Server-Sent Events endpoint (deprecated, use http)stdio: Local process via standard input/output
http/sserequireurlfieldstdiorequirescommandfield
headersmust be an object (key-value pairs) — used for HTTP/SSE authenticationenvmust be an object with valid environment variable names — used for stdio environmentargsmust be an array of strings — used for stdio command arguments
- Server names: Alphanumeric characters, hyphens, and underscores only
- Pattern:
[a-zA-Z0-9_-]+ - Used as configuration object keys
- Pattern:
- URLs: Must be valid HTTP(S) URLs per RFC 3986 specification
- Format:
http[s]://host[:port][/path][?query][#fragment] - Host can be: domain name, IPv4 address, IPv6 address (in brackets), or localhost
- Port is optional (decimal digits 1-5)
- Path, query parameters, and fragments are optional
- Domain labels must start and end with alphanumeric, can contain hyphens
- Examples:
https://api.example.com:3000/path?key=value#section,https://192.168.1.1:8080 - Required for http and sse transports
- Format:
- Scope: One of
local,project,userlocal: User-level configuration stored in~/.claude.json(default)project: Repository-level configuration stored in.mcp.jsonat repository root (shared with team)user: Alias forlocal(for compatibility)
- Environment variable names: POSIX shell variable naming convention
- Pattern:
[a-zA-Z_][a-zA-Z0-9_]* - Must start with letter or underscore
- Can contain letters, digits, and underscores
- Pattern:
- All required fields must be present
- Type-specific required fields are validated (url for http/sse, command for stdio)
- Field types are strictly validated (objects must be objects, arrays must be arrays)
- No extra fields are rejected — the script only uses defined schema fields
- Invalid JSON input is rejected with clear error messages
The script validates all inputs and provides clear, actionable error messages:
$ json-to-mcp-add.sh '{"name":"test"}'
Error: 'type' field is required
$ json-to-mcp-add.sh '{"name":"test","type":"http"}'
Error: 'url' field is required for http transport
$ json-to-mcp-add.sh '{"name":"test","type":"http","url":"invalid-url"}'
Error: Invalid URL format for 'invalid-url'. Must be valid HTTP(S) URL
$ json-to-mcp-add.sh '{"name":"invalid.name","type":"http","url":"https://api.example.com"}'
Error: Server name 'invalid.name' is invalid. Must contain only alphanumeric characters, hyphens, and underscores
$ json-to-mcp-add.sh '{"name":"test","type":"http","url":"https://api.example.com","scope":"invalid"}'
Error: 'scope' must be one of: local, project, user. Got: 'invalid'
$ echo '{"name":"test","type":"stdio","command":"npx","env":{"2INVALID":"val"}}' | json-to-mcp-add.sh
Error: Invalid environment variable name '2INVALID'. Must start with letter or underscore, contain only alphanumeric characters and underscoresOnce you run the generated MCP command (for either Claude Code or Amp), the CLI performs the following sequence:
The server configuration is saved to the specified scope:
local→~/.claude.json(user-level, default) — available across all projectsproject→.mcp.json(repository root) — team-shared configurationuser→~/.claude.json(same aslocal, for compatibility)
When the CLI starts or you interact with the server, it:
- Connects using the configured transport (stdio command, HTTP URL, or SSE endpoint)
- Sends an
InitializeRequestcontaining:- Protocol version requirement
- Implementation name and version
- Requested capabilities (resources, tools, prompts, sampling, etc.)
The server responds with InitializeResponse containing:
- Protocol version agreement
- Available resources — Data sources Claude can access
- Available tools — Functions Claude can invoke
- Available prompts — Message templates Claude can use
- Server capabilities — What advanced features the server supports
Once initialized, the CLI can:
- Request resources from the server for context
- Call tools provided by the server
- Use prompts to structure queries
- For advanced servers: allow server to initiate sampling requests
# Configuration (what this script does)
json-to-mcp-add.sh << 'EOF'
{
"name": "github",
"url": "https://mcp.github.com",
"headers": {"Authorization": "Bearer ghp_..."}
}
EOF
# Output: claude mcp add --transport http github https://mcp.github.com --header "Authorization: Bearer ghp_..."
# What happens after (what the CLI does)
# 1. Stores configuration in ~/.claude.json (user-level, available across projects)
# 2. On next use, connects to https://mcp.github.com
# 3. Sends InitializeRequest negotiating capabilities
# 4. Receives InitializeResponse with available resources/tools
# 5. You can now ask the CLI to search GitHub, create issues, etc.This script implements comprehensive schema validation based on the official MCP specification:
- Schema-Driven — All validation rules are derived from the MCP specification and CLI documentation
- Strict Validation — Fields are validated for type, format, and content according to schema rules
- Transparent Conversion — The script converts between two supported JSON formats (flat and Claude Desktop)
- Clear Errors — Validation failures include specific, actionable error messages citing the schema rule violated
- No Data Loss — All schema-valid configuration is preserved in the generated command
- Layered Architecture — Focuses solely on transport configuration, leaving protocol negotiation to the CLI runtime
The validation functions in the script are documented with:
- The specific MCP schema rule being validated
- The source of the rule (specification version, documentation link)
- The pattern or constraint being enforced
- The purpose of the validation
To understand the schema validation details, see comments in json-to-mcp-add.sh which cite:
- MCP Specification 2025-11-25
- CLI Configuration Documentation
- POSIX Shell Variable Naming Convention
- Bash (4.0+)
jq(1.6+) for JSON parsing and validation
json-to-mcp-add.sh -h
json-to-mcp-add.sh --helpWhen making changes:
- Ensure all validation rules match the MCP specification
- Add comments documenting schema rules and sources
- Update README if changing validation behaviour
- Test with both flat and Claude Desktop JSON formats
- Model Context Protocol Specification
- Claude Code Documentation
- POSIX Shell Command Language
- JSON (RFC 8259)
MIT