Skip to content

GoPlausible/mcp-hitl

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MCP-HITL

A TypeScript reference implementation of enforced Human-in-the-Loop (HITL) for MCP tools using TOTP (Google Authenticator).

The Problem

There is an assumption about MCP tools that agent (Claude, Copilot, etc.) can invoke those tools eventually if want to and there is no way to enforce human in the loop (HITL) for critical operations. For example, if you have a tool that signs a transaction or credential with key material, you want to ensure that a human approves the query before it runs every and each time. This small POC project demostrates a pattern to prove MCP tools can be enforced for HITL when needed, without relying on the agent's cooperation or honesty.

The Solution

Move the approval gate inside the tool itself. The tool literally cannot execute without a valid, human-issued cryptographic proof -- no matter what permissions the agent sets.

Agent calls tool --> Tool checks for human proof --> No proof? Refuse. Period.
                                                     Valid proof? Execute.

How It Works

This POC provides 2 tools that return hardcoded data, but with different security gates:

Tool Gate What's Required
get_color None Nothing -- executes immediately, returns "Dark Blue"
get_weather TOTP 6-digit code from Google Authenticator

OTP Flow (2-turn)

Turn 1: Agent asks user for OTP code (required parameter)

Turn 2: Human opens Google Authenticator, reads 6-digit code, gives it to agent
        Agent calls get_weather({ otp: "482019" })
        --> Verified. Weather data returned.

Quick Start

Prerequisites

  • Node.js 18+
  • npm
  • Google Authenticator (or any TOTP app) on your phone

Install & Build

git clone https://github.com/GoPlausible/mcp-hitl.git
cd mcp-hitl
npm install
npm run build
cp .env.example .env  # Create .env for TOTP secret persistence

Test Run

npm start

On startup, the server prints to stderr:

  1. A QR code for Google Authenticator -- scan it to add the TOTP entry
  2. The TOTP secret (base32) for manual entry

Note: The TOTP secret is generated on first run and persisted to .env. The same secret is reused across restarts -- scan the QR only once.

Agent Configuration

Claude Desktop

Add to your Claude Desktop config (~/Library/Application Support/Claude/claude_desktop_config.json on macOS):

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

Restart Claude Desktop. The server's QR code and setup info appear in the MCP server logs (check Developer Tools > Console or the MCP log file).

Testing:

  1. Ask Claude: "Get the color" -- it will use get_color (no gate)
  2. Ask Claude: "Get the weather using the OTP-protected tool" -- it will call get_weather, ask you for the OTP code, then call with it

Claude Code (CLI)

Add to your project's .mcp.json or global Claude Code MCP config:

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

Or add via CLI:

claude mcp add mcp-hitl node /absolute/path/to/mcp-hitl/dist/index.js

The QR code appears in stderr during the MCP connection. Use /mcp to verify the server is connected.

Cline (VS Code Extension)

  1. Open VS Code Settings > Cline > MCP Servers
  2. Add a new server with:
{
  "mcp-hitl": {
    "command": "node",
    "args": ["/absolute/path/to/mcp-hitl/dist/index.js"]
  }
}

Or edit ~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json:

{
  "mcpServers": {
    "mcp-hitl": {
      "command": "node",
      "args": ["/absolute/path/to/mcp-hitl/dist/index.js"],
      "disabled": false
    }
  }
}

Cline will show tool approval prompts inline. When the OTP gate triggers, Cline will display the APPROVAL_REQUIRED message and you can provide the OTP code in your next message.

GitHub Copilot (VS Code Agent Mode)

  1. Create a .vscode/mcp.json in your project root:
{
  "servers": {
    "mcp-hitl": {
      "command": "node",
      "args": ["/absolute/path/to/mcp-hitl/dist/index.js"]
    }
  }
}
  1. In VS Code, open Copilot Chat in Agent mode (@workspace)
  2. Copilot will discover the MCP tools and list them
  3. Ask Copilot to use the tools -- it will handle the HITL flow

OpenCode

Add to your .opencode/config.json:

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

OpenCode will list the available tools. The HITL flow works the same -- the agent gets the APPROVAL_REQUIRED response and asks you for the OTP code.

Testing the OTP Flow

  1. Setup: When the server starts, scan the QR code with Google Authenticator (or manually enter the base32 secret)
  2. Call the tool: Ask your agent to get weather using the OTP-protected tool
  3. Agent gets gate response: The tool returns APPROVAL_REQUIRED with instructions
  4. Provide OTP: Open Google Authenticator, read the 6-digit code, type it when the agent asks
  5. Agent re-calls: The tool verifies the OTP and returns weather data

Quick Test with MCP Inspector

You can test the server directly with the MCP Inspector:

npx @modelcontextprotocol/inspector node dist/index.js

This opens a web UI where you can call tools directly and see the HITL flow in action.

Security Properties

Property OTP
Requires physical device Yes (authenticator app)
Time-bound Yes (30s TOTP window)
Replay-proof Yes (used codes tracked)
Brute-force resistant Yes (1M combinations per 30s window)
Offline capable Yes
Agent-unbypassable Yes (gate is inside the tool)

Project Structure

mcp-hitl/
  src/
    index.ts          # MCP server with 2 tools + HITL logic
    types.d.ts        # Type declarations for qrcode-terminal
  dist/               # Compiled output (after npm run build)
  PLAN.md             # Architecture & design document
  README.md           # This file
  package.json
  tsconfig.json

Production Considerations

This POC intentionally simplifies to demonstrate the core pattern. For production use:

  • Persist TOTP secrets in secure storage (vault, encrypted env vars) instead of generating per-session
  • Tiered security -- no gate for reads, OTP for writes, multi-factor for high-value operations
  • Audit logging -- log all HITL approvals/rejections to an immutable store
  • Multi-operator support -- per-operator TOTP secrets
  • Rate limiting -- lockout after N failed OTP attempts

License

MIT -- see LICENSE

Author

GoPlausible

About

A typescript reference implementation of enforced HITL for MCP tools using two approaches: OTP and WebAuthn (ed25519 signatures)

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors