Metadata-Version: 2.5
Name: sadakio-mcp
Version: 0.1.0
Summary: MCP server for the Sadakio Public API — read a business's own guests, visits and retention from any MCP client
Project-URL: Homepage, https://sadakio.com/gelistirici
Project-URL: Source, https://github.com/lio-maker/sadakio
License-Expression: MIT
License-File: LICENSE
Keywords: api,cafe,loyalty,mcp,model-context-protocol,retention,sadakio
Requires-Python: >=3.10
Requires-Dist: httpx>=0.27
Requires-Dist: mcp<3,>=2.0
Description-Content-Type: text/markdown

# sadakio-mcp

MCP server for the [Sadakio](https://sadakio.com) Public API. It gives any MCP
client — Claude Code, Claude Desktop, Cursor, your own agent — read access to
**one business's own** guests, visits and retention numbers.

Sadakio is the operating layer for small hospitality businesses in Türkiye:
guest base, loyalty programme, return visits, QR menu, Apple Wallet cards. If
you are building something for cafés, salons or shops — your own product, a
dashboard for a client, or an integration with a POS you already sell — this is
the read side of that data without you having to build and run a loyalty engine.

Read-only today, because the Public API v1 is read-only. Write tools arrive with
the write endpoints, and each one will name the guard it goes through.

## Install

Nothing to install. Point your client at it with `uvx`.

## Get a key

A business owner creates the key in the Sadakio panel under **Ayarlar → API**.
The raw key is shown once, at creation; only its fingerprint is stored.

Each key belongs to exactly one business and carries the `read` scope. It can
never name another business's row — a foreign id answers `404`, not `403`, so
the API cannot be used to discover what exists elsewhere.

## Configure

```json
{
  "mcpServers": {
    "sadakio": {
      "command": "uvx",
      "args": ["sadakio-mcp"],
      "env": { "SADAKIO_API_KEY": "your key from Ayarlar → API" }
    }
  }
}
```

Claude Code takes the same block in `.mcp.json`, Claude Desktop in
`claude_desktop_config.json`, Cursor in `.cursor/mcp.json`.

This is the Python twin of `sadakio-mcp` on npm. Same four tools, same names,
same wording, so an agent gets the same answer whichever one you installed —
pick whichever fits your stack.

| Variable | Required | Default |
|---|---|---|
| `SADAKIO_API_KEY` | yes | — |
| `SADAKIO_BASE_URL` | no | `https://api.sadakio.com/api/v1` |

## Tools

| Tool | What it answers |
|---|---|
| `list_guests` | The business's guest base, cursor-paginated. `updated_since` matches guests who visited **or** were created since that moment, so an incremental sync never misses a brand-new guest. |
| `get_guest` | One guest with their loyalty cards and balances. |
| `list_visits` | The earn-event feed: stamps, points, cashback, redeems. |
| `get_retention` | Returned-guest numbers over a window. |

Two resources come with it: `sadakio://docs/quickstart`, which an agent can read
before its first call, and `sadakio://docs/openapi`, which fetches the live
contract rather than a vendored copy that would drift on the next deploy.

## Four things worth knowing before you trust a number

**Phones are always masked.** Last four digits only, and there is no unmasked
path. This is a KVKK decision, not a scope you can request your way past.

**Pagination is by cursor.** Every list answers with `data` and `next_cursor`;
pass `next_cursor` back as `cursor`, and `null` means you are on the last page.
Cursors run by ascending id, so a page stays stable while new rows arrive.

**A reversed earn keeps its row**, with `reversed_at` set. History is never
deleted, so exclude reversed rows yourself when you count.

**No money figure is invented.** `get_retention` returns
`estimated_returned_value` only when you supply a real `avg_ticket`. Without
one, you get counts and no currency.

## Errors

Failures come back as tool errors carrying the API's own message plus the one
step that fixes it. `401` means the key is missing, unknown or revoked. `403`
means the key lacks a scope, or the business account is not active. `404` means
the row is not inside this key's business. `422` names the parameter. On `429`
the message carries the seconds from `Retry-After`; nothing is retried behind
your back, so your agent decides when to come back.

The host serves only `/api/v1`; everything else there answers 404. The older
`https://app.sadakio.com/api/v1` still answers identically and is not
deprecated, so nothing already integrated needs to move.

## Contract

The machine-readable contract is served publicly, without a key, at
`https://api.sadakio.com/api/v1/openapi.yaml`. Response fields may be **added**
over time, never renamed or removed — so parse leniently and you will not be
broken by a release.

Docs and quickstart: <https://sadakio.com/gelistirici>
Questions: biz@sadakio.com

Python 3.10 and up. MIT licensed. Built by the Sadakio team, and pull requests are welcome.
