agentic-community / agentic-community/mcp-gateway-registry

Standardize MCP Server Schema to Official Anthropic Format

オープン
#264 コメント 0 件 リアクション 1 件 担当者 1 名 @aarora79 が担当を希望しています GitHub で見る
enhancement
主要言語
Python
スター
911
フォーク
234
平均マージ
1日 11時間
マージ済み PR(30日)
62

説明

## Summary

Adopt the official Anthropic MCP Server Schema as our unified server configuration format, extending it with gateway-specific fields via the `_meta` extension point. Use a **one-time migration** approach with no backward compatibility.

## Motivation

- **Interoperability**: Enable importing servers directly from the official MCP registry (https://registry.modelcontextprotocol.io)
- **Single Schema**: Eliminate dual maintenance by merging our homegrown schema into the MCP standard
- **UI Enhancement**: Display rich "Server Cards" with all MCP metadata when users click the info icon
- **Ecosystem Alignment**: Follow the industry standard for MCP server definitions
- **Simplicity**: One-time migration means no adapter layer or dual-format code

## Proposed Changes

### Schema Migration

Migrate from our current format:
```json
{
"server_name": "Current Time API",
"description": "...",
"path": "/currenttime/",
"proxy_pass_url": "http://server:8000/",
"tool_list": [...]
}
```

To unified MCP-extended format:
```json
{
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
"name": "io.mcpgateway/currenttime",
"description": "...",
"version": "1.0.0",
"remotes": [{"type": "streamable-http", "url": "http://server:8000/"}],
"_meta": {
"io.mcpgateway/registry": {
"path": "/currenttime/",
"authType": "none",
"toolList": [...]
}
}
}
```

### Key Design Decisions

1. **Single File Per Server**: All metadata and tools in one JSON file
2. **Standard Fields at Top Level**: `name`, `description`, `version`, `remotes`, `repository`
3. **Gateway Extensions in `_meta`**: Our custom fields (`path`, `authType`, `toolList`, etc.) under `_meta.io.mcpgateway/registry`
4. **No Fields Lost**: Complete mapping of all existing fields (see design doc)

## Implementation Plan

### Phase 1: Create Migration Script
- [ ] Create `registry/utils/schema_migrator.py`
- [ ] Test with dry-run on existing servers

### Phase 2: Run One-Time Migration
- [ ] Run migration with backups
- [ ] Verify all files converted correctly

### Phase 3: Update Backend
- [ ] Create Pydantic models for new schema
- [ ] Update `server_service.py` to use new models
- [ ] Remove old schema handling code

### Phase 4: Update Frontend
- [ ] Create TypeScript types for new schema
- [ ] Update components to use new field names
- [ ] Create Server Card modal component

### Phase 5: Add Import Feature
- [ ] Create `registry/utils/registry_fetcher.py`
- [ ] Add CLI command for importing servers

## Field Mapping Summary

| Current | New Location |
|---------|--------------|
| `server_name` | `name` |
| `proxy_pass_url` | `remotes[0].url` |
| `supported_transports` | `remotes[].type` |
| `path`, `auth_type`, `tags`, etc. | `_meta.io.mcpgateway/registry.*` |
| `tool_list` | `_meta.io.mcpgateway/registry.toolList` |

New fields from MCP: `version`, `repository`, `websiteUrl`, `packages`

## References

- MCP Server Schema: https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json
- Official Registry API: https://registry.modelcontextprotocol.io/v0/servers

## Labels

enhancement, schema, mcp-standard

---

# Full Design Document

Click to expand full design document

## MCP Server Schema Standardization Design

*Created: 2025-11-30*
*Updated: 2025-11-30 - Revised to single unified schema*

### Overview

Standardize MCP server configurations by extending the official Anthropic MCP Server Schema with our gateway-specific fields. This enables:
1. Import of servers from the official MCP registry
2. Display of "Server Cards" in the UI
3. Single unified schema - no dual maintenance

### Design Principle: Single Unified Schema

Instead of maintaining two separate schemas (cards + tools), we extend the official MCP schema by:
1. Using standard MCP fields for metadata
2. Adding `tool_list` inside `_meta` for our runtime tool definitions
3. One file per server - everything in one place

### Current vs Proposed Schema

#### Current (Homegrown)
```json
{
"server_name": "Current Time API",
"description": "A simple API that returns the current server time",
"path": "/currenttime/",
"proxy_pass_url": "http://currenttime-server:8000/",
"auth_type": "none",
"tags": [],
"num_tools": 1,
"num_stars": 0,
"is_python": true,
"license": "MIT-0",
"supported_transports": ["streamable-http"],
"tool_list": [...]
}
```

#### Proposed (Unified MCP-Extended)
```json
{
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
"name": "io.mcpgateway/currenttime",
"description": "A simple API that returns the current server time",
"version": "1.0.0",
"repository": {
"url": "https://github.com/agentic-community/mcp-gateway-registry",
"source": "github",
"subfolder": "mcp-servers/currenttime"
},
"remotes": [
{
"type": "streamable-http",
"url": "http://currenttime-server:8000/"
}
],
"_meta": {
"io.mcpgateway/registry": {
"path": "/currenttime/",
"authType": "none",
"tags": ["time", "utility"],
"numStars": 0,
"isPython": true,
"license": "MIT-0",
"source": "internal",
"toolList": [
{
"name": "current_time_by_timezone",
"description": "Get the current time for a specified timezone",
"inputSchema": {
"type": "object",
"properties": {
"tz_name": {
"type": "string",
"default": "America/New_York",
"description": "Name of the timezone"
}
},
"required": ["tz_name"]
}
}
]
}
}
}
```

### Unified Schema Structure

#### Standard MCP Fields (Top Level)

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `$schema` | string | No | Schema URL for validation |
| `name` | string | Yes | Reverse-DNS format: `namespace/server-name` |
| `description` | string | Yes | Human-readable description (max 100 chars) |
| `version` | string | Yes | Semantic version |
| `repository` | object | No | Source code repository info |
| `websiteUrl` | string | No | Homepage/documentation URL |
| `remotes` | array | No | Remote transport endpoints |
| `packages` | array | No | Package distribution info |

#### Gateway Extension Fields (`_meta.io.mcpgateway/registry`)

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `path` | string | Yes | Gateway routing path (e.g., `/currenttime/`) |
| `authType` | string | Yes | Authentication: `none`, `api_key`, `oauth` |
| `authProvider` | string | No | OAuth provider: `keycloak`, etc. |
| `tags` | array | No | Categorization tags |
| `numStars` | number | No | Popularity metric |
| `isPython` | boolean | No | Python implementation flag |
| `license` | string | No | License identifier |
| `source` | string | Yes | Origin: `internal`, `anthropic-registry`, `smithery` |
| `importedAt` | string | No | ISO timestamp when imported |
| `headers` | array | No | Custom headers for proxy |
| `toolList` | array | No | Runtime tool definitions |

#### Tool Definition Schema (`toolList[]`)

```json
{
"name": "tool_name",
"description": "What the tool does",
"inputSchema": {
"type": "object",
"properties": {...},
"required": [...]
}
}
```

This follows MCP's tool schema format, making it compatible with the protocol.

### Field Mapping (Current -> New)

| Current Field | New Location | Notes |
|---------------|-------------|-------|
| `server_name` | `name` | Convert to reverse-DNS |
| `description` | `description` | Direct mapping |
| `path` | `_meta...path` | Same value |
| `proxy_pass_url` | `remotes[0].url` | Move to standard field |
| `supported_transports` | `remotes[].type` | Move to standard field |
| `auth_type` | `_meta...authType` | Same value |
| `auth_provider` | `_meta...authProvider` | Same value |
| `tags` | `_meta...tags` | Same value |
| `num_tools` | Computed | Count of `toolList` |
| `num_stars` | `_meta...numStars` | Same value |
| `is_python` | `_meta...isPython` | Same value |
| `license` | `_meta...license` | Same value |
| `tool_list` | `_meta...toolList` | Restructure to MCP format |
| `headers` | `_meta...headers` | Same value |
| N/A | `version` | NEW - add semantic version |
| N/A | `repository` | NEW - add repo info |

### Example: Complete Server Definitions

#### Internal Server (Self-Hosted)

```json
{
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
"name": "io.mcpgateway/currenttime",
"description": "A simple API that returns the current server time in various formats",
"version": "1.0.0",
"repository": {
"url": "https://github.com/agentic-community/mcp-gateway-registry",
"source": "github",
"subfolder": "mcp-servers/currenttime"
},
"packages": [
{
"registryType": "oci",
"identifier": "015469603702.dkr.ecr.us-east-1.amazonaws.com/mcp-gateway-currenttime:latest",
"version": "1.0.0",
"transport": {"type": "stdio"}
}
],
"remotes": [
{
"type": "streamable-http",
"url": "http://currenttime-server:8000/"
}
],
"_meta": {
"io.mcpgateway/registry": {
"path": "/currenttime/",
"authType": "none",
"tags": ["time", "utility", "internal"],
"numStars": 0,
"isPython": true,
"license": "MIT-0",
"source": "internal",
"toolList": [
{
"name": "current_time_by_timezone",
"description": "Get the current time for a specified timezone using the timeapi.io API",
"inputSchema": {
"type": "object",
"properties": {
"tz_name": {
"type": "string",
"default": "America/New_York",
"description": "Name of the timezone for which to find out the current time"
}
},
"required": ["tz_name"]
}
}
]
}
}
}
```

#### External Server (Imported from Anthropic Registry)

```json
{
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
"name": "ai.exa/exa",
"description": "Fast, intelligent web search and web crawling",
"version": "3.1.0",
"repository": {
"url": "https://github.com/exa-labs/exa-mcp-server",
"source": "github"
},
"remotes": [
{
"type": "streamable-http",
"url": "https://mcp.exa.ai/mcp"
}
],
"_meta": {
"io.modelcontextprotocol.registry/official": {
"status": "active",
"publishedAt": "2025-11-20T18:00:55.174988Z",
"isLatest": true
},
"io.mcpgateway/registry": {
"path": "/exa/",
"authType": "none",
"tags": ["search", "web", "anthropic-registry"],
"source": "anthropic-registry",
"importedAt": "2025-11-30T16:00:00Z",
"toolList": []
}
}
}
```

#### External Server with OAuth (Smithery)

```json
{
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
"name": "ai.smithery/github",
"description": "Access the GitHub API, enabling file operations, repository management, and search",
"version": "1.0.0",
"remotes": [
{
"type": "streamable-http",
"url": "https://server.smithery.ai/@smithery-ai/github/mcp"
}
],
"_meta": {
"io.mcpgateway/registry": {
"path": "/smithery-github/",
"authType": "oauth",
"authProvider": "keycloak",
"tags": ["github", "smithery", "oauth"],
"source": "smithery",
"importedAt": "2025-11-30T16:00:00Z",
"headers": [
{"Authorization": "Bearer {access_token}"}
],
"toolList": []
}
}
}
```

### Import Flow

#### From Official MCP Registry

```
1. Fetch: GET https://registry.modelcontextprotocol.io/v0/servers/{name}
2. Extract server object from response
3. Add _meta.io.mcpgateway/registry:
- path: Generate from name (e.g., ai.exa/exa -> /exa/)
- authType: "none" (default)
- tags: [name parts, "anthropic-registry"]
- source: "anthropic-registry"
- importedAt: Current ISO timestamp
- toolList: [] (discover at runtime via /tools endpoint)
4. Save to servers/{sanitized-name}.json
```

#### Tool Discovery

For remote servers, tools are discovered dynamically:
1. On first access or explicit refresh
2. Call server's `/tools` endpoint (MCP protocol)
3. Update `toolList` in the server JSON
4. Cache with TTL for performance

### File Structure

```
/home/ubuntu/mcp-gateway/servers/
io.mcpgateway-currenttime.json # Internal servers
io.mcpgateway-mcpgw.json
io.mcpgateway-realserverfaketools.json
ai.exa-exa.json # Imported from Anthropic
ai.smithery-github.json # Imported from Smithery
io.cloudflare-docs.json # External remote
server_state.json # Runtime state (unchanged)
```

### Gateway Runtime Behavior

The gateway reads the unified schema and extracts what it needs:

```python
def load_server_config(server_json: dict) -> GatewayConfig:
meta = server_json.get("_meta", {}).get("io.mcpgateway/registry", {})

return GatewayConfig(
name=server_json["name"],
description=server_json["description"],
path=meta["path"],
proxy_url=server_json["remotes"][0]["url"],
transport=server_json["remotes"][0]["type"],
auth_type=meta.get("authType", "none"),
auth_provider=meta.get("authProvider"),
headers=meta.get("headers", []),
tools=meta.get("toolList", [])
)
```

### UI Server Card Display

When info icon clicked, display the full MCP card:

```
+----------------------------------------------------------+
| ai.exa/exa v3.1.0 |
+----------------------------------------------------------+
| Fast, intelligent web search and web crawling |
+----------------------------------------------------------+
| Repository: github.com/exa-labs/exa-mcp-server |
| Transport: streamable-http |
| Endpoint: https://mcp.exa.ai/mcp |
| Auth: none |
+----------------------------------------------------------+
| Tools (3): |
| - exa_search: Search the web intelligently |
| - exa_crawl: Crawl web pages |
| - exa_code: Code context tool |
+----------------------------------------------------------+
| Tags: search, web, anthropic-registry |
| Source: Anthropic Registry |
| Imported: 2025-11-30 |
+----------------------------------------------------------+
```

### Migration Script

```python
def migrate_server(old_json: dict) -> dict:
"""Convert old schema to unified MCP schema."""

# Extract remote URL and transport
proxy_url = old_json.get("proxy_pass_url", "")
transports = old_json.get("supported_transports", ["streamable-http"])

# Convert tool_list to MCP format
tool_list = []
for tool in old_json.get("tool_list", []):
tool_list.append({
"name": tool["name"],
"description": tool.get("parsed_description", {}).get("main", ""),
"inputSchema": tool.get("schema", {})
})

# Generate name in reverse-DNS format
server_name = old_json.get("server_name", "unknown")
name = f"io.mcpgateway/{_slugify(server_name)}"

return {
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
"name": name,
"description": old_json.get("description", "")[:100],
"version": "1.0.0",
"remotes": [
{
"type": transports[0] if transports else "streamable-http",
"url": proxy_url
}
],
"_meta": {
"io.mcpgateway/registry": {
"path": old_json.get("path", "/"),
"authType": old_json.get("auth_type", "none"),
"authProvider": old_json.get("auth_provider"),
"tags": old_json.get("tags", []),
"numStars": old_json.get("num_stars", 0),
"isPython": old_json.get("is_python", False),
"license": old_json.get("license", "N/A"),
"source": "migrated",
"headers": old_json.get("headers", []),
"toolList": tool_list
}
}
}
```

### Benefits of Unified Approach

1. **Single Source of Truth**: One file per server, no sync issues
2. **Standard Compliance**: Valid MCP schema, can be shared with ecosystem
3. **Extensible**: `_meta` allows our custom fields without breaking standard
4. **Import Ready**: Can directly import from official registry
5. **UI Ready**: All info needed for server card in one place
6. **No Dual Maintenance**: One schema to maintain and validate

### Field Audit: No Fields Lost

#### Current Schema Fields (All Servers)

| Current Field | New Location | Status |
|---------------|--------------|--------|
| `server_name` | `name` | MAPPED (renamed to reverse-DNS) |
| `description` | `description` | MAPPED (direct) |
| `path` | `_meta...path` | MAPPED |
| `proxy_pass_url` | `remotes[0].url` | MAPPED |
| `supported_transports` | `remotes[].type` | MAPPED |
| `auth_type` | `_meta...authType` | MAPPED |
| `auth_provider` | `_meta...authProvider` | MAPPED |
| `tags` | `_meta...tags` | MAPPED |
| `num_tools` | Computed from `toolList.length` | COMPUTED |
| `num_stars` | `_meta...numStars` | MAPPED |
| `is_python` | `_meta...isPython` | MAPPED |
| `license` | `_meta...license` | MAPPED |
| `headers` | `_meta...headers` | MAPPED |
| `tool_list` | `_meta...toolList` | MAPPED |
| `tool_list[].name` | `toolList[].name` | MAPPED |
| `tool_list[].parsed_description` | `toolList[].parsedDescription` | MAPPED |
| `tool_list[].parsed_description.main` | `toolList[].parsedDescription.main` | MAPPED |
| `tool_list[].parsed_description.args` | `toolList[].parsedDescription.args` | MAPPED |
| `tool_list[].parsed_description.returns` | `toolList[].parsedDescription.returns` | MAPPED |
| `tool_list[].parsed_description.raises` | `toolList[].parsedDescription.raises` | MAPPED |
| `tool_list[].schema` | `toolList[].inputSchema` | MAPPED (renamed) |

#### Official MCP Schema Fields

| MCP Field | Status | Notes |
|-----------|--------|-------|
| `$schema` | NEW | Schema URL for validation |
| `name` | MAPPED | From `server_name` |
| `description` | MAPPED | Direct |
| `version` | NEW | Semantic version (required by MCP) |
| `repository` | NEW | Source code info |
| `repository.url` | NEW | Repo URL |
| `repository.source` | NEW | "github", "gitlab", etc. |
| `repository.id` | NEW | Repo ID (optional) |
| `repository.subfolder` | NEW | Monorepo path (optional) |
| `websiteUrl` | NEW | Homepage/docs URL |
| `remotes` | MAPPED | From `proxy_pass_url` + `supported_transports` |
| `remotes[].type` | MAPPED | Transport type |
| `remotes[].url` | MAPPED | Endpoint URL |
| `remotes[].headers` | NEW | Per-transport headers (optional) |
| `packages` | NEW | Package distribution (optional) |
| `packages[].registryType` | NEW | npm, pypi, oci, etc. |
| `packages[].identifier` | NEW | Package name/URL |
| `packages[].version` | NEW | Package version |
| `packages[].transport` | NEW | Transport config |
| `packages[].environmentVariables` | NEW | Env vars for package |
| `_meta` | EXTENSION | Vendor extensions |

#### Complete Unified Schema

```json
{
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
"name": "io.mcpgateway/server-name",
"description": "Server description",
"version": "1.0.0",
"repository": {
"url": "https://github.com/org/repo",
"source": "github",
"subfolder": "path/to/server"
},
"websiteUrl": "https://example.com",
"remotes": [
{
"type": "streamable-http",
"url": "http://server:8000/"
}
],
"packages": [
{
"registryType": "oci",
"identifier": "registry/image:tag",
"version": "1.0.0",
"transport": {"type": "stdio"}
}
],
"_meta": {
"io.mcpgateway/registry": {
"path": "/server-path/",
"authType": "none",
"authProvider": null,
"tags": ["tag1", "tag2"],
"numStars": 0,
"isPython": true,
"license": "MIT",
"source": "internal",
"importedAt": "2025-11-30T00:00:00Z",
"headers": [
{"Authorization": "Bearer {token}"}
],
"toolList": [
{
"name": "tool_name",
"parsedDescription": {
"main": "What the tool does",
"args": "arg1: description, arg2: description",
"returns": "Return value description",
"raises": "Exception conditions"
},
"inputSchema": {
"type": "object",
"properties": {
"param1": {
"type": "string",
"description": "Parameter description"
}
},
"required": ["param1"]
}
}
]
}
}
}
```

#### Verification Checklist

Current fields accounted for:
- [x] `server_name` -> `name`
- [x] `description` -> `description`
- [x] `path` -> `_meta...path`
- [x] `proxy_pass_url` -> `remotes[0].url`
- [x] `supported_transports` -> `remotes[].type`
- [x] `auth_type` -> `_meta...authType`
- [x] `auth_provider` -> `_meta...authProvider`
- [x] `tags` -> `_meta...tags`
- [x] `num_tools` -> computed from `toolList.length`
- [x] `num_stars` -> `_meta...numStars`
- [x] `is_python` -> `_meta...isPython`
- [x] `license` -> `_meta...license`
- [x] `headers` -> `_meta...headers`
- [x] `tool_list` -> `_meta...toolList`
- [x] `tool_list[].name` -> `toolList[].name`
- [x] `tool_list[].parsed_description` -> `toolList[].parsedDescription`
- [x] `tool_list[].parsed_description.main` -> preserved
- [x] `tool_list[].parsed_description.args` -> preserved
- [x] `tool_list[].parsed_description.returns` -> preserved
- [x] `tool_list[].parsed_description.raises` -> preserved
- [x] `tool_list[].schema` -> `toolList[].inputSchema`

New fields from MCP standard:
- [x] `$schema` - schema validation URL
- [x] `version` - semantic version (REQUIRED)
- [x] `repository` - source code metadata
- [x] `websiteUrl` - homepage/docs
- [x] `packages` - distribution info
- [x] `_meta` - extension point

### Open Questions

1. **Name Prefix**: Use `io.mcpgateway/` for internal servers?
- **Decision**: Yes, follows reverse-DNS convention

2. **Tool Discovery Timing**: When to fetch tools for imported servers?
- **Decision**: On-demand with caching (TTL 1 hour)

3. **Version Updates**: How to handle version bumps for imported servers?
- **Decision**: Manual refresh or periodic sync job

4. **Backward Compatibility**: Support old format during transition?
- **Decision**: No - one-time migration, no backward compatibility needed

コントリビューションガイド

コントリビューションガイドを開く

評価

この issue はまだ評価されていません。

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。