Pinax
The overarching service layer that fronts N1’s MCP servers. Pinax owns the cross-cutting concerns every MCP server would otherwise re-solve on its own — configuration, Agno integration, discovery/registration, auth configuration, and external dependency management — so standing up or changing a server is a registration, not bespoke plumbing. Pinax is Agno-exclusive and lives at configuration/build time: it is never in the data call path and never holds a patient token.
The metaphor: Pinax is the chamber of commerce for N1’s MCP servers — a specialized one, for a single trade: it serves MCP servers and only MCP servers (the data-fetching service is one member); agents and infrastructure are outside its concern. A server registers once and the chamber sets it up with everything — directory listing, settings on file, auth references, dependencies kept current — so onboarding is joining, not bespoke plumbing.
Two boundaries the metaphor must keep honest: it is not a checkpoint (a server could operate unregistered — nobody stops it — but it forgoes the setup and is off-policy; Principle 5), and it is never your storefront — the chamber sets members up but never serves their customers (the agents). That second line is the out-of-path boundary: Pinax is build-time, never in the call path, and does not deal with agents at all (Principles 1, 3, 4). The roles and principles below make this precise.
Problem
Section titled “Problem”N1’s MCP servers — the data-fetching service today, more coming — are each wired into Agno independently, and every one re-solves the same cross-cutting concerns alone: discoverability (being found and attached by agents), Agno integration (the MCPTools wiring and connection setup), its own scattered settings, the auth configuration it needs to obtain and present tokens, and keeping its external dependency versions current by hand.
This scales badly on two axes. Per server, the same integration is rebuilt each time, with no single source of truth for settings or auth configuration — so each server configures itself slightly differently and the configuration drifts. Across the fleet the wiring is combinatorial: every agent must know about and attach every server it needs. And the servers — though isolated from each other — each independently depend on the same external building blocks (the Agno framework, shared N1 libraries, the MCP SDK) that evolve on their own; without a central authority each server lags or pins differently and falls out of currency with the moving outside world. There is no overarching layer that owns discovery, the Agno binding, shared settings and auth configuration, and the external dependency versions — so standing up a new MCP server is bespoke plumbing instead of a registration.
Outcome
Section titled “Outcome”Any N1 MCP server plugs into one overarching layer that owns its configuration, its Agno integration, its discovery/registration, and the external dependency versions it builds against. Configuration has a single authoritative source instead of being re-derived per server; discovery is centrally maintained and made available to Agno at configuration time; the shared external dependencies are pinned in one place and kept current there, so as the outside world changes a version bump propagates from that one place rather than each isolated server falling out of currency on its own.
Onboarding or changing a server collapses to: register it with Pinax — discovery, the Agno wiring, settings, auth configuration, and the dictated external dependency versions follow. No per-server integration code is hand-written or hand-maintained.
“Done looks like”: a new MCP server is registered with Pinax and becomes available to Agno — discoverable, correctly configured, and correctly auth-wired — without touching per-server integration code; and a change to a centrally-managed dependency or to auth configuration propagates from one authoritative place. (The concrete bar is in Acceptance criteria; the method is in Verification.)
Core principles
Section titled “Core principles”Five principles govern every scope decision. 1–2 fix where Pinax sits and how hard it grips; 3–4 fix the boundaries (what is Agno’s, what is the consumer’s); 5 fixes the mechanism (how it acts). 3–5 exist because they are the easy mistakes — letting Pinax creep into Agno’s or the consumers’ work, or assuming it conforms servers by force.
1. Centralized configuration, distributed execution
Section titled “1. Centralized configuration, distributed execution”Pinax owns configuration; it executes nothing. Execution is Agno’s (connection lifecycle, reconnection, auth-session establishment, composing agents) and the servers’ (serving data, validating tokens). Pinax is build-time, baked into the artifact — never in the data call path, never on the boot path; a deployed fleet runs with Pinax down. Consequence: Pinax never holds, brokers, or mints a token — it configures how auth is set up, never touches the token (the rejected token-broker model is in the Security gate).
2. Manage it, or don’t touch it
Section titled “2. Manage it, or don’t touch it”For any concern Pinax either manages it (owns it, is the single authoritative source) or doesn’t touch it — no soft middle, no optional “best-practice” library a server may or may not adopt (that is the drift this layer removes). “Single authoritative source” describes ownership — one managed source of each thing — not enforcement; how conformance happens is Principle 5. Collapsing the many per-server re-implementations into one integration is the de-duplication, and is in scope. Anything Pinax does not manage is a non-goal.
Relationship to the data-fetching service: that spec defines a single MCP server (a governed read-only door); Pinax is the chamber of commerce all such businesses register with (see the metaphor at the top).
3. Agno’s job is Agno’s
Section titled “3. Agno’s job is Agno’s”If Agno already does something, Pinax does not reinvent or wrap it — it supplies that mechanism’s configuration and stops. Agno owns the connection/session lifecycle, reconnection, auth-session establishment, runtime multiplexing, and composing agents from the available servers. Test: does Agno already own this? If yes, Pinax’s only move is to provide the config Agno consumes.
4. Consumers are opaque to Pinax
Section titled “4. Consumers are opaque to Pinax”The servers and agents are consumers. Pinax knows servers exist and holds their config; it does not reason about consumer behaviour — what an agent does, which servers it composes, what a setting means in use. Test: am I describing what a consumer does with this? If yes, it is out of Pinax.
5. Pinax acts by incentive and policy, not enforcement
Section titled “5. Pinax acts by incentive and policy, not enforcement”Pinax conforms servers by being the path of least resistance backed by company policy — register once, get everything (config, Agno integration, auth wiring, dependency management) automatically, strictly less work than hand-rolling. It does not gate, discover, or compel; “conforming” means took the sanctioned easy path, not was prevented from doing otherwise. A server that goes around Pinax simply forgoes the free setup and is off-policy. Test: am I trying to enforce or prevent? Then it is the wrong mechanism — and anyway impossible, since Pinax is build-time and out of the path.
Core roles — the concerns Pinax manages
Section titled “Core roles — the concerns Pinax manages”Pinax manages exactly five concerns. This is the authoritative list (Scope references it; everything outside it is, by Principle 2, a non-goal). Each role below is defined to the same template — purpose, what Pinax owns, the authority model, inputs→outputs, who executes it (Agno and/or the server, never Pinax), the verification hook, the boundary, and what is left to the design session. Discovery and auth configuration are distinct concerns applied through the Agno-integration role at the wiring step; they are defined separately because each is managed in its own right.
1. Settings
Section titled “1. Settings”Purpose. Give every MCP server one authoritative source for its operational configuration, so settings don’t live scattered and slightly different across N server repos.
What Pinax owns. The authoritative values of each registered server’s environment-invariant application config — what the server is as an N1 MCP server, the same wherever it deploys. The preliminary set (the authoritative per-server list is confirmed at the design session against each server and api-backend; some entries are owned conceptually by another role and only surface here):
- Connection / transport — transport binding (Streamable HTTP), the server’s endpoint, request timeouts.
- api-backend access — base URL / which endpoints this server reads, the shared
n1_api_clientconfig surface, pagination defaults. - Safety limits — result caps, max page size, query timeouts. These are settings and security controls; owning them centrally gives a fleet-wide safety floor (see Security gate).
- Caching — enable plus per-content-type TTLs / freshness bounds and invalidation-policy hooks (caching is integral-from-v1 in the data-fetching service).
- Auth wiring — registry endpoint, the scope(s) this server requests,
header_providerparameters, and the secret-manager reference (which key) — never the secret value. (Defined under role 4; surfaces here as values.) - Agno / connection-lifecycle — the
refresh_connectionpolicy and the connection/session parameters Agno exposes. (Agno executes; Pinax sets the values.) - Registry / identity — the server’s name/identity and its advertised entry (role 2), and the inverted-pin reference (role 5).
- Operational — log level and the server’s own operational-logging settings (per the observability split). Authority & ownership model. Single source of truth: a server carries no settings of its own and cannot override Pinax’s. Values reach the server only through the one mandatory integration and are baked into the image at build (build-pull). There is exactly one place each value is stated, keyed on server identity — drift is structurally impossible.
Inputs → outputs. In: the registered server’s identity plus its setting values in Pinax’s config. Out (at build): those values baked into the server’s deploy artifact. No runtime emission.
Executed by. The server, reading its baked-in settings at runtime. Pinax neither holds nor serves settings live.
Verification hook. The deterministic single source check: a server’s emitted settings match Pinax’s authoritative values exactly, and a change to a value at that one source flows to the fleet on the next build (a Pinax change is fleet-wide — see build-pull).
Boundary. Pinax owns which values, not what a setting means or does — semantics and effect are the server’s. Secrets are out: only the secret-manager reference is Pinax’s; the secret value is delivered by Helm / the secret manager at deploy (see Relationship to Helm). Infra is out: replicas, resources, probes, namespace are Helm’s, never Pinax settings.
Open at design session. The concrete per-server settings schema (the exact keys each server type exposes), read off the servers and api-backend; and any value that sits on the line between a setting and another role’s concern (e.g. a cap that is also a security control — owned here as a value, governed by the Security gate).
2. Discovery / registration
Section titled “2. Discovery / registration”Purpose. Be the one authoritative answer to “what MCP servers exist and how is each reached,” so a new server becomes available to the fleet by registering with Pinax rather than by editing consumers, and no agent hard-wires standing knowledge of every server.
What Pinax owns. The registry — the authoritative record of every MCP server in the fleet:
its identity and the connect metadata Agno needs to attach it (endpoint/transport, and a handle to the
per-server config the other roles own). Registration is adding a server to this record. The registry
records that a server exists and how to reach it — not a detailed catalogue of the server’s tools:
a server’s tool list/capabilities are discovered over the connection at the MCP handshake (they live
inside the server, e.g. the data-fetching service’s own list/describe), not in Pinax.
Authority & ownership model. Pinax is the single central place an MCP registers, and registering gets it everything automatically — no per-server discovery wiring. It is the authoritative source for the registry; conformance comes from this being the path of least resistance backed by company policy (Principle 5), not from gating — Pinax does not discover, police, or compel servers, and a server that lists itself elsewhere by hand simply forgoes what Pinax provides and is off-policy. The registry is emitted at build (build-pull), baked in — not a live endpoint agents query at runtime (Principle 1).
Inputs → outputs. In: a server’s registration entry in Pinax’s config (identity + connect metadata + its config from roles 1, 4, 5). Out (at build): that server present in the Agno-consumable registry shipped in the artifact; a removed registration drops out.
Executed by. Agno, which reads the emitted registry to know the available servers and attach them, and composes agents from them — Pinax does not model which agent uses which server (Principles 3–4).
Verification hook. The deterministic correct, complete registry for Agno check: the emitted registry lists exactly the registered servers — a registered one appears, a removed one drops out, none are invented — each with correct connect metadata in the form Agno consumes.
Boundary. Pinax owns that a server exists and how to reach it, not who uses it or why (agent composition is Agno’s; Principle 3) and not the server’s internal capabilities (discovered over the connection, not held in the registry). Not a runtime discovery service — no live query endpoint (Principle 1). The detailed per-server config hangs off each registry entry but is owned by roles 1 (settings) and 4 (auth); role 2 owns the list and reachability, not those values (no double-ownership).
Open at design session. The exact registry entry shape (which fields a registration carries) and how connect metadata is represented, read against Agno’s consumable format and the server set; and how a removed registration propagates to already-built artifacts (ties into build-pull / drift detection).
3. Agno integration (the Agno-consumable form)
Section titled “3. Agno integration (the Agno-consumable form)”Purpose. Hand the registry and per-server config to Agno in the exact form Agno consumes, so no server or agent hand-assembles its Agno MCP wiring.
What Pinax owns. The output contract — the form in which roles 1, 2, and 4 are expressed for
Agno: each server rendered as the inputs Agno needs to construct a single-server MCPTools connection
(endpoint/transport, the header_provider/auth wiring, the refresh_connection policy), never
MultiMCPTools (deprecated; see Known Agno constraints). Role 3 owns the shape; the values it
carries are owned by roles 1/2/4 (no double-ownership) — this role is how they reach Agno.
Authority & ownership model. Single authoritative form, emitted at build (build-pull) and baked in. Because Pinax produces the Agno-consumable config and Agno consumes it directly, there is one way an N1 server is wired into Agno — not per-server improvisation. Conformance is by ease + policy (Principle 5), not enforcement.
Inputs → outputs. In: the registry + per-server settings/auth from roles 1, 2, 4. Out (at build): that config in Agno’s consumable form, baked into the artifact for Agno to construct connections from.
Executed by. Agno — it reads the config, constructs the MCPTools connections, manages their
lifecycle, and composes agents from the available servers. Pinax produces the config; Agno does
all the wiring and composition (Principle 3).
Verification hook. Part of the deterministic correct, complete registry/config for Agno check:
each server is emitted as valid single-server MCPTools config (never MultiMCPTools) carrying the
correct values from roles 1/2/4, in the form the pinned Agno version consumes.
Boundary. Pinax produces the config Agno consumes; it does not construct connections, run sessions, reconnect, or compose agents — all Agno’s (Principle 3) — and it does not produce per-agent setups (which servers an agent uses is Agno/agent-side; Principle 4). It is the output form, not a second Agno.
Open at design session. The exact Agno-consumable representation (against the pinned Agno
version), and how the header_provider token-attachment is expressed given Agno’s
fresh-connection-per-context behaviour (see Known Agno constraints).
4. Auth configuration
Section titled “4. Auth configuration”Purpose. Give every server one managed, correct auth setup so each does not reinvent a security-sensitive OAuth flow — while Pinax never touches a token.
What Pinax owns. The configuration of how a server/agent connection authenticates: the
trusted-services registry endpoint, the scope(s) a server requests (scope conventions), the OAuth
client wiring, the header_provider parameters, and the secret-manager reference (which key holds
the OAuth client credential) — never the credential or any token itself.
Authority & ownership model. Single authoritative source for these values, baked in at build (build-pull). One managed auth config, not N hand-rolled ones — which is also a fleet-wide security floor (scopes can’t quietly broaden per server). Pinax owns the config; it owns no token and no secret value (Principle 1).
Inputs → outputs. In: the registry/scope/client/header_provider values + the secret-manager
reference in Pinax’s config. Out (at build): that auth config baked into the artifact, in
Agno-consumable form (role 3).
Executed by. Agno injects the header / establishes the authenticated session (via
header_provider), and each server validates the token it receives, independently, against the
issuer-established token. The token flows only between the connection and Eyad’s registry; Pinax is
never in that path.
Verification hook. The emit reproduces each registered service’s auth config exactly as the managed config holds it (faithful, member-gated — a non-member resolves none); no token or secret value ever appears in an emitted artifact, only the secret-manager reference. (Pinax does not judge whether a scope is correct — that is authored and reviewed in Pinax’s repo; see Security gate.)
Boundary. Pinax configures how auth is set up; it does not issue, hold, broker, validate, or mint tokens (issuance is Eyad’s registry; validation is each server’s), and holds no secret values (Helm/the secret manager delivers those at deploy — see Relationship to Helm). The rejected fleet-wide token-broker model and the full security posture are in the Security gate.
Open at design session. The token-attachment mechanism against the pinned Agno version (the
header_provider fresh-connection-per-context behaviour) and against the registry’s interface
(passthrough vs. token exchange, and the patient-scope claim shape) — shared with the data-fetching
service and confirmed once Eyad’s registry interface is available.
5. External dependency management
Section titled “5. External dependency management”Purpose. Keep every member’s build current against the external functionality it depends on — the frameworks, SDKs, and shared libraries that live outside the server and evolve on their own — so that as those external things change, each server stays on a current, working version rather than independently lagging, pinning, or breaking. The concern is currency of a moving external world, not uniformity for its own sake.
What Pinax owns. The authoritative versions of the external, shared dependencies each MCP
server builds against. Initial examples (illustrative, not an exhaustive list — the actual set is
determined at the design session, see Development): the Agno framework, the Pinax library (the inverted
pin), shared N1 libraries such as n1_api_client, and the MCP SDK. The defining trait is external:
functionality the server consumes from outside and does not own. A server’s internal dependencies —
its own code, and any libraries it chooses for itself that are not in the shared external set — are the
server’s own business; Pinax does not touch them.
Authority & ownership model. Single source: the version of each external dependency a server builds against is dictated centrally and delivered through the one integration at build (build-pull), not chosen per server. A bump happens in one place and the fleet picks it up on next build — no per-server drift. (Conformance is by the path of least resistance, Principle 5, not enforcement.)
Inputs → outputs. In: the authoritative version set in Pinax’s config (the external deps + the Pinax library itself), keyed by server identity. Out (at build): a Pinax-produced lockfile per server, which the build resolves against, so each server is built against exactly the dictated set, baked into its artifact. (This is the inverted pin — see Approach → Repository structure; role 5 is that mechanism applied across the whole external set.)
Executed by. The server’s build, which resolves against the Pinax-produced lockfile. Pinax authors the lockfile from its config; it does not run anything.
Verification hook. A bump to a version in Pinax’s config produces an updated lockfile, so the fleet’s next build picks it up from that one change; no server resolves a version other than the dictated one.
Boundary. This is external dependency management only, and it is not: any notion of one server depending on, calling, or being coupled to another (there is none — servers are isolated; agent-side composition is Agno’s, Principle 3); nor management of a server’s internal/own dependencies (those are the server’s). The line is external-vs-internal: Pinax keeps the shared external set current, nothing else.
Open at design session. Exactly which external dependencies are in the shared set Pinax dictates versus left to each server’s own discretion — identifying that set is an explicit development step (see Development) — and the lockfile’s exact format and how a build fetches its own. The mechanism (a Pinax-produced lockfile, the inverted pin) is settled; only the set’s membership and the field-level format are open.
The two runtime owners these roles defer to — Agno (connection lifecycle, reconnection, authenticated-session establishment) and each MCP server (token validation, data serving) — are detailed in Approach.
Scope & non-goals
Section titled “Scope & non-goals”In scope
Section titled “In scope”Everything in Core roles (the five managed concerns) is in scope, delivered at configuration/build time, with Pinax out of the runtime path. Pinax is Agno-exclusive.
Also in scope as a deliverable of this work (not of Pinax-the-artifact): creating the shared base Helm chart in the infra/platform layer that MCP servers build their charts on. This effort produces it so the consistent-deploy path exists from the start rather than being deferred elsewhere — but it is an infra-layer artifact, not part of the Pinax library/config/registry and not owned or touched by Pinax at all (the Pinax↔Helm boundary in Approach holds). Distinguish throughout: this setup builds the base chart; Pinax does not own it.
Non-goals — what Pinax does not touch
Section titled “Non-goals — what Pinax does not touch”- Not in the data call path. Pinax never routes, proxies, or multiplexes a tool call. Runtime
multiplexing across servers is Agno’s (
MCPToolsinstances on the agent), not Pinax’s. - Never holds, brokers, stores, or mints a patient token. Token issuance is Eyad’s registry; token validation is each server’s.
- No canonical code, templates, or recommended-best-practice library; no adoption-by-convention. (Principle 2.)
- Not a custom Agno client and not a framework-agnostic abstraction. Pinax is Agno-exclusive by design; there is no pluggable-framework layer and no non-Agno transport.
- Not building, scaffolding, or generating MCP servers — and not building agents. The MCP servers and the agents are consumers of Pinax, not part of it. Pinax’s responsibility ends at what it manages and emits (config, the registry, the integration library, the inverted pin); a consumer’s own code, structure, deployment, and behaviour are entirely its own. This spec builds Pinax only.
- Not the trusted-services registry. Eyad’s registry (token pre-authorization and issuance) is a hard dependency, consumed, not built here. (The same registry authorizes the data-fetching service.)
- No runtime observability layer. Runtime observability is delegated to Agno’s OpenTelemetry tracing (which already yields the cross-MCP/fleet-wide view via its trace store); Pinax builds no runtime telemetry system of its own. See Approach → Observability.
Approach
Section titled “Approach”Pinax is built as the authoritative configuration layer for N1’s Agno-based MCP fleet, sitting at configuration/build time and out of the runtime path. The approach falls out of the five principles plus what the research established about who owns what at runtime.
Repository structure — split polyrepo, centralized config
Section titled “Repository structure — split polyrepo, centralized config”Code is distributed; configuration and the external dependency versions are centralized. This is Principles 1–2 expressed as repos.
- The Pinax repo holds two things: (a) the single mandatory integration (the library/CLI a
server imports — defined in The delivery mechanism); and (b) the authoritative config + registry
— which MCP servers exist and what each offers, each server’s settings, the auth configuration
(registry endpoint, scopes,
header_providerwiring), and the dictated version set per server (the inverted pin, below — the external deps plus the Pinax library). This is data Pinax owns, not code servers carry. - Each MCP server is its own repo (preserving the data-fetching spec’s “new repo per service” and per-team ownership), holding only its own server code. It declares that it uses Pinax and is registered in Pinax’s config; it does not hold its own config or a private copy of the integration logic. So the fleet is N+1 repos: one Pinax repo (library + config + registry), N server repos (one per MCP, each independently deployed).
The inverted version pin (the linchpin). A server repo must not choose its own versions or hold its own config — that is the ordinary shared-library pattern, and it is exactly the drift Principle 2 forbids (some servers upgrade, some don’t, copies diverge → “twelve places” again, wearing version numbers). Instead the pin is inverted: the server self-pins nothing; it says “I use Pinax,” and Pinax dictates the entire version set it builds against — the Pinax library itself, the Agno framework, the MCP SDK, the shared N1 libraries: everything. The authoritative statement of “server X builds against this set of versions and this config snapshot” lives in Pinax’s config, keyed on the server’s identity — not in X’s repo. (This is the same mechanism as role 5, external dependency management — role 5 is the inverted pin applied across the whole external set; the Pinax library is simply one more entry in that dictated set. One mechanism, one place, dictating all versions, not two.)
Concretely, the dictation is delivered as a Pinax-produced lockfile: a normal version lockfile — except Pinax authors it, per server, from its central config, rather than the server writing its own. The build resolves against that Pinax-produced lockfile, so it ends up on exactly the dictated set. That the import is mandatory and the lockfile is centrally authored is what makes this management, not adoption-by-convention. (Field-level details — how a build identifies itself to fetch its lockfile, the exact lockfile format — are design-session; the inversion and the Pinax-produced lockfile are fixed here.)
Lifecycle ownership — what Pinax does not start or stop
Section titled “Lifecycle ownership — what Pinax does not start or stop”Two different “lifecycles” must not be confused, and Pinax owns neither:
- Server process / deployment lifecycle (starting and stopping an MCP server) is the standard deploy path — container → ECR → Kubernetes/Helm, exactly as the data-fetching service deploys. Pinax does not orchestrate, start, or stop server processes; doing so would put it adjacent to the runtime path it must stay out of. Pinax’s only contribution to a server’s startup is the config that was baked in at build (build-pull, below).
- MCP connection / session lifecycle (connect, handshake, bind, refresh, teardown) is Agno’s
(
MCPTools,mcp_lifespan,refresh_connection), as established in the research. Pinax owns the configuration of these mechanisms, never their execution.
Relationship to Helm / deploy — a hard boundary
Section titled “Relationship to Helm / deploy — a hard boundary”Pinax never touches infra, deploy, or Helm. The two are different layers, different times, with no shared surface:
- Pinax = environment-invariant application/identity config, baked into the image at build. The settings (see Core roles → Settings), auth wiring, registry/identity, and the inverted pin — what the server is, the same wherever it runs. Baked into the image (not a ConfigMap or Helm values file) deliberately: it keeps the seam hard and forecloses a live-edit backdoor, so the only way to change Pinax-owned config is a rebuild from Pinax.
- Helm = per-environment infra + the actual secret values, applied at deploy. Replicas, resources, probes, namespace/cluster/environment, image tag; and it mounts the real secret value from the secret manager into the pod. How this deployment runs — different across staging/prod.
- The one handoff is secrets: Pinax owns the secret-manager reference (which key), baked in; Helm/the secret-manager delivers the value at deploy. Pinax holds no secret material. Each MCP keeps its own Helm chart — its repo, its responsibility — and Pinax does not generate, own, or orchestrate deployment. Since every server needs a chart to deploy at all, the path is a shared base Helm chart (infra/platform layer) as the obvious starting point — the common deploy shape (probes, resource floors, the secret-mount pattern) — that a server builds its chart on and adds only what’s specific to it, rather than hand-rolling the baseline from scratch. Creating that base chart is in scope for this work (so the consistent path exists from the start), but it is an infra-layer artifact: Pinax does not own, contain, or touch it — the boundary holds. This is the same incentive logic as the inverted pin (Principle 5): the provided base is strictly less work and the consistent path, so building a chart off it is the natural default; a server could hand-roll its own instead, but that is off-policy and needlessly inconsistent — and, like everything about how a server builds and runs, nobody prevents it and the server owns the result. The infra layer owns the base; what a server does with it is the server’s. That base chart is a referenced convention here, not defined by this spec (it is infra, not Pinax), and Pinax’s only contribution to it is the values a chart consumes (e.g. the secret-manager reference, above). The model is symmetric: a shared base keeps infra consistent the way Pinax keeps app config consistent — same incentive principle, two domains, clean seam, neither reaching into the server.
Who owns what
Section titled “Who owns what”- Agno — connection/session lifecycle, reconnection, authenticated-session establishment, runtime multiplexing, and composing agents (the mechanisms in Lifecycle ownership and Known Agno constraints). Pinax configures these; Agno executes them.
- Each server — serving data and validating its own token, independently against the issuer-established token (the data-fetching service’s model, unchanged).
- Pinax — the configuration that drives the above: the registry, each server’s Agno-consumable
MCPToolsconfig, theheader_provider/scope/registry wiring, the refresh policy, settings, and the external dependency versions. Build-time only.
Known Agno constraints to design against
Section titled “Known Agno constraints to design against”- Use
MCPTools, notMultiMCPTools.MultiMCPToolsis deprecated in favour of multipleMCPToolsinstances; the multi-server case is a list of single-server toolkits. (This is also a correction the data-fetching spec needs — see Sibling-spec corrections.) - No mid-run reconnection resilience in Agno. Agno does not auto-retry failed connections or
implement backoff mid-run; there is a known issue (~Agno v2.3.24) where a failed reconnection raises
a
CancelledErrorthat can cancel the whole agent run. How Pinax’s configuration accounts for this (refresh policy, failure handling expectations) is a design-session item. header_providerheader timing.header_providerestablishes a fresh connection per context rather than passing per-request headers on a shared connection; how patient-scoped tokens attach via this mechanism must be confirmed against the pinned Agno version.
The delivery mechanism — build-pull through one mandatory integration
Section titled “The delivery mechanism — build-pull through one mandatory integration”In plain terms first. Pinax is a central place that holds each server’s config. There are two common ways a server could get config from such a place: pull it at runtime (the server phones home to a config service when it starts — the more common pattern) or pull it at build time (the config is baked into the server’s image when the image is built, and the running server never phones home). Pinax uses the build-time way, on purpose: because this config governs PHI-gating auth, we do not want a live service the whole fleet depends on at runtime, and we want every config change to go through normal deploy review. The trade is that changing config means rebuilding and redeploying — which is fine here, because this config changes occasionally and should be reviewed when it does.
“Build-pull” is the name for that build-time pull: at build, the server fetches its config from Pinax and bakes it into the image — the same shape as a build fetching its dependencies or pulling a secret reference. “The emit” is the output of that step: the concrete config artifacts the build produces from Pinax’s stored config and writes into the image. (So when Verification says it tests “the emit,” it means it checks that this build step produces the right config for a server — and since that build step is the only Pinax-related thing that ever runs, it is the only thing there is to test.)
Configuration reaches servers and agents through exactly one mandatory integration (Principle 2) — a single “get config / Agno wiring / auth setup from Pinax” path, not re-implemented per server — and that integration pulls from Pinax at build/deploy time and bakes the result into the deployed artifact (“build-pull”).
Consequences, which the rest of the spec rests on:
- Pinax is never needed at runtime or at boot. A deployed server/agent runs entirely on baked-in config; Pinax can be down with no effect on the running fleet. This is the strongest form of “never in the path” — Pinax is not even on the boot path — and the conservative posture for a layer that configures PHI-gating auth.
- Config changes land through deploy. A change in Pinax (a scope, a setting, a dependency version) takes effect when the affected artifacts are re-emitted and redeployed — not live. This is deliberate: security-sensitive config inherits the normal deploy flow’s review, diff, versioning, and rollback, rather than propagating on an unreviewed restart.
- A deployment’s config is explicit and reproducible — pinned to what Pinax emitted at build time, answerable from the artifact itself.
- Rollback is ordinary deploy rollback — fleet-wide, heavy but simple. Because config is baked into the image, the previous artifacts already carry the previous config, so redeploying the prior fleet rolls its config back with it — Pinax is not even involved. To instead roll forward to a corrected state, revert the change in Pinax’s config repo and re-emit + redeploy — fleet-wide, since any Pinax change is fleet-wide by nature (above). Either way it is a standard deploy operation, not a bespoke Pinax mechanism, and there is no live config to “un-set.” The single integration is MCP-side (decided): the server imports the mandatory Pinax library (the “I use Pinax” declaration), and the server’s build runs it to pull config from Pinax and produce the emit baked into the image. Pinax itself runs nothing — it is config/data the build reads, which is what keeps “Pinax never executes” (Principle 1) a concrete fact rather than just a stance; a Pinax-side integration would risk becoming a component that runs. What is also fixed: there is exactly one such integration, it is the sanctioned single path (going around it is hand-rolling — off-policy and unmanaged, not something Pinax prevents; Principle 5), and it is build-pull.
The accepted trade is that config changes require a redeploy to take effect; rejected alternative was startup-pull (servers read Pinax live at boot), which would make Pinax a fleet-wide boot dependency with its own HA/uptime requirement and would let security-sensitive config changes bypass the deploy review gate — costs not worth the only prize, no-redeploy config changes, which N1 does not need for the kind of config Pinax owns.
Agno version pin, the exact registry interface, and the token-attachment mechanism are confirmed at a build-time design session against the live Agno version and Eyad’s registry, mirroring the data-fetching service’s design-session model.
Observability — delegated to Agno; build-time provenance to the pipeline
Section titled “Observability — delegated to Agno; build-time provenance to the pipeline”Runtime observability is Agno’s, not Pinax’s. Agno’s OpenTelemetry tracing captures the agent execution flow as hierarchical spans (LLM calls, tool executions, delegations) with token usage, latency, errors, and I/O — stored in your own infrastructure. Because every agent’s runs flow through it, a cross-MCP / fleet-wide runtime view already exists as a query over that trace store; it is not a Pinax feature to build.
Two things fall outside Agno’s agent-side tracing, and neither makes Pinax a runtime-observability owner: a server’s internal operations (its api-backend reads, cache hits, error-mapping) are the server’s own operational logging; and Pinax’s own actions are build-time emit/registration events whose record is build/CI logging and artifact provenance (what config produced which artifact, when) — a byproduct of build-pull in the deploy pipeline, not a runtime system. So Pinax builds no runtime observability layer (that would breach Principles 1 and 3).
Risks & failure modes
Section titled “Risks & failure modes”- Mis-configuration blast radius. Pinax is the single authoritative source of configuration for the fleet; a wrong auth-config, scope, or registry entry can mis-configure many servers at once. This is the price of centralization. It is bounded not by Pinax validating content (Pinax does not judge config correctness) but by where the config is authored and how it ships: registry entries are authored and reviewed in Pinax’s repo (ordinary code review on the change), and build-pull means a bad entry cannot reach the fleet until it is emitted and redeployed — so it also passes the normal deploy review/diff and is rollbackable like any other deploy. Two review gates (authoring + deploy), not a Pinax content-check. Residual risk: a wrong-but-well-formed entry that passes both reviews — accepted, same as any reviewed config change.
- Agno coupling. Agno-exclusivity plus reliance on Agno’s native mechanisms means Agno changes
(the deprecation of
MultiMCPTools, the reconnectionCancelledErrorbug,header_providerbehaviour) propagate to Pinax. Mitigation: pin the Agno version; track these at the design session. - Cross-team dependency on the trusted-services registry (Eyad’s). Until it is live and Pinax (and each server) is registered, the auth-configuration path is incomplete. Shared with the data-fetching service. (See Open questions.)
- Drift in anything Pinax does not manage. By Principle 2, anything outside Pinax’s managed set is the server’s own; that is an accepted trade-off, but the boundary must be explicit so gaps are chosen, not accidental.
- A server going around the inverted pin. The split-polyrepo structure (Architecture) keeps versions from drifting for conforming servers: registration delivers the Pinax-dictated version set automatically (the Pinax-produced lockfile), so a conforming server never self-pins. A server could hand-roll its own versions/config instead — but that is going off the sanctioned path (Principle 5), forgoing what Pinax delivers for free, and Pinax neither prevents nor polices it. The residual risk is therefore an off-policy server drifting; it is addressed the same way all conformance is — ease and policy, plus ordinary review — not by a mechanism Pinax enforces. (Pinax’s own emit is still correct regardless: it only ever emits the versions it dictates.)
- Config changes are fleet-wide by nature (build-pull trade). Pinax is a fleet-wide layer: a change to its config means the whole fleet is now built against a superseded snapshot, so all MCP services rebuild and redeploy to pick it up — not a subset. This is not a silent-drift problem needing detection (there is no “which servers drifted?” question — the answer is always “all, until the fleet redeploys”); it is simply a property of a centralized build-time layer. The redeploy itself is owned by infra / the deploy pipeline (the same boundary as Helm — Pinax does not orchestrate it). The cost is real and deliberate: a one-line Pinax change triggers a fleet-wide redeploy — heavy, but simple, and it inherits the normal deploy flow’s review, diff, and rollback. The only residual is ordinary deploy discipline (the fleet redeploy must actually be run after a Pinax change), an operational matter, not a Pinax mechanism.
Verification
Section titled “Verification”What is verified is the emit — the build-time step that turns Pinax’s stored config into the artifacts a server’s build bakes in (defined in Approach → The delivery mechanism). Because it is build-time, not a running service, verification is largely deterministic and runs in the build/test pipeline: given a fixture set of registered servers, external dependency versions, and auth config, assert that the emitted artifacts are exactly correct. There is no live agent to observe here — and Pinax does not produce per-agent setups (Agno composes agents, Principle 3); Pinax’s job ends at producing the right config and registry. The method is a loop — declare expected, emit, verify output, fix the emit (never the fixture), re-run — built harness-first and run through every milestone (see Development).
Two tiers, checked differently:
Deterministic — must always hold (asserted mechanically; a failure is a hard defect):
- Correct, complete registry/config for Agno. The emitted registry lists exactly the registered
servers with each server’s correct config (endpoints, settings, auth wiring, refresh policy) in the
form Agno consumes, expressed via single-server
MCPTools(neverMultiMCPTools). - Faithful auth config. Each registered server’s emitted
header_provider/scope/registry wiring reproduces the managed auth config exactly — what the registry holds is what the artifact carries, no more and no less. (Whether that managed scope is itself correct is authoring-side, not a Pinax emit check.) - Single source. Every emitted value traces to one authoritative source in Pinax; a change at that source flows to the whole fleet’s next build (fleet-wide by nature — see build-pull).
- External dependency currency. A bump to an external dependency’s version in Pinax propagates to every server’s emitted build from the one change, so the fleet stays current with the external world; no server resolves a version other than the dictated one. (Servers are isolated — this is each build picking up the dictated external version, not any inter-server relationship.)
- Member-gated, faithful emit. Config is emitted only for a registered service; an unregistered service resolves nothing (unprovisioned, never a default). For a registered service, the emit faithfully and completely reproduces what Pinax’s registry holds for it — no partial artifact, no filled-in default. Pinax does not judge whether the registry’s contents are correct or safe (that is the maintainer’s, authored and reviewed in Pinax’s repo); it asserts the emit is a complete, member-gated reproduction of the authoritative registry.
- Fail, don’t guess, on an unresolvable reference. If a registration points at something the emit cannot resolve (e.g. an unknown/unresolvable pinned Pinax version), the emit fails rather than guessing or substituting a default — a mechanical-integrity failure, not a content judgement.
- No token material. No patient token, OAuth client secret, or other credential material ever appears in an emitted artifact (only configuration that points at where execution obtains them).
- Out-of-path invariant. Emitted artifacts contain no runtime/boot-time call back to Pinax — a deployed unit depends on Pinax nowhere (the build-pull guarantee, asserted). Best-effort — judged, not asserted: whether a registration is ergonomic — that registering a server is genuinely “register and done,” with no hand-edited per-agent or per-server integration left over. Reviewed, not gated.
Failure-first scenarios (the suite is built around these, not the happy path): an unregistered service’s build pulls config (must resolve nothing — no partial, no default — leaving it unprovisioned); a registration pointing at an unknown/unresolvable pinned Pinax version (the emit must fail, not guess); a stale registration removed (must drop out of the emitted registry on the next emit); a change to one managed value (must propagate fleet-wide from the one change). Each must produce the correct emit result — including correctly serving nothing to a non-member and correctly failing on an unresolvable reference — with no manual fix-up. (These test the emit’s membership gate and mechanical integrity, not the correctness of registry contents, which Pinax does not judge.)
The fixture format, the assertion harness, and where it runs in CI are build-time setup, confirmed at the design session. What is fixed here is the two tiers and that verification targets the emit.
Development — build and verify in milestones (harness first)
Section titled “Development — build and verify in milestones (harness first)”Pinax does not exist yet — repo, the one mandatory integration, the config/registry format, and the verification harness all have to be stood up. As with the data-fetching service, the harness is built first and every later milestone is proven against it before the next begins; nothing is “done” on its first pass. Because Pinax’s behaviour is a deterministic build-time emit, the rig is simpler than the sibling spec’s (no live agent, no LLM judge) — it asserts emitted artifacts against expected ones.
The shared rig (built at M1, reused throughout):
- A fixture set of registered servers — each a registry entry (identity, settings, auth config, pinned version) — plus external dependency versions and (negative) unregistered-server cases. Fixtures carry no PHI and no secret values (only secret-manager references), per the Security gate.
- An assertion harness that runs the emit over a fixture set and checks the deterministic tier: member-gated faithful reproduction, single-source propagation, external-dependency currency, fail-on- unresolvable, no-token-material, and the out-of-path invariant.
- Golden expected artifacts per fixture, so “faithful reproduction” is an exact diff, and negative fixtures (unregistered service, unresolvable pin, malformed entry) that must produce nothing or a failed emit — the failure-first scenarios are first-class, not an afterthought.
- A place it runs: Pinax’s own CI, on changes to Pinax’s config/registry (the authoring gate) and on the integration’s build path. The verification loop (every milestone runs through it). Development is a loop, not a one-shot build-then-test. For each milestone:
- Declare expected. For the milestone’s testable output (below), author the golden for each positive fixture and the expected refusal/empty outcome for each negative case — up front, because we control the fixtures and therefore know exactly what a correct emit must produce.
- Run the emit over the fixture set through the same integration a real build uses (no test-only emit path — the thing under test is the real one).
- Verify the output against the expected, using the check-types below, at the milestone’s stated success bar.
- On mismatch, fix the emit or the config/registry logic — never the fixture or the golden to make a bad emit pass. This is the load-bearing discipline, the Pinax analog of the sibling spec’s “fix the tool, never the agent”: a fixture or golden changes only when the expectation itself was wrong, not to paper over a wrong emit. If a green run depends on having loosened a check or edited a golden to match a suspect output, that is a defect hidden, not fixed.
- Re-run to green, then extend the fixture set (more servers, more negatives, the next milestone’s cases) and repeat. The suite only grows; a scenario once green stays in. What the emit produces (the testable outputs). For each registered server, build-pull produces a baked-in config made of distinct, separately-checkable blocks: (a) its Agno-consumable connection config (endpoint/transport, the auth-header wiring, the refresh policy); (b) its operational settings (the role-1 set — caps, timeouts, cache policy, etc.); (c) its auth config (registry endpoint, scope(s), and a secret-manager reference — never a value); (d) the resolved external dependency versions it builds against; and (e) its entry in the emitted registry (identity + connect metadata). For an unregistered server the emit produces nothing. These blocks — not “the artifact” in the abstract — are what goldens are written for and what each check below inspects. (Exact field names per block are design-session detail; the blocks and what each must/must-not contain are fixed here.)
Output verification — how an emitted output is judged correct. Five check-types, all mechanical and all able to fail (a check that cannot fail is not evidence — n1 security litmus); each milestone states which blocks it emits and which checks at what bar constitute its success:
-
Exact golden diff (faithful reproduction). Each emitted block is compared field-for-field to the authored golden for that fixture; any drift — a missing field, an extra default, a changed scope, a reordered list — fails. The primary positive check, and the direct assertion of “faithful, member-gated reproduction.”
-
Structural / schema validity. Each block is valid in the form its consumer needs — the connection config is valid single-server
MCPTools(neverMultiMCPTools), parseable by the pinned Agno version; the registry entry is well-formed — so a faithful output is also a usable one. -
Determinism / reproducibility. The same fixture set emits identical blocks every run (build-pull bakes these into deployable artifacts, so a nondeterministic emit is a defect).
-
Invariant property-checks across all fixtures (not golden-specific): no token or secret value appears in any block (only references), and no block contains a runtime/boot-time callback to Pinax (the out-of-path invariant). These hold for every emit, positive or negative.
-
Negative outcomes are asserted, not just absent. An unregistered service must emit nothing; an unresolvable pin / malformed entry must fail the emit. The harness asserts the refusal happened and that no partial block leaked — “no output” is a pass only when the check could have wrongly produced one. Ordered milestones (each built and run through the loop above until green before the next). Each states its testable output — the concrete thing produced that the harness inspects — and success — the exact pass condition on that output. Where a milestone’s output isn’t an emit (M0, M1), the testable output is named accordingly.
-
M0 — Identify the external dependency set. Build: enumerate, against the real stack, which external dependencies are in the shared set Pinax manages versus left to each server’s own discretion (the role-5 examples are illustrative, not the final list). Testable output: the written set — every dependency classified external (Pinax-managed) or internal (server’s own), plus the rule that decides the class. Success: every dependency in the real stack is classified with none left unclassified, and the rule is precise enough that a new dependency can be placed without a judgement call. (A completeness/unambiguity check on a deliverable, not an emit test.)
-
M1 — Harness first. Build: the fixture format, the assertion harness, golden + negative fixtures, and the CI wiring. Testable output: the harness’s verdict — pass/fail plus which check failed — on a set of planted fixtures whose correct verdict is known in advance. Success: every planted known-good fixture passes, every planted known-bad fixture fails on the specific check that should catch it (the harness discriminates and localizes). A harness that passes a planted-bad fixture, or fails on the wrong check, is itself the defect. (This tests the test before any real emit is trusted.)
-
M2 — The emit for one server, end to end. Build: the config/registry format and the one mandatory integration far enough to emit one server’s config via build-pull. Testable output: for one registered fixture server, blocks (a)–(e) as baked in; for one unregistered fixture, whatever the emit produces. Success: the registered server’s five blocks each exactly match their authored golden (faithful), the connection block is structurally valid/usable, and no block contains a secret value (reference only); the unregistered server emits nothing — no partial block. Both asserted by the harness, not by eye.
-
M3 — The inverted pin (Pinax-produced lockfile) across versions. Build: central version dictation keyed on server identity — Pinax produces each server’s lockfile (the whole set: external deps + the Pinax library), the build resolves against it. Testable output: the version set in block (d) of each server’s emit (sourced from its Pinax-produced lockfile), captured before and after a single central version change; plus the emit result for a server whose dictated set references an unresolvable version. Success: before the change every server’s lockfile/emit shows the old version; after changing it in one place and re-emitting, the whole fleet’s next build shows the new version (fleet-wide from the one change); a server resolves no version other than its dictated one; and the unresolvable-version case fails the emit rather than guessing or defaulting.
-
M4 — Full deterministic tier + failure-first suite. Build: turn on every check-type and the whole negative suite. Testable output: the suite’s full result matrix over all positive and negative fixtures, plus a second identical run. Success: all five check-types green on positives; each negative fixture yields its correct outcome (unregistered → nothing, unresolvable/malformed → failed emit, stale registration → dropped from the registry block, single managed-value change → propagates fleet-wide from the one change); the re-run is byte-identical (determinism); and the cross-fixture invariants (no secret value, no callback to Pinax) hold on every block. “Green” explicitly includes the correct refusals.
-
M5 — First real member: the data-fetching service (auth-independent). Build: register the real data-fetching server and emit its config through Pinax (applying the Sibling-spec corrections). This includes emitting the auth-config block (c) — the registry endpoint, scopes, and secret-manager reference — as config; it does not exercise live auth against Eyad’s registry (that is the separate Auth milestone). Testable output: the data-fetching server’s real emitted blocks, and the deployed server’s boot behaviour with Pinax unreachable. Success: it is registered and its real emit gives it blocks (a)–(e) with no self-pinned version and no private config; and the deployed unit boots on baked-in config with Pinax down — proving the out-of-path invariant on a real artifact, not just asserting it on a fixture. (Authenticating and serving real data is the Auth milestone, below.)
-
M6 — Base Helm chart + deploy (auth-independent). Build: create the shared base Helm chart (in scope, infra-layer, not owned by Pinax) and have the first member build its chart on it. Testable output: a member actually deployed via a chart built on the base, and the provenance of the secret — value vs. reference. Success: the member deploys via a chart that inherits the base; the secret value is delivered at deploy by Helm/the secret-manager while Pinax’s emitted block (c) carried only the reference — the Pinax↔Helm boundary proven in practice, with Pinax holding/emitting no secret value. (Deploy mechanics and the boundary only; live auth is the Auth milestone.)
-
Auth — live integration with the trusted-services registry (separate, out-of-band, gated on Eyad). Everything above is built auth-independent: Pinax emits auth config (the block-(c) shape) as ordinary config, but live token obtain/validate against Eyad’s registry is walled off here. No work happens on this milestone until Eyad’s registry is live and Pinax is registered in it — it is not a fixed position in the M0–M6 sequence but a separate track triggered by that readiness; the rest of the build does not wait on it. Build (when unblocked): wire the emitted auth config to the live registry and exercise it end to end — a real member obtains a patient-scoped token, the server validates it, and real data is served. Testable output: a deployed real member authenticating via the registry and serving. Success: end-to-end auth works against the live registry, default-deny on missing/expired/ over-scoped tokens holds (server-side), and the config Pinax emitted needed no change to light up — confirming auth was a clean seam built around, not retrofitted. Critical path & external dependencies. The sequence is built so that nothing core waits on another team. M0–M4 is fully self-contained — fixtures only, no external system (no Eyad, no base chart, not even live Agno), so Pinax is fully proven on invented data before any dependency lands. M5–M6 add the first real member and real deploy but are auth-independent by construction: they emit real config (including auth config as config) and prove boot/deploy/boundary without live auth. The base Helm chart M6 needs is in-scope-to-create (infra-layer). Live auth is the only thing gated on an external team (Eyad’s registry) and is walled into its own out-of-band milestone, worked only once that registry is live. So the two external dependencies (Eyad’s registry, the base chart) gate only the tail, never the core.
The build-time design session (the external dependency set of M0, the Agno version pin, the registry interface and token-attachment mechanism, the exact config/registry and lockfile schema) feeds M0–M3 and is normal kickoff, not a spec gate — the architecture, roles, and boundaries above are already settled here.
Acceptance criteria
Section titled “Acceptance criteria”Pinax is accepted when all of the following hold. Each maps to a Verification check (the emit tiers), a principle, or a gate.
Function — the five roles work end to end:
-
A new MCP server is registered with Pinax and, with no hand-written per-server integration code, becomes available to Agno: present in the emitted registry; correctly configured (as single-server
MCPToolsconfig — neverMultiMCPTools); correctly auth-wired; and built against the dictated external dependency versions. (How agents are composed from the available servers is Agno’s, not Pinax’s.) -
Settings and auth config have a single authoritative source (the Pinax repo); a change to either propagates from that one place to the whole fleet on the next build (fleet-wide by nature).
-
External dependency versions are managed centrally; a version bump propagates from one place so the fleet stays current with the evolving external world — servers are isolated, with no dependency on one another, and their internal/own dependencies remain their own. Structure — split polyrepo + inverted pin:
-
Each MCP server is its own repo importing the Pinax library; it carries no private config and self-pins no versions.
-
The entire version set a conforming server builds against — the Pinax library, Agno, the MCP SDK, shared N1 libraries — is dictated by Pinax’s central config (the inverted pin), keyed on server identity and delivered automatically by registration as a Pinax-produced lockfile; a conforming server self-pins nothing because it does not have to. (Going off the sanctioned path to self-pin is off-policy and unmanaged, not a build Pinax fails; Principle 5.) Pinax’s emit fails on a registration referencing an unknown/unresolvable version rather than guessing. Boundaries — out of the path, build-time only:
-
Pinax is not involved in starting or stopping MCP server processes (that is the K8s/Helm deploy path); a deployed server/agent boots and runs on baked-in config, and the running fleet depends on Pinax nowhere (Pinax can be down with no runtime effect).
-
Pinax is demonstrably out of the data call path and never holds, brokers, or mints a token.
-
Runtime observability is Agno’s; Pinax ships no runtime telemetry layer (only build-time emit provenance in the pipeline). Emit correctness (Verification deterministic tier holds):
-
The emit is a complete, member-gated, faithful reproduction of Pinax’s registry: config is produced only for registered services (a non-member resolves nothing — unprovisioned, never a default), and a registered service’s artifact reproduces exactly what the registry holds, with no partial or filled-in default. The emit fails rather than guessing on an unresolvable reference (e.g. an unknown pinned version). No token or secret value appears in any emitted artifact (only references). (Pinax does not judge the correctness or safety of the registry’s contents — that is the maintainer’s, authored and reviewed in Pinax’s repo.) Principles and gates:
-
All five principles hold under review: Pinax owns configuration and never executes in the path (1); it manages or doesn’t touch — nothing it provides is merely advisory (2); it does not reinvent Agno’s work (3) or reason about consumer behaviour (4); and it conforms servers by ease and policy, never by gating (5).
-
The Security gate is satisfied: config reaches only registered services (membership gate), no token handling, no token/secret value in any artifact, server-side enforcement and default-deny at runtime, and blast radius bounded by authoring + deploy review. The Clinical-accuracy gate is
N/Aas stated. -
Verification passes on its own bar: the emit suite (deterministic tier asserted, failure-first scenarios producing the correct emit result including correct refusals) is green.
Clinical-accuracy gate
Section titled “Clinical-accuracy gate”N/A — Pinax is configuration/build-time infrastructure. It does not generate, interpret, or alter any clinical content, returns no data, and is not in the data path; clinical correctness is owned by the MCP servers and upstream. Pinax does configure the auth that gates PHI access — that risk is owned in the Security gate, not here.
Security gate
Section titled “Security gate”Pinax touches auth configuration, so this gate is in scope even though Pinax never holds a token.
- Boundary. Pinax manages auth configuration (registry endpoint, scopes,
header_providerwiring); it never holds, brokers, stores, or mints a patient token. The token exists only between a server/agent connection and the registry, and is validated by each server independently. - The risk Pinax owns is narrow: that its config reaches only registered servers, so a random MCP someone stands up cannot inherit the fleet’s auth wiring and setup just by pointing at Pinax. Control: registration is service-level acceptance, and build-pull serves config only to a service whose identity is on the register. A service is accepted once (added to Pinax’s registry); thereafter its builds pull config. An unregistered service’s build resolves no config — no auth wiring, no scopes, no listing — so it is simply unprovisioned, never accidentally set up. (“Visit the office first.”) This is membership presence, not a credential the build presents.
- What this control is and isn’t. It is a least-surprise / least-privilege gate against accidental or unsanctioned pickup of fleet config — not a boundary against a hostile server. The correctness of a registered service’s config contents (scopes, audience, endpoints) is the registry maintainer’s (authored in Pinax, trusted, not re-checked by Pinax); and the real defense against a malicious caller is server-side: each server validates the real token at runtime (next bullet) regardless of any config it holds. Pinax gates who gets set up, not who gets access.
- Enforcement lives server-side. Read-only, patient scoping, and token validation are enforced at each MCP server (and in the token’s scope), never by Pinax and never client-side. Pinax cannot widen a scope at runtime because it is not in the path.
- No token or secret material in config. What Pinax serves is configuration that points at where execution obtains secrets (a secret-manager reference, the registry endpoint) — never a token, OAuth client secret, or other credential value. Asserted by the no-token-material check in Verification (the litmus “report paths, not values”).
- Fails secure — unprovisioned, not permissively provisioned. The failure mode is a service that is not registered: its build resolves no config, so it is unprovisioned (no auth wiring at all) — it never falls back to a default or broader setup. And a deployed server that ends up with no valid auth config must deny at runtime, never fall back to a broader credential (server-side, the data-fetching model). Both directions fail toward no access, never toward a permissive default.
- The fleet-wide-token-broker model was considered and rejected on security grounds (single high-value PHI-token store; caller-asserted identity / IDOR). (Principle 1.)
Open questions
Section titled “Open questions”Only two items are genuinely open, both external — no open design decisions remain.
- Trusted-services registry (Eyad’s) — must be live and Pinax registered in it; hard cross-team dependency, tracked separately (shared with the data-fetching service). Gates only the Auth milestone, not the core build (see Development → Critical path).
- Agno + registry specifics, confirmed at the design session — the Agno version pin; the
header_providertoken-attachment behaviour and the registry’s token interface (passthrough vs. exchange, patient-scope claim shape); handling of the reconnectionCancelledErrorissue. These are field-level confirmations against live systems, not open decisions. Resolved here, recorded so they are not reopened: the delivery mechanism (build-pull, MCP-side integration — Approach → The delivery mechanism); Agno-config ownership (yes — Pinax owns each server’s Agno-consumable config; role 3); observability (delegated to Agno; Pinax owns only build-time emit provenance — Approach → Observability); integration placement (MCP-side); and the inverted-pin mechanism (a Pinax-produced lockfile dictating the whole version set — Approach → Repository structure).
Sibling-spec corrections (data-fetching service, apply on its next edit)
Section titled “Sibling-spec corrections (data-fetching service, apply on its next edit)”These are edits to the data-fetching service spec — documentation, not a migration or code retrofit here.
- Drop
MultiMCPToolseverywhere; use multipleMCPToolsinstances. - Soften / correct the “Agno does not auto-reconnect” claim: Agno reconnects at the run boundary
(
refresh_connection) and establishes authenticated sessions (header_provider); what it lacks is mid-run retry/backoff, plus the knownCancelledErrorpropagation bug. - Incorporate Pinax adoption: the data-fetching server is registered in Pinax and gets its config, Agno wiring, and auth setup through the Pinax integration (build-pull, inverted pin) rather than wiring these itself; its repo carries no self-pinned Pinax version or private config. (The server’s charter, governed-door posture, and per-server repo are unchanged.)
