Skip to main content
Glama
itcustomsolution

Approval Gate MCP

approval-gate-mcp

An MCP server that makes your AI agent stop and ask a human before it sends, deletes, publishes, pays or deploys.

npm

license: MIT

Why

Agents are good at doing things. The hard part is making sure the consequential things (sending an email, deleting data, publishing, spending money) only happen when a human said yes. This server is that gate, extracted from a system I have run in production for years: after an automation incident taught me the lesson the hard way, my standing rule became no automated destructive action without a human gate, ever. This is the generic, reusable version of that rule.

Related MCP server: actionproxy

30-second quickstart

claude mcp add approval-gate -- npx -y @itcustomsolution/approval-gate-mcp

Claude Desktop (claude_desktop_config.json):

{
  "mcpServers": {
    "approval-gate": {
      "command": "npx",
      "args": ["-y", "@itcustomsolution/approval-gate-mcp"]
    }
  }
}

Approve or deny from your terminal with npx -p @itcustomsolution/approval-gate-mcp approval-gate list.

Example

  1. Your agent wants to email a customer. It calls request_approval with action: "send email to alice@example.com, subject: Invoice 1042" and gets back id a1b2c3d4e5f6.

  2. It calls wait_for_approval. Nothing is sent.

  3. In your own terminal: approval-gate list, then approval-gate approve a1b2c3d4e5f6 looks right.

  4. The agent sees approved and sends. Had you denied, ignored it, or let it expire, the answer is no.

  5. ~/.approval-gate/audit.jsonl now has the request and your decision.

Design principles

  • Fail closed. The only thing that authorizes an action is an explicit approved. Pending, denied, expired, timeout, error: all of those mean no.

  • The gate never executes anything. It records intent and decisions. Your agent asks first, then acts only on approval. Separation of decision and execution keeps the gate simple and auditable.

  • Decisions are out-of-band. A human approves or denies with a CLI in their own terminal, not inside the agent's context, so the agent cannot talk itself through the gate.

  • Everything is audited. Every request, decision, and expiry is appended to ~/.approval-gate/audit.jsonl.

  • Requests expire. Default TTL is 1 hour; a stale approval is not an approval.

Install

From npm:

npm install -g @itcustomsolution/approval-gate-mcp   # installs `approval-gate` and `approval-gate-mcp`

From source:

npm install
npm run build
npm link        # makes `approval-gate` and `approval-gate-mcp` available on PATH

Wire it into an MCP client

Claude Code:

claude mcp add approval-gate -- node /path/to/approval-gate-mcp/dist/index.js

Or in any MCP client config:

{
  "mcpServers": {
    "approval-gate": {
      "command": "node",
      "args": ["/path/to/approval-gate-mcp/dist/index.js"]
    }
  }
}

Tools

Tool

Purpose

request_approval

Register an intended action (action, reason, optional ttl_seconds). Returns an id.

check_approval

Poll a request's status: pending / approved / denied / expired.

wait_for_approval

Block up to timeout_seconds for a decision. Timeout = still pending = not approved.

list_pending

List requests awaiting a human.

decide_approval

Record a decision when the human is driving the session. Agents must never self-approve; prefer the CLI.

The human side

approval-gate list                # pending requests
approval-gate show a1b2c3d4e5f6
approval-gate approve a1b2c3d4e5f6 looks right, ship it
approval-gate deny a1b2c3d4e5f6 wrong recipient

Suggested agent policy

Put something like this in your agent's system prompt or policy file:

Before any outbound or destructive action (send, publish, delete, purchase, deploy), call request_approval and then wait_for_approval. Proceed only if the returned status is exactly approved. Never call decide_approval for your own requests.

State

