1. Po co w ogóle jest potrzebny inspektor MCP
Wyobraź sobie, że debugujesz frontend, ale nie wolno ci otworzyć DevTools. Mniej więcej tak wygląda życie bez inspektora MCP. Protokół MCP działa „pod maską” ChatGPT i Apps SDK; jeśli patrzysz tylko na odpowiedź w czacie i myślisz: „Dlaczego on nie widzi mojego narzędzia?”, — w istocie strzelasz na oślep.
Inspektory takie jak MCP Inspector (oficjalny) lub MCP Jam to specjalni klienci MCP dla deweloperów. Potrafią:
- połączyć się z twoim serwerem MCP tak samo, jak robi to ChatGPT;
- przejść przez handshake / capabilities;
- pobrać listę tools/resources/prompts;
- wywołać dowolne narzędzie ręcznie z arbitralnymi argumentami;
- pokazywać surowe wiadomości JSON (requests / replies / errors).
W gruncie rzeczy to „Postman dla MCP, ale z głową”. W odróżnieniu od zwykłego klienta REST, inspektor zna specyfikę MCP: rozumie tools/list, tools/call, umie pokazywać schematy argumentów, czasem obsługuje nawet przepływ OAuth dla chronionych serwerów.
Jeśli nie masz inspektora, debugowanie wygląda tak: uruchamiasz ChatGPT, próbujesz wywołać App, widzisz „Error talking to app” albo to, że narzędzie w ogóle się nie wywołuje, i zaczynasz zgadywać: model nie chciał zawołać narzędzia, twój MCP nie wstał, czy to błąd JSON? Z inspektorem możesz sprawdzić każdą warstwę osobno: najpierw serwer MCP sam na sam z inspektorem, potem dopiero para ChatGPT ↔ MCP.
2. Mini‑przegląd inspektorów: MCP Inspector, Jam i spółka
W praktyce najczęściej będziesz używać dwóch rodzajów inspektorów dla MCP.
Po pierwsze, to oficjalny MCP Inspector z repozytorium Model Context Protocol. To aplikacja webowa (zwykle SPA na React), którą uruchamia się lokalnie lub przez npx/Docker i która potrafi łączyć się z twoim serwerem MCP przez HTTP/SSE.
Po drugie, są inspektory pokroju MCP Jam, które często dodają wygody wokół OAuth. Mogą same odczytać .well-known/oauth-protected-resource, wyciągnąć z niej authorization_endpoint i token_endpoint, przejść przepływ PKCE i już w stanie autoryzowanym odwoływać się do MCP.
MCP Jam powstał na bazie MCP Inspector. Jeśli MCP Inspector realizuje minimalny zestaw narzędzi do debugowania, to MCP Jam oferuje wszystko, czego deweloper potrzebuje w codziennej pracy z MCP. Osobiście polecam od razu używać MCP Jam, żeby później nie zmieniać przyzwyczajeń.
Z punktu widzenia naszego kursu różnica jest taka, że:
- bazowy Inspector przyda się zawsze, nawet dla najprostszego, niezabezpieczonego serwera MCP;
- MCP Jam (lub odpowiednik) staje się użyteczny, gdy dojdziesz do modułów o uwierzytelnianiu i autoryzacji.
Ale idea jest jedna: to zwykły klient MCP, tylko potrafiący ładnie pokazać to, co ChatGPT robi „po cichu”.
3. Typowy scenariusz pracy z inspektorem MCP
Przejdźmy przez typowy scenariusz: napisałeś nowe narzędzie w swoim serwerze MCP i chcesz upewnić się, że naprawdę działa.
W poprzednim wykładzie uruchomiłeś minimalny serwer MCP. Teraz dodamy do tego systemowe podejście do weryfikacji: przepuścimy pełny cykl „serwer → inspektor → logika JSON” krok po kroku.
Krok 1 — uruchamiamy serwer MCP
Robiłeś to już wcześniej: załóżmy, że masz skrypt npm run mcp-dev:
# przykład uruchomienia serwera MCP
npm run mcp-dev
# pod spodem coś w rodzaju: ts-node src/mcp-server.ts
Ważne, aby serwer nasłuchiwał wybranego transportu: w kursie zwykle jest to endpoint HTTP /mcp na jakimś porcie, na przykład http://localhost:4001/mcp.
Krok 2 — uruchamiamy MCP Jam
Drugi terminal:
# jeden ze sposobów uruchomienia MCP Jam
npx @mcpjam/inspector@latest
# w razie potrzeby można dodać --port 4002 itp.
Po tym inspektor otwiera się w przeglądarce, najczęściej pod http://localhost:6274 lub podobnym portem.
Na ekranie startowym MCP Jam poprosi o URL serwera MCP. Wpisujesz:
http://localhost:4001/mcp
albo swój tunelowany URL, jeśli już przepuszczasz wszystko przez ngrok.
Krok 3 — handshake / capabilities
Gdy tylko MCP Jam się łączy, automatycznie robi to samo, co ChatGPT:
- Wysyła żądanie inicjalizacyjne (initialize) z danymi o kliencie.
- Otrzymuje odpowiedź z wersją protokołu i capabilities twojego serwera.
- Na podstawie capabilities rozumie, czy serwer wspiera tools, resources, prompts i inne funkcje.
W UI zwykle widzisz coś w rodzaju:
Connected
Protocol: mcp/2025-06-18
Capabilities:
- tools: list, call
- resources: list, read
- prompts: list, get
Jeśli już na tym etapie inspektor nie może się połączyć (connection refused, CORS, 500 itd.), od razu widzisz błąd i wiesz: problem na pewno nie jest w modelu ani w ChatGPT, tylko w twojej warstwie serwerowej lub sieci.
Krok 4 — discovery: przeglądamy tools/resources/prompts
Po udanym handshake inspektor zwykle sam wywołuje metody takie jak tools/list, resources/list, prompts/list, aby zapełnić panel boczny. Zobaczysz:
- listę narzędzi z opisami i JSON Schema parametrów wejściowych;
- listę zasobów pogrupowanych według kolekcji/ścieżek;
- listę promptów z krótkimi opisami.
Jeśli właśnie dodałeś nowe narzędzie, ale nie ma go na liście, to znaczy, że jest źle zarejestrowane na serwerze albo serwer nie wstał z odświeżonym kodem. Znacznie łatwiej zauważyć to tutaj, niż zgadywać, czemu ChatGPT „nie chce” wywołać twojego narzędzia.
4. Ręczne wywołanie tools przez MCP Jam
Najbardziej przydatna funkcja MCP Jam to ręczne wywoływanie narzędzi. To twój prywatny UI dla tools/call.
Wybieramy narzędzie i wypełniamy argumenty
Załóżmy, że w poprzednim module napisałeś narzędzie suggest_gifts:
// gdzieś w src/mcp/tools/suggestGifts.ts
export const suggestGiftsTool = {
name: "suggest_gifts",
description: "Proponuje pomysły na prezenty według wieku, budżetu i zainteresowań",
inputSchema: {
type: "object",
properties: {
age: { type: "number" },
budget: { type: "number" },
interests: {
type: "array",
items: { type: "string" }
}
},
required: ["age", "budget"]
},
// handler zdefiniowany osobno
};
W MCP Jam klikasz na suggest_gifts. Po prawej otwiera się formularz wygenerowany na podstawie inputSchema. Gdzieś tam wypełniasz:
{
"age": 30,
"budget": 100,
"interests": ["gry", "książki"]
}
i naciskasz „Call” lub podobny przycisk.
Inspektor wysyła żądanie MCP tools/call i od razu widzisz:
- surowe dane JSON żądania (co dokładnie idzie na serwer);
- surowe dane JSON odpowiedzi (result lub error);
- ewentualnie wygodny podgląd wyniku.
Czytamy logi JSON w inspektorze
Zwykle inspektor pokazuje coś w rodzaju:
// Request
{
"id": "1",
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "suggest_gifts",
"arguments": {
"age": 30,
"budget": 100,
"interests": ["gry", "książki"]
}
}
}
// Reply
{
"id": "1",
"jsonrpc": "2.0",
"result": {
"content": [
{
"type": "text",
"text": "1) Gra planszowa ... 2) Bon podarunkowy do księgarni ..."
}
]
}
}
Jeśli twój handler kończy się wyjątkiem, zobaczysz error w stylu JSON‑RPC:
{
"id": "1",
"jsonrpc": "2.0",
"error": {
"code": -32603,
"message": "Internal error",
"data": "TypeError: Cannot read properties of undefined ..."
}
}
To bardzo ważne: właśnie tutaj widzisz poziom protokołu. Jeśli odpowiedź ma inny format, niż oczekuje Apps SDK/ChatGPT, zauważysz to, zanim zaczniesz winić „bugi GPT”.
5. Debugowanie zasobów i promptów
Narzędzia to nie wszystko, co potrafi MCP. Wiesz już, że są jeszcze resources i prompts.
Przez inspektor możesz:
- otworzyć listę zasobów (resources/list) i obejrzeć ich metadane;
- przeczytać konkretny zasób (resources/read) i upewnić się, że zwracane dane są poprawne;
- wykonać wyszukiwanie po zasobach (jeśli tę możliwość zaimplementowałeś);
- zobaczyć przygotowane prompty i ich treść.
Na przykład, jeśli masz zasób gift_catalog:
// pseudokod rejestracji zasobu
registerResource({
uri: "resource://giftgenius/catalog",
name: "Katalog prezentów",
mimeType: "application/json",
handler: async () => {
return JSON.stringify(giftCatalogData);
}
});
W inspektorze zobaczysz ten zasób, klikniesz go i od razu podejrzysz JSON. Jeśli okaże się, że JSON jest niepoprawny albo MIME‑typ dziwny — złapiesz to, zanim ChatGPT zacznie się potykać, próbując go odczytać lub osadzić w widżecie.
6. Logi serwera MCP: co, gdzie i jak logować
MCP Jam jest świetny, ale to za mało: potrzebne są logi samego serwera MCP. Bez nich każdy produkcyjny system zmieni się w loterię.
Co logować
Przydatne minimum:
- każdą przychodzącą wiadomość MCP (request/notification) z:
- czasem;
- metodą (tools/call, tools/list itd.);
- nazwą narzędzia (jeśli jest);
- przyciętymi argumentami (bez danych wrażliwych);
- każdą wychodzącą odpowiedzią:
- status (sukces / błąd);
- czas wykonania;
- skróconą wersją wyniku lub chociaż typem;
- błędami technicznymi:
- parsowanie JSON;
- nieoczekiwane wyjątki w handlerach.
Przy tym bardzo ważne jest nie logować PII i sekretów w całości: tokenów, haseł, pełnych treści poufnych żądań. W zaleceniach dotyczących logowania produkcyjnego często wprost mówi się o logowaniu danych z przyciętym PII.
Dokąd pisać logi: stdout / stderr
W MCP jest ważny wymóg: wiadomości JSON muszą iść „właściwym kanałem”, a wszystkie logi debugowe — innym. Na przykład, jeśli używasz transportu ponad stdout/stderr, to:
- wiadomości JSON‑RPC powinny iść do stdout;
- wszystkie console.log, console.error itd. powinny trafiać do stderr.
Jeśli zmieszasz JSON i logi tekstowe w jednym strumieniu, klient (MCP Jam lub ChatGPT) po prostu nie zdoła sparsować wiadomości, bo wśród JSON nagle pojawi się linia w rodzaju Server started at http://localhost:4001. To jeden z częstych błędów w serwerach MCP.
W scenariuszu HTTP problem jest prostszy, ale zasada ta sama: w odpowiedzi HTTP powinien być czysty JSON, a wszystkie logi — do konsoli/pliku, ale nie do treści odpowiedzi.
Prosty logger dla serwera MCP w TypeScript
Dodajmy do naszego przykładowego serwera MCP mały logger:
// src/logger.ts
export function logRequest(method: string, details: unknown) {
console.error(
JSON.stringify({
level: "info",
type: "request",
method,
details,
ts: new Date().toISOString(),
})
);
}
export function logError(method: string, error: unknown) {
console.error(
JSON.stringify({
level: "error",
type: "error",
method,
error: String(error),
ts: new Date().toISOString(),
})
);
}
I w handlerze tools:
// src/mcp-server.ts (fragment)
server.setRequestHandler("tools/call", async (req) => {
logRequest("tools/call", {
name: req.params?.name,
// tutaj lepiej nie umieszczać całej payload, tylko pola bezpieczne
});
try {
const result = await handleToolCall(req);
return result;
} catch (e) {
logError("tools/call", e);
throw e;
}
});
Dzięki temu w konsoli zobaczysz strukturalne logi JSON, które potem łatwo ze sobą powiązać po ts albo dodatkowym requestId.
7. Połączenie: MCP Jam + logi
Poprawna strategia debugowania MCP prawie zawsze wygląda tak:
- Reprodukujesz problem w inspektorze: widzisz, że tools/list zwraca pustą listę, tools/call się wywraca, odpowiedź JSON jest dziwna itd.
- W tym samym czasie patrzysz w logi serwera MCP: co wypisuje przy starcie, jakie błędy zwraca na każde żądanie, czy jest stack trace.
- Porównujesz id, method, ts w logach z tym, co widzi inspektor.
Na przykład, widzisz w inspektorze:
{
"error": {
"code": -32603,
"message": "Internal error"
}
}
I równolegle w logach:
{
"level": "error",
"type": "error",
"method": "tools/call",
"error": "TypeError: Cannot read properties of undefined (reading 'age')",
"ts": "2025-11-21T10:15:12.345Z"
}
Gotowe, diagnoza jest jasna: gdzieś w handlerze oczekujesz age, ale schemat/argumenty są inne.
8. Mini‑checklista „czy serwer MCP jest gotowy do integracji z App”
Zanim podłączysz serwer MCP do prawdziwego ChatGPT App, warto przejść przez krótką checklistę w inspektorze.
Po pierwsze, handshake i capabilities muszą przechodzić bez błędów. MCP Jam powinien pokazywać, że serwer wspiera potrzebne ci byty: przynajmniej tools oraz, jeśli używasz, resources / prompts.
Po drugie, lista tools/resources/prompts w inspektorze powinna pokrywać się z zestawem narzędzi, zasobów i promptów, który uważasz za zaimplementowany. Literówki w name, zapomniane rejestracje itd. łapią się tutaj natychmiast.
Po trzecie, wywołania narzędzi z poprawnymi argumentami powinny stabilnie zwracać prawidłowy result. Warto spróbować kilku typowych przypadków (takich, na które liczysz w produkcji).
Po czwarte, wywołania z niepoprawnymi argumentami powinny zwracać zrozumiałe odpowiedzi error w stylu JSON‑RPC, a nie padać 500. Na przykład, gdy brakuje obowiązkowego parametru, dobrze zwrócić ustrukturyzowany błąd, który ChatGPT potem da się zamienić na zrozumiały dla użytkownika komunikat.
Po piąte, logi serwera nie powinny zasypywać konsoli gigabajtami stack trace przy byle okazji. Błędy mają być ustrukturyzowane, a dane wrażliwe — starannie odfiltrowane.
Jeśli to wszystko działa w inspektorze, można z dużo spokojniejszą głową podłączać serwer MCP do Apps SDK i bawić się widżetami w Dev Mode.
9. Typowe bugi serwera MCP i jak je łapać przez inspektor
Przejdźmy teraz przez to, co najczęściej się psuje i jak to zobaczyć.
Konfiguracja i połączenie
Czasem wydaje się, że „serwer nie działa”, a problem w tym, że w ogóle nie nasłuchuje właściwego portu lub endpointu. Inspektor w takim wypadku uczciwie pokaże connection refused albo w ogóle się nie połączy. Częste przyczyny: niepoprawny URL (np. /mcp zamiast /api/mcp), port zajęty przez inny proces, tunel nie podniesiony lub CORS obcina żądania.
Nieważny JSON / mieszanie logów i protokołu
Jedna z najbardziej bolesnych historii — gdy wypisujesz console.log("Server started") do stdout, a ponad tym powinny iść wiadomości JSON‑RPC. Klient oczekuje czystego JSON, a dostaje tekst + JSON, próbuje parsować i wysypuje się błędem formatu.
Rozwiązanie proste: sztywno rozdzielić, co idzie do strumienia protokołowego (stdout lub ciało odpowiedzi HTTP), a co do logów (stderr lub osobny plik z logami).
Niedopasowanie schematu i implementacji narzędzia
Kolejny popularny błąd: w inputSchema zadeklarowałeś jedno, a w kodzie oczekujesz czegoś innego. Na przykład, schemat mówi: age — liczba, interests — opcjonalna tablica stringów, a kod próbuje zrobić arguments.interests.toLowerCase(). Model (i inspektor) uczciwie podsyłają interests jako null albo w ogóle nie przysyłają pola — i wszystko się wywraca.
Inspektor pozwala jawnie zobaczyć, jaki JSON faktycznie idzie w tools/call, i zestawić to z twoim kodem.
Nieprawidłowe nazwy tools/resources
Jeśli w capabilities / tools/list eksportujesz narzędzie jako suggest_gifts_v2, a w manifeście Apps lub widżecie oczekujesz suggest_gifts, to „narzędzie nie znalezione” będzie ci towarzyszyć do końca projektu. W inspektorze po liście narzędzi i ich polach name widać to od razu, bez zgadywania, co „myśli” GPT.
Wolne lub zawieszające się narzędzia
Jeśli wywołanie narzędzia w inspektorze trwa 30 sekund, a potem pada przez timeout — nie licz, że ChatGPT poradzi sobie lepiej. Inspektor MCP pomoże zrozumieć, na którym etapie spowalniasz: wywołanie sieciowe, baza danych, zewnętrzne API. W logach dobrze mieć czas rozpoczęcia i zakończenia obsługi każdego żądania, aby od razu widzieć odstające przypadki.
10. Typowe błędy przy inspekcji i debugowaniu MCP
Błąd nr 1: próba debugowania MCP wyłącznie przez ChatGPT.
Wielu deweloperów najpierw podłącza MCP do App, widzi, że „coś nie działa”, i zaczyna zmieniać prompty, opis narzędzia, a nawet wersję modelu. Tymczasem serwer MCP w ogóle nie wstaje albo tools/list jest pusty. Zawsze zaczynaj od inspektora: jeśli tam jest źle, model nie ma tu nic do rzeczy.
Błąd nr 2: mieszanie JSON‑RPC i logów w jednym strumieniu.
Gdy klient MCP oczekuje czystego JSON, a ty drukujesz do stdout linie debugowe, rezultat jest przewidywalny — parsowanie się sypie, Inspector pokazuje dziwne błędy. Logi powinny iść osobno (stderr, pliki, zewnętrzne systemy logowania), a wiadomości protokołowe — ściśle swoim kanałem.
Błąd nr 3: nieuwzględnianie capabilities i listy tools.
Często narzędzie „znika” tylko dlatego, że zapomniałeś je zarejestrować albo włączyć odpowiednią capability. Jeśli nie patrzysz na capabilities i tools/list w inspektorze, możesz długo sądzić, że winny jest model, a nie twój kod rejestracji.
Błąd nr 4: ignorowanie błędów schematu i niezgodności JSON.
Gdy inputSchema i faktyczny JSON się rozchodzą, model i inspektor zachowują się naturalnie „dziwnie”. Jeśli nie patrzysz na surowe wiadomości JSON w inspektorze i nie walidujesz schematu, te błędy będą wychodzić w najmniej oczekiwanych miejscach.
Błąd nr 5: logowanie wszystkiego, łącznie z PII i tokenami.
W ferworze debugowania łatwo zacząć wypisywać do logów całe request body, w tym potencjalne dane osobowe czy sekrety. W produkcji to tykająca bomba: wycieki, problemy z compliance itd. Loguj tylko to, co naprawdę potrzebne do diagnostyki, i z przyciętymi/zanonimizowanymi danymi.
Błąd nr 6: brak reprodukcji problemu minimalnymi przypadkami.
Czasem bug ujawnia się w złożonej rozmowie przez ChatGPT i deweloper próbuje debugować to „jak leci”. Znacznie skuteczniej jest odtworzyć ten sam scenariusz w inspektorze jednym‑dwoma żądaniami MCP, odciąć wpływ promptów, historii dialogu i „nastroju” modelu.
GO TO FULL VERSION