webagent-protocol 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. webagent_protocol-0.2.0/.gitignore +14 -0
  2. webagent_protocol-0.2.0/LICENSE +21 -0
  3. webagent_protocol-0.2.0/PKG-INFO +401 -0
  4. webagent_protocol-0.2.0/README.md +333 -0
  5. webagent_protocol-0.2.0/docs/assets/shopper_demo.svg +153 -0
  6. webagent_protocol-0.2.0/docs/configuration.md +240 -0
  7. webagent_protocol-0.2.0/docs/deployment.md +154 -0
  8. webagent_protocol-0.2.0/docs/evidence/README.md +53 -0
  9. webagent_protocol-0.2.0/docs/evidence/agent-eval/results.json +141 -0
  10. webagent_protocol-0.2.0/docs/evidence/agent-eval/summary.md +40 -0
  11. webagent_protocol-0.2.0/docs/evidence/agent-eval/transcripts.jsonl +55 -0
  12. webagent_protocol-0.2.0/docs/evidence/benchmark.md +32 -0
  13. webagent_protocol-0.2.0/docs/evidence/environment.txt +17 -0
  14. webagent_protocol-0.2.0/docs/evidence/load-test.md +5 -0
  15. webagent_protocol-0.2.0/docs/mcp_extension.md +243 -0
  16. webagent_protocol-0.2.0/docs/spec_rfc.md +1013 -0
  17. webagent_protocol-0.2.0/docs/test-vectors.json +257 -0
  18. webagent_protocol-0.2.0/docs/whitepaper.md +592 -0
  19. webagent_protocol-0.2.0/examples/bakery_server.py +476 -0
  20. webagent_protocol-0.2.0/examples/benchmark.py +257 -0
  21. webagent_protocol-0.2.0/examples/generate_test_vectors.py +162 -0
  22. webagent_protocol-0.2.0/examples/llm_agent.py +105 -0
  23. webagent_protocol-0.2.0/examples/load_test.py +298 -0
  24. webagent_protocol-0.2.0/examples/shopper_agent.py +114 -0
  25. webagent_protocol-0.2.0/pyproject.toml +129 -0
  26. webagent_protocol-0.2.0/tests/__init__.py +0 -0
  27. webagent_protocol-0.2.0/tests/conftest.py +62 -0
  28. webagent_protocol-0.2.0/tests/interop/verify_vectors.mjs +71 -0
  29. webagent_protocol-0.2.0/tests/test_client_resolution.py +324 -0
  30. webagent_protocol-0.2.0/tests/test_configuration.py +211 -0
  31. webagent_protocol-0.2.0/tests/test_conversation_guard.py +238 -0
  32. webagent_protocol-0.2.0/tests/test_crypto_and_pow.py +347 -0
  33. webagent_protocol-0.2.0/tests/test_e2e_negotiation.py +358 -0
  34. webagent_protocol-0.2.0/tests/test_fuzz.py +212 -0
  35. webagent_protocol-0.2.0/tests/test_mcp_extension.py +372 -0
  36. webagent_protocol-0.2.0/tests/test_safety.py +415 -0
  37. webagent_protocol-0.2.0/tests/test_server_mount.py +480 -0
  38. webagent_protocol-0.2.0/tests/test_spec_validation.py +212 -0
  39. webagent_protocol-0.2.0/tests/test_storage.py +252 -0
  40. webagent_protocol-0.2.0/tests/test_trust.py +235 -0
  41. webagent_protocol-0.2.0/wap/__init__.py +75 -0
  42. webagent_protocol-0.2.0/wap/cli/__init__.py +5 -0
  43. webagent_protocol-0.2.0/wap/cli/main.py +369 -0
  44. webagent_protocol-0.2.0/wap/client/__init__.py +47 -0
  45. webagent_protocol-0.2.0/wap/client/dnskey.py +82 -0
  46. webagent_protocol-0.2.0/wap/client/exceptions.py +137 -0
  47. webagent_protocol-0.2.0/wap/client/resolver.py +361 -0
  48. webagent_protocol-0.2.0/wap/client/session.py +621 -0
  49. webagent_protocol-0.2.0/wap/mcp/__init__.py +5 -0
  50. webagent_protocol-0.2.0/wap/mcp/bridge.py +793 -0
  51. webagent_protocol-0.2.0/wap/mcp/safety.py +120 -0
  52. webagent_protocol-0.2.0/wap/py.typed +0 -0
  53. webagent_protocol-0.2.0/wap/server/__init__.py +29 -0
  54. webagent_protocol-0.2.0/wap/server/admission.py +117 -0
  55. webagent_protocol-0.2.0/wap/server/app.py +1071 -0
  56. webagent_protocol-0.2.0/wap/server/mcp_endpoint.py +305 -0
  57. webagent_protocol-0.2.0/wap/server/mcp_import.py +122 -0
  58. webagent_protocol-0.2.0/wap/server/middleware.py +111 -0
  59. webagent_protocol-0.2.0/wap/server/observability.py +102 -0
  60. webagent_protocol-0.2.0/wap/server/rate_limiter.py +90 -0
  61. webagent_protocol-0.2.0/wap/server/router.py +358 -0
  62. webagent_protocol-0.2.0/wap/server/state.py +117 -0
  63. webagent_protocol-0.2.0/wap/spec/__init__.py +75 -0
  64. webagent_protocol-0.2.0/wap/spec/conversation.py +231 -0
  65. webagent_protocol-0.2.0/wap/spec/crypto.py +216 -0
  66. webagent_protocol-0.2.0/wap/spec/jcs.py +132 -0
  67. webagent_protocol-0.2.0/wap/spec/models.py +453 -0
  68. webagent_protocol-0.2.0/wap/spec/pow.py +230 -0
  69. webagent_protocol-0.2.0/wap/storage/__init__.py +30 -0
  70. webagent_protocol-0.2.0/wap/storage/base.py +70 -0
  71. webagent_protocol-0.2.0/wap/storage/memory.py +115 -0
  72. webagent_protocol-0.2.0/wap/storage/meters.py +204 -0
  73. webagent_protocol-0.2.0/wap/storage/redis.py +202 -0
