Documentation Get help

MCP server

Flowstate’s MCP server lets Claude, ChatGPT or another AI assistant read your Flowstate data and make changes in a scenario. You ask a question in the assistant, and it looks up the answer in Flowstate. It acts as you: it sees only what you can see in Flowstate, and changes only what you can change.

You need MCP access switched on for your organisation. Ask your Flowstate contact.

Choose your assistant

You useFollow
ClaudeConnect Flowstate to Claude
ChatGPTConnect Flowstate to ChatGPT
Another assistant that supports MCPConnect any other assistant, below

Connect any other assistant

  1. In Flowstate, select Ask Eddy in the top bar, then New chat (the +). Under Connect from Claude or ChatGPT, select Copy MCP URL. The address is your Flowstate address followed by /api/mcp/protocol, for example https://acme.flowstate.inc/api/mcp/protocol.
  2. In your assistant, add a remote MCP server and paste that address.
  3. When your assistant opens the Flowstate page, sign in with your Flowstate account and select Approve.

What you can ask

Try questions like these:

  • “Who joins the Platform team next month, and what will they cost?”
  • “What is the Payments team working on, and how much effort went into each project last quarter?”
  • “Which teams spent the most on AI this month?”
  • “How far has the forecast moved from the budget we locked in January?”
  • “Which projects are completed but still have people allocated?”
  • “Who hasn’t submitted their effort report for the last four weeks?”
  • “How much capitalisable spend haven’t we claimed yet this year?”
  • “Show me the functional structure under Engineering, with open seats.”

What it can change

In a scenario. Adding, moving or removing people, teams, vacancies, contractors, projects, allocations, budgets and AI agents happens in a scenario, never in live data. You see the changes in Flowstate, and they reach live data only when someone submits, approves and merges the scenario there. See Live data and scenarios.

In live data, straight away. Some changes apply as soon as the assistant makes them, exactly as if you’d made them in Flowstate:

  • the budget workflow: budget requests and proposals
  • initiatives, objectives and key results
  • CapEx claims and capitalisation records
  • functional groups, and who manages them
  • which project your own AI sessions count towards

Not at all. It can’t approve or merge a scenario, lock a budget, change settings (apart from who may see functional group cost), or do anything your Flowstate role doesn’t allow.

Tool catalogue

These are the 107 tools the assistant can call. Every tool runs with the permissions of the person who connected: if you can’t see salaries in Flowstate, a tool that needs them refuses or leaves them out. Where a tool needs a permission most people don’t have, it’s noted.

Start here

ToolWhat it answers
get_organization_contextYour organisation’s reporting currency and how many teams, people and projects it has
get_geographiesYour locations and their IDs
list_job_rolesYour job roles; takes an optional query
list_custom_attributesYour custom fields, to filter the search_* tools with customAttributeFilters; takes an optional entityType
list_scenariosExisting scenarios

People and teams

ToolWhat it answers
search_employeesFind people by name, email, location, skill or custom field
get_employee_detailsOne person, with their team and project allocations and salary
rank_employeesThe highest or lowest paid people, by SALARY or BONUS. Needs View Detailed Financials
search_teamsFind a team by name or custom field and get its ID. Archived teams are left out unless archived is archived or all; an empty query matches every team
get_team_detailsOne team now: its people, open roles, contractors, parent and child teams, the projects it’s allocated to, and whether it’s archived
search_contractorsFind contractors by name, team or custom field; activeOnly defaults to true
search_vacanciesFind vacancies by role, team, status or custom field; status defaults to open

Projects, initiatives and objectives

ToolWhat it answers
search_projectsFind projects by name, deliveryStatus, owner or custom field. pmsLinked: false finds projects not linked to Jira, Azure DevOps or Linear; sourceSystem filters by how the project was created. Returns the total number of matches as well as the page
get_project_detailsOne project’s allocations, costs, how it was created and which PMS projects it’s linked to
find_projects_with_issuesProjects that are completed but still have allocations, or have no allocations at all
search_initiativesFind initiatives by name, status, financeMode or portfolio lens
get_initiative_detailsOne initiative’s finance fields, effort actuals, forecast and variance, its child projects, and whether it can be capitalised
list_portfolioThe objective → initiative → project tree, with a variance figure for each initiative
list_objectivesObjectives with key result progress and how many initiatives each has
get_project_value_summaryWhether a project returned what it promised: cost to build against forecast, AI share, and each value driver’s before, target and after. Pass projectId or projectName, or neither for the top projects

Effort

ToolWhat it answers
get_project_effortThe effort actually reported on a project, by person, with cost if you can see it
get_team_effortWhat a team reported time on, by project, with cost if you can see it. Values are person-days over the period
get_unattributed_effortEffort not linked to any Flowstate project
get_effort_gapsPeople active in the period with no tracked effort, or with tracked effort but no report filed
get_uncosted_effortPeople with effort but no salary or rate, so their effort is costed at nothing
get_submission_leaderboardOn-time and late effort report submissions, per person

Effort tools default to the last three months; pass startDate and endDate to change the period.

Analytics and the forecast

ToolWhat it answers
query_analyticsAny “X by Y” question: cost, FTE and headcount (actual or forecast), AI spend and usage, pull requests, and effort submissions, broken down by team, project, person, location, cost centre, provider, model, repository and more. Pass planId to query a scenario. Cost needs View Financial Summary

query_analytics takes metrics, dimensions and dataSource (ACTUAL, FORECAST or SUBMISSION), plus optional startDate, endDate, timeGranularity (MONTH, QUARTER or YEAR), filters, costSection, and sort, order and limit for top-N questions (limit is at most 25). The tool’s own description lists every metric and dimension. For AI spend examples, see AI usage.

Budgets

ToolWhat it answers
list_budget_requestsBudget requests, by fiscalYear and status
get_budget_requestOne budget request with its proposals and their statuses
get_budget_proposalOne proposal’s status, assignee and approver
get_budget_envelopeA team’s budget envelope for a fiscal year, and where it differs
list_forecast_budget_snapshotsThe locked budgets the forecast is compared against. Needs View Financial Summary
get_forecast_budget_drift_summaryHow far the live forecast has moved from a locked budget; takes a snapshotId. Needs View Financial Summary

CapEx and R&D

ToolWhat it answers
get_capex_project_breakdownCapEx cost per project, by resource type. Needs View Financial Summary
get_capex_initiative_breakdownCapitalisation cost rolled up by initiative; filter by capitalisationStatuses. Needs View Financial Summary
get_capex_declared_vs_claimedDeclared against claimed effort cost, showing capitalisable spend you haven’t claimed. Needs View Financial Summary
list_capex_claimsCapEx claims with their initiative, project count and status. Needs View R&D Tax Relief
list_rd_claimsR&D tax claims with stage, project, fiscal year and amounts. Needs View R&D Tax Relief

The three breakdowns take submittedOnly: true counts submitted effort only, false includes effort still in draft. It defaults to false, except on get_capex_declared_vs_claimed, where it defaults to true.

Code delivery

Need View Effort Tracking. Cost per person needs View Financial Summary.

ToolWhat it answers
get_code_delivery_summaryPull request throughput for a period with cost and effort; filter by teamIds, repoNames or initiativeIds
get_code_delivery_trendsMonthly code delivery and spend per initiative, with the same filters
list_pull_requestsPull requests with cost, effort and their project and initiative. unlinked: true finds ones linked to neither; page and limit page through them

AI and insights

ToolWhat it answers
get_ai_adoption_summaryPeople using AI in the last 30 days, weekly active users, tools in use, idle licensed seats and what they cost, and the teams adopting most and least
get_ai_security_metricsShadow AI use, data-loss signals and customer-data exfiltration events; groupBy is provider, subject or team
get_hybrid_workforce_summaryEmployees, contractors and AI agents: headcount, monthly cost, share and change against a year ago, and AI spend against the monthly cap. Needs View Financial Summary
get_business_metrics_summaryEvery active business metric with its function, volume, AI share, cost per unit and owner. Needs View Business Metrics; cost needs View Financial Summary

For AI spend and usage by team, person, provider or model, use query_analytics.

Functional groups

ToolWhat it answers
list_functional_groupsThe whole functional structure on one day: headcount, filled and open seats, FTE shortfall, arrivals and leavers in the next 30 days, managers and monthly cost
get_functional_groupOne group in full: seats and who fills them, managers, the next twelve months’ schedule and headcount, and its history

Both take asOf (a day, YYYY-MM-DD, default today) and an optional planId to read a scenario.

Change a scenario

Every tool here needs Edit Scenario Plans and a planId for a scenario that’s still a draft. Create one first with create_scenario.

ToolWhat it does
create_scenarioCreate a scenario and return its ID; takes title and an optional type (HEADCOUNT, BUDGET or RESTRUCTURE). Needs Use Scenario Plans
add_team, update_team, delete_teamAdd, change or remove a team
add_employee, update_employee, move_employeeAdd a person, change their salary, role, location or manager, or move them to another team
terminate_employeesEnd one or more people’s employment on a date; employeeIds is a comma-separated list
add_vacancy, update_vacancy, delete_vacancyAdd planned hires (count adds several at once), delay or change one, or remove it. targetEndDate makes a role fixed-term
add_contractor, update_contractor, delete_contractorAdd, change or remove a contractor, with their rate and rateType
add_project, update_project, delete_projectAdd, change or remove a project
allocate_employee_to_project, allocate_contractor_to_project, allocate_vacancy_to_project, allocate_team_to_projectPut a person, contractor, vacancy or team on a project at an FTE, for optional dates
update_allocationChange an allocation’s FTE or end date
remove_allocationRemove an allocation
add_ai_agent, update_ai_agent, delete_ai_agentAdd, change or remove an AI agent. Each runs a model from the catalogue and needs a human owner (ownerEmployeeId or ownerContractorId)
update_budgetSet a budget amount from an effectiveDate

Change functional groups

These change live data unless you pass a planId. Each follows the functional group’s own access: Edit all functional groups, or being a manager of the group or a group above it. The tools refuse with FEATURE_DISABLED if your organisation doesn’t have functional groups. See Map your functional organisation.

ToolWhat it does
add_functional_groupCreate a group from a start date. Without parentFunctionalGroupId it’s a top-level group, which needs Edit all functional groups
update_functional_groupReplace a group’s details. Send everything you want to keep: optional fields you leave out are cleared, apart from the type
move_functional_groupFile a group under another, or at the top, from an effectiveDate
add_functional_positionCreate a seat with the FTE it needs
update_functional_positionReplace a seat’s details; fields you leave out are cleared
fill_functional_positionPut exactly one employee, contractor or vacancy in a seat, for a share of it and set dates
update_functional_assignmentChange someone’s share of a seat or their dates
move_functional_positionFrom a day, change who a seat reports to (kind: POSITION), or move a person to another seat in the same group (kind: ASSIGNMENT)
add_functional_group_manager, remove_functional_group_managerAppoint or remove a manager. Needs Share functional groups or being a manager of that group. Always changes live access, even with a planId
set_functional_group_cost_visibilityWho may see what groups cost: HIDDEN, MANAGERS or EVERYONE. The same setting as Settings → Delivery → Functional groups, and needs the same access

Change budgets

These change live data, and work through the same steps as the budget workflow in Flowstate.

ToolWhat it does
create_budget_requestStart a budget request for a fiscalYear. Needs Create Budget Requests
cancel_budget_requestCancel a budget request so you can start again for that year. Needs Create Budget Requests
assign_budget_proposalGive a team without a manager’s proposal to someone. Needs Edit Budget Proposals
submit_budget_proposalSubmit a proposal for review. Needs Submit Budget Proposals
approve_budget_proposalApprove a submitted proposal; a team’s proposal rolls up into its parent team’s. Needs Approve Budget Proposals
reject_budget_proposalSend a proposal back to its assignee, with an optional comment. Needs Approve Budget Proposals
merge_budget_proposalDoesn’t merge. It explains that you finish a budget by locking the budget request in Flowstate
add_budget_proposal_commentComment on a proposal. Needs Edit Budget Proposals, and only an approver or the assignee can comment

