Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

53 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

openmcp-sdk

Sdílený Python runtime pro MCP konektory platformy OpenMCP.cz.

Konektor napsaný nad tímto SDK je jeden FastMCP server, který beze změny kódu běží ve třech režimech: lokálně přes stdio v Claude Desktopu, hostovaně na platformě OpenMCP, nebo self-hosted za vlastním OIDC. O identitu, přihlašovací údaje, politiku zápisu, pseudonymizaci osobních údajů a tvar odpovědí se stará SDK — konektor řeší jen svoje API.

  • Verze: 0.4.2 · Python: 3.12+ (CI ověřuje 3.12, 3.13 a 3.14) · Licence: MIT
  • Konektory postavené nad SDK: ares-mcp (veřejný rejstřík, běží bez jediného tajemství), upgates-com-mcp (e-shop, API klíč), dotykacka-mcp (pokladní systém, delegovaný OAuth)

Instalace

SDK není na PyPI — jméno openmcp-sdk tam patří nesouvisejícímu projektu. Instalujte přímo z GitHubu, ideálně připnuté na konkrétní commit:

pip install "openmcp-sdk @ git+https://github.com/mcp-open/openmcp-sdk@<commit-sha>"

Konektory z organizace mcp-open navíc nosí ověřený snapshot SDK přímo v repozitáři, takže se dají postavit i bez sítě:

python release/materialize_sdk.py --root . --output /tmp/openmcp-sdk
pip install /tmp/openmcp-sdk -e .

Skript ověří SHA-256 archivu proti release/openmcp-sdk.sha256 a commit proti .sdk-ref; nesoulad zastaví instalaci.

Rychlý start

Konektor je adresář se třemi věcmi: connector.yaml (manifest), nástroje zaregistrované do FastMCP a jeden run_connector().

# src/connector/__main__.py
from fastmcp import FastMCP
from mcp.types import ToolAnnotations
from openmcp_sdk import run_connector, current_context, UpstreamClient

mcp = FastMCP("muj-konektor")

@mcp.tool(annotations=ToolAnnotations(readOnlyHint=True))
def get_faktura(cislo: str) -> dict:
    ctx = current_context()
    client = UpstreamClient(base_url="https://api.dodavatel.cz/v1",
                            headers={"X-Api-Key": ctx.secrets["api_key"]})
    return client.get_json(f"/faktury/{client.seg(cislo)}")

run_connector("connector.yaml", mcp)

Spuštění lokálně:

export OPENMCP_MODE=local-stdio          # výchozí hodnota, dá se vynechat
export MUJKONEKTOR_API_KEY='...'         # <SLUG>_<KLÍČ> velkými písmeny
python -m connector

Připojení do MCP klienta (Claude Desktop, Claude Code):

{
  "mcpServers": {
    "muj-konektor": {
      "command": "/cesta/k/.venv/bin/python",
      "args": ["-m", "connector"],
      "cwd": "/cesta/ke/konektoru",
      "env": {
        "OPENMCP_MODE": "local-stdio",
        "MUJKONEKTOR_API_KEY": "..."
      }
    }
  }
}

Lokální režim nepotřebuje účet na OpenMCP ani žádnou běžící službu platformy.

Odpovědi nástrojů

Nástroj vrací obálku s daty a provenancí, aby model věděl, odkud údaj je a jak je čerstvý:

{
  "data": {"ico": "27074358", "obchodniJmeno": "Asseco Central Europe, a.s."},
  "provenance": {
    "source_id": "ares",
    "source_url": "https://ares.gov.cz/ekonomicke-subjekty-v-be/rest/ekonomicke-subjekty/27074358",
    "retrieved_at": "2026-07-28T17:41:55Z",
    "freshness": "live"
  },
  "warnings": []
}

Provenance vynucuje HTTP(S) URL, bezpečný source_id a UTC časové razítko s koncovkou Z.

Přesměrování se ve výchozím nastavení nenásleduje — klient s Basic auth by mohl odnést přihlašovací údaje na cizí origin. Když ho upstream potřebuje (typicky kanonizace URL záznamu), povolte ho výslovně a jen pro GET:

client.request("GET", "/faktury/ext:kod:FV-1", same_origin_redirects=1)

Následuje se pouze Location se stejným scheme, hostem i portem; cokoli jiného zůstane chybou.

Chyby jsou ConnectorError s uzavřenou množinou kódů — invalid_input, forbidden, rate_limited, upstream_unavailable, upstream_error, internal. Text upstream odpovědi se do zprávy nikdy nekopíruje: mohl by nést obsah napsaný cizím uživatelem a putoval by rovnou do kontextu modelu. HTTP stav zůstává dostupný v ConnectorError.status pro strukturované rozhodování.

Request context

Identita a přihlašovací údaje se nikdy nepředávají jako argument nástroje — model by je viděl a mohl si je vymyslet. Čtou se z kontextu, který transport založí pro každý request zvlášť:

from openmcp_sdk import current_context

ctx = current_context()
ctx.secrets["api_key"]     # per-request kopie, neměnná
ctx.config["region"]       # typovaná podle manifestu
ctx.principal.sub          # ověřený subjekt
ctx.policy                 # co workspace dovoluje zapisovat
ctx.oauth                  # klient delegovaného OAuth, nebo None

Režimy běhu

Režim vybírá OPENMCP_MODE; výchozí je local-stdio.

local-stdio

Jednouživatelský režim (sub=local). Hodnoty se čtou z proměnných prostředí pojmenovaných <SLUG>_<KLÍČ> velkými písmeny a striktně se převádějí na typ z manifestu (string, int, bool, enum). Booleany berou jen true, false, 1, 0, yes, no, on, off — cokoli jiného zastaví start. Chybějící povinný secret je chyba, ne varování.

hosted

Konektor běží na platformě za gatewayí. Ta posílá X-OpenMCP-Sub spolu s X-OpenMCP-Gateway-Token; token se porovnává v konstantním čase ještě před čtením jakéhokoli tajemství, takže samotná hlavička se subjektem není bezpečnostní hranicí a přímé volání podu bez tokenu dostane 403.

Přihlašovací údaje se čtou z HashiCorp Vaultu (KV v2) z cesty {mount}/{users|teams}/{owner_id}/{slug} a vždy v přesné verzi, kterou určí control plane hlavičkou X-OpenMCP-Credential-Version. Verzi latest runtime nikdy nečte — mohla by to být rozpracovaná rotace, která ještě neprošla testem.

Povinné: OPENMCP_INTERNAL_TOKEN (nejméně 32 znaků), VAULT_ADDR (pokud konektor má nějaké credentials).

self-hosted

Identita pochází výhradně z ověřeného OIDC access tokenu (FastMCP JWTVerifier kontroluje podpis, issuer i audience); klientské hlavičky X-OpenMCP-* se v tomto režimu jako záloha nepoužijí.

Povinné: OIDC_ISSUER, OIDC_JWKS_URI a VAULT_ADDR (má-li konektor credentials).

Proměnné prostředí

Proměnná Režim Výchozí Význam
OPENMCP_MODE vše local-stdio local-stdio, hosted, self-hosted
OPENMCP_PII_SALT vše povinný, když manifest má runtime.pii_salt: true
OPENMCP_LOG_LEVEL vše INFO úroveň logu
OPENMCP_LOG_FORMAT vše json text přepne na čitelný formát
OPENMCP_LOG_SETUP vše true false ponechá logging na hostiteli
OPENMCP_COMPONENT vše slug konektoru název komponenty v JSON logu
OPENMCP_INTERNAL_TOKEN hosted, self-hosted sdílené tajemství gatewaye, min. 32 znaků
OPENMCP_ALLOWED_ORIGINS hosted, self-hosted https://claude.ai,https://claude.com CORS allowlist
PORT hosted, self-hosted 8000 port HTTP serveru
VAULT_ADDR hosted, self-hosted adresa Vaultu
VAULT_ROLE hosted, self-hosted slug konektoru Kubernetes auth role
VAULT_MOUNT hosted, self-hosted secret KV v2 mount
VAULT_TTL hosted, self-hosted 60 TTL cache přihlašovacích údajů (s)
VAULT_TIMEOUT hosted, self-hosted 10 timeout volání Vaultu (s)
VAULT_CACERT hosted, self-hosted PEM CA bundle pro interní TLS
VAULT_REQUIRE_HTTPS hosted, self-hosted false vynutí https:// adresu Vaultu
VAULT_CACHE_MAX hosted, self-hosted 1024 strop položek cache
OIDC_ISSUER, OIDC_JWKS_URI self-hosted ověření access tokenu
OIDC_AUDIENCE self-hosted slug konektoru očekávaná audience

SDK nemá přepínač, kterým by šlo vypnout ověřování TLS certifikátu Vaultu.

Manifest

connector.yaml je striktní schéma (extra: forbid) — překlep v názvu pole zastaví start, ne až první request. Validuje se mimo jiné DNS-safe slug, verze, unikátnost klíčů, enum hodnoty a defaulty, zákaz tajemství zapsaných napevno v YAML, egress pravidla (host, port, prefix cesty, HTTP metody) a kurátorovaný blok display.

