76 lines
2.2 KiB
TypeScript
76 lines
2.2 KiB
TypeScript
import type { ToolDef } from '../llm/openai-compat.js';
|
|
|
|
const SLUG = /^[a-z0-9_-]{1,64}$/;
|
|
|
|
export function normalizeToolName(serverId: string, toolName: string): string {
|
|
return `mcp__${serverId}__${toolName}`;
|
|
}
|
|
|
|
export function parseToolName(name: string): { serverId: string; toolName: string } | null {
|
|
if (!name.startsWith('mcp__')) return null;
|
|
const parts = name.split('__');
|
|
if (parts.length !== 3) return null;
|
|
const [, serverId, toolName] = parts;
|
|
if (!SLUG.test(serverId) || !SLUG.test(toolName)) return null;
|
|
return { serverId, toolName };
|
|
}
|
|
|
|
export function matchesAnyPattern(name: string, patterns: string[]): boolean {
|
|
return patterns.some((p) => {
|
|
if (p === name) return true;
|
|
if (p.endsWith('__*')) {
|
|
const prefix = p.slice(0, -1);
|
|
return name.startsWith(prefix) && name.length > prefix.length;
|
|
}
|
|
return false;
|
|
});
|
|
}
|
|
|
|
export interface CachedTool {
|
|
serverId: string;
|
|
toolName: string;
|
|
description: string | null;
|
|
inputSchema: string | null;
|
|
}
|
|
|
|
const MAX_DESCRIPTION = 1000;
|
|
|
|
export function buildToolDefsFromCache(
|
|
cache: CachedTool[],
|
|
patterns: string[],
|
|
serverNames: Map<string, string>,
|
|
): ToolDef[] {
|
|
const mcpPatterns = patterns.filter((p) => p.startsWith('mcp__'));
|
|
if (mcpPatterns.length === 0) return [];
|
|
|
|
const defs: ToolDef[] = [];
|
|
for (const tool of cache) {
|
|
if (!SLUG.test(tool.serverId) || !SLUG.test(tool.toolName)) continue;
|
|
const canonical = normalizeToolName(tool.serverId, tool.toolName);
|
|
if (!matchesAnyPattern(canonical, mcpPatterns)) continue;
|
|
|
|
let params: unknown = { type: 'object', properties: {} };
|
|
if (tool.inputSchema) {
|
|
try {
|
|
params = JSON.parse(tool.inputSchema);
|
|
} catch {
|
|
// Keep default object schema
|
|
}
|
|
}
|
|
|
|
const serverLabel = serverNames.get(tool.serverId) ?? tool.serverId;
|
|
const rawDesc = tool.description ?? '';
|
|
const description = `[外部ツール: ${serverLabel} 提供] ${rawDesc}`.slice(0, MAX_DESCRIPTION);
|
|
|
|
defs.push({
|
|
type: 'function',
|
|
function: {
|
|
name: canonical,
|
|
description,
|
|
parameters: params as Record<string, unknown>,
|
|
},
|
|
});
|
|
}
|
|
return defs;
|
|
}
|