Package Exports
- @auvh/climeter-mcp
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