Refactor/expand into a more substantial example

This commit is contained in:
Justin Spahr-Summers 2024-11-18 15:21:22 +00:00
commit d8a8c6ee01
No known key found for this signature in database

View file

@ -1,12 +1,38 @@
#!/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
*/
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListResourcesRequestSchema,
ListToolsRequestSchema,
ReadResourceRequestSchema,
} 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)
* and tools (to create new notes).
*/
const server = new Server(
{
name: "<%= name %>",
@ -17,41 +43,111 @@ const server = new Server(
resources: {},
tools: {},
},
},
}
);
/**
* 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: [
{
uri: "example://hello",
mimeType: "text/plain",
name: "Example resource",
},
],
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: "example",
description: "An example tool",
name: "create_note",
description: "Create a new note",
inputSchema: {
type: "object",
properties: {
message: {
title: {
type: "string",
description: "Message to echo",
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");
}
});
/**
* 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);