Skip to content

AgentsID-dev/agentsid-proxy

Repository files navigation

@agentsid/proxy

MAP enforcement proxy for MCP servers.

MAP — Model Context Authorization Protocol — is an open policy format for controlling what AI agents are allowed to do. @agentsid/proxy sits between your AI client (Claude Desktop, Cursor, etc.) and any MCP server, evaluating every tool call against a MAP policy before it reaches the upstream server.


How it works

AI Client  ──stdin/stdout──▶  agentsid-proxy  ──stdin/stdout──▶  MCP Server
                                     │
                               MAP Policy
                               Evaluator
                                     │
                              Signed Audit Log

Every tools/call is intercepted. The proxy evaluates the call against your policy, then either:

  • allows it (forwards to the upstream server)
  • denies it (returns a JSON-RPC error -32000)
  • gates it (returns a non-blocking approval request — human must approve before the call proceeds)

Non-tool-call methods (initialize, tools/list, etc.) pass through unchanged.


Install

npm install -g @agentsid/proxy
# or use without installing:
npx @agentsid/proxy run --policy ./policy.json -- npx @modelcontextprotocol/server-github

Quick start

1. Write a policy (policy.json):

{
  "version": "1.0",
  "rules": [
    {
      "tools": ["github.push_files"],
      "action": "deny"
    },
    {
      "tools": ["github.*"],
      "action": "allow",
      "conditions": {
        "branch": { "enum": ["main", "develop"] }
      }
    },
    {
      "tools": ["filesystem.write_file"],
      "action": "allow",
      "conditions": {
        "path": { "pattern": "^/home/user/projects/" }
      }
    },
    {
      "tools": ["shell.*"],
      "action": "deny"
    }
  ]
}

2. Wrap your MCP server:

agentsid-proxy run \
  --policy ./policy.json \
  -- npx @modelcontextprotocol/server-github

3. Point Claude Desktop at the proxy (~/Library/Application Support/Claude/claude_desktop_config.json):

{
  "mcpServers": {
    "github-guarded": {
      "command": "agentsid-proxy",
      "args": [
        "run",
        "--policy", "/path/to/policy.json",
        "--",
        "npx", "@modelcontextprotocol/server-github"
      ],
      "env": {
        "GITHUB_PERSONAL_ACCESS_TOKEN": "<your-token>"
      }
    }
  }
}

Policy reference

Structure

{
  "version": "1.0",
  "expiresAt": "2026-12-31T00:00:00Z",
  "rules": [ ...PermissionRule[] ]
}

PermissionRule

Field Type Description
tools string[] Tool patterns to match (glob, negation, exact)
action "allow" | "deny" Decision when rule matches
conditions Record<string, ConditionDef> Argument constraints (optional)
constraints Constraint[] Runtime constraints (optional)

Tool patterns

Pattern Matches
** Any tool
github.* All tools in the github namespace
github.push_files Exact match
["**", "!shell.*"] Any tool except shell.*

Rules are evaluated first-match wins. Default is deny.

Conditions (argument validation)

"conditions": {
  "branch": { "enum": ["main", "develop"] },
  "path":   { "pattern": "^/home/user/" },
  "query":  { "maxLength": 500 },
  "content": { "notContains": ["DROP TABLE", "rm -rf"] }
}
Condition Description
pattern Regex that the argument value must match
enum Allowlist of permitted values
maxLength Maximum string length
minLength Minimum string length
notContains Forbidden substrings

Constraints (runtime enforcement)

Rate limit

{ "type": "rateLimit", "max": 10, "windowSeconds": 60 }

Schedule

{ "type": "schedule", "hoursUTC": [9, 17], "daysOfWeek": [1, 2, 3, 4, 5] }

hoursUTC: [startHour, endHour] (exclusive). daysOfWeek: ISO day numbers (1=Monday, 7=Sunday).

Approval gate

