Semaphor
MCP

Tools Reference

Every Semaphor MCP tool, its arguments, what it returns, and which callers can use it.

Semaphor MCP has 21 tools in ten families. Each session sees only the tools its caller can use, so most sessions list fewer. Every tool name on these pages links here, and each tool's argument table is read from the server's own tool list, so it always matches the tool.

Who Sees Which Tools

Every session gets the session context, semantic discovery, governed query and dashboard tools, and semaphor_report_problem. The rest depend on who is calling:

ToolsWho gets them
semaphor_list_projectsInteractive logins only. Token sessions are pinned to the token's project.
Physical discovery and semaphor_query_sql_advancedSessions with SQL: an organization Author or higher (unless the token turns SQL off), or an end user whose token doesn't turn it off
semaphor_propose_changeUsers who can author dashboards: an organization Author or higher, or an end user with the Power User role. Proposing saves nothing, so read-only sessions get it too.
semaphor_apply_changeThe same users, in a session with the mcp:write scope
CallerSQLWriteTools
Logged in, Author or AdminYesYes21
Logged in, Author or AdminYesNo20
Logged in, ViewerNoNo14
Automation token, Author or AdminYesYes20
Automation token, Author or AdminYesNo19
Automation token, Author or AdminTurned offYes15
End-user token, Power UserYesYes20
End-user token, Power UserTurned offNo14
End-user token, ViewerYesNo18
End-user token, ViewerTurned offNo13

Hiding a tool is for clarity, not the security control: the server authorizes every call. An argument that reaches physical tables or SQL inside a tool every caller sees (a physical source in semaphor_matrix, for example) returns capability_required for a session without SQL. Call semaphor_get_access_context when a tool you expected is missing.

Common Arguments

ArgumentDescription
projectIdThe project to work in. Interactive sessions pass it on project-scoped tools; a user with exactly one project can omit it. Token sessions use the token's project, and passing another project returns an error.
responseFormat"json" (the default) or "markdown". JSON results also arrive as structured content when your client supports it.

Argument names are camelCase. A tool rejects an argument it doesn't define and names it in the error, so a misspelled argument is never silently ignored:

An unknown argument
MCP error -32602: Input validation error: Invalid arguments for tool semaphor_get_analysis_context: Unrecognized key(s) in object: 'question'

Session

semaphor_list_projects

Lists the projects you can access. Available only in interactive sessions.

ArgumentTypeRequiredDescription
responseFormat"markdown", "json"No (default "json")Output format: "markdown" for human-readable or "json" for machine-readable

semaphor_get_access_context

Reports who the session is and what it can do. Call it when a tool you expected is missing.

ArgumentTypeRequiredDescription
responseFormat"markdown", "json"No (default "json")Output format: "markdown" for human-readable or "json" for machine-readable

Returns: actorType (organization or tenant), userId, organizationId, projectId, projectCount, accountProjectListingAvailable, scopeSummary (one sentence), mode (org_interactive, org_headless or tenant_embedded), credential, scopes and capabilities.sql. End-user sessions add tenantId and the token's semanticDomainAccess.

Example response
{
  "actorType": "organization",
  "projectId": "p_abc123",
  "mode": "org_headless",
  "credential": "mcp_project_token",
  "scopes": ["mcp:read"],
  "capabilities": { "sql": true },
  "accountProjectListingAvailable": false
}

Semantic Discovery

semaphor_get_analysis_context

The first call for any data question. It checks the semantic domains the caller can see and recommends where to go next.

ArgumentTypeRequiredDescription
projectIdstringNoThe project to work in. Interactive sessions pass it on project-scoped tools (a user with one project can omit it); token sessions use the token's project.
responseFormat"markdown", "json"No (default "json")Output format: "markdown" for human-readable or "json" for machine-readable

Returns: recommendedPath, recommendedNextTool, defaultAnalysisTool, fallbackAnalysisTool, guidance, semanticDomains, semanticDomainCount, fallbackConnections, projectId, projectCount, scopeSummary, actorType, mode and credential.

recommendedPath is one of:

ValueMeaning
semanticSemantic domains are available. Next: semaphor_list_semantic_domains.
physicalNo semantic domains, but the caller can use connections. Next: semaphor_list_connections.
restrictedDomains exist, but the token's semanticDomainAccess grants none of them.
unavailableNothing the caller can query in this project.
Example response (shortened)
{
  "projectId": "p_abc123",
  "semanticDomainCount": 3,
  "recommendedPath": "semantic",
  "recommendedNextTool": "semaphor_list_semantic_domains",
  "defaultAnalysisTool": "semaphor_analyze",
  "fallbackAnalysisTool": "semaphor_query_sql_advanced",
  "guidance": "Use semantic domains first for business questions. Then call semaphor_list_datasets for the chosen domain...",
  "semanticDomains": [{ "id": "dom_abc", "name": "sales", "label": "Sales" }]
}

semaphor_list_semantic_domains

Lists the semantic domains the caller can see.

ArgumentTypeRequiredDescription
projectIdstringNoProject ID to list domains from (uses authenticated user project if not specified)

Returns: domains, each with id, projectId, name, label, description, createdAt and createdBy.

semaphor_list_datasets

Lists the datasets in a semantic domain. Call it before semaphor_get_dataset_schema so the agent never guesses dataset names.

ArgumentTypeRequiredDescription
projectIdstringNoThe project to work in. Interactive sessions pass it on project-scoped tools (a user with one project can omit it); token sessions use the token's project.
domainIdstringYesSemantic domain ID

Returns: datasets, each with name, label, description, analyticsRole (such as fact or dimension), type, domainId, and connection metadata (connectionId, connectionType, dialect, database, table).

semaphor_get_dataset_schema

Returns the fields of a semantic dataset, or the columns of a physical table.

  • Semantic mode (preferred): pass domainId and datasetName. Any connectionId passed along is ignored.
  • Physical mode: pass mode: "physical" or connection coordinates (connectionId or connectionName, with databaseName, schemaName and tableName as the connection needs). Physical mode needs SQL access.
ArgumentTypeRequiredDescription
projectIdstringNoThe project to work in. Interactive sessions pass it on project-scoped tools (a user with one project can omit it); token sessions use the token's project.
mode"semantic", "physical"No"semantic" (preferred) or "physical"; inferred from the coordinates you pass
domainIdstringNoSemantic mode: the domain from semaphor_list_semantic_domains
datasetNamestringNoSemantic mode: the exact dataset name from semaphor_list_datasets
connectionIdstringNoPhysical mode: the connection from semaphor_list_connections (ignored in semantic mode)
connectionNamestringNoPhysical mode: the connection by name, instead of connectionId
databaseNamestringNoPhysical mode: the database or catalog, when the connection has them
schemaNamestringNoPhysical mode: the schema, when the connection has them
tableNamestringNoPhysical mode: the table
includeCalculatedFieldsbooleanNo (default true)Include calculated fields (default true)
responseDetail"compact", "standard"NoUse "compact" for agent-friendly fields-only schema output that omits verbose relationship previews and heavy field metadata; key fields keep their references.

Returns: dataset (its source), fields, recommendedNextTool and toolChoiceGuidance. Each field has name, label, dataType, modelRole (fact, dimension or identifier), fieldKind and description; with the standard detail it also carries its source and a grounded fieldRef. With "responseDetail": "compact" a field leaves both out, since the dataset's location is in dataset once; name a field in a query as { "name", "datasetName" }. In semantic mode with the standard detail, reachableDatasets previews related datasets one join away, with example refs such as { "name": "supplier_name", "datasetName": "dim_supplier" }. A field also carries its aiContext (instructions, synonyms and example questions), and its format and isDisplayField when set: some of the values a semantic model change replaces; the proposal's own lines show the rest.

Semantic mode
{ "domainId": "dom_abc", "datasetName": "orders", "responseDetail": "compact" }

semaphor_get_domain_relationships

Returns the relationships between datasets in a semantic domain. Useful before multi-dataset questions.

ArgumentTypeRequiredDescription
projectIdstringNoThe project to work in. Interactive sessions pass it on project-scoped tools (a user with one project can omit it); token sessions use the token's project.
domainIdstringYesSemantic domain ID

Returns: relationships, each with id, name, from, to, cardinality, defaultJoinType, isAutoJoin, joinPriority, confidence, isActive and description.

Physical Discovery

These tools need SQL access. Use them when the project has no semantic coverage for the question. Follow the capabilities semaphor_list_connections returns through the database and schema levels each connection needs.

semaphor_list_connections

ArgumentTypeRequiredDescription
projectIdstringNoThe project to work in. Interactive sessions pass it on project-scoped tools (a user with one project can omit it); token sessions use the token's project.

Returns: connections, with ID, name, type, dialect and the discovery levels each one has.

