mergestorm-mcp
v0.2.23
Published
Mergestorm MCP server — identity, review, and read-only stack tools over stdio
Maintainers
Readme
mergestorm-mcp
Stdio MCP server for Mergestorm. Tools: whoami, credits, review_list, review_submit, review_wait, review_wait_pr, review_dismiss, issue_create, stack_adopt, stack_list, stack_publish, stack_publish_status, stack_set, stack_status, stack_wait, queue_status, settings_get, settings_set, repo_settings_get, repo_settings_set.
Every tool declares MCP annotations for read-only access, destructive changes, idempotence, and external systems. Reads are read-only; submission, adoption, dismissal, and policy changes are writes. Tools that can change existing policy, topology, or review gates are marked potentially destructive even when some inputs only preview or read. Annotations describe effects; the client still controls approval and authorization.
review_wait({ job_id, timeout_s? }) reads a local review job by the job_id that review_submit returned. review_wait_pr({ owner, repo, pr_number, after_sha?, pass?, after_pass?, timeout_s? }) reads the Vortex pass on a GitHub PR. Both poll one 45s slice by default; timeout_s: 0 reads once without polling.
review_dismiss({ owner, repo, pr_number, head_sha, review_id, finding_ids?, scope?, reason?, evidence_url?, preview? }) records an audited dismissal of Vortex findings the agent verified are wrong. The rules:
- Exact identity.
head_shais the PR's live head andreview_idis a Vortex review (Core or seam) made at that head.finding_idsare that review's GitHub review comment ids (offdiff-<n>for a finding listed only in the review body,reviewfor a body-only review).scope: "review"with nofinding_idscovers every finding of the review. - Preview first.
preview: truelists the review's finding ids and the seam gate, and writes nothing. - Authorization. The API key's account must monitor the repository or own the PR's stack, and its linked GitHub account needs write access to the repository.
- Refusals. A moved head (
stale_head), another review (review_mismatch,review_not_found), an id outside that review (unknown_finding), a thinreason(invalid_input) or a missing permission (forbidden) is refused, and nothing is written. - Audit and retries. Each finding is stored with the actor's GitHub login, the reason,
evidence_url, the scope and the time. A retry reports the earlier record underalready_dismissed. - Gates.
gate.seam.clearedis true only when every finding of that integration review is dismissed and it is still the member's current seam verdict.gate.vortex.clearedis true once every Vortex Blocker (error finding) at the live head is dismissed (every Core review merged into that head) and no Vortex review of the head is running or incomplete; with a warning still open the head readscomment, which Auto land accepts; otherwisegate.vortex.reasonandmessagesay what still holds it: that head's Vortex verdict becomes approve for Auto land, the stack watch, the dashboard and Cyclone. A partial dismissal clears nothing. CI and Auto land policy are unchanged (gate.other_gates: "unchanged"). For a stack PR the result carrieswatchwith the nextstack_waitcall. - No resurfacing. Vortex does not raise a dismissed finding again on the same diff: the same head, or a restack whose diff is unchanged.
issue_create({ title, body?, labels?, owner?, repo?, cwd? }) opens a GitHub issue through Mergestorm with no GitHub credentials: the Surge App creates it and names the caller in the body. It defaults to the origin repository of cwd. Labels must already exist on the repository. The same title again returns the open issue it already made (created: false), so a retry does not duplicate it. Put Fixes #<number> in the commit or pull request body.
The stack tools expose reads, one scoped policy write, and publication of your own branch:
stack_adopt({ owner, repo, pr_number, auto_land?, auto_review?, auto_patch?, auto_resolve_conflicts?, auto_fix_ci? })adopts an open PR chain with optional per-stack policy. Omitted policy does not force Cyclone off.auto_review,auto_patch,auto_resolve_conflicts, andauto_fix_ciaccepttrue,false, ornull;nullclears the override. Readstack_statuswith the returnedresult.stack.idto verify policy and Cyclone ownership. Adoption needs the Mergestorm Surge GitHub App on the repository (Cyclone is the auto-patch App). A refusal keeps the codecyclone_not_connectedorcyclone_not_installed; its message, anderror.reasonstarting withsurge_when Surge is the one to install, say which App is missing.stack_listreturns the current API key owner's registered stacks.stack_publish({ cwd?, owner?, repo?, branch?, base?, title?, body?, replace_head?, auto_land? })publishes a local branch with no GitHub credentials. It reads the branch's commits from the repository atcwdand sends them to Mergestorm, which writes them and the branch on GitHub as the Surge App, opens a ready pull request, and registers the stack with Auto patch pinned off, Auto land on for a new stack, and Auto review left to the account. It never runsgh,git pushorgit fetch. Call it again for every later commit to that branch, and after a lost response: it resumes and does not duplicate the branch, commits, pull request or stack. It refuses withhead_changedwhen the branch on GitHub holds work the local branch does not;replace_headnames the exact GitHub head to replace. Limits: 20 commits, 500 changed paths, 2 MiB a file, 8 MiB in total; no submodules or merge commits. The result carries the verified head commit and tree, the pull request, the stack and itswatch.stack_publish_status({ cwd?, owner?, repo?, branch? })reads how far a publication got (absent,pushed,opened,registered) with the head on GitHub, the pull request and the stack's Auto patch and Auto land state. It changes nothing.stack_set({ stack_id, auto_land?, auto_review?, auto_patch?, auto_resolve_conflicts?, auto_fix_ci? })sets per-stack policy on one owned stack.auto_landflips Auto land.auto_review,auto_patch,auto_resolve_conflicts, andauto_fix_cipin Vortex auto-review, Cyclone auto-patch, Auto-resolve merge conflicts, or Auto-fix failing CI for that stack in either direction (trueorfalse);nullclears the pin so the stack follows the account setting. Pass at least one key. Account settings are never changed here.stack_status({ stack_id })returns one owned stack with enriched checks and agent state, plusattention,issues, andcurrentCandidatefrom the same blocker rules asstack_wait, read against that stack's own merge queue. It returns a structuredstack_not_foundorrate_limitederror.stack_wait({ stack_id, timeout_s?, enrolled_head_sha?, after_finished_at?, bounce_id? })waits for a stack or queue state change and returns the latest snapshots.timeout_s: 0returns one snapshot; a positive timeout after a snapshot with no attention returns the currentwaitingorin_progressstatus with selectors to use for the next call. An unread deadline returnsfailed. While Cyclone or Vortex is still working on the blocked PR,stack_waitandstack_statusreturnin_progressinstead ofattention, withbusy[]naming the PR, blocker, and agent, andagentscarrying that PR'svortexStatus,cycloneStatus,vortexReview, and busy flags. A nonemptybusy[]means wait. A queued Vortex run holds for at most 15 minutes,seam_pendingalone does not hold, and reviewing or patching holds until the run finishes or its lease or check run is cleared. A blocker no agent run clears (a merge conflict vs the live parent, a restackConflictorRestack failed, a Draft PR, a seam review pending with no review running, "Seam review stuck in reviewing, no review running", "Seam re-review pending, no review running", "Seam verdict is for an older head, no review running", or a review skipped for review quota) is still named during thatin_progress, inblocker,prNumber,headSha, andissues[], withactAfter: "agents_idle"andwaitingOnlisting the busy agents; act when a snapshot after the agents finish returnsattention, re-reading the blocker then. For the three stuck-seam blockers,repairhas kindseam_review_stuck: do not patch or push; tell the human to click Continue, which re-queues the seam review at the live head.repairis included when another concrete repair hint is available. Blockers the agents can change keepblockerandactAfternull. If a hold looks stuck, refreshstack_statusand tell the human.
The background CLI watcher is optional. It monitors Mergestorm without model calls, but does not itself resume an agent conversation. Hand waiting to mg stack watch <stack-id> only after the host has confirmed a notification that resumes the task on completion or output matching MS-WATCH (ATTENTION|LANDED). Without that facility, use bounded stack_wait calls with watch.next.args, or tell the user automatic follow-up is unavailable. A shell session ID is not proof of a wakeup subscription.
watch.next.background is the monitoring command, not a registered notification. Keep the latest cursor across stack_wait calls and reconnects. Cancellation stops the current wait, not the remote stack. After a host notification, read stack_status once and follow the returned repair and ownership rules.
The watcher exits on attention (exit 3), a failed read that failed again on retry (exit 3), a closed, archived, or missing stack (exit 3), or a confirmed landing (exit 0). Verify missing stacks before reporting a landing. The stack is still not done until it lands. Do not subscribe to GitHub events to wait on a stack.
Auth is MERGESTORM_API_KEY, then ~/.mergestorm/config.json (same as mg).
context_files passed to review_submit may only read files under the host-configured sandbox root (MERGESTORM_SANDBOX_ROOT, default: the MCP server's working directory), so a prompt-injected agent cannot use them to exfiltrate files outside it.
Client setup
Requires Node.js 22+ and a Mergestorm API key. Run mg login on the machine running the MCP server, or supply MERGESTORM_API_KEY through the host's environment. The stdio server uses that credential; it does not provide an OAuth endpoint. An agent on that machine can sign in without a browser: mg login --start prints a link and a code and exits, the account owner approves the link on any device, and mg login --finish stores the key. Remote hosts need their own configuration and access to any reviewed checkout.
Codex
codex mcp add mergestorm -- npx -y mergestorm-mcpFor explicit configuration in ~/.codex/config.toml (replace the checkout path):
[mcp_servers.mergestorm]
command = "npx"
args = ["-y", "mergestorm-mcp"]
startup_timeout_sec = 30
tool_timeout_sec = 60
[mcp_servers.mergestorm.env]
MERGESTORM_SANDBOX_ROOT = "/absolute/path/to/checkout"The server reads the login saved by mg login. If using an environment key instead, pass it with env_vars = ["MERGESTORM_API_KEY"] in the server table. Approval remains controlled by the host; annotations do not authorize a write. Codex MCP configuration.
Claude Code
claude mcp add --transport stdio mergestorm -- npx -y mergestorm-mcpLaunch with MERGESTORM_SANDBOX_ROOT set to the checkout when local file context is needed. Claude Code MCP configuration.
Cursor
Add to project .cursor/mcp.json:
{
"mcpServers": {
"mergestorm": {
"type": "stdio",
"command": "npx",
"args": ["-y", "mergestorm-mcp"],
"env": {
"MERGESTORM_SANDBOX_ROOT": "${workspaceFolder}"
}
}
}
}For global ~/.cursor/mcp.json, use an explicit sandbox path. Authentication uses the saved mg login on the server machine. Cursor MCP configuration.
Connection and watch checks
In each client, confirm the server connects and lists tools, then call whoami. It should return the account key prefix without exposing the full key. On a known owned stack, stack_wait with timeout_s: 0 should return one snapshot; use watch.next.args for the next bounded wait. Default waits use 45-second slices, so leave the host tool timeout above 45 seconds.
Standard stdio calls are portable; task wakeups are host features. Claude Code supports background notifications in some session modes, while availability in Codex or Cursor depends on the host and execution mode. Confirm the actual notification facility before claiming automatic continuation. This server does not advertise Claude channels or install a Codex automation or Cursor extension. After reconnecting, reuse the last returned cursor; if it was lost, take a fresh snapshot rather than claiming unseen events were handled.
Tests exercise the SDK protocol, not the UI or notification delivery of all three apps. Client configuration and the host's ability to resume the task should be checked in that client.
Results and errors
Successful results retain their existing fields in structuredContent, including meaningful null values. The same data is serialized as a text block after the human summary for clients that consume only text content. Shared workflow policy stays in server initialization instructions; stack tools include a short reminder to follow the returned watch cursor.
Failures retain isError: true and a readable message. structuredContent.error includes the original message and, when supplied by the CLI error, code, reason, exit_code, and retry_after_seconds. Missing retry metadata does not authorize repeating a write; inspect the relevant state first. The server does not manufacture request IDs or label arbitrary transport failures safe to retry.
