askhuman
Supports delivering AskHuman requests or notifications to Discord via webhook adapters in the optional shared service, with responses handled through browser response links.
Supports LangChain through a native adapter, allowing LangChain agents to use AskHuman for human clarification, decisions, approvals, and notifications.
Supports LangGraph through a native adapter, allowing LangGraph workflows to use AskHuman for human clarification, decisions, approvals, and notifications.
Supports OpenAI Agents SDK through a native adapter and provides portable JSON Schema tools for OpenAI runtimes, allowing agents to request human clarification, decisions, approvals, and notifications.
Enables human-in-the-loop questions and replies through Slack thread replies using Socket Mode, and can deliver notifications or response links via Slack webhooks when the optional shared service is used.
Enables human-in-the-loop questions and replies through Telegram polling, with support for configured allowed human user IDs and delivery of retained replies when a new process starts.
Provides server-side adapters for sending AskHuman questions or notifications over Twilio SMS and WhatsApp, with responses handled through browser response links.
Provides server-side integration for sending AskHuman questions or notifications through WhatsApp via Twilio adapters, with responses handled through browser response links.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@askhumanask a human to approve deploying commit abc123 to staging"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
askhuman ↗
Give your agent a human when it gets stuck.
AskHuman runs inside your Python process, like an embedded database. Import it, ask a question, and continue with a structured human answer. The default terminal channel needs no server, account, API key, or initialization command.
from askhuman import AskHuman
async with AskHuman() as human:
answer = await human.decide(
"Which customer definition should this report use?",
options=["Active subscription", "Any customer with revenue"],
)
print(answer.selected_option)Humans configure how agents reach them. Embedded mode supports terminal input, Telegram replies through polling, and Slack thread replies through Socket Mode. An optional HTTP service provides a shared web inbox and additional channels.
Try it locally
Python 3.11+ is required. This package is not yet published to PyPI or npm.
git clone https://github.com/alevtdagen/askhuman.git
cd askhuman
python3 -m venv .venv
source .venv/bin/activate
pip install -e .
python examples/python_agent.pyAnswer in the terminal. Requests and responses are saved in .askhuman/embedded.sqlite3.
The core depends on httpx and pydantic; FastAPI, MCP, and Slack SDK dependencies are
optional. Nothing listens on a network port in embedded mode.
You can also use the CLI:
askhuman ask 'Which database should I use?' --kind decision --option Postgres --option BigQueryThe Python ask_human(...) convenience function uses the same default configuration.
For concurrent questions, share one AskHuman instance and use asyncio.gather(...).
Related MCP server: AskMeMCP
Let the human choose a channel
askhuman configure # interactive channel setup
askhuman configure --channel telegram # a dedicated bot and allowed human user IDs
pip install -e '.[slack]'
askhuman configure --channel slack # bot token, app token, channel, allowed user IDsChoose one setup command for your initial configuration. The command writes a private
.askhuman/config.json file and refuses to overwrite an existing file accidentally.
Edit that file or import a complete replacement with askhuman configure --from-file FILE.
Changes take effect when you create a new AskHuman instance. Tokens may be environment
references such as env:TELEGRAM_BOT_TOKEN.
The agent still calls human.ask(...); it does not choose delivery credentials.
Recipient aliases such as recipient="data-owner" map to human-configured channels.
Multiple channels can receive a question; the first valid answer wins.
See embedded channel setup for configuration examples and permissions.
Ask, decide, approve
async with AskHuman() as human:
detail = await human.clarify("Which dataset is authoritative?")
choice = await human.decide("Which approach?", options=["Fast", "Thorough"])
approval = await human.approve(
"Deploy commit abc123 to staging?",
context="Tests passed. This changes the staging API and is reversible.",
timeout_seconds=3600,
)
if approval.approved is True:
print("The described deployment was approved.")
await human.notify("The report is ready for review.")An answer includes request_id, answer, selected_option, approved, respondent,
timestamp, and source. Approval always requires an explicit choice. For direct channels,
reply Approve / Reject, or the displayed option number. Generic prose such as “yes”
does not grant approval. For decisions, use the exact option label or its number.
Telegram/Slack replies must come from a configured human ID in the configured chat/thread.
Their respondent values are provider IDs such as telegram:123 or slack:U123.
Terminal respondent labels identify the local OS user (or the name entered by the human
CLI operator). Web response names are self-reported. The application still enforces whether
an approved action may actually run.
Other request kinds are ask, expertise, verification, exception, and notification.
notify() returns after its initial delivery attempt, without waiting for an answer.
Its notified status means queued; inspect deliveries for delivery outcome.
Persist and resume
from askhuman import AskHuman, HumanTimeout
async with AskHuman(data_dir="./workflow-state") as human:
request = await human.create(
"May I publish report Q3 revision 4?",
kind="approval",
idempotency_key="report-q3-revision4-publish",
timeout_seconds=86400,
)
# Persist request.id in your workflow state before waiting.
try:
answer = await human.wait(request.id, wait_timeout=25)
except HumanTimeout as pending:
print(pending.request_id, "expired" if pending.expired else "still pending")
# Later, even in a new process with the same data_dir:
# answer = await human.wait(pending.request_id)SQLite stores requests, answers, delivery attempts, message-to-request mappings, and Telegram update offsets. Retrying the same question with the same idempotency key reuses its ID. Reusing a key for different input fails with a conflict. Deadlines, cancellations, and single-winner responses are enforced by the shared store in both modes.
wait_timeout limits a waiting session; timeout_seconds sets the request's lifetime.
A timeout or cancellation of the calling coroutine never becomes an approval and does not
cancel the saved request. Use human.cancel(id) when the question is no longer relevant.
Terminal waits are cancellable and do not leave a blocked input thread behind.
Keep the async context open while receiving messaging replies. Closing it stops delivery and receiver tasks and releases the workspace lock. Telegram can deliver replies retained by its service when the next process starts (up to 24 hours of retention). Slack Socket Mode has no offline inbox replay in this release: the process must be running for live replies. An agent host must resume the saved workflow; AskHuman does not restart agent processes.
Use one active embedded runtime per data directory and dedicated messaging bot. Share a
client across tasks in one process. Separate agent processes should use distinct workspaces
and bots, or connect to the optional server. A filesystem lock prevents two embedded runtimes
from consuming the same workspace's replies. Local human inbox and answer commands can
operate while that runtime is waiting.
Framework tools, MCP, and skills
The Python API works inside any async agent function. Optional native adapters support OpenAI Agents SDK and LangChain/LangGraph, with portable JSON Schema tools for OpenAI, Anthropic, Gemini, and custom runtimes. See integration recipes. All Python adapters use the embedded engine by default.
For MCP hosts:
pip install -e '.[mcp]'{
"mcpServers": {
"askhuman": {
"command": "/absolute/path/to/askhuman/.venv/bin/askhuman-mcp",
"env": {
"ASKHUMAN_MODE": "embedded",
"ASKHUMAN_DATA_DIR": "/absolute/path/to/human-workspace"
}
}
}
}The host launches a stdio process, with no separately deployed HTTP service. Configure a messaging channel in that workspace, or answer from another terminal:
export ASKHUMAN_DATA_DIR=/absolute/path/to/human-workspace
askhuman --mode embedded inbox
askhuman --mode embedded answer REQUEST_ID --approve --name 'Sam'MCP never reads terminal input from its protocol stream. Its tools are ask_human,
get_human_response, wait_for_human, and cancel_human_request. Each wait is bounded to
25 seconds; pending results retain their IDs for a later call.
Install the reusable agent skill with askhuman install-skill .agents/skills. It supports
both modes and teaches agents to preserve IDs, distinguish pending from answered, and
require explicit approvals. The installer never overwrites an existing skill.
Optional shared service
Use the service when you need a browser inbox, independently running agents, or a receiver that remains available while agents stop. It retains the original REST API and server-side adapters for Slack/Teams/Discord webhooks, Telegram notifications, SMTP email, Twilio SMS/WhatsApp, and signed custom webhooks. Those adapters send browser response links.
pip install -e '.[server]'
askhuman init
askhuman serveOpen http://127.0.0.1:8765 and sign in with the output of askhuman token --admin.
Use askhuman token for the agent key. Select HTTP explicitly:
async with AskHuman(base_url="http://127.0.0.1:8765", api_key="your-agent-key") as human:
answer = await human.ask("Which source should I trust?")AskHuman(mode="remote") loads local server settings, while explicit
ASKHUMAN_BASE_URL / ASKHUMAN_API_KEY environment variables also select remote mode.
ASKHUMAN_MODE=embedded forces local operation even if remote environment variables exist.
Use askhuman --mode remote ... for CLI commands targeting the service.
The TypeScript package remains an HTTP client for this service. See
packages/typescript for build and installation instructions.

