ai2human-mcp 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.
@@ -0,0 +1,23 @@
1
+ # macOS
2
+ .DS_Store
3
+
4
+ # Node
5
+ node_modules/
6
+
7
+ # Next.js
8
+ .next/
9
+ out/
10
+
11
+ # Logs
12
+ npm-debug.log*
13
+ yarn-debug.log*
14
+ yarn-error.log*
15
+ pnpm-debug.log*
16
+
17
+ # Env
18
+ .env*
19
+
20
+ # Local MVP data and generated campaign artifacts.
21
+ data/*
22
+ !data/db.seed.json
23
+ .vercel
@@ -0,0 +1,75 @@
1
+ Metadata-Version: 2.4
2
+ Name: ai2human-mcp
3
+ Version: 0.1.0
4
+ Summary: AI2Human MCP server — Claude can dispatch human-execution tasks with proof verification.
5
+ Author: AI2Human
6
+ License: MIT
7
+ Keywords: agent,ai2human,claude,human-in-the-loop,mcp
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Requires-Python: >=3.10
16
+ Requires-Dist: fastmcp<4,>=3.0
17
+ Requires-Dist: httpx<1,>=0.27
18
+ Description-Content-Type: text/markdown
19
+
20
+ # AI2Human MCP Server
21
+
22
+ Claude can dispatch human-execution tasks with proof verification.
23
+
24
+ When your Claude agent hits a step it can't complete alone — local verification, physical checks, identity actions, compliance reviews — AI2Human dispatches it to a human operator with cryptographic proof verification.
25
+
26
+ ## Quickstart
27
+
28
+ ```bash
29
+ pip install ai2human-mcp
30
+ ```
31
+
32
+ Set your API key:
33
+
34
+ ```bash
35
+ export AI2HUMAN_API_KEY="your-key"
36
+ ```
37
+
38
+ Run:
39
+
40
+ ```bash
41
+ ai2human-mcp
42
+ ```
43
+
44
+ ## Claude Desktop Config
45
+
46
+ ```json
47
+ {
48
+ "mcpServers": {
49
+ "ai2human": {
50
+ "command": "uvx",
51
+ "args": ["ai2human-mcp"],
52
+ "env": {
53
+ "AI2HUMAN_API_KEY": "YOUR_KEY"
54
+ }
55
+ }
56
+ }
57
+ }
58
+ ```
59
+
60
+ ## Tools
61
+
62
+ | Tool | Description |
63
+ |------|-------------|
64
+ | `ai2human_list_categories` | List supported task categories and proof types |
65
+ | `ai2human_create_task` | Dispatch a human-execution task |
66
+ | `ai2human_check_task` | Check task status, verification, and settlement |
67
+ | `ai2human_get_proof` | Retrieve evidence bundle and settlement receipt |
68
+
69
+ ## Environment
70
+
71
+ | Variable | Required | Default |
72
+ |----------|----------|---------|
73
+ | `AI2HUMAN_API_KEY` | Yes | — |
74
+ | `AI2HUMAN_AGENT_KEY` | No | Fallback for API key |
75
+ | `AI2HUMAN_BASE_URL` | No | `https://ai2human.io` |
@@ -0,0 +1,56 @@
1
+ # AI2Human MCP Server
2
+
3
+ Claude can dispatch human-execution tasks with proof verification.
4
+
5
+ When your Claude agent hits a step it can't complete alone — local verification, physical checks, identity actions, compliance reviews — AI2Human dispatches it to a human operator with cryptographic proof verification.
6
+
7
+ ## Quickstart
8
+
9
+ ```bash
10
+ pip install ai2human-mcp
11
+ ```
12
+
13
+ Set your API key:
14
+
15
+ ```bash
16
+ export AI2HUMAN_API_KEY="your-key"
17
+ ```
18
+
19
+ Run:
20
+
21
+ ```bash
22
+ ai2human-mcp
23
+ ```
24
+
25
+ ## Claude Desktop Config
26
+
27
+ ```json
28
+ {
29
+ "mcpServers": {
30
+ "ai2human": {
31
+ "command": "uvx",
32
+ "args": ["ai2human-mcp"],
33
+ "env": {
34
+ "AI2HUMAN_API_KEY": "YOUR_KEY"
35
+ }
36
+ }
37
+ }
38
+ }
39
+ ```
40
+
41
+ ## Tools
42
+
43
+ | Tool | Description |
44
+ |------|-------------|
45
+ | `ai2human_list_categories` | List supported task categories and proof types |
46
+ | `ai2human_create_task` | Dispatch a human-execution task |
47
+ | `ai2human_check_task` | Check task status, verification, and settlement |
48
+ | `ai2human_get_proof` | Retrieve evidence bundle and settlement receipt |
49
+
50
+ ## Environment
51
+
52
+ | Variable | Required | Default |
53
+ |----------|----------|---------|
54
+ | `AI2HUMAN_API_KEY` | Yes | — |
55
+ | `AI2HUMAN_AGENT_KEY` | No | Fallback for API key |
56
+ | `AI2HUMAN_BASE_URL` | No | `https://ai2human.io` |
@@ -0,0 +1,32 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "ai2human-mcp"
7
+ version = "0.1.0"
8
+ description = "AI2Human MCP server — Claude can dispatch human-execution tasks with proof verification."
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ requires-python = ">=3.10"
12
+ authors = [{ name = "AI2Human" }]
13
+ keywords = ["ai2human", "mcp", "claude", "agent", "human-in-the-loop"]
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ ]
23
+ dependencies = [
24
+ "fastmcp>=3.0,<4",
25
+ "httpx>=0.27,<1",
26
+ ]
27
+
28
+ [project.scripts]
29
+ ai2human-mcp = "ai2human_mcp.server:main"
30
+
31
+ [tool.hatch.build.targets.wheel]
32
+ packages = ["src/ai2human_mcp"]
@@ -0,0 +1,3 @@
1
+ from .client import AI2HumanAPIError, AI2HumanClient
2
+
3
+ __all__ = ["AI2HumanAPIError", "AI2HumanClient"]
@@ -0,0 +1,104 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from typing import Any
5
+
6
+ import httpx
7
+
8
+
9
+ class AI2HumanAPIError(RuntimeError):
10
+ def __init__(self, message: str, status_code: int, details: Any) -> None:
11
+ super().__init__(message)
12
+ self.status_code = status_code
13
+ self.details = details
14
+
15
+
16
+ class AI2HumanClient:
17
+ def __init__(
18
+ self,
19
+ *,
20
+ api_key: str | None = None,
21
+ base_url: str | None = None,
22
+ transport: httpx.BaseTransport | None = None,
23
+ async_transport: httpx.AsyncBaseTransport | None = None,
24
+ ) -> None:
25
+ self.api_key = api_key or os.getenv("AI2HUMAN_API_KEY") or os.getenv("AI2HUMAN_AGENT_KEY") or ""
26
+ self.base_url = (base_url or os.getenv("AI2HUMAN_BASE_URL") or "https://ai2human.io").rstrip("/")
27
+ self.transport = transport
28
+ self.async_transport = async_transport
29
+
30
+ def _headers(self) -> dict[str, str]:
31
+ if not self.api_key:
32
+ raise AI2HumanAPIError(
33
+ "AI2HUMAN_API_KEY is required. Create one at https://ai2human.io/developers/api-keys.",
34
+ 401,
35
+ {"code": "AGENT_API_KEY_REQUIRED"},
36
+ )
37
+ return {"Accept": "application/json", "x-agent-api-key": self.api_key}
38
+
39
+ @staticmethod
40
+ def _read(response: httpx.Response) -> dict[str, Any]:
41
+ try:
42
+ payload = response.json()
43
+ except ValueError:
44
+ payload = {"error": f"HTTP {response.status_code}"}
45
+ if response.is_error:
46
+ raise AI2HumanAPIError(str(payload.get("error") or response.reason_phrase), response.status_code, payload)
47
+ return payload
48
+
49
+ def _request(self, method: str, path: str, **kwargs: Any) -> dict[str, Any]:
50
+ with httpx.Client(base_url=self.base_url, headers=self._headers(), transport=self.transport, timeout=30) as client:
51
+ return self._read(client.request(method, path, **kwargs))
52
+
53
+ async def _arequest(self, method: str, path: str, **kwargs: Any) -> dict[str, Any]:
54
+ async with httpx.AsyncClient(
55
+ base_url=self.base_url,
56
+ headers=self._headers(),
57
+ transport=self.async_transport,
58
+ timeout=30,
59
+ ) as client:
60
+ return self._read(await client.request(method, path, **kwargs))
61
+
62
+ def create_task(self, payload: dict[str, Any]) -> dict[str, Any]:
63
+ return self._request("POST", "/api/agent/tasks", json=payload)
64
+
65
+ async def acreate_task(self, payload: dict[str, Any]) -> dict[str, Any]:
66
+ return await self._arequest("POST", "/api/agent/tasks", json=payload)
67
+
68
+ def check_task(self, task_id: str) -> dict[str, Any]:
69
+ return self._request("GET", f"/api/tasks/{task_id}")
70
+
71
+ async def acheck_task(self, task_id: str) -> dict[str, Any]:
72
+ return await self._arequest("GET", f"/api/tasks/{task_id}")
73
+
74
+ def get_proof(self, task_id: str) -> dict[str, Any]:
75
+ payload = self.check_task(task_id)
76
+ task = payload.get("task") or {}
77
+ campaign = task.get("campaign") or {}
78
+ return {
79
+ "task_id": task.get("id"),
80
+ "title": task.get("title"),
81
+ "proof_requirements": campaign.get("proofRequirements") or [],
82
+ "evidence": task.get("evidence") or [],
83
+ "verification_status": task.get("status"),
84
+ "payment": payload.get("payment"),
85
+ }
86
+
87
+ async def aget_proof(self, task_id: str) -> dict[str, Any]:
88
+ payload = await self.acheck_task(task_id)
89
+ task = payload.get("task") or {}
90
+ campaign = task.get("campaign") or {}
91
+ return {
92
+ "task_id": task.get("id"),
93
+ "title": task.get("title"),
94
+ "proof_requirements": campaign.get("proofRequirements") or [],
95
+ "evidence": task.get("evidence") or [],
96
+ "verification_status": task.get("status"),
97
+ "payment": payload.get("payment"),
98
+ }
99
+
100
+ def list_categories(self) -> dict[str, Any]:
101
+ return self._request("GET", "/api/agent/tasks/categories")
102
+
103
+ async def alist_categories(self) -> dict[str, Any]:
104
+ return await self._arequest("GET", "/api/agent/tasks/categories")
@@ -0,0 +1,98 @@
1
+ """AI2Human MCP server — expose human-execution tools to Claude and other MCP clients."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from typing import Literal
7
+
8
+ from mcp.server.fastmcp import FastMCP
9
+
10
+ from .client import AI2HumanClient
11
+
12
+ mcp = FastMCP("ai2human")
13
+ client = AI2HumanClient()
14
+
15
+ TaskCategory = Literal[
16
+ "local_verification", "identity_action", "physical_task",
17
+ "digital_task", "compliance_check", "errand",
18
+ ]
19
+ ProofType = Literal[
20
+ "screenshot", "photo", "url", "timestamp", "receipt", "signature", "notes",
21
+ ]
22
+
23
+
24
+ def _json(v: object) -> str:
25
+ return json.dumps(v, ensure_ascii=False, indent=2)
26
+
27
+
28
+ @mcp.tool(
29
+ name="ai2human_create_task",
30
+ description=(
31
+ "Dispatch a workflow step that software cannot complete alone to a human operator. "
32
+ "Use for local verification, physical checks, identity-bound actions, document review, "
33
+ "compliance checks, or errands. Payment remains gated by proof verification."
34
+ ),
35
+ )
36
+ async def ai2human_create_task(
37
+ title: str,
38
+ description: str,
39
+ category: TaskCategory, # type: ignore[valid-type]
40
+ proof_requirements: list[ProofType], # type: ignore[valid-type]
41
+ reward_usdc: float,
42
+ deadline_hours: float,
43
+ location: str | None = None,
44
+ agent_name: str | None = None,
45
+ acceptance_criteria: str | None = None,
46
+ ) -> str:
47
+ payload: dict[str, object] = {
48
+ "title": title,
49
+ "description": description,
50
+ "category": category,
51
+ "proof_requirements": proof_requirements,
52
+ "reward_usdc": reward_usdc,
53
+ "deadline_hours": deadline_hours,
54
+ }
55
+ if location is not None:
56
+ payload["location"] = location
57
+ if agent_name is not None:
58
+ payload["agent_name"] = agent_name
59
+ if acceptance_criteria is not None:
60
+ payload["acceptance_criteria"] = acceptance_criteria
61
+
62
+ result = await client.acreate_task(payload)
63
+ return _json(result)
64
+
65
+
66
+ @mcp.tool(
67
+ name="ai2human_check_task",
68
+ description="Check human execution, proof verification, and settlement state for a dispatched task.",
69
+ )
70
+ async def ai2human_check_task(task_id: str) -> str:
71
+ result = await client.acheck_task(task_id)
72
+ return _json(result)
73
+
74
+
75
+ @mcp.tool(
76
+ name="ai2human_get_proof",
77
+ description="Retrieve the structured evidence bundle and recorded settlement receipt for a dispatched task.",
78
+ )
79
+ async def ai2human_get_proof(task_id: str) -> str:
80
+ result = await client.aget_proof(task_id)
81
+ return _json(result)
82
+
83
+
84
+ @mcp.tool(
85
+ name="ai2human_list_categories",
86
+ description="List supported reality-bound task categories and proof types before dispatching human work.",
87
+ )
88
+ async def ai2human_list_categories() -> str:
89
+ result = await client.alist_categories()
90
+ return _json(result)
91
+
92
+
93
+ def main() -> None:
94
+ mcp.run(transport="stdio")
95
+
96
+
97
+ if __name__ == "__main__":
98
+ main()
File without changes