Cordon for MCP

io.github.marras0914由 GitHub 账号 marras0914 发布v0.3.0更新于 Oct 24, 2026

Cordon for MCP. Security gateway with policy enforcement, audit log, and HITL approvals.

已验证STDIO仅桌面开发者工具安全与监控

概览

AI 生成的概览

本地安全网关,代理 MCP 工具调用,在请求到达上游服务器前执行按工具的策略、审计日志与人工审批。

可以做什么
  • 把 MCP 客户端的工具调用经由单一聚合代理转发到已配置的上游 MCP 服务器
  • 按服务器和按工具应用 allow、block、approve、approve-writes、read-only、log-only、hidden 等策略
  • 将标记的调用暂停,在终端或 Slack 中等待人工审批,超时请求会保留并可重放
  • 解析 SQL 参数,放行读取并拦截或审批写入,并可用封闭式工具清单限制工具面
  • 把每次调用记录为结构化 JSON 审计事件,输出到标准输出、文件或托管仪表板
  • 用调用图规则拦截或审批危险的工具调用序列
适用场景
当助手已能访问数据库、文件系统或云 API 等强力 MCP 服务器,而你希望在无人值守运行前获得可见性、写入审批和审计记录时适用。既适合想要安全兜底的个人开发者,也适合需要策略即代码与合规日志的团队。
运行要求
通过 npx 从 npm 包 @getcordon/cli 在本地运行,需要 Node.js。初始化会读取并修改现有的 Claude Desktop MCP 配置,并生成 cordon.config.ts。可选的 HTTP 模式需要通过 CORDON_GATEWAY_TOKEN 设置 bearer 令牌。Slack 审批需在仪表板中连接工作区。
安装前请注意
它位于助手与服务器之间,配置决定助手能做什么,策略配置不当可能让工具保持开放。审批与重放会执行真实工具调用,包括写入和删除,请逐条核对。审计输出可发送到托管仪表板,Slack 审批会把调用参数发到频道。HTTP 模式为单租户且默认仅监听本机,对外暴露会扩大访问面。

安装

在 SourceWeft 中

  1. 打开 控制台中的 Cordon for MCP,将其添加到工作区。
  2. 为需要使用其工具的对话启用该服务。

Desktop only,通过 STDIO。 STDIO 服务会启动本地进程,因此需要 SourceWeft 桌面宿主。

其他 MCP 客户端

参照 仓库 中的启动说明。

README

Cordon for MCP

The Security Gateway for MCP Tool Calls

Website • Writeup

Quickstart • Why Cordon • How It Works • Configuration • Roadmap • Contributing

[npm version] [license] [stars]


Every company wants to deploy AI agents. No company is willing to give an agent the keys to their database.

Cordon closes the trust gap.

From the maintainer of Agent Toolbelt — 25+ MCP tools, ~1,600 weekly npm downloads.


Demo

https://github.com/user-attachments/assets/153d978f-6303-443a-b49b-b4ec7ebf0452


The Problem

The Model Context Protocol (MCP) has made it trivially easy to give AI agents access to powerful tools — databases, file systems, APIs, cloud infrastructure.

But MCP has no built-in security model. No audit logs. No approval workflows. No rate limits. Today, an AI agent is either off or full admin. There is nothing in between.

This is the single biggest blocker preventing AI agents from reaching production.

The Solution

Cordon is the security gateway that sits between the LLM and your MCP servers.

It acts as a firewall, an auditor, and a remote control — giving you complete visibility and authority over what your AI agents can and cannot do.

┌─────────┐      ┌──────────┐      ┌──────────────┐│  LLM /  │ ──▶  │  Cordon  │ ──▶  │  MCP Server  ││  Agent  │ ◀──  │ Gateway  │ ◀──  │  (database,  │└─────────┘      └──────────┘      │   fs, APIs)  │                   │               └──────────────┘                   ├── Policy Engine                   ├── Audit Logger                   └── Approval Workflows

No infrastructure changes. No rewrites. One config file.


Quickstart

Step 1 — Initialize

Run this inside your project (where your claude_desktop_config.json exists):

bash
npx @getcordon/cli init

