Serveur MCP
@rebasepro/mcp est un serveur Model Context Protocol
qui fournit à un assistant IA de véritables outils sur un projet Rebase : lire et
écrire des lignes, gérer les utilisateurs, exécuter des migrations, invoquer des fonctions,
piloter le serveur de développement.
Il communique en MCP via stdio uniquement. Il n’y a ni port ni écouteur réseau — le processus dispose exactement du même niveau de confiance que ce qui l’a lancé, et il n’y a aucun appelant distant à authentifier. C’est la partie sécurisée. Les questions intéressantes concernent toutes ce qu’il fait une fois en cours d’exécution, et cette page y répond avant de vous présenter le bloc de configuration.
Un backend déployé peut également servir du MCP lui-même, via HTTP, aux personnes qui utilisent votre application. Il s’agit d’une chose différente avec un modèle d’identifiants différent : voir Le point de terminaison distant.
Connecter un client
Section intitulée « Connecter un client »Le serveur s’exécute depuis votre projet : @rebasepro/mcp est une devDependency
que chaque scaffold rebase init épingle avec la CLI, et chaque bloc ci-dessous — l’intégration
complète — démarre cette copie (pnpm exec rebase-mcp, ou npx --no rebase-mcp dans un projet
npm), jamais une plus récente depuis npm. Un projet plus ancien l’ajoute une fois, avec
rebase skills install --mcp ou pnpm add -D @rebasepro/mcp.
rebase init écrit le bloc pour chaque agent sélectionné lorsqu’il
configure vos agents de codage IA, tout en
conservant les autres serveurs déjà présents dans le fichier. rebase init --agent cursor,codex fait
de même sans poser de questions.
Claude Code — .mcp.json à la racine de votre projet. rebase init écrit ce
fichier pour vous :
{ "mcpServers": { "rebase": { "command": "pnpm", "args": ["exec", "rebase-mcp"], "env": { "REBASE_PROJECT_DIR": "." } } }}Cursor — la même structure, dans .cursor/mcp.json. Cursor résout
${workspaceFolder} par la racine du projet :
{ "mcpServers": { "rebase": { "command": "pnpm", "args": ["exec", "rebase-mcp"], "env": { "REBASE_PROJECT_DIR": "${workspaceFolder}" } } }}Gemini CLI — .gemini/settings.json, sous la même clé :
{ "mcpServers": { "rebase": { "command": "pnpm", "args": ["exec", "rebase-mcp"], "env": { "REBASE_PROJECT_DIR": "." } } }}Codex CLI — en TOML plutôt qu’en JSON, dans le fichier .codex/config.toml du projet.
Codex ne lit la configuration d’un projet qu’après que vous avez approuvé le projet :
[mcp_servers.rebase]command = "pnpm"args = ["exec", "rebase-mcp"]
[mcp_servers.rebase.env]REBASE_PROJECT_DIR = "."Kiro — .kiro/settings/mcp.json :
{ "mcpServers": { "rebase": { "command": "pnpm", "args": ["exec", "rebase-mcp"], "env": { "REBASE_PROJECT_DIR": "." } } }}GitHub Copilot dans VS Code — .vscode/mcp.json, sous servers et avec
un transport explicite :
{ "servers": { "rebase": { "type": "stdio", "command": "pnpm", "args": ["exec", "rebase-mcp"], "env": { "REBASE_PROJECT_DIR": "${workspaceFolder}" } } }}Windsurf lit les serveurs MCP uniquement depuis sa configuration utilisateur, il n’y a donc
aucun fichier de projet à écrire. Ajoutez le serveur dans ses paramètres MCP avec
"command": "pnpm", "args": ["--dir", "/absolute/path/to/your/project", "exec", "rebase-mcp"], avec ce chemin comme REBASE_PROJECT_DIR.
Tout client MCP capable de lancer un serveur stdio fonctionne ; la structure reste la même.
Sur quel répertoire il opère
Section intitulée « Sur quel répertoire il opère »REBASE_PROJECT_DIR est le répertoire contenant rebase.json. Il existe un seul
ordre de priorité, et il est identique dans chaque client :
- Le bloc d’environnement —
REBASE_PROJECT_DIR,REBASE_BASE_URL,REBASE_API_TOKEN. Si l’un d’eux est défini, le projetdefaultest reconstruit à partir de ceux-ci à chaque démarrage. - Le répertoire de travail du serveur, lorsqu’il contient un
rebase.json. Un projet dans lequel vous vous trouvez a la priorité sur tout ce qui est mémorisé dans~/.rebase/projects.json. - Le projet
defaultpersistant dans~/.rebase/projects.json, lorsque aucun des deux premiers n’indique quoi que ce soit.
L’auto-découverte à partir de .rebase/state.json comble les manques dans les trois cas et n’écrase
jamais une valeur fournie par l’un d’eux.
Les blocs au niveau du projet désignent le projet — ".", le répertoire de travail du client ou
${workspaceFolder} de l’éditeur — car la règle 3 lit un fichier partagé par tous les projets
de la machine. Une configuration au niveau utilisateur, telle que celle de Windsurf, spécifie
quant à elle un chemin absolu.
Ce à quoi le serveur peut accéder
Section intitulée « Ce à quoi le serveur peut accéder »C’est la section à lire avant de pointer un assistant vers une base de données à laquelle vous tenez.
Le serveur utilise un identifiant ambiant unique pour l’ensemble du processus. Il n’y a pas d’identité par outil ni de mode lecture seule ; chaque outil utilise le même jeton, et le seul commutateur du paquet permet d’étendre la portée plutôt que de la restreindre.
De quel identifiant il s’agit, par ordre de priorité :
REBASE_API_TOKEN/REBASE_TOKENissu de l’environnementREBASE_SERVICE_KEYlu dans le fichier.envdu projet- La clé de service découverte automatiquement depuis
.rebase/state.jsonpendant querebase devs’exécute
Un jeton enregistré pour un projet l’emporte sur l’auto-découverte. La découverte ne fait que combler un manque.
Lui attribuer un identifiant aux droits restreints
Section intitulée « Lui attribuer un identifiant aux droits restreints »Enregistrez une clé d’API restreinte et le modèle à deux verrous s’applique
réellement. Une clé de service s’exécute avec les rôles ["service"], que les politiques
d’administration injectées ne mentionnent pas — ainsi, le RLS ne lui accorde rien à moins que l’une
de vos propres politiques n’en dispose autrement, et ses portées la restreignent davantage :
rebase api-keys create -n "claude-code" \ --scopes data:read:articles \ --expires-in 30Transmettez ensuite la clé rk_live_… obtenue au serveur au lieu de le laisser découvrir une clé de service :
{ "mcpServers": { "rebase": { "command": "pnpm", "args": ["exec", "rebase-mcp"], "env": { "REBASE_PROJECT_DIR": "/absolute/path/to/your/project", "REBASE_API_TOKEN": "rk_live_..." } } }}Deux choses que cela ne fait pas, toutes deux importantes à savoir avant de vous reposer dessus :
- Cela ne restreint pas les outils CLI.
rebase_db_push,rebase_db_migrate,rebase_doctoret les outils de branches lancent la CLI Rebase, qui se connecte avecDATABASE_URLet ne voit jamais votre jeton. Le verrou loopback ci-dessous est le seul garde-fou face à ceux-ci. - Une clé n’atteint un outil d’administration qu’avec la portée de cet outil.
list_usersetlist_rolesexigentusers:read;create_user,update_user,delete_useretrebase_auth_reset_passwordexigentusers:write; les outils de stockage et de cron exigent la portéestorage:*oucron:*correspondante ;invoke_functionexigefunctions:invoke. Sans elle, l’appel répond403 SCOPE_MISSING. Même avecusers:write, une clé ne peut pas modifier le compte d’un administrateur : un administrateur détientkeys:readetkeys:write, qu’aucune clé ne peut détenir, et personne ne peut gérer un compte qui détient plus que lui.
Une clé créée avec --roles admin est une autre affaire : elle porte les rôles
["service", "admin"], ce qui valide les mêmes politiques admin par défaut que la
clé de service. Ajoutez-lui aussi --full-access et sa portée est celle de la clé de service,
moins la gestion des clés. Son avantage est qu’elle est révocable, dotée d’une expiration et soumise
à une limitation de débit par clé, ce qui n’est pas le cas de la clé de service — renouveler cette
dernière implique de modifier .env et de redémarrer le serveur.
Consultez Agents et serveurs MCP pour le guide complet sur la restriction des clés.
Rendre une collection totalement inaccessible
Section intitulée « Rendre une collection totalement inaccessible »La raison pour laquelle un identifiant administrateur lit tout est la politique de base que Rebase
injecte dans chaque collection, accordant l’accès au contexte de serveur de confiance et au
rôle admin. Une collection peut désactiver cette politique de base et assumer l’entière
responsabilité de son propre RLS :
import { defineCollection } from "@rebasepro/cms-types";
export const medicalRecordsCollection = defineCollection({ slug: "medical_records", name: "Medical records", table: "medical_records", properties: { patient_id: { name: "Patient", type: "string" }, notes: { name: "Notes", type: "string" } }, // Remove the injected admin/server baseline — nothing is readable // except what the rules below allow. disableDefaultPolicies: true, securityRules: [ { operations: ["select", "update"], ownerField: "patient_id" } ]});Désormais, la seule façon d’y accéder est de correspondre à patient_id. L’uid de la clé de service
étant la chaîne littérale service, une règle de propriété ne correspond jamais — les lectures renvoient
zéro ligne et les écritures sont rejetées par Postgres. Il s’agit du seul mécanisme qui restreint
l’identifiant par défaut du serveur MCP plutôt que de lui donner carte blanche.
N’oubliez pas qu’il s’agit d’une modification concrète de RLS, et non purement documentaire : elle ne prend effet
qu’une fois que rebase schema generate et une migration ont appliqué les politiques. Voir
Règles de sécurité (RLS).
Le verrou loopback
Section intitulée « Le verrou loopback »rebase_project_add accepte n’importe quelle baseUrl, et les outils CLI se connectent à la
DATABASE_URL déclarée par le projet. La même liste d’outils qui modifie une base temporaire
sur votre ordinateur portable peut donc supprimer des lignes en production, sans autre intermédiaire
que le jugement de l’assistant quant au projet actif.
Tout outil qui modifie l’environnement cible est refusé sauf si cette cible se trouve sur l’interface loopback. Le verrou est conçu comme une liste de ce qui n’est pas verrouillé, de sorte qu’un outil ajouté ultérieurement est protégé par défaut.
- Non verrouillés — lectures :
rebase_schema_plan,rebase_doctor,rebase_db_branch_list,rebase_db_branch_info,list_documents,get_document,list_users,list_roles,storage_list_objects,storage_get_download_url,cron_list_jobs,cron_get_job,cron_get_job_logs,rebase_dev_logs. - Non verrouillés — locaux uniquement :
rebase_schema_introspect,rebase_schema_generate,rebase_db_generate,rebase_generate_sdk, les outils du serveur de dev et les outils du registre de projets. Ceux-ci écrivent des fichiers locaux ou un état local et n’ont aucune cible distante à vérifier. - Verrouillés selon
DATABASE_URL: les outils CLI restants —rebase_db_push,rebase_db_migrate,rebase_db_branch_create,rebase_db_branch_delete. - Verrouillés selon la
baseUrldu projet : les outils SDK restants —create_document,update_document,delete_document,create_user,update_user,delete_user,rebase_auth_reset_password,storage_delete_object,cron_trigger_job,cron_toggle_job,invoke_function.
Les deux cibles ne sont pas interchangeables. Les outils CLI ne voient jamais baseUrl, ainsi un backend
localhost associé à une DATABASE_URL de production est contrôlé par rapport à la base de données,
et non par rapport au backend.
Un refus ressemble à ceci :
Error: Refusing to run "delete_document": project "default" points athttps://api.example.com/, which is not local. Set REBASE_MCP_ALLOW_REMOTE_WRITES=trueto allow destructive tools against remote environments.Si aucune chaîne de connexion ne peut être résolue, les outils de base de données soumis au verrou sont refusés.
Sans DATABASE_URL, la CLI utilise la base de développement gérée, qui est la valeur par défaut d’un
nouveau projet, et le verrou y refuse ses quatre outils de base de données. Le refus en donne la raison
et nomme ce qui fonctionne à la place. Il ne suggère pas la désactivation du verrou :
Error: Refusing to run "rebase_db_push": project "default" sets no DATABASE_URL, so theCLI would use the managed development database (PGlite, started by `rebase dev`). Youmost likely do not need this tool: the dev server applies the collections to thatdatabase at boot, additively. After changing a collection, restart it withrebase_dev_stop and rebase_dev_start. …Depuis 0.25 Depuis un terminal,
rebase db push et rebase db migrate s’exécutent bel et bien sur cette base de
données ; seuls les outils MCP sont refusés. En 0.24, la CLI les y refuse aussi.
Seule l’interface loopback est considérée comme locale : localhost, *.localhost, 127.0.0.0/8, ::1.
Les plages privées telles que 10.x et 192.168.x ne le sont pas — celles-ci ont autant de chances
d’être un cluster de staging partagé qu’un ordinateur portable, et les traiter comme locales laisserait
passer précisément l’accident que ce verrou a pour rôle d’empêcher.
Définissez REBASE_MCP_ALLOW_REMOTE_WRITES=true pour désactiver cette protection. Le définir globalement
dans la configuration de votre client MCP désactive le verrou pour tous les projets auxquels le serveur a accès,
et pas seulement pour celui auquel vous pensiez.
Marquage des données non fiables
Section intitulée « Marquage des données non fiables »Les lignes, enregistrements utilisateurs, listes de stockage, tâches cron, réponses de fonctions et sorties CLI sont renvoyés enveloppés dans une balise explicite :
<<<UNTRUSTED_DATA source="list_documents" id="9b2f4c1e-…">>>[ … rows … ]<<<END_UNTRUSTED_DATA id="9b2f4c1e-…">>>Tout ce qui est stocké dans votre base de données a été écrit par quelqu’un, et cela arrive sur le même canal que le contrat d’outils que l’assistant suit. L’enveloppe indique au modèle de traiter cela comme un contenu inerte plutôt que comme des instructions.
L’id est généré à nouveau pour chaque réponse, après l’écriture des données, et seul le marqueur
de fin le portant clôture le bloc. Le texte à l’intérieur des données qui ressemble à un marqueur est
scindé par une espace sans chasse (zero-width space), de sorte qu’une ligne qui contiendrait
<<<END_UNTRUSTED_DATA>>> ne puisse pas fermer prématurément l’enveloppe et exposer ce qui la suit.
Le point de terminaison distant clôture ses résultats d’outils
de la même façon et le signale au client ; son structuredContent porte le résultat brut.
Depuis 0.25 list_collections et
describe_collection font exception : seul un modèle les lit, elles ne sont donc envoyées qu’une fois,
dans le texte. Avant la 0.24, la liste portait aussi une copie dans structuredContent.
Il s’agit d’un marqueur, pas d’un bac à sable (sandbox). Un assistant disposant de ces outils n’est sûr qu’à hauteur de la fiabilité du contenu que vous lui permettez de lire.
Projets multiples
Section intitulée « Projets multiples »Les configurations des projets sont stockées dans ~/.rebase/projects.json, et le serveur peut en
gérer plusieurs à la fois — très utile si vous travaillez à cheval entre des environnements locaux et
distants. Pendant que rebase dev s’exécute, le serveur lit le port actif et la clé de service depuis
.rebase/state.json dans le répertoire du projet, ce qui permet le fonctionnement sans configuration en local.
Les jetons sont stockés dans ce registre en clair. Il s’agit d’un fichier dans votre répertoire personnel qui contient des identifiants administrateurs pour chaque projet que vous avez enregistré ; traitez-le en conséquence.
Référence des outils
Section intitulée « Référence des outils »42 outils, répartis en neuf groupes : schéma et base de données, planification de schéma, documents, utilisateurs et rôles, stockage, cron, fonctions, le serveur de dev et le registre de projets. Chacun, avec ce dont il a besoin et si le verrou le refuse sur une cible non locale, se trouve dans la référence des outils MCP.
Ressources
Section intitulée « Ressources »Au-delà des outils, le serveur expose des ressources MCP permettant à un client de récupérer le contexte du projet sans consommer d’appel d’outil :
| URI | Description |
|---|---|
rebase://collections/{name} |
Code source TypeScript de la définition d’une collection |
rebase://schema |
Le schéma Drizzle généré (schema.generated.ts) |
Les collections sont découvertes depuis app/config/collections/,
config/collections/ ou collections/ dans le répertoire du projet actif —
selon ce qui existe.
rebase://schema n’est listé que si le schéma généré existe.
findBackendDir recherche backend/ puis app/backend/ dans le répertoire du
projet actif, et lit src/schema.generated.ts dans celui qu’il trouve —
ainsi, la structure générée par défaut tout comme celle de ce monorepo fonctionnent,
tandis qu’un projet organisé d’une autre manière, ou qui n’a pas encore exécuté
rebase schema generate, ne verra tout simplement pas la ressource proposée.
Le point de terminaison distant
Section intitulée « Le point de terminaison distant »Tout ce qui précède concerne un outil de développement : il s’exécute sur votre machine et détient une clé
de service ou une clé d’API. Un backend déployé peut également servir du MCP lui-même, sur /mcp, pour
les personnes qui utilisent votre application. Un assistant connecté par l’une d’elles lit et
écrit dans le projet en tant que cette personne, et chaque appel s’exécute sous ses propres
règles de sécurité au niveau des lignes (RLS).
Il est désactivé par défaut, et les deux variables sont obligatoires :
REBASE_MCP_ENABLED=trueREBASE_PUBLIC_URL=https://app.example.com # this deployment's real originSans REBASE_PUBLIC_URL, un secret JWT ou un pilote de données capable de restreindre une requête
à un utilisateur, le point de terminaison refuse de se monter et en explique la raison dans le journal de démarrage.
Aucun REBASE_ROLE ne permet de l’activer.
- OAuth, avec écran de consentement. Un client découvre le serveur d’autorisation
via
/.well-known/oauth-protected-resource, s’enregistre lui-même (l’enregistrement dynamique est activé par défaut ;REBASE_MCP_OPEN_REGISTRATION=falsele limite aux clients que vous enregistrez), et redirige la personne vers un écran de consentement qui la connecte via votre/auth/loginexistant. - Huit outils, trois portées (scopes). Les mêmes portées
que celles de tous les identifiants.
data:readproposelist_collections,describe_collection,query_collection,count_documentsetget_document;data:writeajoutecreate_documentetupdate_document;data:deleteajoutedelete_document. Un client qui ne demande rien obtientdata:read. Chacune se restreint à une collection :data:read:postsliste, décrit et litpostset rien d’autre. Une portée détermine les outils proposés et les collections qu’ils atteignent, pas les lignes accessibles : une liste vide peut simplement être le résultat du RLS, etdata:writene peut toujours pas écrire une ligne à laquelle la personne n’a pas accès. - Les autorisations accordées avant 0.24 gardent leur portée.
mcp:readest lu commedata:read, etmcp:writecommedata:write data:delete, sur les autorisations enregistrées et sur les jetons déjà émis. - Une clé API fonctionne aussi.
/mcpaccepte aussiAuthorization: Bearer rk_…, pour un client configuré avec un en-tête plutôt qu’avec un flux OAuth. La clé atteint les outils que couvrent ses portéesdata:*, en tant que l’identité pour laquelle elle agit : une clé personnelle en tant que son propriétaire, une clé de service en tant queapi-key:<id>. - Le vocabulaire du SDK, les réponses de REST. Les outils prennent ce que prend le SDK —
where({"status": ["==", "paid"]}),orderBy(["created_at", "desc"]ou"created_at:desc"),limit,offset,searchString, etdatapour une écriture — et lisent à travers le chemin deGET /api/data/<collection>, si bien qu’une ligne revient comme REST la sert (dates ISO, unbelongsTosous forme de clé étrangère, par ex.authorId) et peut être renvoyée telle quelle dans une mise à jour.query_collectionrépond{ data, meta }avecmeta.totaletmeta.hasMore, etcount_documentsrépond{ count }. Comme sur REST, unlimitau-delà de 1000, un argument non déclaré et une modification ou suppression d’une ligne dans la corbeille (404) sont refusés ; remettre le champ de soft delete ànullla restaure. - Depuis 0.25 La liste nomme les collections ;
describe_collectiondonne le schéma de l’une d’elles.list_collectionsrépondname,titleetdescriptionde chaque collection, etdescribe_collection { collection }les schémas OpenAPIrowetcreatede cette collection, plussoftDeleteFieldquand les lignes vont dans une corbeille. Les descriptions des outils demandent au modèle de décrire une collection avant d’y écrire. La liste d’un projet de 200 collections pèse environ 5k jetons, et chaque description moins de 1k ; avec tous les schémas dans la liste, elle en pesait environ 150k. - Un jeton réservé à ce point de terminaison. Un jeton d’accès MCP est refusé par
/api/data,/api/adminet le WebSocket, de sorte que connecter un assistant ne lui confère pas une session complète.
Une limite : déconnecter un client (DELETE /api/oauth/grants/:clientId, avec la
propre session de la personne) révoque immédiatement ses jetons de rafraîchissement (refresh tokens),
mais un jeton d’accès déjà émis reste valide jusqu’à son expiration, dans la limite d’une heure.
Ce même délai d’une heure encadre tout le reste : chaque rafraîchissement relit les rôles de la
personne et rejette un compte supprimé ou une autorisation antérieure à sa dernière action « se déconnecter de partout »
ou à son changement de mot de passe. Ainsi, une rétrogradation de rôle ou une déconnexion prend effet
pour un client connecté dans la durée de vie d’un jeton d’accès. Une session invité ne peut en aucun cas
donner son consentement.
Les routes se trouvent dans Points de terminaison et les variables dans Configuration.
Configuration recommandée
Section intitulée « Configuration recommandée »- Pointez le serveur vers un projet local et laissez
REBASE_MCP_ALLOW_REMOTE_WRITESnon défini. Le verrou est l’élément le plus précieux de ce paquet. - Pour tout projet distant, enregistrez une clé d’API
rk_restreinte plutôt que de laisser l’auto-découverte transmettre une clé de service. - Vérifiez
rebase_project_currentlorsque le résultat semble incorrect. Le projet actif est persistant et réside en dehors de votre dépôt. - Traitez
~/.rebase/projects.jsoncomme un fichier de secrets.