ableton-mcp-server-rag
Version:
Ableton Live MCP depend on Ableton JS
125 lines • 6.06 kB
JavaScript
// src/mcp/tool-vector.ts
import { MemoryVectorStore } from "langchain/vectorstores/memory";
import { OpenAIEmbeddings } from "@langchain/openai";
let toolVectorStore = null;
let toolMetadataMap = new Map();
// Global registry to track tool metadata during decoration
export const toolRegistry = new Map();
export function registerToolMetadata(name, description, paramsSchema) {
const metadata = createEnhancedToolMetadata(name, description, paramsSchema);
toolRegistry.set(name, metadata);
// console.log(`📝 Registered tool metadata: ${name} (${metadata.category})`) // Silent for MCP
}
export async function initToolVectorStore() {
console.log('🔧 Initializing tool vector store...'); // ✅ Use console instead
if (toolRegistry.size === 0) {
console.warn('⚠️ No tools registered in toolRegistry');
return;
}
// Skip re-initialization if already done
if (toolVectorStore && toolMetadataMap.size > 0) {
console.log(`✅ Tool vector store already initialized with ${toolRegistry.size} tools`);
return;
}
console.log(`📋 Found ${toolRegistry.size} registered tools`);
// Create tool metadata with enhanced descriptions
const toolDocs = [];
const toolMetadata = [];
for (const [name, metadata] of toolRegistry) {
toolMetadataMap.set(name, metadata);
// Create searchable document for vector store
const doc = `${metadata.name}: ${metadata.description}. Category: ${metadata.category}. Keywords: ${metadata.keywords.join(', ')}`;
toolDocs.push(doc);
toolMetadata.push(metadata);
console.log(`🔨 Indexed tool: ${name} (${metadata.category})`); // ✅ Use console instead
}
// Create vector store for tool similarity search
const apiKey = process.env.OPENAI_API_KEY || "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";
toolVectorStore = await MemoryVectorStore.fromTexts(toolDocs, toolMetadata.map((_, i) => ({ id: i })), new OpenAIEmbeddings({ apiKey }));
console.log(`✅ Tool vector store initialized with ${toolRegistry.size} tools`); // ✅ Use console instead
}
function createEnhancedToolMetadata(name, description, paramsSchema) {
// Categorize tools based on name patterns
let category = 'general';
let keywords = [];
if (name.includes('track')) {
category = 'tracks';
keywords = ['track', 'channel', 'audio', 'midi', 'create', 'delete', 'modify'];
}
else if (name.includes('device') || name.includes('load')) {
category = 'devices';
keywords = ['device', 'effect', 'instrument', 'plugin', 'reverb', 'delay', 'eq', 'compressor', 'load', 'add'];
}
else if (name.includes('clip')) {
category = 'clips';
keywords = ['clip', 'audio', 'midi', 'notes', 'recording', 'create', 'modify'];
}
else if (name.includes('song')) {
category = 'song';
keywords = ['song', 'tempo', 'time signature', 'arrangement', 'session'];
}
else if (name.includes('browser') || name.includes('resource')) {
category = 'browser';
keywords = ['browser', 'preset', 'sample', 'instrument', 'effect', 'search', 'load'];
}
else if (name.includes('rag') || name.includes('query') || name.includes('smart')) {
category = 'knowledge';
keywords = ['knowledge', 'help', 'information', 'guide', 'documentation'];
}
// Add specific keywords based on tool name and description
const lowerName = name.toLowerCase();
const lowerDesc = description.toLowerCase();
if (lowerName.includes('reverb') || lowerDesc.includes('reverb'))
keywords.push('reverb', 'spatial', 'room', 'hall');
if (lowerName.includes('delay') || lowerDesc.includes('delay'))
keywords.push('delay', 'echo', 'repeat');
if (lowerName.includes('eq') || lowerDesc.includes('equalizer'))
keywords.push('equalizer', 'frequency', 'bass', 'treble');
if (lowerName.includes('tempo') || lowerDesc.includes('tempo'))
keywords.push('bpm', 'speed', 'timing');
if (lowerName.includes('create') || lowerDesc.includes('create'))
keywords.push('create', 'new', 'add');
if (lowerName.includes('modify') || lowerDesc.includes('modify'))
keywords.push('modify', 'change', 'edit', 'set');
if (lowerName.includes('get') || lowerDesc.includes('get'))
keywords.push('get', 'retrieve', 'fetch', 'info');
return {
name,
description,
category,
keywords: [...new Set(keywords)], // Remove duplicates
params: paramsSchema || {}
};
}
export async function findRelevantTools(query, maxTools = 4) {
if (!toolVectorStore) {
// console.warn('⚠️ Tool vector store not initialized, returning empty results')
return [];
}
// console.log(`🔍 Finding relevant tools for: "${query}"`)
try {
// Search for similar tools
const results = await toolVectorStore.similaritySearch(query, maxTools * 2);
const relevantTools = [];
const seenCategories = new Set();
for (const result of results) {
const toolIndex = result.metadata.id;
const toolName = result.pageContent.split(':')[0];
const metadata = toolMetadataMap.get(toolName);
if (metadata && relevantTools.length < maxTools) {
// Prefer diversity across categories
if (!seenCategories.has(metadata.category) || relevantTools.length < 2) {
relevantTools.push(metadata);
seenCategories.add(metadata.category);
// console.log(`✅ Selected tool: ${metadata.name} (${metadata.category})`)
}
}
}
return relevantTools;
}
catch (error) {
// console.error('❌ Error finding relevant tools:', error)
return [];
}
}
//# sourceMappingURL=tool-vector.js.map