semaphor_list_databases

ArgumentTypeRequiredDescription
projectIdstringNoThe project to work in. Interactive sessions pass it on project-scoped tools (a user with one project can omit it); token sessions use the token's project.
connectionIdstringYesConnection ID to inspect

Returns: databases (databases or catalogs).

semaphor_list_schemas

ArgumentTypeRequiredDescription
projectIdstringNoThe project to work in. Interactive sessions pass it on project-scoped tools (a user with one project can omit it); token sessions use the token's project.
connectionIdstringYesConnection ID to inspect
databaseNamestringYesDatabase or catalog name

Returns: schemas.

semaphor_list_tables

ArgumentTypeRequiredDescription
projectIdstringNoThe project to work in. Interactive sessions pass it on project-scoped tools (a user with one project can omit it); token sessions use the token's project.
connectionIdstringYesConnection ID to inspect
databaseNamestringNoDatabase or catalog name
schemaNamestringNoSchema name when the connection supports schemas
nameCandidatesarray of stringNoCandidate table/entity names to match after normalization
limitinteger (1 to 200)No (default 50)Up to 200 tables (default 50)

Returns: tables with exact coordinates. Pass nameCandidates when you have likely entity names, such as ["orders", "order_lines"].

Governed Queries

semaphor_analyze

The default query tool. It runs governed analytics over a semantic domain: metric by dimension, top-N, time series, filters, date windows counted from now or from the newest row, period comparisons, period-change driver analysis, and joins through modeled relationships. Every call applies the caller's security policies.

ArgumentTypeRequiredDescription
projectIdstringNoThe project to work in. Interactive sessions pass it on project-scoped tools (a user with one project can omit it); token sessions use the token's project.
fromCardobjectNoRun a dashboard card exactly as it runs on its dashboard, for example { "dashboardId": "...", "cardId": "..." }: its fields, filters, drill steps, dashboard inputs, date range and comparisons. Pass it without the query fields; the result is the card's own rows, with executionResult.constraints naming the filters and date windows it applied and any it skipped. A Matrix card, a text card and a card in a Document section cannot be run this way.
fromVariableobjectNoWhat a dashboard's data variable shows at a tab or a Document, derived as its chips are there, for example { "dashboardId": "...", "name": "yard_address", "sheetId": "..." }: its value, its parts, whether none, one or many rows matched, and the constraints applied. inputs gives a Document's own inputs their values by name. Pass it alone, without fromCard or the query fields.
domainIdstringNoSemantic domain id from semaphor_list_semantic_domains.
datasetNamestringNoExact dataset name from semaphor_list_datasets.
measuresarray of string or object or nullNoRequired for semantic analysis: at least one measure. To count rows, use the "__count" measure. Measure field names or source-bearing refs, for example ["sales", "profit"] or [{ "name": "profit", "datasetName": "finance" }]. Optional measure aggregate override is supported, for example { "name": "gross_margin_value", "datasetName": "fact_sales_line", "aggregate": "AVG" }. Omit aggregate to use the semantic model default.
primaryMeasureobjectNo
dateFieldobjectNo
timeGrain"day", "week", "month", "quarter", "year"NoTime grouping for dateField.
dimensionsarray of objectNoDimension column names, for example ["segment", "region"], or source-bearing refs from joined semantic datasets.
comparisonobjectNoCanonical comparison object, for example { "kind": "previous_period" }, { "kind": "previous_year" }, or { "kind": "target", "targetValue": 100 }. Omit comparison when no comparison is requested.
analysisobjectNoCanonical shared metric analysis object. Use { "kind": "period_change" } for spike, decline, volatility, or period-over-period change analysis.
analysisOrderBy"absolute_change", "positive_change", "negative_change", "period"NoOrdering for period-change analysis. Use "absolute_change" for biggest movements or "period" for chronological trend output.
orderByobjectNoExplicit result ordering for standard metric queries, for example { "field": { "name": "net_purchase_value", "datasetName": "fact_purchase_line" }, "direction": "desc" }. The field must be one selected metric, dateField, or dimension.
timeWindowobjectNoWhole periods: { "kind": "periods", "unit": "month", "from": -3, "to": -1, "anchor": "now" } is the last 3 complete months (from 0 to 0: this month; add "toDate": true for so far). Elapsed: { "kind": "rolling", "unit": "day", "n": 14, "anchor": "now" }; minutes and hours are rolling only. The anchor is required: "now", "latest_data" (the newest row) or { "lastComplete": "month" }: { "kind": "periods", "unit": "year", "from": 0, "to": 0, "toDate": true, "anchor": { "lastComplete": "month" } } is year to last completed month. { "kind": "monthDay", "day": "last" }. Fixed: { "kind": "absolute", "start": "2025-01-01", "end": "2025-12-31" }.
calendarContextobjectNoOptional execution calendar context for timezone-aware date windows and grouping, for example { "tz": "America/Chicago", "weekStart": 0, "anchor": "now" }. Defaults to token calendar context or UTC.
driverMode"absolute_delta", "largest_negative", "largest_positive", "positive_and_negative", "all"NoDriver output mode for previous-period comparisons. Use "all" for absolute, negative, and positive drivers.
limitintegerNoMost rows to return, at most 1000. Default 100.
includePopulationbooleanNo (default false)When true, also return filtered population totals for the requested measures before dimension grouping and row limit. Use for share, concentration, and percent-of-total analysis.
activeFiltersarray of object or stringNo (default [])Optional canonical analytics filters or JSON-stringified filter arrays, for example [{ "field": "direction", "operator": "equals", "values": ["Inbound"] }]. Use "values" arrays, not singular "value". Scalar operators such as equals/contains require exactly one value; between requires exactly two; in/not_in accept multiple values.
responseFormat"markdown", "json"No (default "json")Output format: "markdown" for human-readable or "json" for machine-readable
responseDetail"compact", "standard", "debug"No (default "standard")Controls JSON payload verbosity. "compact" removes duplicate root arrays, full SQL text, and repeated source-heavy intent blocks; "standard" preserves today's full governed response; "debug" is currently equivalent to standard.

