POST /api/v1/search-tools accepts any non-empty name:
name: str = Field(min_length=1, description="Name callers pass as 'search_tool_name' or in /api/v1/search/{tool}.")
A name containing / stores fine and then cannot be addressed. Neither /api/v1/search-tools/team%2Fbrave nor the bare /api/v1/search-tools/team/brave matches a /{name} route: Starlette's path parameters do not span a segment boundary, and the encoded form is decoded after routing, so it 404s too. Confirmed against a minimal app, both spellings.
The result is a row that can be created but never read, updated or deleted through the API. It also cannot be dispatched to, since /api/v1/search/{tool} has the same shape.
Why this is filed rather than fixed on #1211
The same gap was raised against the new guardrail store on #1211 and fixed there: #1211 (comment)
CreateGuardrailCredentialRequest.name now carries pattern=r"^[^/]+$". The search-tool store is a different resource on a branch that does not touch it, and the fix moves docs/public/openapi.json, the Postman collection and web/src/client/schema.ts, so it belongs in its own change rather than widening a feature branch.
Scope
pattern=r"^[^/]+$" on the search-tool create request's name, matching what the guardrail store now does.
- Check whether
provider_credentials has the same shape; its instance names are addressed the same way.
- Existing rows are unaffected: nothing can have reached the table through a path, only through this body, so a deployment that already stored one still needs a manual cleanup. Worth a line in the PR description if any are found.
- The four generated artifacts regenerate with it.
POST /api/v1/search-toolsaccepts any non-empty name:A name containing
/stores fine and then cannot be addressed. Neither/api/v1/search-tools/team%2Fbravenor the bare/api/v1/search-tools/team/bravematches a/{name}route: Starlette's path parameters do not span a segment boundary, and the encoded form is decoded after routing, so it 404s too. Confirmed against a minimal app, both spellings.The result is a row that can be created but never read, updated or deleted through the API. It also cannot be dispatched to, since
/api/v1/search/{tool}has the same shape.Why this is filed rather than fixed on #1211
The same gap was raised against the new guardrail store on #1211 and fixed there: #1211 (comment)
CreateGuardrailCredentialRequest.namenow carriespattern=r"^[^/]+$". The search-tool store is a different resource on a branch that does not touch it, and the fix movesdocs/public/openapi.json, the Postman collection andweb/src/client/schema.ts, so it belongs in its own change rather than widening a feature branch.Scope
pattern=r"^[^/]+$"on the search-tool create request'sname, matching what the guardrail store now does.provider_credentialshas the same shape; its instance names are addressed the same way.