Skip to content

Commit 403773d

Browse files
committed
changes followig CR
1 parent 3c34954 commit 403773d

File tree

3 files changed

+59
-27
lines changed

3 files changed

+59
-27
lines changed

docs/04_advanced_usage.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,13 @@ mcp = FastApiMCP(
6161
include_tags=["public"]
6262
)
6363

64+
# Limit maximum allowed length for combined tool name (calculated as server name + operation_id)
65+
# Some vendors throw errors if combined tool names are too long
66+
mcp = FastApiMCP(
67+
app,
68+
max_tool_name_length=25,
69+
)
70+
6471
mcp.mount()
6572
```
6673

fastapi_mcp/server.py

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@
1717

1818
logger = logging.getLogger(__name__)
1919

20-
FULL_TOOL_NAME_MAX_LENGTH = 55
21-
2220

2321
class LowlevelMCPServer(Server):
2422
def call_tool(self):
@@ -111,6 +109,10 @@ def __init__(
111109
Optional[List[str]],
112110
Doc("List of tags to exclude from MCP tools. Cannot be used with include_tags."),
113111
] = None,
112+
max_tool_name_length: Annotated[
113+
Optional[int],
114+
Doc("Maximum length allowed for tools (some vendors prohibit long names).")
115+
] = None,
114116
auth_config: Annotated[
115117
Optional[AuthConfig],
116118
Doc("Configuration for MCP authentication"),
@@ -138,6 +140,7 @@ def __init__(
138140
self._exclude_operations = exclude_operations
139141
self._include_tags = include_tags
140142
self._exclude_tags = exclude_tags
143+
self._max_tool_name_length = max_tool_name_length
141144
self._auth_config = auth_config
142145

143146
if self._auth_config:
@@ -485,6 +488,7 @@ def _filter_tools(self, tools: List[types.Tool], openapi_schema: Dict[str, Any])
485488
and self._exclude_operations is None
486489
and self._include_tags is None
487490
and self._exclude_tags is None
491+
and self._max_tool_name_length is None
488492
):
489493
return tools
490494

@@ -502,23 +506,6 @@ def _filter_tools(self, tools: List[types.Tool], openapi_schema: Dict[str, Any])
502506
)
503507
continue
504508

505-
operation_full_name = self.get_tool_full_name(operation_id)
506-
if len(operation_full_name) > FULL_TOOL_NAME_MAX_LENGTH:
507-
logger.warning(f"Skipping operation with exceedingly long operationId: {operation_full_name}")
508-
continue
509-
510-
"""
511-
if method not in ["get", "post", "put", "delete", "patch"]:
512-
logger.warning(f"Skipping non-HTTP method: {method.upper()} {path}")
513-
continue
514-
515-
# Get operation metadata
516-
operation_id = operation.get("operationId")
517-
if not operation_id:
518-
logger.warning(f"Skipping operation with no operationId: {method.upper()} {path}, details: {operation}")
519-
continue
520-
"""
521-
522509
tags = operation.get("tags", [])
523510
for tag in tags:
524511
if tag not in operations_by_tag:
@@ -527,11 +514,14 @@ def _filter_tools(self, tools: List[types.Tool], openapi_schema: Dict[str, Any])
527514

528515
operations_to_include = set()
529516

517+
all_operations = {tool.name for tool in tools}
518+
530519
if self._include_operations is not None:
531520
operations_to_include.update(self._include_operations)
532521
elif self._exclude_operations is not None:
533-
all_operations = {tool.name for tool in tools}
534522
operations_to_include.update(all_operations - set(self._exclude_operations))
523+
elif self._max_tool_name_length is not None:
524+
operations_to_include.update(all_operations) # all_operations
535525

536526
if self._include_tags is not None:
537527
for tag in self._include_tags:
@@ -541,9 +531,14 @@ def _filter_tools(self, tools: List[types.Tool], openapi_schema: Dict[str, Any])
541531
for tag in self._exclude_tags:
542532
excluded_operations.update(operations_by_tag.get(tag, []))
543533

544-
all_operations = {tool.name for tool in tools}
545534
operations_to_include.update(all_operations - excluded_operations)
546535

536+
if self._max_tool_name_length is not None:
537+
long_operations = {
538+
tool.name for tool in tools if len(self.get_combined_full_name(tool.name)) > self._max_tool_name_length
539+
}
540+
operations_to_include = operations_to_include - long_operations
541+
547542
filtered_tools = [tool for tool in tools if tool.name in operations_to_include]
548543

549544
if filtered_tools:
@@ -554,5 +549,17 @@ def _filter_tools(self, tools: List[types.Tool], openapi_schema: Dict[str, Any])
554549

555550
return filtered_tools
556551

557-
def get_tool_full_name(self, operation_id: str) -> str:
552+
def get_combined_full_name(self, operation_id: str) -> str:
553+
"""
554+
Combined name consists of server name + operation_id
555+
556+
Args:
557+
operation_id: As defined during creation
558+
559+
Returns:
560+
concatenated string of server name + operation_id
561+
"""
562+
if not self.name:
563+
return operation_id
564+
558565
return f"{self.name}\\{operation_id}"

tests/test_configuration.py

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

20+
assert mcp_server._max_tool_name_length is None
21+
2022

2123
def test_custom_configuration(simple_fastapi_app: FastAPI):
2224
"""Test a custom configuration of FastApiMCP."""
@@ -372,13 +374,21 @@ async def delete_item(item_id: int):
372374
async def search_items():
373375
return [{"id": 1}]
374376

377+
@app.get("/long_search/", operation_id="search_items_long_id", tags=["search"])
378+
async def search_items_long():
379+
return [{"id": 1}]
380+
381+
# benchmark - no filter
382+
no_filter = FastApiMCP(app)
383+
assert len(no_filter.tools) == 7
384+
375385
# Test include_operations
376386
include_ops_mcp = FastApiMCP(app, include_operations=["get_item", "list_items"])
377387
assert len(include_ops_mcp.tools) == 2
378388
assert {tool.name for tool in include_ops_mcp.tools} == {"get_item", "list_items"}
379389

380390
# Test exclude_operations
381-
exclude_ops_mcp = FastApiMCP(app, exclude_operations=["delete_item", "search_items"])
391+
exclude_ops_mcp = FastApiMCP(app, exclude_operations=["delete_item", "search_items", "search_items_long_id"])
382392
assert len(exclude_ops_mcp.tools) == 4
383393
assert {tool.name for tool in exclude_ops_mcp.tools} == {"get_item", "list_items", "create_item", "update_item"}
384394

@@ -389,13 +399,21 @@ async def search_items():
389399

390400
# Test exclude_tags
391401
exclude_tags_mcp = FastApiMCP(app, exclude_tags=["write", "delete"])
392-
assert len(exclude_tags_mcp.tools) == 3
393-
assert {tool.name for tool in exclude_tags_mcp.tools} == {"get_item", "list_items", "search_items"}
402+
assert len(exclude_tags_mcp.tools) == 4
403+
assert {tool.name for tool in exclude_tags_mcp.tools} == {
404+
"get_item",
405+
"list_items",
406+
"search_items",
407+
"search_items_long_id",
408+
}
394409

395410
# Test combining include_operations and include_tags
396411
combined_include_mcp = FastApiMCP(app, include_operations=["delete_item"], include_tags=["search"])
397-
assert len(combined_include_mcp.tools) == 2
398-
assert {tool.name for tool in combined_include_mcp.tools} == {"delete_item", "search_items"}
412+
assert len(combined_include_mcp.tools) == 3
413+
assert {tool.name for tool in combined_include_mcp.tools} == {"delete_item", "search_items", "search_items_long_id"}
414+
415+
max_long_name_mcp = FastApiMCP(app, name="mcp_server", max_tool_name_length=25)
416+
assert len(max_long_name_mcp.tools) == 6
399417

400418
# Test invalid combinations
401419
with pytest.raises(ValueError):

0 commit comments

Comments
 (0)