toolcall
Enables creating MCP (Model Context Protocol) servers with zero boilerplate, full TypeScript support, and multiple transports (stdio and HTTP).
README
toolcall
Create MCP (Model Context Protocol) servers with zero boilerplate.
Features
- Minimal API - Just two functions:
serve()andtool() - 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 handshakenotifications/initialized- Client initialization acknowledgmenttools/list- List available toolstools/call- Execute a toolping- 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)
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.