AI Connect/Core concepts/Architecture

Architecture

Ask AI about Vinkius

How AI Connect really works: the scope chain you write against, the control plane that provisions state, the MCP runtime that executes every capability, and the rules that keep multi-user use correct.

AI Connect has one surface for your code and two planes underneath it. You write against a short chain of handles: application, user, connector, capability. The platform runs a control plane that provisions who can do what, and an execution plane, an MCP runtime, that lists and runs every capability. Once you see the split, every behavior of the SDK becomes predictable.

The chain: application, user, connector, capability

typescript
const user = vinkius.user('alice_123');
const github = user.connector('github');
const capabilities = await user.capabilities();

Each object adds only scope. The Vinkius client carries your application identity: one Application Key is the single secret you manage, and it can only ever act for your own app. user() binds one of your users by the id from your own auth system: there is no Vinkius user id to resolve, sync or store, the platform addresses everything by your externalId. connector() binds one connector, and a Capability is one concrete action that user can run.

Creating handles is local and cheap: nothing contacts the platform until you call an operation such as connect(), status(), schema(), capabilities() or execute(). Building a fresh chain per request keeps your code obviously scoped.

Two planes: control and execution

Provisioning and state live on the control plane:

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

Execution lives on its own plane. When connect() creates (or finds) the user's connection, AI Connect mints exactly one data-plane token for it, a vk_live_* credential, and returns the runtime URL that embeds it, once. Every capability later built from that connection carries its runtime pre-bound, so your call sites never pass routing coordinates: no tokens, no connection ids, no endpoints.

The token is the heart of the design. Listing tools is free; every execution is metered against it, so spend, traffic and failures are attributable per user connection. It is also a kill switch: when the user disables or deletes the connection, the token dies with it, and calls fail closed. The SDK never silently re-mints a revoked token, a revoked connection cannot quietly start billing again.

Every connection is an MCP server

The runtime speaks standard MCP. That makes each connected user a real MCP endpoint: the same surface the SDK uses to list and call capabilities is what any MCP-capable client, Claude Desktop, Cursor, your other agents, connects to directly. Persist the URL connect() returns when you want to hand that endpoint to another client.

The same portability runs through the model layer. Nine zero-dependency adapter subpaths, from OpenAI, Anthropic, Gemini, the Vercel AI SDK, LangChain, LlamaIndex and Cloudflare Workers AI to a neutral JSON Schema output, convert a CapabilitySet into your provider's tool format, with factory injection instead of peer dependencies. Change models, clients or frameworks: your capabilities stay yours.

Capabilities carry their owner and their route

When the platform lists capabilities, each one already knows its connector, the user's connection to it, its display name and its input schema:

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

Calling execute() on that object runs the action on that user's GitHub, with that user's credentials; you never pass a token, a connection ID or an endpoint. Display names are namespaced per connector (github__create_issue) and customizable via namespaceCapability. A CapabilitySet is a real array with ergonomic helpers (findCapability, forConnector), so it composes with whatever you already do with arrays. And because a capability is bound to the user that produced it, never reuse one user's capability objects for another user's request.

Aggregation is failure tolerant by design

user.capabilities() is not one API call. The platform lists each connection's tools at its own runtime, so aggregation is a bounded fan-out over the user's ready connections, merged into one set. The design protects you in both directions: one flaky connector cannot sink the batch (partial failures are skipped and surfaced through onConnectorError, and the call only throws if every connection failed), and many connectors do not open unlimited simultaneous sockets.

Scope cheaply with include/exclude, and when you need one connector, go directly with user.connector(slug).capabilities(): it skips the full fan-out.

Credentials are write-only, status is derived

You can push credentials, ask what a connector requires (credentials.schema()) and see which fields are configured (credentials.status()), but secret values never come back out. They live in the platform's vault, and the model never receives raw secrets. Readiness is owned by the backend and surfaces as exactly four states: not_connected, needs_credentials, ready, disabled. Only ready connectors contribute capabilities, so a half-configured connection can never leak a broken tool into your agent loop.

What the platform does on every call

  • Execution is normalized. Whatever protocol a connector speaks underneath, REST, GraphQL or streaming, execute() returns one structured result: { content, isError }.
  • Safe retries happen automatically. Transient instability is retried on idempotent operations; for an action that must run at most once, pass a stable, non-empty idempotencyKey to execute().
  • Governance is built in. Every execution is metered and observed by the console's AI Governance surface: traffic, spend, failures and security posture per connector and per user.

Two failure channels, one rule

execute() separates an action that ran and reported a problem from a call that never completed:

typescript
const result = await capability.execute(args, { idempotencyKey });

if (result.isError) {
  // The capability ran and reported failure: feed it back to the agent.
}

Platform-level problems throw a VinkiusError subclass: AuthError, ConnectorNotConnectedError, RateLimitError, QuotaError, ValidationError and friends, each carrying status, code and requestId. Feed isError results back to your model so it can recover; catch thrown errors to decide what your application does. Mixing the two hides whether an action actually reached the real world.

State is local to a handle

A handle resolves the user's connection and its runtime the first time an operation needs them, and reuses both for the rest of that handle's life: repeated calls on the same handle skip the lookup. There is no global cache. A brand-new handle re-resolves, disconnect() clears what that handle remembered, and ResolverCache is an opt-in utility you can wire into your own memoization. That is why the safe pattern is a fresh handle chain per request; reusing one within the same request is a pure win.

Next steps