import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import fs from "fs";
import path from "path";
interface ToolDefinition {
name: string;
description: string;
schema: any;
handler: (params: any) => Promise<any>;
}
const tools: ToolDefinition[] = [
{
name: "hello-world",
description: "Say hello to the user",
schema: {
name: z.string().describe("The name of the user"),
},
handler: async ({ name }) => {
const response = `Hello ${name}`;
return {
content: [
{
type: "text",
text: response,
},
],
};
},
},
{
name: "get-mcp-docs",
description: "Make an MCP server",
schema: {
name: z.string().describe("The name of the MCP server"),
},
handler: async () => {
const response = `
# Main file for the MCP server
\`\`\`ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// Create the MCP server
const server = new McpServer({
name: "hello-world",
version: "1.0.0",
});
// Tool: Store conversation with embeddings
server.tool(
"hello-world",
"Say hello to the user",
{
name: z.string().describe("The name of the user"),
},
async ({ name }) => {
const response = \`Hello \${name}\`;
return {
content: [
{
type: "text",
text: response,
},
];
};
}
);
// Start the server
async function main() {
try {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("MCP Hello World Server running...");
} catch (error) {
console.error("Error starting server:", error);
process.exit(1);
}
}
main().catch(console.error);
\`\`\`
`;
return {
content: [
{
type: "text",
text: response,
},
],
};
},
},
{
name: "save-context",
description: "Save conversation context to a markdown file with proper formatting for future Claude sessions",
schema: {
projectName: z.string().describe("Name of the project/topic"),
contextType: z.enum(["planning", "progress", "technical-notes", "decisions", "general"]).describe("Type of context being saved"),
content: z.string().describe("The conversation content to save"),
summary: z.string().optional().describe("Optional summary of the context"),
nextSteps: z.array(z.string()).optional().describe("List of next steps or action items"),
keyDecisions: z.array(z.string()).optional().describe("Important decisions made"),
filePath: z.string().optional().describe("Custom file path (relative to current directory)")
},
handler: async ({ projectName, contextType, content, summary, nextSteps, keyDecisions, filePath }) => {
try {
// Generate filename if not provided
const timestamp = new Date().toISOString().split('T')[0];
const defaultFilename = `${projectName}-${contextType}-${timestamp}.md`;
const filename = filePath || path.join(process.cwd(), "context", defaultFilename);
// Ensure directory exists
const dir = path.dirname(filename);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
// Format content according to the workflow patterns from Reddit post
const formattedContent = `# ${projectName} - ${contextType.charAt(0).toUpperCase() + contextType.slice(1)}
**Created:** ${new Date().toISOString()}
**Context Type:** ${contextType}
## Summary
${summary || "Context saved from conversation"}
## Content
${content}
${keyDecisions && keyDecisions.length > 0 ? `## Key Decisions Made
${keyDecisions.map(decision => `- ${decision}`).join('\n')}
` : ''}${nextSteps && nextSteps.length > 0 ? `## Next Steps
${nextSteps.map((step, index) => `${index + 1}. ${step}`).join('\n')}
` : ''}## Technical Context
- **Status:** In Progress
- **Last Updated:** ${new Date().toISOString()}
- **Context Window Management:** Fresh context available for next session
## Usage Instructions
This file can be referenced in future Claude sessions by:
1. Starting a new conversation
2. Uploading this file or referencing its contents
3. Asking Claude to continue from where we left off
---
*Generated by save-context MCP tool*
`;
// Write the file
fs.writeFileSync(filename, formattedContent, 'utf8');
return {
content: [
{
type: "text",
text: `✅ Context saved successfully to: ${filename}\n\n**File Contents Preview:**\n\`\`\`markdown\n${formattedContent.slice(0, 300)}...\n\`\`\`\n\n**Usage:** Reference this file in future conversations to maintain context continuity.`
}
]
};
} catch (error) {
return {
content: [
{
type: "text",
text: `❌ Error saving context: ${error.message}`
}
]
};
}
},
},
{
name: "list-context-files",
description: "List all saved context files in the context directory",
schema: {
projectName: z.string().optional().describe("Filter by project name")
},
handler: async ({ projectName }) => {
try {
const contextDir = path.join(process.cwd(), "context");
if (!fs.existsSync(contextDir)) {
return {
content: [
{
type: "text",
text: "No context directory found. Save some context first using the save-context tool."
}
]
};
}
const files = fs.readdirSync(contextDir)
.filter(file => file.endsWith('.md'))
.filter(file => !projectName || file.includes(projectName))
.sort((a, b) => {
const statA = fs.statSync(path.join(contextDir, a));
const statB = fs.statSync(path.join(contextDir, b));
return statB.mtime.getTime() - statA.mtime.getTime(); // Most recent first
});
if (files.length === 0) {
return {
content: [
{
type: "text",
text: projectName
? `No context files found for project: ${projectName}`
: "No context files found in the context directory."
}
]
};
}
const fileList = files.map(file => {
const filePath = path.join(contextDir, file);
const stats = fs.statSync(filePath);
const content = fs.readFileSync(filePath, 'utf8');
const summaryMatch = content.match(/## Summary\n(.*?)\n\n/s);
const summary = summaryMatch ? summaryMatch[1].trim() : "No summary available";
return `**${file}**\n- Modified: ${stats.mtime.toISOString().split('T')[0]}\n- Summary: ${summary}\n- Path: ${filePath}`;
}).join('\n\n');
return {
content: [
{
type: "text",
text: `## Context Files ${projectName ? `for ${projectName}` : ''}\n\n${fileList}\n\n**Tip:** Use these files to maintain context across conversations by referencing them in new sessions.`
}
]
};
} catch (error) {
return {
content: [
{
type: "text",
text: `❌ Error listing context files: ${error.message}`
}
]
};
}
},
}
];
export function registerTools(server: McpServer, enabledTools?: string[]) {
const toolsToRegister =
enabledTools && enabledTools.length > 0
? tools.filter((tool) => enabledTools.includes(tool.name))
: tools;
for (const tool of toolsToRegister) {
server.tool(tool.name, tool.description, tool.schema, tool.handler);
}
console.error(
`Registered ${toolsToRegister.length} tools: ${toolsToRegister
.map((t) => t.name)
.join(", ")}`
);
}