Skip to main content
Glama
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

Maintenance

ActivityInactive
ResponsivenessNo issues