Documentation
¶
Overview ¶
Package mcp is Atlas's Model Context Protocol server: it lets an AI agent drive a running Atlas server through tools — deploy a BPMN model, manage design-time projects and artifacts, start an instance, complete human tasks, inspect live runtime state, and maintain the self-service shop's product catalogue.
Shape: an adapter over the HTTP API, on two transports ¶
The server speaks JSON-RPC 2.0 and translates each tool call into an HTTP request against a running Atlas server; it holds no engine state of its own. Two transports share one dispatch path:
- stdio (Serve) — newline-delimited JSON, one message per line. This is the MCP stdio transport a local client (Claude Desktop, Claude Code) spawns.
- Streamable HTTP (ServeHTTP) — the remote transport. Mount it at a path such as /mcp and a remote client can reach the same tools. Mounted through api.WithMCP it sits inside the Atlas server's own access boundary, so under --auth a request without a credential is refused there; the transport then forwards whatever authenticated the caller to the API, so a tool call is exactly as privileged as whoever made it and no more (ADR-0196).
This is deliberate. The engine is a single-writer partition (invariant I3): exactly one goroutine may touch a partition's processor and state, a discipline the api package already enforces behind its HTTP surface. By proxying to that surface rather than embedding the engine, the MCP server can never violate an engine invariant — it only ever makes HTTP calls. It is a pure adapter, and an AI agent sees the same deployments and instances a human sees in the web UI.
Project deletion ¶
A design-time project is a grouping folder, not an execution aggregate (ADR-0034). atlas_delete_project therefore calls the public project DELETE endpoint and removes only that folder. Drafts and decision references tagged with its id remain available as ungrouped artifacts; deployed definitions and process instances are unaffected. The API operation is idempotent and enforces the project owner role when authentication is enabled.
No new dependencies ¶
The protocol is implemented by hand (see server.go), matching the repository's preference for small, self-contained code over pulled-in SDKs. The only surface area is the four MCP methods a tools-only server needs: initialize, tools/list, tools/call, and ping.
Running it ¶
Remote (Streamable HTTP) — atlas serve mounts the transport at /mcp:
atlas serve --addr :8080 # engine + HTTP API + UI + /mcp
Local (stdio) — a per-agent, short-lived adapter an MCP client spawns:
atlas mcp --server http://localhost:8080 atlas mcp --server https://atlas.example.com --token "$ATLAS_TOKEN"
The token is what a stdio adapter authenticates with against a server running --auth, and is needed there: unlike the HTTP transport, which forwards each request's own caller, a stdio adapter is one process with one identity for its whole life. Without it every tool call comes back 401.
What a stdio adapter cannot reach ¶
An API token carries roles, and no API token carries productmanager: minting is admin-only and tokenRoles hands an admin minter the legacy set, which ADR-0315 deliberately kept the role out of so that no account is granted catalogue control by an upgrade. So over stdio the catalogue tools read and do not write, and a write answers 403. Over the HTTP transport, where the caller's own credential is forwarded, a signed-in product manager reaches exactly their own catalogues. See ADR-0376.
For stdio, diagnostics go to stderr; stdout carries protocol traffic only.
Index ¶
Constants ¶
const TransportHeader = "X-Atlas-Via-MCP"
TransportHeader marks an API request as one a tool call made, rather than one a client made directly against /api/v1 with the same credential.
Atlas stamps it on every request entering /mcp and this adapter forwards it, the same way it forwards the caller's own credential and for the same reason: what arrives at the API has to be recognisable as what it is. A token a person approved for the transport alone is otherwise confined away from the very API calls its tools are made of.
It is not a credential this adapter holds — it holds none (ADR-0196). It arrives on the request, is carried verbatim like the other two, and means nothing except to the server that wrote it.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is a thin HTTP client for the Atlas server API (see the api package). The MCP server owns no engine state of its own; every tool call is translated into an HTTP request against a running Atlas server, which remains the single writer of its partition (invariant I3). That keeps the MCP surface a pure adapter — it can never violate an engine invariant because it never touches the engine directly.
func NewClient ¶
func NewClient(baseURL string, opts ...ClientOption) *Client
NewClient builds a Client for the Atlas server at baseURL (e.g. "http://localhost:8080"). A trailing slash is tolerated.
func (*Client) CatalogueOffered ¶ added in v0.9.0
CatalogueOffered asks the server whether it serves the shop, the catalogue, the orders and the inventory, which an operator can switch off with --catalogue=false (ADR-0434). It is how the stdio adapter, a separate process that cannot read the server's flags, decides whether to build itself WithoutCatalogue. A server that does not say — one older than the switch — offers it. An error is returned as an error and not read as either answer: the caller cannot tell a server that is down from one that decided.
It asks with a short deadline of its own rather than the client's thirty seconds. The adapter asks before it reads its first message, and an agent's host waits on that first answer: a server behind a firewall that drops packets would otherwise hold the whole adapter for half a minute to decide which tools to list.
type ClientOption ¶
type ClientOption func(*Client)
ClientOption configures a Client at construction.
func WithBearer ¶
func WithBearer(token string) ClientOption
WithBearer attaches an Authorization: Bearer <token> header to every request that carries no caller credential of its own. That is the stdio adapter's case: it is a per-agent process with one identity for its whole life, given on the command line (atlas mcp --token).
The HTTP transport does not use it. There, each request brings its own caller and forCaller takes precedence — see ADR-0196 for why the adapter no longer holds a credential of its own on that path.
An empty token is a no-op, so callers can pass it unconditionally.
func WithTLSRoots ¶ added in v0.5.0
func WithTLSRoots(pool *x509.CertPool) ClientOption
WithTLSRoots verifies the server's certificate against pool in addition to the host's roots, for the stdio adapter pointed at an https:// Atlas whose certificate an internal CA issued (atlas mcp --tls-ca).
It is a trust anchor and not a way around verification: there is no skip-verify switch here, in api/targetstore.go, or anywhere else in Atlas (ADR-0191). A nil pool leaves the client exactly as it was — verifying against the host's roots — so callers can pass it unconditionally.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server is a Model Context Protocol server that exposes the Atlas API as MCP tools over a stdio JSON-RPC 2.0 transport. It is deliberately dependency-free and hand-written, in keeping with the rest of the repository.
func NewServer ¶
func NewServer(client *Client, opts ...ServerOption) *Server
NewServer builds an MCP server that proxies tool calls to the Atlas server reachable through client.
func (*Server) Serve ¶
Serve runs the JSON-RPC loop, reading newline-delimited messages from in and writing responses to out, until in reaches EOF. It returns the first read error, or nil on a clean EOF. Diagnostics must never be written to out (that is the protocol channel); callers should log to stderr.
func (*Server) ServeHTTP ¶
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request)
ServeHTTP makes Server an http.Handler implementing the MCP "Streamable HTTP" transport, so the same tool surface reachable over stdio (Serve) can be mounted at a path such as /mcp and reached by a remote MCP client — for example a claude.ai custom connector.
Message dispatch is shared with the stdio loop via handleWith; this method is transport only. The handler is stateless: it assigns no Mcp-Session-Id and requires none, which is sufficient for a tools-only server.
Authentication is the api package's, not this handler's: mount it with api.WithMCP and it sits inside the same boundary as every other route, so a request without a credential never reaches here. What this method does is the other half — it forwards the credential the request arrived with to the Atlas API, so a tool call is exactly as privileged as whoever made it. It carries no identity of its own to lend (ADR-0196).
type ServerOption ¶ added in v0.9.0
type ServerOption func(*serverConfig)
ServerOption configures a Server at construction.
func WithoutCatalogue ¶ added in v0.9.0
func WithoutCatalogue() ServerOption
WithoutCatalogue builds an adapter for a server that switched the shop, the catalogue, the orders and the inventory off (--catalogue=false): it neither lists nor dispatches the tools marked Catalogue, so an agent is not offered a capability whose every call would answer "no such endpoint" (ADR-0434). The server is still what refuses them; this only stops the adapter from advertising what is not there.
type Tool ¶
type Tool struct {
Name string
Description string
InputSchema map[string]any
Handler func(c *Client, args map[string]any) (string, error)
// Catalogue marks a tool of the shop, the catalogue, the orders or the
// inventory: one an adapter built WithoutCatalogue does not offer, because the
// server it fronts does not serve the route behind it
// (ADR-0434).
// TestWithoutCatalogueOffersNoToolOfTheArea fails for a tool of the area that
// does not say so.
Catalogue bool
}
Tool is one MCP tool: its advertised name, human/model-facing description, JSON Schema for arguments, and the handler that fulfils a call by talking to the Atlas server.