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

# Agent Workflow

> The canonical research workflow the agent follows — session_context, data tools, propose_strategy, backtest_run, subscribe_run, report — with server instructions and a full NVDA example session.

Every Ithaca research session follows the same canonical workflow. The agent opens a traced session, researches the market with data tools, proposes a declarative strategy, runs a backtest, subscribes to the run's progress, and reports the findings to the human. Every step is traced and replayable in the web UI.

## The canonical workflow

```
session_context  →  research (data tools)  →  propose_strategy
                                                       ↓
                  report  ←  subscribe_run  ←  backtest_run
```

<Steps>
  <Step title="Open a traced session with session_context">
    The first call is always `session_context`. It attaches the user's prompt and an optional title to the MCP trace, opens (or reuses) a persistent `research` run, and emits a `session_open` event. Every subsequent tool call in the session is grouped under the same `run_id` so the web UI lists and replays it as one session.

    ```json theme={null}
    session_context(
      prompt="Research NVDA — momentum thesis, check insiders, backtest",
      title="NVDA momentum research"
    )
    ```

    If you pass an existing research-run UUID as `session_id`, future calls attach to that run instead of creating a new one. This lets the web UI start a session and the agent join it.
  </Step>

  <Step title="Research with data tools">
    The agent calls data-layer tools to gather evidence. Each call is traced with a `tool_call` + `tool_result` event pair and returns a provenance envelope (`source`, `freshness`, `as_of`, `coverage`). Typical research calls:

    * `market_get_ohlcv(symbol, range, interval)` — price history
    * `market_screener(sector, limit)` — peer screen
    * `fundamentals_get(symbol)` — metric cards
    * `insider_activity(symbol)` — SEC Form 4/5 transactions
    * `congress_trades(ticker)` — STOCK Act disclosures
    * `analyst_ratings(symbol)` — Wall Street consensus
    * `technical_indicators(symbol)` — RSI, MACD, Bollinger, ATR
    * `vol_realized(symbol)` — annualized realized volatility

    Use `search_skills(query)` for progressive discovery rather than loading all 60+ tools into context at once.
  </Step>

  <Step title="Propose a strategy with propose_strategy">
    Once the agent has a thesis, it assembles a declarative `StrategySpec` and calls `propose_strategy`. The spec is validated against a **closed schema** — any free-form or unknown field is rejected. The server stamps `id`, `version`, and `tenant_id`; the agent's values for those are ignored. The strategy is registered as a `draft` — nothing deploys.

    ```json theme={null}
    propose_strategy({
      "spec_version": "1.0",
      "name": "nvda-momentum-60d",
      "universe": {"symbols": ["NVDA"], "source": "prices"},
      "strategy": {"id": "rank_returns", "params": {"lookback": 60, "top": 1}},
      "construction": {"scheme": "rank_weight", "long_only": true, "gross": 1.0},
      "risk": {"max_gross": 1.0, "max_net": 1.0, "max_name_pct": 1.0},
      "execution": {"cost_bps": 5, "algo": "vwap", "rebalance": "monthly@close"},
      "seed": 42
    })
    ```

    Returns `{strategy_id, status: "draft", spec}`. The `strategy_proposed` event is traced.
  </Step>

  <Step title="Run the backtest with backtest_run">
    The agent calls `backtest_run` with the validated spec and a backtest range. The backtest engine runs trusted, audited numpy code over point-in-time price history — **no agent-authored code ever executes**. The result includes stats (CAGR, Sharpe, max drawdown, win rate), the equity curve, trade log, and a risk-gate decision.

    ```json theme={null}
    backtest_run(spec=<the validated spec>, range="1y")
    ```

    For durable, queued backtests (hosted MCP), use `run_skill("backtest.run", {spec, range})` instead — it creates a durable run, enqueues a worker job, and returns a `run_id` immediately.
  </Step>

  <Step title="Subscribe to run progress with subscribe_run">
    For durable runs, the agent polls `subscribe_run(run_id, cursor)` to follow progress. Each call returns trace events after the supplied cursor. The agent repeats this until the run reaches a terminal state (`succeeded`, `failed`, `cancelled`).

    ```json theme={null}
    subscribe_run(run_id="...", cursor=-1)
    ```
  </Step>

  <Step title="Report findings to the human">
    The agent synthesizes the research and backtest results into a report for the human. This is pure narrative — the agent writes its summary to the chat. The human then decides whether to promote the strategy to paper trading from the web UI.

    <Callout type="warn">
      The agent **cannot** promote a strategy to paper trading. That is a human-only action, enforced at the code level. The agent can only propose and backtest.
    </Callout>
  </Step>
