@marginfront/mcp
v1.4.3
Published
MCP server for MarginFront. Lets AI agents (Claude, Cursor, Copilot) record usage events, query revenue / cost / MRR analytics, manage customers + subscriptions + pricing plans, browse the supported-services catalog, and check invoices via plain English.
Downloads
1,308
Maintainers
Readme
@marginfront/mcp
Official Model Context Protocol server for MarginFront. Lets you ask Claude, ChatGPT, Gemini, or any other MCP-compatible AI client to operate on your MarginFront account in plain English. Look up customers, query revenue, run backfills, fix unrecognized models, and trigger admin actions without writing code.
Full Documentation · Tools Reference · LLM-friendly docs (for agents) · MarginFront Docs
What this is for
MCP is the operator interface for human-to-LLM ops on a MarginFront account. Use it when:
- You want to ask an AI to backfill historical events from a CSV or SQL dump.
- You're debugging in the dashboard and want to ask Claude to look up a specific customer's revenue or fix unrecognized models in bulk.
- You're a non-developer customer-success or finance person who wants to query MarginFront without learning the API.
MCP is not how you instrument your product. For per-event tracking from your own production code, use the @marginfront/sdk package or call the REST API directly. SDK and API calls are typed, fast, and meant to run inline with your product logic. MCP runs through an LLM, which is great for ad-hoc work and terrible for per-request hot paths.
What you get
30 tools that any MCP-compatible AI client can call directly. Organized by purpose:
Connection
verify: confirm the API key works and see which organization it belongs to
Usage recording
record_usage/record_usage_batch: log what your customers are using (tokens, quantity, metadata)
Customer lookup
list_customers/get_customer/create_customer: manage your customer list
Invoices
list_invoices/get_invoice: read finalized bills and line items
Events
list_events: browse recorded usage events with filters (pass customerExternalId to scope by your own customer ID)
Analytics (canonical)
get_usage_analytics: aggregated usage totals plus margin (pass customerExternalId to scope by your own customer ID)get_customer_revenue: canonical revenue, cost, margin, and marginPercent for a single customer over a windowget_cost_metrics: canonical cost totals plus breakdown by agent, customer, signal, day, plan, and modelget_mrr: canonical MRR (canonical, run-rate, committed, or all three)
Subscriptions
list_subscriptions/create_subscription: track which customer is on which pricing plan
Customer portal
create_portal_session/get_portal_session/list_portal_sessions/revoke_portal_session: mint, inspect, list, and revoke single-use 1-hour portal links so customers can view their plan, invoices, and usage without exposing your API key
Pricing setup
create_pricing_plan/list_pricing_plans/get_pricing_plan: manage pricing planscreate_pricing_strategy/list_pricing_strategies: add pricing rules (usage, recurring, credit pool, etc.) to planslink_plan_to_agent: connect a plan to an agent before subscribing customers
Data repair
get_needs_attention/map_model: find and resolve events with unrecognized modelsget_missing_volume/fill_volume: find and resolve events with missing token or quantity counts
Catalog discovery
list_catalog_services: browse the globalservice_pricingcatalog so the AI can look up canonical model + provider names before firing usage events (avoids the "guess and check until cost resolves" cycle)
The signal-naming rule for AI agents
When you use record_usage via this MCP server, the signal_name you pick becomes the line item your user's customer reads on their invoice. Pick it deliberately:
- Fire ONE call per business outcome. A 50-page report that just finished is ONE
record_usagecall withquantity: 50, not 50 calls withquantity: 1. Looping multiplies the bill. - Match the signal name to how the user wants to bill. Before creating a signal on a user's behalf, ask them what unit goes on the invoice. "Per page" →
signal_name: "pages". "Per report" →signal_name: "reports". "Per minute" →signal_name: "minutes". - Don't invent internal-sounding names. Bad:
llm_call,gpt-4o-call,api-requests. Good:messages,reports-generated,pages,sms-sent,minutes. quantityis the count of that billing unit for this one outcome. A 50-page report fired aspageshasquantity: 50. Fired asreports,quantity: 1. Same underlying LLM call, different invoice.
Full mental model (including a three-way comparison of the same LLM call billed three different ways): Choosing your signal name and quantity.
Installation
The MCP server runs as a Node.js process over stdio. You don't npm install it into your app; you configure your MCP client (Claude Desktop, Claude Code, etc.) to spawn it.
Claude Desktop
Edit ~/Library/Application Support/Claude/claude_desktop_config.json on macOS (or the equivalent on Windows/Linux):
{
"mcpServers": {
"marginfront": {
"command": "npx",
"args": ["-y", "@marginfront/mcp"],
"env": {
"MF_API_SECRET_KEY": "mf_sk_your_secret_key_here"
}
}
}
}Restart Claude Desktop. The MarginFront tools appear automatically.
Claude Code
Add to your project's .mcp.json:
{
"mcpServers": {
"marginfront": {
"command": "npx",
"args": ["-y", "@marginfront/mcp"],
"env": {
"MF_API_SECRET_KEY": "${MF_API_SECRET_KEY}",
"MF_API_BASE_URL": "${MF_API_BASE_URL:-https://api.marginfront.com/v1}"
}
}
}
}Full setup walkthroughs (including screenshots) are in the MCP setup docs.
Environment variables
| Name | Required? | Purpose |
| ------------------- | --------- | ------------------------------------------------------------------------------------------------------------ |
| MF_API_SECRET_KEY | yes | Your secret API key (mf_sk_*). Get one from the MarginFront Developer Zone. |
| MF_API_BASE_URL | no | Override the API endpoint. Defaults to https://api.marginfront.com/v1. Useful for testing against staging. |
Example: backfilling historical events
The most common reason to fire events through MCP is a one-time backfill. You hand the AI a list of past activity, it records each event for you, and you can move on.
"I have 47 prospect reports my agent generated last week that never got recorded. Each one used Claude Opus with around 4000 input tokens and 1500 output tokens. The customer was
acme-001, the agent wasoutreach-agent, the signal isprospect_reports, and quantity is 1 per report. Record them all."
The assistant calls record_usage_batch and returns a confirmation:
Processed 47 event(s): 47 succeeded, 0 failed.If any model isn't in the pricing catalog yet, those events are still saved with cost: null and flagged NEEDS_COST_BACKFILL. The assistant can then call map_model to fix it, and every flagged past event with that model gets a real cost retroactively.
For per-event tracking from your production code, use the SDK instead. MCP fires events through an LLM, which is fine for ad-hoc batches and terrible for the hot path.
Example: AI agent recording multi-service usage
When one outcome uses several services (e.g. a cold-outreach report that searches the web, calls an LLM, and sends an email), MCP records it as ONE event with a services array. The customer is billed per signal; MarginFront tracks per-service cost under the hood. Same backfill pattern as above:
"Record one prospect report for
acme-001onoutreach-agent, signalprospect_reports, quantity 1. The services were 12 Exa search calls, Claude Opus 4 with 4200 input plus 1800 output tokens, and 3 SendGrid emails."
The assistant calls record_usage with the multi-service shape and gets one confirmation:
Processed 1 event(s): 1 succeeded, 0 failed.
Successful:
- acme-001 / prospect_reports: $0.142 across 3 services (event: 5de8fbf1-...)Per-service cost lines live under the parent event for margin reporting. The customer's invoice shows one line: prospect_reports x 1.
Example: asking about revenue and cost
The canonical analytics tools answer business questions in plain English:
"What revenue did we get from customer acme-001 in April?"
Calls get_customer_revenue (after resolving the UUID via list_customers) and returns { revenue, cost, margin, marginPercent } plus a per-strategy-type breakdown.
"What did we spend on model providers this month?"
Calls get_cost_metrics and returns total cost plus breakdowns by agent, customer, signal, day, plan, and model.
"What's our MRR right now?"
Calls get_mrr (defaults to MRR1, last complete calendar month). Ask for "all three" to get MRR1 (actually billed), MRR2 (run-rate trajectory), and MRR3 (contractual floor) in one response.
How it relates to the SDK
- Use
@marginfront/sdkto instrument your product. Every time your agent does work for a customer, your code fires a typed function call (client.usage.record({ ... })). This is the integration path. - Use
@marginfront/mcpfor human-to-LLM ops on the MarginFront account: backfills, batch fixes, queries, admin actions. This is the operator path.
Both wrap the same REST API. They are not interchangeable: the SDK is built for production hot paths, MCP runs through an LLM and is built for conversational use. Pick the one that matches the job.
Troubleshooting
"Missing MF_API_SECRET_KEY environment variable"
Your MCP client didn't pass the env var. Check the env block in your MCP config.
"Authentication failed. The API key is invalid or missing"
The key was passed but rejected. Verify the key in the Developer Zone and confirm you're using a mf_sk_* secret key (not a mf_pk_* publishable key).
"Could not reach the MarginFront API"
Your host is blocking the outbound connection to api.marginfront.com. If you're running a local API for development, set MF_API_BASE_URL=http://localhost:4000/v1.
Source & issues
This package is developed in the Lowcountry-AI/platform monorepo under packages/mcp/.
Report bugs or request features on the issues page.
License
MIT
