mirror of
https://github.com/run-llama/mcp-server-llamacloud.git
synced 2026-06-30 20:47:54 -04:00
init repo
This commit is contained in:
@@ -0,0 +1,4 @@
|
|||||||
|
node_modules/
|
||||||
|
build/
|
||||||
|
*.log
|
||||||
|
.env*
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
# mcp-server-llamacloud MCP Server
|
||||||
|
|
||||||
|
A MCP server connecting to a managed index on LlamaCloud
|
||||||
|
|
||||||
|
This is a TypeScript-based MCP server that implements a simple notes system. It demonstrates core MCP concepts by providing:
|
||||||
|
|
||||||
|
- Resources representing text notes with URIs and metadata
|
||||||
|
- Tools for creating new notes
|
||||||
|
- Prompts for generating summaries of notes
|
||||||
|
|
||||||
|
## 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
|
||||||
|
- `create_note` - Create new text notes
|
||||||
|
- Takes title and content 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
|
||||||
|
|
||||||
|
Install dependencies:
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
Build the server:
|
||||||
|
```bash
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
For development with auto-rebuild:
|
||||||
|
```bash
|
||||||
|
npm run watch
|
||||||
|
```
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
To use with Claude Desktop, add the server config:
|
||||||
|
|
||||||
|
On MacOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
||||||
|
On Windows: `%APPDATA%/Claude/claude_desktop_config.json`
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"mcp-server-llamacloud": {
|
||||||
|
"command": "/path/to/mcp-server-llamacloud/build/index.js"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run inspector
|
||||||
|
```
|
||||||
|
|
||||||
|
The Inspector will provide a URL to access debugging tools in your browser.
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"name": "mcp-server-llamacloud",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "A MCP server connecting to a managed index on LlamaCloud",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"bin": {
|
||||||
|
"mcp-server-llamacloud": "./build/index.js"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"build"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
|
||||||
|
"prepare": "npm run build",
|
||||||
|
"watch": "tsc --watch",
|
||||||
|
"inspector": "npx @modelcontextprotocol/inspector build/index.js"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@modelcontextprotocol/sdk": "0.6.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^20.11.24",
|
||||||
|
"typescript": "^5.3.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
+222
@@ -0,0 +1,222 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a template MCP server that implements a simple notes system.
|
||||||
|
* 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 { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
||||||
|
import {
|
||||||
|
CallToolRequestSchema,
|
||||||
|
ListResourcesRequestSchema,
|
||||||
|
ListToolsRequestSchema,
|
||||||
|
ReadResourceRequestSchema,
|
||||||
|
ListPromptsRequestSchema,
|
||||||
|
GetPromptRequestSchema,
|
||||||
|
} from "@modelcontextprotocol/sdk/types.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type alias for a note object.
|
||||||
|
*/
|
||||||
|
type Note = { title: string, content: string };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple in-memory storage for notes.
|
||||||
|
* 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(
|
||||||
|
{
|
||||||
|
name: "mcp-server-llamacloud",
|
||||||
|
version: "0.1.0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
capabilities: {
|
||||||
|
resources: {},
|
||||||
|
tools: {},
|
||||||
|
prompts: {},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for listing available notes as resources.
|
||||||
|
* Each note is exposed as a resource with:
|
||||||
|
* - A note:// URI scheme
|
||||||
|
* - 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.
|
||||||
|
* Exposes a single "create_note" tool that lets clients create new notes.
|
||||||
|
*/
|
||||||
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
||||||
|
return {
|
||||||
|
tools: [
|
||||||
|
{
|
||||||
|
name: "create_note",
|
||||||
|
description: "Create a new note",
|
||||||
|
inputSchema: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
title: {
|
||||||
|
type: "string",
|
||||||
|
description: "Title of the note"
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
type: "string",
|
||||||
|
description: "Text content of the note"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: ["title", "content"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for the create_note tool.
|
||||||
|
* Creates a new note with the provided title and content, and returns success message.
|
||||||
|
*/
|
||||||
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||||
|
switch (request.params.name) {
|
||||||
|
case "create_note": {
|
||||||
|
const title = String(request.params.arguments?.title);
|
||||||
|
const content = String(request.params.arguments?.content);
|
||||||
|
if (!title || !content) {
|
||||||
|
throw new Error("Title and content are required");
|
||||||
|
}
|
||||||
|
|
||||||
|
const id = String(Object.keys(notes).length + 1);
|
||||||
|
notes[id] = { title, content };
|
||||||
|
|
||||||
|
return {
|
||||||
|
content: [{
|
||||||
|
type: "text",
|
||||||
|
text: `Created note ${id}: ${title}`
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error("Unknown tool");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
* This allows the server to communicate via standard input/output streams.
|
||||||
|
*/
|
||||||
|
async function main() {
|
||||||
|
const transport = new StdioServerTransport();
|
||||||
|
await server.connect(transport);
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch((error) => {
|
||||||
|
console.error("Server error:", error);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "Node16",
|
||||||
|
"moduleResolution": "Node16",
|
||||||
|
"outDir": "./build",
|
||||||
|
"rootDir": "./src",
|
||||||
|
"strict": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"forceConsistentCasingInFileNames": true
|
||||||
|
},
|
||||||
|
"include": ["src/**/*"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user