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

# Accessing patient state

> Read compiled patient data from your backend or agent runtime via the MCP Patient State server, the Python SDK, or REST.

This guide covers **reading** compiled patient data from your backend or agent runtime (after logs have been ingested) via the **MCP Patient State** server, the **Python SDK** state-read APIs, or **REST** with the same auth rules.

## State views vs state modules (what to call, what you get)

Downstream workflows usually need either **rendered views** (summaries your templates generate) or **raw module payloads** (structured regions of patient state).

| Concept                 | What it is                                                                                                                                                                                                | How you access it (MCP)                                                         | What it returns                                                                                                                                                                                                                                                                   |
| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **State view**          | A **generated snapshot** for a view key (`view_type`, often aligned with template `summary_type`): blocks of text/JSON produced on a cadence from templates (e.g. symptom snapshot, medication snapshot). | `get_view`, `list_views_and_blocks`, `get_view_block`, `get_view_recent_events` | **Markdown** (human-readable) or **raw** JSON: block content, segments (`week` / `long_term` / `temp`), provenance-style refs depending on tool. Use this when an agent or workflow should consume **the same narrative or structured blocks** your product shows in the Console. |
| **Stable modules**      | Slow-changing facts: demographics, condition/diagnosis, medications, preferences, etc.                                                                                                                    | `get_stable_data` (optional `modules` filter).                                  | Module-keyed **payloads** (structured fields), not free-form summaries. Ideal for **deterministic** steps: eligibility, dosing context, identifiers.                                                                                                                              |
| **Event-state modules** | Rolling, event-driven regions (symptoms, labs, adherence, etc.).                                                                                                                                          | `list_event_state_modules`, `get_event_state_module`.                           | **Module-typed** rolling state (e.g. lists of symptoms, recent events): structured payloads aligned with **Patient State** module keys.                                                                                                                                           |
| **Logs & events**       | Raw ingestion history and event audit (`get_events`).                                                                                                                                                     | `get_logs`, `get_events`.                                                       | Rows you can filter by time, log type, or **trace** for lineage.                                                                                                                                                                                                                  |

For a **workflow** that must branch on "what the clinician sees in the chart summary," start from **views** (`get_view` / blocks). For rules that depend on **structured facts** (medication list, diagnosis codes), use **stable** or **event-state modules**. Combine both when an agent needs narrative plus verifiable fields.

## Choose an access path

| Approach                          | Typical use                                                                                              |
| --------------------------------- | -------------------------------------------------------------------------------------------------------- |
| **MCP Patient State**             | Runtimes that discover tools (`get_stable_data`, `get_view`, `get_event_state_module`, …) over JSON-RPC. |
| **Python SDK Patient State read** | Backend jobs already using `olira`; same semantics as MCP with typed helpers.                            |
| **REST API**                      | Non-Python services; mirror auth and scopes from the SDK.                                                |

## Endpoint and credentials (MCP)

Your organization's MCP URL is in [**Console → Settings → MCP**](/console/settings#mcp):

```text theme={null}
https://mcp-patient-state.prod.olira.ai/mcp
```

Send `Authorization: Bearer <token>` on every request:

1. **Opaque API key** (`YOUR_API_KEY`) with **`mcp:patient-state`**: server-side agents and automation.
2. **Short-lived JWT** from `POST /auth/token` (exchange API key).
3. **Patient Token**: see below; **best practice** when the agent must only ever see **one** patient.
4. **Auth0 JWT** from **`olira login`**: interactive developer sessions.

## Agent frameworks

**OpenAI Agents SDK**: attach the MCP base URL and headers so tools load from Olira:

```python theme={null}
from agents import Agent

agent = Agent(
    name="clinical-assistant",
    mcp_servers=[{
        "url": "https://mcp-patient-state.prod.olira.ai/mcp",
        "headers": {"Authorization": "Bearer YOUR_API_KEY"},
    }],
    instructions=(
        "Use get_stable_data and get_view before clinical answers. "
        "Do not invent facts."
    ),
)
```

**LangChain / LangGraph**: point an MCP adapter at the same URL with `streamable_http` and the same `Authorization` header.

## Using MCP resources

**Resources** carry **patient state (and related organization) context** as URI-addressable snapshots (constitution-backed sections, population-view material, or other canonical blobs) over the **same** MCP base URL as tools.

**Design choice:** decide whether each resource's content should land **directly in the prompt** or stay behind **runtime fetch**.

* **Inject into the prompt**: Call `resources/list`, pick the URIs that matter for this workflow, then `resources/read` **before** the model runs and paste the returned **`text`** into your system prompt, developer message, or user preamble. Best when you want **deterministic, fixed context** every turn and accept the token cost up front.
* **Expose at runtime**: Register resources with your agent runtime (when it supports MCP resources) so the model can **request** `resources/read` **when it chooses**, similar to tools. Best when context is **large or optional** and you want the agent to **pull** only what it needs, trading a round-trip for a smaller default context.

Flow:

1. **`resources/list`**: JSON-RPC `method`: `resources/list`, `params`: `{}`. Inspect each entry's **`uri`** and **`name`** to decide what to fetch.
2. **`resources/templates/list`**: Optional; lists resource **templates** when the server advertises them (`resourceTemplates` in the result).
3. **`resources/read`**: Pass **`params.uri`** from the list response. The result includes **`contents`** with **`text`** (often JSON) for that URI.

Provider and organization API keys may see **additional** organization-scoped resources merged into `resources/list`; behavior depends on auth context.

Same **`Authorization`** header as tools. If your stack does not surface MCP resources in the agent UI, call these JSON-RPC methods from your integration layer and inject or bridge the text yourself.

## Using MCP prompts

**Prompts** are reusable templates for different use cases (for example missing-data elicitation or provider alerts). They **guide behavior** in two ways:

* **In-session**: Resolve with `prompts/get` and apply the messages as **system** / **developer** instructions, or prepend to the user turn, so the **same** agent follows Olira's intended tone and structure.
* **Across channels**: The resolved text can be **sent directly** to other parties or surfaces: a **patient**-facing message to probe for missing information, a **provider** alert, or any template meant to be delivered as-is outside the main chat. You still obtain the canonical wording via `prompts/list` → `prompts/get` so it stays aligned with what Olira ships.

Typical flow:

