Billing SDK for MCP servers
Project description
metrify
Billing SDK for MCP servers. Add pay-per-call monetization to any MCP tool in under 5 minutes — no payment infrastructure required.
Quickstart
1. Register as a provider
Sign up at the Metrify dashboard and get your pk_live_... provider key.
2. Install the SDK
pip install metrify
3. Configure your provider key
export METRIFY_PROVIDER_KEY="pk_live_your_key_here"
Or pass it directly in code (see step 4).
4. Decorate your tools and register them
from metrify import Metrify
m = Metrify() # reads METRIFY_PROVIDER_KEY from env
@m.tool(price=0.01, unit="per_call")
async def summarize(consumer_api_key: str, text: str) -> str:
"""Summarize a block of text."""
# Balance is pre-checked before this runs; charge happens only on success
return f"Summary of: {text[:50]}..."
# On startup, push all decorated tools to the Metrify dashboard:
await m.register_tools()
register_tools() is idempotent — run it every time your server starts.
Tools with no docstring get description=None; pass description= explicitly
to override:
@m.tool(price=0.05, unit="per_call", description="OCR a PDF page")
async def ocr_page(consumer_api_key: str, page_url: str) -> str:
...
5. Deploy
Your tool now charges consumers on every call. The consumer_api_key is
extracted automatically by the decorator — consumers pass it as the first
argument or as a keyword argument.
Full example with FastMCP
from metrify import Metrify
from mcp.server.fastmcp import FastMCP
m = Metrify(provider_key="pk_live_your_key_here")
server = FastMCP("my-mcp-server")
@server.tool()
@m.tool(price=0.05, unit="per_call")
async def analyze_document(consumer_api_key: str, content: str) -> str:
"""Analyze a document and return insights."""
# billing.charge() runs before this line
analysis = await run_analysis(content)
return analysis
@server.tool()
@m.tool(price=0.001, unit="per_token")
async def generate_text(consumer_api_key: str, prompt: str) -> str:
"""Generate text from a prompt."""
result = await llm.complete(prompt)
return result
Billing flow
The decorator runs a two-phase billing flow on every call:
- Pre-check — verify the consumer has enough balance (no charge yet).
- Execute — run your tool function.
- Charge — deduct only if the function completed without error.
If the function raises any exception the consumer is never charged.
Exception reference
| Exception | Raised by | Behavior |
|---|---|---|
InsufficientBalanceError |
Pre-check | Propagated — consumer has no funds |
UpstreamError |
Your tool | Caught — returns message to consumer, no charge |
ProviderSuspendedError |
Pre-check / charge | Propagated — your account is suspended |
ConsumerSuspendedError |
Pre-check / charge | Propagated — consumer account is suspended |
ProviderNotFoundError |
Pre-check / charge | Propagated — your provider key is invalid |
GatewayError |
Pre-check / charge | Propagated — network error or backend 5xx |
Any other Exception |
Your tool | Caught — returns "Tool error: ...", no charge |
Signalling upstream failures with UpstreamError
Use UpstreamError when an external API your tool depends on fails. The
decorator catches it, skips billing, and returns your message to the consumer:
from metrify import Metrify, UpstreamError
m = Metrify()
@m.tool(price=0.05, unit="per_call")
async def summarize(consumer_api_key: str, text: str) -> str:
try:
result = await anthropic_client.messages.create(...)
except anthropic.APIError as e:
raise UpstreamError(f"Anthropic API error: {e.message}")
return result.content[0].text
The consumer receives "Anthropic API error: ..." and is not charged.
from metrify.exceptions import InsufficientBalanceError, GatewayError
# In your MCP server error handler:
try:
result = await my_tool(consumer_api_key=ck, query=q)
except InsufficientBalanceError:
return "Insufficient balance. Please top up at the Metrify dashboard."
except GatewayError:
return "Billing service temporarily unavailable. Please retry."
Environment variables
| Variable | Default | Description |
|---|---|---|
METRIFY_PROVIDER_KEY |
— | Your provider key (pk_live_...) |
METRIFY_GATEWAY_URL |
https://airy-wholeness-production-fcc4.up.railway.app |
Override backend URL |
METRIFY_MCP_URL |
— | Public URL of your MCP server (e.g. https://myserver.railway.app/mcp) |
Supported billing units (V1)
All units charge a flat price per call. The unit field is displayed in your
dashboard for reporting purposes.
| Unit | Use case |
|---|---|
per_call |
Fixed price per tool invocation |
per_token |
LLM completions (flat in V1) |
per_minute |
Audio/video processing |
per_image |
Image generation or analysis |
per_page |
Document processing |
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file metrify_sdk-0.2.2.tar.gz.
File metadata
- Download URL: metrify_sdk-0.2.2.tar.gz
- Upload date:
- Size: 13.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.8.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dc799eb60e7e6ddbc10ceb07b936aec0b376ebd9392fc80720acb16e8fb89f2f
|
|
| MD5 |
9535c68e1113160fd9f2188c4c322eae
|
|
| BLAKE2b-256 |
fdf27be7a61cb5b7ce14cf3544fadbda42bf78ff0fd263966b70e5464a32707c
|
File details
Details for the file metrify_sdk-0.2.2-py3-none-any.whl.
File metadata
- Download URL: metrify_sdk-0.2.2-py3-none-any.whl
- Upload date:
- Size: 7.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.8.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cc86cfe7a09b9b78cafbc8fed1ed564b7970326ea15abdded3a051b69f08f4e2
|
|
| MD5 |
250cc9cb39cc50391027a86fb9c680a4
|
|
| BLAKE2b-256 |
16046227636f6fd931d09dd7d09e427f552e53244b95cca27641e40d1039b29e
|