Skip to content

feat: add new mcp tools#4

Open
ogbanugot wants to merge 3 commits into
mainfrom
add-more-tools
Open

feat: add new mcp tools#4
ogbanugot wants to merge 3 commits into
mainfrom
add-more-tools

Conversation

@ogbanugot

@ogbanugot ogbanugot commented Apr 6, 2026

Copy link
Copy Markdown

Summary by CodeRabbit

  • New Features

    • Author search by name/ORCID/OpenAlex
    • Retrieve and filter an author's works (year range, citations, open access)
    • Novelty-score lookup for papers
    • Automated literature-review generation with difficulty and format options
    • Connector UI now exposes the expanded set of tools
  • Chores

    • Dev-only opt-in HTTP request/response debug logging (documented in env sample)
    • Improved dev server startup/configuration and diagnostic output

@vercel

vercel Bot commented Apr 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
sciweave-mcp Ready Ready Preview, Comment Apr 7, 2026 8:34pm

Request Review

@coderabbitai

coderabbitai Bot commented Apr 6, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7723cf80-0841-40d5-bd3b-04519b0c543f

📥 Commits

Reviewing files that changed from the base of the PR and between c38ce88 and b5ee269.

📒 Files selected for processing (1)
  • src/api-client.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/api-client.ts

📝 Walkthrough

Walkthrough

Adds 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 SCIWEAVE_MCP_DEBUG_HTTP, updates dev startup/env handling, and exposes the new tools in connector metadata.

Changes

Cohort / File(s) Summary
Configuration & Dev Startup
\.env.example, src/dev.ts
Added optional SCIWEAVE_MCP_DEBUG_HTTP entry (commented) to .env.example; dev loader attempts to load .env, uses dynamic imports, and reads SCIWEAVE_ML_BACKEND_URL, SCIWEAVE_WEB_API_URL, and SCIWEAVE_MCP_DEBUG_HTTP for runtime config and startup logging.
API Metadata
api/index.ts
Expanded connector metadata/tool list and HTML rendering to include find_references, search_authors, get_works_by_author, lookup_novelty_score, generate_literature_review.
API Client & Types
src/api-client.ts, src/types.ts
Added opt-in HTTP debug wrapper fetchWithDebug (driven by SCIWEAVE_MCP_DEBUG_HTTP) and replaced existing fetch calls with it; added three client functions (searchAuthors, getWorksByAuthor, lookupNoveltyScore) and five new TypeScript interfaces (AuthorMatch, SearchAuthorsResult, AuthorWork, WorksByAuthorResult, NoveltyScoreResult).
MCP Server Tool Registrations
src/server.ts
Registered four new MCP tools (search_authors, get_works_by_author, lookup_novelty_score, generate_literature_review) by importing schemas/wrappers and calling server.registerTool.
Tool Implementations
src/tools/authors.ts, src/tools/novelty.ts, src/tools/reviews.ts
Added three tool modules with Zod schemas, input normalization, API-client calls, formatted markdown/text outputs, and error handling for: author search/listing, works-by-author, novelty score lookup, and literature-review generation.

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
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 I hopped through lines to add new springs,
Authors, works, and novelty things,
Reviews that gather papers bright,
Debug logs peeking in the night,
A scholarly hop — small code, big wings.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 46.15% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add new mcp tools' directly and accurately summarizes the main change—introducing multiple new MCP tool implementations (search_authors, get_works_by_author, lookup_novelty_score, generate_literature_review) across the codebase.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch add-more-tools

Comment @coderabbitai help to get the list of available commands and usage tips.

@ogbanugot ogbanugot changed the title add new mcp tools feat: add new mcp tools Apr 6, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 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 uses searchAuthorsSchema (which requires at least one of query, orcid, or openalex_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 to lookup_novelty_score.

Similar to search_authors, this uses lookupNoveltyScoreInputSchema for registration but validates with the refined schema that requires at least one of doi or work_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 for min_year <= max_year.

Neither schema validates that min_year does not exceed max_year. Providing min_year: 2025, max_year: 2020 would 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_year conventionally 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 || ... and input.min_year && ...) would treat 0 as falsy. While year 0 is 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

📥 Commits

Reviewing files that changed from the base of the PR and between e017d26 and fd12aa7.

📒 Files selected for processing (9)
  • .env.example
  • api/index.ts
  • src/api-client.ts
  • src/dev.ts
  • src/server.ts
  • src/tools/authors.ts
  • src/tools/novelty.ts
  • src/tools/reviews.ts
  • src/types.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b66d0cc4-8751-4854-8478-50b4f45bb532

📥 Commits

Reviewing files that changed from the base of the PR and between fd12aa7 and c38ce88.

📒 Files selected for processing (1)
  • src/api-client.ts

Comment thread src/api-client.ts
Comment thread src/api-client.ts Outdated
Comment on lines 159 to 165
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
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 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.ts

Repository: 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.ts

Repository: 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 8

Repository: 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.

Suggested change
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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant