Używamy cookies, żeby zwiększyć Twoje doświadczenia na stronie
CodeWorlds
Powrót do kolekcji
Przewodnik10 min czytania

PostgreSQL MCP, baza dla asystenta

Podłączenie bazy do asystenta przez MCP. Dlaczego oficjalny serwer został wycofany, na czym polegał obchodzony tryb odczytu i jak zrobić to bezpiecznie.

PostgreSQL przez MCP, czyli asystent pytający bazę

Podłączenie bazy danych do asystenta brzmi jak oczywista wygoda. Zamiast pisać zapytanie, pytasz o liczbę zamówień z ostatniego tygodnia i dostajesz odpowiedź razem z zapytaniem, które ją dało.

Wygoda jest realna, a ryzyko większe niż przy jakimkolwiek innym narzędziu tej klasy. Baza produkcyjna to miejsce, w którym pomyłka nie objawia się gorszą odpowiedzią, tylko usuniętymi danymi.

Ten tekst zaczyna się od historii, która to ilustruje, bo dotyczy narzędzia używanego do dziś przez dziesiątki tysięcy osób tygodniowo, mimo że zostało wycofane.

Tryb tylko do odczytu, który nim nie był

Oficjalny serwer referencyjny do PostgreSQL, wydany razem z protokołem, oferował tryb ograniczający asystenta do odczytu. Wyglądał sensownie i miał lukę, która ten tryb całkowicie znosiła.

Mechanizm polegał na opakowaniu zapytania w transakcję oznaczoną jako tylko do odczytu. Tekst przychodzący od modelu trafiał w całości do prostego wywołania sterownika, a to wykonuje kilka poleceń rozdzielonych średnikiem w jednym przebiegu, więc nic nie stało na przeszkodzie, żeby zamknąć tę transakcję i wykonać w tym samym łańcuchu kolejne polecenia.

Wystarczyło zapytanie zaczynające się od zatwierdzenia bieżącej transakcji, po którym następowała dowolna operacja. Ochrona znikała, a kolejne polecenie wykonywało się z pełnymi uprawnieniami połączenia, łącznie z usunięciem schematu wraz z całą zawartością.

Projekt został zarchiwizowany w 2025 roku, a pakiety oznaczono jako wycofane. Mimo to nadal jest pobierany dziesiątki tysięcy razy tygodniowo, bo poradniki sprzed dwóch lat wskazują właśnie na niego, a komunikat o wycofaniu widzi wyłącznie ten, kto go szuka.

Wniosek praktyczny jest prosty i wart powtórzenia: jeśli w Twojej konfiguracji figuruje pakiet o tej nazwie, wymień go. Istnieją zamienniki naprawiające tę lukę i utrzymywane dalej; poprawka po stronie narzędzia polega na przekazaniu zapytania jako polecenia przygotowanego, bo taka droga nie przyjmuje kilku poleceń naraz.

Czego uczy ta historia

Warto wyciągnąć z tego wnioski szersze niż jeden pakiet, bo dotyczą całej klasy narzędzi.

Pierwszy: ograniczenie zapisane w kodzie narzędzia nie jest zabezpieczeniem. Tryb tylko do odczytu realizowany przez opakowanie zapytania zależy od tego, czy zapytanie da się rozszerzyć, a przy tekście przychodzącym od modelu zawsze da się.

Drugi: prawdziwe ograniczenie należy do bazy, nie do narzędzia. Konto z uprawnieniami wyłącznie do odczytu nie wykona operacji zapisu niezależnie od tego, co przyjdzie w zapytaniu i jak sprytnie zostanie sformułowane.

Ten drugi wniosek ma granicę, którą warto znać, bo nie wszędzie istnieje warstwa, do której da się ograniczenie przenieść. Serwer plikowy pilnuje dozwolonych katalogów sprawdzeniem we własnym kodzie i to sprawdzenie się broni, bo porównuje rozwiniętą ścieżkę, czyli rzecz skończoną, a nie tekst zapytania, który zawsze da się rozbudować. Różnica nie leży więc w tym, czy sprawdzenie jest w kodzie narzędzia, tylko w tym, czy sprawdza wartość zamkniętą, czy język.

Trzeci: obecność w katalogu narzędzi nie oznacza aktualności ani bezpieczeństwa. Rejestr, opisany w tekście o katalogu serwerów, przechowuje opisy i weryfikuje nazwy, a nie prowadzi przeglądu kodu.

Czwarty, najbardziej niewygodny: narzędzie może być oficjalne i jednocześnie nienadające się do użycia. Sygnatura wydawcy mówi wyłącznie o pochodzeniu, a nie o jakości kodu ani o tym, czy ktokolwiek je jeszcze utrzymuje.

Jak zrobić to bezpiecznie

Trzy warstwy, z których każda działa niezależnie od pozostałych. Warto mieć wszystkie, bo każda zawodzi w inny sposób.

Pierwsza to osobne konto w bazie, z uprawnieniami wyłącznie do odczytu i wyłącznie do tych tabel, które asystent ma widzieć.

Code
SQL
CREATE ROLE asystent_odczyt LOGIN PASSWORD 'haslo';
GRANT CONNECT ON DATABASE sklep TO asystent_odczyt;
GRANT USAGE ON SCHEMA public TO asystent_odczyt;
GRANT SELECT ON zamowienia, produkty TO asystent_odczyt;

To jest jedyne zabezpieczenie, które działa bez względu na to, co przyjdzie w zapytaniu. Jeśli asystent ma widzieć wyłącznie zamówienia i produkty, tabela z danymi pracowników jest poza jego zasięgiem na poziomie bazy, a nie na poziomie dobrych intencji.

Druga warstwa to limit czasu wykonania. Zapytanie bez ograniczenia potrafi zablokować bazę na minuty, a model nie wie, że zapytanie łączące trzy duże tabele bez warunku wykona się godzinę.

Code
SQL
ALTER ROLE asystent_odczyt SET statement_timeout = '10s';

Trzecia to osobna instancja albo replika przeznaczona do odczytu. Asystent pytający replikę nie dotyka bazy obsługującej użytkowników, więc nawet zapytanie zbudowane fatalnie nie wpłynie na działanie aplikacji ani na czas odpowiedzi.

Do tego dochodzą trzy rzeczy oczywiste i pomijane. Maskowanie kolumn z danymi osobowymi, bo asystent czytający tabelę użytkowników przekazuje ich dane do modelu. Zapis wykonanych zapytań, żeby wiedzieć, o co pytano. Oraz wyłączenie dostępu do bazy produkcyjnej w środowisku deweloperskim, bo to tam najczęściej trafia konfiguracja skopiowana na szybko.

Warto też pamiętać, że w PostgreSQL istnieje mechanizm ograniczania dostępu na poziomie wierszy, a nie tylko tabel. Przy bazie wielodostępnej, w której jedna tabela przechowuje dane wielu klientów, to jedyny sposób, żeby asystent widział wyłącznie dane właściwego klienta, i działa niezależnie od tego, jak zostanie sformułowane zapytanie.

Co asystent naprawdę potrafi z bazą

Warto opisać zastosowania konkretnie, bo od nich zależy, czy ryzyko ma uzasadnienie.

Najbardziej użyteczne jest odczytanie struktury. Pytanie o to, które tabele przechowują dane o zamówieniach i jak są ze sobą powiązane, oszczędza godzinę czytania schematu przy wejściu w nieznany projekt.

Drugie to pisanie zapytań. Model znający strukturę tworzy sensowne zapytania analityczne szybciej, niż zajmuje przypomnienie sobie składni funkcji okna. Warto natomiast czytać, co powstało, bo zapytanie zwracające liczbę nie mówi, czy liczy to, o co pytałeś.

Trzecie to diagnostyka. Zapytanie o plan wykonania, o brakujące indeksy albo o tabele rosnące najszybciej daje odpowiedzi, do których w innym trybie trzeba dojść przez kilka narzędzi.

Czego nie warto robić: modyfikacji danych. Kuszące jest pozwolenie asystentowi na poprawienie jednego rekordu, a różnica między poprawieniem jednego i poprawieniem wszystkich sprowadza się do brakującego warunku, którego model nie zauważy, bo zapytanie wykona się poprawnie.

