Skip to content

Conversation

@zenflow7
Copy link

@zenflow7 zenflow7 commented Nov 5, 2025

Summary

This PR fixes several MCP SDK compatibility issues and improves the reliability of tool handlers.

Changes

  • Tool handler return types: Updated all tool handlers to return a consistent structuredContent wrapper instead of strict Zod inference, ensuring compatibility with MCP SDK >= 1.19
  • Collection update fix: Added support for parentId parameter when updating collections
  • Bulk edit raindrops rewrite: Completely rewrote handleBulkEditRaindrops to use RaindropService instead of direct API calls, improving error handling and reliability
  • Schema registration fix: Fixed schema registration to extract Zod schema shape directly (zodSchema.shape) instead of converting to JSON Schema, resolving compatibility issues with MCP SDK wrapper

Testing

✅ All tool handlers now return consistent response structures
✅ Bulk operations handle errors gracefully with per-item tracking
✅ Schema registration works correctly with MCP SDK >= 1.19

Related Issues

Fixes compatibility issues with MCP SDK 1.19+ and improves reliability of bulk operations.

antoan and others added 6 commits October 22, 2025 18:52
…/sdk to ^1.20.1\n- convert Zod input schemas to JSON Schema for registerTool\n- fallback to minimal {type:'object'} schema for safety\n- ensure getManifest emits JSON-Schema-friendly tool definitions\n
…efault zod for legacy clients\n- json for MCP SDK 1.19+ validators\n
- Goose 1.11+ requires JSON Schema (no more Zod shapes)
- Remove conditional logic; toObjectJsonSchema() always applied
- Simplifies codebase and aligns with MCP SDK 1.19+ requirements
- zod-to-json-schema 3.24.6 is incompatible with Zod 4.x
- Zod 4.x was producing invalid JSON Schema (type: 'string' for z.object())
- Downgraded zod from ^4.1.9 to ^3.23.8
- Simplified toObjectJsonSchema() function - now works correctly
- Validated with MCP Inspector: all tools pass schema validation
- Goose 1.11.1 still fails with 'keyValidator._parse' error (client bug)
- Fix tool handler return types to use structuredContent wrapper for consistency
- Fix collection update to support parentId parameter
- Rewrite bulk edit raindrops handler to use RaindropService instead of direct API calls
- Fix schema registration to extract Zod schema shape directly instead of converting to JSON Schema
- Improve error handling in bulk operations with per-item error tracking

These changes ensure proper compatibility with MCP SDK >= 1.19 and improve reliability of bulk operations.
@gemini-code-assist
Copy link

Summary of Changes

Hello @rhumphries7, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request primarily focuses on enhancing compatibility with the latest MCP SDK (version 1.19+) and improving the robustness of various tool handlers. The changes ensure that the system's interactions with the SDK are consistent and reliable, particularly for collection management and bulk operations, while also refining how schemas are registered.

Highlights

  • MCP SDK Compatibility: Updated the SDK to version "1.20.1" and adjusted tool handlers and schema registration to align with the new SDK requirements, specifically for versions "1.19+".
  • Consistent Tool Handler Returns: All tool handlers now return a "structuredContent" wrapper, ensuring a consistent response structure compatible with the updated MCP SDK.
  • Collection Update Enhancement: Added support for the "parentId" parameter when updating collections, allowing for better organization.
  • Improved Bulk Edit Operations: The "handleBulkEditRaindrops" function has been completely rewritten to use "RaindropService" for individual bookmark updates, significantly improving error handling and reliability for bulk operations.
  • Schema Registration Fix: Corrected the schema registration process to directly extract the Zod schema shape ("zodSchema.shape") instead of converting to JSON Schema, resolving compatibility issues with the MCP SDK wrapper.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist
Copy link

Summary of Changes

Hello @rhumphries7, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request focuses on a critical update to ensure full compatibility with the latest MCP SDK (version 1.19+), while also significantly enhancing the robustness and functionality of several Raindrop tool handlers. The changes standardize tool output formats, introduce new capabilities for collection management, and drastically improve the reliability of bulk operations. These updates aim to provide a more stable and predictable experience when interacting with the Raindrop.io API through the MCP server.

