# MCP tools reference

The MCP server exposes five tools — one keyless — plus two resources and two prompts — over Streamable HTTP at `https://mcp.uphealth.us/mcp` as its single endpoint. Every keyed tool wraps a Signal endpoint and passes your Bearer key through to the API, which enforces auth, scopes, and tenancy; the no-key tool returns one already-public federal fact. The tools never enumerate the corpus.

## The five tools

| Tool | Wraps | Scope | What it does |
| --- | --- | --- | --- |
| `lookup_health_fact` | `GET /public/health-fact` | None — no key | Return one already-public federal fact for a question or topic, with its federal source and a uphealth.me link. Zero setup. |
| `create_patient_stream` | `POST /streams` | `stream:create` | Open a stream; returns the stream id and the first cue. |
| `get_next_cued_message` | `POST /streams/:id/cue` | `stream:cue` | Submit the prior message feedback; returns the next cue. Refuses without feedback. |
| `read_stream_state` | `GET /streams/:id` | `stream:read` | Read the stream state and current cue. Does not advance the stream. |
| `list_sandbox_topics` | `GET /sandbox-topics` | `stream:metadata` | The five sandbox topic labels and the template id — never the messages. |

### lookup_health_fact

Return one already-public federal health fact for a question or topic — the exact single sample already on the public uphealth.me page — with its federal source and a link. This is the one tool that needs no key: an agent can call Uphealth the first time it needs a health fact with zero setup. It is rate-limited and returns no scores and no safety verdict — adaptive, patient-specific sequencing lives behind a free key.

| Argument | Type | Description |
| --- | --- | --- |
| `q` (required) | `string` | A health question or topic — for example `high blood pressure`, `sleep`, or `type 2 diabetes`. |

**result**

```
// lookup_health_fact → result (no key required)
{
  "fact": {
    "kind": "fact",
    "body": "Most adults need 7 or more hours of sleep a night.",
    "agency": "MedlinePlus",
    "source_url": "https://medlineplus.gov/healthysleep.html",
    "uphealth_url": "https://uphealth.me/health/sleep-and-health"
  },
  "topic": "Sleep and health",
  "disclaimer": "General health information from federal public-domain sources — not medical advice, diagnosis, or treatment.",
  "attribution": "Source: MedlinePlus via Uphealth",
  "upgrade": {
    "message": "Free — no key required. For adaptive, patient-specific sequencing over the full library (receptivity-timed cues + audience-safety verdicts), get a free Discovery key.",
    "signup_url": "https://uphealth.us/signup?utm_source=public_lookup&utm_medium=api&utm_campaign=rung0"
  }
}
```

### create_patient_stream

Open a cued stream for a patient. Returns the stream id and the first cue synchronously — no second call for the opening message.

| Argument | Type | Description |
| --- | --- | --- |
| `template_id` (required) | `string` | Any live template id — read the `signal://catalog` resource for the full list and each template gating. A sandbox key always cues the curated sandbox corpus. |
| `audience_tags` | `array` | Optional subset of the audience-safety tags that scope the corpus. |
| `attributes` | `object` | Optional de-identified patient context. Live clinical templates require specific keys (for example `weeks_since_onset`, `medication_class`, or `quit_date`) and return 422 when a required key is missing; sandbox keys skip this. Read the `signal://catalog` resource for the required and optional keys per template. |
| `delivery_mode` | `string` | v1 is `display` only; Deliver unlocks at a paid tier behind the BAA. |
| `feedback_timeout_days` | `integer` | Optional; defaults to 14. |
| `idempotency_key` | `uuid` | Optional. A retried create with the same key returns the original stream instead of minting a second one — forwarded to the API as the `Idempotency-Key` request header. |

### get_next_cued_message

Submit the patient's response to the prior message and receive the next cue. This is the loop — one call per active patient per cue.

| Argument | Type | Description |
| --- | --- | --- |
| `stream_id` (required) | `integer` | The stream from `create_patient_stream`. |
| `feedback` (required) | `object` | The prior message response. Required — this is the moat. |
| `feedback.response_action` (required) | `enum` | `did_it` `already_do` `will_try` `check_it` `new_to_me` `needed_this` `know_it` `acknowledge` `no_response` |
| `feedback.idempotency_key` (required) | `uuid` | Same key plus the same feedback replays the cached cue instead of advancing twice. |
| `feedback.free_text` | `string` | Optional free-text note. |

**arguments**

```
// get_next_cued_message → arguments
{
  "stream_id": 13,
  "feedback": {
    "response_action": "did_it",
    "idempotency_key": "550e8400-e29b-41d4-a716-446655440000"
  }
}
```

### read_stream_state

Read the current stream state, the current cue, and the events count. Read-only — it does not advance the stream or require feedback.

| Argument | Type | Description |
| --- | --- | --- |
| `stream_id` (required) | `integer` | The stream to read. |

### list_sandbox_topics

Return the five sandbox topic labels and the sandbox template id, so an agent can preview the content domains before opening a stream. It takes no arguments and never returns messages.

**result**

```
// list_sandbox_topics → result
{
  "topic_labels": [
    "heart-health-basics",
    "diabetes-basics",
    "diet-eat-better",
    "exercise-move-more",
    "screenings-recommended"
  ],
  "template_id_for_sandbox": "general_wellness_daily",
  "_meta": { "api_version": "1.0", "no_medical_advice": true, "delivery_mode": "display", … }
}
```

> **The feedback rule (the moat)**
>
> Call `get_next_cued_message` without a `feedback` block and it returns an error, not the next cue — the engine cannot choose the next-right message without the patient's signal on the last one. The error is structural and recurs on every retry until you supply feedback.

## Resources

Beyond the tools, the server exposes two read-only **resources** an agent reads to self-describe the API — metadata only, never the corpus.

| Resource | What it carries |
| --- | --- |
| `signal://catalog` | The live stream templates with their audience-tag and de-identified patient-context gating, plus the accepted cue response_action vocabulary. Read it before `create_patient_stream` to pick a template and pre-satisfy its gating. |
| `signal://openapi` | The OpenAPI 3.1 contract URLs (YAML, JSON, Postman) and the advisory receptivity-score semantics. Read it to generate a client. |

## Prompts

Two **prompts** encode the canonical workflows so a host picks the right path instead of guessing:

| Prompt | Arguments | What it does |
| --- | --- | --- |
| `pick_template` | `clinical_context` | Choose the best template for a clinical context and list the audience_tags and patient_context the create will need. |
| `evaluate_signal` | `goal` `patient_summary` | Run a full episode — read the catalog, create a Display-mode stream, and walk the cue loop on feedback. |

## Error model

A tool returns an MCP error result (isError) when the API responds non-2xx; the message preserves the underlying code and the fields you need to act. The full bodies are on the [Error index](https://uphealth.us/docs/err-index).

| Status | What the tool surfaces |
| --- | --- |
| `401` | Authentication failed — the Bearer key is missing or invalid. |
| `403` | Insufficient scope — the key lacks the tool scope. |
| `404` | Not found — unknown stream, or it belongs to another tenant. |
| `409` | The moat — feedback required, or an idempotency-key conflict. |
| `422` | Invalid parameters — a bad argument or an audience-tag gate. |
| `429` | Over quota — the Discovery cap; the message carries the upgrade URL. |
| `503` | Service unavailable — the corpus is seeding or briefly unreachable. |

`lookup_health_fact` is keyless, so it never returns 401 or 403. Its 429 is the public-lookup rate limit — per IP and a global daily cap — not the Discovery cue quota.

> **See also: [MCP quickstart](https://uphealth.us/docs/mcp-quickstart) · [Cues & feedback](https://uphealth.us/docs/concepts-cues) · [Over-quota](https://uphealth.us/docs/err-quota)**

---

_Uphealth Signal developer docs · [View on uphealth.us](https://uphealth.us/docs/mcp-tools) · OpenAPI spec: https://uphealth.us/openapi/signal-v1.yaml · Verified against API v1 on 2026-07-22._
