Projektowanie baz danych i zapytania
ENSerwer 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.
Co zyskujesz na pracy z bazą przez agenta
Dział zatytułowany „Co zyskujesz na pracy z bazą przez agenta”- 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.
Który serwer MCP do bazy danych podłączyć?
Dział zatytułowany „Który serwer MCP do bazy danych podłączyć?”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.
| Serwer | Silniki | Wybierz, gdy | Kontrola trybu tylko do odczytu |
|---|---|---|---|
Postgres MCP Pro (postgres-mcp, PyPI) | PostgreSQL | Stroisz 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, SQLite | Masz 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 Supabase | Baza działa w Supabase | parametr URL read_only=true |
MongoDB MCP Server (mongodb-mcp-server, npm) | MongoDB, Atlas | Bazy dokumentowe i potoki agregacji | flaga --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 valuesGRANT 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.
Podłącz serwer w Claude Code, Codeksie i Cursorze
Dział zatytułowany „Podłącz serwer w Claude Code, Codeksie i Cursorze”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:
claude mcp add postgres -s project \ -e DATABASE_URI=postgresql://mcp_readonly@localhost:5432/app_dev \ -- uvx postgres-mcp --access-mode=restrictedclaude mcp listCzł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ę.
Uruchom w terminalu:
codex mcp add postgres \ --env DATABASE_URI=postgresql://mcp_readonly@localhost:5432/app_dev \ -- uvx postgres-mcp --access-mode=restrictedcodex mcp listMożesz też sam wpisać tabelę do ~/.codex/config.toml. env_vars przekazuje zmienną z twojej powłoki zamiast zapisywać jej wartość, a enabled_tools skraca listę narzędzi (oba klucze sprawdzone w źródłach codex-cli 0.157.1):
[mcp_servers.postgres]command = "uvx"args = ["postgres-mcp", "--access-mode=restricted"]env_vars = ["DATABASE_URI"]enabled_tools = ["list_schemas", "list_objects", "get_object_details", "execute_sql", "explain_query", "get_top_queries", "analyze_query_indexes"]Wyeksportuj DATABASE_URI w powłoce, z której uruchamiasz Codeksa (na przykład export DATABASE_URI=postgresql://mcp_readonly@localhost:5432/app_dev); inaczej serwer wystartuje bez ciągu połączenia.
Codex bez żadnego błędu ignoruje nieznane klucze, więc literówka w nazwie klucza po cichu nic nie robi. Uruchom codex mcp get postgres, żeby zobaczyć, co faktycznie wczytał.
Dodaj serwer do .cursor/mcp.json (projekt) albo ~/.cursor/mcp.json (globalnie):
{ "mcpServers": { "postgres": { "command": "uvx", "args": ["postgres-mcp", "--access-mode=restricted"], "env": { "DATABASE_URI": "postgresql://mcp_readonly@localhost:5432/app_dev" } } }}Włącz serwer w ustawieniach MCP w Cursorze i upewnij się, że jego narzędzia są na liście, zanim zaczniesz czat, który od nich zależy.
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 = truemax_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 databaseCREATE 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 fileCREATE INDEX CONCURRENTLY products_created_at_price_idx ON products (created_at, price);
-- Query rewrite: aggregate reviews once per product, then joinWITH 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_countFROM products pLEFT JOIN product_stats ps ON ps.product_id = p.idWHERE 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.
-
Expand. Dodaj kolumnę dopuszczającą
NULL. To zmiana wyłącznie w metadanych, ale nadal wymaga krótkiej blokadyACCESS EXCLUSIVE, więc najpierw ustawlock_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łędemlock_timeout.SET lock_timeout = '3s';ALTER TABLE users ADD COLUMN email_normalized text; -
Podwójny zapis. Wdróż kod aplikacji, który zapisuje
email_normalizedprzy każdym wstawieniu i aktualizacji wiersza. -
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
UPDATEalbo pętla, która nigdy nie robiCOMMIT, trzyma wszystkie blokady wierszy do samego końca. Filtremail IS NOT NULLsprawia, ż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 walidacjaNOT NULLsię nie powiedzie.UPDATE users SET email_normalized = lower(trim(email))WHERE id IN (SELECT id FROM usersWHERE email_normalized IS NULL AND email IS NOT NULLORDER BY id LIMIT 10000); -
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 CONCURRENTLYprzed 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śliADD CONSTRAINT ... NOT VALIDiVALIDATEdzielą transakcję, skan walidacji odbywa się pod blokadąACCESS EXCLUSIVEi blokuje zapisy.-- Migration A (non-transactional): duplicate check + CREATE UNIQUE INDEX CONCURRENTLYSELECT email_normalized, count(*) FROM usersGROUP BY 1 HAVING count(*) > 1;CREATE UNIQUE INDEX CONCURRENTLY users_email_normalized_keyON users (email_normalized);-- Migration B: brief ACCESS EXCLUSIVE lock; commit before validatingSET lock_timeout = '3s';ALTER TABLE users ADD CONSTRAINT users_email_normalized_nnCHECK (email_normalized IS NOT NULL) NOT VALID;-- Migration C: VALIDATE scans without blocking writes; SET NOT NULL locks only brieflySET lock_timeout = '3s';ALTER TABLE users VALIDATE CONSTRAINT users_email_normalized_nn;-- PostgreSQL 12+ uses the validated check and skips the full-table scanALTER TABLE users ALTER COLUMN email_normalized SET NOT NULL;ALTER TABLE users DROP CONSTRAINT users_email_normalized_nn; -
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.
| Bramka | Co wyłapuje | Gdzie działa | Kto 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 blokad | CI, dla każdego pliku migracji | Tech lead ustawia .squawk.toml |
| W górę, w dół, w górę na jednorazowej kopii | Migracje, które się wywracają, i rollbacki, które nie przywracają schematu | CI, w jednorazowym kontenerze Postgres odtworzonym z pg_dump --schema-only | Autor |
| Diff schematu | Niezamierzone zmiany: usunięty indeks, zmieniona wartość domyślna | CI: diff pg_dump --schema-only przed i po | Recenzent czyta diff, nie SQL |
| Zapytania niezmiennikowe | Osierocone wiersze, duplikaty, utracone dane po uzupełnianiu | CI i po każdej fazie na produkcji: każde zapytanie musi zwrócić zero wierszy | Tech lead albo DBA pisze je jako pierwsze |
| Dowód z planu | „Poprawkę”, która nie zmienia planu | Wynik explain_query przed i po wklejony do pull requesta | Autor |
| Testy integracyjne na prawdziwej bazie | Kod aplikacji, który psuje się na nowym schemacie | CI | Zespół |
Squawk to jedno polecenie w CI (wersja 2.66.0 w npm, sprawdzone 2026-09-26):
npx --package squawk-cli squawk migrations/*.sqlZatwierdzanie 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ę.
Wymuś izolację najemców przez row-level security
Dział zatytułowany „Wymuś izolację najemców przez row-level security”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.
Dodaj wiedzę o Postgresie przez skill
Dział zatytułowany „Dodaj wiedzę o Postgresie przez skill”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.
claude plugin marketplace add supabase/agent-skillsclaude plugin install postgres-best-practices@supabase-agent-skillsnpx skills add supabase/agent-skills --skill supabase-postgres-best-practicesnpx skills add supabase/agent-skills --skill supabase-postgres-best-practicesUż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.
Co się psuje, gdy agenci pracują na bazie danych?
Dział zatytułowany „Co się psuje, gdy agenci pracują na bazie danych?”Dokąd dalej z pracą na bazach danych
Dział zatytułowany „Dokąd dalej z pracą na bazach danych”- Serwery MCP dla baz SQL i NoSQL: pełny katalog serwerów z poleceniami instalacji dla każdego silnika.
- Wzorce migracji baz danych i wzorce optymalizacji SQL: przepisy do przekazania agentowi, gdy ten przepływ już działa.
- Strategie danych testowych i fixtures: realistyczne dane dla jednorazowej kopii, zgodne z nowymi ograniczeniami.
- Tworzenie API z AI: warstwa zbudowana na właśnie zmienionym schemacie.