> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mcprouter.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Models, MCP servers, and policy

> Set up trusted connections and host-owned authorization before filtering or execution.

# Models, MCP servers, and policy

> **Public documentation preview — incomplete.** The package and optional AI SDK peers remain private and unpublished. This configuration is illustrative; no live model/server compatibility or authorization is provided by the docs.

Use a predictable separation: connect model adapters, supply a trusted MCP server array, and declare routing preferences and authorization under `policy`. **This is a configuration pattern, not a working provider/server fixture**: it needs an authorized OpenAI-compatible endpoint, private compatible AI SDK peers, credentials, and two already running Streamable HTTP MCP servers exposing the stated tools. It is not necessary for the [model-free local quickstart](/start/first-call).

The source-controlled `examples/code.ts` module illustrates this pattern for maintainers with repository access; it is not a public release or tested live walkthrough. Adapt the exact tool names and schemas to your servers.

```ts theme={null}
import { createRouter, type ServerConfig } from '@surf-lab/mcprouter';
import { createOpenAI } from '@ai-sdk/openai';
import {
  createBoundedModel,
  createRoutingModel,
  createModelRanker,
} from '@surf-lab/mcprouter/ai-sdk';

function env(name: string): string {
  const value = process.env[name];
  if (!value) throw new Error(`Missing ${name}`);
  return value;
}

const connection = {
  baseURL: env('MODEL_BASE_URL'),
  apiKey: env('MODEL_API_KEY'),
};

function connectModel(modelId: string) {
  const model = createBoundedModel({
    fetch: globalThis.fetch,
    createModel: fetch => createOpenAI({ ...connection, fetch }).chat(modelId),
  });
  return {
    routing: createRoutingModel({ model }),
    ranking: createModelRanker({ model }),
  };
}

const models = {
  general: connectModel(env('GENERAL_MODEL_ID')),
  database: connectModel(env('DATABASE_MODEL_ID')),
  billing: connectModel(env('BILLING_MODEL_ID')),
};

const mcpServers: ServerConfig[] = [
  { serverId: 'database-server', transport: 'http',
    url: env('DATABASE_MCP_URL'),
    headers: { Authorization: `Bearer ${env('DATABASE_MCP_TOKEN')}` } },
  { serverId: 'billing-server', transport: 'http',
    url: env('BILLING_MCP_URL'),
    headers: { Authorization: `Bearer ${env('BILLING_MCP_TOKEN')}` } },
];

function createAppRouter() {
  return createRouter({
    models,
    model: 'general',
    policy: {
      modelBindings: {
        servers: { 'database-server': 'database', 'billing-server': 'general' },
        tools: [{ serverId: 'billing-server',
          toolName: 'get_customer_balance', model: 'billing' }],
      },
      modelAccess: { allowedModels: ['general', 'database', 'billing'] },
      eligibility: (tool, context) => context.role === 'reader' &&
        'serverId' in tool &&
        (tool.serverId === 'database-server' && tool.name === 'list_tables' ||
         tool.serverId === 'billing-server' && tool.name === 'get_customer_balance'),
      authorize: (call, context) => {
        const target = `${call.origin.serverId}/${call.descriptor.upstreamName}`;
        if (context.role !== 'reader') return { decision: 'deny' };
        if (target === 'database-server/list_tables') return { decision: 'allow' };
        if (target === 'billing-server/get_customer_balance' &&
            typeof context.customerId === 'string' && context.customerId &&
            call.arguments.customerId === context.customerId) return { decision: 'allow' };
        return { decision: 'deny' };
      },
      revision: () => 1,
    },
  });
}
```

This **configuration fragment** fails on missing environment variables; the host must still approve its endpoints and model IDs **before** construction. Keep credentials server-side; use trusted, authenticated context for each request, constrain your actual tenant/resource permissions and increment the policy revision when grants change. Do not use MCP-provided metadata or model output to create server configurations or grant access. Close the router in an operation-level `finally`.

No constructor connects or sends a model request. `createBoundedModel` forwards a host-supplied `fetch`, but the host must restrict destinations, exposure, spending and logging. The direct AI SDK `generateText` model is separate and **not** bounded by router `modelAccess`. See [model bindings](/integrate/model-bindings), [filtering](/integrate/filter), [scoped execution](/integrate/execute), and [AI SDK hosting](/integrate/ai-sdk).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.