Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions mcphero/adapters/openai.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,23 @@ class MCPToolAdapterOpenAI(
"""

@staticmethod
def _to_openai_tool(tool: MCPToolDefinition) -> ChatCompletionToolParam:
def _to_openai_tool(
tool: MCPToolDefinition, *, strict: bool = True
) -> ChatCompletionToolParam:
"""Convert MCPToolDefinition to OpenAI format."""
if strict:
return {
"type": "function",
"function": {
"name": tool.name,
"description": tool.description,
"strict": True,
"parameters": {
**tool.input_schema,
"additionalProperties": False,
},
},
}
return {
"type": "function",
"function": {
Expand All @@ -49,11 +64,11 @@ def _to_openai_tool(tool: MCPToolDefinition) -> ChatCompletionToolParam:
}

async def get_tool_definitions(
self, *, parallel: bool = True
self, *, parallel: bool = True, strict: bool = True
) -> list[ChatCompletionToolParam]:
"""Fetch tools and return OpenAI-compatible definitions."""
tools = await self.discover_tools(parallel=parallel)
return [self._to_openai_tool(t) for t in tools]
return [self._to_openai_tool(t, strict=strict) for t in tools]

async def _execute_single_tool_call(
self, tool_call: ChatCompletionMessageToolCall, *, return_errors: bool
Expand Down
20 changes: 19 additions & 1 deletion tests/test_openai_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ async def test_converts_mcp_tools_to_openai_format(
tools[0]["function"]["description"]
== "Get the current weather for a location"
)
assert tools[0]["function"]["parameters"] == sample_mcp_tools[0]["inputSchema"]
assert tools[0]["function"]["strict"] is True
assert tools[0]["function"]["parameters"] == {
**sample_mcp_tools[0]["inputSchema"],
"additionalProperties": False,
}

@respx.mock
async def test_handles_missing_input_schema(
Expand All @@ -51,8 +55,22 @@ async def test_handles_missing_input_schema(
assert tools[0]["function"]["parameters"] == {
"type": "object",
"properties": {},
"additionalProperties": False,
}

@respx.mock
async def test_strict_false_omits_strict_fields(self, base_url, sample_mcp_tools):
respx.post(base_url).mock(
return_value=httpx.Response(200, json=_tools_response(sample_mcp_tools))
)

adapter = MCPToolAdapterOpenAI(MCPServerConfig(url=base_url, init_mode="none"))
tools = await adapter.get_tool_definitions(strict=False)

assert tools[0]["function"]["parameters"] == sample_mcp_tools[0]["inputSchema"]
assert "strict" not in tools[0]["function"]
assert "additionalProperties" not in tools[0]["function"]["parameters"]

@respx.mock
async def test_handles_empty_tools(self, base_url):
respx.post(base_url).mock(
Expand Down