# Hitl Mcp Cli

> A HITL (Human-In-The-Loop) MCP server that runs on CLI.

- **Type:** MCP server
- **Install:** `agentstack add mcp-geehexx-hitl-mcp-cli`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [geehexx](https://agentstack.voostack.com/s/geehexx)
- **Installs:** 0
- **Category:** [Integrations](https://agentstack.voostack.com/c/integrations)
- **Latest version:** 0.1.0
- **License:** Apache-2.0
- **Upstream author:** [geehexx](https://github.com/geehexx)
- **Source:** https://github.com/geehexx/hitl-mcp-cli

## Install

```sh
agentstack add mcp-geehexx-hitl-mcp-cli
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## 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

```bash
# Run directly without installation (recommended)
uvx hitl-mcp-cli

# Or install globally
uv tool install hitl-mcp-cli
```

### Start the Server

```bash
# 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):

```json
{
  "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**:
```python
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 display
- `input_type` (Literal["text", "path", "multiline"]): Input mode (default: "text")
- `default` (str, optional): Pre-filled value
- `validation_pattern` (str, optional): Regex pattern for validation
- `validation_message` (str, optional): Custom validation error message
- `context` (str, optional): Additional context displayed above the prompt
- `strip_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 when `input_type="path"`
- `agent_name` (str, optional): Calling agent identifier (shown in Sessions panel)
- `project_id` (str, optional): Project identifier for session grouping
- `step` (int, optional): Current step number (shown as "Step X/Y")
- `total_steps` (int, optional): Total steps in workflow
- `notes` (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**:
```python
# 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 display
- `choices` (list[str], optional): Simple option strings
- `options` (list[dict], optional): Rich options with value/label/description
- `multiple` (bool): Enable checkbox mode (default: False)
- `default` (str, optional): Pre-selected option
- `fuzzy_search` (bool, optional): Force fuzzy search on/off (auto for >15 items)
- `context` (str, optional): Additional context displayed above the prompt
- `agent_name` (str, optional): Calling agent identifier (shown in Sessions panel)
- `project_id` (str, optional): Project identifier for session grouping
- `step` (int, optional): Current step number (shown as "Step X/Y")
- `total_steps` (int, optional): Total steps in workflow
- `notes` (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**:
```python
# 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 question
- `default` (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 prompt
- `timeout_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 grouping
- `step` (int, optional): Current step number (shown as "Step X/Y")
- `total_steps` (int, optional): Total steps in workflow
- `notes` (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**:
```python
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 title
- `agent_name` (str, optional): Calling agent identifier (shown in Sessions panel)
- `project_id` (str, optional): Project identifier for session grouping
- `step` (int, optional): Current step number (shown as "Step X/Y")
- `total_steps` (int, optional): Total steps in workflow
- `notes` (str, optional): Freeform context displayed as a dimmed line below the notification

---

## 📖 Usage Patterns

### Pattern 1: Clarification

When requirements are ambiguous, ask specific questions:

```python
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:

```python
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:

```python
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:

```python
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`:

```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:

```json
{
  "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:

```bash
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**:
1. Verify the server is running (`hitl-mcp` should show startup banner)
2. 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](https://github.com/geehexx)
- **Source:** [geehexx/hitl-mcp-cli](https://github.com/geehexx/hitl-mcp-cli)
- **License:** Apache-2.0

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-geehexx-hitl-mcp-cli
- Seller: https://agentstack.voostack.com/s/geehexx
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
