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

# Add context providers

> Give the assistant a short brief about your product at the start of a turn.

A context provider is a short summary Tappify asks your server for and puts in front of the
assistant, so an answer about your extension starts from your facts instead of a guess.

```bash theme={null}
tappify extension add context glossary --when turn_start
```

Leave the flag off and the command asks when the assistant should ask for it. It writes
`server/context/glossary.ts`, adds the handler to your server's `context` map, adds the
`ai:skills` scope, and gives the manifest a `server` block if it has none.

## The manifest entry

```json theme={null}
{
  "scopes": [
    { "key": "ui:render" },
    {
      "key": "ai:skills",
      "justification": "The glossary tells the assistant what our funnel stages count before it answers about them."
    }
  ],
  "contributes": {
    "ai": {
      "context": [
        {
          "id": "glossary",
          "description": "what our funnel stages mean",
          "when": ["turn_start", "mention"],
          "cache": "5m"
        }
      ]
    }
  },
  "server": { "baseUrl": "https://funnel-lab.dev" }
}
```

The command writes this entry. You can hand-edit `tappify.extension.json` instead — the
`$schema` line gives your editor completion and validation, and
[`tappify extension doctor`](/extensions/test/doctor-checks) checks the result.

`description` is 10 to 300 characters and finishes the line the assistant reads above your
summary: `Context from Funnel Lab (what our funnel stages mean):`. Write it as a noun phrase, in
lower case, with no full stop; the label line carries the first 300 characters of it and counts
towards the block's 2 KB.

`when` decides how often you are asked:

| Value        | Called                                                            |
| ------------ | ----------------------------------------------------------------- |
| `turn_start` | On every turn in a project where your extension is installed      |
| `mention`    | Only on a turn where the owner named one of your objects with `@` |

A turn that carries a mention runs both sets, and a provider that declares both phases still
answers once. Use `turn_start` for something short that is true all the time, and `mention` for
anything that depends on which object the owner picked.

## The handler

```ts theme={null}
import { createTappifyHandler } from "@tappify/extension-sdk/server";

export default createTappifyHandler({
  extensionId: "funnel-lab",
  context: {
    glossary: async (request) => ({
      summary:
        "Stage 2 is first open, not install. Cohorts are calendar weeks starting Monday.",
      data: { stages: ["install", "open", "activated", "paid"] },
    }),
  },
});
```

Tappify calls `GET /tappify/context/glossary` under your `server.baseUrl` with the install's
token. It is a GET, so it carries no body: no input, no credentials and no stored documents reach
this route. Anything that needs those belongs in a tool, which the assistant calls with the whole
call body.

Answer with `{ summary, data? }`. `summary` is prose the assistant can quote as it stands;
`data` is the numbers behind it. Anything else is refused and the block is dropped.

## The 2 KB cap

The whole block — the label line, your summary and your serialised `data` — is capped at 2 KB.
Over the cap Tappify drops `data` first, and only then trims `summary`, at a word boundary. Write
the summary so the first sentence is the one that matters.

## When your server is slow or down

A context provider runs before the assistant starts answering, so the owner waits for it. You
have 3 seconds — the health-check budget, not the 30 a tool call gets. Past that, or on any
failure, Tappify drops the block. The owner is not shown an error and the turn carries on without
your context. The call is recorded against the install as a `context_call` with an error outcome:
your Runtime page counts those calls by type over the last thirty days, and the owner sees the
failed one in their install's recent activity.

A provider that fails is then left alone for a minute: the next turns skip it without calling
you, so a server having a bad minute is asked once, not once a turn. Anything you cannot compute
in 3 seconds belongs behind `cache`, or in a tool the assistant calls when it actually needs it.

## Limits

Three context providers per extension, each block at most 2 KB, each call answered within 3
seconds. `cache` accepts `1m`, `5m` or `1h`, and Tappify keys the cache on the install and the
provider, so one workspace's block never reaches another's. The owner who asked is not part of
that key, so don't personalise a cached block by `claims.userId` — the three owners of one
install share the window. Skills, prompts and context providers all sit behind the same
`ai:skills` scope, so declaring your first one asks the owner for it once.
