Skip to content

docs: serve a model in "Your own mesh", cover the three service types - #447

Merged
aojea merged 1 commit into
google:mainfrom
aojea:docs-service-types
Sep 19, 2026
Merged

aojea merged 1 commit into
google:mainfrom
aojea:docs-service-types

Conversation

@aojea

@aojea aojea commented Sep 19, 2026

Copy link
Copy Markdown
Collaborator

What

The "Your own mesh" walkthrough declared a static file server as type: mcp and fetched it through the raw proxy path, which taught the mesh as a generic HTTP passthrough. The type is a contract: the node verifies that the backend speaks the declared protocol (model list, MCP initialize, agent card) before advertising it, and the docs should say so and show a real service.

  • your-own-mesh: node A publishes Ollama as an inference service; node B calls it with curl over its socket (/v1/models, then /v1/chat/completions). The section explains what the node checks for each type and why a plain web server is never advertised. A new "MCP servers and A2A agents" section shows the other two declarations and links to the quick start's MCP call, Exposing services, A2A chat (feat(node): add A2A support #347) and Gemini Buddy. mcp-client is no longer a prerequisite for this page.
  • quickstart: after the MCP call, note that MCP is one of three types and list the testnet's models with curl --unix-socket … /v1/models.
  • node-api reference: curl examples for inference and for fetching an A2A card through the proxy path, next to the existing MCP one.
  • sandboxed-agents: the boundary table said <service>.mcp.sam.alt; ParseMeshHost/dialMeshService are type-generic, so it now reads <service>.<type>.sam.alt for mcp, inference and a2a.

Testing

tests/e2e/standalone.bats mirrored the old passthrough example (python3 -m http.server declared as mcp, fetched via /sam/<peer>/mcp/smoke/hello.txt). It now follows the doc: a stdlib fake OpenAI backend (tests/e2e/fixtures/fake_openai.py) declared as inference on node A, asserted through node B's /v1/models (owner is A's peer ID) and /v1/chat/completions. The same server is also declared as type: mcp; the test asserts the node logs not advertising it and that B's discovery never lists it, so the type contract cannot regress silently.

Passes locally in ~6.5s. Ollama itself is not installed here, so the gemma3:1b steps were checked against the code path (/v1/models on the backend root) and the e2e rather than a live Ollama.

The walkthrough declared a static file server as `type: mcp` and fetched
it through the raw proxy path, teaching the mesh as an HTTP passthrough.
The type is a contract: the node verifies the backend speaks the declared
protocol (model list, MCP initialize, agent card) before advertising it.

- your-own-mesh: publish Ollama as an inference service and call it with
  curl from the second node's /v1 endpoint; add a section on MCP and A2A
  that links to their guides and use cases.
- quickstart: note the three types and list the testnet's models.
- node-api: curl examples for inference and the A2A card, next to MCP's.
- sandboxed-agents: the boundary resolves <service>.<type>.sam.alt for
  every type, not only mcp.

standalone.bats mirrored the old passthrough example. It now serves a
fake OpenAI backend as inference across the dataplane and asserts that
the same server mislabelled as mcp is never advertised.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the documentation and end-to-end tests to transition from a generic HTTP smoke service to an OpenAI-compatible inference service type, utilizing a new fake_openai.py mock backend. It also documents the a2a and mcp service types. Feedback is provided on the mock server's request parsing logic to prevent potential crashes or hangs from malformed requests.

Comment on lines +26 to +30
length = int(self.headers.get("Content-Length") or 0)
try:
req = json.loads(self.rfile.read(length) or b"{}")
except ValueError:
req = {}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Parsing Content-Length directly using int(self.headers.get("Content-Length") or 0) can raise a ValueError if the header is present but invalid (e.g., contains non-numeric characters). Additionally, if a negative value is supplied, self.rfile.read() will block indefinitely waiting for EOF. Finally, if the request body is not a JSON object (e.g., a list or a primitive), req.get will raise an AttributeError and crash the handler.\n\nUsing a robust try-except block with max(0, ...) and validating that req is a dictionary ensures the server does not crash or hang due to malformed requests.

        try:\n            length = max(0, int(self.headers.get("Content-Length", 0)))\n        except ValueError:\n            length = 0\n        try:\n            req = json.loads(self.rfile.read(length) or b"{}")\n        except ValueError:\n            req = {}\n        if not isinstance(req, dict):\n            req = {}

@aojea
aojea merged commit b4313a8 into google:main Sep 19, 2026
19 of 21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant