Storno CLI
Official# Storno CLI — MCP Server
A **Model Context Protocol (MCP) server** that exposes the entire [Storno.ro](https://storno.ro) e-invoicing API as AI-callable tools. Enables Claude Code, Cursor, Windsurf, and any MCP-compatible AI assistant to manage invoices, clients, companies, ANAF e-Factura, and more.
## Features
- **237 tools** covering all Storno.ro API endpoints
- Full invoice lifecycle: create, issue, submit to ANAF, email, download PDF/XML
- Proforma invoices, recurring invoices, delivery notes, credit notes
- Client & product catalog, payment tracking, VAT rates
- ANAF e-Factura integration (sync, token management, message retrieval)
- Multi-company support with company switching
- Automatic JWT token refresh
- Webhooks, API keys, team member management
- Exchange rates (BNR), reports, exports
## Quick Start
### Hosted Server (recommended)
Storno hosts a public MCP server at **https://mcp.storno.ro/mcp** with OAuth authentication — no API keys or tokens to manage.
Visit **[mcp.storno.ro](https://mcp.storno.ro)** for setup instructions and a one-click "Add to Claude" button.
**Claude (claude.ai):**
1. Copy the URL: `https://mcp.storno.ro/mcp`
2. Go to [claude.ai/settings/connectors](https://claude.ai/settings/connectors)
3. Click "Add" and paste the URL
4. Authorize via OAuth
**Claude Code:**
```bash
claude mcp add storno --transport http https://mcp.storno.ro/mcp
```
**Cursor / Windsurf:**
```json
{
"mcpServers": {
"storno": {
"url": "https://mcp.storno.ro/mcp"
}
}
}
```
**Claude Desktop:**
```json
{
"mcpServers": {
"storno": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://mcp.storno.ro/mcp"]
}
}
}
```
### Self-hosted / Token-based Setup
If you prefer to use a JWT token directly (e.g. for self-hosted Storno instances):
#### 1. Install
```bash
npm install -g storno-cli
```
Or use directly with `npx` — no install needed.
#### 2. Configure for Claude Code
Add to your project's `.claude/settings.json` or global `~/.claude/settings.json`:
```json
{
"mcpServers": {
"storno": {
"command": "npx",
"args": ["-y", "storno-cli"],
"env": {
"STORNO_TOKEN": "your-jwt-token",
"STORNO_COMPANY_ID": "your-company-uuid"
}
}
}
}
```
#### 3. Configure for Cursor
Add to `.cursor/mcp.json`:
```json
{
"mcpServers": {
"storno": {
"command": "npx",
"args": ["-y", "storno-cli"],
"env": {
"STORNO_TOKEN": "your-jwt-token",
"STORNO_COMPANY_ID": "your-company-uuid"
}
}
}
}
```
#### 4. Configure for Claude Desktop
Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
```json
{
"mcpServers": {
"storno": {
"command": "npx",
"args": ["-y", "storno-cli"],
"env": {
"STORNO_TOKEN": "your-jwt-token",
"STORNO_COMPANY_ID": "your-company-uuid"
}
}
}
}
```
## Environment Variables
| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `STORNO_BASE_URL` | No | `https://api.storno.ro` | API base URL |
| `STORNO_TOKEN` | No* | — | JWT access token |
| `STORNO_REFRESH_TOKEN` | No | — | JWT refresh token (for auto-renewal) |
| `STORNO_COMPANY_ID` | No* | — | Default company UUID |
| `STORNO_EMAIL` | No | — | Email for auto-login (if no token) |
| `STORNO_PASSWORD` | No | — | Password for auto-login (if no token) |
| `STORNO_HTTP_PORT` | No | — | If set, starts Streamable HTTP transport on this port |
| `STORNO_HTTP_HOST` | No | `127.0.0.1` | HTTP bind address (used with `STORNO_HTTP_PORT`) |
\* You can authenticate at runtime using the `auth_login` tool instead.
## Authentication
Three ways to authenticate:
### Option A: Pre-configured token (recommended for production)
Set `STORNO_TOKEN` in your MCP server environment config.
### Option B: Auto-login on startup
Set `STORNO_EMAIL` and `STORNO_PASSWORD`. The server will automatically log in when it starts and keep the token refreshed.
### Option C: Interactive login via AI
Ask the AI: *"Log me in to Storno with email user@example.com and password mypassword"*
The AI will call the `auth_login` tool, which stores the token in memory for the session.
## Multi-Company Support
All company-scoped tools require a company context. Three ways to provide it:
1. **Environment variable**: Set `STORNO_COMPANY_ID` in your MCP config
2. **Select command**: Ask the AI to call `companies_select` with a company UUID
3. **Per-request override**: Every company-scoped tool accepts an optional `companyId` parameter
Example AI conversation:
```
You: List my companies
AI: [calls companies_list] You have 2 companies:
- ABC SRL (uuid: 550e8400...)
- XYZ SRL (uuid: 661f9500...)
You: Switch to XYZ SRL
AI: [calls companies_select with uuid] Done, now using XYZ SRL
You: Show me recent invoices
AI: [calls invoices_list — automatically uses XYZ SRL context]
```
## Tool Reference
See [TOOLS.md](./TOOLS.md) for the complete reference of all 237 tools with descriptions and parameters.
### Tool Categories
| Category | Tools | Description |
|----------|-------|-------------|
| `auth_*` | 7 | Login, register, refresh tokens, profile management |
| `companies_*` | 8 | Company CRUD + context switching + ANAF settings |
| `invoices_*` | 32 | Full invoice lifecycle (create → issue → submit → email → PDF/XML) |
| `clients_*` | 10 | Client management (synced from e-Factura) |
| `products_*` | 2 | Product catalog |
| `payments_*` | 3 | Payment recording and tracking |
| `proforma_invoices_*` | 12 | Proforma lifecycle (create → send → accept/reject → convert) |
| `recurring_invoices_*` | 10 | Recurring invoice schedules |
| `delivery_notes_*` | 18 | Delivery note lifecycle + status management |
| `receipts_*` | 13 | Fiscal receipts (bonuri fiscale) + conversion to invoices |
| `vat_rates_*` | 4 | VAT rate CRUD |
| `bank_accounts_*` | 4 | Bank account CRUD |
| `document_series_*` | 5 | Invoice number series management |
| `suppliers_*` | 8 | Supplier management |
| `exchange_rates_*` | 2 | BNR exchange rates + currency conversion |
| `email_templates_*` | 4 | Email template CRUD |
| `anaf_*` | 8 | ANAF token management + e-Factura sync + validation |
| `efactura_messages_*` | 2 | e-Factura message retrieval |
| `einvoice_*` | 6 | Multi-country e-invoicing (EU-wide) |
| `dashboard_*` | 1 | Dashboard statistics |
| `defaults_*` | 1 | Invoice defaults (currencies, units, VAT rates) |
| `members_*` | 3 | Organization team members |
| `invitations_*` | 4 | Team invitations |
| `notifications_*` | 6 | Notification management + preferences |
| `webhooks_*` | 10 | Webhook CRUD + testing + delivery logs |
| `api_keys_*` | 5 | API token management |
| `reports_*` | 7 | VAT reports + financial reports |
| `exports_*` | 1 | Export file downloads |
| `import_*` | 8 | CSV/XLSX/XML import for clients, products, invoices |
| `borderou_*` | 8 | Bank reconciliation (borderou) |
| `backup_*` | 6 | Backup creation + restore |
| `admin_*` | 3 | System administration (requires admin role) |
| `licensing_*` | 4 | License key management |
| `pdf_template_*` | 4 | PDF invoice template customization |
| `storage_config_*` | 5 | File storage configuration |
| `accounting_export_*` | 3 | Export to accounting formats |
| `company_registry_*` | 2 | Company registry lookups |
| `cpv_codes_*` | 1 | CPV code lookup |
| `nc_codes_*` | 1 | NC code lookup |
| `system_*` | 2 | System health + version info |
## Common AI Workflows
### Create and send an invoice
```
You: Create an invoice for client Acme SRL, 10 hours web development at 100 RON/hour, due in 30 days
AI: [calls clients_list to find Acme SRL]
[calls invoices_create with clientId, lines, dates]
[calls invoices_issue to mark as issued]
[calls invoices_email to send to client]
Invoice FAC-2024-042 created, issued, and emailed to contact@acme.ro
```
### Check ANAF sync status
```
You: What's the ANAF sync status for my company?
AI: [calls anaf_status]
Last sync: 2 hours ago, 156 invoices synced, token valid until March 16
```
### Generate a monthly report
```
You: Show me all invoices from January 2024
AI: [calls invoices_list with from=2024-01-01, to=2024-01-31]
Found 23 invoices, total: 45,230 RON
- 18 issued, 3 paid, 2 overdue
```
## HTTP Transport
By default, storno-cli uses **stdio transport** for local MCP clients. Set `STORNO_HTTP_PORT` to enable **Streamable HTTP transport** for remote access.
```bash
# Start HTTP server on port 3000
STORNO_TOKEN=your-token STORNO_COMPANY_ID=your-company-uuid \
STORNO_HTTP_PORT=3000 node dist/index.js
# Bind to all interfaces (for production behind a reverse proxy)
STORNO_HTTP_PORT=3000 STORNO_HTTP_HOST=0.0.0.0 node dist/index.js
```
The server exposes a single `/mcp` endpoint supporting POST, GET, and DELETE per the MCP Streamable HTTP spec. Each client connection creates an isolated stateful session.
**Initialize a session:**
```bash
curl -X POST http://127.0.0.1:3000/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}'
# Response includes mcp-session-id header
```
**Call a tool:**
```bash
curl -X POST http://127.0.0.1:3000/mcp \
-H "Content-Type: application/json" \
-H "mcp-session-id: <session-id>" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"system_health","arguments":{}}}'
```
## Development
```bash
npm run dev # Watch mode (recompile on changes)
npm run build # Production build
npm run start # Start MCP server (stdio)
```
## Architecture
```
storno-cli/
├── src/
│ ├── index.ts # Entry point — dispatches to stdio or HTTP transport
│ ├── server.ts # McpServer factory (creates server + registers all tools)
│ ├── http-server.ts # Streamable HTTP transport with session management
│ ├── config.ts # Environment configuration
│ ├── client.ts # HTTP client with JWT auth + auto-refresh
│ ├── tools/ # 40 tool domain files (237 tools total)
│ │ ├── auth.ts
│ │ ├── invoices.ts # Largest: 32 tools for full invoice lifecycle
│ │ ├── companies.ts
│ │ └── ... # 37 more domain files
│ └── utils/
│ ├── errors.ts # AI-readable error formatting
│ └── pagination.ts # Pagination helpers
├── dist/ # Compiled JavaScript output
├── package.json
├── tsconfig.json
├── README.md # This file
└── TOOLS.md # Complete tool reference
```
## Requirements
- Node.js >= 20.0.0
- npm
## License
[Elastic License 2.0 (ELv2)](LICENSE)
## Tool annotations
All tools carry explicit MCP annotations (`title`, `readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`), so a client can tell reads from writes and writes from deletes before calling anything. See the *Tool annotations* section of [TOOLS.md](TOOLS.md); `npm run check:annotations -- --table` prints the table.
TDQS
Scored across 374 tools
Most tools are scoped to a clear resource and action, and the descriptions are detailed enough to separate similar operations. However, the sheer number produces several near-overlapping flows (companies_select vs companies_set_active, declarations_submit vs declarations_file_via_agent vs agent_submit_declaration_pdf, retired declarations_sync vs spv_sync_prepare) that an agent can easily misselect.
Names mostly follow a resource_action snake_case pattern, but prefixes mix singular and plural forms (company_registry_search vs clients_list, product_categories_list), and some tools are noun phrases rather than verb-led (dashboard_stats, balance_analysis, fiscal_calendar). The surface remains readable but is not fully predictable at 374 tools.
374 tools is an extreme mismatch for a single MCP server, far beyond the 50+ threshold for severe overload. Even for a large ERP, this many tools overwhelms selection and violates the principle that each tool should clearly earn its place in a focused set.
The surface covers full lifecycle operations for invoices, proformas, delivery notes, receipts, payments, clients, suppliers, bank/cash registers, VAT, declarations, SPV, e-Factura, e-Transport, imports/exports, backups, admin, webhooks, and licensing. Almost every domain an agent would need is represented, with only intentional constraints such as sync-only product creation.