This reads your existing Claude Desktop MCP config, generates cordon.config.ts, and patches Claude Desktop to route all tool calls through Cordon.

Step 2 — Start

bash
npx @getcordon/cli start

Cordon starts, connects to your MCP servers, and begins intercepting tool calls. Restart Claude Desktop and every tool call now flows through the gateway.

Manual setup

If you prefer to configure manually, install globally and create a config:

bash
npm install -g @getcordon/clicordon init

cordon init generates a cordon.config.ts:

typescript
import { defineConfig } from '@getcordon/policy';
export default defineConfig({  servers: [    {      name: 'database',      transport: 'stdio',      command: 'npx',      args: ['-y', '@my-org/db-mcp-server'],      policy: 'read-only',        // Block all write operations    },    {      name: 'github',      transport: 'stdio',      command: 'npx',      args: ['-y', '@modelcontextprotocol/server-github'],      policy: 'approve-writes',   // Reads pass; writes require approval      tools: {        delete_branch: 'block',   // Never, regardless of approval      },    },  ],
  audit: {    enabled: true,    output: 'stdout',             // or 'file'  },
  approvals: {    channel: 'terminal',    timeoutMs: 60_000,            // auto-deny after 60s; the request is retained                                  // and replayable if approved late  },});

Why Cordon

Without CordonWith Cordon
Agent has unrestricted tool accessGranular per-tool policies
No visibility into what agents didStructured audit trail of every call
"Did the agent just drop a table?"Real-time terminal approvals
Reads and writes treated the sameapprove-writes lets reads through automatically
Compliance team says no to AIAudit logs ready for export

Features

Policy Engine

Define rules per tool, per server, or globally. Tool-level policies override server policies.

typescript
// Server-level defaultpolicy: 'approve-writes',
// Per-tool overridestools: {  query:        'allow',    // reads: pass through  execute:      'approve',  // writes: pause for human approval  drop_table:   'block',    // catastrophic: always reject  list_tables:  'log-only', // audit but don't interrupt},

Human-in-the-Loop Approvals

When a tool call requires approval, Cordon pauses the agent and prompts you directly in your terminal:

╔══════════════════════════════════════╗║  ⚠  APPROVAL REQUIRED               ║╚══════════════════════════════════════╝  Server : database  Tool   : execute_sql  Args   :  {    "query": "DELETE FROM sessions WHERE expires_at < NOW()"  }
  [A]pprove  [D]eny  >

The agent waits. You decide.

Prefer Slack? Connect your workspace once with Add to Slack in the dashboard — no bot token to create or paste — then set approvals: { channel: 'slack' }. A flagged call posts an Approve / Deny card to your channel and pauses until a human clicks, and the approver's name is written onto the audit record. See the Slack approvals setup.

When nobody answers in time. The call is denied and the agent moves on, but the request isn't lost. Cordon retains the pending approval with its full context, and the dashboard lists it under What changed → Timed-out approvals. If someone approves the card after it expired, that late decision is recorded and the call can be re-run:

bash
cordon replay <callId>

The replay executes the tool and logs the outcome to the same audit stream with reason replay of late-approved call. It recovers the tool call, not the agent's session — the agent that asked is long gone. Only a call approved after timing out is replayable, since replaying a call that already ran would double-execute it.

Audit Logging

Every tool call is logged as structured JSON — the request, the policy decision, the response, and timing. Pipe to stdout or write to a file for your compliance team.

json
{"event":"tool_call_received","callId":"...","serverName":"database","toolName":"execute_sql","timestamp":1773434469641}{"event":"approval_requested","callId":"...","serverName":"database","toolName":"execute_sql","timestamp":1773434469641}{"event":"tool_call_approved","callId":"...","serverName":"database","toolName":"execute_sql","timestamp":1773434471203}{"event":"tool_call_completed","callId":"...","durationMs":34,"isError":false,"timestamp":1773434471237}

Read-Only Mode

One policy setting to block all write operations across a server. Zero guesswork about what counts as a write — Cordon detects it from the tool name.

typescript
policy: 'read-only'  // any tool starting with write/create/update/delete/drop/execute/... is blocked

Hidden Tools

For tools the model should never even see — not just rejected on call, but filtered from the tools/list response entirely. Closes a prompt-injection surface: if the model never knows a tool exists, it can't be tricked into calling it.

typescript
{  name: 'database',  policy: 'approve-writes',  tools: {    drop_table:      'block',   // call attempts are rejected    internal_admin:  'hidden',  // not advertised to the client at all  },}

SQL-Aware Policies

For database MCP servers where a single tool takes arbitrary SQL (Postgres, SQLite, BigQuery, etc.), tool-name heuristics aren't precise enough — the name query doesn't tell you whether the agent's about to SELECT or DROP TABLE. Cordon ships two policies that parse the SQL itself and decide based on the statement type.

typescript
tools: {  // Allow SELECTs (including CTEs that wrap a SELECT). Block everything else.  query: 'sql-read-only',
  // Reads pass; writes (INSERT/UPDATE/DELETE/DROP/ALTER/...) pause for human approval.  execute: 'sql-approve-writes',
  // When the tool takes SQL in a different arg name:  run: { action: 'sql-read-only', sqlArg: 'statement' },}

Both policies use the PostgreSQL dialect by default (others coming later) and are fail-closed: unparseable SQL is blocked rather than allowed. Prompt-injection patterns like SELECT 1; DROP TABLE users; and block-comment-wrapped keywords are correctly classified as writes by the AST parser.

Closed-World Tool Catalogs

Declare the exact tool surface your upstream server is expected to advertise. When the upstream adds a new tool in a future release, Cordon blocks it automatically until you explicitly promote it.

typescript
{  name: 'postgres',  command: 'npx',  args: ['-y', '@modelcontextprotocol/server-postgres', process.env.POSTGRES_URL!],  policy: 'read-only',  knownTools: ['query', 'list_tables', 'describe_table'],  // your approved surface  onUnknownTool: 'block',                                  // default when knownTools is set}

If the next Postgres MCP release adds truncate_table, Cordon blocks it with a stderr warning — no policy update needed. Leave knownTools undefined for backwards-compatible open-world behavior.

Per-Agent Policies + Call-Graph Constraints

[Tier 1 demo — read_data succeeds, write_file blocked by call-graph rule]

Per-tool policies catch dangerous individual calls. Call-graph rules catch dangerous sequences — combinations of allowed tools used in unintended order. The classic example: an agent reads sensitive data with read_data (allowed) and then writes it to disk with write_file (also allowed). Each call is fine in isolation. The sequence is the problem.

Cordon's call-graph rules apply on top of your per-tool policy and are additive — they can only raise severity, never lower it.

typescript
export default defineConfig({  // Optional identity for this Cordon process. Surfaced in audit logs.  agentId: 'et-acquisition-agent',
  servers: [{    name: 'demo-db',    transport: 'stdio',    command: 'npx',    args: ['-y', '@my-org/db-mcp'],    policy: 'approve-writes',  }],
  callGraph: [    // Reading data and then writing to disk is the exfil shape — block it,    // even though write_file would only require approval on its own.    { from: 'read_data', to: 'write_file', action: 'block',      reason: 'No file writes after database reads.' },
    // Glob matching: any send_* tool right after a database read needs human approval.    { from: 'read_data', to: 'send_*', action: 'approve' },
    // `*` matches anything: after a privileged read, every next call must be approved.    { from: 'sensitive_read', to: '*', action: 'approve' },  ],});

How rules combine with per-tool policies (additive severity):

Severity ordering: allow < approve < block.

A call-graph rule only takes effect if its action is higher severity than the per-tool decision. That means:

  • A rule with action: 'block' overrides an allow per-tool policy ✓
  • A rule with action: 'approve' overrides allow, but doesn't lower a block ✓
  • A rule with action: 'allow' never weakens a stricter base — it's a no-op
  • When multiple rules match, the highest-severity one wins (config order doesn't matter)

One-way ratchet — tighten only, never loosen.

Audit log gains chain context. Every tool_call_received event now carries previousTool (what was last successfully executed). Blocked / approval-required events also include callGraphRule: { from, to } when a rule fired:

json
{"event":"tool_call_received","toolName":"write_file","previousTool":"read_data","..."}{"event":"tool_call_blocked","toolName":"write_file","reason":"No file writes after database reads.","previousTool":"read_data","callGraphRule":{"from":"read_data","to":"write_file"},"..."}

v1 scoping (read this if you're shipping per-agent policies in production):

In stdio mode (and in the current single-tenant HTTP mode), one Cordon process serves one agent identity — so agentId in config identifies this deployment, not a per-call runtime identity. The multi-tenant HTTP gateway (where one Cordon instance hosts many agents with distinct policies) is still queued — agentId will gain an HTTP-header source then, no config rewrite needed.

lastTool advances only on a clean upstream return. Blocked, denied, and transport-failed calls don't advance the chain. v1 doesn't catch probing — an adversary that attempts fs:delete (blocked) and then http:post will see http:post evaluated against whatever came before the probe, not the probe itself. We chose this on purpose: tracking every blocked attempt false-positives too aggressively on legitimate flows that get blocked once and continue. Revisit when there's real adversarial-misuse data.


How It Works

Cordon runs as a single aggregating MCP proxy. Instead of Claude Desktop connecting directly to your MCP servers, it connects to Cordon. Cordon then manages your servers internally.

Before:  Claude ──▶ MCP Server A (full access)         Claude ──▶ MCP Server B (full access)
After:   Claude ──▶ Cordon ──▶ MCP Server A (governed)                          ──▶ MCP Server B (governed)

Your LLM client and MCP servers don't change at all. cordon init handles the config patching.

HTTP Transport (n8n and other HTTP-speaking MCP clients)

For clients that talk MCP over HTTP — n8n's MCP Client Tool node, hosted agents that can't spawn child processes, anything else that wants a network endpoint — Cordon can run as an HTTP listener instead of stdio.

bash
export CORDON_GATEWAY_TOKEN=your-secret-tokennpx @getcordon/cli start --http --port 7777# [cordon] HTTP gateway listening on http://127.0.0.1:7777/mcp

Or set it in your config:

typescript
import { defineConfig } from '@getcordon/policy';
export default defineConfig({  servers: [ /* your upstream MCP servers */ ],  gateway: {    transport: 'http',    authToken: process.env.CORDON_GATEWAY_TOKEN!,    port: 7777,    host: '127.0.0.1',  // localhost-only by default; set to '0.0.0.0' to expose externally  },});

Clients connect to http://your-host:7777/mcp with an Authorization: Bearer <your-token> header. The transport implements the MCP Streamable HTTP spec — one endpoint serves both SSE response streams and direct JSON responses, with session multiplexing handled by session IDs in headers.

All policy enforcement, audit logging, and approval workflows work identically across transports — only the wire protocol changes.

Connecting from n8n. Point the MCP Client Tool node at your Cordon URL with the Bearer token as the credential. The deprecated SSE-only endpoint shape (older n8n versions on the legacy MCP SSE spec) is not implemented — Cordon ships only the current Streamable HTTP transport, which n8n is migrating to.

Single-tenant. The current HTTP transport is single-tenant: all sessions share the same upstream MCP servers, policy config, and auth token. A multi-tenant hosted gateway (per-call agentId from headers, per-tenant policy) is on the roadmap.


Configuration

Policy actions

PolicyBehavior
allowPass through immediately
blockReject — agent receives an error
approvePause pending human approval in terminal
approve-writesReads pass through; writes require approval
read-onlyAll write operations are blocked
log-onlyPass through but flagged in the audit log
hiddenFiltered from tools/list — the model never sees it
sql-read-onlyParse the SQL arg, allow SELECT/WITH-SELECT, block everything else (fail-closed on unparseable)
sql-approve-writesParse the SQL arg, allow reads, pause writes for human approval, block unparseable

Policies can be set at the server level (default for all tools) or per-tool (overrides the server default):

typescript
{  name: 'my-server',  policy: 'approve-writes',   // server default  tools: {    safe_read:   'allow',     // override: always allow    nuke_db:     'block',     // override: always block  },}

Approval channels

ChannelStatus
terminalAvailable — interactive prompt in your terminal
slackAvailable — one-click Add to Slack (OAuth), server-posted Block Kit cards, HMAC-verified interactions, approver captured on the record
webComing in v0.3
webhookComing in v0.3

Audit outputs

OutputStatus
stdoutAvailable
fileAvailable — JSON lines written to a local file
hostedAvailable — ships events to the Cordon dashboard
otlpComing in v0.3

Packages

PackageDescription
@getcordon/cliThe CLI — npx @getcordon/cli start
@getcordon/policyTypeScript config SDK — defineConfig() and all types
@getcordon/coreCore proxy engine — policy evaluator, audit logger, approval manager

Roadmap

  • MCP proxy with aggregator model (multiple servers, one gateway)
  • Policy engine — allow, block, approve, approve-writes, read-only, log-only, hidden
  • Closed-world tool catalogs — knownTools + onUnknownTool for future-proof upstream surface control
  • SQL-aware policies — sql-read-only / sql-approve-writes parse SQL arguments at call time (PostgreSQL dialect)
  • Terminal approval channel with TTY-safe prompt
  • Slack approval channel — Block Kit messages, polls for response
  • Structured JSON audit logging to stdout, file, or hosted dashboard
  • cordon init — auto-reads Claude Desktop config and patches it
  • Rate limiting — sliding window, global / per-server / per-tool
  • Hosted dashboard — audit log history, CSV/JSON export, GitHub OAuth, free for individuals
  • Per-agent policies + call-graph constraints — sequence-aware blocking with additive severity
  • HTTP/Streamable HTTP transport — single-tenant gateway for n8n and HTTP-speaking MCP clients
  • Drift detection — "what changed about how your agents behave this week" in the dashboard
  • Suggested policies — analyzes your audit history and proposes call-graph rules and gates
  • Durable approvals — timed-out approvals are retained, surfaced, and replayable via cordon replay
  • Multi-tenant HTTP gateway — many agents on one Cordon instance with per-call agentId from headers
  • OpenTelemetry export
  • Team accounts and centralized governance

Examples

See examples/security-showcase for a working demo of Cordon intercepting an agent that attempts to drop a production database table.

bash
cd examples/security-showcasenpm installnpm run demo

Use Cases

Enterprise — Centralized governance across all AI agent deployments. Policy-as-code, structured logs, and a clear path to SOC2-ready audit trails.

Startup Team — Deploy agents with confidence. Every tool call is logged, writes require approval, and your compliance team has a trail.

Solo Developer — Secure your local Claude/Cursor setup. See exactly what your agent is calling and block anything dangerous before it reaches production.


Works great with

  • Agent Toolbelt — a typed toolkit of ready-made MCP tools (web search, fetch, filesystem, and more). Wire it into Claude Desktop, then route those tool calls through Cordon for policy enforcement and audit logging. Agent Toolbelt gives your agents power; Cordon makes sure they ask before using it.

  • Build & Ship MCP Tools — the companion course that walks through building your own MCP servers end to end. 7 modules, 33 lessons; Module 0 is free, Module 6 covers securing your server with Cordon.


Contributing

Cordon is open source and we welcome contributions.

bash
git clone https://github.com/marras0914/cordon.gitcd cordonnpm installnpm run buildnpm run dev

License

MIT — see LICENSE for details.


Stop trusting. Start governing.
⭐ Star on GitHub

Privacy · Terms · Support · Partners
© 2026 Elephant Tortoise LLC

来源:README.md,提交 6e9a1a1

工具

0
SourceWeft 尚未在沙箱中启动过该软件包,因此其工具未知。

版本历史

1
  1. v0.3.0最新Sep 16, 2026

评分

已满足适用检查项的 50%

该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。

  • 近 30 天内可连通25 分

    请保持端点能够响应 MCP 握手;SourceWeft 每 30 天探测一次。

  • 已声明认证方式10 分

    请在注册中心条目中声明凭据请求头,或保持端点能够响应探测,以便判定其认证方式。

  • 已列出工具15 分

    请让服务器在无凭据时响应 tools/list,或发布可在沙箱中无凭据启动的软件包。

  • 描述必需15 分
  • 图标10 分
  • 文档15 分
  • 许可证10 分