Everything lives in ~/.approval-gate/ (override with APPROVAL_GATE_DIR): requests/*.json for current state, audit.jsonl for the append-only history.

Why so small?

Because the pattern matters more than the plumbing. In my production systems this same shape (request, out-of-band human decision, fail-closed default, audit trail) sits in front of email, publishing, and deployments, and it has turned entire classes of automation incidents into structural impossibilities. Start small, gate the scary things, and let your incident postmortems tell you what to gate next.

Contributing

Issues and pull requests are welcome. Keep changes small and fail-closed: anything that could turn a "no" into a "yes" needs a test. Run npm test before opening a PR. See CHANGELOG.md for release history.

License

MIT, see LICENSE.


Built by Olufela "Lu" Fagbure · itcustomsolution.com · MIT license

Available Tools

5 tools
check_approvalA

Check the current status of an approval request. Statuses: pending, approved, denied, expired. Only 'approved' authorizes the action.

ParametersJSON Schema
NameRequiredDescriptionDefault
idYesThe request id returned by request_approval

TDQS

A3.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and delivers the key behavior: the status value set (pending, approved, denied, expired) and the decision rule 'Only approved authorizes the action.' That is genuinely useful semantics not present in the schema. It stops short of stating whether the call is side-effect-free or requires permissions.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three short sentences, front-loaded with the core action, followed by the status set and the one rule that actually changes agent behavior. No wasted words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a single-parameter read tool with no output schema, the description supplies the return values (the statuses) and the interpretation rule, which is what the missing output schema would otherwise need to convey. Only the safety/permission profile is left unstated, which is minor here.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the single id parameter is documented there as the value returned by request_approval. The description adds no syntax or format detail beyond the schema, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource ('Check the current status of an approval request'), which is unambiguous on its own. It does not explicitly name how it differs from siblings like wait_for_approval (blocking) or list_pending (bulk), though the point-in-time framing implicitly separates it.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is only implied: 'check the current status' suggests a non-blocking poll, but the description never states when to prefer this over wait_for_approval or list_pending, nor any prerequisites or exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

decide_approvalA

Record a human decision on a pending request. IMPORTANT: this tool exists for setups where the human is driving this very session and asks the agent to record their decision. An agent must NEVER call this on its own initiative to approve its own request; self-approval defeats the gate. Prefer the out-of-band CLI: approval-gate approve .

ParametersJSON Schema
NameRequiredDescriptionDefault
idYes
reasonNoHuman's stated reason, verbatim
approveYestrue to approve, false to deny

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does disclose the key behavioral trait: this is a gated operation that must not be self-invoked, and a safer out-of-band channel exists. It does not describe the downstream effect of a decision, idempotency, or error behavior for an unknown id, which keeps it short of a 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, front-loaded with the core action, then the critical guardrail, then the alternate channel. No sentence is redundant and the IMPORTANT marker directs attention to the highest-risk instruction.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 3-parameter, no-output-schema mutation gate with no annotations, the description supplies the essential decision-routing guidance an agent needs. It stops short of describing the post-decision effect or failure modes, which would round it out.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 67%: 'reason' and 'approve' are documented in the schema, while 'id' is undocumented. The description adds no parameter-level meaning beyond the schema, so the baseline of 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: 'Record a human decision on a pending request.' The decision-recording role is clearly distinct from siblings like request_approval and check_approval, though no sibling is named explicitly.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly defines when to use (only when the human driving the session asks the agent to record their decision), when NOT to use (an agent must never self-approve), and the preferred alternative (the out-of-band CLI 'approval-gate approve <id>'). This is a textbook when/when-not/alternative statement.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_pendingB

List all approval requests currently awaiting a human decision.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

B3.4/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full behavioral burden. 'List' implies a read-only operation and it discloses the pending filter, but says nothing about ordering, pagination, volume limits, or auth requirements.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single sentence with zero waste; the object of the listing and the pending scope are front-loaded and immediately actionable.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple parameterless list tool this covers the essential purpose, but with no annotations and no output schema the description omits any hint about what a returned record looks like or how many results to expect.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so the baseline is 4. There are no parameters whose semantics could be misread, and the description's scoping phrase fully defines the implicit filter.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (List) and resource (approval requests) with a scoping qualifier (currently awaiting a human decision). It is clear what it returns, though it does not explicitly name how it differs from siblings like check_approval or wait_for_approval.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The phrase 'currently awaiting a human decision' implies the usage context, but there is no explicit when-to-use/when-not guidance or reference to the alternative siblings such as check_approval or decide_approval.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

request_approvalA

Register a consequential action for human approval BEFORE performing it. Returns a request id. The action is NOT executed by this server; the calling agent must check approval status and only proceed on an explicit 'approved'. Anything else means no (fail closed).

ParametersJSON Schema
NameRequiredDescriptionDefault
actionYesPrecise description of the action you intend to take, e.g. 'send email to alice@example.com with subject X'
reasonYesWhy this action is needed right now
ttl_secondsNoHow long the request stays pending before it expires (default 1 hour)
requested_byNoIdentifier of the requesting agent or workflow

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries full burden and discloses the two most important non-obvious behaviors: the server does NOT execute the action, and anything other than 'approved' means no (fail closed). It also notes a request id is returned. Gaps remain on auth requirements, whether the action payload is re-verified at approval time, and pending-request lifecycle beyond the schema's ttl field.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Four short sentences, front-loaded with the core purpose and then the critical fail-closed contract. No filler, and each sentence adds a distinct operational fact.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description appropriately states the return value (a request id) and the full workflow contract an agent needs. Combined with the fully documented schema, nothing required to call this correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so all four parameters are already documented with useful examples (action, reason, ttl_seconds default/limits, requested_by). The description adds no parameter-level detail beyond that, making the baseline 3 appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('register a consequential action for human approval') and explicitly scopes when in the lifecycle it runs ('BEFORE performing it'). This cleanly separates it from check_approval/wait_for_approval/decide_approval, which operate on an already-registered request rather than creating one.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives clear context: register before acting, then check status and proceed only on 'approved', with fail-closed semantics spelled out. It does not name the concrete sibling (check_approval/wait_for_approval) to call next, so the routing is implied rather than explicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

wait_for_approvalA

Block until a human decides the request, it expires, or the timeout elapses. Returns the final (or still-pending) request. A timeout returns status 'pending': treat it as NOT approved.

ParametersJSON Schema
NameRequiredDescriptionDefault
idYesThe request id returned by request_approval
poll_secondsNoPolling interval
timeout_secondsNoMaximum time to wait (default 5 minutes)

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden and does so well: it discloses blocking behavior, the three termination conditions (decision, expiry, timeout), the return value, and the critical interpretation rule that a pending timeout is NOT approval. It omits auth/permission requirements and any rate or polling cost caveats.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three short sentences, no waste, and the blocking behavior is front-loaded followed immediately by the safety-critical timeout rule. Every sentence earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no annotations and no output schema, the description must explain the interaction contract, and it covers the blocking lifecycle, the return content, and the timeout interpretation. It leaves a small gap around what 'expires' means versus a timeout and offers no comparison to the other approval siblings.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so id, poll_seconds, and timeout_seconds are already fully documented in the schema, including defaults and bounds. The description adds only the interpretation of the timeout outcome, which is useful behavior-level context rather than additional parameter semantics, so the baseline 3 holds.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and behavior: 'Block until a human decides the request, it expires, or the timeout elapses.' The blocking semantics inherently distinguish it from the non-blocking check_approval/list_pending siblings, but it never names an alternative, so the differentiation is implicit rather than explicit.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives clear operational context for handling outcomes ('A timeout returns status pending: treat it as NOT approved'), which is exactly the guidance an agent needs to avoid misinterpreting a timeout as consent. It stops short of saying when to prefer this over check_approval or poll-based siblings.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 5 tool updatesv0.1.0
    • First observedcheck_approval
    • First observeddecide_approval
    • First observedlist_pending
    • First observedrequest_approval
    • First observedwait_for_approval

TDQS

A3.9/5.0

Scored across 5 tools

Disambiguation4/5

Each tool maps to a distinct action in the approval lifecycle (request, check, wait, list, decide), and descriptions make the boundaries clear. The only mild overlap is check_approval vs wait_for_approval, which both return status but differ in blocking behavior, so an agent must read carefully to pick the right one.

Naming Consistency4/5

Most names follow a verb_noun pattern with a consistent '_approval' suffix (request_approval, check_approval, wait_for_approval, decide_approval). list_pending breaks the pattern by naming on state rather than the resource, a minor deviation but still readable.

Tool Count5/5

Five tools is well-scoped for a human-in-the-loop gate: each covers a necessary stage (create request, poll, block, enumerate, decide). No redundant or filler tools are present.

Completeness4/5

The surface covers the core lifecycle including creation, status checks, blocking waits, listing, and decision recording, with expiry handled implicitly. A cancel/revoke tool for withdrawing a pending request is the notable missing operation, but agents can work around it via expiry.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    Human-in-the-loop approval gate for AI agents. Your agent calls submit_approval before any irreversible action; a human reviews on a branded page; a signed webhook fires back with the decision.
    1
    1
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    A local human-in-the-loop gate for AI agents that blocks actions until human approval, with timeout defaulting to denial.
    37 npm
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    Human approval for AI agents: request_human_approval pauses a high-stakes action until a named person approves or rejects (console, email, Telegram, Slack); silence is not consent. Every decision is sealed in a hash-chained audit log. EU-hosted.
    3
    18 PyPI
    MIT