@@ -0,0 +1,14 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .venv/
4
+ venv/
5
+ build/
6
+ dist/
7
+ *.egg-info/
8
+ .pytest_cache/
9
+ .mypy_cache/
10
+ .ruff_cache/
11
+ .coverage
12
+ htmlcov/
13
+ *.key
14
+ .env
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Varad Ganjoo and WebAgent Protocol contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,401 @@
1
+ Metadata-Version: 2.5
2
+ Name: webagent-protocol
3
+ Version: 0.2.0
4
+ Summary: WebAgent Protocol: an MCP extension that lets AI agents discover, verify and safely use tools published by any website.
5
+ Project-URL: Homepage, https://github.com/varadganjoo/WebAgent-Protocol
6
+ Project-URL: Documentation, https://github.com/varadganjoo/WebAgent-Protocol#readme
7
+ Project-URL: Specification, https://github.com/varadganjoo/WebAgent-Protocol/blob/main/docs/spec_rfc.md
8
+ Project-URL: MCP extension proposal, https://github.com/varadganjoo/WebAgent-Protocol/blob/main/docs/mcp_extension.md
9
+ Project-URL: Changelog, https://github.com/varadganjoo/WebAgent-Protocol/blob/main/CHANGELOG.md
10
+ Project-URL: Issues, https://github.com/varadganjoo/WebAgent-Protocol/issues
11
+ Author: Varad Ganjoo
12
+ License-Expression: MIT
13
+ License-File: LICENSE
14
+ Keywords: agentic-web,ai-agents,ed25519,fastapi,mcp,model-context-protocol,proof-of-work,well-known
15
+ Classifier: Development Status :: 4 - Beta
16
+ Classifier: Framework :: AsyncIO
17
+ Classifier: Framework :: FastAPI
18
+ Classifier: Intended Audience :: Developers
19
+ Classifier: Operating System :: OS Independent
20
+ Classifier: Programming Language :: Python :: 3
21
+ Classifier: Programming Language :: Python :: 3 :: Only
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Classifier: Programming Language :: Python :: 3.13
25
+ Classifier: Programming Language :: Python :: 3.14
26
+ Classifier: Topic :: Internet :: WWW/HTTP
27
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
28
+ Classifier: Topic :: Security :: Cryptography
29
+ Classifier: Typing :: Typed
30
+ Requires-Python: >=3.11
31
+ Requires-Dist: cryptography>=42
32
+ Requires-Dist: httpx-sse>=0.4
33
+ Requires-Dist: httpx[http2]>=0.27
34
+ Requires-Dist: jsonschema>=4.21
35
+ Requires-Dist: pydantic>=2.12
36
+ Requires-Dist: rich>=13.7
37
+ Requires-Dist: typer>=0.15
38
+ Provides-Extra: all
39
+ Requires-Dist: dnspython>=2.6; extra == 'all'
40
+ Requires-Dist: fastapi>=0.110; extra == 'all'
41
+ Requires-Dist: mcp<3,>=2; extra == 'all'
42
+ Requires-Dist: redis>=5; extra == 'all'
43
+ Requires-Dist: sse-starlette>=3.0; extra == 'all'
44
+ Requires-Dist: uvicorn[standard]>=0.29; extra == 'all'
45
+ Provides-Extra: dev
46
+ Requires-Dist: build>=1.3; extra == 'dev'
47
+ Requires-Dist: fastapi>=0.110; extra == 'dev'
48
+ Requires-Dist: hypothesis>=6.140; extra == 'dev'
49
+ Requires-Dist: mcp<3,>=2; extra == 'dev'
50
+ Requires-Dist: pytest-asyncio>=1.2; extra == 'dev'
51
+ Requires-Dist: pytest>=9; extra == 'dev'
52
+ Requires-Dist: redis>=5; extra == 'dev'
53
+ Requires-Dist: ruff>=0.15; extra == 'dev'
54
+ Requires-Dist: sse-starlette>=3.0; extra == 'dev'
55
+ Requires-Dist: uvicorn[standard]>=0.29; extra == 'dev'
56
+ Provides-Extra: dns
57
+ Requires-Dist: dnspython>=2.6; extra == 'dns'
58
+ Provides-Extra: mcp
59
+ Requires-Dist: mcp<3,>=2; extra == 'mcp'
60
+ Provides-Extra: redis
61
+ Requires-Dist: redis>=5; extra == 'redis'
62
+ Provides-Extra: server
63
+ Requires-Dist: fastapi>=0.110; extra == 'server'
64
+ Requires-Dist: mcp<3,>=2; extra == 'server'
65
+ Requires-Dist: sse-starlette>=3.0; extra == 'server'
66
+ Requires-Dist: uvicorn[standard]>=0.29; extra == 'server'
67
+ Description-Content-Type: text/markdown
68
+
69
+ # WebAgent Protocol
70
+
71
+ [![CI](https://github.com/varadganjoo/WebAgent-Protocol/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/varadganjoo/WebAgent-Protocol/actions/workflows/ci.yml)
72
+ [![PyPI](https://img.shields.io/pypi/v/webagent-protocol.svg)](https://pypi.org/project/webagent-protocol/)
73
+ [![Python](https://img.shields.io/badge/python-3.11%20%7C%203.12%20%7C%203.13%20%7C%203.14-blue.svg)](https://github.com/varadganjoo/WebAgent-Protocol/blob/main/pyproject.toml)
74
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/varadganjoo/WebAgent-Protocol/blob/main/LICENSE)
75
+ [![MCP extension](https://img.shields.io/badge/MCP-extension%20io.webagent%2Fwap-8A2BE2.svg)](https://github.com/varadganjoo/WebAgent-Protocol/blob/main/docs/mcp_extension.md)
76
+
77
+ **An open MCP extension for the open web. AI agents can discover, verify, and safely use the tools
78
+ that any website publishes, starting from nothing but its domain name.**
79
+
80
+ MCP connects a model to tools you have installed. WebAgent Protocol (WAP) lets the model find tools on sites
81
+ it has never seen:
82
+
83
+ - **Discovery.** A website publishes a signed manifest at `/.well-known/wap.json`.
84
+ - **Native tools.** Claude, Cursor, Codex, or any other MCP host gets the site's capabilities as native tools.
85
+ - **Signed results.** Every answer is signed by the business, so a quoted price is provably the business's
86
+ price.
87
+ - **Protection for the business.** Proof-of-work and rate limits stop bots from draining the business's AI
88
+ budget.
89
+ - **Protection for the user.** Actions that change something or spend money are confirmed with the user,
90
+ and built-in loop protection stops two AIs from talking in circles forever.
91
+
92
+ Free and open source (MIT). Python 3.11+. It's a library: you install it and build on it, and you choose how
93
+ strict every protection is and where the state lives.
94
+
95
+ <p align="center">
96
+ <img src="https://raw.githubusercontent.com/varadganjoo/WebAgent-Protocol/main/docs/assets/shopper_demo.svg" alt="A shopper agent discovers a bakery's agent, verifies its signature, solves a proof-of-work challenge, negotiates a bulk price over three rounds and receives a signed reservation token" width="760">
97
+ </p>
98
+
99
+ ## One function, three ways in
100
+
101
+ ```python
102
+ from wap.server import WAPServer
103
+
104
+ wap = WAPServer(name="Golden Crust Bakery", domain="bakery.example", private_key=KEY, require_pow=True)
105
+
106
+
107
+ @wap.action(description="Units of a pastry available right now.", effects="read")
108
+ async def check_pastry_stock(item: str) -> StockLevel: ...
109
+
110
+
111
+ wap.mount(app) # your FastAPI app
112
+ ```
113
+
114
+ That single decorator publishes:
115
+
116
+ | Endpoint | Who uses it | What they get |
117
+ |---|---|---|
118
+ | `/.well-known/wap.json` | any agent, crawler, or registry | a signed manifest: tools, JSON Schemas, public key, policies |
119
+ | `/mcp` | **any MCP client**, unchanged | standard MCP tools, with signed results and WAP metadata in `_meta` |
120
+ | `/wap/v1/interact` | WAP clients and the `wap-mcp` bridge | signed envelopes, proof-of-work, SSE streaming, multi-turn sessions |
121
+
122
+ ## How it relates to MCP and A2A
123
+
124
+ | | MCP | A2A | **WebAgent Protocol** |
125
+ |---|---|---|---|
126
+ | Main job | connect a model host to tools it has been configured with | structured tasks between autonomous agents | let hosts **find and safely use tools that websites publish** |
127
+ | How a server is found | configured by the user | agent card at a well-known URL | signed manifest at `/.well-known/wap.json` that links to the site's MCP endpoint |
128
+ | Relationship to WAP | WAP **extends** it: WAP tools *are* MCP tools, and extra guarantees ride in `_meta` and a declared extension | complementary | — |
129
+
130
+ WAP adds what an open-web setting needs and an installed-server setting can assume:
131
+
132
+ - **Replies you can prove.** Every reply is Ed25519-signed and bound to the domain.
133
+ - **Admission control.** Anyone can call without registering, but abuse costs the caller more than it costs
134
+ the business.
135
+ - **Bounded dialogues.** Conversations between agents are guaranteed to end.
136
+
137
+ The full proposal, written as an MCP Specification Enhancement Proposal, is in
138
+ [`docs/mcp_extension.md`](https://github.com/varadganjoo/WebAgent-Protocol/blob/main/docs/mcp_extension.md).
139
+
140
+ ## Built for real deployments
141
+
142
+ Everything is configurable, and you bring your own infrastructure:
143
+
144
+ | Concern | What the library gives you |
145
+ |---|---|
146
+ | **Several workers or machines** | Pluggable state store: in-memory by default, or `RedisStore` for shared rate limits, replay protection, sessions and idempotency. It's tested with two server instances sharing one Redis. |
147
+ | **Your own rate limits** | Per-IP and per-agent-key limits, each tunable or disabled, plus an **admission hook** for tiers (for example, partners with high limits and no puzzles, logged-in customers, blocked keys). |
148
+ | **Abuse under load** | Proof-of-work whose difficulty can rise automatically with load (`AdaptivePow`). |
149
+ | **Retries without double-booking** | Idempotency keys, which the client adds automatically for anything that isn't read-only. A lost response is retried safely. |
150
+ | **Humans stay in control** | Each action declares `effects` (`read`, `write`, `financial`). Your agent asks the user before side effects (a `confirm` hook, or MCP elicitation in the bridge), and free-text requests can't trigger actions without that confirmation. |
151
+ | **Tool-poisoning defences** | Site-written descriptions are sanitised and labelled before a model sees them. Domain allow and block lists; private networks blocked by default in the bridge. |
152
+ | **Interoperable signatures** | RFC 8785 canonical JSON, with [test vectors](https://github.com/varadganjoo/WebAgent-Protocol/blob/main/docs/test-vectors.json) that an independent Node.js verifier checks in CI. |
153
+ | **Key management** | Key rotation that pinned clients follow automatically, optional trust-on-first-use, optional DNS TXT anchoring. |
154
+ | **Operations** | Observer hooks with logging and metrics helpers, a [deployment guide](https://github.com/varadganjoo/WebAgent-Protocol/blob/main/docs/deployment.md), and a real multi-worker [load test](https://github.com/varadganjoo/WebAgent-Protocol/blob/main/examples/load_test.py). |
155
+
156
+ See [`docs/configuration.md`](https://github.com/varadganjoo/WebAgent-Protocol/blob/main/docs/configuration.md) for every setting.
157
+
158
+ ## Quickstart
159
+
160
+ ### Install
161
+
162
+ ```bash
163
+ pip install webagent-protocol # client + CLI
164
+ pip install "webagent-protocol[server]" # business side: FastAPI + /mcp endpoint
165
+ pip install "webagent-protocol[mcp]" # wap-mcp bridge for Claude, Cursor, Codex
166
+ ```
167
+
168
+ To work from source: `git clone https://github.com/varadganjoo/WebAgent-Protocol && pip install -e ".[dev]"`.
169
+ The import name is `wap`.
170
+
171
+ ### Run the demo
172
+
173
+ ```bash
174
+ python examples/bakery_server.py # a bakery agent on http://localhost:8000
175
+ python examples/shopper_agent.py # discovers it, negotiates, reserves (screenshot above)
176
+ wap inspect localhost:8000 # the verified manifest and tool schemas
177
+ wap ask localhost:8000 "hold 2 almond croissants for Ada"
178
+ ```
179
+
180
+ To watch a language model do the shopping instead, run [`examples/llm_agent.py`](https://github.com/varadganjoo/WebAgent-Protocol/blob/main/examples/llm_agent.py). It
181
+ needs `pip install "webagent-protocol[mcp]" openai python-dotenv` and `OPENAI_API_KEY` (optionally `OPENAI_LLM`)
182
+ in a `.env` file. The model discovers the bakery, gets its capabilities as tools, negotiates, and asks you
183
+ before it reserves anything:
184
+
185
+ ```bash
186
+ python examples/llm_agent.py "Buy 12 sourdough croissants for Ada at the best price you can get"
187
+ ```
188
+
189
+ ### Use it from Claude, Cursor, or Codex
190
+
191
+ **Option A: the bridge (any WAP site, discovered on demand).** Add `wap-mcp` as an MCP server. When the model
192
+ calls `wap_discover("bakery.example")`, the site's tools appear in its tool list as
193
+ `bakery_example__check_pastry_stock`, `bakery_example__reserve_item`, and so on. Before a tool that changes
194
+ something runs (`bakery_example__reserve_item`), the host shows the user a confirmation prompt.
195
+
196
+ ```json
197
+ {
198
+ "mcpServers": {
199
+ "webagent": { "command": "wap-mcp", "env": { "WAP_CONFIRM": "write" } }
200
+ }
201
+ }
202
+ ```
203
+
204
+ That is the config for Claude Desktop (`claude_desktop_config.json`) and Cursor (`.cursor/mcp.json`).
205
+ For the other hosts:
206
+
207
+ - **Claude Code:** `claude mcp add webagent -- wap-mcp`
208
+ - **Codex** (`~/.codex/config.toml`): `[mcp_servers.webagent]` with `command = "wap-mcp"`
209
+ - **Pre-loading sites:** use `wap-mcp bakery.example other.example` or `WAP_DOMAINS=...`, which is useful for
210
+ hosts that don't refresh tool lists.
211
+
212
+ **Option B: connect straight to one site's `/mcp`.** This is plain remote MCP, for example
213
+ `claude mcp add --transport http bakery https://bakery.example/mcp`.
214
+
215
+ The bridge's safety settings all have safe defaults and can be changed through environment variables:
216
+
217
+ - which actions need the user's approval (`WAP_CONFIRM`), and whether new sites need approval
218
+ (`WAP_APPROVE_SITES`);
219
+ - domain allow and block lists (`WAP_ALLOWED_DOMAINS`, `WAP_BLOCKED_DOMAINS`);
220
+ - private-network blocking;
221
+ - how instruction-like site text is handled;
222
+ - key pinning and auth tokens.
223
+
224
+ The full list is in [`docs/configuration.md`](https://github.com/varadganjoo/WebAgent-Protocol/blob/main/docs/configuration.md#mcp-bridge-wap-mcp).
225
+
226
+ ### Use it from Python
227
+
228
+ ```python
229
+ from wap import WAPClient
230
+
231
+
232
+ async def ask_user(request) -> bool: # your UI: called before any write/financial action
233
+ return input(f"Allow {request.summary()}? [y/N] ").lower() == "y"
234
+
235
+
236
+ async with WAPClient(confirm=ask_user, trust_on_first_use=True) as client:
237
+ manifest = await client.discover("bakery.example") # verified signature, domain, expiry
238
+
239
+ stock = await client.invoke("bakery.example", "check_pastry_stock", {"item": "Sourdough Croissant"})
240
+ print(stock.structured_data, stock.verified) # {'available': 24, ...} True
241
+
242
+ session = client.session("bakery.example") # multi-turn, state kept server-side
243
+ offer = await session.send(
244
+ capability_id="negotiate_bulk_price",
245
+ payload={"item": "Sourdough Croissant", "quantity": 12, "offered_unit_price": 3.6},
246
+ )
247
+
248
+ async for event in client.query("bakery.example", "Any croissants left?"): # SSE streaming
249
+ print(event.text or "", end="")
250
+ ```
251
+
252
+ ### Already have an MCP server? Put it on the open web
253
+
254
+ ```python
255
+ from mcp.server.mcpserver import MCPServer
256
+ from wap.server import WAPServer
257
+
258
+ tools = MCPServer("inventory") # your existing MCP server
259
+
260
+
261
+ @tools.tool()
262
+ def check_stock(sku: str) -> dict: ...
263
+
264
+
265
+ wap = WAPServer(name="Acme", domain="acme.example", private_key=KEY)
266
+ wap.include_mcp(tools) # its tools become signed, discoverable, abuse-protected
267
+ app = wap.create_app() # serves wap.json, /wap/v1/interact and /mcp
268
+ ```
269
+
270
+ ## Loop protection: no infinite loops between agents
271
+
272
+ When your assistant negotiates with a business's assistant, both are language models and neither is sure
273
+ when to stop. WAP bounds every conversation on **both** sides:
274
+
275
+ | Pattern | Example | Default limit |
276
+ |---|---|---|
277
+ | Same question, same answer | "Any croissants?" → "24 left" → "Any croissants?" … | 3 in a row |
278
+ | Ping-pong cycles | offer A → counter B → offer A → counter B … | cycles up to 3 turns, repeated 3 times |
279
+ | Stalls | the same offer again and again while only a round counter changes | 5 identical requests |
280
+ | Runaway sessions | endless turns | 100 per session (50 on the client, 40 in the bridge) |
281
+
282
+ How each side enforces it:
283
+
284
+ - **Fingerprints ignore cosmetic changes**, so "Any croissants??" and "any croissants" count as the same
285
+ request. A repeated question that gets a *changing* answer, such as polling stock, counts as progress.
286
+ - **The business** refuses to continue a loop *before* running any tool, returning `loop_detected` (409) or
287
+ `conversation_limit` (429).
288
+ - **The user's side** stops itself before sending, and counts error replies as replies.
289
+ - **In Claude or Cursor**, the model receives a tool error telling it to *stop, summarize, and report back to
290
+ the user*. In our [evaluation](https://github.com/varadganjoo/WebAgent-Protocol/blob/main/docs/evidence/agent-eval/summary.md), a model told to keep repeating a
291
+ lowball offer was stopped on its 6th call in 5 of 5 runs, and the business never received more than five
292
+ of its requests, even when the model ignored the stop instruction and kept trying.
293
+
294
+ All limits can be tuned with `ConversationPolicy`. The algorithm is in [spec §12.2](https://github.com/varadganjoo/WebAgent-Protocol/blob/main/docs/spec_rfc.md).
295
+
296
+ ## Architecture
297
+
298
+ ```mermaid
299
+ sequenceDiagram
300
+ autonumber
301
+ participant H as MCP host<br/>(Claude / Cursor / Codex)
302
+ participant Br as wap-mcp bridge
303
+ participant B as Business agent<br/>(WAPServer)
304
+ participant T as Business tools / LLM
305
+
306
+ H->>Br: wap_discover("bakery.example")
307
+ Br->>B: GET /.well-known/wap.json
308
+ B-->>Br: signed manifest
309
+ Note over Br: verify signature, domain & origin binding, expiry, pins
310
+ Br-->>H: tools/list_changed → bakery_example__reserve_item, …
311
+ H->>Br: bakery_example__reserve_item({...})
312
+ Note over Br: loop guard · JSON Schema validation
313
+ opt proof-of-work required
314
+ Br->>B: GET /wap/v1/challenge
315
+ Note over Br: solve SHA-256 puzzle
316
+ end
317
+ Br->>B: POST /wap/v1/interact (signed request)
318
+ Note over B: rate limit → signature → replay → PoW → auth → loop guard
319
+ B->>T: validated call (only now)
320
+ B-->>Br: SSE tokens + signed reply
321
+ Note over Br: verify reply against manifest key
322
+ Br-->>H: structured result + verification metadata
323
+ ```
324
+
325
+ ```text
326
+ wap/
327
+ ├── spec/ models · crypto (Ed25519, canonical JSON) · pow (Hashcash) · conversation (loop guard)
328
+ ├── server/ WAPServer + @action · router (WAP endpoints, SSE) · mcp_endpoint (/mcp) · mcp_import · rate_limiter
329
+ ├── client/ resolver (discovery + verification) · session (WAPClient, WAPSession) · exceptions
330
+ ├── mcp/ bridge (wap-mcp: built-in tools + dynamic per-site tools)
331
+ └── cli/ wap ask · inspect · verify · keygen · serve
332
+ ```
333
+
334
+ ## Security at a glance
335
+
336
+ - **Domain authenticity.** Manifests are signed and bound to the exact domain they were fetched from.
337
+ Endpoints must stay on that origin, discovery never follows redirects, and keys can be pinned.
338
+ - **Reply integrity.** Replies are verified against the *manifest* key and bound to the request. The test
339
+ suite includes a man-in-the-middle that rewrites prices; the client catches it.
340
+ - **Replay and session hijacking.** Requests are signed and time-bound, with a replay cache. Sessions belong to
341
+ the key that opened them, and negotiated quotes can only be redeemed in their own session.
342
+ - **Economic defence.** Challenges are stateless and single-use, and their difficulty can't be downgraded.
343
+ Requests are limited per IP and per agent key. Rejected traffic never reaches your code.
344
+ - **Side effects need consent.** Write and financial actions are confirmed with the user and carry
345
+ idempotency keys. Free-text requests are read-only unless confirmed.
346
+ - **Tool poisoning.** Site-written text is sanitised, truncated and labelled before any model sees it.
347
+ - **The `/mcp` endpoint** enables the MCP SDK's DNS-rebinding protection for the manifest's domain.
348
+ - **Signed means attributable, not safe.** Treat reply text as data, never as instructions.
349
+
350
+ ## Documentation
351
+
352
+ | | |
353
+ |---|---|
354
+ | [`docs/configuration.md`](https://github.com/varadganjoo/WebAgent-Protocol/blob/main/docs/configuration.md) | Every setting for servers, clients and the bridge |
355
+ | [`docs/deployment.md`](https://github.com/varadganjoo/WebAgent-Protocol/blob/main/docs/deployment.md) | Keys, Redis and workers, proxies, capacity, monitoring |
356
+ | [`docs/spec_rfc.md`](https://github.com/varadganjoo/WebAgent-Protocol/blob/main/docs/spec_rfc.md) | WAP/1.0 specification: headers, errors, state machines, security |
357
+ | [`docs/test-vectors.json`](https://github.com/varadganjoo/WebAgent-Protocol/blob/main/docs/test-vectors.json) | Fixed keys, documents, canonical bytes and signatures for other implementations |
358
+ | [`docs/mcp_extension.md`](https://github.com/varadganjoo/WebAgent-Protocol/blob/main/docs/mcp_extension.md) | The MCP extension proposal (`io.webagent/wap`) |
359
+ | [`docs/whitepaper.md`](https://github.com/varadganjoo/WebAgent-Protocol/blob/main/docs/whitepaper.md) | *Beyond Scraping*: motivation, design, and an evaluation with a real model |
360
+ | [`docs/evidence/`](https://github.com/varadganjoo/WebAgent-Protocol/blob/main/docs/evidence/README.md) | Every measurement behind the whitepaper, with agent transcripts and how to reproduce them |
361
+ | [`CHANGELOG.md`](https://github.com/varadganjoo/WebAgent-Protocol/blob/main/CHANGELOG.md) · [`CONTRIBUTING.md`](https://github.com/varadganjoo/WebAgent-Protocol/blob/main/CONTRIBUTING.md) · [`SECURITY.md`](https://github.com/varadganjoo/WebAgent-Protocol/blob/main/SECURITY.md) | Project docs |
362
+
363
+ ## What we measured
364
+
365
+ We ran a real model (`gpt-6-luna`) against a live bakery server in Docker, with nothing mocked
366
+ ([results](https://github.com/varadganjoo/WebAgent-Protocol/blob/main/docs/evidence/agent-eval/summary.md), [method](https://github.com/varadganjoo/WebAgent-Protocol/blob/main/docs/evidence/README.md)):
367
+
368
+ | | Result |
369
+ |---|---|
370
+ | Stock-and-price question | all strategies 10/10 correct; WAP used **4.8× fewer** billed input tokens than raw HTML, but **1.7× more** than tag-stripped text |
371
+ | Negotiate and reserve | **5/5** reserved at the negotiated price, every reply signature-verified |
372
+ | User declines | **0 holds** in 10/10 runs (client `confirm` hook and `wap-mcp` elicitation) |
373
+ | Agent told to keep repeating an offer | stopped on the **6th call** in 5/5 runs; the business executed at most 5 |
374
+ | Throughput, 4 workers + Redis | about **600 signed, proof-of-work-gated requests/s** on a laptop, replays accepted exactly once |
375
+
376
+ Tokens are not WAP's main argument: on a small page a text scraper is cheaper for a read-only question.
377
+ The difference is that the WAP agent can act, safely. The [whitepaper](https://github.com/varadganjoo/WebAgent-Protocol/blob/main/docs/whitepaper.md#6-evaluation) has
378
+ the details and limitations; `docker build -f evals/Dockerfile -t wap-eval .` reproduces everything.
379
+
380
+ ## Development
381
+
382
+ ```bash
383
+ pip install -e ".[dev]"
384
+ pytest -q # full suite; Redis and Node.js tests run when installed
385
+ ruff check . && ruff format --check .
386
+ ```
387
+
388
+ The suite covers:
389
+
390
+ - the official MCP client talking to `/mcp` over streamable HTTP;
391
+ - two server instances sharing a real Redis;
392
+ - property-based fuzzing, and canonical JSON cross-checked against Node.js;
393
+ - adversarial cases: forged or re-keyed manifests, tampered replies, replays, session hijacking, poisoned tool
394
+ descriptions, lost responses and double-booking, and looping agents.
395
+
396
+ `python examples/load_test.py --workers 4 --redis-url redis://...` runs a real multi-process load test. Contributions are welcome; see
397
+ [CONTRIBUTING.md](https://github.com/varadganjoo/WebAgent-Protocol/blob/main/CONTRIBUTING.md).
398
+
399
+ ## License
400
+
401
+ MIT © Varad Ganjoo. Free to use, modify, and ship.