> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tilt.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Connect to Tilt Agent over MCP

> Connect an application to Tilt Agent, authorize a user, delegate work, consume run results, and continue a session.

Connect to Tilt Agent over the Model Context Protocol (MCP) when your
application owns the chat or agent experience. Your application acts as the MCP
host and client; Tilt Agent acts as a specialist sub-agent for supported
investment workflows.

Send a complete outcome to Tilt Agent instead of decomposing it into generic
searches or individual construction operations. For example, delegate “Create
a quarterly nuclear energy Tilt with 25 constituents and a 10% position cap,”
then continue the same Tilt Agent session when the user asks for a change.

## Understand the connection

The integration has four separate layers:

| Layer | Purpose |
| - | - |
| MCP over HTTP | Discover tools and resources, then invoke them with JSON-RPC. |
| OAuth | Authorize the connection as a Tilt user. |
| Tilt Agent tools | Start work, read progress and results, continue, cancel, or close a session. |
| MCP Apps | Optionally render Tilt's interactive session UI inside a supporting host. |

The remote MCP endpoint is:

```text theme={null}
https://tilt.io/mcp/tilt
```

Tilt supports two protocol paths:

* MCP `2026-07-28` uses `server/discover` and request-scoped transport state.
* MCP `2025-11-25` compatibility uses `initialize` and stateless transport.

Use an MCP SDK to negotiate the protocol. Do not use an MCP transport session
as Tilt Agent conversation state: conversation continuity comes from the
`session_id` returned by `delegate_to_agent`.

The server exposes tools and resources. It does not expose an MCP prompt
catalog.

## Authorize the user

Tilt uses OAuth 2.0 authorization code flow with S256 PKCE. A capable MCP client
can discover the authorization flow from the endpoint:

1. An unauthenticated MCP request returns `401 Unauthorized`. The
   protected-resource metadata appears in the response's
   `WWW-Authenticate` challenge.
2. [RFC 9728 protected-resource metadata](https://www.rfc-editor.org/rfc/rfc9728)
   identifies the authorization server, required `mcp` scope, and exact
   resource `https://tilt.io/mcp/tilt`.
3. Authorization-server metadata advertises the authorization, token, and
   dynamic client registration endpoints.
4. The client registers its callback, creates a PKCE verifier and S256
   challenge, and sends the user to Tilt to sign in and consent.
5. The client exchanges the authorization code for a resource-bound access
   token and sends it as `Authorization: Bearer <access_token>` on MCP requests.
6. The client uses the refresh token when the access token expires. Tilt
   rotates refresh tokens, so store the replacement after every successful
   refresh.

The standard flow uses a public OAuth client with token endpoint authentication
method `none`. Public clients have no client secret, but the MCP endpoint still
requires an access token.

<Note>
  The standard external OAuth connection currently runs in the authorized
  user's personal Tilt organization. Naming another organization in a prompt
  or context reference does not change the connection's authorization. Each
  end user authorizes their own Tilt account.
</Note>

Let the MCP SDK follow discovery metadata rather than hard-coding authorization
or token endpoints. A custom OAuth implementation must preserve the exact
resource through authorization and token exchange, validate callback state,
use the registered redirect URI, and store each user's credentials separately.

## Connect with an MCP client

The following TypeScript example uses the MCP SDK's Streamable HTTP transport
after OAuth has supplied an access token. Keep token acquisition and refresh in
your OAuth provider rather than embedding a token in source code.

```typescript theme={null}
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import {
  StreamableHTTPClientTransport,
} from "@modelcontextprotocol/sdk/client/streamableHttp.js";

const client = new Client({
  name: "example-host",
  version: "1.0.0",
});

const transport = new StreamableHTTPClientTransport(
  new URL("https://tilt.io/mcp/tilt"),
  {
    requestInit: {
      headers: {
        Authorization: `Bearer ${await getAccessToken()}`,
      },
    },
  },
);

await client.connect(transport);
```

Adapt the initialization to the MCP SDK and protocol version your host uses.
Configuration file formats are client-specific.

## Delegate a complete task

Call `delegate_to_agent` without `session_id` for the first turn:

```typescript theme={null}
type DelegatedRunResult = {
  run: {
    runId: string;
    sessionId: string;
    status: string;
  };
};

const result = await client.callTool({
  name: "delegate_to_agent",
  arguments: {
    message:
      "Create a US nuclear energy Tilt with 25 constituents, " +
      "exposure-proportional weighting, a 10% position cap, " +
      "and quarterly rebalancing.",
    client_time_zone: "America/Toronto",
  },
});

const { run } = result.structuredContent as DelegatedRunResult;
const { runId, sessionId } = run;
```

Persist both IDs. `runId` identifies this turn; `sessionId` identifies the
durable Tilt Agent conversation. Map the session to the same authorized user
and conversation in your application.

`delegate_to_agent` accepts the task and returns before Tilt Agent finishes.
The initial result can report `queued`, `starting`, or `running`. Receiving
the tool result does not mean the task succeeded.

## Wait for progress and consume the result

Call `wait_for_agent_run` until it returns a terminal status: `succeeded`,
`failed`, or `canceled`. Each call can wait for up to 30 seconds. Carry
`last_event_id` into the next call as `after_event_id` so that the server only
returns newer events.

```typescript theme={null}
type WaitResult = {
  status: string;
  last_event_id?: number;
  new_events?: unknown[];
};

const terminalStatuses = new Set(["succeeded", "failed", "canceled"]);
let afterEventId: number | undefined;

while (true) {
  const result = await client.callTool({
    name: "wait_for_agent_run",
    arguments: {
      run_id: runId,
      ...(afterEventId === undefined
        ? {}
        : { after_event_id: afterEventId }),
      wait_ms: 25_000,
    },
  });

  const snapshot = result.structuredContent as WaitResult;
  afterEventId = snapshot.last_event_id;

  if (terminalStatuses.has(snapshot.status)) {
    if (snapshot.status !== "succeeded") {
      throw new Error(`Tilt run ${snapshot.status}`);
    }
    break;
  }
}
```

Return each complete tool result to the orchestrating model. Tilt Agent output
uses structured UI elements with descriptive names. A model-driven MCP host can
interpret those elements and answer through its own interface. Pass the full
result rather than its status-summary text, and do not hard-code a parser for
one element type.

Carry events across every wait response when your MCP library does not preserve
the tool conversation for the model. `get_agent_run` returns the complete event
history when a client reconnects or loses its cursor.

## Choose how to present output

Choose between two presentation options:

### Let the MCP host interpret it

A model-driven host can receive the complete structured result from the run
tools. The model interprets Tilt's messages, cards, tables, and other UI elements,
then responds through the host's native interface.

### Use Tilt's MCP App

An MCP Apps-capable host can render Tilt's complete interactive experience
without mapping UI elements itself. Use the app to show Tilt cards, progress,
and follow-up interactions. Tilt can also customize the MCP App experience for
a third-party integration.

<Note>
  Tilt is exploring additional custom rendering options. Contact Tilt to
  discuss a specific integration.
</Note>

## Continue the same Tilt Agent session

Reuse `session_id` for a follow-up after the previous run reaches a terminal
status:

```typescript theme={null}
const followUp = await client.callTool({
  name: "delegate_to_agent",
  arguments: {
    session_id: sessionId,
    message:
      "Require companies to have a market capitalization of at least " +
      "$1 billion. Keep the other rules unchanged.",
  },
});
```

Do not restate object IDs merely to recreate continuity. Tilt Agent receives
the conversation and linked Tilt context through the durable session. Only one
run can be active in a session, so serialize follow-up turns.

Creating or refining a Tilt does not publish an index. Publication remains a
separate review process.

## Handle retries and failures

Delegation can change saved Tilt state and is not idempotent. Do not
repeat `delegate_to_agent` after an ambiguous timeout. If you received the run
ID, inspect the existing run with `get_agent_run` before deciding what to do
next.

Handle three failure layers separately:

* HTTP or OAuth failures mean the connection or token could not authorize the
  request.
* JSON-RPC or tool errors do not prove that no state changed. Inspect any known
  run or session before retrying the call.
* A retrieved run with status `failed` means the delegated turn started but did
  not complete successfully; inspect its error and events.

Canceling an active run stops further work when possible; it does not roll back
changes already completed. Closing a session archives it. Do not close a
session after every turn when users may continue the conversation.

## Orchestration tools

* `delegate_to_agent` starts a run or adds a turn to an existing session. It
  changes state.
* `wait_for_agent_run` waits for new events or a terminal status. It is
  read-only.
* `get_agent_run` reads the complete current run snapshot. It is read-only.
* `get_agent_session` reads session context, transcript, and the latest-run
  summary. It is read-only.
* `cancel_agent_run` cancels an active run. It changes state.
* `close_agent_session` archives a session. It changes state.

## Host Tilt's MCP App

MCP Apps is an optional presentation layer on the same authenticated MCP
connection. Tilt advertises the `io.modelcontextprotocol/ui` extension and
associates `delegate_to_agent` with this resource:

```text theme={null}
URI: ui://tilt/session.html
MIME type: text/html;profile=mcp-app
```

An MCP Apps-capable host reads the resource through MCP, loads it in the host's
Apps environment, and supplies tool results through the Apps bridge. The app
can display run progress and Tilt cards, send follow-up user messages, request
app-only tools, and ask the host to open links.

Do not treat the `ui://` URI as a public web URL or load it directly in a
general iframe. Follow the [MCP Apps host
documentation](https://modelcontextprotocol.io/docs/extensions/apps), enforce
the resource's security metadata, and hide app-only tools from the model.

Core MCP and MCP Apps support are separate host capabilities. If a host does
not support MCP Apps, let its model interpret the complete structured run-tool
output. Use the terminal run status to determine whether the delegation
succeeded.