Highlights

  • MCP SDK Compatibility: Updated tool handlers to return a consistent structuredContent wrapper, ensuring compatibility with MCP SDK versions 1.19 and higher.
  • Collection Management: Added support for the parentId parameter when updating collections, allowing for better organization.
  • Bulk Edit Reliability: Completely rewrote the handleBulkEditRaindrops function to use RaindropService for individual bookmark updates, significantly improving error handling and overall reliability for bulk operations.
  • Schema Registration Fix: Resolved compatibility issues with the MCP SDK wrapper by fixing schema registration to extract the Zod schema shape directly (zodSchema.shape) instead of converting to JSON Schema.
  • Dependency Updates: Upgraded various dependencies, including @modelcontextprotocol/sdk to ^1.20.1 and zod to ^3.23.8, along with other development dependencies.
  • Server Error Handling: Enhanced server-side error handling for optimized Streamable HTTP requests, ensuring graceful error responses.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request addresses several compatibility issues with the MCP SDK and improves the reliability of tool handlers. Key changes include updating dependency versions, ensuring tool handlers return a consistent structuredContent wrapper, adding support for parentId in collection updates, and rewriting the handleBulkEditRaindrops function to use RaindropService for improved error handling. The schema registration has also been fixed to correctly extract Zod schema shapes.

While the changes significantly improve compatibility and robustness, there are a few areas that could be further optimized for performance and type safety, particularly in the handleBulkEditRaindrops function and the use of any for return types.

