Skip to content

Commit dcc56dc

Browse files
jonburdoclaude
andcommitted
Update RFC 0004: address review feedback and add enhancements
Changes from reviewer feedback (TomeHirata, mprahl, thkim-us): - Add server_version and server_alias as direct params on search_mcp_access_bindings for common binding lookups - Add transport_types field to MCPServerVersion (read-only, extracted from server_json at creation time) - Add description fallback from server_json (API resolves like status) - Change icon to icons on MCPServer (list of sized variants matching upstream server.json icon schema) - Add environment variables and tools display paragraphs to UI section - Add transport_type filter for access binding search - Update server.json schema reference from 2025-09-29 to 2025-12-11 - tools.name and transport_types filters work at server-level (matching against resolved latest version) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Jon Burdo <jon@jonburdo.com>
1 parent f187881 commit dcc56dc

1 file changed

Lines changed: 29 additions & 14 deletions

File tree

rfcs/0004-mcp-registry/0004-mcp-registry.md

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ Journey 1 and the additional Phase 1 flows below are fully in scope for Phase 1.
214214

215215
## MCP registry spec alignment
216216

217-
This design aligns with the [upstream MCP registry specification](https://registry.modelcontextprotocol.io/docs) where possible. The spec references in this RFC are based on registry repo [v1.6.0](https://github.com/modelcontextprotocol/registry/releases/tag/v1.6.0) (2026-04-15) and the [server.json schema draft (2025-09-29)](https://json-schema.app/view/%23?url=https%3A%2F%2Fstatic.modelcontextprotocol.io%2Fschemas%2F2025-09-29%2Fserver.schema.json).
217+
This design aligns with the [upstream MCP registry specification](https://registry.modelcontextprotocol.io/docs) where possible. The spec references in this RFC are based on registry repo [v1.6.0](https://github.com/modelcontextprotocol/registry/releases/tag/v1.6.0) (2026-04-15) and the [server.json schema draft (2025-12-11)](https://json-schema.app/view/%23?url=https%3A%2F%2Fstatic.modelcontextprotocol.io%2Fschemas%2F2025-12-11%2Fserver.schema.json).
218218

219219
**What we adopt directly:**
220220
- The `server.json` (ServerJSON) payload format as the canonical MCP server definition
@@ -271,8 +271,8 @@ from enum import StrEnum
271271
class MCPServer:
272272
name: str # extracted from server_json; reverse-DNS format (e.g., "io.github.user/server"); PK within workspace
273273
display_name: str | None = None # mutable human-readable label; falls back to server_json["title"], then name
274-
description: str | None = None
275-
icon: str | None = None # mutable; URL or data URI for UI rendering
274+
description: str | None = None # mutable; API falls back to server_json["description"] from the resolved latest version
275+
icons: list[dict] | None = None # mutable; sized icon variants following the upstream server.json icon schema (src, sizes, mimeType, theme)
276276
workspace: str | None = None # resolved via resolve_entity_workspace_name()
277277
status: MCPStatus | None = None # read-only; derived from the resolved latest version's status
278278
tags: dict[str, str] = field(default_factory=dict)
@@ -285,7 +285,9 @@ class MCPServer:
285285
last_updated_timestamp: int | None = None
286286
```
287287

288-
**Name identity**: `name` is extracted from `server_json["name"]` and follows the upstream spec's reverse-DNS format (e.g., `io.github.user/brave-search`). This format prevents name collisions by construction — the namespace portion identifies the publisher. The `name` is immutable and serves as the primary key within a workspace. For display purposes, `display_name` is a mutable user-supplied label on `MCPServer`. UIs resolve display names as: `display_name` (if set) → `server_json["title"]` (if present) → `name`.
288+
**Name identity**: `name` is extracted from `server_json["name"]` and follows the upstream spec's reverse-DNS format (e.g., `io.github.user/brave-search`). This format prevents name collisions by construction — the namespace portion identifies the publisher. The `name` is immutable and serves as the primary key within a workspace. For display purposes, `display_name` is a mutable user-supplied label on `MCPServer`. The API resolves display names as: `display_name` (if set) → `server_json["title"]` (if present) → `name`.
289+
290+
**Description resolution**: `MCPServer.description` is a mutable MLflow-managed field. The API resolves descriptions as: `MCPServer.description` (if set) → `server_json["description"]` from the resolved latest version (if present) → empty. This follows the same pattern as `status`, which is derived from the resolved latest version and populated in the response.
289291

290292
**Audit field population**: `created_by` and `last_updated_by` are populated from the authenticated MLflow user when authentication is enabled. In unauthenticated installs, these fields remain empty.
291293

@@ -323,6 +325,7 @@ class MCPServerVersion:
323325
display_name: str | None = None # mutable human-readable label
324326
status: MCPStatus = MCPStatus.DRAFT
325327
tools: list[MCPTool] | None = None # mutable; declared tools this server can provide
328+
transport_types: list[str] | None = None # read-only; extracted from server_json packages[].transport.type + remotes[].type at creation time
326329
aliases: list[str] = field(default_factory=list) # read-only; alias names from parent mcp_server_aliases rows currently pointing at this version
327330
tags: dict[str, str] = field(default_factory=dict)
328331
source: str | None = None # provenance URI (e.g., git repository URL)
@@ -337,6 +340,8 @@ class MCPServerVersion:
337340

338341
**Tools metadata**: The `tools` field on `MCPServerVersion` stores the declared tools this MCP server version can provide. Each tool is represented as an `MCPTool` dataclass matching the [MCP Tool schema](https://modelcontextprotocol.io/specification/2025-11-25/server/tools) — name, title, description, input/output schemas, annotations, icons, and execution hints. The upstream `server.json` schema does not include a tools field; this is an MLflow extension for discovery and governance. `tools` is mutable so it can be populated after initial registration — for example, when a platform engineer registers an external server by URL and later populates tools after observing what the endpoint exposes. `MCPServerVersion` is the source of truth for tools; `MCPAccessBinding` responses project tools read-only from the resolved version for search convenience. Tool filtering is a future gateway concern.
339342

343+
**Transport types**: The `transport_types` field is a read-only list extracted from `server_json` at creation time — the union of `packages[].transport.type` and `remotes[].type` (e.g., `["stdio", "streamable-http"]`). This enables filtering versions by transport capability without querying inside the JSON payload.
344+
340345
Retaining older versions enables:
341346

342347
- **Trace provenance**: show exactly which `server_json` was in effect when a trace ran
@@ -538,7 +543,7 @@ Six tables, created via a single Alembic migration. All tables are workspace-sco
538543
| `name` | `String(256)` | PK |
539544
| `display_name` | `String(256)` | mutable human-readable label |
540545
| `description` | `String(5000)` | |
541-
| `icon` | `String(2048)` | optional icon; URL or data URI |
546+
| `icons` | `JSON` | nullable; sized icon variants (src, sizes, mimeType, theme) |
542547
| `latest_version` | `String(256)` | optional explicit version string to resolve as `latest` |
543548
| `created_by` | `String(256)` | |
544549
| `last_updated_by` | `String(256)` | |
@@ -556,6 +561,7 @@ Six tables, created via a single Alembic migration. All tables are workspace-sco
556561
| `display_name` | `String(256)` | mutable human-readable label |
557562
| `status` | `String(20)` | default `'draft'` |
558563
| `tools` | `JSON` | nullable; mutable tool metadata (names, descriptions, input schemas) |
564+
| `transport_types` | `JSON` | nullable; read-only; extracted from server_json at creation (e.g., `["stdio", "streamable-http"]`) |
559565
| `source` | `String(512)` | provenance URI |
560566
| `created_by` | `String(256)` | |
561567
| `last_updated_by` | `String(256)` | |
@@ -651,7 +657,7 @@ class MCPServerRegistryMixin:
651657

652658
# --- MCPServer operations ---
653659

654-
def create_mcp_server(self, name: str, description: str | None = None, icon: str | None = None) -> MCPServer:
660+
def create_mcp_server(self, name: str, description: str | None = None, icons: list[dict] | None = None) -> MCPServer:
655661
raise NotImplementedError(self.__class__.__name__)
656662

657663
def get_mcp_server(self, name: str) -> MCPServer:
@@ -671,7 +677,7 @@ class MCPServerRegistryMixin:
671677
name: str,
672678
description: str | None = None,
673679
display_name: str | None = None,
674-
icon: str | None = None,
680+
icons: list[dict] | None = None,
675681
latest_version: str | None = None,
676682
) -> MCPServer:
677683
raise NotImplementedError(self.__class__.__name__)
@@ -741,6 +747,8 @@ class MCPServerRegistryMixin:
741747
def search_mcp_access_bindings(
742748
self,
743749
server_name: str | None = None,
750+
server_version: str | None = None,
751+
server_alias: str | None = None,
744752
filter_string: str | None = None,
745753
max_results: int = 100,
746754
order_by: list[str] | None = None,
@@ -853,13 +861,13 @@ class MCPToolPayload(BaseModel):
853861
class CreateMCPServerRequest(BaseModel):
854862
name: str
855863
description: str | None = None
856-
icon: str | None = None
864+
icons: list[dict] | None = None
857865

858866

859867
class UpdateMCPServerRequest(BaseModel):
860868
display_name: str | None = None
861869
description: str | None = None
862-
icon: str | None = None
870+
icons: list[dict] | None = None
863871
latest_version: str | None = None
864872

865873

@@ -908,7 +916,7 @@ class MCPServerResponse(BaseModel):
908916
name: str
909917
display_name: str | None = None
910918
description: str | None = None
911-
icon: str | None = None
919+
icons: list[dict] | None = None
912920
status: str | None = None # derived from the resolved latest version's status
913921
access_bindings: list[MCPAccessBindingSummaryResponse] = Field(default_factory=list)
914922
latest_version: str | None = None
@@ -927,6 +935,7 @@ class MCPServerVersionResponse(BaseModel):
927935
display_name: str | None = None
928936
status: str = "draft"
929937
tools: list[MCPToolPayload] | None = None
938+
transport_types: list[str] | None = None
930939
aliases: list[str] = Field(default_factory=list)
931940
tags: dict[str, str] = Field(default_factory=dict)
932941
source: str | None = None
@@ -988,7 +997,9 @@ The `filter_string` parameter supports expressions following existing MLflow fil
988997
- `status = 'active'`
989998
- `status IN ('active', 'deprecated')`
990999
- `has_access_bindings = true` (server-level only; return only governed servers that currently have at least one approved direct-access binding and in the future, gateway acces bindings)
991-
- `tools.name = 'web_search'` (version-level; return versions that declare a tool with the given name)
1000+
- `tools.name = 'web_search'` (server-level and version-level; at server-level, matches against the resolved latest version)
1001+
- `transport_types = 'stdio'` (server-level and version-level; at server-level, matches against the resolved latest version)
1002+
- `transport_type = 'streamable-http'` (binding-level; filter access bindings by transport type)
9921003
- `tags.team = 'platform'`
9931004

9941005
`search_mcp_servers()` is the catalog-discovery API across governed MCP servers and always returns attached access binding summaries on each `MCPServer` result. `search_mcp_access_bindings()` lists approved direct-access bindings across the workspace, and `search_mcp_access_bindings(server_name=...)` narrows that same API to a specific governed server. The `has_access_bindings = true` filter is available for callers that only want directly usable MCP servers.
@@ -1024,7 +1035,7 @@ def create_mcp_server(
10241035
*,
10251036
name: str,
10261037
description: str | None = None,
1027-
icon: str | None = None,
1038+
icons: list[dict] | None = None,
10281039
) -> MCPServer: ...
10291040

10301041
def get_mcp_server(*, name: str) -> MCPServer: ...
@@ -1042,7 +1053,7 @@ def update_mcp_server(
10421053
name: str,
10431054
display_name: str | None = None,
10441055
description: str | None = None,
1045-
icon: str | None = None,
1056+
icons: list[dict] | None = None,
10461057
latest_version: str | None = None,
10471058
) -> MCPServer: ...
10481059

@@ -1098,6 +1109,8 @@ def get_mcp_access_binding(*, server_name: str, binding_id: int) -> MCPAccessBin
10981109
def search_mcp_access_bindings(
10991110
*,
11001111
server_name: str | None = None,
1112+
server_version: str | None = None,
1113+
server_alias: str | None = None,
11011114
filter_string: str | None = None,
11021115
max_results: int = 100,
11031116
order_by: list[str] | None = None,
@@ -1140,7 +1153,7 @@ bindings = mlflow.genai.search_mcp_access_bindings()
11401153

11411154
### server_json validation
11421155

1143-
The `server_json` field in `CreateMCPServerVersionRequest` uses a typed Pydantic model (`ServerJSONPayload`) mirroring the upstream [server.json schema](https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json), with `extra="allow"` for forward compatibility. FastAPI validates the payload automatically at request time — no separate validation step needed.
1156+
The `server_json` field in `CreateMCPServerVersionRequest` uses a typed Pydantic model (`ServerJSONPayload`) mirroring the upstream [server.json schema](https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json), with `extra="allow"` for forward compatibility. FastAPI validates the payload automatically at request time — no separate validation step needed.
11441157

11451158
**Required fields:**
11461159
- `name` (string) — extracted as the server identifier
@@ -1179,6 +1192,8 @@ The detail view shows the server's metadata, versions list, aliases, direct acce
11791192

11801193
**Tools display and filtering**: The version detail page displays the declared tools for that version — tool name, description, and input schema. In the registry listing, users can filter servers by tool name (e.g., `tools.name = 'web_search'`) to discover servers that provide a specific capability. This supports the tool catalog use case: platform engineers and end users can search across governed servers to find which ones offer the tools they need.
11811194

1195+
**Environment variables**: The version detail page can also surface the `packages[].environmentVariables[]` from `server_json`, including `isRequired` and `isSecret` flags. This helps users understand what configuration a server needs before they can use it.
1196+
11821197
As a possible future UI integration, the registry detail view could expose a `Deploy` action that publishes a governed MCP server to MLflow MCP Gateway by creating or updating a gateway-owned deployment record against the same underlying version and alias model, so users experience registry and gateway as one workflow rather than two separate products.
11831198

11841199
### Trace linking

0 commit comments

Comments
 (0)