Simplr client library for MCP services
Project description
mcp-simplr
Python client library for MCP Simplr — payments, emailing, and auth for MCP services.
Installation
pip install mcp-simplr
Payments
Customers register once on the platform and receive a customer_token. MCP owners accept this token from their users and pass it to charge() — no customer registration code needed on your end.
Token billing — charge per output token
Best for AI tools where cost scales with usage.
from mcp_simplr import MCPPayments, PaymentConfig, PaymentModel
payments = MCPPayments(PaymentConfig(
api_key="simplr_owner_...",
payment_model=PaymentModel.TOKEN,
price_per_token=0.000_5, # AUD per output token
currency="aud",
environment="production",
))
# Charge explicitly
await payments.charge(customer_token, tokens=500)
# Or use the decorator — counts tokens and charges automatically
@payments.track_usage(customer_token="<customer-token>")
async def my_tool(query: str) -> str:
return await run_model(query)
# Dynamic customer token from request payload
@payments.track_usage(customer_token_key="customer_token")
async def my_tool(query: str, customer_token: str) -> str:
return await run_model(query)
With token billing, the cost is only known after the model call finishes — so charge() necessarily runs after you've already paid for the LLM call yourself. Use check_ready() first to fail fast on a customer who can't be charged, before spending anything:
if not await payments.check_ready(customer_token):
return "Payment isn't set up for this customer right now."
result = await run_model(query)
await payments.charge(customer_token, tokens=result.usage.output_tokens)
Fixed billing — charge a set amount per call
Best for tools with a predictable cost per request.
from mcp_simplr import MCPPayments, PaymentConfig, PaymentModel
payments = MCPPayments(PaymentConfig(
api_key="simplr_owner_...",
payment_model=PaymentModel.FIXED,
currency="aud",
environment="production",
))
await payments.charge(
customer_token,
amount_cents=500, # AUD $5.00
description="Property report",
)
Recurring billing — subscription plans
Best for services with ongoing access (monthly/yearly).
from mcp_simplr import MCPPayments, PaymentConfig, PaymentModel, PlanConfig, BillingInterval
payments = MCPPayments(PaymentConfig(
api_key="simplr_owner_...",
payment_model=PaymentModel.RECURRING,
currency="aud",
environment="production",
plans=[
PlanConfig(id="basic", name="Basic", amount=999, interval=BillingInterval.MONTH),
PlanConfig(id="pro", name="Pro", amount=2999, interval=BillingInterval.MONTH),
],
))
# Subscribe a customer to a plan
await payments.charge(customer_token, plan_id="pro")
# Cancel a subscription
payments.cancel_subscription(customer_token, plan_id="pro")
# List available plans
plans = payments.list_plans()
Charge history
history = await payments.get_charges(customer_token)
history = await payments.get_charges(customer_token, from_date="2026-06-01", to_date="2026-06-30")
Testing
Use MCPPayments.TEST_CUSTOMER_TOKEN in sandbox mode to test the full charge flow without a real customer:
await payments.charge(MCPPayments.TEST_CUSTOMER_TOKEN, tokens=500)
TEST_CUSTOMER_TOKEN only works when environment=Environment.SANDBOX — its payment method exists only in
Stripe's sandbox, so check_ready() returns False and charge() raises PaymentFailedError for it under
environment=Environment.PRODUCTION, even if the token is passed in some other way (e.g. via oauth.verify()).
Emailing
Send emails to your users from your branded slug@mcp-simplr.au address. Subscribe to the email service on the platform website first — your project API key encodes the service slug automatically.
Customer replies are forwarded to the personal inbox you registered during subscription.
from mcp_simplr import MCPEmail, EmailServiceConfig
email = MCPEmail(EmailServiceConfig(
api_key="simplr_owner_...", # project key — encodes your service slug
))
await email.send(
to="customer@example.com",
subject="Your property report is ready",
body="Hi Sarah, your report for 123 Main St is attached.",
)
Auth
Gate your MCP tools so only registered customers can call them. Customers authorise via OAuth — they log in with their email and approve your tool, no tokens for them to copy around. Subscribe to the Auth service on the platform website first.
Setup
from mcp_simplr import AuthConfig
from mcp_simplr.oauth import MCPOAuth
oauth = MCPOAuth(
auth_config=AuthConfig(api_key="simplr_owner_..."), # project key — encodes your service slug
oauth_client_id="...", # from POST /oauth/register — omit for MCP-discovery only
frontend_url="https://yourapp.com", # required together with oauth_client_id
)
app.include_router(oauth.router)
# Serves /.well-known/oauth-authorization-server (always), plus /auth/login,
# /auth/callback, /auth/refresh when oauth_client_id + frontend_url are set.
One flow, no sandbox/production switch to configure — see Testing below.
Auth-only (decorator)
@oauth.require(bearer_token_key="bearer_token")
async def search_properties(query: str, bearer_token: str) -> str:
return await do_search(query)
Raises AuthenticationError if the bearer token is missing, invalid, or expired.
Auth + charge in the same tool
Pass result_key to inject the full verify result — including simplr_token — into your handler:
from mcp_simplr import AuthConfig, MCPPayments, PaymentConfig, PaymentModel, Currency
from mcp_simplr.oauth import MCPOAuth
oauth = MCPOAuth(auth_config=AuthConfig(api_key="simplr_owner_..."))
payments = MCPPayments(PaymentConfig(
api_key="simplr_owner_...",
payment_model=PaymentModel.TOKEN,
currency=Currency.AUD,
environment="production",
price_per_token=0.0005,
))
@oauth.require(bearer_token_key="bearer_token", result_key="customer")
async def search_properties(query: str, bearer_token: str, customer: dict = None) -> str:
result = await run_model(query)
await payments.charge(customer["simplr_token"], tokens=result.usage.output_tokens)
return result
Calling verify() directly
Prefer manual control over the decorator? oauth.verify() is the same call, just awaited directly:
customer = await oauth.verify(bearer_token)
# {
# "customer_id": "uuid", # ← stable UUID — safe to use as a DB key
# "email": "...",
# "name": "...",
# "simplr_token": "simplr_...", # ← use this to charge the customer
# }
Testing
To test your own integration, log in on the consent screen as test@simplr.dev with any 6-digit code — that account skips the real OTP check too, so testing never depends on retrieving a code from logs or email. It always passes, regardless of your project's Auth service subscription status, and returns customer["simplr_token"] == MCPPayments.TEST_CUSTOMER_TOKEN, a working test card safe to call charge() against. Any other customer needs a real OTP and your project needs an active Auth service subscription.
customer = await oauth.verify(test_bearer_token)
await payments.charge(customer["simplr_token"], tokens=500) # never touches a real card
This only works to spend money in sandbox — if your MCPPayments config has environment=Environment.PRODUCTION,
charge() raises PaymentFailedError for customer["simplr_token"] here, since that test payment method only
exists in Stripe's sandbox. The auth bypass itself still lets test@simplr.dev through either way; it's charge()
and check_ready() that refuse to bill it in production.
Support
Contact us at contact@mcp-simplr.au
Project details
Release history Release notifications | RSS feed
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 mcp_simplr-0.11.2.tar.gz.
File metadata
- Download URL: mcp_simplr-0.11.2.tar.gz
- Upload date:
- Size: 21.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f6702528d1e10daf9931523aedaad2e6e89de974866b0cf8befe0320f1d4b2de
|
|
| MD5 |
4854817cdcfdf3809edbae723a44aa64
|
|
| BLAKE2b-256 |
3f3234f19ff8ad10d84aeb93e01a03b0c4a1c6b6d0e8aca40b85cb848c1d45cd
|
File details
Details for the file mcp_simplr-0.11.2-py3-none-any.whl.
File metadata
- Download URL: mcp_simplr-0.11.2-py3-none-any.whl
- Upload date:
- Size: 24.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ce94ca5f6b31523bab82671d813cb8009bce010f21c51747fc7affff0650829e
|
|
| MD5 |
cc2c615bf6ff45bd7ead45f6803a1388
|
|
| BLAKE2b-256 |
160297d1fa4c5b002e6b7e545abeb04a591131a4e5534719a67acceaf9a8579b
|