# MCP server

Let a model read and act on your meetings directly, over the Model Context Protocol.

Everything the REST API does is also available over the [Model Context Protocol](https://modelcontextprotocol.io), so an assistant can read your meetings, pull a transcript and send the notetaker into a call without you writing a tool layer first.

```text Endpoint
https://api-notetaker.nabrah.ai/mcp
```

The transport is Streamable HTTP and the server is stateless — one `POST` per JSON-RPC message, no session to keep alive, and nothing to install.

## How it works


Your client speaks JSON-RPC over HTTPS and authenticates with the same `nt_` key the REST API takes. Every tool calls the same service function the matching REST route calls, so an answer through MCP and an answer from `GET /ext/v1/meetings` are the same object — there is no second code path to drift.

## Before you connect

Issue an API key from **Dashboard → Developers**. A key is shown once, in the response that creates it; there is no way to read it back, so copy it before you close the panel.

| Access | What the model is shown |
| --- | --- |
| `readonly` | The twelve read tools. The write tools are not listed at all, and are refused if one is called anyway. |
| `full` | Every tool, including sending and stopping a bot. |

:::tip Give it the narrowest key that does the job
A model that can see a capability will try it. `readonly` is the right default for anything whose job is to answer questions.
:::

If the person who issued the key owns or administers an organization, the key is organization-wide and the two `get_organization_*` tools answer. For an ordinary member they are not offered at all. Paying for a single seat does not make a key organization-wide either: an individual on a paid plan has no team, so those two tools stay absent.

## Connect your client

:::tabs
### Claude Code

One command, and the server is available in every session:

```bash Terminal
claude mcp add --transport http nabrah https://api-notetaker.nabrah.ai/mcp \
  --header "Authorization: Bearer $NABRAH_API_KEY"
```

Run `/mcp` inside Claude Code to see it listed, and `claude mcp remove nabrah` to take it away again.

### Cursor

Add it to `.cursor/mcp.json` in the project, or `~/.cursor/mcp.json` to have it everywhere:

```json .cursor/mcp.json
{
  "mcpServers": {
    "nabrah": {
      "url": "https://api-notetaker.nabrah.ai/mcp",
      "headers": {
        "Authorization": "Bearer nt_your_key_here"
      }
    }
  }
}
```

Cursor picks the file up on save. Settings → MCP shows the server and the tools it advertised.

### VS Code

GitHub Copilot reads `.vscode/mcp.json`. Take the key as an input rather than committing it:

```json .vscode/mcp.json
{
  "servers": {
    "nabrah": {
      "type": "http",
      "url": "https://api-notetaker.nabrah.ai/mcp",
      "headers": {
        "Authorization": "Bearer ${input:nabrah_key}"
      }
    }
  },
  "inputs": [
    {
      "id": "nabrah_key",
      "type": "promptString",
      "description": "Nabrah Notetaker API key",
      "password": true
    }
  ]
}
```

VS Code asks for the key the first time the server starts and keeps it in its own secret store.

### Claude Desktop

Claude Desktop's config file speaks stdio, so bridge it with `mcp-remote`:

```json claude_desktop_config.json
{
  "mcpServers": {
    "nabrah": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://api-notetaker.nabrah.ai/mcp",
        "--header",
        "Authorization: Bearer nt_your_key_here"
      ]
    }
  }
}
```

The file is at `~/Library/Application Support/Claude/claude_desktop_config.json` on macOS and `%APPDATA%\Claude\claude_desktop_config.json` on Windows. Restart the app after editing it.

### Anything else

Any client that can send a header connects with the same three facts — an HTTP transport, the URL, and the key:

```json Generic
{
  "mcpServers": {
    "nabrah": {
      "type": "http",
      "url": "https://api-notetaker.nabrah.ai/mcp",
      "headers": {
        "Authorization": "Bearer nt_your_key_here"
      }
    }
  }
}
```

If your client only speaks stdio, put `npx -y mcp-remote <url> --header "Authorization: Bearer nt_…"` in front of it, as in the Claude Desktop tab.
:::

:::note Config formats move
Clients rename these fields more often than the protocol changes. Whatever yours calls them, the three facts it needs are the same: an **HTTP** transport, `https://api-notetaker.nabrah.ai/mcp`, and an `Authorization: Bearer` header.
:::

## Check the connection

`tools/list` is the cheapest call that proves the URL, the key and the access level at once.

```bash Terminal
curl -s https://api-notetaker.nabrah.ai/mcp \
  -H "Authorization: Bearer $NABRAH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
```

A working key comes back with a `tools` array. A wrong one comes back `401`. A `readonly` key comes back with the read tools only — which is the fastest way to tell which kind of key you pasted.


## The tools

### Reading

| Tool | What it returns |
| --- | --- |
| `list_meetings` | Meetings on the connected calendar — titles, times and links, not recordings. |
| `get_schedule` | What is coming up **and** whether the notetaker will join each one. When it will not, the reason is given — the same decision the recording engine makes. |
| `get_meeting` | One meeting, with its bot status and its notes if it has been recorded. |
| `list_recordings` | Finished and in-progress recordings, newest first. Calendar meetings, manual joins and uploads alike. |
| `get_recording` | A recording's summary, attendees and action items. The transcript is not included. |
| `get_transcript` | The utterances of one recording, in order, with speakers and offsets. Paged. |
| `list_action_items` | The action items extracted from one recording. |
| `get_bot` | The live state of one bot run — whether it has joined, and for how long. |
| `list_calendar_connections` | Which calendars are connected, and the auto-join rules on each. |
| `get_usage` | This month's meetings and AI credits used and left, bonus credits, minutes per meeting, and when limits reset. Any key. |
| `get_organization_usage` | Seats, members and this month's usage for the whole organization. Organization key only. |
| `get_organization_analytics` | Counts and durations for the organization, with insights: change vs the previous window, join rate, weekdays, sources and AI questions. Never meeting content. |

### Acting

Offered only to a `full` key.

| Tool | What it does |
| --- | --- |
| `send_bot_to_meeting` | Sends the notetaker into a meeting that is happening now. Counts against the monthly allowance. |
| `stop_bot` | Pulls the notetaker out of a call it is currently in. |
| `set_meeting_recording` | Turns recording on or off for one meeting. Outranks every auto-join rule and survives calendar re-syncs; pass `null` to hand the decision back to the rules. |
| `complete_action_item` | Marks an action item done, or reopens it. |
| `create_calendar_connect_link` | Produces a URL a **person** must open in a browser. This one cannot be finished headlessly. |

:::note One implementation, two ways of asking
There is no MCP-specific business logic. Each tool is a thin wrapper over the service its REST route already calls, which is why the two surfaces cannot disagree about what a meeting is.
:::

## What the model is told up front

`initialize` returns instructions carrying the facts a model would otherwise guess wrong:

> Meeting recordings, transcripts and action items for one Nabrah account. One bot can be live per account at a time. Recording limits are per calendar month. Connecting a calendar needs a person to open a link in a browser.

Three sentences, and they prevent a whole class of confidently wrong answers — a model that does not know a calendar connection needs a human will otherwise promise to make one.

## Things worth asking it

Once the server is connected these are ordinary questions rather than API calls:

- *"What is on my calendar tomorrow, and which of those will be recorded?"*
- *"Summarise yesterday's client call and list the action items assigned to me."*
- *"Send the notetaker into the meeting I am in right now."*
- *"Which action items from the last two weeks are still open?"*
- *"Pull the transcript from Monday's review and find where we agreed the launch date."*

## Limits

| Limit | Value |
| --- | --- |
| Requests | 600 per 15-minute window, counted per key |
| Live bots | One per account at a time |
| Meetings | Your plan's monthly allowance, reset on the 1st. `get_usage` shows what is left |
| Minutes per meeting | Set by your plan; the bot leaves at the cap |
| Transcript size | `get_transcript` is paged — take `limit` and `offset` rather than the whole call at once |

Every response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset`, so a client can pace itself before it overruns. A spent window answers `429` with `Retry-After` in seconds.

## When it will not connect

:::warning Clients that insist on OAuth
This server authenticates with an API key, not an authorization server. A client that requires full OAuth 2.1 discovery will not connect to it, and no amount of configuration changes that. It is a real limitation rather than an oversight — worth knowing before you spend an afternoon on it.

A client that lets you set a header, or one you bridge with `mcp-remote`, connects with the key.
:::

| Symptom | Cause |
| --- | --- |
| `401` on every call | The key is wrong, revoked, or missing the `Bearer ` prefix. |
| A write tool is missing from `tools/list` | The key is `readonly`. Issue a `full` one — the access level cannot be changed after the fact. |
| `get_organization_usage` is missing | The key was issued by an ordinary member rather than an owner or admin. |
| `429` | The 15-minute window is spent. Honour `Retry-After`. |
| `503` with `Retry-After: 5` | Rate limiting itself is unavailable, and the API refuses rather than running unmetered. Wait and retry. |

## Where to go next

:::cards 3
- {book} [REST API](/docs/api) — the same capabilities over plain HTTP, with a Try it panel on every endpoint.
- {key} [Authentication](/docs/api/authentication) — key format, access levels, and how far a key reaches.
- {blocks} [Agent skills](/docs/api/skills) — everything an agent needs, in one file.
:::
