Skip to main content
@atlasent/mcp-server brings AtlaSent authorization to agents that speak the Model Context Protocol: Claude Desktop, Claude Code, Cursor, Windsurf, or your own MCP host. The agent asks AtlaSent before a consequential action, receives a Decision, and on allow gets a bounded Permit that must pass verification before the side effect runs.

How it works

Evaluation isn’t execution. An allow Decision alone is not the gate: the Permit has to verify immediately before the protected side effect.

Modes

The server runs in one of two modes, chosen by ATLASENT_MODE:
Local mode isn’t an enforcement boundary. Its permits are unsigned, it has no audit evidence, and its built-in rules allow anything they don’t recognize. Use remote mode for anything that must actually be protected. The server refuses to start in local mode when NODE_ENV=production.
When ATLASENT_MODE is unset, the server uses remote if ATLASENT_API_KEY is set and local otherwise.

Install

Try it with no account (local mode)

Add it to your MCP host with ATLASENT_MODE=local, then ask your agent to “deploy billing-api to production.” The built-in rules deny it because it has no approvals.

Connect to AtlaSent (remote mode)

  1. Create an account.
  2. In the console, open API keys and create a key with the evaluate:write and verify:execute scopes.
  3. Add the server to your MCP host with the key, as shown below.

Claude Desktop

Add to claude_desktop_config.json, then restart Claude Desktop:
  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json

Cursor and Windsurf

Use the same mcpServers block:
  • Cursor: .cursor/mcp.json in the project, or ~/.cursor/mcp.json globally.
  • Windsurf: ~/.codeium/windsurf/mcp_config.json.
Any other MCP host works the same way: command npx, arguments ["-y", "@atlasent/mcp-server"], and the same environment variables. Registry-aware hosts can install it directly as io.github.Atlasent/mcp-server.

Streamable HTTP transport

The server speaks stdio by default. For hosts that connect over HTTP, start it with --transport streamable-http (or set ATLASENT_MCP_TRANSPORT=streamable-http):
By default it listens on http://127.0.0.1:3333/mcp. ATLASENT_MCP_HTTP_HOST, ATLASENT_MCP_HTTP_PORT, and ATLASENT_MCP_HTTP_PATH change that. Set ATLASENT_MCP_HTTP_BEARER to require hosts to send Authorization: Bearer <value>; when it’s unset, the server accepts requests without host authentication and logs a warning at startup, so leave it unset only for loopback, single-user use. The bearer authenticates your host to this server; ATLASENT_API_KEY still authenticates this server to AtlaSent.

Claude Code plugin

A separate, local-only Claude Code plugin makes destructive and shipping commands (for example DROP TABLE, terraform destroy, git push --force, production deploys) wait for a person at the keyboard. It needs no account.
The plugin records that someone at the keyboard said yes. It doesn’t issue an organizational Permit; for that, use the MCP server in remote mode.

Docker

The repository ships a Dockerfile. Build it from a clone of the source repository:

Environment variables

Core tools

The server also exposes read and administrative tools for policies, permits, audit events, evidence exports, webhooks, SCIM, SIEM, and VQP. Call MCP tools/list for the exact inventory of the version you installed. No tool approves an action. Approvals are made by a person in the AtlaSent console, never by an agent.

Agent pattern

A prompt, wrapper, or MCP tool definition isn’t automatically a gate. The side effect must be unreachable unless authorization and Permit verification succeeded.

Fail-closed behavior

Treat each of these as stop:
  • any Decision other than allow
  • a missing Permit
  • an authentication, network, or API error (the server turns errors into deny, or into valid: false on verify)
  • an invalid, expired, revoked, replayed, or mismatched Permit
  • a missing required binding

Binding the Permit to the exact payload and target

To make a Permit valid only for one exact payload, bind a digest at evaluate time and present the same digest at verify time.
  • Send execution_payload_hash as a bare 64-character hexadecimal SHA-256 digest at the top level of the evaluate request, not inside context and not with a sha256: prefix. For ordinary action types the API drops a value, without an error, in any other shape and binds the Permit to its own hash of the request instead, so your digest never constrains execution. Change-control action types deny it outright.
  • The MCP server helps: it strips a sha256: prefix, converts the digest to lowercase, and refuses anything that isn’t a 64-hex SHA-256 value rather than sending it.
  • Check execution_payload_hash_accepted in the evaluate response. false means the Permit isn’t bound to your digest.
  • Pass target_id to bind the Permit to one target. Presenting a different target at verify time fails. Presenting a target at verify time that was never bound at evaluate time checks nothing.

Change plans and approvals

production.deploy, infrastructure.change, production.rollback, and secret.configuration.change require a change_plan ({ operation, revision?, artifact_ref? }, with a revision or an artifact reference). Pass it to deploy_service, evaluate, or atlasent_evaluate. When an action is held for a person, the result carries an approval_request_id. Call atlasent_await_approval with it. A rejection, expiry, or timeout returns no Permit, and the action doesn’t run. If your plan changed while you waited, pass the new plan as change_plan and the server files one linked re-request for it.