Open
Description
Check Existing Issues
- I have searched the existing issues and discussions.
- I am using the latest version of mcpo.
mcpo Version
v0.0.16
Open WebUI Version (if applicable)
No response
Operating System
macOS Sequioa
Browser (if applicable)
No response
Confirmation
- I have read and followed all instructions in
README.md
. - I am using the latest version of both MCPO and Open WebUI.
- I have included the browser console logs.
- I have included the Docker container logs.
- I have listed steps to reproduce the bug in detail.
Expected Behavior
For a MCP server returning with stdio and using multiple types in any of the fields I expect the mcpo server to start it without issues
Actual Behavior
mcpo crashes (logs are attached below)
Steps to Reproduce
server.py
from fastmcp import FastMCP
from pydantic import BaseModel
class Nested(BaseModel):
pass
mcp: FastMCP = FastMCP("MCPO Demo")
@mcp.tool
def say(
param: Nested | None
) -> str:
return "hello"
if __name__ == "__main__":
mcp.run()
config.json
{
"mcpServers": {
"test-mcp-server": {
"command": "uv"
"args": ["run", "server.py"]
}
}
}
Running mcpo uvx mcpo --config ./config.json
Logs & Screenshots
Starting MCP OpenAPI Proxy with config file: ./config.json
2025-07-03 15:34:26,696 - INFO - Starting MCPO Server...
2025-07-03 15:34:26,696 - INFO - Name: MCP OpenAPI Proxy
2025-07-03 15:34:26,696 - INFO - Version: 1.0
2025-07-03 15:34:26,696 - INFO - Description: Automatically generated API from MCP Tool Schemas
2025-07-03 15:34:26,696 - INFO - Hostname: Umars-Macbook-Pro
2025-07-03 15:34:26,696 - INFO - Port: 8000
2025-07-03 15:34:26,696 - INFO - API Key: Not Provided
2025-07-03 15:34:26,696 - INFO - CORS Allowed Origins: ['*']
2025-07-03 15:34:26,696 - INFO - Path Prefix: /
2025-07-03 15:34:26,696 - INFO - Loading MCP server configurations from: ./config.json
2025-07-03 15:34:26,697 - INFO - Configured MCP Servers:
2025-07-03 15:34:26,697 - INFO - Configuring Stdio MCP Server 'test-mcp-server' with command: uv with args ['run', 'server.py']
2025-07-03 15:34:26,697 - INFO - Uvicorn server starting...
INFO: Started server process [56250]
INFO: Waiting for application startup.
[07/03/25 15:34:27] INFO Starting MCP server 'MCPO Demo' with transport 'stdio' server.py:1327
ERROR: + Exception Group Traceback (most recent call last):
| File "/Users/umar/.cache/uv/archive-v0/B1gFudSbrmPgt_9yFPxLN/lib/python3.13/site-packages/starlette/routing.py", line 692, in lifespan
| async with self.lifespan_context(app) as maybe_state:
| ~~~~~~~~~~~~~~~~~~~~~^^^^^
| File "/opt/homebrew/Cellar/[email protected]/3.13.3_1/Frameworks/Python.framework/Versions/3.13/lib/python3.13/contextlib.py", line 214, in __aenter__
| return await anext(self.gen)
| ^^^^^^^^^^^^^^^^^^^^^
| File "/Users/umar/.cache/uv/archive-v0/B1gFudSbrmPgt_9yFPxLN/lib/python3.13/site-packages/mcpo/main.py", line 101, in lifespan
| await stack.enter_async_context(
| route.app.router.lifespan_context(route.app), # noqa
| )
| File "/opt/homebrew/Cellar/[email protected]/3.13.3_1/Frameworks/Python.framework/Versions/3.13/lib/python3.13/contextlib.py", line 668, in enter_async_context
| result = await _enter(cm)
| ^^^^^^^^^^^^^^^^
| File "/opt/homebrew/Cellar/[email protected]/3.13.3_1/Frameworks/Python.framework/Versions/3.13/lib/python3.13/contextlib.py", line 214, in __aenter__
| return await anext(self.gen)
| ^^^^^^^^^^^^^^^^^^^^^
| File "/Users/umar/.cache/uv/archive-v0/B1gFudSbrmPgt_9yFPxLN/lib/python3.13/site-packages/mcpo/main.py", line 113, in lifespan
| async with stdio_client(server_params) as (reader, writer):
| ~~~~~~~~~~~~^^^^^^^^^^^^^^^
| File "/opt/homebrew/Cellar/[email protected]/3.13.3_1/Frameworks/Python.framework/Versions/3.13/lib/python3.13/contextlib.py", line 235, in __aexit__
| await self.gen.athrow(value)
| File "/Users/umar/.cache/uv/archive-v0/B1gFudSbrmPgt_9yFPxLN/lib/python3.13/site-packages/mcp/client/stdio/__init__.py", line 173, in stdio_client
| anyio.create_task_group() as tg,
| ~~~~~~~~~~~~~~~~~~~~~~~^^
| File "/Users/umar/.cache/uv/archive-v0/B1gFudSbrmPgt_9yFPxLN/lib/python3.13/site-packages/anyio/_backends/_asyncio.py", line 772, in __aexit__
| raise BaseExceptionGroup(
| "unhandled errors in a TaskGroup", self._exceptions
| ) from None
| ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
+-+---------------- 1 ----------------
| Exception Group Traceback (most recent call last):
| File "/Users/umar/.cache/uv/archive-v0/B1gFudSbrmPgt_9yFPxLN/lib/python3.13/site-packages/mcp/client/stdio/__init__.py", line 179, in stdio_client
| yield read_stream, write_stream
| File "/Users/umar/.cache/uv/archive-v0/B1gFudSbrmPgt_9yFPxLN/lib/python3.13/site-packages/mcpo/main.py", line 114, in lifespan
| async with ClientSession(reader, writer) as session:
| ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
| File "/Users/umar/.cache/uv/archive-v0/B1gFudSbrmPgt_9yFPxLN/lib/python3.13/site-packages/mcp/shared/session.py", line 218, in __aexit__
| return await self._task_group.__aexit__(exc_type, exc_val, exc_tb)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| File "/Users/umar/.cache/uv/archive-v0/B1gFudSbrmPgt_9yFPxLN/lib/python3.13/site-packages/anyio/_backends/_asyncio.py", line 772, in __aexit__
| raise BaseExceptionGroup(
| "unhandled errors in a TaskGroup", self._exceptions
| ) from None
| ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
+-+---------------- 1 ----------------
| Traceback (most recent call last):
| File "/Users/umar/.cache/uv/archive-v0/B1gFudSbrmPgt_9yFPxLN/lib/python3.13/site-packages/mcpo/main.py", line 116, in lifespan
| await create_dynamic_endpoints(app, api_dependency=api_dependency)
| File "/Users/umar/.cache/uv/archive-v0/B1gFudSbrmPgt_9yFPxLN/lib/python3.13/site-packages/mcpo/main.py", line 52, in create_dynamic_endpoints
| form_model_fields = get_model_fields(
| f"{endpoint_name}_form_model",
| ...<2 lines>...
| inputSchema.get("$defs", {}),
| )
| File "/Users/umar/.cache/uv/archive-v0/B1gFudSbrmPgt_9yFPxLN/lib/python3.13/site-packages/mcpo/utils/main.py", line 236, in get_model_fields
| python_type_hint, pydantic_field_info = _process_schema_property(
| ~~~~~~~~~~~~~~~~~~~~~~~~^
| _model_cache,
| ^^^^^^^^^^^^^
| ...<4 lines>...
| schema_defs,
| ^^^^^^^^^^^^
| )
| ^
| File "/Users/umar/.cache/uv/archive-v0/B1gFudSbrmPgt_9yFPxLN/lib/python3.13/site-packages/mcpo/utils/main.py", line 126, in _process_schema_property
| type_hint, _ = _process_schema_property(
| ~~~~~~~~~~~~~~~~~~~~~~~~^
| _model_cache,
| ^^^^^^^^^^^^^
| ...<3 lines>...
| False,
| ^^^^^^
| )
| ^
| File "/Users/umar/.cache/uv/archive-v0/B1gFudSbrmPgt_9yFPxLN/lib/python3.13/site-packages/mcpo/utils/main.py", line 112, in _process_schema_property
| assert ref in schema_defs, "Custom field not found"
| ^^^^^^^^^^^^^^^^^^
| TypeError: argument of type 'NoneType' is not iterable
+------------------------------------
ERROR: Application startup failed. Exiting.
Additional Information
None
type is not special here. Adding any other type along with a class that extends pydantic's BaseModel fails. Just having a single pydantic type does not cause any issues.