Repository navigation
feat(website): a website env may be an MCP server too, its tools declared on its card (FD-4683) - #508
Open
williamloo-scale wants to merge 18 commits into
Open
williamloo-scale wants to merge 18 commits into
williamloo-scale wants to merge 18 commits into
Conversation
…ses the conformance check (FD-4683) This PR's change, rebased as one commit onto #428. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…s, and adds a note once per submit (FD-4683) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…sign-in fails, and reloads itself at its slash-ending address (FD-4683) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…hrough data/get, not /go (FD-4683) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…s, settles a save the server accepted, and reloads itself on its own origin (FD-4683) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…through website/v1's ready alone (FD-4683) The SDK on main keeps no store and serves no health path, so Notes holds its users and notes itself, starting from data/seed.json, serves them through @reset_data, @add_data and @get_data, and names its acting user with acting_user(). Its readiness is website/v1's ready (@ready_check seeded); env.toml and the image's health check no longer name /healthz, which the compose health check now reads as the card. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
williamloo-scale
force-pushed
the
wloo/website-mcp
branch
from
November 5, 2026 22:58
ca89cc2 to
6bacb91
Compare
williamloo-scale
changed the base branch from
main
to
wloo/website-notes-example
November 5, 2026 22:58
…oncurrent writes keep every note (FD-4683) Plain def handlers run in worker threads, so two creates could choose one id, and a create could append to a list a delete was replacing. Every read and write of the site's data is under one lock, ids chosen and notes inserted together, and the handlers answer copies. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ared on its card (FD-4683) - A WebsiteEnvironment with @tool or @resource methods serves them at /mcp (streamable HTTP, the mcp package) beside its pages, @api routes and data plane, in the site's process and under its clock; its card declares that endpoint as its mcp interface and lists the tools. A site without them serves no /mcp and declares none (serves_mcp()). - The gateway reads each site's card when it starts and serves the tools of one whose card declares an mcp interface as it does an MCP env's, with the caller's role in ToolContext. A site's interfaces are among the paths its card advertises, so its browsing route never reaches its /mcp. - Conformance checks a declared mcp endpoint: it answers initialize and lists the card's tools; a site without one skips the check. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…e, sites are found at once, and conformance reads every page - A call to a site's MCP endpoint through /svc/<name>/ gets the gateway's role in its JSON-RPC _meta, as an MCP server's does, at the endpoint its card declares (site_mcp_endpoints), so a caller can't claim another role there. - The gateway asks every site for its card at once, so a site that never comes up leaves another its own retries. - Conformance reads every page of tools/list, fails a listed tool the card doesn't name, and warns on a card's tool it doesn't list, as a hidden-by-default one is listed to no caller. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
williamloo-scale
force-pushed
the
wloo/website-mcp
branch
from
November 5, 2026 23:06
6bacb91 to
7a909f1
Compare
…wever its card writes it, and conformance reads whole events - The gateway takes a card's MCP endpoint written as a full URL when it names the site's own server, joining a path under the site's address as before, and leaves out, saying so, one on another server. - A site's reserved paths come from each URL its card advertises, a full one or one with a query as well as a bare path, so its browsing route never reaches /mcp however the card writes it. - Conformance reads an event stream's events whole, their data lines joined, and takes the reply to its own request, as ToolSession does, so a reply split over lines or beside notifications is read right. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…d the /svc role stamp matches it by address A card may write its own server differently from the gateway's base (https://shop.test:443/mcp beside https://shop.test). Discovery now returns the endpoint under the base's own spelling, and site_mcp_endpoints compares parsed scheme, host and port rather than a string prefix, so a call through /svc/<name>/ to that endpoint still gets the gateway's role in its _meta. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… own path A card's full URL already names its path from the server's root, so the gateway keeps that path on the site's scheme and host instead of joining it under the site's base path, which a prefixed base doubled. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
# Conflicts: # packages/agentenv-protocol/examples/website/notes/Dockerfile # packages/agentenv-protocol/examples/website/notes/README.md # packages/agentenv-protocol/examples/website/notes/web/index.html # packages/agentenv-protocol/tests/test_website_notes_example.py
…oute, and a tools/list with no list fails conformance (FD-4683) - A card naming its MCP endpoint by full URL, for a site whose address has a path (http://shop/base, the card's http://shop/base/mcp), reserved /base/mcp, while the site's route forwards /mcp to /base/mcp: a browser reached the endpoint with its own _meta role. A full URL's path is now reserved under the site's address's path. - A tools/list reply with a null result, no tools or a tools value that isn't a list counted as an empty list; it now fails the mcp check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…the mcp check is required
…read (FD-4683) It waited up to its internal-server timeout for each site's card, then started without the tools of a site still down, and never added them once the site came up. It now raises, naming every such site, as it does for an MCP server that never comes up, and compose restarts it until the sites answer. A site answering no card (a 4xx) still counts as up, with no tools. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…stays off its route (FD-4683) Discovery resolves a relative interface url (mcp, tools/mcp) under the site's address, but the reserved paths kept only those starting with /, so /website/<name>/mcp reached the endpoint with a caller's own _meta role. A relative path is now reserved under the site's root. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A website env can now also be an MCP server. A site gives agents tools beside its pages, its card declares them, and the gateway serves them as it does an MCP env's. Stacked on #429 (the Notes example, rebased onto main after #428 merged). The Notes example skips the new
mcpcheck, since it serves no tools.What changes
agentenv_protocol.website):WebsiteEnvironmentwith@toolor@resourcemethods serves them at/mcp(streamable HTTP, needs themcppackage). They run in the site's own process, beside its pages,@apiroutes and data plane, so a tool call changes what the pages show and whatdata/getreturns, and it reads the universe clock (self.now()).mcpinterface (additionalInterfaces) and lists the tools undercapabilities.tools./mcpand declares no interface.serves_mcp()tells you which kind a site is.mcpinterface, the gateway adds the site as an internal MCP server, so agents see its tools with the rest, called with the caller's role inToolContext.site_reserved_pathsnow includes a card's interfaces, so the browsing route (/website/<name>/) never reaches a site's/mcp, as with the data plane.mcpcheck. When the card declares the interface, the endpoint must answerinitializeandtools/listmust name exactly the card's tools. A site without one skips the check.Tests
packages/agentenv-protocol/tests/test_website_mcp.py:/mcpand its tools;data/getreturn, with the caller's role andX-Universe-Now;/mcp;mcpcheck fails on a tools mismatch or a missing endpoint, and sends nothing when the interface names another server.tst/unit/env/gateway/website_mcp_test.py:/website/shop/mcpanswers 404, and/svc/shop/agentenvdata/getsees the order the tool placed.cua_driver_test,test_disk_recipeand two intest_optional_extras_stay_optional.process_scope_socket_test::test_an_armed_time_trigger_keeps_firing_after_a_client_leaves, is a timing test that failed under full-suite load and passed 5 out of 5 on its own.tst/unit/env/gatewaypasses on its own (810 tests).E2E (agent-environments-admin VM, real gateway, local sandbox, Nov 6)
A shop site with two tools (
place_order,count_orders), the Notes site without, and an MCP env in one multi env, with the run's website browser, at merge 555ab9b:/mcpact on the site: its API, itsdata/getand its page in the website browser all show the orders, with the caller's role (agent) inToolContext./website/shop/mcpanswers 404 (GET and POST); through/svc/shop/mcpa_metarole ofadminsent with roledefaultreaches the tool as the default role./svc/shop: themcpcheck passes (2 tools); through/svc/notesit's skipped.RuntimeError: Website card(s) not readable after 180s: shop, compose restarts it (on-failure), and it comes up with the shop's tools once the shop is back (203 s).mcpcheck is required (after feat(website): a session cookie per site, and conformance levels (FD-4683) #444's levels): through/svc/shopit passes, through/svc/notesit's skipped.mcp): its tools are served and itsmcpcheck passes;/website/<site>/mcp(and./mcp) answers 404 for both shops, and a_metarole ofadminthrough/svc/reaches the tool as the default role. The same call against the gateway before the fix (bc3a539) reached the relative shop's endpoint through/website/shoprel/mcpand ran asadmin. Chaos: 30/30 concurrent calls, a site restart, and a gateway restart with that shop down 20 s (up at 23 s with its tools).🤖 Generated with Claude Code


Confidence Score: 5/5
The PR appears safe to merge; the previously reported relative-path gap is fixed.Summary
Websites can serve MCP tools alongside their pages. The gateway discovers these tools from each website's card, and conformance checks the declared endpoint.
Diagram
%%{init: {'theme': 'neutral'}}%% flowchart LR Card[Website card] --> Discovery[Gateway discovers MCP endpoint] Discovery --> Tools[Gateway tool routes] Tools --> Site[Website tools and shared data] Browser[Browser request] --> Check{Reserved path?} Check -->|Yes| Block[404] Check -->|No| Pages[Website pages]Reviews (11) · Last reviewed commit: "fix(gateway): an MCP endpoint a site's c..." · Reviewed by Greptile