Skip to main content

MCP API Reference

Reference for the FlowPOS MCP server in apps/backend/src/mcp/.

This page documents current behavior from source code (controllers, guards, services, and tool definitions), including:

  • HTTP endpoints
  • Auth and token flows
  • Session lifecycle
  • Runtime tool catalog and visibility rules

The current runtime catalog registers 35 tools. If an AI client shows a smaller list, start with the visibility and scope rules below before assuming a registration bug.


Architecture map (hexagonal)

domain/
mcp-principal.interface.ts
mcp-tool.interface.ts
mcp-api-key-repository.domain.ts

application/
mcp-api-key.service.ts
mcp-token.service.ts
mcp-session.service.ts
mcp-tools-initializer.service.ts

infrastructure/
mcp-api-key.repository.ts
mcp-prompts.handler.ts
mcp-resources.handler.ts

interfaces/
mcp.controller.ts
mcp-token.controller.ts
mcp-api-key.controller.ts
mcp-well-known.controller.ts
guards/

Infrastructure handles HTTP, Redis, and DB details. Domain defines the principal/tool contracts and security errors; application services implement token exchange, session state, and tool registration.


Authentication modes

FlowPOS supports two runtime credential types on the same /mcp transport. Both are sent as Authorization: Bearer ...; McpAuthGuard tries API key validation first and MCP JWT validation second.

ModeBest forBearer valuePrincipal sourceSession state
V1 API keyinternal automations, server-to-server scriptsfp_mcp_<token>mcp_api_key metadatain-memory transport only
V2 MCP JWTmerchant-facing assistantsJWT from POST /mcp/tokenFirebase user -> active business_user membershipsin-memory transport + Redis principal mirror

