npm package: @mimik/mcp-kit
Description: A lightweight, flexible library for building Model Context Protocol (MCP) servers with comprehensive schema validation and Zod-like syntax support in mimik serverless environment.
Latest version: 1.1.1

# mcp-kit: MCP Server Library

A lightweight, flexible library for building Model Context Protocol (MCP) servers with comprehensive schema validation and Zod-like syntax support in mimik serverless environment.

## Table of Contents

- [Installation](#installation)
- [Quick Start](#quick-start)
- [Core Concepts](#core-concepts)
- [Schema Definition](#schema-definition)
- [API Reference](#api-reference)
- [Examples](#examples)
- [Error Handling](#error-handling)
- [Best Practices](#best-practices)

## Installation

```bash
npm install @mimik/mcp-kit
```

```javascript
const { McpServer, z } = require('@mimik/mcp-kit');
```

## Quick Start

```javascript
const { McpServer, z } = require('@mimik/mcp-kit');

// Create a server instance
const server = new McpServer({
  name: 'my-mcp-server',
  version: '1.0.0'
});

// Register a tool
server.tool(
  'greet',
  'Greets a user with optional formality',
  {
    name: z.string().describe('The name to greet'),
    formal: z.boolean().default(false).describe('Use formal greeting')
  },
  async (args) => {
    const { name, formal } = args;
    const greeting = formal ? `Good day, ${name}` : `Hi ${name}!`;
    return greeting;
  }
);

// Handle MCP requests
const response = await server.handleMcpRequest(request);
```

## Core Concepts

### Server Instance

The `McpServer` class is the main entry point for creating MCP servers. It handles JSON-RPC 2.0 protocol communication and manages tools, resources, and prompts.

### Tools

Tools are functions that can be called by MCP clients. Each tool has:
- **Name**: Unique identifier
- **Description**: Human-readable description
- **Input Schema**: Defines expected parameters
- **Handler**: Async function that processes the request

### Resources

Resources represent readable content (files, data, etc.) that clients can access.

### Prompts

Prompts are templates for generating messages with dynamic content.

## Schema Definition

This library supports multiple schema definition formats for maximum flexibility.

### Zod-like Syntax (Recommended)

```javascript
{
  name: z.string().describe('User name'),
  age: z.number().default(25).describe('User age'),
  role: z.enum(['admin', 'user', 'guest']).default('user').describe('User role'),
  active: z.boolean().describe('Is user active'),
  tags: z.array(z.string()).describe('User tags'),
  profile: z.object({
    bio: z.string().describe('Biography'),
    website: z.optional(z.string()).describe('Website URL')
  }),
  nickname: z.optional(z.string()).describe('Optional nickname')
}
```

### Custom Format

```javascript
{
  name: { type: 'string', description: 'User name' },
  age: { type: 'number', optional: true, default: 25, description: 'User age' },
  role: { type: 'string', enum: ['admin', 'user', 'guest'], default: 'user' }
}
```

### Pure JSON Schema

```javascript
{
  type: 'object',
  properties: {
    name: { type: 'string', description: 'User name' },
    age: { type: 'number', default: 25, description: 'User age' }
  },
  required: ['name']
}
```

## API Reference

### McpServer

#### Constructor

```javascript
new McpServer(serverInfo?)
```

**Parameters:**
- `serverInfo` (optional): Object with `name` and `version` properties

#### Methods

##### tool(name, description, inputSchema, handler)

Registers a new tool.

**Parameters:**
- `name` (string): Unique tool identifier
- `description` (string): Tool description
- `inputSchema` (object): Parameter schema definition
- `handler` (async function): Tool implementation

**Returns:** `this` (for chaining)

##### resource(uri, name, description, mimeType, handler)

Registers a new resource.

**Parameters:**
- `uri` (string): Unique resource URI
- `name` (string): Resource name
- `description` (string): Resource description
- `mimeType` (string): MIME type of the resource
- `handler` (async function): Resource content provider

**Returns:** `this` (for chaining)

##### prompt(name, description, argumentsSchema, handler)

Registers a new prompt.

**Parameters:**
- `name` (string): Unique prompt identifier
- `description` (string): Prompt description
- `argumentsSchema` (object): Arguments schema
- `handler` (async function): Prompt generator

**Returns:** `this` (for chaining)

##### handleMcpRequest(requestBody)

Processes an MCP request.

**Parameters:**
- `requestBody` (string | object): JSON-RPC 2.0 request

**Returns:** Promise<object> - JSON-RPC 2.0 response

### Schema Helpers (z)

#### Basic Types

```javascript
z.string()    // String type
z.number()    // Number type
z.boolean()   // Boolean type
```

#### Complex Types

```javascript
z.enum(['option1', 'option2', 'option3'])           // Enumeration
z.array(z.string())                                 // Array of strings
z.object({ key: z.string() })                       // Object with properties
z.optional(z.string())                              // Optional field
```

#### Modifiers

```javascript
.describe('Field description')    // Add description
.default(value)                  // Set default value (makes field optional)
```

#### Chaining

All modifiers can be chained in any order:

```javascript
z.enum(['small', 'medium', 'large'])
  .default('medium')
  .describe('Size selection')
```

## Examples

### Basic Tool

```javascript
server.tool(
  'add',
  'Adds two numbers',
  {
    a: z.number().describe('First number'),
    b: z.number().describe('Second number')
  },
  async ({ a, b }) => {
    return `${a} + ${b} = ${a + b}`;
  }
);
```

### Tool with Optional Parameters

```javascript
server.tool(
  'search',
  'Search for items',
  {
    query: z.string().describe('Search query'),
    limit: z.number().default(10).describe('Maximum results'),
    category: z.optional(z.string()).describe('Filter by category')
  },
  async ({ query, limit, category }) => {
    // Implementation here
    return `Searching for "${query}" (limit: ${limit}, category: ${category || 'all'})`;
  }
);
```

### Tool with Enum and Complex Objects

```javascript
server.tool(
  'createOrder',
  'Create a new order',
  {
    type: z.enum(['pickup', 'delivery', 'dine-in']).default('pickup'),
    priority: z.enum(['low', 'normal', 'high']).default('normal'),
    customer: z.object({
      name: z.string(),
      email: z.string(),
      phone: z.optional(z.string())
    }),
    items: z.array(z.object({
      id: z.string(),
      quantity: z.number(),
      notes: z.optional(z.string())
    }))
  },
  async (args) => {
    const { type, priority, customer, items } = args;
    
    return {
      content: [
        {
          type: 'text',
          text: `Order created: ${items.length} items for ${customer.name} (${type}, ${priority} priority)`
        }
      ]
    };
  }
);
```

### Resource Example

```javascript
server.resource(
  'file://logs/system.log',
  'System Log',
  'Current system log file',
  'text/plain',
  async () => {
    // Return log content
    return 'Log entries...';
  }
);
```

### Prompt Example

```javascript
server.prompt(
  'codeReview',
  'Generate code review prompt',
  {
    language: z.string().describe('Programming language'),
    level: z.enum(['beginner', 'intermediate', 'advanced']).default('intermediate')
  },
  async ({ language, level }) => {
    return [
      {
        role: 'user',
        content: {
          type: 'text',
          text: `Please review this ${language} code focusing on ${level}-level best practices.`
        }
      }
    ];
  }
);
```

## Error Handling

The library provides comprehensive error handling:

### Validation Errors

```javascript
// Missing required parameter
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid arguments: Missing required parameter: name"
  }
}

// Wrong parameter type
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid arguments: Parameter 'age' should be a number, got string"
  }
}
```

### Tool Execution Errors

```javascript
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Tool execution error",
    "data": "Division by zero"
  }
}
```

### Common Error Codes

- `-32700`: Parse error (malformed JSON)
- `-32601`: Method not found
- `-32602`: Invalid parameters
- `-32603`: Internal error

## Best Practices

### 1. Use Descriptive Names and Descriptions

```javascript
// Good
server.tool(
  'calculateTax',
  'Calculate tax amount based on income and tax rate',
  {
    income: z.number().describe('Annual income in dollars'),
    rate: z.number().describe('Tax rate as decimal (0.1 for 10%)')
  },
  handler
);

// Avoid
server.tool('calc', 'Does math', { a: z.number(), b: z.number() }, handler);
```

### 2. Provide Sensible Defaults

```javascript
server.tool(
  'search',
  'Search documents',
  {
    query: z.string().describe('Search query'),
    maxResults: z.number().default(20).describe('Maximum results to return'),
    sortBy: z.enum(['relevance', 'date', 'title']).default('relevance')
  },
  handler
);
```

### 3. Use Enums for Limited Options

```javascript
// Good - clear valid options
status: z.enum(['active', 'inactive', 'pending']).default('pending')

// Avoid - unclear what values are valid
status: z.string().describe('Status (active, inactive, or pending)')
```

### 4. Validate Input Early

The library automatically validates parameters against the schema before calling your handler, so focus on business logic validation:

```javascript
async ({ email, age }) => {
  // Schema validation already ensures email is string, age is number
  
  // Add business logic validation
  if (age < 0 || age > 150) {
    throw new Error('Age must be between 0 and 150');
  }
  
  if (!email.includes('@')) {
    throw new Error('Invalid email format');
  }
  
  // Process the request...
}
```

### 5. Return Structured Responses

```javascript
// For simple text responses
return "Operation completed successfully";

// For complex responses
return {
  content: [
    { type: 'text', text: 'Results:' },
    { type: 'text', text: JSON.stringify(data, null, 2) }
  ]
};
```

### 6. Handle Async Operations Properly

```javascript
server.tool(
  'fetchData',
  'Fetch data from external API',
  { url: z.string() },
  async ({ url }) => {
    try {
      const response = await fetch(url);
      const data = await response.json();
      return JSON.stringify(data, null, 2);
    } catch (error) {
      throw new Error(`Failed to fetch data: ${error.message}`);
    }
  }
);
```

## Integration Examples

### Express-like Middleware

The key to integrating with Express or any similar web framework is using `server.handleMcpRequest()` to process all MCP requests:

```javascript
const { McpServer, z } = require('@mimik/mcp-kit');

const server = new McpServer({ name: 'my-api', version: '1.0.0' });

// Register your tools...
server.tool('ping', 'Simple ping', {}, async () => 'pong');

// IMPORTANT: Use server.handleMcpRequest() to handle all MCP communication
app.post('/mcp', async (req, res) => {
  try {
    const response = await server.handleMcpRequest(req.body);
    if (response) {
      res.json(response);
    } else {
      res.status(204).end(); // For notifications - no response needed
    }
  } catch (error) {
    res.status(500).json({
      jsonrpc: '2.0',
      id: req.body?.id || null,
      error: { code: -32603, message: 'Internal error', data: error.message }
    });
  }
});
```

#### Alternative Promise-based Approach

```javascript
// In your middleware app
app.post('/mcp', (req, res) => {
  server.handleMcpRequest(req.body).then((response) => {
    if (response) {
      res.json(response);
    } else {
      // Notification - no response needed
      res.status(204).end();
    }
  }).catch((error) => {
    res.status(500).json({ 
      jsonrpc: '2.0',
      id: req.body?.id || null,
      error: { code: -32603, message: 'Internal error', data: error.message }
    });
  });
});
```

**Key Points:**
- Always use `server.handleMcpRequest(req.body)` to process MCP requests
- Handle both responses (requests) and notifications (no response)
- Use status 204 for notifications, not 200
- Properly format error responses as JSON-RPC 2.0

### Testing Your Server

```javascript
async function testServer() {
  // Initialize
  const initResponse = await server.handleMcpRequest({
    jsonrpc: '2.0',
    id: 1,
    method: 'initialize',
    params: {}
  });
  
  // List tools
  const toolsResponse = await server.handleMcpRequest({
    jsonrpc: '2.0',
    id: 2,
    method: 'tools/list'
  });
  
  // Call a tool
  const callResponse = await server.handleMcpRequest({
    jsonrpc: '2.0',
    id: 3,
    method: 'tools/call',
    params: {
      name: 'greet',
      arguments: { name: 'World', formal: true }
    }
  });
  
  console.log('Tool response:', callResponse);
}
```