Fix server name normalization for tool routing compatibility
## Problem
MCP server names with invalid characters (spaces, special chars, etc.)
caused tool routing failures when prefixed to tool names, as OpenRouter
API requires tool names to match pattern ^[a-zA-Z0-9_-]{1,64}$
## Solution
- **Added normalizeServerName utility**: Converts invalid characters to underscores
- **Centralized normalization logic**: Shared utility prevents code duplication
- **Fixed tool routing**: Agent loop now matches normalized server names correctly
- **Updated tool discovery**: MCP tools use normalized prefixes consistently
## Examples
- "My Weather Service\!" → "My_Weather_Service"
- "Test@Server#123" → "Test_Server_123"
- "Server...with...dots" → "Server_with_dots"
- " spaces_and__double__underscores " → "spaces_and_double_underscores"
## Technical Details
- Created /src/utils/mcpUtils.ts for shared normalization function
- Updated MCPConnectionManager to use normalized prefixes in tool names
- Fixed agent loop to match connections using normalized names
- Maintains original server names in UI while using normalized names for API
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
8c46e57b49
commit
0266b4871e
4 changed files with 28 additions and 12 deletions
|
|
@ -15,6 +15,7 @@ import type {
|
|||
import type { ChatMessage, Tool, ToolCall, InferenceRequest } from '@/types/inference';
|
||||
import { useInference } from '@/contexts/InferenceContext';
|
||||
import { useMCP } from '@/contexts/MCPContext';
|
||||
import { normalizeServerName } from '@/utils/mcpUtils';
|
||||
|
||||
// Test tools that work alongside MCP tools
|
||||
const testTools: TestTool[] = [
|
||||
|
|
@ -226,17 +227,19 @@ export function useAgentLoop(config: Partial<AgentLoopConfig> = {}): UseAgentLoo
|
|||
|
||||
// Check if it's an MCP tool (prefixed with server name using double underscore)
|
||||
if (toolCall.function.name.includes('__')) {
|
||||
// Extract server name from tool name (format: "server__tool_name")
|
||||
const [serverName] = toolCall.function.name.split('__');
|
||||
// Extract normalized server name from tool name (format: "normalized_server__tool_name")
|
||||
const [normalizedServerName] = toolCall.function.name.split('__');
|
||||
|
||||
// Find the connection ID for this server name
|
||||
const connection = connections.find(conn => conn.name === serverName);
|
||||
// Find the connection ID by comparing normalized server names
|
||||
const connection = connections.find(conn => {
|
||||
return normalizeServerName(conn.name) === normalizedServerName;
|
||||
});
|
||||
|
||||
if (connection) {
|
||||
const result = await callMCPTool(connection.id, toolCall.function.name, toolCall.function.arguments);
|
||||
return { result };
|
||||
} else {
|
||||
throw new Error(`MCP server "${serverName}" not found or not connected`);
|
||||
throw new Error(`MCP server with normalized name "${normalizedServerName}" not found or not connected`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import type {
|
|||
MCPError,
|
||||
} from '@/types/mcp';
|
||||
import type { Tool } from '@/types/inference';
|
||||
import { normalizeServerName } from '@/utils/mcpUtils';
|
||||
|
||||
interface MCPOAuthState {
|
||||
codeVerifier: string;
|
||||
|
|
@ -587,10 +588,11 @@ export class MCPConnectionManager {
|
|||
|
||||
// Transform MCP tools to our Tool interface with name prefixing
|
||||
// Use double underscore instead of dot to comply with OpenRouter API requirements
|
||||
const normalizedServerName = normalizeServerName(this.connection.name);
|
||||
return result.tools.map(tool => ({
|
||||
type: 'function' as const,
|
||||
function: {
|
||||
name: `${this.connection.name}__${tool.name}`,
|
||||
name: `${normalizedServerName}__${tool.name}`,
|
||||
description: `[${this.connection.name}] ${tool.description || ''}`,
|
||||
parameters: tool.inputSchema || {},
|
||||
},
|
||||
|
|
@ -643,8 +645,9 @@ export class MCPConnectionManager {
|
|||
}
|
||||
|
||||
// Remove the server prefix from the tool name (using double underscore separator)
|
||||
const unprefixedName = toolName.startsWith(`${this.connection.name}__`)
|
||||
? toolName.slice(this.connection.name.length + 2)
|
||||
const normalizedServerName = normalizeServerName(this.connection.name);
|
||||
const unprefixedName = toolName.startsWith(`${normalizedServerName}__`)
|
||||
? toolName.slice(normalizedServerName.length + 2)
|
||||
: toolName;
|
||||
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -126,10 +126,6 @@ export class OpenRouterClient {
|
|||
const inputCost = parseFloat(openRouterModel.pricing.prompt);
|
||||
const outputCost = parseFloat(openRouterModel.pricing.completion);
|
||||
|
||||
// Check if model supports tools by looking at supported_parameters
|
||||
const supportsTools = openRouterModel.supported_parameters?.includes('tools') || false;
|
||||
console.log(`Model ${openRouterModel.id} supports tools: ${supportsTools}`);
|
||||
|
||||
return {
|
||||
id: openRouterModel.id,
|
||||
name: openRouterModel.name,
|
||||
|
|
|
|||
14
src/utils/mcpUtils.ts
Normal file
14
src/utils/mcpUtils.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
// Utility functions for MCP server name normalization
|
||||
|
||||
/**
|
||||
* Normalize server name to comply with OpenRouter API tool naming requirements
|
||||
* Pattern: ^[a-zA-Z0-9_-]{1,64}$
|
||||
*/
|
||||
export function normalizeServerName(name: string): string {
|
||||
return name
|
||||
.replace(/[^a-zA-Z0-9_-]/g, '_') // Replace invalid characters with underscore
|
||||
.replace(/_{2,}/g, '_') // Replace multiple underscores with single
|
||||
.replace(/^_+|_+$/g, '') // Remove leading/trailing underscores
|
||||
.substring(0, 32) // Limit length to leave room for tool name
|
||||
|| 'server'; // Fallback if name becomes empty
|
||||
}
|
||||
Loading…
Reference in a new issue