MCP
cardaga.in speaks the Model Context Protocol, so an agent can work on a collection directly. It is one URL and one key. The agent discovers what it can do from tools/list, which means you do not configure a tool list anywhere — the server decides what your key may see.
The endpoint
https://mcp.cardaga.in/mcp
# self-hosting, or running locally:
http://localhost:7145/mcpAuthentication is a bearer token: Authorization: Bearer cardi_…. It is the same credential the HTTP API takes, checked in the same place, so revoking a key stops it working on both in the same instant.
Get a key
Create one on your account page. Choose read only unless the agent needs to change things — you can create a second key later, and a key that can only look is a key you never have to think about again.
Configure your client
Claude Code, from a terminal. The other clients take the same two values in their own configuration format.
claude mcp add --transport http cardagain https://mcp.cardaga.in/mcp \
--header "Authorization: Bearer $CARDAGAIN_API_KEY"Claude Desktop and Cursor read a JSON file. Put the key in an environment variable rather than in the file — a config with a credential in it gets copied into a chat window sooner or later.
{
"mcpServers": {
"cardagain": {
"type": "http",
"url": "https://mcp.cardaga.in/mcp",
"headers": { "Authorization": "Bearer ${CARDAGAIN_API_KEY}" }
}
}
}The tools
Sixteen, covering finding, creating and editing cards and decks. Everything else the server can do — cloze conversion, splitting and merging notes, note-type changes, speech synthesis, layout auditing — is deliberately not published to third-party keys yet.
| Tool | Access | What it does |
|---|---|---|
| search_notes | read | Search notes with the browser query language (deck:, tag:, is:due, prop:ivl>21). Returns a keyset page. |
| get_note | read | Fetch one note by id, with its fields keyed by field id and its tags. |
| get_card | read | Fetch one card by id, including its scheduling state and its rendered question and answer. |
| list_decks | read | List decks with their display paths, so a deck can be named before it is used. |
| list_note_types | read | List note types with their fields, templates, and whether each already has card-writing guidance (hasGuide), to pick a type and field ids. |
| list_tags | read | List tags with usage counts, to reuse an existing tag rather than inventing one. |
| create_note | write | Create a note in an explicit deck, generating one card per template. Fields are keyed by field id. |
| create_notes | write | Create many notes at once in ONE deck with ONE note type, generating one card per template for each. Prefer this over repeated create_note whenever you are writing more than one card: name the deck, the note type and any shared tags once, then list the notes. Fields are keyed by field id. The whole batch is one change the user accepts or undoes in one action. |
| create_deck | write | Create a deck, optionally nested under a parent. Applies immediately. Undo deletes the deck, and only works while the deck is still empty -- undo the notes inside it first. |
| update_note_fields | write | Replace the named fields of one note. Fields left out are untouched. Proposes a reviewable diff. |
| add_tags | write | Add tags to an explicit list of notes. Tags use :: for hierarchy. |
| remove_tags | write | Remove tags from an explicit list of notes. Tags not present are ignored. |
| move_cards | write | Move an explicit list of cards into a target deck, leaving scheduling untouched. |
| suspend_cards | write | Suspend or unsuspend an explicit list of cards. Suspension is a timestamp, not a scheduling state. |
| set_flag | write | Set the flag colour (0 clears, 1-7 are the palette) on an explicit list of cards. |
| reposition_new_cards | write | Renumber the new-queue position of an explicit list of cards, optionally shifting the cards after them. |
What happens when an agent edits your collection
By default, nothing is applied. A write tool does not change your notes. It records a proposal — the exact before-and-after, field by field — and returns status: "proposed". The change shows up in the app for you to accept or reject, and accepting is what applies it.
You can grant one key the right to apply its edits directly, without asking. Do it from the accept control — it names the key it is about to trust — or from the key's own row on your account page. It is off until you turn it on, it is per key rather than global, and the same switch turns it back off.
Either way every change is recorded and reversible: an applied edit can be reverted from its full prior state rather than by computing an inverse, so undo cannot drift from what was done.
Errors, and what an agent sees
A tool your key may not use does not exist as far as your session is concerned: tools/list omits it and tools/call answers unknown tool — the same answer a misspelled name gets. A revoked or expired key fails to authenticate on the next call. An id belonging to another account is not found, never forbidden, because "forbidden" would confirm the row exists.
Calling a write tool twice with the same requestId produces one change and returns the original result, so an agent retrying after a dropped connection is safe rather than merely lucky.
The HTTP API reference covers everything the MCP does not.