Skip to content

[Epic]: Fully implement MCP 2025-06-18 compliance across all endpoints #1285

@crivetimihai

Description

@crivetimihai

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
33 [x] Extra fields 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 to isError (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-552
  • mcpgateway/services/resource_service.py line 386
  • mcpgateway/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:

  1. Implement properly: Add cursor to resource_service, track cursor state, return nextCursor when more results exist
  2. 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 {} for completion/* 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 {} for sampling/* 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 method list_roots (line 3471) - LEGACY NAME
  • ❌ Spec requires method name roots/list (per client/roots.mdx line 57)
  • /rpc returns {} for roots/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:

  1. Implement: Create ElicitationHandler similar to SamplingHandler
  2. 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 {} for logging/* 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 ALL notifications/* (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]] to annotations: Optional[ToolAnnotations]
  • ToolRead in schemas.py line 1380: Change annotations: Optional[Dict[str, Any]] to annotations: 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, optional total, optional message
  • 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:

  1. Extract progressToken from request params._meta
  2. Implement progress tracking in long-running operations (tools/call, resources/read, etc.)
  3. Send notifications/progress at appropriate intervals
  4. 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:

  1. Track active requests with their IDs
  2. When cancellation received, signal operation to stop
  3. Clean up resources
  4. 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:

  1. After successful initialization, extract negotiated protocol version
  2. Include MCP-Protocol-Version header on all subsequent HTTP requests
  3. 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.

⚠️ DO NOT REMOVE EXTRA FIELDS: These fields are used by REST API, Admin UI, and extended functionality. Removing them would break the REST API while providing no MCP compliance benefit.


📋 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

Metadata

Metadata

Assignees

Labels

mcp-2025-06-18MCP 2025-06-18 protocol specificationmcp-protocolAlignment with MCP protocol or specification

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions