From f04bc0753cdc18575907df16099469a6d1536be2 Mon Sep 17 00:00:00 2001 From: Kian Jones <11655409+kianjones9@users.noreply.github.com> Date: Fri, 17 Oct 2025 16:37:16 -0700 Subject: [PATCH] feat: add path parameter validation for tool_id (#5525) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.ai/code) Co-authored-by: Claude --- fern/openapi.json | 40 +++++++++++++++++++--- letta/server/rest_api/routers/v1/agents.py | 4 +-- letta/server/rest_api/routers/v1/tools.py | 7 ++-- 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/fern/openapi.json b/fern/openapi.json index edf2d369..2f11ed22 100644 --- a/fern/openapi.json +++ b/fern/openapi.json @@ -269,8 +269,14 @@ "required": true, "schema": { "type": "string", + "minLength": 41, + "maxLength": 41, + "pattern": "^tool-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$", + "description": "The ID of the tool in the format 'tool-'", + "examples": ["tool-123e4567-e89b-42d3-8456-426614174000"], "title": "Tool Id" - } + }, + "description": "The ID of the tool in the format 'tool-'" } ], "responses": { @@ -306,8 +312,14 @@ "required": true, "schema": { "type": "string", + "minLength": 41, + "maxLength": 41, + "pattern": "^tool-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$", + "description": "The ID of the tool in the format 'tool-'", + "examples": ["tool-123e4567-e89b-42d3-8456-426614174000"], "title": "Tool Id" - } + }, + "description": "The ID of the tool in the format 'tool-'" } ], "responses": { @@ -345,8 +357,14 @@ "required": true, "schema": { "type": "string", + "minLength": 41, + "maxLength": 41, + "pattern": "^tool-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$", + "description": "The ID of the tool in the format 'tool-'", + "examples": ["tool-123e4567-e89b-42d3-8456-426614174000"], "title": "Tool Id" - } + }, + "description": "The ID of the tool in the format 'tool-'" } ], "requestBody": { @@ -4242,8 +4260,14 @@ "required": true, "schema": { "type": "string", + "minLength": 41, + "maxLength": 41, + "pattern": "^tool-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$", + "description": "The ID of the tool in the format 'tool-'", + "examples": ["tool-123e4567-e89b-42d3-8456-426614174000"], "title": "Tool Id" - } + }, + "description": "The ID of the tool in the format 'tool-'" }, { "name": "agent_id", @@ -4298,8 +4322,14 @@ "required": true, "schema": { "type": "string", + "minLength": 41, + "maxLength": 41, + "pattern": "^tool-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$", + "description": "The ID of the tool in the format 'tool-'", + "examples": ["tool-123e4567-e89b-42d3-8456-426614174000"], "title": "Tool Id" - } + }, + "description": "The ID of the tool in the format 'tool-'" }, { "name": "agent_id", diff --git a/letta/server/rest_api/routers/v1/agents.py b/letta/server/rest_api/routers/v1/agents.py index f41ea677..87f92a9b 100644 --- a/letta/server/rest_api/routers/v1/agents.py +++ b/letta/server/rest_api/routers/v1/agents.py @@ -434,7 +434,7 @@ async def list_agent_tools( @router.patch("/{agent_id}/tools/attach/{tool_id}", response_model=AgentState, operation_id="attach_tool") async def attach_tool( - tool_id: str, + tool_id: str = PATH_VALIDATORS["tool"], agent_id: str = PATH_VALIDATORS["agent"], server: "SyncServer" = Depends(get_letta_server), headers: HeaderParams = Depends(get_headers), @@ -450,7 +450,7 @@ async def attach_tool( @router.patch("/{agent_id}/tools/detach/{tool_id}", response_model=AgentState, operation_id="detach_tool") async def detach_tool( - tool_id: str, + tool_id: str = PATH_VALIDATORS["tool"], agent_id: str = PATH_VALIDATORS["agent"], server: "SyncServer" = Depends(get_letta_server), headers: HeaderParams = Depends(get_headers), diff --git a/letta/server/rest_api/routers/v1/tools.py b/letta/server/rest_api/routers/v1/tools.py index 21dfcccb..c0e8642c 100644 --- a/letta/server/rest_api/routers/v1/tools.py +++ b/letta/server/rest_api/routers/v1/tools.py @@ -38,6 +38,7 @@ from letta.services.mcp.oauth_utils import MCPOAuthSession, drill_down_exception from letta.services.mcp.stdio_client import AsyncStdioMCPClient from letta.services.mcp.types import OauthStreamEvent from letta.settings import tool_settings +from letta.validators import PATH_VALIDATORS router = APIRouter(prefix="/tools", tags=["tools"]) @@ -46,7 +47,7 @@ logger = get_logger(__name__) @router.delete("/{tool_id}", operation_id="delete_tool") async def delete_tool( - tool_id: str, + tool_id: str = PATH_VALIDATORS["tool"], server: SyncServer = Depends(get_letta_server), headers: HeaderParams = Depends(get_headers), ): @@ -149,7 +150,7 @@ async def count_tools( @router.get("/{tool_id}", response_model=Tool, operation_id="retrieve_tool") async def retrieve_tool( - tool_id: str, + tool_id: str = PATH_VALIDATORS["tool"], server: SyncServer = Depends(get_letta_server), headers: HeaderParams = Depends(get_headers), ): @@ -297,8 +298,8 @@ async def upsert_tool( @router.patch("/{tool_id}", response_model=Tool, operation_id="modify_tool") async def modify_tool( - tool_id: str, request: ToolUpdate = Body(...), + tool_id: str = PATH_VALIDATORS["tool"], server: SyncServer = Depends(get_letta_server), headers: HeaderParams = Depends(get_headers), ):