MCP Fusion/Security and governance/Conectores multi-tenant
Conectores multi-tenant
Un conector, muchos tenants, ningún módulo especial: resolución del tenant por solicitud, visibilidad de capacidades filtrada por etiquetas y Presenters basados en roles que muestran a cada tenant lo que puede ver.
Atender a varios clientes desde un solo conector no es una función que se añade después; es una propiedad de cómo se construyen el contexto, la visibilidad y la percepción. MCP Fusion no tiene un módulo de tenancy porque las piezas que ya usas se combinan en uno. Esta página es la receta.
1. Resuelve el tenant por solicitud
Cada solicitud empieza con un ctx nuevo, así que el tenant se decide una vez, antes de cualquier 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,
};
},
});Dos reglas lo hacen seguro. La factory devuelve un objeto nuevo por solicitud: ninguna solicitud puede observar el contexto de otra. El middleware escribe en ese objeto con protecciones contra la contaminación del prototipo, de modo que un nombre de argumento malicioso no pueda alcanzar __proto__. Los handlers usan ctx.db o ctx.tenantId como única puerta a los datos, y el id del tenant nunca procede de los argumentos de la herramienta.
2. Limita las capacidades por etiqueta
Los tenants rara vez reciben la misma superficie. Las etiquetas y un filtro deciden qué existe para cada uno:
const filterFor = (role: string) =>
role === 'admin'
? { anyTag: ['public', 'admin'] }
: { tags: ['public'], exclude: ['internal'] };
attachToServer(server, { filter: filterFor(ctx.role) });Con esta forma, un despliegue gratuito expone herramientas públicas, uno empresarial expone herramientas administrativas y el código de los handlers es idéntico. El filtro se evalúa donde se construye la lista de herramientas, bajo el contexto propio de la solicitud. Consulta Tool exposition.
3. Da forma a la percepción por rol
La visibilidad decide qué herramientas ve un rol; la percepción decide qué muestran esas herramientas. El patrón verificado consiste en dos herramientas sobre un handler, seleccionadas por el filtro del paso 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 agente con rol viewer solo recibe people.list_public y nunca ve campos de contacto; un agente admin recibe ambas, con el Presenter completo. Las reglas de campos y la redacción viven en un solo lugar por audiencia, y el digest del contrato registra ambas superficies. Las reglas contextuales, escritas como (data, ctx) => string[], añaden orientación dependiente del rol. Consulta Models and Presenters.
4. Aísla las credenciales por tenant
Las claves de proveedores tampoco deben compartirse entre tenants. Decláralas con defineCredentials y léelas por solicitud: en Vinkius Cloud el runtime inyecta los secretos de cada comprador en cada solicitud y, en local, el mismo accessor lee tu entorno. Consulta Credentials.
5. Atribuye coste y tráfico
Cada llamada puede atribuirse: pasa ctx.tenantId como clave de rate limit para que un tenant no agote el presupuesto de otro y emite el id del tenant en eventos de auditoría y telemetría. En la consola de Vinkius, el mismo conector muestra coste y fiabilidad por servidor, documentados en AI spend y Tool reliability.
El patrón en una tabla
| 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 |
No hay un interruptor de runtime que olvidar ni una caché entre tenants que configurar mal, porque nada se comparte salvo que lo compartas explícitamente. Esa es la diferencia entre un conector multi-tenant y uno single-tenant con una columna de tenant.
Próximos pasos
- Authentication: de dónde viene la identidad
- Middleware and context: mecánicas de derivación y aislamiento
- Security pipeline: las capas que atraviesa cada solicitud
