Inspiration

Most agent demos stop at a chat answer. In document operations the expensive moment is the action that follows: retry this invoice, escalate that one, and remember why. The failure mode is just as expensive — an agent that retries a $96,500 invoice because it sounded confident.

I wanted to build the smallest complete loop I could defend end to end: diagnose with evidence, draft a plan that cites policy, stop for a human, execute, and remember the decision.

What it does

DocOps Commander is a simulated Alexa+ operations agent for document processing. On a synthetic board of 17 jobs it:

  • Diagnoses failures by pattern — OCR confidence, missing fields, vendor master-data mismatch, schema errors, upstream timeouts — with SLA pressure and dollar exposure, scoped to the question asked (invoice, vendor, or the whole board).
  • Prioritises cases by SLA risk, value and failure class.
  • Drafts a three-segment plan — retry, human review, escalate — citing the seven policies applied and explaining the risk.
  • Waits. Nothing executes until a human approves. Rejection is recorded just as durably.
  • Remembers, so “What did we decide about Vendor Orion?” returns the original plan, jobs, policies and outcome.

The demo starts at 11 failing jobs and $211,470 at risk; approving the plan moves all 11, and five retries recover about three seconds later.

How I built it

One Python process. FastAPI serves both the MCP endpoint and the static console; SQLite stores decisions, pending actions, messages and audit events.

The agent brain is deliberately deterministic: an intent parser, a first-match-wins policy engine, and a tool chain (diagnose_jobs → prioritise_cases → build_batches → draft_action). Narration is generated from tool facts, so the same input produces the same answer with no API key and no network.

The MCP layer is written directly against the 2025-11-25 spec: JSON-RPC over Streamable HTTP at POST /mcp, initialize / notifications/initialized / tools/list / tools/call, Mcp-Session-Id, nine tools with JSON Schemas, and both content and structuredContent in every result. GET /mcp returns 405 with Allow: POST, DELETE because this server is request/response only.

The console is vanilla ES2020 and a real MCP client: it performs the handshake, lists the nine tools, and renders only what tools/call returns. It uses a light Amazon-style palette by design, with an inline plan card, a compact approval modal, an “Approved” receipt, and a Memory tab that highlights the decisions a recall query matched.

I verified with two standard-library-only suites — 37 HTTP assertions and 28 MCP conformance assertions — plus browser runs at a deliberately short 1265×529 viewport.

Challenges

  1. Transport ambiguity. The spec is written for streaming servers. Deciding what a request/response-only server should do for GET took careful reading, and mounting StaticFiles at / before the MCP router made /mcp silently return the SPA — which looked like a protocol bug.
  2. Approval is not a first-class primitive. I modelled it as two tools: draft_resolution_plan cannot execute, approve_plan is the only executor, a second approval returns an error, and the pending decision is written at draft time so a disconnected client still leaves a record.
  3. A real bug caught by testing. MCP-drafted plans originally skipped the decision write, so approvals through an external client left memory empty. The fix was to record the pending decision in both the conversational and MCP paths.
  4. Honesty under a miss. Recall used to fall back to the most recent decision when nothing matched. That fabricated an answer, so the fallback is gone: the agent now says it has decisions on file but none match.
  5. Header casing. Mcp-Session-Id is lowercased on the wire by common HTTP stacks, so a case-sensitive lookup silently started a new session on every call. The conformance suite now asserts the session round-trip.
  6. Density at short viewports. The approval modal overflowed at 529 px height; the fix was a compact modal plus a 100dvh flex shell so the page itself never scrolls.

What I learned

  • For agent products, the tool contract matters more than the chat: discovery, descriptions and error semantics travel with the tools.
  • Approval should be a tool, not a UI convention, if it is to survive arbitrary clients.
  • A deterministic fallback is a feature: it makes demos reproducible and claims verifiable.
  • Conformance tests double as the demo script — writing them forced the protocol decisions into the open.

What's next

Per-tenant policy packs, adapters for real OCR and extraction queues, and optional model-written narration behind the same tool contract.

Built With

Share this project:

Updates

Submission history