Skip to content

MCP server

Flowstate is an MCP server. Install it once into Claude.ai or ChatGPT, and your engineers can ask questions about workforce, AI spend, projects, scenarios — answered against your live Flowstate data — from the chat window. Same server, two install paths.

For the install side, see Flowstate in Claude and Flowstate in ChatGPT. This page is the protocol reference.

Endpoints

The server is mounted at /api/mcp on every tenant subdomain.

MethodPathPurpose
GET/.well-known/oauth-authorization-serverOAuth discovery (RFC 8414)
POST/api/mcp/oauth/registerDynamic client registration (RFC 7591)
GET / POST/api/mcp/oauth/authorizeOAuth consent and authorization-code issuance
POST/api/mcp/oauth/tokenToken exchange and refresh
POST/api/mcp/protocolMCP JSON-RPC requests (Streamable HTTP)
GET/api/mcp/protocolSSE for server-initiated messages
DELETE/api/mcp/protocolOptional session cleanup

Base URL example:

https://acme.flowstate.inc/api/mcp

Feature toggle

The entire /api/mcp surface is gated behind the per-tenant mcp_external_access feature toggle. With the toggle off, every endpoint returns:

json
{
  "error": "mcp_disabled",
  "error_description": "MCP external access is not enabled for this organization"
}

with HTTP status 403. Contact Flowstate support to flip the toggle on for your tenant.

OAuth flow

1. Discovery

The MCP client fetches the discovery document:

GET https://{tenant}.flowstate.inc/.well-known/oauth-authorization-server

Response:

json
{
  "issuer": "https://{tenant}.flowstate.inc",
  "authorization_endpoint": "https://{tenant}.flowstate.inc/api/mcp/oauth/authorize",
  "token_endpoint": "https://{tenant}.flowstate.inc/api/mcp/oauth/token",
  "registration_endpoint": "https://{tenant}.flowstate.inc/api/mcp/oauth/register",
  "response_types_supported": ["code"],
  "grant_types_supported": ["authorization_code", "refresh_token"],
  "code_challenge_methods_supported": ["S256"],
  "token_endpoint_auth_methods_supported": ["client_secret_post"],
  "scopes_supported": ["openid", "profile"]
}

2. Dynamic client registration

The MCP client registers itself:

POST /api/mcp/oauth/register
Content-Type: application/json

{
  "client_name": "Claude.ai",
  "redirect_uris": ["https://claude.ai/api/mcp/callback"]
}

Response (201 Created):

json
{
  "client_id": "mcp_<32 hex>",
  "client_secret": "<64 hex>",
  "client_name": "Claude.ai",
  "redirect_uris": ["https://claude.ai/api/mcp/callback"],
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "token_endpoint_auth_method": "client_secret_post"
}

3. Authorization

Standard authorization-code with PKCE (S256 only). The user is redirected to the consent page on /api/mcp/oauth/authorize and approves the requested scopes. Auth codes have a 5-minute TTL.

4. Token exchange

POST /api/mcp/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&
code=<auth code>&
client_id=<client id>&
client_secret=<client secret>&
redirect_uri=<registered redirect>&
code_verifier=<PKCE verifier>

Response:

json
{
  "access_token": "<JWT, 1 hour>",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "<opaque, 30 days>",
  "scope": "openid profile"
}

Refresh tokens are rotated on every use of the refresh_token grant.

Calling tools

Tool calls go to the protocol endpoint:

POST /api/mcp/protocol
Authorization: Bearer <access token>
Content-Type: application/json

{ "jsonrpc": "2.0", "method": "tools/call", "id": 1, "params": { ... } }

The transport is Streamable HTTP (per the MCP spec). A fresh server context is created per request, scoped to the calling user.

Rate limiting

100 requests per minute per user. Implemented as a sliding-window counter. If the limiter's backing store is unreachable the limiter fails open (the request is allowed).

Every response carries:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: <0–100>

Exceeding the limit returns:

json
{
  "jsonrpc": "2.0",
  "error": { "code": -32000, "message": "Rate limit exceeded. Try again shortly." },
  "id": null
}

with HTTP status 429.

Tool catalogue

Read tools — workforce and analytics

No scenario required.

ToolPurpose
get_organization_contextOrg overview — call this first
search_employeesSearch by name, email, geography, skill, or custom attributes
get_employee_detailsFull details including allocations and salary
rank_employeesBy salary or bonus — financial permission required
search_teamsTeam search by name or custom attributes
get_team_detailsMembers, vacancies, contractors, projects
search_projectsBy name, delivery status, owner, or custom attributes
get_project_detailsAllocations and financial data
find_projects_with_issuesCompleted-with-allocations or no-allocations
search_contractorsBy name or team
search_vacanciesBy role, team, status
get_geographiesLocations and IDs
list_job_rolesStandardised role titles
list_custom_attributesThe org's custom attribute definitions, for use with the search_* filters
query_analyticsSlice-and-dice workforce metrics (FTE, cost, headcount), AI spend and usage (AI_COST, AI_TOKENS, AI_REQUESTS, AI_SESSIONS), code-delivery throughput (PR_COUNT, COST_PER_PR, LINES_CHANGED) and effort submissions across teams, projects, employees, providers, models, repos, cost centres, work types
list_scenariosExisting scenario plans

Read tools — AI usage and security

AI spend/usage breakdowns route through query_analytics (see above) — the former dedicated spend tools were removed.

ToolPurpose
get_ai_security_metricsShadow AI usage, DLP signal counts, and customer-data exfiltration event counts

Read tools — effort

ToolPurpose
get_project_effortActual effort breakdown for a project
get_team_effortActual effort breakdown for a team
get_unattributed_effortEffort not linked to any Flowstate project
get_effort_gapsPeople active in the period whose effort never reaches the capitalisation split
get_uncosted_effortWorkers with real effort but no resolvable salary or rate
get_submission_leaderboardOn-time vs late effort-report submissions per submitter

Read tools — code delivery

Require the effort-view permission; person cost requires financial permissions.

ToolPurpose
get_code_delivery_summaryPeriod-scoped PR throughput with attributed cost and effort, filterable by team, repo, or initiative
get_code_delivery_trendsMonthly code delivery trend plus per-initiative spend series
list_pull_requestsPaginated, searchable pull requests with attributed cost, effort, and project and initiative links

Read tools — portfolio and initiatives

ToolPurpose
search_initiativesSearch initiatives by name; filter by status, financeMode, or portfolio lens
get_initiative_detailsAn Initiative's finance fields + rollups (effort actuals, forecast, variance), child projects, and the derived capitalisable / isInert signal
list_portfolioThe Objective → Initiative → Project tree with a variance figure per Initiative
list_objectivesObjectives (the strategy layer) with Key-Result progress and linked-initiative counts

Read tools — budgets and forecast

Financial permissions required.

ToolPurpose
list_budget_requestsBudget requests, filterable by fiscal year and status
get_budget_requestA budget request with all its proposals and statuses
get_budget_proposalA budget proposal with status, assignee, and approver
get_budget_envelopeA team's budget envelope for a fiscal year, with divergence data
list_forecast_budget_snapshotsThe locked baselines that variance and drift compare the live forecast against
get_forecast_budget_drift_summaryHow far the live forecast has drifted from a locked budget snapshot

Read tools — CapEx and R&D

Financial or R&D-tax view permissions required.

ToolPurpose
get_capex_project_breakdownPer-project CapEx cost breakdown by resource type for the period
get_capex_initiative_breakdownCapitalisation cost rolled up by Initiative
get_capex_declared_vs_claimedDeclared vs claimed effort cost, surfacing unclaimed capitalisable spend
list_capex_claimsCapEx claims with initiative, project count, and status
list_rd_claimsR&D tax claims with workflow stage, project, fiscal year, and amounts

Write tools — scenarios

Every scenario write tool mutates a specific scenario, never live data. The user must call create_scenario first; subsequent write calls take a planId argument.

ToolPurpose
create_scenarioNew scenario; required before any write
add_team, update_team, delete_teamTeam CRUD
add_employee, update_employee, move_employee, terminate_employeesEmployee CRUD + lifecycle
add_vacancy, update_vacancy, delete_vacancyVacancy CRUD
add_contractor, update_contractor, delete_contractorContractor CRUD
add_project, update_project, delete_projectProject CRUD
allocate_employee_to_projectAllocate one engineer
allocate_contractor_to_projectAllocate a contractor
allocate_vacancy_to_projectAllocate a vacancy (planned hire)
allocate_team_to_projectAllocate a team
update_allocationChange FTE, dates, or end an allocation
remove_allocationRemove an allocation from the scenario
add_ai_agent, update_ai_agent, delete_ai_agentResourced AI agent CRUD; an agent runs a catalogue model and must have a human owner
update_budgetSet a budget amount on an effective date

Budget workflow tools