Change initiatives, objectives and CapEx

These change live data. They take no planId.

ToolWhat it does
add_initiativeCreate an initiative, optionally with its financeMode, objective and portfolio lens. Needs Create Initiatives
update_initiativeChange only the finance fields you pass; null clears one. Needs Update Initiatives
delete_initiativeDelete an initiative. Needs Delete Initiatives
adopt_project_to_initiativePut a project under an initiative. A project can have only one; otherwise it refuses with PROJECT_ALREADY_ADOPTED. Needs Update Initiatives
detach_project_from_initiativeTake a project out of its initiative, restoring its own cost centre. Needs Update Initiatives
add_objectiveCreate an objective. Needs Create Initiatives
update_objective, add_key_result, update_key_result, delete_key_resultChange an objective, or add, change or delete its key results (each with a target and INCREASE or DECREASE). Needs Update Initiatives
delete_objectiveDelete an objective and its key results. Needs Delete Initiatives
create_capex_claimCreate a CapEx or R&D claim for a period: from an initiativeId (its projects come with it), or from chosen projectIds. A project can be in one claim per period; a second refuses with PROJECT_ALREADY_CLAIMED. Needs Create R&D Tax Relief Claims
delete_capitalisationDelete a project’s capitalisation record (not a CapEx claim). Needs Delete Financial Configuration

Your own AI sessions

ToolWhat it does
mark_my_ai_sessions_as_projectCount your own AI sessions towards a project, so their spend shows against it. Sessions that aren’t yours are skipped. Changes live data

If something’s not right

“MCP external access is not enabled for this organization” — MCP is switched off for your organisation. Ask your Flowstate contact.

There’s no Connect from Claude or ChatGPT box in Eddy — MCP isn’t switched on for your organisation yet.

“User is inactive or not a member of this organization” — your Flowstate account has been deactivated or moved to another organisation. Ask a Flowstate admin, then connect again.

The assistant says you don’t have permission — your Flowstate role doesn’t allow it. Salaries and costs, for example, need a financial permission. Ask a Flowstate admin.

A scenario change is refused — the scenario isn’t a draft any more, or it belongs to a budget proposal that’s been submitted. Create a new scenario, or reopen the proposal in Flowstate.

“Rate limit exceeded. Try again shortly.” — you’ve made too many requests in a minute. Wait a minute and ask again.

To disconnect, remove the Flowstate connector from your assistant. If MCP is switched off for your organisation, every connection stops working straight away.

For developers

This part is for people building their own MCP client or checking a connection.

Transport and endpoint

The server speaks MCP over Streamable HTTP, without sessions:

https://{tenant}.flowstate.inc/api/mcp/protocol
MethodPathPurpose
GET/.well-known/oauth-authorization-serverOAuth server metadata (RFC 8414)
POST/api/mcp/oauth/registerDynamic client registration (RFC 7591)
GET / POST/api/mcp/oauth/authorizeSign-in and consent; issues the authorisation code
POST/api/mcp/oauth/tokenCode exchange and refresh
POST/api/mcp/protocolMCP JSON-RPC requests
GET/api/mcp/protocolServer-sent events stream
DELETE/api/mcp/protocolSession end (accepted; there’s no session to close)

Every protocol request carries the access token:

POST /api/mcp/protocol
Authorization: Bearer <access token>
Content-Type: application/json
Accept: application/json, text/event-stream

{ "jsonrpc": "2.0", "id": 1, "method": "tools/list" }

Tool results come back as a single text content item holding JSON. A tool refuses in one of two ways: its JSON has an error field, or the result is marked isError: true with the reason as text. A missing permission reads Forbidden: You do not have the required permission: ….