</Steps>

## Server instructions

When the agent connects to the Ithaca MCP server, it receives these instructions as part of the server's `instructions` field in the `initialize` response. The agent is expected to follow them:

```text theme={null}
You are connected to Ithaca, a quant research and paper-trading observability platform.

CANONICAL WORKFLOW — follow this for every research request:
1. Call session_context first with the user's prompt. This opens a traced research session.
2. Research the market using data tools (market_get_ohlcv, fundamentals_get, insider_activity,
   congress_trades, analyst_ratings, technical_indicators, vol_realized, etc.).
   Use search_skills to discover tools progressively — do not assume the full catalog.
3. When you have a thesis, call propose_strategy with a declarative StrategySpec.
   The spec is a closed schema — unknown fields are rejected. Do not include code.
4. Call backtest_run (or run_skill with skill_id "backtest.run") to test the strategy.
5. If the backtest is durable, call subscribe_run repeatedly to follow progress.
6. Report the findings to the human: thesis, evidence, strategy, backtest stats (Sharpe,
   CAGR, max drawdown), and a recommendation.

CONSTRAINTS:
- You can READ data and PROPOSE strategies. You cannot deploy, promote, or move money.
  Paper-trading promotion is a human action in the web UI.
- Every tool call is traced. The human watches your research unfold live in the web UI.
- Strategies are declarative specs, not code. Never attempt to submit executable code.
- All data is tenant-scoped. You can only see data belonging to the authenticated tenant.

PROVENANCE:
- Every data tool returns a provenance envelope: source, freshness, as_of, coverage.
- If freshness is "stale" or coverage is partial, note this in your report.
- Never fabricate data. If a tool returns an error, report the error and retry or pivot.
```

## Example: NVDA research session

Here is a full NVDA research session showing the agent's tool calls and expected outputs.

### 1. Open the session

```json theme={null}
// tool call
session_context(
  prompt="Research NVDA — momentum thesis, check insiders and congress trades, propose a 60-day momentum strategy, backtest it over 1 year",
  title="NVDA momentum research"
)

// expected output
{
  "status": "ok",
  "session_id": "550e8400-e29b-41d4-a716-446655440000",
  "prompt_captured": true
}
```

The `session_open` event is traced. The web UI now shows a new research session.

### 2. Pull price history

```json theme={null}
// tool call
market_get_ohlcv(symbol="NVDA", range="1y", interval="1d")

// expected output (abbreviated)
{
  "data": {
    "symbol": "NVDA",
    "points": [
      {"d": "2024-06-03", "o": 122.57, "h": 124.12, "l": 121.80, "c": 122.90, "v": 4.2e8},
      {"d": "2024-06-04", "o": 123.01, "h": 125.50, "l": 122.88, "c": 124.30, "v": 3.8e8},
      ...
    ],
    "quote": {"symbol": "NVDA", "price": 178.40, "asOf": "2025-06-02T20:00:00Z"}
  },
  "provenance": {
    "source": "ithaca-archive",
    "freshness": "delayed",
    "as_of": "2025-06-02T20:00:00Z",
    "coverage": {"requested": 252, "resolved": 252, "missing": []}
  }
}
```

### 3. Screen peers

```json theme={null}
// tool call
market_screener(sector="Technology", limit=20)

// expected output (abbreviated)
{
  "data": {
    "title": "Technology Screener",
    "rows": [
      {"symbol": "NVDA", "name": "NVIDIA Corp", "mktCap": 4.3e12, "return1y": 0.45},
      {"symbol": "AMD",  "name": "Adv. Micro Devices", "mktCap": 2.6e11, "return1y": 0.12},
      ...
    ],
    "count": 20
  },
  "provenance": {"source": "ithaca-archive", "freshness": "delayed", ...}
}
```

### 4. Check fundamentals

```json theme={null}
// tool call
fundamentals_get(symbol="NVDA")

// expected output (abbreviated)
{
  "data": {
    "symbol": "NVDA",
    "cards": [
      {"metric": "P/E (TTM)", "value": 68.4, "sectorMedian": 35.2},
      {"metric": "Revenue Growth YoY", "value": 2.08, "sectorMedian": 0.12},
      {"metric": "Gross Margin", "value": 0.75, "sectorMedian": 0.48},
      ...
    ]
  },
  "provenance": {...}
}
```

### 5. Check insider activity

```json theme={null}
// tool call
insider_activity(symbol="NVDA", limit=20)

// expected output (abbreviated)
{
  "data": {
    "symbol": "NVDA",
    "transactions": [
      {"filingDate": "2025-05-15", "insider": "Jen-Hsun Huang", "type": "sell", "shares": 60000, "value": 10.7e6},
      ...
    ],
    "netSummary": {"buys": 0, "sells": 12, "netShares": -480000}
  },
  "provenance": {...}
}
```

### 6. Check congress trades

```json theme={null}
// tool call
congress_trades(ticker="NVDA", limit=20)

// expected output (abbreviated)
{
  "data": {
    "trades": [
      {"member": "Rep. Nancy Pelosi", "transactionDate": "2024-11-22", "type": "buy", "amount": "$1M-$5M"},
      ...
    ],
    "highlights": ["Pelosi buy 30 days before earnings announcement"]
  },
  "provenance": {...}
}
```

### 7. Propose the strategy

```json theme={null}
// tool call
propose_strategy({
  "spec_version": "1.0",
  "name": "nvda-momentum-60d",
  "universe": {"symbols": ["NVDA"], "source": "prices"},
  "strategy": {"id": "rank_returns", "params": {"lookback": 60, "top": 1}},
  "construction": {"scheme": "rank_weight", "long_only": true, "gross": 1.0},
  "risk": {"max_gross": 1.0, "max_net": 1.0, "max_name_pct": 1.0},
  "execution": {"cost_bps": 5, "algo": "vwap", "rebalance": "monthly@close"},
  "seed": 42
})

// expected output
{
  "strategy_id": "stg_a1b2c3d4e5f6",
  "status": "draft",
  "spec": {
    "spec_version": "1.0",
    "name": "nvda-momentum-60d",
    "id": "stg_a1b2c3d4e5f6",
    "version": 1,
    ...
  }
}
```

### 8. Run the backtest

```json theme={null}
// tool call (in-process)
backtest_run(spec=<validated spec>, range="1y")

// expected output (abbreviated)
{
  "data": {
    "stats": {
      "cagr": 0.38,
      "sharpe": 1.42,
      "maxDrawdown": -0.18,
      "winRate": 0.58,
      "trades": 12
    },
    "equityCurve": [...],
    "tradeLog": [...],
    "risk": {"ok": true, "gross": 1.0, "net": 1.0}
  },
  "provenance": {"source": "computed", "freshness": "live", ...}
}
```

For a durable (queued) backtest over hosted MCP:

```json theme={null}
// tool call
run_skill(skill_id="backtest.run", input={"spec": <validated spec>, "range": "1y"})

// expected output (immediate)
{
  "run_id": "r_9f8e7d6c5b4a",
  "status": "queued",
  "status_url": "/api/runs/r_9f8e7d6c5b4a",
  "estimated_profile": "backtest",
  "job_id": "j_1a2b3c4d",
  "approval_required": false
}
```