These operate on the live budget request and proposal workflow, not a scenario. They drive the same workflow as Finance → Budgets in the app.

ToolPurpose
create_budget_requestCreate a budget request for the organization
cancel_budget_requestCancel (close) a budget request, freeing the fiscal-year slot for a fresh cycle
assign_budget_proposalAssign a proposal to a user for an unmanaged team
submit_budget_proposalSubmit a proposal for review
approve_budget_proposalApprove a submitted proposal; a child proposal rolls up into its parent team's proposal
reject_budget_proposalReject a submitted proposal and return it to the assignee
merge_budget_proposalRefuses and explains the correct flow: budgets are finalised by locking the budget request, never merged
add_budget_proposal_commentAppend a comment to a proposal's review thread

Initiative finance tools

These manage initiatives. Unlike the scenario write tools above, they write LIVE data directly — an Initiative is a live governance object (like a cost centre or a budget), not a scenario edit, so there is no planId and no merge step. Money fields require financial permissions.

ToolPurpose
add_initiativeCreate a human-authored Initiative; optionally set financeMode and link an objective / portfolio lens
update_initiativePatch finance fields (financeMode, cost centres, lens, objective, budgetedCost/budgetCurrency, marginPct, committedFte, AI budget) — partial update
delete_initiativeDelete an Initiative (hard delete)
adopt_project_to_initiativeAdopt a project into an Initiative as its finance parent (≤1 parent; rejects with PROJECT_ALREADY_ADOPTED)
detach_project_from_initiativeDetach a project, restoring its own cost centre
create_capex_claimCreate a CapEx/R&D claim — Initiative-anchored (auto-pulls child projects; the Initiative must be CapEx-eligible) or ad-hoc over projectIds; the one-claim-per-project-per-period guard returns PROJECT_ALREADY_CLAIMED
delete_capitalisationDelete a per-project capitalisation workflow record (not the CapEx claim)

There is no MCP tool for planning a commitment (committed FTE + AI budget) onto an Initiative. That is a GraphQL-only operation. The GraphQL contract the Initiative tools map onto is documented at API → Initiatives.

Objective tools

Objectives are the strategy layer above Initiatives. These write live data.

ToolPurpose
add_objective, update_objective, delete_objectiveObjective CRUD: name, description, status, owner, start/target dates
add_key_result, update_key_result, delete_key_resultKey Result CRUD under an Objective: measurable targets with an increase/decrease direction

AI attribution

ToolPurpose
mark_my_ai_sessions_as_projectAttribute your own AI sessions to a project so their spend rolls up to it; sessions you don't own are ignored. Writes live data.

Live writes, not scenarios

The budget workflow, Initiative finance, Objective, and AI attribution tools mutate live data and inherit the caller's RBAC: add_initiative needs ROADMAP_INITIATIVES_CREATE, update_initiative / adopt / detach need ROADMAP_INITIATIVES_UPDATE, and create_capex_claim needs FINANCIALS_VIEW_SUMMARY.

Permissions

Tool calls inherit the OAuth user's Flowstate permissions. If the user can't read salaries in the UI, rank_employees returns a permission error from MCP. There's no privilege escalation through the connector.

Audit and SIEM

Every protocol request emits a SIEM event with:

  • The RPC method
  • The OAuth client name
  • The user, org and tenant identifiers
  • Request ID, IP, user agent

Rate-limit hits and request errors emit higher-severity events. If your org has SIEM integration wired up, MCP traffic flows there alongside the rest of the platform.

Scenarios via MCP

Write operations require an explicit planId. The model is:

  1. Call create_scenario({ title, type }) → returns a scenario id.
  2. Pass that id as planId on every subsequent write call.
  3. Once happy with the scenario, the user submits it for approval through the Workforce → Scenarios → Inbox UI. There's no MCP tool for approval — that's deliberately a human gate.

Read tools work against live data by default. To read from a specific scenario, pass planId to query_analytics (it accepts an optional scenario ID for what-if reads).

Disabling

To disable MCP for the whole org, ask Flowstate support to flip mcp_external_access off. The connector immediately starts returning 403 mcp_disabled. Existing tokens are not revoked but become unusable.

To revoke a single user's grant, remove the Flowstate connector from that user's chat client (Claude.ai or ChatGPT). Access tokens expire after one hour and refresh tokens are rotated on every use, so a removed connector loses access quickly. MCP OAuth grants do not appear on the Settings → Users & Access → API Keys page; that page manages REST API keys only.

See also

Flowstate Documentation