Storno CLI
# 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)
TDQS
Scored across 367 tools
With 367 tools, there is substantial overlap and multiple paths to the same outcome: declarations can be created via declarations_create, declarations_upload, or declaration_build, and filed via declarations_submit, declarations_file_via_agent, agent_submit_declaration_pdf, or the two-step declarations_prepare/declarations_agent_result. Several retired and active sync/status tools also blur boundaries (declarations_refresh_statuses vs declarations_sync), making reliable tool selection difficult.
The dominant pattern is domain_verb snake_case (invoices_list, clients_create, vehicles_update), which is mostly predictable. However, there are notable deviations: bare-noun tools like agent_certificates and cash_register_ledger, reversed constructions like defaults_invoice and client_statement, and phrase-style names like declarations_file_via_agent and invoices_share_links_revoke. The mixed conventions are readable but not consistently predictable at this scale.
367 tools is an extreme mismatch for any MCP server surface. Even though the underlying platform is broad, exposing nearly 400 individually selectable tools creates an unmanageable discovery and selection burden for an agent, far beyond the 3-15 tool sweet spot. This should be aggressively grouped, consolidated, or split into multiple focused servers.
The surface is remarkably comprehensive, covering auth, companies, clients, suppliers, products, invoices, proformas, receipts, delivery notes, recurring billing, e-invoicing, declarations, dosare, expiries, vehicles, cash register, reports, imports/exports, webhooks, admin, and more. The main completeness issues are not missing major lifecycle operations but rather redundant and retired endpoints that create confusing dead ends within the declaration and sync workflows.