-
Notifications
You must be signed in to change notification settings - Fork 60k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support MCP( WIP) #5974
base: main
Are you sure you want to change the base?
Support MCP( WIP) #5974
Conversation
Deployment failed with the following error:
View Documentation: https://vercel.com/docs/accounts/team-members-and-roles |
WalkthroughThis pull request introduces a comprehensive implementation of the Multi-Channel Protocol (MCP) system for NextChat. The changes span multiple files and include new modules for client management, server configuration, and user interface components. The implementation adds robust support for dynamic server interactions, tool management, and a new market page for exploring and configuring MCP servers. The changes enhance the application's extensibility and provide users with more advanced AI interaction capabilities. Changes
Sequence DiagramsequenceDiagram
participant User
participant McpMarketPage
participant McpActions
participant McpClient
User->>McpMarketPage: Open MCP Market
McpMarketPage->>McpActions: Get MCP Configuration
McpActions-->>McpMarketPage: Return Configuration
User->>McpMarketPage: Add New Server
McpMarketPage->>McpActions: Initialize Client
McpActions->>McpClient: Create Client
McpClient-->>McpActions: Client Created
McpActions-->>McpMarketPage: Server Added
Poem
📜 Recent review detailsConfiguration used: CodeRabbit UI ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
Your build has completed! |
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
There was a problem hiding this comment.
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 (9)
app/store/chat.ts (1)
429-447
: Validate JSON parsing & protect against malformed message content
- The regular expression approach to identify ```json:mcp blocks is functional, but consider graceful fallbacks if the block is missing or incomplete.
- You already catch JSON parsing errors, which is good. In case of invalid JSON from user-provided data, ensuring a user-facing error response could be beneficial.
- If multiple MCP calls can happen concurrently, verify the system’s concurrency handling to avoid race conditions.
app/mcp/actions.ts (1)
1-33
: Leverage stronger typing and ensure concurrency safety
- Currently,
fsClient
is typed asany
—consider adding an explicit interface or type to make maintenance easier and reduce accidental misuse.- When multiple calls happen in quick succession, ensure
initFileSystemClient
can safely handle simultaneous initialization requests.app/mcp/logger.ts (1)
1-60
: Ensure cross-platform log compatibility
- ANSI color codes might not display as intended across all terminals (e.g., some CI/CD environments or Windows shells).
- Consider adding timestamps or structured logging for easier troubleshooting and correlation with other logs.
app/mcp/client.ts (4)
1-5
: Use absolute imports selectively.Imports from the "@modelcontextprotocol" package and Zod library are fine. However, ensure that these external imports remain minimal for faster builds and better maintainability. If future code only requires submodules, consider selective imports from large packages (e.g.,
import { ... } from "@modelcontextprotocol/sdk/client"
).
6-10
: Clarify optional fields inServerConfig
interface.Currently,
args
andenv
are optional. Provide doc comments describing the scenarios in which these fields would be omitted to improve code clarity.
44-47
: ValidatePrimitive
structure and values.As
Primitive.value
is typed toany
, adding a Zod schema or another validation layer for resource, tool, or prompt objects can improve type safety and help catch unexpected data structures from the server.
83-87
: Use structured logging for request execution.Currently, you only do
console.log(r)
after executing the request. Consider usinglogger.info
or a more structured approach to log request/response pairs for debugging and auditing.... const r = client.request(request, z.any()); - console.log(r); + logger.info("Request response:", r); return r; }tsconfig.json (1)
26-26
: Cleanup references to removed files ininclude
.Removing
"app/calcTextareaHeight.ts"
from theinclude
array is fine. Ensure that all references (e.g., imports) to that file are also removed throughout the codebase to avoid confusion.package.json (1)
25-25
: Leverage the@modelcontextprotocol/sdk
dependency effectively.Ensure that all relevant modules from the new SDK are utilized. If only a small subset is needed, consider partial imports or code splitting to reduce bundle size.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
yarn.lock
is excluded by!**/yarn.lock
,!**/*.lock
📒 Files selected for processing (9)
app/mcp/actions.ts
(1 hunks)app/mcp/client.ts
(1 hunks)app/mcp/example.ts
(1 hunks)app/mcp/logger.ts
(1 hunks)app/mcp/mcp_config.ts
(1 hunks)app/store/chat.ts
(2 hunks)next.config.mjs
(3 hunks)package.json
(2 hunks)tsconfig.json
(2 hunks)
🔇 Additional comments (8)
app/store/chat.ts (1)
32-32
: Add test coverage for 'executeMcpAction' usage
The new import integrates MCP action processing into the chat flow. To reduce the risk of unhandled edge cases (e.g., unavailable server, invalid JSON structures, etc.), please ensure the code path invoking executeMcpAction
is tested thoroughly.
app/mcp/mcp_config.ts (1)
1-40
: Secure storage of credentials and externalize paths
- The placeholders for API keys and tokens should never be replaced by real secrets in the repository; instead, reading from environment variables or a vault is recommended.
- The path
"/Users/kadxy/Desktop"
is hard-coded. Consider making this path configurable for different environments.
app/mcp/example.ts (1)
1-92
: Harden the file reading mechanism
- The code showcases reading arbitrary files from a specified path. Confirm that server-side controls or directory whitelisting is in place to guard against unauthorized access.
- Good job using Zod schemas for validation—this helps ensure the integrity of server responses.
app/mcp/client.ts (1)
49-81
: Parallel asynchronous operations are well handled; keep an eye on performance.
The approach of collecting promises in an array and awaiting them simultaneously is a good practice. For large-scale data, ensure the server handles concurrent calls effectively to avoid resource contention or throttling.
next.config.mjs (2)
35-35
: Ensure experimental features are stable enough for production.
serverActions: true
is an experimental feature. Confirm that your production environment accepts potential breaking changes or instabilities. Consider adding a feature gate or fallback logic.
75-78
: Maintain consistent pattern for rewrite placeholders.
Rewrite placeholders, such as :resource_name
and :deploy_name
, are used in the destination URL. Confirm that you handle all edge cases (like missing or invalid resource_name
/ deploy_name
). Potentially add server-side validation to avoid runtime errors.
tsconfig.json (1)
3-3
: Check compatibility of ES2022 target.
Confirm that your deployment environment supports ES2022 features, or use a transpilation step if targeting older browsers. Some hosting platforms may not yet fully support ES2022 natively.
package.json (1)
57-58
: Check versions & synergy for zod
and zustand
.
zod
is now widely used for schema validation. Keep an eye on potential conflicts when using zustand
for state management if any store-based validations are needed.
app/mcp/client.ts
Outdated
export async function createClient( | ||
serverConfig: ServerConfig, | ||
name: string, | ||
): Promise<Client> { | ||
logger.info(`Creating client for server ${name}`); | ||
|
||
const transport = new StdioClientTransport({ | ||
command: serverConfig.command, | ||
args: serverConfig.args, | ||
env: serverConfig.env, | ||
}); | ||
const client = new Client( | ||
{ | ||
name: `nextchat-mcp-client-${name}`, | ||
version: "1.0.0", | ||
}, | ||
{ | ||
capabilities: { | ||
roots: { | ||
// listChanged indicates whether the client will emit notifications when the list of roots changes. | ||
// listChanged 指示客户端在根列表更改时是否发出通知。 | ||
listChanged: true, | ||
}, | ||
}, | ||
}, | ||
); | ||
await client.connect(transport); | ||
return client; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider robust error handling for client creation.
When creating the client and connecting to the transport (line 40), include error handling for potential failures (e.g., invalid command, environment issues). This ensures unexpected errors are caught gracefully.
...
await client.connect(transport);
+ // TODO: Consider wrapping this in try/catch:
+ // try {
+ // await client.connect(transport);
+ // } catch (err) {
+ // logger.error('Failed to connect to transport', err);
+ // throw err;
+ // }
return client;
}
Committable suggestion skipped: line range outside the PR's diff.
@@ -49,11 +50,12 @@ | |||
"remark-breaks": "^3.0.2", | |||
"remark-gfm": "^3.0.1", | |||
"remark-math": "^5.1.1", | |||
"rt-client": "https://github.com/Azure-Samples/aoai-realtime-audio-sdk/releases/download/js/v0.5.0/rt-client-0.5.0.tgz", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codebase verification
Warning: The rt-client
dependency is actively used and cannot be removed
The rt-client
package is an essential dependency that is actively used in the realtime chat functionality:
- It's imported and used extensively in
app/components/realtime-chat/realtime-chat.tsx
- Core functionalities depend on it:
- Real-time audio streaming and processing
- Voice chat features
- Server communication for real-time responses
- Audio transcription and playback
Removing this dependency would break the real-time chat feature. The package should be kept unless there's a planned replacement or the real-time chat functionality is being removed.
🔗 Analysis chain
Confirm removal of the deprecated rt-client
dependency.
Verify that no modules still rely on rt-client
. Skipping this verification might lead to runtime errors if references remain.
Let me gather more information about the actual usage of rt-client in the code.
Let me check the actual usage in the realtime-chat.tsx file to understand if this is an active dependency.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Searching for references to rt-client in the codebase:
rg 'rt-client' -A 3 -B 3
Length of output: 2671
Script:
#!/bin/bash
# Check for imports and actual usage of rt-client in the realtime components
ast-grep --pattern 'import { $$$ } from "rt-client"'
# Check for any direct usage of rt-client package
rg -l 'new RTClient|RTClient\.'
Length of output: 675
Script:
#!/bin/bash
# Check the implementation in realtime-chat.tsx
cat app/components/realtime-chat/realtime-chat.tsx
Length of output: 10998
There was a problem hiding this comment.
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 (12)
app/mcp/actions.ts (3)
7-17
: Global variables and maps
Storing clients in a globalMap
and a globalinitialized
flag may be problematic in multi-instance or serverless environments. Consider a more robust design (e.g., an initialization manager) if concurrency or horizontal scaling are required.
18-51
: initializeMcpClients function
- Code loops over
conf.mcpServers
and initializes each client with proper logging.- Catches errors and tracks failed clients in
errorClients
.- Marks
initialized = true
to prevent reinitialization.- Logs final status and available clients.
This flow is user-friendly and understandable. Consider adding retries or fallback logic for partial initialization failures.
53-70
: executeMcpAction function
- Fetches the client from
clientsMap
by ID and executes the request.- Logs any execution errors and rethrows them.
Consider returning a standardized error object for better client-side handling, especially if this function may be extended for user-facing error messages in the future.app/store/chat.ts (2)
363-367
: MCP check on new messages
Line 366 callscheckMcpJson
whenever a new message arrives. This integration ensures automatic detection of MCP requests without additional code in the message pipeline. Be sure to handle potential performance impacts if message volume is very high.
773-798
: checkMcpJson function
- Validates if message content matches MCP JSON format.
- On success, extracts the MCP payload and invokes
executeMcpAction
.- Uses
onUserInput
to feed back results as a new message.
This is a clean, modular approach. However, watch for large JSON payloads or malicious content. Consider adding rate-limits or size checks for production.app/mcp/utils.ts (2)
1-3
: isMcpJson function
Returns the match result of a regex. This function is concise but slightly at risk of returning null or array. Consider returning a boolean explicitly (!!content.match(...)
) if you only need a truthy check.
5-11
: extractMcpJson function
CapturesclientId
and parses JSON from the second capture group. The approach is straightforward. A try-catch block for JSON parsing (or an upfront validity check) might guard against syntax errors or maliciously malformed JSON.app/page.tsx (1)
9-10
: Await MCP initialization
Blocking initialization ensures MCP dependencies are ready before UI rendering. Ensure that this call won’t degrade page responsiveness for users if the initialization is slow. Consider deferring or parallelizing if needed.app/mcp/logger.ts (1)
24-65
: Logging methods
- Provides functions for info, success, error, warn, and debug with consistent output formatting.
debug
is controlled by a flag to prevent noisy logs.- A structured
formatMessage
method safely handles objects.
Well-structured, though consider a fallback or custom transport in production to integrate with external logging systems (e.g., Winston, Bunyan).app/mcp/client.ts (3)
32-35
: Document or remove commented capabilities configuration.The commented capabilities configuration lacks explanation. Either document why it's preserved for future use or remove it if unnecessary.
28-28
: Consider extracting version to a configuration constant.The hardcoded version "1.0.0" should be moved to a configuration constant for better maintainability.
+const CLIENT_VERSION = "1.0.0"; export async function createClient( serverConfig: ServerConfig, name: string, ): Promise<Client> { // ... const client = new Client( { name: `nextchat-mcp-client-${name}`, - version: "1.0.0", + version: CLIENT_VERSION, }, // ... );
52-76
: Refactor duplicate promise handling pattern.The promise handling pattern is repeated for resources, tools, and prompts. Consider extracting this to a helper function.
+const createPrimitivePromise = ( + client: Client, + type: Primitive["type"], + listFn: () => Promise<{ [key: string]: any[] }> +) => { + return listFn().then((result) => { + const items = result[`${type}s`]; + items.forEach((item) => primitives.push({ type, value: item })); + }); +}; if (capabilities?.resources) { - promises.push( - client.listResources().then(({ resources }) => { - resources.forEach((item) => - primitives.push({ type: "resource", value: item }), - ); - }), - ); + promises.push(createPrimitivePromise(client, "resource", () => client.listResources())); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
yarn.lock
is excluded by!**/yarn.lock
,!**/*.lock
📒 Files selected for processing (10)
.eslintignore
(1 hunks)app/mcp/actions.ts
(1 hunks)app/mcp/client.ts
(1 hunks)app/mcp/example.ts
(1 hunks)app/mcp/logger.ts
(1 hunks)app/mcp/mcp_config.json
(1 hunks)app/mcp/utils.ts
(1 hunks)app/page.tsx
(1 hunks)app/store/chat.ts
(4 hunks)package.json
(3 hunks)
✅ Files skipped from review due to trivial changes (2)
- .eslintignore
- app/mcp/mcp_config.json
🚧 Files skipped from review as they are similar to previous changes (2)
- app/mcp/example.ts
- package.json
🔇 Additional comments (10)
app/mcp/actions.ts (3)
1-2
: Use of "use server" directive
These lines establish a server-only execution context. Be mindful of referencing browser-specific APIs in server files to avoid runtime errors.
3-6
: Import dependencies
Imports for createClient
, executeRequest
, MCPClientLogger
, and the JSON config look consistent with the intended usage. Ensure that underlying modules handle errors gracefully.
72-77
: getAvailableClients function
Filters out clients that failed to initialize. This is straightforward and aligns with the error management strategy. Ensure future expansions remain in sync (e.g., re-checking previously error-prone clients).
app/store/chat.ts (2)
32-33
: New MCP imports
Imports from mcp/actions
and mcp/utils
introduce the MCP functionality into the chat store. This is a logical location given the store’s role in handling messages.
434-434
: Asynchronous onFinish callback
This line suggests the onFinish
callback is asynchronous. Confirm that all references to onFinish
handle promises and potential errors correctly to avoid unhandled promise rejections.
app/page.tsx (1)
4-4
: Import initializeMcpClients
This sets up a new dependency on actions.ts
. Verify that all required environment variables and config references are available at runtime.
app/mcp/logger.ts (2)
1-10
: Color codes definition
Defines ANSI codes for styling the log output. Straightforward and correct for Node.js environments.
12-22
: Logger constructor and fields
The prefix
and debugMode
options provide flexible logging. This is a neat approach for customizing logs.
app/mcp/client.ts (2)
1-10
: LGTM! Well-structured imports and interface definition.
The ServerConfig interface is well-designed with appropriate optional parameters for args and env configuration.
14-40
: Add error handling for client creation and connection.
The client creation process should include proper error handling for transport creation and connection failures.
app/mcp/client.ts
Outdated
|
||
interface Primitive { | ||
type: "resource" | "tool" | "prompt"; | ||
value: any; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Replace 'any' type with a more specific type.
Using 'any' type reduces type safety. Consider defining specific types for resource, tool, and prompt values.
app/mcp/client.ts
Outdated
/** Execute a request */ | ||
export async function executeRequest(client: Client, request: any) { | ||
return client.request(request, z.any()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Improve request validation and error handling.
The current implementation has several concerns:
- Using
z.any()
bypasses the benefits of Zod's type validation - No request validation before execution
- No error handling for failed requests
Consider implementing proper validation and error handling:
-export async function executeRequest(client: Client, request: any) {
- return client.request(request, z.any());
+export async function executeRequest(client: Client, request: unknown) {
+ const requestSchema = z.object({
+ // Define your request schema here
+ // Example:
+ // method: z.string(),
+ // params: z.record(z.unknown())
+ });
+
+ try {
+ const validatedRequest = requestSchema.parse(request);
+ return await client.request(validatedRequest, z.unknown());
+ } catch (error) {
+ if (error instanceof z.ZodError) {
+ throw new Error(`Invalid request format: ${error.message}`);
+ }
+ throw error;
+ }
+}
Committable suggestion skipped: line range outside the PR's diff.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (6)
app/store/chat.ts (2)
Line range hint
376-402
: Enhance type safety in onUserInput function.The function could benefit from improved type safety:
- The
isMcpResponse
parameter should have a default value since it's optional- The content type assertion should be more explicit
Consider applying these changes:
async onUserInput( content: string, attachImages?: string[], - isMcpResponse?: boolean, + isMcpResponse: boolean = false, ) { const session = get().currentSession(); const modelConfig = session.mask.modelConfig; - let mContent: string | MultimodalContent[] = isMcpResponse + let mContent: string | MultimodalContent[] = isMcpResponse === true ? content : fillTemplateWith(content, modelConfig);
782-810
: Enhance error handling and maintainability in checkMcpJson.Consider the following improvements:
- Add specific error type handling
- Add debug logging for the response formatting
- Extract JSON response formatting to a separate function
Consider applying these changes:
checkMcpJson(message: ChatMessage) { const content = getMessageTextContent(message); if (isMcpJson(content)) { try { const mcpRequest = extractMcpJson(content); if (mcpRequest) { console.debug("[MCP Request]", mcpRequest); executeMcpAction(mcpRequest.clientId, mcpRequest.mcp) .then((result) => { console.log("[MCP Response]", result); - const mcpResponse = - typeof result === "object" - ? JSON.stringify(result) - : String(result); + const mcpResponse = formatMcpResponse(result); + console.debug("[MCP Formatted Response]", mcpResponse); get().onUserInput( `\`\`\`json:mcp:${mcpRequest.clientId}\n${mcpResponse}\n\`\`\``, [], true, ); }) - .catch((error) => showToast(String(error))); + .catch((error) => { + console.error("[MCP Execution Error]", error); + showToast(error instanceof Error ? error.message : String(error)); + }); } - } catch (error) { + } catch (error: unknown) { console.error("[MCP Error]", error); + showToast("Failed to process MCP request"); } } }, + function formatMcpResponse(result: unknown): string { + return typeof result === "object" ? JSON.stringify(result) : String(result); + }app/mcp/types.ts (2)
1-2
: Optional: Confirm the specification link remains stable.The comment references an external spec URL. If it’s crucial, ensure the link remains valid. If the spec is versioned, consider pinning a specific version for documentation stability.
21-32
: Response interface structure is flexible and aligns well with standard JSON-RPC patterns.The
error
structure is appropriately typed, ensuring systematic error reporting. Consider adding clearer distinction for success vs. error states if your logic requires it.app/mcp/client.ts (2)
43-46
: Primitive interface uses 'any'.Using
any
type forvalue
reduces type safety. Define a union or more explicit shape if the data is known.-interface Primitive { - type: "resource" | "tool" | "prompt"; - value: any; -} +interface Resource { /* shape */} +interface Tool { /* shape */} +interface Prompt { /* shape */} +type PrimitiveValue = Resource | Tool | Prompt; + +interface Primitive { + type: "resource" | "tool" | "prompt"; + value: PrimitiveValue; +}
48-80
: listPrimitives merges capability checks with asynchronous calls.
- The approach is valid and merges results from multiple promises.
- Consider an early break if no capabilities exist to optimize performance in some edge cases.
- See if any concurrency issues might arise if the server is large or if many resources are loaded at once.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
app/mcp/actions.ts
(1 hunks)app/mcp/client.ts
(1 hunks)app/mcp/types.ts
(1 hunks)app/store/chat.ts
(7 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- app/mcp/actions.ts
🔇 Additional comments (14)
app/store/chat.ts (4)
Line range hint 1-37
: LGTM! Import changes are well-organized.
The new imports for MCP functionality are properly structured and necessary for the implementation.
62-62
: LGTM! Type extension is clean and minimal.
The addition of isMcpResponse
to ChatMessage type is well-typed and follows TypeScript best practices.
368-372
: LGTM! Clean integration of MCP checking.
The addition of checkMcpJson
call is well-placed and maintains the existing message processing flow.
Line range hint 1-810
: Verify integration with existing chat functionality.
Since this is a WIP PR adding MCP support, please ensure:
- The changes don't affect existing chat functionality
- Error states are properly propagated to the UI
- MCP responses don't break the chat history display
✅ Verification successful
Based on the verification results, I can now provide a final response:
MCP integration maintains existing chat functionality with proper error handling
The code review shows that the MCP (Message Control Protocol) integration:
-
Properly maintains existing chat functionality:
- Core message handling (
onNewMessage
,onUserInput
) remains intact - Message rendering and display components are unaffected
- Existing error states and UI feedback are preserved
- Core message handling (
-
Has appropriate error handling:
- MCP errors are caught and displayed via
showToast
- Failed MCP actions are properly logged:
console.error("[MCP Error]", error)
- Error states are propagated to the UI through existing message error flags
- MCP errors are caught and displayed via
-
Safely integrates with chat history:
- MCP responses are clearly marked with
isMcpResponse
flag - Messages are properly added to chat history with correct formatting
- Template handling is properly skipped for MCP responses
- MCP responses are clearly marked with
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check for potential regressions in chat functionality
echo "Checking for chat message handling..."
rg -A 5 "onUserInput|onNewMessage" --type ts
echo "Checking for error handling patterns..."
rg "showToast|console\.error" --type ts
echo "Checking for message display components..."
rg -A 5 "ChatMessage|renderMessage" --type tsx
Length of output: 10897
Script:
#!/bin/bash
# Let's check for message display components with proper extension
echo "Checking for message display components..."
rg -A 5 "ChatMessage|renderMessage" --type ts --type tsx
# Check for MCP-related changes
echo "Checking for MCP-related code..."
rg -A 5 "mcp|MCP" --type ts --type tsx
# Check for error handling in chat store
echo "Checking error handling in chat store..."
ast-grep --pattern 'onError(error) {
$$$
}'
Length of output: 464
Script:
#!/bin/bash
# Let's try with just ts extension first
echo "Checking for message display components..."
rg -A 5 "ChatMessage|renderMessage" ./*.ts ./app/**/*.ts
echo "Checking for MCP-related code..."
rg -A 5 "mcp|MCP" ./*.ts ./app/**/*.ts
echo "Checking error handling in chat store..."
rg -A 5 "onError.*{" ./app/store/chat.ts
# Check for message rendering components
echo "Checking message components..."
fd -e tsx . -x cat {} | rg -A 5 "ChatMessage|renderMessage"
Length of output: 23199
app/mcp/types.ts (5)
5-12
: Interfaces with typed methods and flexible params are well structured.
The McpRequestMessage
interface design uses a union for the id
and retains flexibility in params
. This is good for requests where the schema can evolve over time.
14-20
: Schema validation ensures robust request handling.
Good use of Zod for optional jsonrpc
and union types for id
. This schema can mitigate malformed requests by typechecking at runtime.
34-47
: Robust schema for JSON-RPC responses.
The optional error
object’s shape is well-defined. Ensure the client code handles partial responses (e.g., result
or error
fields) gracefully.
49-55
: Encapsulated notifications match JSON-RPC patterns.
Separating notifications into a distinct interface clarifies the difference between requests and notifications.
57-61
: Notifications schema aligns with the interface.
The use of z.record(z.unknown())
for params
preserves flexibility while still enforcing structural constraints.
app/mcp/client.ts (5)
1-6
: Imports are consistent and descriptive.
All necessary dependencies (Client
, StdioClientTransport
, custom logger, and McpRequestMessage
) are imported cleanly. The use of zod
is appropriate for runtime type checks.
7-11
: ServerConfig is concise and extendable.
Storing command, arguments, and environment variables in an interface fosters clarity. If future expansions are likely (e.g., working directories, timeouts), consider grouping them under optional sub-objects or adding additional properties.
13-14
: Logger usage is straightforward.
A dedicated logger is beneficial for debugging and operational visibility. Ensure logging levels are adjustable in production environments if needed.
15-41
: createClient handles basic client setup.
- Logging the server name is helpful for troubleshooting.
- Consider error handling around
client.connect
in case the process fails to start or times out.
Would you like a script to search for unhandled promise rejections around connect
calls and propose structured error-handling code if discovered?
82-88
: 🛠️ Refactor suggestion
executeRequest returns data without intermediate validation or error handling.
Even though z.any()
is used, you might want stronger type checks if the response format is critical. Also, consider how errors from client.request
are caught and propagated.
export async function executeRequest(
client: Client,
request: McpRequestMessage,
) {
- return client.request(request, z.any());
+ try {
+ const response = await client.request(request, z.unknown());
+ // Potentially parse the response with the McpResponseMessageSchema
+ return McpResponseMessageSchema.parse(response);
+ } catch (err) {
+ logger.error('Failed to execute MCP request', err);
+ throw err;
+ }
}
Likely invalid or redundant comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
app/mcp/actions.ts (1)
66-86
: Enhance error handling with specific error types.The error handling could be more specific to help with debugging and user feedback.
export async function executeMcpAction( clientId: string, request: McpRequestMessage, ) { try { const client = clientsMap.get(clientId)?.client; if (!client) { - logger.error(`Client ${clientId} not found`); - return; + throw new Error(`MCP client '${clientId}' not found`); } logger.info(`Executing MCP request for ${clientId}`); return await executeRequest(client, request); } catch (error) { - logger.error(`MCP execution error: ${error}`); - throw error; + const errorMessage = error instanceof Error ? error.message : String(error); + logger.error(`MCP execution error for client '${clientId}':`, errorMessage); + throw new Error(`Failed to execute MCP action: ${errorMessage}`); } }app/store/chat.ts (1)
813-840
: Enhance error handling in MCP JSON processing.The error handling could be more specific and provide better user feedback.
checkMcpJson(message: ChatMessage) { const content = getMessageTextContent(message); if (isMcpJson(content)) { try { const mcpRequest = extractMcpJson(content); if (mcpRequest) { console.debug("[MCP Request]", mcpRequest); executeMcpAction(mcpRequest.clientId, mcpRequest.mcp) .then((result) => { console.log("[MCP Response]", result); const mcpResponse = typeof result === "object" ? JSON.stringify(result) : String(result); get().onUserInput( `\`\`\`json:mcp-response:${mcpRequest.clientId}\n${mcpResponse}\n\`\`\``, [], true, ); }) - .catch((error) => showToast("MCP execution failed", error)); + .catch((error) => { + const errorMessage = error instanceof Error ? error.message : String(error); + showToast( + "MCP Execution Failed", + `Failed to execute MCP action for client '${mcpRequest.clientId}': ${errorMessage}` + ); + console.error("[MCP Execution Error]", error); + }); } } catch (error) { - console.error("[MCP Error]", error); + const errorMessage = error instanceof Error ? error.message : String(error); + console.error("[MCP Processing Error]", error); + showToast( + "MCP Processing Failed", + `Failed to process MCP message: ${errorMessage}` + ); } } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
app/components/chat.tsx
(11 hunks)app/constant.ts
(1 hunks)app/mcp/actions.ts
(1 hunks)app/mcp/client.ts
(1 hunks)app/mcp/example.ts
(1 hunks)app/store/chat.ts
(11 hunks)
✅ Files skipped from review due to trivial changes (1)
- app/components/chat.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- app/mcp/example.ts
🔇 Additional comments (7)
app/constant.ts (2)
256-261
: LGTM! Well-structured template for MCP primitives.The template clearly separates clientId and primitives sections with good formatting.
263-365
: LGTM! Comprehensive system template with clear documentation.The template provides:
- Clear sections for tools, usage rules, and interaction flow
- Well-documented examples
- Proper markdown formatting for code blocks
app/mcp/client.ts (3)
43-46
: 🛠️ Refactor suggestionReplace 'any' type with a more specific type.
The
value
field uses theany
type which reduces type safety. Consider defining a proper type or interface.export interface Primitive { type: "resource" | "tool" | "prompt"; - value: any; + value: { + name: string; + description?: string; + parameters?: Record<string, unknown>; + }; }Likely invalid or redundant comment.
82-88
: 🛠️ Refactor suggestionImprove request validation.
The function uses
z.any()
which bypasses Zod's type validation benefits. Consider implementing proper validation schema.export async function executeRequest( client: Client, request: McpRequestMessage, ) { - return client.request(request, z.any()); + const responseSchema = z.object({ + // Define expected response schema + result: z.unknown(), + error: z.string().optional(), + }); + return client.request(request, responseSchema); }Likely invalid or redundant comment.
15-41
:⚠️ Potential issueAdd error handling for client connection.
The client connection lacks error handling which could lead to unhandled promise rejections.
export async function createClient( serverConfig: ServerConfig, name: string, ): Promise<Client> { logger.info(`Creating client for server ${name}`); const transport = new StdioClientTransport({ command: serverConfig.command, args: serverConfig.args, env: serverConfig.env, }); const client = new Client( { name: `nextchat-mcp-client-${name}`, version: "1.0.0", }, { capabilities: { // roots: { // listChanged: true, // }, }, }, ); - await client.connect(transport); + try { + await client.connect(transport); + } catch (error) { + logger.error(`Failed to connect client ${name}:`, error); + throw new Error(`Failed to initialize MCP client ${name}: ${error.message}`); + } return client; }Likely invalid or redundant comment.
app/mcp/actions.ts (1)
28-63
: LGTM! Well-implemented initialization with proper error handling.The function includes:
- Singleton pattern to prevent multiple initializations
- Comprehensive error handling
- Good logging of success and failure cases
app/store/chat.ts (1)
201-219
: LGTM! Well-implemented system prompt generation.The function effectively:
- Filters relevant primitives
- Handles template replacement
- Maintains clear structure
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 7
🧹 Nitpick comments (14)
app/constant.ts (1)
264-366
: Consider enhancing the system template documentation.The template is well-structured with comprehensive documentation. Consider these improvements:
- Add version information to track template changes
- Use more realistic paths in examples (e.g.,
~/Desktop
orC:\Users\username\Desktop
)export const MCP_SYSTEM_TEMPLATE = ` +// Template Version: 1.0.0 You are an AI assistant with access to system tools... 5. EXAMPLE INTERACTION: User: "What files do I have on my desktop?" Assistant: "I'll check which directories I have access to. \`\`\`json:mcp:filesystem { "method": "tools/call", "params": { "name": "list_allowed_directories", "arguments": {} } } \`\`\`" User: "\`\`\`json:mcp-response:filesystem { - "directories": ["/path/to/desktop"] + "directories": ["~/Desktop"] } \`\`\`"app/mcp/actions.ts (3)
52-68
: ConsolidatereinitializeMcpClients
andrestartAllClients
FunctionsThe functions
reinitializeMcpClients
(lines 52-68) andrestartAllClients
(lines 179-194) perform similar actions: closing clients, clearing states, and reinitializing MCP clients. To avoid code duplication and improve maintainability, consider refactoringrestartAllClients
to callreinitializeMcpClients
.Apply this refactor:
export async function restartAllClients() { logger.info("Restarting all MCP clients..."); - // 清空状态 - clientsMap.clear(); - errorClients = []; - initialized = false; - - // 重新初始化 - await initializeMcpClients(); + // Delegate to reinitializeMcpClients + await reinitializeMcpClients(); return { success: errorClients.length === 0, errorClients, }; }
123-127
: Improve Error Handling When Client Is Not FoundIn the
executeMcpAction
function (lines 123-127), when a client is not found, the function logs an error but does not throw an exception or provide feedback to the caller. This could lead to silent failures. Consider throwing an error to ensure that the calling function can handle this scenario appropriately.Apply this change:
const client = clientsMap.get(clientId)?.client; if (!client) { logger.error(`Client ${clientId} not found`); - return; + throw new Error(`Client ${clientId} not found`); }
197-216
: CombinegetAllClientStatus
andgetClientErrors
FunctionsThe functions
getAllClientStatus
(lines 197-205) andgetClientErrors
(lines 208-216) both return a record of client IDs to their error messages. To reduce redundancy, consider combining these functions into one or clarifying their distinct purposes if they are intended to provide different information.app/components/mcp-market.tsx (2)
185-250
: RefactorrenderConfigForm
to Improve ReadabilityThe
renderConfigForm
function (lines 181-250) handles multiple input types and contains nested loops and conditional logic, making it complex. Consider refactoring by extracting input field rendering into separate components to enhance readability and maintainability.
436-438
: Handle Long Descriptions in Server InfoIn the server info section (lines 436-438), long descriptions may overflow or break the layout. Consider adding CSS styles to handle text overflow, such as ellipsis, or provide tooltips for full descriptions.
app/components/home.tsx (1)
77-83
: Maintain Consistent Import OrderThe import order of
useEffect
anduseState
(line 5) deviates from the convention used in the rest of the file, where hooks are typically imported after other modules. Consider reorganizing the imports for consistency.app/mcp/types.ts (2)
5-61
: LGTM! Well-structured message types with proper validation.The MCP message interfaces and schemas are well-defined and properly validated using Zod. The implementation follows the JSON-RPC 2.0 specification.
Consider adding:
- JSDoc comments for better IDE integration
- Examples of valid message structures
- Constants for common error codes
Example:
/** MCP request message following JSON-RPC 2.0 specification */ export interface McpRequestMessage { /** JSON-RPC version. Must be "2.0" when present */ jsonrpc?: "2.0"; // ... rest of the interface } /** Common MCP error codes */ export const McpErrorCodes = { PARSE_ERROR: -32700, INVALID_REQUEST: -32600, METHOD_NOT_FOUND: -32601, // ... add more error codes } as const;
63-99
: LGTM! Well-designed server configuration types.The server configuration interfaces provide good type safety and flexibility for different server configurations.
Consider adding:
- Type guards for runtime validation
- Utility functions for configuration validation
- Constants for common server types
Example:
export const SERVER_TYPES = { FILESYSTEM: 'filesystem', DOCKER: 'docker-mcp', POSTGRES: 'postgres', // ... add more server types } as const; export type ServerType = typeof SERVER_TYPES[keyof typeof SERVER_TYPES]; export function isValidServerConfig(config: unknown): config is ServerConfig { // Add runtime validation logic }app/mcp/preset-server.json (2)
74-78
: Enhance database connection securityBoth MongoDB and PostgreSQL configurations use plain connection strings. Consider:
- Supporting connection options (SSL, timeout, etc.)
- Implementing credential management
- Adding connection pooling configuration
Also applies to: 138-142
161-165
: Add API configuration safeguardsFor both Brave Search and Google Maps APIs, consider adding:
- Rate limiting configuration
- Error handling guidance
- API key rotation support
Also applies to: 184-188
app/components/mcp-market.module.scss (3)
86-95
: Extract status colors to CSS variablesReplace hardcoded colors with CSS variables for consistency:
- background-color: #10b981; + background-color: var(--status-success); - background-color: #ef4444; + background-color: var(--status-error); - background-color: #f59e0b; + background-color: var(--status-warning);
188-389
: Reduce style duplication with mixinsConsider extracting common input styles into SCSS mixins:
- Form input styles
- Button hover/focus states
- Common transitions
574-612
: Move global styles to a separate fileGlobal styles should be moved to a dedicated global stylesheet to:
- Prevent scope conflicts
- Improve maintainability
- Better separate concerns
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
app/icons/mcp.svg
is excluded by!**/*.svg
yarn.lock
is excluded by!**/yarn.lock
,!**/*.lock
📒 Files selected for processing (13)
app/components/home.tsx
(4 hunks)app/components/mcp-market.module.scss
(1 hunks)app/components/mcp-market.tsx
(1 hunks)app/components/sidebar.tsx
(2 hunks)app/constant.ts
(2 hunks)app/locales/cn.ts
(1 hunks)app/mcp/actions.ts
(1 hunks)app/mcp/mcp_config.json
(1 hunks)app/mcp/preset-server.json
(1 hunks)app/mcp/types.ts
(1 hunks)app/mcp/utils.ts
(1 hunks)app/store/chat.ts
(11 hunks)next.config.mjs
(2 hunks)
✅ Files skipped from review due to trivial changes (1)
- next.config.mjs
🚧 Files skipped from review as they are similar to previous changes (2)
- app/mcp/utils.ts
- app/store/chat.ts
🔇 Additional comments (11)
app/constant.ts (3)
50-50
: LGTM! Path addition follows conventions.The new
McpMarket
path is well-structured and consistent with existing path definitions.
257-262
: LGTM! Clean and focused template structure.The template follows the existing patterns and provides a clear structure for clientId and primitives interpolation.
257-367
: LGTM! Well-integrated with existing constants.The new MCP-related constants are properly placed within the file's organization, maintaining logical grouping and following existing patterns.
app/mcp/actions.ts (1)
23-66
:⚠️ Potential issueEnsure Thread-Safe Access to Shared State Variables
The module uses shared state variables (
clientsMap
,initialized
,errorClients
) without synchronization mechanisms. If functions likeinitializeMcpClients
orreinitializeMcpClients
are called concurrently, it may lead to race conditions or inconsistent state. Consider implementing synchronization to ensure thread-safe operations.To verify concurrency issues, please review how and where these functions are invoked. If they can be called concurrently, synchronization mechanisms like mutexes or atomic variables should be introduced to protect shared state.
app/components/mcp-market.tsx (1)
253-270
: Ensure Robust Error Handling When Loading PrimitivesIn the
loadPrimitives
function (lines 253-270), if the server is not running, an empty array is set forprimitives
. Ensure that components consumingprimitives
can handle empty arrays without errors to prevent potential runtime issues.app/components/home.tsx (1)
77-83
: Verify Dynamic Import Path forMcpMarketPage
Ensure that the dynamic import path
'./mcp-market'
(lines 77-83) correctly resolves to theMcpMarketPage
component. This is important for the component to load properly, especially in build and deployment environments.app/components/sidebar.tsx (1)
254-262
: LGTM!The new MCP icon button is well-integrated into the sidebar, following the existing patterns for icon buttons and conditional rendering.
app/locales/cn.ts (1)
629-631
: LGTM!The MCP localization entry follows the established pattern and maintains consistency with other localization entries.
app/mcp/preset-server.json (3)
11-16
: Add path validation constraints for filesystem accessThe filesystem paths configuration lacks validation constraints. Consider adding:
- Path pattern restrictions
- Absolute/relative path validation
- Directory traversal prevention
35-39
: Specify required GitHub token scopesThe GitHub PAT configuration should specify the minimum required scopes for security. Consider adding:
- List of required scopes in the description
- Scope validation in the implementation
199-204
: Enhance Docker configuration securityThe Docker module lacks essential configuration options. Consider adding:
- Container resource limits
- Security options (e.g., no-new-privileges)
- Network access controls
app/mcp/actions.ts
Outdated
} catch (error) { | ||
console.error("Failed to read MCP config:", error); | ||
return { mcpServers: {} }; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Handle Missing Configuration File Gracefully
In the getMcpConfig
function (lines 35-38), if the configuration file is missing (e.g., on the first run), it logs an error. Instead, consider checking if the error is due to the file not existing (ENOENT
) and initialize with a default configuration without logging an error. This provides a smoother user experience.
Apply this change:
} catch (error) {
- console.error("Failed to read MCP config:", error);
- return { mcpServers: {} };
+ if (error.code === 'ENOENT') {
+ logger.warn("MCP config file not found, initializing with default configuration.");
+ return { mcpServers: {} };
+ } else {
+ logger.error("Failed to read MCP config:", error);
+ throw error;
+ }
}
📝 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.
} catch (error) { | |
console.error("Failed to read MCP config:", error); | |
return { mcpServers: {} }; | |
} | |
} catch (error) { | |
if (error.code === 'ENOENT') { | |
logger.warn("MCP config file not found, initializing with default configuration."); | |
return { mcpServers: {} }; | |
} else { | |
logger.error("Failed to read MCP config:", error); | |
throw error; | |
} | |
} |
app/components/mcp-market.tsx
Outdated
|
||
try { | ||
// 构建服务器配置 | ||
const args = [...preset.baseArgs]; | ||
const env: Record<string, string> = {}; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add Null Checks for preset.configSchema
Before Use
In the saveServerConfig
function (lines 130-135), preset.configSchema
is accessed without null checks. If preset.configSchema
is undefined or null, it may cause runtime errors. Ensure that preset.configSchema
is validated before accessing its properties.
Apply this change:
if (!preset || !preset.configSchema || !editingServerId) return;
+ if (!preset.configSchema) {
+ showToast("Server configuration schema is missing.");
+ return;
+ }
Committable suggestion skipped: line range outside the PR's diff.
app/components/mcp-market.tsx
Outdated
const bError = clientErrors[b.id] !== null; | ||
|
||
if (aAdded !== bAdded) { | ||
return aAdded ? -1 : 1; | ||
} | ||
if (aAdded && bAdded) { | ||
if (aError !== bError) { | ||
return aError ? -1 : 1; | ||
} | ||
} | ||
return 0; | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct Sorting Logic for Server Error Status
In the server list sorting logic (lines 389-404), the comparison of aError
and bError
may be inverted, causing servers with errors to appear before those without. Adjust the sorting condition to ensure servers without errors are prioritized.
Apply this fix:
if (aAdded && bAdded) {
if (aError !== bError) {
- return aError ? -1 : 1;
+ return aError ? 1 : -1;
}
}
📝 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 bError = clientErrors[b.id] !== null; | |
if (aAdded !== bAdded) { | |
return aAdded ? -1 : 1; | |
} | |
if (aAdded && bAdded) { | |
if (aError !== bError) { | |
return aError ? -1 : 1; | |
} | |
} | |
return 0; | |
}) | |
const bError = clientErrors[b.id] !== null; | |
if (aAdded !== bAdded) { | |
return aAdded ? -1 : 1; | |
} | |
if (aAdded && bAdded) { | |
if (aError !== bError) { | |
return aError ? 1 : -1; | |
} | |
} | |
return 0; | |
}) |
app/mcp/mcp_config.json
Outdated
{ | ||
"mcpServers": { | ||
"filesystem": { | ||
"command": "npx", | ||
"args": [ | ||
"-y", | ||
"@modelcontextprotocol/server-filesystem", | ||
"/Users/kadxy/Desktop" | ||
] | ||
}, | ||
"docker-mcp": { | ||
"command": "uvx", | ||
"args": ["docker-mcp"] | ||
}, | ||
"difyworkflow": { | ||
"command": "mcp-difyworkflow-server", | ||
"args": ["-base-url", "23"], | ||
"env": { | ||
"DIFY_WORKFLOW_NAME": "23", | ||
"DIFY_API_KEYS": "23" | ||
} | ||
}, | ||
"postgres": { | ||
"command": "docker", | ||
"args": ["run", "-i", "--rm", "mcp/postgres", null] | ||
}, | ||
"playwright": { | ||
"command": "npx", | ||
"args": ["-y", "@executeautomation/playwright-mcp-server"] | ||
}, | ||
"gdrive": { | ||
"command": "npx", | ||
"args": ["-y", "@modelcontextprotocol/server-gdrive"] | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add documentation and validation guidance.
The configuration file lacks documentation for server configurations and validation requirements.
Consider adding:
- A documentation section describing each server type and its purpose
- Required environment variables for each server
- Validation rules for configuration parameters
- Error handling guidance
Example structure:
{
"documentation": {
"version": "1.0",
"servers": {
"filesystem": {
"description": "Local filesystem MCP server",
"required_env": ["MCP_FILESYSTEM_PATH"],
"validation": {
"MCP_FILESYSTEM_PATH": "Must be a valid directory path"
}
},
// ... other servers
}
},
"mcpServers": {
// ... existing configurations
}
}
app/mcp/mcp_config.json
Outdated
"postgres": { | ||
"command": "docker", | ||
"args": ["run", "-i", "--rm", "mcp/postgres", null] | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix null argument in postgres configuration.
The postgres server configuration contains a null
argument which could cause runtime issues.
Remove the null argument or replace it with a valid value:
- "args": ["run", "-i", "--rm", "mcp/postgres", null]
+ "args": ["run", "-i", "--rm", "mcp/postgres"]
📝 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.
"postgres": { | |
"command": "docker", | |
"args": ["run", "-i", "--rm", "mcp/postgres", null] | |
}, | |
"postgres": { | |
"command": "docker", | |
"args": ["run", "-i", "--rm", "mcp/postgres"] | |
}, |
app/mcp/mcp_config.json
Outdated
"difyworkflow": { | ||
"command": "mcp-difyworkflow-server", | ||
"args": ["-base-url", "23"], | ||
"env": { | ||
"DIFY_WORKFLOW_NAME": "23", | ||
"DIFY_API_KEYS": "23" | ||
} | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace placeholder values in difyworkflow configuration.
The configuration contains placeholder values "23" for base URL and API keys, which are not valid production values.
Consider using environment variables:
- "args": ["-base-url", "23"],
+ "args": ["-base-url", "${DIFY_BASE_URL}"],
"env": {
- "DIFY_WORKFLOW_NAME": "23",
- "DIFY_API_KEYS": "23"
+ "DIFY_WORKFLOW_NAME": "${DIFY_WORKFLOW_NAME}",
+ "DIFY_API_KEYS": "${DIFY_API_KEYS}"
}
📝 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.
"difyworkflow": { | |
"command": "mcp-difyworkflow-server", | |
"args": ["-base-url", "23"], | |
"env": { | |
"DIFY_WORKFLOW_NAME": "23", | |
"DIFY_API_KEYS": "23" | |
} | |
}, | |
"difyworkflow": { | |
"command": "mcp-difyworkflow-server", | |
"args": ["-base-url", "${DIFY_BASE_URL}"], | |
"env": { | |
"DIFY_WORKFLOW_NAME": "${DIFY_WORKFLOW_NAME}", | |
"DIFY_API_KEYS": "${DIFY_API_KEYS}" | |
} | |
}, |
app/mcp/mcp_config.json
Outdated
"filesystem": { | ||
"command": "npx", | ||
"args": [ | ||
"-y", | ||
"@modelcontextprotocol/server-filesystem", | ||
"/Users/kadxy/Desktop" | ||
] | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove hardcoded filesystem path.
The filesystem server configuration contains a hardcoded path /Users/kadxy/Desktop
which will not work across different environments and could pose security risks.
Consider using environment variables or configuration parameters for the path:
- "/Users/kadxy/Desktop"
+ "${MCP_FILESYSTEM_PATH}"
📝 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.
"filesystem": { | |
"command": "npx", | |
"args": [ | |
"-y", | |
"@modelcontextprotocol/server-filesystem", | |
"/Users/kadxy/Desktop" | |
] | |
}, | |
"filesystem": { | |
"command": "npx", | |
"args": [ | |
"-y", | |
"@modelcontextprotocol/server-filesystem", | |
"${MCP_FILESYSTEM_PATH}" | |
] | |
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 7
🧹 Nitpick comments (6)
app/mcp/actions.ts (2)
43-47
: Improve variable naming for clarity ingetAvailableClientsCount
Using
map
as the parameter name in theforEach
method can be confusing becauseclientsMap
is aMap
object. Consider renaming the parameter toclientData
orclient
to improve readability.Apply this diff to rename the parameter:
clientsMap.forEach((map) => { + // Rename 'map' to 'clientData' - clientsMap.forEach((map) => { + clientsMap.forEach((clientData) => { if (!map.errorMsg) { - count += map?.tools?.tools?.length ?? 0; + count += clientData?.tools?.tools?.length ?? 0; } });
204-206
: Simplify the catch block inupdateMcpConfig
The catch block in the
updateMcpConfig
function only rethrows the original error without adding any context. You can simplify the code by removing thetry/catch
block entirely, allowing the error to propagate naturally.Apply this diff to remove the unnecessary
try/catch
block:async function updateMcpConfig(config: McpConfigData): Promise<void> { - try { await fs.writeFile(CONFIG_PATH, JSON.stringify(config, null, 2)); - } catch (error) { - throw error; - } }app/mcp/preset-server.json (1)
3-6
: Fix duplicated text in description.The description for the Filesystem server contains repeated text: "Secure file operations with configurable access controls" appears multiple times.
Apply this fix:
- "description": "Secure file operations with configurable access controlsSecure file operations with configurable access controlsSecure file operations with configurable access controlsSecure file operations with configurable access controls", + "description": "Secure file operations with configurable access controls",app/store/chat.ts (1)
Line range hint
399-425
: Optimize MCP system prompt inclusion and validate MCP responsesThe message handling could be improved in two areas:
- Conditional inclusion of MCP system prompt
- Validation of MCP response format
Consider these improvements:
async onUserInput( content: string, attachImages?: string[], isMcpResponse?: boolean, ) { + if (isMcpResponse && !content.includes('mcp-response')) { + console.warn('[MCP] Invalid response format'); + return; + } // ... rest of the code } // In getMessagesWithMemory -const mcpSystemPrompt = await getMcpSystemPrompt(); +const mcpSystemPrompt = modelConfig.enableMcp ? await getMcpSystemPrompt() : '';Also applies to: 550-569
app/mcp/types.ts (1)
106-115
: Enhance type safety forArgsMapping
interface.The position property should be constrained to non-negative integers, and the type property could use a more specific union type.
export interface ArgsMapping { // 参数映射的类型 - type: "spread" | "single" | "env"; + type: ArgsType; // 参数映射的位置 - position?: number; + position?: NonNegativeInteger; // 参数映射的 key key?: string; } + +type ArgsType = "spread" | "single" | "env"; +type NonNegativeInteger = number & { __brand: "NonNegativeInteger" };app/components/mcp-market.tsx (1)
372-506
: Optimize server list rendering performance.The current implementation recalculates filtered and sorted servers on every render. Consider:
- Memoizing the filtered list
- Using virtualization for large lists
- Moving sorting logic to a useMemo hook
+const useSortedServers = (servers: PresetServer[], searchText: string, clientStatuses: Record<string, any>) => { + return useMemo(() => { + return servers + .filter((server) => { + if (searchText.length === 0) return true; + const searchLower = searchText.toLowerCase(); + return ( + server.name.toLowerCase().includes(searchLower) || + server.description.toLowerCase().includes(searchLower) || + server.tags.some((tag) => tag.toLowerCase().includes(searchLower)) + ); + }) + .sort((a, b) => { + // ... existing sorting logic + }); + }, [servers, searchText, clientStatuses]); +}; const renderServerList = () => { - return presetServers - .filter(...) - .sort(...) + const sortedServers = useSortedServers(presetServers, searchText, clientStatuses); + return sortedServers .map((server) => ( // ... existing render logic )); };
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
app/icons/tool.svg
is excluded by!**/*.svg
📒 Files selected for processing (14)
.gitignore
(1 hunks)app/components/chat.tsx
(15 hunks)app/components/home.tsx
(6 hunks)app/components/mcp-market.module.scss
(1 hunks)app/components/mcp-market.tsx
(1 hunks)app/constant.ts
(3 hunks)app/mcp/actions.ts
(1 hunks)app/mcp/client.ts
(1 hunks)app/mcp/example.ts
(1 hunks)app/mcp/mcp_config.json
(1 hunks)app/mcp/preset-server.json
(1 hunks)app/mcp/types.ts
(1 hunks)app/page.tsx
(1 hunks)app/store/chat.ts
(11 hunks)
✅ Files skipped from review due to trivial changes (1)
- .gitignore
🚧 Files skipped from review as they are similar to previous changes (4)
- app/mcp/mcp_config.json
- app/mcp/example.ts
- app/components/mcp-market.module.scss
- app/components/chat.tsx
🧰 Additional context used
🪛 Biome (1.9.4)
app/mcp/actions.ts
[error] 214-214: The catch clause that only rethrows the original error is useless.
An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.
(lint/complexity/noUselessCatch)
🔇 Additional comments (8)
app/mcp/actions.ts (1)
194-197
: Handle missing configuration file gracefullyIf the configuration file is missing (e.g., on the first run), the function logs an error. Instead, consider checking if the error is due to the file not existing (
ENOENT
) and initialize with a default configuration without logging an error. This provides a smoother user experience.Apply this diff to handle the missing file scenario:
} catch (error) { - logger.error(`Failed to load MCP config, using default config: ${error}`); - return DEFAULT_MCP_CONFIG; + if (error.code === 'ENOENT') { + logger.warn("MCP config file not found, initializing with default configuration."); + return DEFAULT_MCP_CONFIG; + } else { + logger.error(`Failed to load MCP config: ${error}`); + throw error; + } }app/mcp/client.ts (2)
30-31
: Consider robust error handling for client creationWhen creating the client and connecting to the transport, include error handling for potential failures (e.g., invalid command, environment issues). This ensures unexpected errors are caught gracefully.
Apply this change to add error handling:
await client.connect(transport); + // Wrap the connection in a try/catch block + try { + await client.connect(transport); + } catch (error) { + logger.error(`Failed to connect transport for client [${id}]: ${error}`); + throw error; + }
47-47
: Implement proper request validation and error handling inexecuteRequest
Using
z.any()
bypasses the benefits of Zod's type validation. Consider defining a specific schema for the request and implementing error handling for failed requests to improve robustness.Apply this change to enhance validation:
export async function executeRequest( client: Client, request: McpRequestMessage, ) { - return client.request(request, z.any()); + try { + const requestSchema = z.object({ + // Define your request schema here + // Example: + // method: z.string(), + // params: z.record(z.unknown()) + }); + const validatedRequest = requestSchema.parse(request); + return await client.request(validatedRequest, z.unknown()); + } catch (error) { + if (error instanceof z.ZodError) { + throw new Error(`Invalid request format: ${error.message}`); + } + throw error; + } }app/mcp/preset-server.json (1)
224-225
: Verify theuvx
command availability.The Docker MCP server uses the
uvx
command, which is uncommon. Verify if this is the correct command or if it should benpx
instead.✅ Verification successful
The
uvx
command appears to be correctThis is a specific command used by the Docker MCP preset (https://github.com/QuantGeekDev/docker-mcp) for managing Docker containers and logs. While uncommon, it appears to be intentionally set as
uvx
and not a typo ofnpx
.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for uvx usage in the repository rg -l 'uvx' # Check if there's any documentation about uvx fd -t f -e md -e txt . | xargs rg 'uvx'Length of output: 98
Script:
#!/bin/bash # Check package.json files for uvx-related entries fd package.json --exec cat {} | jq -r '.' # Search for docker-mcp usage for more context rg -A 5 -B 5 "docker-mcp" # Look for setup or installation scripts fd -t f -e sh -e bash . | xargs rg "uvx"Length of output: 4154
app/store/chat.ts (1)
Line range hint
1-64
: Clean implementation of MCP-related imports and type definitions!The new imports and type definitions are well-structured and follow TypeScript best practices.
app/components/home.tsx (1)
248-254
: LGTM! Well-implemented error handling.The implementation properly handles initialization errors and provides user feedback through toast notifications.
app/constant.ts (1)
258-367
: LGTM! Well-structured templates with clear documentation.The MCP templates provide comprehensive instructions and examples for tool usage.
app/components/mcp-market.tsx (1)
140-195
: 🛠️ Refactor suggestionEnhance error handling in
saveServerConfig
.The function could benefit from more specific error handling and validation:
- Validate user input before processing
- Handle specific error types separately
- Provide more detailed error messages
const saveServerConfig = async () => { const preset = presetServers.find((s) => s.id === editingServerId); - if (!preset || !preset.configSchema || !editingServerId) return; + if (!preset) { + showToast("Server preset not found"); + return; + } + if (!preset.configSchema) { + showToast("Server configuration schema is missing"); + return; + } + if (!editingServerId) { + showToast("No server selected for configuration"); + return; + } try { setIsLoading(true); + // Validate user input + const validationErrors = validateUserConfig(userConfig, preset.configSchema); + if (validationErrors.length > 0) { + throw new Error(`Invalid configuration: ${validationErrors.join(", ")}`); + } // ... rest of the function } catch (error) { + if (error instanceof ValidationError) { + showToast(`Configuration error: ${error.message}`); + } else if (error instanceof NetworkError) { + showToast("Failed to connect to server"); + } else { showToast( error instanceof Error ? error.message : "Failed to save configuration", ); + } } };Likely invalid or redundant comment.
"id": "mongodb", | ||
"name": "MongoDB", | ||
"description": "Direct interaction with MongoDB databases", | ||
"repo": "", | ||
"tags": ["database", "mongodb", "nosql"], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codebase verification
Invalid MongoDB server configuration detected
The MongoDB server configuration appears to be invalid or incomplete:
- The repository URL is empty
- The referenced implementation at "dist/index.js" doesn't exist in the codebase
- No MongoDB-related implementation files were found
🔗 Analysis chain
Add missing repository URL.
The MongoDB server configuration has an empty repository URL. This should either be filled with a valid URL or a comment should be added explaining why it's empty.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check if the repo exists in the organization
gh api graphql -f query='
{
search(query: "org:modelcontextprotocol server-mongodb", type: REPOSITORY, first: 1) {
nodes {
... on Repository {
url
}
}
}
}'
Length of output: 222
Script:
#!/bin/bash
# Check the content of preset-server.json for patterns
cat app/mcp/preset-server.json
# Search for any MongoDB-related repositories in the organization
gh api graphql -f query='
{
search(query: "org:modelcontextprotocol mongodb", type: REPOSITORY, first: 10) {
nodes {
... on Repository {
name
url
}
}
}
}'
Length of output: 6719
Script:
#!/bin/bash
# Check if MongoDB implementation exists in the codebase
fd mongodb
Length of output: 12
Script:
#!/bin/bash
# Search for MongoDB-related files with various patterns
fd -i mongodb
# Search for MongoDB-related code patterns
rg -i "mongodb|mongo" --type js --type ts -l
# Look specifically for the dist/index.js referenced in the config
fd -i "index.js" -p "dist/"
Length of output: 181
async function getMcpSystemPrompt(): Promise<string> { | ||
const tools = await getAllTools(); | ||
|
||
let toolsStr = ""; | ||
|
||
tools.forEach((i) => { | ||
// error client has no tools | ||
if (!i.tools) return; | ||
|
||
toolsStr += MCP_TOOLS_TEMPLATE.replace( | ||
"{{ clientId }}", | ||
i.clientId, | ||
).replace( | ||
"{{ tools }}", | ||
i.tools.tools.map((p: object) => JSON.stringify(p, null, 2)).join("\n"), | ||
); | ||
}); | ||
|
||
return MCP_SYSTEM_TEMPLATE.replace("{{ MCP_TOOLS }}", toolsStr); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add error handling and validation in getMcpSystemPrompt
The function needs improvements in several areas:
- Error handling for getAllTools() failure
- Validation of tool structure
- Memory optimization for large tool sets
Consider this implementation:
async function getMcpSystemPrompt(): Promise<string> {
- const tools = await getAllTools();
+ try {
+ const tools = await getAllTools();
+ let toolsStr = "";
- let toolsStr = "";
+ for (const tool of tools) {
+ if (!tool?.tools?.tools?.length) continue;
- tools.forEach((i) => {
- // error client has no tools
- if (!i.tools) return;
+ toolsStr += MCP_TOOLS_TEMPLATE.replace(
+ "{{ clientId }}",
+ tool.clientId,
+ ).replace(
+ "{{ tools }}",
+ tool.tools.tools
+ .slice(0, 1000) // Limit tools to prevent memory issues
+ .map((p: object) => JSON.stringify(p, null, 2))
+ .join("\n"),
+ );
+ }
- toolsStr += MCP_TOOLS_TEMPLATE.replace(
- "{{ clientId }}",
- i.clientId,
- ).replace(
- "{{ tools }}",
- i.tools.tools.map((p: object) => JSON.stringify(p, null, 2)).join("\n"),
- );
- });
-
- return MCP_SYSTEM_TEMPLATE.replace("{{ MCP_TOOLS }}", toolsStr);
+ return MCP_SYSTEM_TEMPLATE.replace("{{ MCP_TOOLS }}", toolsStr);
+ } catch (error) {
+ console.error("[MCP] Failed to generate system prompt:", error);
+ return MCP_SYSTEM_TEMPLATE.replace("{{ MCP_TOOLS }}", "");
+ }
}
📝 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.
async function getMcpSystemPrompt(): Promise<string> { | |
const tools = await getAllTools(); | |
let toolsStr = ""; | |
tools.forEach((i) => { | |
// error client has no tools | |
if (!i.tools) return; | |
toolsStr += MCP_TOOLS_TEMPLATE.replace( | |
"{{ clientId }}", | |
i.clientId, | |
).replace( | |
"{{ tools }}", | |
i.tools.tools.map((p: object) => JSON.stringify(p, null, 2)).join("\n"), | |
); | |
}); | |
return MCP_SYSTEM_TEMPLATE.replace("{{ MCP_TOOLS }}", toolsStr); | |
} | |
async function getMcpSystemPrompt(): Promise<string> { | |
try { | |
const tools = await getAllTools(); | |
let toolsStr = ""; | |
for (const tool of tools) { | |
if (!tool?.tools?.tools?.length) continue; | |
toolsStr += MCP_TOOLS_TEMPLATE.replace( | |
"{{ clientId }}", | |
tool.clientId, | |
).replace( | |
"{{ tools }}", | |
tool.tools.tools | |
.slice(0, 1000) // Limit tools to prevent memory issues | |
.map((p: object) => JSON.stringify(p, null, 2)) | |
.join("\n"), | |
); | |
} | |
return MCP_SYSTEM_TEMPLATE.replace("{{ MCP_TOOLS }}", toolsStr); | |
} catch (error) { | |
console.error("[MCP] Failed to generate system prompt:", error); | |
return MCP_SYSTEM_TEMPLATE.replace("{{ MCP_TOOLS }}", ""); | |
} | |
} |
/** check if the message contains MCP JSON and execute the MCP action */ | ||
checkMcpJson(message: ChatMessage) { | ||
const content = getMessageTextContent(message); | ||
if (isMcpJson(content)) { | ||
try { | ||
const mcpRequest = extractMcpJson(content); | ||
if (mcpRequest) { | ||
console.debug("[MCP Request]", mcpRequest); | ||
|
||
executeMcpAction(mcpRequest.clientId, mcpRequest.mcp) | ||
.then((result) => { | ||
console.log("[MCP Response]", result); | ||
const mcpResponse = | ||
typeof result === "object" | ||
? JSON.stringify(result) | ||
: String(result); | ||
get().onUserInput( | ||
`\`\`\`json:mcp-response:${mcpRequest.clientId}\n${mcpResponse}\n\`\`\``, | ||
[], | ||
true, | ||
); | ||
}) | ||
.catch((error) => showToast("MCP execution failed", error)); | ||
} | ||
} catch (error) { | ||
console.error("[Check MCP JSON]", error); | ||
} | ||
} | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Address security concerns in MCP JSON handling
The current implementation needs security improvements:
- Validate mcpRequest structure
- Add rate limiting and timeouts
- Sanitize error messages
Consider these security improvements:
checkMcpJson(message: ChatMessage) {
const content = getMessageTextContent(message);
if (isMcpJson(content)) {
try {
const mcpRequest = extractMcpJson(content);
- if (mcpRequest) {
+ if (mcpRequest && validateMcpRequest(mcpRequest)) {
console.debug("[MCP Request]", mcpRequest);
- executeMcpAction(mcpRequest.clientId, mcpRequest.mcp)
+ Promise.race([
+ executeMcpAction(mcpRequest.clientId, mcpRequest.mcp),
+ new Promise((_, reject) =>
+ setTimeout(() => reject(new Error('MCP timeout')), 5000)
+ )
+ ])
.then((result) => {
console.log("[MCP Response]", result);
const mcpResponse =
typeof result === "object"
? JSON.stringify(result)
: String(result);
get().onUserInput(
`\`\`\`json:mcp-response:${mcpRequest.clientId}\n${mcpResponse}\n\`\`\``,
[],
true,
);
})
- .catch((error) => showToast("MCP execution failed", error));
+ .catch((error) => {
+ console.error("[MCP] Execution failed:", error);
+ showToast("MCP execution failed", "Action could not be completed");
+ });
}
} catch (error) {
console.error("[Check MCP JSON]", error);
+ showToast("MCP execution failed", "Invalid request format");
}
}
}
+function validateMcpRequest(request: any): boolean {
+ return (
+ request &&
+ typeof request.clientId === 'string' &&
+ request.mcp &&
+ typeof request.mcp === 'object'
+ );
+}
Committable suggestion skipped: line range outside the PR's diff.
// 初始化 MCP 系统 | ||
await initializeMcpSystem(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider handling initialization errors gracefully.
The MCP system initialization is performed without error handling, which could lead to unhandled promise rejections. Consider adding a try-catch block to handle initialization failures gracefully.
- await initializeMcpSystem();
+ try {
+ await initializeMcpSystem();
+ } catch (error) {
+ console.error("Failed to initialize MCP system:", error);
+ // Consider showing an error message to the user or implementing a retry mechanism
+ }
📝 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.
// 初始化 MCP 系统 | |
await initializeMcpSystem(); | |
// 初始化 MCP 系统 | |
try { | |
await initializeMcpSystem(); | |
} catch (error) { | |
console.error("Failed to initialize MCP system:", error); | |
// Consider showing an error message to the user or implementing a retry mechanism | |
} |
export interface ListToolsResponse { | ||
tools: { | ||
name?: string; | ||
description?: string; | ||
inputSchema?: object; | ||
[key: string]: any; | ||
}; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Improve type safety for ListToolsResponse
interface.
The interface uses [key: string]: any
which bypasses TypeScript's type checking. Consider defining a more specific type for additional properties or removing the index signature if not needed.
export interface ListToolsResponse {
tools: {
name?: string;
description?: string;
inputSchema?: object;
- [key: string]: any;
+ // If additional properties are needed, define them explicitly:
+ // parameters?: Record<string, unknown>;
+ // version?: string;
};
}
📝 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.
export interface ListToolsResponse { | |
tools: { | |
name?: string; | |
description?: string; | |
inputSchema?: object; | |
[key: string]: any; | |
}; | |
} | |
export interface ListToolsResponse { | |
tools: { | |
name?: string; | |
description?: string; | |
inputSchema?: object; | |
}; | |
} |
<input | ||
type="text" | ||
className={styles["search-bar"]} | ||
placeholder={"Search MCP Server"} | ||
autoFocus | ||
onInput={(e) => setSearchText(e.currentTarget.value)} | ||
/> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add accessibility improvements to the search input.
The search input lacks proper accessibility attributes.
<input
type="text"
className={styles["search-bar"]}
placeholder={"Search MCP Server"}
autoFocus
onInput={(e) => setSearchText(e.currentTarget.value)}
+ aria-label="Search MCP servers"
+ role="searchbox"
+ aria-describedby="search-description"
/>
+<span id="search-description" className="sr-only">
+ Search for MCP servers by name, description, or tags
+</span>
📝 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.
<input | |
type="text" | |
className={styles["search-bar"]} | |
placeholder={"Search MCP Server"} | |
autoFocus | |
onInput={(e) => setSearchText(e.currentTarget.value)} | |
/> | |
<input | |
type="text" | |
className={styles["search-bar"]} | |
placeholder={"Search MCP Server"} | |
autoFocus | |
onInput={(e) => setSearchText(e.currentTarget.value)} | |
aria-label="Search MCP servers" | |
role="searchbox" | |
aria-describedby="search-description" | |
/> | |
<span id="search-description" className="sr-only"> | |
Search for MCP servers by name, description, or tags | |
</span> |
<input | ||
type="text" | ||
value={value} | ||
placeholder={`${itemLabel} ${index + 1}`} | ||
onChange={(e) => { | ||
const newValue = [...currentValue] as string[]; | ||
newValue[index] = e.target.value; | ||
setUserConfig({ ...userConfig, [key]: newValue }); | ||
}} | ||
/> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implement proper input sanitization.
The input values are directly used without sanitization, which could lead to XSS vulnerabilities.
<input
type="text"
value={value}
placeholder={`${itemLabel} ${index + 1}`}
onChange={(e) => {
+ const sanitizedValue = sanitizeInput(e.target.value);
const newValue = [...currentValue] as string[];
- newValue[index] = e.target.value;
+ newValue[index] = sanitizedValue;
setUserConfig({ ...userConfig, [key]: newValue });
}}
/>
Committable suggestion skipped: line range outside the PR's diff.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
♻️ Duplicate comments (1)
app/components/mcp-market.tsx (1)
273-352
:⚠️ Potential issueAdd input validation and sanitization.
The form inputs lack proper validation and sanitization, which could lead to security issues.
The previous review already identified this issue. Please implement proper input sanitization as suggested in the past review comments.
🧹 Nitpick comments (5)
app/mcp/actions.ts (3)
48-57
: Consider optimizing getAllTools using Array.map.The function can be more concise and performant using Array.from() with map.
export async function getAllTools() { - const result = []; - for (const [clientId, status] of clientsMap.entries()) { - result.push({ + return Array.from(clientsMap.entries()).map(([clientId, status]) => ({ clientId, tools: status.tools, - }); - } - return result; + })); }
81-94
: Consider parallel initialization for better performance.The sequential initialization of clients could be slow with many servers. Consider using Promise.all for parallel initialization.
export async function initializeMcpSystem() { logger.info("MCP Actions starting..."); try { const config = await getMcpConfigFromFile(); - // 初始化所有客户端 - for (const [clientId, serverConfig] of Object.entries(config.mcpServers)) { - await initializeSingleClient(clientId, serverConfig); - } + // Parallel initialization of all clients + await Promise.all( + Object.entries(config.mcpServers).map(([clientId, serverConfig]) => + initializeSingleClient(clientId, serverConfig) + ) + ); return config; } catch (error) { logger.error(`Failed to initialize MCP system: ${error}`); throw error; } }
205-213
: LGTM! Consider adding status update.The function correctly handles client reinitialization. Consider updating the client status in the clientsMap after reinitialization.
export async function reinitializeClient(clientId: string) { const config = await getMcpConfigFromFile(); const serverConfig = config.mcpServers[clientId]; if (!serverConfig) { throw new Error(`Server config not found for client ${clientId}`); } await initializeSingleClient(clientId, serverConfig); + // Update client status + const status = await getClientStatus(clientId); + setClientStatuses((prev) => ({ + ...prev, + [clientId]: status, + })); }🧰 Tools
🪛 Biome (1.9.4)
[error] 210-210: The catch clause that only rethrows the original error is useless.
An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.(lint/complexity/noUselessCatch)
app/components/mcp-market.tsx (2)
42-58
: Enhance type safety with proper TypeScript definitions.The component's state management could benefit from stricter typing.
- const [userConfig, setUserConfig] = useState<Record<string, any>>({}); + interface UserConfig { + [key: string]: string | string[]; + } + const [userConfig, setUserConfig] = useState<UserConfig>({}); - const [clientStatuses, setClientStatuses] = useState< - Record< - string, - { - status: "active" | "error" | "undefined"; - errorMsg: string | null; - } - > - >({}); + interface ClientStatus { + status: "active" | "error" | "undefined"; + errorMsg: string | null; + } + const [clientStatuses, setClientStatuses] = useState<Record<string, ClientStatus>>({});
547-611
: Enhance modal accessibility with keyboard navigation.Add keyboard navigation support for better accessibility.
<Modal title={`Configure Server - ${editingServerId}`} onClose={() => !isLoading && setEditingServerId(undefined)} + onKeyDown={(e) => { + if (e.key === 'Escape' && !isLoading) { + setEditingServerId(undefined); + } + }} + role="dialog" + aria-modal="true" actions={[ <IconButton key="cancel" text="Cancel" onClick={() => setEditingServerId(undefined)} bordered disabled={isLoading} + onKeyDown={(e) => { + if (e.key === 'Enter') { + setEditingServerId(undefined); + } + }} />, // ... rest of the modal ]} >
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
app/components/mcp-market.tsx
(1 hunks)app/mcp/actions.ts
(1 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
app/mcp/actions.ts
[error] 210-210: The catch clause that only rethrows the original error is useless.
An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.
(lint/complexity/noUselessCatch)
🔇 Additional comments (3)
app/mcp/actions.ts (1)
1-22
: LGTM! Well-structured initialization.The imports are well-organized, and the setup code establishes proper server-side context with appropriate type safety.
app/components/mcp-market.tsx (2)
354-494
: LGTM! Well-implemented status management.The server status management, sorting, and filtering logic are well-implemented and properly handle all edge cases.
533-542
: Add accessibility attributes to search input.The previous review already identified the need for accessibility improvements. Please implement the suggested changes from the past review comments.
export async function executeMcpAction( | ||
clientId: string, | ||
request: McpRequestMessage, | ||
) { | ||
try { | ||
const client = clientsMap.get(clientId); | ||
if (!client?.client) { | ||
throw new Error(`Client ${clientId} not found`); | ||
} | ||
logger.info(`Executing request for [${clientId}]`); | ||
return await executeRequest(client.client, request); | ||
} catch (error) { | ||
logger.error(`Failed to execute request for [${clientId}]: ${error}`); | ||
throw error; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add request validation to prevent invalid actions.
The function should validate the request structure before execution.
export async function executeMcpAction(
clientId: string,
request: McpRequestMessage,
) {
try {
+ // Validate request structure
+ if (!request || typeof request !== 'object') {
+ throw new Error('Invalid request format');
+ }
+ if (!request.action || typeof request.action !== 'string') {
+ throw new Error('Missing or invalid action in request');
+ }
+
const client = clientsMap.get(clientId);
if (!client?.client) {
throw new Error(`Client ${clientId} not found`);
}
logger.info(`Executing request for [${clientId}]`);
return await executeRequest(client.client, request);
} catch (error) {
logger.error(`Failed to execute request for [${clientId}]: ${error}`);
throw error;
}
}
📝 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.
export async function executeMcpAction( | |
clientId: string, | |
request: McpRequestMessage, | |
) { | |
try { | |
const client = clientsMap.get(clientId); | |
if (!client?.client) { | |
throw new Error(`Client ${clientId} not found`); | |
} | |
logger.info(`Executing request for [${clientId}]`); | |
return await executeRequest(client.client, request); | |
} catch (error) { | |
logger.error(`Failed to execute request for [${clientId}]: ${error}`); | |
throw error; | |
} | |
} | |
export async function executeMcpAction( | |
clientId: string, | |
request: McpRequestMessage, | |
) { | |
try { | |
// Validate request structure | |
if (!request || typeof request !== 'object') { | |
throw new Error('Invalid request format'); | |
} | |
if (!request.action || typeof request.action !== 'string') { | |
throw new Error('Missing or invalid action in request'); | |
} | |
const client = clientsMap.get(clientId); | |
if (!client?.client) { | |
throw new Error(`Client ${clientId} not found`); | |
} | |
logger.info(`Executing request for [${clientId}]`); | |
return await executeRequest(client.client, request); | |
} catch (error) { | |
logger.error(`Failed to execute request for [${clientId}]: ${error}`); | |
throw error; | |
} | |
} |
// 更新 MCP 配置文件 | ||
async function updateMcpConfig(config: McpConfigData): Promise<void> { | ||
try { | ||
await fs.writeFile(CONFIG_PATH, JSON.stringify(config, null, 2)); | ||
} catch (error) { | ||
throw error; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Implement atomic file writes for configuration updates.
Use temporary files and atomic rename operations to prevent corruption during writes.
async function updateMcpConfig(config: McpConfigData): Promise<void> {
try {
- await fs.writeFile(CONFIG_PATH, JSON.stringify(config, null, 2));
+ const tempPath = `${CONFIG_PATH}.tmp`;
+ await fs.writeFile(tempPath, JSON.stringify(config, null, 2));
+ await fs.rename(tempPath, CONFIG_PATH);
} catch (error) {
+ // Clean up temp file if it exists
+ try {
+ await fs.unlink(`${CONFIG_PATH}.tmp`);
+ } catch {
+ // Ignore cleanup errors
+ }
throw error;
}
}
📝 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.
// 更新 MCP 配置文件 | |
async function updateMcpConfig(config: McpConfigData): Promise<void> { | |
try { | |
await fs.writeFile(CONFIG_PATH, JSON.stringify(config, null, 2)); | |
} catch (error) { | |
throw error; | |
} | |
} | |
// 更新 MCP 配置文件 | |
async function updateMcpConfig(config: McpConfigData): Promise<void> { | |
try { | |
const tempPath = `${CONFIG_PATH}.tmp`; | |
await fs.writeFile(tempPath, JSON.stringify(config, null, 2)); | |
await fs.rename(tempPath, CONFIG_PATH); | |
} catch (error) { | |
// Clean up temp file if it exists | |
try { | |
await fs.unlink(`${CONFIG_PATH}.tmp`); | |
} catch { | |
// Ignore cleanup errors | |
} | |
throw error; | |
} | |
} |
// 移除服务器 | ||
export async function removeMcpServer(clientId: string) { | ||
try { | ||
const currentConfig = await getMcpConfigFromFile(); | ||
const { [clientId]: _, ...rest } = currentConfig.mcpServers; | ||
const newConfig = { | ||
...currentConfig, | ||
mcpServers: rest, | ||
}; | ||
await updateMcpConfig(newConfig); | ||
|
||
// 关闭并移除客户端 | ||
const client = clientsMap.get(clientId); | ||
if (client?.client) { | ||
await removeClient(client.client); | ||
} | ||
clientsMap.delete(clientId); | ||
|
||
return newConfig; | ||
} catch (error) { | ||
logger.error(`Failed to remove server [${clientId}]: ${error}`); | ||
throw error; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix potential memory leak in removeMcpServer.
The client's resources might not be fully cleaned up before removal. Ensure proper cleanup by awaiting the removeClient operation before updating the configuration.
export async function removeMcpServer(clientId: string) {
try {
+ // Clean up client resources first
+ const client = clientsMap.get(clientId);
+ if (client?.client) {
+ await removeClient(client.client);
+ clientsMap.delete(clientId);
+ }
+
const currentConfig = await getMcpConfigFromFile();
const { [clientId]: _, ...rest } = currentConfig.mcpServers;
const newConfig = {
...currentConfig,
mcpServers: rest,
};
await updateMcpConfig(newConfig);
-
- // 关闭并移除客户端
- const client = clientsMap.get(clientId);
- if (client?.client) {
- await removeClient(client.client);
- }
- clientsMap.delete(clientId);
return newConfig;
} catch (error) {
logger.error(`Failed to remove server [${clientId}]: ${error}`);
throw error;
}
}
📝 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.
// 移除服务器 | |
export async function removeMcpServer(clientId: string) { | |
try { | |
const currentConfig = await getMcpConfigFromFile(); | |
const { [clientId]: _, ...rest } = currentConfig.mcpServers; | |
const newConfig = { | |
...currentConfig, | |
mcpServers: rest, | |
}; | |
await updateMcpConfig(newConfig); | |
// 关闭并移除客户端 | |
const client = clientsMap.get(clientId); | |
if (client?.client) { | |
await removeClient(client.client); | |
} | |
clientsMap.delete(clientId); | |
return newConfig; | |
} catch (error) { | |
logger.error(`Failed to remove server [${clientId}]: ${error}`); | |
throw error; | |
} | |
} | |
// 移除服务器 | |
export async function removeMcpServer(clientId: string) { | |
try { | |
// Clean up client resources first | |
const client = clientsMap.get(clientId); | |
if (client?.client) { | |
await removeClient(client.client); | |
clientsMap.delete(clientId); | |
} | |
const currentConfig = await getMcpConfigFromFile(); | |
const { [clientId]: _, ...rest } = currentConfig.mcpServers; | |
const newConfig = { | |
...currentConfig, | |
mcpServers: rest, | |
}; | |
await updateMcpConfig(newConfig); | |
return newConfig; | |
} catch (error) { | |
logger.error(`Failed to remove server [${clientId}]: ${error}`); | |
throw error; | |
} | |
} |
// 重启所有客户端 | ||
export async function restartAllClients() { | ||
logger.info("Restarting all clients..."); | ||
try { | ||
// 关闭所有客户端 | ||
for (const client of clientsMap.values()) { | ||
if (client.client) { | ||
await removeClient(client.client); | ||
} | ||
} | ||
// 清空状态 | ||
clientsMap.clear(); | ||
|
||
// 重新初始化 | ||
const config = await getMcpConfigFromFile(); | ||
for (const [clientId, serverConfig] of Object.entries(config.mcpServers)) { | ||
await initializeSingleClient(clientId, serverConfig); | ||
} | ||
return config; | ||
} catch (error) { | ||
logger.error(`Failed to restart clients: ${error}`); | ||
throw error; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Enhance error resilience in client restart process.
Individual client failures during restart shouldn't prevent other clients from restarting.
export async function restartAllClients() {
logger.info("Restarting all clients...");
try {
// 关闭所有客户端
- for (const client of clientsMap.values()) {
- if (client.client) {
- await removeClient(client.client);
- }
- }
+ await Promise.allSettled(
+ Array.from(clientsMap.values()).map(async (client) => {
+ if (client.client) {
+ try {
+ await removeClient(client.client);
+ } catch (error) {
+ logger.error(`Failed to remove client: ${error}`);
+ }
+ }
+ })
+ );
// 清空状态
clientsMap.clear();
// 重新初始化
const config = await getMcpConfigFromFile();
- for (const [clientId, serverConfig] of Object.entries(config.mcpServers)) {
- await initializeSingleClient(clientId, serverConfig);
- }
+ await Promise.all(
+ Object.entries(config.mcpServers).map(([clientId, serverConfig]) =>
+ initializeSingleClient(clientId, serverConfig)
+ )
+ );
return config;
} catch (error) {
logger.error(`Failed to restart clients: ${error}`);
throw error;
}
}
📝 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.
// 重启所有客户端 | |
export async function restartAllClients() { | |
logger.info("Restarting all clients..."); | |
try { | |
// 关闭所有客户端 | |
for (const client of clientsMap.values()) { | |
if (client.client) { | |
await removeClient(client.client); | |
} | |
} | |
// 清空状态 | |
clientsMap.clear(); | |
// 重新初始化 | |
const config = await getMcpConfigFromFile(); | |
for (const [clientId, serverConfig] of Object.entries(config.mcpServers)) { | |
await initializeSingleClient(clientId, serverConfig); | |
} | |
return config; | |
} catch (error) { | |
logger.error(`Failed to restart clients: ${error}`); | |
throw error; | |
} | |
} | |
// 重启所有客户端 | |
export async function restartAllClients() { | |
logger.info("Restarting all clients..."); | |
try { | |
// 关闭所有客户端 | |
await Promise.allSettled( | |
Array.from(clientsMap.values()).map(async (client) => { | |
if (client.client) { | |
try { | |
await removeClient(client.client); | |
} catch (error) { | |
logger.error(`Failed to remove client: ${error}`); | |
} | |
} | |
}) | |
); | |
// 清空状态 | |
clientsMap.clear(); | |
// 重新初始化 | |
const config = await getMcpConfigFromFile(); | |
await Promise.all( | |
Object.entries(config.mcpServers).map(([clientId, serverConfig]) => | |
initializeSingleClient(clientId, serverConfig) | |
) | |
); | |
return config; | |
} catch (error) { | |
logger.error(`Failed to restart clients: ${error}`); | |
throw error; | |
} | |
} |
const saveServerConfig = async () => { | ||
const preset = presetServers.find((s) => s.id === editingServerId); | ||
if (!preset || !preset.configSchema || !editingServerId) return; | ||
|
||
try { | ||
setIsLoading(true); | ||
// 构建服务器配置 | ||
const args = [...preset.baseArgs]; | ||
const env: Record<string, string> = {}; | ||
|
||
Object.entries(preset.argsMapping || {}).forEach(([key, mapping]) => { | ||
const value = userConfig[key]; | ||
if (mapping.type === "spread" && Array.isArray(value)) { | ||
const pos = mapping.position ?? 0; | ||
args.splice(pos, 0, ...value); | ||
} else if ( | ||
mapping.type === "single" && | ||
mapping.position !== undefined | ||
) { | ||
args[mapping.position] = value; | ||
} else if ( | ||
mapping.type === "env" && | ||
mapping.key && | ||
typeof value === "string" | ||
) { | ||
env[mapping.key] = value; | ||
} | ||
}); | ||
|
||
const serverConfig: ServerConfig = { | ||
command: preset.command, | ||
args, | ||
...(Object.keys(env).length > 0 ? { env } : {}), | ||
}; | ||
|
||
// 更新配置并初始化新服务器 | ||
const newConfig = await addMcpServer(editingServerId, serverConfig); | ||
setConfig(newConfig); | ||
|
||
// 更新状态 | ||
const status = await getClientStatus(editingServerId); | ||
setClientStatuses((prev) => ({ | ||
...prev, | ||
[editingServerId]: status, | ||
})); | ||
|
||
setEditingServerId(undefined); | ||
showToast("Server configuration saved successfully"); | ||
} catch (error) { | ||
showToast( | ||
error instanceof Error ? error.message : "Failed to save configuration", | ||
); | ||
} finally { | ||
setIsLoading(false); | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Enhance error handling in saveServerConfig.
The function should validate configuration values before saving.
const saveServerConfig = async () => {
const preset = presetServers.find((s) => s.id === editingServerId);
if (!preset || !preset.configSchema || !editingServerId) return;
try {
setIsLoading(true);
+ // Validate required fields
+ for (const [key, prop] of Object.entries(preset.configSchema.properties)) {
+ if (prop.required && !userConfig[key]) {
+ throw new Error(`${key} is required`);
+ }
+ if (prop.type === 'array' && prop.minItems &&
+ (!Array.isArray(userConfig[key]) || userConfig[key].length < prop.minItems)) {
+ throw new Error(`${key} requires at least ${prop.minItems} items`);
+ }
+ }
// 构建服务器配置
const args = [...preset.baseArgs];
const env: Record<string, string> = {};
// ... rest of the function
} catch (error) {
showToast(
- error instanceof Error ? error.message : "Failed to save configuration",
+ error instanceof Error ? error.message : String(error),
);
+ logger.error(`Failed to save configuration for ${editingServerId}:`, error);
} finally {
setIsLoading(false);
}
};
Committable suggestion skipped: line range outside the PR's diff.
💻 变更类型 | Change Type
🔀 变更说明 | Description of Change
📝 补充信息 | Additional Information
Summary by CodeRabbit
Release Notes
New Features
Supported Models
Improvements
Documentation