Repository navigation
feat(docs): add a public search route for the deprecated docs MCP server - #166
Conversation
🟡 Tier B · Needs changes before merging
This pull request adds a public Latest changes: The route now streams incoming request bytes and returns 413 once they exceed 16 KiB, with the documentation and end-to-end test updated to cover that limit.
Fix with agent prompt### Issue 1
src/mcp_server_appwrite/http_app.py:606
**Public route leaves paid embeddings open to unlimited requests**
This now mounts an unauthenticated endpoint for every deployment with docs search enabled, and each accepted POST triggers an OpenAI embedding call. The 500-character cap only bounds an individual request; without an authentication or rate limit, anyone can repeatedly call the public route and consume the deployment's paid API quota.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.📂 Walkthrough · 6
⏳ Still open from earlier reviews · 1
✅ Fixed since the last review · 1
Reviewed the commits since |
| methods=["POST"], | ||
| ) | ||
| ] | ||
| if operator.docs_search is not None |
There was a problem hiding this comment.
Public route leaves paid embeddings open to unlimited requests
This now mounts an unauthenticated endpoint for every deployment with docs search enabled, and each accepted POST triggers an OpenAI embedding call. The 500-character cap only bounds an individual request; without an authentication or rate limit, anyone can repeatedly call the public route and consume the deployment's paid API quota.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/mcp_server_appwrite/http_app.py
Line: 606
Comment:
**Public route leaves paid embeddings open to unlimited requests**
This now mounts an unauthenticated endpoint for every deployment with docs search enabled, and each accepted POST triggers an OpenAI embedding call. The 500-character cap only bounds an individual request; without an authentication or rate limit, anyone can repeatedly call the public route and consume the deployment's paid API quota.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.🟠 Major · security · Reply if this doesn't apply.
There was a problem hiding this comment.
Each call is bounded here, and the route is rate limited per IP at the edge before this ships.
There was a problem hiding this comment.
Per-IP edge limiting addresses repeated requests from one address, but it does not bound aggregate embedding spend: clients using many IPs can stay below each IP’s limit while continuing to make requests. Every accepted POST calls docs_search.find (route handler), which ranks the query (embedding path). So the per-call bounds and per-IP limit still leave aggregate paid usage unbounded; a global quota or equivalent cap would close that gap.
There was a problem hiding this comment.
This does not apply any more: a per-IP rate limit on this path is now live at the edge in front of the hosted server, at 60 requests a minute. It sits outside this repository, so it does not appear in this diff. Together with the 16 KiB body cap, the 500-character query cap and the fixed page count, that bounds what the route can spend. Keeping the route unauthenticated is deliberate, because the deprecated server it supports is also public.
There was a problem hiding this comment.
I accept that the edge rule is live. A limit of 60 requests per IP per minute still does not bound aggregate spend: requests from many distinct IPs can each stay within the limit, and each accepted request reaches the embedding call (route, embedding). The request-size and query limits bound individual calls, not their total. The finding remains open unless there is also an aggregate cap or equivalent control.
Clients of the deprecated docs server have no OAuth session, so its replacement cannot use appwrite_search_docs. Sharing the index here means it needs no OpenAI key or 35 MB artifact, and search stays as fresh as the daily refresh. Queries are capped and the page count fixed, so each call costs one small embedding.
750d388 to
05170b8
Compare
The route is unauthenticated, so an oversized body should not be buffered in full.
Why
The deprecated docs MCP server at
mcp-for-docs.appwrite.iois being replaced by a much smaller service that keeps its URL and tools for existing clients. Those clients have no OAuth session, so it can't useappwrite_search_docsover the MCP endpoint, and it shouldn't carry its own 35 MB index and OpenAI key.What
POST /docs/searchreturns{"results": [...]}using the existingDocsSearch.DocsSearch.findsplits the ranking from the MCP content formatting. The tool's behaviour is unchanged.Testing
Added
DocsSearchRoutetotests/e2e/test_tools.py, against the real app and index with only the OpenAI embedding stubbed: unauthenticated search, fixed page count, invalid and oversized queries, and malformed bodies. Unit suite,tests/e2e/test_tools.py, ruff and black pass locally.