feat: add new mcp tools#4
Conversation
|
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)
📝 WalkthroughWalkthroughAdds four MCP tools (author search, works-by-author, novelty lookup, literature-review generation), expands API client and types with three new endpoints and five interfaces, enables optional HTTP debug logging via Changes
Sequence Diagram(s)sequenceDiagram
participant Client as Client
participant MCP as MCP Server
participant Validator as Input Validator
participant APIClient as API Client
participant Backend as ML Backend API
Client->>MCP: Invoke tool (e.g., search_authors)
MCP->>Validator: Validate & normalize input
alt invalid
Validator-->>MCP: Validation error
MCP-->>Client: Error response
else valid
Validator-->>MCP: Parsed input
MCP->>APIClient: call (e.g., searchAuthors)
APIClient->>Backend: POST /api/mcp/[endpoint]
Backend-->>APIClient: JSON response
alt success
APIClient-->>MCP: parsed result
MCP->>MCP: format markdown/text output
MCP-->>Client: formatted content
else error
APIClient-->>MCP: error/status
MCP-->>Client: error response
end
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (5)
src/server.ts (2)
109-119: Consider exposing the refined schema for better client hints.The tool is registered with
searchAuthorsInputSchema(which allows all fields to be optional) while validation usessearchAuthorsSchema(which requires at least one ofquery,orcid, oropenalex_id). MCP clients won't see this constraint in the schema.This works correctly since the refinement validation happens at parse time, but clients may submit requests with no identifiers and receive a validation error rather than being guided by the schema.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/server.ts` around lines 109 - 119, The tool registration exposes the looser searchAuthorsInputSchema while actual validation uses the refined searchAuthorsSchema, so clients don't see the "at least one of query/orcid/openalex_id" constraint; update the server.registerTool call for "search_authors" to expose the refined schema (searchAuthorsSchema) instead of searchAuthorsInputSchema so clients get the correct hints, and ensure the handler still calls searchAuthorsSchema.parse(input) before invoking searchAuthorsTool(apiKey, ...).
133-143: Same pattern applies tolookup_novelty_score.Similar to
search_authors, this useslookupNoveltyScoreInputSchemafor registration but validates with the refined schema that requires at least one ofdoiorwork_id. The validation still works, but the constraint isn't visible to MCP clients in the schema.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/server.ts` around lines 133 - 143, Registration currently exposes lookupNoveltyScoreInputSchema but validates with the stricter lookupNoveltyScoreSchema, so the "at least one of doi or work_id" constraint isn't visible to MCP clients; change the server.registerTool call for "lookup_novelty_score" to use lookupNoveltyScoreSchema as the inputSchema (replace lookupNoveltyScoreInputSchema with lookupNoveltyScoreSchema) and keep the runtime parse using lookupNoveltyScoreSchema.parse(input) before calling lookupNoveltyScoreTool(apiKey, ...), ensuring the public schema matches the runtime validation.src/tools/reviews.ts (3)
46-47: Consider adding validation formin_year <= max_year.Neither schema validates that
min_yeardoes not exceedmax_year. Providingmin_year: 2025, max_year: 2020would create an invalid range, potentially causing unexpected API behavior or empty results without a clear error.♻️ Proposed fix using Zod's `.refine()`
export const generateLiteratureReviewSchema = z.object({ query: z .string() .min(5) .describe("Research topic or review question"), difficulty: z .enum(["simple", "intermediate", "expert"]) .default("intermediate") .describe("Response complexity for the literature review"), list_ids: z .array(z.string()) .optional() .describe("Restrict review to specific collection IDs"), include_literature: z .boolean() .default(true) .describe("When list_ids are provided, also include broader literature"), min_year: z.number().int().optional().describe("Filter papers published after this year"), max_year: z.number().int().optional().describe("Filter papers published before this year"), format: z .enum(["narrative", "bullets"]) .default("narrative") .describe("Preferred review format"), -}); +}).refine( + (data) => !data.min_year || !data.max_year || data.min_year <= data.max_year, + { message: "min_year must not exceed max_year", path: ["min_year"] } +);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/tools/reviews.ts` around lines 46 - 47, The schema that defines min_year and max_year needs a validation ensuring min_year <= max_year; update the Zod object that contains the fields min_year and max_year (in src/tools/reviews.ts) to add a cross-field check using Zod's .refine or .superRefine on that object, returning a validation error message like "min_year must be <= max_year" and adding the error to the max_year (or both) path when min_year > max_year so invalid ranges are rejected with a clear error.
21-22: Ambiguous year filter descriptions may confuse users.The field names
min_year/max_yearconventionally imply inclusive bounds, but the descriptions say "after"/"before" which suggests exclusive bounds. Consider clarifying:
min_year: "Filter papers published in or after this year"max_year: "Filter papers published in or before this year"Alternatively, verify the API behavior and align descriptions accordingly.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/tools/reviews.ts` around lines 21 - 22, The min_year and max_year Zod schema descriptions are ambiguous about inclusivity; update the descriptors in the schema (the min_year and max_year fields in the Zod object in src/tools/reviews.ts) to explicitly state inclusive bounds—e.g., change min_year description to "Filter papers published in or after this year" and max_year to "Filter papers published in or before this year"—or, if the API actually uses exclusive bounds, adjust the text to "after" / "before" to match behavior; ensure the wording in the z.number().int().optional().describe(...) calls for min_year and max_year matches the API semantics.
82-89: Consider explicit null checks for year values.The truthiness checks (
input.min_year || ...andinput.min_year && ...) would treat0as falsy. While year0is unlikely in practice, using explicit null checks is more semantically correct.♻️ Proposed fix
- if (input.min_year || input.max_year) { + if (input.min_year != null || input.max_year != null) { filter.pub_year = { range: { - ...(input.min_year && { min: input.min_year }), - ...(input.max_year && { max: input.max_year }), + ...(input.min_year != null && { min: input.min_year }), + ...(input.max_year != null && { max: input.max_year }), }, }; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/tools/reviews.ts` around lines 82 - 89, The current truthiness checks around input.min_year and input.max_year can mis-handle 0; update the condition that builds filter.pub_year to use explicit null/undefined checks (e.g., input.min_year != null || input.max_year != null) and likewise use explicit checks when spreading the min/max properties (e.g., input.min_year != null && { min: input.min_year }) so filter.pub_year is only created when a year value is actually provided; locate the logic around filter.pub_year in src/tools/reviews.ts and replace the truthy checks with these explicit null checks.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/server.ts`:
- Around line 109-119: The tool registration exposes the looser
searchAuthorsInputSchema while actual validation uses the refined
searchAuthorsSchema, so clients don't see the "at least one of
query/orcid/openalex_id" constraint; update the server.registerTool call for
"search_authors" to expose the refined schema (searchAuthorsSchema) instead of
searchAuthorsInputSchema so clients get the correct hints, and ensure the
handler still calls searchAuthorsSchema.parse(input) before invoking
searchAuthorsTool(apiKey, ...).
- Around line 133-143: Registration currently exposes
lookupNoveltyScoreInputSchema but validates with the stricter
lookupNoveltyScoreSchema, so the "at least one of doi or work_id" constraint
isn't visible to MCP clients; change the server.registerTool call for
"lookup_novelty_score" to use lookupNoveltyScoreSchema as the inputSchema
(replace lookupNoveltyScoreInputSchema with lookupNoveltyScoreSchema) and keep
the runtime parse using lookupNoveltyScoreSchema.parse(input) before calling
lookupNoveltyScoreTool(apiKey, ...), ensuring the public schema matches the
runtime validation.
In `@src/tools/reviews.ts`:
- Around line 46-47: The schema that defines min_year and max_year needs a
validation ensuring min_year <= max_year; update the Zod object that contains
the fields min_year and max_year (in src/tools/reviews.ts) to add a cross-field
check using Zod's .refine or .superRefine on that object, returning a validation
error message like "min_year must be <= max_year" and adding the error to the
max_year (or both) path when min_year > max_year so invalid ranges are rejected
with a clear error.
- Around line 21-22: The min_year and max_year Zod schema descriptions are
ambiguous about inclusivity; update the descriptors in the schema (the min_year
and max_year fields in the Zod object in src/tools/reviews.ts) to explicitly
state inclusive bounds—e.g., change min_year description to "Filter papers
published in or after this year" and max_year to "Filter papers published in or
before this year"—or, if the API actually uses exclusive bounds, adjust the text
to "after" / "before" to match behavior; ensure the wording in the
z.number().int().optional().describe(...) calls for min_year and max_year
matches the API semantics.
- Around line 82-89: The current truthiness checks around input.min_year and
input.max_year can mis-handle 0; update the condition that builds
filter.pub_year to use explicit null/undefined checks (e.g., input.min_year !=
null || input.max_year != null) and likewise use explicit checks when spreading
the min/max properties (e.g., input.min_year != null && { min: input.min_year })
so filter.pub_year is only created when a year value is actually provided;
locate the logic around filter.pub_year in src/tools/reviews.ts and replace the
truthy checks with these explicit null checks.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 16e5fda0-867f-461e-93a6-73c0c1097a32
📒 Files selected for processing (9)
.env.exampleapi/index.tssrc/api-client.tssrc/dev.tssrc/server.tssrc/tools/authors.tssrc/tools/novelty.tssrc/tools/reviews.tssrc/types.ts
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
src/api-client.ts (1)
220-244: Add runtime response-shape checks for new lookup endpoints.These functions return
res.json()directly as typed results. If backend payloads drift, invalid shapes can flow through and break tool handlers later. Add minimal runtime guards before returning.🧩 Example guard pattern (apply similarly to all 3 endpoints)
export async function searchAuthors( apiKey: string, opts: { @@ ): Promise<SearchAuthorsResult> { @@ - return res.json(); + const json = await res.json() as Partial<SearchAuthorsResult>; + if (!Array.isArray(json.authors) || typeof json.total !== "number") { + throw new Error("Author search returned invalid response shape"); + } + return { authors: json.authors, total: json.total }; }Also applies to: 246-271, 273-295
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/api-client.ts` around lines 220 - 244, The searchAuthors function (and the two other lookup endpoints in this file that currently return res.json() directly) need minimal runtime response-shape validation to avoid silently accepting backend schema drift; add small validators (e.g., validateSearchAuthorsResult) invoked before returning that check required properties and types (for example confirm the top-level object and that expected arrays/fields exist and have correct primitives), and throw a clear Error when the check fails so callers don't receive an invalid shape—apply the same pattern to the other two search/lookup functions in this file.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/api-client.ts`:
- Around line 28-31: The summarizeBody function currently returns raw string
bodies which can leak secrets when SCIWEAVE_MCP_DEBUG_HTTP is enabled; update
summarizeBody (and the analogous logic used around findReferences) to detect and
redact known sensitive JSON fields (e.g., "api_key", "password", "token",
"authorization", "secret", "access_token") before truncation: attempt to
JSON.parse the body string, walk the object to replace sensitive values with
"[REDACTED]" (fall back to regex-based redaction for non-JSON strings), then
serialize back and apply the existing 400-char truncation logic so logs never
contain raw secret values. Ensure the redaction logic is used wherever
summarizeBody is called (notably in findReferences) so all debug HTTP body
logging is sanitized.
- Around line 159-165: The fetchWithDebug call in askWithCitations is malformed:
the signal: AbortSignal.timeout(120_000) was placed after the call closes,
causing a syntax error and no timeout; fix it by moving the signal property into
the RequestInit object passed as the second argument to fetchWithDebug (the same
object that contains method, headers, and body), so the call becomes
fetchWithDebug(url, { method: "POST", headers: webAuthHeaders(apiKey), body:
JSON.stringify(body), signal: AbortSignal.timeout(120_000) },
"ask_research_question") and ensure the parentheses and commas are balanced.
---
Nitpick comments:
In `@src/api-client.ts`:
- Around line 220-244: The searchAuthors function (and the two other lookup
endpoints in this file that currently return res.json() directly) need minimal
runtime response-shape validation to avoid silently accepting backend schema
drift; add small validators (e.g., validateSearchAuthorsResult) invoked before
returning that check required properties and types (for example confirm the
top-level object and that expected arrays/fields exist and have correct
primitives), and throw a clear Error when the check fails so callers don't
receive an invalid shape—apply the same pattern to the other two search/lookup
functions in this file.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
| const res = await fetchWithDebug(`${WEB_API_URL}/api/answer-with-citations`, { | ||
| method: "POST", | ||
| headers: webAuthHeaders(apiKey), | ||
| body: JSON.stringify(body), | ||
| }, "ask_research_question"); | ||
| signal: AbortSignal.timeout(120_000), // 2 min cap for SSE streams | ||
| }); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify the malformed call structure around askWithCitations.
# Expected result: 'signal:' appears after a ');' line in current code.
rg -n -A14 -B4 '\bfetchWithDebug\(\`\$\{WEB_API_URL\}/api/answer-with-citations\`' src/api-client.tsRepository: desci-labs/sciweave-mcp
Length of output: 216
🏁 Script executed:
# Read the specific lines mentioned in the review
sed -n '155,170p' src/api-client.tsRepository: desci-labs/sciweave-mcp
Length of output: 603
🏁 Script executed:
# Also search for fetchWithDebug calls with a simpler pattern
rg -n "fetchWithDebug" src/api-client.ts -A 8Repository: desci-labs/sciweave-mcp
Length of output: 3054
Fix malformed fetchWithDebug call in askWithCitations — syntax error, build-breaking.
Lines 164-165: signal and the closing }); appear outside the RequestInit object, after the function call closes. This is invalid syntax that prevents code from parsing and drops the timeout entirely.
Proposed fix
const res = await fetchWithDebug(`${WEB_API_URL}/api/answer-with-citations`, {
method: "POST",
headers: webAuthHeaders(apiKey),
body: JSON.stringify(body),
+ signal: AbortSignal.timeout(120_000), // 2 min cap for SSE streams
- }, "ask_research_question");
- signal: AbortSignal.timeout(120_000), // 2 min cap for SSE streams
- });
+ }, "ask_research_question");📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const res = await fetchWithDebug(`${WEB_API_URL}/api/answer-with-citations`, { | |
| method: "POST", | |
| headers: webAuthHeaders(apiKey), | |
| body: JSON.stringify(body), | |
| }, "ask_research_question"); | |
| signal: AbortSignal.timeout(120_000), // 2 min cap for SSE streams | |
| }); | |
| const res = await fetchWithDebug(`${WEB_API_URL}/api/answer-with-citations`, { | |
| method: "POST", | |
| headers: webAuthHeaders(apiKey), | |
| body: JSON.stringify(body), | |
| signal: AbortSignal.timeout(120_000), // 2 min cap for SSE streams | |
| }, "ask_research_question"); |
🧰 Tools
🪛 Biome (2.4.10)
[error] 165-165: Expected an expression but instead found '}'.
(parse)
[error] 165-165: Expected a statement but instead found ')'.
(parse)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/api-client.ts` around lines 159 - 165, The fetchWithDebug call in
askWithCitations is malformed: the signal: AbortSignal.timeout(120_000) was
placed after the call closes, causing a syntax error and no timeout; fix it by
moving the signal property into the RequestInit object passed as the second
argument to fetchWithDebug (the same object that contains method, headers, and
body), so the call becomes fetchWithDebug(url, { method: "POST", headers:
webAuthHeaders(apiKey), body: JSON.stringify(body), signal:
AbortSignal.timeout(120_000) }, "ask_research_question") and ensure the
parentheses and commas are balanced.
Summary by CodeRabbit
New Features
Chores