Skip to content

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 audience FLOH_MCP_AUDIENCE. (FLOH_RESOURCE_ID / OIDC_AUDIENCE is operator-tooling metadata and is not accepted as aud.)

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):

pnpm run sync-authifi-rbac -- --dry-run

Dry-run is the default and performs no mutation. Review every create, update, link, unlink, and delete action. Apply interactively only after the preview:

pnpm run sync-authifi-rbac -- --apply

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:

pnpm run sync-authifi-rbac -- --apply --prune

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:

cd <path-to-repo>
pnpm --filter @floh/mcp build

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

  1. Restart Claude Desktop (quit fully and reopen)
  2. Look for "floh" in the MCP server list (hammer icon in the chat input)
  3. Try a read-only tool: ask Claude to "list my Floh workflows"
  4. If the server fails to start, check ~/Library/Logs/Claude/mcp*.log for 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 scope claim. 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