summaryrefslogtreecommitdiff
path: root/mcp-servers/simple-mcp-server/.claude/commands
diff options
context:
space:
mode:
authorTheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com>2026-01-16 08:30:14 +0900
committerTheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com>2026-01-16 08:30:14 +0900
commit3fbb9a18372f2b6a675dd6c039ba52be76f3eeb4 (patch)
treeaa694a36cdd323a7853672ee7a2ba60409ac3b06 /mcp-servers/simple-mcp-server/.claude/commands
updates
Diffstat (limited to 'mcp-servers/simple-mcp-server/.claude/commands')
-rw-r--r--mcp-servers/simple-mcp-server/.claude/commands/add-prompt.md242
-rw-r--r--mcp-servers/simple-mcp-server/.claude/commands/add-resource.md243
-rw-r--r--mcp-servers/simple-mcp-server/.claude/commands/add-tool.md207
-rw-r--r--mcp-servers/simple-mcp-server/.claude/commands/build.md377
-rw-r--r--mcp-servers/simple-mcp-server/.claude/commands/debug.md310
-rw-r--r--mcp-servers/simple-mcp-server/.claude/commands/deploy.md376
-rw-r--r--mcp-servers/simple-mcp-server/.claude/commands/init.md178
-rw-r--r--mcp-servers/simple-mcp-server/.claude/commands/test.md261
8 files changed, 2194 insertions, 0 deletions
diff --git a/mcp-servers/simple-mcp-server/.claude/commands/add-prompt.md b/mcp-servers/simple-mcp-server/.claude/commands/add-prompt.md
new file mode 100644
index 0000000..5f9f007
--- /dev/null
+++ b/mcp-servers/simple-mcp-server/.claude/commands/add-prompt.md
@@ -0,0 +1,242 @@
+# Add Prompt Template to MCP Server
+
+Adds a new prompt template to your MCP server for reusable conversation patterns.
+
+## Usage
+
+```
+/add-prompt <name> <description> [arguments]
+```
+
+## Examples
+
+```
+/add-prompt code_review "Review code for improvements" language:string file:string?
+/add-prompt analyze_data "Analyze data patterns" dataset:string metrics:array
+/add-prompt generate_docs "Generate documentation" codebase:string style:enum[minimal,detailed]
+```
+
+## Implementation
+
+```typescript
+import * as fs from 'fs/promises';
+import * as path from 'path';
+
+async function addPrompt(
+ name: string,
+ description: string,
+ argumentDefs?: string[]
+) {
+ // Parse arguments
+ const args = parsePromptArguments(argumentDefs || []);
+
+ // Generate prompt file
+ const promptContent = generatePromptFile(name, description, args);
+
+ // Write prompt file
+ const promptPath = path.join('src/prompts', `${name}.ts`);
+ await fs.writeFile(promptPath, promptContent);
+
+ // Update prompt index
+ await updatePromptIndex(name);
+
+ // Generate test file
+ const testContent = generatePromptTest(name);
+ const testPath = path.join('tests/unit/prompts', `${name}.test.ts`);
+ await fs.writeFile(testPath, testContent);
+
+ console.log(`โœ… Prompt "${name}" added successfully!`);
+ console.log(` - Implementation: ${promptPath}`);
+ console.log(` - Test file: ${testPath}`);
+ console.log(`\nNext steps:`);
+ console.log(` 1. Define the prompt template in ${promptPath}`);
+ console.log(` 2. Test with MCP Inspector`);
+}
+
+interface PromptArgument {
+ name: string;
+ description: string;
+ required: boolean;
+ type: string;
+}
+
+function parsePromptArguments(argumentDefs: string[]): PromptArgument[] {
+ return argumentDefs.map(def => {
+ const [nameWithType, description] = def.split('=');
+ const [nameType] = nameWithType.split(':');
+ const isOptional = nameType.endsWith('?');
+ const name = isOptional ? nameType.slice(0, -1) : nameType;
+ const type = nameWithType.split(':')[1] || 'string';
+
+ return {
+ name,
+ description: description || `${name} parameter`,
+ required: !isOptional,
+ type: type.replace('?', ''),
+ };
+ });
+}
+
+function generatePromptFile(
+ name: string,
+ description: string,
+ args: PromptArgument[]
+): string {
+ return `
+import type { Prompt, PromptMessage } from '../types/prompts.js';
+
+export const ${name}Prompt: Prompt = {
+ name: '${name}',
+ description: '${description}',
+ arguments: [
+${args.map(arg => ` {
+ name: '${arg.name}',
+ description: '${arg.description}',
+ required: ${arg.required},
+ },`).join('\n')}
+ ],
+};
+
+export function get${capitalize(name)}Prompt(
+ args: Record<string, unknown>
+): PromptMessage[] {
+ // Validate required arguments
+${args.filter(a => a.required).map(arg => ` if (!args.${arg.name}) {
+ throw new Error('Missing required argument: ${arg.name}');
+ }`).join('\n')}
+
+ // Build prompt messages
+ const messages: PromptMessage[] = [];
+
+ // System message (optional)
+ messages.push({
+ role: 'system',
+ content: {
+ type: 'text',
+ text: buildSystemPrompt(args),
+ },
+ });
+
+ // User message
+ messages.push({
+ role: 'user',
+ content: {
+ type: 'text',
+ text: buildUserPrompt(args),
+ },
+ });
+
+ return messages;
+}
+
+function buildSystemPrompt(args: Record<string, unknown>): string {
+ // TODO: Define the system prompt template
+ return \`You are an expert assistant helping with ${description.toLowerCase()}.\`;
+}
+
+function buildUserPrompt(args: Record<string, unknown>): string {
+ // TODO: Define the user prompt template
+ let prompt = '${description}\\n\\n';
+
+${args.map(arg => ` if (args.${arg.name}) {
+ prompt += \`${capitalize(arg.name)}: \${args.${arg.name}}\\n\`;
+ }`).join('\n')}
+
+ return prompt;
+}
+
+function capitalize(str: string): string {
+ return str.charAt(0).toUpperCase() + str.slice(1);
+}
+`;
+}
+
+function generatePromptTest(name: string): string {
+ return `
+import { describe, it, expect } from 'vitest';
+import { ${name}Prompt, get${capitalize(name)}Prompt } from '../../src/prompts/${name}.js';
+
+describe('${name} prompt', () => {
+ it('should have correct metadata', () => {
+ expect(${name}Prompt.name).toBe('${name}');
+ expect(${name}Prompt.description).toBeDefined();
+ expect(${name}Prompt.arguments).toBeDefined();
+ });
+
+ it('should generate prompt messages', () => {
+ const args = {
+ // TODO: Add test arguments
+ };
+
+ const messages = get${capitalize(name)}Prompt(args);
+
+ expect(messages).toBeInstanceOf(Array);
+ expect(messages.length).toBeGreaterThan(0);
+ expect(messages[0]).toHaveProperty('role');
+ expect(messages[0]).toHaveProperty('content');
+ });
+
+ it('should validate required arguments', () => {
+ const invalidArgs = {};
+
+ expect(() => get${capitalize(name)}Prompt(invalidArgs))
+ .toThrow('Missing required argument');
+ });
+
+ it('should handle optional arguments', () => {
+ const minimalArgs = {
+ // Only required args
+ };
+
+ const messages = get${capitalize(name)}Prompt(minimalArgs);
+ expect(messages).toBeDefined();
+ });
+});
+`;
+}
+
+function capitalize(str: string): string {
+ return str.charAt(0).toUpperCase() + str.slice(1);
+}
+
+async function updatePromptIndex(name: string) {
+ const indexPath = 'src/prompts/index.ts';
+
+ try {
+ let content = await fs.readFile(indexPath, 'utf-8');
+
+ // Add import
+ const importLine = `import { ${name}Prompt, get${capitalize(name)}Prompt } from './${name}.js';`;
+ if (!content.includes(importLine)) {
+ const lastImport = content.lastIndexOf('import');
+ const endOfLastImport = content.indexOf('\n', lastImport);
+ content = content.slice(0, endOfLastImport + 1) + importLine + '\n' + content.slice(endOfLastImport + 1);
+ }
+
+ // Add to exports
+ const exportPattern = /export const prompts = \[([^\]]*)]\;/;
+ const match = content.match(exportPattern);
+ if (match) {
+ const currentExports = match[1].trim();
+ const newExports = currentExports ? `${currentExports},\n ${name}Prompt` : `\n ${name}Prompt\n`;
+ content = content.replace(exportPattern, `export const prompts = [${newExports}];`);
+ }
+
+ await fs.writeFile(indexPath, content);
+ } catch (error) {
+ // Create index file if it doesn't exist
+ const newIndex = `
+import { ${name}Prompt, get${capitalize(name)}Prompt } from './${name}.js';
+
+export const prompts = [
+ ${name}Prompt,
+];
+
+export const promptHandlers = {
+ '${name}': get${capitalize(name)}Prompt,
+};
+`;
+ await fs.writeFile(indexPath, newIndex);
+ }
+}
+``` \ No newline at end of file
diff --git a/mcp-servers/simple-mcp-server/.claude/commands/add-resource.md b/mcp-servers/simple-mcp-server/.claude/commands/add-resource.md
new file mode 100644
index 0000000..aff9e54
--- /dev/null
+++ b/mcp-servers/simple-mcp-server/.claude/commands/add-resource.md
@@ -0,0 +1,243 @@
+# Add Resource to MCP Server
+
+Adds a new resource endpoint to your MCP server with proper URI handling.
+
+## Usage
+
+```
+/add-resource <name> <description> [uri-pattern] [mime-type]
+```
+
+## Examples
+
+```
+/add-resource config "Server configuration" config://settings application/json
+/add-resource users "User database" data://users/{id} application/json
+/add-resource files "File system access" file:///{path} text/plain
+```
+
+## Implementation
+
+```typescript
+import * as fs from 'fs/promises';
+import * as path from 'path';
+
+async function addResource(
+ name: string,
+ description: string,
+ uriPattern?: string,
+ mimeType: string = 'application/json'
+) {
+ // Generate URI pattern if not provided
+ const uri = uriPattern || `${name}://default`;
+
+ // Generate resource file
+ const resourceContent = generateResourceFile(name, description, uri, mimeType);
+
+ // Write resource file
+ const resourcePath = path.join('src/resources', `${name}.ts`);
+ await fs.writeFile(resourcePath, resourceContent);
+
+ // Update resource index
+ await updateResourceIndex(name);
+
+ // Generate test file
+ const testContent = generateResourceTest(name, uri);
+ const testPath = path.join('tests/unit/resources', `${name}.test.ts`);
+ await fs.writeFile(testPath, testContent);
+
+ console.log(`โœ… Resource "${name}" added successfully!`);
+ console.log(` - Implementation: ${resourcePath}`);
+ console.log(` - Test file: ${testPath}`);
+ console.log(` - URI pattern: ${uri}`);
+ console.log(` - MIME type: ${mimeType}`);
+ console.log(`\nNext steps:`);
+ console.log(` 1. Implement the resource provider in ${resourcePath}`);
+ console.log(` 2. Test with MCP Inspector`);
+}
+
+function generateResourceFile(
+ name: string,
+ description: string,
+ uri: string,
+ mimeType: string
+): string {
+ const hasDynamicParams = uri.includes('{');
+
+ return `
+import type { Resource, ResourceContent } from '../types/resources.js';
+
+export const ${name}Resource: Resource = {
+ uri: '${uri}',
+ name: '${name}',
+ description: '${description}',
+ mimeType: '${mimeType}',
+};
+
+export async function read${capitalize(name)}Resource(
+ uri: string
+): Promise<ResourceContent[]> {
+ ${hasDynamicParams ? generateDynamicResourceHandler(uri) : generateStaticResourceHandler()}
+
+ return [
+ {
+ uri,
+ mimeType: '${mimeType}',
+ text: ${mimeType === 'application/json' ? 'JSON.stringify(data, null, 2)' : 'data'},
+ },
+ ];
+}
+
+${generateResourceDataFunction(name, mimeType)}
+`;
+}
+
+function generateDynamicResourceHandler(uriPattern: string): string {
+ return `
+ // Parse dynamic parameters from URI
+ const params = parseUriParams('${uriPattern}', uri);
+
+ // Fetch data based on parameters
+ const data = await fetch${capitalize(name)}Data(params);
+
+ if (!data) {
+ throw new Error(\`Resource not found: \${uri}\`);
+ }
+`;
+}
+
+function generateStaticResourceHandler(): string {
+ return `
+ // Fetch static resource data
+ const data = await fetch${capitalize(name)}Data();
+`;
+}
+
+function generateResourceDataFunction(name: string, mimeType: string): string {
+ if (mimeType === 'application/json') {
+ return `
+async function fetch${capitalize(name)}Data(params?: Record<string, string>) {
+ // TODO: Implement data fetching logic
+ // This is a placeholder implementation
+
+ if (params?.id) {
+ // Return specific item
+ return {
+ id: params.id,
+ name: 'Example Item',
+ timestamp: new Date().toISOString(),
+ };
+ }
+
+ // Return collection
+ return {
+ items: [
+ { id: '1', name: 'Item 1' },
+ { id: '2', name: 'Item 2' },
+ ],
+ total: 2,
+ };
+}
+
+function parseUriParams(pattern: string, uri: string): Record<string, string> {
+ // Convert pattern to regex
+ const regexPattern = pattern
+ .replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
+ .replace(/\{(\w+)\}/g, '(?<$1>[^/]+)');
+
+ const regex = new RegExp(\`^\${regexPattern}$\`);
+ const match = uri.match(regex);
+
+ return match?.groups || {};
+}
+`;
+ } else {
+ return `
+async function fetch${capitalize(name)}Data(params?: Record<string, string>) {
+ // TODO: Implement data fetching logic
+ // This is a placeholder implementation
+
+ return 'Resource content as ${mimeType}';
+}
+`;
+ }
+}
+
+function capitalize(str: string): string {
+ return str.charAt(0).toUpperCase() + str.slice(1);
+}
+
+function generateResourceTest(name: string, uri: string): string {
+ return `
+import { describe, it, expect } from 'vitest';
+import { ${name}Resource, read${capitalize(name)}Resource } from '../../src/resources/${name}.js';
+
+describe('${name} resource', () => {
+ it('should have correct metadata', () => {
+ expect(${name}Resource.name).toBe('${name}');
+ expect(${name}Resource.uri).toBe('${uri}');
+ expect(${name}Resource.description).toBeDefined();
+ expect(${name}Resource.mimeType).toBeDefined();
+ });
+
+ it('should read resource content', async () => {
+ const content = await read${capitalize(name)}Resource('${uri.replace('{id}', 'test-id')}');
+
+ expect(content).toBeInstanceOf(Array);
+ expect(content[0]).toHaveProperty('uri');
+ expect(content[0]).toHaveProperty('mimeType');
+ expect(content[0]).toHaveProperty('text');
+ });
+
+ it('should handle missing resources', async () => {
+ // TODO: Add tests for missing resources
+ });
+
+ it('should validate URI format', () => {
+ // TODO: Add URI validation tests
+ });
+});
+`;
+}
+
+async function updateResourceIndex(name: string) {
+ const indexPath = 'src/resources/index.ts';
+
+ try {
+ let content = await fs.readFile(indexPath, 'utf-8');
+
+ // Add import
+ const importLine = `import { ${name}Resource, read${capitalize(name)}Resource } from './${name}.js';`;
+ if (!content.includes(importLine)) {
+ const lastImport = content.lastIndexOf('import');
+ const endOfLastImport = content.indexOf('\n', lastImport);
+ content = content.slice(0, endOfLastImport + 1) + importLine + '\n' + content.slice(endOfLastImport + 1);
+ }
+
+ // Add to exports
+ const exportPattern = /export const resources = \[([^\]]*)]\;/;
+ const match = content.match(exportPattern);
+ if (match) {
+ const currentExports = match[1].trim();
+ const newExports = currentExports ? `${currentExports},\n ${name}Resource` : `\n ${name}Resource\n`;
+ content = content.replace(exportPattern, `export const resources = [${newExports}];`);
+ }
+
+ await fs.writeFile(indexPath, content);
+ } catch (error) {
+ // Create index file if it doesn't exist
+ const newIndex = `
+import { ${name}Resource, read${capitalize(name)}Resource } from './${name}.js';
+
+export const resources = [
+ ${name}Resource,
+];
+
+export const resourceReaders = {
+ '${name}': read${capitalize(name)}Resource,
+};
+`;
+ await fs.writeFile(indexPath, newIndex);
+ }
+}
+``` \ No newline at end of file
diff --git a/mcp-servers/simple-mcp-server/.claude/commands/add-tool.md b/mcp-servers/simple-mcp-server/.claude/commands/add-tool.md
new file mode 100644
index 0000000..da81212
--- /dev/null
+++ b/mcp-servers/simple-mcp-server/.claude/commands/add-tool.md
@@ -0,0 +1,207 @@
+# Add Tool to MCP Server
+
+Adds a new tool to your MCP server with proper schema validation and error handling.
+
+## Usage
+
+```
+/add-tool <name> <description> [parameters]
+```
+
+## Examples
+
+```
+/add-tool calculate "Performs mathematical calculations"
+/add-tool search "Search for information" query:string limit:number?
+/add-tool process_data "Process data with options" input:string format:enum[json,csv,xml]
+```
+
+## Implementation
+
+```typescript
+import { z } from 'zod';
+import * as fs from 'fs/promises';
+import * as path from 'path';
+
+async function addTool(name: string, description: string, parameters?: string[]) {
+ // Parse parameters into schema
+ const schema = parseParameterSchema(parameters || []);
+
+ // Generate tool file
+ const toolContent = generateToolFile(name, description, schema);
+
+ // Write tool file
+ const toolPath = path.join('src/tools', `${name}.ts`);
+ await fs.writeFile(toolPath, toolContent);
+
+ // Update tool index
+ await updateToolIndex(name);
+
+ // Generate test file
+ const testContent = generateToolTest(name);
+ const testPath = path.join('tests/unit/tools', `${name}.test.ts`);
+ await fs.writeFile(testPath, testContent);
+
+ console.log(`โœ… Tool "${name}" added successfully!`);
+ console.log(` - Implementation: ${toolPath}`);
+ console.log(` - Test file: ${testPath}`);
+ console.log(`\nNext steps:`);
+ console.log(` 1. Implement the tool logic in ${toolPath}`);
+ console.log(` 2. Run tests with "npm test"`);
+ console.log(` 3. Test with MCP Inspector`);
+}
+
+function parseParameterSchema(parameters: string[]): any {
+ const properties: Record<string, any> = {};
+ const required: string[] = [];
+
+ for (const param of parameters) {
+ const [nameType, ...rest] = param.split(':');
+ const isOptional = nameType.endsWith('?');
+ const name = isOptional ? nameType.slice(0, -1) : nameType;
+ const type = rest.join(':') || 'string';
+
+ if (!isOptional) {
+ required.push(name);
+ }
+
+ properties[name] = parseType(type);
+ }
+
+ return {
+ type: 'object',
+ properties,
+ required: required.length > 0 ? required : undefined,
+ };
+}
+
+function parseType(type: string): any {
+ if (type.startsWith('enum[')) {
+ const values = type.slice(5, -1).split(',');
+ return {
+ type: 'string',
+ enum: values,
+ };
+ }
+
+ switch (type) {
+ case 'number':
+ return { type: 'number' };
+ case 'boolean':
+ return { type: 'boolean' };
+ case 'array':
+ return { type: 'array', items: { type: 'string' } };
+ default:
+ return { type: 'string' };
+ }
+}
+
+function generateToolFile(name: string, description: string, schema: any): string {
+ return `
+import { z } from 'zod';
+import type { ToolHandler } from '../types/tools.js';
+
+// Define Zod schema for validation
+const ${capitalize(name)}Schema = z.object({
+${generateZodSchema(schema.properties, ' ')}
+});
+
+export type ${capitalize(name)}Args = z.infer<typeof ${capitalize(name)}Schema>;
+
+export const ${name}Tool = {
+ name: '${name}',
+ description: '${description}',
+ inputSchema: ${JSON.stringify(schema, null, 2)},
+ handler: async (args: unknown): Promise<ToolHandler> => {
+ // Validate input
+ const validated = ${capitalize(name)}Schema.parse(args);
+
+ // TODO: Implement your tool logic here
+ const result = await process${capitalize(name)}(validated);
+
+ return {
+ content: [
+ {
+ type: 'text',
+ text: JSON.stringify(result),
+ },
+ ],
+ };
+ },
+};
+
+async function process${capitalize(name)}(args: ${capitalize(name)}Args) {
+ // TODO: Implement the actual processing logic
+ return {
+ success: true,
+ message: 'Tool executed successfully',
+ input: args,
+ };
+}
+`;
+}
+
+function generateZodSchema(properties: Record<string, any>, indent: string): string {
+ const lines: string[] = [];
+
+ for (const [key, value] of Object.entries(properties)) {
+ let zodType = 'z.string()';
+
+ if (value.type === 'number') {
+ zodType = 'z.number()';
+ } else if (value.type === 'boolean') {
+ zodType = 'z.boolean()';
+ } else if (value.enum) {
+ zodType = `z.enum([${value.enum.map((v: string) => `'${v}'`).join(', ')}])`;
+ } else if (value.type === 'array') {
+ zodType = 'z.array(z.string())';
+ }
+
+ lines.push(`${indent}${key}: ${zodType},`);
+ }
+
+ return lines.join('\n');
+}
+
+function capitalize(str: string): string {
+ return str.charAt(0).toUpperCase() + str.slice(1);
+}
+
+function generateToolTest(name: string): string {
+ return `
+import { describe, it, expect, vi } from 'vitest';
+import { ${name}Tool } from '../../src/tools/${name}.js';
+
+describe('${name} tool', () => {
+ it('should have correct metadata', () => {
+ expect(${name}Tool.name).toBe('${name}');
+ expect(${name}Tool.description).toBeDefined();
+ expect(${name}Tool.inputSchema).toBeDefined();
+ });
+
+ it('should validate input parameters', async () => {
+ const invalidInput = {};
+
+ await expect(${name}Tool.handler(invalidInput))
+ .rejects
+ .toThrow();
+ });
+
+ it('should handle valid input', async () => {
+ const validInput = {
+ // TODO: Add valid test input
+ };
+
+ const result = await ${name}Tool.handler(validInput);
+
+ expect(result).toHaveProperty('content');
+ expect(result.content[0]).toHaveProperty('type', 'text');
+ });
+
+ it('should handle errors gracefully', async () => {
+ // TODO: Add error handling tests
+ });
+});
+`;
+}
+``` \ No newline at end of file
diff --git a/mcp-servers/simple-mcp-server/.claude/commands/build.md b/mcp-servers/simple-mcp-server/.claude/commands/build.md
new file mode 100644
index 0000000..ed99096
--- /dev/null
+++ b/mcp-servers/simple-mcp-server/.claude/commands/build.md
@@ -0,0 +1,377 @@
+# Build MCP Server for Production
+
+Builds and prepares your MCP server for production deployment.
+
+## Usage
+
+```
+/build [target] [options]
+```
+
+## Targets
+
+- `node` - Build for Node.js (default)
+- `docker` - Build Docker image
+- `npm` - Prepare for npm publishing
+
+## Options
+
+- `--minify` - Minify output
+- `--sourcemap` - Include source maps
+- `--analyze` - Analyze bundle size
+
+## Implementation
+
+```typescript
+import { exec } from 'child_process';
+import { promisify } from 'util';
+import * as fs from 'fs/promises';
+import * as path from 'path';
+
+const execAsync = promisify(exec);
+
+async function buildServer(
+ target: 'node' | 'docker' | 'npm' = 'node',
+ options: {
+ minify?: boolean;
+ sourcemap?: boolean;
+ analyze?: boolean;
+ } = {}
+) {
+ console.log('๐Ÿ”จ Building MCP Server for Production');
+ console.log('='.repeat(50));
+
+ // Pre-build checks
+ await runPreBuildChecks();
+
+ // Build based on target
+ switch (target) {
+ case 'node':
+ await buildForNode(options);
+ break;
+ case 'docker':
+ await buildForDocker(options);
+ break;
+ case 'npm':
+ await buildForNpm(options);
+ break;
+ }
+
+ // Post-build validation
+ await validateBuild(target);
+
+ console.log('\nโœ… Build completed successfully!');
+}
+
+async function runPreBuildChecks() {
+ console.log('\n๐Ÿ” Running pre-build checks...');
+
+ // Check for uncommitted changes
+ try {
+ const { stdout: gitStatus } = await execAsync('git status --porcelain');
+ if (gitStatus.trim()) {
+ console.warn('โš ๏ธ Warning: You have uncommitted changes');
+ }
+ } catch {
+ // Git not available or not a git repo
+ }
+
+ // Run tests
+ console.log(' Running tests...');
+ try {
+ await execAsync('npm test');
+ console.log(' โœ… Tests passed');
+ } catch (error) {
+ console.error(' โŒ Tests failed');
+ throw new Error('Build aborted: tests must pass');
+ }
+
+ // Check dependencies
+ console.log(' Checking dependencies...');
+ try {
+ await execAsync('npm audit --production');
+ console.log(' โœ… No vulnerabilities found');
+ } catch (error) {
+ console.warn(' โš ๏ธ Security vulnerabilities detected');
+ console.log(' Run "npm audit fix" to resolve');
+ }
+}
+
+async function buildForNode(options: any) {
+ console.log('\n๐Ÿ“ฆ Building for Node.js...');
+
+ // Clean previous build
+ await fs.rm('dist', { recursive: true, force: true });
+
+ // Update tsconfig for production
+ const tsConfig = JSON.parse(await fs.readFile('tsconfig.json', 'utf-8'));
+ const prodConfig = {
+ ...tsConfig,
+ compilerOptions: {
+ ...tsConfig.compilerOptions,
+ sourceMap: options.sourcemap || false,
+ inlineSources: false,
+ removeComments: true,
+ },
+ };
+
+ await fs.writeFile('tsconfig.prod.json', JSON.stringify(prodConfig, null, 2));
+
+ // Build with TypeScript
+ console.log(' Compiling TypeScript...');
+ await execAsync('npx tsc -p tsconfig.prod.json');
+
+ // Minify if requested
+ if (options.minify) {
+ console.log(' Minifying code...');
+ await minifyCode();
+ }
+
+ // Copy package files
+ console.log(' Copying package files...');
+ await fs.copyFile('package.json', 'dist/package.json');
+ await fs.copyFile('README.md', 'dist/README.md').catch(() => {});
+ await fs.copyFile('LICENSE', 'dist/LICENSE').catch(() => {});
+
+ // Create production package.json
+ const pkg = JSON.parse(await fs.readFile('package.json', 'utf-8'));
+ const prodPkg = {
+ ...pkg,
+ scripts: {
+ start: 'node index.js',
+ },
+ devDependencies: undefined,
+ };
+ await fs.writeFile('dist/package.json', JSON.stringify(prodPkg, null, 2));
+
+ // Analyze bundle if requested
+ if (options.analyze) {
+ await analyzeBundleSize();
+ }
+
+ console.log(' โœ… Node.js build complete');
+ console.log(' Output: ./dist');
+}
+
+async function buildForDocker(options: any) {
+ console.log('\n๐Ÿ‹ Building Docker image...');
+
+ // Build Node.js first
+ await buildForNode(options);
+
+ // Create Dockerfile if it doesn't exist
+ const dockerfilePath = 'Dockerfile';
+ if (!await fs.access(dockerfilePath).then(() => true).catch(() => false)) {
+ await createDockerfile();
+ }
+
+ // Build Docker image
+ console.log(' Building Docker image...');
+ const imageName = 'mcp-server';
+ const version = JSON.parse(await fs.readFile('package.json', 'utf-8')).version;
+
+ await execAsync(`docker build -t ${imageName}:${version} -t ${imageName}:latest .`);
+
+ // Test the image
+ console.log(' Testing Docker image...');
+ const { stdout } = await execAsync(`docker run --rm ${imageName}:latest node index.js --version`);
+ console.log(` Version: ${stdout.trim()}`);
+
+ console.log(' โœ… Docker build complete');
+ console.log(` Image: ${imageName}:${version}`);
+ console.log(' Run: docker run -it ' + imageName + ':latest');
+}
+
+async function buildForNpm(options: any) {
+ console.log('\n๐Ÿ“ฆ Preparing for npm publishing...');
+
+ // Build Node.js first
+ await buildForNode(options);
+
+ // Validate package.json
+ const pkg = JSON.parse(await fs.readFile('package.json', 'utf-8'));
+
+ if (!pkg.name) {
+ throw new Error('package.json must have a name field');
+ }
+
+ if (!pkg.version) {
+ throw new Error('package.json must have a version field');
+ }
+
+ if (!pkg.description) {
+ console.warn('โš ๏ธ Warning: package.json should have a description');
+ }
+
+ // Create .npmignore if needed
+ const npmignorePath = '.npmignore';
+ if (!await fs.access(npmignorePath).then(() => true).catch(() => false)) {
+ await createNpmIgnore();
+ }
+
+ // Run npm pack to test
+ console.log(' Creating package tarball...');
+ const { stdout } = await execAsync('npm pack --dry-run');
+
+ // Parse package contents
+ const files = stdout.split('\n').filter(line => line.includes('npm notice'));
+ console.log(` Package will include ${files.length} files`);
+
+ // Check package size
+ const sizeMatch = stdout.match(/npm notice ([\d.]+[kMG]B)/);
+ if (sizeMatch) {
+ console.log(` Package size: ${sizeMatch[1]}`);
+
+ // Warn if package is large
+ if (sizeMatch[1].includes('M') && parseFloat(sizeMatch[1]) > 10) {
+ console.warn('โš ๏ธ Warning: Package is larger than 10MB');
+ }
+ }
+
+ console.log(' โœ… npm package ready');
+ console.log(' Publish with: npm publish');
+}
+
+async function createDockerfile() {
+ const dockerfile = `
+# Build stage
+FROM node:20-alpine AS builder
+WORKDIR /app
+COPY package*.json ./
+RUN npm ci
+COPY . .
+RUN npm run build
+
+# Production stage
+FROM node:20-alpine
+WORKDIR /app
+
+# Install dumb-init for signal handling
+RUN apk add --no-cache dumb-init
+
+# Create non-root user
+RUN addgroup -g 1001 -S nodejs && \\
+ adduser -S nodejs -u 1001
+
+# Copy built application
+COPY --from=builder /app/dist ./
+COPY --from=builder /app/node_modules ./node_modules
+
+# Change ownership
+RUN chown -R nodejs:nodejs /app
+
+USER nodejs
+
+EXPOSE 3000
+
+ENTRYPOINT ["dumb-init", "--"]
+CMD ["node", "index.js"]
+`;
+
+ await fs.writeFile('Dockerfile', dockerfile);
+ console.log(' Created Dockerfile');
+}
+
+async function createNpmIgnore() {
+ const npmignore = `
+# Source files
+src/
+tests/
+
+# Config files
+.eslintrc*
+.prettierrc*
+tsconfig*.json
+vitest.config.ts
+
+# Development files
+*.log
+.env
+.env.*
+!.env.example
+
+# Build artifacts
+*.tsbuildinfo
+coverage/
+.nyc_output/
+
+# Docker
+Dockerfile
+docker-compose.yml
+
+# Git
+.git/
+.gitignore
+
+# CI/CD
+.github/
+.gitlab-ci.yml
+
+# IDE
+.vscode/
+.idea/
+
+# Misc
+.DS_Store
+Thumbs.db
+`;
+
+ await fs.writeFile('.npmignore', npmignore);
+ console.log(' Created .npmignore');
+}
+
+async function minifyCode() {
+ // Use esbuild or terser to minify
+ try {
+ await execAsync('npx esbuild dist/**/*.js --minify --outdir=dist --allow-overwrite');
+ } catch {
+ console.warn(' Minification skipped (esbuild not available)');
+ }
+}
+
+async function analyzeBundleSize() {
+ console.log('\n๐Ÿ“Š Analyzing bundle size...');
+
+ const files = await fs.readdir('dist', { recursive: true });
+ let totalSize = 0;
+ const fileSizes: Array<{ name: string; size: number }> = [];
+
+ for (const file of files) {
+ const filePath = path.join('dist', file as string);
+ const stat = await fs.stat(filePath);
+
+ if (stat.isFile()) {
+ totalSize += stat.size;
+ fileSizes.push({ name: file as string, size: stat.size });
+ }
+ }
+
+ // Sort by size
+ fileSizes.sort((a, b) => b.size - a.size);
+
+ console.log(' Largest files:');
+ fileSizes.slice(0, 5).forEach(file => {
+ console.log(` ${file.name}: ${(file.size / 1024).toFixed(2)}KB`);
+ });
+
+ console.log(` Total size: ${(totalSize / 1024).toFixed(2)}KB`);
+}
+
+async function validateBuild(target: string) {
+ console.log('\n๐Ÿ” Validating build...');
+
+ // Check that main file exists
+ const mainFile = 'dist/index.js';
+ if (!await fs.access(mainFile).then(() => true).catch(() => false)) {
+ throw new Error('Build validation failed: main file not found');
+ }
+
+ // Try to load the server
+ try {
+ await import(path.resolve(mainFile));
+ console.log(' โœ… Server module loads successfully');
+ } catch (error) {
+ throw new Error(`Build validation failed: ${error.message}`);
+ }
+}
+``` \ No newline at end of file
diff --git a/mcp-servers/simple-mcp-server/.claude/commands/debug.md b/mcp-servers/simple-mcp-server/.claude/commands/debug.md
new file mode 100644
index 0000000..47eca5f
--- /dev/null
+++ b/mcp-servers/simple-mcp-server/.claude/commands/debug.md
@@ -0,0 +1,310 @@
+# Debug MCP Server
+
+Provides comprehensive debugging tools for troubleshooting MCP server issues.
+
+## Usage
+
+```
+/debug [component] [options]
+```
+
+## Components
+
+- `protocol` - Debug protocol messages
+- `tools` - Debug tool execution
+- `resources` - Debug resource access
+- `transport` - Debug transport layer
+- `all` - Enable all debugging (default)
+
+## Options
+
+- `--verbose` - Extra verbose output
+- `--save` - Save debug logs to file
+- `--inspector` - Launch with MCP Inspector
+
+## Implementation
+
+```typescript
+import * as fs from 'fs/promises';
+import { exec, spawn } from 'child_process';
+import * as path from 'path';
+
+async function debugServer(
+ component: 'protocol' | 'tools' | 'resources' | 'transport' | 'all' = 'all',
+ options: {
+ verbose?: boolean;
+ save?: boolean;
+ inspector?: boolean;
+ } = {}
+) {
+ console.log('๐Ÿ” MCP Server Debugger');
+ console.log('='.repeat(50));
+
+ // Set debug environment variables
+ const debugEnv = {
+ ...process.env,
+ DEBUG: component === 'all' ? 'mcp:*' : `mcp:${component}`,
+ LOG_LEVEL: options.verbose ? 'trace' : 'debug',
+ MCP_DEBUG: 'true',
+ };
+
+ // Create debug configuration
+ const debugConfig = await createDebugConfig();
+
+ // Start debug session
+ if (options.inspector) {
+ await launchWithInspector(debugEnv);
+ } else {
+ await runDebugSession(component, debugEnv, options);
+ }
+}
+
+async function createDebugConfig(): Promise<string> {
+ const config = {
+ logging: {
+ level: 'debug',
+ format: 'pretty',
+ includeTimestamp: true,
+ includeLocation: true,
+ },
+ debug: {
+ protocol: {
+ logRequests: true,
+ logResponses: true,
+ logNotifications: true,
+ },
+ tools: {
+ logCalls: true,
+ logValidation: true,
+ logErrors: true,
+ measurePerformance: true,
+ },
+ resources: {
+ logReads: true,
+ logWrites: true,
+ trackCache: true,
+ },
+ transport: {
+ logConnections: true,
+ logMessages: true,
+ logErrors: true,
+ },
+ },
+ };
+
+ const configPath = '.debug-config.json';
+ await fs.writeFile(configPath, JSON.stringify(config, null, 2));
+ return configPath;
+}
+
+async function runDebugSession(
+ component: string,
+ env: NodeJS.ProcessEnv,
+ options: { verbose?: boolean; save?: boolean }
+) {
+ console.log(`\n๐Ÿ” Debugging: ${component}`);
+ console.log('Press Ctrl+C to stop\n');
+
+ // Create debug wrapper
+ const debugScript = `
+import { Server } from '@modelcontextprotocol/sdk/server/index.js';
+import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
+import debug from 'debug';
+import pino from 'pino';
+
+// Enable debug logging
+const log = {
+ protocol: debug('mcp:protocol'),
+ tools: debug('mcp:tools'),
+ resources: debug('mcp:resources'),
+ transport: debug('mcp:transport'),
+};
+
+// Create logger
+const logger = pino({
+ level: process.env.LOG_LEVEL || 'debug',
+ transport: {
+ target: 'pino-pretty',
+ options: {
+ colorize: true,
+ translateTime: 'HH:MM:ss.l',
+ ignore: 'pid,hostname',
+ },
+ },
+});
+
+// Wrap server methods for debugging
+const originalServer = await import('./src/index.js');
+const server = originalServer.server;
+
+// Intercept requests
+const originalSetRequestHandler = server.setRequestHandler.bind(server);
+server.setRequestHandler = (schema, handler) => {
+ const wrappedHandler = async (request) => {
+ const start = Date.now();
+ log.protocol('โ†’ Request: %O', request);
+ logger.debug({ request }, 'Incoming request');
+
+ try {
+ const result = await handler(request);
+ const duration = Date.now() - start;
+
+ log.protocol('โ† Response (%dms): %O', duration, result);
+ logger.debug({ result, duration }, 'Response sent');
+
+ return result;
+ } catch (error) {
+ log.protocol('โœ— Error: %O', error);
+ logger.error({ error }, 'Request failed');
+ throw error;
+ }
+ };
+
+ return originalSetRequestHandler(schema, wrappedHandler);
+};
+
+// Start server with debugging
+logger.info('Debug server starting...');
+const transport = new StdioServerTransport();
+await server.connect(transport);
+logger.info('Debug server ready');
+`;
+
+ // Write debug script
+ const debugScriptPath = '.debug-server.js';
+ await fs.writeFile(debugScriptPath, debugScript);
+
+ // Start server with debugging
+ const serverProcess = spawn('node', [debugScriptPath], {
+ env,
+ stdio: options.save ? 'pipe' : 'inherit',
+ });
+
+ if (options.save) {
+ const logFile = `debug-${component}-${Date.now()}.log`;
+ const logStream = await fs.open(logFile, 'w');
+
+ serverProcess.stdout?.pipe(logStream.createWriteStream());
+ serverProcess.stderr?.pipe(logStream.createWriteStream());
+
+ console.log(`Saving debug output to: ${logFile}`);
+ }
+
+ // Handle cleanup
+ process.on('SIGINT', () => {
+ serverProcess.kill();
+ process.exit();
+ });
+
+ serverProcess.on('exit', async () => {
+ // Cleanup
+ await fs.unlink(debugScriptPath).catch(() => {});
+ await fs.unlink('.debug-config.json').catch(() => {});
+ });
+}
+
+async function launchWithInspector(env: NodeJS.ProcessEnv) {
+ console.log('\n๐Ÿ” Launching with MCP Inspector...');
+ console.log('This will provide an interactive debugging interface.\n');
+
+ // Start server in debug mode
+ const serverProcess = spawn('node', ['--inspect', 'dist/index.js'], {
+ env,
+ stdio: 'pipe',
+ });
+
+ // Parse debug port from output
+ serverProcess.stderr?.on('data', (data) => {
+ const output = data.toString();
+ const match = output.match(/Debugger listening on ws:\/\/(.+):(\d+)/);
+ if (match) {
+ console.log(`๐Ÿ”— Node.js debugger: chrome://inspect`);
+ console.log(` Connect to: ${match[1]}:${match[2]}`);
+ }
+ });
+
+ // Wait a moment for server to start
+ await new Promise(resolve => setTimeout(resolve, 2000));
+
+ // Launch MCP Inspector
+ console.log('\n๐Ÿ” Starting MCP Inspector...');
+ const inspector = exec('npx @modelcontextprotocol/inspector');
+
+ inspector.stdout?.on('data', (data) => {
+ console.log(data.toString());
+ });
+
+ // Cleanup on exit
+ process.on('SIGINT', () => {
+ serverProcess.kill();
+ inspector.kill();
+ process.exit();
+ });
+}
+
+// Additional debug utilities
+export async function analyzeProtocolFlow() {
+ console.log('\n๐Ÿ“Š Analyzing Protocol Flow...');
+
+ const checks = [
+ { name: 'Initialization', test: testInitialization },
+ { name: 'Capability Negotiation', test: testCapabilities },
+ { name: 'Tool Discovery', test: testToolDiscovery },
+ { name: 'Resource Listing', test: testResourceListing },
+ { name: 'Error Handling', test: testErrorHandling },
+ ];
+
+ for (const check of checks) {
+ try {
+ await check.test();
+ console.log(` โœ… ${check.name}`);
+ } catch (error) {
+ console.log(` โŒ ${check.name}: ${error.message}`);
+ }
+ }
+}
+
+async function testInitialization() {
+ // Test initialization flow
+ const { Server } = await import('@modelcontextprotocol/sdk/server/index.js');
+ const server = new Server({ name: 'test', version: '1.0.0' }, {});
+ if (!server) throw new Error('Server initialization failed');
+}
+
+async function testCapabilities() {
+ // Test capability declaration
+ const capabilities = {
+ tools: {},
+ resources: {},
+ prompts: {},
+ };
+ if (!capabilities.tools) throw new Error('Tools capability missing');
+}
+
+async function testToolDiscovery() {
+ // Test tool discovery
+ try {
+ const { tools } = await import('./src/tools/index.js');
+ if (!Array.isArray(tools)) throw new Error('Tools not properly exported');
+ } catch {
+ // Tools may not be implemented yet
+ }
+}
+
+async function testResourceListing() {
+ // Test resource listing
+ try {
+ const { resources } = await import('./src/resources/index.js');
+ if (!Array.isArray(resources)) throw new Error('Resources not properly exported');
+ } catch {
+ // Resources may not be implemented yet
+ }
+}
+
+async function testErrorHandling() {
+ // Test error handling
+ const { handleError } = await import('./src/utils/error-handler.js');
+ const result = handleError(new Error('Test'));
+ if (!result.error) throw new Error('Error handler not working');
+}
+``` \ No newline at end of file
diff --git a/mcp-servers/simple-mcp-server/.claude/commands/deploy.md b/mcp-servers/simple-mcp-server/.claude/commands/deploy.md
new file mode 100644
index 0000000..8b4049e
--- /dev/null
+++ b/mcp-servers/simple-mcp-server/.claude/commands/deploy.md
@@ -0,0 +1,376 @@
+# Deploy MCP Server
+
+Deploys your MCP server to various platforms and registries.
+
+## Usage
+
+```
+/deploy [target] [options]
+```
+
+## Targets
+
+- `npm` - Publish to npm registry
+- `docker` - Push to Docker registry
+- `claude` - Register with Claude Code
+- `github` - Create GitHub release
+
+## Options
+
+- `--tag` - Version tag (default: from package.json)
+- `--registry` - Custom registry URL
+- `--dry-run` - Test deployment without publishing
+
+## Implementation
+
+```typescript
+import { exec } from 'child_process';
+import { promisify } from 'util';
+import * as fs from 'fs/promises';
+import * as path from 'path';
+
+const execAsync = promisify(exec);
+
+async function deployServer(
+ target: 'npm' | 'docker' | 'claude' | 'github',
+ options: {
+ tag?: string;
+ registry?: string;
+ dryRun?: boolean;
+ } = {}
+) {
+ console.log('๐Ÿš€ Deploying MCP Server');
+ console.log('='.repeat(50));
+
+ // Get version info
+ const pkg = JSON.parse(await fs.readFile('package.json', 'utf-8'));
+ const version = options.tag || pkg.version;
+
+ // Pre-deployment checks
+ await runPreDeploymentChecks(version);
+
+ // Deploy based on target
+ switch (target) {
+ case 'npm':
+ await deployToNpm(pkg, version, options);
+ break;
+ case 'docker':
+ await deployToDocker(pkg, version, options);
+ break;
+ case 'claude':
+ await deployToClaude(pkg, version, options);
+ break;
+ case 'github':
+ await deployToGitHub(pkg, version, options);
+ break;
+ }
+
+ console.log('\nโœ… Deployment completed successfully!');
+}
+
+async function runPreDeploymentChecks(version: string) {
+ console.log('\n๐Ÿ” Running pre-deployment checks...');
+
+ // Check git status
+ try {
+ const { stdout: status } = await execAsync('git status --porcelain');
+ if (status.trim()) {
+ throw new Error('Working directory has uncommitted changes');
+ }
+ console.log(' โœ… Working directory clean');
+ } catch (error) {
+ if (error.message.includes('uncommitted')) {
+ throw error;
+ }
+ console.warn(' โš ๏ธ Git not available');
+ }
+
+ // Check if version tag exists
+ try {
+ await execAsync(`git rev-parse v${version}`);
+ console.log(` โœ… Version tag v${version} exists`);
+ } catch {
+ console.warn(` โš ๏ธ Version tag v${version} not found`);
+ console.log(' Create with: git tag v' + version);
+ }
+
+ // Verify build exists
+ const buildExists = await fs.access('dist').then(() => true).catch(() => false);
+ if (!buildExists) {
+ throw new Error('Build not found. Run /build first');
+ }
+ console.log(' โœ… Build found');
+
+ // Run tests
+ console.log(' Running tests...');
+ try {
+ await execAsync('npm test');
+ console.log(' โœ… Tests passed');
+ } catch {
+ throw new Error('Tests must pass before deployment');
+ }
+}
+
+async function deployToNpm(pkg: any, version: string, options: any) {
+ console.log(`\n๐Ÿ“ฆ Deploying to npm (v${version})...`);
+
+ // Check npm authentication
+ try {
+ await execAsync('npm whoami');
+ console.log(' โœ… npm authenticated');
+ } catch {
+ throw new Error('Not authenticated with npm. Run: npm login');
+ }
+
+ // Check if version already published
+ try {
+ const { stdout } = await execAsync(`npm view ${pkg.name}@${version}`);
+ if (stdout) {
+ throw new Error(`Version ${version} already published`);
+ }
+ } catch (error) {
+ if (error.message.includes('already published')) {
+ throw error;
+ }
+ // Version not published yet (good)
+ }
+
+ // Update version if different
+ if (pkg.version !== version) {
+ console.log(` Updating version to ${version}...`);
+ await execAsync(`npm version ${version} --no-git-tag-version`);
+ }
+
+ // Publish package
+ const publishCmd = options.dryRun
+ ? 'npm publish --dry-run'
+ : `npm publish ${options.registry ? `--registry ${options.registry}` : ''}`;
+
+ console.log(' Publishing to npm...');
+ const { stdout } = await execAsync(publishCmd);
+
+ if (options.dryRun) {
+ console.log(' ๐Ÿงช Dry run complete (not published)');
+ } else {
+ console.log(' โœ… Published to npm');
+ console.log(` Install with: npm install ${pkg.name}`);
+ console.log(` View at: https://www.npmjs.com/package/${pkg.name}`);
+ }
+}
+
+async function deployToDocker(pkg: any, version: string, options: any) {
+ console.log(`\n๐Ÿ‹ Deploying to Docker registry (v${version})...`);
+
+ const imageName = pkg.name.replace('@', '').replace('/', '-');
+ const registry = options.registry || 'docker.io';
+ const fullImageName = `${registry}/${imageName}`;
+
+ // Check Docker authentication
+ try {
+ await execAsync(`docker pull ${registry}/hello-world`);
+ console.log(' โœ… Docker authenticated');
+ } catch {
+ console.warn(' โš ๏ธ May not be authenticated with Docker registry');
+ }
+
+ // Build image if not exists
+ try {
+ await execAsync(`docker image inspect ${imageName}:${version}`);
+ console.log(' โœ… Docker image exists');
+ } catch {
+ console.log(' Building Docker image...');
+ await execAsync(`docker build -t ${imageName}:${version} -t ${imageName}:latest .`);
+ }
+
+ // Tag for registry
+ console.log(' Tagging image...');
+ await execAsync(`docker tag ${imageName}:${version} ${fullImageName}:${version}`);
+ await execAsync(`docker tag ${imageName}:latest ${fullImageName}:latest`);
+
+ // Push to registry
+ if (!options.dryRun) {
+ console.log(' Pushing to registry...');
+ await execAsync(`docker push ${fullImageName}:${version}`);
+ await execAsync(`docker push ${fullImageName}:latest`);
+ console.log(' โœ… Pushed to Docker registry');
+ console.log(` Pull with: docker pull ${fullImageName}:${version}`);
+ } else {
+ console.log(' ๐Ÿงช Dry run complete (not pushed)');
+ }
+}
+
+async function deployToClaude(pkg: any, version: string, options: any) {
+ console.log(`\n๐Ÿค– Registering with Claude Code...`);
+
+ // Create Claude integration instructions
+ const instructions = `
+# Claude Code Integration
+
+## Installation
+
+### From npm
+\`\`\`bash
+claude mcp add ${pkg.name} -- npx ${pkg.name}
+\`\`\`
+
+### From local build
+\`\`\`bash
+claude mcp add ${pkg.name} -- node ${path.resolve('dist/index.js')}
+\`\`\`
+
+### With custom configuration
+\`\`\`bash
+claude mcp add ${pkg.name} \\
+ --env LOG_LEVEL=info \\
+ --env CUSTOM_CONFIG=/path/to/config \\
+ -- npx ${pkg.name}
+\`\`\`
+
+## Available Capabilities
+
+- Tools: ${await countTools()}
+- Resources: ${await countResources()}
+- Prompts: ${await countPrompts()}
+
+## Version: ${version}
+`;
+
+ // Save integration instructions
+ const instructionsPath = 'CLAUDE_INTEGRATION.md';
+ await fs.writeFile(instructionsPath, instructions);
+
+ console.log(' โœ… Integration instructions created');
+ console.log(` View: ${instructionsPath}`);
+
+ // Test local integration
+ if (!options.dryRun) {
+ console.log(' Testing local integration...');
+ try {
+ const { stdout } = await execAsync('claude mcp list');
+ if (stdout.includes(pkg.name)) {
+ console.log(' โœ… Already registered with Claude Code');
+ } else {
+ console.log(' Register with:');
+ console.log(` claude mcp add ${pkg.name} -- node ${path.resolve('dist/index.js')}`);
+ }
+ } catch {
+ console.log(' Claude Code CLI not available');
+ }
+ }
+}
+
+async function deployToGitHub(pkg: any, version: string, options: any) {
+ console.log(`\n๐Ÿ’™ Creating GitHub release (v${version})...`);
+
+ // Check gh CLI
+ try {
+ await execAsync('gh --version');
+ } catch {
+ throw new Error('GitHub CLI not installed. Install from: https://cli.github.com');
+ }
+
+ // Check authentication
+ try {
+ await execAsync('gh auth status');
+ console.log(' โœ… GitHub authenticated');
+ } catch {
+ throw new Error('Not authenticated with GitHub. Run: gh auth login');
+ }
+
+ // Create release notes
+ const releaseNotes = await generateReleaseNotes(version);
+ const notesPath = `.release-notes-${version}.md`;
+ await fs.writeFile(notesPath, releaseNotes);
+
+ // Create release
+ if (!options.dryRun) {
+ const releaseCmd = `gh release create v${version} \\
+ --title "v${version}" \\
+ --notes-file ${notesPath} \\
+ --generate-notes`;
+
+ try {
+ await execAsync(releaseCmd);
+ console.log(' โœ… GitHub release created');
+
+ // Get release URL
+ const { stdout } = await execAsync(`gh release view v${version} --json url`);
+ const { url } = JSON.parse(stdout);
+ console.log(` View at: ${url}`);
+ } catch (error) {
+ if (error.message.includes('already exists')) {
+ console.log(' โ„น๏ธ Release already exists');
+ } else {
+ throw error;
+ }
+ }
+ } else {
+ console.log(' ๐Ÿงช Dry run complete (release not created)');
+ console.log(` Release notes saved to: ${notesPath}`);
+ }
+
+ // Cleanup
+ await fs.unlink(notesPath).catch(() => {});
+}
+
+async function generateReleaseNotes(version: string): Promise<string> {
+ const pkg = JSON.parse(await fs.readFile('package.json', 'utf-8'));
+
+ return `
+# ${pkg.name} v${version}
+
+## ๐ŸŽ† What's New
+
+- [Add your changes here]
+
+## ๐Ÿ“ฆ Installation
+
+### npm
+\`\`\`bash
+npm install ${pkg.name}@${version}
+\`\`\`
+
+### Claude Code
+\`\`\`bash
+claude mcp add ${pkg.name} -- npx ${pkg.name}@${version}
+\`\`\`
+
+## ๐Ÿ“„ Documentation
+
+See [README.md](README.md) for usage instructions.
+
+## ๐Ÿ”ง MCP Capabilities
+
+- Tools: ${await countTools()}
+- Resources: ${await countResources()}
+- Prompts: ${await countPrompts()}
+`;
+}
+
+async function countTools(): Promise<number> {
+ try {
+ const files = await fs.readdir('src/tools').catch(() => []);
+ return files.filter(f => f.endsWith('.ts') && f !== 'index.ts').length;
+ } catch {
+ return 0;
+ }
+}
+
+async function countResources(): Promise<number> {
+ try {
+ const files = await fs.readdir('src/resources').catch(() => []);
+ return files.filter(f => f.endsWith('.ts') && f !== 'index.ts').length;
+ } catch {
+ return 0;
+ }
+}
+
+async function countPrompts(): Promise<number> {
+ try {
+ const files = await fs.readdir('src/prompts').catch(() => []);
+ return files.filter(f => f.endsWith('.ts') && f !== 'index.ts').length;
+ } catch {
+ return 0;
+ }
+}
+``` \ No newline at end of file
diff --git a/mcp-servers/simple-mcp-server/.claude/commands/init.md b/mcp-servers/simple-mcp-server/.claude/commands/init.md
new file mode 100644
index 0000000..885af94
--- /dev/null
+++ b/mcp-servers/simple-mcp-server/.claude/commands/init.md
@@ -0,0 +1,178 @@
+# Initialize MCP Server Project
+
+Sets up a new MCP server project with the specified configuration level.
+
+## Usage
+
+```
+/init [basic|standard|full]
+```
+
+## Options
+
+- `basic` - Minimal server with one example tool
+- `standard` - Server with tools and resources (default)
+- `full` - Complete server with all capabilities
+
+## Implementation
+
+```typescript
+async function initializeProject(level: 'basic' | 'standard' | 'full' = 'standard') {
+ // Create project structure
+ const dirs = [
+ 'src',
+ 'src/tools',
+ 'src/resources',
+ 'src/prompts',
+ 'src/utils',
+ 'src/types',
+ 'tests',
+ 'tests/unit',
+ 'tests/integration',
+ ];
+
+ for (const dir of dirs) {
+ await fs.mkdir(dir, { recursive: true });
+ }
+
+ // Create package.json
+ const packageJson = {
+ name: 'mcp-server',
+ version: '1.0.0',
+ type: 'module',
+ scripts: {
+ 'dev': 'tsx watch src/index.ts',
+ 'build': 'tsc',
+ 'start': 'node dist/index.js',
+ 'test': 'vitest',
+ 'lint': 'eslint src',
+ 'typecheck': 'tsc --noEmit',
+ },
+ dependencies: {
+ '@modelcontextprotocol/sdk': '^1.0.0',
+ 'zod': '^3.22.0',
+ },
+ devDependencies: {
+ '@types/node': '^20.0.0',
+ 'typescript': '^5.0.0',
+ 'tsx': '^4.0.0',
+ 'vitest': '^1.0.0',
+ 'eslint': '^8.0.0',
+ },
+ };
+
+ await fs.writeFile('package.json', JSON.stringify(packageJson, null, 2));
+
+ // Create tsconfig.json
+ const tsConfig = {
+ compilerOptions: {
+ target: 'ES2022',
+ module: 'NodeNext',
+ moduleResolution: 'NodeNext',
+ outDir: './dist',
+ rootDir: './src',
+ strict: true,
+ esModuleInterop: true,
+ skipLibCheck: true,
+ forceConsistentCasingInFileNames: true,
+ },
+ include: ['src/**/*'],
+ exclude: ['node_modules', 'dist'],
+ };
+
+ await fs.writeFile('tsconfig.json', JSON.stringify(tsConfig, null, 2));
+
+ // Create main server file
+ let serverContent = '';
+
+ if (level === 'basic') {
+ serverContent = generateBasicServer();
+ } else if (level === 'standard') {
+ serverContent = generateStandardServer();
+ } else {
+ serverContent = generateFullServer();
+ }
+
+ await fs.writeFile('src/index.ts', serverContent);
+
+ // Install dependencies
+ console.log('Installing dependencies...');
+ await exec('npm install');
+
+ console.log('โœ… MCP server initialized successfully!');
+ console.log('\nNext steps:');
+ console.log('1. Run "npm run dev" to start development server');
+ console.log('2. Use "/add-tool" to add custom tools');
+ console.log('3. Test with MCP Inspector: npx @modelcontextprotocol/inspector');
+}
+
+function generateBasicServer(): string {
+ return `
+import { Server } from '@modelcontextprotocol/sdk/server/index.js';
+import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
+import {
+ CallToolRequestSchema,
+ ListToolsRequestSchema,
+} from '@modelcontextprotocol/sdk/types.js';
+import { z } from 'zod';
+
+const server = new Server({
+ name: 'my-mcp-server',
+ version: '1.0.0',
+}, {
+ capabilities: {
+ tools: {},
+ },
+});
+
+// List available tools
+server.setRequestHandler(ListToolsRequestSchema, async () => {
+ return {
+ tools: [
+ {
+ name: 'hello',
+ description: 'Say hello to someone',
+ inputSchema: {
+ type: 'object',
+ properties: {
+ name: {
+ type: 'string',
+ description: 'Name to greet',
+ },
+ },
+ required: ['name'],
+ },
+ },
+ ],
+ };
+});
+
+// Handle tool calls
+server.setRequestHandler(CallToolRequestSchema, async (request) => {
+ const { name, arguments: args } = request.params;
+
+ if (name === 'hello') {
+ const validated = z.object({
+ name: z.string(),
+ }).parse(args);
+
+ return {
+ content: [
+ {
+ type: 'text',
+ text: \`Hello, \${validated.name}!\`,
+ },
+ ],
+ };
+ }
+
+ throw new Error(\`Unknown tool: \${name}\`);
+});
+
+// Start server
+const transport = new StdioServerTransport();
+await server.connect(transport);
+console.error('MCP server running on stdio');
+`;
+}
+``` \ No newline at end of file
diff --git a/mcp-servers/simple-mcp-server/.claude/commands/test.md b/mcp-servers/simple-mcp-server/.claude/commands/test.md
new file mode 100644
index 0000000..eff8fe9
--- /dev/null
+++ b/mcp-servers/simple-mcp-server/.claude/commands/test.md
@@ -0,0 +1,261 @@
+# Test MCP Server
+
+Runs comprehensive tests for your MCP server including unit tests, integration tests, and protocol compliance validation.
+
+## Usage
+
+```
+/test [type] [options]
+```
+
+## Options
+
+- `type` - Test type: `unit`, `integration`, `all` (default: `all`)
+- `--coverage` - Generate coverage report
+- `--watch` - Run tests in watch mode
+- `--inspector` - Launch MCP Inspector for manual testing
+
+## Implementation
+
+```typescript
+import { exec } from 'child_process';
+import { promisify } from 'util';
+import * as fs from 'fs/promises';
+
+const execAsync = promisify(exec);
+
+async function runTests(
+ type: 'unit' | 'integration' | 'all' = 'all',
+ options: {
+ coverage?: boolean;
+ watch?: boolean;
+ inspector?: boolean;
+ } = {}
+) {
+ console.log('๐Ÿงช Running MCP Server Tests...');
+
+ // Run linting first
+ console.log('\n๐Ÿ” Running linter...');
+ try {
+ await execAsync('npm run lint');
+ console.log('โœ… Linting passed');
+ } catch (error) {
+ console.error('โŒ Linting failed:', error.message);
+ return;
+ }
+
+ // Run type checking
+ console.log('\n๐Ÿ“ Type checking...');
+ try {
+ await execAsync('npm run typecheck');
+ console.log('โœ… Type checking passed');
+ } catch (error) {
+ console.error('โŒ Type checking failed:', error.message);
+ return;
+ }
+
+ // Run tests
+ console.log(`\n๐Ÿงช Running ${type} tests...`);
+
+ let testCommand = 'npx vitest';
+
+ if (type === 'unit') {
+ testCommand += ' tests/unit';
+ } else if (type === 'integration') {
+ testCommand += ' tests/integration';
+ }
+
+ if (options.coverage) {
+ testCommand += ' --coverage';
+ }
+
+ if (options.watch) {
+ testCommand += ' --watch';
+ } else {
+ testCommand += ' --run';
+ }
+
+ try {
+ const { stdout } = await execAsync(testCommand);
+ console.log(stdout);
+
+ // Run protocol compliance check
+ if (type === 'all' || type === 'integration') {
+ console.log('\n๐Ÿ”Œ Checking MCP protocol compliance...');
+ await checkProtocolCompliance();
+ }
+
+ // Generate test report
+ if (options.coverage) {
+ console.log('\n๐Ÿ“Š Coverage report generated:');
+ console.log(' - HTML: coverage/index.html');
+ console.log(' - JSON: coverage/coverage-final.json');
+ }
+
+ console.log('\nโœ… All tests passed!');
+
+ // Launch inspector if requested
+ if (options.inspector) {
+ console.log('\n๐Ÿ” Launching MCP Inspector...');
+ await launchInspector();
+ }
+ } catch (error) {
+ console.error('\nโŒ Tests failed:', error.message);
+ process.exit(1);
+ }
+}
+
+async function checkProtocolCompliance() {
+ const tests = [
+ checkInitialization,
+ checkToolsCapability,
+ checkResourcesCapability,
+ checkPromptsCapability,
+ checkErrorHandling,
+ ];
+
+ for (const test of tests) {
+ try {
+ await test();
+ console.log(` โœ… ${test.name.replace('check', '')} compliance`);
+ } catch (error) {
+ console.log(` โŒ ${test.name.replace('check', '')} compliance: ${error.message}`);
+ throw error;
+ }
+ }
+}
+
+async function checkInitialization() {
+ // Test that server properly handles initialization
+ const { Server } = await import('@modelcontextprotocol/sdk/server/index.js');
+ const { TestTransport } = await import('../tests/utils/test-transport.js');
+
+ const server = new Server({
+ name: 'test-server',
+ version: '1.0.0',
+ }, {
+ capabilities: {
+ tools: {},
+ resources: {},
+ prompts: {},
+ },
+ });
+
+ const transport = new TestTransport();
+ await server.connect(transport);
+
+ const response = await transport.request({
+ jsonrpc: '2.0',
+ id: 1,
+ method: 'initialize',
+ params: {
+ protocolVersion: '2024-11-05',
+ capabilities: {},
+ clientInfo: {
+ name: 'test-client',
+ version: '1.0.0',
+ },
+ },
+ });
+
+ if (!response.protocolVersion) {
+ throw new Error('Server did not return protocol version');
+ }
+
+ await server.close();
+}
+
+async function checkToolsCapability() {
+ // Verify tools capability is properly implemented
+ const toolsExist = await fs.access('src/tools')
+ .then(() => true)
+ .catch(() => false);
+
+ if (!toolsExist) {
+ console.log(' (No tools implemented yet)');
+ return;
+ }
+
+ // Check that tools are properly exported
+ const { tools } = await import('../src/tools/index.js');
+ if (!Array.isArray(tools)) {
+ throw new Error('Tools must be exported as an array');
+ }
+}
+
+async function checkResourcesCapability() {
+ // Verify resources capability is properly implemented
+ const resourcesExist = await fs.access('src/resources')
+ .then(() => true)
+ .catch(() => false);
+
+ if (!resourcesExist) {
+ console.log(' (No resources implemented yet)');
+ return;
+ }
+
+ // Check that resources are properly exported
+ const { resources } = await import('../src/resources/index.js');
+ if (!Array.isArray(resources)) {
+ throw new Error('Resources must be exported as an array');
+ }
+}
+
+async function checkPromptsCapability() {
+ // Verify prompts capability is properly implemented
+ const promptsExist = await fs.access('src/prompts')
+ .then(() => true)
+ .catch(() => false);
+
+ if (!promptsExist) {
+ console.log(' (No prompts implemented yet)');
+ return;
+ }
+
+ // Check that prompts are properly exported
+ const { prompts } = await import('../src/prompts/index.js');
+ if (!Array.isArray(prompts)) {
+ throw new Error('Prompts must be exported as an array');
+ }
+}
+
+async function checkErrorHandling() {
+ // Test that server properly handles errors
+ const { handleError } = await import('../src/utils/error-handler.js');
+
+ // Test known error
+ const knownError = new Error('Test error');
+ knownError.code = 'TEST_ERROR';
+ const response1 = handleError(knownError);
+ if (!response1.error || response1.error.code !== 'TEST_ERROR') {
+ throw new Error('Error handler does not preserve error codes');
+ }
+
+ // Test unknown error
+ const unknownError = new Error('Unknown error');
+ const response2 = handleError(unknownError);
+ if (!response2.error || response2.error.code !== 'INTERNAL_ERROR') {
+ throw new Error('Error handler does not handle unknown errors');
+ }
+}
+
+async function launchInspector() {
+ console.log('Starting MCP Inspector...');
+ console.log('This will open an interactive testing UI in your browser.');
+ console.log('Press Ctrl+C to stop the inspector.\n');
+
+ const inspector = exec('npx @modelcontextprotocol/inspector');
+
+ inspector.stdout.on('data', (data) => {
+ console.log(data.toString());
+ });
+
+ inspector.stderr.on('data', (data) => {
+ console.error(data.toString());
+ });
+
+ inspector.on('close', (code) => {
+ console.log(`Inspector exited with code ${code}`);
+ });
+}
+``` \ No newline at end of file