-
Notifications
You must be signed in to change notification settings - Fork 341
Description
MCP Specification 2025-06-18 Remaining tasks:
Spec Version: MCP 2025-06-18
⚠️ IMPORTANT: Scope and Critical Architectural Issue
MCP Spec Requirement
Per MCP spec (transports.mdx lines 70-72): "The server MUST provide a single HTTP endpoint path"
Per MCP spec (transports.mdx lines 86-87): "Every JSON-RPC message sent from the client MUST be a new HTTP POST request to the MCP endpoint"
Current Architecture
The implementation has:
- ✅ A primary
/rpc
JSON-RPC endpoint (spec-compliant) - ❌ ALSO separate REST endpoints:
/notifications
,/completion/complete
,/sampling/createMessage
,/logging/setLevel
⚠️ Many methods work via separate endpoints but return{}
when called via/rpc
What This Report Covers
This report focuses on MCP JSON-RPC compliance via the /rpc
endpoint:
- Methods that should work via
/rpc
but don't (even though they exist elsewhere) - Type definitions required by the MCP spec
- Protocol features missing entirely
Key Finding: Most handlers/services EXIST (in separate REST endpoints) but are NOT connected to the /rpc
endpoint as the MCP spec requires. Clients using standard MCP libraries will call /rpc
and get {}
responses, even though the functionality exists at other URLs.
This report does NOT cover:
- REST API extensions beyond MCP spec
- Admin UI endpoints
- Gateway federation endpoints
📊 Summary Table - All Issues
# | Status | Issue | Priority | File(s) | Description |
---|---|---|---|---|---|
1 | [ ] | ImageContent broken | 🔴 Critical | models.py:123-134 | Uses bytes not str ; extends BaseModel; no camelCase conversion |
2 | [ ] | ToolResult broken | 🔴 Critical | models.py:505-514 | Returns is_error not isError ; missing structuredContent |
3 | [ ] | ResourceContent broken | 🔴 Critical | models.py:137-152 | Returns mime_type not mimeType ; wrong structure |
4 | [ ] | resources/subscribe missing | 🔴 Critical | main.py:3480 | Not in JSON-RPC (only REST); capability advertised |
5 | [ ] | Pagination fake | 🔴 Critical | tool/resource/prompt services | Accepts cursor but ignores; never returns nextCursor |
6 | [ ] | AudioContent missing | 🟡 High | models.py (create new) | Entire class missing - no audio support |
7 | [ ] | ResourceLink missing | 🟡 High | models.py (create new) | Entire class missing - can't link resources |
8 | [ ] | EmbeddedResource missing | 🟡 High | models.py (create new) | Entire class missing - can't embed resources |
9 | [ ] | Annotations interface missing | 🟡 High | models.py (create new) | No structured annotations (audience, priority, lastModified) |
10 | [ ] | ToolAnnotations missing | 🟡 High | models.py (create new) | Tool hints as Dict not structured type |
11 | [ ] | TextResourceContents missing | 🟡 High | models.py (create new) | Spec-compliant resource content types missing |
12 | [ ] | BlobResourceContents missing | 🟡 High | models.py (create new) | Spec-compliant resource content types missing |
13 | [ ] | ContentBlock union missing | 🟡 High | models.py (create new) | Union type for prompt/tool content |
14 | [ ] | PromptMessage missing | 🟡 High | models.py (create new) | Distinct from SamplingMessage |
15 | [ ] | _meta missing | 🟠 High | schemas.py + models.py | Missing from 8+ classes (ToolRead, ResourceRead, etc.) |
16 | [ ] | title field missing | 🟠 High | schemas.py | ResourceRead and PromptRead need title |
17 | [ ] | annotations missing | 🟠 High | schemas.py + models.py | ResourceRead and ResourceTemplate need annotations |
18 | [ ] | TextContent needs fix | 🟢 Medium | models.py:91 | Should extend BaseModelWithConfigDict |
19 | [ ] | Completions not advertised | 🟢 Medium | session_registry.py:1244 | Capability implemented but not advertised |
20 | [ ] | JSONContent bug | 🟢 Medium | models.py:112-120 | Uses type="text", field name="text" but type=dict |
21 | [ ] | PromptArgument incomplete | 🟠 High | models.py:364-377 | Missing title and _meta (should extend BaseMetadata) |
22 | [ ] | completion/* not in /rpc | 🔴 Critical | main.py:3543-3544 | Works via /completion/complete but /rpc returns {} |
23 | [ ] | resources/templates/list stub | 🟡 High | main.py:3533-3534 | Returns {} - REST exists but not in JSON-RPC |
24 | [ ] | PROTOCOL_VERSION outdated | 🟡 High | .env.example, config.py | Currently "2025-03-26" should be "2025-06-18" |
25 | [ ] | sampling/* not in /rpc | 🔴 Critical | main.py:3539-3540 | Works via /sampling/createMessage but /rpc returns {} |
26 | [ ] | roots/* wrong method name | 🔴 Critical | main.py:3471,3535 | list_roots works; spec requires roots/list |
27 | [ ] | elicitation/* not implemented | 🔴 Critical | main.py:3541-3542 | No handler/service anywhere - returns {} |
28 | [ ] | logging/* not in /rpc | 🔴 Critical | main.py:3545-3546 | Works via /logging/setLevel but /rpc returns {} |
29 | [ ] | notifications/* not in /rpc | 🔴 Critical | main.py:3537-3538 | Works via /notifications but /rpc returns {} |
30 | [ ] | Progress notifications missing | 🟡 High | N/A | notifications/progress not implemented - spec requires for long-running ops |
31 | [ ] | Cancellation handling incomplete | 🟡 High | main.py:1268-1275 | Receives but doesn't act on cancellation |
32 | [ ] | MCP-Protocol-Version header | 🟢 Medium | N/A | HTTP transport should include header per spec |
[x] | ✅ Not an issue | N/A | Compatible by design - MCP allows extra fields |
Total: 32 implementation items (10 Critical, 12 High, 4 High Fields, 6 Medium)
Note: All issues verified against MCP JSON-RPC endpoint (/rpc
), not REST API. Issue #19 (Extra Fields) is actually compatible by design - MCP spec allows extra fields.
✅ What Works Correctly (API Verified)
Response Schemas (schemas.py) - These extend BaseModelWithConfigDict
and work perfectly:
- ✅ ToolRead: Returns
inputSchema
,createdAt
,updatedAt
,originalName
(camelCase) ✓ - ✅ ResourceRead: Returns
mimeType
,createdAt
, etc. (camelCase) ✓ - ✅ PromptRead: All fields convert to camelCase properly ✓
Why they work: Extend BaseModelWithConfigDict
with alias_generator=to_camel_case
Note: The issues below are for protocol types in models.py that extend plain BaseModel
🔴 CRITICAL - Breaks Compliance
1. ImageContent Type Issues
File: mcpgateway/models.py
lines 123-134
Problems:
# WRONG (line 123-134):
class ImageContent(BaseModel): # ❌ Should extend BaseModelWithConfigDict
type: Literal["image"]
data: bytes # ❌ Should be str (base64-encoded for JSON)
mime_type: str # ❌ Won't convert to mimeType (no alias_generator)
# ❌ Missing: annotations field
# ❌ Missing: _meta field
Should Be:
class ImageContent(BaseModelWithConfigDict): # ✅ For camelCase conversion
type: Literal["image"]
data: str # ✅ Base64-encoded string for JSON compatibility
mimeType: str = Field(..., alias="mimeType") # ✅ Explicit alias
annotations: Optional[Annotations] = None
_meta: Optional[Dict[str, Any]] = Field(None, alias="_meta")
Impact:
- Cannot be JSON-serialized (bytes not JSON-compatible)
- Field name won't convert to camelCase (extends plain BaseModel)
2. ToolResult Base Class and Missing Fields
File: mcpgateway/models.py
lines 505-514
Problems:
# WRONG (line 505-514):
class ToolResult(BaseModel): # ❌ Should extend BaseModelWithConfigDict
content: List[ContentType]
is_error: bool = False # ❌ Won't convert to isError
# ❌ Missing: structuredContent field
# ❌ Missing: _meta field
Should Be:
class CallToolResult(BaseModelWithConfigDict): # ✅ Rename + extend correct base
content: List[ContentBlock] # ✅ Use ContentBlock union
structuredContent: Optional[Dict[str, Any]] = Field(None, alias="structuredContent")
isError: Optional[bool] = Field(None, alias="isError") # ✅ Explicit alias
_meta: Optional[Dict[str, Any]] = Field(None, alias="_meta")
model_config = ConfigDict(populate_by_name=True)
Impact:
is_error
won't convert toisError
(extends plain BaseModel)- Tools cannot return structured JSON output (missing structuredContent)
✅ API Verified: Tested tools/call
response - confirmed returns "is_error": false
not "isError": false
3. ResourceContent Base Class Issue
File: mcpgateway/models.py
lines 137-152
Problems:
# WRONG (line 137-152):
class ResourceContent(BaseModel): # ❌ Should extend BaseModelWithConfigDict
type: Literal["resource"]
uri: str
mime_type: Optional[str] # ❌ Won't convert to mimeType
text: Optional[str]
blob: Optional[str]
Should Be (replace with spec-compliant types):
class ResourceContents(BaseModelWithConfigDict):
"""Base class for resource contents."""
uri: str
mimeType: Optional[str] = Field(None, alias="mimeType")
_meta: Optional[Dict[str, Any]] = Field(None, alias="_meta")
class TextResourceContents(ResourceContents):
"""Text contents of a resource."""
text: str
class BlobResourceContents(ResourceContents):
"""Binary contents of a resource."""
blob: str # Base64-encoded binary data
Impact: Field names won't convert to camelCase; not spec-compliant structure
✅ API Verified: Tested model serialization - confirmed mime_type
stays as snake_case, not converted to mimeType
4. resources/subscribe Not in JSON-RPC
File: mcpgateway/main.py
(RPC endpoint around line 3480)
Problem:
- REST endpoint exists at line 2713:
POST /resources/subscribe/{uri:path}
- Service method exists:
resource_service.py:866
- Capability advertises:
resources: {subscribe: true}
(session_registry.py:1246) - BUT: Not exposed in
/rpc
JSON-RPC endpoint (lines 3456-3547)
Fix: Add to main.py around line 3490:
elif method == "resources/subscribe":
uri = params.get("uri")
if not uri:
raise JSONRPCError(-32602, "Missing resource URI", params)
result = await resource_service.subscribe_resource(db, uri)
elif method == "resources/unsubscribe":
uri = params.get("uri")
if not uri:
raise JSONRPCError(-32602, "Missing resource URI", params)
result = await resource_service.unsubscribe_resource(db, uri)
Impact: CRITICAL - Capability advertised but not available via JSON-RPC protocol
5. Pagination - Not Implemented
Files:
mcpgateway/services/tool_service.py
line 519-552mcpgateway/services/resource_service.py
line 386mcpgateway/services/prompt_service.py
line 399-442
Problem:
- tool_service.list_tools: Accepts
cursor
parameter but ignores it (line 552: "Placeholder for pagination; ignore for now") - prompt_service.list_prompts: Accepts
cursor
parameter but ignores it (line 442: "can be implemented in the future") - resource_service.list_resources: Does NOT accept
cursor
parameter at all (line 386 - no cursor in signature) - All services: Never return
nextCursor
in responses
Fix Options:
- Implement properly: Add cursor to resource_service, track cursor state, return nextCursor when more results exist
- Remove support: Remove cursor parameter from tool/prompt services if not implementing
Current State: Inconsistent - tools/prompts accept cursor but ignore it; resources don't support it at all
6. completion/* Methods Not Accessible via /rpc
Files: mcpgateway/main.py
lines 1281 (works), 3543-3544 (broken)
Problem:
- ✅
completion_service
fully functional - ✅ Works via separate endpoint:
POST /completion/complete
(line 1281) - ❌
/rpc
endpoint returns{}
forcompletion/*
methods (line 3543) - ❌ Violates MCP spec: All methods MUST work via single MCP endpoint
⚠️ Capability NOT advertised (session_registry.py:1248 - completions missing)
Current Code in /rpc:
elif method.startswith("completion/"):
result = {} # ❌ No implementation in RPC
Separate REST Endpoint (line 1281):
@protocol_router.post("/completion/complete")
async def handle_completion(request: Request, db: Session = Depends(get_db), ...):
body = await request.json()
return await completion_service.handle_completion(db, body) # ✅ Works here
Impact: CRITICAL - Spec violation. Service exists and works but not accessible via /rpc
. Also not advertised in capabilities.
7. resources/templates/list Not Implemented in JSON-RPC
File: mcpgateway/main.py
line 3533-3534
Problem:
- REST endpoint likely exists for templates
- JSON-RPC method returns empty
{}
- No actual implementation in RPC handler
Current Code:
elif method == "resources/templates/list":
result = {} # ❌ Stub implementation
Impact: Clients calling this via MCP JSON-RPC get empty response
Fix: Either implement properly or ensure this method isn't used by MCP clients
8. sampling/* Methods Not Accessible via /rpc
Files: mcpgateway/main.py
lines 1299 (works), 3539-3540 (broken)
Problem:
- ✅
SamplingHandler
exists and is fully functional - ✅ Works via separate endpoint:
POST /sampling/createMessage
(line 1299) - ❌
/rpc
endpoint returns{}
forsampling/*
methods (line 3539) - ❌ Violates MCP spec: All methods MUST work via single MCP endpoint
Current Code in /rpc:
elif method.startswith("sampling/"):
result = {} # ❌ Should call sampling_handler
Separate REST Endpoint (line 1299):
@protocol_router.post("/sampling/createMessage")
async def handle_sampling(request: Request, db: Session = Depends(get_db), ...):
body = await request.json()
return await sampling_handler.create_message(db, body) # ✅ Works here
Fix for /rpc:
elif method == "sampling/createMessage":
result = await sampling_handler.create_message(db, params)
Impact: CRITICAL - Spec violation. Standard MCP clients calling /rpc
get empty {}
; only custom clients using /sampling/createMessage
work
9. roots/* Wrong Method Name (Legacy vs Spec)
File: mcpgateway/main.py
lines 3471 (legacy), 3535 (spec method)
Problem:
- ✅
root_service
fully functional - ✅ Works via
/rpc
with methodlist_roots
(line 3471) - LEGACY NAME - ❌ Spec requires method name
roots/list
(per client/roots.mdx line 57) - ❌
/rpc
returns{}
forroots/list
(line 3535)
Current Code:
# Line 3471 - WORKS but wrong name
elif method == "list_roots": # ❌ Legacy name, not spec-compliant
roots = await root_service.list_roots()
result = {"roots": [r.model_dump(by_alias=True, exclude_none=True) for r in roots]}
# Line 3535 - Spec name but returns {}
elif method.startswith("roots/"): # ❌ Should handle "roots/list" specifically
result = {}
Fix:
# Keep legacy for backwards compatibility
elif method == "list_roots":
roots = await root_service.list_roots()
result = {"roots": [r.model_dump(by_alias=True, exclude_none=True) for r in roots]}
# Add spec-compliant name
elif method == "roots/list": # ✅ Spec-compliant
roots = await root_service.list_roots()
result = {"roots": [r.model_dump(by_alias=True, exclude_none=True) for r in roots]}
Impact: CRITICAL - Standard MCP clients using roots/list
get {}
; only legacy clients using list_roots
work
Note: Should also uncomment and advertise roots
capability in ServerCapabilities (session_registry.py:1249 is commented out)
10. elicitation/* Methods Not Implemented
File: mcpgateway/main.py
lines 3541-3542
Problem:
- No handler or service exists for elicitation
- Spec defines:
elicitation/create
as new client feature in 2025-06-18 - Allows servers to request user input with JSON schema validation
Current Code:
elif method.startswith("elicitation/"):
result = {} # ❌ No implementation at all
Impact: CRITICAL - New spec feature completely missing
Fix Options:
- Implement: Create ElicitationHandler similar to SamplingHandler
- Document as unsupported: If not implementing, ensure capability not advertised
11. logging/* Methods Not Accessible via /rpc
Files: mcpgateway/main.py
lines 3751 (works), 3545-3546 (broken)
Problem:
- ✅
logging_service
fully functional - ✅ Works via separate endpoint:
POST /logging/setLevel
(line 3751) - ❌
/rpc
endpoint returns{}
forlogging/*
methods (line 3545) - ❌ Violates MCP spec: All methods MUST work via single MCP endpoint
- Capability is advertised (session_registry.py:1248)
Current Code in /rpc:
elif method.startswith("logging/"):
result = {} # ❌ No implementation in RPC
Separate REST Endpoint (line 3751):
@utility_router.post("/logging/setLevel")
async def set_log_level(...): # ✅ Works here
Impact: CRITICAL - Spec violation. Standard MCP clients get {}
; capability advertised but not functional via /rpc
Note: Spec also defines server-to-client notifications/message
for log messages (currently sent via SSE)
12. notifications/* Not Accessible via /rpc
Files: mcpgateway/main.py
lines 1253 (works), 3537-3538 (broken), session_registry.py:1381-1393 (sends)
Problem:
- ✅ Handles notifications via separate endpoint:
POST /notifications
(line 1253) - ✅ Handles:
notifications/initialized
,notifications/cancelled
,notifications/message
- ✅ SERVER SENDS list_changed notifications via SSE (session_registry.py:1381-1393)
- ❌
/rpc
endpoint returns{}
for ALLnotifications/*
(line 3537) - ❌ Violates MCP spec: Should work via single MCP endpoint
- ❌ Still missing:
notifications/progress
Current Code in /rpc:
elif method.startswith("notifications/"):
result = {} # ❌ Should handle client notifications
Separate REST Endpoint (line 1253):
@protocol_router.post("/notifications")
async def handle_notification(request: Request, ...):
body = await request.json()
if body.get("method") == "notifications/initialized": # ✅ Works
...
elif body.get("method") == "notifications/cancelled": # ✅ Works
...
elif body.get("method") == "notifications/message": # ✅ Works
...
Server-to-Client Notifications (session_registry.py:1381-1393):
# These ARE sent to clients via SSE after initialize
notifications = [
"tools/list_changed", # ✅ Sent
"resources/list_changed", # ✅ Sent
"prompts/list_changed", # ✅ Sent
]
Impact: CRITICAL - Notifications work but violate single-endpoint requirement. Still missing notifications/progress
entirely
🟡 HIGH PRIORITY - Missing Type Definitions
13. AudioContent Class
File: Create in mcpgateway/models.py
Spec: schema.ts lines 1140-1164
class AudioContent(BaseModelWithConfigDict):
"""Audio content for a message."""
type: Literal["audio"]
data: str # Base64-encoded audio
mimeType: str = Field(..., alias="mimeType")
annotations: Optional[Annotations] = None
_meta: Optional[Dict[str, Any]] = Field(None, alias="_meta")
model_config = ConfigDict(populate_by_name=True)
Impact: Cannot support audio in sampling requests/responses
14. ResourceLink Class
File: Create in mcpgateway/models.py
Spec: schema.ts lines 721-723 (extends Resource)
class ResourceLink(Resource):
"""A resource link included in prompts or tool results.
Note: Inherits uri, name, title, description, mimeType, size, icons,
annotations, _meta from Resource.
"""
type: Literal["resource_link"]
Impact: Cannot link to resources in prompts/tool results
15. EmbeddedResource Class
File: Create in mcpgateway/models.py
Spec: schema.ts lines 731-744
class EmbeddedResource(BaseModelWithConfigDict):
"""The contents of a resource, embedded into a prompt or tool call result."""
type: Literal["resource"]
resource: Union[TextResourceContents, BlobResourceContents]
annotations: Optional[Annotations] = None
_meta: Optional[Dict[str, Any]] = Field(None, alias="_meta")
model_config = ConfigDict(populate_by_name=True)
Impact: Cannot embed resource content in prompts/tool results
16. Annotations Interface
File: Create in mcpgateway/models.py
Spec: schema.ts lines 1047-1077
class Annotations(BaseModel):
"""Optional annotations for client rendering hints.
Attributes:
audience: Who the content is intended for (user, assistant, or both)
priority: Importance from 0 (least) to 1 (most important)
lastModified: ISO 8601 timestamp of last modification
"""
audience: Optional[List[Role]] = None
priority: Optional[float] = Field(None, ge=0, le=1)
lastModified: Optional[str] = Field(None, alias="lastModified")
model_config = ConfigDict(populate_by_name=True)
Then Add To:
- TextContent (line 91+)
- ImageContent (when fixed)
- AudioContent (when created)
- EmbeddedResource (when created)
- ResourceRead in schemas.py (line 1866+)
- ResourceTemplate in models.py (line 536+)
Note: ResourceLink inherits annotations from Resource. Prompt does NOT have annotations per spec.
17. ToolAnnotations Interface
File: Create in mcpgateway/models.py
Spec: schema.ts lines 838-880
class ToolAnnotations(BaseModel):
"""Tool behavior hints for clients.
Attributes:
title: Human-readable display name
readOnlyHint: True if tool doesn't modify environment
destructiveHint: True if tool performs destructive updates
idempotentHint: True if calling repeatedly has no additional effect
openWorldHint: True if tool interacts with open world of external entities
"""
title: Optional[str] = None
readOnlyHint: Optional[bool] = Field(None, alias="readOnlyHint")
destructiveHint: Optional[bool] = Field(None, alias="destructiveHint")
idempotentHint: Optional[bool] = Field(None, alias="idempotentHint")
openWorldHint: Optional[bool] = Field(None, alias="openWorldHint")
model_config = ConfigDict(populate_by_name=True)
Then Update:
- Tool model in models.py line 490: Change
annotations: Optional[Dict[str, Any]]
toannotations: Optional[ToolAnnotations]
- ToolRead in schemas.py line 1380: Change
annotations: Optional[Dict[str, Any]]
toannotations: Optional[ToolAnnotations]
18. ContentBlock Union Type
File: mcpgateway/models.py
Current (line 155):
ContentType = Union[TextContent, JSONContent, ImageContent, ResourceContent]
Add This:
ContentBlock = Union[
TextContent,
ImageContent,
AudioContent,
ResourceLink,
EmbeddedResource
]
Spec: schema.ts lines 1079-1084
Impact: Need this for proper PromptMessage and CallToolResult content typing
19. PromptMessage Type
File: Create in mcpgateway/models.py
Spec: schema.ts lines 711-714
class PromptMessage(BaseModelWithConfigDict):
"""Message in a prompt (can include resources)."""
role: Role
content: ContentBlock # Broader than SamplingMessage
model_config = ConfigDict(populate_by_name=True)
Difference from SamplingMessage:
- PromptMessage: Can include ResourceLink, EmbeddedResource
- SamplingMessage: Only TextContent, ImageContent, AudioContent
🟡 HIGH PRIORITY - Protocol Utilities
30. Progress Notifications Not Implemented
Files: mcpgateway/main.py
, all service files
Problem:
- Spec requires
notifications/progress
for long-running operations - Clients can include
progressToken
in request_meta
- Servers MUST send progress updates with:
progressToken
,progress
, optionaltotal
, optionalmessage
- Currently: No support for progress tokens or notifications
Per Spec (basic/utilities/progress.mdx):
{
"jsonrpc": "2.0",
"method": "notifications/progress",
"params": {
"progressToken": "abc123",
"progress": 50,
"total": 100,
"message": "Processing..."
}
}
Fix Required:
- Extract
progressToken
from requestparams._meta
- Implement progress tracking in long-running operations (tools/call, resources/read, etc.)
- Send
notifications/progress
at appropriate intervals - Progress value MUST increase with each notification
Impact: Clients can't track long-running operation progress; may timeout unnecessarily
31. Cancellation Handling Incomplete
File: mcpgateway/main.py
lines 1268-1275
Problem:
- Receives
notifications/cancelled
but doesn't act on it - Should stop processing, free resources, not send response
- Currently just logs the cancellation
Current Code:
elif body.get("method") == "notifications/cancelled":
request_id = body.get("params", {}).get("requestId")
reason = body.get("params", {}).get("reason", "No reason provided")
logger.info(f"Client cancelled request {request_id}: {reason}")
await logging_service.notify(f"Request {request_id} cancelled: {reason}", LogLevel.INFO)
# ❌ Should actually cancel the operation
Per Spec (basic/utilities/cancellation.mdx):
- Receivers SHOULD stop processing cancelled requests
- Receivers SHOULD free associated resources
- Receivers SHOULD NOT send response for cancelled request
Fix Required:
- Track active requests with their IDs
- When cancellation received, signal operation to stop
- Clean up resources
- Don't send response
Impact: Operations continue running after cancellation, wasting resources
🟠 HIGH PRIORITY - Missing _meta Fields
20. Add _meta to Response Schemas
Files to Update in mcpgateway/schemas.py
:
# ToolRead (after line 1428):
_meta: Optional[Dict[str, Any]] = Field(None, alias="_meta")
# ResourceRead (after line 1909):
_meta: Optional[Dict[str, Any]] = Field(None, alias="_meta")
# PromptRead (after line 2410):
_meta: Optional[Dict[str, Any]] = Field(None, alias="_meta")
Files to Update in mcpgateway/models.py
:
# TextContent (after line 109):
_meta: Optional[Dict[str, Any]] = Field(None, alias="_meta")
# ImageContent (when fixed - after annotations):
_meta: Optional[Dict[str, Any]] = Field(None, alias="_meta")
# Resource (after line 533):
_meta: Optional[Dict[str, Any]] = Field(None, alias="_meta")
# ResourceTemplate (after line 549):
_meta: Optional[Dict[str, Any]] = Field(None, alias="_meta")
# Prompt (after line 389):
_meta: Optional[Dict[str, Any]] = Field(None, alias="_meta")
# Root (after line 641):
_meta: Optional[Dict[str, Any]] = Field(None, alias="_meta")
Purpose: Protocol extension point for custom metadata
🟠 HIGH PRIORITY - Missing Optional Fields
21. Add title Field
Spec: BaseMetadata interface (schema.ts lines 294-309)
# In schemas.py ResourceRead (after line 1879):
title: Optional[str] = None
# In schemas.py PromptRead (after line 2381):
title: Optional[str] = None
Note: Tool has displayName
(line 1391) which serves same purpose, just different name
22. Add annotations to Resource
Files: mcpgateway/schemas.py
Add to ResourceRead (after line 1882):
annotations: Optional[Annotations] = None
Add to ResourceTemplate in models.py (after line 549):
annotations: Optional[Annotations] = None
Note: Prompt does NOT need annotations per spec (schema.ts:670-684). Only Resource and ResourceTemplate require annotations.
24. Update PROTOCOL_VERSION Configuration
Files: .env.example
and mcpgateway/config.py
Problem:
- Current value:
PROTOCOL_VERSION = "2025-03-26"
- Should be:
PROTOCOL_VERSION = "2025-06-18"
- Code is implementing 2025-06-18 spec features, but config claims older version
Fix:
In .env.example
:
# Change from:
PROTOCOL_VERSION=2025-03-26
# To:
PROTOCOL_VERSION=2025-06-18
In mcpgateway/config.py
:
# Change from:
PROTOCOL_VERSION: str = Field(default="2025-03-26", ...)
# To:
PROTOCOL_VERSION: str = Field(default="2025-06-18", ...)
Impact: Ensures clients receive correct protocol version during initialization handshake
🟢 MEDIUM PRIORITY - Improvements
18. Fix TextContent Base Class
File: mcpgateway/models.py
line 91
Current:
class TextContent(BaseModel): # ❌ Should extend BaseModelWithConfigDict
Fix:
class TextContent(BaseModelWithConfigDict):
Impact: Future-proofing for snake_case field name conversion
21. PromptArgument Missing BaseMetadata Fields
File: mcpgateway/models.py
lines 364-377
Current:
class PromptArgument(BaseModel): # ❌ Should extend BaseMetadata or have its fields
name: str
description: Optional[str] = None
required: bool = False
Spec: schema.ts lines 689-698 - PromptArgument extends BaseMetadata
Should Be:
class PromptArgument(BaseModelWithConfigDict):
name: str
title: Optional[str] = None # ✅ From BaseMetadata
description: Optional[str] = None
required: bool = False
_meta: Optional[Dict[str, Any]] = Field(None, alias="_meta") # ✅ From BaseMetadata
model_config = ConfigDict(populate_by_name=True)
Impact: PromptArgument responses missing optional metadata fields per spec
19. Advertise Completions Capability
File: mcpgateway/cache/session_registry.py
line 1244-1250
Current:
ServerCapabilities(
prompts={"listChanged": True},
resources={"subscribe": True, "listChanged": True},
tools={"listChanged": True},
logging={},
# ❌ Missing: completions
)
Add:
ServerCapabilities(
prompts={"listChanged": True},
resources={"subscribe": True, "listChanged": True},
tools={"listChanged": True},
logging={},
completions={}, # ✅ ADD THIS
)
Note: Feature fully implemented (CompleteRequest/CompleteResult exist), just not advertised
20. JSONContent Type Issue (Potential Bug)
File: mcpgateway/models.py
lines 112-120
Current:
class JSONContent(BaseModel):
type: Literal["text"] # ❌ Uses "text" not "json"?
text: dict # ❌ Field name is "text" but type is dict?
Investigation Needed: This appears non-standard. Verify if this is intentional or a bug.
32. MCP-Protocol-Version HTTP Header Missing
Files: All HTTP transport handlers
Problem:
- Spec requires
MCP-Protocol-Version: <protocol-version>
header on HTTP requests after initialization - Per spec (basic/lifecycle.mdx line 140-144): "If using HTTP, the client MUST include the MCP-Protocol-Version: HTTP header on all subsequent requests"
- Currently: No implementation of this header requirement
Per Spec:
MCP-Protocol-Version: 2025-06-18
Fix Required:
- After successful initialization, extract negotiated protocol version
- Include
MCP-Protocol-Version
header on all subsequent HTTP requests - Verify header is sent for SSE transport connections
Impact: MEDIUM - HTTP transport doesn't fully comply with spec, but most implementations tolerate this
Note: This applies to client-side HTTP requests. For server-side, should validate header if present.
✅ NOT AN ISSUE - Extra Fields in Responses
Current Behavior:
- ToolRead returns ~25 fields (spec defines 7)
- ResourceRead returns ~26 fields (spec defines 8)
- PromptRead returns ~25 fields (spec defines 5)
Extra Fields Include: id, createdAt, updatedAt, metrics, gatewayId, teamId, tags, executionCount, etc.
Why This is OK:
- ✅ MCP SDK uses
ConfigDict(extra='allow')
BY DESIGN - explicitly allows extra fields - ✅ Forward-compatible: Clients ignore unknown fields (JSON schema standard)
- ✅ Spec compliance: MCP protocol doesn't forbid additional fields
- ✅ Benefit: Allows extending protocol for REST API while maintaining MCP compatibility
Impact:
- MCP clients work perfectly (tested with official SDK)
- Extra fields useful for admin UI and REST API
- No compliance violation
Recommendation: Keep as-is. This is an intentional design choice that maintains MCP compliance while extending functionality.
📋 Complete Implementation Checklist
Critical (Must Fix for Compliance)
- Fix ImageContent (bytes → str, extend BaseModelWithConfigDict, add mimeType alias, annotations, _meta)
- Fix ToolResult (extend BaseModelWithConfigDict, add isError alias, structuredContent, _meta)
- Fix ResourceContent (extend BaseModelWithConfigDict) OR replace with TextResourceContents/BlobResourceContents
- Add resources/subscribe to JSON-RPC endpoint
- Add resources/unsubscribe to JSON-RPC endpoint
- Fix pagination (implement properly OR remove cursor parameter from tools/prompts, add to resources)
- Add completion/complete to /rpc endpoint (service exists at line 1281, just needs routing)
- Add sampling/createMessage to /rpc endpoint (handler exists at line 1299, just needs routing)
- Add roots/list to /rpc endpoint (works as list_roots at line 3471, add spec-compliant name)
- Add logging/setLevel to /rpc endpoint (works at line 3751, just needs routing to /rpc)
- Add notifications/* to /rpc endpoint (works at line 1253, just needs routing to /rpc)
- Implement elicitation/create handler OR document as unsupported (currently not implemented anywhere)
- Implement notifications/progress for long-running operations (not implemented anywhere)
High Priority - Type Definitions
- Create AudioContent class
- Create ResourceLink class (extends Resource)
- Create EmbeddedResource class
- Create TextResourceContents class
- Create BlobResourceContents class
- Create Annotations interface
- Create ToolAnnotations interface
- Create ContentBlock union type
- Create PromptMessage type
High Priority - Missing Fields
- Add _meta to ToolRead (schemas.py)
- Add _meta to ResourceRead (schemas.py)
- Add _meta to PromptRead (schemas.py)
- Add _meta to TextContent (models.py)
- Add _meta to Resource (models.py)
- Add _meta to ResourceTemplate (models.py)
- Add _meta to Prompt (models.py)
- Add _meta to Root (models.py)
- Add _meta to PromptArgument (models.py)
- Add title to ResourceRead (schemas.py)
- Add title to PromptRead (schemas.py)
- Add title to PromptArgument (models.py)
- Add annotations to ResourceRead (schemas.py)
- Add annotations to ResourceTemplate (models.py)
- Implement resources/templates/list in JSON-RPC OR document as unused
- Update PROTOCOL_VERSION from "2025-03-26" to "2025-06-18" (.env.example and config.py)
- Implement progress notification support in all long-running operations
- Implement proper cancellation handling (stop operation, free resources, no response)
Medium Priority
- Fix TextContent base class (extend BaseModelWithConfigDict)
- Update Tool.annotations to use ToolAnnotations type (models.py:490)
- Update ToolRead.annotations to use ToolAnnotations type (schemas.py:1380)
- Advertise completions capability (session_registry.py)
- Investigate JSONContent type issue (models.py:112-120)
- Implement MCP-Protocol-Version HTTP header for HTTP transports
- Advertise roots capability in ServerCapabilities if implementing (session_registry.py:1249)
Not Required (Already Compatible)
-
Extra fields in responses- COMPATIBLE BY DESIGN - MCP spec allows this
Quick Reference: File Locations
Implementation Files
- Protocol types:
mcpgateway/models.py
- Response schemas:
mcpgateway/schemas.py
- RPC handlers:
mcpgateway/main.py
lines 3414-3600 - Services:
mcpgateway/services/tool_service.py
mcpgateway/services/resource_service.py
mcpgateway/services/prompt_service.py
- Capabilities:
mcpgateway/cache/session_registry.py
lines 1242-1253
Key Line Numbers
- ImageContent: models.py:123-134
- TextContent: models.py:91-109
- ToolResult: models.py:505-514
- ResourceContent: models.py:137-152
- ToolRead: schemas.py:1357-1428
- ResourceRead: schemas.py:1866-1909
- PromptRead: schemas.py:2370-2410
- RPC endpoint: main.py:3414
- Capability declaration: session_registry.py:1242-1253