← Back home

Agent (MCP) documentation

Pay It Forward exposes a Model Context Protocol server so AI agents can read the public kindness grant ledger and, for operators, the membership and payment records behind it. Everything below is the live contract: tool names, arguments, example calls and the exact response shape.

Connection

FieldValueNotes
Endpointhttps://sharepayitforward.com/mcpStreamable HTTP transport (POST).
ProtocolMCP / JSON-RPC 2.0Standard tools/list and tools/call methods.
Server namepay-it-forward-hubVersion 0.1.0.
AuthOAuth 2.1 (bearer)Discovery at /.well-known/oauth-protected-resource. Every tool requires a signed-in account.
Accept headerapplication/json, text/event-streamRequired by the MCP spec; requests without it are rejected with 406.

Most clients (Claude, Cursor, Lovable, VS Code) only need the URL — they run the OAuth flow for you:

{
  "mcpServers": {
    "pay-it-forward": {
      "url": "https://sharepayitforward.com/mcp"
    }
  }
}

Raw HTTP, if you already hold a token:

curl -X POST https://sharepayitforward.com/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

Access levels

Public tools work for any authenticated agent account. Operator tools additionally require the account to hold the admin role; anything else returns an error result rather than partial data. No tool writes data — every tool is read-only and safe to retry.

Tools

list_grants

Public to any signed-in agent

Published kindness grants — issued and planned — with recipient, amount, category and proof link.

Arguments (all optional)

NameTypeDescription
statusstringissued or planned.
categorystringExact category match.
searchstringCase-insensitive partial match on title, recipient or location.
limitintegerPage size 1–100. Default 25.
offsetintegerRows to skip. Default 0.

Response schema (structuredContent)

FieldTypeDescription
grants[].titlestringShort title of the grant.
grants[].recipientstringWho received it.
grants[].locationstring | nullCity or region, when published.
grants[].categorystring | nullGrant category.
grants[].amountCentsnumberGrant amount in cents.
grants[].statusstringissued or planned.
grants[].issuedAtstring | nullISO 8601 date paid, if issued.
grants[].proofUrlstring | nullPublic link to proof.
totalnumberTotal rows matching the filter.
limitnumberPage size actually applied.
offsetnumberOffset actually applied.
nextOffsetnumber | nullOffset for the next page, or null on the last page.

Example request

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "list_grants",
    "arguments": { "status": "issued", "limit": 2 }
  }
}

Example response (result)

{
  "content": [
    {
      "type": "text",
      "text": "Grants 1-2 of 14\nISSUED · $5.00 · Coffee for the night shift → Sacred Heart ER (Spokane, WA)\nISSUED · $5.00 · Art supplies → Ms. Rivera's class (Spokane, WA)"
    }
  ],
  "structuredContent": {
    "grants": [
      {
        "title": "Coffee for the night shift",
        "recipient": "Sacred Heart ER",
        "location": "Spokane, WA",
        "category": "gratitude",
        "amountCents": 500,
        "status": "issued",
        "issuedAt": "2026-08-14T00:00:00.000Z",
        "proofUrl": "https://sharepayitforward.com/grant-ledger"
      }
    ],
    "total": 14,
    "limit": 2,
    "offset": 0,
    "nextOffset": 2
  }
}

get_ripple_report

Operator only (admin account)

Month-to-date totals: gross received, card fees, platform fee, grant pool, grants issued and planned, and members joined.

Arguments (all optional)

NameTypeDescription
monthstringYYYY-MM. Defaults to the current month.

Response schema (structuredContent)

FieldTypeDescription
monthstringMonth reported, YYYY-MM.
grossCentsnumberTotal received in cents.
stripeFeeCentsnumberCard processing fees in cents.
platformFeeCentsnumberPlatform fee in cents.
poolCentsnumberAdded to the grant pool in cents.
grantsIssuedCentsnumberGrants already paid out in cents.
grantsPlannedCentsnumberApproved but unpaid grants in cents.
memberCountnumberMembers who joined in the month.

Example request

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "get_ripple_report",
    "arguments": { "month": "2026-08" }
  }
}

Example response (result)

{
  "content": [{ "type": "text", "text": "Ripple report for 2026-08\nMembers joined: 12\nGross received: $85.00 …" }],
  "structuredContent": {
    "month": "2026-08",
    "grossCents": 8500,
    "stripeFeeCents": 540,
    "platformFeeCents": 1200,
    "poolCents": 6760,
    "grantsIssuedCents": 2500,
    "grantsPlannedCents": 1500,
    "memberCount": 12
  }
}

list_members

Operator only (admin account)

Members with ripple code, join date, membership status and referrer.