{
  "type": "approvalGate",
  "approvers": ["principal"],
  "timeoutSeconds": 300,
  "timeoutAction": "deny",
  "message": "Production push requires approval"
}

When a gate is triggered the tool call returns immediately with a gate ID. The agent can continue other work. Approve or deny from another terminal:

agentsid-proxy approve <gateId>
agentsid-proxy deny <gateId>

Session limit

{ "type": "sessionLimit", "max": 50 }

Cooldown

{ "type": "cooldown", "seconds": 30 }

Policy loading precedence

  1. --policy <path> flag
  2. AgentsID cloud (--api-key + --agent-id, or AGENTSID_API_KEY + AGENTSID_AGENT_ID env vars)
  3. .agentsid/policy.json in the working directory
  4. Deny-all (no policy = nothing gets through)

Cloud mode (AgentsID)

Store and manage policies centrally:

agentsid-proxy run \
  --api-key $AGENTSID_API_KEY \
  --agent-id my-coding-agent \
  -- npx @modelcontextprotocol/server-filesystem /workspace

Cloud mode also syncs audit entries to the AgentsID dashboard for centralized visibility.


Audit log

Every tool call produces a signed, hash-chained audit entry:

{
  "entryId": "uuid",
  "timestamp": "2026-03-29T12:00:00.000Z",
  "agentId": "my-coding-agent",
  "tool": "filesystem.write_file",
  "arguments": { "path": "/home/user/projects/app.ts", "content": "..." },
  "decision": "allow",
  "matchedRule": 1,
  "reason": "rule 1 matched",
  "prevEntryHash": "sha256:abc123...",
  "entryHash": "sha256:def456...",
  "signature": "ed25519:..."
}
  • Arguments containing secrets are redacted before logging (keys: password, token, secret, apiKey, credential, private; values matching OpenAI/GitHub/Slack/AWS patterns)
  • Each entry includes the previous entry's hash — tamper one entry and the whole chain fails verification
  • Signatures use Ed25519 with an ephemeral key per session (or a persistent key for cloud mode)

Programmatic usage

import { evaluate, AuditWriter, generateSigningKey } from "@agentsid/proxy";
import type { MapPolicy, EvaluationContext } from "@agentsid/proxy";

const policy: MapPolicy = {
  version: "1.0",
  rules: [{ tools: ["github.*"], action: "allow" }],
};

const ctx: EvaluationContext = {
  tool: "github.push_files",
  arguments: { owner: "myorg", repo: "myrepo", branch: "main" },
  agentId: "my-agent",
  sessionId: "session-123",
  requestId: "req-456",
  timestamp: new Date(),
};

const result = evaluate(policy, ctx);
console.log(result.decision); // "allow"

const key = generateSigningKey();
const writer = new AuditWriter(key);
const entry = writer.write(ctx, result);
console.log(entry.entryHash); // "sha256:..."

CLI reference

agentsid-proxy run [options] -- <upstream-command...>

Options:
  -p, --policy <path>      Path to MAP policy JSON file
  -k, --api-key <key>      AgentsID API key (or AGENTSID_API_KEY)
  -a, --agent-id <id>      Agent ID (or AGENTSID_AGENT_ID)
  --api-url <url>          AgentsID API base URL (default: https://api.agentsid.dev)
  --log-level <level>      silent|error|warn|info|debug (default: info)

agentsid-proxy approve <gateId> [--by <identity>]
agentsid-proxy deny <gateId> [--by <identity>]

Development

npm install
npm run typecheck    # TypeScript strict check
npm test             # All tests (unit + conformance)
npm run test:unit    # Unit tests only
npm run test:conformance  # MAP spec conformance suite
npm run build        # Compile to dist/

Test coverage target: 80% lines / 75% branches.


MAP Specification

The full AgentsID Permission Specification v1.0 — which defines the policy format, evaluation algorithm, and conformance requirements — is published at:


License

MIT © AgentsID

About

MAP (Model Context Authorization Protocol) — enforcement proxy for MCP servers

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors