accurate-schema-mcp
# accurate-schema-mcp
Schema-aware [Model Context Protocol](https://modelcontextprotocol.io/) server
for [Accurate Online](https://accurate.id/).
## Features
- **Always-fresh OpenAPI spec.** Refetched on every server startup (i.e. each
time Claude Desktop launches) and written over the cached copy, so new
endpoints appear with no code change and no manual step. If the download
fails, the cached copy is used.
- **Host auto-resolution.** Nothing is hardcoded — the server asks Accurate
which host your API Token's database lives on. `refresh_host` re-resolves
if the database migrates mid-session.
- **Delete kill switch.** `generic_call` refuses delete endpoints (HTTP
`DELETE` *and* `*/delete.do`, `*/bulk-delete.do`) unless you explicitly
opt in.
- **Verified list fields.** Accurate's spec documents no response schema for
`list.do`, so real field names are shipped in `list_fields.json` and merged
into every `<resource>/list` lookup.
- Python 3.10+, zero config files beyond `.env`, only `mcp` + `requests` as
dependencies.
## Requirements
- Python version >= 3.10
- [`uv`](https://docs.astral.sh/uv/) for dependency management
- An Accurate Online **API Token** — steps on how to get `Bearer Token` and `API Secret` from https://drive.google.com/file/d/1_EKt6DE6U-6vkmv4M_vizmC-27wXjXa0/view.
## Install
```bash
git clone https://github.com/aol-integration/accurate-schema-mcp.git
cd accurate-schema-mcp
uv sync # creates .venv from the exact versions pinned in uv.lock
```
## Configure
```bash
cp .env.example .env
```
Edit `.env`:
```env
ACCURATE_BEARER_TOKEN=<your_api_token>
ACCURATE_API_SECRET=<your_api_token_secret_key>
# generic_call refuses delete endpoints unless this is true.
ACCURATE_ENABLE_DELETE=false
```
See [this page](https://drive.google.com/file/d/1_EKt6DE6U-6vkmv4M_vizmC-27wXjXa0/view) for more information on Accurate Online API Token authentication.
## Add to Claude Desktop
1. Open **Claude Desktop**.
2. Go to **Settings → Developer → Edit Config**. This opens the folder
containing `claude_desktop_config.json`.
3. Open `claude_desktop_config.json` in a text editor.
4. Paste this in. If the file already has `mcpServers`, add only the
`"accurate-schema"` entry inside it.
```json
{
"mcpServers": {
"accurate-schema": {
"command": "uv",
"args": [
"run",
"--directory", "/absolute/path/to/accurate-schema-mcp",
"python", "-m", "accurate_schema_mcp.server"
]
}
}
}
```
5. Replace `/absolute/path/to/accurate-schema-mcp` with your clone's real path
(`pwd` inside the folder). `--directory` also sets the working directory, so
`.env` is picked up.
6. Save the file and **fully quit** Claude Desktop (Cmd+Q on macOS), then
reopen it. The config is only read at launch.
7. Check the tools menu in the chat input — `accurate-schema` should appear
with 4 tools.
The server speaks MCP over stdio.
## Tools (4)
| Tool | Arguments | Description |
|------|-----------|-------------|
| `list_resources` | — | All resource names (`item`, `vendor`, `purchase-invoice`, `sales-order`, …). Call first if you don't know the resource. |
| `schema_lookup` | `endpoint` | Fields, query params and required-ness for one endpoint. Accepts `'<resource>/<action>'` (e.g. `'purchase-invoice/save'`) or just `'item'` to list that resource's endpoints. |
| `generic_call` | `endpoint`, `params?`, `body?` | Call any endpoint. `params` for GET/DELETE, `body` for POST (`save`, `bulk-save`). |
| `refresh_host` | — | Re-resolve which host the API Token's database lives on. |
### Typical flow
```python
# 1) Which resources exist?
list_resources()
# -> ["access-privilege", "branch", "customer", "item", "purchase-invoice", ...]
# 2) What does this endpoint want?
schema_lookup(endpoint="purchase-invoice/save")
# -> {"method": "POST", "path": "/api/purchase-invoice/save.do",
# "body": {"vendorNo": {"required": true, ...}, ...}}
# 3) Call it
generic_call(
endpoint="item/list",
params={"fields": "id,name,unitPrice", "sp.pageSize": 50},
)
```
## Authentication
Each request is signed fresh — no session or token-refresh step:
```
X-Api-Timestamp: <unix epoch milliseconds>
X-Api-Signature: HMAC-SHA256(api_secret, timestamp)
Authorization: Bearer <bearer_token>
```
The host is resolved at startup by calling
`https://account.accurate.id/api/api-token.do` with the same headers. The host
comes straight from `d.database.host` in that response (e.g.
`https://public.accurate.id`) — one field, no payload walking. A `401` means the
token or signature is invalid; note
that Accurate also signals failure with HTTP `200` + `{"s": false}`, which is
handled as an auth error.
`GET` requests retry up to 3 times with backoff on `502/503/504`. Mutating
requests are never retried — they may already have succeeded.
## Schema cache
The OpenAPI spec is downloaded from
`https://account.accurate.id/open-api/json.do` into
`schema/accurate_openapi.json` on **every server startup**, overwriting the
previous copy (first run simply creates it). The write is atomic — the spec is
staged to a `.tmp` file and swapped in — and a response with no `paths` is
rejected, so a bad download can't clobber a good cache. If the fetch fails and
a cached copy exists, the server logs a warning and starts with the cache.
To refresh it by hand:
```bash
uv run python -m accurate_schema_mcp.fetch_schema --force
```
## Environment variables
| Variable | Required | Notes |
|---|---|---|
| `ACCURATE_BEARER_TOKEN` | yes | Accurate Online → Accurate Store → API Token |
| `ACCURATE_API_SECRET` | yes | Accurate Online → Developer Area |
| `ACCURATE_ENABLE_DELETE` | no | `false` by default; `generic_call` refuses delete endpoints until this is `true` |
## Project layout
```
accurate_schema_mcp/
├── server.py MCP entrypoint (stdio)
├── tools_schema.py the four tools + delete guard
├── client.py HMAC auth, host resolution, HTTP
├── config.py env vars / .env loader (no dependency)
├── schema_index.py parses the OpenAPI spec into a flat index
├── fetch_schema.py downloads the spec (refreshed every startup)
└── list_fields.json verified list.do response fields (absent from the spec)
schema/
└── accurate_openapi.json cached spec
```
## Verifying it works
```bash
uv run python -c "from accurate_schema_mcp.client import get_client; c = get_client(); print(c.base_url)"
```
Printing a base URL like `https://public.accurate.id/accurate` confirms the
credentials, the signature and host resolution all work. A `401` usually means
a wrong secret, a wrong bearer token, or whitespace pasted into `.env`.
## License
MITTDQS
Scored across 4 tools
Each tool targets a distinct phase: list_resources for discovery, schema_lookup for metadata, generic_call for execution, and refresh_host for a specific maintenance edge case. There is no meaningful overlap; an agent can easily pick the right tool based on intent.
All names use consistent snake_case, which is a strong pattern. However, schema_lookup and generic_call are noun/adj+noun rather than the verb_noun pattern seen in list_resources and refresh_host, a minor deviation from an otherwise predictable convention.
Four tools is well-scoped for a generic API gateway: discovery, schema, execution, and host refresh. Each tool earns its place, and no unnecessary tools bloat the surface.
The surface covers discovery, metadata lookup, and execution for any endpoint, enabling full CRUD via generic_call. Minor gaps exist, such as no explicit pagination or batch helper, but these can be handled through generic_call parameters.