Fakvio.McpServer
2.3.0
See the version list below for details.
dotnet tool install --global Fakvio.McpServer --version 2.3.0
dotnet new tool-manifest
dotnet tool install --local Fakvio.McpServer --version 2.3.0
#tool dotnet:?package=Fakvio.McpServer&version=2.3.0
nuke :add-package Fakvio.McpServer --version 2.3.0
Fakvio MCP Server
Kompatibilita 2.0
Verze 2.0.0 je nekompatibilní se všemi předchozími (poslední skutečně vydaná na nuget.org
byla 1.0.2 — 1.0.3/1.0.4 nikdy nevyšly, viz #398 níže). LLM klienti (Claude, ChatGPT), kteří si
schéma nástrojů načtou sami z tools/list, nic dělat nemusí. Skriptovaní klienti (cokoli, co
volá fakvio-mcp s natvrdo zapsanými argumenty) musí upravit volání:
- Typované vstupy místo JSON stringu.
create_client,update_client,create_received_invoice,create_invoice_from_templateacreate_invoiceuž neberou"…Json": "{...}"— parametr je teď skutečný objekt/pole ve stejné JSON-RPC zprávě. currencymístocurrencyId.create_invoiceacreate_received_invoicechtějí ISO kód ("EUR"), ne interní číselné ID — zjistíte ho přeslist_currencies.issuerIdje teď volitelný ucreate_invoice— vynechaný = vlastní firma volajícího.- Nové chybové kódy. Zápis na read-only klíč, neexistující záznam a validační chyba už
nekončí jako
{"error":"internal_error"}bez rozlišení — viz tabulka v sekci „Chování nástrojů" výše (forbidden/not_found/validation_error). - Annotations. Každý nástroj v
tools/listnesereadOnlyHint/destructiveHint/idempotentHint/openWorldHint— klient, který se na ně dřív nedíval, může začít.
Ukázka staré (1.0.2) a nové (2.0.0) volby create_invoice:
// 1.0.2 — jeden JSON string, interní ID, model neví, odkud currencyId/issuerId vzít
{
"invoiceJson": "{\"documentType\":\"Invoice\",\"clientId\":1,\"issuerId\":2,\"currencyId\":1,\"invoiceItem\":[{\"description\":\"Web development\",\"quantity\":10,\"unitPrice\":1500,\"vatRatePercentage\":21}]}"
}
// 2.0.0 — typované argumenty, měna kódem, issuerId volitelné
{
"clientId": 1,
"currency": "EUR",
"items": [{ "description": "Web development", "quantity": 10, "unit": "hrs", "unitPrice": 1500, "vatRatePercentage": 21 }]
}
2.1.0 je jen doplnění nástrojů (story N3 — nastavení, platby, upomínky), zpětně kompatibilní — žádné volání ze 2.0.0 se neláme.
2.2.0 doplňuje 7 nástrojů nad opakovanými fakturami (Tools/RecurringTools.cs, DEVGUIDE
§4.13) — list_recurring_schedules, get_recurring_schedule, create_recurring_schedule,
update_recurring_schedule, pause_recurring_schedule, resume_recurring_schedule,
delete_recurring_schedule. Zpětně kompatibilní, žádné volání ze 2.1.0 se neláme.
2.3.0 doplňuje 1 nástroj — export_invoice_ubl (Tools/InvoiceTools.cs, ADR 0002 N7) —
export vydané faktury jako UBL 2.1 / Peppol BIS Billing 3.0 XML (SK e-fakturace 2027, ViDA
2030). Vrací base64 XML stejně jako export_invoice_isdoc; při nepřipravené faktuře (Draft,
proforma, chybějící Peppol ID…) API vrací 400 s čitelnými kódy EINVOICE_*. Zpětně kompatibilní,
žádné volání ze 2.2.0 se neláme.
Aplikace, která zpřístupňuje fakturaci Fakvio AI klientům přes Model Context Protocol (MCP). Umí dva režimy — sada nástrojů je v obou totožná, liší se jen tím, odkud se bere credential:
stdio AI klient ←stdio→ Fakvio.McpServer ←HTTP + API klíč→ Fakvio.API ←EF Core→ PostgreSQL
http AI klienti ←HTTP/MCP→ Fakvio.McpServer ←HTTP + API klíč→ Fakvio.API ←EF Core→ PostgreSQL
Ve stdio režimu si server spustí klient (Claude Code, Claude Desktop, …) jako
podproces; jeden proces obsluhuje jednoho uživatele, takže credential procesu
(FAKVIO_API_TOKEN) je zároveň credential toho uživatele. Ve http režimu jeden proces obsluhuje mnoho
volajících, takže credential nosí každý request zvlášť — je jím API klíč
volajícího, který server jen přeposílá na API.
Server sám nemá přístup k databázi — všechno jde přes REST API, takže platí
úplně stejná autorizace a tenant izolace jako pro webové UI. Rozsah oprávnění
určuje credential, se kterým se volá: role jeho vlastníka, u API klíče navíc
protnutá se scope klíče (read vs read,write — viz DEVGUIDE §2.10).
- Projekt:
Fakvio.McpServer(net10.0,PackAsTool) - Příkaz nainstalovaného nástroje:
fakvio-mcp(ToolCommandNamev csproj) - Jméno serveru hlášené v MCP handshake:
fakvio(Program.cs,ServerInfo) - SDK:
ModelContextProtocol2.2.0, transporty: stdio (výchozí) a Streamable HTTP na/mcp
Požadavky
- .NET 10 SDK
- ASP.NET Core shared framework (
Microsoft.AspNetCore.App) — a to i pro stdio režim. BalíčekModelContextProtocol.AspNetCore, který přináší Streamable HTTP transport, neseFrameworkReference, takže ho potřebuje celý nástroj, ne jen http režim. Na stroji s plným .NET 10 SDK je součástí instalace; na cílovém stroji jen s .NET runtime se musí doinstalovat ASP.NET Core Runtime. - Běžící
Fakvio.API(lokálně nebo v cloudu), dosažitelné z počítače, kde běží AI klient - Credential podle režimu: API klíč
fak_live_…vydaný v UI na/settings/integrations(viz USERGUIDE §20) — ve stdio režimu se vloží doFAKVIO_API_TOKEN, v http režimu ho nese každý request volajícího (server žádný vlastní credential nemá). Ve stdio režimu projde i JWT token uživatele, ale platí jen 24 h, takže na trvalé napojení se nehodí.
Build a spuštění
Vývojově (přímo z repa):
dotnet build Fakvio.McpServer/Fakvio.McpServer.csproj
dotnet run --project Fakvio.McpServer
Jako globální .NET nástroj (příkaz fakvio-mcp) — balíček je na nuget.org,
takže uživatel k instalaci nepotřebuje repozitář:
dotnet tool install --global Fakvio.McpServer
Aktualizace, resp. odinstalace:
dotnet tool update --global Fakvio.McpServer
dotnet tool uninstall --global Fakvio.McpServer
Verzi na nuget.org publikuje workflow mcp-server.yml při pushi do master
(job publish-nuget). Číslo verze se zvedá ručně — <Version> v
Fakvio.McpServer.csproj, ve stejném PR jako změna nástroje. Push jde
s --skip-duplicate, takže merge bez bumpu nic nepublikuje a nic neshodí;
cena za to je, že zapomenutý bump se projeví jen tím, že se oprava k uživatelům
nedostane.
Z rozpracované větve (nepublikovaná verze) se instaluje z lokálního balíčku:
dotnet pack Fakvio.McpServer/Fakvio.McpServer.csproj -c Release -o ./nupkg
dotnet tool install --global --add-source ./nupkg Fakvio.McpServer
Spuštění z terminálu jen ověří konfiguraci — server pak čeká na JSON-RPC zprávy na stdin. Bez MCP klienta se nic „nezobrazí“, to je v pořádku. Ukončíte ho Ctrl+C.
Konfigurace
Server se konfiguruje jen proměnnými prostředí (žádný appsettings.json):
| Proměnná | Povinná | Výchozí | Popis |
|---|---|---|---|
FAKVIO_MCP_TRANSPORT |
ne | stdio |
stdio nebo http. Cokoli jiného = chyba na stderr a exit code 1. |
FAKVIO_API_TOKEN |
jen pro stdio |
– | Bearer credential — API klíč fak_live_… (doporučeno) nebo JWT token. Posílá se beze změny v hlavičce Authorization; API rozliší obojí podle prefixu (fak_ vs eyJ), takže server nemusí vědět, co drží. Chybí-li ve stdio režimu, vypíše chybu na stderr a skončí s exit code 1. V HTTP režimu se nepoužívá. |
FAKVIO_API_URL |
ne | https://localhost:7047 |
Base URL API, např. https://localhost:7047 (lokální Fakvio.API, viz Fakvio.API/Properties/launchSettings.json) nebo https://api.fakvio.cz. |
ASPNETCORE_URLS |
ne | Kestrel default | Jen http režim — na čem server poslouchá, standardní ASP.NET Core proměnná. |
FAKVIO_MCP_OAUTH_ENABLED |
ne | false |
Jen http režim (ADR 0001, docs/adr/0001-mcp-oauth21.md). true = zveřejní Protected Resource Metadata na /.well-known/oauth-protected-resource(/mcp) a přidá resource_metadata/scope do 401 challenge. false = dnešní chování beze změny. |
FAKVIO_MCP_PUBLIC_URL |
jen s OAuth | – | Vlastní veřejná adresa hostu, např. https://mcp.fakvio.cz — použije se pro resource v PRM i pro resource_metadata v challenge. |
FAKVIO_OAUTH_ISSUER |
jen s OAuth | – | Issuer autorizačního serveru, např. https://api.fakvio.cz — zveřejní se v PRM jako authorization_servers. |
FAKVIO_MCP_RESOURCE_PROOF_SECRET |
jen s OAuth | – | Sdílené tajemství s API (McpOAuth:ResourceProofSecret) — posílá se v hlavičce X-Fakvio-Resource-Proof na každém odchozím requestu; bez něj API odmítne OAuth tokeny (T6, confused deputy). |
HTTP režim
FAKVIO_MCP_TRANSPORT=http FAKVIO_API_URL=https://localhost:7047 ASPNETCORE_URLS=http://localhost:5290 dotnet run
Server je obyčejná ASP.NET Core aplikace, takže ho hostuje cokoli, co umí spustit .NET
proces — v tomhle repu běží na Azure App Service (web app fakvio-mcp-web / fakvio-mcp-web-test).
Nasazení popisuje DEVGUIDE §9.1.
Klient posílá na POST /mcp a musí přiložit Authorization: Bearer <API klíč>
(klíč se zakládá v UI, viz ADMINGUIDE / USERGUIDE). Server klíč ověří na
GET /api/api-key/me u každého requestu — nic se necachuje, takže revokovaný
klíč přestane fungovat okamžitě. Neplatný nebo chybějící klíč = 401 s hlavičkou
WWW-Authenticate: Bearer.
Běží stateless (bez Mcp-Session-Id), takže GET /mcp a /sse nejsou k dispozici
a host jde škálovat bez sticky routingu.
Výchozí hodnota sedí jen na lokální vývoj.
FAKVIO_API_URLbez explicitního nastavení míří nahttps://localhost:7047(lokálníFakvio.API, vizFakvio.API/Properties/launchSettings.json). Pro cloud nebo jiný port ho nastavte vždy explicitně.
Při HTTPS na localhost musí být vývojový certifikát důvěryhodný
(dotnet dev-certs https --trust), jinak HTTP volání selžou na validaci certifikátu.
Získání credentialu
Doporučená cesta — API klíč. V UI Fakvia otevřete Nastavení → Integrace
(/settings/integrations), vytvořte klíč, zvolte rozsah (Jen čtení /
Čtení i zápis) a případnou platnost. Klíč se zobrazí právě jednou — server
si ukládá jen jeho SHA-256 otisk, takže ztracený klíč nejde obnovit, jen revokovat
a vydat nový. Stránka rovnou nabídne hotové konfigurační bloky pro oba režimy
(viz „Napojení AI klienta"). Klíč platí do vyplněné expirace nebo do revokace;
revokace je okamžitá (žádná cache, viz HTTP režim výše).
Alternativa — JWT token (jen stdio, platí 24 h)
Nejrychleji přes login endpoint:
curl -X POST https://localhost:7047/api/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"user@example.com","password":"..."}'
Odpověď obsahuje token a expiresAt. Dvě věci, které je dobré vědět předem:
- Produkce má na loginu reCAPTCHA v3 (
X-Captcha-Token). Když jeRecaptcha:SecretKeynakonfigurován, curl bez captcha tokenu dostane HTTP 400. V takovém případě si token vezměte z prohlížeče po přihlášení do UI: localStorage klíčUserSession, poleToken. - Účet se zapnutým 2FA vrátí z
/api/auth/loginodpověď srequiresTwoFactor: truea prázdným tokenem. Přihlášení je pak potřeba dokončit přesPOST /api/twofactor/verify.
Token platí 24 hodin (JwtSettings:ExpirationHours v Fakvio.API/appsettings.json).
Po vypršení začnou nástroje vracet chyby — stačí do konfigurace klienta vložit nový token.
Napojení AI klienta
V kořeni repozitáře je vzor .mcp.json.sample. Obsahuje oba režimy —
nechte si ten, který chcete, druhý blok smažte (dva zápisy najednou nejsou chyba,
jen zbytečně registrují server dvakrát). Pro Claude Code stačí:
cp .mcp.json.sample .mcp.json # a doplnit klíč
Lokální server (stdio) — klienta spouští fakvio-mcp jako podproces:
{
"mcpServers": {
"fakvio": {
"command": "fakvio-mcp",
"args": [],
"env": {
"FAKVIO_API_URL": "https://localhost:7047",
"FAKVIO_API_TOKEN": "fak_live_<váš API klíč>"
}
}
}
}
Vzdálený server (Streamable HTTP) — klient nic neinstaluje, jen volá běžící
HTTP host; url je adresa toho hostu (ASPNETCORE_URLS) plus cesta /mcp:
{
"mcpServers": {
"fakvio-remote": {
"type": "http",
"url": "http://localhost:5290/mcp",
"headers": {
"Authorization": "Bearer fak_live_<váš API klíč>"
}
}
}
}
Tytéž bloky mcpServers patří i do konfigurace Claude Desktop
(claude_desktop_config.json).
Konfigurační bloky s už vyplněným klíčem vypíše stránka Nastavení → Integrace hned po vytvoření klíče — copy-paste je rychlejší a nehrozí překlep. Adresu vzdáleného serveru tam stránka odhaduje z adresy API; pokud HTTP host běží jinde,
urlpo vložení opravte.
Bez instalace nástroje lze server spouštět rovnou ze zdrojáků — místo
command/args použijte:
"command": "dotnet",
"args": ["run", "--project", "<cesta ke klonu repa>/Fakvio.McpServer"]
.mcp.json obsahuje credential v otevřené podobě, proto patří do .gitignore,
nikdy ne do commitu. Verzuje se jen .mcp.json.sample. Když se soubor přesto někam
dostane, klíč revokujte na /settings/integrations — přestane platit okamžitě.
Dostupné nástroje (57)
| Soubor | Počet | Nástroje |
|---|---|---|
Tools/InvoiceTools.cs |
11 | ListInvoices, GetInvoice, FindInvoiceByNumber, CreateInvoice (typed params — clientId, items, currency code, optional issuerId — see below), CompleteInvoice, MarkInvoicePaid, SendInvoiceEmail, ExportInvoicePdf, ExportInvoiceIsdoc, ExportInvoiceUbl, DeleteInvoice |
Tools/ClientTools.cs |
6 | ListClients, GetClient, CreateClient, UpdateClient, LookupAres, GetIssuer |
Tools/ReceivedInvoiceTools.cs |
7 | ListReceivedInvoices, GetReceivedInvoice, CreateReceivedInvoice, ApproveReceivedInvoice, MarkReceivedInvoicePaid, DeleteReceivedInvoice, UploadReceivedInvoiceAttachment |
Tools/ReportingTools.cs |
6 | GetDashboard, GetOverdueInvoices, GetClientInvoices, GetInvoicesByDateRange, GetVatReport, GetOverdueReceivedInvoices |
Tools/TaxTools.cs |
5 | EstimateTax, CompareTaxRegimes, GetAnnualIncome, GetInsuranceAdvance, GetTaxConfig |
Tools/TemplateTools.cs |
3 | ListTemplates, GetTemplate, CreateInvoiceFromTemplate |
Tools/ReadinessTools.cs |
1 | GetReadiness |
Tools/CodeListTools.cs |
1 | ListCurrencies |
Tools/SettingsTools.cs |
6 | ListNumberSequences, ListVatRates, CreateNumberSequence, UpdateNumberSequence, UpdateMyCompany, AddBankAccount |
Tools/PaymentTools.cs |
4 | ListPayments, GetPayment, ListReminders, GetReminderSettings |
Tools/RecurringTools.cs |
7 | ListRecurringSchedules, GetRecurringSchedule, CreateRecurringSchedule, UpdateRecurringSchedule, PauseRecurringSchedule, ResumeRecurringSchedule, DeleteRecurringSchedule |
Zdroj pravdy je vždy kód — atributy [McpServerTool] v Tools/:
grep -rcE '^\s*\[McpServerTool[,(]' Fakvio.McpServer/Tools/*.cs
Chování nástrojů
Každý nástroj vrací JSON jako string. Doménová chyba (404, validace vstupu) se nevyhazuje jako výjimka, ale vrací se jako
{ "error": "..." }— AI klient tak dostane čitelnou zprávu místo pádu spojení.Neočekávaná výjimka jde přes
McpToolError.ToJson(ex)— jedno místo pro všech 38 nástrojů. Zaloguje celou výjimku server-side a vrátí stabilní JSON, nikdyex.Message(to může nést syrové tělo API chyby zFakvioApiClient.EnsureSuccessAsync). Chybová odpověď API (FakvioApiException) se podle status kódu mapuje takto:HTTP status errormessage401 unauthorizedVždy pevný text — klíč je neplatný/expirovaný/revokovaný, vytvořte nový na /settings/integrations.403 forbiddenVždy pevný text — klíč je platný, ale read-only nebo role nemá oprávnění; vytvořte klíč s read+writescope.404 not_foundSafeMessagez těla ({"message":"…"}), jinak "The requested record does not exist."400/409/422 validation_errorSafeMessagez těla, jinak "The API rejected the input."ostatní (5xx…) internal_errorObecná hláška, viz výše. SafeMessageexistuje jen když tělo API chyby je JSON objekt s řetězcovýmmessage, nebo bare JSON string (TaxControllerstyl) — cokoli jiného (ne-JSON tělo, HTML stránka) se nikdy nedostane k modelu, i pro 400/404.Zrušení od volajícího se propaguje:
catch (OperationCanceledException) when (ct.IsCancellationRequested) { throw; }. Filtr je nutný —HttpClientvyhodíTaskCanceledException(potomekOperationCanceledException) i při vlastním timeoutu, kdy token volajícího zrušený není; ten případ má skončit sanitizovaným JSONem, ne výjimkou přes MCP hranici.Deserializace vstupu od modelu má vlastní menší
trypřed tím hlavním, abyJsonExceptionz poškozené úspěšné odpovědi API spadla do sanitizované větve, a ne modelu zpátky jako „vstup je špatně" i s textem výjimky.ExportInvoicePdf,ExportInvoiceIsdocaExportInvoiceUblvracejí soubor jakobase64Content+fileName,mimeType,sizeBytes. Uložení souboru je na klientovi.Logy jdou výhradně na stderr (
LogToStandardErrorThreshold = Trace). Stdout je vyhrazený pro JSON-RPC — jakýkoli zápis do stdout protokol rozbije.Vstup nikdy není „JSON string of …Dto" (N2.5). Parametr typu DTO (
CreateClientDto client,List<CreateInvoiceItemDto> items, …) dá modelu schéma zdarma — SDK ho vygeneruje ze skutečného typu, žádná ruční deserializace v těle toolu. Enumy jako řetězce ("BankTransfer","CreditNote") a camelCase fungují díkyMcpToolJsonOptions.Default, kteréMcpServerRegistrationpředává doWithToolsFromAssembly()— bez něj by SDK čekalo PascalCase a číselné enumy. HlídáToolDiscoveryTests.NoTool_TakesAnOpaqueJsonStringParameter.CreateInvoice(N2.4) překládá modelem srozumitelný vstup na interní ID — bez volání API:currency(ISO kód, výchozíCZK) se přeloží přeslist_currenciesnacurrencyId;issuerIdvynechaný znamená vlastní firmu (get_issuer); položce bezvatRateIddoplní ID podlevatRatePercentage(aktivní sazby kissueDate) — jen když je vystavitel plátce DPH, neplátce necháváme beze změny. Neznámá měna / procento vrátí konkrétní chybu se seznamem platných hodnot a API se vůbec nezavolá. Když stejnému procentu odpovídá víc aktivních sazeb (např. dvě překrývající se platnosti při změně sazby), nevybírá se první — vrátí se chyba se seznamem kandidátů (id, název, platnost) a model musí nejednoznačnost vyřešit sám přesvatRateIdna položce.
Přidání nového nástroje
- Přidejte statickou metodu do existující třídy v
Tools/(nebo novou třídu s atributem[McpServerToolType]—WithToolsFromAssembly()ji najde sama). - Metodu označte
[McpServerTool(Title = "…", ReadOnly = …, Destructive = …, Idempotent = …, OpenWorld = …), Description("…")]a každý parametr[Description("…")]— právě z těchto textů se AI rozhoduje, kdy nástroj zavolat. Hinty jsou povinné (hlídáToolDiscoveryTests.EveryTool_DeclaresItsSideEffects):ReadOnly = true— nástroj nic nemění (typickylist_*/get_*/find_*/export_*).Destructive = true— nevratná akce (mazání, odeslání e-mailu, vystavení faktury). Každýdelete_*nástroj musí mítDestructive = true.Idempotent = true— opakované volání se stejnými argumenty nic dalšího nezmění (typicky zápisy, které jen nastaví stav — „mark as paid", „approve").OpenWorld = true— nástroj mluví s něčím mimo Fakvio (ARES, e-mail,fileUrlstahování).
- Volejte API přes
IFakvioApiClient; chybí-li endpoint, doplňte ho doClient/IFakvioApiClient.cs+Client/FakvioApiClient.cs. - Celé tělo obalte
try+catch (OperationCanceledException) when (ct.IsCancellationRequested) { throw; }catch (Exception ex) { return McpToolError.ToJson(ex); }(viz stávající nástroje) — nikdy vlastní{ error = ex.Message }. Případnou deserializaci vstupu dejte do samostatnéhotrypřed tím hlavním.
- Aktualizujte tabulku výše a DEVGUIDE §4.9.
Testy
Unit testy jsou v Fakvio.Tests.Unit/McpServer/ (mockovaný IFakvioApiClient):
dotnet test Fakvio.Tests.Unit/Fakvio.Tests.Unit.csproj --filter "FullyQualifiedName~McpServer"
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
This package has no dependencies.