List Flow Runs
Retrieves the run history (executions) of Tableau Prep flows on a site. Each flow run records one
execution attempt with its status, start/finish timestamps, progress, the flow it belongs to, the
background job id, and — for Failed runs on Tableau REST API >= 3.30 — a failureReason giving the
cause of the failure.
This is the dedicated, filterable, site-wide run-history tool:
- List Flows lists flow definitions, not executions.
- Get Flow returns recent runs for a single flow (as a capped sidecar). Use
list-flow-runsfor cross-flow questions ("all failures today") or deeper single-flow history.
Required permissions
- Site Role: Requires Explorer (Can Publish) role or higher
APIs called
- Get Flow Runs (requires Tableau REST API >= 3.10)
Required Tableau API scopes
When the MCP server authenticates with OAuth (connected-app JWT), this tool requests at most:
tableau:flow_runs:readtableau:flows:read— to resolve a failed run's flowwebpageUrlinto the run-history deep link inmcp.failureInsight. Only requested on that fallback path; when the runs carry a resolvedfailureReasonthe tool makes no such call.tableau:mcp_site_settings:read
The primary Get Flow Runs call requests only tableau:flow_runs:read plus the always-on
site-settings scope. tableau:flows:read is requested in a separate, best-effort call made
only when the returned window has a Failed run and no resolved failure reason. It resolves that
failed run's flow webpageUrl into the run-history deep link surfaced in mcp.failureInsight (see
Failure reporting). Calls with no failures—or with at least one resolved
reason—never request tableau:flows:read, so they succeed against a Connected App that grants only
tableau:flow_runs:read. See OAuth configuration for how
scopes are negotiated.
Caller-role visibility
- Non-admin callers — Tableau returns runs only for flows the caller can run (the Execute / "Run Flow Now" capability), not flows they can only view. This is stricter than the Tableau web UI run-history page (which needs only view permission), so a user may see a flow's runs in the browser yet get nothing for that flow here.
- Server / site-admin callers — Tableau returns runs for every flow on the site, so
mcp.resultInfo.truncatedis more likely to betrueon a broad call.
Optional arguments
filter
A filter expression in the format field:operator:value. Multiple expressions are combined with a
comma using a logical AND.
| Field | Operators | Notes |
|---|---|---|
flowId | eq, in | Flow LUID (UUID). The most common filter — scope runs to one or more flows. |
userId | eq, in | LUID of the user who initiated the run. |
progress | eq, gt, gte, lt, lte | Percent complete (0–100). |
startedAt | eq, gt, gte, lt, lte | ISO 8601 YYYY-MM-DDTHH:MM:SSZ or date-only YYYY-MM-DD (midnight UTC). |
completedAt | eq, gt, gte, lt, lte | ISO 8601 YYYY-MM-DDTHH:MM:SSZ or date-only YYYY-MM-DD (midnight UTC). |
status | eq, in | One of Pending, InProgress, Success, Failed, Cancelled. Server-side on REST API 3.30+; client-side fallback on older APIs. |
Examples:
flowId:eq:6f8a2966-e173-11e8-ae74-ffd84c19d7f3status:eq:Failed,startedAt:gt:2025-01-01status:in:[Failed,Cancelled]
status API-version behavior
REST API 3.30 and later support filtering and sorting flow runs by status; this tool passes those
expressions through unchanged. A multi-value status:in filter on those versions may contain only
terminal statuses (Success, Failed, and/or Cancelled). A mixed list such as
status:in:[Pending,Success] is rejected because the underlying query builder cannot represent the
required OR predicate without risking incorrect results.
On older REST API versions, the tool removes status from the server request and applies filtering
in-process. Pair status with a flowId and/or a startedAt window so the fallback operates on a
bounded set of runs. Status sorting is not available on older REST API versions; use startedAt,
completedAt, or progress sorting instead. Unknown status values are rejected with the allowed
list, and the value is case-sensitive.
flowId must be a UUID
flowId matches the flow's LUID (canonical 8-4-4-4-12 hex form). The Get Flow Runs endpoint
responds 404 for a flowId that does not resolve to a real, visible flow (a flow name, or a
valid-format but nonexistent UUID); the tool converts that 404 into an empty result rather than
surfacing a raw HTTP error. When the result is empty and flowId is not a UUID, the tool also
returns a recovery hint suggesting a List Flows lookup by name.
in: — bracket-and-comma form
Multi-value lists use the form flowId:in:[uuid1,uuid2] or status:in:[Failed,Cancelled], where
commas separate the items. Items are not quoted, so a single item's value cannot itself contain a
comma (every comma is read as an item separator).
sort
A sort expression in the format field:asc or field:desc using startedAt or completedAt.
When omitted, the result is ordered newest first by run recency — by completedAt, falling back
to startedAt for runs that haven't completed; runs with neither timestamp (e.g. Pending) sort
last. This is a best-effort "most recent" within the returned window.
Why the default isn't a plain
startedAt:desc. The Tableau Get Flow Runs endpoint orders rows whose chosen sort field is empty to the front under:desc.startedAtis empty for every never-started run (e.g.Cancelled-before-start), sostartedAt:descsurfaces a stale, unordered block of those runs ahead of genuinely recent ones. The tool fetchescompletedAt:desc(a far smaller empty band) and then re-orders the fetched window bycompletedAt ?? startedAt. Supplying an explicitsortis honored verbatim and bypasses this correction.
Example: completedAt:desc
limit
The maximum number of runs to return. The tool paginates the Tableau endpoint and returns at most
this many runs. When omitted, it falls back to the administrator cap if one is configured; otherwise
it returns the newest 100 runs as a safety backstop (reported as truncated with
truncationReason: "default-cap") rather than walking the entire run history. Pass an explicit
limit to go beyond the default.
See also: MAX_RESULT_LIMIT and
MAX_RESULT_LIMITS=list-flow-runs:N.
Response shape
The tool returns a JSON object:
{
"flowRuns": [
/* one record per run, see "Example result" below */
],
"mcp": {
"resultInfo": {
"returnedCount": 12,
"truncated": false
}
}
}
The top-level flowRuns array and mcp.resultInfo are always present. resultInfo reports
whether the returned list is complete:
returnedCount— the number of runs inflowRuns.truncated—falsemeansflowRunsis the complete set matching the request;truemeans more matching runs exist on the server than were returned.truncationReason— present only whentruncatedistrue:"requested-limit"— the caller's ownlimitcut the result short. Call again with a higherlimit."default-cap"— the caller supplied nolimitand no admin cap is configured, so the tool returned the newest 100 runs as a safety backstop rather than walking the entire run history (which accumulates quickly and would load the server). Pass a higherlimitand/or a narrowerfilter(e.g. a singleflowIdor astartedAtwindow) to go deeper."admin-cap"— a site-administrator per-call cap (MAX_RESULT_LIMITorMAX_RESULT_LIMITS=list-flow-runs:N) cut the result short. Narrow thefilter(e.g. a tighterstartedAtwindow or a singleflowId), or ask the administrator to raise the cap.
Unlike List Flows, there is no totalAvailable — the Tableau Flow Runs
endpoint does not return a total count, so completeness is reported via the truncated flag only
(computed with a "+1 probe" — the tool fetches one more run than limit to detect that more exist).
When truncated is true, report "at least N" — never invent a total.
:::tip For client / LLM authors mcp.resultInfo is a signal for the model, not text to show the
user. Translate it into one plain sentence — "These are all 12 matching runs" or "Here are the first
50; more match" — and never surface the field names to the end user. :::
Failure reporting
When the returned window contains a Failed run, the tool reports on it through up to two fields.
mcp.failureSummary appears whenever a Failed run carries a reason; mcp.failureInsight is added
whenever no reason resolves the failure. They therefore co-occur when every reason in the window is
available: false — the summary counts the failures, the link is where the cause can actually be read.
mcp.failureSummary
Present when the window's Failed runs carry a failureReason (Tableau REST API >= 3.30). It groups
the window's failures by the reported message and whether Tableau marked the reason available, so a
caller can tell one systemic problem from many unrelated ones without reading every record:
failedRunCount—Failedruns in the returned window.failedFlowCount— distinct flows with a failure in that window.reasons— one entry per distinct{ message, available }pair, largest first, as{ message, available, runCount, flowIds }.runCountis how many runs reported that pair, andflowIdslists the distinct flows it affected (its blast radius).
A group is not always a single root cause. Tableau reports a short localized message, and several
distinct underlying errors can map to the same one — a flow-execution failure and an authentication
failure both surface as Flow processing error, for example. The underlying error identifier is not
exposed by the REST API, so treat a group as one symptom, which may have more than one cause.
available: falsemeans Tableau could not determine the cause andmessageis a generic placeholder. Do not present it as a diagnosis. Unresolved groups are common on real sites, and are often the largest — to investigate one, call Get Flow on any of itsflowIdsand append/runHistoryto the returnedwebpageUrl.
failedRunCount and failedFlowCount cover all Failed runs, while reasons covers only those
carrying a reason, so sum(reasons[].runCount) can be less than failedRunCount. Both counts
describe the returned window only — when truncated is true they are not site-wide failure totals
or rates.
mcp.failureInsight
The fallback, for failures that cannot explain themselves — either the server predates REST API 3.30
(no failureReason at all) or every reason returned has available: false. It carries the same
failedRunCount / failedFlowCount (identical values, since both fields count the same window), plus
an example of { flowId, runHistoryUrl } for one affected flow (the most-recent failure) when
resolvable. Open runHistoryUrl in a browser and expand the failed run to read the error in the
Tableau UI.
Only one flow is resolved, which costs one extra
Query Flow
call; use Get Flow to resolve the others if failedFlowCount is greater than 1.
In a mixed window — some reasons resolved, some not — this field is deliberately absent, since the
resolved reasons already answer the question and the link would cost that extra call. An
available: false group in such a window therefore has no link of its own; reach one through
Get Flow as described above.
Bounded context (fail-closed)
A flow run carries no project or tag, so when the server is restricted to a
INCLUDE_PROJECT_IDS or
INCLUDE_TAGS bounded context this
tool cannot prove a run belongs to the allowed set. Rather than risk leaking runs for flows
outside the allow-list, it returns no runs and explains why. To inspect a specific allowed flow's
run history under a bounded context, use Get Flow with the flow id (it enforces the
bounded context on the flow itself).
Limitations
- Failure reasons need REST API >= 3.30, and only cover
Failedruns. On older serversfailureReasonis absent entirely and the tool falls back to themcp.failureInsightdeep link. Even on 3.30+, there is no reason for aCancelledrun and nothing explaining why a run is slow or stuckInProgress. - A reason is a short message, not a log.
failureReason.messageis a short localized cause. The full underlying job error is not surfaced by the public REST API and reading it requires site-administrator access to the job behindbackgroundJobId. Inspect the run in the Tableau UI for the detail. - No exact total-run count. The endpoint does not expose
totalAvailable; the tool reports only whether more runs exist viatruncated.
Example result
{
"flowRuns": [
{
"id": "a1a1a1a1-1111-1111-1111-111111111111",
"flowId": "d00700fe-28a0-4ece-a7af-5543ddf38a82",
"status": "Success",
"startedAt": "2025-04-01T10:00:00Z",
"completedAt": "2025-04-01T10:05:00Z",
"progress": 100,
"backgroundJobId": "b2b2b2b2-2222-2222-2222-222222222222"
}
],
"mcp": {
"resultInfo": {
"returnedCount": 1,
"truncated": false
}
}
}
With a failure, on Tableau REST API >= 3.30:
{
"flowRuns": [
{
"id": "a2a2a2a2-2222-2222-2222-222222222222",
"flowId": "d00700fe-28a0-4ece-a7af-5543ddf38a82",
"status": "Failed",
"startedAt": "2025-04-02T10:00:00Z",
"completedAt": "2025-04-02T10:01:00Z",
"progress": 100,
"backgroundJobId": "b3b3b3b3-3333-3333-3333-333333333333",
"failureReason": {
"available": true,
"message": "Table \"orders\" was not found."
}
}
],
"mcp": {
"resultInfo": {
"returnedCount": 1,
"truncated": false
},
"failureSummary": {
"failedRunCount": 1,
"failedFlowCount": 1,
"reasons": [
{
"message": "Table \"orders\" was not found.",
"available": true,
"runCount": 1,
"flowIds": ["d00700fe-28a0-4ece-a7af-5543ddf38a82"]
}
]
}
}
}
On a server below REST API 3.30 the same window has no failureReason and the tool returns the deep
link instead:
{
"mcp": {
"resultInfo": {
"returnedCount": 1,
"truncated": false
},
"failureInsight": {
"failedRunCount": 1,
"failedFlowCount": 1,
"example": {
"flowId": "d00700fe-28a0-4ece-a7af-5543ddf38a82",
"runHistoryUrl": "https://my.tableau.example.com/#/site/mysite/flows/96151/runHistory"
}
}
}
}