A read-only Model Context Protocol server that lets an AI agent platform answer questions from one Poof organization's books — financial summaries, P&L, job profitability, receivables, payables, transactions, and month-end close status.
https://mcp.poofai.com/mcpPOST only; GET and DELETE return 405. Responses are JSON (no SSE stream). No session IDs — every request is independent, so it works behind load balancers without affinity.Content-Type: application/json and Accept: application/json, text/event-stream.@modelcontextprotocol/sdk 1.30 server, TypeScript.Every request to /mcp carries a bearer token. A request without one gets 401 with WWW-Authenticate: Bearer resource_metadata="…/.well-known/oauth-protected-resource/mcp", which is where an MCP client starts.
The person connecting signs in to Poof the normal way (password or Google, then their two-step code if they use one), sees which company and what the AI will read, and chooses Allow or Deny. The connection acts as that person in that company, and their role decides which tools appear (an office manager's AI sees receivables and invoices but no P&L). Removing the person from the company, or Disconnect in Settings → Chat apps → AI assistants, ends access on the next call.
/.well-known/oauth-protected-resource/mcp (RFC 9728)/.well-known/oauth-authorization-server (RFC 8414)POST /mcp/oauth/register, dynamic client registration (RFC 7591). Public clients only (token_endpoint_auth_method: none). Redirect URIs must be https://, or http://localhost / 127.0.0.1 (any port).GET /mcp/oauth/authorize, authorization code with PKCE S256 (required). The resource parameter, if sent, must be this server's /mcp URL.POST /mcp/oauth/token (form or JSON): authorization_code and refresh_token grants.POST /mcp/oauth/revoke (RFC 7009) ends the connection.mcp:readpoof, with no secret, when its return address is that app's own site (claude.ai, chatgpt.com, grok.com).Poof can issue an agent key per organization, per bot, on request. Keys look like agk_<accountId>_<secret>, are shown once, are bcrypt-hashed at rest, carry a scope list (below) and are revoked instantly. Send it as Authorization: Bearer agk_….
Nineteen read-only tools. Every tool is annotated readOnlyHint: true, destructiveHint: false, openWorldHint: false and carries a title. Each declares the scope an agent key must hold (a key with the blanket mcp:read sees all of them); for a signed-in person, their role decides. Dates are YYYY-MM-DD; periods resolve against today.
| Tool | Scope | What it returns |
|---|---|---|
get_financial_summary | mcp:financials.read | One-call overview for a period: revenue, COGS, gross profit, operating expenses, net income, plus open/overdue A/R and unpaid A/P. The right first call for "how is the business doing". |
get_profit_and_loss | mcp:financials.read | Full income statement between two dates, every revenue/COGS/expense account, accrual or cash basis. |
get_job_profitability | mcp:financials.read | Per-job P&L for trade contractors: revenue, COGS split into labor / subcontractors / materials, gross profit and margin, plus an Unassigned bucket. |
search_transactions | mcp:financials.read | Bank/book transactions by text, category, review status, date range and amount. status=FOR_REVIEW lists what is waiting for a person. |
get_ar_aging | mcp:ar.read | Receivables aging as of a date, bucketed current / 1–30 / 31–60 / 61–90 / 90+ by customer, with underlying invoices. |
get_open_invoices | mcp:ar.read | Itemized unpaid invoices: number, customer, total, amount due, due date, days overdue, status, with totals. |
get_ap_aging | mcp:ap.read | Payables aging by vendor in the same buckets. |
get_open_bills | mcp:ap.read | Itemized unpaid bills with due dates and amounts. |
get_ap_cash_plan | mcp:ap.read | The most recent 13-week cash plan: weekly beginning cash, inflows, required and discretionary payments, ending cash and reserve breaches, plus a suggested action per bill (pay now / this week / scheduled / defer). |
get_close_status | mcp:close.read | Month-end close tie-out results for a period: each check with pass / warn / fail / skipped, severity (blocker vs warning) and any dollar delta — GL balance, A/R and A/P subledger ties, bank reconciliation, accrual roll-forward. |
get_accrual_schedules | mcp:accruals.read | Accrual subledger roll-forward for a period: each schedule (accrued liability, customer deposit, deferred revenue, reserve) with opening, accrued, released and closing balances, the matching GL balance, and whether they tie. |
get_report | mcp:financials.read | Business reports with the numbers Poof's screens show: KPIs (close rate, average ticket, top techs, callbacks, members), revenue by service and more. |
get_purchase_orders | mcp:ap.read | Purchase orders to suppliers and what is on order, for a job, one PO, or all open POs. |
get_fixed_assets | mcp:assets.read | Every van, truck, tool and piece of equipment: class, in-service date, cost, depreciation so far and book value. |
get_depreciation | mcp:assets.read | A month's depreciation by asset, whether it is posted, and the year so far. |
get_loans | mcp:assets.read | Each loan: lender, balance owed, rate, payment, payments left, next due and interest paid this year. |
list_proposals | mcp:proposals.read | Proposals waiting for a person in the Approvals inbox: id, tool, summary, status, created. Read only. |
get_proposal | mcp:proposals.read | One proposal with its items, evidence and decision so far. Read only. |
request_approval_link | mcp:proposals.read | A link a person can open to decide a proposal in Poof. The link decides nothing by itself; the person does, signed in, with their own permission. |
Tool results are JSON in a single text content block. Each tool's full input schema is served by tools/list.
mcp:read — blanket: every tool above.mcp:financials.read, mcp:ar.read, mcp:ap.read, mcp:close.read, mcp:accruals.read, mcp:assets.read, mcp:proposals.read — one slice each, for agent keys. A key holding only mcp:ap.read reaches the endpoint but tools/list returns only the A/P tools; other tools are not registered for that key at all, so a call to one fails as an unknown tool.Scopes are set when the key is issued. One key per bot per organization is the intended shape.
Everything returned through this connector passes Poof's egress policy for third-party agent platforms, which cannot be configured below enforce:
C-12, V-3) that stay consistent across calls, so an agent can still reason about "the customer who is 60 days late" without receiving the name. Poof's own app shows the real names to the signed-in user.RateLimit-* headers are returned.curl -X POST https://mcp.poofai.com/mcp \
-H "Authorization: Bearer poof_at_…" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call",
"params":{"name":"get_financial_summary","arguments":{"period":"last_month"}}}'
| HTTP | Meaning |
|---|---|
| 401 | Missing, expired, disconnected or unknown token. The WWW-Authenticate header points to the sign-in metadata. |
| 403 | Key holds no mcp:* scope. |
| 405 | Non-POST request to /mcp. |
| 429 | Rate limit exceeded. |
200 + isError: true | Tool-level failure (a report could not be produced, identity protection unavailable). The message is in the content block; invalid arguments and unknown tools come back as JSON-RPC errors instead. |