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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@mcpkit-dev/core

v1.5.0

Published

Developer-friendly toolkit for building MCP servers with decorators

Readme

@mcpkit-dev/core

Developer-friendly toolkit for building Model Context Protocol (MCP) servers with decorators.

npm version License: MIT

Features

  • Decorator-based API - Clean, declarative syntax with @MCPServer, @Tool, @Resource, @Prompt, @Param, and @Monitor
  • Type-safe - Full TypeScript support with automatic type inference
  • Minimal boilerplate - Focus on your business logic, not protocol details
  • Zod integration - Runtime validation with automatic JSON Schema generation
  • Lifecycle hooks - Built-in logging and monitoring with ServerHooks
  • MCP SDK compatible - Built on top of the official @modelcontextprotocol/sdk

Installation

npm install @mcpkit-dev/core @modelcontextprotocol/sdk zod reflect-metadata

Quick Start

import 'reflect-metadata';
import { MCPServer, Tool, Param } from '@mcpkit-dev/core';

@MCPServer({
  name: 'my-server',
  version: '1.0.0',
})
class MyServer {
  @Tool({ description: 'Greet someone by name' })
  async greet(
    @Param({ name: 'name', description: 'Name to greet' })
    name: string,
  ): Promise<string> {
    return `Hello, ${name}!`;
  }
}

const server = new MyServer();
await server.listen();

Decorators

@MCPServer

Class decorator that marks a class as an MCP server.

@MCPServer({
  name: 'weather-server',
  version: '1.0.0',
  description: 'Get weather information',
})
class WeatherServer {}

@Tool

Method decorator that exposes a method as an MCP tool.

// Using @Param decorators
@Tool({ description: 'Get current weather' })
async getWeather(
  @Param({ name: 'city', description: 'City name' }) city: string,
  @Param({ name: 'unit', optional: true }) unit?: 'celsius' | 'fahrenheit',
): Promise<WeatherData> {
  // implementation
}

// Using explicit Zod schema
@Tool({
  description: 'Get forecast',
  schema: z.object({
    city: z.string(),
    days: z.number().min(1).max(7),
  }),
})
async getForecast(args: { city: string; days: number }) {
  // implementation
}

@Param

Parameter decorator for tool/prompt parameters.

@Param({
  name: 'city',           // Required - parameter name
  description: 'City',    // Optional - shown to AI
  schema: z.string(),     // Optional - explicit Zod schema
  optional: true,         // Optional - is parameter optional?
})

@Resource

Method decorator that exposes data as an MCP resource.

// URI template with parameters
@Resource('weather://cities/{city}/current')
async getCityWeather(city: string) {
  return {
    contents: [{
      uri: `weather://cities/${city}/current`,
      mimeType: 'application/json',
      text: JSON.stringify({ temperature: 22 }),
    }],
  };
}

// Static resource with options
@Resource({
  uri: 'docs://readme',
  name: 'README',
  mimeType: 'text/markdown',
})
async getReadme() {
  return { contents: [{ uri: 'docs://readme', text: '# README' }] };
}

@Prompt

Method decorator that creates a reusable prompt template.

@Prompt({ description: 'Generate a weather report' })
async weatherReport(
  @Param({ name: 'city' }) city: string,
) {
  return {
    messages: [{
      role: 'user',
      content: { type: 'text', text: `Write a weather report for ${city}` },
    }],
  };
}

@Monitor

Method decorator for per-method monitoring and logging.

@Tool({ description: 'Process important data' })
@Monitor({
  logArgs: true,      // Log input arguments
  logResult: true,    // Log return value
  logDuration: true,  // Log execution time (default: true)
  logErrors: true,    // Log errors (default: true)
  logger: customLogger, // Optional custom logger
})
async processData(@Param({ name: 'data' }) data: string) {
  return `Processed: ${data}`;
}

Note: @Monitor only works when hooks are configured on @MCPServer.

Lifecycle Hooks

Add logging, monitoring, and observability to your server with hooks:

import { MCPServer, Tool, Param, type ServerHooks } from '@mcpkit-dev/core';

@MCPServer({
  name: 'monitored-server',
  version: '1.0.0',
  hooks: {
    // Whether to await hook execution (default: true)
    awaitHooks: true,

    // Server lifecycle
    // Note: Use console.error (stderr) for logging - stdout is reserved for MCP protocol
    onServerStart: () => console.error('Server started'),
    onServerStop: () => console.error('Server stopped'),

    // Tool hooks
    onToolCall: ({ toolName, args }) => {
      console.error(`Tool ${toolName} called with`, args);
    },
    onToolSuccess: ({ toolName, duration, result }) => {
      console.error(`Tool ${toolName} completed in ${duration}ms`);
    },
    onToolError: ({ toolName, error, duration }) => {
      console.error(`Tool ${toolName} failed after ${duration}ms:`, error.message);
    },

    // Resource hooks
    onResourceRead: ({ uri }) => console.error(`Reading resource: ${uri}`),
    onResourceSuccess: ({ uri, duration }) => console.error(`Resource read in ${duration}ms`),
    onResourceError: ({ uri, error }) => console.error(`Resource error: ${uri}`, error),

    // Prompt hooks
    onPromptGet: ({ promptName }) => console.error(`Getting prompt: ${promptName}`),
    onPromptSuccess: ({ promptName, duration }) => console.error(`Prompt ready in ${duration}ms`),
    onPromptError: ({ promptName, error }) => console.error(`Prompt error:`, error),
  },
})
class MonitoredServer {
  @Tool({ description: 'Example tool' })
  async example(@Param({ name: 'input' }) input: string) {
    return `Result: ${input}`;
  }
}

File Logging Example

When running under Claude Desktop, the working directory is read-only. Use an absolute path:

import fs from 'fs';
import path from 'path';
import os from 'os';
import { MCPServer, type ServerHooks } from '@mcpkit-dev/core';

// Write to home directory (or os.tmpdir() for temp files)
const logPath = path.join(os.homedir(), 'mcp-server.log');
const logStream = fs.createWriteStream(logPath, { flags: 'a' });

const hooks: ServerHooks = {
  onServerStart: () => logStream.write(`[${new Date().toISOString()}] Server started\n`),
  onToolCall: ({ toolName, args }) => {
    logStream.write(`[${new Date().toISOString()}] Tool ${toolName} called: ${JSON.stringify(args)}\n`);
  },
  onResourceRead: ({ uri }) => {
    logStream.write(`[${new Date().toISOString()}] Reading resource: ${uri}\n`);
  },
};

@MCPServer({ name: 'file-logged-server', version: '1.0.0', hooks })
class MyServer { /* ... */ }

View logs with: tail -f ~/mcp-server.log

Server Lifecycle

const server = new MyServer();

// Start the server (stdio transport)
await server.listen();

// Check connection status
console.log(server.isConnected()); // true

// Graceful shutdown
await server.close();

Using with Claude Desktop

Add your server to Claude Desktop's configuration (~/Library/Application Support/Claude/claude_desktop_config.json on macOS):

{
  "mcpServers": {
    "my-server": {
      "command": "node",
      "args": ["/path/to/your/server/dist/index.js"]
    }
  }
}

TypeScript Configuration

Required TypeScript settings in tsconfig.json:

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext"
  }
}

Requirements

  • Node.js 18+
  • TypeScript 5.0+

Links

License

MIT