diff --git a/documentation/24-mcp-konektory.md b/documentation/24-mcp-konektory.md index 09a43e6..6b0a995 100644 --- a/documentation/24-mcp-konektory.md +++ b/documentation/24-mcp-konektory.md @@ -2,171 +2,160 @@ Navrh, neni naprogramovane. -Cil: firma si vyplni udaje sveho MCP serveru a jeho nastroje se objevi -v builderu jako kterakoliv jina operace. +Cil: firma vyplni udaje sveho MCP serveru a jeho nastroje muze pouzit model, +ktery za ni neco udela. -## Co je MCP z pohledu tohohle systemu +## Co MCP je a co neni -MCP server je **katalog operaci, ktery se zepta az za behu**. Rekne "umim -`create_invoice`, `search_customer`, `send_report`", ke kazde da popis a schema -vstupu, a pak je umi vykonat. +MCP je protokol **pro modely**. Server vystavuje nastroje s popisem a schematem +vstupu a *model* si sam vybere, ktery zavolat a s jakymi argumenty. Smysl je +v tom, ze se nastroj nemusi predem naprogramovat do postupu - model ho najde +a pouzije podle toho, co je zrovna potreba. -To je presne to, co u nas dela `src/data/services.ts` plus slozka `scripts/`. -Rozdil je v jedine veci, a ta rozhoduje o celem navrhu: +**Neni to protokol pro vzdalene volani procedur.** Kdyz nastroj vybere clovek +v builderu a vyplni mu pevna pole, MCP nedava nic navic proti HTTP konektoru, +ktery uz mame. Jen pribude vrstva a s ni JSON-RPC obalka. -| Dnes | MCP | -| ----------------------------------- | ----------------------------------------- | -| Operace jsou zname pri prekladu | Operace se zjisti az od serveru | -| Katalog je stejny pro vsechny firmy | Kazda firma ma jiny server, jine nastroje | -| Skript je nas, prosel code review | Nastroj je zakaznikuv, nevidime do nej | +Z toho plyne jedina vec, na ktere cely navrh stoji: -## Kde to narazi +> **MCP konektor neni zdroj kroku. Je to schopnost, kterou dostane krok +> s modelem.** -Retezec od builderu k behu je dnes tenhle: +V builderu se tedy neobjevi polozka `create_invoice`. Objevi se krok +"Nechat model splnit ukol" a v nem se zaskrtne, ktera napojeni smi pouzit. + +## Jak to vypada v kroku ``` -StepPicker -> service.actions (co nabidnout) -ulozeni -> findOperation(...) (existuje ta operace?) -beh -> scriptIdFor(...) (kdo to vykona) +Krok: Nechat model splnit ukol + Zadani: "Zaloz objednavku podle teto poptavky a vrat jeji cislo." + Data: {{data}} + Napojeni: [x] Interni sklad [ ] Firemni wiki + Model: gpt-4o-mini ``` -Vsechny tri se ptaji **statickeho katalogu**. U MCP zadny staticky katalog -neni: seznam operaci patri konektoru, ne sluzbe. Bez zmeny by slo ulozit jen -krok, ktery uz nekdo predem zapsal do kodu, coz je presny opak toho, o co jde. +Model dostane nastroje zaskrtnutych serveru, sam se rozhodne, ktere zavolat, +a krok vrati vysledek plus **seznam toho, co model opravdu udelal**. -## Navrh: jedna sluzba, operace z konektoru +Ten seznam neni pridavek. Cely tenhle system stoji na tom, ze je v logu videt, +co ktery krok provedl. Krok, ktery udela nekolik zapisu do ciziho systemu +a rekne jen "hotovo", je cerna skrinka a do provozu nepatri. -**Ne sluzba na kazdy MCP server.** Sluzby jsou kod a zakaznik si je nezalozi. -Misto toho jedna sluzba `mcp` a kazdy server je **konektor** pod ni: +## Dve cesty, jak to postavit -``` -Sluzba "MCP server" definujeme my, jednou - | credentials: adresa, token, nazev - | - +-- Konektor "Interni sklad" zaklada si firma - | tools: create_order, get_stock, ... zjisteno od serveru - | - +-- Konektor "Firemni wiki" jiny server, jine nastroje +Rozhodnuti, ktere je potreba udelat vedome: lisi se tim, kudy tece token +zakaznika. + +### A) Server predame modelu + +OpenAI Responses API umi vzit MCP server jako nastroj: + +```json +{ + "type": "mcp", + "server_label": "interni-sklad", + "server_url": "https://mcp.firma.cz", + "headers": { "Authorization": "Bearer ..." }, + "allowed_tools": ["create_order", "get_stock"], + "require_approval": "never" +} ``` -Krok stromu pak nese `serviceId: 'mcp'`, `operationId: ''` -a `connectorId`. Ten treti udaj je u MCP **povinny**, na rozdil od ostatnich -sluzeb: bez nej neni jasne, ktery server se pta, a dva konektory mohou mit -nastroj stejneho jmena. +Malo prace: konektor drzi adresu a token, krok je slozi do pozadavku. -### Kudy se nastroje dostanou do builderu +Cena za to: -Katalog uz umi doplnit nabidku az za behu. `withRuntimeOptions` presne tohle -dela pro resitele, skupiny a typy ticketu - hodnoty, ktere v dobe prekladu -neexistuji. Nastroje MCP jsou tentyz pripad, jen misto polozek vyberu doplni -cele operace. +- **Token zakaznika jde do OpenAI** a OpenAI vola jeho server primo. To musi + firma vedome odsouhlasit, ne se to dozvedet z dokumentace. +- Server musi byt dostupny **z internetu a ze site OpenAI**, ne jen z nasi. + Firemni server za VPN takhle nepouzijeme. +- Volani nejdou pres nas, takze **v nasem logu je jen to, co OpenAI vrati**, + a neplati na ne nase stropy ani redakce. -Schema vstupu prijde jako JSON Schema a prevede se na `OperationField`, tedy -tvar, ktery builder uz umi vykreslit. Prevod je primocary u toho, co se -v praxi pouziva: +### B) MCP klientem jsme my -| JSON Schema | `OperationField` | -| ----------------------- | -------------------- | -| `string` | `text` | -| `string` s `enum` | `choice` s `options` | -| `string` a dlouhy popis | `longtext` | -| `number`, `integer` | `text` s kontrolou | -| `boolean` | `choice` ano/ne | -| `object`, `array` | `json` | +Sami se serveru zeptame na `tools/list`, nastroje predame modelu jako obycejne +funkce (function calling), a kdyz si model o volani rekne, **zavolame ho my** +a vysledek mu vratime. -Co se neprevede, skonci jako `json`. Radsi pole, do ktereho clovek napise -strukturu rucne, nez pole, ktere tvari, ze rozumi necemu, cemu nerozumi. +Vic prace, ale: -### Kdo to vykona +- Token zustava u nas, stejne jako u kazdeho jineho konektoru. +- Server staci, kdyz je dostupny **z nasi adresy** - plati na nej seznamy + povolenych IP, ktere uz kvuli ostatnim sluzbam resime. +- Kazde volani jde pres `ctx.http`, tedy pres nase stropy, timeouty, redakci + a zapis do logu behu. +- Funguje s jakymkoliv modelem, ktery umi function calling, ne jen s OpenAI. -Jeden skript `mcp.call-tool` pro vsechny nastroje. Nazev nastroje je vstup, -ne soubor. Psat skript na kazdy nastroj nejde - nevznikaji u nas. +**Doporucuju B.** Ne proto, ze je hezci, ale protoze zbytek systemu stoji na +tom, ze pristupove udaje neopousti server a ze je v logu videt kazde volani. +Varianta A obe tahle pravidla porusuje, a to u konektoru, ktery si zaklada +zakaznik. + +Varianta A ma smysl jako zkratka pro verejne MCP servery bez tajemstvi. ## Co si firma vyplni -| Pole | K cemu | -| ----------------- | -------------------------------------------------- | -| Nazev | co clovek uvidi v builderu, napr. "Interni sklad" | -| Adresa serveru | HTTPS adresa MCP endpointu | -| Token | tajny, posila se jako `Authorization: Bearer` | -| Vlastni hlavicka | pro servery, ktere chteji neco jineho nez Bearer | -| Povolene nastroje | prazdne = vsechny. Jinak vycet, ktery se smi volat | +| Pole | K cemu | +| ----------------- | ----------------------------------------------------- | +| Nazev | co clovek uvidi u kroku, napr. "Interni sklad" | +| Adresa serveru | HTTPS adresa MCP endpointu | +| Token | tajny, posila se jako `Authorization: Bearer` | +| Vlastni hlavicka | pro servery, ktere chteji neco jineho nez Bearer | +| Povolene nastroje | prazdne = vsechny. Jinak vycet, ktery model smi videt | -Posledni radek neni ozdoba. MCP server muze zpristupnit i nastroje, ktere do -automatizace nepatri (mazani, sprava uctu). Vycet je jednodussi nez vysvetlovat -lidem, ktere nastroje nepouzivat. +**Vycet povolenych nastroju je hlavni pojistka.** U bezneho kroku vybira +operaci clovek, tady vybira model. Server muze vystavovat i nastroje, ktere do +automatizace nepatri (mazani, sprava uctu), a model o nich nevi nic krome +popisu, ktery si napsal jejich autor. Co neni ve vyctu, model vubec neuvidi. -## Jen HTTP, ne stdio +## Schvalovani -MCP zna dva prenosy. `stdio` spousti server jako lokalni proces - to u nas -nepripada v uvahu: znamenalo by to pustit zakaznikuv program uvnitr naseho -containeru, a to je jina trida rizika nez zavolat cizi API. +MCP zna rezim, kdy volani ceka na schvaleni cloveka. V automatizaci, ktera bezi +sama v noci, neni koho se zeptat, takze jsou dve poctive moznosti: -Zustava **Streamable HTTP**, tedy JSON-RPC pres POST. To `ctx.http` umi uz ted, -takze skript nepotrebuje zadny novy pristup k siti. +| Rezim | Kdy | +| ------------------------- | ----------------------------------------------- | +| Bez schvalovani | vychozi. Pojistkou je vycet povolenych nastroju | +| Se schvalenim pres ticket | beh se zastavi, zalozi ticket a ceka na cloveka | -Adresa serveru projde stejnou kontrolou jako u HTTP a SMTP: **nesmi mirit do -vnitrni site** (`isPrivateHost`). Vyplnuje ji firma, takze je to jedina zabrana -proti tomu, aby si nechala zavolat na neco uvnitr. - -## Overeni konektoru - -`tools/list`. Je to cteci volani, vyzaduje autorizaci a jeho vysledek se stejne -potrebuje - takze overeni a zjisteni nastroju je jedno volani, ne dve. - -Vysledek se ulozi ke konektoru a **necte se pri kazdem otevreni builderu**. -Seznam nastroju se meni radove pri nasazeni serveru, ne mezi dvema kliknutimi. -Obnovi se pri overeni konektoru a tlacitkem "Nacist nastroje znovu". - -## Co se stane, kdyz nastroj zmizi - -Ulozeny strom odkazuje na nastroj jmenem. Server se nasadi znovu, nastroj se -prejmenuje a krok ukazuje do prazdna. - -Nesmi to byt chyba ulozeni. Strom uz ulozeny je a firma za to nemuze: - -| Situace | Vysledek | -| ---------------------------------------- | ----------------------------------------- | -| Nastroj v ulozenem seznamu neni | **nedodelek**, jako chybejici konektor | -| Server je nedostupny pri ukladani stromu | ulozit, validovat proti ulozenemu seznamu | -| Nastroj chybi az pri behu | koncova chyba, neopakovat | - -Rozdil proti dnesku: u naseho skriptu je chybejici operace nase chyba, tady je -to bezna zmena na cizi strane. +Druhy rezim je vic prace, ale zapada do produktu: **delegovani na cloveka uz +umime** a je to presne ono - model dojde k mistu, kde si netroufa, a preda to +dal. Do prvni faze bych to nedaval. ## Bezpecnost -- **Do logu jde odpoved nastroje.** Prochazi stejnou redakci jako vsechno - ostatni, ale token do ni patri jen ten nas - co si server pise do odpovedi, - neovlivnime. Stoji za to pri prvnim nasazeni kouknout, co vraci. -- **Strop na velikost odpovedi** uz existuje (`SCRIPT_MAX_RESPONSE_BYTES`). - U MCP je potreba: nastroj muze vratit cely dokument. -- **Idempotence nefunguje.** Nas `Idempotency-Key` je hlavicka, ktere MCP - nerozumi. Druhy pokus po timeoutu tedy nastroj zavola podruhe. U ctecich - nastroju to nevadi, u zapisovych ano - proto se u kroku s MCP **neopakuje - automaticky**, dokud nebude jak rict, ze je nastroj bezpecny opakovat. -- **Nastroj je zakaznikuv.** Nevidime do nej a neruceme za to, co udela. To je - rozdil proti nasim skriptum a musi to byt videt i v portalu. +- **Adresa serveru nesmi mirit do vnitrni site** (`isPrivateHost`), stejne jako + u HTTP a SMTP. Vyplnuje ji firma. +- **Odpoved nastroje jde do logu behu** a prochazi redakci. Co si server pise + do odpovedi, neovlivnime. +- **Strop na velikost odpovedi** uz existuje a je tu potreba: nastroj muze + vratit cely dokument. +- **Idempotence nefunguje.** MCP zadny takovy pojem nema, takze druhy pokus po + timeoutu zavola nastroj podruhe. Krok s MCP se proto **neopakuje sam**. +- **Nastroj je zakaznikuv.** Nevidime do nej a neruceme za to, co udela. + V portalu to musi byt videt. +- **Model muze zavolat vic nastroju za sebou.** Strop na pocet volani v jednom + kroku je potreba, jinak jde spotreba i cas nahoru bez omezeni. ## Postup -| Faze | Co | -| ---- | -------------------------------------------------------------------------- | -| 1 | Sluzba `mcp`, pole konektoru, overeni pres `tools/list`, ulozeni seznamu | -| 2 | Nastroje do katalogu pres `withRuntimeOptions`, prevod JSON Schema na pole | -| 3 | Skript `mcp.call-tool` a vykonani ve strome | -| 4 | Vycet povolenych nastroju, tlacitko na obnoveni seznamu | +| Faze | Co | +| ---- | ------------------------------------------------------------------------ | +| 1 | Sluzba `mcp`, pole konektoru, overeni pres `tools/list`, ulozeni seznamu | +| 2 | Krok "Nechat model splnit ukol" s vyberem napojeni, varianta B | +| 3 | Zaznam volanych nastroju do logu behu a strop na pocet volani | +| 4 | Schvalovani pres ticket | -Prvni faze je uzitecna sama o sobe: firma si napojeni zalozi a overi, i kdyz -se jeste neda pouzit v kroku. +Prvni faze je uzitecna sama o sobe: firma si napojeni zalozi, overi a vidi, +jake nastroje server nabizi, jeste nez se da pouzit v kroku. ## Co bych nedelal -- **Nezpristupnoval bych MCP zdroje a prompty.** Server umi vedle nastroju - i `resources` a `prompts`. Do stromu kroku patri akce, ne cteni dokumentu - - a bez toho je model o polovinu jednodussi. -- **Nedelal bych z nas MCP server.** Zajimava vec, ale je to opacny smer: - pustit cizi modely na nase tickety je jine rozhodnuti nez zavolat cizi - nastroj. -- **Nespoustel bych nastroje bez konektoru.** Zadny "rychly test adresou" - - napojeni je vlastnost firmy a ma jednu cestu. +- **Nedaval bych MCP nastroje do vyberu kroku.** To byla chyba prvni verze + tohohle navrhu. Kdyz nastroj vybira clovek, staci HTTP konektor - MCP je tam + jen vrstva navic. +- **Nezpristupnoval bych `resources` a `prompts`.** Server je umi vedle + nastroju, ale krok stromu ma neco udelat, ne cist dokumenty. +- **Nedelal bych z nas MCP server.** Je to opacny smer a jine rozhodnuti: + pustit cizi modely na nase tickety. diff --git a/documentation/99-zmeny.md b/documentation/99-zmeny.md index b5aae8c..e45d9b3 100644 --- a/documentation/99-zmeny.md +++ b/documentation/99-zmeny.md @@ -7,14 +7,19 @@ Nejnovejsi nahore. Novy dokument [24-mcp-konektory.md](24-mcp-konektory.md). Neni to naprogramovane, je to navrh. -Podstata: MCP server je katalog operaci, ktery se zepta az za behu, kdezto nas -katalog je znamy pri prekladu. Retezec `service.actions` -> `findOperation` --> `scriptIdFor` se dnes cely pta statickeho katalogu, takze by slo pouzit jen -nastroj, ktery uz nekdo predem zapsal do kodu - presny opak toho, o co jde. +**Prvni verze navrhu byla postavena spatne** a je prepsana. Davala MCP nastroje +do vyberu kroku, tedy nastroj vybiral clovek a vyplnil mu pevna pole. Tak MCP +nedava nic navic proti HTTP konektoru, ktery uz mame - je to protokol pro +**modely**, kde si nastroj vybira model podle toho, co je zrovna potreba. -Navrh je **jedna sluzba `mcp` a kazdy server jako konektor pod ni**, s nastroji -doplnenymi do katalogu pres `withRuntimeOptions`, tedy tim samym zpusobem, jakym -uz se doplnuji resitele a typy ticketu. Jen HTTP, ne stdio. +Spravne zadani: MCP konektor **neni zdroj kroku, je to schopnost, kterou dostane +krok s modelem**. V builderu se objevi krok "Nechat model splnit ukol" a v nem +se zaskrtne, ktera napojeni smi pouzit. + +Dokument popisuje dve cesty a lisi se tim, kudy tece token zakaznika: predat +server modelu (OpenAI ho zavola sam), nebo byt MCP klientem my. Doporucena je +druha, protoze zbytek systemu stoji na tom, ze udaje neopousti server a ze je +v logu videt kazde volani. ## 2026-08-28 - transformace maji svou kategorii, pribylo XML