Add Jortt API MCP server

Model Context Protocol server that exposes the full Jortt accounting API
as MCP tools, generated from Jortt's OpenAPI specification.

- OAuth 2.0 client-credentials auth with token caching and 401 refresh
- 82 tools covering customers, invoices, estimates, expenses, projects,
  bank accounts, reports, labels, ledger accounts and payroll
- Spec fetched at build time (with a runtime fetch fallback), so new API
  operations become new tools automatically with no code changes
- Path/query/body parameters handled automatically; request bodies passed
  under a single `body` argument with a resolved JSON Schema
- Readable, stable snake_case tool names derived from method, path and
  operation summary

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016QhaFvGz9gfRg5qDV3ragu
This commit is contained in:
Claude 2026-07-11 14:09:11 +00:00
commit 240a53fade
No known key found for this signature in database
12 changed files with 2057 additions and 0 deletions

148
README.md Normal file
View file

@ -0,0 +1,148 @@
# jortt-mcp
A [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server for the
[Jortt](https://www.jortt.nl) accounting & invoicing API.
It exposes the full Jortt REST API — customers, invoices, estimates, expenses,
projects, bank accounts, reports, and more — as MCP tools, so an MCP-capable
assistant (Claude Desktop, Claude Code, etc.) can read and write your Jortt
administration on your behalf.
The tools are generated directly from Jortt's published
[OpenAPI specification](https://api.jortt.nl/swagger_doc), so coverage stays
faithful to the API: **82 tools** across every documented resource.
## How it works
- The build step downloads the Jortt OpenAPI spec to `src/openapi.json` and
bundles it into `dist/`. On startup the server reads that spec, resolves all
schema references, and turns every operation into an MCP tool with a proper
JSON Schema for its arguments. (If no bundled spec is found, it fetches one
live from Jortt as a fallback.)
- Authentication uses the OAuth 2.0 **client credentials** grant (the flow Jortt
recommends for applications bound to your own administration). The server
fetches a bearer token, caches it until shortly before it expires, and
transparently refreshes on expiry or a `401`.
- Path parameters, query parameters, and JSON request bodies are all handled
automatically. Request bodies are passed under a single `body` argument.
## Prerequisites
1. A [Jortt account](https://app.jortt.nl/van-start).
2. A registered API application. Create one under
**Profile → API**
[OAuth applications](https://app.jortt.nl/new_profile/api/jortt/oauth_applications/list),
selecting the scopes you want to grant. Registration gives you a
**Client ID** and **Client secret**.
## Installation
```bash
git clone https://github.com/deprekated/jortt-mcp.git
cd jortt-mcp
npm install # runs the build, which downloads the current Jortt OpenAPI spec
```
> The build fetches `https://api.jortt.nl/swagger_doc`, so it needs outbound
> network access. Set `JORTT_OPENAPI_URL` to point at a local copy or mirror if
> you build offline.
## Configuration
The server is configured entirely through environment variables:
| Variable | Required | Description |
| -------------------- | -------- | --------------------------------------------------------------------------- |
| `JORTT_CLIENT_ID` | yes | OAuth client ID of your registered application. |
| `JORTT_CLIENT_SECRET`| yes | OAuth client secret. |
| `JORTT_SCOPES` | no | Space-separated scopes to request. Defaults to all scopes; the token server only grants scopes your application was registered with. |
| `JORTT_OPENAPI_URL` | no | Override the OpenAPI spec source (used at build time and as a runtime fallback). Defaults to `https://api.jortt.nl/swagger_doc`. |
### Claude Desktop / Claude Code
Add the server to your MCP client configuration (for Claude Desktop, this is
`claude_desktop_config.json`):
```json
{
"mcpServers": {
"jortt": {
"command": "node",
"args": ["/absolute/path/to/jortt-mcp/dist/index.js"],
"env": {
"JORTT_CLIENT_ID": "your-client-id",
"JORTT_CLIENT_SECRET": "your-client-secret"
}
}
}
}
```
Restrict what the assistant can do by narrowing the scopes:
```json
"env": {
"JORTT_CLIENT_ID": "your-client-id",
"JORTT_CLIENT_SECRET": "your-client-secret",
"JORTT_SCOPES": "invoices:read customers:read reports:read"
}
```
## Tool overview
Tool names follow a predictable `verb_resource` convention derived from the API
path and version. A selection:
| Tool | Method & path | What it does |
| --------------------------- | ----------------------------------- | ----------------------------------------- |
| `list_customers` | `GET /customers` | List / search customers |
| `create_customers` | `POST /customers` | Create a customer |
| `get_customers` | `GET /customers/{customer_id}` | Fetch a single customer |
| `list_invoices` | `GET /invoices` | List / search invoices |
| `create_v3_invoices` | `POST /v3/invoices` | Create an invoice (invoice-level VAT) |
| `send_invoices` | `POST /invoices/{id}/send` | Send an invoice |
| `get_invoices_download` | `GET /invoices/{id}/download` | Get a download URL for the invoice PDF |
| `list_v2_estimates` | `GET /v2/estimates` | List estimates |
| `create_v2_estimates` | `POST /v2/estimates` | Create an estimate |
| `list_v3_expenses` | `GET /v3/expenses` | List expenses |
| `list_v3_bank_accounts` | `GET /v3/bank-accounts` | List bank accounts |
| `get_reports_summaries_profit_and_loss` | `GET /reports/summaries/profit_and_loss` | Dashboard profit & loss summary |
| `get_organizations_me` | `GET /organizations/me` | The organization tied to your credentials |
Newer resources are versioned (`v2`, `v3`) and the version is part of the tool
name. Where Jortt offers multiple versions of the same operation (for example
invoice creation), prefer the highest version unless you have a reason not to —
`create_v3_invoices` supports invoice-level VAT.
Run the server and call `tools/list` to see the full, always-current set with
per-tool argument schemas.
## Development
```bash
npm run build # compile TypeScript to dist/ (and copy the spec)
npm run dev # tsc --watch
npm run typecheck # type-check without emitting
```
### Updating to the latest API
The tool set is generated from the OpenAPI spec, which the build downloads
fresh each time. To pick up new Jortt endpoints, just rebuild:
```bash
npm run build
```
No code changes are needed — new operations become new tools automatically.
## Notes & limits
- Jortt rate-limits the API to ~10 requests/second. List endpoints paginate at a
maximum of 100 items per page (`page` argument).
- This is an unofficial, community project and is not affiliated with or endorsed
by Jortt.
## License
MIT — see [LICENSE](LICENSE).