MCP_TOOL_BEST_PRACTICES.md•5.75 kB
# MCP Tool Handling Best Practices Analysis
## Current Implementation vs. Best Practices
### 🔍 **Your Current Approach**
```typescript
// Your current method (older pattern)
this.server.tool(
"manage_distribution_lists",
distributionListSchema,
wrapToolHandler(async (args: DistributionListArgs) => {
// Tool logic here
})
);
```
### ✅ **Modern MCP Best Practice**
```typescript
// Modern MCP pattern (from official servers)
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "manage_distribution_lists",
description: "Manage distribution lists in M365",
inputSchema: zodToJsonSchema(distributionListSchema) as ToolInput,
},
// ... other tools
],
};
});
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
switch (name) {
case "manage_distribution_lists":
return await handleDistributionList(args);
case "manage_security_groups":
return await handleSecurityGroup(args);
// ... other tools
default:
throw new Error(`Unknown tool: ${name}`);
}
});
```
## 📊 **Comparison with Official MCP Servers**
### **1. Filesystem Server Pattern**
```typescript
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: "read_file",
description: "Read the complete contents of a file",
inputSchema: zodToJsonSchema(ReadFileArgsSchema) as ToolInput,
}
],
}));
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
// Handle tools in switch statement
});
```
### **2. Git Server Pattern**
```python
@server.list_tools()
async def list_tools() -> list[Tool]:
return [
Tool(
name="git_status",
description="Shows the working tree status",
inputSchema=GitStatus.schema(),
)
]
@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
match name:
case "git_status":
return [TextContent(type="text", text=git_status(repo))]
```
### **3. Time Server Pattern**
```python
@server.list_tools()
async def list_tools() -> list[Tool]:
return [
Tool(
name="get_current_time",
description="Get current time in a specific timezone",
inputSchema={
"type": "object",
"properties": {
"timezone": {"type": "string"}
},
"required": ["timezone"],
},
)
]
```
## 🚀 **Recommended Migration Strategy**
### **Step 1: Update Tool Registration**
Replace your current `setupTools()` method with modern request handlers:
```typescript
private setupTools(): void {
// List all available tools
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "manage_distribution_lists",
description: "Manage distribution lists in Microsoft 365",
inputSchema: zodToJsonSchema(distributionListSchema) as ToolInput,
},
{
name: "manage_security_groups",
description: "Manage security groups in Microsoft 365",
inputSchema: zodToJsonSchema(securityGroupSchema) as ToolInput,
},
// ... all your other tools
],
};
});
// Handle tool calls
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
case "manage_distribution_lists":
return { content: [{ type: "text", text: await this.handleDistributionList(args) }] };
case "manage_security_groups":
return { content: [{ type: "text", text: await this.handleSecurityGroup(args) }] };
// ... all your other tools
default:
throw new Error(`Unknown tool: ${name}`);
}
} catch (error) {
return {
content: [{ type: "text", text: `Error: ${error.message}` }],
isError: true,
};
}
});
}
```
### **Step 2: Update Response Format**
Ensure your handlers return proper MCP response format:
```typescript
// Current format (needs updating)
return "Success message";
// Correct MCP format
return {
content: [{
type: "text",
text: JSON.stringify(result, null, 2)
}]
};
```
### **Step 3: Add Proper Error Handling**
```typescript
catch (error) {
return {
content: [{ type: "text", text: `Error: ${error.message}` }],
isError: true,
};
}
```
## 🎯 **Key Benefits of Modern Approach**
1. **Better Error Handling** - Standardized error responses
2. **Improved Type Safety** - Better TypeScript integration
3. **Future Compatibility** - Aligns with latest MCP specifications
4. **Easier Debugging** - Clearer request/response flow
5. **Better Testing** - Request handlers are easier to unit test
## 📝 **Migration Checklist**
- [ ] Replace `this.server.tool()` with `setRequestHandler()`
- [ ] Update tool schemas to use `zodToJsonSchema()`
- [ ] Modify response format to include `content` array
- [ ] Add proper error handling with `isError: true`
- [ ] Test all tools with MCP inspector
- [ ] Update any tool wrapper functions
## 🔧 **Next Steps**
1. Start with one tool as a proof of concept
2. Test thoroughly with MCP inspector
3. Gradually migrate all tools
4. Update documentation and examples
5. Consider adding progress indicators for long-running operations
This migration will make your M365 Core MCP server more maintainable, debuggable, and aligned with modern MCP best practices!