Server channel setup and webhook contracts describe provider credentials, HTTPS deployment, and the distinction between notifications and direct replies.
Configuration, trust, and migration from 0.1
Default changed:
AskHuman()now runs locally. It does not infer remote mode from the existence of.askhuman/settings.json. Supplymode="remote"to retain that behavior.Storage is separate: embedded state uses
embedded.sqlite3; the service keepsaskhuman.sqlite3andsettings.json. Existing server questions are preserved and must be resumed through the remote client. No automatic database migration is performed.Configuration:
ASKHUMAN_DATA_DIRselects the state directory;ASKHUMAN_CONFIGorAskHuman(config="path.json")selects an embedded routing file. Defaults are local to the working directory. Store files securely and use a stable absolute directory for MCP.Process trust: embedded code can access local state and channel credentials. There is no isolation between the agent and the library in one process. Use the service with separate operator/agent keys when that boundary matters.
Delivery: retries are at least once, up to five attempts. A crash after a provider accepts a message can cause a duplicate notification. Recorded responses remain single-winner. Unsupported embedded channels fail with guidance to use the server instead.
This release targets your own trusted humans. It does not provide SSO, multi-tenant RBAC, a secret vault, or automatic execution of approved actions. Keep credentials out of questions.
Development
pip install -e '.[server,mcp,dev,openai,langchain,slack]'
pytest
ruff check src tests examples
ruff format --check src tests examples
python -m build
cd packages/typescript && npm install && npm testTests cover both runtimes, direct-response identity checks, terminal cancellation,
request persistence, idempotency, approval semantics, delivery contracts, native framework
adapters, and live stdio MCP round trips with no HTTP server. Provider network calls are
mocked; real account delivery requires your own credentials. The server serves OpenAPI at
/docs.
The product hypothesis is simple: removing channel-specific human-input code makes agents easier to build and supervise. Validate real workflow adoption before adding expertise inference, organizational memory, marketplaces, voice, or mobile push. The competitor and market-attention claims in the original ideation remain unverified.
Licensed under the Apache License 2.0. See NOTICE for attribution.
This server cannot be deployed
Maintenance
Related MCP Connectors
Give any AI agent a way to ask a person — for approval, a decision, an answer or a review.
Human-input bridge for AI agents with voice-first answer links, MCP tools, and HTTP APIs.
Human-in-the-loop review and approval for AI agents. Audit trail, approval policies, native MCP.
Ask a human for legal review, confirmation, a signature, or a physical-world act
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables AI assistants to request human input through a web interface, allowing them to pause execution and wait for responses via interactive tools like single questions, multiple choice selections, hypothesis challenges, and decision workflows.12 npm2MIT
- AlicenseNot gradedqualityDmaintenanceEnables AI assistants to request human input through a web interface during execution. Supports single questions, multiple choice selections, hypothesis challenges, and decision workflows for human-in-the-loop interactions.12 npmMIT
- AlicenseNot gradedqualityAmaintenanceMCP server that provides an ask_human tool so AI agents can request decisions from users via chat apps, with deadlines, defaults, and an append-only audit log.93 npmApache 2.0
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to send notifications to a unified feed, report activity, request input from humans, and manage notification state through MCP tools.4MIT