Conversation
- Added new internal API endpoint for documentation tools, allowing actions such as listing available docs, searching, and fetching specific documentation by ID. - Updated environment configuration to support optional internal secret for enhanced security. - Refactored existing search functionality to utilize the new docs tools API instead of the previous MCP server. - Improved error handling and response parsing for documentation-related requests. - Expanded documentation to clarify the relationship between the new tools and existing API functionalities. This update streamlines the documentation access process and enhances the overall developer experience.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughRefactors docs tooling to use a new HTTP-backed internal endpoint (/api/internal/docs-tools) with optional secret gating; moves tool implementations into docs-side operations; backend and docs callers now POST typed action payloads and consume JSON results; non-stream AI responses include Changes
Sequence DiagramsequenceDiagram
participant BackendAI as Backend AI Tools
participant BackendHTTP as Backend HTTP Client
participant DocsAPI as Docs /api/internal/docs-tools
participant DocsOps as DocsToolsOperations
participant Content as Docs Content Files
BackendAI->>BackendHTTP: POST action (e.g. search_docs)
BackendHTTP->>DocsAPI: POST /api/internal/docs-tools (body + optional x-stack-internal-docs-tools-secret)
DocsAPI->>DocsAPI: validate secret (if configured)
DocsAPI->>DocsOps: executeDocsToolAction(action)
DocsOps->>Content: read pages, files, OpenAPI specs
DocsOps-->>DocsAPI: return CallToolResult (content array / isError)
DocsAPI-->>BackendHTTP: JSON response
BackendHTTP-->>BackendAI: parsed content (and finalText for non-stream)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile SummaryThis PR refactors the Stack Auth documentation tooling layer by replacing the previous MCP-over-SSE approach (with complex JSON-RPC/SSE parsing) with a simple internal JSON HTTP API ( Key changes:
Confidence Score: 4/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant MCP as MCP Client (Cursor/IDE)
participant DocsMCP as Docs MCP Server<br/>/api/internal/[transport]
participant Backend as Backend API<br/>/api/latest/ai/query/generate
participant DocsTools as Docs Tools API<br/>/api/internal/docs-tools
participant Ops as docs-tools-operations.ts<br/>(file system / page index)
MCP->>DocsMCP: ask_stack_auth { question, reason }
DocsMCP->>Backend: POST /api/latest/ai/query/generate<br/>{ tools: ["docs"], messages: [{question}] }
Backend->>DocsTools: POST /api/internal/docs-tools<br/>{ action: "search_docs" | "get_docs_by_id" | … }
DocsTools->>Ops: executeDocsToolAction(action)
Ops-->>DocsTools: CallToolResult { content }
DocsTools-->>Backend: JSON { content, isError? }
Backend-->>DocsMCP: { finalText, content[] }
DocsMCP-->>MCP: text response
Note over DocsTools,Ops: Also called directly by<br/>/api/search and mcp-browser
Reviews (1): Last reviewed commit: "Merge branch 'dev' into dario-likes-mcps" | Re-trigger Greptile |
There was a problem hiding this comment.
Pull request overview
This PR refactors Stack Auth documentation tooling by introducing a typed internal docs-tools HTTP endpoint on the docs app, migrating backend “docs” AI tools to use it directly (instead of MCP), and simplifying the public MCP surface to a single ask_stack_auth tool that proxies to the backend AI query endpoint.
Changes:
- Added
POST /api/internal/docs-tools(typed actions) and moved docs list/search/fetch logic into a shared server module. - Updated docs UI + docs search API to call the new internal docs-tools endpoint (JSON instead of SSE/JSON-RPC).
- Simplified the public MCP endpoint to only expose
ask_stack_authand added optional shared-secret gating for docs-tools calls.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| docs/src/lib/docs-tools-operations.ts | New shared implementation for list/search/fetch + OpenAPI extraction used by the internal docs-tools route. |
| docs/src/app/mcp-browser/page.tsx | Switches MCP-browser UI from MCP JSON-RPC/SSE parsing to internal docs-tools JSON API. |
| docs/src/app/api/search/route.ts | Refactors docs-site search endpoint to use internal docs-tools search action. |
| docs/src/app/api/internal/docs-tools/route.ts | New internal HTTP API endpoint exposing typed docs tool actions, with optional secret header check. |
| docs/src/app/api/internal/[transport]/setup-instructions.md | Updates wording to reflect ask_stack_auth being the MCP tool. |
| docs/src/app/api/internal/[transport]/route.ts | Replaces multiple MCP tools with a single ask_stack_auth tool that calls backend AI query/generate. |
| docs/content/docs/(guides)/others/mcp-setup.mdx | Documents the new MCP surface (ask_stack_auth) and updated feature set. |
| docs/.env.development | Adds optional STACK_INTERNAL_DOCS_TOOLS_SECRET guidance for docs-tools route. |
| claude/CLAUDE-KNOWLEDGE.md | Updates internal knowledge notes describing new MCP/docs-tools architecture. |
| apps/backend/src/lib/ai/tools/docs.ts | Replaces MCP client usage with direct HTTP calls to docs app /api/internal/docs-tools. |
| apps/backend/src/app/api/latest/ai/query/[mode]/route.ts | Adds finalText to the JSON response payload (alongside content). |
| apps/backend/.env.development | Documents new env vars for docs-tools base URL override + optional shared secret. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (2)
docs/src/app/api/internal/[transport]/route.ts (1)
59-70: Unsafe type assertion on backend response.The
as { finalText?, content? }cast bypasses type safety. If the backend response shape changes or returns an error object, this will fail silently. Per coding guidelines, prefer runtime validation or defensive checks.♻️ Proposed fix to add defensive validation
- const body = (await res.json()) as { - finalText?: string, - content?: Array<{ type: string, text?: string }>, - }; - - const text = - body.finalText ?? - body.content - ?.filter((c): c is { type: "text", text: string } => c.type === "text" && typeof c.text === "string") - .map((c) => c.text) - .join("\n\n") ?? - ""; + const body = await res.json(); + + // Defensive validation of response shape + const finalText = typeof body?.finalText === "string" ? body.finalText : undefined; + const contentArray = Array.isArray(body?.content) ? body.content : []; + + const text = + finalText ?? + contentArray + .filter((c): c is { type: "text", text: string } => + c != null && c.type === "text" && typeof c.text === "string") + .map((c) => c.text) + .join("\n\n") ?? + "";🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/src/app/api/internal/`[transport]/route.ts around lines 59 - 70, The code unsafely casts the fetch response to a specific shape using "as { finalText?, content? }" when computing the "body" and "text" variables in route.ts; replace this with runtime validation: after awaiting res.json(), check res.ok and that the parsed value is an object, then validate that body.finalText (if present) is a string or that body.content is an array whose items have type === "text" and text is a string before using them to build "text"; if validation fails, handle the error path (log or throw) and fall back to "" (or a safe default). If your codebase uses a runtime schema validator (e.g., zod), validate the response against a schema instead of the assertion; update all uses of "body" and the "text" assembly accordingly.docs/src/app/mcp-browser/page.tsx (1)
50-50: Unsafe type assertion on API response.The
as { content: ... }cast bypasses type safety. Per coding guidelines, type casts should be avoided. If the response shape is unexpected, this will fail silently at runtime rather than failing fast.Consider validating the response shape or at least adding a defensive check:
♻️ Proposed fix to add defensive validation
- return await response.json() as { content: Array<{ type: string, text?: string }> }; + const json = await response.json(); + if (!json || !Array.isArray(json.content)) { + throw new Error('Invalid response shape from docs-tools API'); + } + return json as { content: Array<{ type: string, text?: string }> };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/src/app/mcp-browser/page.tsx` at line 50, The current return uses an unsafe cast on response.json() which bypasses type safety; replace the direct "return await response.json() as { content: Array<{ type: string, text?: string }> }" with a defensive parse/validation: call response.json(), verify response.ok, ensure the parsed value has a "content" property that's an array, and that each item has a string "type" and optional string "text"; if validation fails, throw a descriptive error (or return a safe fallback). Consider extracting this into a small helper (e.g., validateApiResponse or parseResponseFromApi) so callers use a typed, validated object instead of the unsafe cast.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/backend/src/lib/ai/tools/docs.ts`:
- Around line 32-36: The fetch to `${base}/api/internal/docs-tools` needs an
explicit timeout: wrap the request in an AbortController, pass controller.signal
into the fetch options used in the const res = await fetch(...) call in docs
tool function, start a setTimeout that calls controller.abort() after a
reasonable ms (e.g., 5s), and clear the timeout when fetch completes; catch the
abort error and throw or return a bounded tool-specific error (e.g.,
"DocsToolTimeoutError" or a descriptive error message) so the caller of the docs
tool (the AI tool path) can surface a bounded timeout response instead of
waiting for the underlying network/socket timeout.
- Around line 43-50: The function currently treats a 200 response with no usable
text as success; instead validate the parsed DocsToolHttpResult payload and fail
loudly: after parsing (the variable data) and computing text, if text is an
empty string (no text items found) then throw an Error (include context like the
raw data or a descriptive message) rather than returning "" or "Unknown docs
tool error"; keep the existing branch that returns when data.isError === true
but add this explicit validation/throw for missing text to surface contract
drift between the docs app and backend tools.
In `@docs/src/app/api/internal/`[transport]/route.ts:
- Around line 81-98: The capabilities object uses the camelCase key askStackAuth
which doesn't match the registered tool name ask_stack_auth and will cause
"Unknown tool" runtime errors; update the capabilities key from askStackAuth to
ask_stack_auth so it exactly matches the registered tool name (referencing the
registered tool identifier ask_stack_auth and the capabilities entry
askStackAuth in route.ts), ensuring the MCP adapter can invoke the tool.
In `@docs/src/app/api/search/route.ts`:
- Around line 12-24: The helper callDocsToolsSearch should not build a backend
target from the untrusted requestOrigin and swallow failures; instead invoke the
docs-tools operation directly (call the internal handler/function exported by
docs/src/app/api/internal/docs-tools/route.ts) or use a trusted server base URL
and include the STACK_INTERNAL_DOCS_TOOLS_SECRET in the request so auth failures
surface; remove the broad try-catch that returns [] on error and let errors
propagate (or throw a specific error) so failures are loud; apply the same fixes
to the other similar fetches around the result_limit lines referenced (79-82 and
107-109) and ensure the call includes the secret header when using an HTTP
request.
- Around line 116-119: The current comparator in sortedResults reorders results
solely by getPlatformPriority, which overrides upstream relevance from
callDocsToolsSearch; update the sort to preserve upstream relevance and only use
platform priority as a tie-breaker: when sorting filteredResults, first compare
the upstream relevance score (use the score/relevance property returned by
callDocsToolsSearch if present), if equal compare getPlatformPriority(a.url) vs
getPlatformPriority(b.url), and finally fall back to original index to ensure a
stable sort (e.g., attach the original index before sorting). Ensure you
reference filteredResults, sortedResults, callDocsToolsSearch, and
getPlatformPriority when making the change.
In `@docs/src/lib/docs-tools-operations.ts`:
- Around line 413-415: The handler for "search_docs" must sanitize and clamp
input.result_limit before it's used (and before any slice(0, result_limit)
calls) to avoid negative values like -1 turning into slice(0, -1); normalize to
an integer (e.g., parse/Number), enforce a minimum of 0, and apply a sensible
maximum cap (e.g., 100) before passing it into searchDocsImpl or using it in
array slicing; update the code around input.result_limit, the call to
searchDocsImpl, and any downstream slice() usage to use this
clampedPositiveLimit instead of the raw input.value.
- Around line 239-305: The search currently scans allPages and calls readFile
per page inside the loop (see variables allPages, readFile, and textContent)
causing a full disk read on each request; fix by extracting the file-reading and
normalization logic into a precomputed in-memory search index (e.g.,
buildSearchIndex or cachedPages map) that runs at startup or on content changes,
store normalized text, titles, descriptions and toc for each page, and update
the search routine to iterate that in-memory index instead of reading files;
ensure docs/src/app/api/search/route.ts uses the cached index and add
invalidation/update hooks when content changes.
---
Nitpick comments:
In `@docs/src/app/api/internal/`[transport]/route.ts:
- Around line 59-70: The code unsafely casts the fetch response to a specific
shape using "as { finalText?, content? }" when computing the "body" and "text"
variables in route.ts; replace this with runtime validation: after awaiting
res.json(), check res.ok and that the parsed value is an object, then validate
that body.finalText (if present) is a string or that body.content is an array
whose items have type === "text" and text is a string before using them to build
"text"; if validation fails, handle the error path (log or throw) and fall back
to "" (or a safe default). If your codebase uses a runtime schema validator
(e.g., zod), validate the response against a schema instead of the assertion;
update all uses of "body" and the "text" assembly accordingly.
In `@docs/src/app/mcp-browser/page.tsx`:
- Line 50: The current return uses an unsafe cast on response.json() which
bypasses type safety; replace the direct "return await response.json() as {
content: Array<{ type: string, text?: string }> }" with a defensive
parse/validation: call response.json(), verify response.ok, ensure the parsed
value has a "content" property that's an array, and that each item has a string
"type" and optional string "text"; if validation fails, throw a descriptive
error (or return a safe fallback). Consider extracting this into a small helper
(e.g., validateApiResponse or parseResponseFromApi) so callers use a typed,
validated object instead of the unsafe cast.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 4f12ae57-f536-4ae7-856b-6955f080d5e7
📒 Files selected for processing (12)
apps/backend/.env.developmentapps/backend/src/app/api/latest/ai/query/[mode]/route.tsapps/backend/src/lib/ai/tools/docs.tsclaude/CLAUDE-KNOWLEDGE.mddocs/.env.developmentdocs/content/docs/(guides)/others/mcp-setup.mdxdocs/src/app/api/internal/[transport]/route.tsdocs/src/app/api/internal/[transport]/setup-instructions.mddocs/src/app/api/internal/docs-tools/route.tsdocs/src/app/api/search/route.tsdocs/src/app/mcp-browser/page.tsxdocs/src/lib/docs-tools-operations.ts
- Introduced error capturing for failed HTTP requests in the docs tools API, improving debugging capabilities. - Updated the API response for unsupported methods to include an 'Allow' header, clarifying the expected request type. These changes enhance the robustness of the documentation tools integration and improve developer experience.
There was a problem hiding this comment.
🧹 Nitpick comments (2)
apps/backend/src/lib/ai/tools/docs.ts (2)
109-118: Consider consolidating duplicate tools.
searchandsearch_docsoverlap in functionality (both search documentation). Similarly,fetchis documented as "identical toget_docs_by_id". This duplication may confuse AI models choosing between tools. If these exist for backward compatibility, consider documenting that intent.Also applies to: 120-129
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/backend/src/lib/ai/tools/docs.ts` around lines 109 - 118, The PR defines duplicate tools — search and search_docs (and fetch vs get_docs_by_id) — which overlap and can confuse tool selection; consolidate by removing one alias or explicitly mark aliases as backward-compatible. Update the tools registration in apps/backend/src/lib/ai/tools/docs.ts: either merge search_docs into search (and remove the duplicate tool) or keep one canonical implementation (e.g., search, get_docs_by_id) and implement the other names as thin wrapper aliases that call the canonical function (via postDocsToolAction) while adding clear descriptions stating they are deprecated/backwards-compatible; also update the description strings to mention the aliasing so AI models know these are identical.
6-9: Consider makingDocsToolHttpResultstricter or adding runtime validation.The type is permissive (all fields optional) and used with a type assertion at line 43. If the contract changes, errors will be silent. A Zod schema would provide runtime safety.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/backend/src/lib/ai/tools/docs.ts` around lines 6 - 9, DocsToolHttpResult is too permissive and is being trusted via a type assertion elsewhere; define a strict runtime schema (e.g., a Zod schema) that mirrors DocsToolHttpResult (require content as array of objects with required type and optional text, and explicit isError boolean) and validate incoming HTTP responses against that schema instead of asserting the type; update usages where the assertion occurs (the code that currently casts to DocsToolHttpResult) to parse/validate with the schema, handle validation failures (treat as error/isError) and then rely on the validated shape for downstream logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@apps/backend/src/lib/ai/tools/docs.ts`:
- Around line 109-118: The PR defines duplicate tools — search and search_docs
(and fetch vs get_docs_by_id) — which overlap and can confuse tool selection;
consolidate by removing one alias or explicitly mark aliases as
backward-compatible. Update the tools registration in
apps/backend/src/lib/ai/tools/docs.ts: either merge search_docs into search (and
remove the duplicate tool) or keep one canonical implementation (e.g., search,
get_docs_by_id) and implement the other names as thin wrapper aliases that call
the canonical function (via postDocsToolAction) while adding clear descriptions
stating they are deprecated/backwards-compatible; also update the description
strings to mention the aliasing so AI models know these are identical.
- Around line 6-9: DocsToolHttpResult is too permissive and is being trusted via
a type assertion elsewhere; define a strict runtime schema (e.g., a Zod schema)
that mirrors DocsToolHttpResult (require content as array of objects with
required type and optional text, and explicit isError boolean) and validate
incoming HTTP responses against that schema instead of asserting the type;
update usages where the assertion occurs (the code that currently casts to
DocsToolHttpResult) to parse/validate with the schema, handle validation
failures (treat as error/isError) and then rely on the validated shape for
downstream logic.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: e673009a-93c6-437e-8777-1827d56adf1c
📒 Files selected for processing (2)
apps/backend/src/lib/ai/tools/docs.tsdocs/src/app/api/internal/docs-tools/route.ts
- Updated the key name in the capabilities section of the API documentation to follow a consistent naming convention, improving clarity and maintainability.
This update streamlines the documentation access process and enhances the overall developer experience.
Summary by CodeRabbit
New Features
Refinement
Documentation