Choose a TypeChain composition boundary

Release status: This guide documents the published @theorvane/type-chain@0.1.1 package. Every optional integration stays behind a dedicated subpath, so importing the root metadata package does not load LangChain or TypeMCP peers.

TypeChain makes tool and policy declarations explicit, then adapts those declarations at a boundary selected by the application. Start with the narrowest import that reaches the behavior you need. The application keeps ownership of models, credentials, authorization, enforcement, state, persistence, streaming, hosting, and deployment.

Before you start

Workspace checkpoint

This is the routing chapter after an inspected root definition. It is not a second foundation: complete the foundation guide for tsconfig.json, @types/node, and the first named files before selecting an optional subpath.

Install

Install the root package first:

npm install @theorvane/type-chain@0.1.1
NeedImportUse it whenKeep in the application
Declare tools and policy intent@theorvane/type-chainApplication code needs immutable, receiver-bound tool definitions.Actual authorization, approval, audit, retry, timeout, idempotency, and redaction decisions.
Pass tools to an existing LangChain composition@theorvane/type-chain/langchainThe application already owns a LangChain model or agent boundary.Model/provider selection, agent lifecycle, state, streaming, and graph topology.
Build a direct LangChain agent from a decorated class@theorvane/type-chain/agentA small application-owned agent boundary is appropriate.Model instance, credentials, all runtime controls, and deployment.
Adapt an in-process TypeMCP server@theorvane/type-chain/typemcpTypeMCP-decorated tools and the LangChain application run in the same Node.js process.MCP transport hosting, resolver dependencies, clients, policy, and cross-process access.

The root package has no required optional peer imports. Install only the peers for the subpath selected by the application:

# Standard LangChain tool or agent composition
npm install @theorvane/type-chain@0.1.1 @langchain/core langchain zod

# In-process TypeMCP composition
npm install @theorvane/type-chain@0.1.1 @theorvane/type-mcp@0.2.2 @langchain/core langchain zod

Begin with one declared tool

A root import records explicit runtime metadata and returns definitions bound to a real application instance. It does not infer schemas from TypeScript types or execute an authorization decision.

// src/catalog-tools.ts
import { z } from "zod";
import { Policy, Tool } from "@theorvane/type-chain";

export class CatalogTools {
  @Policy({ authorization: "required", audit: "required" })
  @Tool({
    name: "find_product",
    description: "Find a product by SKU.",
    schema: z.object({ sku: z.string().min(1) }),
  })
  findProduct({ sku }: { readonly sku: string }) {
    return { sku, available: true };
  }
}

@Tool() captures a name, description, and runtime schema. @Policy() captures declarative intent. Neither decorator chooses who is authorized, records an audit entry, retries a call, or provides a timeout. Those are runtime effects that belong to application middleware or a supplied guard.

Use an existing LangChain composition

Import the LangChain adapter when the application already owns the model and the surrounding agent or graph.

import { toLangChainTools } from "@theorvane/type-chain/langchain";
import { CatalogTools } from "./catalog-tools.js";

const tools = toLangChainTools(new CatalogTools());

toLangChainTools() creates standard LangChain structured tools. It requires a structured object input schema accepted by LangChain’s tool() factory. LangChain owns input parsing and validation at this adapter boundary; TypeChain preserves the declared schema and receiver-bound method invocation.

When a tool declares @Policy(), compose an application-owned guard before adapting it:

import { toGuardedLangChainTools } from "@theorvane/type-chain/langchain";

const tools = toGuardedLangChainTools(new CatalogTools(), async ({
  definition,
  policy,
  input,
}) => {
  await applicationPolicyMiddleware({ definition, policy, input });
});

Throwing or rejecting from the guard prevents the underlying policy-decorated method from running. TypeChain provides no default allow or deny result. Read Policy and guards before exposing a state-changing method.

Build a direct agent when the application owns the model

The agent subpath is a small bridge to LangChain’s createAgent(). Decorate the class with @Agent() and supply the model from the application.

import { Agent, buildAgent } from "@theorvane/type-chain/agent";
import { CatalogTools } from "./catalog-tools.js";

@Agent({ systemPrompt: "Use catalog tools only when they answer the request." })
class CatalogAgent extends CatalogTools {}

const agent = buildAgent(new CatalogAgent(), {
  model: applicationOwnedModel,
});

buildAgent() adapts the decorated tools and delegates construction to LangChain. The caller selects applicationOwnedModel; provider credentials, tracing, state, persistence, streaming, error handling, and deployment do not become TypeChain responsibilities. Use buildGuardedAgent() when the application must run a guard for tools that declare policy intent.

Compose TypeMCP only inside one Node.js process

The TypeMCP bridge is not an MCP host or client. It converts a TypeMCP-decorated server into native LangChain tools, then may delegate those tools to LangChain’s createAgent().

import { createTypeMcpLangChainTools } from "@theorvane/type-chain/typemcp";
import type { PetstoreClient } from "./petstore-client.js";
import { PetstoreServer } from "./petstore-server.js";

// Construct and authorize this client in the application composition root.
declare const petstoreClient: PetstoreClient;

export const tools = await createTypeMcpLangChainTools(PetstoreServer, {
  resolver: { resolve: () => new PetstoreServer().configure(petstoreClient) },
});

The TypeMCP-decorated server must keep a zero-argument constructor under the published 0.2.2 @McpServer contract. The resolver configures application dependencies before conversion; the explicit resolver remains application-owned. TypeMCP validates the MCP declaration and resolves the server instance; TypeChain adapts the resulting tools; LangChain owns agent construction. No stdio or HTTP transport is started. Use TypeMCP’s transport hosts separately when tools must be accessed across process boundaries.

Run and verify

Use the root-only checkpoint first:

npm run check
npm run inspect-tools

Then select one extension and run only the matching local contract:

# Existing LangChain composition
node --test test/langchain-adapter.test.mjs

# Direct application-owned agent with a caller-supplied model
node --test test/agent.test.mjs

# In-process TypeMCP conversion; this does not start MCP stdio or HTTP
node --test test/typemcp.test.mjs

These commands verify adapters without a provider call or public host. Your application must still test its own model, guard, resolver dependencies, credentials, and deployment configuration.

Expected behavior

The root path records immutable definitions without optional peer imports. Each selected subpath adds only its stated adapter: LangChain tools, a caller-model agent bridge, or an in-process TypeMCP conversion. No route starts a network listener or turns policy metadata into authorization automatically.

Failure guide

Responsibility boundary

TypeChain owns explicit tool/policy metadata and the selected adapter. The application owns model/provider selection, credentials, authorization and enforcement, persistence/state, host lifecycle, cross-process transport, observability, retry/timeout behavior, and deployment.

Next steps

The selected subpath should match the integration boundary, not the product label. Use the root package for declarations, the LangChain adapter for existing LangChain composition, the agent bridge only when the application owns a model, and the TypeMCP bridge only for in-process composition.