Serwer MCP w TypeScript dla Claude Code, Codex i Copilot
Serwer MCP w TypeScript: narzędzie z Zod, test w MCP Inspector i ten sam serwer w Claude Code, Codex i Copilot w VS Code.

Mateusz Karwowski
Programista i nauczyciel · MattDevSpis treści
Agent AI w edytorze zna Twój kod, ale nie zna Twojej aplikacji. Nie wie, jak utworzyć w niej rekord ani gdzie sprawdzić ranking. Serwer MCP (Model Context Protocol) to mały program, który mu to umożliwia, bo udostępnia funkcje aplikacji jako narzędzia z opisem i schematem argumentów. Pokażę Ci taki serwer w TypeScript na przykładzie aplikacji z quizami z kursu DevChallenge. Potem podłączymy go do Claude Code, Codex i Copilot w VS Code, bez zmieniania w nim ani jednej linijki.
Co to jest serwer MCP i jak działa
Serwer MCP pośredniczy między agentem AI a Twoją aplikacją. Agent pobiera od niego listę narzędzi, a potem wywołuje te, których potrzebuje, z argumentami zgodnymi ze schematem. W MCP są trzy role. Host to aplikacja, w której rozmawiasz z modelem, na przykład Claude Code. Klient to część hosta, która utrzymuje połączenie z jednym serwerem. Serwer to Twój program z narzędziami.
Najprostszy transport to stdio. Host uruchamia serwer jako osobny proces i wymienia z nim wiadomości JSON-RPC przez standardowe wejście i wyjście. Wynika z tego jedna praktyczna zasada: stdout należy do protokołu. Każdy console.log w serwerze może zepsuć komunikację, dlatego logi wypisujesz na stderr przez console.error.
Taki serwer nie potrzebuje publicznego adresu, certyfikatu ani logowania. Działa na Twoim komputerze i kończy pracę razem z sesją agenta.
Narzędzie MCP w TypeScript
Serwer z kursu korzysta z oficjalnego SDK MCP w wersji 2, czyli z pakietu @modelcontextprotocol/server, i z Zod. Poniżej jest skrócona wersja jego narzędzia, które tworzy i publikuje quiz:
import { randomUUID } from 'node:crypto'
import { McpServer } from '@modelcontextprotocol/server'
import { serveStdio } from '@modelcontextprotocol/server/stdio'
import { z } from 'zod'
const apiUrl = new URL(process.env.DEVCHALLENGE_API_URL ?? 'http://localhost:3000')
const postJson = async (path: string, payload: object) => {
const response = await fetch(new URL(path, apiUrl), {
method: 'POST',
headers: { 'Content-Type': 'application/json', Origin: apiUrl.origin },
body: JSON.stringify(payload),
signal: AbortSignal.timeout(75_000),
})
if (!response.ok) throw new Error(`Żądanie ${path} zwróciło status ${response.status}.`)
return response.json()
}
const server = new McpServer({ name: 'devchallenge', version: '1.0.0' })
server.registerTool(
'create-challenge',
{
title: 'Stwórz challenge DevChallenge',
description:
'Generuje quiz przez AI i publikuje go pod publicznym linkiem. ' +
'Wywołanie kosztuje generację AI, więc potwierdź je w hoście.',
inputSchema: z.object({
topic: z.string().trim().min(1).max(200),
level: z.enum(['easy', 'medium', 'hard']),
questionCount: z.number().int().min(3).max(10),
typeMode: z.enum(['single', 'trueFalse', 'multiple']).default('single'),
}),
annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
},
async ({ topic, level, questionCount, typeMode }) => {
try {
const challenge = await postJson('/api/challenges/generate', {
topic,
level,
questionCount,
typeMode,
documentId: null,
documentToken: null,
hasAiConsent: false,
})
const saved = await postJson('/api/challenges', { publicationId: randomUUID(), challenge })
return { content: [{ type: 'text', text: `Challenge gotowy: ${apiUrl.origin}/c/${saved.id}` }] }
} catch (error) {
const message = error instanceof Error ? error.message : 'Nie udało się utworzyć quizu.'
return { content: [{ type: 'text', text: message }], isError: true }
}
},
)
serveStdio(() => server)
console.error('DevChallenge MCP działa, stdout należy do protokołu.')
W kursie serwer sprawdza jeszcze odpowiedź API schematem Zod, zanim cokolwiek opublikuje. Tutaj pominąłem ten krok, żeby kod zmieścił się na jednym ekranie. Kilka decyzji warto omówić osobno:
- Opis mówi o koszcie. Czyta go model, więc od tego opisu zależy, czy agent zapyta Cię o zgodę, zanim wywoła narzędzie.
- Adnotacje informują hosta o skutkach.
readOnlyHint: falseiopenWorldHint: trueoznaczają, że narzędzie zmienia stan i sięga poza Twój komputer. Host może więc poprosić Cię o potwierdzenie, ale adnotacja to tylko podpowiedź, a nie zabezpieczenie. - Błąd wraca jako wynik. Zamiast rzucać wyjątek, narzędzie zwraca treść błędu z
isError: true. Model zobaczy wtedy na przykład status 429 przy limicie i przekaże Ci go, zamiast zgadywać. - Serwer zna tylko adres API. Klucze OpenAI i dane dostępu do bazy danych zostają w aplikacji.
Node.js 24 uruchamia pliki .ts bezpośrednio, więc serwer nie potrzebuje osobnego kroku budowania. W repozytorium kursu wystarczy polecenie node mcp-server/src/index.ts.
Najpierw sprawdź serwer w MCP Inspector
Zanim podłączysz agenta, sprawdź serwer w darmowym MCP Inspector. Uruchom aplikację w jednym terminalu, a w drugim pobierz listę narzędzi. W poleceniu wpisz własną ścieżkę do serwera:
npx @modelcontextprotocol/inspector --cli \
-e DEVCHALLENGE_API_URL=http://127.0.0.1:3000 \
node /ABSOLUTNA/SCIEZKA/mcp-server/src/index.ts \
--method tools/list
Wynik powinien zawierać create-challenge razem ze schematem argumentów. Lista narzędzi niczego nie generuje, więc ten krok nic nie kosztuje. Bez --cli Inspector otworzy interfejs w przeglądarce, w którym wywołasz narzędzie ręcznie.
Ten sam serwer w Claude Code, Codex i Copilot
Każdy agent potrzebuje tej samej informacji: jakim poleceniem uruchomić serwer i z jakimi zmiennymi środowiskowymi. Różni się tylko miejsce, w którym ją zapisujesz. Agenci zmieniają się szybko, więc przy błędzie zajrzyj do ich aktualnej dokumentacji.
Claude Code
claude mcp add devchallenge \
--transport stdio \
--scope local \
--env DEVCHALLENGE_API_URL=http://127.0.0.1:3000 \
-- node /ABSOLUTNA/SCIEZKA/mcp-server/src/index.ts
Nazwa serwera stoi zaraz po add, bo opcja --env przyjmuje kilka wartości i wzięłaby nazwę za kolejną zmienną. W sesji Claude Code polecenie /mcp pokaże, czy serwer jest połączony. Jeśli agent uruchomi starszy Node niż 24, wpisz zamiast node pełną ścieżkę z polecenia node -p "process.execPath".
Codex
codex mcp add devchallenge \
--env DEVCHALLENGE_API_URL=http://127.0.0.1:3000 \
-- node /ABSOLUTNA/SCIEZKA/mcp-server/src/index.ts
codex mcp list
Codex zapisuje ten wpis w swojej konfiguracji, w pliku ~/.codex/config.toml. Drugie polecenie pokazuje listę serwerów, więc od razu sprawdzisz, czy wpis się zapisał.
Copilot w VS Code
W katalogu projektu utwórz plik .vscode/mcp.json. Główny klucz to servers:
{
"servers": {
"devchallenge": {
"type": "stdio",
"command": "node",
"args": ["${workspaceFolder}/mcp-server/src/index.ts"],
"env": { "DEVCHALLENGE_API_URL": "http://127.0.0.1:3000" }
}
}
}
Potem otwórz Copilot Chat w trybie agenta i sprawdź na liście narzędzi, czy jest tam create-challenge. Jeśli korzystasz z GitHub Copilot na koncie firmowym, administrator organizacji może mieć wyłączone serwery MCP. Kiedy narzędzi nie widać, sprawdź najpierw to ustawienie.
Skill w Claude Code i Codex: kiedy agent ma zapytać o zgodę
Konfiguracja mówi agentowi, jak uruchomić serwer, ale nie mówi, kiedy z niego korzystać. Do tego służy skill, czyli plik SKILL.md z krótką instrukcją i opisem, po którym agent rozpoznaje, że skill pasuje do zadania. Przykładowy skill dla tej aplikacji może wyglądać tak:
---
name: devchallenge-quiz
description: Tworzy quiz w lokalnej aplikacji DevChallenge przez narzędzie MCP create-challenge. Użyj, gdy ktoś prosi o quiz na wybrany temat.
---
1. Ustal temat i streść go w najwyżej 200 znakach.
2. Pokaż argumenty: temat, poziom, liczbę pytań i typ pytań.
3. Przed wywołaniem create-challenge poproś o potwierdzenie. Każde wywołanie kosztuje generację AI i publikuje quiz pod publicznym linkiem.
4. Po błędzie nie ponawiaj wywołania bez zgody. Status 429 oznacza limit generowania.
Claude Code szuka skilli w katalogu .claude/skills, Codex w .agents/skills, a Copilot w VS Code w obu tych miejscach. W repozytorium trzymasz więc skill w .claude/skills/devchallenge-quiz/SKILL.md i jego kopię w .agents/skills/devchallenge-quiz/SKILL.md. Ogólne zasady projektu, na przykład polecenia testów, zapisujesz w AGENTS.md.
Co agent naprawdę robi z Twoim kodem
Poproś agenta: „zrób quiz o tym, jak działa nasz moduł RAG”. Agent przeczyta pliki projektu, streści temat do 200 znaków i wywoła create-challenge. Narzędzie nie dostaje ani kodu, ani PDF-a, tylko ten krótki temat. Quiz powstaje więc z tematu i wiedzy modelu, a nie z Twojego repozytorium.
To nadal jest przydatne, bo agent trafnie nazywa temat na podstawie kodu. Warto jednak to wiedzieć, zanim komuś obiecasz „quiz z Twojego kodu”. Różnicę między takim narzędziem a quizem z dokumentu opisuję we wpisie RAG vs MCP na przykładzie jednej aplikacji.
Bezpieczeństwo serwera MCP w trzech zasadach
- Serwer zna tylko publiczne API. Sekrety zostają w aplikacji, a serwer ma te same limity co przeglądarka, czyli 5 quizów na godzinę z jednego adresu IP.
- Narzędzie, które coś kosztuje, wymaga zgody. Opis narzędzia, adnotacje i skill mówią to samo: najpierw argumenty, potem potwierdzenie. Nie wyłączaj potwierdzeń dla wszystkich narzędzi naraz.
- Zaczynaj od odczytu. W kursie drugie narzędzie,
get-leaderboard, dopisujesz samodzielnie w zadaniu. To narzędzie tylko odczytuje ranking quizu, więc agent może z niego korzystać bez kosztów i bez zmian w danych.
Najczęstsze pytania
Czy serwer MCP musi być publicznie dostępny?
Nie. Serwer stdio działa lokalnie jako proces uruchamiany przez agenta. Publiczny adres jest potrzebny dopiero wtedy, gdy z serwera mają korzystać ChatGPT albo claude.ai. W kursie taki zdalny serwer udostępnia tylko ranking, czyli odczyt bez kosztów, więc działa bez logowania. Płatne tworzenie quizu zostaje w serwerze lokalnym, bo zdalnie wymagałoby logowania i ochrony przed kosztami.
Kiedy wystarczy skill, a kiedy potrzebny jest serwer MCP?
Skill to instrukcja dla agenta. Mówi, kiedy i jak wykonać zadanie, ale niczego nie wywołuje w aplikacji. Serwer MCP daje agentowi narzędzie, które coś robi, na przykład tworzy quiz. Do pilnowania zasad projektu wystarczy skill. Do zmian w danych aplikacji potrzebny jest serwer MCP, a skill podpowiada agentowi, kiedy z niego skorzystać.
Czy do nauki potrzebny jest płatny agent?
Nie. MCP Inspector jest darmowy i wystarcza do sprawdzenia narzędzi. Claude Code wymaga płatnego planu Claude albo konta API z płatnością za użycie, a dostęp do Codex i Copilot zależy od ich aktualnych planów.
Serwer z tego wpisu piszemy w Kursie 4 DevChallenge. Najpierw powstaje narzędzie i testy protokołu, potem łączymy je z Claude Code, Codex i Copilot, razem ze skillem projektu, automatyczną oceną jakości quizów i rankingiem w okienku czatu. Wszystkie lekcje znajdziesz w programie kursu.

Jestem programistą i nauczycielem, magistrem inżynierem informatyki po Politechnice Warszawskiej. Uczę programowania i matematyki na zajęciach 1:1. Wspólnie rozwiązujemy zadania, piszemy kod i wyjaśniamy kolejne kroki.
Czytaj dalej
RAG vs MCP na przykładzie jednej aplikacji
RAG czy MCP? Na przykładzie aplikacji z quizami pokazuję, kiedy model potrzebuje wiedzy z Twojego PDF-a, a kiedy narzędzia, które coś zrobi w aplikacji.
Czytaj artykuł AI w praktyceAutomatyzacja z AI w 6 poziomach: od skryptu po agenta
Jak zautomatyzować pracę z AI? Porównaj skrypt, RPA, n8n, czat z AI i agentów na raporcie z arkusza, który co tydzień wysyłasz mailem przełożonemu.
Czytaj artykułJeden serwer MCP dla wszystkich agentów
W Kursie 4 DevChallenge napiszemy ten serwer razem, podłączymy go do Claude Code, Codex i Copilot w VS Code, a potem sprawdzimy go testami i automatyczną oceną quizów (evals).