@W-22350774: Add Tableau Prep flows tools (list-flows, get-flow)#344
Open
showmethecold wants to merge 1 commit into
Open
@W-22350774: Add Tableau Prep flows tools (list-flows, get-flow)#344showmethecold wants to merge 1 commit into
showmethecold wants to merge 1 commit into
Conversation
25765ac to
358bed6
Compare
ed3f85f to
ca1ff10
Compare
Add two read-only MCP tools that wrap the public Tableau REST Flows API:
- list-flows: lists Prep flows on a site with field:operator:value
filtering (createdAt, name, ownerName, projectId, projectName, updatedAt)
and sorting. Every successful response includes an always-present
mcp.resultInfo { returnedCount, truncated, truncationReason?,
totalAvailable? } so a caller can distinguish a complete list from one
cut short by the caller's limit or the admin MAX_RESULT_LIMIT cap.
totalAvailable is surfaced only when no server-side bounded context
(PROJECT_IDS/TAGS) is configured, since that count is taken before the
tool's allow-list filtering.
- get-flow: retrieves a single flow with optional connections and run
history. Sidecar OAuth scopes are requested only when the corresponding
sidecar is included, and access is gated by the bounded-context resource
checker (isFlowAllowed) so PROJECT_IDS/TAGS restrictions apply. Run
history is bounded by flowRunLimit and emits a FLOW_RUNS_TRUNCATED
warning when more runs exist.
Supporting changes:
- Add the flow REST API definitions, Zod types, and methods, plus the
dedicated tableau:flows:read, tableau:flow_connections:read, and
tableau:flow_runs:read scopes.
- Harden parseAndValidateFilterString bracket / in: list handling
(repo-wide fix).
- Make project.description optional (Tableau returns it on embedded
<project> elements).
- Tools, types, unit tests, a description-quality eval test, and docs for
both tools.
ca1ff10 to
47b3d95
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Onboard Tableau Prep flows into the MCP server with two new read-only tools backed by Tableau's public REST API for flows.
New tools
list-flows: paginated listing with filter/sort matching Tableau's Flows API (filter fields:name,projectId,projectName,ownerName,createdAt,updatedAt; sort fields:name,createdAt,updatedAt). Empty-result messages include recovery hints when callers pass a value shape Tableau silently rejects (e.g. login/email/UUID forownerName, or a non-UUID forprojectId). Every response carries anmcp.resultInfocompleteness signal (see Design note).get-flow: primary flow metadata + output steps, with optional sidecar fetches for connections and recent flow runs. Both sidecars are best-effort; failures degrade to structured warnings undermcp.warnings(SIDECAR_FETCH_FAILED,VERSION_GATE_SKIPPED). The runs sidecar uses a "+1 probe" to detect truncation (Tableau's runs endpoint returns no pagination block); truncation surfaces as aFLOW_RUNS_TRUNCATEDwarning so callers can distinguish a complete history from a windowed view.Design note: result completeness & pagination (for maintainers' consideration)
This is the one place
list-flowsintentionally diverges from the existinglist-*tools, so calling it out explicitly.Bounded results + an explicit completeness signal — no caller-facing cursor in v0.
A site can hold thousands of flows. Rather than surface Tableau's
pageNumber/pageSizeoffset pagination as caller-managed state,list-flowspaginates internally and returns a single bounded result:min(caller 'limit', admin MAX_RESULT_LIMIT); when neither is set, the admin cap (if configured) is the backstop.MAX_RESULT_LIMIT— the tool never streams an unbounded set. This is deliberate: it keeps a single tool call bounded/predictable and protects the model's context-window/token budget.pageNumberpagination or a date-rangefilter), which the tool description and docs point to. A cursor can be added later without changing the response shape.Because results are bounded, completeness is made explicit. Every
list-flowsresponse carriesmcp.resultInfo:returnedCount— flows in this response.truncated+truncationReason(requested-limit|admin-cap) — whether more matched than were returned, and why.totalAvailable— Tableau's server-side total for the query, so the model can honestly report "showing N of M" and decide whether to narrow the filter. Surfaced only when no bounded context (PROJECT_IDS/TAGS) is configured, because that count is taken before the tool's allow-list filtering and would otherwise overstate the accessible total.For the Tableau MCP team:
list-flowsis currently the only tool that emitsmcp.resultInfo— the otherlist-*/get-*tools return bare arrays. We're comfortable shipping it scoped tolist-flows, but flagging it for a team decision on whether this completeness contract (especiallytruncated/totalAvailable) should be standardized across the other tools, since silent truncation under an admin cap is a latent issue for any list tool. Happy to generalize it if the team prefers.Security: bounded-context enforcement
get-flownow enforces the server bounded context. Previously it fetched any flow by ID, ignoring the operator'sPROJECT_IDS/TAGSallow-list — a scoping gapget-workbookalready closed. AddedFlowNotAllowedError(403) andresourceAccessChecker.isFlowAllowed, and gated the fetch behind it (reusing the flow the checker already fetched), with a dedicated flow scope set so the checker doesn't drag intableau:content:read.Auth scopes
Added MCP scope
tableau:mcp:flow:readand Tableau API scopestableau:flows:read,tableau:flow_connections:read,tableau:flow_runs:read; wired intotoolScopeMapand OAuth defaults.get-flowrequests only the scopes its requested sidecars need — connection/run scopes are added only whenincludeConnections/includeFlowRunsare true — so a metadata-only call succeeds against a connected app that grants onlytableau:flows:read.Shared parser fix
parseAndValidateFilterStringpreviously split filter strings on every comma, shredding multi-element:in:lists likename:in:[A,B]into broken sub-expressions. Live verification on REST 3.30 confirmed everylist-*tool that advertises:in:was silently broken on multi-element lists. Replaced the naive split with a bracket-aware top-level split (repo-wide fix).Descriptions: leaner & version-agnostic
Trimmed the tool descriptions (~−23%) for token budget (some clients cap the whole toolset ~5k tokens) and to follow the repo convention that descriptions stay version-agnostic (runtime gating handles version compatibility): de-duplicated repeated filter contracts, collapsed the run-history section, merged the sizing sections, trimmed examples, and removed
>= 3.10/ REST 3.30 references from the descriptions.Tests & docs
mcp.resultInfotruncation signaling, theownerName/projectIdrecovery hints, everyget-flowsidecar combination, bounded-context allow/deny (isFlowAllowed), theFLOW_RUNS_TRUNCATED"+1 probe" cases, and bracket-aware:in:parsing. Full unit suite green.tests/eval/flows.test.ts, site-data-independent) guarding tool selection + argument generation, so the trimmed descriptions still steer the model correctly.totalAvailable,get-flowsidecar combinations, live run-history truncation, recovery hints, and error paths (invalid filter, unknown flow id).docs/docs/tools/flows/with sizing guidance, filter semantics (case sensitivity, whitespace, ISO 8601Zrequirement), the recovery-hints contract, and a "Required Tableau API scopes" section per tool (flows use the dedicatedtableau:flows:readscope).Type of Change
Related Issues
Closes #373
Checklist
npm run version. For example,use
npm run version:patchfor a patch version bump.environment variable or changing its default value.
Contributor Agreement
By submitting this pull request, I confirm that:
its Contribution Checklist.