Migracje schematu również należą do człowieka. Nie dlatego, że model ich nie napisze, tylko dlatego, że przegląd zmiany struktury bazy przed wykonaniem jest tańszy niż odtwarzanie z kopii.

Sensowny układ wygląda tak: model pisze zapytanie i migrację, człowiek je czyta i uruchamia. Asystent ma dostęp do odczytu, więc widzi strukturę i dane potrzebne do napisania sensownego kodu, a wykonanie należy do osoby, która ponosi konsekwencje. To rozdzielenie kosztuje kilkanaście sekund na operację i usuwa całą kategorię problemów.

Wielkość wyniku i kontekst modelu

Osobny problem, mniej groźny od poprzedniego i częściej spotykany w codziennej pracy: zapytanie zwracające za dużo danych.

Model dostaje wynik jako tekst trafiający do kontekstu, więc zapytanie zwracające pięć tysięcy wierszy albo wypełni cały dostępny kontekst, albo zostanie ucięte w połowie. W pierwszym przypadku płacisz za tokeny i tracisz miejsce na resztę rozmowy, w drugim model odpowiada na podstawie fragmentu, nie wiedząc, że czegoś brakuje.

Trzy rzeczy ograniczają ten problem. Pierwsza to domyślne ograniczenie liczby wierszy po stronie serwera, niezależne od tego, co model napisał w zapytaniu. Druga to instrukcja mówiąca wprost, żeby przy pytaniach ilościowych używać agregacji zamiast pobierania wierszy. Trzecia to zwracanie liczby wszystkich pasujących wierszy obok próbki, żeby model wiedział, że widzi wycinek.

Warto też pilnować szerokości. Zapytanie pobierające wszystkie kolumny z tabeli mającej ich czterdzieści zużywa kontekst na dane, których nikt nie potrzebował, a przy tabelach z polami tekstowymi robi to bardzo szybko.

To samo dotyczy odczytu struktury. Schemat bazy z dwustoma tabelami przekazany w całości zajmuje więcej miejsca niż cała reszta rozmowy, więc rozsądny serwer udostępnia listę tabel osobno i opis pojedynczej tabeli na żądanie, zamiast wysyłać wszystko naraz.

Zapis przebiegu i przegląd

Ostatnia warstwa, o której łatwo zapomnieć, a która przy incydencie decyduje o tym, czy wiadomo, co się stało.

Zapisuj wykonane zapytania razem z czasem, identyfikatorem sesji i tym, kto pytał. Baza potrafi to sama, przez ustawienie rejestrujące polecenia dla wybranego konta, więc nie trzeba tego budować w narzędziu.

Ten zapis służy trzem rzeczom. Pierwsza to diagnoza po fakcie, gdy ktoś zauważy, że coś działa wolniej. Druga to przegląd tego, o co ludzie faktycznie pytają, co po miesiącu zwykle pokazuje, że dziesięć pytań powtarza się nieustannie i warto zamienić je na nazwane operacje. Trzecia to wykrycie sytuacji, w której model zaczął pytać o rzeczy spoza swojego zakresu, bo przeczytał coś, co go do tego skłoniło.

Warto też ustawić powiadomienie o zapytaniach przekraczających ustalony czas. To najprostszy sygnał, że ktoś odpytuje bazę produkcyjną w sposób, którego nikt nie przewidział, i jedyny, który przyjdzie sam.

Serwery do wyboru

RozwiązanieMocna stronaSłabośćKiedy wybrać
Zamiennik oficjalnegoNaprawiona luka, zgodny interfejsUtrzymywany przez społecznośćWymiana istniejącej konfiguracji
Serwer z analizą wydajnościDiagnostyka zapytań, tryby ograniczoneWięcej pojęć do przyswojeniaPraca nad wydajnością bazy
Serwer u dostawcy bazyZintegrowany z resztą usługi, uwierzytelnienieDziała wyłącznie u tego dostawcyBaza w chmurze tego dostawcy
Własny serwerDokładnie te zapytania, które dopuszczaszPiszesz i utrzymujesz samWąski zakres i wysokie wymagania

Ostatni wiersz zasługuje na więcej uwagi, niż zwykle dostaje. Zamiast dawać asystentowi możliwość wykonania dowolnego zapytania, możesz wystawić kilka nazwanych operacji: sprawdzenie statusu zamówienia, podsumowanie sprzedaży za okres, listę produktów o niskim stanie. Model wywołuje je z parametrami, a Ty kontrolujesz każde zapytanie.

To rozwiązanie jest bezpieczniejsze i zwykle wystarczające, bo w praktyce asystent pyta o kilkanaście rzeczy, a nie o dowolne. Narzędzia do zbudowania takiego serwera opisuje tekst o zestawie narzędzi protokołu.

Dostawcy baz zarządzanych, w tym opisani w tekstach o Neonie i Supabase, mają własne serwery zintegrowane z uwierzytelnianiem swojej platformy, co przy pracy w ich ekosystemie upraszcza konfigurację.

Typowe błędy

Pierwszy to używanie wycofanego serwera referencyjnego. Tryb tylko do odczytu da się w nim obejść, a projekt nie dostaje poprawek.

Drugi to poleganie na trybie odczytu zapewnianym przez narzędzie zamiast przez bazę. Uprawnienia konta są jedynym ograniczeniem, którego nie da się obejść zapytaniem.

Trzeci to podłączenie asystenta na koncie używanym przez aplikację. To konto ma uprawnienia do zapisu, bo aplikacja ich potrzebuje, więc asystent dostaje je razem z nim.

Czwarty to brak limitu czasu wykonania. Jedno zapytanie bez warunku potrafi obciążyć bazę na minuty, a model nie oszacuje kosztu przed uruchomieniem.

Piąty to dostęp do tabel z danymi osobowymi bez potrzeby. Treść odczytanych wierszy trafia do kontekstu modelu, więc zakres warto zawęzić do tabel faktycznie potrzebnych.

Szósty to pozwalanie na modyfikację danych. Różnica między poprawieniem jednego rekordu a wszystkich to brakujący warunek, którego model nie zauważy, bo zapytanie wykona się poprawnie i nie zgłosi żadnego błędu.

Siódmy to brak ograniczenia liczby zwracanych wierszy. Zapytanie oddające pięć tysięcy rekordów wypełnia kontekst modelu danymi, których nikt nie czytał, i kosztuje przy każdym takim wywołaniu.

FAQ

Czy oficjalny serwer PostgreSQL do MCP jest bezpieczny?

Nie. Został zarchiwizowany w 2025 roku i oznaczony jako wycofany, a jego tryb tylko do odczytu da się obejść: zapytanie może zamknąć transakcję ochronną i wykonać dowolną operację z pełnymi uprawnieniami połączenia. Istnieją zamienniki naprawiające tę lukę.

Jak zapewnić, że asystent tylko czyta?

Osobnym kontem w bazie z uprawnieniami wyłącznie do odczytu i wyłącznie do wybranych tabel. To jedyne zabezpieczenie działające niezależnie od treści zapytania, bo baza odmówi wykonania zapisu, cokolwiek przyjdzie od modelu.

Czy asystent może usunąć dane?

Może, jeśli połączy się kontem mającym takie uprawnienia. Przy koncie ograniczonym do odczytu nie, i właśnie dlatego podłączanie asystenta na koncie używanym przez aplikację jest poważnym błędem konfiguracji.

Czy warto dać dostęp do bazy produkcyjnej?

Do odczytu, przez konto ograniczone i najlepiej do repliki, tak. Bezpośrednio do bazy obsługującej użytkowników lepiej nie, bo jedno źle napisane zapytanie potrafi ją obciążyć na minuty, a limit czasu wykonania nie zawsze zdąży zadziałać.

Czy lepiej napisać własny serwer?

Przy wysokich wymaganiach zwykle tak. Kilka nazwanych operacji z parametrami zamiast dowolnych zapytań daje pełną kontrolę nad tym, co trafia do bazy, a w praktyce pokrywa większość pytań, które asystent i tak zadaje.

Analizę luki opisuje opracowanie zespołu badawczego, a zarchiwizowany kod znajdziesz w repozytorium wycofanych serwerów.