Skip to main content

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mcp_simplr-0.11.2.tar.gz (21.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mcp_simplr-0.11.2-py3-none-any.whl (24.0 kB view details)

Uploaded Python 3

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

Hashes for mcp_simplr-0.11.2.tar.gz
Algorithm Hash digest
SHA256 f6702528d1e10daf9931523aedaad2e6e89de974866b0cf8befe0320f1d4b2de
MD5 4854817cdcfdf3809edbae723a44aa64
BLAKE2b-256 3f3234f19ff8ad10d84aeb93e01a03b0c4a1c6b6d0e8aca40b85cb848c1d45cd

See more details on using hashes here.

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

Hashes for mcp_simplr-0.11.2-py3-none-any.whl
Algorithm Hash digest
SHA256 ce94ca5f6b31523bab82671d813cb8009bce010f21c51747fc7affff0650829e
MD5 cc2c615bf6ff45bd7ead45f6803a1388
BLAKE2b-256 160297d1fa4c5b002e6b7e545abeb04a591131a4e5534719a67acceaf9a8579b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page