subiekt123-mcp

subiekt123-mcp

Stateless MCP server for invoicing in Subiekt 123 using the InsERT API, enabling tools to preview and create invoices, manage documents, and fetch exchange rates.

Category
Visit Server

README

subiekt123-mcp

Stateless MCP server for invoicing in Subiekt 123 (InsERT API).

  • SDK: @modelcontextprotocol/server 2.0.0, spec 2026-07-28
  • Transport: stateless Streamable HTTP (createMcpHandler) and stdio
  • Node ≥ 20, zod v4, ESM

The server keeps no state between requests - createMcpHandler builds a fresh McpServer instance per HTTP request, so it scales horizontally without session affinity and without Mcp-Session-Id.

New here? Start with GETTING-STARTED.md - credentials, first run, connecting a client, and issuing your first invoice. This README covers architecture and reference detail.

Safety gate: HMAC token

create_invoice does not accept an invoice payload - only a token issued by a preview_* tool.

preview_<template>_invoice({month:"2026-08"})
   → preview (buyer, line items, totals, rate, warnings)
   → token: <payload>.<HMAC-SHA256>     (valid for 10 minutes)

create_invoice({token})
   → signature + TTL verification
   → POST /documents

Why: in MCP it is the model that invokes a tool, and annotations.destructiveHint is only a hint to the client - the spec states plainly that annotations do not change how the SDK runs a tool. The token binds issuance to one specific, previously displayed document: changing even a single amount invalidates the signature. The token carries the whole payload, so the server stays stateless.

HMAC key: ~/.config/subiekt123/preview-hmac.key (32 random bytes, generated on first use, chmod 600). It lives outside credentials.json - a token leaked from a conversation transcript is not enough to forge approval.

Installation

cd subiekt123 && npm install

The repository contains no business data - no account numbers, no tax IDs, no amounts, no client names. All of that lives in two files under ~/.config/subiekt123/ (chmod 600).

1. Secrets - credentials.json

{
  "clientId": "<Client ID from the InsERT developer portal>",
  "clientSecret": "<Client Secret>",
  "redirectUri": "http://localhost:53682/callback",
  "subscriptionKey": "<Subiekt 123 API subscription key>",
  "subscriptionHeader": "Ocp-Apim-Subscription-Key"
}

Where to get them: konto.insert.com.plMoje produktyInsERT API → "Uzyskaj dostęp" → the Aplikacje menu → InsERT API (developers.insert.com.pl). There you will find Moje aplikacje (registration: scope subiekt123, return address = redirectUri) and Subskrypcje.

One-time OAuth consent:

npm run auth

The refreshToken rotates on every refresh and is persisted automatically. Inactivity for more than 90 days, or more than 365 days since the first consent, requires running npm run auth again.

2. Invoicing profile - profile.json

cp profile.example.json ~/.config/subiekt123/profile.json
chmod 600 ~/.config/subiekt123/profile.json

The profile describes the issuer, bank accounts, clients and invoice templates. Every template becomes an MCP tool named preview_<key>_invoice, so the tool set depends on the profile rather than on the code. The profile is validated at startup; a missing field or a reference to a non-existent client stops the server with a specific message.

Template field Meaning
key becomes part of the tool name: preview_<key>_invoice
kind date rule: monthlyRecurring, sameDay, prepaid (see below)
title, summary tool title and description as seen by the model
client, bankAccount keys from the clients / bankAccounts sections
currency PLN skips the rate; a foreign currency fetches the NBP rate automatically
dueDays payment term in days, defaults to 14
defaultNetPrice default amount; omit it to force the model to pass an amount explicitly
note, communityTransaction, jpkVatGroups optional document fields
item the single line item: name, sourceProductId, vatRate, pkwiu, gtu

Running

npm start          # stdio - the MCP client spawns the process itself
npm run start:http # stateless HTTP on 127.0.0.1:3123/mcp
npm run inspect    # MCP Inspector

Claude Code / Claude Desktop (stdio)

{
  "mcpServers": {
    "subiekt123": {
      "command": "node",
      "args": ["/path/to/subiekt123/src/stdio.mjs"]
    }
  }
}

HTTP

Bound to 127.0.0.1, with Host and Origin validation ahead of the handler (localhostHostValidation, localhostOriginValidation) - a defence against DNS rebinding, where a malicious site points its own domain at 127.0.0.1 so the browser treats the server as same-origin. A foreign Origin gets 403. PORT changes the port, HOST the address.

Tools

Tool Annotations Description
list_documents read-only Document list (0-indexed paging, pageSize ≤ 50)
get_document read-only Full document with line items
list_clients / get_client read-only Clients (get_client = 13 more fields)
list_products read-only Products
get_exchange_rate read-only NBP rate from the business day before the issue date
preview_<key>_invoice read-only One per profile template - dates and rate resolved automatically
preview_invoice read-only Arbitrary payload, outside the templates
create_invoice destructive Issues a document - from a token only
delete_document destructive Deletes a document (blocked once submitted to KSeF)
print_document read-only PDF as base64, optionally duplicate / ecoMode

Date rules (template.kind)

The rule lives in code (dates.mjs); a profile template merely selects it.

monthlyRecurring - recurring monthly invoice

Field Rule
issueDate today (overridable via the issueDate parameter)
deliveryDate, taxLiabilityDate last day of the billed month (the month parameter)
dueDate +dueDays from the issue date; weekend → Monday (Sat +2, Sun +1)
exchangeRate NBP rate from the last business day before the issue date

Never backdate the issue date - KSeF reports an error when issueDate precedes the day of submission. Hence the default of today rather than the 1st of the month (user's decision, 2026-08-03). For the same reason a foreign-currency invoice cannot be issued ahead of time - the rate from the day before the issue date does not exist yet.

sameDay - one-off invoice

Issue date = sale date = the given date (defaults to today), due +dueDays with the weekend adjustment.

prepaid - invoice for a payment already received

Due date = issue date, and payments is filled automatically with the full amount. Always confirm the sale date with the user - it is the day the payment arrived, which may be earlier than the issue date. A sale date later than the issue date is rejected.

Public holidays are not shifted - weekends only (user's decision, 2026-07-31).

API pitfalls (confirmed empirically)

  1. calculationMethod must match the company's "Licz od" setting - otherwise the fiscalization status diverges. With Net pass netPrice, with Gross pass grossPrice.
  2. exchangeRate is mandatory for foreign currencies. The API does not fetch the rate itself (the UI does) - without it you get EXCHANGE_RATE_MUST_BE_SET_ON_DOCUMENT_IN_FOREIGN_CURRENCY.
  3. items[].symbol conflicts with sourceProductId - DOCUMENT_ITEM_SYMBOL_MUST_BE_EMPTY. The symbol comes from the product record.
  4. vatRate is a string: "23%" or a non-percentage rate such as "o.o." (reverse charge).
  5. eInvoice is a document type, not a KSeF submission. After POST the invoice has invoiceMode: "Ksef" but ksefNumber: null. Submission happens in the UI - the public API has no endpoint for it.
  6. A KSeF invoice cannot be printed before submission - DOCUMENT_MUST_BE_PROCESSED_BY_KSEF_BEFORE_PRINTING. The same error is returned for a non-existent id.
  7. An invoice for a natural person requires a PESEL (tinKind: "Pesel" + tin), otherwise it is created as a company. The UI does not require it - the discrepancy was reported to InsERT, with no response.
  8. pageNumber is 0-indexed, pageSize maxes out at 50 (60 → 400).
  9. POST /documents returns a nested {"id":{"value":"..."}}, without the document number.
  10. /printing requires Content-Type: application/json even with an empty body - otherwise 415. It returns a binary PDF.
  11. Numbers freed by deleted documents are reused.
  12. The public API does not list correction invoices (KFS) - they are visible only in the internal gw/api.

Layout

src/
├── server.mjs          McpServer factory - tool registration
├── stdio.mjs           stdio entry point
├── http.mjs            stateless HTTP entry point
├── auth.mjs            one-time OAuth PKCE consent
└── lib/
    ├── config.mjs      credentials.json
    ├── profile.mjs     profile.json - reading and validation
    ├── api.mjs         OAuth + HTTP client (JSON and binary)
    ├── token.mjs       HMAC preview → create
    ├── dates.mjs       date rules, weekend adjustment
    ├── nbp.mjs         NBP exchange rates
    ├── invoice.mjs     validation, totals, preview
    └── templates.mjs   assembling a payload from a profile template

profile.example.json    profile template (placeholders, no real data)
GETTING-STARTED.md      setup walkthrough, client wiring, troubleshooting
docs/api-notes.md       full API documentation gathered empirically

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured