Group MCP tools by capability#2293
Conversation
Prompt To Fix All With AIFix the following 2 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 2
apps/code/src/renderer/features/mcp-servers/hooks/mcpToolGroups.test.ts:25-37
**Prefer parameterised tests**
The first two `it` blocks each assert multiple cases as individual `expect` calls. When one assertion fails, the rest are skipped, hiding further failures. Prefer `it.each` so each case is its own named test. The same pattern applies to the write/delete block below.
### Issue 2 of 2
apps/code/src/renderer/features/mcp-servers/components/parts/ServerDetailView.tsx:433-456
**OnceAndOnlyOnce: duplicate handler**
The identical `onToolApprovalChange` callback is defined twice — once for each `ToolGroupSection`. Extract it to a `useCallback` (or a plain `const` before the return) so both sections share a single definition.
```suggestion
) : (
<>
<ToolGroupSection
title="Read tools"
tools={groupedTools.read}
onToolApprovalChange={(toolName, approval_state) =>
setToolApproval({ toolName, approval_state })
}
/>
<ToolGroupSection
title="Write / delete tools"
tools={groupedTools.write_delete}
onToolApprovalChange={(toolName, approval_state) =>
setToolApproval({ toolName, approval_state })
}
/>
</>
)}
```
Reviews (1): Last reviewed commit: "Group MCP tools by capability" | Re-trigger Greptile |
| it("classifies clear read verbs from tool names", () => { | ||
| expect(getMcpToolGroup(tool("get_ticket"))).toBe("read"); | ||
| expect(getMcpToolGroup(tool("list_projects"))).toBe("read"); | ||
| expect(getMcpToolGroup(tool("search_docs"))).toBe("read"); | ||
| expect(getMcpToolGroup(tool("lookup_customer"))).toBe("read"); | ||
| }); | ||
|
|
||
| it("classifies clear write and delete verbs from tool names", () => { | ||
| expect(getMcpToolGroup(tool("create_ticket"))).toBe("write_delete"); | ||
| expect(getMcpToolGroup(tool("delete_file"))).toBe("write_delete"); | ||
| expect(getMcpToolGroup(tool("send_message"))).toBe("write_delete"); | ||
| expect(getMcpToolGroup(tool("run_query"))).toBe("write_delete"); | ||
| }); |
There was a problem hiding this comment.
The first two it blocks each assert multiple cases as individual expect calls. When one assertion fails, the rest are skipped, hiding further failures. Prefer it.each so each case is its own named test. The same pattern applies to the write/delete block below.
Context Used: Do not attempt to comment on incorrect alphabetica... (source)
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/code/src/renderer/features/mcp-servers/hooks/mcpToolGroups.test.ts
Line: 25-37
Comment:
**Prefer parameterised tests**
The first two `it` blocks each assert multiple cases as individual `expect` calls. When one assertion fails, the rest are skipped, hiding further failures. Prefer `it.each` so each case is its own named test. The same pattern applies to the write/delete block below.
**Context Used:** Do not attempt to comment on incorrect alphabetica... ([source](https://app.greptile.com/review/custom-context?memory=instruction-0))
How can I resolve this? If you propose a fix, please make it concise.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| ) : ( | ||
| filteredTools.map((tool) => ( | ||
| <ToolRow | ||
| key={tool.tool_name} | ||
| tool={tool} | ||
| onChange={(approval_state) => | ||
| <> | ||
| <ToolGroupSection | ||
| title="Read tools" | ||
| tools={groupedTools.read} | ||
| onToolApprovalChange={(toolName, approval_state) => | ||
| setToolApproval({ | ||
| toolName: tool.tool_name, | ||
| toolName, | ||
| approval_state, | ||
| }) | ||
| } | ||
| /> | ||
| )) | ||
| <ToolGroupSection | ||
| title="Write / delete tools" | ||
| tools={groupedTools.write_delete} | ||
| onToolApprovalChange={(toolName, approval_state) => | ||
| setToolApproval({ | ||
| toolName, | ||
| approval_state, | ||
| }) | ||
| } | ||
| /> | ||
| </> | ||
| )} |
There was a problem hiding this comment.
OnceAndOnlyOnce: duplicate handler
The identical onToolApprovalChange callback is defined twice — once for each ToolGroupSection. Extract it to a useCallback (or a plain const before the return) so both sections share a single definition.
| ) : ( | |
| filteredTools.map((tool) => ( | |
| <ToolRow | |
| key={tool.tool_name} | |
| tool={tool} | |
| onChange={(approval_state) => | |
| <> | |
| <ToolGroupSection | |
| title="Read tools" | |
| tools={groupedTools.read} | |
| onToolApprovalChange={(toolName, approval_state) => | |
| setToolApproval({ | |
| toolName: tool.tool_name, | |
| toolName, | |
| approval_state, | |
| }) | |
| } | |
| /> | |
| )) | |
| <ToolGroupSection | |
| title="Write / delete tools" | |
| tools={groupedTools.write_delete} | |
| onToolApprovalChange={(toolName, approval_state) => | |
| setToolApproval({ | |
| toolName, | |
| approval_state, | |
| }) | |
| } | |
| /> | |
| </> | |
| )} | |
| ) : ( | |
| <> | |
| <ToolGroupSection | |
| title="Read tools" | |
| tools={groupedTools.read} | |
| onToolApprovalChange={(toolName, approval_state) => | |
| setToolApproval({ toolName, approval_state }) | |
| } | |
| /> | |
| <ToolGroupSection | |
| title="Write / delete tools" | |
| tools={groupedTools.write_delete} | |
| onToolApprovalChange={(toolName, approval_state) => | |
| setToolApproval({ toolName, approval_state }) | |
| } | |
| /> | |
| </> | |
| )} |
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/code/src/renderer/features/mcp-servers/components/parts/ServerDetailView.tsx
Line: 433-456
Comment:
**OnceAndOnlyOnce: duplicate handler**
The identical `onToolApprovalChange` callback is defined twice — once for each `ToolGroupSection`. Extract it to a `useCallback` (or a plain `const` before the return) so both sections share a single definition.
```suggestion
) : (
<>
<ToolGroupSection
title="Read tools"
tools={groupedTools.read}
onToolApprovalChange={(toolName, approval_state) =>
setToolApproval({ toolName, approval_state })
}
/>
<ToolGroupSection
title="Write / delete tools"
tools={groupedTools.write_delete}
onToolApprovalChange={(toolName, approval_state) =>
setToolApproval({ toolName, approval_state })
}
/>
</>
)}
```
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
This is IMO the wrong way of going about it. MCP tools expose whether they actually are read only, destructive, etc. via tool annotations. We should use that instead: https://modelcontextprotocol.io/specification/2025-11-25/schema#toolannotations
Summary
Read toolsandWrite / delete toolssections.Write / delete toolsfor safer review.Closes #2233
Testing
corepack pnpm --dir apps/code exec vitest run src/renderer/features/mcp-servers/hooks/mcpToolGroups.test.tscorepack pnpm --dir apps/code typecheckcorepack pnpm exec biome check apps/code/src/renderer/features/mcp-servers/hooks/mcpToolGroups.ts apps/code/src/renderer/features/mcp-servers/hooks/mcpToolGroups.test.ts apps/code/src/renderer/features/mcp-servers/components/parts/ServerDetailView.tsxgit diff --checkNotes