> For the complete documentation index, see [llms.txt](https://docs.flowcp.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.flowcp.ai/reference/execution-logs.md).

# Execution logs

FlowCP records every MCP tool invocation in an execution log. Logs are available from the **Logs** tab on your server's page.

## Log fields

| Field                   | Type           | Description                                                                                                  |
| ----------------------- | -------------- | ------------------------------------------------------------------------------------------------------------ |
| **Tool name**           | string         | The name of the MCP tool that was called                                                                     |
| **End-user connection** | string         | The connection ID of the end user who triggered the call                                                     |
| **Status code**         | number         | The HTTP status code returned by your API (e.g. `200`, `404`, `500`)                                         |
| **Latency**             | number (ms)    | Total time from FlowCP receiving the MCP request to returning a response                                     |
| **Response tokens**     | number \| null | Estimated number of LLM tokens in the tool response (see below); empty for failed calls                      |
| **Error code**          | string \| null | A machine-readable error code if the call failed (e.g. `token_expired`, `tool_disabled`, `validation_error`) |
| **Timestamp**           | datetime       | When the call was made                                                                                       |

## Reading the logs

The Logs page shows the most recent entries first, paginated. Use the pagination controls at the bottom to load older entries.

### Status codes

API status codes follow standard HTTP semantics:

| Code range | Meaning                                           |
| ---------- | ------------------------------------------------- |
| `2xx`      | Successful call                                   |
| `4xx`      | Client error (bad input, not found, unauthorized) |
| `5xx`      | Upstream server error                             |

A `401` from your API typically means the end user's token has expired and could not be refreshed. The user must re-authorize.

### Error codes

FlowCP may return its own error codes before the upstream call is made:

| Error code              | Meaning                                                         |
| ----------------------- | --------------------------------------------------------------- |
| `tool_disabled`         | The tool was disabled between publish and the call              |
| `validation_error`      | The input did not pass the tool's Zod schema                    |
| `token_expired`         | The user's access token could not be refreshed                  |
| `token_missing`         | No connection exists for this user — they must authorize        |
| `confirmation_required` | A destructive tool was called without the required confirmation |
| `workspace_mismatch`    | Request was routed to the wrong workspace                       |

### Response tokens

Each successful call records an **estimate** of how many LLM tokens the tool's response payload consumes. This is the size of the response *after* FlowCP has applied any author-configured projection, field selection, and the hard response cap — i.e. the text the model actually receives — so it is a useful proxy for how much of an agent's context window your tools take up.

Because the MCP protocol does not reliably tell FlowCP which model is calling, the count is a **provider-agnostic estimate** computed with a standard byte-pair tokenizer (the `o200k_base` encoding used by recent GPT models). Treat it as a close approximation, not an exact per-model figure — actual token usage varies by model family.

Failed calls (`4xx`/`5xx`) do not record a token count, so the field is empty for those rows. The count is a size signal only — FlowCP never stores the response body itself (see [What is not logged](#what-is-not-logged)).

The **Stats** page aggregates this into a **Total response tokens** figure for the app and an **Avg tokens** column per tool.

## App-level log filtering

In addition to the per-server **Logs** tab, the app's **Logs** tab shows an execution log aggregated across **all of the app's published servers**. This view adds filters so you can narrow down to exactly the invocations you care about:

| Filter         | Options                                                           |
| -------------- | ----------------------------------------------------------------- |
| **Time range** | All time (default), Last 24h, Last 7 days, Last 30 days           |
| **Tool**       | Any tool that has been called in the selected window              |
| **Status**     | Success (`2xx`/`3xx`), Client error (`4xx`), Server error (`5xx`) |
| **Error code** | Any error code recorded in the selected window                    |
| **MCP client** | Any MCP client that has called the app                            |

Filter options are populated from real traffic in the selected time range. Use **Clear filters** to reset to the unfiltered, all-time view. The list is paginated newest-first; use **Load more** to fetch older entries.

The same privacy guarantees below apply to this view: it shows only aggregate audit fields and never any end-user PII beyond the truncated connection hash.

## Exporting logs

Both the per-server **Logs** tab and the app-level **Logs** tab have an **Export CSV** button that downloads the matching execution logs as a CSV file.

* On the **app-level** view, the export honors whatever filters are active — time range, tool, status, error code, and MCP client — so you can export exactly the slice you're looking at. Clear the filters to export everything in the selected time range.
* On the **per-server** view, the export contains that server's full log.
* The export is sorted newest-first and capped at the 10,000 most recent matching entries.

The CSV columns are: `createdAt` (ISO 8601), `toolName`, `statusCode`, `latencyMs`, `responseTokens`, `errorCode`, `mcpClientName`, `mcpClientVersion`, `llmModel`, and `endUserConnectionId`.

`responseTokens` is the estimated LLM token count of the response (see [Response tokens](#response-tokens)); it is empty for failed calls.

`mcpClientVersion` is the version the client reports in the MCP `initialize` handshake; it is empty when the client does not send one. `llmModel` is a best-effort capture of the underlying language model, populated only when a client or proxy sends an `x-llm-model` header. The MCP protocol does **not** carry the model, so this column is empty for most traffic — FlowCP never guesses the model from the client name. The same privacy guarantees below apply — the export never contains tokens, request or response bodies, or any end-user PII beyond the truncated connection hash.

## Reading logs from an AI agent

The app-level log is also available as an MCP tool — `list_app_logs` — on the [FlowCP platform MCP server](/guides/connect-to-flowcp-mcp.md). It is cursor-paginated and accepts the same `range`, `toolName`, `status`, `errorCode`, and `client` filters, so an agent can pull an audit trail programmatically. The same privacy guarantees apply — no tokens, bodies, or PII beyond the truncated connection hash.

## What is not logged

FlowCP never logs:

* Authorization headers or Bearer tokens
* OAuth access tokens or refresh tokens
* Request or response body contents
* Personally identifiable information beyond the connection ID

The **Response tokens** count is a redacted size signal derived from the response — an integer only. FlowCP never stores or logs the response body itself.

Logs are scoped to your workspace — you can only see logs for your own apps and servers.

## Log retention

Log entries are retained for 30 days.
