human-mcp
human-mcp lets an agent ask a logged-in person for help without leaving the MCP loop. The agent calls a tool, the server creates a task, the human answers in the web UI, and the agent receives the result.
Use it when a workflow needs a person for one small but important step: choosing between options, reading an image, checking a website, retrieving an account-bound code, approving a risky action, or escalating an ambiguous decision.
For Simplified Chinese documentation, see README.zh-CN.md.
Repository Map
| Area | Path | Purpose |
|---|---|---|
| Server | src/ |
Rust MCP server, REST APIs, auth, task lifecycle, and deployment logic. |
| Web UI | human-mcp-webui/ |
React task workbench for humans who receive and answer requests. |
| SDK | crates/human-mcp-sdk/ |
Shared types for plugin manifests and integration code. |
| Examples | examples/ |
Minimal client and integration examples. |
| Deployment | docs/ |
Agent deployment prompt, systemd/nginx notes, packaging, and security details. |
Online Panel
| Purpose | URL |
|---|---|
| Human workbench | https://human.lmm.best/ |
| MCP endpoint | https://human.lmm.best/mcp |
Use the site root for the browser panel. Use the no-trailing-slash endpoint for MCP JSON-RPC: /mcp.
Login And Registration
Use GitHub OAuth to register and log in to the public panel at https://human.lmm.best/. More login methods can be added later.
Normal users do not need a password. There is no ordinary user password to remember, store, or leak. After logging in, users can add a passkey and use passwordless sign-in on supported devices.
Email/password login is only for the administrator account. The admin password is a strong private secret and should not be published in README, issues, screenshots, examples, or user-facing setup guides.
Why People Care
| When the agent hits this | human-mcp gives it this |
|---|---|
| Needs judgment, not more tokens | A typed human task with title, prompt, choices, image, steps, and timeout |
| Needs a real account holder | A logged-in human workbench with identity, presence, and answer history |
| Cannot wait inside one request | Async task creation plus later polling with read_human_replies |
| Needs the right human | Profiles, tags, friends, online status, and reputation-aware discovery |
| Needs new collaboration patterns | Community plugin manifests for request templates, route strategies, scoring rules, and third-party channels |
| Needs operational deployment | Rust server, React UI, systemd, nginx, AUR packages, and release workflow |
How It Feels
flowchart LR
A[Agent] -->|calls MCP tool| B["POST /mcp"]
B --> C[Task envelope]
C --> D[Human workbench]
D -->|answer| E[Result or reply mailbox]
E --> A
C -. timeout .-> F[Expired request]
F --> G[Trash and structured MCP error]
The important design choice: the agent does not directly control a person. It creates a bounded task envelope with a type, owner, prompt, timeout, and lifecycle.
Most Interesting Parts
| Piece | Why it is useful |
|---|---|
| High-value shortcuts | approve, judge, and feedback cover the common agent escalations without making callers pick a task kind. |
| Blocking and async asks | Use ask_human when the agent should wait; use ask_human_*_async when the agent should keep moving. |
| Typed tasks | Text, choice, yes/no judgment, image review, and step-by-step tasks get clearer UI than a single free-form prompt. |
| Human directory | Agents can discover online humans by profile, tag, friend graph, and reputation, subject to server visibility rules. |
| Community plugins | Load request templates, route strategies, scoring rules, and external channel declarations from plugin manifests. |
| Agent-bound identity | Each human has an agent secret, so MCP requests are tied back to a specific human account instead of a global anonymous token. |
| Practical server packaging | The repo includes nginx routing, systemd units, Arch/AUR packaging, release artifacts, and admin-triggered updates. |
Deploy With An Agent
Copy this command, run it anywhere with internet access, then send the fetched prompt to the agent that will deploy human-mcp on your server:
curl -fsSL https://raw.githubusercontent.com/LIghtJUNction/human-mcp/main/docs/AGENT_DEPLOY_PROMPT.md
The prompt tells the agent to inspect the server, install the package, configure the site root and /mcp, verify the MCP endpoint, and keep admin secrets out of public output.
This is the short human-run path for an Arch Linux server. See docs/DEPLOYMENT.md for the longer deployment note.
- Install the package:
paru -S human-mcp-git
# or, after a GitHub Release exists:
paru -S human-mcp-bin
- Initialize the private admin login:
sudo human-mcp init-admin --email <admin-email>
sudoedit /etc/human-mcp.env
- Set the public URL and packaged web directory:
HUMAN_PUBLIC_BASE_URL=https://your-domain.example
HUMAN_WEB_DIST=/usr/share/human-mcp/web
- Enable GitHub OAuth for normal users by setting the GitHub client id and secret in
/etc/human-mcp.env:
HUMAN_GITHUB_CLIENT_ID=<github-oauth-client-id>
HUMAN_GITHUB_CLIENT_SECRET=<github-oauth-client-secret>
- Start the service:
sudo systemctl enable --now human-mcp.service
curl -fsS http://127.0.0.1:8787/healthz
- Configure nginx so:
location = /mcpproxies to backend/mcpfor MCP JSON-RPC.location /proxies to the web UI, API, WebSocket, and static assets.
- Verify:
curl -fsS https://your-domain.example/api/auth/config
curl -i https://your-domain.example/
The panel should load at https://your-domain.example/. Normal users should register with GitHub OAuth, then optionally add a passkey.
Security Model
human-mcp is designed to make the trust boundary explicit: agents may request help, but humans, admins, and server policy decide what is visible and who may act.
| Boundary | Mechanism |
|---|---|
| Agent access | MCP calls require a per-human agent secret through x-human-agent-secret or Authorization: Bearer .... |
| Human access | Normal users log in with GitHub OAuth and can add passkeys for passwordless sign-in. |
| Admin access | Admin APIs call require_admin; email/password login is reserved for the administrator and should stay private. |
| Directory privacy | Agent directory visibility defaults to self_only; optional modes expose friends, public users, or users above a reputation threshold. |
| Reserved identity | #admin is reserved. Users and agents cannot assign it through profiles or task text. |
| Task containment | Requests have typed payloads, required title/prompt validation, normalized choices/tags, and server-clamped timeouts. |
| Runtime containment | The packaged service runs as the human-mcp user with ProtectSystem=strict, ProtectHome=true, and write access limited to /var/lib/human-mcp. |
| Self-update containment | The web UI can only trigger a configured update command. Packaged sudoers limits this to starting human-mcp-self-update.service. |
| Secret hygiene | Ordinary users have no password to leak. Session tokens are stored in memory as SHA-256 hashes keyed by HUMAN_SESSION_SECRET; OAuth client secrets are not returned by public config APIs. |
Human answers are still input. Treat them like any other external result: validate before using them for destructive actions, run the service behind HTTPS, and protect /etc/human-mcp.env.
Technical Details
The rest is folded so the README stays readable. Open only what you need.
- MCP JSON-RPC endpoint at
/mcp. - Human workbench web UI served at
/. - Rust backend with REST APIs and WebSocket updates.
- Bun/Vite/React frontend in the
human-mcp-webuigit submodule. - Blocking and async human request tools for text, choice, yes/no judgment, image review, and step-by-step tasks.
- Per-human agent access secrets, public profiles, tags, friends, reputation, reports, and online presence.
- Community plugin manifests loaded from
HUMAN_PLUGIN_DIR, with SDK types published ashuman-mcp-sdk. - AUR packages for Arch Linux:
human-mcp-git: builds from GitHub source.human-mcp-bin: installs a GitHub Release tarball.
- Admin-only password login, GitHub OAuth registration for normal users, and passkey sign-in support.
- Live presence count and persisted active periods in the users JSON file.
- Request envelope lifecycle: pending, answered, expired, trash, plus persisted late replies for async tools.
| Purpose | Path |
|---|---|
| MCP endpoint | /mcp |
| Web UI | / |
| Local backend bind | 127.0.0.1:8787 by default |
| Packaged web dist | /usr/share/human-mcp/web |
| Service env file | /etc/human-mcp.env |
| User/activity store | /var/lib/human-mcp/users.json |
| SQLite store | /var/lib/human-mcp/human-mcp.sqlite3 |
| systemd unit | human-mcp.service |
| self-update unit | human-mcp-self-update.service |
GET /mcp is intentionally not the UI; it returns a method warning. Open the UI at /.
cp env.example .env
cargo run
Build the frontend inside the submodule:
cd human-mcp-webui
bun install
bun run build
Then restart the backend. For local dev, HUMAN_WEB_DIST=./human-mcp-webui/dist is fine.
Useful checks:
cargo test
cargo check
cd human-mcp-webui && bun run build
curl -fsS http://127.0.0.1:8787/healthz
Implemented MCP methods:
initializenotifications/initializedtools/listtools/call
Current tools include:
| Tool | Purpose |
|---|---|
approve |
Ask for human approval or denial before a proposed action and wait for the answer. |
judge |
Ask for a human yes/no judgment and wait for the answer. |
feedback |
Ask for short free-form human feedback and wait for the answer. |
ask_human |
Create a human request and wait for the answer. |
ask_human_async |
Create a human request and return a request_id immediately. |
ask_human_text_async |
Create a non-blocking text-answer request. |
ask_human_choice_async |
Create a non-blocking choice request. |
ask_human_judgment_async |
Create a non-blocking yes/no judgment request. |
read_human_replies |
Read completed replies for the user attached to this agent secret. |
list_human_nodes |
List configured downstream human-mcp nodes without exposing secrets. |
search_human_network |
Search visible human profiles across downstream human-mcp nodes. |
ask_human_network_async |
Route an async human request to a downstream human-mcp node and collect the answer through read_human_replies. |
read_human_network_ledger |
Read the local hash-chained federation ledger for cross-node audit receipts. |
create_human_task |
Create a visible AI task for the human account attached to this agent secret. |
list_human_tasks |
List AI-created tasks for the attached human account. |
leave_human_memo |
Leave an offline memo on a visible human's memo board. |
list_agent_inbox |
List pending human-to-agent messages, including friend requests and ask-me prompts. |
request_human_friend |
Send a friend request from this connected agent to a visible human. |
accept_human_friend |
Accept a human's pending friend request to this connected agent. |
list_online_humans |
List online human operators and public profiles visible to this agent. |
search_human_profiles |
Search visible human profiles by text or #tag. |
list_human_tags |
List visible #tag usage counts. |
rate_human |
Rate a human from 0 to 10; feedback is weighted by the rater's own reputation. |
report_human |
Report a human to the administrator mailbox and apply a zero-score weighted feedback signal from this actor. |
list_human_plugins |
List loaded community plugins and their request templates, route strategies, scoring rules, and channels. |
create_human_request_from_template |
Create an async human request from a plugin request template. |
approve, judge, and feedback are blocking convenience wrappers over ask_human. Use the ask_human_*_async tools and read_human_replies when the agent should continue work while the human answers.
human-mcp Network
A human-mcp instance can act as a root or branch node and forward requests to downstream instances. The root loads child nodes from HUMAN_FEDERATION_FILE; each edge uses its own downstream agent secret. Remote answers are copied back into the root node's local reply mailbox, so callers still poll the root with read_human_replies.
TOML example:
[[nodes]]
node_id = "branch-cn"
endpoint = "https://branch.example/mcp"
agent_secret = "child-node-agent-secret"
description = "CN operators"
tags = ["#ops", "#cn"]
trust_level = "trusted"
max_hops = 3
Network tools:
list_human_nodes: inspect configured nodes;agent_secretis hidden.search_human_network: send profile searches to downstream nodes.ask_human_network_async: create a cross-node async request and read the returned localrequest_idwithread_human_replies.read_human_network_ledger: inspect recent local federation ledger entries and the current chain head.
The network borrows the useful part of blockchain design: a local append-only hash chain. Each cross-node request creation, expiry, failure, and collected reply appends a ledger entry whose hash includes the previous entry hash. This creates tamper-evident routing receipts without adding proof-of-work, tokens, or global consensus.
Reputation is a weighted trust score, not a raw average. GitHub OAuth seeds an initial prior from public account signals such as account age, public repositories, sampled stars, follower count, source-repo ratio, and recent activity. Each rating is weighted by the rater's current reputation, repeated ratings from the same actor update the prior rating, and reports create an audit entry plus a zero-score feedback signal. Profile and leaderboard responses include a public reputation_breakdown with seed source, seed weight, feedback weight, total weight, and confidence so agents can rank humans without guessing how much evidence backs the score.
ask_human
ask_human accepts simple human tasks:
{
"kind": "choice|judgment|text|image_review|steps",
"title": "Short task title",
"prompt": "What the human should do",
"choices": ["A", "B"],
"image_url": "https://...",
"image_base64": "iVBORw0KGgo...",
"image_mime_type": "image/png",
"steps": ["Open the site", "Read the SMS code"],
"timeout_seconds": 60,
"background": false
}
Image review tasks may use either image_url or image_base64. image_base64 may be raw base64 bytes or a full data:image/...;base64,... URL. When raw base64 is used, image_mime_type defaults to image/png.
timeout_seconds is agent-configurable. If omitted, the backend uses 60 seconds. Values are clamped by the server before the envelope is created. The blocking tool waits for the answer. The async tools return a request_id immediately; poll read_human_replies to collect completed replies.
The backend creates an envelope:
{
"id": "...",
"title": "...",
"prompt": "...",
"kind": "text",
"created_at": 1000,
"timeout_seconds": 60,
"expires_at": 1060
}
If the human does not answer in time, the request is removed from pending, added to trash, and the MCP caller receives a structured error:
{
"code": -32001,
"message": "Human request timed out after 60 seconds",
"data": {
"request_id": "...",
"title": "...",
"timeout_seconds": 60,
"expired_at": 1060,
"suggestion": "Try again with a longer timeout or simplify the request."
}
}
Example payloads are in examples/; examples/README.md maps each file to the
current MCP tool schema.
Tags are normalized to lowercase #tag values. #admin is a reserved tag: clients and agents cannot assign it through profile updates or task text, and the backend only derives it for the configured admin identity.
Community Plugins
Plugins are declarative JSON or TOML manifests loaded at startup from
HUMAN_PLUGIN_DIR. A manifest can contribute request templates, route
strategies, scoring rules, and third-party channel declarations. The server
exposes loaded plugins through list_human_plugins, and agents can create an
async request from a template with create_human_request_from_template.
Template ids use plugin-id/template-id. Template text supports simple
{{variable}} substitution from the tool call's variables object. Plugin
authors can use the published human-mcp-sdk crate to build and validate
manifests before distribution.
Example manifest: examples/community-release-plugin.json.
Authenticated UI APIs:
GET /api/meGET /api/me/profilePOST /api/me/profileGET /api/passkeysPOST /api/passkeys/register/startPOST /api/passkeys/register/finishPOST /api/passkeys/{id}/deleteGET /api/agent/accessPOST /api/agent/secretGET /api/requestsPOST /api/requests/{id}/answerGET /api/sentGET /api/tasksPOST /api/tasks/{id}/statusGET /api/trashPOST /api/trash/clearGET /api/users/onlineGET /api/users/search?q=...POST /api/humans/ratePOST /api/humans/reportGET /api/leaderboardGET /api/tagsGET /api/friendsPOST /api/friendsPOST /api/friends/{email}/acceptPOST /api/friends/{email}/removeGET /api/ws
Admin APIs:
GET /api/admin/usersPOST /api/admin/usersPOST /api/admin/users/{email}POST /api/admin/users/{email}/kickGET /api/admin/reportsGET /api/admin/settingsPOST /api/admin/settingsGET /api/admin/updatePOST /api/admin/updateGET /api/admin/webhooksPOST /api/admin/webhooks
WebSocket events:
{ "type": "request_created", "request": {} }
{ "type": "request_answered", "id": "...", "answer": {} }
{ "type": "request_expired", "id": "...", "expired_request": {} }
{ "type": "trash_cleaned", "removed_count": 1 }
{ "type": "presence_changed", "online_count": 1 }
env.example documents all supported variables. Important production values:
HUMAN_BIND=127.0.0.1:8787
HUMAN_PUBLIC_BASE_URL=https://your-domain.example
HUMAN_WEB_DIST=/usr/share/human-mcp/web
HUMAN_USERS_FILE=/var/lib/human-mcp/users.json
HUMAN_DB_FILE=/var/lib/human-mcp/human-mcp.sqlite3
HUMAN_ADMIN_EMAIL=<admin-email>
HUMAN_ADMIN_PASSWORD=<generated-admin-password>
HUMAN_SESSION_SECRET=<generated-session-secret>
HUMAN_TRASH_RETENTION_SECONDS=604800
HUMAN_CLEANUP_INTERVAL_SECONDS=60
HUMAN_SELF_UPDATE_COMMAND=/usr/bin/sudo -n /usr/bin/systemctl start human-mcp-self-update.service
HUMAN_SELF_UPDATE_TIMEOUT_SECONDS=30
HUMAN_PLUGIN_DIR=/etc/human-mcp/plugins
HUMAN_NODE_ID=root
HUMAN_FEDERATION_FILE=/etc/human-mcp/federation.toml
Production note: after AUR install, make sure /etc/human-mcp.env uses the packaged web directory:
HUMAN_WEB_DIST=/usr/share/human-mcp/web
If this is left as ./human-mcp-webui/dist, https://your-domain.example/ will return 404 because the service runs from /var/lib/human-mcp.
Install with an AUR helper as a normal user, not root:
paru -S human-mcp-git
# or, after a GitHub Release exists:
paru -S human-mcp-bin
On servers where the AUR user is arch, use a login shell:
sudo -iu arch paru -S human-mcp-git
Avoid sudo -u arch paru ...; it can inherit root's working directory/git environment and fail with errors like fatal: error reading '/root/.git'.
Initialize or reset the admin account:
sudo human-mcp init-admin --email <admin-email>
sudoedit /etc/human-mcp.env
sudo systemctl enable --now human-mcp.service
The init-admin command writes /etc/human-mcp.env, generates a new session secret, and prints the generated admin password. Save that password in your password manager; do not commit it.
Packaged installs include admin-triggered self-update support. The web UI calls POST /api/admin/update; the service then starts human-mcp-self-update.service through a sudoers rule limited to that exact systemctl command. The oneshot unit runs the configured AUR helper as the normal AUR user, upgrades human-mcp-bin or human-mcp-git, reloads systemd, and restarts human-mcp.service.
Override updater defaults in /etc/human-mcp-update.env when needed:
HUMAN_UPDATE_AUR_USER=arch
HUMAN_UPDATE_HELPER=paru
HUMAN_UPDATE_PACKAGE=human-mcp-bin
Because the updater runs from systemd without a TTY, the AUR user must be able to run its package installation step non-interactively, for example with sudo -n true succeeding for that user.
After editing /etc/human-mcp.env:
sudo systemctl restart human-mcp.service
Include packaging/nginx/human-mcp.conf in the HTTPS server block for your domain.
Required behavior:
location = /mcpproxies to backend/mcpfor JSON-RPC.location /proxies to backend/for the web UI, API, WebSocket, and static assets./mcp/should redirect to/.
Example verification:
sudo nginx -t
sudo systemctl reload nginx
curl -i https://your-domain.example/
curl -fsS https://your-domain.example/api/auth/config
A working / response should be HTTP 200 with the web UI HTML.
After install or upgrade:
pacman -Q human-mcp-git human-mcp-bin 2>/dev/null || true
human-mcp --version
systemctl is-active human-mcp.service
curl -fsS http://127.0.0.1:8787/healthz
curl -fsS http://127.0.0.1:8787/mcp \
-H 'content-type: application/json' \
--data @examples/mcp-tools-list.json
curl -i https://your-domain.example/
Expected:
- service is
active - health returns
{"ok":true} tools/listincludesask_human/returns the web UI HTML, not 404
Build a binary release tarball:
scripts/package-release.sh
# or explicitly:
scripts/package-release.sh <version>
With no argument, the script reads the version from Cargo.toml. If an explicit
version does not match Cargo.toml, packaging fails instead of producing a
misnamed tarball.
Or publish through GitHub Actions:
git tag v<version>
git push origin v<version>
The Release workflow can also be run manually from GitHub Actions. Its version input is optional and defaults to auto, which uses the version in Cargo.toml. The GitHub Release tag is always v<version>. It builds the Rust binary and web UI, uploads the tarball plus .sha256 as workflow artifacts, and creates or updates that GitHub Release.
The tarball is written to dist-release/ and contains:
human-mcpweb/packaging/systemd/human-mcp.servicepackaging/systemd/human-mcp-self-update.servicepackaging/scripts/human-mcp-self-updatepackaging/sudoers/human-mcp-self-updatepackaging/sysusers/human-mcp.confpackaging/tmpfiles/human-mcp.confenv.example
For human-mcp-bin, upload the tarball to GitHub Release v<version>, then update aur/human-mcp-bin/PKGBUILD and .SRCINFO with the new version and sha256.
https://domain/ returns 404
Check backend root first:
curl -i http://127.0.0.1:8787/
If it is also 404, inspect the running service environment:
pid=$(systemctl show -p MainPID --value human-mcp.service)
sudo tr '\0' '\n' < /proc/$pid/environ | grep HUMAN_WEB_DIST
Fix /etc/human-mcp.env:
HUMAN_WEB_DIST=/usr/share/human-mcp/web
Then restart:
sudo systemctl restart human-mcp.service
AUR build says it is reading /root/.git
Run paru through a login shell for the normal AUR user:
sudo -iu arch bash -lc 'unset GIT_DIR GIT_WORK_TREE; cd ~; paru -S human-mcp-git'
Admin login still shows <admin-email>
The admin account was not initialized. Run:
sudo human-mcp init-admin --email <admin-email>
sudo systemctl restart human-mcp.service
No comments yet
Be the first to share your take.