Arguments (all optional)

NameTypeDescription
statusstringactive, pending, refunded or failed.
searchstringPartial match on email or ripple code.
referredBystringOnly members referred by this ripple code.
joinedFromstringYYYY-MM-DD, inclusive lower bound.
joinedTostringYYYY-MM-DD, inclusive upper bound.
limitintegerPage size 1–100. Default 20.
offsetintegerRows to skip. Default 0.

Response schema (structuredContent)

FieldTypeDescription
members[].rippleCodestringThe member's unique referral code.
members[].emailstring | nullMember email.
members[].joinedAtstringISO 8601 timestamp of joining.
members[].membershipStatusstringactive, pending, refunded or failed.
members[].referredBystring | nullRipple code of the referrer.
totalnumberTotal rows matching the filter.
limitnumberPage size actually applied.
offsetnumberOffset actually applied.
nextOffsetnumber | nullOffset for the next page, or null on the last page.

Example request

{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/call",
  "params": {
    "name": "list_members",
    "arguments": { "status": "active", "limit": 1 }
  }
}

Example response (result)

{
  "content": [{ "type": "text", "text": "Members 1-1 of 4\nx7viegaz · active · joined 2026-09-05 · referred by s4hhud93" }],
  "structuredContent": {
    "members": [
      {
        "rippleCode": "x7viegaz",
        "email": "member@example.com",
        "joinedAt": "2026-09-05T18:12:04.221Z",
        "membershipStatus": "active",
        "referredBy": "s4hhud93"
      }
    ],
    "total": 4,
    "limit": 1,
    "offset": 0,
    "nextOffset": 1
  }
}

list_payments

Operator only (admin account)

Payments with the reconciled split: gross, card fee, platform fee and grant pool share.

Arguments (all optional)

NameTypeDescription
statusstringsucceeded, pending, failed, refunded or disputed.
searchstringPartial match on payer email.
fromstringYYYY-MM-DD, inclusive lower bound.
tostringYYYY-MM-DD, inclusive upper bound.
limitintegerPage size 1–100. Default 20.
offsetintegerRows to skip. Default 0.

Response schema (structuredContent)

FieldTypeDescription
payments[].datestringISO 8601 timestamp of the payment.
payments[].statusstringsucceeded, pending, failed, refunded or disputed.
payments[].emailstring | nullPayer email.
payments[].grossCentsnumberAmount charged in cents.
payments[].stripeFeeCentsnumberCard processing fee in cents.
payments[].platformFeeCentsnumberPlatform fee in cents.
payments[].poolCentsnumberRouted to the grant pool in cents.
payments[].currencystringISO currency code, e.g. usd.
totalnumberTotal rows matching the filter.
limitnumberPage size actually applied.
offsetnumberOffset actually applied.
nextOffsetnumber | nullOffset for the next page, or null on the last page.

Example request

{
  "jsonrpc": "2.0",
  "id": 4,
  "method": "tools/call",
  "params": {
    "name": "list_payments",
    "arguments": { "status": "succeeded", "from": "2026-08-01", "limit": 1 }
  }
}

Example response (result)

{
  "content": [{ "type": "text", "text": "Payments 1-1 of 9\n2026-09-05 · succeeded · $5.00 gross → pool $3.55, platform $1.00, stripe $0.45" }],
  "structuredContent": {
    "payments": [
      {
        "date": "2026-09-05T18:12:04.221Z",
        "status": "succeeded",
        "email": "member@example.com",
        "grossCents": 500,
        "stripeFeeCents": 45,
        "platformFeeCents": 100,
        "poolCents": 355,
        "currency": "usd"
      }
    ],
    "total": 9,
    "limit": 1,
    "offset": 0,
    "nextOffset": 1
  }
}

Conventions

  • All money is integer cents; currency is USD unless a row says otherwise.
  • All timestamps are ISO 8601 in UTC.
  • Paginate with limit and offset; keep calling while nextOffset is not null.
  • Every result carries both a human-readable content[0].text summary and machine-readable structuredContent. Parse the latter.

Errors

Failures come back as a tool result with isError: true and a plain-text reason, not a transport error.

{
  "content": [{ "type": "text", "text": "Admin access required" }],
  "isError": true
}
MessageMeaningFix
Not authenticatedNo valid bearer token.Complete the OAuth flow and retry.
Admin access requiredAccount lacks the operator role.Use list_grants, or ask an operator for access.
month must look like 2026-08Bad month argument.Send YYYY-MM or omit it.
HTTP 406Missing Accept header.Send application/json, text/event-stream.

Building something with this? Tell us at the grant request page — agents that surface real grants are welcome.