MCP EasyWeb podle skutecne specifikace: auth v2 s klicem zarizeni

Predchozi verze posilala na /login jen jmeno, heslo a nazev zarizeni. Server na
to odpovidal 400 Bad Request na cokoliv, i na spravne udaje, protoze cekal neco
uplne jineho.

EasyWeb ma auth v2: token se nevydava proti uctu, ale proti zarizeni, a to je
par klicu ECDSA P-256. Jmeno a heslo se pouziji jedinkrat, pri registraci klice,
a soucasti registrace je podpis, kterym zarizeni dokazuje, ze privatni klic
k poslanemu verejnemu opravdu ma. Od te chvile se podepisuje kazde volani, ktere
s tokeny hybe.

Overeno proti bezicimu serveru: se spravnym telem uz /login nevraci 400, ale 401
s neplatnymi udaji. Ucty z jejich testovaciho settings.json na verejnych
instancich neplati, takze dal se bez skutecnych udaju nedostanu.

Prihlaseni:
- src/mcp/easyweb/crypto.ts - klice, podpisy, otisky. Podpis musi byt P1363,
  tedy hole r||s, 64 bajtu. Node podepisuje ve vychozim nastaveni do DER a ten
  by protistrana neuznala
- src/mcp/easyweb/device.ts - klic se vyrobi jednou a prezije restart, uklada se
  mezi udaje konektoru, ktere uz jsou zasifrovane. Novy priznak `managed` na
  poli sluzby znamena, ze ho vyplnuje portal a ve formulari se nezobrazuje
- src/mcp/easyweb/session.ts - tri tokeny, retez s ustupy (platny pristupovy,
  obnova obnovovacim, obnova zarizenim, cele prihlaseni), jedno prihlaseni
  naraz na konektor, tokeny jen v pameti

Ta posledni pravidla nejsou opatrnost navic: tokeny jsou jednorazove, druhe
pouziti server odmita kodem 409 a umi zarizeni zablokovat.

Transport:
- server si sam vybira, jestli odpovi JSON telem nebo SSE streamem, a streamem
  odpovida i na obycejna volani. Klient nabizi obojí a cte stream po kouscich -
  u dlouhych uloh ho server sam nezavira
- handshake plati na token, ne na volani
- odmitnute sezeni prijde jako chyba -32008 uvnitr uspesne odpovedi
- seznamy se skladaji pres vsechny stranky, bez toho je videt jen prvni
- odpoved se rozbaluje rekurzivne (structuredContent, contents, content, JSON
  zapsany jako text)

Dlouho bezici nastroje se spousti jako uloha a ceka se na ni dotazovanim. Limit
kroku se pri tom posouva z patnacti sekund na deset minut.

Nedodelane: trvaly kanal notifikaci (GET SSE), nahravani souboru po castech
a hlidani zmen kontraktu podle verze serveru.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
JiriUhlir
2026-08-28 10:58:24 +02:00
co-authored by Claude Opus 5
parent d881dab30d
commit 4d156d2837
18 changed files with 1514 additions and 404 deletions
+8 -2
View File
@@ -754,6 +754,8 @@ function ConnectorEditor({
const [issues, setIssues] = useState<Array<{ field: string; message: string }>>([]);
const service = servicesById.get(serviceId) ?? services.find((item) => item.id === serviceId);
// Spravovana pole vyplnuje portal, ne clovek. Do formulare nepatri.
const visibleCredentials = (service?.credentials ?? []).filter((field) => !field.managed);
// Pri otevreni dialogu se formular naplni podle toho, co se upravuje.
// Zavislost je na `editor`, ne na `service` - jinak by prepnuti sluzby
@@ -906,9 +908,13 @@ function ConnectorEditor({
</Field>
</div>
{service && service.credentials.length > 0 ? (
{/*
Pole, ktera spravuje portal (klic zarizeni u MCP EasyWebu), se
nezobrazuji. Uzivatel do nich nema co psat a prepsat by je mohl.
*/}
{service && visibleCredentials.length > 0 ? (
<div className="grid gap-4 border-t border-ink-600/50 pt-4 sm:grid-cols-2">
{service.credentials.map((field) => (
{visibleCredentials.map((field) => (
<CredentialInput
key={field.id}
field={field}
+2
View File
@@ -262,6 +262,8 @@ export interface ServiceCredentialField {
/** true = hodnota se ze serveru nikdy nevraci. */
secret: boolean;
hint?: string;
/** true = hodnotu spravuje portal, ve formulari se nezobrazuje. */
managed?: boolean;
}
export interface ServiceOperation {