### 9. Subscribe to progress (durable runs)

```json theme={null}
// tool call
subscribe_run(run_id="r_9f8e7d6c5b4a", cursor=-1)

// expected output (abbreviated)
{
  "run_id": "r_9f8e7d6c5b4a",
  "cursor": 7,
  "events": [
    {"seq": 1, "type": "run_transition", "data": {"from": "draft", "to": "queued"}},
    {"seq": 2, "type": "run_transition", "data": {"from": "queued", "to": "running"}},
    {"seq": 3, "type": "tool_call", "data": {"tool": "backtest.run", ...}},
    {"seq": 4, "type": "tool_result", "data": {"stats": {"sharpe": 1.42, ...}}},
    {"seq": 5, "type": "run_transition", "data": {"from": "running", "to": "succeeded"}}
  ]
}
```

### 10. Report to the human

The agent synthesizes a narrative report. Example:

> **NVDA Momentum Research — Summary**
>
> **Thesis:** NVDA shows strong 60-day momentum (1Y return +45%, well above sector median +12%). Revenue growth YoY of +208% and gross margin of 75% support the momentum fundamentally.
>
> **Evidence:**
>
> * Price: NVDA returned +45% over the past year vs. Technology sector median +12%.
> * Fundamentals: P/E of 68 is rich, but revenue growth (208%) and margin (75%) are best-in-class.
> * Insiders: Net selling over the past 90 days (-480k shares). This is a caution flag but consistent with planned 10b5-1 sales.
> * Congress: One notable Pelosi buy 30 days before the last earnings beat.
>
> **Strategy:** 60-day rank-momentum, monthly rebalance, long-only, 100% gross, 5 bps cost.
>
> **Backtest (1Y):** CAGR 38%, Sharpe 1.42, max drawdown -18%, win rate 58%, 12 trades. Risk gate passed.
>
> **Recommendation:** The momentum thesis is supported by fundamentals. Insider selling is a watch item. The backtest is strong but the drawdown is meaningful. I recommend promoting to paper trading to test live behavior — you can do this from the Ithaca web UI.

## The run state machine

The control plane owns the run lifecycle. The agent can create and observe runs but cannot drive money-path transitions.

```
draft → queued → running → succeeded
                         ↘ failed
                         ↘ timeout
                         ↘ cancelled

# paper deployment lifecycle (human-only):
queued/running/succeeded → promoted → live ⇄ paused → retired
```

| Transition                           | Who            | How                                           |
| ------------------------------------ | -------------- | --------------------------------------------- |
| `draft → queued`                     | Agent or human | `run_skill` / `POST /api/runs`                |
| `queued → running`                   | Worker         | automatic (job claimed)                       |
| `running → succeeded/failed/timeout` | Worker         | automatic (job completes)                     |
| `→ cancelled`                        | Agent or human | `cancel_run` / `POST /api/runs/{id}/cancel`   |
| `→ promoted`                         | **Human only** | web UI button → `POST /api/runs/{id}/promote` |
| `promoted → live`                    | **Human only** | web UI → activate                             |
| `live ⇄ paused`                      | **Human only** | web UI → pause / resume                       |
| `→ retired`                          | **Human only** | web UI → retire                               |

## Related

<Card title="MCP Connection" icon="plug" href="/agent/connection">
  Transport options, the initialize handshake, and tool discovery.
</Card>

<Card title="Starter Prompt" icon="comment-dots" href="/agent/starter-prompt">
  The starter\_research prompt template and ready-to-paste example prompts.
</Card>

<Card title="MCP Tools" icon="wrench" href="/tools/overview">
  Browse the full 60+ tool catalog.
</Card>

<Card title="StrategySpec" icon="clipboard-list" href="/strategy/spec">
  The declarative strategy schema and how backtests work.
</Card>
