MCP Fusion/Security and governance/Connecteurs multi-tenant
Connecteurs multi-tenant
Un connecteur, plusieurs tenants, aucun module spécial : résolution du tenant par requête, visibilité des capacités filtrée par tags et Presenters selon le rôle qui montrent à chaque tenant ce qu’il peut voir.
Servir plusieurs clients depuis un seul connecteur n’est pas une fonctionnalité ajoutée après coup ; c’est une propriété de la manière dont le contexte, la visibilité et la perception sont construits. MCP Fusion n’a pas de module de tenancy, car les pièces que vous utilisez déjà se composent en un ensemble. Cette page en donne la recette.
1. Résoudre le tenant par requête
Chaque requête commence avec un ctx neuf ; le tenant est donc décidé une fois, avant tout handler :
interface AppContext {
db: PrismaClient;
tenantId: string;
role: 'viewer' | 'admin';
}
registry.attachToServer(server, {
contextFactory: async (extra) => {
const claims = await verify(extra.session?.authToken);
return {
db: pool.forTenant(claims.tenant_id),
tenantId: claims.tenant_id,
role: claims.role,
};
},
});Deux règles rendent cela sûr. La factory renvoie un objet neuf par requête : aucune requête ne peut observer le contexte d’une autre. Le middleware écrit dans cet objet avec des protections contre la pollution du prototype, afin qu’un nom d’argument malveillant ne puisse atteindre __proto__. Les handlers utilisent ctx.db ou ctx.tenantId comme seule porte vers les données, et l’identifiant du tenant ne vient jamais des arguments de l’outil.
2. Restreindre les capacités par tag
Les tenants disposent rarement de la même surface. Les tags et un filtre décident de ce qui existe pour chacun :
const filterFor = (role: string) =>
role === 'admin'
? { anyTag: ['public', 'admin'] }
: { tags: ['public'], exclude: ['internal'] };
attachToServer(server, { filter: filterFor(ctx.role) });Avec cette forme, un déploiement gratuit expose les outils publics, un déploiement entreprise les outils administratifs, et le code des handlers reste identique. Le filtre est évalué là où la liste d’outils est construite, dans le contexte propre à la requête. Voir Tool exposition.
3. Façonner la perception selon le rôle
La visibilité décide quels outils un rôle voit ; la perception décide ce que ces outils montrent. Le modèle vérifié utilise deux outils sur un handler, sélectionnés par le filtre de l’étape 2 :
const listPeople = async (input, ctx) => ctx.db.people.findMany();
f.query('people.list_public')
.describe('List people in the tenant, without contact details')
.tags('public')
.returns(ViewerPresenter) // schema without email or phone
.handle(listPeople);
f.query('people.list_admin')
.describe('List people in the tenant with full records')
.tags('admin')
.returns(AdminPresenter)
.handle(listPeople); // same handler functionUn agent viewer ne reçoit que people.list_public et ne voit jamais les champs de contact ; un agent admin reçoit les deux, avec le Presenter complet. Les règles de champs et la rédaction vivent à un seul endroit par audience, et le digest du contrat enregistre les deux surfaces. Les règles contextuelles, écrites comme (data, ctx) => string[], ajoutent des indications dépendantes du rôle. Voir Models and Presenters.
4. Isoler les credentials par tenant
Les clés de fournisseurs ne doivent pas non plus être partagées entre tenants. Déclarez-les avec defineCredentials et lisez-les par requête : sur Vinkius Cloud, le runtime injecte les secrets de chaque acheteur à chaque requête ; en local, le même accessor lit votre environnement. Voir Credentials.
5. Attribuer coût et trafic
Chaque appel peut être attribué : passez ctx.tenantId comme clé de limitation afin qu’un tenant ne puisse pas épuiser le budget d’un autre, et émettez son identifiant dans les événements d’audit et la télémétrie. Dans la console Vinkius, le même connecteur expose le coût et la fiabilité par serveur, documentés dans AI spend et Tool reliability.
Le modèle en un tableau
| Concern | Mechanism | Where it lives |
|---|---|---|
| Identity | JWT, API key or OAuth token | contextFactory + auth middleware |
| Data isolation | per-tenant client from the context | contextFactory |
| Capability visibility | tags + filter | tool list construction |
| Field isolation | role-based Presenters | Presenter per audience |
| Secret isolation | BYOC credentials | runtime injection |
| Fair use | rate-limit key from ctx | rate limiter middleware |
| Proof | contract digest + audit events | lockfile and audit sink |
Il n’existe aucun interrupteur de runtime à oublier ni cache inter-tenant à mal configurer, car rien n’est partagé sauf si vous le partagez explicitement. C’est la différence entre un connecteur multi-tenant et un connecteur mono-tenant avec une colonne tenant.
Étapes suivantes
- Authentication : d’où vient l’identité
- Middleware and context : mécanismes de dérivation et d’isolation
- Security pipeline : les couches traversées par chaque requête
