diff --git a/README.md b/README.md
index 8bb5424..f75de12 100644
--- a/README.md
+++ b/README.md
@@ -1,16 +1,17 @@
# LlamaCloud MCP Server
-A MCP server connecting to a managed index on [LlamaCloud](https://cloud.llamaindex.ai/)
+A MCP server connecting to multiple managed indexes on [LlamaCloud](https://cloud.llamaindex.ai/)
-This is a TypeScript-based MCP server that implements a connection to a managed index on LlamaCloud.
+This is a TypeScript-based MCP server that creates multiple tools, each connected to a specific managed index on LlamaCloud. Each tool is defined through command-line arguments.
## Features
### Tools
-- `get_information` - Get information from your knowledge base to answer questions.
- - Takes query as required parameters
+- Creates a separate tool for each index you define
+- Each tool provides a `query` parameter to search its specific index
+- Auto-generates tool names like `get_information_index_name` based on index names
## Development
@@ -42,10 +43,11 @@ On Windows: `%APPDATA%/Claude/claude_desktop_config.json`
"llamacloud": {
"command": "node",
"args": [
- "/path/to/llamacloud/build/index.js"
+ "/path/to/llamacloud/build/index.js",
+ "--index", "10k-SEC-Tesla", "--description", "10k SEC documents from 2023 for Tesla",
+ "--index", "10k-SEC-Apple", "--description", "10k SEC documents from 2023 for Apple"
],
"env": {
- "LLAMA_CLOUD_INDEX_NAME": "",
"LLAMA_CLOUD_PROJECT_NAME": "",
"LLAMA_CLOUD_API_KEY": ""
}
@@ -54,6 +56,24 @@ On Windows: `%APPDATA%/Claude/claude_desktop_config.json`
}
```
+### Tool Definition Format
+
+You can define multiple tools by providing pairs of `--index` and `--description` arguments. Each tool definition follows this format:
+
+```
+--index "IndexName" --description "Description text"
+```
+
+For example:
+
+```bash
+node build/index.js --index "10k-SEC-Tesla" --description "10k SEC documents from 2023 for Tesla" --index "10k-SEC-Apple" --description "10k SEC documents from 2023 for Apple"
+```
+
+This will create two tools:
+1. `get_information_10k_sec_tesla` - For querying the 10k-SEC-Tesla index
+2. `get_information_10k_sec_apple` - For querying the 10k-SEC-Apple index
+
### Debugging
Since MCP servers communicate over stdio, debugging can be challenging. We recommend using the [MCP Inspector](https://github.com/modelcontextprotocol/inspector), which is available as a package script:
diff --git a/src/index.ts b/src/index.ts
index d1c0673..72fd4d6 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -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();
+
+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);
}