|
| 1 | +# mcp_server |
| 2 | + |
| 3 | +The `mcp_server` service is a Model Context Protocol (MCP) server implemented with [github.com/mark3labs/mcp-go](https://github.com/mark3labs/mcp-go). It exposes Semaphore workflow, pipeline, and job data to MCP-compatible clients. |
| 4 | + |
| 5 | +## Configuration: |
| 6 | + |
| 7 | +### Claude Code: |
| 8 | + |
| 9 | +In terminal export the env var called MY_MCP_TOKEN with the value of the API token that should be used to connect to Semaphore MCP server. run the following command: |
| 10 | + |
| 11 | +claude mcp add semaphore https://mcp.semaphoreci.com/mcp \ |
| 12 | + --scope user --transport http \ |
| 13 | + --header "Authorization: Bearer $MY_MCP_TOKEN" |
| 14 | + |
| 15 | +Example prompt: "Help me figure out why have my test failed on Semaphore" |
| 16 | + |
| 17 | +### Codex: |
| 18 | + |
| 19 | +Open your ~/.codex/config.toml (if you’re using the CLI) or via the Codex IDE Extension in VS Code (Gear icon → MCP settings → Open config.toml) |
| 20 | + |
| 21 | +[mcp_servers.semaphore] |
| 22 | +url = "https://mcp.semaphoreci.com/mcp" |
| 23 | +bearer_token_env_var = "MY_MCP_TOKEN" |
| 24 | +startup_timeout_sec = 30 |
| 25 | +tool_timeout_sec = 300 |
| 26 | + |
| 27 | +In terminal export the env var called MY_MCP_TOKEN with the value of the API token that should be used to connect to Semaphore MCP server. |
| 28 | + |
| 29 | +You can then use Semaphore MCP in codex CLI by starting it in that same terminal session, or in VS Code codex extension by starting the VS Code from that terminal session with `code <path-to-working-directory>` command. |
| 30 | + |
| 31 | +_Note_: Due to current limitations of Codex extension for VS Code, if you start VS Code in any other way except from the terminal session where MY_MCP_TOKEN env var has correct value, the Semaphore MCP server will not work. |
| 32 | + |
| 33 | +## Contributor Guide |
| 34 | + |
| 35 | +Refer to [`AGENTS.md`](AGENTS.md) for repository guidelines, project structure, and development workflows. |
| 36 | + |
| 37 | +## Exposed tools |
| 38 | + |
| 39 | +| Tool | Description | |
| 40 | +| ---- | ----------- | |
| 41 | +| `echo` | Returns the provided `message` verbatim (handy for smoke tests). | |
| 42 | +| `organizations_list` | Lists organizations that the user can access. | |
| 43 | +| `projects_list` | List projects that belong to a specific organization. | |
| 44 | +| `projects_search` | Search projects inside an organization by project name, repository URL, or description. | |
| 45 | +| `workflows_search` | Search recent workflows for a project (most recent first). | |
| 46 | +| `pipelines_list` | List pipelines associated with a workflow (most recent first). | |
| 47 | +| `pipeline_jobs` | List jobs belonging to a specific pipeline. | |
| 48 | +| `jobs_describe` | Describes a job, surfacing agent details and lifecycle timestamps. | |
| 49 | +| `jobs_logs` | Fetches job logs. Hosted jobs stream loghub events; self-hosted jobs return a URL to fetch logs. | |
| 50 | + |
| 51 | +## Requirements |
| 52 | + |
| 53 | +- Go 1.25 (toolchain `go1.25.2` is configured in `go.mod` and `Dockerfile`). |
| 54 | +- SSH access to `renderedtext/internal_api` for protobuf generation. |
| 55 | + |
| 56 | +## Generating protobuf stubs |
| 57 | + |
| 58 | +The server consumes internal gRPC definitions. Generate (or refresh) the Go descriptors whenever the protos change: |
| 59 | + |
| 60 | +```bash |
| 61 | +cd mcp_server |
| 62 | +make pb.gen INTERNAL_API_BRANCH=master |
| 63 | +``` |
| 64 | + |
| 65 | +`make pb.gen` clones `renderedtext/internal_api` and emits Go code under `pkg/internal_api/`. The generated files are required for builds—remember to commit them after regeneration. |
| 66 | + |
| 67 | +## Configuration |
| 68 | + |
| 69 | +The server dials internal gRPC services based on environment variables. Deployment defaults come from the `INTERNAL_API_URL_*` ConfigMap entries; legacy `MCP_*` variables and historical endpoints remain as fallbacks. |
| 70 | + |
| 71 | +| Purpose | Environment variables (first non-empty wins) | |
| 72 | +| ------- | -------------------------------------------- | |
| 73 | +| Workflow gRPC endpoint | `INTERNAL_API_URL_PLUMBER`, `MCP_WORKFLOW_GRPC_ENDPOINT`, `WF_GRPC_URL` | |
| 74 | +| Pipeline gRPC endpoint | `INTERNAL_API_URL_PLUMBER`, `MCP_PIPELINE_GRPC_ENDPOINT`, `PPL_GRPC_URL` | |
| 75 | +| Job gRPC endpoint | `INTERNAL_API_URL_JOB`, `MCP_JOB_GRPC_ENDPOINT`, `JOBS_API_URL` | |
| 76 | +| Loghub gRPC endpoint (hosted logs) | `INTERNAL_API_URL_LOGHUB`, `MCP_LOGHUB_GRPC_ENDPOINT`, `LOGHUB_API_URL` | |
| 77 | +| Loghub2 gRPC endpoint (self-hosted logs) | `INTERNAL_API_URL_LOGHUB2`, `MCP_LOGHUB2_GRPC_ENDPOINT`, `LOGHUB2_API_URL` | |
| 78 | +| RBAC gRPC endpoint | `INTERNAL_API_URL_RBAC`, `MCP_RBAC_GRPC_ENDPOINT` | |
| 79 | +| Users gRPC endpoint | `INTERNAL_API_URL_USER`, `MCP_USER_GRPC_ENDPOINT` | |
| 80 | +| Featurehub gRPC endpoint | `INTERNAL_API_URL_FEATURE`, `MCP_FEATURE_GRPC_ENDPOINT` | |
| 81 | +| Dial timeout | `MCP_GRPC_DIAL_TIMEOUT` (default `5s`) | |
| 82 | +| Call timeout | `MCP_GRPC_CALL_TIMEOUT` (default `15s`) | |
| 83 | + |
| 84 | +Hosted jobs require `loghub` to be reachable. Self-hosted jobs require `loghub2`. Missing endpoints yield structured MCP errors from the relevant tools. |
| 85 | + |
| 86 | +## Running locally |
| 87 | + |
| 88 | +```bash |
| 89 | +cd mcp_server |
| 90 | +make pb.gen # only needed after proto updates |
| 91 | +go run ./cmd/mcp_server -http :3001 |
| 92 | +# or: make dev.run # launches with stubbed responses on :3001 |
| 93 | +``` |
| 94 | + |
| 95 | +The server advertises itself as `semaphore-echo` and serves the MCP Streamable HTTP transport on `:3001`. Health probes remain on `GET /readyz` and `GET /healthz`. Use `-version` to print the binary version, `-name` to override the advertised implementation identifier, or `-http` to change the listening address. |
| 96 | + |
| 97 | +### Development stubs |
| 98 | + |
| 99 | +When you just want to exercise the MCP tools without wiring real services, export `MCP_USE_STUBS=true` before starting the server. The process will skip gRPC dialing and respond with deterministic in-memory data for workflows, pipelines, jobs, and logs. |
| 100 | + |
| 101 | +```bash |
| 102 | +export MCP_USE_STUBS=true |
| 103 | +go run ./cmd/mcp_server |
| 104 | +# or: make dev.run |
| 105 | +``` |
| 106 | + |
| 107 | +Disable the variable (or set it to anything other than `true`) to talk to real internal APIs again. |
| 108 | + |
| 109 | +> Tip: when [`air`](https://github.com/cosmtrek/air) is installed, `make dev.run` automatically enables hot reloading using `.air.dev.toml`; otherwise it falls back to `go run`. |
| 110 | +
|
| 111 | +## Docker |
| 112 | + |
| 113 | +Build the container image: |
| 114 | + |
| 115 | +```bash |
| 116 | +cd mcp_server |
| 117 | +docker build -t semaphore-mcp-server . |
| 118 | +``` |
| 119 | + |
| 120 | +Run it locally (listening on port 3001): |
| 121 | + |
| 122 | +```bash |
| 123 | +docker run --rm -p 3001:3001 \ |
| 124 | + -e INTERNAL_API_URL_PLUMBER=ppl:50053 \ |
| 125 | + -e INTERNAL_API_URL_JOB=semaphore-job-api:50051 \ |
| 126 | + -e INTERNAL_API_URL_LOGHUB=loghub:50051 \ |
| 127 | + -e INTERNAL_API_URL_LOGHUB2=loghub2-internal-api:50051 \ |
| 128 | + semaphore-mcp-server |
| 129 | +``` |
0 commit comments