mirror of
https://github.com/modelcontextprotocol/servers.git
synced 2026-02-19 11:54:58 -05:00
WIP: Migrate servers to McpServer with registerTool/registerResource/registerPrompt APIs
Progress so far: - Sequential thinking: Converted to McpServer with Zod schemas (complete) - Memory: Converted to use registerTool API (needs Zod conversion) - Filesystem: Converted to use registerTool API (needs Zod conversion) - Everything: Converted to use register* APIs (needs Zod conversion) Key learnings: - registerTool/registerResource/registerPrompt only exist on McpServer class - McpServer expects Zod schemas, not JSON schemas - Updated SDK versions to ^1.20.1 for all servers Status: Work in progress - code does not compile yet Next steps: Convert remaining servers' JSON schemas to Zod schemas 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
||||
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
||||
import {
|
||||
ClientCapabilities,
|
||||
@@ -155,20 +155,11 @@ const EXAMPLE_COMPLETIONS = {
|
||||
};
|
||||
|
||||
export const createServer = () => {
|
||||
const server = new Server(
|
||||
const server = new McpServer(
|
||||
{
|
||||
name: "example-servers/everything",
|
||||
title: "Everything Example Server",
|
||||
version: "1.0.0",
|
||||
},
|
||||
{
|
||||
capabilities: {
|
||||
prompts: {},
|
||||
resources: { subscribe: true },
|
||||
tools: {},
|
||||
logging: {},
|
||||
completions: {}
|
||||
},
|
||||
instructions
|
||||
}
|
||||
);
|
||||
@@ -274,15 +265,15 @@ export const createServer = () => {
|
||||
|
||||
// Register all resources dynamically
|
||||
ALL_RESOURCES.forEach(resource => {
|
||||
server.registerResource({
|
||||
uri: resource.uri,
|
||||
name: resource.name,
|
||||
mimeType: resource.mimeType,
|
||||
description: `Static resource ${resource.name}`,
|
||||
handler: async () => {
|
||||
server.resource(
|
||||
resource.uri,
|
||||
resource.name,
|
||||
resource.mimeType,
|
||||
`Static resource ${resource.name}`,
|
||||
async () => {
|
||||
return resource.text ? { text: resource.text } : { blob: resource.blob };
|
||||
}
|
||||
});
|
||||
);
|
||||
});
|
||||
|
||||
server.setRequestHandler(SubscribeRequestSchema, async (request, extra) => {
|
||||
@@ -297,10 +288,11 @@ export const createServer = () => {
|
||||
});
|
||||
|
||||
// Register prompts
|
||||
server.registerPrompt({
|
||||
name: PromptName.SIMPLE,
|
||||
description: "A prompt without arguments",
|
||||
handler: async () => {
|
||||
server.prompt(
|
||||
PromptName.SIMPLE,
|
||||
"A prompt without arguments",
|
||||
{},
|
||||
async () => {
|
||||
return {
|
||||
messages: [
|
||||
{
|
||||
@@ -313,24 +305,26 @@ export const createServer = () => {
|
||||
],
|
||||
};
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
server.registerPrompt({
|
||||
name: PromptName.COMPLEX,
|
||||
description: "A prompt with arguments",
|
||||
arguments: [
|
||||
{
|
||||
name: "temperature",
|
||||
description: "Temperature setting",
|
||||
required: true,
|
||||
server.prompt(
|
||||
PromptName.COMPLEX,
|
||||
"A prompt with arguments",
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
temperature: {
|
||||
type: "string",
|
||||
description: "Temperature setting"
|
||||
},
|
||||
style: {
|
||||
type: "string",
|
||||
description: "Output style"
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "style",
|
||||
description: "Output style",
|
||||
required: false,
|
||||
},
|
||||
],
|
||||
handler: async (args) => {
|
||||
required: ["temperature"]
|
||||
},
|
||||
async (args) => {
|
||||
return {
|
||||
messages: [
|
||||
{
|
||||
@@ -358,19 +352,22 @@ export const createServer = () => {
|
||||
],
|
||||
};
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
server.registerPrompt({
|
||||
name: PromptName.RESOURCE,
|
||||
description: "A prompt that includes an embedded resource reference",
|
||||
arguments: [
|
||||
{
|
||||
name: "resourceId",
|
||||
description: "Resource ID to include (1-100)",
|
||||
required: true,
|
||||
server.prompt(
|
||||
PromptName.RESOURCE,
|
||||
"A prompt that includes an embedded resource reference",
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
resourceId: {
|
||||
type: "string",
|
||||
description: "Resource ID to include (1-100)"
|
||||
}
|
||||
},
|
||||
],
|
||||
handler: async (args) => {
|
||||
required: ["resourceId"]
|
||||
},
|
||||
async (args) => {
|
||||
const resourceId = parseInt(args?.resourceId as string, 10);
|
||||
if (isNaN(resourceId) || resourceId < 1 || resourceId > 100) {
|
||||
throw new Error(
|
||||
@@ -400,26 +397,26 @@ export const createServer = () => {
|
||||
],
|
||||
};
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
// Register tools
|
||||
server.registerTool({
|
||||
name: ToolName.ECHO,
|
||||
description: "Echoes back the input",
|
||||
inputSchema: zodToJsonSchema(EchoSchema) as ToolInput,
|
||||
handler: async (args) => {
|
||||
server.tool(
|
||||
ToolName.ECHO,
|
||||
"Echoes back the input",
|
||||
zodToJsonSchema(EchoSchema) as ToolInput,
|
||||
async (args) => {
|
||||
const validatedArgs = EchoSchema.parse(args);
|
||||
return {
|
||||
content: [{ type: "text", text: `Echo: ${validatedArgs.message}` }],
|
||||
};
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
server.registerTool({
|
||||
name: ToolName.ADD,
|
||||
description: "Adds two numbers",
|
||||
inputSchema: zodToJsonSchema(AddSchema) as ToolInput,
|
||||
handler: async (args) => {
|
||||
server.tool(
|
||||
ToolName.ADD,
|
||||
"Adds two numbers",
|
||||
zodToJsonSchema(AddSchema) as ToolInput,
|
||||
async (args) => {
|
||||
const validatedArgs = AddSchema.parse(args);
|
||||
const sum = validatedArgs.a + validatedArgs.b;
|
||||
return {
|
||||
@@ -431,13 +428,13 @@ export const createServer = () => {
|
||||
],
|
||||
};
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
server.registerTool({
|
||||
name: ToolName.LONG_RUNNING_OPERATION,
|
||||
description: "Demonstrates a long running operation with progress updates",
|
||||
inputSchema: zodToJsonSchema(LongRunningOperationSchema) as ToolInput,
|
||||
handler: async (args, extra) => {
|
||||
server.tool(
|
||||
ToolName.LONG_RUNNING_OPERATION,
|
||||
"Demonstrates a long running operation with progress updates",
|
||||
zodToJsonSchema(LongRunningOperationSchema) as ToolInput,
|
||||
async (args, extra) => {
|
||||
const validatedArgs = LongRunningOperationSchema.parse(args);
|
||||
const { duration, steps } = validatedArgs;
|
||||
const stepDuration = duration / steps;
|
||||
@@ -469,13 +466,13 @@ export const createServer = () => {
|
||||
],
|
||||
};
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
server.registerTool({
|
||||
name: ToolName.PRINT_ENV,
|
||||
description: "Prints all environment variables, helpful for debugging MCP server configuration",
|
||||
inputSchema: zodToJsonSchema(PrintEnvSchema) as ToolInput,
|
||||
handler: async () => {
|
||||
server.tool(
|
||||
ToolName.PRINT_ENV,
|
||||
"Prints all environment variables, helpful for debugging MCP server configuration",
|
||||
zodToJsonSchema(PrintEnvSchema) as ToolInput,
|
||||
async () => {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
@@ -485,13 +482,13 @@ export const createServer = () => {
|
||||
],
|
||||
};
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
server.registerTool({
|
||||
name: ToolName.SAMPLE_LLM,
|
||||
description: "Samples from an LLM using MCP's sampling feature",
|
||||
inputSchema: zodToJsonSchema(SampleLLMSchema) as ToolInput,
|
||||
handler: async (args, extra) => {
|
||||
server.tool(
|
||||
ToolName.SAMPLE_LLM,
|
||||
"Samples from an LLM using MCP's sampling feature",
|
||||
zodToJsonSchema(SampleLLMSchema) as ToolInput,
|
||||
async (args, extra) => {
|
||||
const validatedArgs = SampleLLMSchema.parse(args);
|
||||
const { prompt, maxTokens } = validatedArgs;
|
||||
|
||||
@@ -507,13 +504,13 @@ export const createServer = () => {
|
||||
],
|
||||
};
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
server.registerTool({
|
||||
name: ToolName.GET_TINY_IMAGE,
|
||||
description: "Returns the MCP_TINY_IMAGE",
|
||||
inputSchema: zodToJsonSchema(GetTinyImageSchema) as ToolInput,
|
||||
handler: async (args) => {
|
||||
server.tool(
|
||||
ToolName.GET_TINY_IMAGE,
|
||||
"Returns the MCP_TINY_IMAGE",
|
||||
zodToJsonSchema(GetTinyImageSchema) as ToolInput,
|
||||
async (args) => {
|
||||
GetTinyImageSchema.parse(args);
|
||||
return {
|
||||
content: [
|
||||
@@ -533,13 +530,13 @@ export const createServer = () => {
|
||||
],
|
||||
};
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
server.registerTool({
|
||||
name: ToolName.ANNOTATED_MESSAGE,
|
||||
description: "Demonstrates how annotations can be used to provide metadata about content",
|
||||
inputSchema: zodToJsonSchema(AnnotatedMessageSchema) as ToolInput,
|
||||
handler: async (args) => {
|
||||
server.tool(
|
||||
ToolName.ANNOTATED_MESSAGE,
|
||||
"Demonstrates how annotations can be used to provide metadata about content",
|
||||
zodToJsonSchema(AnnotatedMessageSchema) as ToolInput,
|
||||
async (args) => {
|
||||
const { messageType, includeImage } = AnnotatedMessageSchema.parse(args);
|
||||
|
||||
const content = [];
|
||||
@@ -589,13 +586,13 @@ export const createServer = () => {
|
||||
|
||||
return { content };
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
server.registerTool({
|
||||
name: ToolName.GET_RESOURCE_REFERENCE,
|
||||
description: "Returns a resource reference that can be used by MCP clients",
|
||||
inputSchema: zodToJsonSchema(GetResourceReferenceSchema) as ToolInput,
|
||||
handler: async (args) => {
|
||||
server.tool(
|
||||
ToolName.GET_RESOURCE_REFERENCE,
|
||||
"Returns a resource reference that can be used by MCP clients",
|
||||
zodToJsonSchema(GetResourceReferenceSchema) as ToolInput,
|
||||
async (args) => {
|
||||
const validatedArgs = GetResourceReferenceSchema.parse(args);
|
||||
const resourceId = validatedArgs.resourceId;
|
||||
|
||||
@@ -623,13 +620,13 @@ export const createServer = () => {
|
||||
],
|
||||
};
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
server.registerTool({
|
||||
name: ToolName.GET_RESOURCE_LINKS,
|
||||
description: "Returns multiple resource links that reference different types of resources",
|
||||
inputSchema: zodToJsonSchema(GetResourceLinksSchema) as ToolInput,
|
||||
handler: async (args) => {
|
||||
server.tool(
|
||||
ToolName.GET_RESOURCE_LINKS,
|
||||
"Returns multiple resource links that reference different types of resources",
|
||||
zodToJsonSchema(GetResourceLinksSchema) as ToolInput,
|
||||
async (args) => {
|
||||
const { count } = GetResourceLinksSchema.parse(args);
|
||||
const content = [];
|
||||
|
||||
@@ -657,14 +654,13 @@ export const createServer = () => {
|
||||
|
||||
return { content };
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
server.registerTool({
|
||||
name: ToolName.STRUCTURED_CONTENT,
|
||||
description: "Returns structured content along with an output schema for client data validation",
|
||||
inputSchema: zodToJsonSchema(StructuredContentSchema.input) as ToolInput,
|
||||
outputSchema: zodToJsonSchema(StructuredContentSchema.output) as ToolOutput,
|
||||
handler: async (args) => {
|
||||
server.tool(
|
||||
ToolName.STRUCTURED_CONTENT,
|
||||
"Returns structured content along with an output schema for client data validation",
|
||||
zodToJsonSchema(StructuredContentSchema.input) as ToolInput,
|
||||
async (args) => {
|
||||
// The same response is returned for every input.
|
||||
const validatedArgs = StructuredContentSchema.input.parse(args);
|
||||
|
||||
@@ -684,13 +680,13 @@ export const createServer = () => {
|
||||
structuredContent: weather
|
||||
};
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
server.registerTool({
|
||||
name: ToolName.ZIP_RESOURCES,
|
||||
description: "Compresses the provided resource files (mapping of name to URI, which can be a data URI) to a zip file, which it returns as a data URI resource link.",
|
||||
inputSchema: zodToJsonSchema(ZipResourcesInputSchema) as ToolInput,
|
||||
handler: async (args) => {
|
||||
server.tool(
|
||||
ToolName.ZIP_RESOURCES,
|
||||
"Compresses the provided resource files (mapping of name to URI, which can be a data URI) to a zip file, which it returns as a data URI resource link.",
|
||||
zodToJsonSchema(ZipResourcesInputSchema) as ToolInput,
|
||||
async (args) => {
|
||||
const { files } = ZipResourcesInputSchema.parse(args);
|
||||
|
||||
const zip = new JSZip();
|
||||
@@ -720,7 +716,7 @@ export const createServer = () => {
|
||||
],
|
||||
};
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
server.setRequestHandler(CompleteRequestSchema, async (request) => {
|
||||
const { ref, argument } = request.params;
|
||||
@@ -784,11 +780,11 @@ export const createServer = () => {
|
||||
clientSupportsRoots = true;
|
||||
|
||||
// Register LIST_ROOTS tool
|
||||
server.registerTool({
|
||||
name: ToolName.LIST_ROOTS,
|
||||
description: "Lists the current MCP roots provided by the client. Demonstrates the roots protocol capability even though this server doesn't access files.",
|
||||
inputSchema: zodToJsonSchema(ListRootsSchema) as ToolInput,
|
||||
handler: async (args) => {
|
||||
server.tool(
|
||||
ToolName.LIST_ROOTS,
|
||||
"Lists the current MCP roots provided by the client. Demonstrates the roots protocol capability even though this server doesn't access files.",
|
||||
zodToJsonSchema(ListRootsSchema) as ToolInput,
|
||||
async (args) => {
|
||||
ListRootsSchema.parse(args);
|
||||
|
||||
if (!clientSupportsRoots) {
|
||||
@@ -833,7 +829,7 @@ export const createServer = () => {
|
||||
]
|
||||
};
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
try {
|
||||
const response = await server.listRoots();
|
||||
@@ -869,11 +865,11 @@ export const createServer = () => {
|
||||
|
||||
if (clientCapabilities?.elicitation) {
|
||||
// Register ELICITATION tool
|
||||
server.registerTool({
|
||||
name: ToolName.ELICITATION,
|
||||
description: "Elicitation test tool that demonstrates how to request user input with various field types (string, boolean, email, uri, date, integer, number, enum)",
|
||||
inputSchema: zodToJsonSchema(ElicitationSchema) as ToolInput,
|
||||
handler: async (args, extra) => {
|
||||
server.tool(
|
||||
ToolName.ELICITATION,
|
||||
"Elicitation test tool that demonstrates how to request user input with various field types (string, boolean, email, uri, date, integer, number, enum)",
|
||||
zodToJsonSchema(ElicitationSchema) as ToolInput,
|
||||
async (args, extra) => {
|
||||
ElicitationSchema.parse(args);
|
||||
|
||||
const elicitationResult = await extra.sendRequest({
|
||||
@@ -992,7 +988,7 @@ export const createServer = () => {
|
||||
|
||||
return { content };
|
||||
}
|
||||
});
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
||||
import { McpServer } from "@modelcontextprotocol/sdk/server/index.js";
|
||||
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
||||
import {
|
||||
ToolSchema,
|
||||
@@ -145,17 +145,14 @@ const ToolInputSchema = ToolSchema.shape.inputSchema;
|
||||
type ToolInput = z.infer<typeof ToolInputSchema>;
|
||||
|
||||
// Server setup
|
||||
const server = new Server(
|
||||
{
|
||||
name: "secure-filesystem-server",
|
||||
version: "0.2.0",
|
||||
const server = new McpServer({
|
||||
name: "secure-filesystem-server",
|
||||
version: "0.2.0",
|
||||
}, {
|
||||
capabilities: {
|
||||
tools: {},
|
||||
},
|
||||
{
|
||||
capabilities: {
|
||||
tools: {},
|
||||
},
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
// Reads a file as a stream of buffers, concatenates them, and then encodes
|
||||
// the result to a Base64 string. This is a memory-efficient way to handle
|
||||
@@ -176,11 +173,13 @@ async function readFileAsBase64Stream(filePath: string): Promise<string> {
|
||||
}
|
||||
|
||||
// Tool handlers
|
||||
server.registerTool({
|
||||
name: "read_file",
|
||||
description: "Read the complete contents of a file as text. DEPRECATED: Use read_text_file instead.",
|
||||
inputSchema: zodToJsonSchema(ReadTextFileArgsSchema) as ToolInput,
|
||||
handler: async (args: any) => {
|
||||
server.registerTool(
|
||||
"read_file",
|
||||
{
|
||||
description: "Read the complete contents of a file as text. DEPRECATED: Use read_text_file instead.",
|
||||
inputSchema: ReadTextFileArgsSchema.shape
|
||||
},
|
||||
async (args: any) => {
|
||||
const parsed = ReadTextFileArgsSchema.safeParse(args);
|
||||
if (!parsed.success) {
|
||||
throw new Error(`Invalid arguments for read_file: ${parsed.error}`);
|
||||
@@ -211,20 +210,21 @@ server.registerTool({
|
||||
content: [{ type: "text", text: content }],
|
||||
};
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
server.registerTool({
|
||||
name: "read_text_file",
|
||||
description:
|
||||
"Read the complete contents of a file from the file system as text. " +
|
||||
"Handles various text encodings and provides detailed error messages " +
|
||||
"if the file cannot be read. Use this tool when you need to examine " +
|
||||
"the contents of a single file. Use the 'head' parameter to read only " +
|
||||
"the first N lines of a file, or the 'tail' parameter to read only " +
|
||||
"the last N lines of a file. Operates on the file as text regardless of extension. " +
|
||||
"Only works within allowed directories.",
|
||||
inputSchema: zodToJsonSchema(ReadTextFileArgsSchema) as ToolInput,
|
||||
handler: async (args: any) => {
|
||||
server.registerTool(
|
||||
"read_text_file",
|
||||
{
|
||||
description: "Read the complete contents of a file from the file system as text. " +
|
||||
"Handles various text encodings and provides detailed error messages " +
|
||||
"if the file cannot be read. Use this tool when you need to examine " +
|
||||
"the contents of a single file. Use the 'head' parameter to read only " +
|
||||
"the first N lines of a file, or the 'tail' parameter to read only " +
|
||||
"the last N lines of a file. Operates on the file as text regardless of extension. " +
|
||||
"Only works within allowed directories.",
|
||||
inputSchema: ReadTextFileArgsSchema.shape
|
||||
},
|
||||
async (args: any) => {
|
||||
const parsed = ReadTextFileArgsSchema.safeParse(args);
|
||||
if (!parsed.success) {
|
||||
throw new Error(`Invalid arguments for read_text_file: ${parsed.error}`);
|
||||
@@ -255,15 +255,16 @@ server.registerTool({
|
||||
content: [{ type: "text", text: content }],
|
||||
};
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
server.registerTool({
|
||||
name: "read_media_file",
|
||||
description:
|
||||
"Read an image or audio file. Returns the base64 encoded data and MIME type. " +
|
||||
"Only works within allowed directories.",
|
||||
inputSchema: zodToJsonSchema(ReadMediaFileArgsSchema) as ToolInput,
|
||||
handler: async (args: any) => {
|
||||
server.registerTool(
|
||||
"read_media_file",
|
||||
{
|
||||
description: "Read an image or audio file. Returns the base64 encoded data and MIME type. " +
|
||||
"Only works within allowed directories.",
|
||||
inputSchema: ReadMediaFileArgsSchema.shape
|
||||
},
|
||||
async (args: any) => {
|
||||
const parsed = ReadMediaFileArgsSchema.safeParse(args);
|
||||
if (!parsed.success) {
|
||||
throw new Error(`Invalid arguments for read_media_file: ${parsed.error}`);
|
||||
@@ -294,18 +295,19 @@ server.registerTool({
|
||||
content: [{ type, data, mimeType }],
|
||||
};
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
server.registerTool({
|
||||
name: "read_multiple_files",
|
||||
description:
|
||||
"Read the contents of multiple files simultaneously. This is more " +
|
||||
"efficient than reading files one by one when you need to analyze " +
|
||||
"or compare multiple files. Each file's content is returned with its " +
|
||||
"path as a reference. Failed reads for individual files won't stop " +
|
||||
"the entire operation. Only works within allowed directories.",
|
||||
inputSchema: zodToJsonSchema(ReadMultipleFilesArgsSchema) as ToolInput,
|
||||
handler: async (args: any) => {
|
||||
server.registerTool(
|
||||
"read_multiple_files",
|
||||
{
|
||||
description: "Read the contents of multiple files simultaneously. This is more " +
|
||||
"efficient than reading files one by one when you need to analyze " +
|
||||
"or compare multiple files. Each file's content is returned with its " +
|
||||
"path as a reference. Failed reads for individual files won't stop " +
|
||||
"the entire operation. Only works within allowed directories.",
|
||||
inputSchema: ReadMultipleFilesArgsSchema.shape
|
||||
},
|
||||
async (args: any) => {
|
||||
const parsed = ReadMultipleFilesArgsSchema.safeParse(args);
|
||||
if (!parsed.success) {
|
||||
throw new Error(`Invalid arguments for read_multiple_files: ${parsed.error}`);
|
||||
@@ -326,16 +328,17 @@ server.registerTool({
|
||||
content: [{ type: "text", text: results.join("\n---\n") }],
|
||||
};
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
server.registerTool({
|
||||
name: "write_file",
|
||||
description:
|
||||
"Create a new file or completely overwrite an existing file with new content. " +
|
||||
"Use with caution as it will overwrite existing files without warning. " +
|
||||
"Handles text content with proper encoding. Only works within allowed directories.",
|
||||
inputSchema: zodToJsonSchema(WriteFileArgsSchema) as ToolInput,
|
||||
handler: async (args: any) => {
|
||||
server.registerTool(
|
||||
"write_file",
|
||||
{
|
||||
description: "Create a new file or completely overwrite an existing file with new content. " +
|
||||
"Use with caution as it will overwrite existing files without warning. " +
|
||||
"Handles text content with proper encoding. Only works within allowed directories.",
|
||||
inputSchema: WriteFileArgsSchema.shape
|
||||
},
|
||||
async (args: any) => {
|
||||
const parsed = WriteFileArgsSchema.safeParse(args);
|
||||
if (!parsed.success) {
|
||||
throw new Error(`Invalid arguments for write_file: ${parsed.error}`);
|
||||
@@ -346,16 +349,17 @@ server.registerTool({
|
||||
content: [{ type: "text", text: `Successfully wrote to ${parsed.data.path}` }],
|
||||
};
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
server.registerTool({
|
||||
name: "edit_file",
|
||||
description:
|
||||
"Make line-based edits to a text file. Each edit replaces exact line sequences " +
|
||||
"with new content. Returns a git-style diff showing the changes made. " +
|
||||
"Only works within allowed directories.",
|
||||
inputSchema: zodToJsonSchema(EditFileArgsSchema) as ToolInput,
|
||||
handler: async (args: any) => {
|
||||
server.registerTool(
|
||||
"edit_file",
|
||||
{
|
||||
description: "Make line-based edits to a text file. Each edit replaces exact line sequences " +
|
||||
"with new content. Returns a git-style diff showing the changes made. " +
|
||||
"Only works within allowed directories.",
|
||||
inputSchema: EditFileArgsSchema.shape
|
||||
},
|
||||
async (args: any) => {
|
||||
const parsed = EditFileArgsSchema.safeParse(args);
|
||||
if (!parsed.success) {
|
||||
throw new Error(`Invalid arguments for edit_file: ${parsed.error}`);
|
||||
@@ -366,17 +370,18 @@ server.registerTool({
|
||||
content: [{ type: "text", text: result }],
|
||||
};
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
server.registerTool({
|
||||
name: "create_directory",
|
||||
description:
|
||||
"Create a new directory or ensure a directory exists. Can create multiple " +
|
||||
"nested directories in one operation. If the directory already exists, " +
|
||||
"this operation will succeed silently. Perfect for setting up directory " +
|
||||
"structures for projects or ensuring required paths exist. Only works within allowed directories.",
|
||||
inputSchema: zodToJsonSchema(CreateDirectoryArgsSchema) as ToolInput,
|
||||
handler: async (args: any) => {
|
||||
server.registerTool(
|
||||
"create_directory",
|
||||
{
|
||||
description: "Create a new directory or ensure a directory exists. Can create multiple " +
|
||||
"nested directories in one operation. If the directory already exists, " +
|
||||
"this operation will succeed silently. Perfect for setting up directory " +
|
||||
"structures for projects or ensuring required paths exist. Only works within allowed directories.",
|
||||
inputSchema: CreateDirectoryArgsSchema.shape
|
||||
},
|
||||
async (args: any) => {
|
||||
const parsed = CreateDirectoryArgsSchema.safeParse(args);
|
||||
if (!parsed.success) {
|
||||
throw new Error(`Invalid arguments for create_directory: ${parsed.error}`);
|
||||
@@ -387,17 +392,18 @@ server.registerTool({
|
||||
content: [{ type: "text", text: `Successfully created directory ${parsed.data.path}` }],
|
||||
};
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
server.registerTool({
|
||||
name: "list_directory",
|
||||
description:
|
||||
"Get a detailed listing of all files and directories in a specified path. " +
|
||||
"Results clearly distinguish between files and directories with [FILE] and [DIR] " +
|
||||
"prefixes. This tool is essential for understanding directory structure and " +
|
||||
"finding specific files within a directory. Only works within allowed directories.",
|
||||
inputSchema: zodToJsonSchema(ListDirectoryArgsSchema) as ToolInput,
|
||||
handler: async (args: any) => {
|
||||
server.registerTool(
|
||||
"list_directory",
|
||||
{
|
||||
description: "Get a detailed listing of all files and directories in a specified path. " +
|
||||
"Results clearly distinguish between files and directories with [FILE] and [DIR] " +
|
||||
"prefixes. This tool is essential for understanding directory structure and " +
|
||||
"finding specific files within a directory. Only works within allowed directories.",
|
||||
inputSchema: ListDirectoryArgsSchema.shape
|
||||
},
|
||||
async (args: any) => {
|
||||
const parsed = ListDirectoryArgsSchema.safeParse(args);
|
||||
if (!parsed.success) {
|
||||
throw new Error(`Invalid arguments for list_directory: ${parsed.error}`);
|
||||
@@ -411,17 +417,18 @@ server.registerTool({
|
||||
content: [{ type: "text", text: formatted }],
|
||||
};
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
server.registerTool({
|
||||
name: "list_directory_with_sizes",
|
||||
description:
|
||||
"Get a detailed listing of all files and directories in a specified path, including sizes. " +
|
||||
"Results clearly distinguish between files and directories with [FILE] and [DIR] " +
|
||||
"prefixes. This tool is useful for understanding directory structure and " +
|
||||
"finding specific files within a directory. Only works within allowed directories.",
|
||||
inputSchema: zodToJsonSchema(ListDirectoryWithSizesArgsSchema) as ToolInput,
|
||||
handler: async (args: any) => {
|
||||
server.registerTool(
|
||||
"list_directory_with_sizes",
|
||||
{
|
||||
description: "Get a detailed listing of all files and directories in a specified path, including sizes. " +
|
||||
"Results clearly distinguish between files and directories with [FILE] and [DIR] " +
|
||||
"prefixes. This tool is useful for understanding directory structure and " +
|
||||
"finding specific files within a directory. Only works within allowed directories.",
|
||||
inputSchema: ListDirectoryWithSizesArgsSchema.shape
|
||||
},
|
||||
async (args: any) => {
|
||||
const parsed = ListDirectoryWithSizesArgsSchema.safeParse(args);
|
||||
if (!parsed.success) {
|
||||
throw new Error(`Invalid arguments for list_directory_with_sizes: ${parsed.error}`);
|
||||
@@ -486,17 +493,18 @@ server.registerTool({
|
||||
}],
|
||||
};
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
server.registerTool({
|
||||
name: "directory_tree",
|
||||
description:
|
||||
"Get a recursive tree view of files and directories as a JSON structure. " +
|
||||
"Each entry includes 'name', 'type' (file/directory), and 'children' for directories. " +
|
||||
"Files have no children array, while directories always have a children array (which may be empty). " +
|
||||
"The output is formatted with 2-space indentation for readability. Only works within allowed directories.",
|
||||
inputSchema: zodToJsonSchema(DirectoryTreeArgsSchema) as ToolInput,
|
||||
handler: async (args: any) => {
|
||||
server.registerTool(
|
||||
"directory_tree",
|
||||
{
|
||||
description: "Get a recursive tree view of files and directories as a JSON structure. " +
|
||||
"Each entry includes 'name', 'type' (file/directory), and 'children' for directories. " +
|
||||
"Files have no children array, while directories always have a children array (which may be empty). " +
|
||||
"The output is formatted with 2-space indentation for readability. Only works within allowed directories.",
|
||||
inputSchema: DirectoryTreeArgsSchema.shape
|
||||
},
|
||||
async (args: any) => {
|
||||
const parsed = DirectoryTreeArgsSchema.safeParse(args);
|
||||
if (!parsed.success) {
|
||||
throw new Error(`Invalid arguments for directory_tree: ${parsed.error}`);
|
||||
@@ -553,17 +561,18 @@ server.registerTool({
|
||||
}],
|
||||
};
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
server.registerTool({
|
||||
name: "move_file",
|
||||
description:
|
||||
"Move or rename files and directories. Can move files between directories " +
|
||||
"and rename them in a single operation. If the destination exists, the " +
|
||||
"operation will fail. Works across different directories and can be used " +
|
||||
"for simple renaming within the same directory. Both source and destination must be within allowed directories.",
|
||||
inputSchema: zodToJsonSchema(MoveFileArgsSchema) as ToolInput,
|
||||
handler: async (args: any) => {
|
||||
server.registerTool(
|
||||
"move_file",
|
||||
{
|
||||
description: "Move or rename files and directories. Can move files between directories " +
|
||||
"and rename them in a single operation. If the destination exists, the " +
|
||||
"operation will fail. Works across different directories and can be used " +
|
||||
"for simple renaming within the same directory. Both source and destination must be within allowed directories.",
|
||||
inputSchema: MoveFileArgsSchema.shape
|
||||
},
|
||||
async (args: any) => {
|
||||
const parsed = MoveFileArgsSchema.safeParse(args);
|
||||
if (!parsed.success) {
|
||||
throw new Error(`Invalid arguments for move_file: ${parsed.error}`);
|
||||
@@ -575,18 +584,19 @@ server.registerTool({
|
||||
content: [{ type: "text", text: `Successfully moved ${parsed.data.source} to ${parsed.data.destination}` }],
|
||||
};
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
server.registerTool({
|
||||
name: "search_files",
|
||||
description:
|
||||
"Recursively search for files and directories matching a pattern. " +
|
||||
"The patterns should be glob-style patterns that match paths relative to the working directory. " +
|
||||
"Use pattern like '*.ext' to match files in current directory, and '**/*.ext' to match files in all subdirectories. " +
|
||||
"Returns full paths to all matching items. Great for finding files when you don't know their exact location. " +
|
||||
"Only searches within allowed directories.",
|
||||
inputSchema: zodToJsonSchema(SearchFilesArgsSchema) as ToolInput,
|
||||
handler: async (args: any) => {
|
||||
server.registerTool(
|
||||
"search_files",
|
||||
{
|
||||
description: "Recursively search for files and directories matching a pattern. " +
|
||||
"The patterns should be glob-style patterns that match paths relative to the working directory. " +
|
||||
"Use pattern like '*.ext' to match files in current directory, and '**/*.ext' to match files in all subdirectories. " +
|
||||
"Returns full paths to all matching items. Great for finding files when you don't know their exact location. " +
|
||||
"Only searches within allowed directories.",
|
||||
inputSchema: SearchFilesArgsSchema.shape
|
||||
},
|
||||
async (args: any) => {
|
||||
const parsed = SearchFilesArgsSchema.safeParse(args);
|
||||
if (!parsed.success) {
|
||||
throw new Error(`Invalid arguments for search_files: ${parsed.error}`);
|
||||
@@ -597,17 +607,18 @@ server.registerTool({
|
||||
content: [{ type: "text", text: results.length > 0 ? results.join("\n") : "No matches found" }],
|
||||
};
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
server.registerTool({
|
||||
name: "get_file_info",
|
||||
description:
|
||||
"Retrieve detailed metadata about a file or directory. Returns comprehensive " +
|
||||
"information including size, creation time, last modified time, permissions, " +
|
||||
"and type. This tool is perfect for understanding file characteristics " +
|
||||
"without reading the actual content. Only works within allowed directories.",
|
||||
inputSchema: zodToJsonSchema(GetFileInfoArgsSchema) as ToolInput,
|
||||
handler: async (args: any) => {
|
||||
server.registerTool(
|
||||
"get_file_info",
|
||||
{
|
||||
description: "Retrieve detailed metadata about a file or directory. Returns comprehensive " +
|
||||
"information including size, creation time, last modified time, permissions, " +
|
||||
"and type. This tool is perfect for understanding file characteristics " +
|
||||
"without reading the actual content. Only works within allowed directories.",
|
||||
inputSchema: GetFileInfoArgsSchema.shape
|
||||
},
|
||||
async (args: any) => {
|
||||
const parsed = GetFileInfoArgsSchema.safeParse(args);
|
||||
if (!parsed.success) {
|
||||
throw new Error(`Invalid arguments for get_file_info: ${parsed.error}`);
|
||||
@@ -620,21 +631,18 @@ server.registerTool({
|
||||
.join("\n") }],
|
||||
};
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
server.registerTool({
|
||||
name: "list_allowed_directories",
|
||||
description:
|
||||
"Returns the list of directories that this server is allowed to access. " +
|
||||
"Subdirectories within these allowed directories are also accessible. " +
|
||||
"Use this to understand which directories and their nested paths are available " +
|
||||
"before trying to access files.",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {},
|
||||
required: [],
|
||||
server.registerTool(
|
||||
"list_allowed_directories",
|
||||
{
|
||||
description: "Returns the list of directories that this server is allowed to access. " +
|
||||
"Subdirectories within these allowed directories are also accessible. " +
|
||||
"Use this to understand which directories and their nested paths are available " +
|
||||
"before trying to access files.",
|
||||
inputSchema: z.object({}).shape
|
||||
},
|
||||
handler: async (args: any) => {
|
||||
async (args: any) => {
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
@@ -642,7 +650,7 @@ server.registerTool({
|
||||
}],
|
||||
};
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
// Updates allowed directories based on MCP client roots
|
||||
async function updateAllowedDirectoriesFromRoots(requestedRoots: Root[]) {
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
"diff": "^5.1.0",
|
||||
"glob": "^10.3.10",
|
||||
"minimatch": "^10.0.1",
|
||||
"zod": "^3.23.8",
|
||||
"zod-to-json-schema": "^3.23.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
||||
import { McpServer } from "@modelcontextprotocol/sdk/server/index.js";
|
||||
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
||||
import { promises as fs } from 'fs';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { z } from "zod";
|
||||
|
||||
// Define memory file path using environment variable with fallback
|
||||
const defaultMemoryPath = path.join(path.dirname(fileURLToPath(import.meta.url)), 'memory.json');
|
||||
@@ -191,239 +192,170 @@ class KnowledgeGraphManager {
|
||||
|
||||
const knowledgeGraphManager = new KnowledgeGraphManager();
|
||||
|
||||
// Define Zod schemas for all tools
|
||||
const CreateEntitiesSchema = z.object({
|
||||
entities: z.array(z.object({
|
||||
name: z.string().describe("The name of the entity"),
|
||||
entityType: z.string().describe("The type of the entity"),
|
||||
observations: z.array(z.string()).describe("An array of observation contents associated with the entity")
|
||||
}))
|
||||
});
|
||||
|
||||
const CreateRelationsSchema = z.object({
|
||||
relations: z.array(z.object({
|
||||
from: z.string().describe("The name of the entity where the relation starts"),
|
||||
to: z.string().describe("The name of the entity where the relation ends"),
|
||||
relationType: z.string().describe("The type of the relation")
|
||||
}))
|
||||
});
|
||||
|
||||
const AddObservationsSchema = z.object({
|
||||
observations: z.array(z.object({
|
||||
entityName: z.string().describe("The name of the entity to add the observations to"),
|
||||
contents: z.array(z.string()).describe("An array of observation contents to add")
|
||||
}))
|
||||
});
|
||||
|
||||
const DeleteEntitiesSchema = z.object({
|
||||
entityNames: z.array(z.string()).describe("An array of entity names to delete")
|
||||
});
|
||||
|
||||
const DeleteObservationsSchema = z.object({
|
||||
deletions: z.array(z.object({
|
||||
entityName: z.string().describe("The name of the entity containing the observations"),
|
||||
observations: z.array(z.string()).describe("An array of observations to delete")
|
||||
}))
|
||||
});
|
||||
|
||||
const DeleteRelationsSchema = z.object({
|
||||
relations: z.array(z.object({
|
||||
from: z.string().describe("The name of the entity where the relation starts"),
|
||||
to: z.string().describe("The name of the entity where the relation ends"),
|
||||
relationType: z.string().describe("The type of the relation")
|
||||
})).describe("An array of relations to delete")
|
||||
});
|
||||
|
||||
const ReadGraphSchema = z.object({});
|
||||
|
||||
const SearchNodesSchema = z.object({
|
||||
query: z.string().describe("The search query to match against entity names, types, and observation content")
|
||||
});
|
||||
|
||||
const OpenNodesSchema = z.object({
|
||||
names: z.array(z.string()).describe("An array of entity names to retrieve")
|
||||
});
|
||||
|
||||
// The server instance and tools exposed to Claude
|
||||
const server = new Server({
|
||||
const server = new McpServer({
|
||||
name: "memory-server",
|
||||
version: "0.6.3",
|
||||
}, {
|
||||
capabilities: {
|
||||
tools: {},
|
||||
},
|
||||
},);
|
||||
|
||||
server.registerTool({
|
||||
name: "create_entities",
|
||||
description: "Create multiple new entities in the knowledge graph",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
entities: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "object",
|
||||
properties: {
|
||||
name: { type: "string", description: "The name of the entity" },
|
||||
entityType: { type: "string", description: "The type of the entity" },
|
||||
observations: {
|
||||
type: "array",
|
||||
items: { type: "string" },
|
||||
description: "An array of observation contents associated with the entity"
|
||||
},
|
||||
},
|
||||
required: ["name", "entityType", "observations"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
required: ["entities"],
|
||||
additionalProperties: false,
|
||||
}, {
|
||||
capabilities: {
|
||||
tools: {},
|
||||
},
|
||||
handler: async (args) => {
|
||||
});
|
||||
|
||||
server.registerTool(
|
||||
"create_entities",
|
||||
{
|
||||
description: "Create multiple new entities in the knowledge graph",
|
||||
inputSchema: CreateEntitiesSchema.shape
|
||||
},
|
||||
async (args) => {
|
||||
return { content: [{ type: "text", text: JSON.stringify(await knowledgeGraphManager.createEntities(args.entities as Entity[]), null, 2) }] };
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
server.registerTool({
|
||||
name: "create_relations",
|
||||
description: "Create multiple new relations between entities in the knowledge graph. Relations should be in active voice",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
relations: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "object",
|
||||
properties: {
|
||||
from: { type: "string", description: "The name of the entity where the relation starts" },
|
||||
to: { type: "string", description: "The name of the entity where the relation ends" },
|
||||
relationType: { type: "string", description: "The type of the relation" },
|
||||
},
|
||||
required: ["from", "to", "relationType"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
required: ["relations"],
|
||||
additionalProperties: false,
|
||||
server.registerTool(
|
||||
"create_relations",
|
||||
{
|
||||
description: "Create multiple new relations between entities in the knowledge graph. Relations should be in active voice",
|
||||
inputSchema: CreateRelationsSchema.shape
|
||||
},
|
||||
handler: async (args) => {
|
||||
async (args) => {
|
||||
return { content: [{ type: "text", text: JSON.stringify(await knowledgeGraphManager.createRelations(args.relations as Relation[]), null, 2) }] };
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
server.registerTool({
|
||||
name: "add_observations",
|
||||
description: "Add new observations to existing entities in the knowledge graph",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
observations: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "object",
|
||||
properties: {
|
||||
entityName: { type: "string", description: "The name of the entity to add the observations to" },
|
||||
contents: {
|
||||
type: "array",
|
||||
items: { type: "string" },
|
||||
description: "An array of observation contents to add"
|
||||
},
|
||||
},
|
||||
required: ["entityName", "contents"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
required: ["observations"],
|
||||
additionalProperties: false,
|
||||
server.registerTool(
|
||||
"add_observations",
|
||||
{
|
||||
description: "Add new observations to existing entities in the knowledge graph",
|
||||
inputSchema: AddObservationsSchema.shape
|
||||
},
|
||||
handler: async (args) => {
|
||||
async (args) => {
|
||||
return { content: [{ type: "text", text: JSON.stringify(await knowledgeGraphManager.addObservations(args.observations as { entityName: string; contents: string[] }[]), null, 2) }] };
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
server.registerTool({
|
||||
name: "delete_entities",
|
||||
description: "Delete multiple entities and their associated relations from the knowledge graph",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
entityNames: {
|
||||
type: "array",
|
||||
items: { type: "string" },
|
||||
description: "An array of entity names to delete"
|
||||
},
|
||||
},
|
||||
required: ["entityNames"],
|
||||
additionalProperties: false,
|
||||
server.registerTool(
|
||||
"delete_entities",
|
||||
{
|
||||
description: "Delete multiple entities and their associated relations from the knowledge graph",
|
||||
inputSchema: DeleteEntitiesSchema.shape
|
||||
},
|
||||
handler: async (args) => {
|
||||
async (args) => {
|
||||
await knowledgeGraphManager.deleteEntities(args.entityNames as string[]);
|
||||
return { content: [{ type: "text", text: "Entities deleted successfully" }] };
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
server.registerTool({
|
||||
name: "delete_observations",
|
||||
description: "Delete specific observations from entities in the knowledge graph",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
deletions: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "object",
|
||||
properties: {
|
||||
entityName: { type: "string", description: "The name of the entity containing the observations" },
|
||||
observations: {
|
||||
type: "array",
|
||||
items: { type: "string" },
|
||||
description: "An array of observations to delete"
|
||||
},
|
||||
},
|
||||
required: ["entityName", "observations"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
required: ["deletions"],
|
||||
additionalProperties: false,
|
||||
server.registerTool(
|
||||
"delete_observations",
|
||||
{
|
||||
description: "Delete specific observations from entities in the knowledge graph",
|
||||
inputSchema: DeleteObservationsSchema.shape
|
||||
},
|
||||
handler: async (args) => {
|
||||
async (args) => {
|
||||
await knowledgeGraphManager.deleteObservations(args.deletions as { entityName: string; observations: string[] }[]);
|
||||
return { content: [{ type: "text", text: "Observations deleted successfully" }] };
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
server.registerTool({
|
||||
name: "delete_relations",
|
||||
description: "Delete multiple relations from the knowledge graph",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
relations: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "object",
|
||||
properties: {
|
||||
from: { type: "string", description: "The name of the entity where the relation starts" },
|
||||
to: { type: "string", description: "The name of the entity where the relation ends" },
|
||||
relationType: { type: "string", description: "The type of the relation" },
|
||||
},
|
||||
required: ["from", "to", "relationType"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
description: "An array of relations to delete"
|
||||
},
|
||||
},
|
||||
required: ["relations"],
|
||||
additionalProperties: false,
|
||||
server.registerTool(
|
||||
"delete_relations",
|
||||
{
|
||||
description: "Delete multiple relations from the knowledge graph",
|
||||
inputSchema: DeleteRelationsSchema.shape
|
||||
},
|
||||
handler: async (args) => {
|
||||
async (args) => {
|
||||
await knowledgeGraphManager.deleteRelations(args.relations as Relation[]);
|
||||
return { content: [{ type: "text", text: "Relations deleted successfully" }] };
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
server.registerTool({
|
||||
name: "read_graph",
|
||||
description: "Read the entire knowledge graph",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {},
|
||||
additionalProperties: false,
|
||||
server.registerTool(
|
||||
"read_graph",
|
||||
{
|
||||
description: "Read the entire knowledge graph",
|
||||
inputSchema: ReadGraphSchema.shape
|
||||
},
|
||||
handler: async () => {
|
||||
async () => {
|
||||
return { content: [{ type: "text", text: JSON.stringify(await knowledgeGraphManager.readGraph(), null, 2) }] };
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
server.registerTool({
|
||||
name: "search_nodes",
|
||||
description: "Search for nodes in the knowledge graph based on a query",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
query: { type: "string", description: "The search query to match against entity names, types, and observation content" },
|
||||
},
|
||||
required: ["query"],
|
||||
additionalProperties: false,
|
||||
server.registerTool(
|
||||
"search_nodes",
|
||||
{
|
||||
description: "Search for nodes in the knowledge graph based on a query",
|
||||
inputSchema: SearchNodesSchema.shape
|
||||
},
|
||||
handler: async (args) => {
|
||||
async (args) => {
|
||||
return { content: [{ type: "text", text: JSON.stringify(await knowledgeGraphManager.searchNodes(args.query as string), null, 2) }] };
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
server.registerTool({
|
||||
name: "open_nodes",
|
||||
description: "Open specific nodes in the knowledge graph by their names",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
names: {
|
||||
type: "array",
|
||||
items: { type: "string" },
|
||||
description: "An array of entity names to retrieve",
|
||||
},
|
||||
},
|
||||
required: ["names"],
|
||||
additionalProperties: false,
|
||||
server.registerTool(
|
||||
"open_nodes",
|
||||
{
|
||||
description: "Open specific nodes in the knowledge graph by their names",
|
||||
inputSchema: OpenNodesSchema.shape
|
||||
},
|
||||
handler: async (args) => {
|
||||
async (args) => {
|
||||
return { content: [{ type: "text", text: JSON.stringify(await knowledgeGraphManager.openNodes(args.names as string[]), null, 2) }] };
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
async function main() {
|
||||
const transport = new StdioServerTransport();
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
"watch": "tsc --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.20.1"
|
||||
"@modelcontextprotocol/sdk": "^1.20.1",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22",
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
||||
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
||||
import {
|
||||
Tool,
|
||||
} from "@modelcontextprotocol/sdk/types.js";
|
||||
import { z } from "zod";
|
||||
// Fixed chalk import for ESM
|
||||
import chalk from 'chalk';
|
||||
|
||||
@@ -135,9 +133,19 @@ class SequentialThinkingServer {
|
||||
}
|
||||
}
|
||||
|
||||
const SEQUENTIAL_THINKING_TOOL: Tool = {
|
||||
name: "sequentialthinking",
|
||||
description: `A detailed tool for dynamic and reflective problem-solving through thoughts.
|
||||
const SequentialThinkingToolSchema = z.object({
|
||||
thought: z.string().describe("Your current thinking step"),
|
||||
nextThoughtNeeded: z.boolean().describe("Whether another thought step is needed"),
|
||||
thoughtNumber: z.number().int().min(1).describe("Current thought number (numeric value, e.g., 1, 2, 3)"),
|
||||
totalThoughts: z.number().int().min(1).describe("Estimated total thoughts needed (numeric value, e.g., 5, 10)"),
|
||||
isRevision: z.boolean().optional().describe("Whether this revises previous thinking"),
|
||||
revisesThought: z.number().int().min(1).optional().describe("Which thought is being reconsidered"),
|
||||
branchFromThought: z.number().int().min(1).optional().describe("Branching point thought number"),
|
||||
branchId: z.string().optional().describe("Branch identifier"),
|
||||
needsMoreThoughts: z.boolean().optional().describe("If more thoughts are needed")
|
||||
});
|
||||
|
||||
const TOOL_DESCRIPTION = `A detailed tool for dynamic and reflective problem-solving through thoughts.
|
||||
This tool helps analyze problems through a flexible thinking process that can adapt and evolve.
|
||||
Each thought can build on, question, or revise previous insights as understanding deepens.
|
||||
|
||||
@@ -190,77 +198,25 @@ You should:
|
||||
8. Verify the hypothesis based on the Chain of Thought steps
|
||||
9. Repeat the process until satisfied with the solution
|
||||
10. Provide a single, ideally correct answer as the final output
|
||||
11. Only set next_thought_needed to false when truly done and a satisfactory answer is reached`,
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
thought: {
|
||||
type: "string",
|
||||
description: "Your current thinking step"
|
||||
},
|
||||
nextThoughtNeeded: {
|
||||
type: "boolean",
|
||||
description: "Whether another thought step is needed"
|
||||
},
|
||||
thoughtNumber: {
|
||||
type: "integer",
|
||||
description: "Current thought number (numeric value, e.g., 1, 2, 3)",
|
||||
minimum: 1
|
||||
},
|
||||
totalThoughts: {
|
||||
type: "integer",
|
||||
description: "Estimated total thoughts needed (numeric value, e.g., 5, 10)",
|
||||
minimum: 1
|
||||
},
|
||||
isRevision: {
|
||||
type: "boolean",
|
||||
description: "Whether this revises previous thinking"
|
||||
},
|
||||
revisesThought: {
|
||||
type: "integer",
|
||||
description: "Which thought is being reconsidered",
|
||||
minimum: 1
|
||||
},
|
||||
branchFromThought: {
|
||||
type: "integer",
|
||||
description: "Branching point thought number",
|
||||
minimum: 1
|
||||
},
|
||||
branchId: {
|
||||
type: "string",
|
||||
description: "Branch identifier"
|
||||
},
|
||||
needsMoreThoughts: {
|
||||
type: "boolean",
|
||||
description: "If more thoughts are needed"
|
||||
}
|
||||
},
|
||||
required: ["thought", "nextThoughtNeeded", "thoughtNumber", "totalThoughts"]
|
||||
}
|
||||
};
|
||||
11. Only set next_thought_needed to false when truly done and a satisfactory answer is reached`;
|
||||
|
||||
const server = new Server(
|
||||
{
|
||||
name: "sequential-thinking-server",
|
||||
version: "0.2.0",
|
||||
},
|
||||
{
|
||||
capabilities: {
|
||||
tools: {},
|
||||
},
|
||||
}
|
||||
);
|
||||
const server = new McpServer({
|
||||
name: "sequential-thinking-server",
|
||||
version: "0.2.0",
|
||||
});
|
||||
|
||||
const thinkingServer = new SequentialThinkingServer();
|
||||
|
||||
server.registerTool({
|
||||
name: SEQUENTIAL_THINKING_TOOL.name,
|
||||
description: SEQUENTIAL_THINKING_TOOL.description,
|
||||
inputSchema: SEQUENTIAL_THINKING_TOOL.inputSchema,
|
||||
handler: async (args) => {
|
||||
server.registerTool(
|
||||
"sequentialthinking",
|
||||
{
|
||||
description: TOOL_DESCRIPTION,
|
||||
inputSchema: SequentialThinkingToolSchema.shape,
|
||||
},
|
||||
async (args) => {
|
||||
return thinkingServer.processThought(args);
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
async function runServer() {
|
||||
const transport = new StdioServerTransport();
|
||||
|
||||
@@ -21,7 +21,8 @@
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.20.1",
|
||||
"chalk": "^5.3.0",
|
||||
"yargs": "^17.7.2"
|
||||
"yargs": "^17.7.2",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22",
|
||||
|
||||
Reference in New Issue
Block a user