Fuul MCP Server
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Fuul MCP Server show me my affiliate program performance"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
@fuul/mcp-server
Fuul Model Context Protocol server for managing affiliate programs, analytics, incentives, and payouts through MCP-compatible clients (Claude Code, Cursor, Claude Desktop).
Table of Contents
Related MCP server: syndicate-links-mcp
Quick Start
# 1. Install (choose one method below)
# 2. Authenticate once:
npx -y --package=@fuul/mcp-server@latest fuul-mcp login
# 3. Verify:
npx -y --package=@fuul/mcp-server@latest fuul-mcp whoamiInstallation Methods
1. Claude Code Plugin (Recommended)
The easiest way to use Fuul MCP with Claude Code. Adds the MCP server plus a skill that documents how to use Fuul tools.
Step 1: Install the plugin
/plugin marketplace add kuyen-labs/mcp_server
/plugin install fuul-mcp@fuul-mcpStep 2: Verify the .mcp.json format
Check the plugin's .mcp.json at:
~/.claude/plugins/cache/fuul-mcp/fuul-mcp/<version>/.mcp.jsonIt must use the --package= format in args:
{
"mcpServers": {
"fuul": {
"command": "npx",
"args": ["-y", "--package=@fuul/mcp-server@latest", "fuul-mcp-server"],
"env": {
"FUUL_API_BASE_URL": "${user_config.FUUL_API_BASE_URL}"
}
}
}
}If args shows ["-y", "@fuul/mcp-server@latest", "fuul-mcp-server"] (without --package=), update it to match the format above.
Step 3: Reload the plugin
/reload-pluginsStep 4: Authenticate (one-time)
Open a terminal and run:
npx -y --package=@fuul/mcp-server@latest fuul-mcp loginThis opens your browser for OAuth. Tokens are saved to ~/.fuul/tokens.json.
Step 5: Verify
npx -y --package=@fuul/mcp-server@latest fuul-mcp whoamiOptional: Use staging environment
Set FUUL_API_BASE_URL in the plugin's user settings:
Environment | URL |
Production (default) |
|
Staging |
|
2. Cursor IDE
Option A: Using npx (no clone required)
Authenticate first:
npx -y --package=@fuul/mcp-server@latest fuul-mcp login npx -y --package=@fuul/mcp-server@latest fuul-mcp whoamiConfigure MCP in Cursor:
Go to Settings → MCP or edit your
mcp.json:{ "mcpServers": { "fuul": { "command": "npx", "args": ["-y", "--package=@fuul/mcp-server@latest", "fuul-mcp-server"], "env": { "FUUL_API_BASE_URL": "https://api.fuul.xyz" } } } }
Option B: Using local clone
Clone and build:
git clone https://github.com/kuyen-labs/mcp_server.git cd mcp_server npm ci npm run buildAuthenticate:
npm run cli -- login npm run cli -- whoamiConfigure MCP in Cursor:
{ "mcpServers": { "fuul": { "command": "node", "args": ["C:\\path\\to\\mcp_server\\dist\\index.js"], "cwd": "C:\\path\\to\\mcp_server" } } }On macOS/Linux:
{ "mcpServers": { "fuul": { "command": "node", "args": ["/path/to/mcp_server/dist/index.js"], "cwd": "/path/to/mcp_server" } } }
3. npx (Any MCP Client)
Use the published npm package without cloning:
Command | Purpose |
| Browser OAuth; writes |
| Verify session ( |
| Clear tokens |
| Start stdio MCP server |
For MCP client configs (JSON), use the --package= format:
{
"command": "npx",
"args": ["-y", "--package=@fuul/mcp-server@latest", "fuul-mcp-server"]
}4. Local Development (Clone)
git clone https://github.com/kuyen-labs/mcp_server.git
cd mcp_server
npm ci
cp .env.example .env # Optional: edit for staging/custom settings
npm run buildRun CLI commands:
npm run cli -- login
npm run cli -- whoami
npm run cli -- logoutRun MCP server:
npm start # Production (uses dist/)
npm run dev # Development (uses tsx, watches src/)Debug with MCP Inspector:
npm run build
npx @modelcontextprotocol/inspector node dist/index.jsAuthentication
Authentication uses OAuth with the Fuul dashboard. Tokens are stored locally and shared by the CLI and MCP server.
Token location
OS | Path |
macOS/Linux |
|
Windows |
|
Login flow
npx -y --package=@fuul/mcp-server@latest fuul-mcp loginThis opens your default browser to the Fuul OAuth page. After authorizing, tokens are saved automatically.
Verify session
npx -y --package=@fuul/mcp-server@latest fuul-mcp whoamiClear tokens
npx -y --package=@fuul/mcp-server@latest fuul-mcp logoutConfiguration
Environment variables are read from process.env and, when present, a .env file in the current working directory.
Variable | Default | Description |
|
| API origin (no trailing slash). Use |
|
| OAuth client ID |
|
| OAuth callback URL |
|
| Per-tool timeout in milliseconds |
| (unset) | Project API key (Bearer) for public API tools: managed affiliates and Events |
|
| Set to |
Example .env file
FUUL_API_BASE_URL=https://api.stg.fuul.xyz
FUUL_MCP_DEBUG=1Note: The
.envfile is for local development only and is not included in the npm package. Tokens are stored in~/.fuul/tokens.json, not in.env.
MCP Tool Reference
Tool Categories
Category | Tools |
Health |
|
Metadata (cached) |
|
Projects |
|
Incentives |
|
Affiliate Analytics |
|
Managed Affiliates (project API key) |
|
Events (project API key) |
|
Referrer / referral codes ( |
|
Payouts (Read) |
|
Payouts (Write) |
|
Tiers |
|
Audiences |
|
Triggers |
|
Payout Terms |
|
Tool Examples
All tools receive JSON arguments. Use real UUIDs from your tenant.
Health checks
// ping (no auth required)
{}
// whoami (requires login)
{}Metadata queries
// list_chains, list_trigger_types, list_payout_schemas
{}Project operations
// list_projects
{ "page": 1, "query": "acme" }
// get_project
{ "project_id": "550e8400-e29b-41d4-a716-446655440000" }Incentives
// list_incentives
{ "project_id": "<uuid>" }
// get_incentive
{ "project_id": "<uuid>", "conversion_id": "<uuid>" }
// get_trigger
{ "project_id": "<uuid>", "trigger_id": "<uuid>" }Agents (Claude Code): draft vs published trigger IDs — see plugin skill
plugins/fuul-mcp/skills/fuul/SKILL.md§ Draft vs published.
Affiliate analytics
// get_affiliate_portal_stats (single affiliate)
{
"project_id": "<uuid>",
"user_identifier": "evm:0x1234..."
}
// get_project_affiliate_total_stats (project totals)
{
"project_id": "<uuid>",
"dateRange": "30d"
}
// get_project_affiliates_breakdown (grouped breakdown)
{
"project_id": "<uuid>",
"groupBy": "region",
"dateRange": "30d"
}groupBy options: audience, tier, region, status
dateRange options: 7d, 30d, 90d, MTD, QTD, custom, all
Events (project API key)
Requires FUUL_MCP_PROJECT_API_KEY or project_api_key on each call. Writes use dry_run then confirmed.
// check_event_status (ingestion only)
{
"user_identifier": "0x80Fe27F878d2d42BD8b387F3cA4b96CBDEc05326",
"user_identifier_type": "evm_address",
"event_name": "trade"
}
// check_event_status (full pipeline after send_event)
{
"verbose": true,
"dedup_id": "4bdabf2c-271a-4d66-afd0-a9f24119810a",
"event_name": "trade"
}
// send_event (dry_run preview)
{
"name": "trade",
"user_identifier": "0x80Fe27F878d2d42BD8b387F3cA4b96CBDEc05326",
"user_identifier_type": "evm_address",
"dedup_id": "4bdabf2c-271a-4d66-afd0-a9f24119810a",
"args": { "volume": 1000 },
"dry_run": true
}
// send_batch_events (confirmed)
{
"events": [
{
"name": "trade",
"user_identifier": "0x80Fe27F878d2d42BD8b387F3cA4b96CBDEc05326",
"user_identifier_type": "evm_address",
"dedup_id": "batch-id-1"
}
],
"confirmed": true
}Payout operations
// list_payouts_pending_approval
{ "project_id": "<uuid>", "page": 1, "page_size": 50 }
// list_rewards_payouts
{ "project_id": "<uuid>", "page": 1 }Write Operations (Two-Step Flow)
All mutation tools require a two-step process:
Preview: Call with
dry_run: true— validates and returns a preview without making changesConfirm: Call with
confirmed: true— executes the mutation
Example: Approve payouts
// Step 1: Preview
{
"project_id": "<uuid>",
"payout_ids": ["<uuid1>", "<uuid2>"],
"dry_run": true
}
// Step 2: Confirm (after user approval)
{
"project_id": "<uuid>",
"payout_ids": ["<uuid1>", "<uuid2>"],
"confirmed": true
}Write tools: approve_payouts, reject_payouts, update_project_tier, update_audience, update_trigger, update_payout_term
Troubleshooting
MCP server fails to start
Symptom: Error like Cannot find package '@fuul/mcp-server' or binary not found.
Solution: Ensure you use the --package= format in args:
{
"args": ["-y", "--package=@fuul/mcp-server@latest", "fuul-mcp-server"]
}Wrong:
{
"args": ["-y", "@fuul/mcp-server@latest", "fuul-mcp-server"]
}401 Unauthorized errors
Symptom: API tools return 401 or whoami fails.
Solution:
Run login again:
npx -y --package=@fuul/mcp-server@latest fuul-mcp loginVerify tokens exist:
macOS/Linux:
~/.fuul/tokens.jsonWindows:
%USERPROFILE%\.fuul\tokens.json
Verify session:
npx -y --package=@fuul/mcp-server@latest fuul-mcp whoami
Rate limiting (HTTP 429)
Symptom: Tools return 429 errors.
Solution: Wait for the Retry-After header duration, then retry. The MCP server handles this automatically in most cases.
Environment not loading
Symptom: Staging URL not being used despite .env file.
Solution:
Ensure
cwdin your MCP config points to the directory containing.envOr pass environment directly in the config:
{ "env": { "FUUL_API_BASE_URL": "https://api.stg.fuul.xyz" } }
Windows path issues
Symptom: Paths not resolving correctly on Windows.
Solution: Use double backslashes or forward slashes:
{
"args": ["C:\\Users\\me\\mcp_server\\dist\\index.js"]
}Or:
{
"args": ["C:/Users/me/mcp_server/dist/index.js"]
}Repository Layout
mcp_server/
├── .claude-plugin/ # Claude Code marketplace manifest
│ └── marketplace.json
├── .github/
│ ├── workflows/ # CI and release workflows
│ │ ├── ci.yml
│ │ └── release.yml
│ └── pull_request_template.md
├── docs/ # Documentation
│ ├── README.md # Docs index
│ ├── AGENTS.md # Tool ↔ HTTP mapping
│ └── mcp-phase2/
│ ├── CONSUMER.md # API expectations
│ └── tool-prompts.md # Sample prompts for evals
├── plugins/
│ └── fuul-mcp/ # Claude Code plugin
│ ├── .claude-plugin/
│ │ └── plugin.json
│ ├── .mcp.json # MCP server config
│ └── skills/
│ └── fuul/
│ └── SKILL.md # Tool usage instructions
├── src/ # TypeScript source
│ ├── affiliate-portal/ # Affiliate analytics
│ ├── agent/ # Write confirmation logic
│ ├── auth/ # OAuth and tokens
│ ├── config/ # Environment config
│ ├── http/ # HTTP client
│ ├── metadata/ # Chains, triggers, schemas
│ ├── payouts/ # Payout operations
│ ├── tools/ # MCP tool definitions
│ ├── triggers/ # Trigger operations
│ ├── util/ # Utilities
│ ├── cli.ts # CLI entry point
│ └── index.ts # MCP server entry point
├── dist/ # Compiled output (gitignored)
├── .env.example # Environment template
├── CHANGELOG.md # Release notes
├── package.json
├── release.config.cjs # semantic-release config
├── tsconfig.json
└── vitest.config.tsScripts Reference
Script | Description |
| Compile TypeScript → |
| Run MCP server ( |
| Run MCP server with hot reload ( |
| Run OAuth CLI ( |
| ESLint on |
| Auto-fix lint issues |
| Run tests with Vitest |
| Run tests in CI mode |
| Format code with Prettier |
Documentation
Resource | Description |
Documentation index | |
Tool ↔ HTTP endpoint mapping | |
Staging/production URLs, API expectations | |
Sample prompts for testing and evals | |
Release notes |
CI and Releases
Continuous Integration
On each push/PR to main, master, beta, or alpha, GitHub Actions runs:
lint — ESLint checks
test — Vitest test suite
build — TypeScript compilation
Publishing
Releases are automated via semantic-release and npm Trusted Publishing (OIDC):
Use Conventional Commits:
feat:,fix:,BREAKING CHANGE:Merge to
main(orbeta/alphafor pre-releases)GitHub Actions runs
semantic-release, which:Determines the next version from commits
Updates
package.jsonandCHANGELOG.mdPublishes to npm
Creates a GitHub release
No manual npm publish or GitHub Release creation needed.
Requirements
Node.js 18 or higher
A running fuul-server (staging or production) with Agent OAuth configured
License
MIT — see package.json
Available Tools
39 toolsapprove_payoutsA
Approves payouts: PATCH /api/v1/projects/:projectId/payouts/approve. Body: payout_ids OR date filters (server validates mutual exclusivity). dry_run then confirmed. Example dry_run: {"project_id":"","payout_ids":[""],"dry_run":true}.
Inputs: project_id (required), dry_run, confirmed, payout_ids, +4 more.
check_event_statusA
Check event ingestion and downstream pipeline. Default (verbose omitted or false): GET /api/v1/events/status with user_identifier, user_identifier_type, event_name → {"created":true|false}. verbose=true: GET /api/v1/events/pipeline — returns event, trigger_executions (status, status_details), attributions, payouts, movements. Requires event_id OR dedup_id + event_name (same dedup_id/name as send_event). Poll every 2–5s after send_event until attributions/payouts appear. 404 → {"created":false}. Auth: project API key only (pass project_api_key or set env FUUL_MCP_PROJECT_API_KEY). Dashboard OAuth from fuul-mcp login is not accepted on these routes. Rate limit: 100 requests/minute. If the API returns HTTP 429, wait Retry-After seconds (if present) before retrying. Example status: {"user_identifier":"0x...","user_identifier_type":"evm_address","event_name":"trade"}. Example verbose: {"verbose":true,"dedup_id":"swap-123","event_name":"trade"}.
Inputs: project_api_key, verbose, user_identifier, user_identifier_type, +3 more.
create_incentiveA
Creates a draft incentive (conversion): POST /api/v1/projects/:projectId/incentives. Body: name, trigger_ids[] (draft or published trigger UUIDs — resolved to current draft), payout_terms[] (PayoutTermDto, min 1 each). REQUIRED: list_payout_schemas first — pick reward_types[].id (fixed-reward | variable-reward | proportional-pool | leaderboard) and use create_payload_example. Schemes on wire: pay-per-attribution (fixed/variable), pool, rank. type: point | onchain-currency. payee_type: affiliate | end-user | both. Fixed: calculation_strategy fixed, referrer_amount/referral_amount. Variable: calculation_strategy variable, trigger_amount_source, base_currency, *_amount_percentage. Pool: scheme pool, amount_source, pool_amount, pool_duration, pool_calculation_day_cron. Leaderboard: scheme rank, rank_scheme_config.ranks, pool window fields. MCP normalizes variable terms (referral_amount → referral_amount_percentage). dry_run then confirmed. Before executing (including dry_run), this tool refreshes project metadata (same as get_project) and resolves trigger_id / conversion_id / trigger_ids[] to the current draft UUIDs. If you pass a published_trigger_id from before a dashboard publish, it is remapped to the current draft_trigger_id for the same ref. Responses include _draft_id_resolution when an ID was remapped. Unknown stale UUIDs fail with an explicit error. On successful execution (not dry_run), the response includes _publish_metadata_reminder: publish project metadata from the dashboard (Project → Incentives or Triggers → Publish now). The MCP cannot publish for you.
Inputs: project_id (required), name (required), trigger_ids (required), payout_terms (required), +2 more.
create_project_affiliate_publicA
Creates a managed project affiliate: POST /api/v1/project-affiliates. Body matches server public DTO (user_identifier, user_identifier_type, optional alias, region, status, note, audiences, tier_protection, approve_project_tier_ids + reviewed_by_user_id when approving tiers). Auth: project API key only (pass project_api_key or set env FUUL_MCP_PROJECT_API_KEY). Dashboard OAuth from fuul-mcp login is not accepted on these routes. Use dry_run: true then confirmed: true like other write tools. Example dry_run: {"user_identifier":"0x...","user_identifier_type":"evm_address","dry_run":true}.
Inputs: user_identifier (required), user_identifier_type (required), dry_run, confirmed, +9 more.
create_triggerA
Creates a draft trigger: POST /api/v1/projects/:projectId/triggers. Body matches CreateTriggerDto (fuul-webapp triggersService.create / encodeByTriggerType). REQUIRED: call list_trigger_types first; use trigger_types[].id as trigger.type and follow create_payload_layout for that id. Layouts: (1) flat_dto — types custom/classic: put context_json_schema fields at trigger ROOT (signature, event_type, expressions, payable, end_user_identifier_property, contract_ids), NOT nested only in context. (2) context_only — token-holder, liquidity-pool-v2: fields only under trigger.context. (3) context_and_root_fields — most presets: fields under trigger.context plus end_user_identifier_property at root when needed. Use create_payload_example from list_trigger_types when present. Call list_chains for chain_id. dry_run then confirmed. Token-holder price reference (CRITICAL): context.volume_currency_expression prices held balance. Before create_trigger for token-holder / liquidity-pool-v2 / balancer / solana-token-holder / fogo-token-holder: call list_price_references for the chain. If token_address is in that list → volume_currency_expression = token_address. If NOT listed → ask user stablecoin vs variable-price and decimals (6/18), then set volume_currency_expression to a listed reference with matching decimals (e.g. 18-decimal stablecoin on Ethereum → DAI 0x6b175474e89094c44da98b954eedeac495271d0f). Using an unlisted token address causes HTTP 201 but broken volume at runtime. Token-holder example (known asset): {"name":"Hold DAI","description":"...","type":"token-holder","context":{"token_address":"0x6b175474e89094c44da98b954eedeac495271d0f","chain_id":1,"volume_currency_expression":"0x6b175474e89094c44da98b954eedeac495271d0f"}}. Token-holder example (unknown 18d stablecoin): token 0xa3931d71877C0E7a3148CB7Eb4463524FEc27fbD → volume_currency_expression 0x6b175474e89094c44da98b954eedeac495271d0f (DAI). Custom off-chain: {"name":"...","description":"...","type":"custom","signature":"event_name","event_type":"off-chain-event","end_user_identifier_property":"address","payable":true,...expressions at root}. On successful execution (not dry_run), the response includes _publish_metadata_reminder: publish project metadata from the dashboard (Project → Incentives or Triggers → Publish now). The MCP cannot publish for you.
Inputs: project_id (required), trigger (required), dry_run, confirmed.
delete_incentiveA
Deletes a draft incentive (conversion): DELETE /api/v1/projects/:projectId/incentives/:conversionId. Same API as delete_conversion. Use draft_conversion_id from list_incentives or get_project conversions[]. dry_run then confirmed. When replacing a trigger, use delete_conversion (step 2 of the replace flow) before delete_trigger. Before executing (including dry_run), this tool refreshes project metadata (same as get_project) and resolves trigger_id / conversion_id / trigger_ids[] to the current draft UUIDs. If you pass a published_trigger_id from before a dashboard publish, it is remapped to the current draft_trigger_id for the same ref. Responses include _draft_id_resolution when an ID was remapped. Unknown stale UUIDs fail with an explicit error. On successful execution (not dry_run), the response includes _publish_metadata_reminder: publish project metadata from the dashboard (Project → Incentives or Triggers → Publish now). The MCP cannot publish for you.
Inputs: project_id (required), conversion_id (required), dry_run, confirmed.
delete_triggerA
Deletes a draft trigger: DELETE /api/v1/projects/:projectId/triggers/:triggerId. Pass draft_trigger_id or a published_trigger_id (post-publish remap). Requires dry_run then confirmed. Never call without explicit user approval. Before delete: ensure no incentives still link this trigger (delete_incentive first). Replace-trigger flow (token/chain change; after telling the user update_trigger cannot change token_address/chain_id): (1) get_project or list_incentives — list every incentive/conversion whose triggers[] includes this draft_trigger_id; (2) delete_conversion for each draft_conversion_id (removes conversion + trigger links); (3) delete_trigger; (4) create_trigger with the new context; (5) create_incentive if the program must be recreated. Do not call delete_trigger until step 2 is done for all linked conversions. If delete still returns HTTP 422, report remaining links and stop — do not retry delete_trigger blindly. Before executing (including dry_run), this tool refreshes project metadata (same as get_project) and resolves trigger_id / conversion_id / trigger_ids[] to the current draft UUIDs. If you pass a published_trigger_id from before a dashboard publish, it is remapped to the current draft_trigger_id for the same ref. Responses include _draft_id_resolution when an ID was remapped. Unknown stale UUIDs fail with an explicit error. On successful execution (not dry_run), the response includes _publish_metadata_reminder: publish project metadata from the dashboard (Project → Incentives or Triggers → Publish now). The MCP cannot publish for you.
Inputs: project_id (required), trigger_id (required), dry_run, confirmed.
delete_user_referrerA
Remove admin-imported user_referrers row: DELETE /api/v1/user-referrers?user_identifier=&user_identifier_type=. Clears PUT/update_user_referrer assignments (including rows with referral_code_id set but no referral_code_uses). Does NOT touch referral_code_uses or actual_uses. If the user has referral code redemptions in this project, API returns 422 — use remove_user_from_referral_code instead. Maps 422 "User referrer relationship not found" to {already_removed:true} for idempotent retries. Requires service_role project API key. Auth: project API key only (pass project_api_key or set env FUUL_MCP_PROJECT_API_KEY). Dashboard OAuth from fuul-mcp login is not accepted on these routes. dry_run then confirmed. Example dry_run: {"user_identifier":"0xUser...","user_identifier_type":"evm_address","dry_run":true}.
Inputs: user_identifier (required), user_identifier_type (required), project_api_key, dry_run, +1 more.
get_affiliate_portal_statsB
Project affiliate stats for one user identifier: GET /api/v1/projects/:projectId/affiliate-portal/stats. Response includes active referred-user counts by multilevel depth (active_referred_users_r2–r4) scoped like volumes. Requires dashboard JWT (same as other project tools). Query params match the dashboard affiliate management UI. Example: {"project_id":"","user_identifier":"evm:0x..."}. If the API returns HTTP 429, wait Retry-After seconds (if present) before retrying.
Inputs: project_id (required), user_identifier (required), from, to, +3 more.
get_incentiveA
Gets one draft incentive with scoped triggers (same merge as list_incentives). conversion_id is the draft conversion UUID from incentives API. Use draft_trigger_id from triggers[] for PATCH update_trigger; published_trigger_id for live/prod comparisons (e.g. SQL on project.metadata_id). Example: {"project_id":"","conversion_id":""}.
Inputs: project_id (required), conversion_id (required).
get_projectA
Loads one project (draft + published trigger mapping). Calls GET /api/v1/projects/:projectId and GET /api/v1/projects/:projectId/customizations in parallel. Replaces triggers[] with scoped rows: ref, signature, draft_trigger_id, published_trigger_id, draft, published. Stable key across versions is ref (not UUID). After publish, draft and published rows get different UUIDs for the same ref. conversions[] are draft incentives with nested triggers merged the same way; published_conversion_id is null until a future published-incentives API. Example: {"project_id":"550e8400-e29b-41d4-a716-446655440000"}.
Inputs: project_id (required).
get_project_affiliate_publicA
Loads one managed project affiliate (full detail: tiers, protections, audiences, tax): GET /api/v1/project-affiliates/:projectAffiliateId. Auth: project API key only (pass project_api_key or set env FUUL_MCP_PROJECT_API_KEY). Dashboard OAuth from fuul-mcp login is not accepted on these routes. Example: {"project_affiliate_id":""} with key via env or project_api_key field.
Inputs: project_affiliate_id (required), project_api_key.
get_project_affiliates_breakdownA
Affiliate breakdown for a project (by audience, tier, region, or status): GET /api/v1/projects/:projectId/affiliate-portal/global-breakdown. groupBy is required (audience | tier | region | status). Optional sortBy, sortOrder, date filters, and dimension filters. Example: {"project_id":"","groupBy":"region","dateRange":"30d"}. If the API returns HTTP 429, wait Retry-After seconds (if present) before retrying.
Inputs: project_id (required), groupBy (required), dateRange, dateFrom, +7 more.
get_project_affiliate_total_statsA
Aggregated project-wide affiliate totals: GET /api/v1/projects/:projectId/affiliate-portal/total-stats. Optional filters: statuses, regions, audiences, tiers, dateRange, dateFrom, dateTo (see fuul-server GetTotalStatsDto). Example: {"project_id":""} or {"project_id":"","dateRange":"30d"}. If the API returns HTTP 429, wait Retry-After seconds (if present) before retrying.
Inputs: project_id (required), statuses, regions, audiences, +4 more.
get_triggerA
Gets one trigger row by UUID: GET /api/v1/projects/:projectId/triggers/:triggerId. Returns whichever row that UUID points to (draft or published copy). Does not resolve project.metadata_id. Prefer get_project or get_incentive triggers[] for draft_trigger_id vs published_trigger_id by ref. Example: {"project_id":"","trigger_id":""}.
Inputs: project_id (required), trigger_id (required).
get_user_referrerA
Read a user referrer from user_referrers: GET /api/v1/user/referrer?user_identifier=&user_identifier_type=. Returns referrer_identifier, referrer_code, referrer_codes, referrer_name, referrer_user_rebate_rate (null referrer fields when unassigned). Unlike GET /referral_codes/status, this reflects PUT /user-referrers assignments even without referral_code_uses. Auth: project API key only (pass project_api_key or set env FUUL_MCP_PROJECT_API_KEY). Dashboard OAuth from fuul-mcp login is not accepted on these routes. Example: {"user_identifier":"0xUser...","user_identifier_type":"evm_address"}.
Inputs: user_identifier (required), user_identifier_type (required), project_api_key.
list_chainsA
Lists supported blockchain chains from GET /public-api/v1/metadata/chains. Uses server metadata (not a hardcoded catalog); responses are cached with ETag/Cache-Control. Each chain includes snake_case fields such as chain_id, is_testnet, optional svm_network and webapp_capabilities, and can_be_used_for_payouts (boolean: true where Fuul reward/payout infra is deployed). Params: none (pass {}). Pagination: not exposed by this tool until the API adds cursor/limit.
Inputs: no named parameters.
list_incentivesA
Lists draft incentives with published trigger IDs merged by ref. Calls GET /api/v1/projects/:projectId/incentives and GET .../customizations. Each item: slug, draft_conversion_id, published_conversion_id (null for now), draft, published (null), triggers[] (scoped merge). Example: {"project_id":""}.
Inputs: project_id (required).
list_payout_schemasA
Lists payout schema metadata from GET /public-api/v1/metadata/payout-schemas (cached), enriched for create_incentive. Includes enums, payout_term_dto.schemes (per PayoutScheme), plus reward_types[] with create_payload_example for: fixed-reward, variable-reward, proportional-pool, leaderboard. Top-level create_incentive_payload_guide documents body shape and webapp encode.ts mappers. Call before create_incentive. Params: {}.
Inputs: no named parameters.
list_payouts_pending_approvalA
Lists payouts pending approval: GET /api/v1/projects/:projectId/payouts/pending-approval. Optional page, page_size. Example: {"project_id":"","page":1,"page_size":50}.
Inputs: project_id (required), page, page_size.
list_price_referencesA
Lists currencies usable as price references for token-holder triggers: GET /api/v1/currencies?price_reference=true&page_size=100. Optional chain_identifier (e.g. "ethereum") filters to that chain. Each result includes identifier (use as volume_currency_expression), name, decimals, chain_identifier. REQUIRED before create_trigger when the held token may not be a known asset (not on CoinGecko/CMC). Workflow: (1) Call with the trigger chain. (2) If token_address is in results[] (EVM: compare identifier case-insensitively), set volume_currency_expression = token_address. (3) If NOT listed, ask the user: stablecoin or variable-price? How many decimals (6 or 18)? Pick a reference from results with matching decimals. Examples: DAI 0x6b175474e89094c44da98b954eedeac495271d0f on Ethereum is listed — use same address. Unknown 18-decimal stablecoin 0xa3931d71877C0E7a3148CB7Eb4463524FEc27fbD on Ethereum is NOT listed — use DAI as volume_currency_expression, not the token address. Wrong reference → trigger creates (201) but never prices volume correctly. Params: {} or {"chain_identifier":"ethereum"}.
Inputs: chain_identifier.
list_projectsA
Lists dashboard projects for the current user: GET /api/v1/projects with optional ?page= (1-based) and ?query=. Example: {"page":1} or {"query":"acme"}.
Inputs: page, query.
list_rewards_payoutsA
Lists rewards payouts history: GET /api/v1/projects/:projectId/payouts/rewards-payouts. Optional page, page_size, status, from_date, to_date. Example: {"project_id":"","page":1}.
Inputs: project_id (required), page, page_size, status, +2 more.
list_trigger_typesA
Lists trigger type metadata from GET /public-api/v1/metadata/trigger-types (cached), enriched for create_trigger. Each trigger_types[] row includes: context_json_schema (field definitions), create_payload_layout (flat_dto | context_only | context_and_root_fields), create_payload_notes, and create_payload_example when available. Top-level create_trigger_payload_guide explains the three layouts (same as fuul-webapp encode.ts). Always call this before create_trigger. Params: {}.
Inputs: no named parameters.
pingA
Health check: returns "pong" if the MCP process is running. No API calls. Example: invoke with empty input {}.
Inputs: no named parameters.
reject_payoutsC
Rejects payouts: PATCH /api/v1/projects/:projectId/payouts/reject. Same body rules as approve_payouts.
Inputs: project_id (required), dry_run, confirmed, payout_ids, +4 more.
remove_user_from_referral_codeA
Remove a user from a referral code: DELETE /api/v1/referral_codes/:code/referrals. Atomically deletes user_referrers + referral_code_uses and decrements actual_uses. Not idempotent on the API; this tool maps known 422 cases to {"already_removed":true,"reason":"..."} for safe retries. Requires service_role project API key (no wallet signature). Auth: project API key only (pass project_api_key or set env FUUL_MCP_PROJECT_API_KEY). Dashboard OAuth from fuul-mcp login is not accepted on these routes. dry_run then confirmed. Example dry_run: {"referral_code":"PROMO2024","user_identifier":"0xUser...","user_identifier_type":"evm_address","referrer_identifier":"0xKol...","referrer_identifier_type":"evm_address","dry_run":true}.
Inputs: referral_code (required), user_identifier (required), user_identifier_type (required), referrer_identifier (required), +4 more.
resolve_token_holder_price_referenceA
Resolves context.volume_currency_expression for token-holder triggers (check + assign). Step 1: pass token_address and chain_identifier (or chain_id). If the token is already in Fuul price references → status listed_use_same_address, use token_address as volume_currency_expression. Step 2: if status needs_user_input, ask: stablecoin or variable? decimals 6 or 18? Then call again with token_kind and decimals. Step 3: status resolved returns assigned volume_currency_expression (e.g. stablecoin + 18 decimals on Ethereum → DAI). Use that in create_trigger. create_trigger rejects unlisted tokens when volume_currency_expression equals token_address. Example listed: {"token_address":"0x6b175474e89094c44da98b954eedeac495271d0f","chain_identifier":"ethereum"}. Example assign: {"token_address":"0xa3931d71877C0E7a3148CB7Eb4463524FEc27fbD","chain_identifier":"ethereum","token_kind":"stablecoin","decimals":18}.
Inputs: token_address (required), chain_identifier, chain_id, token_kind, +1 more.
send_batch_eventsA
Send up to 100 conversion events: POST /api/v1/events/batch. For backfills and bulk ingestion; processing is atomic (all succeed or all fail). Duplicate dedup_id values are silently ignored; response includes ingested_events count. Auth: project API key only (pass project_api_key or set env FUUL_MCP_PROJECT_API_KEY). Dashboard OAuth from fuul-mcp login is not accepted on these routes. dry_run then confirmed. Rate limit: 10 requests/minute. If the API returns HTTP 429, wait Retry-After seconds (if present) before retrying.
args shape for value/revenue: Same as send_event — each event in the batch can include args.value and/or args.revenue as { amount: string, currency: {...} }. See send_event description for currency formats and amount encoding.
Example dry_run: {"events":[{"name":"swap","user_identifier":"0x...","user_identifier_type":"evm_address","dedup_id":"id-1","args":{"value":{"amount":"5000000000","currency":{"identifier":"0xa0b...","identifier_type":"evm_contract","chain_identifier":"evm:1"}}}}],"dry_run":true}.
Inputs: events (required), project_api_key, dry_run, confirmed.
send_eventA
Send one conversion event: POST /api/v1/events. Triggers real-time reward attribution. Required: name (trigger name), user_identifier, user_identifier_type, dedup_id. Optional: args, timestamp (ms). Duplicate dedup_id returns HTTP 409. After send, use check_event_status with verbose=true (dedup_id + event_name) to verify the pipeline. dry_run then confirmed. Auth: project API key only (pass project_api_key or set env FUUL_MCP_PROJECT_API_KEY). Dashboard OAuth from fuul-mcp login is not accepted on these routes. Rate limit: 100 requests/minute. If the API returns HTTP 429, wait Retry-After seconds (if present) before retrying.
args shape for value/revenue: For non-tracking events (swaps, deposits), args can include value and/or revenue, each as { amount: string, currency: {...} }. amount is a string integer in the smallest unit (e.g. "1000000" for 1 USDC with 6 decimals); decimals only for fiat. currency accepts two forms: (1) Official symbol: { "name": "USDC" } or { "name": "USD" } or { "name": "POINT" }, (2) Token identifier: { "identifier": "0xa0b...", "identifier_type": "evm_contract", "chain_identifier": "evm:1" }.
Example with value/revenue: {"name":"swap","user_identifier":"0x...","user_identifier_type":"evm_address","dedup_id":"swap-123","args":{"value":{"amount":"1000000","currency":{"name":"USDC"}},"revenue":{"amount":"3000","currency":{"name":"USDC"}}},"dry_run":true}.
Simple dry_run: {"name":"trade","user_identifier":"0x...","user_identifier_type":"evm_address","dedup_id":"uuid-here","dry_run":true}.
Inputs: name (required), dedup_id (required), user_identifier (required), user_identifier_type (required), +5 more.
swap_user_referral_codeA
Move a user between referral codes with full redemption semantics: DELETE from from_referral_code then PATCH /api/v1/referral_codes/:to_referral_code/use (not atomic). Step 1 tolerates already-removed 422s (e.g. user never had a prior code). Step 2 assigns to the owner of to_referral_code — there are no to_referrer_* params. If PATCH /use fails after DELETE, response has partial:true with use_error; complete with use_referral_code or update_user_referrer. Legacy PUT-only users (user_referrers without referral_code_use) may block step 2 with "User already has a referrer" — clear with delete_user_referrer first when appropriate. For first assign with no prior code, use use_referral_code directly. Requires service_role project API key. Auth: project API key only (pass project_api_key or set env FUUL_MCP_PROJECT_API_KEY). Dashboard OAuth from fuul-mcp login is not accepted on these routes. dry_run then confirmed. Example dry_run: {"user_identifier":"0xUser...","user_identifier_type":"evm_address","from_referral_code":"OLD","from_referrer_identifier":"0xOldKol...","from_referrer_identifier_type":"evm_address","to_referral_code":"NEW","dry_run":true}.
Inputs: user_identifier (required), user_identifier_type (required), from_referral_code (required), from_referrer_identifier (required), +5 more.
update_audienceA
Updates an audience (user list): PATCH /api/v1/projects/:projectId/audiences/:audienceId. Body matches CreateOrUpdateAudienceDto: name (required), optional conditions[] (signature + parameters), condition_match_mode "any"|"all" (required if conditions non-empty), contractId. dry_run then confirmed. Example dry_run: {"project_id":"","audience_id":"","name":"VIP","dry_run":true}.
Inputs: project_id (required), audience_id (required), name (required), dry_run, +4 more.
update_payout_termA
Updates one payout term on a draft conversion: PATCH /api/v1/projects/:projectId/conversions/:conversionId/payout_terms/:payoutTermId. Body is a single PayoutTermDto (use get_incentive, edit fields such as referral_amount / referrer_amount, send as payout_term). For variable rewards, the server expects referral_amount_percentage / referrer_amount_percentage; this tool maps GET aliases automatically (same as the dashboard). Per-unit rewards: edit referral_amount and referrer_amount; do not send zero percentages. dry_run shows the normalized body sent to the API. Example dry_run: {"project_id":"","conversion_id":"","payout_term_id":"","payout_term":{...},"dry_run":true}. Before executing (including dry_run), this tool refreshes project metadata (same as get_project) and resolves trigger_id / conversion_id / trigger_ids[] to the current draft UUIDs. If you pass a published_trigger_id from before a dashboard publish, it is remapped to the current draft_trigger_id for the same ref. Responses include _draft_id_resolution when an ID was remapped. Unknown stale UUIDs fail with an explicit error. On successful execution (not dry_run), the response includes _publish_metadata_reminder: publish project metadata from the dashboard (Project → Incentives or Triggers → Publish now). The MCP cannot publish for you.
Inputs: project_id (required), conversion_id (required), payout_term_id (required), payout_term (required), +2 more.
update_project_affiliate_publicA
Updates a managed project affiliate: PATCH /api/v1/project-affiliates/:projectAffiliateId. Partial body (alias, region, status, note, audiences, tier_protection including null to clear, approve_project_tier_ids with reviewed_by_user_id). Auth: project API key only (pass project_api_key or set env FUUL_MCP_PROJECT_API_KEY). Dashboard OAuth from fuul-mcp login is not accepted on these routes. dry_run then confirmed. Example dry_run: {"project_affiliate_id":"","alias":"New name","dry_run":true}.
Inputs: project_affiliate_id (required), dry_run, confirmed, project_api_key, +8 more.
update_project_tierA
Updates a project affiliate tier: PATCH /api/v1/projects/:projectId/tiers/:tierId. Optional fields: name, description, rank, audience_id (null clears audience). At least one field required. dry_run then confirmed. Example: {"project_id":"","tier_id":"","rank":2,"dry_run":true}.
Inputs: project_id (required), tier_id (required), dry_run, confirmed, +4 more.
update_triggerA
Updates a trigger: PATCH /api/v1/projects/:projectId/triggers/:triggerId. Partial body matching UpdateTriggerDto (name, description, event_type, expressions, payable, ref, contract_ids as single-element array, etc.). Does NOT update context fields such as token_address or chain_id — those are immutable after create. If the user asks to change token_address, chain_id, or the tracked token/contract: do NOT call PATCH. First inform the user clearly: "This cannot be updated in place; you must delete the trigger and create a new one with the new token/chain." Then, only with explicit user approval, run the replace flow: Replace-trigger flow (token/chain change; after telling the user update_trigger cannot change token_address/chain_id): (1) get_project or list_incentives — list every incentive/conversion whose triggers[] includes this draft_trigger_id; (2) delete_conversion for each draft_conversion_id (removes conversion + trigger links); (3) delete_trigger; (4) create_trigger with the new context; (5) create_incentive if the program must be recreated. Do not call delete_trigger until step 2 is done for all linked conversions. Never skip step 1 — always list linked conversions before delete_trigger. At least one patch field required for allowed fields only. dry_run then confirmed. Prefer get_project for current draft_trigger_id by ref. Before executing (including dry_run), this tool refreshes project metadata (same as get_project) and resolves trigger_id / conversion_id / trigger_ids[] to the current draft UUIDs. If you pass a published_trigger_id from before a dashboard publish, it is remapped to the current draft_trigger_id for the same ref. Responses include _draft_id_resolution when an ID was remapped. Unknown stale UUIDs fail with an explicit error. On successful execution (not dry_run), the response includes _publish_metadata_reminder: publish project metadata from the dashboard (Project → Incentives or Triggers → Publish now). The MCP cannot publish for you.
Inputs: project_id (required), trigger_id (required), dry_run, confirmed, +15 more.
update_user_referrerA
Admin override: create or overwrite user_referrers via PUT /api/v1/user-referrers (idempotent upsert). Sets source=project_imported; does NOT create referral_code_uses or increment actual_uses (status may stay referred:false). For real code redemption use use_referral_code instead. Requires service_role project API key. Auth: project API key only (pass project_api_key or set env FUUL_MCP_PROJECT_API_KEY). Dashboard OAuth from fuul-mcp login is not accepted on these routes. dry_run then confirmed. Example dry_run: {"user_identifier":"0xUser...","user_identifier_type":"evm_address","referrer_identifier":"0xKol...","referrer_identifier_type":"evm_address","dry_run":true}.
Inputs: user_identifier (required), user_identifier_type (required), referrer_identifier (required), referrer_identifier_type (required), +4 more.
use_referral_codeA
Redeem a referral code for a user: PATCH /api/v1/referral_codes/:code/use. Creates referral_code_uses, increments actual_uses, sets user_referrers with source=code_redemption (GET /referral_codes/status → referred:true). Referrer is the code owner (do not pass referrer_identifier). Requires user has no existing referrer unless service_role idempotent replay of the same code. No wallet signature with service_role. Auth: project API key only (pass project_api_key or set env FUUL_MCP_PROJECT_API_KEY). Dashboard OAuth from fuul-mcp login is not accepted on these routes. dry_run then confirmed. Example dry_run: {"referral_code":"PROMO2024","user_identifier":"0xUser...","user_identifier_type":"evm_address","dry_run":true}.
Inputs: referral_code (required), user_identifier (required), user_identifier_type (required), project_api_key, +2 more.
whoamiA
Returns the current Fuul dashboard user as JSON from GET /api/v1/auth/user. Requires prior CLI login (tokens in ~/.fuul/tokens.json). Example: {} after npm run cli -- login.
Inputs: no named parameters.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 39 tools
Most tools have clearly distinct purposes, with detailed descriptions that help differentiate them. However, the large number of tools related to referrals and affiliates could cause some confusion for an agent.
All tool names follow a consistent verb_noun pattern in snake_case, such as list_chains, create_trigger, and approve_payouts. No mixed conventions are observed.
39 tools is on the higher side for a single server, covering a broad API surface. While the complexity of the domain may justify the count, it risks overwhelming agents and could benefit from splitting into sub-servers.
The tool set covers CRUD operations for most entities and includes essential workflows like event tracking and payouts. However, the absence of a 'publish' operation (noted as required externally) is a notable gap.
Related MCP Connectors
Run your affiliate program from any agent: partners, commissions, payouts, bounties, recruitment.
Browse affiliates, referrals, commissions and payouts, and set up campaigns and affiliates.
Agent-commerce MCP server for x402/USDC payments and affiliate splits on Base.
MCP Server for agents to onboard, pay, and provision services autonomously with InFlow
Related MCP Servers
- AlicenseAqualityCmaintenanceMCP server for managing affiliate and referral programs. Track referrals, manage affiliates, process conversions, and handle payouts through AI assistants like Claude, Cursor, and ChatGPT.1881 npm2MIT
- AlicenseAqualityAmaintenanceMCP server for AI agent affiliate attribution and commission tracking. 7 tools for program discovery, conversion tracking, attribution token verification, and commission queries.7MIT
- AlicenseAqualityBmaintenanceVendor-neutral MCP server that discovers, routes, and attributes revenue across paid APIs, referral links, and affiliate programs for agent tools.8128 npmMIT
- AlicenseBqualityCmaintenanceMCP server that integrates affiliate networks with Claude and Codex, enabling natural language queries across publisher and advertiser-side data from multiple networks.100166 npm7MIT