Przejdź do głównej zawartości

Serwer MCP do bazy danych łączy Claude Code, Codex lub Cursor z działającą bazą przez rolę tylko do odczytu. Agent projektuje wtedy zmiany schematu, czyta prawdziwe plany wykonania i planuje migracje bez przestojów na podstawie faktycznego schematu, a nie wklejonego zrzutu. Agent pisze pliki migracji; bramki CI i jednorazowa kopia bazy dowodzą ich poprawności, zanim ktokolwiek zatwierdzi wdrożenie.

Ten przewodnik jest dla programistów, którzy odpowiadają za warstwę danych serwisu, i dla tech leadów, którzy zatwierdzają jego migracje. Sytuacja, którą rozwiązuje: zmiana kolumny w tabeli z dziesiątkami milionów wierszy, graf kluczy obcych, którego nikt nie rysował od lat, i agent, który zwraca pewny siebie SQL z nazwami nieistniejących kolumn, bo widział tylko połowę schematu.

  • Utrzymywany serwer MCP do bazy danych podłączony do agenta przez rolę, która nie może pisać.
  • Cztery prompty do skopiowania: projekt schematu, diagnoza wolnego zapytania, plan migracji bez przestojów i skrypt weryfikacyjny (harness).
  • Bramkę migracji do CI (lint, migracja w górę i w dół na kopii, zapytania niezmiennikowe), dzięki której zatwierdzasz dowody, a nie SQL linijka po linijce.
  • Tryby awarii, które pojawiają się najwcześniej, razem ze sposobem naprawy każdego z nich.

Strony poszczególnych narzędzi schodzą głębiej w ich specyfikę: praca z bazą w Claude Code, praca z bazą w Codeksie i projektowanie bazy w Cursorze.

Wybierz serwer według silnika bazy i tego, co agent ma robić. Każda opcja poniżej działa w Claude Code, Codeksie i Cursorze; katalog serwerów MCP do baz danych omawia każdą z nich szczegółowo.

Jeśli dopiero zaczynasz z MCP, przeczytaj najpierw serwery MCP.

SerwerSilnikiWybierz, gdyKontrola trybu tylko do odczytu
Postgres MCP Pro (postgres-mcp, PyPI)PostgreSQLStroisz zapytania: ma explain_query z hipotetycznymi indeksami, get_top_queries, rekomendacje indeksów i analyze_db_health--access-mode=restricted (transakcje tylko do odczytu plus limit czasu wykonania)
DBHub (@bytebase/dbhub, npm)PostgreSQL, MySQL, MariaDB, SQL Server, Oracle, SQLiteMasz kilka silników albo chcesz małej liczby narzędzi (domyślnie dwa)readonly = true dla narzędzia w dbhub.toml
Supabase MCP (zdalny)Postgres w SupabaseBaza działa w Supabaseparametr URL read_only=true
MongoDB MCP Server (mongodb-mcp-server, npm)MongoDB, AtlasBazy dokumentowe i potoki agregacjiflaga --readOnly

Przed wyborem poznaj dwa kompromisy. Postgres MCP Pro ma najgłębsze narzędzia do strojenia, ale jego ostatnie wydanie w PyPI to 0.3.0 z 2025-05-16 (sprawdzone 2026-09-26), więc przypnij wersję i obserwuj repozytorium. Definicje narzędzi nadal kosztują kontekst: Claude Code i Codex ładują je na żądanie przez wyszukiwanie narzędzi (tool search), ale Cursor i inni klienci mogą ładować wszystkie narzędzia od razu, więc domyślne dwa narzędzia DBHub utrzymują długie sesje w ryzach w każdym kliencie; zobacz jak ograniczyć koszt tokenów MCP.

Utwórz rolę tylko do odczytu, zanim cokolwiek podłączysz

Dział zatytułowany „Utwórz rolę tylko do odczytu, zanim cokolwiek podłączysz”

Przełącznik trybu tylko do odczytu w serwerze to druga linia obrony. Pierwszą jest rola w bazie, która nie może pisać, więc wygenerowany UPDATE się nie wykona, nawet jeśli flaga jest źle ustawiona. Uruchom to jako administrator na bazie deweloperskiej lub stagingowej:

CREATE ROLE mcp_readonly WITH LOGIN;
GRANT CONNECT ON DATABASE app_dev TO mcp_readonly;
GRANT USAGE ON SCHEMA public TO mcp_readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO mcp_readonly;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO mcp_readonly;
-- Lets the agent read pg_stat_statements; query texts from all roles become visible and may contain literal values
GRANT pg_read_all_stats TO mcp_readonly;
ALTER ROLE mcp_readonly SET statement_timeout = '30s';

Hasło roli ustaw poleceniem \password mcp_readonly w psql i zapisz je w ~/.pgpass (uprawnienia 0600), a nie w żadnej konfiguracji MCP. Postgres MCP Pro łączy się przez libpq, które czyta ~/.pgpass, więc ciąg połączenia (connection string) przekazany agentowi nie zawiera sekretu i można go bezpiecznie zapisać w repozytorium.

Do strojenia zapytań Postgres MCP Pro potrzebuje też rozszerzeń pg_stat_statements i hypopg w docelowej bazie. W zarządzanym Postgresie (RDS, Cloud SQL, Azure) oba są zwykle dostępne przez CREATE EXTENSION; w samodzielnie utrzymywanym Postgresie pg_stat_statements musi najpierw trafić do shared_preload_libraries.

Serwer i jego argumenty są identyczne we wszystkich trzech narzędziach; różni się tylko miejsce konfiguracji.

Uruchom w terminalu, w katalogu głównym repozytorium. -s project zapisuje .mcp.json, więc cały zespół dzieli ten wpis:

Okno terminala
claude mcp add postgres -s project \
-e DATABASE_URI=postgresql://mcp_readonly@localhost:5432/app_dev \
-- uvx postgres-mcp --access-mode=restricted
claude mcp list

Członek zespołu, który pobierze repozytorium, zobaczy serwer w claude mcp list jako oczekujący na zatwierdzenie, dopóki nie zatwierdzi go w sesji. .mcp.json rozwija też ${VAR} i ${VAR:-default}, więc każdy programista może skierować DATABASE_URI na własną bazę.

Dla innego silnika podmień polecenie. DBHub czyta ustawienia z pliku dbhub.toml wskazanego flagą --config i interpoluje ${VAR}, więc hasło zostaje w twoim środowisku:

[[sources]]
id = "app_dev"
dsn = "postgres://mcp_readonly:${DB_PASSWORD}@localhost:5432/app_dev"
[[tools]]
name = "execute_sql"
source = "app_dev"
readonly = true
max_rows = 1000
[[tools]]
name = "search_objects"
source = "app_dev"
[[tools]]
name = "explain_sql"
source = "app_dev"

Następnie zarejestruj npx @bytebase/dbhub@latest --transport stdio --config ./dbhub.toml jako polecenie, tak samo jak wyżej (sprawdzone z @bytebase/dbhub 1.4.0). DBHub wczytuje dbhub.toml tylko wtedy, gdy wskazuje go --config; nie szuka tego pliku w katalogu roboczym. Dla MongoDB polecenie to npx -y mongodb-mcp-server@latest --readOnly, a ciąg połączenia trafia do MDB_MCP_CONNECTION_STRING.

Zaprojektuj zmianę schematu dopasowaną do tego, co już masz

Dział zatytułowany „Zaprojektuj zmianę schematu dopasowaną do tego, co już masz”

Wartość połączenia polega na tym, że agent czyta twoje konwencje (typy kluczy, nazewnictwo, kolumny czasu), zanim cokolwiek zaproponuje. Proś o plik migracji w narzędziu migracji twojego projektu, nigdy o DDL wykonany przez serwer MCP.

Wynik oparty na prawdziwym schemacie dopasowuje się do users.id (tutaj uuid), zamiast zgadywać bigint, i egzekwuje regułę biznesową w bazie:

CREATE TABLE subscriptions (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
user_id uuid NOT NULL REFERENCES users (id) ON DELETE CASCADE,
plan_id uuid NOT NULL REFERENCES plans (id),
status text NOT NULL
CHECK (status IN ('trialing', 'active', 'past_due', 'canceled')),
current_period_end timestamptz NOT NULL,
created_at timestamptz NOT NULL DEFAULT now()
);
-- One active subscription per user, enforced by the database
CREATE UNIQUE INDEX subscriptions_one_active_per_user
ON subscriptions (user_id) WHERE status = 'active';
CREATE INDEX subscriptions_user_id_idx ON subscriptions (user_id);
CREATE INDEX subscriptions_plan_id_idx ON subscriptions (plan_id);

Przeglądasz listę ograniczeń i reguł biznesowych, a nie DDL. Jeśli przy regule, na której ci zależy, nie stoi żadne ograniczenie, projekt jest niekompletny.

Zdiagnozuj wolne zapytanie na podstawie prawdziwego planu

Dział zatytułowany „Zdiagnozuj wolne zapytanie na podstawie prawdziwego planu”

Porada o indeksie bez planu to zgadywanie. Postgres MCP Pro czyta pg_stat_statements, żeby znaleźć kosztowne zapytania, i używa hypopg do symulacji indeksu, zanim ktokolwiek go utworzy, więc agent pokazuje plan przed i po bez dotykania bazy.

Typowa poprawka wstępnie agreguje złączenie jeden-do-wielu, które mnożyło wiersze, dodaje indeks pokrywający na reviews i jeden indeks pod filtr:

-- Migration: indexes, created without blocking writes.
-- Each CREATE INDEX CONCURRENTLY goes in its own non-transactional migration file.
CREATE INDEX CONCURRENTLY reviews_product_id_rating_idx
ON reviews (product_id) INCLUDE (rating);
-- Separate migration file
CREATE INDEX CONCURRENTLY products_created_at_price_idx
ON products (created_at, price);
-- Query rewrite: aggregate reviews once per product, then join
WITH product_stats AS (
SELECT product_id, avg(rating) AS avg_rating, count(*) AS review_count
FROM reviews
GROUP BY product_id
)
SELECT p.id, p.name, p.price, ps.avg_rating, ps.review_count
FROM products p
LEFT JOIN product_stats ps ON ps.product_id = p.id
WHERE p.price BETWEEN 100 AND 500
AND p.created_at > now() - interval '2 years'
ORDER BY ps.avg_rating DESC NULLS LAST;

Jeśli agent zaproponuje indeks częściowy w rodzaju ON products (price) WHERE created_at > now() - interval '2 years', odrzuć go: PostgreSQL wymaga, by predykat indeksu był IMMUTABLE, a now() jest STABLE, więc baza odmówi utworzenia indeksu. now() w zapytaniu powyżej jest w porządku, bo stoi w klauzuli WHERE, a nie w indeksie. Agenci proponują takie indeksy; przebieg w górę, w dół i w górę na jednorazowej kopii (zobacz bramki weryfikacyjne poniżej) wywraca się na nim jeszcze przed przeglądem.

Zaplanuj migrację bez przestojów metodą expand and contract

Dział zatytułowany „Zaplanuj migrację bez przestojów metodą expand and contract”

Zmiany w dużych tabelach wychodzą jako małe, odwracalne kroki rozłożone na kilka wdrożeń. Agent dobrze radzi sobie z planem fazowym i analizą blokad; to plan jest przedmiotem przeglądu.

  1. Expand. Dodaj kolumnę dopuszczającą NULL. To zmiana wyłącznie w metadanych, ale nadal wymaga krótkiej blokady ACCESS EXCLUSIVE, więc najpierw ustaw lock_timeout. Jeśli blokada nie zostanie przyznana na czas, polecenie kończy się błędem, zamiast czekać w kolejce za długą transakcją i blokować wszystkie zapytania za sobą; niech skrypt wdrożeniowy ponawia migrację, gdy ta zakończy się błędem lock_timeout.

    SET lock_timeout = '3s';
    ALTER TABLE users ADD COLUMN email_normalized text;
  2. Podwójny zapis. Wdróż kod aplikacji, który zapisuje email_normalized przy każdym wstawieniu i aktualizacji wiersza.

  3. Uzupełnianie danych w zatwierdzanych partiach. Uruchamiaj to ze skryptu w pętli, zatwierdzając każdą partię, aż zapytanie zaktualizuje zero wierszy. Jeden wielki UPDATE albo pętla, która nigdy nie robi COMMIT, trzyma wszystkie blokady wierszy do samego końca. Filtr email IS NOT NULL sprawia, że pętla nie wybiera w kółko tych samych wierszy; przed krokiem 4 zdecyduj, co dostają wiersze z pustym (NULL) adresem e-mail (wartość domyślną albo wyłączenie spod ograniczenia), bo inaczej walidacja NOT NULL się nie powiedzie.

    UPDATE users SET email_normalized = lower(trim(email))
    WHERE id IN (
    SELECT id FROM users
    WHERE email_normalized IS NULL AND email IS NOT NULL
    ORDER BY id LIMIT 10000
    );
  4. Egzekwowanie bez blokowania zapisów. Najpierw sprawdź duplikaty; budowa unikalnego indeksu przerywa się na pierwszym z nich. Nieudana budowa zostawia indeks INVALID; usuń go przez DROP INDEX CONCURRENTLY przed ponowną próbą. Trzy części trzymaj w osobnych plikach migracji: współbieżna budowa indeksu nie może działać w transakcji, a jeśli ADD CONSTRAINT ... NOT VALID i VALIDATE dzielą transakcję, skan walidacji odbywa się pod blokadą ACCESS EXCLUSIVE i blokuje zapisy.

    -- Migration A (non-transactional): duplicate check + CREATE UNIQUE INDEX CONCURRENTLY
    SELECT email_normalized, count(*) FROM users
    GROUP BY 1 HAVING count(*) > 1;
    CREATE UNIQUE INDEX CONCURRENTLY users_email_normalized_key
    ON users (email_normalized);
    -- Migration B: brief ACCESS EXCLUSIVE lock; commit before validating
    SET lock_timeout = '3s';
    ALTER TABLE users ADD CONSTRAINT users_email_normalized_nn
    CHECK (email_normalized IS NOT NULL) NOT VALID;
    -- Migration C: VALIDATE scans without blocking writes; SET NOT NULL locks only briefly
    SET lock_timeout = '3s';
    ALTER TABLE users VALIDATE CONSTRAINT users_email_normalized_nn;
    -- PostgreSQL 12+ uses the validated check and skips the full-table scan
    ALTER TABLE users ALTER COLUMN email_normalized SET NOT NULL;
    ALTER TABLE users DROP CONSTRAINT users_email_normalized_nn;
  5. Contract. Przełącz odczyty na nową kolumnę, a stare ścieżki kodu usuń w późniejszym wydaniu, gdy nowa kolumna obsłuży już ruch produkcyjny.