Comment on lines +393 to 445
async function handleBulkEditRaindrops(args: z.infer<typeof BulkEditRaindropsInputSchema>, { raindropService }: ToolHandlerContext) {
try {
const response = await fetch(url, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
const result = await response.json() as { result: boolean; errorMessage?: string; modified?: number };
if (!result.result) {
throw new Error(result.errorMessage || 'Bulk edit failed');
// Get all bookmarks from the source collection
const bookmarks = await raindropService.getBookmarks({ collection: args.collectionId });
const ids = args.ids || bookmarks.items.map(b => b._id);

if (args.collection && ids.length > 0) {
// Move bookmarks one by one (more reliable than batch endpoint)
let moved = 0;
let errors = 0;

for (const id of ids) {
try {
await raindropService.updateBookmark(id, {
collection: { $id: args.collection.$id } as any
});
moved++;
} catch (err) {
errors++;
// Continue with other bookmarks even if one fails
}
}

if (errors > 0) {
throw new Error(`Moved ${moved} bookmarks, ${errors} failed`);
}

const returnValue = {
content: [{
type: 'text',
text: `Bulk edit successful. Moved ${moved} bookmarks to collection ${args.collection.$id}`,
}],
};
return {
content: returnValue.content,
structuredContent: returnValue,
};
} else {
throw new Error('Collection destination and bookmark IDs are required');
}
return {
content: [{
type: 'text',
text: `Bulk edit successful. Modified: ${result.modified ?? 'unknown'}`,
}],
};
} catch (err) {
return {
const returnValue = {
content: [{
type: 'text',
text: `Bulk edit error: ${(err as Error).message}`,
}],
};
return {
content: returnValue.content,
structuredContent: returnValue,
isError: true,
};
}

Choose a reason for hiding this comment

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

high

The rewrite of handleBulkEditRaindrops currently only handles moving bookmarks to another collection (args.collection). It appears to ignore other bulk edit parameters defined in BulkEditRaindropsInputSchema, such as important, tags, media, cover, and nested. This constitutes a functional regression as these capabilities are no longer supported by the tool.

Additionally, the new implementation fetches all bookmarks and then updates them one by one. While this might be more reliable if the batch endpoint had issues, it can be significantly less efficient for large collections, leading to many individual API calls and potential rate limiting issues. If the Raindrop.io API has a functional batch update endpoint, it would be more performant to utilize it for these other bulk operations.

};
}
} catch (err) {
console.error('Error converting Zod schema to JSON Schema:', err);

Choose a reason for hiding this comment

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

medium

Using console.error directly for logging errors within toObjectJsonSchema can lead to inconsistent logging practices. It's generally better to use a centralized logger (like the logger instance used elsewhere in the project) for all error reporting to ensure consistency and proper log management.

Suggested change
console.error('Error converting Zod schema to JSON Schema:', err);
logger.error('Error converting Zod schema to JSON Schema:', err);


async function handleDiagnostics(_args?: z.infer<typeof DiagnosticsInputSchema>, _context?: ToolHandlerContext): Promise<z.infer<typeof DiagnosticsOutputSchema>> {
return {
async function handleDiagnostics(_args?: z.infer<typeof DiagnosticsInputSchema>, _context?: ToolHandlerContext): Promise<any> {

Choose a reason for hiding this comment

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

medium

Changing the return type of handleDiagnostics to Promise<any> reduces type safety. While the structuredContent wrapper is necessary for MCP SDK compatibility, the internal result variable could still adhere to DiagnosticsOutputSchema to maintain type safety within the function. The structuredContent wrapper can then be applied at the very end, ensuring the function's core logic remains strongly typed.

Suggested change
async function handleDiagnostics(_args?: z.infer<typeof DiagnosticsInputSchema>, _context?: ToolHandlerContext): Promise<any> {
async function handleDiagnostics(_args?: z.infer<typeof DiagnosticsInputSchema>, _context?: ToolHandlerContext): Promise<z.infer<typeof DiagnosticsOutputSchema>> {

Comment on lines +233 to +237
const result = { content };
return {
content: result.content,
structuredContent: result,
};

Choose a reason for hiding this comment

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

medium

Similar to handleDiagnostics, changing the return type to Promise<any> for handleCollectionList reduces type safety. Consider defining an internal type for returnValue that aligns with CollectionListOutputSchema before wrapping it in structuredContent.

Suggested change
const result = { content };
return {
content: result.content,
structuredContent: result,
};
const result: z.infer<typeof CollectionListOutputSchema> = { content };
return {
content: result.content,
structuredContent: result,
};

Comment on lines +286 to +290
const returnValue = { content };
return {
content: returnValue.content,
structuredContent: returnValue,
};

Choose a reason for hiding this comment

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

medium

The return type for handleBookmarkSearch is implicitly any due to the structuredContent wrapper. To maintain type safety, you could define an intermediate variable with the expected BookmarkSearchOutputSchema type before wrapping it for the final return.

Suggested change
const returnValue = { content };
return {
content: returnValue.content,
structuredContent: returnValue,
};
const returnValue: z.infer<typeof BookmarkSearchOutputSchema> = { content };
return {
content: returnValue.content,
structuredContent: returnValue,
};

Comment on lines +368 to +374
const result = {
content: [makeBookmarkLink(bookmark)],
};
return {
content: result.content,
structuredContent: result,
};

Choose a reason for hiding this comment

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

medium

The return type for handleGetRaindrop is implicitly any. To improve type safety, define an intermediate variable with the expected GetRaindropOutputSchema type before wrapping it in structuredContent.

Suggested change
const result = {
content: [makeBookmarkLink(bookmark)],
};
return {
content: result.content,
structuredContent: result,
};
const result: z.infer<typeof GetRaindropOutputSchema> = {
content: [makeBookmarkLink(bookmark)],
};
return {
content: result.content,
structuredContent: result,
};

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request successfully addresses several compatibility issues with the MCP SDK by updating dependency versions, tool handler return structures, and schema registration logic. The refactoring of handleBulkEditRaindrops to use the RaindropService is a positive architectural change. However, this refactoring has led to a critical feature regression, as the new implementation only supports moving bookmarks and has lost other bulk update capabilities. I have also identified a high-severity security concern in a fallback schema generation case and a few medium-severity issues related to type safety and code consistency that should be addressed.

} catch (err) {
console.error('Error converting Zod schema to JSON Schema:', err);
}
return { type: 'object', properties: {}, additionalProperties: true };

Choose a reason for hiding this comment

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

high

The fallback return value in case of an error during Zod to JSON schema conversion is too permissive. Returning a schema with additionalProperties: true could allow arbitrary and unvalidated data to be passed to tools if schema generation fails, which poses a security and data integrity risk. It would be safer to return a more restrictive schema that accepts no properties.

Suggested change
return { type: 'object', properties: {}, additionalProperties: true };
return { type: 'object', properties: {}, additionalProperties: false };


async function handleDiagnostics(_args?: z.infer<typeof DiagnosticsInputSchema>, _context?: ToolHandlerContext): Promise<z.infer<typeof DiagnosticsOutputSchema>> {
return {
async function handleDiagnostics(_args?: z.infer<typeof DiagnosticsInputSchema>, _context?: ToolHandlerContext): Promise<any> {

Choose a reason for hiding this comment

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

medium

The return type of this handler (and others in this file) has been changed to Promise<any>. While this might be for compatibility, using any weakens type safety and makes the code harder to maintain. Consider using a more specific type for the structured response to improve robustness.

For example, a more specific type would be:

Promise<{ content: McpContent[], structuredContent: z.infer<typeof DiagnosticsOutputSchema> }>

This approach can be applied to all other tool handlers to avoid using any.

Suggested change
async function handleDiagnostics(_args?: z.infer<typeof DiagnosticsInputSchema>, _context?: ToolHandlerContext): Promise<any> {
async function handleDiagnostics(_args?: z.infer<typeof DiagnosticsInputSchema>, _context?: ToolHandlerContext): Promise<{ content: any[], structuredContent: any }> {

Comment on lines +286 to +290
const returnValue = { content };
return {
content: returnValue.content,
structuredContent: returnValue,
};

Choose a reason for hiding this comment

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

medium

There's an inconsistent use of variable names for the result object across different tool handlers. This function uses returnValue, while others like handleCollectionList and handleGetRaindrop use result. For better code consistency and readability, it would be good to standardize on one name, for example result, across all handlers. This also applies to handleListRaindrops and handleBulkEditRaindrops.

Suggested change
const returnValue = { content };
return {
content: returnValue.content,
structuredContent: returnValue,
};
const result = { content };
return {
content: result.content,
structuredContent: result,
};

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.

2 participants