Agent and MCP

The Agent page is a chat with an AI agent that can look up your catalog, accounts and usage, and change plans, entitlements, add-ons and incentives. Every change waits for your approval. The MCP server page connects outside assistants to the same app.

Turn on the agent

The agent runs in the dashboard, so the key goes on the dashboard (offer-app), not the API:

offer-app/.env.local
ANTHROPIC_API_KEY=sk-ant-...
# optional, see "Signed approvals" below
AGENT_APPROVAL_SECRET=...

Restart the dev server after adding it. Without the key, Agent (/apps/[appId]/agent) shows The agent needs an Anthropic API key, and the chat endpoint POST /api/agent answers 503. On Render, set it on the dashboard service. See Environment variables.

The agent works the same against the hosted API and the mock. Its tools go through the same gateway as the rest of the dashboard, so its answers match what the pages show. You need the same access as for the app itself: signed in, with the app in your active workspace.

The agent is off for the shared demo login. Agent is left out of its nav and Quick actions, /apps/[appId]/agent redirects to the app's Overview, and POST /api/agent answers 403. Sign in as the admin to use it.

Chats

New chat starts a conversation. An empty chat suggests a few prompts, such as “Which accounts are closest to their limits?” and “Raise the Pro plan's AI credits by 50%”. Past chats are listed on the left (behind Chats on narrow screens), where you can rename or delete them. The open chat is in the URL as ?thread=.

Chats are stored by the Offer API under /apps/:appId/agent/threads, a route only the admin key can call. Each chat belongs to the person who started it, and nobody else in the workspace can open it. A new chat gets a short title generated from its first message.

Model
claude-sonnet-5-5 with summarized thinking shown in the chat, up to 10 tool steps per reply. Titles use claude-haiku-4-5-20251001.
Source
offer-app/src/app/api/agent/route.ts (the endpoint and instructions), src/server/agent/tools.ts (the tools) and src/components/agent (the UI).

What it can look up

Read tools run straight away, and the chat shows each one as a collapsed step.

ToolWhat it returns
getAppOverviewThe app's name and how many plans, entitlements, add-ons, incentives and accounts it has.
getCatalogAll plans, entitlements, add-ons or incentives, with limits and pricing.
showCatalogThe same records, shown to you as cards in the chat. The agent uses it whenever you ask to see or compare records.
searchAccountsAccounts by name or ID, optionally filtered by plan or incentive, 20 per page.
getAccountOne account plus its resolved access: every entitlement, its limit after add-ons and incentives, and current usage.
findAccountsNearLimitsAccounts at or above a share of their limits (70% by default), highest first. It scans up to 500 accounts per call.
getUsageUsage totals per entitlement for the app, or for one account, over a period.
getTopAccountsAccounts ranked by usage, optionally for one entitlement.

What it can change

Twelve write tools cover the catalog:

Plans
createPlan, updatePlan and deletePlan. A plan can only be deleted when no accounts are on it.
Entitlements
createEntitlement, updateEntitlement (name and description) and deleteEntitlement, which removes it from every plan and incentive first.
Add-ons
createAddon, updateAddon and deleteAddon, which removes it from every plan and incentive first.
Incentives
createIncentive, updateIncentive and deleteIncentive, which removes it from every account that has it first.

The agent can't change accounts, offers, cancel flows, keys or webhooks. For account changes it points you to the Accounts page.

Approving changes

A write tool never runs on its own. When the agent calls one, the chat shows a change card with what will change, previewed against the live catalog, and two buttons: Decline and Approve. For a delete, the card also lists what else is affected, such as accounts on a plan. The card's badge reads Needs approval while it waits, then Applying, then Applied, Declined or Failed. Once applied, the card shows what actually changed.

  • Only the latest message's cards can still be answered. Older unanswered ones show Not applied.
  • If you decline, the agent doesn't retry the same change. It asks what you want instead.
  • The approval card is the confirmation, so the agent doesn't ask “shall I go ahead?” in text first.

Signed approvals

The browser sends the whole conversation back with each message, including your approvals. To stop a client from forging one, the server signs every approval request with an HMAC key when it issues it, and checks the signature before a write runs.

The key comes from AGENT_APPROVAL_SECRET. When that isn't set, it is derived from BETTER_AUTH_SECRET, then OFFER_API_ADMIN_KEY, then the mock's admin key.

Running more than one dashboard instance? Set the same AGENT_APPROVAL_SECRET on every instance, so an approval signed by one instance verifies on another.

The MCP server page

The agent lives in the dashboard. To work with an app from an AI assistant or coding agent you already use, connect it to the app's MCP server instead. The server runs in the Offer API at <API URL>/apps/<appId>/mcp and has a tool for each endpoint in the API reference.

Developers → MCP server (/apps/[appId]/developers/mcp) is where you manage it:

  • The Enabled switch and the server URL to copy.
  • Connect: setup steps for each client, with OAuth or the secret key.
  • Tools: the app's access level (Read only, Read & write or Full access) and a switch per tool.
  • Connections: who connected which client, at what level, with a button to cut a connection off.
  • Activity: every tool call with its arguments and result, kept for 30 days.

Unlike the agent, an MCP client doesn't show you an approval card for each change. The access level decides what it can do, and most clients ask before running a tool that deletes. Everything about the server, from connecting each client to the OAuth flow, is in MCP server. How AI is used across the product is in AI features.