@auvh/climeter-mcp
v0.1.0
Published
Usage-based billing for MCP servers — wrap any MCP tool with CLIMeter metering
Maintainers
Readme
@auvh/climeter-mcp
Usage-based billing for MCP servers — wrap any MCP tool with CLIMeter metering in one line.
What is this?
@auvh/climeter-mcp is a companion to @auvh/climeter that makes it trivial to add usage-based billing to any MCP (Model Context Protocol) server.
Before:
server.tool('search', SearchSchema, async (params) => {
return doSearch(params.query)
})After — metered:
import { mcpTool } from '@auvh/climeter-mcp'
server.tool('search', SearchSchema, mcpTool('search', async (params) => {
return doSearch(params.query)
}))Every call is tracked: invocation count, execution time, success/error status. Pricing is configured in the CLIMeter dashboard.
Installation
npm install @auvh/climeter @auvh/climeter-mcpQuick Start
1. Wrap a single tool — mcpTool()
Best for one-off billing on specific tools.
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
import { z } from 'zod'
import { meter } from '@auvh/climeter'
import { mcpTool } from '@auvh/climeter-mcp'
meter.configure({
apiKey: process.env.CLIMETER_API_KEY,
toolSlug: 'my-server',
})
const server = new McpServer({ name: 'my-server', version: '1.0.0' })
server.tool(
'search',
{ query: z.string() },
mcpTool('search', async ({ query }) => {
const results = await doSearch(query)
return { content: [{ type: 'text', text: results }] }
})
)
await server.connect(new StdioServerTransport())2. Wrap all tools at once — mcpServer()
Register tools normally, then wrap everything in one call.
import { mcpServer } from '@auvh/climeter-mcp'
const server = new McpServer({ name: 'my-server', version: '1.0.0' })
server.tool('search', SearchSchema, searchHandler)
server.tool('summarize', SumSchema, summarizeHandler)
server.tool('ping', {}, pingHandler)
// Wrap all tools — ping is free (won't be billed)
mcpServer(server, {
toolSlug: 'my-server',
free: ['ping'],
})
await server.connect(transport)3. HTTP middleware — withMeter()
For MCP servers running over HTTP/SSE (Express, Hono, Fastify).
import express from 'express'
import { withMeter } from '@auvh/climeter-mcp'
const app = express()
app.use(express.json())
// Apply withMeter before your MCP HTTP handler
app.use('/mcp', withMeter({ toolSlug: 'my-server' }), mcpHttpHandler)
app.listen(3000)API Reference
mcpTool(name, handler, options?)
Wraps a single MCP tool handler with CLIMeter billing.
| Parameter | Type | Description |
|-----------|------|-------------|
| name | string | Tool name (used as billing event name) |
| handler | (params: T) => Promise<R> | Original tool handler |
| options | McpMeterOptions | Optional configuration |
McpMeterOptions:
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| billingName | string | name | Override event name for billing |
| metadata | Record<string, unknown> | {} | Extra metadata attached to every event |
| free | boolean | false | Skip billing entirely for this tool |
mcpServer(server, options?)
Wraps ALL registered tools on an MCP McpServer instance.
Call after registering all tools, before server.connect().
| Parameter | Type | Description |
|-----------|------|-------------|
| server | McpServer | The MCP server instance |
| options | McpServerOptions | Optional configuration |
McpServerOptions:
| Option | Type | Description |
|--------|------|-------------|
| toolSlug | string | Slug attached to all billing events |
| free | string[] | Tool names to skip billing for |
withMeter(options?)
Express/Connect middleware for HTTP MCP servers. Intercepts tools/call requests and tracks billing after the response.
| Parameter | Type | Description |
|-----------|------|-------------|
| options | WithMeterOptions | Optional configuration |
WithMeterOptions:
| Option | Type | Description |
|--------|------|-------------|
| toolSlug | string | Slug attached to all billing events |
guardBalance(toolSlug, minBalance?)
Pre-flight balance check. Throws if balance is below threshold. Use on the consumer side.
await guardBalance('my-server', 0.01) // throws if balance < $0.01withBalanceGuard(toolSlug, fn, minBalance?)
Wraps a function with balance guard — checks before executing.
const search = withBalanceGuard('my-server', async (params) => {
return mcpClient.callTool('search', params)
})
// Throws InsufficientBalanceError if balance is too low
const result = await search({ query: 'hello' })Claude Desktop Configuration
Add your metered MCP server to claude_desktop_config.json:
{
"mcpServers": {
"my-server": {
"command": "node",
"args": ["/path/to/my-server/dist/index.js"],
"env": {
"CLIMETER_API_KEY": "clim_v1_..."
}
}
}
}Events Tracked
Every metered call records:
| Field | Description |
|-------|-------------|
| event | Tool name (or billingName override) |
| duration_ms | Execution time in milliseconds |
| status | "success" or "error" |
| transport | "mcp" (stdio) or "http" |
| tool_slug | Your tool slug (if provided) |
Pricing
Pricing per call is configured in the CLIMeter dashboard — not in the SDK. This ensures billing integrity and lets you change pricing without redeploying.
Links
License
MIT
