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

toolcall

v0.1.0

Published

FastMCP for JavaScript - Create MCP servers with zero boilerplate

Readme

toolcall

Create MCP (Model Context Protocol) servers with zero boilerplate.

npm version License: MIT

Features

  • Minimal API - Just two functions: serve() and tool()
  • Type-safe - Full TypeScript support with Zod schema validation
  • Multiple transports - Supports both stdio and HTTP
  • MCP compliant - Implements MCP protocol version 2024-11-05
  • Client included - Connect to any MCP server programmatically

Installation

npm install toolcall zod

Quick Start

Create an MCP server in just a few lines:

import { serve, tool } from 'toolcall'
import { z } from 'zod'

serve({
  name: 'my-server',
  version: '1.0.0',
  tools: {
    greet: tool({
      description: 'Greet someone by name',
      parameters: z.object({
        name: z.string().describe('The name of the person to greet')
      }),
      execute: ({ name }) => `Hello, ${name}!`
    }),

    add: tool({
      description: 'Add two numbers',
      parameters: z.object({
        a: z.number().describe('First number'),
        b: z.number().describe('Second number')
      }),
      execute: ({ a, b }) => ({ result: a + b })
    })
  }
})

Run it:

npx tsx server.ts

Claude Code Integration

toolcall servers integrate seamlessly with Claude Code. Add your server to Claude Code's MCP configuration:

1. Create your server file

// my-tools.ts
import { serve, tool } from 'toolcall'
import { z } from 'zod'

serve({
  name: 'my-tools',
  tools: {
    get_weather: tool({
      description: 'Get current weather for a city',
      parameters: z.object({
        city: z.string().describe('City name'),
        unit: z.enum(['celsius', 'fahrenheit']).default('celsius')
      }),
      execute: async ({ city, unit }) => {
        // Your implementation here
        return { city, temperature: 22, unit, condition: 'sunny' }
      }
    })
  }
})

2. Configure Claude Code

Add to your Claude Code MCP settings (~/.claude/claude_desktop_config.json or via Claude Code settings):

{
  "mcpServers": {
    "my-tools": {
      "command": "npx",
      "args": ["tsx", "/path/to/my-tools.ts"]
    }
  }
}

Or if you've compiled your TypeScript:

{
  "mcpServers": {
    "my-tools": {
      "command": "node",
      "args": ["/path/to/my-tools.js"]
    }
  }
}

3. Use in Claude Code

Once configured, Claude Code will automatically discover your tools. You can ask Claude to use them:

"Use my get_weather tool to check the weather in Tokyo"

API Reference

serve(options)

Creates and starts an MCP server.

serve({
  name: 'my-server',        // Server name (default: 'toolcall-server')
  version: '1.0.0',         // Server version (default: '1.0.0')
  transport: 'stdio',       // Transport type: 'stdio' | 'http' (default: 'stdio')
  port: 3000,               // Port for HTTP transport (default: 3000)
  tools: {                  // Tool definitions
    // ... your tools
  }
})

tool(definition)

Defines a type-safe tool with Zod schema validation.

tool({
  description: 'Tool description shown to clients',
  parameters: z.object({
    // Zod schema for parameters
  }),
  execute: async (params) => {
    // Tool implementation
    // Can return string, object, or any JSON-serializable value
  }
})

Parameter Types

toolcall supports all Zod types:

import { z } from 'zod'

// Strings
z.string()
z.string().min(1).max(100)
z.string().email()
z.string().url()

// Numbers
z.number()
z.number().min(0).max(100)
z.number().int()

// Booleans
z.boolean()

// Enums
z.enum(['option1', 'option2', 'option3'])

// Arrays
z.array(z.string())

// Optional with defaults
z.string().optional()
z.number().default(10)

// Descriptions (shown in tool schema)
z.string().describe('Parameter description')

Return Values

Tools can return any JSON-serializable value:

// String return
execute: ({ name }) => `Hello, ${name}!`

// Object return (automatically JSON-stringified)
execute: ({ a, b }) => ({ result: a + b, operation: 'addition' })