Authentication

MCP uses OAuth 2.0 authorisation code with PKCE. Each person signs in with their own Flowstate account. There’s no API key for MCP: REST API keys from Settings → Users & Access → API Keys don’t work on /api/mcp/protocol, and MCP connections don’t appear on that page.

1. Discover. GET /.well-known/oauth-authorization-server on your Flowstate address:

{
  "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. Register. No authentication needed:

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

{ "client_name": "My assistant", "redirect_uris": ["https://example.com/callback"] }
  • client_name is required, at most 200 characters.
  • redirect_uris holds one to five addresses, each https, or http on localhost, 127.0.0.1 or [::1].

The 201 response carries client_id and client_secret, with token_endpoint_auth_method: "client_secret_post". Registration is limited to 20 an hour per IP address.

3. Authorise. Send the person to /api/mcp/oauth/authorize with:

  • response_type=code
  • client_id and redirect_uri, which must be one you registered
  • code_challenge and code_challenge_method=S256
  • an optional state

If they aren’t signed in to Flowstate, they’re asked to sign in first. When they approve, you get code and state back on the redirect; if they deny, you get error=access_denied. Codes can be used once, within five minutes.

4. Exchange the code.

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

grant_type=authorization_code&code=<code>&client_id=<client id>&client_secret=<client secret>&redirect_uri=<redirect uri>&code_verifier=<PKCE verifier>
{
  "access_token": "<JWT>",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "<opaque>"
}

5. Refresh. Send grant_type=refresh_token with refresh_token, client_id and client_secret.

  • Access tokens last an hour.
  • Refresh tokens last 30 days.
  • Each refresh returns a new refresh token, and the old one stops working.

Permissions aren’t stored in the token. Every call checks the person’s current Flowstate role, so a role change applies straight away. A person who’s deactivated, or whose organisation has MCP switched off, is refused on their next call.

Scenarios

Scenario writes take a planId:

  1. Call create_scenario for a new scenario, or list_scenarios to find an existing one.
  2. Pass its id as planId on each write.
  3. Read it back with query_analytics, or the functional group reads, using the same planId.

A write is refused if the person doesn’t have Edit Scenario Plans, if the scenario belongs to another organisation, if it isn’t a draft, or if it backs a budget proposal that’s been submitted, is in review or has been approved. Merging happens in Flowstate. See Scenarios.

Rate limiting

Protocol requests are limited to 100 a minute per person, in fixed one-minute windows. POST responses carry:

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

Over the limit:

HTTP/1.1 429 Too Many Requests
{
  "jsonrpc": "2.0",
  "error": { "code": -32000, "message": "Rate limit exceeded. Try again shortly." },
  "id": null
}

Errors

StatusWhenBody
400A missing or invalid OAuth parameter, an unknown client, an unregistered redirect address, or an expired or reused code or refresh token{ "error": "invalid_request" | "invalid_client" | "invalid_grant" | "unsupported_grant_type", "error_description": "…" }
401No bearer token, or it’s invalid or expired{ "error": "Missing or invalid Authorization header" }, { "error": "Invalid token" } or { "error": "Token expired" }
401The person is deactivated or no longer in the organisation{ "error": "User is inactive or not a member of this organization" }
401Wrong client secret at the token endpoint{ "error": "invalid_client" }
403MCP is switched off, on a protocol request{ "error": "MCP external access is not enabled for this organization" }
403MCP is switched off, during sign-in or refresh{ "error": "mcp_disabled", "error_description": "MCP external access is not enabled for this organization" }
429Rate limit reachedJSON-RPC error -32000, as above
500The request failed on our sideJSON-RPC error -32603, "Internal server error"

When a token expires, refresh it and retry. On 429, wait for the next minute.

Audit

Each protocol request is recorded as an audit event with:

  • the MCP method
  • the person, organisation and tenant
  • the request ID, IP address and user agent

Consent, token issue, refresh and client registration are recorded too. If you’ve connected a SIEM, these events arrive there with the rest of Flowstate’s.

See also