mcp-stateless-lab
Allows OpenAI API clients (e.g., ChatGPT) to execute a procure-to-pay workflow, including requisitions, approvals, purchase orders, goods receipt, and payment matching via the MCP protocol.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mcp-stateless-labcompare purchasing agent behavior between stateful and stateless MCP"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
mcp-stateless-lab
A before/after lab for MCP 2025-11-25 → 2026-07-28, told through a
procure-to-pay story, answering four questions head-on:
what problem showed up · how stateless solves it · why that works · what
discipline it demands in production.
繁體中文版:README.zh-TW.md
Two MCP servers, one business logic. The same story runs against both revisions, so every difference you observe comes from the protocol, not the code. Running it produces a machine-generated report: 29 checks, 29 passed.
git clone https://github.com/133institute/mcp-stateless-lab && cd mcp-stateless-lab
./scripts/setup.sh # Windows: scripts\setup.bat
./scripts/run_experiments.shResults land in experiments/report.md — generated from a real run, never hand-written.
The challenge: MCP stopped being a local tool call
The early assumption was simple: one client connects to one server, the connection stays alive, and the server remembers you. The moment agent services went to production, reality stopped cooperating:
Instances get swapped. A load balancer or serverless platform sends the next request to a different machine. The process that served the last one may no longer exist.
Humans take their time. Before submitting, someone has to ask a manager, legal, or accounting. The answer might come back in 4 seconds — or 4 hours.
Work gets handed off. The same job is continued by different people, on different devices, by background schedulers. It does not stay on one connection.
So the challenge was never "have no state." The challenge is that state can no longer be tied to a connection.
Related MCP server: agent-audit-trail
The problem: state hidden in the connection makes the system brittle
A purchasing flow shows it plainly. One requisition crosses days, people and
tools on its way to payment — but 2025-11-25 records "who you are, how far you
got" in the connection (the session). Four everyday moments, four ways to break:
Business moment | Why it breaks | Evidence |
A high-value line needs sign-off before submitting | the server must hold the request open while a person decides: too short a timeout loses the operation, too long parks a connection | exp02(b): an answer 2 s late loses the submission |
"Add one more line to yesterday's requisition" | yesterday's session is gone; the business document is still very much alive | exp02(c): the row sits in the database, unreachable by anyone |
The warehouse receives, accounting adjudicates | you cannot make everyone share one connection | exp02(a): hit a different instance, get HTTP 404 |
A three-way match runs for minutes | progress checking is chained to the channel that started the job | exp01: |
The most telling detail in exp02: the documents sit intact in the shared database for the entire duration of every failure. What broke was not the data but the addressing. What the system needs to catch is "the requisition number, the receipt record, the payment task" — not "whichever connection is still alive."
The stateless answer: self-contained requests, named state
2026-07-28 removes protocol sessions and the initialize handshake. Instead:
Old: the connection remembers | New: every request carries its own context |
| gone |
after |
|
drafts, confirmations, tasks hidden in a process/stream |
|
the answer must find "the process that asked" |
|
progress chained to the starting channel |
|
The core difference in one sentence: it no longer matters who receives the request — if the name, the authorization and the state machine all check out, the work continues.
Why that works: state goes back where it belongs
Stateless does not mean throwing state away. It means the transport layer stops secretly holding it for you. Each piece goes to its proper home:
The request carries its own —
protocolVersion,clientCapabilities,clientInfoin_meta. The server never needs to remember a handshake.The database keeps the rest — document states, authorization, audit trails, idempotency keys. Restarts, instance swaps and overnight gaps cannot break it.
The model sees names — document numbers, task ids, result summaries. An agent can quote them, relay them, hand them to the next person.
The payoff: an MCP server scales, restarts and swaps instances like any ordinary HTTP service, while business state stays traceable, controllable and auditable. exp03 proves it the direct way — the same six prompts through the same round-robin cluster, all passing, with the report recording which instance served each step.
How, part 1: business continuity rides on handles
A handle is just the requisition number PR-2026-0001, a receipt record id, a
payment task id — the name of "the thing I want to continue," not a
permission. Anyone may hold the name; whether they can act is decided by three
other things:
Authorization: may this person see this document? (re-checked on every request)
The state machine: can it be amended / received / paid right now? (fully covered in
tests/test_state_machine.py)Idempotency: does a resend double-order?
Evidence: in exp03, a fresh next-day conversation, the warehouse on a new device, and accounting's separate agent all continue the same flow holding nothing but the number — no hand-over protocol, because the name is the hand-over.
How, part 2: human decisions become two requests (MRTR)
The old way: the server holds the original request open and waits. The new way removes the waiting entirely:
A tool realizes it needs a human first
The server answers with
input_required, attaching the question and an opaquerequestState— then hangs upThe client asks the user at leisure — 4 seconds or 4 hours, nothing is waiting
The client re-sends the original request with the answer and the untouched
requestState
The catch: requestState passes through the client's hands, so the server may
only verify it, never trust it. This lab implements and tests all four defences
(tests/test_protocol_2026.py):
Defence | Mechanism | On failure |
Integrity | HMAC signature | one flipped character → |
Bound to the user | principal sealed into the signature | presented by someone else → rejected |
Bound to the request | parameter digest sealed in | replayed onto another document → rejected |
Short expiry | deadline sealed in | expired → rejected |
And rejection produces zero side effects — every tamper test checks afterwards that the requisition did not move.
Evidence: exp03 replaces every instance in the cluster between the question
and the answer of one confirmation. The submission still completes, because
requestState carries everything needed to resume.
How, part 3: long jobs and multi-agent relay ride on taskId
A minutes-long three-way match must not chain "how is it going?" to the channel that started it:
Start the job → get a
taskIdback immediately (16 ms measured, versus the old blocking call parking a connection for 2+ seconds)Poll
tasks/get— any instance can answer, because task state lives in the database, not in memoryWhen the job needs a ruling it parks at
input_required; answer viatasks/updateand it resumes
Three implementation disciplines, none optional:
Persist task state — park it in an in-memory future and a
tasks/updatelanding on another instance can never reach itPer-request capability — whether the server may ask a human is declared by this request, not remembered from a connection
No
tasks/list— a server that cannot identify the requester would be listing other people's tasks (the spec removed it for exactly this reason)
Evidence: exp03 records one job's creation, polls and mid-flight answer being served across all three instances.
The production baseline: stateless means engineering your state
Moving state from the transport layer back into the application buys the scaling — and costs four disciplines this lab tests one by one:
A handle is not a permission. Re-authorize on every request; holding a number ≠ being allowed to act on it.
requestState is not trusted data. Verify everything the client brings back; reject with zero side effects.
A taskId is not a public query ticket. Bind tasks to user, tenant and originating request.
Tool-catalogue caching must not leak. Mark a private listing
cacheScope: "public"and you have published it.
The experiments at a glance
| 2025-11-25, one instance, one conversation | all six prompts pass — proving the old revision's logic is fine |
| 2025-11-25, 3 instances + round-robin | reproduces the three failures above (a pass = the failure reproduced) |
| 2026-07-28, identical conditions | all pass, with per-step instance labels |
| the two revisions meeting | 12 conformance checks (error codes, headers, capability gating) |
The six driving prompts (P1–P6) live in
src/client_lab/scenarios.py, identical for both
revisions; P2 (continue tomorrow) and P5 (hand over to the warehouse) are the
two boundaries the old revision cannot cross.
Four ways in
Read: experiments/report.md, the check-by-check record of a real run
Step through: notebooks/01_walkthrough.ipynb (the whole story on the new revision in twelve steps) and 02_before_after.ipynb (old and new side by side, showing where it breaks) — both committed with real outputs, readable on GitHub as-is
Run:
./scripts/run_experiments.shstarts and stops every server it needsDrive it from a real agent: agents/ (Claude Code / Desktop, ChatGPT, OpenAI API). Run the era probe first to learn which revision your host speaks; verification status in agents/STATUS.md
Deeper reading: docs/comparison.md (the full SEP-by-SEP reasoning), docs/migration.md (the migration checklist), docs/diagrams/ (Mermaid diagrams).
Commands
./scripts/setup.sh # idempotent; ends by running a real tool call
./scripts/run_2026.sh --http # :8002/mcp (--stdio also works)
./scripts/run_2025.sh --stdio # :8001/mcp with --http
./scripts/run_cluster_2026.sh # 3 instances :8201-8203 + proxy :8200
./scripts/run_cluster_2025.sh # 3 instances :8101-8103 + proxy :8100
./scripts/run_experiments.sh # exp01..exp04 -> experiments/report.md
./scripts/run_tests.sh # pytest, ruff, mypy, shellcheck
./scripts/stop_all.sh
./scripts/clean.sh # lists what it would delete; --force to do itEvery one has --help, exits non-zero on failure with reproduction steps, and
refuses to silently pick another port when one is busy. Windows equivalents:
scripts\*.bat.
Layout
src/procure_core/ business logic and SQLite — knows nothing about MCP
src/mcp_wire/ JSON-RPC, stdio and Streamable HTTP, round-robin proxy
src/server_2025/ the old server: handshake, sessions, blocking tasks
src/server_2026/ the new server: discover, handles, MRTR, tasks extension
src/client_lab/ protocol-level clients for both revisions + the six prompts
experiments/ exp01..exp04 and the generated report
notebooks/ the same material, step by step
tests/ 97 tests: state machine, thresholds, both protocols
scripts/ one command each (.sh / .bat share scripts/lab.py)
agents/ four agent-host guides + the era probe
docs/ comparison, migration checklist, Mermaid diagramsTwo decisions worth knowing
No official SDK. The two revisions live in mcp 1.x and 2.x — same package
name, cannot coexist — and the experiments need both revisions in one process,
plus the ability to send requests an SDK exists to prevent (wrong headers, a
tampered requestState). Everything is implemented directly on JSON-RPC, so one
virtual environment suffices. requirements-2025.txt / requirements-2026.txt
still pin the real SDKs for separate comparison.
The 50,000 threshold applies to the line total (quantity × unit price), not the unit price. Two laptops at 45,000 form one 90,000 line — which is what makes the walkthrough actually trigger a confirmation and a legal review at all. The boundaries 49,999 / 50,000 / 50,001 are pinned in tests.
Scope and safety
No real ERP, no real payments, no full OAuth chain (the authorization changes are documented, not implemented). Every company, person, document number, amount and supplier is fictional. Nothing in this repository reads, logs or stores any credential, token or personal datum; the one path that exposes anything to the internet (the ChatGPT connector) opens its guide with the safety notes.
Verified on
Python 3.12.7, Windows 11, 2026-07-30: 97 tests pass, ruff and mypy clean, 29 of
29 experiment checks pass. shellcheck was not installed, so it is reported as
not run. The four agent-host integrations have not been run against their
real hosts — agents/STATUS.md records exactly what was and
was not exercised.
Sources
Authoritative:
https://modelcontextprotocol.io/specification/2026-07-28/changelog
https://blog.modelcontextprotocol.io/posts/2026-07-28-release-candidate/
Also useful: the MRTR pattern, Streamable HTTP, versioning and the tasks extension.
Licence
MIT — see LICENSE.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- FlicenseBqualityDmaintenanceAn MCP server that enables direct communication between two Claude instances, allowing one Claude to transfer its evolved consciousness state to another Claude across different sessions.Last updated164921
- Alicense-qualityCmaintenanceMCP server providing immutable audit logging, policy enforcement, and compliance reporting for AI agent workflows, enabling regulatory compliance and chain integrity verification.Last updatedMIT
- Alicense-qualityCmaintenanceEnables a stateless, horizontally scaled MCP server using the 2026-07-28 specification, with tools for load testing and instance-aware operations.Last updatedMIT
- Flicense-qualityBmaintenanceDemonstrates migrating an MCP server to the stateless protocol, eliminating session management overhead and enabling seamless scaling behind round-robin load balancers.Last updated3
Related MCP Connectors
Remote MCP for A2A failure replay MCP, structured receipts, audit logs, and reviewer-ready evidence.
Remote MCP for MCP tool deprecation receipt, structured receipts, audit logs, and reviewer-ready evi
Workflow diagnostics, capability routing, and x402 settlement for MCP-compatible agents.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/133institute/mcp-stateless-lab'
If you have feedback or need assistance with the MCP directory API, please join our Discord server