AI Connect/Core concepts/Arquitectura
Arquitectura
Cómo funciona AI Connect realmente: la cadena de alcance con la que trabajas, el plano de control que aprovisiona el estado, el runtime MCP que ejecuta cada capacidad y las reglas que mantienen correcto el uso multiusuario.
AI Connect tiene una superficie para tu código y dos planos debajo. Tú escribes contra una cadena corta de manejadores: aplicación, usuario, conector, capacidad. La plataforma ejecuta un plano de control que aprovisiona quién puede hacer qué, y un plano de ejecución, un runtime MCP, que enumera y ejecuta cada capacidad. Una vez ves la división, todo comportamiento del SDK se vuelve predecible.
La cadena: aplicación, usuario, conector, capacidad
const user = vinkius.user('alice_123');
const github = user.connector('github');
const capabilities = await user.capabilities();Cada objeto solo añade alcance. El cliente Vinkius lleva la identidad de tu aplicación: la Application Key es el único secreto que gestionas, y solo puede actuar por su propia app. user() vincula a uno de tus usuarios por el id de tu propio sistema de autenticación: no hay ningún id de usuario de Vinkius que resolver, sincronizar ni almacenar; la plataforma direcciona todo con tu externalId. connector() vincula un conector, y una Capability es una acción concreta que ese usuario puede ejecutar.
Crear manejadores es local y barato: nada contacta con la plataforma hasta que llamas a una operación como connect(), status(), schema(), capabilities() o execute(). Construir una cadena nueva por solicitud mantiene el alcance de tu código evidente.
Dos planos: control y ejecución
El aprovisionamiento y el estado viven en el plano de control:
await user.ensure({ plan: 'pro' }); // provision the user
await github.credentials.set({ TOKEN: 'x' }); // write credentials
await github.status(); // derived readiness
await vinkius.catalog.list(); // discover connectorsLa ejecución vive en su propio plano. Cuando connect() crea (o encuentra) la conexión del usuario, AI Connect emite exactamente un token de datos para esa conexión, una credencial vk_live_*, y devuelve la URL del runtime que lo incluye, una sola vez. Cada capacidad construida desde esa conexión lleva su runtime pre-vinculado, así tus llamadas nunca pasan coordenadas de enrutamiento: ni tokens, ni IDs de conexión, ni endpoints.
El token es el corazón del diseño. Enumerar herramientas es gratis; cada ejecución se mide contra él, de modo que el gasto, el tráfico y los fallos se atribuyen por conexión de usuario. También es un kill switch: cuando el usuario desactiva o elimina la conexión, el token muere con ella, y las llamadas fallan cerradas. El SDK nunca vuelve a emitir en silencio un token revocado; una conexión revocada no puede empezar a facturar de nuevo a escondidas.
Cada conexión es un servidor MCP
El runtime habla MCP estándar. Eso convierte a cada usuario conectado en un endpoint MCP real: la misma superficie que el SDK usa para enumerar y llamar capacidades es la que cualquier cliente MCP, Claude Desktop, Cursor, tus otros agentes, conecta directamente. Persiste la URL que connect() devuelve cuando quieras entregar ese endpoint a otro cliente.
La misma portabilidad recorre la capa de modelos. Nueve subpaths de adaptador sin dependencias, desde OpenAI, Anthropic, Gemini, Vercel AI SDK, LangChain, LlamaIndex y Cloudflare Workers AI hasta una salida JSON Schema neutra, convierten un CapabilitySet en el formato de tools de tu proveedor, con inyección de fábrica en lugar de peer dependencies. Cambia de modelo, cliente o framework: tus capacidades siguen siendo tuyas.
Las capacidades llevan su dueño y su ruta
Cuando la plataforma enumera capacidades, cada una ya conoce su conector, la conexión de ese usuario, su nombre visible y su esquema de entrada:
const capability = capabilities.findCapability('github__create_issue');
console.log(capability?.rawName); // create_issue
console.log(capability?.connector); // github
console.log(capability?.inputSchema); // JSON Schema of the argumentsLlamar a execute() sobre ese objeto ejecuta la acción en el GitHub de ese usuario, con las credenciales de ese usuario; nunca pasas un token, un ID de conexión ni un endpoint. Los nombres visibles llevan espacio de nombres por conector (github__create_issue) y son personalizables con namespaceCapability. Un CapabilitySet es un array real con ayudas ergonómicas (findCapability, forConnector), así que se compone con todo lo que ya haces con arrays. Y como una capacidad está vinculada al usuario que la produjo, no reutilices objetos de capacidad de un usuario para la solicitud de otro.
La agregación es tolerante a fallos por diseño
user.capabilities() no es una sola llamada a la API. La plataforma enumera las herramientas de cada conexión en su propio runtime, así que la agregación es un fan-out limitado sobre las conexiones listas del usuario, fusionado en un único conjunto. El diseño te protege en ambos sentidos: un conector inestable no hunde el lote (los fallos parciales se omiten y se muestran mediante onConnectorError, y la llamada solo lanza si todas las conexiones fallaron), y muchos conectores no abren sockets ilimitados a la vez.
Delimita de forma barata con include/exclude, y cuando necesites un solo conector, ve directo con user.connector(slug).capabilities(): se salta el fan-out completo.
Las credenciales son de solo escritura, el estado se deriva
Puedes enviar credenciales, preguntar qué exige un conector (credentials.schema()) y ver qué campos están configurados (credentials.status()), pero los valores secretos nunca vuelven. Viven en la bóveda de la plataforma, y el modelo nunca recibe secretos sin procesar. La preparación la posee el backend y se presenta en exactamente cuatro estados: not_connected, needs_credentials, ready, disabled. Solo los conectores listos contribuyen capacidades, de modo que una conexión a medio configurar nunca filtra una tool rota en el bucle de tu agente.
Lo que la plataforma hace en cada llamada
- La ejecución se normaliza. Sea cual sea el protocolo del conector por debajo, REST, GraphQL o streaming,
execute()devuelve un único resultado estructurado:{ content, isError }. - Los reintentos seguros ocurren solos. La inestabilidad transitoria se reintenta en operaciones idempotentes; para una acción que debe ejecutarse como máximo una vez, pasa un
idempotencyKeyestable y no vacío aexecute(). - La gobernanza viene incluida. Cada ejecución se mide y la observa la superficie de AI Governance de la consola: tráfico, gasto, fallos y postura de seguridad por conector y por usuario.
Dos canales de fallo, una regla
execute() separa la acción que se ejecutó y reportó un problema de la llamada que nunca se completó:
const result = await capability.execute(args, { idempotencyKey });
if (result.isError) {
// La capacidad se ejecutó y reportó fallo: devuélveselo al agente.
}Los problemas de la plataforma lanzan una subclase de VinkiusError: AuthError, ConnectorNotConnectedError, RateLimitError, QuotaError, ValidationError y compañía, cada una con status, code y requestId. Devuelve los resultados isError a tu modelo para que se recupere; captura los errores lanzados para decidir qué hace tu aplicación. Confundir ambos oculta si la acción llegó realmente al mundo real.
El estado es local al manejador
Un manejador resuelve la conexión del usuario y su runtime en la primera operación que los necesita, y reutiliza ambos durante toda la vida de ese manejador: las llamadas repetidas sobre el mismo manejador omiten la resolución. No hay caché global. Un manejador recién creado vuelve a resolver, disconnect() borra lo que ese manejador recordaba y ResolverCache es una utilidad opcional que puedes conectar a tu propia memorización. Por eso el patrón seguro es una cadena nueva de manejadores por solicitud, y reutilizarla dentro de la misma solicitud es ganancia pura.
