Free lesson · GenAI Platform Engineering
Design tool registry model with MCP server metadata
Define Pydantic models for MCP tool entries: server URL, capabilities, input/output schemas, version, owner team, and deployment status.
Course: AI Developer Platform Engineering · Chapter 12 · Tool Registry & MCP Hub
Free to read — no subscription required.
Introduction
When you operate dozens of MCP servers across an organization—each exposing tools for code generation, database queries, or deployment automation—the absence of a centralized registry turns tool discovery into a Slack-thread archaeology project. Engineers spend cycles hunting for the right server URL, the correct input schema, and whether the server is even reachable. A well-designed tool registry data model captures every dimension of an MCP server's identity: its endpoint, the tools it advertises, their version lineage, ownership, and access control boundaries. By the end of this lesson, you will be able to design a normalized five-entity schema for an MCP registry and translate it into Pydantic models that support tool discovery, version tracking, and team-level access control.
Key terminology
- MCP Server: A process that implements the Model Context Protocol and exposes tools, resources, or prompts over a supported transport (stdio, SSE, or streamable HTTP).
- Tool Registry: A centralized catalog that stores metadata about every MCP server and tool in an organization, enabling discovery, versioning, and governance.
- Transport Type: The communication mechanism between an MCP client and server—stdio for local subprocess communication, SSE for server-sent events, or streamable HTTP for bidirectional request-response flows.
- Deployment Status: A lifecycle label indicating whether an MCP server is actively serving traffic, staging for pre-production validation, deprecated and pending removal, or fully decommissioned.
- Backward Compatibility: The property of a new tool version whose input schema accepts all inputs that the previous version accepted, ensuring existing callers do not break on upgrade.
- Permission Level: A tiered access grant (discover, invoke, admin) that controls what actions a team may perform against a registered tool.
- Health Record: A timestamped probe result capturing an MCP server's reachability status and response latency, used for monitoring dashboards and automated traffic routing decisions.
Concepts
Five Entities for Five Rates of Change
A flat document that stores an MCP server's name, URL, and a list of tool names looks adequate until queries cross concerns. "Which version of query-warehouse was active before the breaking schema change?" cannot be answered without time-stamped version records. "Which teams may call deploy-production but not deploy-staging?" cannot be answered without per-tool permission rows. A flat model collapses these distinct concerns into a single record, forcing consumers to parse interleaved data or simply go without the answers.
The five entities exist because each concern changes at a different rate and in response to a different trigger. A server's deployment_status flips during incident response. A tool's description is updated in a documentation sprint. A new ToolVersion is published each time engineers cut a release. A TeamAccess row is added when a new team onboards. A HealthRecord is written every time a scheduled probe fires. Normalizing these concerns into separate entities means each can evolve independently without touching unrelated records—updating a tool description does not disturb the server record, and revoking one team's access leaves version history intact.
Schema Evolution and Migration Windows
Tool interfaces are not static. A query-warehouse tool might accept a bare sql string in v1.0.0 and a structured object with sql, timeout_ms, and dry_run in v2.0.0. If the registry stored input_schema and output_schema on the Tool entity, publishing v2 would overwrite the v1 contract—callers still on the old interface would lose their reference the moment v2 landed.
Placing input_schema, output_schema, semver, and backward_compatible on ToolVersion means every historical contract persists simultaneously. When backward_compatible is false, the registry signals a breaking change without destroying the prior record. Existing callers continue resolving the v1 ToolVersion while they update their integrations; new callers land on whichever semver the Tool.current_version pointer indicates. This separation is what makes a safe migration window possible (see Code Walkthrough).
Foreign-Key Placement as a Governance Decision
Where a foreign key lands is not an implementation detail—it is a statement about the granularity of control the schema can express, and the two FK choices here have direct operational consequences.
TeamAccess binds to tool_id, not server_id. A team responsible for staging deployments can hold a TeamAccess row for deploy-staging without holding one for deploy-production, even when both tools are served by the same MCPServer process. Server-level grants cannot express this distinction; they are all-or-nothing for every tool on that server.
HealthRecord binds to server_id because MCP health probes target a server's transport endpoint—if that endpoint is unreachable, every tool on the server is unavailable regardless of which specific tool is being queried. This placement also supports two natural query patterns without additional joins: fetching the most recent record for current status, and ranging over checked_at for latency trend analysis. Both patterns map directly to the HealthRecord fields shown in the Pydantic models (see Code Walkthrough).
Code Walkthrough
Building on the five core entities from the Concepts section—MCPServer, Tool, ToolVersion, TeamAccess, and HealthRecord—the ER diagram below shows how those entities relate and what fields each must carry.
Three design decisions are worth calling out explicitly. First, url and transport belong to MCPServer because they describe the server process, not any individual tool—every tool on a server shares the same network endpoint and communication protocol. Second, input_schema and output_schema live on ToolVersion rather than Tool because schemas evolve across releases; the registry must hold both old and new schemas simultaneously to support migration windows without breaking existing callers. Third, TeamAccess binds to Tool rather than to MCPServer, enabling fine-grained governance: a team may be permitted to invoke deploy-staging but not deploy-production even when both tools are served from the same process. HealthRecord links to server_id because MCP health probes target the server endpoint—this placement enables both a latest-record lookup for current status and an aggregate query for latency trends over time, corresponding directly to the two query patterns described in the Concepts section.
The Pydantic models translate this schema into Python types you will use in the lab:
Code snippetpython
1from datetime import datetime 2from typing import Any 3from pydantic import BaseModel 4 5class MCPServer(BaseModel): 6 server_id: str 7 name: str 8 url: str 9 transport: str # "stdio" | "sse" | "http" 10 owner_team: str 11 deployment_status: str # "active" | "staging" | "deprecated" | "decommissioned" 12 registered_at: datetime 13 14class ToolVersion(BaseModel): 15 version_id: str 16 tool_id: str 17 semver: str 18 input_schema: dict[str, Any] 19 output_schema: dict[str, Any] 20 backward_compatible: bool 21 published_at: datetime 22 23class HealthRecord(BaseModel): 24 record_id: str 25 server_id: str 26 status: str # "healthy" | "degraded" | "unreachable" 27 latency_ms: float 28 checked_at: datetime
Confirm that each model instantiates cleanly with representative test data and that foreign-key fields such as server_id and tool_id exactly match the primary-key field names on their parent models—this naming consistency is what makes cross-entity lookups unambiguous before a database layer is wired up.
Do's and Don'ts
Having walked through the five-entity schema and its Pydantic translation, distil the design decisions into rules you can apply when extending the registry.
Do's
- ✓Do place
input_schemaandoutput_schemaonToolVersion, not onTool— schemas evolve across releases, and the registry must hold both old and new schemas simultaneously so existing callers continue working during migration windows while new callers adopt the updated contract. - ✓Do bind
TeamAccesstotool_idrather than toserver_id— tool-level granularity is what lets you grant a team permission to invokedeploy-stagingwhile withholding access todeploy-production, even when both tools are served by the same MCPServer process. - ✓Do align every FK field name exactly with its parent entity's PK name (e.g.,
server_idinToolandHealthRecordmust matchserver_idinMCPServer) — this naming consistency makes cross-entity lookups unambiguous and prevents silent misjoins before a database layer is wired up.
Don'ts
- ✗Don't store
urlandtransportonTool— those fields describe the server process, not individual tools; duplicating them per-tool means every row must be updated if the server's endpoint or communication protocol changes, and inconsistency silently breaks callers pointing at stale URLs. - ✗Don't collapse
ToolVersionrows into a single mutableschemafield onTool— overwriting the schema on each release destroys version history and eliminates thebackward_compatibleflag that the registry depends on to identify breaking changes and protect callers mid-migration. - ✗Don't attach
TeamAccesstoMCPServer— server-level grants cannot express the distinction between permitted and restricted tools on the same server, so a team that should only accessread-logsends up implicitly authorized for every other tool the server exposes.
This lesson is free to read. Its 3 hands-on labs — real code, in a cloud IDE — are part of the GenAI Platform Engineering subscription.
From · cancel anytime
More free lessons in AI Developer Platform Engineering
- Ch 9Deploy cost dashboards with Grafana
- Ch 10Deploy onboarding system with ArgoCD integration
- Ch 12Design tool registry model with MCP server metadataYou are here
- Ch 12Deploy MCP hub with Helm and agent integration
- Ch 13Deploy managed pgvector with Helm StatefulSet
- Ch 14Deploy evaluation platform with Helm and Grafana
- Ch 16Deploy SLA monitoring with Grafana dashboards