add llamacloud server

This commit is contained in:
Marcus Schiesser
2024-12-04 14:50:35 +07:00
parent 7c447e22bd
commit cdc90228ea
4 changed files with 6724 additions and 167 deletions
+15 -22
View File
@@ -1,29 +1,14 @@
# mcp-server-llamacloud MCP Server # LlamaCloud MCP Server
A MCP server connecting to a managed index on LlamaCloud A MCP server connecting to a managed index on [LlamaCloud](https://cloud.llamaindex.ai/)
This is a TypeScript-based MCP server that implements a simple notes system. It demonstrates core MCP concepts by providing: This is a TypeScript-based MCP server that implements a connection to a managed index on LlamaCloud.
- Resources representing text notes with URIs and metadata
- Tools for creating new notes
- Prompts for generating summaries of notes
## Features ## Features
### Resources
- List and access notes via `note://` URIs
- Each note has a title, content and metadata
- Plain text mime type for simple content access
### Tools ### Tools
- `create_note` - Create new text notes - `get_information` - Get information from your knowledge base to answer questions.
- Takes title and content as required parameters - Takes query as required parameters
- Stores note in server state
### Prompts
- `summarize_notes` - Generate a summary of all stored notes
- Includes all note contents as embedded resources
- Returns structured prompt for LLM summarization
## Development ## Development
@@ -52,8 +37,16 @@ On Windows: `%APPDATA%/Claude/claude_desktop_config.json`
```json ```json
{ {
"mcpServers": { "mcpServers": {
"mcp-server-llamacloud": { "llamacloud": {
"command": "/path/to/mcp-server-llamacloud/build/index.js" "command": "node",
"args": [
"/path/to/llamacloud/build/index.js",
],
"env": {
"LLAMA_CLOUD_INDEX_NAME": "<YOUR_INDEX_NAME>",
"LLAMA_CLOUD_PROJECT_NAME": "<YOUR_PROJECT_NAME>",
"LLAMA_CLOUD_API_KEY": "<YOUR_API_KEY>"
}
} }
} }
} }
+6676
View File
File diff suppressed because it is too large Load Diff
+7 -4
View File
@@ -2,7 +2,9 @@
"name": "mcp-server-llamacloud", "name": "mcp-server-llamacloud",
"version": "0.1.0", "version": "0.1.0",
"description": "A MCP server connecting to a managed index on LlamaCloud", "description": "A MCP server connecting to a managed index on LlamaCloud",
"private": true, "license": "MIT",
"author": "LlamaIndex Inc.",
"bugs": "https://github.com/run-llama/mcp-server-llamacloud/issues",
"type": "module", "type": "module",
"bin": { "bin": {
"mcp-server-llamacloud": "./build/index.js" "mcp-server-llamacloud": "./build/index.js"
@@ -17,10 +19,11 @@
"inspector": "npx @modelcontextprotocol/inspector build/index.js" "inspector": "npx @modelcontextprotocol/inspector build/index.js"
}, },
"dependencies": { "dependencies": {
"@modelcontextprotocol/sdk": "0.6.0" "@modelcontextprotocol/sdk": "0.6.0",
"llamaindex": "0.8.2"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^20.11.24", "@types/node": "^22.9.3",
"typescript": "^5.3.3" "typescript": "^5.6.2"
} }
} }
+26 -141
View File
@@ -1,120 +1,58 @@
#!/usr/bin/env node #!/usr/bin/env node
/** /**
* This is a template MCP server that implements a simple notes system. * This is a MCP server that connects to a managed index on LlamaCloud.
* It demonstrates core MCP concepts like resources and tools by allowing:
* - Listing notes as resources
* - Reading individual notes
* - Creating new notes via a tool
* - Summarizing all notes via a prompt
*/ */
import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { LlamaCloudIndex, MetadataMode } from 'llamaindex';
import { import {
CallToolRequestSchema, CallToolRequestSchema,
ListResourcesRequestSchema,
ListToolsRequestSchema, ListToolsRequestSchema,
ReadResourceRequestSchema,
ListPromptsRequestSchema,
GetPromptRequestSchema,
} from "@modelcontextprotocol/sdk/types.js"; } from "@modelcontextprotocol/sdk/types.js";
/**
* Type alias for a note object.
*/
type Note = { title: string, content: string };
/** /**
* Simple in-memory storage for notes. * Create an MCP server with capabilities for tools
* In a real implementation, this would likely be backed by a database.
*/
const notes: { [id: string]: Note } = {
"1": { title: "First Note", content: "This is note 1" },
"2": { title: "Second Note", content: "This is note 2" }
};
/**
* Create an MCP server with capabilities for resources (to list/read notes),
* tools (to create new notes), and prompts (to summarize notes).
*/ */
const server = new Server( const server = new Server(
{ {
name: "mcp-server-llamacloud", name: "llamacloud-mcp-server",
version: "0.1.0", version: "0.1.0",
}, },
{ {
capabilities: { capabilities: {
resources: {},
tools: {}, tools: {},
prompts: {},
}, },
} }
); );
/** const index = new LlamaCloudIndex({
* Handler for listing available notes as resources. name: process.env.LLAMA_CLOUD_INDEX_NAME || (() => { throw new Error('LLAMA_CLOUD_INDEX_NAME is not set') })(),
* Each note is exposed as a resource with: projectName: process.env.LLAMA_CLOUD_PROJECT_NAME || (() => { throw new Error('LLAMA_CLOUD_PROJECT_NAME is not set') })(),
* - A note:// URI scheme apiKey: process.env.LLAMA_CLOUD_API_KEY || (() => { throw new Error('LLAMA_CLOUD_API_KEY is not set') })(),
* - Plain text MIME type
* - Human readable name and description (now including the note title)
*/
server.setRequestHandler(ListResourcesRequestSchema, async () => {
return {
resources: Object.entries(notes).map(([id, note]) => ({
uri: `note:///${id}`,
mimeType: "text/plain",
name: note.title,
description: `A text note: ${note.title}`
}))
};
});
/**
* Handler for reading the contents of a specific note.
* Takes a note:// URI and returns the note content as plain text.
*/
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
const url = new URL(request.params.uri);
const id = url.pathname.replace(/^\//, '');
const note = notes[id];
if (!note) {
throw new Error(`Note ${id} not found`);
}
return {
contents: [{
uri: request.params.uri,
mimeType: "text/plain",
text: note.content
}]
};
}); });
/** /**
* Handler that lists available tools. * Handler that lists available tools.
* Exposes a single "create_note" tool that lets clients create new notes. * Exposes a single "get_information" tool that lets clients retrieve information from the LlamaIndex.
*/ */
server.setRequestHandler(ListToolsRequestSchema, async () => { server.setRequestHandler(ListToolsRequestSchema, async () => {
return { return {
tools: [ tools: [
{ {
name: "create_note", name: "get_information",
description: "Create a new note", description: "Get information from your knowledge base to answer questions.",
inputSchema: { inputSchema: {
type: "object", type: "object",
properties: { properties: {
title: { query: {
type: "string", type: "string",
description: "Title of the note" description: "The query used to get information about your knowledge base."
}, },
content: {
type: "string",
description: "Text content of the note"
}
}, },
required: ["title", "content"] required: ["query"]
} }
} }
] ]
@@ -122,25 +60,27 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
}); });
/** /**
* Handler for the create_note tool. * Handler for the get_information tool.
* Creates a new note with the provided title and content, and returns success message. * Retrieves information from the LlamaIndex and returns the result as text.
*/ */
server.setRequestHandler(CallToolRequestSchema, async (request) => { server.setRequestHandler(CallToolRequestSchema, async (request) => {
switch (request.params.name) { switch (request.params.name) {
case "create_note": { case "get_information": {
const title = String(request.params.arguments?.title); const query = String(request.params.arguments?.query);
const content = String(request.params.arguments?.content); if (!query) {
if (!title || !content) { throw new Error("query parameter is required");
throw new Error("Title and content are required");
} }
const id = String(Object.keys(notes).length + 1); const retriever = index.asRetriever();
notes[id] = { title, content }; 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 { return {
content: [{ content: [{
type: "text", type: "text",
text: `Created note ${id}: ${title}` text: context,
}] }]
}; };
} }
@@ -150,62 +90,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
} }
}); });
/**
* Handler that lists available prompts.
* Exposes a single "summarize_notes" prompt that summarizes all notes.
*/
server.setRequestHandler(ListPromptsRequestSchema, async () => {
return {
prompts: [
{
name: "summarize_notes",
description: "Summarize all notes",
}
]
};
});
/**
* Handler for the summarize_notes prompt.
* Returns a prompt that requests summarization of all notes, with the notes' contents embedded as resources.
*/
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
if (request.params.name !== "summarize_notes") {
throw new Error("Unknown prompt");
}
const embeddedNotes = Object.entries(notes).map(([id, note]) => ({
type: "resource" as const,
resource: {
uri: `note:///${id}`,
mimeType: "text/plain",
text: note.content
}
}));
return {
messages: [
{
role: "user",
content: {
type: "text",
text: "Please summarize the following notes:"
}
},
...embeddedNotes.map(note => ({
role: "user" as const,
content: note
})),
{
role: "user",
content: {
type: "text",
text: "Provide a concise summary of all the notes above."
}
}
]
};
});
/** /**
* Start the server using stdio transport. * Start the server using stdio transport.