inspector
inspector copied to clipboard
add support for JSON Schema $defs and definitions
refs: https://github.com/modelcontextprotocol/java-sdk/pull/146
When add $defs in the response, the inspector will not work
When pydantic creates the json schema for inputSchema all of the parameters are referred to by $defs. So if one is creating a tool with input parameters that are derived from a pydantic BaseModel, the parameters won't appear correctly in the inspector.
Thanks for this report. I have Pydantic models in my servers too, so I checked on this.
The Inspector will work for both $defs and definitions, but the dynamic form cannot render the input fields (a known limitation at the moment), so only JSON input will work.
@He-Pin By "will not work", do you mean the form, or something else?
@uppersaranac Can you provide an input schema which will not work? I can use it as a test case.
To demonstrate, here's a server with a tool inputSchema using both $defs and definitions, for two different objects. Omitting either object, or required properties, will generate an error (the server does validation).
tool `inputSchema`
{
"tools": [
{
"name": "mixed_ref_tool",
"description": "A tool with a schema that uses both $defs and definitions.",
"inputSchema": {
"type": "object",
"properties": {
"user": {
"$ref": "#/$defs/User"
},
"address": {
"$ref": "#/definitions/Address"
}
},
"required": [
"user",
"address"
],
"$defs": {
"User": {
"type": "object",
"title": "User",
"properties": {
"name": {
"type": "string",
"title": "Name"
}
},
"required": [
"name"
]
}
},
"definitions": {
"Address": {
"type": "object",
"title": "Address",
"properties": {
"street": {
"type": "string",
"title": "Street"
},
"city": {
"type": "string",
"title": "City"
}
},
"required": [
"street",
"city"
]
}
}
}
}
]
}
Server `ref_defs_definitions_server.py`
# ref_defs_definitions_server.py
import asyncio
from typing import Any
import jsonschema
import mcp.server.stdio
import mcp.types as types
from mcp.server.lowlevel import NotificationOptions, Server
from mcp.server.models import InitializationOptions
input_schema = {
"type": "object",
"properties": {
"user": {"$ref": "#/$defs/User"},
"address": {"$ref": "#/definitions/Address"},
},
"required": ["user", "address"],
"$defs": {
"User": {
"type": "object",
"title": "User",
"properties": {
"name": {"type": "string", "title": "Name"},
},
"required": ["name"],
},
},
"definitions": {
"Address": {
"type": "object",
"title": "Address",
"properties": {
"street": {"type": "string", "title": "Street"},
"city": {"type": "string", "title": "City"},
},
"required": ["street", "city"],
},
},
}
# Create the low-level server and tool
server = Server("RefDefsDefinitionsServer")
mixed_tool = types.Tool(
name="mixed_ref_tool",
description="A tool with a schema that uses both $defs and definitions.",
inputSchema=input_schema,
)
@server.list_tools()
async def handle_list_tools() -> list[types.Tool]:
return [mixed_tool]
@server.call_tool()
async def handle_call_tool(name: str, arguments: dict[str, Any]) -> list[types.TextContent]:
if name == "mixed_ref_tool":
try:
jsonschema.validate(instance=arguments, schema=mixed_tool.inputSchema)
return [types.TextContent(type="text", text=f"Successfully called tool with arguments: {arguments}")]
except jsonschema.exceptions.ValidationError as e:
return [types.TextContent(type="text", text=f"Argument validation error: {e.message}")]
raise ValueError(f"Unknown tool: {name}")
async def main():
"""Run the server over stdio."""
async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
await server.run(
read_stream,
write_stream,
InitializationOptions(
server_name="RefDefsDefinitionsServer",
server_version="0.1.0",
capabilities=server.get_capabilities(
notification_options=NotificationOptions(),
experimental_capabilities={},
),
),
)
if __name__ == "__main__":
asyncio.run(main())
Run it using uv:
$ mkdir refs-example && cd refs-example
$ cat - > ref_defs_definitions_server.py
# paste
$ uv init --bare . && uv add 'mcp[cli]'
$ npx @modelcontextprotocol/inspector uv run python ref_defs_definitions_server.py
Sorry, redid my approach and eliminated the code that created the $defs. Should have pasted it in this ticket when I had it.