kt-mcp

kt-mcp

Self-hosted MCP server that connects AI agents to your diet log on kaloricketabulky.cz, enabling food search, meal logging, recipe creation, and daily nutrition summaries via natural language.

Category
Visit Server

README

kt-mcp

🇨🇿 Česky · 🇬🇧 English

Self-hosted MCP server, který propojí AI agenta s vaším jídelníčkem na kaloricketabulky.cz.

Řeknete „snědl jsem 3 vejce a banán" — agent prohledá českou databázi potravin, vybere správnou porci a zapíše záznamy do vašeho skutečného jídelníčku. Oficiální aplikace zůstává zdrojem pravdy, takže prémiové funkce fungují dál.

Postaveno a otestováno s Claudem (desktop, mobil i web přes custom connector), ale server mluví standardním MCP přes HTTPS s OAuth 2.1, takže se může připojit jakýkoli agent, který podporuje vzdálené MCP servery.

Neoficiální projekt, bez vazby na Dine4Fit

kaloricketabulky.cz provozuje společnost Dine4Fit, a.s., která s tímto projektem nemá nic společného a nijak ho neschvaluje. Veřejné API neexistuje; zde použité endpointy byly vyčteny z frontendového JavaScriptu samotného webu a mohou se kdykoli bez varování změnit nebo rozbít.

Podmínky Dine4Fit udělují osobní, nekomerční licenci. Tento software je zveřejněn proto, abyste si mohli zautomatizovat přístup ke svému vlastnímu účtu — což licence dovoluje. Provozovat ho jako placenou nebo víceuživatelskou službu, případně dál šířit potravinová data, která vrací, je jiná věc a jde na váš vrub. Databáze patří Dine4Fit a je chráněna evropskými databázovými právy.

Držte frekvenci požadavků na lidské úrovni. Nestavte na tom konkurenčního klienta.

Co s tím jde dělat

Jakmile je agent připojený, přestane být zapisování jídel otravným proklikáváním vyhledávání. Pár vzorů, které v praxi fungují dobře:

  • Zapisujte mluvením. „K snídani jsem měl rohlík se šunkou a dvě vejce." Agent vyhledá potraviny, zvolí přirozené porce (kus (55 g) místo odhadovaných gramů) a zapíše každou položku do správného denního jídla.

  • Zautomatizujte rutinu. Pijete každý den dvě kávy s mlékem — proč je vypisovat? Pokud váš agent umí naplánované úlohy (Claude umí), řekněte mu to jednou: „Každý den v 9:00 mi zapiš do jídelníčku dvě espressa s mlékem." Denní konstanty se zapisují samy.

  • Vyfoťte talíř. Pošlete agentovi fotku oběda a zeptejte se, co na ní je. Rozpozná potraviny, odhadne reálné gramáže z obrázku, spočítá kalorie — a po potvrzení zaloguje. Odhad je odhad, ale u míchaných talířů je to lepší než nezapsat nic.

  • Vyfoťte recept. Vyfoťte recept z kuchařky nebo screenshot z webu a řekněte „ulož mi to jako jídlo". Agent přečte suroviny, každou najde v databázi a vytvoří uložený recept — příště zalogujete jedním záznamem. Cestou můžete upravovat: „vyměň smetanu za bílý jogurt a dej poloviční cukr."

  • Ptejte se, jak vám jde den. „Kolik kalorií mi dnes zbývá?" přečte denní součty přímo z jídelníčku.

Nástroje

Nástroj Účel
search_food Najde potraviny podle českého názvu nebo 13místného EAN kódu. Vrací id potraviny, které potřebují ostatní nástroje.
get_food_portions Vypíše přirozené porce potraviny (kus (55 g), velký kus (60 g)), takže „3 vejce" se zapíšou jako tři kusy, ne odhadnutá váha.
get_food_nutrition Kalorie a makra pro dané množství, přepočítané samotným webem. Pouze čtení.
log_food Zapíše záznam o jídle do jídelníčku.
get_day_summary Přečte denní součty.
create_meal Uloží recept složený z existujících potravin.
list_my_meals Vypíše uložené recepty s jejich id a celkovou energií.
log_meal Zaloguje celý uložený recept jako jeden záznam.
delete_meal Smaže uložený recept.

Recepty

Web říká uloženému receptu jídlo (meal). create_meal ho složí z existujících potravin, takže „3 vejce a 200 ml mléka" je jedna znovupoužitelná položka místo dvou záznamů vypisovaných pokaždé znovu.

Množství se řídí stejným pravidlem jako log_food: amount počítá jednotky, ne gramy. Vynecháte-li unit_id, použije se základní jednotka potraviny — gramy u pevných potravin, mililitry u tekutin — tedy to, v čem se recepty běžně píší. Když předáte unit_id z get_food_portions, počet ho násobí: 2 × porce (250 ml) je 500 ml. Splést se je snadné: jeden raný test zadal 100 jednotek 100 ml a potichu vytvořil recept s deseti litry mléka.

Jak to funguje

kaloricketabulky.cz je Spring MVC aplikace, kde každá routa odpoví i v JSON, když přidáte ?format=json — vrací obálku {code, data, message}. Žádné oficiální ani dokumentované API neexistuje; endpointy byly zrekonstruovány z frontendového JavaScriptu webu. Úplná mapa je v src/kt/client.ts.

Agent  ──OAuth 2.1──►  kt-mcp  ──JSESSIONID──►  kaloricketabulky.cz
       ──MCP/HTTPS─►

Dvě hesla, každé hlídá něco jiného:

Tajemství Hlídá
MCP_AUTH_PASSWORD Kdo smí tento server připojit k agentovi. Zadáváte ho jednou v prohlížeči při OAuth souhlasu.
KT_PASSWORD Účet na kaloricketabulky.cz, do kterého nástroje zapisují. Nikdy neopouští server.

Co se stane při připojení

Žádný token ručně nevytváříte ani nekopírujete — OAuth to udělá za vás:

  1. Do prostředí serveru (.env) vložíte svůj login na kaloricketabulky.cz (KT_EMAIL / KT_PASSWORD) a přístupovou frázi, kterou si vymyslíte (MCP_AUTH_PASSWORD). Přihlašovací údaje ke kalorickým tabulkám zůstávají na vašem serveru; agent je nikdy nevidí.
  2. V agentovi přidáte URL konektoru, https://<your-host>/mcp. Agent se u vašeho serveru zaregistruje sám (dynamická registrace klientů).
  3. Otevře se stránka v prohlížeči — obrazovka souhlasu vašeho serveru. Jednou zadáte MCP_AUTH_PASSWORD.
  4. Váš server vygeneruje unikátní náhodný přístupový token a předá ho agentovi, který si ho uloží a automaticky obnovuje. Od té chvíle je každé volání nástroje ověřené tímto tokenem.

Protože si každý provozuje vlastní instanci, každý token patří jen jemu: váš server zná jen váš jídelníček a tokeny vydává jen tomu, kdo zná vaši frázi. Mezi instalacemi se nesdílí nic a žádná centrální služba neexistuje.

Lokální vývoj

npm install
cp .env.example .env      # doplňte své údaje
npm run build
npm test

set -a; source .env; set +a
PUBLIC_URL=https://localhost:8092 node dist/index.js

GET /healthz vrací výsledný veřejný MCP endpoint — nejrychlejší způsob, jak ověřit, že PUBLIC_URL je opravdu to, co si myslíte.

Vlastní hosting

⚠️ Server by měl běžet nepřetržitě — na uspávaném PC selžou naplánované zápisy. Podrobnosti a doporučení v HOSTING.md.

Referenční nasazení používá Docker, nginx a Cloudflare Tunnel zdarma — na hostiteli ani routeru se neotvírá žádný příchozí port. Funguje ale cokoli jiného, co před kontejner postaví HTTPS hostname; tunel je jen nejlevnější bezpečná výchozí volba.

Cesta požadavku:

Cloudflare → cloudflared (host network) → 127.0.0.1:80 → nginx → kt-mcp:8092

nginx poslouchá jen na loopbacku hostitele a cloudflared se připojuje ven. Konfigurace nginx (deploy/nginx.conf.template) vypíná proxy buffering — bez toho by Server-Sent Events čekaly na konec streamu a volání nástrojů by se zasekávala.

1. Vytvořte tunel (jednou), v Cloudflare Zero Trust dashboardu → Networks → Tunnels:

  • Vytvořte tunel, zkopírujte tunnel token.
  • Přidejte public hostname se službou HTTPlocalhost:80. cloudflared ze stacku běží v host network módu, takže localhost:80 je nginx publikovaný na loopbacku hostitele. Obyčejné http, protože TLS ukončuje tunel.

Zvolený hostname je dále označován <your-host>; funguje jakákoli doména na vašem Cloudflare účtu a nic v kódu na ni není vázané.

2. Sestavte image, z tohoto adresáře:

docker compose build

3. Nasaďte stack. deploy/stack.yml je runtime compose soubor (funguje samostatně i jako Portainer stack); nastavte tyto proměnné prostředí:

Proměnná Hodnota
KT_EMAIL / KT_PASSWORD Váš login na kaloricketabulky.cz
MCP_HOSTNAME <your-host> — holý hostname bez schématu. nginx si ho při startu doplní do konfigurace
MCP_AUTH_PASSWORD Dlouhá přístupová fráze, kterou si zvolíte (min. 12 znaků)
PUBLIC_URL https://<your-host> — musí přesně odpovídat hostname tunelu
TUNNEL_TOKEN Z kroku 1

Udržujte docker-compose.yml a deploy/stack.yml v souladu — první sestavuje image, druhý ho spouští.

4. Ověřte nasazení, než ho připojíte k agentovi:

./scripts/verify-deployment.sh https://<your-host>

5. Připojte agenta. V Claudovi: Settings → Connectors → Add custom connector → zadejte https://<your-host>/mcp. Agent se sám zaregistruje, otevře stránku souhlasu a vy jednou zadáte MCP_AUTH_PASSWORD. Ostatní MCP klienti mají vlastní postup „přidat vzdálený MCP server" se stejnou URL.

Pozdější změna domény

Hostname není zapečený v image — shodovat se musí jen PUBLIC_URL a public hostname tunelu. Přesun:

  1. Přesměrujte public hostname tunelu na novou doménu (nebo vytvořte nový tunel a vyměňte TUNNEL_TOKEN).
  2. Aktualizujte PUBLIC_URL na stacku a znovu nasaďte.
  3. Znovu spusťte ověřovací skript, pak v agentovi konektor odeberte a přidejte znovu.

Krok 3 je nutný: PUBLIC_URL je OAuth issuer a identifikátor zdroje podle RFC 8707, takže tokeny vydané pod starým hostname jsou po přesunu správně odmítnuty.

Zaseklí, nebo nemáte kde hostovat?

Pokud při vlastním hostování narazíte, založte GitHub issue — rád pomůžu, od toho issue tracker je.

Vlastní hosting potřebuje stroj, který běží nepřetržitě (viz HOSTING.md), a účet u Cloudflare. Pokud nemáte kde server provozovat, ozvěte se na kt-mcp@stanwhy.me a něco vymyslíme — jen počítejte s tím, že provoz privátní instance znamená reálné náklady na server, které by šly za vámi.

Autentizace

Server je sám sobě OAuth 2.1 autorizačním serverem a implementuje, co MCP specifikace od chráněného zdroje vyžaduje:

  • /.well-known/oauth-protected-resource/mcp — RFC 9728 discovery
  • /.well-known/oauth-authorization-server — RFC 8414 discovery
  • /authorize, /token, /register, /revoke — authorization code + PKCE, dynamická registrace klientů, rotace refresh tokenů

Přístupové tokeny žijí 30 dní a persistují se do /data na volume, takže redeploy nevynutí nové připojení konektoru.

Co chrání přístupová fráze

MCP_AUTH_PASSWORD je jediná cesta k získání tokenu. Každý požadavek na /mcp vyžaduje platný bearer token a tokeny vydává výhradně formulář souhlasu po porovnání s frází v konstantním čase. Ověřeno auditem (scripts/security-audit.sh): neautentizovaná volání i volání s padělaným tokenem jsou odmítnuta, vymyšlené autorizační kódy a refresh tokeny jsou odmítnuty, žádný veřejný endpoint nevydává credential a žádná variace metody či cesty se k nástrojům bez tokenu nedostane.

Dvě věci jsou veřejné záměrně a bezpečně: OAuth discovery dokumenty (vyžaduje je specifikace) a dynamická registrace klientů. Registrace klienta útočníkovi nic nedá — token bez fráze stále nezíská.

Hrubou sílu omezuje limit 100 požadavků za 15 minut na /authorize. Limiter klíčuje podle IP klienta, proto nginx přepisuje X-Forwarded-For ověřenou hlavičkou CF-Connecting-IP od Cloudflare a aplikace věří přesně jednomu proxy hopu. Připojování k hlavičce od klienta — nebo trust proxy: true — by útočníkovi dovolilo vyrobit si nový bucket pro každý požadavek a limit by přestal existovat.

Zbytková rizika, o kterých je dobré vědět: kdokoli s frází má plný přístup; jednotlivé tokeny nelze odvolat jinak než smazáním /data/oauth-state.json; a protože registrace klientů je otevřená, phishingový odkaz na stránku souhlasu by mohl frázi zachytit, kdybyste ji tam zadali — před autorizací zkontrolujte jméno klienta zobrazené ve formuláři.

Omezení

  • Nedokumentovaný upstream. Dine4Fit tu nic negarantuje; refactoring frontendu může bez varování změnit tvary odpovědí. Klient při čemkoli nečekaném vyhodí chybu místo hádání, takže se rozbití projeví hlasitou chybou, ne špatným počtem kalorií.
  • md5(hesla) je ekvivalent hesla. Web hashuje heslo na klientovi, hash je tedy stejně citlivý jako heslo samotné. Podle toho s KT_PASSWORD zacházejte.
  • Jeden účet na instanci. Server se přihlašuje k jedinému účtu kaloricketabulky.cz. Dva lidé, dva kontejnery.
  • Osobní použití. Automatizace zápisů do vlastního účtu je mnohem měkčí pozice než hromadný scraping, ale pořád je mimo cokoli, co Dine4Fit posvětil. Držte frekvenci požadavků na lidské úrovni a nestavte na tom konkurenčního klienta.
  • Čtení jednotlivých záznamů dne je nejméně ověřený endpoint; get_day_summary posílá odpověď webu beze změny dál, protože její tvar není plně zdokumentovaný.
  • Část porce receptu zalogovat nejde. Formulář pro přidání jídla sice nabízí count proti pseudo-jednotkám porce, procenta a gramy, ale server ho ignoruje a zaloguje vždy celý recept — měřeno na receptu o 249 kcal přidalo 0,5 porce i polovina váhy plných 249. log_meal proto nemá argument portions; když snědená byla jen část receptu, zalogujte suroviny jednotlivě přes log_food.

Licence

MIT. Databáze potravin a služba kaloricketabulky.cz patří Dine4Fit, a.s. — tato licence pokrývá pouze kód v tomto repozitáři.

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