Claude Code MCP Server
Summary: It's an MCP server that lets an AI orchestrator drive Claude Code — creating and messaging cloud coding sessions, reading their branches/PRs, running Claude Code locally under guardrails, and checking plan usage and sign-in state.
Create cloud sessions (
claude_create_cloud_session): start aclaude --cloudtask as a background job, optionally selecting a GitHub repo (owner/nameor URL, cloned into a cache) or an allowlisted local directory, withbase_branch,environment_id, and timeout/idle-timeout controls.Send follow-ups (
claude_send_followup): queue a message into an existing cloud session (e.g. to ask for agit format-patchwhen the session can't push).Read session results (
claude_get_session): poll a create or local-run job's progress/result, or runclaude --teleportto report the branch a cloud session pushed and its pull request number/URL (via output orgh pr list).Cancel jobs (
claude_cancel_session): stop a stuck create or local run and every process it spawned (SIGTERM, then SIGKILL); does not stop the cloud session itself.Run Claude Code locally (
claude_run_local): headlessclaude -pin a checkout insideCLAUDE_MCP_LOCAL_ROOTS, opt-in only, restricted (no shell/MCP/hooks) unlessCLAUDE_MCP_LOCAL_ALLOW_SHELLis set; returns result text, turns, cost, usage, denied tools, and changed files.Check plan usage (
claude_usage): report session/weekly usage windows, reset times, severity and credit spend, and signal when new work is blocked byCLAUDE_MCP_USAGE_STOP_PERCENT.Check auth (
claude_auth_status): report whether the local Claude Code CLI is signed in.Transport & safety: stdio by default, opt-in loopback Streamable HTTP with bearer token and Host/Origin checks; strict typed input/output schemas, no shell escape hatch, deterministic argv, and structured error codes.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Claude Code MCP Server start a cloud session to add unit tests for the auth module"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Claude Code MCP Server
MCP server that lets AI agents with an always-on cloud computer (e.g. Muse by Meta, Grok Bot by SpaceXSI, ChatGPT Dots by OpenAI, Claude Duet by Anthropic, etc) orchestrate Claude Code cloud sessions natively via the Claude Code CLI.
It is the first server in a planned MCP orchestration suite: one MCP server per agent tool (Claude Code now; Codex, Antigravity, Grok and Cursor later), so an orchestrator such as Muse, Grok Bot, ChatGPT Dots or Claude Duet can coordinate tasks across several coding agents through the same kind of tool calls. The servers are meant to share one job model (#28), so one orchestration loop works with all of them.
Requires the Claude Code CLI, installed and signed in, on the machine that runs this server.
Install it:
npm install -g @anthropic-ai/claude-codeSign in: run
claudeonce in a terminal and complete the claude.ai login (claude auth statusshould then report"loggedIn": true).Cloud sessions need a claude.ai account with Claude Code on the web, and the Claude GitHub app on the repositories sessions work in.
Without the CLI, every tool returns
CLI_NOT_FOUNDwith the install command (and the server prints the same warning on stderr at startup). When the CLI is not signed in, tools returnCLI_NOT_SIGNED_IN, which says to sign in withclaudefirst.
It exposes seven tools over stdio: create a cloud session, queue a follow-up message into one, read the GitHub branch and pull request a session produced, cancel a create or local run that is stuck, run Claude Code headless in a local checkout (off until you list allowed directories), report how much of the Claude plan's usage allowance is left, and check the CLI's sign-in state. Each tool maps to one fixed claude invocation, and each returns structured JSON. Agents call typed tools and get typed results back, so they never need to write shell commands.
Design
Strict input schemas. Every field is typed, required fields are listed explicitly, and every schema sets
additionalProperties: false. Invalid input is rejected by schema validation before any process is spawned.No escape hatch. The tool surface is exactly the seven tools below. There is no "run any claude command" tool and no shell access.
Deterministic argv. Arguments are built from validated inputs in a fixed order and passed as an array (
shell: false). Values are attached as--flag=value, or placed after--, so a value that starts with-can never be read as a flag.claude --cloud=<description>is executed on a pseudo-terminal because the CLI rejects that form when stdout is a pipe; the pty helperexecs the same argv and never goes through a shell.Structured outputs. Every tool declares an
outputSchemaand returnsstructuredContentwith parsed fields, such assession_idandsession_url. CLI stdout and stderr are returned incliwith terminal control sequences removed.Cloud sessions. Create and follow-up always pass
--cloud=. Session status runs print-modeclaude --teleport=<session>inside the server's cache so the CLI can check out the branch the cloud session pushed, then the process exits. That argv carries no prompt and is not attached to a terminal.Local runs are opt-in.
claude_run_localis the only tool that runs Claude Code on this machine (claude -p). It refuses every call untilCLAUDE_MCP_LOCAL_ROOTSlists directories, only runs inside them (after resolving symlinks), always passes an explicit permission mode from a fixed set, and never bypasses permissions. Until a second opt-in,CLAUDE_MCP_LOCAL_ALLOW_SHELL, is set, every run uses Claude Code's--restrictedmode: no shell or code-running tools, and the checkout's settings, hooks and.mcp.jsonaren't loaded.No orphaned processes. Every child (git,
gh, the pty helper,claude) leads its own process group and is tracked. A cancelled create, or an MCP request the client cancels (for example on its own timeout), stops its processes with SIGTERM, then SIGKILL 5 s later. When the client closes stdin, or the server gets SIGINT, SIGTERM or SIGHUP, the server stops every child before it exits. If the server is killed outright, the pty helper notices and stopsclaude --cloudthe same way. Onlygitandghprocesses that are already running can outlive a SIGKILL, and each is bound byCLAUDE_MCP_TIMEOUT_MS.Remote access is opt-in and private. By default the server speaks MCP over stdio and opens no port. Setting
CLAUDE_MCP_HTTP_PORTadds a Streamable HTTP listener on127.0.0.1. It refuses to start withoutCLAUDE_MCP_TOKEN, checks theHostandOriginheaders against an allow list (DNS-rebinding protection), and answers 401 to any request without the right bearer token before MCP sees it. Expose it to other machines withtailscale serve, never Funnel or the public internet.No telemetry. The server runs the local
claudebinary. Its own network calls aregit clone/git fetchof a validatedhttps://github.com/owner/name.gitinto the cache, and a read-onlygh pr listwhen status needs a pull request number.
Related MCP server: Claude Code MCP
Prerequisites
Node.js 20 or later
The Claude Code CLI (
npm install -g @anthropic-ai/claude-code), signed in by runningclaudeonce and completing the claude.ai login (claude auth statusshould report"loggedIn": true). The account needs cloud sessions (Claude Code on the web). SetCLAUDE_BINwhenclaudeis not on the server'sPATH.Python 3 (the standard library only:
pty,fcntl,termios) forclaude_create_cloud_session. No pip packages. Follow-ups, session status, and auth status do not need Python. On Debian/Ubuntu this is thepython3package, which is usually already installed.Git on
PATH, when a call names a github.com repository. The server runsgitwith a fixed argument list andcore.hooksPath=/dev/null.The Claude GitHub app installed on each private repository the cloud VM should clone, push, or open a pull request in. Public repositories can be cloned once the claude.ai account is connected to GitHub. See GitHub access below.
The
ghCLI, signed in, when session status should resolve a pull request that teleport's output does not already name. The branch is still returned whenghis absent.
Install
The package on npm is @sohampatwardhan/claude-code-mcp-server. Orchestrator agents start it over stdio:
npx -y @sohampatwardhan/claude-code-mcp-server@latestThe published bin is claude-code-mcp-server (#!/usr/bin/env node). Stdio is the only transport. Logs go to stderr so they stay off the JSON-RPC stream. The MCP server name is claude-code-mcp-server.
The client must be able to run the Claude Code CLI. Install it and sign in on the same machine (claude auth status should report "loggedIn": true), with cloud sessions available on the account. Python 3 (stdlib only) is required for claude_create_cloud_session. See Prerequisites.
claude --cloud packages the working directory. Set the client's cwd to the repository the session should work on, or set CLAUDE_MCP_CWD when the host can pass environment variables but not a working directory. The full list is in Environment variables.
Cursor
Add this to ~/.cursor/mcp.json or the project's .cursor/mcp.json:
{
"mcpServers": {
"claude-code": {
"command": "npx",
"args": ["-y", "@sohampatwardhan/claude-code-mcp-server@latest"],
"cwd": "/absolute/path/to/the/repo/sessions/should/work/on",
"env": {
"CLAUDE_MCP_CWD": "/absolute/path/to/the/repo/sessions/should/work/on"
}
}
}
}cwd and CLAUDE_MCP_CWD should be the same directory. If the client has no cwd field, CLAUDE_MCP_CWD alone is enough.
Claude Desktop
The same mcpServers block goes in the desktop config:
macOS:
~/Library/Application Support/Claude/claude_desktop_config.jsonWindows:
%APPDATA%\Claude\claude_desktop_config.jsonLinux:
~/.config/Claude/claude_desktop_config.json
Restart Claude Desktop after saving. Point cwd / CLAUDE_MCP_CWD at the repository the cloud session should package.
Generic stdio
Any MCP client that launches a local process uses the same command. Field names vary (mcpServers vs servers, and some clients omit type).
{
"mcpServers": {
"claude-code": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@sohampatwardhan/claude-code-mcp-server@latest"],
"env": {
"CLAUDE_MCP_CWD": "/absolute/path/to/your/repo",
"CLAUDE_BIN": "claude",
"CLAUDE_MCP_TIMEOUT_MS": "300000"
}
}
}
}Run
The server speaks MCP over stdin/stdout. Normally the MCP client launches it, so you don't run it by hand. From a checkout, after npm run build:
npm start # same as: node dist/src/index.jsRemote access over HTTP
To drive Claude Code on one machine (for example a Mac with your checkouts and toolchain) from an orchestrator running somewhere else (Muse, Grok Bot, ChatGPT Dots, Claude Duet), run the server with the Streamable HTTP transport and reach it over your tailnet.
export CLAUDE_MCP_TOKEN="$(openssl rand -hex 32)"
CLAUDE_MCP_HTTP_PORT=8787 npx -y @sohampatwardhan/claude-code-mcp-server@latestIt prints listening on http://127.0.0.1:8787/mcp on stderr and serves MCP at that one path:
Stateless Streamable HTTP. Only
POST /mcpis answered, with JSON responses.GETandDELETEget 405, and other paths 404. Jobs are shared across requests and connections, so ajob_idfrom one request can be polled or cancelled from any later one, as on stdio.Checks, in order, before any MCP handling:
The
Hostheader (any port) must name an allowed host, else 403. That's loopback, the bind address, orCLAUDE_MCP_HTTP_ALLOWED_HOSTS.An
Originheader, when present, must too, else 403.Authorization: Bearer <CLAUDE_MCP_TOKEN>must match, compared in constant time, else 401 withWWW-Authenticate: Bearer.Bodies over 1 MiB get 413.
Shutdown. SIGINT, SIGTERM or SIGHUP closes the listener and stops every child the server started (create jobs, local runs, git), as on stdio. Closing stdin does nothing in HTTP mode, so it runs under
launchdwith stdin on/dev/null.
Expose it privately with tailscale serve. Keep the server on 127.0.0.1 and let Tailscale terminate HTTPS for your tailnet only:
tailscale serve --bg --https=443 http://127.0.0.1:8787Then set CLAUDE_MCP_HTTP_ALLOWED_HOSTS to the machine's tailnet name (for example mymac.tail1234.ts.net), because Tailscale forwards requests with that name as their Host header (a 403 "Host not allowed" means it's missing). Clients on your tailnet connect to https://mymac.tail1234.ts.net/mcp with the bearer token. Never use tailscale funnel, port forwarding, or a public bind address. The token is the only credential on this endpoint, and the tools can start Claude Code sessions on your account.
Run it under launchd on macOS so it starts at login in your user session. Signed-in Claude Code needs your login keychain, which a launch agent in your session can reach and a bare SSH session often can't. Save as ~/Library/LaunchAgents/com.example.claude-code-mcp.plist, put the token in it, chmod 600 it, then launchctl load it:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key><string>com.example.claude-code-mcp</string>
<key>ProgramArguments</key>
<array>
<string>/opt/homebrew/bin/npx</string>
<string>-y</string>
<string>@sohampatwardhan/claude-code-mcp-server@latest</string>
</array>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key><string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
<key>CLAUDE_MCP_HTTP_PORT</key><string>8787</string>
<key>CLAUDE_MCP_TOKEN</key><string>paste-the-token-here</string>
<key>CLAUDE_MCP_HTTP_ALLOWED_HOSTS</key><string>mymac.tail1234.ts.net</string>
</dict>
<key>RunAtLoad</key><true/>
<key>KeepAlive</key><true/>
<key>StandardErrorPath</key><string>/tmp/claude-code-mcp.log</string>
</dict>
</plist>Add the other variables you use (CLAUDE_MCP_REPO_CACHE, CLAUDE_MCP_LOCAL_ROOTS, ...) to the same EnvironmentVariables block. The log file gets the listening line and warnings, never the token.
Environment variables
Variable | Default | Meaning |
|
| Path or name of the Claude Code CLI binary. |
| unset | Default cloud environment id (for example |
|
| Hard timeout for each spawned CLI process, in milliseconds (positive integer). On timeout the process gets SIGTERM, then SIGKILL 5 s later. A create call can override it with |
| unset (off) | Stop |
| unset (off) |
|
|
| Python 3 interpreter used to allocate the pseudo-terminal for create. |
| process working directory | Directory the CLI runs in when a call omits |
| empty | Comma-separated allowlist of local directories a create call may select. Each entry is an absolute directory that exists at startup. A github.com |
|
| Directory that holds checkouts of GitHub repositories named by a call. Created on first use. An existing path must be a directory. This directory is the allowlist for where clones are written. |
| empty (local runs off) | Comma-separated absolute directories |
| unset (no check) | An integer from 1 to 100. When set, create and |
| off |
|
| unset (stdio) | Port for the Streamable HTTP transport. Setting it is what turns HTTP on; stdio stays the default. |
|
| Address the HTTP transport binds. Keep it on loopback and put |
| unset | Bearer token every HTTP request must send ( |
| empty | Extra comma-separated hostnames (no port or scheme) the HTTP transport accepts in the |
If any of these values is invalid, the server exits at startup with code 2 and prints a message on stderr.
When a create call omits repo, the CLI runs in CLAUDE_MCP_CWD, or in the server process's working directory when that variable is unset or blank. claude --cloud packages the repository in the directory it is started in: the cloud VM clones that directory's GitHub remote at the current branch, or uploads the local tree when it cannot clone the remote. Set the client's cwd or CLAUDE_MCP_CWD to the default repository. An invalid CLAUDE_MCP_CWD (missing path, or a path that is not a directory) makes the server exit at startup with code 2.
Choosing a repository per call
claude --cloud has no flag for a repository URL. The repository is whichever checkout the command runs in, and the cloud VM clones that directory's GitHub remote at the current branch (Use Claude Code in the cloud). The web UI can pick a repository and base branch directly; the CLI cannot, so this server prepares the checkout:
A github.com
owner/name(orhttps://github.com/owner/name) needs no local checkout and does not have to be listed inCLAUDE_MCP_REPOS. The server clones it intoCLAUDE_MCP_REPO_CACHE/<owner>/<name>fromhttps://github.com/<owner>/<name>.git, setsoriginto that URL, fetches, and checks outbase_branchwhen the call sets one. Omittingbase_branchchecks out the remote's default branch. The first clone is partial (git clone --filter=blob:none): it downloads every commit and tree but only the file contents the checkout needs, and git fetches older contents on demand. Later calls never reclone a healthy cache. They fetch again and reset that cache checkout, including removing untracked files there. A cache directory that is not a git checkout is removed and cloned again. The create output'scachefield says whether a call cloned (cloned: true) or reused the cache, and how long that took (duration_ms). A cache directory whoseoriginis a different repository is left unchanged and the call fails.base_branchmust already exist onorigin.An absolute directory on
CLAUDE_MCP_REPOSis used as the CLI working directory. The server does not fetch, reset, or otherwise modify that checkout, andbase_branchis rejected for it.Any other value, including a subdirectory of an allowlisted directory, a relative path, another host, or a URL with a token, fails before the CLI starts (
REPO_NOT_ALLOWED). A clone, fetch, or missing-branch failure isREPO_PREPARE_FAILED.
The clone URL is built by the server. Tool arguments cannot name a host, a credential, or a refspec. Private clones use whatever credential helper git already has on this machine; that credential is what lets the local cache see the repository. It is not uploaded, and it is not what the cloud VM uses to push.
Workspace trust follows the directory that was selected. With CLAUDE_MCP_TRUST_CWD=1, the opt-in writes the trust flag for that directory's main git checkout (or the directory itself when it is not a repository) and answers the dialog only for that folder.
GitHub access
Two different GitHub identities are involved. They are easy to mix up because both end up talking about the same owner/name.
Claude Code's GitHub integration is what the cloud virtual machine uses. From Use Claude Code in the cloud and the web quickstart:
The VM clones the GitHub remote of the directory
--cloudwas started in, at that directory's current branch. It does not upload the local tree when that remote can be cloned. Push local commits first if the VM should see them; this server's cache is reset to the remote, so the branch on GitHub is what the VM clones.Connecting the claude.ai account to GitHub lets a cloud session clone public repositories. Private repositories need the Claude GitHub app installed on that repository (repository settings, or the install URL Claude shows). The app is also required for auto-fix and for Projects.
/web-setupin a local Claude Code session stores the machine'sghtoken on the claude.ai account. Cloud sessions can then use any repository that token can access. The app is still required for auto-fix.Each cloud task gets its own session and its own branch. After the branch is pushed, the session opens a pull request with
gh pr createinside the cloud conversation. This server does not call the GitHub API to open a pull request.The browser repository-and-branch picker is not available on the CLI.
base_branchplus the cache checkout is how a CLI caller selects them.
This server only makes the local claude --cloud see the right remote and branch, then reads the artifacts back:
git clone --filter=blob:none/git fetch/git checkoutinCLAUDE_MCP_REPO_CACHE, with hooks disabled and submodules not checked out.claude --teleport=<session> --debug --debug-file=<cache>/sessions/<session>.teleport.log, stdin closed, stdout a pipe. On Claude Code 2.1.300, print mode validates the session, checks out the branch the session pushed, logsBranch after checkout: '<name>', and exits. It does not open a local interactive session. The debug file is required: without--debugthat line is not emitted, and a signed-out print-mode teleport exits with empty stdout and stderr.git symbolic-ref HEADafter teleport, used when it disagrees with the pre-teleport branch.gh pr list --repo owner/name --head <branch> --json number,url --limit 1when the teleport output did not include a pull request URL. That reads the pull request the cloud session already opened.
Teleport has to run in a checkout of the same repository (not a fork) with a clean work tree. The server runs it only in the cache, after a clean reset. It is never run in a directory from CLAUDE_MCP_REPOS. A session that has not pushed a branch yet fails teleport; status then returns CLI_EXIT_NONZERO and no branch. claude --resume, claude logs, and claude agents list local sessions, and claude -p --cloud <session> only queues a follow-up (CLI reference, sessions, headless). None of those report the pushed branch or the pull request.
Workspace trust
claude --cloud <description> requires an interactive terminal. On a pseudo-terminal the CLI then stops at its workspace-trust dialog until someone answers. Older versions asked "Do you trust the files in this folder?"; Claude Code 2.1.316 asks "Quick safety check: Is this a project you created or one you trust?" with "No, exit" selected above "Yes, I trust this folder". The server recognises both wordings. It renders the CLI's cursor moves as the spaces they draw, so a dialog built from positioned words still matches. Trust matters because accepting it lets that folder's project hooks, allow rules, and .mcp.json servers run.
This server does not answer that dialog on its own. Either:
Trust the folder yourself: run
claudeonce in that directory and accept the dialog, or setprojects["<path>"].hasTrustDialogAcceptedtotruein~/.claude.json.<path>is the main git checkout when the selected directory is a repository or worktree, and the directory itself otherwise. The selected directory isrepowhen the call sets one, andCLAUDE_MCP_CWDotherwise (the process working directory when that variable is unset). Claude Code documents this key under project allow rules and workspace trust.Or set
CLAUDE_MCP_TRUST_CWD=1. Create then writes that same key for the one directory above and, if the dialog still appears, answers it once. It reads which option is selected (❯) and moves to "Yes, I trust this folder" with the arrow keys before pressing Enter, since Enter alone would choose "No, exit" on current versions. If the menu's selection can't be read, it doesn't answer and fails withWORKSPACE_NOT_TRUSTED. The opt-in covers that one directory. Create still runs with the CLI's normal permission prompts.
The trust edit holds ~/.claude.json.lock (exclusive create, treated as stale after 5 seconds) so two of these servers do not interleave. Inside the lock it re-reads ~/.claude.json immediately before an atomic rename, skips the write when hasTrustDialogAccepted is already true, and retries the merge when the file changed since the first read so other fields are kept.
Claude Code does not take that lock. A Claude process that saves ~/.claude.json after this server's second read can overwrite the trust flag, and a save that lands between that read and the rename can itself be overwritten. If the dialog comes back, set the flag again while no Claude process is writing the file.
Without the opt-in, the dialog fails the call with WORKSPACE_NOT_TRUSTED as soon as it is drawn (under a second on Claude Code 2.1.316), never after the full timeout. A description that merely contains the words "Do you trust this folder?" is not treated as the dialog. The Created cloud session: title line is ignored when the server looks for the prompt.
Trust is stored in ~/.claude.json in the home directory (not under ~/.claude/). When ~/.claude.json is missing or is not a JSON object, the opt-in leaves it unchanged and answers the dialog on the pty instead. Claude Code treats the home directory itself as trusted for the current session only, so a server whose CLAUDE_MCP_CWD is $HOME still needs the opt-in for the pty to answer the dialog.
Sign-in check
Before create and claude_run_local start anything, the server runs claude auth status in CLAUDE_MCP_CWD, which makes no model call. If it reports "loggedIn": false, the call fails in about a second with CLI_NOT_SIGNED_IN, naming the auth method the CLI reports and the remedy (claude auth login on the server's host; on macOS over SSH the login keychain may be locked). No clone, --cloud or -p process starts. A signed-in result is reused for 60 seconds. If auth status can't be read, times out (10 seconds), or fails for another reason, the run goes ahead and the CLI's own error still surfaces. A missing CLI fails with CLI_NOT_FOUND.
Tool reference
Every successful call returns structuredContent that matches the tool's outputSchema, and the same JSON as a text content block.
claude_create_cloud_session
Creates a new cloud session. Runs, on a pseudo-terminal: claude --cloud=<description> [--environment=<id>]
Create runs in the background and returns within about 2 seconds. On a large repository the clone and claude --cloud can take minutes, longer than an MCP client waits for one request (60 seconds by default in the MCP SDK). The call starts a job, waits up to 2 seconds, then returns:
status: "done": the job finished in time, and the fields below are final. A failure in that window comes back as the usual tool error.status: "cloning","starting"or"running": the job is still going. Callclaude_get_sessionwithjob_idevery few seconds untiljob.statusis"done"or the call returns a tool error (the same codes as below).cloningmeans the GitHub cache checkout is being prepared.startingmeansclaude --cloudis running and hasn't printed a session yet.runningmeans it printed the session id (session_idandsession_urlare already set) and hasn't exited. A job stopped withclaude_cancel_sessionreportscancelled.
Jobs live in the server's memory only. A finished job is kept for an hour (at most 100 finished jobs), and all jobs are lost when the server exits. Each process a job runs is bound by timeout_s (default CLAUDE_MCP_TIMEOUT_MS), and claude --cloud also by idle_timeout_s (default CLAUDE_MCP_IDLE_TIMEOUT_MS, off). Both time-outs fail with CLI_TIMEOUT. Use claude_cancel_session to stop a job early.
The CLI rejects --cloud with a task description when stdout is a pipe (--cloud requires an interactive terminal). There is a non-interactive create form, claude -p <task> --environment <ccpool>, but it only targets self-hosted environments and it omits --cloud. This server keeps --cloud= on every create so an older CLI cannot fall through to a local session. A pty has a single output stream, so the CLI's stdout and stderr are both returned in cli.stdout (cli.stderr is empty on this tool). cli.stdout and cli.stderr have terminal control sequences removed (cursor save and restore, CSI including private parameters, and other C0 controls) before they are returned.
Input schema:
{
"type": "object",
"properties": {
"description": { "type": "string", "minLength": 1, "maxLength": 100000, "pattern": "\\S" },
"environment_id": { "type": "string", "maxLength": 200, "pattern": "^[A-Za-z0-9][A-Za-z0-9_-]*$" },
"repo": { "type": "string", "minLength": 1, "maxLength": 4096 },
"base_branch": { "type": "string", "minLength": 1, "maxLength": 255 },
"timeout_s": { "type": "integer", "minimum": 1, "maximum": 86400 },
"idle_timeout_s": { "type": "integer", "minimum": 1, "maximum": 86400 }
},
"required": ["description"],
"additionalProperties": false
}The environment id is chosen in this order: environment_id from the call, then CLAUDE_CLOUD_ENVIRONMENT_ID, then none (no --environment flag). repo matches an absolute path, a github.com owner/name, or a https://github.com/owner/name URL. A github.com value is cloned into the cache; an absolute path has to be on CLAUDE_MCP_REPOS. Omit repo to use the default directory; the output field is then null. When repo is set, cwd is the directory the CLI ran in and repo is the normalized value (owner/name, or the canonical directory path). base_branch is the branch checked out in the cache before --cloud runs. It is null in the output when the call did not name a github.com repository. For a GitHub repository it is the requested branch, or the remote default branch when the call omitted it. cache is { cloned, duration_ms } once the cache checkout is ready: cloned is true when this call made the first (partial) clone and false when it reused the cache with git fetch, and duration_ms covers the clone or fetch, checkout and clean, not a wait for another call holding the same repository's lock. It is null while the job is still cloning and for a local or default directory. When claude --cloud has to upload the repository instead of having the VM clone it from GitHub (no Claude GitHub app on the repository), a partial clone uploads as a snapshot of the working tree without history (Send local repositories without GitHub).
Output:
{
"job_id": "job_5f0c2a9e7b1d43c8a6e2f019",
"status": "done",
"elapsed_ms": 41230,
"session_id": "session_01AbC...",
"session_url": "https://claude.ai/code/session_01AbC...?from=cli&m=0",
"title": "Fix flaky login test",
"parsed": true,
"environment_id": null,
"cwd": "/absolute/path/to/the/repo",
"repo": "owner/name",
"base_branch": "main",
"args": ["--cloud=Fix flaky login test"],
"cli": { "exit_code": 0, "stdout": "Created cloud session: ...", "stderr": "" }
}parsed is true when both session_id and session_url were found in the CLI output. If the output can't be parsed, the call still succeeds with parsed: false, the unparsed fields set to null, and the cleaned output in cli. While the job is still going, cli.exit_code is null and cli.stdout holds the last 4,000 characters of output so far. For a GitHub repository, base_branch is the requested branch, or null if none was requested, until the checkout is ready.
claude_send_followup
Queues a message into an existing cloud session. Runs: claude --cloud=<session> -p -- <message>
Input schema:
{
"type": "object",
"properties": {
"session": {
"type": "string",
"maxLength": 2000,
"pattern": "^(?:(?:session|cse)_[A-Za-z0-9][A-Za-z0-9_-]*|https:\\/\\/claude\\.ai\\/code\\/[^\\s\"'<>]+)$"
},
"message": { "type": "string", "minLength": 1, "maxLength": 100000, "pattern": "\\S" }
},
"required": ["session", "message"],
"additionalProperties": false
}Output:
{
"session": "session_01AbC...",
"session_id": "session_01AbC...",
"session_url": "https://claude.ai/code/session_01AbC...?from=cli&m=0",
"sent": true,
"parsed": true,
"args": ["--cloud=session_01AbC...", "-p", "--", "Also update the docs"],
"cli": { "exit_code": 0, "stdout": "Sent to cloud session.\n...", "stderr": "" }
}sent is true when the CLI confirmed delivery (Sent to cloud session.).
claude_get_session
session takes a create or local-run job_id, or a cloud session.
With a job id (job_ and 24 hex characters), it reads the job from memory and runs nothing. For a create job it returns job with the same fields as the create output: progress while it runs, the final result once job.status is "done". For a claude_run_local job it returns local_job the same way, with job set to null. Top-level session_id mirrors the job's session_id. The other top-level fields are null, false or [], and repo is ignored. A failed job returns its tool error. An id this server doesn't know, because it expired, was never issued, or belonged to a server that has since exited, returns JOB_NOT_FOUND.
{
"job": { "job_id": "job_5f0c2a9e7b1d43c8a6e2f019", "status": "cloning", "elapsed_ms": 18400, "session_id": null, "...": "..." },
"session_id": null,
"repo": null, "branch": null, "base_branch": null,
"pr_number": null, "pr_url": null, "pr_source": null, "pr_error": null,
"parsed": false, "args": [], "cli": null
}With a session id or URL, it reports the GitHub artifacts of a cloud session: the branch it pushed, and the pull request URL and number when one exists. Runs, with stdin closed and stdout a pipe:
claude --teleport=<session_id> --debug --debug-file=<cache>/sessions/<session_id>.teleport.log
in the cache checkout of the session's repository. The process is killed if it exceeds CLAUDE_MCP_TIMEOUT_MS. No follow-up text is sent.
Input schema:
{
"type": "object",
"properties": {
"session": {
"type": "string",
"maxLength": 2000,
"pattern": "(?:^(?:(?:session|cse)_[A-Za-z0-9][A-Za-z0-9_-]*|https:\\/\\/claude\\.ai\\/code\\/[^\\s\"'<>]+)$)|(?:^job_[0-9a-f]{24}$)"
},
"repo": { "type": "string", "minLength": 1, "maxLength": 4096 }
},
"required": ["session"],
"additionalProperties": false
}repo is a github.com owner/name or https://github.com/owner/name URL. Omit it when claude_create_cloud_session created this session with a github.com repo: that slug and base branch are stored under the cache. A session created from a local directory has no record; pass repo then. Passing repo selects that repository even when a record exists. When the slug matches the record, the recorded base branch is checked out before teleport so the cache is clean.
Output:
{
"job": null,
"session_id": "session_01AbC...",
"repo": "owner/name",
"branch": "claude/add-retry",
"base_branch": "main",
"pr_number": 17,
"pr_url": "https://github.com/owner/name/pull/17",
"pr_source": "gh",
"pr_error": null,
"parsed": true,
"args": ["--teleport=session_01AbC...", "--debug", "--debug-file=/.../sessions/session_01AbC....teleport.log"],
"cli": { "exit_code": 0, "stdout": "", "stderr": "Branch after checkout: 'claude/add-retry'" }
}parsed is true when branch was identified. branch is null when teleport exits 0 without moving HEAD off the base branch and without logging a checkout. pr_number and pr_url are null when the branch has no pull request. pr_source is output when teleport's text included the pull request, gh when gh pr list supplied it, and null when neither did. pr_error is set when gh is missing or fails; an empty pull-request list leaves pr_error null.
This tool does not return the assistant's latest reply. Open session_url from create for the conversation. claude -p --cloud <session> queues a message and exits; --output-format stream-json is not supported with --cloud (cloud sessions, CLI reference, also at docs.claude.com). --resume, claude logs, and claude agents --json address local sessions (sessions). --remote is a deprecated alias of --cloud.
On a self-hosted environment a Stop hook on the runner can capture replies (see Test self-hosted environments). That hook runs on the runner, outside this server.
If a session can't push
Cloud sessions sometimes fail to push their branch to the GitHub remote. claude_get_session then returns no branch (teleport fails with CLI_EXIT_NONZERO), or the session says so at its session_url. When that happens, don't wait for a retry; move the changes through the calling agent instead:
Use
claude_send_followupto ask the session for a patch (prompt below).Copy the patch from the
session_url.claude_get_sessionreports only the pushed branch and pull request, not replies, so the patch is visible only there.Apply it to your own checkout of the base branch with
git am(forformat-patchoutput) orgit apply --3way(for a plain diff).Run the tests, commit, push, and open or update the pull request yourself.
Ready-to-send follow-up (replace <base> with the base branch, e.g. main):
Pushing your branch to GitHub failed, so I'll apply your changes myself. Please commit any outstanding work, then run `git format-patch origin/<base>..HEAD --stdout` and paste the complete output in a single fenced code block, with nothing else inside it. If format-patch isn't possible, paste a full unified `git diff origin/<base>` that also includes new and deleted files (stage them first with `git add -A` and use `git diff --cached origin/<base>`). Don't try to push again.claude_cancel_session
Stops a create job or claude_run_local job that is still running. Every process the job started on this machine (git, the pty helper, claude --cloud or claude -p, and anything they started) gets SIGTERM, then SIGKILL 5 s later. The call waits up to about 6 s for them to exit. Runs no new process.
It does not stop a cloud session. If claude --cloud already created one (the job's status was running), that session keeps running at its session_url. Open it there to stop it.
Input schema:
{
"type": "object",
"properties": {
"session": { "type": "string", "maxLength": 2000, "pattern": "<same as claude_get_session>" }
},
"required": ["session"],
"additionalProperties": false
}session is the job id, or for a create job the session id or URL it already printed. Output:
{
"job": { "job_id": "job_5f0c2a9e7b1d43c8a6e2f019", "status": "cancelled", "session_id": null, "...": "..." },
"local_job": null,
"cancelled": true,
"stopped": true
}For a local run, job is null and local_job holds the run (its session_id can still be resumed). cancelled is true when this call cancelled the job. For a job that already finished or was already cancelled it is false, and job shows that job as it stands. A job that already failed returns its tool error. stopped is true when every process the job started has exited. After a cancel, claude_get_session reports the job with status: "cancelled". A job id the server doesn't hold, or a session no unfinished job printed, returns JOB_NOT_FOUND.
Follow-ups, session status and auth status need no cancel tool. When the client cancels one of those requests (MCP notifications/cancelled, which the SDK sends when its own request timeout fires), the server stops that request's processes.
claude_run_local
Runs Claude Code headless on this machine, in an existing checkout, for work that needs the local toolchain or hardware rather than a cloud VM. Runs:
claude -p [--restricted --strict-mcp-config] --output-format=stream-json --verbose --permission-mode=<mode> [--model=<model>] [--max-turns=<n>] [--allowedTools=<tools>] [--resume=<session_id>] -- <prompt>
in cwd, stdin closed, stdout a pipe. It is off by default: with CLAUDE_MCP_LOCAL_ROOTS unset, every call fails with LOCAL_RUNS_DISABLED.
Restricted unless you allow shell access. With CLAUDE_MCP_LOCAL_ALLOW_SHELL off (the default), the run gets --restricted --strict-mcp-config (restricted mode, Claude Code 2.1.248 or later; an older CLI rejects the flag and the run fails with CLI_EXIT_NONZERO). Claude Code then:
removes Bash, PowerShell and the other tools that run commands or code, and WebFetch
loads only managed settings: not your user settings, and not the checkout's
.claude/settings*.json, hooks or allow rulesstarts no MCP servers, including the checkout's
.mcp.jsonconfines the file tools to
cwd, and refuses writes to settings, git and tool-configuration files (there's no one to approve them)
So a checkout can't give the run shell access, and the run can't rewrite .git/config or .claude/ to give a later run shell access either. Print mode never shows the workspace-trust dialog. With CLAUDE_MCP_LOCAL_ALLOW_SHELL=1 none of this applies: the checkout's settings, hooks and MCP servers load, so listing a root then trusts every checkout under it. Only turn it on for roots whose checkouts you'd open in Claude Code yourself.
Input schema:
{
"type": "object",
"properties": {
"cwd": { "type": "string", "minLength": 1, "maxLength": 4096, "pattern": "^\\/[^\\0\\n\\r]*$" },
"prompt": { "type": "string", "minLength": 1, "maxLength": 100000 },
"resume": { "type": "string", "pattern": "<UUID>" },
"model": { "type": "string", "pattern": "<alias or model name>" },
"permission_mode": { "enum": ["dontAsk", "acceptEdits", "plan", "auto"] },
"allowed_tools": { "type": "array", "items": { "enum": ["Read", "Glob", "Grep", "Edit", "Write", "NotebookEdit", "Bash", "WebFetch", "WebSearch"] } },
"max_turns": { "type": "integer", "minimum": 1, "maximum": 1000 },
"timeout_s": { "type": "integer", "minimum": 1, "maximum": 86400 }
},
"required": ["cwd", "prompt"],
"additionalProperties": false
}cwdmust be an existing directory inside aCLAUDE_MCP_LOCAL_ROOTSroot, or a root itself. Both are compared afterrealpath, so a symlink that leads out of a root is refused, one that leads into a root is allowed, and macOS/var/...and/private/var/...spellings agree. Anything else isREPO_NOT_ALLOWED, and the CLI is not started.permission_modedefaults todontAsk: reads andallowed_toolsrun, and anything that would prompt is denied (and listed inpermission_denials).acceptEditsalso allows file edits and common filesystem commands incwd,planis read-only exploration, andautolets a background classifier review each action, shell commands included. The mode is always passed explicitly, so a settings file'sdefaultModecan't change it.bypassPermissionsand--dangerously-skip-permissionsare never used.allowed_toolspre-approves built-in tools by name only. No patterns and no MCP tools.Bashallows every shell command.Shell access is a separate opt-in.
allowed_toolscontainingBash, orpermission_mode: "auto", lets the run execute shell commands on this machine without asking anyone, with your user's credentials and network access. Both fail withLOCAL_SHELL_DISABLEDunlessCLAUDE_MCP_LOCAL_ALLOW_SHELL=1. Without it the run is restricted (above): file reads and, inacceptEdits, file edits incwd.WebFetchinallowed_toolsthen has no effect, because restricted mode removes it.resumeis thesession_idof an earlier run. Claude Code finds the session by id, and may continue it under a new id; the result'ssession_idis the one to resume next.timeout_soverridesCLAUDE_MCP_TIMEOUT_MSfor this run.
Like create, the run is a background job and the call returns within about 2 seconds. Until status is "done", poll claude_get_session with job_id and read local_job. starting means claude -p hasn't reported a session yet, running means session_id is known, and claude_cancel_session with the job_id stops the run. A finished run:
{
"job_id": "job_9c1d0e2f3a4b5c6d7e8f9012",
"status": "done",
"elapsed_ms": 48210,
"session_id": "1f0e2c9a-6d0b-4c11-9f39-2a77c1d4e8b5",
"cwd": "/Users/dev/GitRepos/app",
"permission_mode": "acceptEdits",
"restricted": true,
"assistant_messages": 6,
"parsed": true,
"subtype": "success",
"is_error": false,
"result": "Fixed the failing test in src/parse.ts.",
"num_turns": 7,
"total_cost_usd": 0.0421,
"usage": { "input_tokens": 1200, "output_tokens": 950, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 18000 },
"permission_denials": ["Bash"],
"rate_limit": null,
"errors": [],
"files": [
{ "path": "src/parse.ts", "status": " M", "from": null, "before": null },
{ "path": "notes.md", "status": " M", "from": null, "before": " M" }
],
"args": ["-p", "--restricted", "--strict-mcp-config", "--output-format=stream-json", "--verbose", "--permission-mode=acceptEdits", "--", "Fix the failing test"],
"cli": { "exit_code": 0, "stdout": "", "stderr": "" }
}result,subtype,is_error,num_turns,total_cost_usd,usageandpermission_denialscome from the CLI's final result line. A run that hitsmax_turns, or ends on an API error, is still returned as data:is_erroristrue,subtypesays why (for exampleerror_max_turns), anderrorsholds the CLI's messages.filesisgit statusafter the run (--porcelaincodes), plus paths that were dirty before and are clean now (status: null).beforeis each path's status before the run, sobefore: nullmeans the run changed a clean file. Changes the run committed don't show up.filesisnullwhencwdisn't a git work tree, and until the job is done.The stream-json transcript is read as it arrives and not kept:
cli.stdoutis empty when the result line was read, and otherwise holds the last 4,000 characters for diagnosis.cli.stderris its last 4,000 characters.With no result line, a non-zero exit is
CLI_EXIT_NONZERO(orCLI_NOT_SIGNED_IN), and running past the time limit isCLI_TIMEOUT, whose message names the session so it can be resumed.
claude_usage
Reports how much of the Claude plan's usage allowance is used, the same numbers /usage shows in Claude Code ("Current session: 68% used · resets at 5:10pm"). Runs:
claude -p --restricted --strict-mcp-config --output-format=stream-json --verbose -- /usage
in CLAUDE_MCP_REPO_CACHE. /usage is a local command: Claude Code reads the plan's meters and runs no model turn, so a call costs nothing and takes about 2 seconds. Restricted mode means none of your hooks or settings run. A reading is reused for 60 seconds, and concurrent calls share one run.
Input schema:
{
"type": "object",
"properties": { "refresh": { "type": "boolean" } },
"additionalProperties": false
}refresh: true reads usage now instead of reusing a recent reading. Output:
{
"checked_at": "2026-11-02T20:41:07.120Z",
"cached": false,
"parsed": true,
"windows": [
{ "kind": "session", "group": "session", "percent": 68, "resets_at": "2026-11-02T22:10:00.474794+00:00", "severity": "normal", "is_active": true, "scope": null },
{ "kind": "weekly_scoped", "group": "weekly", "percent": 0, "resets_at": "2026-11-02T21:00:00+00:00", "severity": "normal", "is_active": false, "scope": "Cowork" }
],
"extra_usage": { "is_enabled": false, "monthly_limit": null, "used_credits": null, "utilization": null, "currency": null },
"stop_percent": 90,
"blocked_by": null,
"args": ["-p", "--restricted", "--strict-mcp-config", "--output-format=stream-json", "--verbose", "--", "/usage"],
"cli": { "exit_code": 0, "stdout": "", "stderr": "" }
}windowslists each meter the plan has:sessionis the rolling session window,weekly_allthe all-models weekly window, andweekly_scopeda weekly window for one model or surface (named inscope).percentruns from 0 to 100, andresets_atis when the window resets.severityis the plan's own reading (normal,warning,critical).windowsisnullwhen Claude Code can't read the plan's usage, for example with API-key auth or a token without the profile scope.extra_usageis the usage-credit spend for the billing period, in minor units ofcurrency, when the plan has credits.stop_percentisCLAUDE_MCP_USAGE_STOP_PERCENT, andblocked_bynames the window that makes new work fail withUSAGE_LIMIT_NEAR(nullwhen nothing blocks).The report's text and its summary of local activity aren't returned:
cli.stdoutis empty when the report was read.
Orchestrators should check claude_usage before fanning out, and schedule new work after resets_at instead of retrying into a limit. During a claude_run_local run, the job's rate_limit field carries the latest rate-limit event the CLI reported (allowed_warning when nearing the limit, rejected when it's hit).
claude_auth_status
Reports the CLI's sign-in state. Runs: claude auth status
Input schema:
{ "type": "object", "properties": {}, "additionalProperties": false }Output (fields the CLI does not print are null):
{
"loggedIn": true,
"authMethod": "claude.ai",
"email": "dev@example.com",
"orgName": "Example Org",
"subscriptionType": "max",
"cli": { "exit_code": 0, "stdout": "{...}", "stderr": "" }
}Errors
Failures come back as MCP tool errors (isError: true). The text content block holds JSON with this fixed shape:
{
"error": {
"code": "CLI_EXIT_NONZERO",
"message": "claude exited with code 1: Error: Unable to create cloud session ...",
"args": ["--cloud=..."],
"cli": { "exit_code": 1, "stdout": "", "stderr": "Error: ..." }
}
}
| When |
| The CLI exited non-zero or was killed by a signal. |
| The CLI ran longer than |
|
|
| The sign-in check before create or a local run found |
| The process could not be started for another reason. |
|
|
| Create could not start the Python interpreter that allocates the pseudo-terminal ( |
| Create reached Claude Code's workspace-trust dialog and |
|
|
| Preparing the GitHub cache checkout failed (git missing, clone or fetch error, |
| Session status was called without |
|
|
|
|
|
|
|
|
Schema violations (a missing required field, an unknown property, a value that doesn't match a pattern) are rejected by the MCP SDK's input validation as tool errors, and the CLI is never run.
Client configuration
Cursor, Claude Desktop, and a generic stdio entry are in Install. The same npx command is what Muse, Grok Bot, ChatGPT Dots, Claude Duet, and other orchestrators should launch. Pass CLAUDE_CLOUD_ENVIRONMENT_ID when every create should target one cloud environment, CLAUDE_MCP_REPOS for local checkouts a create call may select, and CLAUDE_MCP_REPO_CACHE to choose where GitHub repositories are cloned:
{
"mcpServers": {
"claude-code": {
"command": "npx",
"args": ["-y", "@sohampatwardhan/claude-code-mcp-server@latest"],
"env": {
"CLAUDE_MCP_CWD": "/absolute/path/to/your/repo",
"CLAUDE_CLOUD_ENVIRONMENT_ID": "ccpool_your_environment_id",
"CLAUDE_MCP_REPOS": "/absolute/path/to/a/local/checkout",
"CLAUDE_MCP_REPO_CACHE": "/absolute/path/to/the/github/cache"
}
}
}
}A global install (npm install -g @sohampatwardhan/claude-code-mcp-server) exposes the same bin as "command": "claude-code-mcp-server" with "args": [].
An orchestrator on another machine connects over HTTP instead: a Streamable HTTP server entry with the URL and the bearer token header, in whatever form the client takes it, for example:
{
"mcpServers": {
"claude-code": {
"type": "http",
"url": "https://mymac.tail1234.ts.net/mcp",
"headers": { "Authorization": "Bearer <CLAUDE_MCP_TOKEN>" }
}
}
}Reporting problems (Muse, Grok Bot, ChatGPT Dots, Claude Duet and other agents)
The server's MCP instructions ask calling agents to report problems with the server itself as issues on this repository, using the agent's own GitHub access (the server makes no GitHub calls for this). If your client doesn't pass server instructions to its model, put this text in the agent's own instructions:
Report problems with this server as GitHub issues at https://github.com/sohampatwardhan/claude-code-mcp-server/issues, using your own GitHub access. Report a tool result that looks wrong for what happened: an error code that doesn't fit, an unexpected CLI_EXIT_NONZERO or CLI_TIMEOUT, parsed false on output that did name a session, a job that never finishes, or a CLI prompt or message the server didn't recognise. Search open issues first and comment on a match instead of opening a duplicate. Title it
fix: <what went wrong>(orfeat: <what is missing>), and include the tool name and arguments, the error code and message, cli.exit_code and the relevant lines of cli.stdout and cli.stderr, the server version from serverInfo, the host OS, what you expected, and whether it happens again. Before posting, remove tokens, credentials, email addresses, and private repository names, paths and code; the issue tracker is public. Don't file issues for problems in the session's own work (failing tests, a wrong change), or for an error whose message already says what to do (CLI_NOT_FOUND, CLI_NOT_SIGNED_IN, WORKSPACE_NOT_TRUSTED, REPO_NOT_ALLOWED) unless doing that didn't help.
Agents report server bugs: an error code that doesn't fit, a timeout or parse failure, a CLI prompt the server didn't recognise. They don't report failures in the session's own work, or setup errors whose message already says what to do. Reports are public, so the instructions tell agents to strip tokens, email addresses and private repository details first.
Development
git clone https://github.com/sohampatwardhan/claude-code-mcp-server.git
cd claude-code-mcp-server
npm install
npm run build
npm test # unit tests + stdio smoke test (uses a fake CLI; creates no sessions)src/
index.ts entry point (bin): loads config, starts the stdio server
server.ts tool definitions, input/output schemas, error shape
argv.ts deterministic argv construction for each tool
repos.ts per-call directory, GitHub checkout cache, session record, gh pr list, git status
local.ts CLAUDE_MCP_LOCAL_ROOTS and the local-run directory check
parse.ts CLI output parsing (session id / URL regexes, teleport branch and PR, auth JSON, stream-json, git status, /usage report)
runner.ts child_process spawn with timeout and output caps (follow-up, session status, local runs, and auth)
pty.ts pseudo-terminal spawn used by create (embedded Python helper)
trust.ts workspace-trust dialog detection and the opt-in ~/.claude.json edit
jobs.ts in-memory create and local-run jobs (ids, status, retention)
children.ts registry of child processes: process groups, cancel scopes, shutdown
http.ts opt-in Streamable HTTP transport: Host/Origin checks, bearer token, stateless per-request servers
config.ts environment variable parsing and validation
test/
parse.test.ts parser unit tests against realistic CLI output
argv-config.test.ts argv and config unit tests
repos.test.ts allowlist, path checks, GitHub cache, and base branch
trust.test.ts trust-dialog detection, ~/.claude.json edits, worktree path
jobs.test.ts job store: ids, status, retention
children.test.ts process groups, scope cancel, stopAll
http.test.ts HTTP transport over real requests: token, Host/Origin, limits, shared jobs, shutdown
local.test.ts local roots and cwd checks: symlinks, /var vs /private/var, prefixes
smoke.test.ts starts the built server over stdio and exercises every tool
fixtures/fake-claude.mjs stand-in CLI used by the smoke test (TTY, trust dialog, teleport, print-mode stream-json, /usage)
fixtures/fake-gh.mjs stand-in gh used when session status looks up a pull requestprepack compiles TypeScript into dist/ and marks dist/src/index.js executable before the package is packed. CI runs npm ci, then npm run build and npm test.
Releasing
The owner published the first version, 0.1.1, once from a maintainer machine (npm refused 0.1.0 because that version number had been used before). Trusted publishing on npmjs.com can be saved only after that package exists. Later versions then publish from the tag workflow. Skip the v0.1.1 tag for this bootstrap: the workflow would try to publish 0.1.1 again.
npm login
npm ci
npm publish --access publicLeave --provenance off for that local publish. Provenance is added by the GitHub Actions workflow, which has the OIDC token.
On npmjs.com, open @sohampatwardhan/claude-code-mcp-server → Settings → Trusted publishing (the section is also labeled Trusted Publisher). Choose GitHub Actions and enter:
Field | Value |
Organization or user |
|
Repository |
|
Workflow filename |
|
Environment name | empty |
The filename is release.yml only, not .github/workflows/release.yml. Allow direct npm publish. A publisher created after 3 September 2026 starts with npm stage publish allowed and does not allow npm publish until that action is selected. Leave dist-tag management off. The same link can be created with npm CLI 11.15.0 or newer:
npm trust github @sohampatwardhan/claude-code-mcp-server \
--repo sohampatwardhan/claude-code-mcp-server \
--file release.yml \
--allow-publishThe first successful publish from that workflow has to happen within two days of saving the publisher, or the configuration expires. Cut the next version for that check:
npm version patch # or minor, or major
git push --follow-tagsnpm version writes the version and a vX.Y.Z tag. .github/workflows/release.yml runs on that tag (and on a manual run that names the tag). It refuses the job when the tag is not v plus package.json "version", then runs npm ci, build, and test, publishes with npm publish --provenance --access public, and opens a GitHub Release with generated notes. Trusted publishing authenticates that step with GitHub OIDC.
License
Available Tools
7 toolsclaude_auth_statusClaude Code auth statusARead-onlyIdempotent
Report whether the local Claude Code CLI is signed in, via claude auth status. If it is not, sign in with claude first (run it once in a terminal and complete the claude.ai login). A missing CLI returns CLI_NOT_FOUND with the install command (npm install -g @anthropic-ai/claude-code).
Inputs: no named parameters. Returns: cli, email, orgName, loggedIn, +2 more
claude_cancel_sessionCancel a create job or local runADestructiveIdempotent
Stop a claude_create_cloud_session job or claude_run_local job that is still running: every process it started on this machine (git, the pty helper, claude --cloud or claude -p) gets SIGTERM, then SIGKILL 5 s later. Pass the job_id, or for a create job the session id or URL it already printed. Waits up to about 6 s for the processes to exit and reports stopped. This does not stop a cloud session: one the CLI already created keeps running at its session_url. A job that already finished is left as it is (cancelled false), and one that failed returns its error.
Inputs: session (required). Returns: job, stopped, cancelled, local_job
claude_create_cloud_sessionCreate Claude Code cloud sessionA
Create a new Claude Code cloud session via claude --cloud on a pseudo-terminal (the CLI rejects this form when stdout is a pipe). The work runs as a background job and this call returns within a few seconds. If status is done, the result is final: the parsed session_id and session_url, or, when parsing fails, parsed false and the CLI output (terminal controls removed) in cli. Otherwise poll claude_get_session with job_id until job.status is done or the call returns an error; failures surface there with the same error codes. A github.com repo is cloned into CLAUDE_MCP_REPO_CACHE (origin set to that GitHub repository) and, when base_branch is set, checked out there before the CLI runs. Any other absolute directory must be on CLAUDE_MCP_REPOS. Omit repo to use CLAUDE_MCP_CWD. When Claude Code asks to trust the chosen folder, the call fails with WORKSPACE_NOT_TRUSTED unless CLAUDE_MCP_TRUST_CWD=1. The cloud VM's clone, push, and pull request use the Claude GitHub app (or /web-setup), not this server. If the session later can't push its branch to GitHub, ask it via claude_send_followup for a git format-patch (or full git diff) of its changes and apply, test, push and open the PR yourself.
Inputs: description (required), repo, timeout_s, base_branch, +2 more. Returns: cli, cwd, args, repo, +10 more
claude_get_sessionCloud session GitHub statusAIdempotent
With a job id from claude_create_cloud_session, report that create's progress in job (status, elapsed time, the tail of the CLI output, and session_id once the CLI prints it), its final result once job.status is done, or its error. With a job id from claude_run_local, report that run in local_job the same way. A job read uses memory only and runs nothing. With a session id or URL, report the branch a cloud session pushed and any pull request it opened. Prepares the github.com checkout in CLAUDE_MCP_REPO_CACHE (origin set to that repository), then runs print-mode claude --teleport=<session> --debug there and stops when the CLI exits. Print mode checks out the session branch and exits; it is not given a prompt and it is not started on a terminal, so it does not continue as a local interactive session. The branch is taken from the debug line Branch after checkout when that matches HEAD, otherwise from a HEAD move. The pull request number and URL come from that output when present, otherwise from a read-only gh pr list. Claude's GitHub app (or /web-setup) is what pushes the branch and opens the pull request; this tool only reads them.
Inputs: session (required), repo. Returns: cli, job, args, repo, +9 more
claude_run_localRun Claude Code locallyADestructive
Run Claude Code headless on this machine (claude -p) in an existing checkout, instead of a cloud session: for work that needs the local toolchain or hardware. cwd must be inside a CLAUDE_MCP_LOCAL_ROOTS directory after resolving symlinks; with no roots configured every call fails with LOCAL_RUNS_DISABLED. Unless CLAUDE_MCP_LOCAL_ALLOW_SHELL is set, the run uses Claude Code's --restricted mode: no shell or code tools, no WebFetch, no MCP servers, and the checkout's settings, hooks and .mcp.json are not loaded (needs Claude Code 2.1.248 or later). The run is a background job and this call returns within a few seconds: if status is not done, poll claude_get_session with job_id until local_job.status is done or the call returns an error. The final result has the session_id (pass it as resume to continue), the result text, turns, cost and usage, denied tools, and the files git status lists. permission_mode defaults to dontAsk, which denies anything not read-only or in allowed_tools; permission bypass is never used. Pre-approving Bash or using auto mode runs shell commands unasked and fails with LOCAL_SHELL_DISABLED unless CLAUDE_MCP_LOCAL_ALLOW_SHELL is set. claude_cancel_session stops a run.
Inputs: cwd (required), prompt (required), model, resume, +4 more. Returns: cli, cwd, args, files, +17 more
claude_send_followupSend follow-up to Claude Code cloud sessionA
Queue a follow-up message into an existing Claude Code cloud session via claude --cloud <session> -p <message>. Replies are only visible at the session_url; claude_get_session reports the pushed branch and pull request. If the session can't push to GitHub, use this to ask for git format-patch origin/<base>..HEAD --stdout (or a full git diff including new files), then apply it locally with git am / git apply --3way and push it yourself.
Inputs: message (required), session (required). Returns: cli, args, sent, parsed, +3 more
claude_usageClaude plan usageARead-onlyIdempotent
Report how much of the Claude plan's usage allowance is used: each window (the rolling session window, weekly windows, model- or surface-scoped ones) with its percent, reset time and severity, plus usage-credit spend. Runs print-mode claude -p --restricted -- /usage, a local command that makes no model call and costs nothing; readings are reused for 60 s unless refresh is true. When CLAUDE_MCP_USAGE_STOP_PERCENT is set, blocked_by names the window that makes create and claude_run_local fail with USAGE_LIMIT_NEAR. windows is null when the CLI can't read plan usage.
Inputs: refresh. Returns: cli, args, cached, parsed, +5 more
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 7 tools
Each tool has a largely distinct role: create cloud session, run local job, send follow-up, cancel, usage, auth. The main soft spot is claude_get_session, which serves two purposes (polling a background job and inspecting a session's branch/PR), but its description clearly separates the job-id versus session-id modes so an agent can still pick correctly.
All names share a claude_ prefix and snake_case, with verb_noun forms like create_cloud_session, send_followup, get_session, cancel_session, run_local. The two noun-only names (claude_usage, claude_auth_status) are a minor deviation but are still readable as report-style tools.
Seven tools is well-scoped for a Claude Code job runner: lifecycle (create/run/cancel/poll), interaction (followup), and environment checks (auth/usage). No tool feels redundant or missing from the count perspective.
The surface covers the full lifecycle of cloud and local jobs plus auth and usage preflight checks, which are the key dead-end preventers. The notable gap is the absence of a list/enumerate tool, so agents must retain session and job ids themselves, with no cleanup operation for stale sessions.
Maintenance
Related MCP Connectors
Source-checked CLI guides and model-aware planning for Claude Code, Codex, and Grok Build.
Work on your own computers from Claude or ChatGPT: run commands, edit and search files.
Use your Mac, Windows or Linux computer from ChatGPT, Claude or Codex: files, commands, documents.
Persistent cloud workspaces for AI agents: run commands, edit files, use git and a browser.
Related MCP Servers
- AlicenseAqualityDmaintenanceWraps Anthropic Claude Code CLI as tools, allowing MCP clients to invoke headless Claude Code sessions.2393 npmMIT
- AlicenseAqualityDmaintenanceEnables AI agents to interact programmatically with Claude Code CLI, managing sessions, streaming outputs, and handling permission requests.73 npm1MIT
- AlicenseNot gradedqualityDmaintenanceDelegates coding tasks to the local Claude Code CLI via MCP, offering run, review, and status tools for MCP-compatible agents.25 npmMIT
- AlicenseNot gradedqualityBmaintenanceEnables voice assistants to start, monitor, message, and approve local Claude Code sessions on your computer, including sessions already open in a terminal. It exposes session management, recaps, approvals, and history search as MCP tools.7GPL 3.0