Klíč sdk_min_version je zároveň migrační přepínač: od 0.4.0 jsou startovací kontroly tvrdé (nesoulad manifestu a zaregistrovaných nástrojů shodí start), níže jen varují.

Read-only filtr je fail-closed. Když je instance read-only, přežijí jen nástroje s výslovným ToolAnnotations(readOnlyHint=True); chybějící nebo nejednoznačná anotace znamená „mutující", a nástroj se odstraní.

Kontrola bez spouštění serveru:

openmcp-sdk validate connector.yaml

Osobní údaje

Konektory, které vracejí data o lidech, pseudonymizují citlivá pole ještě před odesláním modelu. Token má tvar <KATEGORIE_a1b2c3d4e5f6> a je jednosměrný: HMAC-SHA256 z "kategorie:hodnota" pod klíčem odvozeným z OPENMCP_PII_SALT a rozsahu tenanta. Nikde nevzniká re-identifikační mapa.

Konektor dodává jen tabulku polí, logiku má SDK. Salt patří do secretu runtime prostředí — nikdy do manifestu ani do image, protože vstupy typu e-mail mají nízkou entropii a jeho únik by umožnil re-identifikaci hrubou silou.

Bez OPENMCP_PII_SALT konektor s runtime.pii_salt: true záměrně nenastartuje: tichý fallback na náhodný salt by tokeny rozbil až po prvním restartu.

Delegovaný OAuth

Konektor, který volá upstream jménem uživatele, deklaruje v manifestu:

auth:
  type: oauth_delegated
  provider: dotykacka
  operator_fields:
    - {key: client_id, label: "Client ID", type: string, required: true}
    - {key: client_secret, label: "Client Secret", type: string, secret: true, required: true}
  runtime_secrets: [refresh_token, cloud_id]

operator_fields registruje provozovatel konektoru jednou. Platforma provede uživatelský souhlas a uloží runtime_secrets do per-user Vault blobu. Nástroj si pak vezme krátkodobý access token:

token = current_context().oauth.access_token()   # líná výměna, cache do expirace

Při upstream 401 zavolejte ctx.oauth.invalidate(), aby se token při dalším volání vyměnil znovu. Klient je thread-safe a souběžná volání nespustí lavinu výměn. Zabudovaný provider: dotykacka.

HTTP endpointy (hosted a self-hosted)

Cesta Ochrana K čemu
GET /healthz žádná liveness a readiness
GET,POST /mcp gateway token nebo OIDC MCP protokol, bezstavový
POST /internal/credential-test X-OpenMCP-Test-Token ověří přesnou verzi údajů z Vaultu; nepřijímá žádný vstup
POST /internal/public-safe-test X-OpenMCP-Public-Safe-Test-Token syntetický test konektoru bez tajemství
POST /test X-OpenMCP-Test-Token starší cesta s předaným {secrets, config}; s Vaultem se nemontuje

MCP transport běží bezstavově (stateless_http), protože hostovaný konektor má víc replik a následný request může dorazit na jiný pod.

Distribuce do klientů

Z jednoho release kontraktu umí SDK vyrenderovat balíčky pro Claude Desktop (MCPB 0.4), OpenAI a Gemini CLI:

openmcp-sdk render-adapters connector.yaml distribution.yaml --output /tmp/adaptery
openmcp-sdk verify-release release-gate.yaml --root . \
  --source-commit "$GITHUB_SHA" --output release/release-evidence.json

Brána je fail-closed: vyžaduje zamčené závislosti, přesný content manifest, SPDX SBOM skutečného payloadu, výsledek scanu, keyless podpis a SLSA provenance. Lokální a vzdálený režim se nemíchají — .mcpb vzniká jen pro lokální distribuci, vzdálené konektory dostanou operátorský handoff, ne instalovatelný balíček.

Podrobnosti a bezpečnostní invarianty: docs/DISTRIBUTION.md.

Další příkazy CLI: pack-mcpb, build-contents-manifest, bind-trivy-report, build-provenance, withdraw-release.

Vývoj

uv venv -p 3.13 && . .venv/bin/activate
pip install -e '.[test]' ruff==0.15.22
ruff check openmcp_sdk tests
pytest -q

Balík obsahuje py.typed. Verze má jediný zdroj pravdy v openmcp_sdk/_version.py. Vyžaduje FastMCP >=3.2,<4; FastMCP 2.x se do nového release locku nesmí dostat.

SDK řeší jen společný runtime. Klient konkrétního API, doménové modely a závislosti na nich patří do konektoru.

Přispívání a bezpečnost

About

Sdílený Python runtime pro MCP konektory platformy OpenMCP.cz — manifest, identita, credentials, PII pseudonymizace a distribuce v jednom kontraktu.

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages