Skip to content

Commit 5c80302

Browse files
committed
changes followig CR
1 parent 198e32e commit 5c80302

File tree

3 files changed

+58
-27
lines changed

3 files changed

+58
-27
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,13 @@ mcp = FastApiMCP(
151151
include_tags=["public"]
152152
)
153153

154+
# Limit maximum allowed length for combined tool name (calculated as server name + operation_id)
155+
# Some vendors throw errors if combined tool names are too long
156+
mcp = FastApiMCP(
157+
app,
158+
max_tool_name_length=25,
159+
)
160+
154161
mcp.mount()
155162
```
156163

fastapi_mcp/server.py

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616

1717
logger = getLogger(__name__)
1818

19-
FULL_TOOL_NAME_MAX_LENGTH = 55
20-
2119

2220
class FastApiMCP:
2321
def __init__(
@@ -33,6 +31,7 @@ def __init__(
3331
exclude_operations: Optional[List[str]] = None,
3432
include_tags: Optional[List[str]] = None,
3533
exclude_tags: Optional[List[str]] = None,
34+
max_tool_name_length: Optional[int] = None,
3635
):
3736
"""
3837
Create an MCP server from a FastAPI app.
@@ -52,6 +51,8 @@ def __init__(
5251
exclude_operations: List of operation IDs to exclude from MCP tools. Cannot be used with include_operations.
5352
include_tags: List of tags to include as MCP tools. Cannot be used with exclude_tags.
5453
exclude_tags: List of tags to exclude from MCP tools. Cannot be used with include_tags.
54+
max_tool_name_length: Maximum length allowed for tools (some vendors prohibit long names).
55+
Tools breaching this restriction will be filtered out.
5556
"""
5657
# Validate operation and tag filtering options
5758
if include_operations is not None and exclude_operations is not None:
@@ -75,6 +76,7 @@ def __init__(
7576
self._exclude_operations = exclude_operations
7677
self._include_tags = include_tags
7778
self._exclude_tags = exclude_tags
79+
self._max_tool_name_length = max_tool_name_length
7880

7981
self._http_client = http_client or httpx.AsyncClient()
8082

@@ -323,6 +325,7 @@ def _filter_tools(self, tools: List[types.Tool], openapi_schema: Dict[str, Any])
323325
and self._exclude_operations is None
324326
and self._include_tags is None
325327
and self._exclude_tags is None
328+
and self._max_tool_name_length is None
326329
):
327330
return tools
328331

@@ -340,23 +343,6 @@ def _filter_tools(self, tools: List[types.Tool], openapi_schema: Dict[str, Any])
340343
)
341344
continue
342345

343-
operation_full_name = self.get_tool_full_name(operation_id)
344-
if len(operation_full_name) > FULL_TOOL_NAME_MAX_LENGTH:
345-
logger.warning(f"Skipping operation with exceedingly long operationId: {operation_full_name}")
346-
continue
347-
348-
"""
349-
if method not in ["get", "post", "put", "delete", "patch"]:
350-
logger.warning(f"Skipping non-HTTP method: {method.upper()} {path}")
351-
continue
352-
353-
# Get operation metadata
354-
operation_id = operation.get("operationId")
355-
if not operation_id:
356-
logger.warning(f"Skipping operation with no operationId: {method.upper()} {path}, details: {operation}")
357-
continue
358-
"""
359-
360346
tags = operation.get("tags", [])
361347
for tag in tags:
362348
if tag not in operations_by_tag:
@@ -365,11 +351,14 @@ def _filter_tools(self, tools: List[types.Tool], openapi_schema: Dict[str, Any])
365351

366352
operations_to_include = set()
367353

354+
all_operations = {tool.name for tool in tools}
355+
368356
if self._include_operations is not None:
369357
operations_to_include.update(self._include_operations)
370358
elif self._exclude_operations is not None:
371-
all_operations = {tool.name for tool in tools}
372359
operations_to_include.update(all_operations - set(self._exclude_operations))
360+
elif self._max_tool_name_length is not None:
361+
operations_to_include.update(all_operations) # all_operations
373362

374363
if self._include_tags is not None:
375364
for tag in self._include_tags:
@@ -379,9 +368,14 @@ def _filter_tools(self, tools: List[types.Tool], openapi_schema: Dict[str, Any])
379368
for tag in self._exclude_tags:
380369
excluded_operations.update(operations_by_tag.get(tag, []))
381370

382-
all_operations = {tool.name for tool in tools}
383371
operations_to_include.update(all_operations - excluded_operations)
384372

373+
if self._max_tool_name_length is not None:
374+
long_operations = {
375+
tool.name for tool in tools if len(self.get_combined_full_name(tool.name)) > self._max_tool_name_length
376+
}
377+
operations_to_include = operations_to_include - long_operations
378+
385379
filtered_tools = [tool for tool in tools if tool.name in operations_to_include]
386380

387381
if filtered_tools:
@@ -392,5 +386,17 @@ def _filter_tools(self, tools: List[types.Tool], openapi_schema: Dict[str, Any])
392386

393387
return filtered_tools
394388

395-
def get_tool_full_name(self, operation_id: str) -> str:
389+
def get_combined_full_name(self, operation_id: str) -> str:
390+
"""
391+
Combined name consists of server name + operation_id
392+
393+
Args:
394+
operation_id: As defined during creation
395+
396+
Returns:
397+
concatenated string of server name + operation_id
398+
"""
399+
if not self.name:
400+
return operation_id
401+
396402
return f"{self.name}\\{operation_id}"

tests/test_configuration.py

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ def test_default_configuration(simple_fastapi_app: FastAPI):
2121
assert mcp_server._describe_all_responses is False
2222
assert mcp_server._describe_full_response_schema is False
2323

24+
assert mcp_server._max_tool_name_length is None
25+
2426

2527
def test_custom_configuration(simple_fastapi_app: FastAPI):
2628
"""Test a custom configuration of FastApiMCP."""
@@ -406,13 +408,21 @@ async def delete_item(item_id: int):
406408
async def search_items():
407409
return [{"id": 1}]
408410

411+
@app.get("/long_search/", operation_id="search_items_long_id", tags=["search"])
412+
async def search_items_long():
413+
return [{"id": 1}]
414+
415+
# benchmark - no filter
416+
no_filter = FastApiMCP(app)
417+
assert len(no_filter.tools) == 7
418+
409419
# Test include_operations
410420
include_ops_mcp = FastApiMCP(app, include_operations=["get_item", "list_items"])
411421
assert len(include_ops_mcp.tools) == 2
412422
assert {tool.name for tool in include_ops_mcp.tools} == {"get_item", "list_items"}
413423

414424
# Test exclude_operations
415-
exclude_ops_mcp = FastApiMCP(app, exclude_operations=["delete_item", "search_items"])
425+
exclude_ops_mcp = FastApiMCP(app, exclude_operations=["delete_item", "search_items", "search_items_long_id"])
416426
assert len(exclude_ops_mcp.tools) == 4
417427
assert {tool.name for tool in exclude_ops_mcp.tools} == {"get_item", "list_items", "create_item", "update_item"}
418428

@@ -423,13 +433,21 @@ async def search_items():
423433

424434
# Test exclude_tags
425435
exclude_tags_mcp = FastApiMCP(app, exclude_tags=["write", "delete"])
426-
assert len(exclude_tags_mcp.tools) == 3
427-
assert {tool.name for tool in exclude_tags_mcp.tools} == {"get_item", "list_items", "search_items"}
436+
assert len(exclude_tags_mcp.tools) == 4
437+
assert {tool.name for tool in exclude_tags_mcp.tools} == {
438+
"get_item",
439+
"list_items",
440+
"search_items",
441+
"search_items_long_id",
442+
}
428443

429444
# Test combining include_operations and include_tags
430445
combined_include_mcp = FastApiMCP(app, include_operations=["delete_item"], include_tags=["search"])
431-
assert len(combined_include_mcp.tools) == 2
432-
assert {tool.name for tool in combined_include_mcp.tools} == {"delete_item", "search_items"}
446+
assert len(combined_include_mcp.tools) == 3
447+
assert {tool.name for tool in combined_include_mcp.tools} == {"delete_item", "search_items", "search_items_long_id"}
448+
449+
max_long_name_mcp = FastApiMCP(app, name="mcp_server", max_tool_name_length=25)
450+
assert len(max_long_name_mcp.tools) == 6
433451

434452
# Test invalid combinations
435453
with pytest.raises(ValueError):

0 commit comments

Comments
 (0)