V1: API key

  • Key management endpoints: /mcp/keys/*
  • Runtime auth: Authorization: Bearer fp_mcp_<token>
  • Key is validated by SHA-256 hash lookup in mcp_api_key
  • Principal comes from DB key metadata:
    • role
    • businessId -> activeBusinessId
    • scopes
    • createdBy -> userId (nullable)

V2: Firebase -> MCP JWT

  • Exchange endpoint: POST /mcp/token
  • Refresh endpoint: POST /mcp/token/refresh
  • Runtime auth: Authorization: Bearer <jwt>
  • Role is always merchant for V2 tokens
  • Default token TTL is 24 hours (MCP_TOKEN_TTL_SECONDS overrides it)
  • Refresh accepts recently expired tokens for up to 7 days and re-resolves memberships from the database
  • Backend startup fails if MCP_TOKEN_SECRET is missing
  • Scopes are fixed by token service:
    • pos:read
    • pos:write
    • reports:read
    • psa:read
    • pos:intents

Guard chain on /mcp

McpAuthGuard attempts:

  1. McpApiKeyGuard
  2. McpTokenGuard

If both fail -> 401 Unauthorized.

Multi-business behavior

V2 tokens include every active business membership for the Firebase user. The first membership becomes activeBusinessId; multi-business users can call set_active_business when that tool is visible. Refresh preserves the previous active business if it is still authorized.

API-key sessions are scoped to the key's businessId, so they do not receive set_active_business.

Discovery metadata and token exchange

McpWellKnownController publishes:

  • GET /.well-known/oauth-authorization-server
  • GET /.well-known/oauth-protected-resource

The current metadata is intentionally minimal. It identifies /mcp as the protected resource and points token-capable clients to the FlowPOS Firebase-to-MCP exchange at POST /mcp/token.

Do not assume a standard authorization-code + PKCE flow exists in the backend today:

  • there is no current /mcp/oauth/authorize endpoint
  • there is no current /mcp/oauth/token endpoint
  • mcp-oauth-discovery-design is a historical/proposed design document, not the runtime contract

For AI clients that cannot supply a Firebase ID token, use the V1 API-key flow until a full OAuth controller is implemented.


Endpoint reference

POST /mcp/keys

Create a new MCP API key.

  • Auth: Firebase app auth (global AuthGuard)
  • Controller: McpApiKeyController
  • Raw key is returned once (rawKey) and cannot be recovered
  • platform_operator key creation is rejected by service

Request body:

{
"businessId": "uuid",
"role": "merchant",
"scopes": ["pos:read", "reports:read"],
"label": "My key",
"expiresAt": "2027-01-01T00:00:00.000Z"
}

Notes:

  • businessId is effectively required for this endpoint
  • Allowed roles in DTO include platform_operator, but service forbids it
  • rawKey is returned only in the create response; store it immediately

GET /mcp/keys?businessId=<uuid>

List key metadata for a business.

  • Auth: Firebase app auth
  • Does not return key hash or raw key

PATCH /mcp/keys/:id/revoke

Soft revoke key (isActive = false).

  • Auth: Firebase app auth
  • Effect is immediate for MCP auth

DELETE /mcp/keys/:id

Hard delete key record.

  • Auth: Firebase app auth
  • Response: 204

POST /mcp/token

Exchange Firebase ID token for MCP JWT.

  • Public endpoint (@IsPublic)
  • Validates Firebase token
  • Resolves internal user and active business memberships
  • Returns MCP access token and business metadata

Request:

{ "firebaseIdToken": "<firebase-id-token>" }

Response includes the JWT plus the active and authorized business context:

{
"accessToken": "<mcp-jwt>",
"expiresIn": 86400,
"role": "merchant",
"activeBusinessId": "<business-uuid>",
"authorizedBusinessIds": ["<business-uuid>"],
"authorizedBusinesses": [{ "id": "<business-uuid>", "name": "Acme Store" }],
"scopes": ["pos:read", "pos:write", "reports:read", "psa:read", "pos:intents"]
}

Common errors:

  • 401: invalid/expired Firebase token
  • 403: no active business memberships

POST /mcp/token/refresh

Refresh MCP JWT without requiring Firebase token.

  • Public endpoint
  • Accepts valid or expired JWT
  • Expired token is refreshable for up to 7 days
  • Memberships are reloaded from DB during refresh

Request:

{ "token": "<mcp-jwt>" }

Common errors:

  • 401: invalid signature/claims
  • 401: expired beyond 7-day grace window

GET /.well-known/oauth-authorization-server

OAuth Authorization Server Metadata (RFC 8414).


GET /.well-known/oauth-protected-resource

OAuth Protected Resource Metadata (RFC 9728).


POST /mcp

Primary MCP JSON-RPC endpoint.

  • Auth: McpAuthGuard (API key or MCP JWT)
  • First request must be initialize
  • On initialize success, response includes mcp-session-id header
  • Non-initialize requests require mcp-session-id

Error patterns:

  • 400: missing mcp-session-id on non-initialize request
  • 400: unknown session id
  • 401: invalid credential

Minimal initialize request:

curl -i -X POST "http://localhost:4000/mcp" \
-H "Authorization: Bearer <mcp-jwt-or-fp_mcp_key>" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-03-26",
"capabilities": {},
"clientInfo": { "name": "local-dev", "version": "0.1.0" }
}
}'

Save the mcp-session-id response header and send it on later requests:

curl -s -X POST "http://localhost:4000/mcp" \
-H "Authorization: Bearer <mcp-jwt-or-fp_mcp_key>" \
-H "mcp-session-id: <session-id-from-initialize>" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "get_active_business",
"arguments": {}
}
}'

GET /mcp

Open SSE stream for existing session.

  • Requires both:
    • Authorization
    • mcp-session-id

DELETE /mcp

Close existing session.

  • Requires both:
    • Authorization
    • mcp-session-id

Session lifecycle and state

Source: mcp-session.service.ts

  1. The first POST /mcp must be JSON-RPC initialize and must not require an existing session.
  2. The successful initialize response includes mcp-session-id.
  3. Tool calls, GET /mcp, and DELETE /mcp must include the same mcp-session-id.
  4. DELETE /mcp or transport close removes the in-memory session.

For V2 MCP JWT sessions, principal is additionally stored in Redis key:

mcp:session:{sessionId}

Important constraint:

  • Redis stores principal state, not transport
  • Session transport remains in-memory only
  • Sessions do not survive instance restart
  • GET /mcp and DELETE /mcp require the in-memory session; Redis cannot restore the streamable transport by itself
  • after deploys, restarts, or Cloud Run routing changes, clients should re-initialize and use the new mcp-session-id

Context tools (set_active_business, set_active_tenant) update the session principal. For V2 MCP JWT sessions, the Redis principal mirror is updated too; memberships, API keys, and Firebase claims are not modified.


Runtime tool catalog (35 tools)

The runtime tool list is built by McpToolsInitializer. Tool descriptions in source are written as instructions for AI clients. Keep this table aligned with the tool factories under apps/backend/src/mcp/tools/ when adding or renaming tools.

For implementation guidance and scope/visibility checklists, see MCP Tool Authoring Guide.

Maintaining tool descriptions

Tool descriptions are public interface copy for AI clients. Keep them precise, imperative, and source-aligned:

  • tool name, description, input schema, and scope requirements live in each tool factory under apps/backend/src/mcp/tools/*
  • McpToolsInitializer is the registration list; a factory that is not registered there is not visible at runtime
  • visibility flags (operatorOnly, multiBusinessOnly, skipTenantCheck) are part of the domain contract in McpToolDefinition
  • prompt and resource copy is shared from packages/global/consts/mcp-capabilities.consts.ts and registered by McpPromptsHandler / McpResourcesHandler

When adding or renaming a tool:

  1. Add or update the tool factory, including Zod input descriptions.
  2. Register it in McpToolsInitializer.
  3. Assign the narrowest required scopes and visibility flags.
  4. Add or update registry/tool tests for visibility and handler behavior.
  5. Update this table and any related runbook entry.

Platform and context tools (5)

ToolWhen to useScopes / visibility
list_tenantsPlatform operator lists registered businesses before selecting a tenant.operator only
set_active_tenantPlatform operator switches tenant context for later domain calls.operator only, no tenant check
set_active_businessMulti-business merchant switches active business within one session.visible only when authorizedBusinessIds.length > 1
get_active_businessAI client or developer orients itself with active business metadata and locations.pos:read
list_locationsResolve location UUIDs returned by inventory and sales tools.pos:read

Commerce and reporting tools (15)

ToolPurposeScopes
get_productsPaginated product listing with optional filterspos:read
search_productsFast lookup by name/SKU/barcodepos:read
get_inventoryInventory snapshot per product/locationpos:read
get_low_stock_alertsFlat low-stock list by reorder thresholdpos:read
list_ordersPaginated restaurant order listingpos:read
get_orderRestaurant order detail by IDpos:read
create_orderCreate restaurant orderpos:write
void_transactionVoid retail sale transactionpos:write
list_salesPaginated retail sales listingpos:read
get_saleRetail sale detail by IDpos:read
list_purchasesPaginated purchase listingpos:read
get_purchasePurchase detail by IDpos:read
get_sales_reportRaw date-range totals (revenue, count, AOV)reports:read
get_top_productsTop-selling products by revenuereports:read
get_sales_by_locationRevenue breakdown by locationreports:read

FEL tools (7)

ToolPurposeScopes
get_taxpayer_infoNIT lookup via FEL shared infopos:read
list_credit_notesPaginated credit note listingpos:read
get_credit_noteCredit note detail by IDpos:read
list_debit_notesPaginated debit note listingpos:read
get_debit_noteDebit note detail by IDpos:read
list_cancellationsPaginated cancellation listingpos:read
get_cancellationCancellation detail by IDpos:read

Implementation Portal (PSA) tools (4)

ToolPurposeScopes
list_implementationsList implementation boardspsa:read
get_implementationFull board tree (phases and steps)psa:read
log_hoursLog time entry on hourly stepspsa:read
get_implementation_statusCompletion/status summary for one boardpsa:read

log_hours uses the authenticated principal's userId. Only platform_operator principals may override userId; merchant/API-key callers need a resolvable principal user ID.

Intent tools (4)

ToolPurposeScopes
summarize_dayDaily business narrative summarypos:intents
summarize_periodMulti-day summary with prior-period comparisonpos:intents
get_inventory_healthPrioritized low-stock/out-of-stock health reportpos:intents
get_client_healthPSA board health summary with hours and blockerspos:intents, psa:read

Tool argument and behavior quick reference

Use this section when debugging AI-client calls or changing tool descriptions in source. Argument names are the public contract exposed through each tool's Zod input schema.

Platform and context

ToolKey argumentsDefaults and constraints
list_tenantspage, sizeoperator only; default page=1, size=20; size max 100
set_active_tenantbusinessIdoperator only; requires a UUID; updates only the current MCP session principal
set_active_businessbusinessIdvisible only for multi-business principals; businessId must already be in authorizedBusinessIds; updates only the current MCP session principal
get_active_businessnonereturns business id/name/country and up to 100 locations for activeBusinessId
list_locationspage, limitdefault page=1, limit=50; limit max 100

Products, inventory, orders, sales, and purchases

ToolKey argumentsDefaults and constraints
get_productssearch, categoryId, page, limitdefault page=1, limit=20; limit max 100; category filter requires UUID
search_productsquery, limitquery is required; default limit=10; limit max 50; use get_products for paginated/category browsing
get_inventorylocationId, page, limitdefault page=1, limit=100; limit max 200; use list_locations to resolve location UUIDs
get_low_stock_alertspage, limitdefault page=1, limit=100; limit max 200; flat threshold list, not velocity-prioritized
list_orderslocationId, page, limitrestaurant orders only; default page=1, limit=20; limit max 100
get_orderorderIdrestaurant order detail by UUID; use get_sale for retail transactions
create_orderlocationId, employeeId, tableId, orderTyperestaurant order creation; locationId and employeeId required; orderType defaults to dine_in and accepts dine_in, takeout, delivery
void_transactiontransactionId, confirmretail sale void only; omitted confirm returns a preview; confirm: true executes the irreversible void
list_salespage, limit, status, createdAtFrom, createdAtToretail sales only; default page=1, limit=20; date filters require ISO 8601 date-times
get_salesaleIdretail sale detail by UUID
list_purchasespage, limit, status, createdAtFrom, createdAtTopurchase orders / goods received notes; default page=1, limit=20; date filters require ISO 8601 date-times
get_purchasepurchaseIdpurchase order detail by UUID

Reports and intent summaries

ToolKey argumentsDefaults and constraints
get_sales_reportfrom, to, locationIdraw structured totals; default range is the last 24 hours; locationId optional
get_top_productsfrom, to, limitdefault range is the last 30 days; default limit=10; limit max 50
get_sales_by_locationfrom, todefault range is the last 30 days
summarize_daydate, locationIddefaults to today; returns daily summary, yesterday comparison, top 3 products, and low-stock alert count; partial failures are returned in _partialErrors
summarize_periodfrom, toboth dates are required ISO 8601 date-times; prior-period comparison uses an equal-length period immediately before from; partial failures are returned in _partialErrors
get_inventory_healthlocationIdreturns up to 100 low-stock alerts prioritized with out-of-stock items first, then velocity score from 30-day top products; partial failures are returned in _partialErrors
get_client_healthimplementationIdrequires pos:intents and psa:read; computes completion, current phase, hours burned/estimated, blocker count, and next milestone from the board tree

FEL

ToolKey argumentsDefaults and constraints
get_taxpayer_infonitrequired NIT; uses active business FEL certifier through FelService.getSharedInfo
list_credit_notespage, limit, statusdefault page=1, limit=20; limit max 100
get_credit_notecreditNoteIdcredit note detail by UUID
list_debit_notespage, limit, statusdefault page=1, limit=20; limit max 100
get_debit_notedebitNoteIddebit note detail by UUID
list_cancellationspage, limit, statusdefault page=1, limit=20; limit max 100
get_cancellationcancellationIdcancellation detail by UUID

Implementation Portal (PSA)

ToolKey argumentsDefaults and constraints
list_implementationspage, limit, statusdefault page=1, limit=20; status accepts draft, active, completed, archived; total is currently returned as null and hasMore is inferred from page size
get_implementationboardIdfull board tree by UUID; use only when step-level detail is needed
get_implementation_statusboardIdlightweight status and completion count from the board tree
log_hoursstepId, hours, description, date, userIdstepId and positive hours required; date is ISO 8601 and defaults to now; non-operators cannot override userId; current scope is psa:read despite write behavior

Tenant scoping notes

  • List and aggregate tools pass principal.activeBusinessId into the owning service/repository call.
  • Context-switching tools only change the in-memory session principal and, for V2 sessions, the Redis principal mirror.
  • Detail-by-ID tools delegate to the owning module's application service by ID. When tightening isolation or adding new detail tools, verify tenant scoping in the owning service rather than relying on MCP wrapper code alone.
  • Tool schemas validate declared argument shapes before handlers run. Unknown fields are stripped by default because McpSessionService registers each schema through z.object(...).

Tool visibility rules

Visibility is filtered when a session is initialized through ToolRegistry.listFor. The MCP HTTP path registers only the tools visible to that principal on the session's McpServer; calls then invoke each registered tool handler with the current session principal.

ToolRegistry.dispatch also contains secondary scope and tenant guards for direct registry callers and tests. Do not assume those guards are an extra runtime layer for the streamable HTTP MCP path unless the controller is changed to route calls through dispatch.

Key rules:

  • operatorOnly tools -> only platform_operator
  • multiBusinessOnly tools -> only when authorizedBusinessIds.length > 1
  • pos:intents tools:
    • require pos:intents scope
    • never visible to tenant_developer
  • most non-operator tool logic expects an activeBusinessId; context-switching tools use skipTenantCheck = true

Tool visibility is calculated at session initialization. If role, scopes, or business memberships change, create a new session so tools/list reflects the new principal.


Scope reference

  • pos:read: product/inventory/order/sale/purchase/FEL reads
  • pos:write: create_order, void_transaction
  • reports:read: report tools
  • psa:read: PSA tools (list_implementations, log_hours, etc.)
  • pos:intents: intent tools

Note on log_hours: write behavior is intentionally protected by psa:read in current implementation.


Prompt shortcuts (8)

McpPromptsHandler registers merchant-facing prompt shortcuts on every session. Prompts are instructions for the AI client; they do not bypass tool visibility or scopes.

PromptTypical useTool path it asks the client to follow
daily_briefingDaily store-owner summarysummarize_day, then get_low_stock_alerts when alerts exist
end_of_day_reportClosing reportsummarize_day, get_top_products, get_inventory_health
weekly_reviewWeek-over-week performancesummarize_period for target and prior week
monthly_performanceMonth-over-month performancesummarize_period for target and prior month, plus get_top_products
low_stock_checkCurrent inventory riskget_inventory_health, get_low_stock_alerts
top_sellersBest-selling products for a periodget_top_products with calculated date range
restock_planSuggested replenishment actionsget_inventory_health, get_low_stock_alerts, get_top_products
sales_by_locationLocation comparisonget_active_business, get_sales_by_location, get_sales_report

Prompt arguments are optional strings parsed by the prompt handler, such as date, locationId, period, limit, month, weekOffset, and daysToProject.


Resources (5)

McpResourcesHandler registers static documentation resources and dynamic resource templates. Dynamic resources return instructions telling the client which tools to call for live data.

Resource URITypePurpose
flowpos://docs/quick-startstatic markdownAssistant capabilities and prompt shortcut overview
flowpos://docs/prompt-guidestatic markdownDetailed reference for the 8 prompts
flowpos://docs/currency-guidestatic markdownGuatemala business context, GTQ formatting, FEL terms, UTC-6
flowpos://business/profiledynamic templateLive business and location context; hydrate with get_active_business
flowpos://inventory/low-stockdynamic templateLive low-stock snapshot; hydrate with get_inventory_health and get_low_stock_alerts