Use apps
Connect Gmail, Slack, GitHub, Notion, Linear and other apps to your Flux account, then let your agent find and run actions in them through the Flux tools server.
Apps let your agent act for you in the tools you already use: read a Gmail thread, file a Linear issue, post in Slack, update a Notion page. You connect each app once, by signing in to it yourself, and your agent then runs actions in it through the Flux tools server, on the same Flux key and the same bill.
Status: Shipping
If apps are not switched on for your key yet, every apps tool answers ability_disabled. That answer tells your agent not to retry and to carry on without the tool.
What apps are
Flux connects to hundreds of apps. The Flux tools server gives your agent four tools to use them:
| Tool | What it does | Arguments | Billed? |
|---|---|---|---|
apps_list | Shows the apps connected to your Flux account, and whether each is connected or still pending. | none | No |
apps_connect | Returns a sign-in link for one app. You open it and sign in. | app, optional alias | No, but see free runs |
apps_find_actions | Finds the actions that can do a task, with the arguments each one takes. | task, optional app | No |
apps_execute | Runs one action on a connected account and returns its result. | action, arguments | Yes, per action |
Apps are reached only through these tools. There is no separate apps endpoint for your own code to call; your code calls the same tools on the tools server, as the examples below show.
Set it up
Add the Flux tools server to your agent once, with your Flux key. The same server carries every Flux tool, so if you have already added it for another ability there is nothing more to do. Full details are on Set up the Flux tools server.
Built into Murage, which decides which abilities it uses. Nothing to set up here. See murage.ai.
Built into Wayland, which decides which abilities it uses. Nothing to set up here. See getwayland.com.
Add the Flux tools server from your terminal.
claude mcp add --transport http flux-tools https://api.fluxrouter.ai/mcp-tools/ \
--header "Authorization: Bearer sk-YOUR-FLUX-KEY"
Then ask Claude Code:
Use the Flux apps tools to connect my Gmail, then summarise my five newest unread emails.
From Claude Code's own docs, checked 2026-09-28.
Add the Flux tools server to the same config. Codex reads the key from FLUX_API_KEY, which you exported above. File: ~/.codex/config.toml
[mcp_servers.flux-tools]
url = "https://api.fluxrouter.ai/mcp-tools/"
bearer_token_env_var = "FLUX_API_KEY"
Then ask Codex:
Use the Flux apps tools to connect my Gmail, then summarise my five newest unread emails.
From Codex's own docs, checked 2026-09-28.
Add the Flux tools server to your global MCP config, then restart Cursor. File: ~/.cursor/mcp.json
{
"mcpServers": {
"flux-tools": {
"url": "https://api.fluxrouter.ai/mcp-tools/",
"headers": {
"Authorization": "Bearer sk-YOUR-FLUX-KEY"
}
}
}
}
Then ask Cursor:
Use the Flux apps tools to connect my Gmail, then summarise my five newest unread emails.
From Cursor's own docs, checked 2026-09-28.
Add the Flux tools server to the same config, then restart Hermes. File: ~/.hermes/config.yaml
mcp_servers:
flux-tools:
url: "https://api.fluxrouter.ai/mcp-tools/"
headers:
Authorization: "Bearer sk-YOUR-FLUX-KEY"
Then ask Hermes:
Use the Flux apps tools to connect my Gmail, then summarise my five newest unread emails.
From Hermes's own docs, checked 2026-09-28.
Not checked yet. We have not checked how OpenClaw adds an MCP server, so we do not show a recipe. See OpenClaw's own docs.
Open Cline's MCP servers panel, edit the settings file, and add the Flux tools server.
{
"mcpServers": {
"flux-tools": {
"type": "streamableHttp",
"url": "https://api.fluxrouter.ai/mcp-tools/",
"headers": {
"Authorization": "Bearer sk-YOUR-FLUX-KEY"
}
}
}
}
Then ask Cline:
Use the Flux apps tools to connect my Gmail, then summarise my five newest unread emails.
From Cline's own docs, checked 2026-09-28.
# 1. Ask for a sign-in link for Gmail. Open connect_url in a browser.
curl https://api.fluxrouter.ai/mcp-tools/ \
-H "Authorization: Bearer sk-YOUR-FLUX-KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call",
"params":{"name":"apps_connect","arguments":{"app":"gmail"}}}'
# 2. Once you have signed in, check it shows "connected": true.
curl https://api.fluxrouter.ai/mcp-tools/ \
-H "Authorization: Bearer sk-YOUR-FLUX-KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call",
"params":{"name":"apps_list","arguments":{}}}'
import json
import requests
TOOLS_URL = "https://api.fluxrouter.ai/mcp-tools/"
HEADERS = {
"Authorization": "Bearer sk-YOUR-FLUX-KEY",
"Content-Type": "application/json",
"Accept": "application/json, text/event-stream",
}
def call_tool(name, arguments=None):
"""Call one Flux tool and return its structured answer."""
body = {
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {"name": name, "arguments": arguments or {}},
}
res = requests.post(TOOLS_URL, headers=HEADERS, json=body, timeout=320)
res.raise_for_status()
for line in res.text.splitlines():
if line.startswith("data:"):
message = json.loads(line[len("data:"):])
result = message["result"]["structuredContent"]
if "error" in result:
raise RuntimeError(f"{result['error']}: {result.get('message')}")
return result
raise RuntimeError("no answer from the tools server")
# Find the action for a task, then run it on your connected account.
found = call_tool("apps_find_actions", {"task": "list my unread emails", "app": "gmail"})
print(json.dumps(found["actions"], indent=2))
# Use the action slug and argument names that apps_find_actions returned.
# The ones below are an example.
run = call_tool("apps_execute", {
"action": "GMAIL_FETCH_EMAILS",
"arguments": {"query": "is:unread", "max_results": 5},
})
print(run["ok"], run["result"])
const TOOLS_URL = 'https://api.fluxrouter.ai/mcp-tools/';
async function callTool(name: string, args: Record<string, unknown> = {}) {
const res = await fetch(TOOLS_URL, {
method: 'POST',
headers: {
Authorization: 'Bearer sk-YOUR-FLUX-KEY',
'Content-Type': 'application/json',
Accept: 'application/json, text/event-stream',
},
body: JSON.stringify({
jsonrpc: '2.0',
id: 1,
method: 'tools/call',
params: { name, arguments: args },
}),
});
if (!res.ok) throw new Error(`tools server answered ${res.status}`);
const text = await res.text();
const data = text
.split('\n')
.find((line) => line.startsWith('data:'))
?.slice('data:'.length);
if (!data) throw new Error('no answer from the tools server');
const result = JSON.parse(data).result.structuredContent;
if (result.error) throw new Error(`${result.error}: ${result.message}`);
return result;
}
// What is connected on this Flux account?
const { apps } = await callTool('apps_list');
console.log(apps);
// Not connected yet? Get a sign-in link and open it in a browser.
const { connect_url } = await callTool('apps_connect', { app: 'slack' });
console.log('Sign in here:', connect_url);
Connect an account
- Ask for a link. Your agent calls
apps_connectwith the app's name in lower case, such asgmail,slack,github,notionorlinear. It gets back aconnect_urland anext_stepsentence. - Sign in yourself. Your agent shows you the link. It opens our connection partner's secure sign-in page, which hands you on to the app's own sign-in. You approve access there. It is not a Flux page, and your agent never sees your password.
- Check it worked. Your agent calls
apps_listuntil the app shows"connected": true. While you are still signing in, it shows"pending": true.
{
"apps": [
{
"app": "gmail",
"connected": true,
"pending": false
}
]
}
Each entry also carries the connection's status and the accounts connected on that app.
apps_list shows only apps you have connected, or started to connect. It does not list every app Flux can reach. To find out whether an app is available, ask apps_find_actions for the task you have in mind, or call apps_connect with its name: an unknown name answers invalid_request.
More than one account on an app
To connect a second account on the same app, such as a work and a personal Gmail, pass an alias to apps_connect: a short label of your own, such as work. Without one, the call is refused so that the first connection is not replaced. Each alias must be new for that app, and one app can hold up to 5 accounts.
Find and run actions
An action is one thing an app can do, such as sending an email or creating an issue. Each has a slug in capitals, such as GMAIL_SEND_EMAIL.
Find the action. Your agent calls
apps_find_actionswith the task in plain words, and the app if it knows it:{ "task": "create an issue", "app": "linear" }The answer lists matching actions with the arguments each takes, and a
next_stepthat says which tool to call next. Finding actions is not billed.Run it. Your agent calls
apps_executewith one action slug and its arguments. The slug and argument names here are an example:{ "action": "SLACK_SEND_MESSAGE", "arguments": { "channel": "#team", "text": "The release notes are ready." } }The answer carries the action, whether it worked, and the app's own result:
{ "action": "SLACK_SEND_MESSAGE", "ok": true, "result": { ... } }When
okisfalse, the app refused the action andresultholds the app's reason, for example a missing argument. Fix the arguments and run it again.
Always take the slug and the argument names from apps_find_actions, not from memory: they are the app's own and can change. apps_execute runs exactly one action per call. A slow action can take a few minutes; the server keeps the connection open until the app answers.
Doing several things in a row
Most real jobs touch more than one app, for example: read an email, file an issue about it, then tell the team in Slack. You ask for the whole job in one sentence, and your agent's own model does the chaining:
- It runs one
apps_executecall per step, and carries what it needs (an id, a link, a summary) from one step's result into the next step's arguments. - There is no workflow on the Flux side, and no rollback. If the third step fails, the first two have still happened. A retried job can create a second issue unless the agent checks first.
- Ask your agent to look before it creates anything, such as searching for an existing issue before filing a new one.
Worked examples, with the prompts and the code, are in Chain apps and Meeting notes to Slack.
Who can see your connections
- Connections belong to your Flux account, not to one key. Every key on the account can use every app connected on it. If you share an account with a teammate or give a key to an agent, that key can act in every connected app. Use a separate Flux account when you need apps kept apart.
- Access is whatever you approved at sign-in. Flux does not narrow an app's access per key.
- Flux never holds your app passwords or sign-in tokens. The app sign-in is kept by our connection partner. Flux stores only which apps your account has connected.
Disconnecting an app
You cannot disconnect an app through Flux yet. Until you can, remove Flux's access from the app's own security settings (most apps list connected third-party apps there), or contact support and we will remove the connection for you.
What it costs
Only apps_execute is billed: one action run per call. Listing, connecting and finding actions are not billed. A run is charged only once its result is delivered.
- App actions: priced per call.
Accounts without a paid plan or credit get 50 runs a day for free, counted in UTC days. Each apps_connect also uses one of those free runs. When they are gone, the tools answer payment_required until you add credit in Billing or the next UTC day starts. Paid accounts are billed per run instead.
Your agent's own model calls, as it plans each step, are billed as normal chat on the same key.
Limits
These apply per Flux account, across every key:
| Limit | Value |
|---|---|
| Requests | 120 requests a minute |
| Action runs per hour | 600 runs an hour |
| Action runs per day | 2,000 runs a day (lower for new accounts) |
| Accounts per app | 5 |
Over a limit, the tool answers rate_limited with retryable: true, and may say how long to wait in retry_after_seconds. The daily limit resets at midnight UTC.
When a tool says no
Every refusal is a normal tool result with an error code, a message your agent can act on, and a retryable flag. The common ones for apps:
error | What it means |
|---|---|
ability_disabled | Apps are not switched on for your key yet. Do not retry. |
payment_required | The free runs are used up. Add credit, or wait for the next UTC day. |
rate_limited | Too many calls. Wait, then retry. |
invalid_request | The input was refused: an unknown app, a missing alias for a second account, too many accounts, or an action slug that is not valid. |
apps_error | Finding or running actions failed on the app side. Rephrase the task and retry. |
auth_failed | The Flux key on the tools server entry was not accepted. |
Fixes for each are on App problems, and every code across all abilities is in the error reference.