-
Notifications
You must be signed in to change notification settings - Fork 385
Description
Bug Description
ContextForge strips the outputSchema field from MCP tools during gateway discovery, breaking structured content support and MCP spec compliance.
MCP Specification Context
Per the MCP Tools Specification (2025-06-18):
Tools may also provide an output schema for validation of structured results. If an output schema is provided:
- Servers MUST provide structured results that conform to this schema.
- Clients SHOULD validate structured results against this schema.
The outputSchema field is part of the official Tool definition:
interface Tool {
name: string;
description?: string;
inputSchema: JSONSchema;
outputSchema?: JSONSchema; // ← This is being stripped
annotations?: ToolAnnotations;
}How FastMCP Uses outputSchema
Modern MCP servers built with FastMCP automatically generate outputSchema from function return type annotations:
@server.tool()
def get_weather_data(location: str) -> WeatherData:
"""Returns structured weather data."""
return WeatherData(temperature=22.5, conditions="Partly cloudy", humidity=65)FastMCP generates:
{
"name": "get_weather_data",
"inputSchema": { "type": "object", "properties": { "location": { "type": "string" } } },
"outputSchema": {
"type": "object",
"properties": {
"temperature": { "type": "number" },
"conditions": { "type": "string" },
"humidity": { "type": "number" }
},
"required": ["temperature", "conditions", "humidity"]
}
}The tool then returns BOTH unstructured and structured content:
{
"content": [{ "type": "text", "text": "{\"temperature\": 22.5, ...}" }],
"structuredContent": { "temperature": 22.5, "conditions": "Partly cloudy", "humidity": 65 }
}The Bug in ContextForge
Location: mcpgateway/services/gateway_service.py:2866-2868
response = await session.list_tools()
tools = response.tools
tools = [tool.model_dump(by_alias=True, exclude_none=True) for tool in tools] # Has outputSchema ✅
tools = [ToolCreate.model_validate(tool) for tool in tools] # Strips outputSchema ❌Root Cause
The ToolCreate, ToolUpdate, and ToolRead schemas in mcpgateway/schemas.py do NOT include an output_schema field:
class ToolCreate(BaseModel):
name: str
description: Optional[str]
input_schema: Optional[Dict[str, Any]] = Field(..., alias="inputSchema")
# ❌ NO output_schema field
annotations: Optional[Dict[str, Any]]When ToolCreate.model_validate(tool) is called, Pydantic silently drops the outputSchema field because it's not defined in the schema.
Similarly, the database Tool model has no output_schema column to store this data.
Impact
- Loss of Type Information: Clients and LLMs cannot understand what structured data format tools return
- No Response Validation: Cannot validate tool responses against expected schemas
- Breaks MCP Spec Compliance: Tools advertising
outputSchemalose that metadata when proxied through ContextForge - Poor Developer Experience: No autocomplete, type hints, or documentation for tool outputs
- Breaks Structured Workflows: Modern AI agents rely on typed tool outputs for reliable automation
Evidence from MCP SDK
The official MCP Python SDK types show outputSchema is a standard field:
# From mcp/types.py
class Tool(BaseMetadata):
"""Definition for a tool the client can call."""
description: str | None = None
inputSchema: dict[str, Any]
outputSchema: dict[str, Any] | None = None # ← Official field
icons: list[Icon] | None = None
annotations: ToolAnnotations | None = NoneExpected Behavior
ContextForge should:
- Preserve
outputSchemawhen discovering tools from MCP servers - Store
outputSchemain the database alongsideinput_schema - Return
outputSchemain API responses (/tools,/servers/{id}/tools, etc.) - Pass through
structuredContentfrom tool invocations - Optionally validate
structuredContentagainstoutputSchema(per spec: "Clients SHOULD validate")
Proposed Solution
Add output_schema field (with alias outputSchema) to:
-
Database Model (
mcpgateway/db.py):class Tool(Base): # ... input_schema = Column(JSON, nullable=False) output_schema = Column(JSON, nullable=True) # ← Add this
-
Pydantic Schemas (
mcpgateway/schemas.py):class ToolCreate(BaseModel): input_schema: Dict[str, Any] = Field(..., alias="inputSchema") output_schema: Optional[Dict[str, Any]] = Field(None, alias="outputSchema") # ← Add class ToolRead(BaseModel): input_schema: Dict[str, Any] output_schema: Optional[Dict[str, Any]] # ← Add class ToolUpdate(BaseModel): input_schema: Optional[Dict[str, Any]] output_schema: Optional[Dict[str, Any]] # ← Add
-
Database Migration (Alembic):
ALTER TABLE tools ADD COLUMN output_schema JSON NULL;
Reproduction Steps
-
Create an MCP server with FastMCP that uses typed return annotations:
@server.tool() def structured_tool(x: int) -> dict[str, int]: return {"result": x * 2}
-
Register the server as a gateway in ContextForge
-
Query
GET /toolsorGET /servers/{id}/tools -
Observe: The returned tool definition has
inputSchemabut NOToutputSchema
Additional Context
- This affects all modern FastMCP servers (FastMCP automatically generates
outputSchema) - The MCP 2025-06-18 specification explicitly includes
outputSchema - Structured outputs are becoming standard in AI workflows for reliability
- ContextForge already stores
input_schemain the database, so addingoutput_schemais a natural extension
Labels
bug: Core functionality (MCP spec compliance) is brokenenhancement: Adds missing feature supportmcp-protocol: Related to MCP specification adherencepython: Python codebase changes required
Priority
High - This breaks a core MCP specification feature that modern servers rely on.