Jami

io.github.kenawak由 GitHub 账号 kenawak 发布v0.3.0更新于 Nov 1, 2026

Jami creator payouts for your agent: ETB checkouts, orders, payouts, webhooks. Powered by ArifPay.

概览

AI 生成的概览

JamiDev 的 MCP 服务器,让助手创建 ETB 结账、管理产品与订阅、查询订单和提现,并调试 webhook。

可以做什么
  • 创建与管理结账:为产品或任意金额发起结账、查询状态、等待买家完成支付
  • 管理产品与订阅:创建、修改、读取和列出产品,生成订阅链接,列出、查看、取消或恢复订阅,并在沙箱中模拟续费
  • 查看订单与收益:按状态列出订单,读取可提现余额
  • 处理提现:预估提现手续费与到账净额,跟踪提现进度
  • 处理 webhook:校验投递签名,生成带签名的测试事件
  • 查阅开发者文档:搜索并以 markdown 读取 JamiDev 文档页面
适用场景
适合在构建或运营基于埃塞俄比亚比尔的 Jami/ArifPay 支付集成时使用:让助手创建结账、核对订单、查看订阅,或在编写集成代码时排查 webhook 签名问题。
运行要求
通过 npx(Node.js)以 stdio 在本地运行,也可用参数启用 HTTP 模式。账户类工具需要 JAMI_TOKEN 中的 JamiDev API 令牌(沙箱或正式);文档、提现预估和 webhook 工具无需令牌。JAMI_WEBHOOK_SECRET 可选,用于提供默认 webhook 密钥。需要访问 Jami API 与文档的网络。
安装前请注意
使用正式令牌时,结账会向真实买家收款,提现会动用真实资金;提现默认关闭,需以 --enable-withdrawals 启动,且每笔提现需幂等键。取消订阅会停止后续扣款,且不提供退款。令牌与 webhook 密钥属于机密,保存在环境变量中。用于报表的助手建议启用 --read-only。

安装

在 SourceWeft 中

  1. 打开 控制台中的 Jami,将其添加到工作区。
  2. 为需要使用其工具的对话启用该服务。

Desktop only,通过 STDIO。 STDIO 服务会启动本地进程,因此需要 SourceWeft 桌面宿主。

其他 MCP 客户端

参照 仓库 中的启动说明。

README

jami-mcp

The official Model Context Protocol server for JamiDev, the SDK for Jami's creator payouts. Connect it to Claude, Cursor, VS Code, Codex or any MCP client and your agent can create ETB checkouts, watch them settle, reconcile orders, preview and send payouts, debug webhooks, and read the Jami docs while it writes your integration. Payments are processed by ArifPay.

Built on the official jami-sdk, so every tool behaves exactly like the SDK and the REST API.

bash
npx -y jami-mcp

Quick start

  1. Get a token: Jami dashboard → JamiDev → Developer → API Tokens. Start with a sandbox org — its jamidev_test_… token moves no real money.
  2. Add the server to your client (below), with the token as JAMI_TOKEN.
  3. Ask: "Charge [email protected] 1,237 birr for order #1042 on Telebirr, phone 251900000001, and wait for it."

No token yet? The server still runs. The docs, withdrawal-quote and webhook tools work without one, so you can start building right away.

Claude Code

bash
claude mcp add jami -e JAMI_TOKEN=jamidev_test_xxx -- npx -y jami-mcp

Claude Desktop · Cursor · Windsurf

Add to claude_desktop_config.json, ~/.cursor/mcp.json or ~/.codeium/windsurf/mcp_config.json:

json
{  "mcpServers": {    "jami": {      "command": "npx",      "args": ["-y", "jami-mcp"],      "env": { "JAMI_TOKEN": "jamidev_test_xxx" }    }  }}

VS Code

.vscode/mcp.json — VS Code prompts for the token and stores it securely:

json
{  "inputs": [    { "type": "promptString", "id": "jami-token", "description": "JamiDev API token", "password": true }  ],  "servers": {    "jami": {      "type": "stdio",      "command": "npx",      "args": ["-y", "jami-mcp"],      "env": { "JAMI_TOKEN": "${input:jami-token}" }    }  }}

Codex

~/.codex/config.toml:

toml
[mcp_servers.jami]command = "npx"args = ["-y", "jami-mcp"]env = { JAMI_TOKEN = "jamidev_test_xxx" }

Tools

ToolWhat it doesNeeds token
jami_create_checkoutCharge a product (p_… token or id) or any amount (amount_minor + description). With gateway: pushes the prompt to the buyer's phone. Without: returns a Jami payment link to send them.✓
jami_create_product · jami_update_productCreate or edit products; each comes back with its public page and p_… token.✓
jami_get_product · jami_list_productsRead the catalog.✓
jami_get_checkout_statusCheck a session once, with the failure reason when it expired.✓
jami_wait_for_checkoutWait for the buyer to finish, with progress updates. Never hangs: returns open at the timeout.✓
jami_list_ordersOrders, newest first, paginated and filterable by status.✓
jami_get_balanceWithdrawable earnings and whether the org can withdraw.✓
jami_quote_withdrawalFee, tax and net for an amount, plus auto-pay and cap checks. Offline.—
jami_create_withdrawalPay out to Telebirr, M-Pesa or CBE Birr. Opt-in (see Safety).✓
jami_get_withdrawal · jami_list_withdrawalsTrack payouts.✓
jami_verify_webhookCheck a captured delivery's signature, and why it fails.—
jami_create_test_webhookBuild a correctly signed test event, and optionally POST it to your local handler.—
jami_search_docs · jami_read_docSearch and read the live docs at dev.jami.bio.—

Every tool returns a readable markdown summary plus typed structuredContent. Pass response_format: "json" for the raw payload. Money is always integer santim, and every parameter that takes money ends in _minor: amount_minor: 15000 = 150.00 ETB.

Prompts: integrate_jami (build a full integration in your stack), go_live_checklist, debug_payment. Resource: jami://reference — fees, limits, sandbox numbers, failure codes and webhook rules in one page.

Safety

The defaults are set so an agent can't move money by accident.

  • Withdrawals are off until you start the server with --enable-withdrawals (or JAMI_ENABLE_WITHDRAWALS=true). When they're on, clients that support elicitation ask you to confirm each payout, and every payout needs an idempotency_key, so a retry can never pay twice. Sandbox tokens are refused before any request is made.
  • --read-only registers only the tools that read data. Use it for reporting agents.
  • Live mode is announced. With a jamidev_live_ token, the server tells the agent that checkouts charge real buyers and to confirm first.
  • Tokens stay in the environment. They never appear in tool output.

Options