Jak zweryfikować migrację napisaną przez agenta bez czytania każdej linijki?

Dział zatytułowany „Jak zweryfikować migrację napisaną przez agenta bez czytania każdej linijki?”

Zatwierdzasz dowody. Zdefiniuj kontrole, zanim agent napisze migrację, trzymaj je w katalogu, którego agent nie może edytować (zobacz ochronę wyroczni), i pozwól CI uruchamiać je dla każdego pliku migracji.

BramkaCo wyłapujeGdzie działaKto za nią odpowiada
Lint migracji w Squawk (squawk-cli, npm i PyPI)Blokujące budowy indeksów, niebezpieczne NOT NULL, przepisywanie tabel i inne ryzyka blokadCI, dla każdego pliku migracjiTech lead ustawia .squawk.toml
W górę, w dół, w górę na jednorazowej kopiiMigracje, które się wywracają, i rollbacki, które nie przywracają schematuCI, w jednorazowym kontenerze Postgres odtworzonym z pg_dump --schema-onlyAutor
Diff schematuNiezamierzone zmiany: usunięty indeks, zmieniona wartość domyślnaCI: diff pg_dump --schema-only przed i poRecenzent czyta diff, nie SQL
Zapytania niezmiennikoweOsierocone wiersze, duplikaty, utracone dane po uzupełnianiuCI i po każdej fazie na produkcji: każde zapytanie musi zwrócić zero wierszyTech lead albo DBA pisze je jako pierwsze
Dowód z planu„Poprawkę”, która nie zmienia planuWynik explain_query przed i po wklejony do pull requestaAutor
Testy integracyjne na prawdziwej bazieKod aplikacji, który psuje się na nowym schemacieCIZespół

Squawk to jedno polecenie w CI (wersja 2.66.0 w npm, sprawdzone 2026-09-26):

Okno terminala
npx --package squawk-cli squawk migrations/*.sql

Zatwierdzanie wygląda tak: człowiek odpowiedzialny, tech lead albo DBA, akceptuje plan fazowy, raport blokad i zieloną bramkę, a potem CI stosuje każdą migrację w jej wdrożeniu. Nikt nie wykonuje DDL przez serwer MCP i nikt nie kieruje agenta z prawem zapisu na produkcję.

Row-level security (RLS) to obszar wysokiego ryzyka, bo zawodzi po cichu: w tabeli bez włączonego RLS, dla właściciela tabeli bez FORCE albo dla roli z BYPASSRLS widoczne są wiersze wszystkich najemców. Proś agenta jednocześnie o polityki i o testy, które dowodzą, że działają.

O tym, czy polityka działa, decydują dwa szczegóły. Właściciele tabel omijają RLS, chyba że tabela ma też ALTER TABLE ... FORCE ROW LEVEL SECURITY, a pooler połączeń w trybie transakcyjnym nie zachowuje sesyjnego SET, więc najemcę trzeba ustawiać w każdej transakcji.

Serwer MCP daje agentowi dostęp; skill daje mu reguły do zastosowania. Supabase publikuje supabase-postgres-best-practices, który obejmuje wydajność Postgresa i RLS i działa z każdą bazą Postgres, nie tylko w Supabase. Codex i Cursor instalują skill tym samym poleceniem skills CLI.

Okno terminala
claude plugin marketplace add supabase/agent-skills
claude plugin install postgres-best-practices@supabase-agent-skills

Używaj skilla razem z serwerem MCP: serwer dostarcza fakty o twoim schemacie, a skill listę kontrolną do przeglądu. Zakresy instalacji i aktualizacje opisuje strona instalowanie skilli i zarządzanie nimi.