Capabilities / Developers

Connect your AI

Connect Claude, Codex, Cursor or any MCP client, or call Unity's agent tools and HTTP API with a key: from nothing to a shared report, with the assistant's own tools.

X4 Tech SolutionsUpdated November 2, 202611 min read

Unity's own assistant works through a set of tools: it inspects the report and its data model, runs queries, builds pages and visuals, previews what it built, and shares the result. The same tools are available to your AI. Connect an MCP client, or call the tools over HTTP, and your model drives Unity directly with the same contracts, permissions and receipts. Every call runs as your account and respects its report access.

Choose an approach

You want toUse
Let an AI client (Claude, Codex, Cursor, your own agent) operate UnityThe MCP endpoint, https://api.x4tech.org/mcp
Give your own model Unity's tools in your applicationGET /api/reports/{report_id}/tools and POST /api/reports/{report_id}/tools/{name}
Write a deterministic integrationThe HTTP API reference and its OpenAPI document
Ask Unity's assistant to do the workAn agent run, POST /api/agent/runs

All four reach the same operations. A change made one way is visible to the others. The agent tool reference lists every tool with its exact schema.

Connect an MCP client

Unity serves the Model Context Protocol over Streamable HTTP at https://api.x4tech.org/mcp. A client connects in one of two ways.

Sign in from your AI app

Add the server URL to your AI app and connect. The app opens Unity in your browser, you approve the connection, and the app receives its own access; there is nothing to copy. Menu names vary by client version:

  • Claude (claude.ai, Desktop, mobile): add a custom connector with the URL, then select Connect.
  • Claude Code: run claude mcp add --transport http unity https://api.x4tech.org/mcp, then open /mcp in Claude Code and authenticate the unity server.
  • Codex: run codex mcp add unity --url https://api.x4tech.org/mcp, then codex mcp login unity.
  • Cursor: add the server to mcp.json with only its url, then sign in from Cursor's MCP settings.
  • ChatGPT: add a custom MCP server (in Plugins) with the URL and OAuth authentication, then select Continue to Unity.

Unity's connection page shows which app is asking, where it returns you, and what it will be able to do. Allow changes is on when the app asks to change reports; turn it off to connect the app read-only. The app's access lasts an hour and renews itself while you keep using it; after 30 days without use, connect again. It works only with Unity's MCP server, not the rest of the API. See and disconnect your apps under API access & sessions → Connected apps.

Use an API key

  1. Sign in to Unity with a regular account (demo accounts cannot create keys) and open API access & sessions in the app panel.
  2. Enter a Token name, choose Expires in (7, 30, 60 or 90 days), and choose Access: Read only (workspace:read) or Read & change (workspace:write, which also reads).
  3. Select Create token, then Copy. Unity shows the secret once; if you lose it, revoke the key and create another.

Keep the key in a server environment variable or secret store and send it in the Authorization header, never in a URL, source control or browser code. Each key belongs to your account; another person creates their own. Revoke disables a key; Sign out everywhere revokes every browser session and key and disconnects every app. Revoking a key does not undo saved changes.

In Claude Code:

cURL
claude mcp add --transport http unity https://api.x4tech.org/mcp --header "Authorization: Bearer $UNITY_API_KEY"

Clients that accept a remote server with custom headers use the same two values:

JSON
{
  "mcpServers": {
    "unity": {
      "url": "https://api.x4tech.org/mcp",
      "headers": { "Authorization": "Bearer YOUR_UNITY_API_KEY" }
    }
  }
}

One report or all of them

Without a report in the URL, every report tool takes a reportId argument, and the server adds ListReports and CreateReport. Add ?reportId=YOUR_REPORT_ID to bind the server to one report: its tools then act on that report only, whether you sign in or use a key. An app connected to one report can create, attach, detach and test the connections that report uses, but cannot rename or delete connections, and it shares only that report.

The server also offers resources: Unity's guides at unity://docs/{topic} (for example unity://docs/sql), the HTTP API at unity://openapi.json, and the schema of a report definition at unity://schemas/report-definition.json.

Permissions

CredentialWhat it permits
Read-only connection or workspace:read keyTools and requests that only read: inspecting, querying, previewing data and stopping a running read.
Connection that allows changes or workspace:write keyAlso the tools and requests that change a report, which need Edit access to that report.
Browser sessionAccount, API key, connected app and billing management; organization administration; typing a connection's credentials; changing an existing connection's settings.

Scopes never override report roles. A write key does not make a View-only report editable, and sharing still needs the report's Owner or an Admin. Tools your access could never call are left out; a tool you could call with more access or a write key is listed with available: false and the reason, and calling it answers that sentence.

If a report was shared with you page by page, ListReports shows it with access level Pages and the pages you can reach, and the tools you are offered are scoped to those pages: reading them, the report's data model and queries as a View-access person has them, and, on a page you may edit, changing it.

From nothing to a shared report

An agent can build and share a report from an empty account over MCP alone:

  1. CreateReport (unbound /mcp) returns the new reportId.
  2. Connect data. SaveConnection with type, authType and config (where to connect and how, never a secret) creates a connection attached to the report; ReadUnityDocumentation topic=connections lists each type's settings. If the sign-in needs credentials, the result has needsCredentials and a credentialsUrl: give that link to the connection's owner, who opens it in Unity, sees exactly where the credentials go, types them and selects Save. Credentials never pass through the agent. TestConnection then discovers the tables. A public provider that needs no key (World Bank, US Census) needs no person at all. Or import a CSV file: attach it with AttachReportFile, then ImportCsvFile turns it into a data source.
  3. Model it. SaveReportModelSource with sourceMode: tables models the tables; the model tools add relationships, calculated fields and measures.
  4. Build pages. CreatePage and CreateVisual build native pages (CreateVisual, UpdateVisual and DeleteVisual take up to 100 visuals per call, all or nothing); the HTML tools build HTML pages. QueryReportData checks a visual's numbers.
  5. See it. PreviewPage returns a screenshot of a saved page, native or HTML, as Unity renders it; MCP clients receive it as an image.
  6. Share it. InspectAccess shows who can open the report and each page. SaveAccess makes one change per call: share the report or a page with a person (an email or user ID) or a team, set a page's audience, transfer ownership (confirm: true), or publish a page at a public link (publicLink: "create" with confirm: true, since anyone with the link can see the page) and revoke it ("revoke"). Only the report's Owner and Admins see a link's URL. Sharing sends no email.
  7. Export it. GetReportDefinition returns the whole report as one JSON document.

UpdateReport renames the report, saves its instructions for agents, or deletes it (the owner only, with confirm: true). A report you delete stays in ListReports with its deletion's status until the deletion completes, then leaves the list. A rename and instructions make a report version, and so does attaching or detaching a connection (creating one attaches it); renaming or deleting a connection, and sharing, make none; a rename is recorded with the report's next change, as a version by Unity labelled "Changed outside the report".

Work the way Unity's assistant does

The tools are designed for an inspect, change, verify loop:

  1. Inspect. InspectReport lists pages (HTML and native), visuals, layout and the report's instructions. InspectDataModel lists entities, fields, measures and relationships. ReadUnityDocumentation explains report SQL, visuals and HTML pages.
  2. Query. QueryReportData reads what the report shows: a saved visual, a saved HTML dataset or a trial visual query, under the report's filters and your clicks. QueryDataModel runs report SQL (DuckDB) over the whole model, without the report's filters, including joins across sources and measures. QueryConnection runs one source's own SQL.
  3. Change. The changing tools save immediately and return a receipt. Read a resource before changing it and send the revision you read; a stale revision is refused with the current one (currentRevision; currentVersion for an HTML page's baseVersion). A model write's revision is modelRevision, not reportVersion. A visual is named by its visualUid alone.
  4. Verify. QueryReportData reads a saved visual's data; PreviewPage shows the page.

When someone else is editing what a call would change, the call is refused and nothing is saved (result.code element_held, page_held or page_busy); retry later. A call never takes over someone else's editing.

Call tools over HTTP

List the tools a report offers you, with their input schemas, then call one:

cURL
curl --fail-with-body "$UNITY_API_BASE/api/reports/$UNITY_REPORT_ID/tools" \
  -H "Authorization: Bearer $UNITY_API_KEY"

curl --fail-with-body "$UNITY_API_BASE/api/reports/$UNITY_REPORT_ID/tools/QueryDataModel" \
  -H "Authorization: Bearer $UNITY_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{"arguments": {"query": "SELECT region, COUNT(*) AS customers FROM customers GROUP BY region", "rowLimit": 20}}'

The hosted base URL is https://api.x4tech.org. Each listed tool has name, description, access (view or edit), readOnly, inputSchema and available; pass the entries to your model as function definitions. Through these endpoints and MCP, a property that accepts null is optional, and a *Json parameter (and operation) takes the JSON value or its text. sessionId is optional: calls with the same session ID are recorded together in your change history.

Results and errors

A call that ran answers 200 with status, result and images; a call that saved a change also returns its reportVersion. status is error when the tool ran and refused, for example an invalid definition, a stale revision or confirm_required; result is {error, code, …}: error says what to change in one sentence, and code is stable to branch on (wrong_page_kind for a tool aimed at the other kind of page). A call that did not run is an HTTP error: 404 tool_not_found, 422 invalid_tool_arguments naming the failing field, or 403 for missing access or key scope. Over MCP the same refusals arrive as tool results with isError: true, and an unknown tool name is the JSON-RPC error -32602. A changing tool called with read-only access answers 403 insufficient_scope, so an app connected by signing in can ask you for more access. Over MCP you can make up to 1,500 tool calls every 15 minutes; past that, calls answer 429 with Retry-After.

Values stay exact: decimal columns are text such as "15913.23", and each column's valueEncoding says how its values are written. Every HTTP error carries error, message (the same text) and a code.

QueryReportData returns rowLimit rows (50 by default, at most 1,000) from pageIndex, for every target; its pagination (totalRows, returnedRows, hasMore, nextPageIndex) describes the whole result, so read on with nextPageIndex until hasMore is false. The pages of a table, matrix, paged dataset or trial query come from one copy of its rows, kept until 10 minutes after the last page. QueryDataModel and QueryConnection return up to rowLimit rows (at most 200) with hasMore.

Ask Unity's assistant

POST /api/agent/runs (workspace:write) queues Unity's assistant on one of your report chats (POST /api/datachats/add creates one) and returns 202. Poll GET /api/agent/runs/{run_id}/events?view=conversation, stream GET /api/agent/runs/{run_id}/stream, or stop it with POST /api/agent/runs/{run_id}/stop. With View access the run answers and queries but changes nothing. The HTTP API reference gives every parameter.

The report definition

GetReportDefinition exports the report as one JSON document, unityReportDefinition version 1: its name and instructions, the model (entities, fields by fieldUid, relationships, calculated tables and measures), every page (a native page's layout and visuals by visualUid; an HTML page's source, datasets and bindings), the saved filters, and the connections it uses by ID, name and type, never their settings or credentials. Lists keep the report's own order where it means something (page tabs, a model's tables and columns, a visual's dataset bindings), filters and connections are ordered by ID, and keys are sorted, so the same report always exports the same bytes and two exports diff cleanly. The JSON Schema is the MCP resource unity://schemas/report-definition.json and UnityReportDefinition in the OpenAPI document.

History and restore

Every saved change makes a report version, and a tool call that made one returns it as reportVersion. ListReportVersions lists them newest first (one assistant run or one burst of edits per entry; expand lists an entry's versions one by one), DiffReportVersions compares two, GetReportDefinition reads one, and RestoreReportVersion brings back the whole report, one page or a deleted page: a dry run first, then the restore with the dry run's expectedVersion and expectedWarnings. Version n is the report after change n, so to undo one change, restore the version listed before it. There is no other undo.

Questions about your environment? Contact us.