cd /news/developer-tools/gocd-mcp-server Β· home β€Ί topics β€Ί developer-tools β€Ί article
[ARTICLE Β· art-65589] src=github.com β†— pub= topic=developer-tools verified=true sentiment=Β· neutral

GoCD MCP Server

A production-grade GoCD MCP Server has been released, enabling AI agents to inspect and operate GoCD pipelines via the Model Context Protocol while enforcing per-user authentication and GoCD's own RBAC. The server offers 17 tools across three risk tiers, including read-only, safe actions, and config editing, with audit logging and health probes.

read11 min views1 publishedJul 20, 2026
GoCD MCP Server
Image: source

A production-grade Model Context Protocol (MCP) server that exposes GoCD over Streamable HTTP, authenticated per-user with GoCD Personal Access Tokens (PAT).

It lets MCP-capable agents (Claude Code, Claude Desktop, IDE extensions, …) inspect and operate GoCD pipelines β€” list and trigger pipelines, read statuses/history/console logs, /cancel, and (optionally) edit pipeline configuration β€” while GoCD's own RBAC is enforced for every call, because the server acts strictly as the authenticated user.

Protocol: MCP revision2025-11-25

, Streamable HTTP transportSDK: officialmodelcontextprotocol/go-sdk

Language: Go (1.25+)Target: a single GoCD instance (verified against GoCD25.4.0

)

FeaturesTools & resourcesRequirementsInstallationConfigurationAuthentication & authorizationRunning the serverConnecting an MCP clientToolsets (risk tiers)ArchitectureSecurityLogging & observabilityDevelopmentTroubleshootingContributingLicense

  • πŸ” Per-user identityβ€” the MCP client presents a GoCD PAT as a bearer token; the server validates it and acts as that user, so GoCD RBAC applies automatically. No shared service account. - 🧰 17 tools across three risk tiersβ€” read-only, safe actions, and config editing β€” gated by a singleTOOLSET

switch. - 🧱 Typed, compact outputsβ€” focused projections of GoCD's responses to keep token usage low. - πŸ““ Audit logβ€” every mutating operation is logged (login, action, target); tokens are never logged. - 🩺 Health probesβ€”/healthz

(liveness) and/readyz

(checks GoCD reachability). - πŸ§ͺ Well-testedβ€” unit (domain), contract (GoCD client viahttptest

), and end-to-end (MCP over the real SDK client) tests.

Mutating tools carry MCP annotations (destructiveHint

/ idempotentHint

) so the host can ask for user confirmation before running them.

Tool Toolset Kind Description
whoami
all read Authenticated GoCD login (verifies the token)
list_pipelines
all read Dashboard pipelines with latest run status, /lock state (optional group filter)
get_pipeline_status
all read / lock / schedulable state of a pipeline
get_pipeline_history
all read Past runs (paginated via offset ) with stage statuses
get_pipeline_instance
all read One run's detail incl. per-stage and per-job state/result
get_job_console_log
all read Console log of a job run (last tail_lines , default 200)
list_agents
all read Build agents and their config/runtime state
get_pipeline_config
all read Full pipeline config + ETag (needed to update)
trigger_pipeline
actions, full action Schedule a pipeline run
_pipeline
actions, full action a pipeline (reason required)
un_pipeline
actions, full action Resume a d pipeline
cancel_stage
actions, full action Cancel a running stage
comment_on_pipeline
actions, full action Comment on a pipeline instance
update_pipeline_config
full destructive
Replace a pipeline config (optimistic locking via ETag/If-Match)
create_pipeline
full destructive
Create a new pipeline in a group
update_agent
full destructive
Patch an agent (enable/disable, resources, environments)
delete_pipeline
full destructive
Delete a pipeline (irreversible)

Read-only mirrors of dashboard data, addressable by URI for a user/app to attach as context:

gocd://dashboard

β€” all pipelines and latest statusgocd://agents

β€” all build agentsgocd://pipeline/{name}/config

β€” a pipeline's configurationgocd://pipeline/{name}/history

β€” a pipeline's recent runs

  • Go 1.25+(to build), or Docker - Network access to a GoCD server, and its URL ( GOCD_BASE_URL

β€”required, no default) - A GoCD Personal Access Token per user (see below) - TLS certificate/key for production (or a TLS-terminating proxy)

go install github.com/ivinco/gocd-mcp/cmd/gocd-mcp@latest   # -> $GOBIN/gocd-mcp
git clone https://github.com/ivinco/gocd-mcp.git
cd gocd-mcp
go build -o gocd-mcp ./cmd/gocd-mcp

The image is multi-stage and runs on distroless/static

as a non-root user (nonroot

, uid 65532). It carries no configuration of its own: mount the config file (and TLS certificates), and pass any overrides via environment variables.

docker build -t gocd-mcp .

Recommended: config file + certs mounted, secrets/overrides via env.

Prepare a config.yaml

(see config.example.yaml) and your TLS material, then mount them read-only and point

CONFIG_FILE

at the mounted path:

docker run --rm -p 8443:8443 \
  -v /etc/gocd-mcp/config.yaml:/etc/gocd-mcp/config.yaml:ro \
  -v /etc/gocd-mcp/certs:/etc/gocd-mcp/certs:ro \
  -e CONFIG_FILE=/etc/gocd-mcp/config.yaml \
  gocd-mcp

Because the file wins over the environment, you can ship a baseline config.yaml

and still override a single value at runtime only for keys the file omits β€” keep environment-specific or sensitive values out of the file and inject them with -e

(e.g. from your secret store):

docker run --rm -p 8443:8443 \
  -v /etc/gocd-mcp/config.yaml:/etc/gocd-mcp/config.yaml:ro \
  -v /etc/gocd-mcp/certs:/etc/gocd-mcp/certs:ro \
  -e CONFIG_FILE=/etc/gocd-mcp/config.yaml \
  -e LOG_LEVEL=debug \
  gocd-mcp

Note: the file takes priority, so a key set in

config.yaml

cannot be overridden by-e

. Put values you want to override at runtime in the environment and omit them from the file.

Env-only (no file) β€” omit CONFIG_FILE

and pass everything as environment variables:

docker run --rm -p 8443:8443 \
  -e GOCD_BASE_URL=https://gocd.example.com \
  -e TLS_CERT_FILE=/certs/tls.crt -e TLS_KEY_FILE=/certs/tls.key \
  -e TOOLSET=readonly \
  -v /etc/gocd-mcp/certs:/certs:ro \
  gocd-mcp

Each MCP user still authenticates with their own GoCD PAT via the Authorization: Bearer

header (see Authentication); the container holds no GoCD credential of its own.

The server can be configured from environment variables, from a YAML config file, or both. Precedence is:

built-in default  <  environment variable  <  YAML file   (the file wins)

A key present in the file overrides the matching environment variable; a key omitted from the file falls back to the env var, then to the default. The file is loaded only when CONFIG_FILE

points at it (otherwise configuration is env-only, as before).

Variable YAML key Description Default
CONFIG_FILE
β€” Path to a YAML config file (enables file-based config) β€”
GOCD_BASE_URL
gocd_base_url
GoCD server root URL β€” required
β€”
LISTEN_ADDR
listen_addr
Address to listen on :8443
TLS_CERT_FILE
tls_cert_file
TLS certificate (set together with key) β€”
TLS_KEY_FILE
tls_key_file
TLS key (set together with cert) β€”
MCP_ENDPOINT_PATH
mcp_endpoint_path
Path of the MCP endpoint /mcp
GOCD_REQUEST_TIMEOUT
gocd_request_timeout
Per-request timeout to GoCD 30s
TOKEN_CACHE_TTL
token_cache_ttl
How long a validated PAT is cached 60s
TOOLSET
toolset
readonly actions full
full
LOG_LEVEL
log_level
debug info warn error
info
LOG_FILE
log_file
Log destination; empty β†’ stderr, else append JSON to this file β€”

GOCD_BASE_URL

is the only required setting β€” the server refuses to start without it, from either source. Everything else has a working default.

See config.example.yaml for a fully annotated sample. Durations use Go syntax (

30s

, 2m

, 1h

).The MCP client sends the user's GoCD PAT as a bearer token on every request:

Authorization: Bearer <gocd-personal-access-token>

The server validates the token against GoCD's current_user

endpoint (caching the result for TOKEN_CACHE_TTL

), then builds a per-request GoCD client bound to that token. As a result, every GoCD call is made as that user and is subject to GoCD's role-based access control. The server stores no credentials of its own.

Design note.This is a deliberate, documented deviation from the OAuth 2.1 section of the MCP spec: GoCD itself is the identity provider and the PAT is the credential (conceptually the stdio "credentials from the environment" model, lifted onto HTTP). A migration path to full OAuth 2.1 is kept open by isolating token verification behind a single interface.

⚠️ TLS is required in production.The PAT travels in theAuthorization

header. Run withTLS_CERT_FILE

/TLS_KEY_FILE

, or terminate TLS at a trusted proxy. The PAT is never logged.

In GoCD, open your user menu β†’ Personal Access Tokens β†’ create a token. Each user uses their own; the token's GoCD permissions define what that user can do through this server.

TLS_CERT_FILE=server.crt TLS_KEY_FILE=server.key \
GOCD_BASE_URL=https://gocd.example.com \
TOOLSET=readonly \
./gocd-mcp

Health checks (unauthenticated):

curl -fsS https://localhost:8443/healthz   # -> ok
curl -fsS https://localhost:8443/readyz    # -> ready (200) if GoCD is reachable

Unauthenticated requests to the MCP endpoint return 401 Unauthorized

.

The binary is static and configuration-free by itself, so a unit is all it takes. Install it to /usr/local/bin/gocd-mcp

, put a config.yaml

next to your TLS material, and point CONFIG_FILE

at it:

[Unit]
Description=GoCD MCP server
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
Environment=CONFIG_FILE=/etc/gocd-mcp/config.yaml
ExecStart=/usr/local/bin/gocd-mcp
Restart=always
RestartSec=5s

[Install]
WantedBy=multi-user.target

Set log_file

