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
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; business rules (scope checks, token exchange behavior, tool filtering) live in application/domain.
Authentication modes
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:
rolebusinessId->activeBusinessIdscopescreatedBy->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
merchantfor V2 tokens - Scopes are fixed by token service:
pos:readpos:writereports:readpsa:readpos:intents
Guard chain on /mcp
McpAuthGuard attempts:
McpApiKeyGuardMcpTokenGuard
If both fail -> 401 Unauthorized.
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_operatorkey 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:
businessIdis effectively required for this endpoint- Allowed roles in DTO include
platform_operator, but service forbids it
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>" }
Common errors:
401: invalid/expired Firebase token403: 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/claims401: 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-idheader - Non-initialize requests require
mcp-session-id
Error patterns:
400: missingmcp-session-idon non-initialize request400: unknown session id401: invalid credential
GET /mcp
Open SSE stream for existing session.
- Requires both:
Authorizationmcp-session-id
DELETE /mcp
Close existing session.
- Requires both:
Authorizationmcp-session-id
Session lifecycle and state
Source: mcp-session.service.ts
initialize-> creates in-memory session transport- tool requests reuse session via
mcp-session-id DELETE /mcpor transport close -> session removed
For OAuth 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
Runtime tool catalog (35 tools)
The runtime tool list is built by McpToolsInitializer.
Platform and context tools (5)
| Tool | Purpose | Scopes |
|---|---|---|
list_tenants | List all businesses on the platform (operator only) | none |
set_active_tenant | Set active business for platform operator sessions | none |
set_active_business | Switch active business for multi-business merchants | none |
get_active_business | Fetch current business metadata and location list | pos:read |
list_locations | List locations for current active business | pos:read |
Commerce and reporting tools (15)
| Tool | Purpose | Scopes |
|---|---|---|
get_products | Paginated product listing with optional filters | pos:read |
search_products | Fast lookup by name/SKU/barcode | pos:read |
get_inventory | Inventory snapshot per product/location | pos:read |
get_low_stock_alerts | Flat low-stock list by reorder threshold | pos:read |
list_orders | Paginated restaurant order listing | pos:read |
get_order | Restaurant order detail by ID | pos:read |
create_order | Create restaurant order | pos:write |
void_transaction | Void retail sale transaction | pos:write |
list_sales | Paginated retail sales listing | pos:read |
get_sale | Retail sale detail by ID | pos:read |
list_purchases | Paginated purchase listing | pos:read |
get_purchase | Purchase detail by ID | pos:read |
get_sales_report | Raw date-range totals (revenue, count, AOV) | reports:read |
get_top_products | Top-selling products by revenue | reports:read |
get_sales_by_location | Revenue breakdown by location | reports:read |
FEL tools (7)
| Tool | Purpose | Scopes |
|---|---|---|
get_taxpayer_info | NIT lookup via FEL shared info | pos:read |
list_credit_notes | Paginated credit note listing | pos:read |
get_credit_note | Credit note detail by ID | pos:read |
list_debit_notes | Paginated debit note listing | pos:read |
get_debit_note | Debit note detail by ID | pos:read |
list_cancellations | Paginated cancellation listing | pos:read |
get_cancellation | Cancellation detail by ID | pos:read |
Implementation Portal (PSA) tools (4)
| Tool | Purpose | Scopes |
|---|---|---|
list_implementations | List implementation boards | psa:read |
get_implementation | Full board tree (phases and steps) | psa:read |
log_hours | Log time entry on hourly steps | psa:read |
get_implementation_status | Completion/status summary for one board | psa:read |
Intent tools (4)
| Tool | Purpose | Scopes |
|---|---|---|
summarize_day | Daily business narrative summary | pos:intents |
summarize_period | Multi-day summary with prior-period comparison | pos:intents |
get_inventory_health | Prioritized low-stock/out-of-stock health report | pos:intents |
get_client_health | PSA board health summary with hours and blockers | pos:intents, psa:read |
Tool visibility rules
Visibility is filtered at initialize (ToolRegistry.listFor) and rechecked at dispatch (ToolRegistry.dispatch).
Key rules:
operatorOnlytools -> onlyplatform_operatormultiBusinessOnlytools -> only whenauthorizedBusinessIds.length > 1pos:intentstools:- require
pos:intentsscope - never visible to
tenant_developer
- require
- non-operator tools require
activeBusinessIdunlessskipTenantCheck = true
Scope reference
pos:read: product/inventory/order/sale/purchase/FEL readspos:write:create_order,void_transactionreports:read: report toolspsa: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.