Disclaimer. SubiektMCP to niezależny wdrożeniowiec wykorzystujący MCP do połączenia Subiekta GT z Claude’em. Nie jesteśmy partnerem Anthropic (twórcy MCP) ani InsERT (twórcy Subiekta GT). Wszystkie integracje opisane w tym tutorialu są niezależne i otwarte.
Klient pyta: “Podłącz mi proszę Claude’a do naszej bazy zamówień, niech mogę pytać po polsku”. Stary stack to 2-4 tygodnie pracy: custom REST API, prompt engineering, prompt injection ochrony, integracja LLM SDK, fallbacki. Z MCP to 2-3 godziny od zera. Ten tutorial pokazuje krok po kroku jak to zrobić w Pythonie, z testowanymi code samples.
Co zbudujemy: działający MCP server z 3 narzędziami (top klienci, marża produktu, wartość magazynu), połączony z SQLite jako sample dataset, wpięty w Claude Desktop. Wszystko lokalnie, bez chmury, bez kosztów infrastruktury. Po przeczytaniu i zrobieniu kroków będziesz mieć working server gotowy do pokazania klientowi.
Wymagania: Python 3.10 lub nowszy, podstawowa znajomość pip i wirtualnych środowisk, dowolna baza danych klienta (w tutorialu używamy SQLite, w realu może to być MSSQL, PostgreSQL, MySQL - wymiana 1 linijki). Czas: 60-90 minut na przejście całego tutoriala, plus 30 min na test z Claude Desktop. Audience: programista / IT freelancer / integrator który ma już klientów MŚP i chce dodać “AI integration” do oferty.
Co to jest MCP w 1 minute (dla devs)
MCP (Model Context Protocol) to otwarty standard opublikowany przez Anthropic w listopadzie 2024. Definiuje jak LLM (Claude, ChatGPT z Codex, Gemini przez bridge) rozmawia z lokalnymi narzędziami i danymi. Architektura trzyelementowa:
- Host: aplikacja końcowa użytkownika (Claude Desktop, ChatGPT Codex, custom apka)
- Client: biblioteka wbudowana w host, mówi protokołem MCP
- Server: Twój kod, wystawia tools i resources dla LLM
Transport domyślny to stdio (server jako lokalny proces, host go uruchamia i komunikuje przez stdin/stdout). Dla remote setupów istnieje też SSE (Server-Sent Events przez HTTP). W tym tutorialu używamy stdio bo to default i nie wymaga deployment na serwer.
Capabilities które server wystawia: - Tools: funkcje które LLM może wywołać (np. “pobierz top klientów”, “policz marżę”) - Resources: dane statyczne lub dynamiczne dostępne dla kontekstu (np. lista plików, schema bazy) - Prompts: predefiniowane templates promptów dla użytkownika
Dla większości use case’ów MŚP wystarczą same tools. To zbudujemy w tym tutorialu. Jeśli chcesz głębszego kontekstu o MCP bez kodu, zobacz MCP po polsku dla MŚP. Tu lecimy code-first.
Setup environment
Cały tutorial używa mcp[cli] packagu z PyPI, oficjalnego SDK od Anthropic dla Pythona. Repo: github.com/modelcontextprotocol/python-sdk.
Python 3.10+ i wirtualne środowisko
Sprawdź wersję Pythona:
python3 --version
# Wymagane: 3.10 lub nowszy
Jeśli masz starszy, zainstaluj nowszego (na macOS przez Homebrew: brew install python@3.12, na Ubuntu: sudo apt install python3.12, na Windows: ze python.org).
Stwórz katalog projektu i venv:
mkdir mcp-tutorial
cd mcp-tutorial
python3 -m venv venv
source venv/bin/activate # macOS / Linux
# venv\Scripts\activate # Windows PowerShell
Zainstaluj SDK:
pip install "mcp[cli]"
Pakiet mcp[cli] instaluje sam SDK plus narzędzie mcp CLI do testowania i debugowania (przyda się w sekcji Testing).
Verify install
Szybki test że wszystko działa:
python -c "from mcp.server.fastmcp import FastMCP; print('OK')"
# Expected output: OK
Plus check CLI:
mcp --version
# Expected: mcp version 1.x.x (lub nowszy)
Jeśli oba zwracają OK, gotowe. Idziemy do pierwszego servera.
Pierwszy MCP server. Hello world (10 linii kodu)
FastMCP to wysokopoziomowy interface w SDK, zbliżony filozofią do FastAPI. Mniej boilerplate niż low-level Server class. Dla 95% tutoriali i większości production servers FastMCP wystarczy.
Stwórz plik hello_server.py:
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("hello-world")
@mcp.tool()
def hello(name: str = "świecie") -> str:
"""Zwraca powitanie po polsku."""
return f"Witaj, {name}!"
if __name__ == "__main__":
mcp.run()
Co tu się dzieje:
FastMCP("hello-world"): tworzy server instance z identyfikatorem (widoczny w Claude Desktop jako nazwa konektora)@mcp.tool(): decorator który eksportuje funkcję jako MCP tool dostępny dla LLM- Type hints (
name: str,-> str): FastMCP używa ich do auto-generowania JSON Schema dla LLM (LLM widzi że funkcja przyjmuje string i zwraca string) - Docstring: pierwsza linia docstringu staje się opisem toola który widzi LLM (KRYTYCZNE dla dobrego tool selection przez modele)
mcp.run(): uruchamia server w stdio mode (czeka na connection od host)
Test bezpośredni przez mcp dev:
mcp dev hello_server.py
Output:
MCP Inspector running at http://localhost:5173
Connected to hello-world server
Otwórz http://localhost:5173 w przeglądarce. Zobaczysz Inspector UI gdzie możesz wywołać hello tool, zobaczyć schemę argumentów i odpowiedź. To debug environment, równoważnik Postmana dla MCP.
Zrób Ctrl+C aby zatrzymać i przejdź dalej.
Dodanie tool z parametrami i walidacją
Hello world to zabawa. W produkcji chcesz tools z parametrami, walidacją wejść i obsługą błędów. Stwórz plik sklep_server.py:
from mcp.server.fastmcp import FastMCP
from pydantic import Field
from typing import Annotated
mcp = FastMCP("sklep-demo")
@mcp.tool()
def licz_marze(
cena_sprzedazy: Annotated[float, Field(description="Cena netto sprzedaży w PLN")],
cena_zakupu: Annotated[float, Field(description="Cena netto zakupu w PLN")],
) -> dict:
"""Liczy marżę kwotową i procentową dla pojedynczego SKU."""
if cena_zakupu <= 0:
return {"error": "Cena zakupu musi być większa od zera"}
if cena_sprzedazy <= 0:
return {"error": "Cena sprzedaży musi być większa od zera"}
marza_kwotowa = cena_sprzedazy - cena_zakupu
marza_procentowa = (marza_kwotowa / cena_sprzedazy) * 100
return {
"cena_sprzedazy_pln": round(cena_sprzedazy, 2),
"cena_zakupu_pln": round(cena_zakupu, 2),
"marza_kwotowa_pln": round(marza_kwotowa, 2),
"marza_procentowa": round(marza_procentowa, 2),
}
if __name__ == "__main__":
mcp.run()
Trzy ważne wzorce:
1. Annotated[type, Field(description=...)] zamiast samego type. To daje LLM dokładniejszy schema - opis każdego parametru, jednostkę, format. Bez tego LLM zgaduje co jest cena_sprzedazy_a co cena_zakupu, czasem trafia, czasem nie. Z opisem trafia zawsze.
2. Walidacja przed obliczeniem. Sprawdzamy czy ceny > 0, zwracamy {"error": "..."} przy invalid input. LLM widzi error message i może albo poprawić, albo zwrócić użytkownikowi pytanie (“Cena zakupu wydaje się być 0, czy to literówka?”).
3. Return jako dict z konkretnymi kluczami. LLM lepiej formatuje odpowiedź gdy zwracasz strukturalny dict niż string. User dostaje czytelny output (“Marża 23.5%, kwotowo 142.30 zł”) zamiast surowych liczb.
Test ponownie:
mcp dev sklep_server.py
W Inspector wywołaj licz_marze(cena_sprzedazy=199.99, cena_zakupu=130.00). Powinieneś dostać dict z marzą 34.99%.
Ctrl+C, idziemy dalej.
Integracja z bazą danych. SQLite jako sample
Teraz konkret biznesowy. Stwórz sample bazę SQLite z 3 tabelami (klienci, produkty, faktury), załaduj dane, dodaj 3 tools które ją odpytują.
Setup sample bazy
Plik setup_db.py (uruchamiany raz):
import sqlite3
conn = sqlite3.connect("sklep.db")
cur = conn.cursor()
cur.executescript("""
DROP TABLE IF EXISTS faktury;
DROP TABLE IF EXISTS produkty;
DROP TABLE IF EXISTS klienci;
CREATE TABLE klienci (
id INTEGER PRIMARY KEY,
nazwa TEXT NOT NULL,
nip TEXT,
miasto TEXT
);
CREATE TABLE produkty (
id INTEGER PRIMARY KEY,
nazwa TEXT NOT NULL,
cena_zakupu REAL NOT NULL,
cena_sprzedazy REAL NOT NULL,
stan_magazynowy INTEGER NOT NULL
);
CREATE TABLE faktury (
id INTEGER PRIMARY KEY,
klient_id INTEGER NOT NULL,
produkt_id INTEGER NOT NULL,
ilosc INTEGER NOT NULL,
wartosc_netto REAL NOT NULL,
data_wystawienia TEXT NOT NULL,
FOREIGN KEY (klient_id) REFERENCES klienci(id),
FOREIGN KEY (produkt_id) REFERENCES produkty(id)
);
""")
# Sample data
klienci = [
(1, "Hurtownia Kowalski", "1234567890", "Lublin"),
(2, "Sklep Nowak", "9876543210", "Kraków"),
(3, "Warsztat Wiśniewski", "5555555555", "Warszawa"),
]
produkty = [
(1, "Klocki hamulcowe Bosch", 45.00, 89.00, 120),
(2, "Olej Mobil 5W30", 32.00, 65.00, 80),
(3, "Filtr powietrza Mann", 28.00, 55.00, 200),
]
faktury = [
(1, 1, 1, 10, 890.00, "2026-05-01"),
(2, 1, 2, 5, 325.00, "2026-05-03"),
(3, 2, 1, 3, 267.00, "2026-05-07"),
(4, 3, 3, 8, 440.00, "2026-05-10"),
(5, 1, 3, 12, 660.00, "2026-05-15"),
]
cur.executemany("INSERT INTO klienci VALUES (?, ?, ?, ?)", klienci)
cur.executemany("INSERT INTO produkty VALUES (?, ?, ?, ?, ?)", produkty)
cur.executemany("INSERT INTO faktury VALUES (?, ?, ?, ?, ?, ?)", faktury)
conn.commit()
conn.close()
print("Setup done. sklep.db created with 3 klientow, 3 produktow, 5 faktur.")
Uruchom raz:
python setup_db.py
# Expected: Setup done. sklep.db created with 3 klientow, 3 produktow, 5 faktur.
Plik sklep.db powinien się pojawić w katalogu projektu.
MCP server z bazą
Plik sklep_db_server.py:
import sqlite3
from pathlib import Path
from mcp.server.fastmcp import FastMCP
from pydantic import Field
from typing import Annotated
mcp = FastMCP("sklep-db")
DB_PATH = Path(__file__).parent / "sklep.db"
def _connect():
"""Otwiera connection do SQLite z row_factory dla łatwego dict access."""
conn = sqlite3.connect(DB_PATH)
conn.row_factory = sqlite3.Row
return conn
@mcp.tool()
def top_klienci(
limit: Annotated[int, Field(description="Ilu klientów zwrócić", ge=1, le=100)] = 10,
) -> list[dict]:
"""Zwraca top N klientów posortowanych po sumie zakupów (faktury netto)."""
with _connect() as conn:
rows = conn.execute(
"""
SELECT k.nazwa AS klient, k.miasto, SUM(f.wartosc_netto) AS suma_pln
FROM klienci k
JOIN faktury f ON f.klient_id = k.id
GROUP BY k.id
ORDER BY suma_pln DESC
LIMIT ?
""",
(limit,),
).fetchall()
return [
{"klient": r["klient"], "miasto": r["miasto"], "suma_pln": round(r["suma_pln"], 2)}
for r in rows
]
@mcp.tool()
def marza_produktu(
produkt_id: Annotated[int, Field(description="ID produktu z bazy", ge=1)],
) -> dict:
"""Liczy marżę produktu z bazy. Zwraca błąd jeśli produkt nie istnieje."""
with _connect() as conn:
row = conn.execute(
"SELECT nazwa, cena_zakupu, cena_sprzedazy, stan_magazynowy FROM produkty WHERE id = ?",
(produkt_id,),
).fetchone()
if row is None:
return {"error": f"Produkt o id {produkt_id} nie istnieje"}
marza_kwotowa = row["cena_sprzedazy"] - row["cena_zakupu"]
marza_procentowa = (marza_kwotowa / row["cena_sprzedazy"]) * 100
return {
"produkt": row["nazwa"],
"cena_zakupu_pln": row["cena_zakupu"],
"cena_sprzedazy_pln": row["cena_sprzedazy"],
"marza_kwotowa_pln": round(marza_kwotowa, 2),
"marza_procentowa": round(marza_procentowa, 2),
"stan_magazynowy_szt": row["stan_magazynowy"],
}
@mcp.tool()
def wartosc_magazynu() -> dict:
"""Sumuje wartość magazynu wg cen zakupu i wg cen sprzedaży."""
with _connect() as conn:
row = conn.execute(
"""
SELECT
SUM(stan_magazynowy * cena_zakupu) AS wartosc_zakupowa,
SUM(stan_magazynowy * cena_sprzedazy) AS wartosc_sprzedazowa,
COUNT(*) AS liczba_sku
FROM produkty
"""
).fetchone()
return {
"wartosc_zakupowa_pln": round(row["wartosc_zakupowa"], 2),
"wartosc_sprzedazowa_pln": round(row["wartosc_sprzedazowa"], 2),
"potencjalna_marza_pln": round(row["wartosc_sprzedazowa"] - row["wartosc_zakupowa"], 2),
"liczba_sku": row["liczba_sku"],
}
if __name__ == "__main__":
mcp.run()
Trzy tools, każdy odpytuje SQLite, zwraca dict ze strukturalnymi danymi. Wzorce do zauważenia:
with _connect() as conn: używa context managera - connection auto-close, transactional safety, brak resource leak.
conn.row_factory = sqlite3.Row umożliwia dostęp do kolumn po nazwie (r["klient"]) zamiast po indeksie (r[0]). Czyściej, mniej bug-prone.
Field(ge=1, le=100) validates że limit jest w sensownym zakresie. Bez tego LLM mógłby wysłać limit=99999999 i robić DOS na bazie.
Każdy tool zwraca dict z konkretnymi kluczami nazwowymi. LLM lepiej formatuje natural language response z named fields niż z tuple lub raw rows.
Test:
mcp dev sklep_db_server.py
W Inspector wywołaj:
- top_klienci(limit=3) → powinno zwrócić Hurtownia Kowalski na #1 (1875 zł)
- marza_produktu(produkt_id=1) → Klocki Bosch, marża 49.4%
- wartosc_magazynu() → ~17 740 zł zakupowa, ~30 540 zł sprzedażowa
Działa, idziemy do integracji z Claude Desktop.
Testing i integracja z Claude Desktop
Test bezpośredni (mcp dev)
Już wiesz: mcp dev sklep_db_server.py uruchamia Inspector. Tam testujesz tools bezpośrednio, sprawdzasz schemy, debug’ujesz błędy. To Twoje primary dev environment, nie Claude Desktop.
Konfiguracja Claude Desktop
Claude Desktop czyta config z pliku JSON. Lokalizacja:
- macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
- Windows: %APPDATA%\Claude\claude_desktop_config.json
- Linux: ~/.config/Claude/claude_desktop_config.json
Jeśli plik nie istnieje, stwórz go. Zawartość:
{
"mcpServers": {
"sklep-db": {
"command": "/absolutna/sciezka/do/venv/bin/python",
"args": ["/absolutna/sciezka/do/sklep_db_server.py"]
}
}
}
WAŻNE: command MUSI być absolutną ścieżką do Pythona w Twoim venv (nie samym python). Inaczej Claude Desktop nie znajdzie SDK. Sprawdź ścieżkę:
which python
# /Users/twoj/mcp-tutorial/venv/bin/python
Skopiuj output do command. Analogicznie absolutna ścieżka do sklep_db_server.py w args.
Po zapisaniu configu zrestartuj Claude Desktop (Cmd+Q / Alt+F4 i odpal ponownie). Po restarcie w lewym dolnym rogu okna chatu pojawia się ikonka narzędzi (mała “wtyczka”) - kliknij, powinno tam być sklep-db z 3 tools widocznymi.
Pierwsza konwersacja
W Claude Desktop wpisz:
Pokaż top 5 klientów z bazy sklepu.
Claude wywoła top_klienci(limit=5), dostanie dict, zformatuje po polsku:
Top klientów w bazie:
- Hurtownia Kowalski (Lublin): 1 875,00 zł
- Warsztat Wiśniewski (Warszawa): 440,00 zł
- Sklep Nowak (Kraków): 267,00 zł
Hurtownia Kowalski dominuje z prawie 80% sumy zakupów. Pozostali klienci znacznie poniżej.
Działa. Spróbuj kolejne pytania:
Jaka jest marża na klockach hamulcowych Bosch?
Ile jest warty magazyn?
Czy klocki Bosch mają wyższą marżę niż olej Mobil?
Claude rozumie polski, mapuje na tools, składa odpowiedź. Bez pisania ani jednego dodatkowego promptu.
Debug gdy nie działa
Najczęstsze problemy i fixy:
| Problem | Symptom | Fix |
|---|---|---|
| Server nie startuje | Claude Desktop nie pokazuje tools | Sprawdź absolutną ścieżkę do Pythona w configu |
| ModuleNotFoundError | “No module named ‘mcp’” w logach | Aktywuj venv przed install, użyj venv Pythona w command |
| Tool widoczny ale nie wywoływany | Claude pyta o coś związanego, ale nie odpala toola | Zmień docstring na bardziej opisowy, dodaj przykłady użycia |
| Błąd SQL przy każdym wywołaniu | “no such table” | Sprawdź ścieżkę DB w DB_PATH, uruchom setup_db.py |
Logi Claude Desktop:
- macOS: ~/Library/Logs/Claude/
- Windows: %APPDATA%\Claude\logs\
Znajdź mcp-*.log files, tam są stderr Twojego servera. Mocne źródło debugu.
Deployment. Jak udostępnić klientowi
Tutorial tutorial, ale Twoi klienci nie będą sami uruchamiać pip install ani edytować JSON config. Musisz zapakować i zautomatyzować.
Opcja A: PyInstaller jednoplikowy exe
Dla klientów na Windows (większość polskich MŚP z Subiektem GT). Zamienia server.py + venv w pojedynczy .exe:
pip install pyinstaller
pyinstaller --onefile --name sklep-mcp-server sklep_db_server.py
Output w dist/sklep-mcp-server.exe. Klient kopiuje plik + dane (sklep.db), edytuje config Claude Desktop podając ścieżkę do .exe. Bez Pythona po stronie klienta.
Caveat: PyInstaller bundle waży 30-80 MB (cały Python runtime). Akceptowalne.
Opcja B: Installer Inno Setup (Windows)
Bardziej production-ready: Inno Setup tworzy .exe installer który:
- Kopiuje server + zależności do Program Files
- Edytuje config Claude Desktop automatycznie
- Dodaje shortcut do “uninstall”
- Wersjonuje (klient widzi w “Programy i funkcje”)
Tutorial Inno Setup jest poza scope tego artykułu, ale Anthropic ma oficjalny guide plus przykłady w MCP repo. Liczę 2-3h na pierwszy installer, potem template.
Opcja C: Subiekt-specific case study
Dla integracji z Subiektem GT istnieje gotowy pattern. Konkretny tutorial step-by-step (z pyodbc + Sybase / SQL Server connection do bazy Subiekta) opisałem w osobnym artykule: Własny MCP server dla Subiekta w 2h. Tam też reference dla wyceny (Tier B-C, 2-10 tys zł netto).
Versioning i update
Każdy server powinien mieć __version__ w kodzie + check przy starcie. Update strategy:
__version__ = "1.0.0"
@mcp.tool()
def get_version() -> str:
"""Zwraca wersję servera."""
return __version__
Klient pyta “jaką wersję serwera mam” → Claude wywołuje tool → odpowiada. Ty wiesz że klient ma 1.0.0, masz 1.2.0, czas pushnąć update. Manual lub przez auto-updater w installerze.
Co MCP daje, czego REST nie
Częste pytanie od devs: “po co MCP skoro mam REST API?”. Trzy konkretne różnice:
1. Native LLM tool use, zero prompt engineering. Z REST musisz pisać prompt który mówi LLM “masz endpoint /api/customers, parametry limit int max 100, format response JSON, błędy w polu error”. Z MCP zero promptu - type hints + docstring automatycznie generują schema, LLM widzi i używa.
2. Standardowy schema discovery. LLM nie wie a priori jakie tools są dostępne. Przy REST musisz wysłać OpenAPI spec w system prompt (zużywa tokens, czasem mylą się parametry). Z MCP host pyta server “co umiesz”, server odpowiada strukturalnie, LLM się uczy bez user input.
3. Bezpieczeństwo lokalne by default. REST API zwykle siedzi na serwerze publicznym (https://api.firma.pl). Trzeba auth, rate limiting, monitoring, RODO compliance. MCP stdio default to lokalny proces - dane nigdy nie opuszczają maszyny, brak network surface, brak public endpoint.
Komplementarność: REST dla service-to-service (Twój sklep ↔ kurier), MCP dla LLM-to-service (user przez Claude ↔ Twoja baza). Pełniejsza analiza w MCP vs REST. Co wybrać dla integracji ERP.
Następne kroki
Masz działający MCP server z SQLite. Co dalej:
Connector do realnej bazy klienta. Wymień sqlite3 na pyodbc (MSSQL / Sybase Subiekta GT), psycopg2 (PostgreSQL), mysql-connector-python (MySQL). Reszta kodu się nie zmienia - same tools dalej działają.
Subiekt GT specifically. Konkretny case Własny MCP server dla Subiekta w 2h pokazuje pyodbc setup, recommended tools (faktury sprzedaży, klienci, marża), tipy do konfigów Sybase. Alternatywnie używasz gotowy MCP server dla Subiekt GT jako reference (SubiektMCP, production-ready, 13 tools, instalator Windows).
Wycena dla klienta. Dla typowego małego klienta MŚP (1-2 bazy danych, 5-10 tools) policz 2-3h pracy plus testowanie i wdrożenie. Tier B: 2 000 - 5 000 zł netto fixed price. Tier C (complex integration, multi-source, custom auth): 5 000 - 10 000 zł netto. Jeśli klient potrzebuje recurring support, doliczasz 200-500 zł netto/mc subscription.
Partner program dla developerów. Jeśli wdrażasz SubiektMCP u klientów, jest program partnerski (20% rekurencyjne flat-rate na wszystkich tier’ach klienta). Więcej w Programie partnerskim SubiektMCP.
Sfera GT vs MCP. Subiekt GT ma własne API (Sfera). Porównanie kiedy używać czego: Sfera GT vs MCP. Kiedy co wybrać.
FAQ
Czy MCP zastąpi REST API?
Nie. REST API i MCP są komplementarne. REST jest do service-to-service i klasycznych aplikacji webowych (frontend ↔ backend, microservice ↔ microservice). MCP jest do LLM tool use (user przez Claude lub ChatGPT ↔ Twoje narzędzia i dane). W produkcji często widać oba: REST do integracji z kurierem i KSeF, MCP do integracji z Claude Desktop dla raportów ad-hoc.
Czy MCP działa z ChatGPT?
Tak, przez 2 ścieżki. Pierwsza: OpenAI Codex (część ChatGPT Plus / Pro) ma natywne wsparcie MCP od stycznia 2026. Konfiguracja przez ~/.codex/config.toml, format zbliżony do Claude Desktop. Druga: MCP-compatible bridges (np. mcp-bridge package) które tłumaczą MCP na OpenAI function calling. Pełna analiza w Claude vs ChatGPT dla polskiej firmy.
Jak wycenić własny MCP server dla klienta?
Trzy tier’y. Tier B (2 000 - 5 000 zł netto): prosty connector do jednej bazy, 5-10 tools, setup u klienta. Czas: 2-4h pracy. Tier C (5 000 - 10 000 zł netto): multi-source (np. Subiekt + Allegro + WooCommerce), 15-25 tools, custom auth, monitoring. Czas: 1-2 dni. Tier D (10 000 zł+ netto): enterprise scope, własna infra, multi-tenant, dedykowane wsparcie. Czas: tygodnie.
Plus recurring opcjonalnie (200-500 zł netto/mc subscription support) dla klientów którzy chcą updatów i hotfixów bez negocjacji ad-hoc.
Czy potrzebuję cloud hosting?
Default to NIE. MCP stdio transport uruchamia server jako lokalny proces na maszynie klienta. Zero cloud, zero hosting bills, zero security surface. Cloud hosting potrzebny tylko gdy: multi-tenant SaaS (jeden server, wielu klientów), remote access (klient pracuje z 3 lokalizacji, dane w chmurze), lub specific compliance (np. centralized audit log). Dla większości MŚP integracji lokalny stdio wystarcza.
Co z bezpieczeństwem danych klienta?
Trzy warstwy zabezpieczeń. 1. Local execution: server działa na maszynie klienta, dane nie idą na chmurę (poza zapytaniem do LLM o sformatowanie odpowiedzi). 2. Scoped permissions per tool: ograniczasz w kodzie co tool może zobaczyć (np. tool “faktury_publiczne” zwraca tylko zsumowane kwoty bez NIPów). 3. Audit log: każde wywołanie tool zapisujesz do pliku/bazy, klient ma evidence trail dla audytora i RODO. Plus oczywiście tunelujesz przez HTTPS jeśli używasz SSE transport zamiast stdio.