// Async operations
execute: async ({ url }) => {
  const response = await fetch(url)
  return await response.json()
}

Transports

Stdio (Default)

The stdio transport reads JSON-RPC messages from stdin and writes responses to stdout. This is the standard transport for MCP servers used by Claude Code and other MCP clients.

serve({
  transport: 'stdio',  // or omit - stdio is default
  tools: { /* ... */ }
})

HTTP

The HTTP transport creates an HTTP server that accepts JSON-RPC POST requests.

serve({
  transport: 'http',
  port: 3000,
  tools: { /* ... */ }
})

Test with curl:

# Initialize
curl -X POST http://localhost:3000 \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{}}}'

# List tools
curl -X POST http://localhost:3000 \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'

# Call a tool
curl -X POST http://localhost:3000 \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"greet","arguments":{"name":"World"}}}'

Client Usage

toolcall includes a client for connecting to any MCP server:

import { connect } from 'toolcall'

// Connect to a stdio server
const client = await connect('npx tsx ./server.ts')

// Or connect to an HTTP server
const client = await connect('http://localhost:3000')

// List available tools
console.log(client.listTools())

// Call a tool
const result = await client.call('greet', { name: 'World' })
console.log(result)  // "Hello, World!"

// Clean up
client.close()

Complete Example

import { serve, tool } from 'toolcall'
import { z } from 'zod'

serve({
  name: 'example-server',
  version: '1.0.0',
  tools: {
    // Simple string return
    greet: tool({
      description: 'Greet someone by name',
      parameters: z.object({
        name: z.string().describe('The name of the person to greet')
      }),
      execute: ({ name }) => `Hello, ${name}!`
    }),

    // Object return
    add: tool({
      description: 'Add two numbers together',
      parameters: z.object({
        a: z.number().describe('First number'),
        b: z.number().describe('Second number')
      }),
      execute: ({ a, b }) => ({ result: a + b })
    }),

    // Async with enum and default
    get_weather: tool({
      description: 'Get the current weather for a city',
      parameters: z.object({
        city: z.string().describe('City name'),
        unit: z.enum(['celsius', 'fahrenheit']).default('celsius').describe('Temperature unit')
      }),
      execute: async ({ city, unit }) => {
        // Simulate API call
        const temp = Math.round(Math.random() * 30 + 10)
        const tempInUnit = unit === 'fahrenheit' ? Math.round(temp * 9 / 5 + 32) : temp
        return {
          city,
          temperature: tempInUnit,
          unit,
          condition: ['sunny', 'cloudy', 'rainy'][Math.floor(Math.random() * 3)]
        }
      }
    }),

    // Constrained parameters
    search: tool({
      description: 'Search for information',
      parameters: z.object({
        query: z.string().describe('Search query'),
        limit: z.number().min(1).max(100).default(10).describe('Maximum results')
      }),
      execute: async ({ query, limit }) => {
        return {
          query,
          results: Array.from({ length: Math.min(limit, 3) }, (_, i) => ({
            title: `Result ${i + 1} for "${query}"`,
            url: `https://example.com/result/${i + 1}`
          }))
        }
      }
    })
  }
})

Error Handling

toolcall automatically validates parameters against your Zod schemas. Invalid parameters return a JSON-RPC error:

{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid parameters",
    "data": {
      "name": { "_errors": ["Required"] }
    }
  }
}

Errors thrown in tool execution are caught and returned as internal errors:

{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error",
    "data": "Error message here"
  }
}

Protocol Details

toolcall implements the Model Context Protocol specification:

  • Protocol Version: 2024-11-05
  • Transport: JSON-RPC 2.0 over stdio or HTTP
  • Methods:
    • initialize - Server initialization handshake
    • notifications/initialized - Client initialization acknowledgment
    • tools/list - List available tools
    • tools/call - Execute a tool
    • ping - Health check

Development

# Install dependencies
npm install

# Build
npm run build

# Watch mode
npm run dev

# Run example server
npx tsx examples/server.ts

# Run tests
npm test

License

MIT

Author

Yi Min Yang (https://www.yiminyang.dev)