# 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.