# Kweenkl — Push Notifications for AI Agents

Kweenkl sends instant push notifications to your human's iPhone. One HTTP request. That's it.

MCP Server URL: https://api.kweenkl.com/mcp
Website: https://kweenkl.com
App Store: https://apps.apple.com/us/app/kweenkl/id6754047897

---

## Quick Start

If you already have a channel token, send a notification now:

```
POST https://api.kweenkl.com/webhook/{channel_token}
Content-Type: application/json

{
  "message": "Your task is complete.",
  "title": "Agent Update",
  "priority": "normal"
}
```

That's all you need. The human gets an instant push notification on their iPhone.

---

## Setup

### Step 1: Check if already set up

Call `kweenkl_list_channels`. If it returns channels, you're done — skip to "Sending Notifications".

If it fails with "User not found" or "Device token not configured", the MCP server is not yet connected. Guide the human through Step 2.

### Step 2: Install the app

The human needs the kweenkl iOS app on their iPhone or iPad. Tell them:

> Download kweenkl from the App Store: https://apps.apple.com/us/app/kweenkl/id6754047897
> Open the app. It will register your device automatically.

### Step 3: Connect the MCP server

The setup depends on which AI client the human is using. Guide them to the right one:

#### Claude Code (CLI)

Tell the human to run this in their terminal:

```
claude mcp add kweenkl https://api.kweenkl.com/mcp --transport http --scope user --header "X-Kweenkl-Device-Token: DEVICE_TOKEN"
```

To get their device token: open the kweenkl app → Settings → tap "Copy Device Token", then paste it in place of `DEVICE_TOKEN` above. Restart Claude Code after.

#### ChatGPT

Tell the human to:

1. Open ChatGPT on desktop (not mobile)
2. Go to **Settings** → **Apps & Connectors** (requires ChatGPT Plus/Team/Enterprise)
3. Click **Add new connector**
4. Enter the URL: `https://api.kweenkl.com/mcp`
5. A kweenkl authorization page will open — enter the device token from the kweenkl app (Settings → Copy Device Token)
6. Click **Authorize** — done

No manual headers or API keys needed. ChatGPT uses OAuth to authenticate with kweenkl automatically.

#### Claude Desktop & Claude.ai (browser)

Tell the human to:

1. In Claude Desktop or on claude.ai, open **Customize → Connectors** (Free/Pro/Max/Team/Enterprise are all supported)
2. Click **+** → **Add custom connector**
3. Enter the URL: `https://api.kweenkl.com/mcp`
4. A kweenkl authorization page opens — paste the device token from the kweenkl app (Settings → Copy Device Token), then click **Authorize**
5. Done — Claude can now call the kweenkl tools

**Do not attempt the 4-character code flow (`kweenkl_login`) on Claude Desktop or claude.ai.** Their connector UI has no custom-header field, so even after a successful pairing the AI cannot apply the device token and the user is left without a working connection. Always direct them to the Customize → Connectors path above.

#### Any other MCP client

- MCP endpoint: `https://api.kweenkl.com/mcp`
- Transport: HTTP (streamable)
- Authentication options:
  - **Header auth**: set `X-Kweenkl-Device-Token` header to the device token
  - **OAuth**: discovery at `https://api.kweenkl.com/.well-known/oauth-authorization-server`

### Step 4: Verify

After setup, call `kweenkl_list_channels` to confirm the connection works. If the human has no channels yet, help them create one with `kweenkl_create_channel`.

### Step 5: Pair via AI (alternative path)

**Use this path only for clients that can self-update their MCP auth: Claude Code (CLI), Cursor, and other agents with shell access — and ChatGPT, which supports re-running the connector OAuth with a new token.**

**Do not run this on Claude Desktop or claude.ai.** Their connector UI accepts only URL + OAuth client credentials — no custom header field — so there is no way to apply the device token returned by `kweenkl_login_check`. Send those users to the **Claude Desktop & Claude.ai** instructions in Step 3 instead.

If the MCP client uses OAuth (ChatGPT) or the human prefers not to paste their device token into a config, call `kweenkl_login` with `has_app: true`. The tool returns two equally-supported options the human can pick from:

- **Option A — Code pairing** *(latest kweenkl iOS app)*. The AI displays a 4-character code (e.g. `AB7K`). The human opens the **kweenkl** app → **Settings** → **Connect AI Assistant**, types the 4 characters, and taps **Connect**. The code expires in 2 minutes. *(If the human happens to be reading the AI's response on iPhone Safari, they can also tap the `tweekl://pair` link to jump straight to the pairing screen.)*
- **Option B — Device token** *(works on any kweenkl app build)*. The human opens **kweenkl → Settings → Copy Device Token**, then asks the AI to re-run `kweenkl_login` with `device_token="<paste>"`. Pairs immediately, no code needed. This path stays supported indefinitely and is the right fallback for users on older app builds.

After approval, poll `kweenkl_login_check` with the returned `session_id` (every ~3 s) until the status is `approved`. **Keep polling — don't give up after one or two tries.** Per-tool permission prompts in some clients can pause polling at the wrong moment; if you stop too early you'll miss the approval and frustrate the user.

#### What to do once `kweenkl_login_check` returns approved

The response will include the human's **device token**. The AI must now register this token as MCP auth and reload the connection — the user should not have to do this manually.

- **If you have shell access** (Claude Code, Cursor, agents with Bash tool): run

  ```
  claude mcp remove kweenkl 2>/dev/null
  claude mcp add kweenkl https://api.kweenkl.com/mcp --transport http --scope user --header "X-Kweenkl-Device-Token: <TOKEN>"
  ```

  then tell the user: *"Exit and relaunch Claude Code (`claude`) to apply the auth."*

- **If you're a hosted connector with no shell access** (ChatGPT): you cannot apply the header programmatically. Tell the human to remove and re-add the kweenkl connector via Settings → Apps & Connectors, pasting the device token shown into the OAuth authorization page.
- **If you're Claude Desktop or claude.ai**: stop here. There is no custom-header field in the connector UI, so the device token cannot be applied. Direct the human to the **Claude Desktop & Claude.ai** instructions in Step 3 — that path uses the OAuth page to capture the device token in one shot.

After the restart, call `kweenkl_status` to confirm authentication, then `kweenkl_list_channels` to verify full access.

---

## Two Tokens

Kweenkl uses two tokens:

| Token | What it is | What it's for |
|-------|-----------|---------------|
| **Device token** | Identifies the human's iPhone | Managing channels: list, create, organize. Obtained from the kweenkl app Settings. |
| **Channel token** (webhook token) | Identifies a specific notification channel | Sending notifications. Each channel has its own token. Used in the webhook URL. |

A human has **one device token** and **many channel tokens** (one per channel). You need the device token to manage channels. You need a channel token to send a notification.

---

## Sending Notifications

Use the `kweenkl` tool with a channel's webhook token:

```
kweenkl(webhook_token: "CHANNEL_TOKEN", message: "Your task is complete.", title: "Agent Update", priority: "normal")
```

Or via HTTP:

```
POST https://api.kweenkl.com/webhook/{channel_token}
Content-Type: application/json

{
  "message": "Your task is complete.",
  "title": "Agent Update",
  "priority": "normal"
}
```

### Request Body

| Field      | Type   | Required | Description                              |
|------------|--------|----------|------------------------------------------|
| `message`  | string | yes      | The notification body text               |
| `title`    | string | no       | Notification title (shown in bold)       |
| `priority` | string | no       | `"low"`, `"normal"` (default), or `"high"` |
| `glance`   | object | no       | Structured visualization rendered as a card inside the kweenkl iOS app. See [Glance Cards](#glance-cards). |

---

## Glance Cards

A *glance card* is a small structured visualization that the kweenkl iOS app renders natively as a card inside the channel — no image is sent over the wire, the device renders it from the data you pass. v1 supports a single type: `line_chart`.

When you have a numeric series worth showing (sales over time, error counts per day, deploys per week, a metric trend), pass a `glance` field alongside `title` and `message`. The human sees the chart as the most recent message in the channel; older app versions fall back to plain text automatically.

### `line_chart` fields

| Field      | Type     | Required | Description                                                                       |
|------------|----------|----------|-----------------------------------------------------------------------------------|
| `type`     | string   | yes      | Must be `"line_chart"`                                                            |
| `labels`   | string[] | yes      | X-axis labels, ≥ 2 entries, same length as `values`                               |
| `values`   | number[] | yes      | Y-axis numeric values, ≥ 2 entries, same length as `labels`                       |
| `subtitle` | string   | no       | Small text under the title                                                        |
| `accent`   | string   | no       | Hex color for the line, e.g. `"#ff6b6b"`. Defaults to coral.                      |
| `comment`  | string   | no       | Annotation displayed near the auto-computed delta badge                           |
| `x_label`  | string   | no       | Optional X-axis title                                                             |
| `y_label`  | string   | no       | Optional Y-axis title                                                             |

The delta percentage (e.g. `+95.5%`) and color (red for up, green for down) are computed by the device from `values.first` vs `values.last`. You don't pass them.

### Example

```
kweenkl(
  webhook_token: "CHANNEL_TOKEN",
  title: "Accidents de vélo · Paris",
  message: "1 310 en 2024 · +95,5% sur 10 ans",
  glance: {
    "type": "line_chart",
    "subtitle": "Évolution 2015 → 2024",
    "labels": ["2015","2016","2017","2018","2019","2020","2021","2022","2023","2024"],
    "values": [670, 720, 780, 810, 920, 850, 1050, 1180, 1240, 1310],
    "accent": "#ff6b6b",
    "comment": "presque le double"
  }
)
```

### When to use a glance card

- You're reporting a metric trend (sales, latency, error rate, conversions)
- You're sending a periodic summary that benefits from visual context
- The numbers are more meaningful as a shape than as text

Don't pass a glance card for one-off events ("deploy succeeded"), error alerts, or anything where the chart would just be a flat line.

---

## Available MCP Tools

| Tool                    | Token needed   | Description                                |
|-------------------------|----------------|--------------------------------------------|
| `kweenkl_status`        | none           | Check server connectivity                  |
| `kweenkl_list_channels` | device token   | List channels and get their channel tokens |
| `kweenkl_create_channel`| device token   | Create a new notification channel          |
| `kweenkl_update_channel`| device token   | Update a channel's name, description, color, or icon |
| `kweenkl_delete_channel`| device token   | Delete a notification channel              |
| `kweenkl`              | channel token  | Send a push notification                   |

---

## Channels

Channels organize notifications by concern, not by message type. Examples:
- **Research** — deep work, papers, analysis
- **Product** — deploys, metrics, errors
- **Reminders** — time-sensitive actions
- **Finance** — payments, invoices, alerts

Use `kweenkl_create_channel` to create new channels as new concerns emerge.

---

## When to Notify

Good reasons to send a notification:
- A long-running task finished
- You need human approval before proceeding
- An error occurred that requires attention
- A scheduled report or summary is ready
- A monitored condition was triggered (price alert, server down, etc.)

Keep notifications concise. Use `title` for the category, `message` for the details. Use `"priority": "high"` only for urgent/critical alerts.

---

## Limits

- 100 notifications per day (free plan)
- 20 notifications per minute per channel
- Delivery is under 1 second
- `message` max recommended length: 500 characters for optimal mobile display
