MCP — Tool Catalog
All tools require a valid Cognito access token with a tenant_id claim. No tool accepts tenant_id as a parameter. Tenant context is always from the JWT.
Tenants
| Tool | Permission | Description |
|---|---|---|
create_tenant(name, slug?, billing_provider?) | (authenticated) | Create a new tenant. Caller becomes owner. Provisions Stripe customer + subscription and seeds system roles atomically. No credit is applied — see the note below. billing_provider (“stripe” or “copebit”) only takes effect for a caller holding platform:billing:write; an ordinary caller supplying it gets 403, not a silent fallback. |
list_my_tenants() | (authenticated) | List all tenants the calling user is a member of, with role and last-seen per tenant. |
get_tenant() | tenant:settings:read | Fetch current tenant metadata (name, slug, tier, status, features map). |
update_tenant(name?, slug?) | tenant:settings:write | Update name or slug. Slug rename is transactional (old slug released, new slug reserved atomically). |
soft_delete_tenant(confirm: str) | tenant:delete | Owner only. Initiates 30-day soft-delete grace period. Cancels Stripe subscription and revokes all API keys. |
transfer_owner(new_owner_sub) | tenant:transfer:owner | Atomic owner role swap. Both the current and new owner must be active members. |
check_tenant_name(name) | (authenticated) | Check whether a slug is available and not reserved. |
list_tenant_features() | tenant:settings:read | Return the features map (hubspot, slack, apn, etc.) with current enabled/disabled state. |
set_tenant_feature(feature, enabled: bool) | tenant:features:write (owner, admin) | Toggle a feature flag. Disabling preserves all data. Emits FEATURE_TOGGLED audit event. |
tenant:features:write is currently held by owner and admin roles —
see Roles.
No free-credit balance is created on tenant creation — see “Free credit” in Metering.
Members and Invites
| Tool | Permission | Description |
|---|---|---|
list_members() | tenant:members:read | List all active members with their roles and last-seen timestamps. |
change_member_role(sub, new_role) | tenant:members:role:write | Change a member’s role. Cannot promote to or demote from owner role. |
remove_member(sub) | tenant:members:remove | Remove a member from the tenant. Owner cannot be removed. Revokes the removed member’s API keys. |
create_invite(email, role) | tenant:members:invite | Send an email invitation. Subject to rate limits: 50 pending invites per tenant, 10 per hour, 5 to the same email per 7 days. |
list_invites() | tenant:members:read | List all pending invitations. |
revoke_invite(sub) | tenant:members:invite | Delete a pending invitation. |
resend_invite(sub) | tenant:members:invite | Resend a pending invitation email. |
Users (self-service)
| Tool | Permission | Description |
|---|---|---|
get_my_profile() | (authenticated) | Return the calling user’s profile, active memberships, and default tenant. |
update_my_profile(name?, picture?) | (authenticated) | Update display name or profile picture URL. |
delete_my_account(confirm: str) | (authenticated) | GDPR self-deletion. Returns 409 if the user is the owner of an undeleted tenant. Anonymises email in all logs. |
select_active_tenant(target_tenant_id) | (authenticated) | Not supported over MCP. The parameter is target_tenant_id, not tenant_id, and the tool always returns {"error": "NOT_SUPPORTED", ...} directing the caller to the web UI — it does not re-issue a token. Use the app’s tenant switcher instead. |
export_my_data() | (authenticated) | Enqueue a GDPR data export job. Emits account.gdpr_export_ready notification with presigned S3 URL when done. |
get_my_export_status(job_id) | (authenticated) | Poll the status of an in-flight export job. |
Grants
| Tool | Permission | Description |
|---|---|---|
list_grants() | tenant:grants:read | List all per-membership grants in the current tenant. |
get_grant(sub) | tenant:grants:read (own grant readable by self) | Fetch a specific member’s grant. |
set_grant(sub, allowed_features?, allowed_regions?, monthly_budget_usd?, monthly_token_cap?, rate_limit_rpm?) | tenant:grants:write | Create or replace a member’s grant. Grants can only restrict — they cannot grant more than the member’s role allows. |
revoke_grant(sub) | tenant:grants:write | Remove a member’s grant override, returning them to role defaults. |
API Keys
| Tool | Permission | Description |
|---|---|---|
create_api_key(name, scopes, expires_in_days?) | tenant:keys:write | Create an API key. The raw key is returned once and never stored in plaintext. |
list_api_keys() | tenant:keys:read (owner/admin) | List API keys with prefix, scopes, status, and expiry. Matches the REST route. |
revoke_api_key(prefix) | tenant:keys:write (creator or admin+) | Revoke an API key immediately. |
rotate_api_key(prefix) | tenant:keys:write | Issue a new key with a 7-day grace period during which both old and new keys are valid. |
Billing
| Tool | Permission | Description |
|---|---|---|
get_billing_summary() | tenant:billing:read | Current subscription status, usage this billing period, and next invoice estimate. There is no free-credit balance to report — see Metering. |
list_invoices(limit?) | tenant:billing:read | Recent invoices with amounts and download links. |
open_billing_checkout(provider?) | tenant:billing:write | Start a new Stripe Checkout session. |
open_billing_portal() | tenant:billing:write | Generate a Stripe Customer Portal session URL. |
get_usage_breakdown(period?) | tenant:billing:read | Usage by metering dimension for the specified period (default: current month). Agentic Sales Manager and APN Sync usage currently reads back as 0 — see Metering. |
Cancellation is an organisation-level act (one org, one subscription) — there is no tenant-level cancel tool. Use cancel_org_subscription(org_id) (org:billing:write, org-admin of that org, or a platform admin) to schedule the org’s Stripe subscription to cancel at the end of the current billing period.
Notifications
| Tool | Permission | Description |
|---|---|---|
get_my_notification_preferences() | (authenticated) | Return your global preferences (enabled_categories, bounce_state, unsubscribe_token) together with your per-tenant category_overrides for the current tenant. |
update_my_notification_preferences(categories: {category: bool}, scope: "global" or "tenant") | (authenticated, self) | Enable or disable notification categories. Always-on categories are ignored. |
list_notification_categories() | (authenticated) | Full catalog of every notification category with description, default on/off, and always-on flag. |
send_test_notification(category) | tenant:notifications:write:self | Send a test email to yourself for the specified category. |
disable_notification_category(category, scope: "global" or "tenant") | (authenticated) | Disable a specific category, as an in-app preference toggle — distinct from the public one-click email unsubscribe link. |
unsubscribe_all(scope) | (authenticated) | Disable all non-always-on categories. |
list_notification_subscribers(category?) | tenant:notifications:read (admin+) | Show which members of the current tenant are subscribed to each category. |
list_notification_log(before?, category?, limit?) | tenant:notifications:audit:read (admin+) | Tenant-wide notification send log, paginated. Every logged entry currently has status: "sent" — there is no producer for any other value, so no status filter is exposed. |
re_verify_my_email() | (authenticated) | Send a delivery-check email to re-enable sending after a hard bounce; a no-op unless the account is currently hard_bounce (never re-sent to a complaint address). |
Audit
| Tool | Permission | Description |
|---|---|---|
list_audit_events(before?, type?, actor?, limit?) | tenant:notifications:audit:read | Paginated audit log for the current tenant. |
get_audit_event(event_id) | tenant:notifications:audit:read | Full event payload (fetched from S3 for large events). |
export_audit_log(start, end, format: "csv" or "json") | tenant:notifications:audit:read | Enqueue an async audit export job. |
HubSpot Integration
| Tool | Permission | Description |
|---|---|---|
get_hubspot_status() | tenant:integrations:hubspot:read | Connection state, scopes granted, hub_id, and webhook health. |
start_hubspot_oauth() | tenant:integrations:hubspot:connect | Return the HubSpot OAuth authorization URL to open in a browser. |
complete_hubspot_oauth(code, state) | tenant:integrations:hubspot:connect | Exchange authorization code for tokens and store the refresh token. |
test_hubspot() | tenant:integrations:hubspot:read | Call the HubSpot pipelines endpoint to verify the stored token is valid. |
refresh_hubspot_oauth() | tenant:integrations:hubspot:connect | Manually trigger a token refresh. |
disconnect_hubspot(confirm: str) | tenant:integrations:hubspot:disconnect | Delete the stored token and mark the integration disconnected. |
provision_hubspot_properties() | tenant:integrations:hubspot:connect | Idempotently create the flowstate_apn property group and all 10 custom properties. |
list_hubspot_custom_properties() | tenant:integrations:hubspot:read | Verify all 10 APN custom properties exist in the connected HubSpot portal. |
Slack Integration
| Tool | Permission | Description |
|---|---|---|
get_slack_status() | tenant:integrations:slack:read | Workspace name and bot token status. |
start_slack_oauth() | tenant:integrations:slack:write | Return the Slack OAuth authorization URL. In practice the onboarding UI is a bring-your-own-Slack-app wizard (copy a manifest, create the app, paste back Client ID/Secret) — there is no one-click install and no default-channel step. |
complete_slack_oauth(code, state) | tenant:integrations:slack:write | Exchange code for bot token and store it. |
test_slack() | tenant:integrations:slack:write | DM the caller’s own Slack account a fixed connection-test message. Takes no parameters — the target is always the caller’s own JWT email claim, never an agent-supplied value. |
list_slack_channels() | tenant:integrations:slack:write | List channels available to the FlowState bot. |
set_slack_notification_routing(rules) | tenant:integrations:slack:write | Store a list of {event_category, channel_id} routing rules. The two rules FlowState currently reads — deals.outdated and deals.weekly_digest — drive real delivery from the daily pipeline cron; any other event_category is accepted and saved but has no producer yet. See Slack Integration → Notifications. |
disconnect_slack() | tenant:integrations:slack:write | Delete the bot token and mark the integration disconnected. |
APN Integration — Config
Requires tenant:integrations:apn:configure (admin+).
| Tool | Description |
|---|---|
enable_apn_integration(role_arn, catalog: "Sandbox" or "AWS") | Validate and enable the APN integration. Runs AssumeRole + ListOpportunities verification. Implemented today. |
disable_apn_integration() | Soft-disable APN sync. Preserves all sync state. Implemented today. |
test_apn_connection() | Live AssumeRole + ListOpportunities(MaxResults=1) test. Implemented today. |
rotate_apn_external_id() | Rotate the tenant’s ExternalId used in the trust policy. |
get_apn_onboarding_artifacts() | Return the ExternalId, FlowState principal ARN, Tofu module, CFN template URL, and manual steps. |
list_apn_solutions() | List the tenant’s configured AWS Partner Central solutions. |
APN Integration — Read
Requires tenant:integrations:apn:read.
| Tool | Description |
|---|---|
list_apn_sync_state(state?, limit?, cursor?) | Paginated list of every deal’s sync state, optionally filtered by state. state, limit, and cursor are currently ignored — every deal is returned regardless. |
list_apn_conflicts() | All deals with conflict_state != none. |
APN Integration — Sync Actions
| Tool | Permission | Description |
|---|---|---|
resolve_apn_conflict_batch(deal_id, resolutions: dict) | tenant:integrations:apn:configure | Resolve a deal’s sync conflicts with a per-field {field_name: "hubspot" or "apn"} choice. Marks the conflict resolved and clears the deal’s conflict_state so list_apn_conflicts stops surfacing it. |
delete_apn_sync_state(deal_id) | tenant:integrations:apn:configure | Delete a deal’s APN sync-state record. Diagnostic and irreversible — fully unlinks the deal from APN sync tracking. |
trigger_apn_sync(deal_id?) | tenant:integrations:apn:configure | Re-enqueue deals for outbound APN sync. With deal_id, enqueues only that deal; without it, enqueues every eligible deal in the tenant. |
trigger_apn_reconcile() | tenant:integrations:apn:configure | Start an async reconcile of every APN opportunity linked to this tenant. Only one reconcile may be in flight per tenant. |
trigger_apn_inbound_sync(opp_id?) | tenant:integrations:apn:configure | Start an async inbound pull of APN lifecycle fields into HubSpot. With opp_id, narrows the pull to one opportunity. |
APN Integration — Stage Mapping
Parity with the REST routes under /v1/tenants/{id}/integrations/apn/stage-mapping*.
| Tool | Permission | Description |
|---|---|---|
get_apn_stage_mapping() | tenant:integrations:apn:read | Return the tenant’s HubSpot pipelines joined with the stored APN stage mapping, plus the seven valid APN stage values. |
set_apn_stage_mapping(pipelines: dict) | tenant:integrations:apn:configure | Persist pipeline_id → {hubspot_stage_id: apn_stage}. Rejects any value outside the seven-stage enum. |
clear_apn_stage_mapping(pipeline_id) | tenant:integrations:apn:configure | Clear the mapping for one HubSpot pipeline, typically after the pipeline was deleted in HubSpot. |
APN Integration — Matching
Parity with the REST routes under /v1/tenants/{id}/apn/match/sessions/*.
| Tool | Permission | Description |
|---|---|---|
start_apn_match_session() | tenant:integrations:apn:configure | Fetch all open APN opportunities and unlinked sync-flagged HubSpot deals, run Bedrock confidence matching, and persist a session for human confirmation. |
list_apn_match_sessions() | tenant:apn:read | List recent match sessions for the tenant, most-recent first. |
get_apn_match_session(session_id) | tenant:apn:read | Fetch one session with its full candidate and unmatched-side lists. |
confirm_apn_matches(session_id, candidate_ids) | tenant:integrations:apn:configure | Confirm one or more Bedrock-suggested matches. Writes apn_opportunity_id and apn_sync_with_aws_enabled=true on the HubSpot deals. |
manual_pair_apn_match(session_id, apn_opportunity_id, hubspot_deal_id) | tenant:integrations:apn:configure | Manually pair an opportunity with a deal from the session’s unmatched lists (drag-and-drop equivalent); implicitly confirms the pair. |
create_apn_deal(session_id, apn_opportunity_id, amount?) | tenant:integrations:apn:configure | Create a new HubSpot deal from an unmatched APN opportunity, link the pair, and drop the opportunity from the session’s unmatched list. |
Deal Analytics
Requires tenant:deals:read + tenant:mcp:invoke + features.hubspot enabled.
| Tool | Description |
|---|---|
get_deal_pipeline_summary(from_date?, to_date?) | Pipeline health summary: stage distribution, total value, close-date adherence. |
list_stalled_deals(days_inactive?, from_date?, to_date?) | Deals with no activity past days_inactive days (default 30). |
get_deal_details(deal_id) | Full deal data including AI summary, activities, and APN sync state. |
get_pipeline_stages() | All configured pipeline stages with deal counts and values. |
get_deals_by_stage(from_date?, to_date?) | Open deal counts and total values grouped by pipeline stage, optionally filtered by close date range. |
get_deal_activities(deal_id, max_activities?) | Activity history for a deal (notes, emails, calls, tasks). max_activities defaults to 20. |
get_owner_action_items(from_date?, to_date?) | Per-owner list of deals requiring action. |
get_inconsistent_deals() | Deals with data inconsistencies (missing close date, missing amount, etc.). |
get_rep_won_amounts(from_date?, to_date?) | Won revenue per rep for the date range. |
list_outdated_deals(limit?) | Deals past their close date still in an open stage. |
get_win_loss_deals(since_date?, include_activities?) | All closed deals (won and lost) since since_date. include_activities (default true) controls whether per-deal activity history is fetched and included. |
Pipeline Operations
| Tool | Permission | Description |
|---|---|---|
refresh_pipeline() | tenant:deals:analyze | Enqueue a pipeline snapshot refresh for the current tenant. |
get_queue_status() | tenant:settings:read | Current pipeline-analysis queue depth and active workers. |
analyze_deal(deal_id, prompt?) | tenant:deals:analyze | Invoke AI analysis on a single deal with an optional custom prompt. |
reanalyze_deal(deal_id) | tenant:deals:reanalyze | Force a fresh analysis, bypassing the 8-day summary cache. |
Goals and Win/Loss
| Tool | Permission | Description |
|---|---|---|
get_goals(year?) | tenant:goals:read | Tenant goals for the year: target won amount, target deal count, and per-rep quotas. |
set_goals(year, target_won_amount?, target_won_count?, rep_quotas?) | tenant:goals:write | Set or update tenant goals. |
get_win_loss(window?: "12m"/"24m"/"36m"/"48m") | tenant:win-loss:read | Aggregated win/loss data for the window. Defaults to "12m" when omitted. |
trigger_win_loss_analysis() | tenant:win-loss:trigger | Trigger a win/loss analysis batch — fans out up to 48 Bedrock month-workers. Gated on the write permission rather than tenant:win-loss:read, since a batch run has unbounded inference cost. |