An MCP (Model Context Protocol) server that lets an AI agent drive Codemagic CI/CD — start builds, poll status, and diagnose failures from build logs — built around two hard constraints: token economy and secret privacy.
Privacy in one paragraph. The Codemagic API token is read only from the
CODEMAGIC_API_TOKENenvironment variable — never a tool parameter, never written to disk, never echoed in output or errors. The values of your Codemagic environment variables are never exposed; group and variable names only. Every string the server returns passes through a redaction pass that strips PEM blocks, long base64 blobs, JWTs,key=valuesecrets, credentials in URLs, and signed download URLs. Builds cannot be triggered unless you explicitly setCODEMAGIC_MCP_ALLOW_BUILDS=true. No telemetry, no cache, no filesystem writes. See SECURITY.md for the threat model and the honest limits.
Mobile release engineering is a slow feedback loop: trigger a build, wait, read a long log, fix, repeat. An AI agent can drive that loop — but only if it can start builds, poll status, and read failures without burning its context window on megabytes of log, and without piping your signing keys into a chat transcript.
A real Codemagic iOS build log runs to megabytes. This server turns one into ~730 tokens with the actionable error on the first line:
step: Build ipa (failed, 8m29s)
selected: failed step
mode: error-focused
lines: 41/625
matches: 6
firstError: Error (Xcode): No profiles for 'com.example.myapp' were found: Xcode couldn't find
any iOS App Store provisioning profiles matching 'com.example.myapp'. Automatic signing is
disabled and unable to generate a profile. To enable automatic signing, pass
-allowProvisioningUpdates to xcodebuild.
--
…
Measured numbers for every tool: docs/TOKEN-BUDGET.md.
Letting an AI install it for you: point your agent at docs/AI-INSTALL.md — it is a deterministic, step-by-step recipe written for an agent to follow, with the expected output of each step.
By hand, with a Go toolchain:
go install github.com/lexandro/codemagic-mcp@latestIf you also develop this server, install it somewhere other than go install's output
directory — see Installing alongside development.
Or download a binary for your platform from the
releases page. It is a single static
executable with no runtime dependencies. Verify it against checksums.txt from the same
release — you are about to give this binary an API token.
-
Get an API token: Codemagic → Account settings → API token.
-
Export it in the environment that launches your MCP client:
export CODEMAGIC_API_TOKEN="your-token-here" # macOS / Linux setx CODEMAGIC_API_TOKEN "your-token-here" # Windows, then reopen the terminal
-
Register the server:
codemagic-mcp register project # writes ./.mcp.json codemagic-mcp register user # writes ~/.claude.json
To also enable build triggering, set the gate before registering:
CODEMAGIC_MCP_ALLOW_BUILDS=true codemagic-mcp register project
-
Restart your MCP client and call
statusto confirm.
register writes an entry like this. Note that the token is a reference, not a value — your
MCP client expands it at launch, so this file is safe to commit:
{
"mcpServers": {
"codemagic": {
"command": "/usr/local/bin/codemagic-mcp",
"args": [],
"env": {
"CODEMAGIC_API_TOKEN": "${CODEMAGIC_API_TOKEN}"
}
}
}
}Add "CODEMAGIC_MCP_ALLOW_BUILDS": "true" to the env block to enable start_build and
cancel_build.
| Variable | Required | Effect |
|---|---|---|
CODEMAGIC_API_TOKEN |
yes | The API token. The server refuses to start without it. |
CODEMAGIC_MCP_ALLOW_BUILDS |
no | Set to true to enable start_build and cancel_build. Anything else leaves the server read-only. |
| Flag | Default | Effect |
|---|---|---|
--timeout |
30s |
Timeout for a single Codemagic API request. |
There is deliberately no flag to change the API host. One pointing at another host would send your token there.
| Tool | Purpose | Typical cost |
|---|---|---|
status |
Token validity, reachability of both API hosts, whether builds are enabled | ~40 tokens |
list_apps |
Applications the token can see | ~17 tokens/app |
get_app |
Workflows, branches, environment variable group names | ~55 tokens |
list_builds |
Recent builds, filterable by app, workflow, branch, tag, status | ~24 tokens/build |
get_build |
One build's status — the polling tool | ~52 tokens |
get_build_log |
Diagnose a failure; error-focused by default, never the whole log | ~730–3,000 tokens |
list_artifacts |
What a build produced. Content is never fetched, and signed download URLs are never returned | ~5 tokens/artifact |
start_build |
Trigger a build. Requires CODEMAGIC_MCP_ALLOW_BUILDS=true |
~15 tokens |
cancel_build |
Stop a build. Same gate | ~10 tokens |
Every list tool takes limit (default 10, max 100) and reports clipping as truncated: 10/42.
Every tool takes verbosity (summary by default, detail on request).
$ status
status: ok
account: 60a0b1c2d3e4f56789abcdef
codemagic.io: ok
api.codemagic.io: ok
CODEMAGIC_API_TOKEN is set (36 characters)
redaction: always on
builds: disabled (set CODEMAGIC_MCP_ALLOW_BUILDS=true and restart to enable start_build and cancel_build)
$ get_app appId=60a0b1c2d3e4f56789abcdef
id: 60a0b1c2d3e4f56789abcdef
name: myapp-ios
repository: https://github.com/example/myapp
provider: github
defaultBranch: main
branches: main, develop
workflows: ios-workflow=iOS Release
variableGroups: appstore, firebase
$ get_build buildId=6890abcdef1234567890abcd
id: 6890abcdef1234567890abcd
status: failed
workflow: ios-workflow=iOS Release
branch: main
build: #42
started: 2026-07-26T10:12:03Z
duration: 8m41s
failedStep: Build ipa
commit: a1b2c3d fix: bump build number
$ list_artifacts buildId=6890abcdef1234567890abcd
Runner.ipa 43MB ipa
Fields with nothing to say produce no line at all. That omission is the single largest token saving in the server.
Errors are classified so an agent knows whether to retry, wait, or stop:
Error [fatal]: invalid or expired API token
Action: Generate a new token in Codemagic > Account settings > API token, set CODEMAGIC_API_TOKEN to it, then restart the MCP server
Details: HTTP 401 from api.codemagic.io
transient means retrying helps and the server already retried. degraded means back off — rate
limiting, usually with a wait in seconds. fatal means a human has to change something. The
server never exits on a connection failure: once Codemagic recovers, the next call just works.
Worth knowing before your first build:
- A
codemagic.yamlapp reports the wrong workflows, or none at all. Codemagic does not store the yaml configuration, soget_appreturns whatever workflow the web UI has — often a stale "Default Workflow" that no build has ever used — or nothing. For those apps theworkflowIdyou pass tostart_buildis the workflow key from yourcodemagic.yaml(for exampleios-workflow), not an id fromget_app. This is the most common first-run failure. The reliable way to find the real key islist_builds, which shows the workflow each build actually ran. - Two APIs on two hosts. Codemagic runs a legacy API on
api.codemagic.ioand a current one oncodemagic.io/api/v3, and this server needs both — only v1 can read build logs or start a build, only v3 has typed build detail and variable groups. Egress is restricted to exactly those two hosts. - Some endpoints are undocumented.
GET /builds,GET /builds/:id/step/:stepId(the only way to read a log), and the shape of v1 responses are not in the published docs. Responses are decoded defensively: a missing field becomes an omitted output line, never a crash. - Codemagic returns 404 for things your token cannot see, not 403. The server says "not found, or not accessible with this token" rather than guessing which it was.
- Artifact download URLs are pre-signed in the path, not the query string, so there is nothing
to strip and the link works for anyone who has it.
list_artifactstherefore returns names, sizes and types but no URL. Download from the Codemagic UI. - Build logs arrive as HTML. Codemagic wraps coloured lines in
<span>elements and escapes the content. The markup is stripped and the entities decoded before anything else happens.
One runtime dependency, on purpose:
| Dependency | Why |
|---|---|
github.com/modelcontextprotocol/go-sdk |
The official MCP protocol implementation. |
| Go standard library | HTTP, JSON, regexp, TLS. No HTTP client library, no logging framework. |
go build -o codemagic-mcp . # build
go vet ./... # vet
go test ./... # everything, no live token needed
go test ./test -run Test_TokenBudget -v # regenerate the token budget tableNever register the binary you are rebuilding. A running MCP server holds an open handle on
its own executable, so on Windows the next go install fails with "Access is denied" as soon as
any editor has the server loaded — and the editor that has it loaded is usually the one you are
using to fix it.
Keep the two apart: the dev build stays in the repository, and MCP clients point at an installed copy you update deliberately.
scripts/install.bat # Windows -> %LOCALAPPDATA%\Programs\codemagic-mcp\
scripts/install.sh # macOS/Linux -> ~/.local/bin (override with CODEMAGIC_MCP_PREFIX)Both build first, then install by renaming the old binary aside rather than overwriting it, so updating works while a client is still running the previous version. The client keeps using the renamed file until it restarts, then picks up the new one.
Then register that path:
"$LOCALAPPDATA/Programs/codemagic-mcp/codemagic-mcp.exe" register projectIf you only use this server and never modify it, none of this applies — go install or a release
binary is fine.
Tests never touch the network: they run against a fake Codemagic and a real MCP session, so the redaction middleware and the protocol layer are exercised the same way an agent would.
See CLAUDE.md for the architecture and coding principles.
MIT — see LICENSE.