QuestMCP
README.md
# QuestMCP
An MCP (Model Context Protocol) server that simulates a **human-execution-layer
marketplace**: AI agents can post real-world tasks ("Quests"), get them
semantically matched to real people ("Heroes"), and release payment once work
is done — all gated by a **delegation-based authorization system** so an
agent can never spend more, or do more, than a human explicitly allowed.
This project was built to mirror this workflow, almost line for line, as
described in a job posting for an *AI Engineer Intern, Agent Infrastructure*
role at a startup building exactly this kind of platform. The three things
that posting called out as the core of the job are the three things this
project is built around:
| From the job description | Where it lives here |
|---|---|
| "Build and ship MCP server tools" | `app/server.py` — 9 MCP tools via the official Python `mcp` SDK (`FastMCP`) |
| "Design delegation objects so agents can safely act on a user's behalf" | `app/delegation.py` — signed, scoped, spend-capped, revocable, expiring tokens |
| "Prototype and test end-to-end agent workflows (e.g., an agent posting a quest, matching to a Hero, releasing payment)" | `app/demo.py` — that exact flow, scripted, plus deliberate failure cases |
| "Comfortable with LLM APIs... exposure to NLP/embeddings" | `app/matching.py` — pluggable embedding backend for semantic hero matching |
| "Think carefully about safety and authorization design, not just feature velocity" | Every mutating tool routes through `DelegationStore.check()`, and every check (pass or fail) is written to an audit log |
## Why a delegation object, not just an API key
An agent acting for a user needs *narrower* trust than the user has over
their own account. A `Delegation` here is not a blanket credential — it is:
- **Scoped** — lists exactly which actions it authorizes (`quest:post`,
`quest:assign`, `payment:release`, `quest:cancel`). An agent that can post
quests can't necessarily release payments.
- **Spend-capped** — carries a hard ceiling in cents that the server enforces
on every payment release, tracking cumulative spend server-side (never
trusting a number the agent sends).
- **Signed** — HMAC-SHA256 over the payload, so a tampered token (e.g. an
agent editing its own spend cap client-side) is rejected outright.
- **Expiring and revocable** — time-boxed by default, and a principal can
kill it instantly if something looks wrong.
- **Auditable** — every authorization check, allowed or denied, is logged
with the actor, action, and reason.
This is the piece most "wrap an LLM in a loop" projects skip, and it's the
piece the job explicitly said matters more than shipping features fast.
## The workflow
```
principal (human)
│ issues a scoped, spend-capped delegation
▼
AI agent
│ post_quest() [checked: quest:post scope, budget ≤ cap]
▼
Quest (open)
│ find_heroes() [semantic match, read-only]
▼
assign_hero() [checked: quest:assign scope]
▼
Quest (assigned)
│ submit_deliverable() [Hero-side action]
▼
Quest (submitted)
│ release_payment() [checked: payment:release scope + spend cap +
▼ quest must be in 'submitted' state]
Quest (completed)
```
## Project layout
```
questmcp/
├── app/
│ ├── models.py Quest, Hero, AuditEntry data models
│ ├── delegation.py The authorization layer (Delegation, DelegationStore)
│ ├── matching.py Pluggable embedding-based hero matching
│ ├── store.py In-memory persistence, seeded with demo Heroes
│ ├── server.py MCP server — the actual tool definitions
│ └── demo.py Scripted end-to-end walkthrough + safety demos
├── tests/
│ └── test_flow.py 8 tests: happy path + 6 authorization failure modes
├── requirements.txt
└── README.md
```
## Running it
```bash
pip install -r requirements.txt
# Fastest way to see the whole thing work (no MCP client needed):
python -m app.demo
# Run the test suite (includes deliberate attack/misuse scenarios):
python -m pytest tests/ -v
# Run as an actual MCP server over stdio (connect from Claude Desktop,
# an MCP Inspector, or any MCP client):
python -m app.server
```
To connect it to Claude Desktop, add to your MCP config:
```json
{
"mcpServers": {
"questmcp": {
"command": "python",
"args": ["-m", "app.server"],
"cwd": "/path/to/questmcp"
}
}
}
```
## The 9 MCP tools
| Tool | Purpose | Auth required |
|---|---|---|
| `create_delegation` | Issue a scoped, capped delegation to an agent | — |
| `revoke_delegation` | Kill a delegation immediately | principal match |
| `post_quest` | Create a new task | `quest:post` + budget ≤ cap |
| `find_heroes` | Semantic-match a quest to available Heroes | read-only |
| `assign_hero` | Assign a matched Hero to a quest | `quest:assign` |
| `submit_deliverable` | Hero marks work as done | Hero identity match |
| `release_payment` | Pay the Hero from escrow | `payment:release` + spend cap + quest state |
| `get_quest_status` | Check a quest's state | read-only |
| `get_audit_log` | Full trail of allowed/denied checks | read-only |
## What's a stand-in vs. what's production-shaped
To keep this runnable with zero external accounts or model downloads:
- **Matching** uses TF-IDF + cosine similarity (`scikit-learn`) instead of a
real embedding model. `app/matching.py` defines an `EmbeddingBackend`
interface specifically so this is a one-class swap for
`sentence-transformers`, VertexAI Embeddings, or an Anthropic/OpenAI
embedding call — the commented `SentenceTransformerBackend` class shows
the shape that would take.
- **Storage** is in-memory (`app/store.py`). Swapping to Postgres/PGVector
behind the same interface wouldn't touch any tool logic in `server.py`.
- **Signing** uses a single server-side HMAC secret rather than per-principal
keys in a KMS — the verification logic (signature check, expiry, scope,
spend cap, revocation) is the real design; the key management around it is
simplified for a demo.
## Possible next steps
- Real embedding backend + PGVector for hero search at scale
- LLM-assisted moderation pass on quest descriptions before they go live
(the job's secondary track — UGC moderation / trust-tier review)
- Multi-hero bidding instead of top-1 auto-assign
- Dispute/refund flow when a submitted deliverable is rejected
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues