Prepsany navrh MCP: patri k modelu, ne do vyberu kroku
Prvni verze byla postavena spatne. 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. Byla to zamena kategorie. Spravne: 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. Krok vraci vysledek plus seznam toho, co model opravdu zavolal - bez nej je to cerna skrinka a do provozu to nepatri. Dokument popisuje dve cesty, lisi se tim kudy tece token zakaznika: - predat server modelu (OpenAI ho zavola sam) - malo prace, ale token jde do OpenAI, server musi byt dostupny z jeho site a volani nejdou pres nas log - byt MCP klientem my - vic prace, ale token zustava u nas, plati nase stropy, redakce i seznamy povolenych IP, a funguje to s jakymkoliv modelem Doporucena je druha. Tvar tool objektu overen proti dokumentaci OpenAI (type, server_label, server_url, headers, authorization, allowed_tools, require_approval). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
2d38ac1350
commit
3e6b365eec
+117
-128
@@ -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: '<nazev nastroje>'`
|
||||
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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user