Skip to content

issue: MCPO Crashes on Startup when Using Union Types (e.g., param: PydanticModel | None) in Tool Definitions #189

Open
@Gleek

Description

@Gleek

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions