Install
$ agentstack add mcp-geehexx-hitl-mcp-cli ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
🤝 HITL MCP CLI
Human-in-the-Loop MCP Server — Bridge the gap between AI autonomy and human judgment
[](https://opensource.org/licenses/Apache-2.0) [](https://www.python.org/downloads/) [](https://modelcontextprotocol.io)
██╗ ██╗██╗████████╗██╗ ███╗ ███╗ ██████╗██████╗
██║ ██║██║╚══██╔══╝██║ ████╗ ████║██╔════╝██╔══██╗
███████║██║ ██║ ██║ ██╔████╔██║██║ ██████╔╝
██╔══██║██║ ██║ ██║ ██║╚██╔╝██║██║ ██╔═══╝
██║ ██║██║ ██║ ███████╗ ██║ ╚═╝ ██║╚██████╗██║
╚═╝ ╚═╝╚═╝ ╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝╚═╝
🎯 Why Human-in-the-Loop?
AI agents are transforming how we work, but they shouldn't operate in isolation. HITL MCP CLI enables AI agents to request human input at critical decision points, combining the speed of automation with the wisdom of human judgment.
The Problem
AI agents face situations where they need human guidance:
- 🤔 Ambiguity: Requirements aren't always clear-cut
- ⚠️ Risk: Some operations are too sensitive to automate blindly
- 🎨 Preference: Multiple valid approaches exist, but humans have context
- ✅ Validation: Assumptions need confirmation before proceeding
The Solution
HITL MCP CLI provides a standardized, elegant interface for AI agents to request human input without breaking their workflow. Instead of agents making potentially wrong assumptions or halting entirely, they can:
- Ask clarifying questions when requirements are ambiguous
- Request approval before destructive or sensitive operations
- Present options and let humans choose the best approach
- Confirm assumptions to ensure alignment with human intent
Real-World Scenarios
🤖 Agent: "I found 3 ways to implement this feature. Which approach do you prefer?"
👤 Human: [Selects Option B: Balanced performance and maintainability]
🤖 Agent: "Implementing Option B..."
🤖 Agent: "I'm about to delete 150 deprecated files. Proceed?"
👤 Human: "Yes, proceed"
🤖 Agent: "Deleted 150 files. ✅ Complete"
🤖 Agent: "Should I deploy to staging or production?"
👤 Human: "Staging first"
🤖 Agent: "Deploying to staging environment..."
✨ Features
- 🎯 5 Interactive Tools: Collect input, ask questions, choose from options, confirm actions, and send notifications
- 🖥️ Full TUI: Textual-based terminal UI with sessions panel, queue history, and collapsible messages
- 🚀 Instant Setup: Works with
uvx— no installation required - 🔌 MCP Standard: Seamless integration with any MCP-compatible AI agent
- ⚡ Lightning Fast: Async-first design with minimal overhead
- 🛡️ Type-Safe: Full type hints for reliability and IDE support
- 📊 Interaction Logging: All tool calls logged to
~/.local/state/hitl-mcp/interactions.jsonl - 🔧 Customizable: Customize host/port, log level
⚠️ Critical Configuration
Timeout Setting Required: HITL operations require infinite timeout because human response time is unpredictable. Without this, tool calls will fail after 60 seconds.
Set "timeout": 0 in your MCP client configuration (see below).
🚀 Quick Start
Installation
# Run directly without installation (recommended)
uvx hitl-mcp-cli
# Or install globally
uv tool install hitl-mcp-cli
Start the Server
# Default: TUI mode on localhost:5555
hitl-mcp
# Custom host/port
hitl-mcp --host 0.0.0.0 --port 8080
# Using environment variables
export HITL_HOST=0.0.0.0
export HITL_PORT=8080
export HITL_LOG_LEVEL=INFO
hitl-mcp
Environment Variables:
HITL_HOST: Server host (default: 127.0.0.1)HITL_PORT: Server port (default: 5555)HITL_LOG_LEVEL: Logging level - DEBUG, INFO, WARNING, ERROR (default: ERROR)HITL_NO_BANNER: Disable startup banner - true/false (default: false)
Configure Your AI Agent
Add to your MCP client configuration (e.g., Claude Desktop, Cline):
{
"mcpServers": {
"hitl": {
"url": "http://127.0.0.1:5555/mcp",
"transport": "streamable-http",
"timeout": 0
}
}
}
⚠️ Important: Set "timeout": 0 for infinite timeout. Human input is unpredictable - users may take seconds or minutes to respond. The default 60-second MCP timeout will cause tool calls to fail if users don't respond quickly enough.
> Note: The server runs in stateless HTTP mode, which is required for MCP clients > that make independent HTTP requests (including Kiro CLI and most MCP clients).
That's it! Your AI agent can now request human input.
🛠️ Available Tools
1. hitl_collect — Collect Input
Collect a single input value from the user. Use for text, file paths, or multiline content.
When to use:
- Collecting names, descriptions, or free-form input
- Getting file/directory paths with completion
- Requesting multi-line content (code snippets, descriptions)
Example:
name = await hitl_collect(
message="What should we name this project?",
default="my-project",
validation_pattern=r"^[a-z0-9-]+$"
)
# Path input
config = await hitl_collect(
message="Select configuration file:",
input_type="path"
)
# Multiline input
description = await hitl_collect(
message="Enter project description:",
input_type="multiline"
)
Parameters:
message(str): Question to displayinput_type(Literal["text", "path", "multiline"]): Input mode (default: "text")default(str, optional): Pre-filled valuevalidation_pattern(str, optional): Regex pattern for validationvalidation_message(str, optional): Custom validation error messagecontext(str, optional): Additional context displayed above the promptstrip_whitespace(bool): Strip leading/trailing whitespace from input (default: False)required(bool): Reject empty input (default: False)path_type(Literal["file", "dir", "any"], optional): Validate path type wheninput_type="path"agent_name(str, optional): Calling agent identifier (shown in Sessions panel)project_id(str, optional): Project identifier for session groupingstep(int, optional): Current step number (shown as "Step X/Y")total_steps(int, optional): Total steps in workflownotes(str, optional): Freeform context displayed as a dimmed line below the message
2. hitl_ask — Ask a Question
Alias for hitl_collect. Use whichever name reads more naturally in your agent's workflow.
3. hitl_choose — Present Choices
Present a list of options for the user to select from. Supports single or multiple selection, fuzzy search for long lists, and rich option descriptions.
When to use:
- Choosing between implementation approaches
- Selecting deployment environments
- Picking features to enable
Example:
# Simple choices
env = await hitl_choose(
message="Which environment should I deploy to?",
choices=["Development", "Staging", "Production"],
default="Staging"
)
# Rich options with descriptions
approach = await hitl_choose(
message="Select implementation approach:",
options=[
{"value": "fast", "label": "Fast", "description": "Quick but uses more memory"},
{"value": "safe", "label": "Safe", "description": "Slower but reliable"},
]
)
# Multiple selections
features = await hitl_choose(
message="Which features should I enable?",
choices=["Authentication", "Caching", "Logging", "Monitoring"],
multiple=True
)
Parameters:
message(str): Question to displaychoices(list[str], optional): Simple option stringsoptions(list[dict], optional): Rich options with value/label/descriptionmultiple(bool): Enable checkbox mode (default: False)default(str, optional): Pre-selected optionfuzzy_search(bool, optional): Force fuzzy search on/off (auto for >15 items)context(str, optional): Additional context displayed above the promptagent_name(str, optional): Calling agent identifier (shown in Sessions panel)project_id(str, optional): Project identifier for session groupingstep(int, optional): Current step number (shown as "Step X/Y")total_steps(int, optional): Total steps in workflownotes(str, optional): Freeform context displayed as a dimmed line below the message
> Escape hatch: In multiple-selection mode, if the user selects all or none of the options, they are offered a free-text input to explain their intent.
4. hitl_confirm — Get Confirmation
Ask the user to confirm or reject an action. Use severity='high' for destructive operations.
When to use:
- Before destructive operations (delete, overwrite)
- Before expensive operations (API calls, deployments)
- Confirming assumptions or interpretations
Example:
# Standard confirmation
result = await hitl_confirm(
message="I will delete 50 unused dependencies. Proceed?",
default=False
)
if result["action"] == "accept":
...
# High severity — requires typed "yes"
result = await hitl_confirm(
message="Delete production database?",
severity="high",
context="This will affect 10,000 active users.\nDowntime: ~30 seconds."
)
# Timed confirmation
result = await hitl_confirm(
message="Deploy to production?",
severity="high",
timeout_seconds=300
)
if result.get("timed_out"):
print("Approval timed out")
Parameters:
message(str): Yes/no questiondefault(bool): Default answer (default: False)severity(Literal["low", "medium", "high"]): Confirmation intensity (default: "medium")context(str, optional): Additional context displayed in a panel above the prompttimeout_seconds(int): Seconds to wait, 0 = infinite (default: 0)agent_name(str, optional): Calling agent identifier (shown in Sessions panel)project_id(str, optional): Project identifier for session groupingstep(int, optional): Current step number (shown as "Step X/Y")total_steps(int, optional): Total steps in workflownotes(str, optional): Freeform context displayed as a dimmed line below the message
Returns: {"action": "accept"|"decline"|"cancel"}. When timeout_seconds > 0, also includes "timed_out": bool.
5. hitl_notify — Display Notifications
Display a styled notification to the user. Non-blocking — does not wait for input.
When to use:
- Confirming successful operations
- Reporting errors or warnings
- Providing progress updates
Example:
await hitl_notify(
message="Successfully deployed v2.1.0 to production\n\nURL: https://app.example.com",
level="success",
title="Deployment Complete"
)
await hitl_notify(
message="The old API will be removed in v3.0",
level="warning",
title="Deprecation Warning"
)
Parameters:
message(str): Detailed message (supports multi-line)level(Literal["success", "info", "warning", "error"]): Visual style (default: "info")title(str, optional): Notification titleagent_name(str, optional): Calling agent identifier (shown in Sessions panel)project_id(str, optional): Project identifier for session groupingstep(int, optional): Current step number (shown as "Step X/Y")total_steps(int, optional): Total steps in workflownotes(str, optional): Freeform context displayed as a dimmed line below the notification
📖 Usage Patterns
Pattern 1: Clarification
When requirements are ambiguous, ask specific questions:
approach = await hitl_choose(
message="I can implement this feature in two ways. Which do you prefer?",
choices=[
"Option A: Fast implementation, higher memory usage",
"Option B: Slower but more memory efficient",
"Option C: Balanced approach (recommended)"
],
default="Option C: Balanced approach (recommended)"
)
Pattern 2: Approval Gate
Request approval before significant actions:
files_to_delete = find_unused_files()
result = await hitl_confirm(
message=f"I found {len(files_to_delete)} unused files. Delete them?",
default=False
)
if result["action"] == "accept":
delete_files(files_to_delete)
await hitl_notify(
message=f"Deleted {len(files_to_delete)} unused files",
level="success",
title="Cleanup Complete"
)
else:
await hitl_notify(
message="No files were deleted",
level="info",
title="Cancelled"
)
Pattern 3: Information Gathering
Collect structured data through multiple prompts:
project_name = await hitl_collect(
message="Project name:",
validation_pattern=r"^[a-z0-9-]+$"
)
language = await hitl_choose(
message="Programming language:",
choices=["Python", "TypeScript", "Go", "Rust"]
)
features = await hitl_choose(
message="Select features to include:",
choices=["Testing", "Linting", "CI/CD", "Documentation"],
multiple=True
)
output_dir = await hitl_collect(
message="Output directory:",
input_type="path"
)
Pattern 4: Progressive Disclosure
Start with high-level choices, then drill down:
action = await hitl_choose(
message="What would you like to do?",
choices=["Deploy", "Rollback", "View Logs", "Run Tests"]
)
if action == "Deploy":
env = await hitl_choose(
message="Deploy to which environment?",
choices=["Staging", "Production"]
)
if env == "Production":
result = await hitl_confirm(
message="Deploy to PRODUCTION?",
context="This will affect live users.",
severity="high"
)
if result["action"] == "accept":
await deploy_to_production()
🏗️ Architecture
AI Agent (Claude, GPT, etc.)
↓ HTTP (MCP Protocol)
FastMCP Server
↓ Async Calls
TUI Layer (Textual)
↓ Terminal I/O
User
See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for detailed architecture documentation.
🖥️ TUI Features
The Textual-based TUI provides a rich terminal interface:
- Sessions panel: Shows agent names and projects, color-coded by recency (active 15 items) automatically enable search filtering
- ✅ Terminal compatibility: Works with screen readers through terminal emulators
See [docs/ACCESSIBILITY.md](docs/ACCESSIBILITY.md) for detailed accessibility information, testing methodology, and recommendations for users with diverse needs.
💻 VS Code Terminal
For the best experience in VS Code's integrated terminal, add to your settings.json:
{
"terminal.integrated.allowChords": false,
"terminal.integrated.sendKeybindingsToShell": true
}
This ensures Ctrl+\ (command palette) and other key bindings reach the TUI.
> Note: ctrl+b and ctrl+\ may be intercepted by VS Code. Add these to commandsToSkipShell in your VS Code settings, or use f2 (log level) and f3 (toggle sessions) as alternatives.
🔧 Troubleshooting
Tool Calls Timeout After 60 Seconds
Problem: Tools fail with "Request timed out" error when user takes longer than 60 seconds to respond.
Solution: Set "timeout": 0 in your MCP client configuration:
{
"mcpServers": {
"hitl": {
"url": "http://127.0.0.1:5555/mcp",
"transport": "streamable-http",
"timeout": 0
}
}
}
Why: The MCP protocol has a default 60-second timeout. Human input is unpredictable - users may need minutes to make decisions. Setting timeout to 0 means infinite wait.
Server Won't Start
Problem: Port already in use.
Solution: Either stop the other process using port 5555, or start the server on a different port:
hitl-mcp --port 8080
Don't forget to update your MCP client configuration to match the new port.
Tools Not Appearing in Agent
Problem: Agent doesn't see the HITL tools.
Solution:
- Verify the server is running (
hitl-mcpshould show startup banner) - Check you
…
Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: geehexx
- Source: geehexx/hitl-mcp-cli
- License: Apache-2.0
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.