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_ISSUERandOIDC_CLIENT_ID. The MCP channel audienceFLOH_MCP_AUDIENCEdefaults tohttps://mcp.floh.api. (FLOH_RESOURCE_ID/OIDC_AUDIENCEis operator-tooling metadata and is not accepted asaud.)
FLOH_MCP_AUDIENCE must match on 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.
The MCP process sends FLOH_MCP_AUDIENCE as the RFC 8707 resource
parameter on refresh, so Authifi mints the access token with the MCP
channel identifier as aud. Both sides default to https://mcp.floh.api,
and the MCP process prints a note on stderr when it uses the default. If
you set the variable on one side, set the same value on the other; a
mismatch starts cleanly and then rejects every MCP request. The default
applies only to floh-mcp-client: with the floh-client rollback client
and FLOH_MCP_AUDIENCE unset, the MCP process sends no resource, but a
set value is still sent whichever client is configured.
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 is 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, portal, and MCP OIDC clients against their
matching channel resource servers. Existing-client reconciliation is
additive: every registered callback and post-logout URI is preserved,
including one from another environment sharing this tenant, except a
callback whose parsed pathname is exactly /api/auth/callback or
/api/auth/callback/. Pass the origins this invocation needs — they are
appended to whatever the client already has, not substituted for it:
pnpm run setup-authifi-oidc-clients -- --tenant ls --yes --dry-run \
--console-origin https://console.example.com \
--portal-origin https://portal.example.com
pnpm run setup-authifi-oidc-clients -- --tenant ls --yes \
--console-origin https://console.example.com \
--portal-origin https://portal.example.com
The OIDC command's dry-run previews URI changes and verifies that the console,
portal, and MCP resource servers each resolve uniquely with a positive numeric
id. It cannot preview numeric client relationships before absent clients are
created, so running the RBAC dry-run/apply first remains required. Every
credentialed run resolves and confirms the target tenant, then setup removes
managed sibling-channel links, binds each client only to its own channel
resource server, and rereads the managed state before reporting success. The
three channel identifiers must be pairwise distinct, and none may equal the
token-derived AUTHIFI_ADMIN_RESOURCE.
Setup does not inspect or remove unrelated relationships. Pre-existing legacy transitional links remain untouched until the explicit cleanup under LSA-10182. Verify the three managed channel 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": "https://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 |
no | MCP RS id sent as RFC 8707 resource on refresh; default https://mcp.floh.api |
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": "https://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 |