Also accepted, for Semaphor's own clients only: chartTitle, chartType, connectionId, cardConfig, cardDataSource. Agents should use the arguments above.

Measures and fields. Measures are names or refs such as { "name": "revenue", "datasetName": "orders" }; a ref may add aggregate to override the modeled aggregate for this call, and "__count" counts rows. Dimensions and dateField take names or refs, including fields on related datasets.

Time windows. A timeWindow takes the same date windows a dashboard's date filter saves, so an answer and a dashboard on the same window show the same dates. Every periods and rolling window states its anchor, the point it counts from:

  • "now": when the query runs.
  • "latest_data": the newest row's date in dateField.
  • { "lastComplete": "month" }: the end of the last complete month (or day, week, quarter, year).
WindowExample
This month{ "kind": "periods", "unit": "month", "from": 0, "to": 0, "anchor": "now" }
Month to date{ "kind": "periods", "unit": "month", "from": 0, "to": 0, "toDate": true, "anchor": "now" }
Last quarter{ "kind": "periods", "unit": "quarter", "from": -1, "to": -1, "anchor": "now" }
Last 3 complete months{ "kind": "periods", "unit": "month", "from": -3, "to": -1, "anchor": "now" }
Year to last completed month{ "kind": "periods", "unit": "year", "from": 0, "to": 0, "toDate": true, "anchor": { "lastComplete": "month" } }
Last 3 complete months of data{ "kind": "periods", "unit": "month", "from": -3, "to": -1, "anchor": "latest_data" }
Last 14 days, as elapsed time{ "kind": "rolling", "unit": "day", "n": 14, "anchor": "now" }
Last 45 minutes{ "kind": "rolling", "unit": "minute", "n": 45, "anchor": "now" }
Last day of the month{ "kind": "monthDay", "day": "last" }
Fixed dates{ "kind": "absolute", "start": "2025-01-01", "end": "2025-12-31" }
  • periods counts whole days, weeks, months, quarters or years. from and to count from the anchor's period: 0 is that period, -1 the one before it. Add "toDate": true to stop at the anchor instead of the period's end.
  • rolling counts n units of elapsed time back from the anchor. Minutes and hours are rolling only.
  • monthDay is the first or last day of the current month.

A window in any other form is refused. The refusal says what's wrong, and its fix lists the windows to send instead. When the nearest windows mean different things, the fix lists each one: ask your user which they mean.

Comparisons and drivers. comparison is { "kind": "previous_period" }, { "kind": "previous_year" } or { "kind": "target", "targetValue": 100000 }, stated once for the analysis, at the top level or the same on each measure that states it. A measure also takes a table calculation, transform (see What a Number Means). analysis: { "kind": "period_change" } finds spikes, declines and period-over-period moves, ordered by analysisOrderBy. driverMode picks which drivers a comparison returns. See Metric Comparisons.

Revenue by region, last 3 complete months of data
{
  "domainId": "dom_abc",
  "datasetName": "orders",
  "measures": [{ "name": "revenue", "datasetName": "orders" }],
  "dimensions": [{ "name": "region", "datasetName": "customers" }],
  "dateField": { "name": "order_date", "datasetName": "orders" },
  "timeWindow": { "kind": "periods", "unit": "month", "from": -3, "to": -1, "anchor": "latest_data" },
  "limit": 10
}

Returns:

FieldWhat it holds
answerSummaryOne sentence about the result
data.recordsThe rows, keyed by column label, with data.rowCount and data.rowLimitExceeded
executionResult.statusanswered, partial or failed
executionResult.resultThe rows' typed columns (name, label, role, data type, source), rowCount, limit and rowLimitExceeded
executionResult.fieldsUsedEach field the query ran with, its role and aggregate
executionResult.constraintsWhat the query applied, and what it skipped (below)
executionResult.coverageansweredObligations and missingObligations: what part of the question was answered
executionResult.validationok, errors, warnings and repairHints
executionResult.relationshipDiagnosticsHow the query joined datasets, and any fan-out risk
querySpec, compiledQuery, metadataThe query as resolved and compiled, with the join plan and the generated SQL
metadata.resolvedTimeWindowThe date window the query ran with (below)
comparison, drivers, changesFor comparison and period-change calls
Example response (shortened)
{
  "answerSummary": "Successfully executed query spec (5 rows).",
  "data": {
    "records": [
      { "Region": "West", "Revenue": 1758699.06 },
      { "Region": "East", "Revenue": 1670049.2 }
    ],
    "rowCount": 5,
    "rowLimitExceeded": true
  },
  "executionResult": {
    "status": "answered",
    "resultShape": "records",
    "constraints": {
      "applied": [
        { "kind": "filter", "field": { "name": "orders.order_date", "label": "Order Date" }, "operation": "between",
          "values": ["2026-09-17", "2026-10-17"], "valueType": "date", "timezone": "America/Chicago" }
      ],
      "skipped": []
    },
    "coverage": { "answeredObligations": ["governed_query_executed"], "missingObligations": [] }
  }
}

executionResult.constraints says what the query applied:

  • applied: each filter it ran with (field, operation, and values as executed; a relative date window shows the dates it resolved to), the card's own filters when you ran a card, and each measure's comparison windows. When measures ran over different periods (one for the full month, one month to date), a filter that applied to some of them names them in measures. A date filter names the calendar zone the query ran in as timezone: bounds that span whole days in that zone are those dates.
  • skipped: each filter it left out, with the reason: dataset_not_reachable, field_not_resolvable, missing_metadata or unsupported_calculated_filter.

It never includes security policies or their values. "responseDetail": "compact" leaves out duplicate arrays, full field source metadata and the full SQL text, and lists what it left out in omitted.

metadata.resolvedTimeWindow says which dates a timeWindow covered:

The window that ran
{
  "window": { "kind": "periods", "unit": "month", "from": -3, "to": -1, "anchor": "latest_data" },
  "label": "Last 3 months of data",
  "definition": "the 3 months before the month of the latest date in the data, whole months only",
  "dateField": "order_date",
  "start": "2026-01-01T00:00:00.000Z",
  "end": "2026-03-31T23:59:59.999Z",
  "timezone": "UTC",
  "latestData": "2026-04-14T00:00:00.000Z"
}
  • window is the window you asked for, in the form you'd send to get the same dates again. label and definition say it in words.
  • start and end are the bounds the query ran with. Their day boundaries are in timezone. They're the window's own bounds: other filters in executionResult.constraints can narrow the rows further.
  • latestData is the newest row's date that a latest_data window counted back from. When no rows match the query's other filters, the window has no start or end.
  • On a comparison, the window has no bounds of its own: executionResult.constraints lists the comparison windows it placed.

Period change results. A period-change call reports its window in metadata.timeWindow, with the same fields. When it finds no changes, it says why in metadata.noChanges, never just an empty list:

noChanges.reasonWhat it meansfix
row_limitThe source query stopped at its row limit, so the changes can't be worked outNarrow the dimensions or add a filter
no_changes_in_directionThere are changes in the window, but none in the direction asked for (positive_change or negative_change)Order by "absolute_change" to rank every change
no_values_in_windowNo rows in the window have a value for the measureFor a window counted from now, count back from the newest row with "anchor": "latest_data"
no_comparable_valuesThe window has values, but no group also has one in the period it's compared withNone
No revenue this month
{
  "reason": "no_values_in_window",
  "message": "No Revenue values from 2026-10-01 to 2026-10-31 (This month).",
  "fix": "To count back from the newest row, use anchor \"latest_data\"."
}

Report noChanges.message to your user. An empty period change doesn't mean nothing changed.

When a field is wrong or missing, the tool returns isError: true with a validation payload: a code such as missing_measure or invalid_dimension, the valid candidates from the schema, and a recommended next step.

Run a dashboard card

Pass fromCard to answer from a dashboard card exactly as your users see it. The tool runs the card's own query with everything it runs on its dashboard: its fields, filters and drill steps, the dashboard's default inputs and date range, its parameters and its comparisons. The card runs as a new viewer opens the dashboard, under your security policies, and you need access to the dashboard.

The card's own answer
{ "fromCard": { "dashboardId": "dash_abc", "cardId": "card_123" } }

The result is the card's rows (at most 1,000) with executionResult.metadata.queryApproach set to "dashboard_card_replay", and executionResult.constraints naming the filters and date windows the card applied. To ask something the card doesn't show, build the query with the arguments above instead.

Error codeWhen
card_not_foundThe dashboard or card doesn't exist, or you can't open the dashboard
card_not_supportedA Matrix card, a text card or a card in a Document section; details.reason is matrix, no_query or document_section
from_card_unavailableThe card can't run right now (its dashboard filter mapping needs fixing), or the call also passed query arguments

See Querying Data for more examples.

semaphor_matrix

Runs a governed Matrix window: multi-level row and column hierarchies, totals and subtotals, scoped sorting, and Top or Bottom N with Others.

ArgumentTypeRequiredDescription
intentobjectYesThe Matrix definition: a grounded source, rows, columns, measures, filters, sorts and rankings
matrixWindowRequestobjectYesWhich pages and blocks of the matrix to load: each page names an axis, a parent path and a size
projectIdstringNoThe project to work in. Interactive sessions pass it on project-scoped tools (a user with one project can omit it); token sessions use the token's project.
calendarContextobjectNoTime zone and week start for date windows, as for semaphor_analyze

Returns: executionResult, with the requested window at executionResult.result.window. A page cursor asks for the next sibling window; it doesn't mean the data was cut off. A window is never a complete export, and unloaded cells aren't zeros. This tool always returns JSON.

semaphor_plan_analytics_recovery

Plans the next governed tool calls when an agent has a typed question and schema evidence but still has unanswered parts. It returns plannedToolCalls (such as semaphor_get_dataset_schema or semaphor_analyze) and diagnostics. It plans; it doesn't run queries.

ArgumentTypeRequiredDescription
operationIntentobjectYesThe typed question to answer
groundedSchemasarray of objectNoDataset summaries already read, with measure, date and dimension candidates
semanticTargetsarray of objectNoDomains available for schema discovery
physicalTargetsarray of objectNoGrounded physical tables (needs SQL access)
remainingToolCallsintegerNoPlanning budget, up to 20
responseFormat"markdown", "json"No"json" (default) or "markdown"

SQL

semaphor_query_sql_advanced

Runs SQL against a connection, under the same security policies as every other query, with optional Python post-processing. Use it when a question can't be expressed through semaphor_analyze. Needs SQL access.

ArgumentTypeRequiredDescription
projectIdstringNoThe project to work in. Interactive sessions pass it on project-scoped tools (a user with one project can omit it); token sessions use the token's project.
connectionIdstringYesThe connection from semaphor_list_connections
analyzeFallbackReason"user_explicitly_requested_sql", "no_semantic_domain_available", "field_not_exposed_in_semantic_model", "unsupported_sql_construct", "unsupported_join_or_grain", "needs_raw_rows", "debugging_or_validation_query"YesRequired reason this SQL escape hatch is necessary instead of governed semantic analysis. Do not use SQL merely because the analyze tool was not loaded; load it for governed BI questions.
analyzeFallbackExplanationstringYesRequired concise explanation of the specific limitation or user request that makes governed semantic analysis insufficient for this SQL call.
sqlstringYesThe query. Include a row limit for exploration.
pythonCodestringNoPython that post-processes the result rows

Returns: records, rowLimitExceeded, optional output and warnings, the echoed fallback reason, and executionResult.

Example
{
  "connectionId": "conn_123",
  "analyzeFallbackReason": "unsupported_sql_construct",
  "analyzeFallbackExplanation": "Needs a running total with a window function over monthly revenue.",
  "sql": "SELECT month, revenue, SUM(revenue) OVER (ORDER BY month) AS running_total FROM monthly_revenue ORDER BY month LIMIT 100"
}

Dashboards

semaphor_list_dashboards

Lists the dashboards the caller can open in a project, most recently updated first.

ArgumentTypeRequiredDescription
projectIdstringNoProject ID. Optional for project-token sessions because the token determines the active project. Interactive org-user sessions should pass projectId explicitly.
searchstringNoSearch dashboards by title or description
tagsarray of stringNoExact dashboard tags to match. Tags must already be canonical lowercase values.
tagMatch"all", "any"No (default "all")Tag matching mode. "all" requires every tag; "any" requires at least one. Requires tags.
origin"org_created", "tenant_created", "unknown"NoWho created the dashboard: "org_created" (an organization user), "tenant_created" (a tenant user) or "unknown" (no creator recorded: created before creators were recorded, or the creator was deleted).
limitinteger (1 to 500)No (default 100)Maximum number of dashboards to return
offsetintegerNoNumber of results to skip, for page-numbered views. Can't be combined with cursor or updatedSince. Offset pages can skip or repeat dashboards that change while you read.
cursorstringNonextCursor from the previous page of the same listing (same filters and updatedSince). With updatedSince (oldest change first) a scan sees every dashboard even while they change; newest first, a dashboard edited between pages can be missed.
updatedSincestringNoISO timestamp. Returns dashboards updated at or after it, oldest change first; follow nextCursor to the end. Use 1970-01-01T00:00:00Z to scan everything. A dashboard edited during the scan appears again later, so de-duplicate by id.
idsOnlybooleanNoWhen true, returns only { ids, total, nextCursor }. A finished scan from updatedSince 1970-01-01T00:00:00Z with no search, tags or origin lists every accessible dashboard, so an id missing from it was deleted or is no longer accessible. Any other scan is partial by design.
responseFormat"markdown", "json"No (default "json")Output format: "markdown" for human-readable or "json" for machine-readable

To read every dashboard while people may be editing them, scan oldest change first: pass updatedSince (1970-01-01T00:00:00Z for everything) and follow nextCursor to the end. A dashboard edited during the scan moves to the end and is read again, so de-duplicate by ID. The default newest-first order, and offset, can miss a dashboard that's edited between pages.

To find deleted dashboards, run a full scan with idsOnly: true: updatedSince: "1970-01-01T00:00:00Z" and no search, tags or origin. An ID that's missing from that finished scan was deleted, or you no longer have access to it. An incremental scan (a later updatedSince) or a filtered one leaves out dashboards on purpose, so don't treat its missing IDs as deletions.

Returns: dashboards (a page of rows), total and nextCursor (null on the last page). Each row has id, title, description, projectId, tags, origin, its creator and update times, and access: who can open the dashboard, as the dashboards console shows it, with a one-line summary in access.line. For tenant callers, and for dashboards that belong to a tenant, access lists only the rules that reach that tenant. With idsOnly, the response is ids, total and nextCursor.

semaphor_get_dashboard_details

Returns one dashboard's details, or several at once.

ArgumentTypeRequiredDescription
dashboardIdstringNoThe unique ID of the dashboard to retrieve. Use this or dashboardIds.
dashboardIdsarray of stringNoUp to 20 dashboard IDs to retrieve at once. Use this or dashboardId.
fieldsarray of "summary", "cards", "bindings", "sql", "template", "outline"NoWhich parts to return. "summary" (the default) is the dashboard record with its access; "cards", "bindings" (every dashboard filter and control as an input, and card control bindings), "sql" (saved card SQL) and "template" add those parts. "outline" (one dashboardId only) describes the dashboard for semaphor_propose_change.
sheetIdsarray of stringNoLimit cards, bindings, SQL, template and the outline to these sheets.
cardIdsarray of stringNoLimit cards, bindings, SQL, template and the outline to these cards.
responseFormat"markdown", "json"No (default "json")Output format: "markdown" for human-readable or "json" for machine-readable

Returns: for one dashboard, its record (title, description, project, tenant, tags, access, dates) plus the parts you asked for. For several, dashboards and notFound (IDs that don't exist or that you can't open).

With a live token, every part but summary describes the dashboard on the user's screen, unsaved edits included, and names one dashboardId.

outline takes one dashboardId. It describes the dashboard for making changes: every sheet, card and filter by ID and name, each sheet's layout, each card's query in the same fields semaphor_analyze uses, each filter's reach and its default as the dashboard loads it, which cards and filters can be changed (and why not), the dashboard's updatedAt, and access: canEdit, or canCopy when you can save a private copy instead. With fields: ["outline"] alone, the response is { id, title, outline }.

Two dashboards with their cards
{
  "dashboardIds": ["d_q1sales", "d_pipeline"],
  "fields": ["summary", "cards"]
}

semaphor_get_dashboard_analysis_context

Returns compact grounding for a dashboard the agent already knows: its cards, filters, authored measures, dimensions and date fields, source references, and optional query inputs for semaphor_analyze. Use it before broad discovery when a question starts from a dashboard. It reads the dashboard and never runs its queries. The project comes from the dashboard, so there's no projectId.

ArgumentTypeRequiredDescription
dashboardIdstringYesThe unique ID of the dashboard to inspect
includeQueryInputsbooleanNo (default true)Include bounded cardConfig/cardDataSource query inputs when available so agents can use the dashboard's authored query setup.
maxCardsinteger (1 to 50)No (default 30)Maximum dashboard cards to return in the compact context.
sheetIdsarray of stringNoOnly cards on these sheets.
cardIdsarray of stringNoOnly these cards.
responseFormat"markdown", "json"No (default "json")Output format: "json" for machine-readable or "markdown" for human-readable

Returns: dashboard (metadata and link), summary (counts, and missingSheetIds and missingCardIds for IDs that don't exist), filters, cards (per-card analyses with their saved filterConditions and AND and OR groups), referencedSemanticDomains, referencedDatasets, referencedFieldCandidates, referencedConnections, referencedPhysicalSources, physicalSourceLimitations, analysisHints and suggestedNextSteps. A relative window such as "This month" comes back as its relative intent, and a latest-value condition as latestValue: true, because the server resolves both on every run.

Changes

semaphor_propose_change previews a change and saves nothing. semaphor_apply_change applies a stored proposal by its proposalId, as previewed (a semantic model change only if nothing it replaces has changed since). See Making Changes for every change, its fields, the responses, retries and errors.

semaphor_propose_change

ArgumentTypeRequiredDescription
projectIdstringNoFor create and relationship.add in interactive sessions; dashboard changes use the dashboard's project
dashboardIdstringNoThe dashboard to change (dashboard changes)
createobjectNoA new private dashboard in place of dashboardId. Its changes add sheets, cards and filters; with no sheet.add it has one sheet.
domainIdstringNoThe semantic domain to change (relationship.add). Ignored with dashboard changes.
changesarray of objectYes1 to 20 changes for a dashboard, up to 50 for a new one, applied in order, all or nothing. See Making Changes for each change.
saveAs"original", "copy"No"original" (default) or "copy": save the changes as your own private copy
preview"sample", "rows", "none"No"sample" (default, up to 5 rows per changed card), "rows" (up to 50) or "none"
includearray of "outline"No["outline"] also returns the outline as it will be after the changes
rationalestringNoWhy the change is needed, up to 500 characters; kept with the proposal

Returns: proposalId, expiresAt, risk, target, summary, changes, warnings, preview, proposedOutline (with include: ["outline"]) and next. See Proposals. With a live token, the change is sent to the user's screen and the result also has delivery: its deliveryId and what became of it (applied, not_applied or not_confirmed).

semaphor_apply_change

ArgumentTypeRequiredDescription
proposalIdstringYesThe proposalId from semaphor_propose_change.

Returns: applied, replayed, target, created, summary and warnings. Safe to retry with the same proposalId: a retry returns the recorded result with replayed: true and never applies the change twice. See Save, Copy and Apply.

Report a Problem

semaphor_report_problem

Sends Semaphor support the agent's account of what went wrong, with the user's agreement. See Report a Problem.

ArgumentTypeRequiredDescription
descriptionstringYesYour account of what happened, in your own words.
userGoalstringNoWhat the user was trying to do in this conversation, in a sentence or two: their goal, not their last message.
proposalIdstringNoThe proposal involved, if any; its details are attached.

Returns: reportId, the report's number to give the user, and next. A report that couldn't be sent returns report_not_sent; try again later.

On this page