Add MCP servers
Most MCP servers’ READMEs show a JSON block for mcpServers.
Six harnesses want that block in six files, with six ways to spell an environment variable.
palm takes the block once and writes all six, with no secret in any of them.
A README without a block, such as one that shows only claude mcp add commands, works through flags instead.
$ palm install mcp --snippet - <<'JSON'{ "mcpServers": { "github": { "url": "https://api.githubcopilot.com/mcp/", "headers": { "Authorization": "Bearer ${GITHUB_PAT}" } } }}JSON+ mcp github .mcp.json, .codex/config.toml +4 merged (needs GITHUB_PAT in the environment)1 installed. Commit palm.yaml, palm.lock.yaml, .mcp.json, .codex/, .vscode/, .cursor/, .gemini/ and opencode.json together.$ palm get mcpkind name source ref sha targets files──── ────── ───────── ─── ─── ─────────────────────────────────────────── ───────────mcp github palm.yaml claude,codex,copilot,cursor,gemini,opencode 0 +6 mergedgithub needs GITHUB_PAT (not set)Four ways in
Section titled “Four ways in”| Way | Command | Use it when |
|---|---|---|
| From a source | palm install <source> mcp:<name> |
a repository or plugin ships the server in its .mcp.json |
| From a README snippet | palm install mcp --snippet - |
the server’s README shows a JSON block |
| By flags | palm install mcp <name> --url ... or --command ... |
you know the URL or command, or the README has no JSON block |
| By hand | an entry under mcp: in palm.yaml, then palm install |
you prefer to edit YAML |
All four end in the same entry, so palm get mcp lists every server with the variables it needs, and palm describe mcp <name> prints the block each harness received.
palm has no registry client and never searches for a server by name.
palm install mcp <registry-name> and the removed palm search both answer with the --snippet - form.
From a README snippet
Section titled “From a README snippet”-
Copy the JSON block from the README.
-
Pipe it into palm.
Terminal window pbpaste | palm install mcp --snippet -On Linux, use
xclip -oorwl-paste.--snippet server.jsonreads a file instead. -
Export the variables palm names, and restart the harness.
palm reads the { "mcpServers": { ... } } form, the flat form without the wrapper, and VS Code’s servers form.
Every server in the block becomes an mcp: entry in palm.yaml.
A literal secret in the snippet becomes ${NAME}, with a notice.
So does VS Code’s ${input:name}, whose inputs array palm drops.
A value to fill in, such as Bearer YOUR_API_KEY, becomes an optional reference named after its header, such as Bearer ${CONTEXT7_TOKEN:-}, so the server works until you export a key.
A server name that already exists stops with E_CONFLICT unless you pass --force.
palm install mcp <name> with no flags and no snippet renders the server palm.yaml declares under that name again. After a hand edit of a generated file, palm install mcp <name> --force restores it.
For a name palm.yaml does not declare, it stops with a hint that shows --url, --command and --snippet -.
Paste the README block with the last one.
By flags
Section titled “By flags”$ palm install mcp docs --url https://docs.example.com/mcp --header 'Authorization=Bearer ${DOCS_TOKEN}'+ mcp docs .mcp.json, .codex/config.toml +1 merged (needs DOCS_TOKEN in the environment)1 installed. Commit palm.yaml, palm.lock.yaml, .mcp.json, .codex/ and .cursor/ together.$ palm install mcp xcodebuild --command npx --arg -y --arg xcodebuildmcp@latestThis install adds 1 program that will run on your machine.
1. mcp xcodebuild from palm.yaml stdio npx -y xcodebuildmcp@latest env: none targets: claude (.mcp.json), codex (.codex/config.toml), cursor (.cursor/mcp.json) ! unpinned: npx resolves xcodebuildmcp@latest at run time; pin an exact version to review what runs
palm never runs this itself. If you say yes, its hash goes into palm.lock.yaml,so teammates and CI install it without being asked; any change asks again.x 1 program needs your consent and there is no terminal review: palm install mcp xcodebuild --command npx --arg=-y --arg xcodebuildmcp@latest --dry-run --review then: palm install mcp xcodebuild --command npx --arg=-y --arg xcodebuildmcp@latest --allow-exec mcp:xcodebuild@palm.yaml=sha256:53d801a8a7170e99fe4a273d9c75ddfab7aa62030e36f9f8fbb72d355d21d46f$ palm install mcp xcodebuild --command npx --arg -y --arg xcodebuildmcp@latest --allow-exec mcp:xcodebuild@palm.yaml=sha256:53d801a8a7170e99fe4a273d9c75ddfab7aa62030e36f9f8fbb72d355d21d46fThis install adds 1 program that will run on your machine.
1. mcp xcodebuild from palm.yaml stdio npx -y xcodebuildmcp@latest env: none targets: claude (.mcp.json), codex (.codex/config.toml), cursor (.cursor/mcp.json) ! unpinned: npx resolves xcodebuildmcp@latest at run time; pin an exact version to review what runs
palm never runs this itself. If you say yes, its hash goes into palm.lock.yaml,so teammates and CI install it without being asked; any change asks again.
allowed by --allow-exec: mcp:xcodebuild@palm.yaml (sha256:53d801a8)+ mcp xcodebuild .mcp.json, .codex/config.toml +1 merged~ trusted mcp xcodebuild sha256:53d801a81 installed.| Flag | For |
|---|---|
--url <url> |
a remote server |
--header <K=V> |
a request header, repeatable |
--command <program> |
a stdio server |
--arg <value> |
one argument, repeatable, in order |
--env <K=V> |
an environment variable, repeatable |
--transport http|sse|stdio |
when palm cannot infer it from the other flags |
Quote a value that holds ${...}, so your shell does not expand it.
Not every README has a JSON block. Some show only a harness command, such as claude mcp add --transport http context7 https://mcp.context7.com/mcp.
Take the name, the URL or the command and its arguments from that line, and pass them as flags.
palm install mcp context7 --url https://mcp.context7.com/mcppalm install mcp context7 --command npx --arg -y --arg @upstash/context7-mcpThe first form is a remote server. The second starts a local program, so it asks for consent.
A secret you type is never stored.
--env BRAVE_API_KEY=<value> writes ${BRAVE_API_KEY} into palm.yaml and every harness file, and palm prints the export line to run before you start the harness.
A value that is no secret, such as --env LOG_LEVEL=debug, is written as you typed it, and an i line says so.
Only --secrets literal writes a secret itself, as Secrets describes.
Stdio servers run programs
Section titled “Stdio servers run programs”A remote server only needs a URL, and nothing runs on your machine.
A stdio server is a program the harness starts with your permissions, so it goes through consent like a hook.
palm shows the command, arguments, working directory and environment keys, and pins your yes by hash in the lock.
Without a terminal, the error prints the --allow-exec line, as in the session above.
The hash pins the command, such as npx -y xcodebuildmcp@latest, not what npx downloads when the harness starts it.
With @latest or no version, each start may run a newer package under the same consent.
Write an exact version, such as xcodebuildmcp@1.4.2, when you want a new release to ask again.
What each harness receives
Section titled “What each harness receives”$ palm describe mcp docsmcp docs (installed, project scope) source palm.yaml path mcp/docs selected by palm.yaml claude .mcp.json /mcpServers/docs { "type": "http", "url": "https://docs.example.com/mcp", "headers": { "Authorization": "Bearer ${DOCS_TOKEN}" } } codex .codex/config.toml /mcp_servers/docs [mcp_servers.docs] url = "https://docs.example.com/mcp" bearer_token_env_var = "DOCS_TOKEN" cursor .cursor/mcp.json /mcpServers/docs { "url": "https://docs.example.com/mcp", "headers": { "Authorization": "Bearer ${env:DOCS_TOKEN}" } } note needs DOCS_TOKEN in the environment variables DOCS_TOKEN (not set)| Harness | File | Key |
|---|---|---|
| Claude Code | .mcp.json |
mcpServers |
| Codex | .codex/config.toml |
[mcp_servers.<name>] |
| GitHub Copilot | .vscode/mcp.json |
servers |
| Cursor | .cursor/mcp.json |
mcpServers |
| Gemini CLI | .gemini/settings.json |
mcpServers |
| OpenCode | opencode.json |
mcp |
palm merges its entry into the file and keeps your other servers.
An identical server you already added by hand is adopted, not added twice.
palm remove docs takes out exactly that entry, checks that it is gone, and deletes a JSON file left as {}.
In your home directory
Section titled “In your home directory”Add -g to any of these commands, and palm writes to the harness home files, such as ~/.claude.json or ~/.codex/config.toml, and declares the server in ~/.palm/palm.yaml.
The global scope writes environment references too.
A literal secret needs --secrets literal there.
palm writes it only when the file’s real path lies outside every git worktree, so it never lands in a dotfiles repository. --force does not change that.