0bc06512f5
Two things, both discovered by instrumenting the failure rather than trusting the message it printed. The MCP session never failed on protocol. macOS resolves any `.local` name through mDNS first and takes a flat ~5.0s doing it, which sits exactly on httpx's 5s default connect timeout; fastmcp reported the resulting ConnectTimeout only as "Failed to initialize server session". Earlier tests used `curl --resolve`, which bypasses DNS entirely and made it look like DNS had been eliminated. Settings.http_timeout (60s) now covers the shared client, the client fastmcp builds, and MCPToolset's init_timeout. list-products, get-product and create-product now enumerate over the real gateway. Inference used a static "unused" api_key against a gateway whose openid-connect plugin requires audience ["llm-proxy"]. The model call now carries a token exchanged for llm-proxy-audience, so it is attributable to the same (agent, end user) pair as every tool call. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
71 lines
2.9 KiB
Markdown
71 lines
2.9 KiB
Markdown
# Catalogue — product management agent
|
|
|
|
**Framework:** PydanticAI + FastA2A · **Mode:** conversational · **Client id:** `classic-models-agent-catalogue`
|
|
|
|
Serves the product manager: create and update products and product lines, set
|
|
prices, decide what to discontinue.
|
|
|
|
## Scopes and MCP servers
|
|
|
|
| | |
|
|
|---|---|
|
|
| MCP | catalog (`mcp-catalog-audience`), sales (`mcp-sales-audience`) |
|
|
| Scopes | `catalog:read`, `catalog:write`, `sales:read` |
|
|
|
|
`sales:read` is there to answer "does this product actually sell?" before a
|
|
pricing or discontinuation decision. Read only — this agent never touches orders.
|
|
|
|
## A2A
|
|
|
|
**Calls** — `Approvisionnement.check_availability`, to know remaining stock and
|
|
open orders before discontinuing a reference.
|
|
|
|
**Exposes** — nothing yet. Add skills only when another agent needs them.
|
|
|
|
## Why PydanticAI here
|
|
|
|
Not arbitrary. This is the agent that **writes structured data under
|
|
constraints** — prices, quantities, references, product line membership. Typed
|
|
validation of the model's output earns its place here in a way it would not on a
|
|
purely conversational agent.
|
|
|
|
## FastA2A, not to_a2a()
|
|
|
|
Use FastA2A directly. `Agent.to_a2a()` still works in PydanticAI 1.x but emits
|
|
a deprecation warning and is removed in v2.
|
|
|
|
FastA2A is framework-agnostic, so whatever is built here is also the natural
|
|
basis for the A2A adapter in front of the n8n agents — see
|
|
`classic-models-agent-flows`.
|
|
|
|
## Before writing code
|
|
|
|
Read `classic-models-agent-platform/CLAUDE.md` first. It holds the identity
|
|
model, the MCP and LLM endpoints, the token-exchange recipe and the known
|
|
blockers. This file only covers what is specific to this agent.
|
|
|
|
## Non-negotiables
|
|
|
|
- **Inference goes through the Kong AI gateway**, `https://llm-dev.sttlab.local/llm/v1/chat/completions`.
|
|
Calling a provider directly bypasses the rate limits and prompt guards.
|
|
- **One MCP client per conversation turn**, built with freshly exchanged tokens.
|
|
Do not hold a long-lived MCP connection with a fixed token.
|
|
- **A2A calls return answers, not data.** Expose narrow decision-shaped skills,
|
|
never a generic `query`.
|
|
- Deliverables in English.
|
|
|
|
## Running it against the lab from macOS
|
|
|
|
macOS resolves every `.local` name through mDNS before falling back to
|
|
`/etc/hosts`, and that costs a **flat ~5.0s per hostname** — `dscacheutil -q host
|
|
-a name catalog-dev.sttlab.local` shows it, and so does `curl -w
|
|
%{time_namelookup}`. httpx's default connect timeout is 5s, so the very first
|
|
request to each host lands on the boundary and fails intermittently. fastmcp
|
|
reports that as `Failed to initialize server session` and swallows the cause,
|
|
which is why it read as an MCP protocol problem for a long time. `curl --resolve`
|
|
hides it completely — do not use `--resolve` to "prove" DNS is fine.
|
|
|
|
`Settings.http_timeout` (60s) is the fix, applied to the shared httpx2 client,
|
|
to the client fastmcp builds, and to `MCPToolset(init_timeout=...)`. In the
|
|
cluster these names resolve instantly and the timeout never binds.
|