npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mimik/mcp-kit

v1.1.1

Published

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

Downloads

226

Readme

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

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

Quick Start

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)

{
  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

{
  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

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

API Reference

McpServer

Constructor

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 - JSON-RPC 2.0 response

Schema Helpers (z)

Basic Types

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

Complex Types

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

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

Chaining

All modifiers can be chained in any order:

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

Examples

Basic Tool

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

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

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

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

Prompt Example

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

// 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

{
  "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

// 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

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

// 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:

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

// 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

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:

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

// 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

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);
}