MCP Fusion/Operate and integrate/Typed client
Typed client
Call a connector from TypeScript with a router inferred from its builders: execute dotted actions, batch calls, use a fluent proxy and parse tool_error into MCPFusionClientError.
MCP Fusion is useful on both sides of the boundary. The server owns the security pipeline; the typed client gives the application that calls it a compiler-checked contract. No generated SDK and no duplicated argument interfaces.
Export the router once
On the server, wrap the builders in the curried typed registry and export its inferred router type:
import { createTypedRegistry } from '@mcpfusion/core';
import type { InferRouter } from '@mcpfusion/core';
const registry = createTypedRegistry<AppContext>()(
listProjects,
createProject,
);
registry.registry.attachToServer(server, { contextFactory });
export type AppRouter = InferRouter<typeof registry>;The runtime value is registry.registry; _builders and _context are type-carrying fields. InferRouter intersects the phantom router maps accumulated by each builder, so changing a .withString() parameter changes the client compile contract without a code generation step.
Execute with the contract
import { createMCPFusionClient } from '@mcpfusion/core/client';
import type { AppRouter } from './server.js';
const client = createMCPFusionClient<AppRouter>(transport, {
throwOnError: true,
});
await client.execute('projects.create', {
workspace_id: 'ws_1',
name: 'V2',
});A dotted action maps to the wire namespace: projects.create becomes callTool('projects', { ...args, action: 'create' }). A single segment passes through unchanged. The discriminator key defaults to action and is configurable with discriminatorKey.
Batch and proxy
executeBatch(calls) uses Promise.all and returns results in call order. Pass { sequential: true } when the second call depends on the first. The fluent proxy is a recursive JavaScript Proxy:
await client.proxy.projects.create({
workspace_id: 'ws_1',
name: 'V2',
});
await client.proxy.platform.users.list({ limit: 10 });DevTools inspection and the Promise then lookup are guarded, so the proxy does not accidentally execute while a debugger or test runner examines it.
Client middleware
Client middleware is compiled once when the client is created:
const client = createMCPFusionClient<AppRouter>(transport, {
middleware: [async (action, args, next) => {
const started = Date.now();
const result = await next(action, args);
metrics.record(action, Date.now() - started, result.isError);
return result;
}],
});The signature is (action, args, next) => Promise<ToolResponse>. Use it for client-side tracing, retries with an explicit policy, or adding a correlation header. The server still owns authentication, validation and redaction.
Error parsing
With throwOnError: false the client always returns ToolResponse; inspect isError. With true, a response carrying isError: true becomes MCPFusionClientError with code, recovery, frozen availableActions, severity and the raw response. It parses the framework's <tool_error> XML, including legacy comma-separated action lists.
External AI clients
There are no MCP Fusion branded adapters for model frameworks. Use their official MCP clients:
| Runtime | Package | Shape |
|---|---|---|
| Vercel AI SDK | @ai-sdk/mcp | createMCPClient() then client.tools() for generateText or streamText |
| LangChain | @langchain/mcp-adapters | MultiServerMCPClient.loadTools() then a LangGraph agent |
| LlamaIndex | @llamaindex/tools | mcp(...).tools() then agent({ tools }) |
The framework on the server remains responsible for the MVA Presenter, DLP and tenant boundary. The model runtime only consumes the resulting MCP surface.
Next steps
- Errors: the XML your client parses
- Tool exposition: how dotted actions become tools
- Runtime architecture: how the server dispatches them
