Obsah
1. Model Context Protocol: vznikající standard pro potřeby aplikací využívajících AI a LLM
4. Instalace nástrojů nutných pro spuštění MCP Inspectoru
5. Implementace jednoduchého MCP serveru s jediným definovaným zdrojem
6. Spuštění serveru s jeho otestováním pomocí MCP Inspectoru
7. MCP server s definovaným dynamickým zdrojem (resource template)
8. Otestování nové varianty MCP serveru
9. Zdroj vracející strukturovaná data
10. Server s definicí nástroje (tool)
11. Otestování MCP serveru s voláním nástroje
12. Složitější MCP servery s několika zdroji a/nebo nástroji
13. Jak MCP Inspector spouští MCP server?
14. Přímé spuštění MCP serveru z příkazové řádky s transportem STDIO
15. Přímé spuštění MCP serveru z příkazové řádky s transportem SSE
16. MCP klient spouštějící server s komunikací přes STDIO
17. MCP klient s komunikací přes SSE
18. Klient, který přečte zdroj z MCP serveru
19. Repositář s demonstračními příklady
1. Model Context Protocol: vznikající standard pro potřeby aplikací využívajících AI a LLM
V posledním roce je možné sledovat velmi rychlý vývoj aplikací, které nějakým způsobem využívají umělou inteligenci (AI) popř. velké jazykové modely (LLM). Takové aplikace nebo služby se mohou skládat z většího množství komponent, které spolu musí nějakým způsobem komunikovat – a to většinou oboustranně a asynchronně. Samozřejmě již existuje několik více či méně standardních způsobů takové komunikace – REST API, GraphQL, RPC, do jisté míry i komunikační protokol Apache Kafky. Ovšem každému zmíněnému komunikačnímu mechanismu něco chybí. REST API je mechanismus pro práci se zdroji (i když ho lze různými způsoby ohýbat), GraphQL je určen především pro provádění dotazů, RPC naopak pro volání vzdáleného kódu a protokol Apache Kafky pro realizaci komunikačních strategií pub-sub, push-pull a streamingu.
2. Základní vlastnosti MCP
Řešením by mohl být Model Context Protocol neboli MCP. V něm dochází ke kombinaci několika způsobů komunikace. Lze pracovat se zdroji (resources), což do značné míry odpovídá klasickému REST API, lze volat takzvané nástroje (tools), což odpovídá RPC a navíc je umožněna oboustranná a asynchronní komunikace mezi klienty a servery. V současnosti jsou definovány dva takzvané transporty, což je způsob realizace komunikace mezi klientem a serverem na nižší vrstvě. Buď se využívá STDIO, což vlastně není nic jiného než přímá komunikace mezi klientem a serverem přes standardní vstupy a výstupu (a tedy komunikace probíhá lokálně, navíc si klient může server přímo spustit). A druhým transportem je SSE neboli Server Sent Events; komunikace poté probíhá přes HTTP/HTTPS.
3. Instalace balíčku mcp
MCP je možné využívat mnoha různými způsoby. Je například podporován některými frameworky určenými pro tvorbu aplikací založených na velkých jazykových modelech atd. Ovšem pro začátek je dobré zjistit, jaké jsou vlastně základní vlastnosti MCP a z tohoto důvodu si naprogramujeme vlastní MCP server (resp. hned několik jeho variant) a taktéž jednoduché MCP klienty. V současnosti je možné aplikace používající MCP naprogramovat v mnoha jazycích; my se pro jednoduchost zaměříme na Python. Pro ekosystém Pythonu vznikl balíček nazvaný příhodně mcp, který si nainstalujeme příkazem:
$ pip install --user mcp
Resp. na systémech rozlišujících Python 2 a Python 3:
$ pip3 install --user mcp
Balíček mcp má sice několik závislostí (například celý Pydantic a Uvicorn), ovšem i tak by měla být jeho instalace prakticky okamžitá:
Collecting mcp
Downloading mcp-1.5.0-py3-none-any.whl.metadata (20 kB)
Requirement already satisfied: anyio>4.5 in /home/ptisnovs/.local/lib/python3.12/site-packages (from mcp) (4.6.2.post1)
Collecting httpx-sse>0.4 (from mcp)
Downloading httpx_sse-0.4.0-py3-none-any.whl.metadata (9.0 kB)
Requirement already satisfied: httpx>0.27 in /home/ptisnovs/.local/lib/python3.12/site-packages (from mcp) (0.28.0)
Collecting pydantic-settings>2.5.2 (from mcp)
Downloading pydantic_settings-2.8.1-py3-none-any.whl.metadata (3.5 kB)
Requirement already satisfied: pydantic<3.0.0,>2.7.2 in /home/ptisnovs/.local/lib/python3.12/site-packages (from mcp) (2.10.5)
Collecting sse-starlette>1.6.1 (from mcp)
Downloading sse_starlette-2.2.1-py3-none-any.whl.metadata (7.8 kB)
Requirement already satisfied: starlette>0.27 in /home/ptisnovs/.local/lib/python3.12/site-packages (from mcp) (0.41.3)
Requirement already satisfied: uvicorn>0.23.1 in /home/ptisnovs/.local/lib/python3.12/site-packages (from mcp) (0.34.0)
Requirement already satisfied: idna>2.8 in /usr/lib/python3.12/site-packages (from anyio>4.5->mcp) (3.7)
Requirement already satisfied: sniffio>1.1 in /home/ptisnovs/.local/lib/python3.12/site-packages (from anyio>4.5->mcp) (1.3.1)
Requirement already satisfied: certifi in /home/ptisnovs/.local/lib/python3.12/site-packages (from httpx>0.27->mcp) (2024.8.30)
Requirement already satisfied: httpcore==1.* in /home/ptisnovs/.local/lib/python3.12/site-packages (from httpx>0.27->mcp) (1.0.7)
Requirement already satisfied: h11<0.15,>0.13 in /home/ptisnovs/.local/lib/python3.12/site-packages (from httpcore==1.*->httpx>0.27->mcp) (0.14.0)
Requirement already satisfied: annotated-types>0.6.0 in /home/ptisnovs/.local/lib/python3.12/site-packages (from pydantic<3.0.0,>2.7.2->mcp) (0.7.0)
Requirement already satisfied: pydantic-core==2.27.2 in /home/ptisnovs/.local/lib/python3.12/site-packages (from pydantic<3.0.0,>2.7.2->mcp) (2.27.2)
Requirement already satisfied: typing-extensions>4.12.2 in /usr/lib/python3.12/site-packages (from pydantic<3.0.0,>2.7.2->mcp) (4.12.2)
Requirement already satisfied: python-dotenv>0.21.0 in /home/ptisnovs/.local/lib/python3.12/site-packages (from pydantic-settings>2.5.2->mcp) (1.0.1)
Collecting anyio>4.5 (from mcp)
Downloading anyio-4.9.0-py3-none-any.whl.metadata (4.7 kB)
Requirement already satisfied: click>7.0 in /home/ptisnovs/.local/lib/python3.12/site-packages (from uvicorn>0.23.1->mcp) (8.1.8)
Downloading mcp-1.5.0-py3-none-any.whl (73 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 73.7/73.7 kB 1.6 MB/s eta 0:00:00
Downloading httpx_sse-0.4.0-py3-none-any.whl (7.8 kB)
Downloading pydantic_settings-2.8.1-py3-none-any.whl (30 kB)
Downloading sse_starlette-2.2.1-py3-none-any.whl (10 kB)
Downloading anyio-4.9.0-py3-none-any.whl (100 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100.9/100.9 kB 4.4 MB/s eta 0:00:00
Installing collected packages: httpx-sse, anyio, sse-starlette, pydantic-settings, mcp
Attempting uninstall: anyio
Found existing installation: anyio 4.6.2.post1
Uninstalling anyio-4.6.2.post1:
Successfully uninstalled anyio-4.6.2.post1
Successfully installed anyio-4.9.0 httpx-sse-0.4.0 mcp-1.5.0 pydantic-settings-2.8.1 sse-starlette-2.2.1
Po instalaci by měl být k dispozici i příkaz mcp, jehož existenci a dostupnost si snadno ověříme:
Usage: mcp [OPTIONS] COMMAND [ARGS]... MCP development tools ╭─ Options ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ --help Show this message and exit. │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─ Commands ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ version Show the MCP version. │ │ dev Run a MCP server with the MCP Inspector. │ │ run Run a MCP server. │ │ install Install a MCP server in the Claude desktop app. │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
Taktéž si můžeme přímo v interaktivní smyčce Pythonu naimportovat některou třídu z tohoto balíčku (například FastMCP) a zobrazit si její nápovědu:
$ python Python 3.12.9 (main, Feb 4 2025, 00:00:00) [GCC 14.2.1 20240912 (Red Hat 14.2.1-3)] on linux Type "help", "copyright", "credits" or "license" for more information. >>> from mcp.server.fastmcp import FastMCP >>> help(FastMCP)
S výsledkem:
Help on class FastMCP in module mcp.server.fastmcp.server: class FastMCP(builtins.object) | FastMCP(name: 'str | None' = None, instructions: 'str | None' = None, **settings: 'Any') | | Methods defined here: | | __init__(self, name: 'str | None' = None, instructions: 'str | None' = None, **settings: 'Any') | Initialize self. See help(type(self)) for accurate signature. | | add_prompt(self, prompt: 'Prompt') -> 'None' | Add a prompt to the server. | | Args: | prompt: A Prompt instance to add | | add_resource(self, resource: 'Resource') -> 'None' | Add a resource to the server. | | Args: | resource: A Resource instance to add | ... ... ...
4. Instalace nástrojů nutných pro spuštění MCP Inspectoru
Balíček mcp, který jsme právě nainstalovali, nabízí možnost využití takzvaného MCP Inspectoru, což je aplikace s webovým grafickým uživatelským rozhraním, kterou lze použít pro otestování MCP serverů. Tato aplikace vyžaduje instalaci npm a node.js. Distribuce Linuxu většinou tyto nástroje mají ve svých repositářích balíčků (i když možná ne nejnovější verze, což nám ale nemusí vadit), takže jejich instalace je jednoduchá. Pro distribuce založené na RPM lze použít například:
$ dnf install npm $ dnf install nodejs
Pro jistotu si otestujeme, jestli jsou tyto nástroje skutečně nainstalovány:
$ npm --version 10.8.2 $ node --version v20.18.2
V případě, že se vypsaly verze (mohou být i nepatrně starší) jak npm, tak i node.js, mělo by být možné MCP Inspector použít.
5. Implementace jednoduchého MCP serveru s jediným definovaným zdrojem
Pokusme se nyní naprogramovat nějaký velmi jednoduchý MCP server, tj. službu, která dokáže komunikovat s klienty právě přes protokol MCP. V tomto serveru je definován pouze jediný zdroj (resource) nazvaný pozdrav, jenž je implementován pomocí běžné Pythonovské funkce s dekorátorem. Jedná se o zdroj, který vrací konstantní řetězec. Samotný zdroj je popsán dekorátorem @mcp.resource a typ hodnoty, kterou zdroj vrací, je odvozena na základě typové informace (jméno typu za šipkou definuje typ návratové hodnoty funkce):
"""Jednoduchý MCP server s jediným definovaným zdrojem."""
from mcp.server.fastmcp import FastMCP
# konstrukce serveru
mcp = FastMCP("Test")
@mcp.resource("pozdrav://")
def pozdrav() -> str:
"""Odpověď s pozdravem."""
return "Hello, dear client"
6. Spuštění serveru s jeho otestováním pomocí MCP Inspectoru
MCP server, jehož zdrojový kód jsme si ukázali v předchozí kapitole, je možné spustit několika možnými způsoby. Nejjednodušší je použití příkazu mcp, kterému se předá jméno zdrojového kódu s implementací serveru a popř. i způsob transportu (tedy konkrétního režimu komunikace mezi klientem a serverem). Ovšem my prozatím nemáme naprogramován žádný klient, takže je nutné využít nějaký jiný nástroj pro otestování našeho serveru. Takový nástroj ve skutečnosti existuje a jmenuje se MCP Inspector (ostatně jsme instalovali jeho závislosti). Pro otestování našeho serveru spustíme následující příkaz (ten očekává, že jméno zdrojového kódu s implementací MCP serveru je mcp_server1.py):
$ mcp dev mcp_server_1.py
Při prvním spuštění (později již ne) bude mcp potřebovat doinstalovat jeden npm balíček, takže se zeptá, zda může tuto operaci spustit. Odpovíme, že ano:
Need to install the following packages: @modelcontextprotocol/inspector@0.7.0 Ok to proceed? (y) y
Dále se vypíše informace o tom, na které adrese (včetně portu) je MCP inspektor dostupný:
Starting MCP inspector... Proxy server listening on port 3000 ? MCP Inspector is up and running at http://localhost:5173 ?
Ve webovém prohlížeči je nutné přejít na výše vypsanou adresu http://localhost:5173. Měla by se zobrazit stránka, která v levé části nabízí způsob připojení k serveru. Vyplňte nyní údaje tak, jak je to zobrazeno na screenshotu:
Obrázek 1: Takto může vypadat vyplněný dialog pro připojení k MCP serveru.
Po stisku tlačítka Connect by se měl server znovu spustit (to je poněkud nešťastné) a MCP Inspector by měl začít se serverem komunikovat přes standardní vstup a výstup (STDIO).
Dále v části Resources stiskněte tlačítko List Resources. Měl by se zobrazit seznam zdrojů, který obsahuje pouze jedinou položku „pozdrav://“, což je skutečně název našeho zdroje:
Obrázek 2: Seznam dostupných zdrojů, které MCP server nabízí.
Pokud tento zdroj vyberete, provede se komunikace s MCP serverem a měla by se vrátit odpověď ve formátu JSON:
{
"contents": [
{
"uri": "pozdrav://",
"mimeType": "text/plain",
"text": "Hello, dear client"
}
]
}
Tím jsme ověřili nejenom to, že MCP server nabízí příslušný zdroj, ale že je možné s ním komunikovat s využitím MCP.
7. MCP server s definovaným dynamickým zdrojem (resource template)
Druhá varianta MCP serveru, kterou vytvoříme, bude nepatrně složitější, protože je v ní definován dynamický zdroj (původní název resource template ovšem může být poněkud matoucí). Jedná se o zdroj, který má ve svém názvu měnitelnou část (selector) name. Výsledkem čtení tohoto zdroje bude řetězec ve formátu „Hello, {name}“, ve kterém se za „{name}“ doplní identifikátor ze jména zdroje. Teoreticky tedy můžeme tímto způsobem předávat MCP serveru parametry a získávat odpovědi na základě těchto parametrů, i když z hlediska sémantiky by bylo v mnoha případech vhodnější využít spíše nástroje (tools), s nimiž se seznámíme v dalších kapitolách. Povšimněte si, že nyní má funkce pozdrav s implementací zdroje parametr name, který je typu řetězec:
"""Jednoduchý MCP server s jediným definovaným dynamickým zdrojem."""
from mcp.server.fastmcp import FastMCP
# konstrukce serveru
mcp = FastMCP("Test")
@mcp.resource("pozdrav://{name}")
def pozdrav(name: str) -> str:
"""Odpověď s osobním pozdravem."""
return f"Hello, {name}"
8. Otestování nové varianty MCP serveru
Opět si otestujeme základní funkcionalitu druhé varianty našeho MCP serveru. Podobně jako v prvním případě i nyní spustíme MCP Inspector se zadáním jména zdrojového kódu s implementací serveru:
$ mcp dev mcp_server_2.py Starting MCP inspector... Proxy server listening on port 3000 ? MCP Inspector is up and running at http://localhost:5173 ?
Ve webovém prohlížeči otevřeme stránku na adrese http://localhost:5173. V prvním kroku se k MCP serveru připojíme, přičemž parametry připojení mohou vypadat následovně:
Obrázek 3: Parametry připojení k MCP serveru. Zelené kolečko u „Connected“ naznačuje, že MCP Inspector skutečně navázal připojení.
Nyní ovšem zdroj definovaný v MCP serveru nenalezneme v sekci „Resources“, ale v sekci nazvané „Resource Templates“. Vypíšeme si obsah těchto zdrojů s měnitelnou částí:
Obrázek 4: Seznam zdrojů se selektory.
Vybereme (jediný) takový zdroj a zadáme potřebný selektor:
Obrázek 5: Zápis selektoru a získání zdroje z MCP serveru. Povšimněte si, že se v popisu zdroje objevuje komentář z funkce, kterou je zdroj implementován.
Výsledkem by měla být následující datová struktura. Povšimněte si, že do ní bylo skutečně doplněno předané jméno (tedy onen selektor):
{
"contents": [
{
"uri": "pozdrav://Pavel",
"mimeType": "text/plain",
"text": "Hello, Pavel"
}
]
}
Obrázek 6: Odpověď MCP serveru zobrazená přímo v MCP Inspectoru.
9. Zdroj vracející strukturovaná data
Pokusme se nyní definovat poněkud složitější zdroj. Ten bude mít dva selektory, které jsou oddělené pomlčkou (ovšem stejně dobře je možné použít lomítko – RFC 6570 umožňuje více způsobů). Ovšem zajímavější je, že tento zdroj vrací pro zadané jméno a příjmení spisovatele seznam jeho knih, tj. v pojetí jazyka Python list. Můžete být ovšem i přesnější a definovat i seznam slovníků atd. – využívají se zde totiž standardizované typové informace (type hints). Pro jednoduchost není seznam knih získáván z databáze, ale pro jakéhokoli autora vrátíme stejný seznam, takže implementace MCP serveru by mohla vypadat takto:
"""Jednoduchý MCP server s jediným definovaným dynamickým zdrojem."""
from mcp.server.fastmcp import FastMCP
# konstrukce serveru
mcp = FastMCP("Test")
@mcp.resource("author://{name}-{surname}")
def author(name: str, surname: str) -> list:
"""Knihy od vybraného autora."""
return [
{"name": name, "surname": surname, "title": "Foo", "year": 1900},
{"name": name, "surname": surname, "title": "Bar", "year": 2005},
{"name": name, "surname": surname, "title": "Baz", "year": 2025},
]
Po připojení k MCP serveru se pokusíme získat seznam knih pro autora se jménem John a příjmením Doe. Povšimněte si, že samotná odpověď MCP serveru je sice ve formátu JSON, ovšem to se týká strukturování odpovědi tak, že dostaneme i různá metadata. Ovšem vlastní data jsou předána v parametru text ve formě slovníku serializovaného do řetězce:
{
"contents": [
{
"uri": "author://John-Doe",
"mimeType": "text/plain",
"text": "[{\"name\": \"John\", \"surname\": \"Doe\", \"title\": \"Foo\", \"year\": 1900}, {\"name\": \"John\", \"surname\": \"Doe\", \"title\": \"Bar\", \"year\": 2005}, {\"name\": \"John\", \"surname\": \"Doe\", \"title\": \"Baz\", \"year\": 2025}]"
}
]
}
Bude výsledek odlišný v případě, že zvolíme jiný formát odpovědi, tedy odlišný mimeType? To si můžeme snadno otestovat nepatrnou změnou MCP serveru:
"""Jednoduchý MCP server s jediným definovaným dynamickým zdrojem."""
from mcp.server.fastmcp import FastMCP
# konstrukce serveru
mcp = FastMCP("Test")
@mcp.resource("author://{name}-{surname}", mime_type="application/json")
def author(name: str, surname: str) -> list:
"""Knihy od vybraného autora."""
return [
{"name": name, "surname": surname, "title": "Foo", "year": 1900},
{"name": name, "surname": surname, "title": "Bar", "year": 2005},
{"name": name, "surname": surname, "title": "Baz", "year": 2025},
]
Odpověď je až na odlišný mimeType totožná s předchozí implementací:
{
"contents": [
{
"uri": "author://John-Doe",
"mimeType": "application/json",
"text": "[{\"name\": \"John\", \"surname\": \"Doe\", \"title\": \"Foo\", \"year\": 1900}, {\"name\": \"John\", \"surname\": \"Doe\", \"title\": \"Bar\", \"year\": 2005}, {\"name\": \"John\", \"surname\": \"Doe\", \"title\": \"Baz\", \"year\": 2025}]"
}
]
}
10. Server s definicí nástroje (tool)
Zdroje (resources) slouží resp. měly by sloužit jako zdroj dat a jejich chování by mělo být idempotentní. To znamená, že několikeré volání toho samého zdroje se stejnými selektory by mělo vracet totožné výsledky. Ovšem v oblasti AI a LLM je nutné volat resp. spouštět složitější operace, které provádí nějakou komplikovanější činnost a obecně nejsou idempotentní (třeba nákup letenky nebo rezervace schůzky do kalendáře). Pro tento účel slouží ve světě MCP takzvané nástroje (tool), které se začaly dosti bouřlivým způsobem vyvíjet v posledních cca deseti měsících – protože právě díky nástrojům se z původních „kecálků“ typu ChatGTP stávají reálně použitelná rozhraní mezi lidmi a IT systémy (více si řekneme v samostatném článku – toto je opravdu velmi dynamická oblast).
Vytvoření MCP serveru s nástrojem může být snadné. Abychom si to ukázali, naprogramujeme server, který má kromě zdroje definován i nástroj nazvaný factorial. Tento nástroj akceptuje celé číslo poslané v dotazu a odpoví hodnotou faktoriálu tohoto čísla. Pro jednoduchost jsem zvolil tu nejsnadnější implementaci výpočtu. Nejde nám totiž o vlastní výpočet, ale o realizaci komunikace přes MCP:
"""MCP server s dynamickým zdrojem a definicí nástroje (tool)."""
from mcp.server.fastmcp import FastMCP
# konstrukce serveru
mcp = FastMCP("Test")
@mcp.tool()
def factorial(n: int) -> int:
"""Výpočet faktoriálu ve smyčce."""
f = 1
for x in range(1, n + 1):
f *= x
return f
@mcp.resource("pozdrav://{name}")
def pozdrav(name: str) -> str:
"""Odpověď s osobním pozdravem."""
return f"Hello, {name}"
11. Otestování MCP serveru s voláním nástroje
Pro otestování nové varianty MCP serveru opět spustíme MCP Inspectora:
$ mcp dev mcp_server_3.py Starting MCP inspector... Proxy server listening on port 3000 ? MCP Inspector is up and running at http://localhost:5173 ?
Po otevření stránky s MCP Inspectorem a připojení k MCP serveru známým způsobem je nutné v horní části stránky se seznamem záložek vybrat záložku nazvanou Tools (ve výchozím stavu je vybrána záložka Resources, kterou jsme používali doposud). Ukáže se menu s položkou (tlačítkem) List Tools a po jeho stisku by se měl zobrazit název nástroje i jeho popis získaný z dokumentačního řetězce funkce, ve které je nástroj implementován:

Obrázek 7: Seznam nástrojů, které nabízí MCP server.
Nástroj vybereme a do zobrazeného dialogu zadáme vstupní parametr, který je poslán do nástroje, jenž na základě tohoto parametru (celé číslo) vypočítá faktoriál. Výsledek je vrácen formou jediné hodnoty, což znamená, že není nutné parsovat odpověď v JSONu:

Obrázek 8: Volání nástroje s předáním parametru. Vypočtená hodnota je
vrácena do MCP Inspectoru, kde je taktéž zobrazena.
12. Složitější MCP servery s několika zdroji a/nebo nástroji
Typicky bývá v MCP serveru realizováno větší množství zdrojů a popř. nástrojů. Ukažme si příklad, jak by mohla implementace takového serveru vypadat. Ve variantě MCP serveru zobrazené pod tímto odstavcem je definován nástroj pojmenovaný factorial, který známe. A dále se zde nachází definice dvou zdrojů, které oba začínají URI „pozdrav://“. Ovšem první z těchto zdrojů nemá selektor (URI je neměnné), zatímco u druhého zdroje je selektor specifikován a musí být použit při čtení tohoto zdroje:
"""MCP server se zdrojem, dynamickým zdrojem a definicí nástroje (tool)."""
from mcp.server.fastmcp import FastMCP
# konstrukce serveru
mcp = FastMCP("Test")
@mcp.tool()
def factorial(n: int) -> int:
"""Výpočet faktoriálu ve smyčce."""
f = 1
for x in range(1, n + 1):
f *= x
return f
@mcp.resource("pozdrav://")
def pozdrav1() -> str:
"""Odpověď s pozdravem."""
return "Hello, dear client"
@mcp.resource("pozdrav://{name}")
def pozdrav2(name: str) -> str:
"""Odpověď s osobním pozdravem."""
return f"Hello, {name}"
13. Jak MCP Inspector spouští MCP server?
V předchozích kapitolách jsme si řekli, že MCP Inspector se dokáže sám připojit k MCP serveru. Ovšem současně jsme tento MCP server spustili i přímo příkazem mcp. Bylo by tedy zajímavé zjistit, co se děje „pod pokličkou“. Naši implementaci MCP serveru tedy upravíme do takové podoby, aby se při každém jeho spuštění vytvořil soubor, jehož jméno bude odpovídat číslu procesu. Tak zjistíme, kolikrát se vlastně MCP inicializoval. Poněkud naivní implementace tohoto MCP serveru může vypadat následovně:
"""Jednoduchý MCP server s jediným definovaným dynamickým zdrojem."""
import os
from mcp.server.fastmcp import FastMCP
from datetime import datetime
# konstrukce serveru
mcp = FastMCP("Test")
pid = os.getpid()
# při každém spuštění serveru se vytvoří nový soubor
with open(f"{pid}.txt", "w") as fout:
fout.write(str(datetime.now()))
@mcp.resource("pozdrav://")
def pozdrav() -> str:
"""Odpověď s pozdravem."""
return "Hello, dear client"
Opět provedeme spuštění MCP Inspectoru:
$ mcp dev mcp_server_4.py
Výsledek bude jediný soubor s PID, který značí, že server byl spuštěn, i když jsme se k němu nepřipojili:
3288345.txt
Nyní soubor smažeme, opět spustíme MCP Inspector a připojíme se k serveru (connect). Výsledkem bude dvojice souborů, protože byl server spuštěný dvakrát:
3288553.txt 3288667.txt
Ovšem získání (resp. přesněji řečeno přečtení) jednotlivých zdrojů atd. již nevede ke spuštění nového MCP serveru – vše je obslouženo jediným serverem, který bude běžet až do té doby, dokud nedojde ke vzniku nezachycené výjimky nebo pokud server explicitně neukončíme (Ctrl+C).
14. Přímé spuštění MCP serveru z příkazové řádky s transportem STDIO
Spouštění MCP serveru nepřímo přes nástroj MCP Inspector je vhodné provádět jen v průběhu jeho vývoje a ladění. Ovšem nasazení takového serveru může vypadat odlišně. V tom nejjednodušším případě můžeme server spustit přímo z interpretru Pythonu, tj. bez použití příkazu mcp. Do zdrojového kódu MCP serveru je v takovém případě pouze nutné přidat programové řádky, které ho spustí. V základní podobě se MCP serveru spustí s volbou transportu STDIO, tj. se serverem se bude komunikovat přes vstupně-výstupní proudy (standard input stream, standard output stream):
# přímé spuštění serveru v režimu STDIO
if __name__ == "__main__":
mcp.run()
Celý zdrojový kód serveru se změní jen nepatrně – přidali jsme do něj tři výše uvedené řádky zajišťující jeho spuštění:
"""MCP server se zdrojem, dynamickým zdrojem a definicí nástroje (tool)."""
from mcp.server.fastmcp import FastMCP
# konstrukce serveru
mcp = FastMCP("Test")
@mcp.tool()
def factorial(n: int) -> int:
"""Výpočet faktoriálu ve smyčce."""
f = 1
for x in range(1, n + 1):
f *= x
return f
@mcp.resource("pozdrav://")
def pozdrav1() -> str:
"""Odpověď s pozdravem."""
return "Hello, dear client"
@mcp.resource("pozdrav://{name}")
def pozdrav2(name: str) -> str:
"""Odpověď s osobním pozdravem."""
return f"Hello, {name}"
# přímé spuštění serveru v režimu STDIO
if __name__ == "__main__":
mcp.run()
15. Přímé spuštění MCP serveru z příkazové řádky s transportem SSE
Druhým podporovaným transportem je SSE neboli Server Sent Events. V tomto případě MCP server naslouchá na IP portu a odpovídá na dotazy MCP klientů popř. přímo sám posílá události těmto klientům (nejedná se tedy o triviální komunikaci typu dotaz-odpověď, ale o komunikaci oboustrannou). Aby se MCP server spustil v režimu transportu SSE, je možné ho spustit tímto způsobem:
# přímé spuštění serveru v režimu SSE (Server-Sent Events)
if __name__ == "__main__":
mcp.run(transport="sse")
Upravený zdrojový kód MCP serveru vypadá takto:
"""MCP server se zdrojem, dynamickým zdrojem a definicí nástroje (tool)."""
from mcp.server.fastmcp import FastMCP
# konstrukce serveru
mcp = FastMCP("Test")
@mcp.tool()
def factorial(n: int) -> int:
"""Výpočet faktoriálu ve smyčce."""
f = 1
for x in range(1, n + 1):
f *= x
return f
@mcp.resource("pozdrav://")
def pozdrav1() -> str:
"""Odpověď s pozdravem."""
return "Hello, dear client"
@mcp.resource("pozdrav://{name}")
def pozdrav2(name: str) -> str:
"""Odpověď s osobním pozdravem."""
return f"Hello, {name}"
# přímé spuštění serveru v režimu SSE (Server-Sent Events)
if __name__ == "__main__":
mcp.run(transport="sse")
I bez podrobnějších znalostí protokolu, který se pro komunikaci mezi MCP serverem a MCP klientem používá, si můžeme ověřit alespoň to, že server je skutečně spuštěn a naslouchá na portu. Server tedy spustíme:
$ python mcp_server_6.py
Výchozím portem je 8000, takže v dalším terminálu provedeme připojení k serveru s využitím (například) známého nástroje curl:
$ curl localhost:8000/sse
MCP server by měl odpovědět tak, že pošle zpátky ID sezení (session), které lze použít pro další komunikaci:
event: endpoint data: /messages/?session_id=e233fdbae32d43b29c1d3fd9b58d9145
Navíc není spojení ihned ukončeno (připomínám: nejedná se o klasický systém dotaz-odpověď). Pokud nástroj curl neukončíme, bude patrné, že server po určitých časových intervalech cca 15 sekund posílá zprávy ping, aby si ověřil připojení klienta:
: ping - 2025-03-30 09:26:17.741441+00:00 : ping - 2025-03-30 09:26:32.743079+00:00 : ping - 2025-03-30 09:26:47.744446+00:00 : ping - 2025-03-30 09:27:02.746181+00:00
16. MCP klient spouštějící server s komunikací přes STDIO
V této chvíli již máme základní znalosti o tom, jakým způsobem se implementují MCP servery. Ovšem s těmito servery jsme komunikovali buď přes MCP Inspectora nebo z příkazové řádky (viz výše uvedený příkaz curl). V praxi se ovšem k MCP serverům takřka výhradně připojují další naprogramované služby a nástroje – MCP klienti. Ukažme si tedy, jak by mohl vypadat jednoduchý MCP klient, který po svém spuštění vypíše seznam zdrojů, nástrojů atd. nabízených MCP serverem. Přímo v kódu klienta nebudeme se serverem komunikovat na úrovni MCP protokolu, ale na vyšší úrovni abstrakce (ostatně ani v implementaci serveru jsme s protokolem přímo nepřišli do styku). MCP klient navíc server sám spustí v režimu používajícím transport STDIO:
# parametry pro spuštění MCP serveru
server_params = StdioServerParameters(
command="python", # spustí se tento příkaz
args=["mcp_server_5.py"], # a předají se mu následující parametry
env=None, # lze definovat i proměnné prostředí
)
Jak je ze zdrojového kódu klienta patrné, je realizován takovým způsobem, že veškeré operace jsou v něm prováděny asynchronně (v Pythonu se pro tento účel používají klíčová slova async a await). Klient získá seznamy všech zdrojů atd. a vypíše je na standardní výstup:
"""MCP klient, který spustí server, se kterým se komunikuje přes STDIO."""
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
# parametry pro spuštění MCP serveru
server_params = StdioServerParameters(
command="python", # spustí se tento příkaz
args=["mcp_server_5.py"], # a předají se mu následující parametry
env=None, # lze definovat i proměnné prostředí
)
async def run():
"""Realizace klienta."""
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
prompts = await session.list_prompts()
print(prompts)
resources = await session.list_resources()
print(resources)
templates = await session.list_resource_templates()
print(templates)
tools = await session.list_tools()
print(tools)
# přímé spuštění klienta
if __name__ == "__main__":
import asyncio
asyncio.run(run())
Tento klient spustíme:
$ python mcp_client_1.py
Klient po své inicializaci sám automaticky spustí příslušný MCP server (má k tomu všechny potřebné informace), získá seznam dostupných zdrojů atd. a ten vytiskne. Poté je ukončen.
Vypsané informace by měly vypadat přesně takto:
meta=None nextCursor=None prompts=[]
meta=None nextCursor=None resources=[Resource(uri=AnyUrl('pozdrav://'), name='pozdrav://', description=None, mimeType='text/plain', size=None, annotations=None)]
meta=None nextCursor=None resourceTemplates=[ResourceTemplate(uriTemplate='pozdrav://{name}', name='pozdrav2', description='Odpověď s osobním pozdravem.', mimeType=None, annotations=None)]
meta=None nextCursor=None tools=[Tool(name='factorial', description='Výpočet faktoriálu ve smyčce.', inputSchema={'properties': {'n': {'title': 'N', 'type': 'integer'}}, 'required': ['n'], 'title': 'factorialArguments', 'type': 'object'})]
17. MCP klient s komunikací přes SSE
Druhou možností komunikace mezi MCP serverem a MCP klientem je využití transportu SSE (Server-side events). V tomto případě je možné MCP server inicializovat ještě před spuštěním klienta. Jak se tato inicializace provede jsme si ukázali v patnácté kapitole. Předpokládejme tedy, že MCP server je inicializován a naslouchá na portu 8000. Klient se může k takovému serveru připojit způsobem, který lze naprogramovat následovně:
"""MCP klient, který spustí server, se kterým se komunikuje přes SSE."""
from mcp import ClientSession
from mcp.client.sse import sse_client
async def run():
"""Realizace klienta."""
async with sse_client(url="http://localhost:8000/sse") as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
prompts = await session.list_prompts()
print(prompts)
resources = await session.list_resources()
print(resources)
templates = await session.list_resource_templates()
print(templates)
tools = await session.list_tools()
print(tools)
# přímé spuštění klienta
if __name__ == "__main__":
import asyncio
asyncio.run(run())
Od klienta z předchozí kapitoly se tedy tento klient odlišuje pouze odlišným kontextem:
async with sse_client(url="http://localhost:8000/sse") as (read, write):
18. Klient, který přečte zdroj z MCP serveru
MCP klienti dokážou číst jakékoli zdroje nabízené MCP serverem, volat v nich definované nástroje (tools) atd. V dnešním posledním demonstračním příkladu si ukážeme způsob přečtení zdroje (pro jednoduchost prozatím zdroje bez selektoru). Komunikace s MCP serverem bude opět probíhat asynchronně:
"""MCP klient, který přečte zvolený zdroj."""
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
# parametry pro spuštění MCP serveru
server_params = StdioServerParameters(
command="python", # spustí se tento příkaz
args=["mcp_server_5.py"], # a předají se mu následující parametry
env=None, # lze definovat i proměnné prostředí
)
async def run():
"""Realizace klienta."""
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
# přečtení zdroje
data = await session.read_resource("pozdrav://")
print("Data returned:", data)
print("Type:", type(data))
# přímé spuštění klienta
if __name__ == "__main__":
import asyncio
asyncio.run(run())
Po spuštění tohoto klienta se inicializuje MCP server, provede se přečtení zdroje pozdrav:// a následně dojde k ukončení činnosti jak serveru, tak posléze i klienta:
Data returned: meta=None contents=[TextResourceContents(uri=AnyUrl('pozdrav://'), mimeType='text/plain', text='Hello, dear client')]
Type: <class 'mcp.types.ReadResourceResult'>
19. Repositář s demonstračními příklady
Zdrojové kódy všech prozatím popsaných demonstračních příkladů určených pro Python a balíček mcp byly uloženy do repositáře dostupného na adrese https://github.com/tisnik/most-popular-python-libs. V tabulce zobrazené níže jsou odkazy na jednotlivé příklady:
20. Odkazy na Internetu
- MCP Python SDK
https://github.com/modelcontextprotocol/python-sdk?tab=readme-ov-file#running-your-server - MCP protocol: Resources
https://modelcontextprotocol.info/docs/concepts/resources/ - Example Servers
https://modelcontextprotocol.io/examples - Core architecture
https://modelcontextprotocol.io/docs/concepts/architecture - Unleashing the Power of Model Context Protocol (MCP): A Game-Changer in AI Integration
https://techcommunity.microsoft.com/blog/educatordeveloperblog/unleashing-the-power-of-model-context-protocol-mcp-a-game-changer-in-ai-integrat/4397564 - MPC inspector
https://github.com/modelcontextprotocol/inspector - Model Context Protocol servers
https://github.com/modelcontextprotocol/servers - python-sdk na GitHubu
https://github.com/modelcontextprotocol/python-sdk - typescript-sdk na GitHubu
https://github.com/modelcontextprotocol/typescript-sdk - mcp-golang
https://github.com/metoro-io/mcp-golang - MCP server: A step-by-step guide to building from scratch
https://composio.dev/blog/mcp-server-step-by-step-guide-to-building-from-scrtch/ - How to Build an MCP Server Fast: A Step-by-Step Tutorial
https://medium.com/@eugenesh4work/how-to-build-an-mcp-server-fast-a-step-by-step-tutorial-e09faa5f7e3b - Step-by-Step Guide: Building an MCP Server using Python-SDK, AlphaVantage & Claude AI
https://medium.com/@syed_hasan/step-by-step-guide-building-an-mcp-server-using-python-sdk-alphavantage-claude-ai-7a2bfb0c3096 - RFC 6570: URI Template
https://datatracker.ietf.org/doc/html/rfc6570 - Return resources as structured JSON instead of text?
https://github.com/modelcontextprotocol/python-sdk/issues/279 - Python standard library: pprint
https://docs.python.org/3/library/pprint.html - Python standard library: json — JSON encoder and decoder¶
https://docs.python.org/3/library/json.html - Calling MCP Servers the Hard Way
https://deadprogrammersociety.com/2025/03/calling-mcp-servers-the-hard-way.html - mcptools
https://github.com/f/mcptools - Server-sent events
https://en.wikipedia.org/wiki/Server-sent_events