MCP Fusion/Security and governance/Connecteurs multi-tenant

Connecteurs multi-tenant

Demandez à l’IA à propos de Vinkius

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 :

typescript
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 :

typescript
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 :

typescript
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 function

Un 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

ConcernMechanismWhere it lives
IdentityJWT, API key or OAuth tokencontextFactory + auth middleware
Data isolationper-tenant client from the contextcontextFactory
Capability visibilitytags + filtertool list construction
Field isolationrole-based PresentersPresenter per audience
Secret isolationBYOC credentialsruntime injection
Fair userate-limit key from ctxrate limiter middleware
Proofcontract digest + audit eventslockfile 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