in the config to write JSON logs to a path (rotate it with logrotate

, using copytruncate

β€” the server keeps the file open), or leave it unset to log to stderr and let journald collect it.

Point the client at the Streamable HTTP endpoint and supply the GoCD PAT as a bearer token.

Claude Code (.mcp.json

in your project, or via claude mcp add

):

{
  "mcpServers": {
    "gocd": {
      "type": "http",
      "url": "https://your-host:8443/mcp",
      "headers": { "Authorization": "Bearer ${GOCD_PAT}" }
    }
  }
}

Claude Desktop (claude_desktop_config.json

): use the same mcpServers

block with an http

transport and the Authorization

header.

Set GOCD_PAT

in your environment to your GoCD Personal Access Token.

TOOLSET

gates which tools are registered, so you can deploy the least-privileged surface appropriate to a use case:

TOOLSET | Includes | |---|---| readonly | Queries only β€” no state changes | actions | readonly + trigger / / un / cancel / comment | full | actions + config editing (update / create / delete pipeline, update agent) |

A read-only deployment is useful for broad, low-risk access; full

should be reserved for trusted operators (GoCD RBAC still applies on top).

The protocol layer is intentionally thin; all logic lives in a transport-agnostic domain layer, so the GoCD integration is independently testable.

MCP client ──Streamable HTTP (Bearer PAT)──▢ httpx (TLS, middleware, /healthz /readyz)
                                                └─ bearer-token auth (validate PAT vs GoCD)
                                                    └─ MCP server (tools / resources / prompts)
                                                        └─ domain (validation, orchestration)
                                                            └─ gocd (typed REST client, acts as user)
Package Responsibility
cmd/gocd-mcp
Entry point: config β†’ wiring β†’ graceful shutdown
internal/config
Env-based configuration + validation
internal/httpx
net/http server, TLS, middleware (recovery / request-id / access log), health probes
internal/auth
Bearer-token verification (PAT β†’ GoCD), per-request principal, validation cache
internal/mcpsrv
MCP tools/resources, argument schemas, audit logging, error mapping
internal/domain
Input validation and orchestration of GoCD calls (no MCP, no net/http)
internal/gocd
Typed GoCD REST client (pinned API media-type versions, ETag/If-Match, error mapping)
internal/obs
Structured logging (slog)

GoCD's API uses per-endpoint versioned media types (application/vnd.go.cd.vN+json

) and optimistic locking (ETag + If-Match

) for config edits; both are handled in internal/gocd

.

TLS required in production; the PAT is a bearer credential.Tokens are never loggedβ€” access and audit logs contain the GoCD login only.** Authorization is delegated to GoCD**β€” the server never widens a user's permissions.** Audit trail**β€” every mutating action logsaction

,login

, andtarget

.Least privilegeβ€” run withTOOLSET=readonly

(oractions

) where full access isn't needed.Input validationβ€” pipeline/agent identifiers are validated before building request paths.- Destructive tools are annotated so hosts can require explicit user confirmation.

Structured JSON logs (slog

) at LOG_LEVEL

. Destination is LOG_FILE

β€” unset means stderr (captured by Docker/journald); a path appends JSON there (rotate externally, e.g. logrotate). Tokens are never logged. Three event types:

msg | When | Key fields | |---|---|---| request | every HTTP request | method , path , status , duration_ms , request_id , login (for authenticated /mcp calls) | tool_call | every tool invocation (reads and writes) | tool , login , ok , duration_ms , request_id | audit | every mutating tool | action , login , target |

request_id

correlates the request

and tool_call

lines of the same call. Tool arguments are not logged (to avoid leaking config bodies); the audit

line carries the mutation target.

go test ./...     # unit (domain), contract (gocd via httptest), e2e (MCP via SDK client)
go vet ./...
gofmt -l .        # should print nothing
go build ./...

Project layout follows the package table in Architecture. Tests run fully offline (a fake GoCD via httptest

); no live GoCD instance is required.

Symptom Likely cause / fix
401 Unauthorized on /mcp
Missing/invalid Authorization: Bearer <PAT> , or GoCD rejected the token
Tool error "your GoCD user lacks permission" The user's GoCD RBAC does not allow the operation
Tool error "version conflict (ETag mismatch)" Config changed since you read it β€” re-run get_pipeline_config and retry
/readyz returns 503
GoCD is unreachable from the server (check GOCD_BASE_URL / network)
A tool isn't listed It's gated by TOOLSET β€” raise the tier (actions / full )
GOCD_BASE_URL is required on startup
The base URL has no default β€” set it in the config file or the environment

Issues and pull requests are welcome β€” see CONTRIBUTING.md for the build/test loop and the layering rules the codebase keeps to.

Found a security problem? Please report it privately: see SECURITY.md.

MIT Β© Ivinco

── more in #developer-tools 4 stories Β· sorted by recency
── more on @gocd 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain β€” perfect for shipping the agent you just read about.

$git push zahid main
β†’ Live at https://your-agent.zahid.host βœ“
Get free account β†’ Pricing
from €0/mo Β· no card required
LIVE [news/gocd-mcp-server] indexed:0 read:11min 2026-07-20 Β· β€”