mirror of
https://github.com/run-llama/mcp-server-llamacloud.git
synced 2026-07-01 20:35:21 -04:00
feat: add multi-index with description
This commit is contained in:
+118
-47
@@ -1,7 +1,8 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* This is a MCP server that connects to a managed index on LlamaCloud.
|
||||
* This is a MCP server that connects to multiple managed indexes on LlamaCloud.
|
||||
* Each index is exposed as a separate tool.
|
||||
*/
|
||||
|
||||
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
||||
@@ -12,6 +13,58 @@ import {
|
||||
ListToolsRequestSchema,
|
||||
} from "@modelcontextprotocol/sdk/types.js";
|
||||
|
||||
// Define the tool definition interface
|
||||
interface ToolDefinition {
|
||||
indexName: string;
|
||||
description: string;
|
||||
toolName?: string;
|
||||
}
|
||||
|
||||
// Parse command line arguments
|
||||
function parseToolDefinitions(): ToolDefinition[] {
|
||||
const args = process.argv.slice(2);
|
||||
if (args.length === 0) {
|
||||
console.error('No tool definitions provided. Use format: --index "IndexName" --description "Description"');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const toolDefinitions: ToolDefinition[] = [];
|
||||
let currentIndexName: string | null = null;
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (args[i] === '--index' && i + 1 < args.length) {
|
||||
// Save the current index name. We'll wait for the description to complete the definition
|
||||
currentIndexName = args[i + 1].trim();
|
||||
i++; // Skip the next argument since we consumed it
|
||||
} else if (args[i] === '--description' && i + 1 < args.length && currentIndexName) {
|
||||
// We have both an index name and a description, so we can create a tool definition
|
||||
const description = args[i + 1].trim();
|
||||
const toolName = `get_information_${currentIndexName.toLowerCase().replace(/[^a-z0-9]/g, '_')}`;
|
||||
|
||||
toolDefinitions.push({
|
||||
indexName: currentIndexName,
|
||||
description,
|
||||
toolName
|
||||
});
|
||||
|
||||
// Reset for the next pair
|
||||
currentIndexName = null;
|
||||
i++; // Skip the next argument since we consumed it
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we have an index without a description at the end
|
||||
if (currentIndexName) {
|
||||
console.warn(`Warning: Index '${currentIndexName}' was specified without a description.`);
|
||||
}
|
||||
|
||||
if (toolDefinitions.length === 0) {
|
||||
console.error('No valid tool definitions found. Use format: --index "IndexName" --description "Description"');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
return toolDefinitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an MCP server with capabilities for tools
|
||||
@@ -28,67 +81,80 @@ const server = new Server(
|
||||
}
|
||||
);
|
||||
|
||||
const index = new LlamaCloudIndex({
|
||||
name: process.env.LLAMA_CLOUD_INDEX_NAME || (() => { throw new Error('LLAMA_CLOUD_INDEX_NAME is not set') })(),
|
||||
projectName: process.env.LLAMA_CLOUD_PROJECT_NAME || (() => { throw new Error('LLAMA_CLOUD_PROJECT_NAME is not set') })(),
|
||||
apiKey: process.env.LLAMA_CLOUD_API_KEY || (() => { throw new Error('LLAMA_CLOUD_API_KEY is not set') })(),
|
||||
});
|
||||
// Get the project name and API key from environment variables
|
||||
const projectName = process.env.LLAMA_CLOUD_PROJECT_NAME || (() => { throw new Error('LLAMA_CLOUD_PROJECT_NAME is not set') })();
|
||||
const apiKey = process.env.LLAMA_CLOUD_API_KEY || (() => { throw new Error('LLAMA_CLOUD_API_KEY is not set') })();
|
||||
|
||||
// Parse tool definitions from command line arguments
|
||||
const toolDefinitions = parseToolDefinitions();
|
||||
|
||||
// Create indexes for each tool definition
|
||||
const indexes = new Map<string, LlamaCloudIndex>();
|
||||
|
||||
for (const definition of toolDefinitions) {
|
||||
const index = new LlamaCloudIndex({
|
||||
name: definition.indexName,
|
||||
projectName,
|
||||
apiKey,
|
||||
});
|
||||
|
||||
indexes.set(definition.toolName!, index);
|
||||
console.log(`Created index for tool ${definition.toolName}: ${definition.indexName} - ${definition.description}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler that lists available tools.
|
||||
* Exposes a single "get_information" tool that lets clients retrieve information from the LlamaIndex.
|
||||
* Exposes a tool for each index that lets clients retrieve information.
|
||||
*/
|
||||
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
||||
return {
|
||||
tools: [
|
||||
{
|
||||
name: "get_information",
|
||||
description: "Get information from your knowledge base to answer questions.",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
query: {
|
||||
type: "string",
|
||||
description: "The query used to get information about your knowledge base."
|
||||
},
|
||||
tools: toolDefinitions.map(definition => ({
|
||||
name: definition.toolName!,
|
||||
description: `Get information from the ${definition.indexName} index. The index contains ${definition.description}`,
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
query: {
|
||||
type: "string",
|
||||
description: `The query used to get information from the ${definition.indexName} index.`
|
||||
},
|
||||
required: ["query"]
|
||||
}
|
||||
},
|
||||
required: ["query"]
|
||||
}
|
||||
]
|
||||
}))
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Handler for the get_information tool.
|
||||
* Retrieves information from the LlamaIndex and returns the result as text.
|
||||
* Handler for tool calls.
|
||||
* Routes requests to the appropriate index based on the tool name.
|
||||
*/
|
||||
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||
switch (request.params.name) {
|
||||
case "get_information": {
|
||||
const query = String(request.params.arguments?.query);
|
||||
if (!query) {
|
||||
throw new Error("query parameter is required");
|
||||
}
|
||||
|
||||
const retriever = index.asRetriever();
|
||||
const nodesWithScore = await retriever.retrieve({ query });
|
||||
|
||||
const nodes = nodesWithScore.map((node) => node.node);
|
||||
const context = nodes.map((r) => r.getContent(MetadataMode.NONE)).join("\n\n");
|
||||
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: context,
|
||||
}]
|
||||
};
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Error("Unknown tool");
|
||||
const toolName = request.params.name;
|
||||
const index = indexes.get(toolName);
|
||||
|
||||
if (!index) {
|
||||
throw new Error(`Unknown tool: ${toolName}`);
|
||||
}
|
||||
});
|
||||
|
||||
const query = String(request.params.arguments?.query);
|
||||
if (!query) {
|
||||
throw new Error("query parameter is required");
|
||||
}
|
||||
|
||||
const retriever = index.asRetriever();
|
||||
const nodesWithScore = await retriever.retrieve({ query });
|
||||
|
||||
const nodes = nodesWithScore.map((node) => node.node);
|
||||
const context = nodes.map((r) => r.getContent(MetadataMode.NONE)).join("\n\n");
|
||||
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: context,
|
||||
}]
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -97,6 +163,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||
* This allows the server to communicate via standard input/output streams.
|
||||
*/
|
||||
async function main() {
|
||||
console.log(`Starting MCP server with ${toolDefinitions.length} tools:`);
|
||||
toolDefinitions.forEach(def => {
|
||||
console.log(`- ${def.toolName}: ${def.indexName} - ${def.description}`);
|
||||
});
|
||||
|
||||
const transport = new StdioServerTransport();
|
||||
await server.connect(transport);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user