--http                 Streamable HTTP on POST /mcp instead of stdio--port <n>             HTTP port (default 3333)--host <addr>          HTTP bind address (default 127.0.0.1)--read-only            Only read tools--enable-withdrawals   Register jami_create_withdrawal--base-url <url>       Override the API base (default https://jami.bio/api/jamidev)--docs-url <url>       Override the docs origin (default https://dev.jami.bio)
EnvPurpose
JAMI_TOKENjamidev_test_… or jamidev_live_…
JAMI_WEBHOOK_SECRETDefault secret for the webhook tools
JAMI_READ_ONLY, JAMI_ENABLE_WITHDRAWALSSame as the flags

Remote (HTTP) mode

bash
npx -y jami-mcp --http --port 3333# → http://127.0.0.1:3333/mcp   (health check: /healthz)

The HTTP server is stateless, so any number of clients can share one process. Without JAMI_TOKEN, each request authenticates with its own Authorization: Bearer jamidev_… header, which makes one deployment safe to share across teams. On loopback it rejects foreign Host headers (DNS-rebinding protection). In HTTP mode, jami_create_test_webhook never sends requests; it only returns the signed payload and a curl command.

Embedding

ts
import { createJamiServer } from 'jami-mcp';import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
const server = createJamiServer({ token: process.env.JAMI_TOKEN, docsUrl: 'https://dev.jami.bio', readOnly: true, withdrawals: false });await server.connect(new StdioServerTransport());

Development

bash
npm installnpm test          # in-memory MCP client against a mocked APInpm run buildnpm run inspect   # opens the MCP Inspector on dist/index.js

Full guide: dev.jami.bio/docs/mcp · Support: [email protected]

MIT © Jami

来源:README.md,提交 98fc572

工具

22

jami_create_checkout

Create checkout

Start a checkout and get back how the buyer pays — either for one of the organization's published products, or for ANY amount with no product ("charge this buyer 1,237 ETB for order #1042"). Buyers pay in ETB via Telebirr, M-Pesa or CBE Birr; nothing is added on top. In LIVE mode a "direct" checkout pushes a real payment prompt to the buyer's phone, so only call this when the user actually wants to charge someone. What to charge: - Product: product_id (p_… token or 24-char id, must be published). amount_minor only for flexible-price (pwyw) products. - Custom amount: omit product_id, pass amount_minor (≥ 100 = 1.00 ETB) and description (what the buyer sees, ≤ 120 chars). How to collect it: - Direct push: pass gateway + customer_email + customer_phone. The prompt goes straight to the buyer's phone (kind 'direct'). - Payment link: omit gateway. You get a Jami link in checkout_url (kind 'redirect') to send the buyer by SMS/Telegram/email; they pick the method and enter their details there, and can retry a failed attempt until the link expires (30 min). customer_* is optional. Args: - customer_email / customer_phone: required for a direct push; optional for a link. Phone like "251911223344" or "0911223344". Sandbox magic numbers: 251900000001 = instant success, 251900000002 = instant failure. - gateway: 'telebirr' | 'mpesa' | 'cbe'. Omit for a payment link. - amount_minor: integer santim. 123700 = 1,237.00 ETB. - metadata: optional string map (e.g. { "orderId": "1042" }) stored on the order and sent back on order webhooks. Returns: { session_id, kind: 'free' | 'redirect' | 'direct', checkout_url, order_id }. - free: completed instantly; order_id is set. - redirect: send the buyer to checkout_url. - direct: the buyer confirms on their phone; call jami_wait_for_checkout next.

输入参数gatewaymetadataproduct_iddescriptionsuccess_urlamount_minorcheckout_linkcustomer_namecustomer_emailcustomer_phonecollected_fieldscustomer_external_id

jami_get_checkout_status

Get checkout status

Check a checkout session once. The server reconciles with the payment provider on every call, so this resolves even when webhooks are late. Args: - session_id: from jami_create_checkout. Returns: { session_id, status: 'open' | 'completed' | 'expired', order_id, order_status, amount_minor, amount, expires_at, failure }. When status is 'expired', failure.code explains why: payment_rejected · payment_failed · payment_canceled · payment_timed_out · initiate_failed · session_expired. Use jami_wait_for_checkout instead if you want to block until the buyer finishes.

输入参数session_id必填response_format

jami_wait_for_checkout

Wait for checkout

Poll a checkout session until it's paid or ends, up to timeout_seconds. Reports progress while it waits. If the buyer hasn't finished when the timeout hits, this returns status 'open' (not an error) — call it again to keep waiting. Sessions expire 30 minutes after creation. Args: - session_id: from jami_create_checkout. - timeout_seconds: 5–300 (default 90). - interval_seconds: 2–30 between polls (default 3). Returns the same shape as jami_get_checkout_status.

输入参数session_id必填response_formattimeout_secondsinterval_seconds

jami_create_product

Create product

Create a product the organization sells, and get its public Jami page (url) and p_… token. Published by default, so the page and checkouts work immediately. Pricing (can't be changed after creation; the amounts can): - fixed: amount_minor is the price, e.g. 250000 = 2,500.00 ETB. - pwyw (flexible): minimum_amount_minor; each checkout then sends its own amount. - free: no price. Subscription plans: product_type 'recurring' + pricing_model 'fixed' + billing_interval ('week' | 'month' | 'year') and optional interval_count (default 1; ≤ 52 weeks, ≤ 12 months, 1 year). amount_minor is charged from the payer's Jami wallet every period, 100–1,000,000 santim (1–10,000 ETB). The interval is locked after creation; a new price only applies to new subscribers. Args: title (1–120 chars), pricing_model, amount_minor / minimum_amount_minor, product_type? ('one_time' default | 'recurring'), billing_interval?, interval_count?, description?, status? ('published' | 'draft'). Returns: { product_id, token, url, title, status, pricing_model, product_type, billing_interval, interval_count, price }. To charge for a one-time product, call jami_create_checkout with product_id = token (omit gateway for a payment link). For a recurring plan, call jami_create_subscription_link instead.

输入参数title必填statusdescriptionamount_minorproduct_typepricing_model必填interval_countresponse_formatbilling_intervalminimum_amount_minor

jami_update_product

Update product

Change a product's title, description, status or price amount. The pricing model itself is locked. Args: product_id (p_… token or id), and any of: title, description, status ('published' | 'draft' | 'archived' — archived stops new sales), amount_minor (fixed price), minimum_amount_minor (flexible floor).

输入参数titlestatusproduct_id必填descriptionamount_minorresponse_formatminimum_amount_minor

jami_get_product

Get product

Fetch one product by its p_… token or id: price, status, its public page url, and for a subscription plan its billing interval. Args: - product_id: p_… token or 24-character id. Returns: { product_id, token, url, title, status, pricing_model, product_type, billing_interval, interval_count, price }.

输入参数product_id必填response_format

jami_list_products

List products

List the organization's products, newest first. Args: status ('published' | 'draft' | 'archived', optional), page (default 1), limit 1–100 (default 20). Returns: { products: [{ product_id, token, url, title, status, pricing_model, price }], total, page, limit, count, has_more, next_page }.

输入参数pagelimitstatusresponse_format

jami_list_subscriptions

List subscriptions

List the organization's wallet subscriptions, newest first. Args: - status: 'active' | 'past_due' | 'canceled' (optional). past_due = a renewal failed and is being retried. - product_id: optional p_… token or id of the plan. - customer_external_id: optional, your own user id passed when the link was created. - page (default 1), limit 1–100 (default 20). Returns: { subscriptions: [{ subscription_id, status, product_title, amount_minor, cadence, next_charge_at, customer_email, … }], total, page, limit, count, has_more, next_page }.

输入参数pagelimitstatusproduct_idresponse_formatcustomer_external_id

jami_get_subscription

Get subscription

Fetch one wallet subscription with its newest 24 charges (one per billing period) — use it to see why a renewal failed or when the next one runs. Args: - subscription_id: the sub_… id. Returns: { subscription: { subscription_id, status, amount_minor, cadence, current_period_end, next_charge_at, cancel_at_period_end, cancellation_reason, last_failure, … }, charges: [{ charge_id, sequence, status, amount_minor, failure, order_id, paid_at }] }. Failure codes: insufficient_funds, kyc_required, payer_unavailable, simulated_failure, internal_error.

输入参数response_formatsubscription_id必填

jami_cancel_subscription

Cancel subscription

Cancel a wallet subscription so the payer is not charged again. Confirm with the user first — a cancel the payer didn't ask for loses a customer, and there are no refunds. Args: - subscription_id: the sub_… id. - at_period_end: default true — no more charges, and it ends at current_period_end (the payer keeps what they paid for). You can undo this kind of cancel before then with jami_resume_subscription. false ends it immediately (a past_due subscription always ends immediately). Returns the updated subscription: { subscription_id, status, cancel_at_period_end, ended_at, cancellation_reason, … }. Idempotent: cancelling twice is harmless.

输入参数at_period_endresponse_formatsubscription_id必填

jami_resume_subscription

Resume subscription

Undo a cancel that YOU scheduled (jami_cancel_subscription with at_period_end true), so the subscription keeps renewing at next_charge_at. Only works on a cancel the organization scheduled and while the subscription hasn't ended yet. A cancel the customer made can't be undone by the organization (the payer has to subscribe again), and an ended subscription can't be resumed — both return 409 not_allowed. Args: - subscription_id: the sub_… id. Returns the updated subscription: { subscription_id, status, cancel_at_period_end: false, next_charge_at, … }. Idempotent: resuming an already-renewing subscription changes nothing.

输入参数response_formatsubscription_id必填

jami_simulate_subscription_renewal

Simulate subscription renewal

Sandbox only: run a subscription's next renewal right now, succeeding or failing, to test subscription.renewed / subscription.payment_failed / subscription.canceled handling without waiting a billing period. No money moves. Refused with a live (jamidev_live_) token — live renewals only run on schedule. Args: - subscription_id: the sub_… id of a sandbox subscription. - outcome: 'success' (default) or 'failure'. Failures follow the real dunning schedule: retries at 1, 3 and 7 days, then the subscription is canceled with cancellation_reason payment_failed. Returns: { result: 'renewed' | 'retry_scheduled' | 'canceled' | 'skipped' | 'deferred', subscription }.

输入参数outcomeresponse_formatsubscription_id必填

jami_list_orders

List orders

List the organization's orders, newest first, one page at a time. Args: - status: 'paid' | 'pending' | 'failed' (optional filter). - page: 1-based page (default 1). - limit: 1–100 per page (default 20). Returns: { orders: [{ order_id, status, amount_minor, amount, product: { id, title }, customer: { id, email, name }, paid_at, created_at, metadata }], total, page, limit, count, has_more, next_page }. To total revenue or find a specific buyer, page through with next_page until has_more is false. Amounts are integer santim (100 = 1.00 ETB); this is what buyers paid, before any withdrawal fee.

输入参数pagelimitstatusresponse_format

jami_get_balance

Get balance

Get the organization's withdrawable JamiDev earnings — the ceiling on what jami_create_withdrawal can pay out. Returns: { balance_minor, balance, eligible, currency }. eligible is false (and balance 0) when the org can't withdraw yet: it's a sandbox org, Developer Mode is off, or KYC isn't approved. The money itself sits in the owner's unified Jami wallet, so a withdrawal can still fail with 402 if that wallet was spent elsewhere.

输入参数response_format

jami_quote_withdrawal

Quote withdrawal

Preview what reaches the destination for a withdrawal — offline, no API call, works without a token. The JamiDev usage fee (7% unless Jami set a custom rate on the account) and any government tax are cut FROM the amount: you withdraw amount, the account receives net. Rounding is round-half-up per component. The server is authoritative; the real breakdown comes back on the withdrawal. Args: - amount_minor: gross santim (min 10000 = 100.00 ETB). - fee_rate / tax_rate: optional overrides (0–1) if you know the account's real rates. Returns the breakdown plus whether the amount auto-pays (≤ 10,000 ETB) or goes to manual review, and the per-org 100,000 ETB rolling-24h cap.

输入参数fee_ratetax_rateamount_minor必填response_format

jami_get_withdrawal

Get withdrawal

Fetch one withdrawal by id to track it from pending → processing → completed (or rejected). Args: - withdrawal_id: from jami_create_withdrawal or jami_list_withdrawals.

输入参数withdrawal_id必填response_format

jami_list_withdrawals

List withdrawals

List the organization's withdrawals, newest first. Args: - status: 'pending' | 'reviewing' | 'approved' | 'rejected' | 'processing' | 'completed' (optional). - page (default 1), limit 1–100 (default 20). Returns: { withdrawals: [...], total, page, limit, count, has_more, next_page }.

输入参数pagelimitstatusresponse_format

jami_verify_webhook

Verify webhook

Check whether a webhook delivery is genuine — the same timing-safe HMAC check and replay guard the SDK runs. Use it to debug "signature mismatch" errors in a handler. Args: - payload: the RAW request body exactly as received (not re-serialized JSON — whitespace matters). - signature: the X-JamiDev-Signature header, "t=<unix>,v1=<hex>". - secret: the subscription's webhook secret (defaults to JAMI_WEBHOOK_SECRET). - tolerance_seconds: max age of the timestamp (default 300). Raise it to check an old captured delivery. Returns: { valid, reason, event_type, event_id, livemode }. An invalid signature is a normal result (valid: false), not an error.

输入参数secretpayload必填signature必填tolerance_seconds

jami_create_test_webhook

Create test webhook

Build a correctly signed JamiDev webhook delivery for testing a handler locally — no real event needed. Args: - type: 'order.created' | 'order.completed' | 'benefit.granted' | 'checkout.session.expired' | 'withdrawal.paid' | 'withdrawal.failed' | 'subscription.created' | 'subscription.renewed' | 'subscription.payment_failed' | 'subscription.updated' | 'subscription.canceled'. - secret: signing secret (defaults to JAMI_WEBHOOK_SECRET; any string works for a local test). - data: optional fields to merge over the realistic sample payload. - livemode: default false (sandbox). - target_url: optional. When set, the delivery is POSTed there (e.g. "http://localhost:3000/webhooks/jami") and the response status is reported. Returns: { payload, signature, headers, curl } — the exact body and X-JamiDev-Signature to send, plus delivery_status when target_url is set. Handlers must verify the RAW body and reply 2xx within 10 seconds.

输入参数datatype必填secretlivemodetarget_url

jami_search_docs

Search Jami docs

Search the official JamiDev developer docs (https://dev.jami.bio/docs) — the source of truth for integrating Jami payments. Works without an API token. Use this before writing integration code: SDK usage, REST endpoints, webhooks and signature checks, sandbox magic numbers, Sign in with Jami (OAuth/OIDC), the wallet API, the browser storefront, fees and limits. Args: - query: what you need, e.g. "verify webhook signature express", "pay what you want amount", "withdrawal fee". - limit: 1–10 results (default 5). Returns: { results: [{ title, path, url, snippet }] }. Read a full page with jami_read_doc(path).

输入参数limitquery必填

jami_read_doc

Read Jami doc page

Read one JamiDev docs page as markdown, with every code sample intact. Args: - path: a docs path from jami_search_docs, e.g. "/docs/webhooks", "api/checkout", or a full URL. Use "/docs" for the overview. - offset: character offset for long pages (default 0); the result says where to continue. Returns: { title, url, markdown, offset, next_offset? }.

输入参数path必填offset

版本历史

1
  1. v0.3.0最新Oct 25, 2026

评分

已满足适用检查项的 90%

该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。

  • 近 30 天内可连通25 分
  • 已声明认证方式10 分

    请在注册中心条目中声明凭据请求头,或保持端点能够响应探测,以便判定其认证方式。

  • 已列出工具必需15 分
  • 描述必需15 分
  • 图标10 分
  • 文档15 分
  • 许可证10 分