1. **`prompts/list`**: Discover **`name`**, description, and expected **arguments** schema for each prompt.
2. **`prompts/get`**: Pass **`params.name`** and, when required, **`params.arguments`** (key/value object matching that prompt's schema).

Enable **`prompts/list`** and **`prompts/get`** when your framework supports them. If the runtime only exposes **tools**, call these methods over the same **`POST /mcp`** JSON-RPC transport yourself, or **copy** template text into your own prompts until wiring is complete; see [MCP → Resources and prompts](/mcp-server/resources-and-prompts) for method names.

## Querying logs (filter / project / aggregate)

`get_logs()` is a simple time-cursor fetch. When you need richer filtering, field projection, or server-side aggregation, use the **log query builder** (`sdk:state-read` scope).

### Single-patient queries

```python theme={null}
import olira
from olira import F

olira.init(api_key="YOUR_API_KEY")

# Filter + order + limit: returns LogQueryResult
rows = (
    olira.logs("PATIENT_ID")
        .eq("type", "symptom_report")
        .gt("payload.score", 4)
        .ilike("payload.metric_type", "%pain%")
        .order("timestamp", desc=True)
        .limit(25)
        .execute()
)
for row in rows:
    print(row["timestamp"], row["payload"])

# IN filter
rows = (
    olira.logs("PATIENT_ID")
        .in_("type", ["symptom_report", "mood_report"])
        .limit(50)
        .execute()
)

# OR boolean group via F()
rows = (
    olira.logs("PATIENT_ID")
        .or_(F("payload.score").gt(7), F("type").eq("mood_report"))
        .limit(10)
        .execute()
)

# Projection: rows become dicts with only the selected keys
rows = (
    olira.logs("PATIENT_ID")
        .eq("type", "health_metric")
        .select("timestamp", score="payload.score")   # alias=path kwarg
        .limit(10)
        .execute()
)

# Count only: no rows returned
n = olira.logs("PATIENT_ID").eq("type", "symptom_report").count()

# Aggregation: group by type, count per type, average score
agg = (
    olira.logs("PATIENT_ID")
        .group_by("type")
        .count_agg("n")
        .avg("payload.score", "avg_score")
        .execute()
)
```

### Organization / cohort queries

`population_logs()` posts to `POST /v1/state/logs/query` (no patient\_id in the path). Omit `patient_ids` to span the whole org; pass a list for a cohort.

```python theme={null}
# Whole org: last 50 health_metric events
rows = (
    olira.population_logs()
        .eq("type", "health_metric")
        .order("timestamp", desc=True)
        .limit(50)
        .execute()
)

# Explicit cohort
rows = (
    olira.population_logs(patient_ids=["p_1", "p_2"])
        .gt("payload.score", 6)
        .limit(100)
        .execute()
)

# Org-wide event counts by type
agg = (
    olira.population_logs()
        .group_by("type")
        .count_agg("n")
        .execute()
)
```

### Terminals at a glance

| Terminal          | Returns          | Behavior                                                             |
| ----------------- | ---------------- | -------------------------------------------------------------------- |
| `.execute()`      | `LogQueryResult` | All matching rows (or aggregated rows)                               |
| `.count()`        | `int`            | Server-side count only; no rows returned                             |
| `.single()`       | `dict`           | Exactly one row or `ValidationError`                                 |
| `.maybe_single()` | `dict \| None`   | Zero or one row; `ValidationError` if > 1                            |
| `.as_logs()`      | `list[LogEntry]` | Parse rows into typed `LogEntry`; valid when no `.select()` was used |

`LogQueryResult` is iterable, indexable, and `len()`-able. The async client (`AsyncOliraClient`) returns `AsyncLogQuery` with identical interface and `async def` terminals.

### Allowed field roots

The server only allows `type`, `timestamp`, `trace`, and `payload` as filter/projection root paths. Any other root (e.g. `id`, `user_id`) returns HTTP 422, which the SDK surfaces as `ValidationError`.

## Patient-scoped tokens (single-patient agents)

**Best practice:** when an agent or client must only access **one** patient's data (for example a patient-facing assistant), mint a **Patient Token** on your server and pass **`token.access_token`** as the Bearer credential. The token **binds** to that `patient_id`; callers cannot substitute another patient, which **reduces cross-context contamination** (no accidental mixing of patients in one session).

```python theme={null}
import olira

olira.init(api_key="YOUR_API_KEY")  # API key must include sdk:patient-token
token = olira.get_patient_token(patient_id="<patient-id>")
# Client sends: Authorization: Bearer <token.access_token>
# Omit patient_id from MCP tool arguments when using a Patient Token
```

Fields and expiry are documented under [Patient token](/reference/sdk/patient-token/get-patient-token) in the Python SDK reference. For **provider or backend** agents that legitimately need to switch patients, use an API key with **`mcp:patient-state`** and pass **`patient_id`** explicitly per call instead.

## Prompting

**Use only the tools your workflow needs.** MCP exposes many tools (`get_stable_data`, `get_view`, `get_event_state_module`, `get_logs`, …). Registering or describing **the smallest set** that satisfies each agent or step keeps context windows smaller, cuts redundant round-trips, and usually **improves latency and cost**. Prefer **narrow calls**: e.g. `get_stable_data` with a `modules` filter instead of pulling every stable module when you only need medications; request **`raw`** format when the model must parse JSON, **`markdown`** when a short human-readable summary is enough.

For **resources** and **prompts**, follow **Using MCP resources** and **Using MCP prompts** above; they use the same MCP endpoint as tools with `resources/list`, `resources/read`, `prompts/list`, and `prompts/get`.

**Match tool choice to the task** (see the **State views vs state modules** table above): narrative or chart-aligned answers → **views** (`get_view` / blocks); rules, scoring, or integrations → **stable** or **event-state modules**; audits or lineage → **logs** / **events** (`get_events`). Avoid adding unrelated state to the prompt; retrieve in steps and only escalate to broader tools if the first pass is insufficient.

**Scope credentials the same way:** Patient Tokens for a **single** patient session; broader API keys only where the workflow must switch patients. Tighter scope reduces risk of cross-patient mistakes and keeps prompts aligned with one person's data.

Choose **`get_view`** when you need template-driven output; **`get_stable_data`** / **`get_event_state_module`** when you need authoritative structured slices for logic or validation.
