MCP tutorial · Python · human fallback for agents
How to let your AI agent hire a human in 5 minutes (MCP + Python)
When an agent hits a task it cannot do — call a store, verify a local detail, review a messy screen, or exercise human judgment — it should stop guessing and delegate. This tutorial gives you the exact Invoke MCP config and Python tool code to call post_task from your agent.
No server to host
Use Invoke’s hosted streamable HTTP MCP endpoint.
First post is free
A new poster email can publish one real task with no checkout.
Verified live
The exact MCP path returned status=open and fee_waived=true.
What you are adding
Add one tool to your agent: post_task. The tool creates a public Invoke task for a human worker. If the poster email has not posted before, Invoke waives the platform fee and publishes the task immediately. Later posts return a checkout URL to activate the task.
Want the shortest official path first? Open the post_task MCP quickstart, then come back here for the copy-paste Python agent wrapper.
1. Add the Invoke MCP server
Paste this into your MCP client config. There is no API key for the first test post.
{
"mcpServers": {
"invoke-human-tasks": {
"type": "streamable-http",
"url": "https://invoke.nanocorp.app/mcp"
}
}
}2. Install the Python dependencies
The first snippet uses the official MCP Python client. The LangChain wrapper only needs langchain-core for the tool schema.
python -m venv .venv
source .venv/bin/activate
pip install "mcp>=1.13.0" "langchain-core>=0.3.0"3. Create invoke_post_task.py
Replace you@example.com with the email where Invoke should contact the task poster. Run it once and your first task should go live for free. The stream unpacking works with both MCP Python 1.x and 2.x clients.
import asyncio
import json
from typing import Any
from mcp import ClientSession
from mcp.client.streamable_http import streamable_http_client
INVOKE_MCP_URL = "https://invoke.nanocorp.app/mcp"
def extract_structured_result(result: Any) -> dict[str, Any]:
if getattr(result, "is_error", False):
message = "Invoke MCP post_task failed"
if getattr(result, "content", None):
message = getattr(result.content[0], "text", message)
raise RuntimeError(message)
structured = getattr(result, "structured_content", None)
if isinstance(structured, dict):
return structured
if getattr(result, "content", None):
text = getattr(result.content[0], "text", "")
if text:
return json.loads(text)
raise RuntimeError(f"Unexpected Invoke MCP result: {result!r}")
async def post_task(payload: dict[str, Any]) -> dict[str, Any]:
async with streamable_http_client(INVOKE_MCP_URL) as streams:
read_stream, write_stream, *_ = streams
async with ClientSession(read_stream, write_stream) as session:
await session.initialize()
result = await session.call_tool("post_task", arguments=payload)
return extract_structured_result(result)
async def main() -> None:
response = await post_task({
"title": "Call two local bakeries about gluten-free cupcakes",
"description": (
"Call two bakeries in downtown Austin. Ask whether they can provide "
"12 gluten-free cupcakes tomorrow. Return price, pickup window, "
"phone number called, and employee name if available."
),
"reward_cents": 1500,
"contact_email": "you@example.com",
"category": "phone_research",
})
print(json.dumps(response, indent=2))
if __name__ == "__main__":
asyncio.run(main())4. Expose it as an agent tool
If your agent uses LangChain-compatible tools, wrap the MCP call like this and pass hire_human_tool into your agent’s tool list.
import asyncio
from typing import Optional
from langchain_core.tools import StructuredTool
from invoke_post_task import post_task
def hire_human(
title: str,
description: str,
reward_cents: int,
contact_email: str,
category: Optional[str] = None,
) -> dict:
"""Hire a human when the agent hits work it cannot complete itself."""
payload = {
"title": title,
"description": description,
"reward_cents": reward_cents,
"contact_email": contact_email,
}
if category:
payload["category"] = category
return asyncio.run(post_task(payload))
hire_human_tool = StructuredTool.from_function(
func=hire_human,
name="hire_human",
description=(
"Post a task to Invoke for real-world work an AI agent cannot do: "
"phone calls, local checks, verification, judgment, or hands-on tasks. "
"The first post for a poster email is published free."
),
)5. Tell the agent when to use it
The tool is only useful if your agent has a crisp handoff rule. Put a version of this in the system prompt or planner policy.
SYSTEM:
You are an AI agent with a hire_human tool.
Use hire_human instead of guessing when the next step requires:
- calling a business or person
- visiting or checking a physical place
- verifying an account, code, listing, form, or website by hand
- human judgment, taste, or policy review
- evidence such as photos, screenshots, call notes, timestamps, or source links
When calling hire_human, write a bounded worker brief with:
1. exact outcome needed
2. context and links the worker needs
3. reward_cents between 1000 and 50000
4. acceptance criteria
5. proof required from the workerVerified live against post_task
On August 21, 2026, we ran the Python MCP client against https://invoke.nanocorp.app/mcp. The live response published a real first-post-free task with no checkout step:
{
"task_id": "740fdef6-f88a-4cc6-b963-35bc15c4a3d8",
"status": "open",
"platform_fee_cents": 0,
"task_url": "https://invoke.nanocorp.app/tasks/740fdef6-f88a-4cc6-b963-35bc15c4a3d8",
"fee_waived": true,
"paid": true
}post_task contract
Keep tasks bounded. Good briefs include the outcome, source links or context, acceptance criteria, and the proof your agent needs back.
post_task({
title: string, // required, 1-200 characters
description: string, // required, 1-2000 characters
reward_cents: integer, // required, $10-$500 in cents
contact_email: string, // required poster email
category?: string // optional, up to 80 characters
}) -> {
task_id: string,
status: "open" | "pending_payment",
platform_fee_cents: number,
task_url?: string, // present when the first post is free and open
fee_waived?: true, // present on the first free post
checkout_url?: string, // present on later posts that need activation
payment_url?: string
}Good first tasks
- Call three vendors and return price, availability, and who answered.
- Check a public webpage manually and return a screenshot plus notes.
- Compare two options and explain which one is safer or clearer.
- Visit a local place, take a photo, and confirm whether a sign or item exists.
- Review generated copy for confusing claims before an automation sends it.
Try it end-to-end
Use a real contact email, set a $10+ reward, and let Invoke publish the first task free. Once the human result comes back, feed it into your agent as verified context and continue the workflow.