{"_id":"@atlasent/mcp-server","name":"@atlasent/mcp-server","dist-tags":{"latest":"2.11.0"},"versions":{"2.11.0":{"name":"@atlasent/mcp-server","version":"2.11.0","description":"AtlaSent MCP server — authorize-before-execute for any MCP-compatible AI agent","type":"module","main":"./dist/index.js","types":"./dist/index.d.ts","bin":{"atlasent-mcp":"dist/index.js"},"scripts":{"build":"tsc","prepare":"npm run build","start":"node dist/index.js","dev":"tsx src/index.ts","typecheck":"tsc --noEmit","test":"node --import tsx --test src/server.test.ts src/v2Client.test.ts src/v2Tools.test.ts src/complianceTools.test.ts src/streamableHttp.integration.test.ts src/trajectoryVerify.test.ts","test:integration":"node --import tsx --test src/integration.test.ts src/integration.write.test.ts","demo":"node examples/demo.mjs"},"dependencies":{"@modelcontextprotocol/sdk":"^1.10.1","zod":"^4.4.3"},"devDependencies":{"@types/node":"^25.9.1","tsx":"^4.22.3","typescript":"^5.4.5"},"engines":{"node":">=18"},"publishConfig":{"access":"public","provenance":true},"repository":{"type":"git","url":"git+https://github.com/AtlaSent-Systems-Inc/atlasent-mcp-server.git"},"homepage":"https://github.com/AtlaSent-Systems-Inc/atlasent-mcp-server#readme","bugs":{"url":"https://github.com/AtlaSent-Systems-Inc/atlasent-mcp-server/issues"},"keywords":["mcp","model-context-protocol","atlasent","authorization","policy","agent","ai","claude"],"license":"MIT","_id":"@atlasent/mcp-server@2.11.0","gitHead":"bf7aac48fc7c81dd55e8b89c3acd8e630b4c61d0","_nodeVersion":"22.22.3","_npmVersion":"10.9.8","dist":{"integrity":"sha512-VeUFe9aeeN7nh1u06aAIgwrSp2CjnGxwZ8YDjHZ0TRVzf7ZVcgFcTzPhHLZ5DjImZnMMuiYWh8pSmSf0OXkfbg==","shasum":"ae4d772528a3ab6ad816006e847f8ae7848fe342","tarball":"https://registry.npmjs.org/@atlasent/mcp-server/-/mcp-server-2.11.0.tgz","fileCount":36,"unpackedSize":311205,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@atlasent%2fmcp-server@2.11.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCyfXU0JpIzEYwpUn8lnnvfmNurFrIIDuKQG4wXla/s2AIgDiNfbZdZq3XXNIKK+K4jTrfiSsoWk70GpNDSQOxFca0="}]},"_npmUser":{"name":"bettyc","email":"betty@atlasent.io"},"directories":{},"maintainers":[{"name":"bettyc","email":"betty@atlasent.io"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/mcp-server_2.11.0_1780990993821_0.29820390885470704"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-09T07:43:13.673Z","2.11.0":"2026-06-09T07:43:13.994Z","modified":"2026-06-09T07:43:14.405Z"},"maintainers":[{"name":"bettyc","email":"betty@atlasent.io"}],"description":"AtlaSent MCP server — authorize-before-execute for any MCP-compatible AI agent","homepage":"https://github.com/AtlaSent-Systems-Inc/atlasent-mcp-server#readme","keywords":["mcp","model-context-protocol","atlasent","authorization","policy","agent","ai","claude"],"repository":{"type":"git","url":"git+https://github.com/AtlaSent-Systems-Inc/atlasent-mcp-server.git"},"bugs":{"url":"https://github.com/AtlaSent-Systems-Inc/atlasent-mcp-server/issues"},"license":"MIT","readme":"# @atlasent/mcp-server\n\n**Authorize every AI agent tool call before it executes.**\n\nAn MCP server that plugs into any [Model Context Protocol](https://modelcontextprotocol.io)-compatible agent (Claude Desktop, Cursor, Claude Code, Copilot, LangChain) and enforces a simple contract: *no protected action runs until AtlaSent has authorized it*. Denied calls never reach the target system. Allowed calls return a permit token you can verify afterwards to close the audit loop.\n\nShips with a local rules engine so you can run the full evaluate → act → verify flow in under a minute, with zero credentials. The hosted AtlaSent backend is a configuration swap, not a rewrite.\n\n## Run the demo in 60 seconds\n\n```bash\ngit clone https://github.com/AtlaSent-Systems-Inc/atlasent-mcp-server.git\ncd atlasent-mcp-server\nnpm install\nnpm run build\nnpm run demo\n```\n\nYou'll see five scenarios run end-to-end — two use cases in one demo:\n\n**Deploy gate (CI/CD pipelines):**\n```\nScenario A: agent attempts unauthorized deploy (prod, no approvals)\n    [1] Agent calls deploy_service\n    [2] MCP intercepts → calls authorize(ctx) → policy engine decides\n    [3] Tool execution BLOCKED\n        ✓ deploy did NOT run. The target system was not touched.\n        ✓ reason: Production action 'production.deploy' requires at least one approval...\n\nScenario B: agent attempts authorized deploy (prod, with approval)\n    [3] Tool execution PROCEEDS\n        result: {\"status\":\"deployed\",\"service\":\"billing-api\",...}\n\nScenario C: verify_permit closes the audit loop\n    result: {\"outcome\":\"verified\",\"valid\":true,...}\n```\n\n**Agent tool call governance (MCP / AI-native builders):**\n```\nScenario D: agent evaluates agent.db.delete without a change window\n    [1] Agent calls evaluate before running agent.db.delete\n    [3] Tool execution HELD — awaiting human review\n        ✓ database delete did NOT run. Queued for human review.\n\nScenario E: agent evaluates agent.search.web (safe read)\n    [3] Tool execution PROCEEDS — agent searches the web\n        ✓ web search authorized.\n    [4] verify_permit closes the audit loop: {\"outcome\":\"verified\",\"valid\":true}\n```\n\nAtlaSent governs any `action_type` — not just deploys. The same evaluate →\npermit → verify flow that gates CI/CD pipelines also governs your agent's\ndatabase writes, web searches, and external API calls.\n\nThe demo uses `local` mode (no API key needed). To run the same demo against the hosted AtlaSent backend once your API key is issued:\n\n```bash\nATLASENT_MODE=remote \\\n  ATLASENT_API_KEY=ask_live_xxx \\\n  ATLASENT_BASE_URL=https://api.atlasent.io \\\n  ATLASENT_MCP_READONLY=1 \\\n  npm run demo\n```\n\n`ATLASENT_MCP_READONLY=1` is **recommended for any live-API demo** — see [Read-only mode](#read-only-mode-for-live-demos) below.\n\n## Agent tool call governance\n\nAtlaSent is not a deploy gate product. It is execution-time authorization infrastructure. The `action_type` field is yours to define — any string your agent platform uses to name a tool invocation becomes a protected action.\n\n**Common agent action types:**\n\n```ts\n// Database mutations\n\"agent.db.write\"         // INSERT / UPDATE\n\"agent.db.delete\"        // DELETE / TRUNCATE (destructive — triggers hold)\n\n// External network calls\n\"agent.search.web\"       // Read-only web search\n\"agent.api.post\"         // Outbound POST to an external service\n\n// File system\n\"agent.fs.write\"         // Write or overwrite a file\n\"agent.fs.delete\"        // Delete a file\n\n// Code execution\n\"agent.code.execute\"     // Run user-supplied code\n```\n\n**Pattern — agent evaluates before every sensitive tool call:**\n\n```ts\n// Before running a tool, the agent calls evaluate\nconst ctx = {\n  action_type: \"agent.db.delete\",   // your action namespace\n  actor_id: \"agent:data-pipeline\",   // which agent is acting\n  environment: \"production\",\n  // any context your policies need\n};\n\nconst decision = await authorize(ctx);   // interception point\nif (decision.decision !== \"allow\") {\n  // Tell the LLM the tool was blocked and why\n  return `Tool blocked: ${decision.reason}`;\n}\n\n// Run the tool\nconst result = await runDatabaseDelete(ctx);\n\n// Close the audit loop\nawait verify(decision.permit_token, ctx);\n```\n\nIf you are using **LangChain**, the `@atlasent/guard` package wraps this pattern as a one-liner decorator — see [atlasent-sdk](https://github.com/AtlaSent-Systems-Inc/atlasent-sdk).\n\nIf you are building your own MCP server with protected tools, copy the `deploy_service` handler in `src/server.ts` — it is the canonical 20-line interception pattern. Wire `atlasent_evaluate` before any tool that writes, deletes, calls external APIs, or runs code.\n\n## Tools\n\n### `atlasent_evaluate` — evaluate an action against AtlaSent policies\n\nEvaluate whether a subject is permitted to perform an action on a resource. Returns a `decision` (`allow`/`deny`/`hold`/`escalate`), a `permit_token` if allowed, an `evaluation_id`, and an optional `reason`.\n\n```\nInput:  { subject, action, resource, org_id, context? }\nOutput: { decision, permit_token?, evaluation_id?, reason?, ... }\n```\n\n**Example:**\n```json\n{\n  \"subject\": \"user:alice\",\n  \"action\": \"production.deploy\",\n  \"resource\": \"env:prod\",\n  \"org_id\": \"org_abc123\",\n  \"context\": { \"ip\": \"10.0.0.1\" }\n}\n```\n\n### `atlasent_list_policies` — list all policies for an organization\n\nReturns all policies for the given org, optionally filtered by status.\n\n```\nInput:  { org_id, status? }   (status: \"draft\" | \"shadow\" | \"enforce\")\nOutput: array of policy objects\n```\n\n**Example:** `{ \"org_id\": \"org_abc123\", \"status\": \"enforce\" }`\n\n### `atlasent_get_policy` — get a single policy by ID\n\nFetches the full policy definition for a given `policy_id`.\n\n```\nInput:  { policy_id, org_id }\nOutput: policy object\n```\n\n**Example:** `{ \"policy_id\": \"pol_xyz789\", \"org_id\": \"org_abc123\" }`\n\n### `atlasent_list_audit_events` — query the audit event log\n\nQuery recent evaluation decisions. Use to verify that an evaluation was recorded or to investigate a sequence of decisions.\n\n```\nInput:  { org_id, evaluation_id?, from?, to?, limit? }\n        (from/to: ISO 8601; limit: 1–100, default 20)\nOutput: array of audit event objects\n```\n\n**Example:**\n```json\n{\n  \"org_id\": \"org_abc123\",\n  \"from\": \"2025-01-01T00:00:00Z\",\n  \"to\": \"2025-01-02T00:00:00Z\",\n  \"limit\": 50\n}\n```\n\n### `atlasent_create_policy` — create an authorization policy\n\n> **Mutating tool — gated off when `ATLASENT_MCP_READONLY=1`.**\n\nDefine a new authorization rule. New policies are created in `draft` state (no enforcement) and must be promoted to `shadow` or `enforce` to take effect.\n\n```\nInput:  { org_id, policy_id, title, policy_type, rules, description?, status?, ... }\nOutput: { policy_id, title, status, ... }\n```\n\n### `atlasent_update_policy` — update an existing policy\n\n> **Mutating tool — gated off when `ATLASENT_MCP_READONLY=1`.**\n\nPartial-update (PATCH) a policy's rules, metadata, or lifecycle status (e.g. promote `draft` → `enforce`).\n\n```\nInput:  { policy_id, org_id, title?, rules?, status?, ... }\nOutput: { policy_id, status, ... }\n```\n\n### `atlasent_delete_policy` — permanently delete a policy\n\n> **Destructive tool — gated off when `ATLASENT_MCP_READONLY=1`.**\n\nIrreversibly remove a policy. Prefer setting `status: \"archived\"` via `atlasent_update_policy` when you only want to disable enforcement.\n\n```\nInput:  { policy_id, org_id }\nOutput: {} (empty on success)\n```\n\n### `atlasent_list_permits` — list issued permits\n\nPaginated list of permits for an organization, with optional filters by status, actor, action type, and time range.\n\n```\nInput:  { org_id, status?, actor_id?, action_type?, from?, to?, limit?, cursor? }\nOutput: { permits: [...], next_cursor? }\n```\n\n### `atlasent_revoke_permit` — revoke an issued permit\n\n> **Destructive tool — gated off when `ATLASENT_MCP_READONLY=1`.**\n\nImmediately invalidate a permit so subsequent verify calls fail with `permit_revoked`. Idempotent.\n\n```\nInput:  { permit_id, org_id, reason? }\nOutput: { revoked, permit_id, ... }\n```\n\n### `atlasent_permit` — issue a permit token out-of-band\n\n> **Mutating tool — gated off when `ATLASENT_MCP_READONLY=1`.**\n\nMint a time-limited permit for a subject/action/resource outside of the standard evaluate flow — for example when a human pre-approves access.\n\n```\nInput:  { subject, action, resource, org_id, ttl_seconds?, context? }\nOutput: { permit_token, expires_at?, ... }\n```\n\n### `atlasent_verify_permit` — verify a permit token (v1 REST)\n\nVerify a permit token is currently valid for a given subject/action/resource. Use after completing an authorized action to close the audit loop.\n\n```\nInput:  { permit_token, org_id, action?, resource? }\nOutput: { valid, outcome, reason? }\n```\n\n### `atlasent_create_approval_request` — request human approval\n\nSubmit an action for human sign-off. Call when `atlasent_evaluate` returns `hold` or when the agent knows approval is required. Do not proceed until resolved.\n\n```\nInput:  { subject, action, resource, org_id, justification?, context? }\nOutput: { approval_request_id, status, created_at, ... }\n```\n\n### `atlasent_resolve_approval_request` — approve or deny a request\n\nApprove or deny a pending approval request on behalf of a human reviewer.\n\n```\nInput:  { approval_request_id, org_id, resolution, resolver_id, comment? }\n        (resolution: \"approve\" | \"deny\")\nOutput: { approval_request_id, status, resolver_id, ... }\n```\n\n### `atlasent_record_execution_evaluation` — record execution outcome\n\nRecord the outcome of an authorized action to complete the full audit loop: evaluate → execute → record.\n\n```\nInput:  { evaluation_id, org_id, outcome, executed_at?, details? }\n        (outcome: \"success\" | \"failure\" | \"skipped\")\nOutput: { execution_id, outcome, recorded_at, ... }\n```\n\n### `atlasent_create_webhook` — register a webhook\n\n> **Mutating tool — gated off when `ATLASENT_MCP_READONLY=1`.**\n\nSubscribe an external HTTPS endpoint to real-time AtlaSent events (e.g. `evaluation.deny`, `approval.requested`, `permit.revoked`).\n\n```\nInput:  { org_id, url, events, description?, secret? }\nOutput: { webhook_id, url, events, secret?, ... }\n```\n\n### `atlasent_delete_webhook` — remove a webhook\n\n> **Destructive tool — gated off when `ATLASENT_MCP_READONLY=1`.**\n\nPermanently deregister a webhook. AtlaSent stops sending events to that URL immediately.\n\n```\nInput:  { webhook_id, org_id }\nOutput: {} (empty on success)\n```\n\n### `evaluate` — for agents that gate themselves (local/remote mode)\n\nThe agent calls `evaluate` before any sensitive action and respects the decision.\n\n```\nInput:  { action_type, actor_id, environment, approvals?, change_window? }\nOutput: { decision: \"allow\" | \"deny\" | \"hold\", permit_token?, reason?, audit_id?, ... }\n```\n\n### `verify_permit` — close the audit loop\n\nAfter the action runs, the agent calls `verify_permit` with the issued token.\n\n```\nInput:  { permit_token, action_type, actor_id, environment, ... }\nOutput: { outcome: \"verified\" | \"expired\" | \"invalid\" | \"error\", valid: boolean, ... }\n```\n\n### `deploy_service` — demo of the interception pattern\n\nA protected tool that authorizes itself before executing. Every call:\n\n1. Builds an action context from the tool arguments\n2. Calls `authorize(ctx)` — **this is the interception point**\n3. If the decision is anything other than `allow`, returns the decision and does NOT execute\n4. On `allow`, runs the deploy and returns the result plus the permit token\n\nSee `src/server.ts` (the `deploy_service` handler) for the exact 20-line pattern every protected tool should follow. In production, your domain tools live on other MCP servers and call AtlaSent's `evaluate` tool before executing; this demo co-locates them so you can see the full flow today.\n\n## Execution Flow\n\n```\n  ┌─────────────┐\n  │    Agent     │  wants to call a protected tool\n  └─────┬───────┘\n        │\n        ▼\n  ┌───────────────────────┐\n  │    Protected tool       │\n  │  (e.g. deploy_service)  │\n  └─────┬───────────────────┘\n        │ (1) build ActionContext\n        ▼\n  ┌─────────────────┐       ┌──────────────────────────┐\n  │  authorize(ctx)  │──────▶│  engine (local | remote)  │\n  └─────┬───────────┘       └────────┬─────────────────┘\n        │ (2) Decision               │\n        │   allow | deny | hold      │\n        ▼                            ▼\n  ┌────────────────────────────────────┐\n  │  decision === \"allow\"?                    │\n  └──┬───────────────────────┬───────────────┘\n     │ no                    │ yes\n     ▼                       ▼\n  BLOCKED              (3) execute the action\n  return decision            │\n                             ▼\n                      (4) return { decision, permit_token, result }\n                             │\n                             ▼\n                      later: verify_permit closes the audit loop\n```\n\nThe **interception point** is step (2): `authorize()` runs before the action. That's the entire guarantee — if `decision !== \"allow\"`, the action does not run.\n\n## Decision envelope\n\nEvery authorization result uses the same shape, so agents and hosts handle all outcomes uniformly:\n\n```ts\ntype Decision =\n  | { decision: \"allow\";  permit_token: string; audit_id?: string; conditions?: string[] }\n  | { decision: \"deny\";   reason: string;       audit_id?: string }\n  | { decision: \"hold\";   reason: string;       hold_id?: string; audit_id?: string };\n```\n\nVerification has a parallel shape:\n\n```ts\ntype VerifyResult = {\n  outcome: \"verified\" | \"expired\" | \"invalid\" | \"error\";\n  valid: boolean;\n  reason?: string;\n  audit_id?: string;\n};\n```\n\n**Handling each case:**\n\n| Decision | Agent behavior |\n|---|---|\n| `allow` | Proceed. Pass `permit_token` to `verify_permit` after completing the action. |\n| `deny` | Do not proceed. Surface `reason` to the user. |\n| `hold` | Do not proceed. Tell the user the action is queued for human review; reference `hold_id`. |\n| **verification failure** (verify returns `valid: false` or `outcome: \"error\"`) | Flag the action for review. Something happened outside policy. |\n\n## Local vs Remote mode\n\nThe engine behind `authorize()` is pluggable. The same tool handlers work in both modes — swapping the backend is a configuration change.\n\n| Mode | When selected | What it does |\n|---|---|---|\n| `local` | `ATLASENT_MODE=local`, or both `ATLASENT_API_KEY` and `ATLASENT_BASE_URL` are unset | Runs a small in-process rules engine (`src/localEngine.ts`) — no network, no credentials |\n| `remote` | `ATLASENT_MODE=remote`, or both `ATLASENT_API_KEY` and `ATLASENT_BASE_URL` are set | Calls the hosted AtlaSent backend at `POST /v1-evaluate` and `POST /v1-verify-permit` |\n\n**Local rules** (for demos):\n\n1. Production action + no approvals → **deny**\n2. Destructive action (`delete`, `drop`, `destroy`, `truncate`, `purge`, `wipe`, `rm`) + no `change_window` → **hold**\n3. Otherwise → **allow** (with a `pt_local_*` permit valid for 5 minutes)\n\n**Environment variables:**\n\n| Variable | Required | Default | Purpose |\n|---|---|---|---|\n| `ATLASENT_MODE` | no | auto-detect | Force `local` or `remote` |\n| `ATLASENT_API_KEY` | remote only | — | Bearer token for the hosted API (prefix: `ask_live_` / `ask_test_`) |\n| `ATLASENT_BASE_URL` | remote only | `https://api.atlasent.io` | Hosted API base URL |\n| `ATLASENT_ANON_KEY` | no | — | Optional `x-anon-key` header |\n| `ATLASENT_MCP_RATE_LIMIT` | no | `600` | Per-tool calls per minute (token bucket) |\n| `ATLASENT_MCP_READONLY` | no | (unset) | If `1` or `true`, skip registration of the 7 mutating tools. **Recommended for live-API demos.** See [Read-only mode](#read-only-mode-for-live-demos). |\n\n## Read-only mode (for live demos)\n\nIn `local` mode, every tool either calls the in-process rules engine or short-circuits with no side effects, so it doesn't matter what the agent tries to do. The story is different the moment you point the server at a live AtlaSent backend with `ATLASENT_MODE=remote` and a real API key: the mutating CRUD tools call the hosted API **directly**. They do not go through the `authorize()` interception. An adversarial prompt, a hallucinated \"let me clean up\" step, or a tool-misuse mistake by the agent can damage real demo-org state.\n\nSet `ATLASENT_MCP_READONLY=1` (or `true`) and the server will skip registration of these 7 tools at startup:\n\n- `atlasent_create_policy`\n- `atlasent_update_policy`\n- `atlasent_delete_policy`\n- `atlasent_create_webhook`\n- `atlasent_delete_webhook`\n- `atlasent_revoke_permit`\n- `atlasent_permit`\n\nEverything else stays available, including the full demo flow:\n\n- The agent-gating loop (`evaluate` → `verify_permit`) and the protected-tool demo (`deploy_service`)\n- All read tools (`atlasent_list_policies`, `atlasent_get_policy`, `atlasent_list_permits`, `atlasent_list_audit_events`, `atlasent_evaluate`, `atlasent_verify_permit`)\n- The approval-request workflow (`atlasent_create_approval_request`, `atlasent_resolve_approval_request`, `atlasent_record_execution_evaluation`)\n\nOn startup, the server emits a `server.readonly_mode` structured log line to stderr listing the disabled tools so the operator can confirm the gate is active.\n\n**When to enable:** any live demo (customer call, conference, screencast) that uses a real API key — even on a dedicated demo org. The cost is zero (the read flow, the approval flow, and the `deploy_service` proof all stay functional); the upside is that no LLM mishap can destroy live state. Disable the flag only when you are intentionally exercising the mutating tools from a trusted operator console.\n\n## Claude Desktop config\n\n```json\n{\n  \"mcpServers\": {\n    \"atlasent\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@atlasent/mcp-server\"],\n      \"env\": {\n        \"ATLASENT_MODE\": \"remote\",\n        \"ATLASENT_API_KEY\": \"ask_live_xxxxxxxxxxxxxxxx\",\n        \"ATLASENT_BASE_URL\": \"https://api.atlasent.io\",\n        \"ATLASENT_MCP_READONLY\": \"1\"\n      }\n    }\n  }\n}\n```\n\nLocation: `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\\Claude\\claude_desktop_config.json` (Windows).\n\nRemove `ATLASENT_MCP_READONLY` from `env` only when you intend to use the mutating CRUD tools from a trusted operator session.\n\n## Cursor config\n\nAdd to `.cursor/mcp.json` in your project root (or `~/.cursor/mcp.json` globally), same shape as above.\n\n## Claude Code\n\n```bash\nclaude mcp add atlasent -- npx -y @atlasent/mcp-server\nexport ATLASENT_API_KEY=ask_live_xxxxxxxxxxxxxxxx\nexport ATLASENT_MCP_READONLY=1   # safe default; unset for ops sessions\n```\n\n## Fail-closed guarantees\n\nEvery error path collapses to `{ decision: \"deny\" }`:\n\n- API unreachable → **deny**\n- Request timeout (10s) → **deny**\n- Malformed response → **deny**\n- Remote returns `allow` without a `permit_token` → **deny**\n- Unknown/invalid decision string → **deny**\n- Verify returns an unrecognized `verify_error_code` → **invalid** (treated as not valid)\n\nThe agent never proceeds without an explicit `allow`.\n\n## How the hosted backend plugs in\n\nNothing in the tool handlers changes when you move from local to remote. The `deploy_service` handler calls `authorize(ctx)`; `authorize()` reads `ATLASENT_MODE` on every call and picks the engine. Switching to the hosted backend is three env vars.\n\nThe remote adapter (`src/engine.ts`) speaks the AtlaSent API edge-function shape directly. Both endpoints are served by `atlasent-api/supabase/functions/v1-{evaluate,verify-permit}/handler.ts`.\n\n- `POST /v1-evaluate`\n  - request: `{ action_type, actor_id, context }` — flat top-level fields. mcp-server passes `environment`, `approvals`, and `change_window` inside `context` so policy expressions can read them.\n  - response: `{ decision, permit_token?, request_id, expires_at?, denial?, ... }` — top-level `permit_token` (raw UUID) is exposed to MCP hosts as the MCP envelope's `permit_token`; `request_id` becomes `audit_id`.\n- `POST /v1-verify-permit`\n  - request: `{ permit_token, action_type, actor_id }`\n  - response: `{ valid, outcome: \"allow\" | \"deny\", verify_error_code?, reason? }` — server `outcome === \"allow\"` becomes `verified`; `verify_error_code` is mapped to `expired` / `invalid` / `error` and falls through to `invalid` for anything unrecognized.\n\n## Development\n\n```bash\nnpm install\nnpm run build              # compile TypeScript\nnpm test                   # 26 unit tests (local + remote mocked) + readonly-mode tests\nnpm run test:integration   # requires ATLASENT_API_KEY + ATLASENT_BASE_URL; skips otherwise\nnpm run demo               # end-to-end authorization demo (local mode)\n```\n\n## License\n\n[MIT](LICENSE)\n","readmeFilename":"README.md","_rev":"1-286be15945e9f5c3bf160f812707c5e4"}