Co to jest MCP? I jak zintegrować serwer MCP z AI SDK?

MCP jest równie gorącym tematem w bańce AI jak architektura agentowa. Bardzo często są wymieniane razem jak złoty graal i rozwiązanie wszystkich problemów (zaczynając od problemu programistów). Ale czy tak jest naprawdę? I jak można to wdrożyć z pomocą AI SDK od Vercel'a?
Aktualizacja 3.06. Ostatnio nastąpiły zmiany w standardzie MCP i zostało dodane m.in wsparcie dla OAuth i Sampling. Również AI SDK pracuje nad kolejnymi usprawnieniami więc niektóre informacje mogą nie być 100% aktualne. Pełna aktualizacja artykułu jest w trakcie przygotowania.
Czym jest MCP?
Jeśli ominął cię temat MCP i cały hype, to mam dla ciebie krótkie podsumowanie. MCP (czyli Model Context Protocol) jest otwartym protokołem, który ma standaryzować jak aplikacje mają przekazywać context do LLM. Został stworzony przez Anthropic na potrzeby Claude Desktop, ale zaczyna się rozszerzać na inne aplikacje i sytuacje.
W założeniu jeśli dane narzędzie wspiera standard MCP, to może być bezproblemowo użyte przez dowolną aplikację, która wykorzystuje ten protokół. Twórcy podali przykład portu USB, do którego można wsadzić dowolne, pasujące akcesorium. Innym przykładem, który do mnie przemawia jest instalowanie wtyczek do Wordpress'a, które rozszerzają jego możliwości.

Co nam to daje? W założeniu ogromne możliwości integracji i rozszerzania możliwości modelu. Mając odpowiednie wtyczki dajemy LLM możliwość kontroli muzyki, odpytywania baz danych, integracja z istniejącymi aplikacjami (np.: tworzenie modeli 3D w Blenderze). Teoretycznie mamy nieograniczone możliwości rozszerzania możliwości modeli LLM.
Początkowo zostało to stworzone z myślą o Claude Desktop, potem wdrożył do Copilot, WIndsurf i inne aplikacje. Ale nic nie szkodzi, by wykorzystać to w aplikacjach opartych o LLM jako część flow. AI SDK dodała eksperymentalne wsparcie dla MCP i zamierzam pokazać, jak można je użyć.
Integracja AI SDK z MCP
Zakładam, że wiesz jak korzystać z AI SDK. Szczególnie w kontekście używania narzędzi oraz budowania agentów. Jeśli nigdy nie pracowałeś/aś z AI SDK, to sprawdź mój DARMOWY ebook o tym.
Wykorzystane narzędzia:
- Qdrant - jedna z popularniejszych baz wektorowych, które można uruchomić na własnym serwerze
- MCP Server Qdrant - oficjalny serwer MCP do Qdrant. Pozwala odczytywać i zapisywać dane do bazy.
- AI SDK
Przygotowanie serwera MCP
Qdrant Server MCP jest napisany w Python i uruchamia się przez środowisko Python'owe. Mimo że pracuję z tym językiem i mam skonfigurowany komputer pod to, to nie udało mi się uruchomić w trybie stdio z poziomu Node'a. Ale zawsze jest tryb SSE i obraz Dockerowy
Moje kroki do uruchomienia serwera:
- Sklonować repozytorium MCP Server Qdrant
- Zbudować obraz dockerowy (
docker build -t mcp-server-qdrant .) - Opakować wszystko w docker-compose, by umożliwić komunikację między serwerem a instancją qdrant
Poniżej znajdziesz gotowy plik docker-compose.yml, który możesz wykorzystać
1version: '3'
2
3services:
4 qdrant:
5 image: qdrant/qdrant
6 ports:
7 - "6333:6333"
8 - "6334:6334"
9 volumes:
10 - ./qdrant_storage:/qdrant/storage:z
11 networks:
12 - app-network
13
14 mcp-server:
15 image: mcp-server-qdrant
16 ports:
17 - "8000:8000"
18 environment:
19 - QDRANT_URL=http://qdrant:6333
20 - COLLECTION_NAME=ai-fullstack
21 depends_on:
22 - qdrant
23 networks:
24 - app-network
25
26networks:
27 app-network:
28 driver: bridge
29
30Ta część zabrała mi najwięcej czasu i potem jest już łatwiej.
Integracja z AI SDK
1import { experimental_createMCPClient, generateText } from 'ai';
2
3const client = await experimental_createMCPClient({
4 transport: {
5 type: 'sse',
6 url: 'http://localhost:8000/sse',
7 }
8});
9
10const tools = await client.tools();
11AI SDK daje metodę pozwalającą stworzyć klienta MCP. Na razie jest eksperymentalna, więc prawdopodobnie zmieni się w przyszłości.
Najważniejsze elementy:
- type: 'sse' - mamy wolnostojący serwer więc możemy jedynie przy pomocy SSE. Standard na razie nie przewiduje innej komunikacji oprócz sse lub stdio. (Aktualizacja 3.06 - Standard aktualnie obsługuje: sse, stdio i Streamable HTTP. SSE jest już niepolecane)
- http://localhost:8000/sse - bardzo istotne jest /sse w adresie. Chwilę mi zajęło, zanim wstawiłem tę wartość, bo nie widziałem jej nigdzie w dokumentacji i dopiero po analizie kodu serwera i innych serwów trafiłem na tą wartość.
- client.tools() zwraca nam obiekt z dostępnymi narzędziami zgodny z formatem AI SDK.
Przykładowa aplikacja wygląda następująco
1import { openai } from '@ai-sdk/openai';
2import { experimental_createMCPClient, generateText } from 'ai';
3
4const client = await experimental_createMCPClient({
5 transport: {
6 type: 'sse',
7 url: 'http://localhost:8000/sse',
8 }
9});
10
11const tools = await client.tools();
12
13const completion = await generateText({
14 system: `You are a helpful assistant. You have access to memory with information like location, personal things etc. Before you answer, check if you have the answer in your memory. If you do, use it. If you don't, ask the user for the information and save it in your memory.
15
16 In memory you have access to the following information:
17 - Location
18 - Personal information
19 - Interests
20 - Preferences
21 - Hobbies
22 - Skills
23 - Work experience
24 `,
25
26
27
28 // prompt: 'My location is in Katowice, Poland. Please save my location in the memory.',
29 prompt: 'What is my location?',
30 model: openai('gpt-4o-mini'),
31 tools,
32 maxSteps: 5,
33})
34
35console.dir(completion.text, {depth: null});
36client.close()
37W prompcie systemowych warto dać znać modelowi, jakie informacje mogą się znajdować w pamięci, by ułatwić wyszukiwanie oraz uniknąć halucynacji lub odpowiedzi w stylu "nie znam twojej lokalizacji".
Warto też testować różne modele, bo nie wszystkie tak dobrze działają. Ja ostatecznie zdecydowałem się na gpt-4o-mini bo modele od Google'a nie dawały sobie rady.
Integracja z innymi narzędziami
W przykładzie wyżej jest sytuacja gdzie mamy tylko jedno narzędzie. Jest to przykład akademicki i w prawdziwych aplikacjach będziemy mieć dużo więcej narzędzi do wyboru. Natomiast bardzo łatwo możemy dołożyć kolejne narzędzia i zbudować mini agenta, który wykorzysta zarówno serwer MCP jak i własne narzędzia (w przykładzie masz też jak zintegrować OpenTelemetry co jest według mnie kluczowe jak mówimy o agentach)
1import { experimental_createMCPClient, generateObject, generateText, tool } from 'ai';
2import { z } from 'zod';
3import { fetchLocation } from '../tool/fetchLocation';
4import { fetchWeather } from '../tool/fetchWeather';
5
6import { NodeSDK } from "@opentelemetry/sdk-node";
7import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
8import { LangfuseExporter } from "langfuse-vercel";
9import { openai } from '@ai-sdk/openai';
10
11
12const sdk = new NodeSDK({
13 traceExporter: new LangfuseExporter(),
14 instrumentations: [getNodeAutoInstrumentations()],
15});
16
17sdk.start();
18
19
20const client = await experimental_createMCPClient({
21 transport: {
22 type: 'sse',
23 url: 'http://localhost:8000/sse',
24 }
25});
26
27
28const tools = await client.tools();
29
30
31const prompt = `What is weather in my current location?`;
32
33
34const completion = await generateText({
35 system: `You are a helpful assistant. You have access to memory with information like location, personal things etc. Before you answer, check if you have the answer in your memory. If you do, use it. If you don't, ask the user for the information and save it in your memory.
36
37 In memory you have access to the following information:
38 - Location
39 - Personal information
40 - Interests
41 - Preferences
42 - Hobbies
43 - Skills
44 - Work experience
45 `,
46 prompt: prompt,
47 model: openai('gpt-4o-mini'),
48 tools: {
49 ...tools,
50 location: tool({
51 description: 'Get the location of a place',
52 parameters: z.object({
53 location: z.string().describe('The location to get the lat&lon for'),
54 }),
55 execute: async ({ location }) => {
56 return fetchLocation(location)
57 }
58 }),
59 weather: tool({
60 description: 'Get the weather for a location',
61 parameters: z.object({
62 lat: z.string().describe('The lat of location'),
63 lon: z.string().describe('The lon of location'),
64 }),
65 execute: async ({ lat, lon }) => {
66 return fetchWeather(lat, lon)
67 }
68 }),
69 },
70 maxSteps: 5,
71 experimental_telemetry: {
72 isEnabled: true,
73 },
74});
75
76console.dir(completion.text, { depth: null });
77client.close()
78
79await sdk.shutdown();
80Zagrożenia MCP
Zanim postanowisz wdrażać MCP do swoich aplikacji wstrzymaj się na chwilę.
MCP jest nowym standardem i nie wiemy jeszcze jakie zagrożenia i podatności tam są
A przynajmniej nie wszystkie, bo działy bezpieczeństwa już zaczęły działać i mają pierwsze wnioski.
Głównym problemem jest brak autoryzacji. Ten protokół został zaprojektowany na razie dla pojedynczego użytkownika, który daje dostęp do swoich danych. Nie mamy tutaj mechanizmów decydowania czy użytkownik może wykonać daną akcję - to dyskwalifikuje te rozwiązanie z aplikacji GenAI(Aktualizacja 3.06 - W najnowszym standardzie zostało dodane wsparcie dla OAuth)- Serwery MCP pojawiają się jak grzyby po deszczu, ale trzeba uważać, co się używa. Serwer MCP ma dostęp do twoich danych i może je wykorzystać
Brak opcji dodatkowego zabezpieczenia publicznych serwerów SSE(Aktualizacja 3.06 - SEE nie jest już polecane i powinno się używać S)Brak wsparcia dla architektury human-in-the-loop(Aktualizacja 3.06 - W najnowszej wersji standard wprowadził Sampling, który pozwala wdrożyć architekturę human-in-the-loop)
To są typowe problemy nowych standardów, które muszą dojrzeć. Na razie to jest udane MVP, ale brakuje kluczowych funkcjonalności, by wdrożyć to produkcyjnie. Pojawiają się rozwiązania, które łatają te problemy (Cloudflare ostatnio ogłosił pakiet narzędzi do tego) i pewnie za jakiś czas będzie to też w standardzie.
Używać czy nie MCP?
Jeśli korzystasz z Claude Desktop, dajesz dostęp do swoich danych, korzystasz z zaufanych serwerów MCP i liczysz się z ryzykiem, to nie ma problemu. Jeśli budujesz aplikacje korzystające z LLM to trzymaj się na razie zwykłych narzędzi i buduj wszystko pod siebie. Będzie bezpieczniej i wydajniej. Jeśli potrzebujesz inspiracji/pomocy, to zawsze możesz znaleźć serwer MCP i podejrzeć implementację. Może w przyszłości serwery MCP będą gotowe na produkcje, ale na razie tak nie jest
Aktualizacja 3.06
MCP z najnowszymi zmianami jest już ciekawą opcją do wdrożenia np.: w organizacji, by połączyć ze sobą Claude Desktop i wewnętrzne dane. Wraz OAuth i Sampling jest to ciekawa opcja.


