From d8a8c6ee0180022144b9c65b6f5b67b82afd5e95 Mon Sep 17 00:00:00 2001 From: Justin Spahr-Summers Date: Mon, 18 Nov 2024 15:21:22 +0000 Subject: [PATCH] Refactor/expand into a more substantial example --- template/src/index.ts.ejs | 128 +++++++++++++++++++++++++++++++++----- 1 file changed, 112 insertions(+), 16 deletions(-) diff --git a/template/src/index.ts.ejs b/template/src/index.ts.ejs index e73c8db..a3e2407 100644 --- a/template/src/index.ts.ejs +++ b/template/src/index.ts.ejs @@ -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);