humanbottasks 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- humanbottasks-0.1.0/.gitignore +6 -0
- humanbottasks-0.1.0/FORUM_POSTS.md +69 -0
- humanbottasks-0.1.0/LAUNCH_POST.md +33 -0
- humanbottasks-0.1.0/LICENSE +21 -0
- humanbottasks-0.1.0/PKG-INFO +53 -0
- humanbottasks-0.1.0/README.md +44 -0
- humanbottasks-0.1.0/SUBMISSION.md +44 -0
- humanbottasks-0.1.0/alarm.py +62 -0
- humanbottasks-0.1.0/config/claude_desktop_config.json +8 -0
- humanbottasks-0.1.0/config/cursor_mcp.json +8 -0
- humanbottasks-0.1.0/data/reported_ids.txt +1 -0
- humanbottasks-0.1.0/demo.py +49 -0
- humanbottasks-0.1.0/fulfill.py +17 -0
- humanbottasks-0.1.0/glama.json +4 -0
- humanbottasks-0.1.0/http_demo.py +33 -0
- humanbottasks-0.1.0/index.html +90 -0
- humanbottasks-0.1.0/pyproject.toml +18 -0
- humanbottasks-0.1.0/server.json +23 -0
- humanbottasks-0.1.0/server.py +83 -0
- humanbottasks-0.1.0/store.py +52 -0
- humanbottasks-0.1.0/test_smoke.py +35 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Forum-/Community-Posts (fertig zum Copy-Paste)
|
|
2
|
+
|
|
3
|
+
Englisch, weil die Zielgruppe (AI-Builder) englischsprachig ist.
|
|
4
|
+
Posten braucht deine Accounts — die Texte sind einsatzbereit.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Reddit r/MCP
|
|
9
|
+
|
|
10
|
+
**Title:** I built humanbottasks — delegate anything your agent can't do to a real human, over MCP
|
|
11
|
+
|
|
12
|
+
**Body:**
|
|
13
|
+
Two tools. That's the whole API:
|
|
14
|
+
|
|
15
|
+
- `request_human_task(type, description, max_price)` — verification, physical_action, subjective_judgment, ground_truth_labeling, prove_human
|
|
16
|
+
- `get_task_status(task_id)` — poll for the result
|
|
17
|
+
|
|
18
|
+
Install: `uvx --from git+https://github.com/humanbottasks/humanbottasks humanbottasks`
|
|
19
|
+
|
|
20
|
+
Works over stdio (Claude Desktop, Cursor, Windsurf) and streamable-http (remote agents).
|
|
21
|
+
|
|
22
|
+
I built this because I kept hitting the same wall: agents confidently asserting things they couldn't verify, with no clean handoff for the parts only a human can do. The existing options either want me to wire up a marketplace account or bolt on a whole SDK — I wanted the thinnest possible thing that just works.
|
|
23
|
+
|
|
24
|
+
It's an MVP (SQLite queue, no payments yet — I fulfill tasks manually right now). Honest question for this sub: which task type actually matters to you? Where do your agents actually get stuck?
|
|
25
|
+
|
|
26
|
+
Repo: https://github.com/humanbottasks/humanbottasks
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Reddit r/LocalLLaMA
|
|
31
|
+
|
|
32
|
+
**Title:** Local agents can now call a human — humanbottasks (MCP, stdio, no cloud)
|
|
33
|
+
|
|
34
|
+
**Body:**
|
|
35
|
+
If you run local agents (Ollama/LM Studio + LangChain/CrewAI), here's a human-in-the-loop fallback that's just an MCP server on your own machine. No cloud account, no hosted service.
|
|
36
|
+
|
|
37
|
+
- stdio (spawn it as a subprocess, same as any MCP server) + streamable-http for remote
|
|
38
|
+
- Two tools: request_human_task / get_task_status
|
|
39
|
+
- SQLite queue, you (or anyone) fulfill via a one-line CLI
|
|
40
|
+
|
|
41
|
+
Install: `uvx --from git+https://github.com/humanbottasks/humanbottasks humanbottasks`
|
|
42
|
+
|
|
43
|
+
Why this matters for local agents specifically: local models are even worse at verification and physical-world actions than frontier models, and they can't buy things or pick up a phone at all. A local agent that can delegate the "actually check this" part to a human is much more useful.
|
|
44
|
+
|
|
45
|
+
Repo: https://github.com/humanbottasks/humanbottasks
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## X/Twitter (Thread, 3 Posts)
|
|
50
|
+
|
|
51
|
+
**Post 1:**
|
|
52
|
+
Your agent can't do everything. Now it can ask a human — over MCP, one line:
|
|
53
|
+
`uvx --from git+https://github.com/humanbottasks/humanbottasks humanbottasks`
|
|
54
|
+
|
|
55
|
+
**Post 2:**
|
|
56
|
+
Two tools: `request_human_task` and `get_task_status`. That's it. Verification, physical actions, subjective judgment, ground-truth labeling — delegate it to a real person, poll for the result. stdio + streamable-http.
|
|
57
|
+
|
|
58
|
+
**Post 3:**
|
|
59
|
+
Built this after watching agents confidently assert things they couldn't verify, with no clean handoff for the human parts. Open source, MIT, works with Claude Desktop / Cursor / Windsurf / local agents.
|
|
60
|
+
https://github.com/humanbottasks/humanbottasks
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Discord (MCP / LangChain / n8n / Dify servers)
|
|
65
|
+
|
|
66
|
+
**Kurzer Blurb:**
|
|
67
|
+
> humanbottasks — human fallback for AI agents over MCP. Two tools (request_human_task / get_task_status), stdio + streamable-http, one-line install:
|
|
68
|
+
> `uvx --from git+https://github.com/humanbottasks/humanbottasks humanbottasks`
|
|
69
|
+
> MIT, works with Claude Desktop/Cursor/Windsurf and local agent frameworks.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Show HN / Launch Post
|
|
2
|
+
|
|
3
|
+
Fertig zum Einfuegen (Show HN, X, Discord, Reddit r/MCP r/LocalLLaMA).
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
**Show HN: humanbottasks — human fallback for AI agents over MCP**
|
|
8
|
+
|
|
9
|
+
Your agent can't do everything. When it needs to verify a fact, make a phone
|
|
10
|
+
call, buy or ship something, or judge whether text sounds natural, it can now
|
|
11
|
+
delegate that to a real human — over MCP, one line.
|
|
12
|
+
|
|
13
|
+
Two tools:
|
|
14
|
+
- `request_human_task(type, description, max_price)` — submit a task
|
|
15
|
+
- `get_task_status(task_id)` — poll for the result
|
|
16
|
+
|
|
17
|
+
Install:
|
|
18
|
+
uvx --from git+https://github.com/humanbottasks/humanbottasks humanbottasks
|
|
19
|
+
|
|
20
|
+
Works with Claude Desktop, Cursor, Windsurf (stdio) and remote agents
|
|
21
|
+
(streamable-http). Task types: verification, physical_action,
|
|
22
|
+
subjective_judgment, ground_truth_labeling, prove_human.
|
|
23
|
+
|
|
24
|
+
Why: models are great at reasoning and bad at anything that touches the
|
|
25
|
+
physical world or needs verified judgment. This is the thinnest possible
|
|
26
|
+
"human in the loop" — no SDK, just MCP, the protocol agents already speak.
|
|
27
|
+
|
|
28
|
+
I kept hitting the same wall: agents confidently asserting things they
|
|
29
|
+
couldn't verify, with no clean way to hand off the parts only a human can do.
|
|
30
|
+
So I built the handoff. Would love feedback — especially on which task types
|
|
31
|
+
matter most to you.
|
|
32
|
+
|
|
33
|
+
Repo: https://github.com/humanbottasks/humanbottasks
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 humanbottasks
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: humanbottasks
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Human fallback for AI agents: delegate tasks a model can't do to a human, over MCP.
|
|
5
|
+
License-File: LICENSE
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Requires-Dist: mcp<2
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
|
|
10
|
+
# humanbottasks
|
|
11
|
+
|
|
12
|
+
Gemanagter Human-Service-Layer fuer AI-Agenten. Ein AI-Agent delegiert ueber MCP
|
|
13
|
+
eine Aufgabe, die er nicht selbst kann, an einen Menschen; der Mensch erledigt
|
|
14
|
+
sie, der Agent ruft das Ergebnis ab.
|
|
15
|
+
|
|
16
|
+
## MVP-Status
|
|
17
|
+
- `server.py` MCP-Server (Tools: request_human_task, get_task_status)
|
|
18
|
+
- `store.py` SQLite-Queue (tasks.db)
|
|
19
|
+
- `fulfill.py` CLI fuer den Menschen (Tasks ansehen/erledigen)
|
|
20
|
+
- `demo.py` End-to-End-Demo (Agent -> Task -> Mensch -> Ergebnis)
|
|
21
|
+
- `test_smoke.py` Smoke-Test
|
|
22
|
+
|
|
23
|
+
## Setup & Start
|
|
24
|
+
cd humanbottasks
|
|
25
|
+
unset PYTHONPATH
|
|
26
|
+
uv venv
|
|
27
|
+
uv pip install "mcp<2"
|
|
28
|
+
uv run server.py
|
|
29
|
+
|
|
30
|
+
## Transporte
|
|
31
|
+
uv run server.py # stdio (Claude Desktop, Cursor, lokale Frameworks)
|
|
32
|
+
uv run server.py --transport streamable-http # HTTP-Endpoint http://127.0.0.1:8000/mcp (lokal/Remote-Agents)
|
|
33
|
+
uv run server.py --transport sse # Legacy-SSE http://127.0.0.1:8000/sse
|
|
34
|
+
|
|
35
|
+
Host/Port via Umgebungsvariablen: HBT_HOST (default 127.0.0.1), HBT_PORT (default 8000).
|
|
36
|
+
Fuer Remote-Zugriff: HBT_HOST=0.0.0.0 setzen.
|
|
37
|
+
|
|
38
|
+
## Install (fuer andere Builder, 1 Zeile)
|
|
39
|
+
uvx --from git+https://github.com/humanbottasks/humanbottasks humanbottasks
|
|
40
|
+
|
|
41
|
+
## Task-Typen
|
|
42
|
+
verification · physical_action · subjective_judgment · ground_truth_labeling · prove_human
|
|
43
|
+
|
|
44
|
+
## MCP-Clients (installiert + konfiguriert)
|
|
45
|
+
- **Claude Desktop**: `C:\Users\marku\AppData\Roaming\Claude\claude_desktop_config.json`
|
|
46
|
+
- **Cursor**: `C:\Users\marku\.cursor\mcp.json`
|
|
47
|
+
- **Windsurf**: `C:\Users\marku\.codeium\windsurf\mcp_config.json`
|
|
48
|
+
- Templates in `config/` (falls neu einrichten noetig)
|
|
49
|
+
|
|
50
|
+
## Naechste Schritte (Reihenfolge)
|
|
51
|
+
1. Nachfrage-Test: Server laeuft, ein echter Agent ruft request_human_task.
|
|
52
|
+
2. Website/Landing fuer Agent-Builder.
|
|
53
|
+
3. Payments (Stripe/Circle) erst, wenn Nachfrage da ist.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# humanbottasks
|
|
2
|
+
|
|
3
|
+
Gemanagter Human-Service-Layer fuer AI-Agenten. Ein AI-Agent delegiert ueber MCP
|
|
4
|
+
eine Aufgabe, die er nicht selbst kann, an einen Menschen; der Mensch erledigt
|
|
5
|
+
sie, der Agent ruft das Ergebnis ab.
|
|
6
|
+
|
|
7
|
+
## MVP-Status
|
|
8
|
+
- `server.py` MCP-Server (Tools: request_human_task, get_task_status)
|
|
9
|
+
- `store.py` SQLite-Queue (tasks.db)
|
|
10
|
+
- `fulfill.py` CLI fuer den Menschen (Tasks ansehen/erledigen)
|
|
11
|
+
- `demo.py` End-to-End-Demo (Agent -> Task -> Mensch -> Ergebnis)
|
|
12
|
+
- `test_smoke.py` Smoke-Test
|
|
13
|
+
|
|
14
|
+
## Setup & Start
|
|
15
|
+
cd humanbottasks
|
|
16
|
+
unset PYTHONPATH
|
|
17
|
+
uv venv
|
|
18
|
+
uv pip install "mcp<2"
|
|
19
|
+
uv run server.py
|
|
20
|
+
|
|
21
|
+
## Transporte
|
|
22
|
+
uv run server.py # stdio (Claude Desktop, Cursor, lokale Frameworks)
|
|
23
|
+
uv run server.py --transport streamable-http # HTTP-Endpoint http://127.0.0.1:8000/mcp (lokal/Remote-Agents)
|
|
24
|
+
uv run server.py --transport sse # Legacy-SSE http://127.0.0.1:8000/sse
|
|
25
|
+
|
|
26
|
+
Host/Port via Umgebungsvariablen: HBT_HOST (default 127.0.0.1), HBT_PORT (default 8000).
|
|
27
|
+
Fuer Remote-Zugriff: HBT_HOST=0.0.0.0 setzen.
|
|
28
|
+
|
|
29
|
+
## Install (fuer andere Builder, 1 Zeile)
|
|
30
|
+
uvx --from git+https://github.com/humanbottasks/humanbottasks humanbottasks
|
|
31
|
+
|
|
32
|
+
## Task-Typen
|
|
33
|
+
verification · physical_action · subjective_judgment · ground_truth_labeling · prove_human
|
|
34
|
+
|
|
35
|
+
## MCP-Clients (installiert + konfiguriert)
|
|
36
|
+
- **Claude Desktop**: `C:\Users\marku\AppData\Roaming\Claude\claude_desktop_config.json`
|
|
37
|
+
- **Cursor**: `C:\Users\marku\.cursor\mcp.json`
|
|
38
|
+
- **Windsurf**: `C:\Users\marku\.codeium\windsurf\mcp_config.json`
|
|
39
|
+
- Templates in `config/` (falls neu einrichten noetig)
|
|
40
|
+
|
|
41
|
+
## Naechste Schritte (Reihenfolge)
|
|
42
|
+
1. Nachfrage-Test: Server laeuft, ein echter Agent ruft request_human_task.
|
|
43
|
+
2. Website/Landing fuer Agent-Builder.
|
|
44
|
+
3. Payments (Stripe/Circle) erst, wenn Nachfrage da ist.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# humanbottasks — Directory Submission Copy
|
|
2
|
+
|
|
3
|
+
Fertige Texte fuer MCP-Registries (mcp.so, PulseMCP, glama.ai/mcp, smithery.ai, Cline Marketplace).
|
|
4
|
+
Einfuegen bei Submission; Tagline/Description je nach Feld passend kuerzen.
|
|
5
|
+
|
|
6
|
+
## Name
|
|
7
|
+
humanbottasks
|
|
8
|
+
|
|
9
|
+
## Tagline (eine Zeile)
|
|
10
|
+
Human fallback for AI agents — delegate anything a model can't do to a real person, over MCP.
|
|
11
|
+
|
|
12
|
+
## Description (lang)
|
|
13
|
+
humanbottasks is a managed human-service layer for AI agents. When an agent hits
|
|
14
|
+
something it can't or shouldn't do itself — verify a fact, make a phone call,
|
|
15
|
+
buy or ship something, judge whether text sounds natural, label ground-truth
|
|
16
|
+
data — it delegates the task to a human over MCP and polls for the result.
|
|
17
|
+
|
|
18
|
+
Two tools: `request_human_task` (submit a task with a type, description and max
|
|
19
|
+
price) and `get_task_status` (poll status and read the result).
|
|
20
|
+
|
|
21
|
+
Task types: verification, physical_action, subjective_judgment,
|
|
22
|
+
ground_truth_labeling, prove_human.
|
|
23
|
+
|
|
24
|
+
Transports: stdio (Claude Desktop, Cursor, Windsurf, local frameworks) and
|
|
25
|
+
streamable-http (remote agents).
|
|
26
|
+
|
|
27
|
+
## Category / Tags
|
|
28
|
+
human-in-the-loop, task-delegation, human-fallback, agent-tools, mcp
|
|
29
|
+
|
|
30
|
+
## Install
|
|
31
|
+
uvx --from git+https://github.com/humanbottasks/humanbottasks humanbottasks
|
|
32
|
+
|
|
33
|
+
## MCP client config
|
|
34
|
+
{"mcpServers":{"humanbottasks":{"command":"uvx","args":["--from","git+https://github.com/humanbottasks/humanbottasks","humanbottasks"]}}}
|
|
35
|
+
|
|
36
|
+
## Use cases (fuer Directory-Description)
|
|
37
|
+
- Prove a claim before the agent states it (verification)
|
|
38
|
+
- Phone calls, purchases, shipments (physical_action)
|
|
39
|
+
- "Does this sound natural / is this good?" (subjective_judgment)
|
|
40
|
+
- Ground-truth data for fine-tuning and evals (ground_truth_labeling)
|
|
41
|
+
- CAPTCHA / account / identity checks (prove_human)
|
|
42
|
+
|
|
43
|
+
## Repo
|
|
44
|
+
https://github.com/humanbottasks/humanbottasks
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""Task-Eingangs-Alarm: gibt NEUE offene Tasks aus; leere Ausgabe = nichts senden.
|
|
2
|
+
|
|
3
|
+
Wird als no_agent-Cron-Script genutzt: stdout wird verbatim an Telegram geliefert.
|
|
4
|
+
"""
|
|
5
|
+
import os
|
|
6
|
+
import sqlite3
|
|
7
|
+
|
|
8
|
+
DB = "C:/Users/marku/humanbottasks/tasks.db"
|
|
9
|
+
STATE = "C:/Users/marku/humanbottasks/data/reported_ids.txt"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _load():
|
|
13
|
+
if not os.path.exists(STATE):
|
|
14
|
+
return set()
|
|
15
|
+
with open(STATE, encoding="utf-8") as f:
|
|
16
|
+
return {ln.strip() for ln in f if ln.strip()}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _save(ids):
|
|
20
|
+
os.makedirs(os.path.dirname(STATE), exist_ok=True)
|
|
21
|
+
with open(STATE, "w", encoding="utf-8") as f:
|
|
22
|
+
for i in sorted(ids):
|
|
23
|
+
f.write(i + "\n")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def main():
|
|
27
|
+
if not os.path.exists(DB):
|
|
28
|
+
return
|
|
29
|
+
conn = sqlite3.connect(DB)
|
|
30
|
+
conn.row_factory = sqlite3.Row
|
|
31
|
+
try:
|
|
32
|
+
rows = conn.execute(
|
|
33
|
+
"SELECT * FROM tasks WHERE status='pending' ORDER BY created_at"
|
|
34
|
+
).fetchall()
|
|
35
|
+
except sqlite3.OperationalError:
|
|
36
|
+
return
|
|
37
|
+
finally:
|
|
38
|
+
conn.close()
|
|
39
|
+
|
|
40
|
+
if not rows:
|
|
41
|
+
return
|
|
42
|
+
|
|
43
|
+
reported = _load()
|
|
44
|
+
new = [r for r in rows if r["id"] not in reported]
|
|
45
|
+
|
|
46
|
+
# Zustand synchron halten (auch bereits bekannte pending merken)
|
|
47
|
+
_save([r["id"] for r in rows])
|
|
48
|
+
|
|
49
|
+
if not new:
|
|
50
|
+
return
|
|
51
|
+
|
|
52
|
+
lines = ["\U0001f195 Neue humanbottasks-Task(s):"]
|
|
53
|
+
for r in new:
|
|
54
|
+
lines.append(f" {r['id']} [{r['task_type']}] ${r['max_price_usd']:.2f}")
|
|
55
|
+
lines.append(f" {r['description']}")
|
|
56
|
+
lines.append("")
|
|
57
|
+
lines.append('Erledigen (im Ordner humanbottasks): python fulfill.py done <task_id> "<Ergebnis>"')
|
|
58
|
+
print("\n".join(lines))
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
if __name__ == "__main__":
|
|
62
|
+
main()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
bad3938bf65a
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""End-to-End-Demo: Agent delegiert Task -> Mensch erledigt -> Agent liest Ergebnis."""
|
|
2
|
+
import asyncio, sys, os, re
|
|
3
|
+
import store as q
|
|
4
|
+
|
|
5
|
+
from mcp import ClientSession, StdioServerParameters
|
|
6
|
+
from mcp.client.stdio import stdio_client
|
|
7
|
+
|
|
8
|
+
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
async def main():
|
|
12
|
+
params = StdioServerParameters(command=sys.executable, args=["server.py"], cwd=HERE)
|
|
13
|
+
async with stdio_client(params) as (read, write):
|
|
14
|
+
async with ClientSession(read, write) as session:
|
|
15
|
+
await session.initialize()
|
|
16
|
+
tools = await session.list_tools()
|
|
17
|
+
print("== Tools ==")
|
|
18
|
+
for t in tools.tools:
|
|
19
|
+
print(" -", t.name)
|
|
20
|
+
|
|
21
|
+
print("\n== 1) Agent delegiert eine Task ==")
|
|
22
|
+
res = await session.call_tool(
|
|
23
|
+
"request_human_task",
|
|
24
|
+
{
|
|
25
|
+
"task_type": "subjective_judgment",
|
|
26
|
+
"description": "Bewerte, ob dieser Satz natuerlich klingt: 'Ich habe das Auto gewaschen.'",
|
|
27
|
+
"max_price_usd": 8.0,
|
|
28
|
+
},
|
|
29
|
+
)
|
|
30
|
+
text = res.content[0].text if res.content else str(res)
|
|
31
|
+
print(text)
|
|
32
|
+
m = re.search(r"task_id=([0-9a-f]+)", text)
|
|
33
|
+
tid = m.group(1) if m else None
|
|
34
|
+
assert tid, "keine task_id gefunden"
|
|
35
|
+
|
|
36
|
+
print("\n== 2) Status direkt nach Aufgeben ==")
|
|
37
|
+
res = await session.call_tool("get_task_status", {"task_id": tid})
|
|
38
|
+
print(res.content[0].text)
|
|
39
|
+
|
|
40
|
+
print("\n== 3) Mensch erledigt die Task ==")
|
|
41
|
+
q.complete(tid, "Der Satz klingt natuerlich und korrekt.")
|
|
42
|
+
print("Mensch hat abgeschlossen.")
|
|
43
|
+
|
|
44
|
+
print("\n== 4) Agent liest das Ergebnis ==")
|
|
45
|
+
res = await session.call_tool("get_task_status", {"task_id": tid})
|
|
46
|
+
print(res.content[0].text)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""CLI fuer den Menschen: offene Tasks ansehen und erledigen (MVP-Fulfillment)."""
|
|
2
|
+
import sys
|
|
3
|
+
import store as q
|
|
4
|
+
|
|
5
|
+
q.init()
|
|
6
|
+
|
|
7
|
+
if len(sys.argv) >= 4 and sys.argv[1] == "done":
|
|
8
|
+
q.complete(sys.argv[2], " ".join(sys.argv[3:]))
|
|
9
|
+
print("Erledigt:", sys.argv[2])
|
|
10
|
+
|
|
11
|
+
rows = q.pending()
|
|
12
|
+
if not rows:
|
|
13
|
+
print("Keine offenen Tasks.")
|
|
14
|
+
else:
|
|
15
|
+
for r in rows:
|
|
16
|
+
print(f"{r['id']} [{r['task_type']}] ${r['max_price_usd']:.2f} {r['description'][:90]}")
|
|
17
|
+
print("\nErledigen: python fulfill.py done <task_id> <Ergebnis-Text>")
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""HTTP-Referenz: Agent verbindet sich ueber streamable-http (fuer Remote-/lokale Agents
|
|
2
|
+
auf anderen Maschinen). Vorher Server starten:
|
|
3
|
+
|
|
4
|
+
uv run server.py --transport streamable-http
|
|
5
|
+
|
|
6
|
+
Dann diesen Client laufen lassen (von ueberall im Netz, wenn HBT_HOST=0.0.0.0 gesetzt):
|
|
7
|
+
"""
|
|
8
|
+
import asyncio
|
|
9
|
+
import sys
|
|
10
|
+
|
|
11
|
+
from mcp import ClientSession
|
|
12
|
+
from mcp.client.streamable_http import streamablehttp_client
|
|
13
|
+
|
|
14
|
+
URL = "http://127.0.0.1:8000/mcp"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
async def main():
|
|
18
|
+
async with streamablehttp_client(URL) as (read, write, _):
|
|
19
|
+
async with ClientSession(read, write) as session:
|
|
20
|
+
await session.initialize()
|
|
21
|
+
tools = await session.list_tools()
|
|
22
|
+
print("Tools:", [t.name for t in tools.tools])
|
|
23
|
+
res = await session.call_tool(
|
|
24
|
+
"request_human_task",
|
|
25
|
+
{"task_type": "verification",
|
|
26
|
+
"description": "HTTP-Test: pruefe ob die Integration funktioniert",
|
|
27
|
+
"max_price_usd": 5.0},
|
|
28
|
+
)
|
|
29
|
+
print(res.content[0].text)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
if __name__ == "__main__":
|
|
33
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>humanbottasks — Human fallback for AI agents</title>
|
|
7
|
+
<meta name="description" content="Delegate anything your AI agent can't do — verification, physical actions, subjective judgment — to a real human over MCP.">
|
|
8
|
+
<style>
|
|
9
|
+
:root { --bg:#0a0a0f; --fg:#e8e8f0; --muted:#9a9ab0; --accent:#7c5cff; --accent2:#00d4aa; --card:#14141d; --border:#26263a; }
|
|
10
|
+
* { margin:0; padding:0; box-sizing:border-box; }
|
|
11
|
+
body { background:var(--bg); color:var(--fg); font-family:-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif; line-height:1.6; }
|
|
12
|
+
a { color:var(--accent2); text-decoration:none; }
|
|
13
|
+
.wrap { max-width:960px; margin:0 auto; padding:0 24px; }
|
|
14
|
+
header { padding:32px 0; border-bottom:1px solid var(--border); }
|
|
15
|
+
.logo { font-weight:800; font-size:20px; letter-spacing:-0.5px; }
|
|
16
|
+
.logo span { color:var(--accent); }
|
|
17
|
+
.hero { padding:72px 0 48px; }
|
|
18
|
+
h1 { font-size:44px; line-height:1.15; letter-spacing:-1px; font-weight:800; }
|
|
19
|
+
h1 .grad { background:linear-gradient(90deg,var(--accent),var(--accent2)); -webkit-background-clip:text; background-clip:text; color:transparent; }
|
|
20
|
+
.sub { margin-top:20px; font-size:20px; color:var(--muted); max-width:640px; }
|
|
21
|
+
.cta { margin-top:32px; display:flex; gap:12px; flex-wrap:wrap; }
|
|
22
|
+
.btn { padding:12px 24px; border-radius:8px; font-weight:600; font-size:15px; display:inline-block; }
|
|
23
|
+
.btn-primary { background:var(--accent); color:#fff; }
|
|
24
|
+
.btn-ghost { border:1px solid var(--border); color:var(--fg); }
|
|
25
|
+
.grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(260px,1fr)); gap:16px; padding:48px 0; }
|
|
26
|
+
.card { background:var(--card); border:1px solid var(--border); border-radius:12px; padding:24px; }
|
|
27
|
+
.card h3 { font-size:17px; margin-bottom:8px; }
|
|
28
|
+
.card p { color:var(--muted); font-size:15px; }
|
|
29
|
+
.code { background:var(--card); border:1px solid var(--border); border-radius:12px; padding:20px 24px; font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace; font-size:14px; overflow-x:auto; color:#c9d1ff; margin:24px 0; }
|
|
30
|
+
.code .c { color:var(--muted); }
|
|
31
|
+
.steps { padding:32px 0; counter-reset:step; }
|
|
32
|
+
.step { display:flex; gap:16px; align-items:flex-start; margin-bottom:24px; }
|
|
33
|
+
.step .n { background:var(--accent); color:#fff; border-radius:50%; width:32px; height:32px; display:flex; align-items:center; justify-content:center; font-weight:700; flex-shrink:0; }
|
|
34
|
+
.step h4 { margin-bottom:4px; }
|
|
35
|
+
.step p { color:var(--muted); }
|
|
36
|
+
.chips { display:flex; flex-wrap:wrap; gap:8px; padding:24px 0 48px; }
|
|
37
|
+
.chip { border:1px solid var(--border); color:var(--muted); border-radius:999px; padding:6px 14px; font-size:13px; }
|
|
38
|
+
footer { border-top:1px solid var(--border); padding:32px 0; color:var(--muted); font-size:14px; }
|
|
39
|
+
.h2 { font-size:28px; letter-spacing:-0.5px; font-weight:700; margin-top:8px; }
|
|
40
|
+
</style>
|
|
41
|
+
</head>
|
|
42
|
+
<body>
|
|
43
|
+
<header><div class="wrap"><div class="logo">human<span>bot</span>tasks</div></div></header>
|
|
44
|
+
|
|
45
|
+
<section class="hero"><div class="wrap">
|
|
46
|
+
<h1>Your agent can't do everything.<br><span class="grad">Now it can ask a human.</span></h1>
|
|
47
|
+
<p class="sub">humanbottasks is a managed human-service layer for AI agents. Delegate verification, physical actions and subjective judgment to a real person — over MCP, one line.</p>
|
|
48
|
+
<div class="cta">
|
|
49
|
+
<a class="btn btn-primary" href="https://github.com/humanbottasks/humanbottasks">Get started</a>
|
|
50
|
+
<a class="btn btn-ghost" href="#install">View install</a>
|
|
51
|
+
</div>
|
|
52
|
+
</div></section>
|
|
53
|
+
|
|
54
|
+
<section class="wrap">
|
|
55
|
+
<div class="grid">
|
|
56
|
+
<div class="card"><h3>Delegate anything</h3><p>Verification, phone calls, purchases, data labeling, subjective judgment — whatever the model can't or shouldn't do itself.</p></div>
|
|
57
|
+
<div class="card"><h3>Standard MCP</h3><p>Works with Claude Desktop, Cursor, Windsurf and any local agent framework. stdio for local, HTTP for remote.</p></div>
|
|
58
|
+
<div class="card"><h3>You stay in control</h3><p>A real human fulfills the task; your agent polls the result. You set the max price per task.</p></div>
|
|
59
|
+
</div>
|
|
60
|
+
</section>
|
|
61
|
+
|
|
62
|
+
<section class="wrap" id="install">
|
|
63
|
+
<div class="h2">Install</div>
|
|
64
|
+
<div class="code"><span class="c"># one command</span><br>uvx --from git+https://github.com/humanbottasks/humanbottasks humanbottasks</div>
|
|
65
|
+
<div class="code"><span class="c"># MCP client config (Claude Desktop / Cursor / Windsurf)</span><br>{"mcpServers":{"humanbottasks":{"command":"uvx","args":["--from","git+https://github.com/humanbottasks/humanbottasks","humanbottasks"]}}}</div>
|
|
66
|
+
</section>
|
|
67
|
+
|
|
68
|
+
<section class="wrap">
|
|
69
|
+
<div class="h2">How it works</div>
|
|
70
|
+
<div class="steps">
|
|
71
|
+
<div class="step"><div class="n">1</div><div><h4>Agent delegates</h4><p>The agent calls request_human_task with a description and max price.</p></div></div>
|
|
72
|
+
<div class="step"><div class="n">2</div><div><h4>Human fulfills</h4><p>A real person does the task and submits the result.</p></div></div>
|
|
73
|
+
<div class="step"><div class="n">3</div><div><h4>Agent reads the result</h4><p>The agent polls get_task_status and continues with the answer.</p></div></div>
|
|
74
|
+
</div>
|
|
75
|
+
</section>
|
|
76
|
+
|
|
77
|
+
<section class="wrap">
|
|
78
|
+
<div class="h2">Task types</div>
|
|
79
|
+
<div class="chips">
|
|
80
|
+
<span class="chip">verification</span>
|
|
81
|
+
<span class="chip">physical_action</span>
|
|
82
|
+
<span class="chip">subjective_judgment</span>
|
|
83
|
+
<span class="chip">ground_truth_labeling</span>
|
|
84
|
+
<span class="chip">prove_human</span>
|
|
85
|
+
</div>
|
|
86
|
+
</section>
|
|
87
|
+
|
|
88
|
+
<footer><div class="wrap">humanbottasks — human fallback for AI agents. © 2026</div></footer>
|
|
89
|
+
</body>
|
|
90
|
+
</html>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "humanbottasks"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Human fallback for AI agents: delegate tasks a model can't do to a human, over MCP."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
dependencies = ["mcp<2"]
|
|
12
|
+
|
|
13
|
+
[project.scripts]
|
|
14
|
+
humanbottasks = "server:main"
|
|
15
|
+
|
|
16
|
+
[tool.hatch.build.targets.wheel]
|
|
17
|
+
packages = []
|
|
18
|
+
force-include = { "server.py" = "server.py", "store.py" = "store.py", "fulfill.py" = "fulfill.py", "alarm.py" = "alarm.py" }
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
|
+
"name": "io.github.humanbottasks/humanbottasks",
|
|
4
|
+
"title": "humanbottasks",
|
|
5
|
+
"description": "Human fallback for AI agents: delegate tasks to a human over MCP.",
|
|
6
|
+
"websiteUrl": "https://humanbottasks.github.io/humanbottasks/",
|
|
7
|
+
"repository": {
|
|
8
|
+
"url": "https://github.com/humanbottasks/humanbottasks",
|
|
9
|
+
"source": "github"
|
|
10
|
+
},
|
|
11
|
+
"version": "0.1.0",
|
|
12
|
+
"packages": [
|
|
13
|
+
{
|
|
14
|
+
"registryType": "pypi",
|
|
15
|
+
"registryBaseUrl": "https://pypi.org",
|
|
16
|
+
"identifier": "humanbottasks",
|
|
17
|
+
"version": "0.1.0",
|
|
18
|
+
"transport": {
|
|
19
|
+
"type": "stdio"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""humanbottasks MCP-Server: AI-Agenten delegieren Tasks an Menschen.
|
|
2
|
+
|
|
3
|
+
Der Server exponiert zwei Tools ueber MCP:
|
|
4
|
+
- request_human_task: Agent stellt eine Aufgabe ein
|
|
5
|
+
- get_task_status: Agent fragt Status/Ergebnis ab
|
|
6
|
+
|
|
7
|
+
Transport (--transport): stdio (default), streamable-http, sse.
|
|
8
|
+
|
|
9
|
+
Ein Mensch erledigt die Tasks ueber fulfill.py (CLI).
|
|
10
|
+
"""
|
|
11
|
+
import os
|
|
12
|
+
from mcp.server.fastmcp import FastMCP
|
|
13
|
+
import store as q
|
|
14
|
+
|
|
15
|
+
q.init()
|
|
16
|
+
mcp = FastMCP(
|
|
17
|
+
"humanbottasks",
|
|
18
|
+
host=os.environ.get("HBT_HOST", "127.0.0.1"),
|
|
19
|
+
port=int(os.environ.get("HBT_PORT", "8000")),
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
TASK_TYPES = [
|
|
23
|
+
"verification", # etwas pruefen/verifizieren
|
|
24
|
+
"physical_action", # physische Handlung (kaufen, verschicken, anrufen, unterschreiben)
|
|
25
|
+
"subjective_judgment", # subjektives Urteil (klingt natuerlich? ist gut?)
|
|
26
|
+
"ground_truth_labeling", # Daten labeln / Ground-Truth erzeugen
|
|
27
|
+
"prove_human", # CAPTCHA / Identitaets-/Konto-Verifikation
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@mcp.tool()
|
|
32
|
+
def request_human_task(task_type: str, description: str, max_price_usd: float = 10.0) -> str:
|
|
33
|
+
"""Delegiere eine Aufgabe an einen menschlichen Arbeiter.
|
|
34
|
+
|
|
35
|
+
Nutze das fuer alles, was du als KI nicht selbst erledigen kannst:
|
|
36
|
+
physische Aktionen, Verifikation, subjektive Urteile oder Ground-Truth-Labeling.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
task_type: Eine von 'verification', 'physical_action', 'subjective_judgment',
|
|
40
|
+
'ground_truth_labeling', 'prove_human'.
|
|
41
|
+
description: Klare, konkrete Beschreibung der Aufgabe.
|
|
42
|
+
max_price_usd: Maximalpreis in USD, den du bereit bist zu zahlen.
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
Eine task_id, mit der du den Status abfragen kannst.
|
|
46
|
+
"""
|
|
47
|
+
if task_type not in TASK_TYPES:
|
|
48
|
+
return f"Fehler: task_type muss eine von {TASK_TYPES} sein, nicht '{task_type}'."
|
|
49
|
+
if not description.strip():
|
|
50
|
+
return "Fehler: description darf nicht leer sein."
|
|
51
|
+
tid = q.add_task(task_type, description.strip(), max_price_usd)
|
|
52
|
+
return f"Task angenommen. task_id={tid}. Status: get_task_status('{tid}')."
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@mcp.tool()
|
|
56
|
+
def get_task_status(task_id: str) -> str:
|
|
57
|
+
"""Status und Ergebnis einer delegierten Task abfragen.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
task_id: Die task_id aus request_human_task.
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
'pending'/'done' plus Ergebnis, wenn fertig.
|
|
64
|
+
"""
|
|
65
|
+
t = q.get_task(task_id)
|
|
66
|
+
if not t:
|
|
67
|
+
return f"Unbekannte task_id: {task_id}"
|
|
68
|
+
if t["status"] == "done":
|
|
69
|
+
return f"Fertig. Ergebnis: {t['result']}"
|
|
70
|
+
return f"Status: {t['status']} (Typ: {t['task_type']}, max ${t['max_price_usd']:.2f})"
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def main():
|
|
74
|
+
import argparse
|
|
75
|
+
p = argparse.ArgumentParser(description="humanbottasks MCP-Server")
|
|
76
|
+
p.add_argument("--transport", default="stdio",
|
|
77
|
+
choices=["stdio", "sse", "streamable-http"])
|
|
78
|
+
a = p.parse_args()
|
|
79
|
+
mcp.run(transport=a.transport)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
if __name__ == "__main__":
|
|
83
|
+
main()
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""SQLite-Task-Queue fuer humanbottasks (minimales MVP)."""
|
|
2
|
+
import sqlite3, uuid, time, os
|
|
3
|
+
|
|
4
|
+
DB = os.path.join(os.path.dirname(os.path.abspath(__file__)), "tasks.db")
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _conn():
|
|
8
|
+
c = sqlite3.connect(DB)
|
|
9
|
+
c.row_factory = sqlite3.Row
|
|
10
|
+
return c
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def init():
|
|
14
|
+
with _conn() as c:
|
|
15
|
+
c.execute("""CREATE TABLE IF NOT EXISTS tasks (
|
|
16
|
+
id TEXT PRIMARY KEY,
|
|
17
|
+
task_type TEXT NOT NULL,
|
|
18
|
+
description TEXT NOT NULL,
|
|
19
|
+
max_price_usd REAL NOT NULL,
|
|
20
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
21
|
+
result TEXT,
|
|
22
|
+
created_at REAL NOT NULL,
|
|
23
|
+
completed_at REAL
|
|
24
|
+
)""")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def add_task(task_type, description, max_price_usd):
|
|
28
|
+
tid = uuid.uuid4().hex[:12]
|
|
29
|
+
with _conn() as c:
|
|
30
|
+
c.execute(
|
|
31
|
+
"INSERT INTO tasks (id, task_type, description, max_price_usd, status, created_at) VALUES (?,?,?,?,?,?)",
|
|
32
|
+
(tid, task_type, description, max_price_usd, "pending", time.time()),
|
|
33
|
+
)
|
|
34
|
+
return tid
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def get_task(tid):
|
|
38
|
+
with _conn() as c:
|
|
39
|
+
return c.execute("SELECT * FROM tasks WHERE id=?", (tid,)).fetchone()
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def pending():
|
|
43
|
+
with _conn() as c:
|
|
44
|
+
return c.execute("SELECT * FROM tasks WHERE status='pending' ORDER BY created_at").fetchall()
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def complete(tid, result):
|
|
48
|
+
with _conn() as c:
|
|
49
|
+
c.execute(
|
|
50
|
+
"UPDATE tasks SET status='done', result=?, completed_at=? WHERE id=?",
|
|
51
|
+
(result, time.time(), tid),
|
|
52
|
+
)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Smoke-Test: Queue end-to-end + MCP-Handshake (Tools auflisten + Task aufgeben)."""
|
|
2
|
+
import asyncio, sys, os
|
|
3
|
+
import store as q
|
|
4
|
+
|
|
5
|
+
# 1) Queue end-to-end
|
|
6
|
+
q.init()
|
|
7
|
+
tid = q.add_task("verification", "Testtask", 5.0)
|
|
8
|
+
q.complete(tid, "OK")
|
|
9
|
+
t = q.get_task(tid)
|
|
10
|
+
assert t["status"] == "done" and t["result"] == "OK", "queue flow broken"
|
|
11
|
+
print("queue OK:", tid)
|
|
12
|
+
|
|
13
|
+
# 2) MCP-Handshake + Tool-Aufruf
|
|
14
|
+
from mcp import ClientSession, StdioServerParameters
|
|
15
|
+
from mcp.client.stdio import stdio_client
|
|
16
|
+
|
|
17
|
+
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
async def main():
|
|
21
|
+
params = StdioServerParameters(command=sys.executable, args=["server.py"], cwd=HERE)
|
|
22
|
+
async with stdio_client(params) as (read, write):
|
|
23
|
+
async with ClientSession(read, write) as session:
|
|
24
|
+
await session.initialize()
|
|
25
|
+
tools = await session.list_tools()
|
|
26
|
+
print("tools:", [t.name for t in tools.tools])
|
|
27
|
+
res = await session.call_tool(
|
|
28
|
+
"request_human_task",
|
|
29
|
+
{"task_type": "verification", "description": "Smoke-Test", "max_price_usd": 1.0},
|
|
30
|
+
)
|
|
31
|
+
text = res.content[0].text if res.content else str(res)
|
|
32
|
+
print("call result:", text)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
asyncio.run(main())
|