MCP Server Setup¶
Overview¶
The Floh MCP (Model Context Protocol) server exposes workflow management, run control, connector operations, and configuration tools to AI assistants like Claude and Cursor. Authentication uses Authifi OIDC tokens with RBAC-controlled scopes.
The resource server also checks the token's authorized party (azp / client_id).
Documented MCP uses floh-mcp-client (MCP_SCOPES only). pnpm run setup-authifi-oidc-clients
registers that confidential client. Do not set MCP process env to floh-client:
a missing scope claim then authenticates with an empty permission set instead of 401.
A console-session refresh token cannot be reused with floh-mcp-client.
Mutating MCP calls send Authorization: Bearer without a Floh session cookie, so they skip CSRF on /api.
Prerequisites¶
- Running Floh server with OIDC configured in
.env:OIDC_ISSUER,OIDC_CLIENT_ID, and the MCP channel audienceFLOH_MCP_AUDIENCE. (FLOH_RESOURCE_ID/OIDC_AUDIENCEis operator-tooling metadata and is not accepted asaud.)
Set FLOH_MCP_AUDIENCE on both the API and the MCP process, or every MCP request fails with 401
The API builds its accepted-aud allow-list solely from
FLOH_CONSOLE_AUDIENCE, FLOH_PORTAL_AUDIENCE, and FLOH_MCP_AUDIENCE,
and startup requires only one of the three. An API that omits
FLOH_MCP_AUDIENCE therefore starts cleanly and then rejects every MCP
request. The MCP process sends the same value as the RFC 8707 resource
parameter on refresh, so Authifi mints the access token with the MCP
channel identifier as aud; omit it there and the token carries the
tenant default instead, which the API does not accept.
The symptom is 401 Invalid token, not a 403: the audience check happens
inside verifyToken(), and authenticate() catches that failure before
the authorized-party check that produces 403 CHANNEL_DENIED is ever
reached. A 401 here means the audience, not the credential.
- Authifi tenant with admin access
- Node.js 24+
1. Reconcile Authifi authorization¶
Floh manages three distinct Authifi resource servers: console, portal, and MCP. Set these non-secret identifiers, keeping every value different:
FLOH_CONSOLE_AUDIENCE=https://console-api.example.com
FLOH_PORTAL_AUDIENCE=https://portal-api.example.com
FLOH_MCP_AUDIENCE=https://mcp-api.example.com
The sync also needs gitignored .authifi-admin-token. Never put the token on
the command line or commit it. The command loads repo .env when present,
while direct shell values override file values. AUTHIFI_BASE_URL and
AUTHIFI_TENANT_ID are derived from the token when unset. OIDC client setup
uses the same token file. Preview the complete
three-channel plan (the command rebuilds @floh/shared first):
Dry-run is the default and performs no mutation. Review every create, update, link, unlink, and delete action. Apply interactively only after the preview:
The command creates or updates each channel's resource server, permissions,
access roles, groups, and relationships. It is safe to rerun; after apply, run
the dry-run again and expect No changes. There is no production auto-apply in
CI or deployment workflows.
Prune is optional and destructive. It removes only extra Floh-owned
floh-console:, floh-portal:, and floh-mcp: children and managed
permissions; resource servers and client assignments are never deleted:
Prune requires a second interactive confirmation. Reserve --yes for
controlled non-interactive maintenance after its exact dry-run has been
reviewed.
Next, reconcile the console and portal OIDC clients against their matching resource servers. Pass the full deployed and local origin set because URI reconciliation is authoritative:
pnpm run setup-authifi-oidc-clients -- --dry-run \
--console-origin https://console.example.com \
--portal-origin https://portal.example.com
pnpm run setup-authifi-oidc-clients -- \
--console-origin https://console.example.com \
--portal-origin https://portal.example.com
The OIDC command's dry-run previews URI changes but does not validate or preview
resource-server bindings. On a first run, client creation supplies the tenant
and numeric client ids needed for binding lookup, so a missing channel resource
server can surface only during apply. Running the RBAC dry-run/apply first is
therefore required. Setup removes console↔portal sibling links,
binds each client to its own channel resource server, and binds all three to
the separate FLOH_RESOURCE_ID resource server. That last binding is
transitional bookkeeping only: the BFFs do not request it — the deploy
compose sets AUTH_RESOURCE to ${FLOH_CONSOLE_AUDIENCE} — and the API does
not verify it, since acceptedAudiences is built solely from
FLOH_CONSOLE_AUDIENCE, FLOH_PORTAL_AUDIENCE, and FLOH_MCP_AUDIENCE. A
token minted for FLOH_RESOURCE_ID is rejected. Its only job is to satisfy the
setup script's binding lookup (#1188).
All four identifiers must be pairwise distinct.
Verify all four client relations in Authifi after apply.
To roll back catalog changes, restore the previous catalog revision, preview
with --dry-run --prune, then apply with --apply --prune. A normal apply
leaves removed catalog entries in Authifi. Relationship removals happen before
object deletion.
Restore OIDC client URIs from the previous known-good origin list. The tooling
does not delete resource servers or unrelated tenant objects.
All MCP access roles and groups remain namespaced under floh-mcp:.
pnpm run setup-authifi-oidc-clients registers floh-mcp-client alongside
the console and portal browser clients. DB RBAC and proprietary-token removal
are LSA-9939, and runtime grant rechecks are LSA-9940.
2. Permission Reference¶
API Permissions (Scopes)¶
| Permission | Description |
|---|---|
workflow:read |
List and view workflow definitions |
workflow:create |
Create new workflow definitions |
workflow:update |
Update draft workflow definitions |
workflow:publish |
Publish workflows to active status |
workflow:start |
Start new workflow runs |
run:read |
List and view workflow runs |
run:update |
Retry failed runs |
run:cancel |
Cancel running workflows |
connector:read |
List and view connectors |
connector:manage |
Create, update, test, and execute connectors |
project:read |
List projects and workflow sets |
role_definition:read |
List role definitions |
user:read |
List system users |
group:read |
List user groups |
schedule:read |
List scheduled triggers |
config:export |
Export system configuration |
config:import |
Import system configuration |
Access Role Bundles¶
Access roles are namespaced under floh-mcp: in Authifi. Each has a matching group for user assignment.
| Access Role | Permissions |
|---|---|
floh-mcp:reader |
workflow:read, run:read, connector:read, project:read, role_definition:read, user:read, group:read, schedule:read |
floh-mcp:author |
Reader + workflow:create, workflow:update, workflow:publish, workflow:start, config:export |
floh-mcp:operator |
All permissions |
Tool-to-Scope Mapping¶
| Tool | Required Scope | Destructive | Requires Confirmation |
|---|---|---|---|
list_workflows |
workflow:read |
no | no |
get_workflow |
workflow:read |
no | no |
validate_workflow |
workflow:read |
no | no |
generate_workflow_docs |
workflow:read |
no | no |
create_workflow |
workflow:create |
no | no |
update_workflow |
workflow:update |
no | no |
create_version |
workflow:create |
no | no |
publish_workflow |
workflow:publish |
yes | yes |
start_run |
workflow:start |
yes | yes |
list_runs |
run:read |
no | no |
get_run |
run:read |
no | no |
diagnose_run |
run:read |
no | no |
retry_run |
run:update |
no | yes |
cancel_run |
run:cancel |
yes | yes |
import_config |
config:import |
yes | yes |
export_config |
config:export |
no | no |
update_connector_script |
connector:manage |
yes | yes |
execute_connector_command |
connector:manage |
yes | yes |
3. Obtain a Refresh Token¶
The MCP server uses a refresh token minted for floh-mcp-client. A refresh
token from a Floh console BFF session belongs to floh-client and will not
authenticate against the MCP client.
Method 1: Authorization Code Flow¶
Request the offline_access scope during a direct OIDC authorization code flow
against floh-mcp-client (placeholder callback https://localhost:4242) to
receive a refresh token in the token response.
Method 2: Authifi Admin Tools¶
Use Authifi admin API or UI to issue a refresh token for a user on
floh-mcp-client. Refer to Authifi docs
for available endpoints.
4. Configure Claude Desktop¶
Build the MCP server¶
The Claude Desktop config points to the compiled dist/ output, so build first:
Config file location¶
| OS | Path |
|---|---|
| macOS | ~/Library/Application Support/Claude/claude_desktop_config.json |
| Windows | %APPDATA%\Claude\claude_desktop_config.json |
| Linux | ~/.config/Claude/claude_desktop_config.json |
Add the floh MCP server¶
Merge the following into the mcpServers object in your config file:
{
"mcpServers": {
"floh": {
"command": "node",
"args": ["<path-to-repo>/packages/mcp/dist/index.js"],
"env": {
"FLOH_API_URL": "https://localhost:7070",
"OIDC_ISSUER": "https://a-ci.ncats.io/_api/auth/ls",
"OIDC_CLIENT_ID": "floh-mcp-client",
"OIDC_CLIENT_SECRET": "<mcp-client-secret>",
"FLOH_MCP_AUDIENCE": "http://mcp.floh.api",
"FLOH_REFRESH_TOKEN": "<your-refresh-token>",
"NODE_EXTRA_CA_CERTS": "<path-to-repo>/certs/localhost.crt"
}
}
}
}
Environment variables¶
| Variable | Required | Description |
|---|---|---|
FLOH_API_URL |
yes | Base URL of the Floh API server |
OIDC_ISSUER |
yes | Authifi OIDC issuer URL (e.g. https://a-ci.ncats.io/_api/auth/ls) |
OIDC_CLIENT_ID |
yes | MCP client id (floh-mcp-client). Process env; MCP_OIDC_CLIENT_ID wins if both are set |
OIDC_CLIENT_SECRET |
no | MCP client secret (MCP_OIDC_CLIENT_SECRET preferred; from env/mcp.env, not console) |
FLOH_MCP_AUDIENCE |
yes | Channel catalog RS id sent as RFC 8707 resource on refresh so API aud verifies |
FLOH_REFRESH_TOKEN |
yes | Refresh token minted for floh-mcp-client (see Section 3) |
NODE_EXTRA_CA_CERTS |
no | Path to TLS certificate file — needed for local dev with self-signed certs |
Replace <path-to-repo> with the absolute path to your Floh repository clone.
Verification¶
- Restart Claude Desktop (quit fully and reopen)
- Look for "floh" in the MCP server list (hammer icon in the chat input)
- Try a read-only tool: ask Claude to "list my Floh workflows"
- If the server fails to start, check
~/Library/Logs/Claude/mcp*.logfor error details
5. Configure Cursor IDE¶
Add to .cursor/mcp.json in the workspace root:
{
"mcpServers": {
"floh": {
"command": "npx",
"args": ["tsx", "packages/mcp/src/index.ts"],
"env": {
"FLOH_API_URL": "https://localhost:7070",
"OIDC_ISSUER": "https://a-ci.ncats.io/_api/auth/ls",
"OIDC_CLIENT_ID": "floh-mcp-client",
"OIDC_CLIENT_SECRET": "<mcp-client-secret>",
"FLOH_MCP_AUDIENCE": "http://mcp.floh.api",
"FLOH_REFRESH_TOKEN": "<your-refresh-token>",
"NODE_EXTRA_CA_CERTS": "<path-to-repo>/certs/localhost.crt"
}
}
}
}
6. Tool Confirmation Behavior¶
Destructive tools (publish, start, cancel, import, update connector scripts, execute commands) require explicit confirmation before execution. When called without confirmed: true, they return a preview of what will happen.
The AI assistant will present this preview to you and ask for confirmation before re-calling the tool with confirmed: true.
7. Security Notes¶
- Refresh tokens are long-lived — store them securely (environment variables, secret managers). Never commit them to version control.
- Token rotation: If Authifi rotates the refresh token, the MCP server stores the new one in memory automatically.
- Access tokens are short-lived (typically 1 hour), cached in memory only, and refreshed automatically.
- Audit trail: All MCP actions are recorded in the Floh audit log with the user's identity.
- Authorization: Floh derives the caller's permissions entirely from the access token's
scopeclaim. Authifi controls which scopes are issued, based on the user's group/role assignments there — Floh keeps no parallel permission store.
8. Troubleshooting¶
| Error | Cause | Fix |
|---|---|---|
| "Invalid or expired token" on every call | Static floh_* API token (no longer supported) |
Switch to OIDC refresh token auth |
| "Token refresh failed: 400" | Refresh token expired or revoked | Obtain a new refresh token |
| "OIDC discovery failed" | OIDC_ISSUER URL unreachable |
Check network and issuer URL |
| "Permission denied" | User lacks required scope | Add user to appropriate floh-mcp:* group in Authifi |
| "CONFIRMATION REQUIRED" | Destructive tool needs confirmed: true |
Normal behavior — confirm the action |