mcpField
O
openrouter-agent-migration

openrouter-agent-migration

github.com/openrouterteamDevelopment309319 installsUpdated 1d ago
Repository

About openrouter-agent-migration

A migration guide for developers moving agent functionality from `@openrouter/sdk` to `@openrouter/agent`. It maps imports and highlights capabilities available only in the standalone package.

Key features

  1. 01Import mapping for migration
  2. 02Agent client and model calls
  3. 03Tool definitions and stop conditions
  4. 04Message format converters
  5. 05Shared context and approval flow

Use cases

  1. 01Updating an agent project to `@openrouter/agent`
  2. 02Keeping SDK features alongside agent features
  3. 03Adding approval or shared context to tools

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/openrouterteam/skills --skill openrouter-agent-migration

After installing, type/openrouter-agent-migrationin Claude Code to confirm it loaded.

Bundled filesmetadata.jsonREADME.md

Details

From the project README

Migrating from @openrouter/sdk to @openrouter/agent

Agent functionality (callModel, tool(), stop conditions, format converters, streaming helpers) has moved from @openrouter/sdk to the standalone @openrouter/agent package. The @openrouter/agent package includes its own OpenRouter client class, so you do not need @openrouter/sdk for agent use cases.


When This Applies

Migrate if your code imports any of these from @openrouter/sdk:

  • callModel or uses client.callModel()
  • tool() factory function
  • Stop conditions: stepCountIs, hasToolCall, maxCost, maxTokensUsed, finishReasonIs
  • Format converters: fromClaudeMessages, toClaudeMessage, fromChatMessages, toChatMessage
  • Types: Tool, ToolWithExecute, ToolWithGenerator, ManualTool, CallModelInput, ModelResult

Quick Migration

Step 1: Install
npm install @openrouter/agent

If you only use agent features, you can remove @openrouter/sdk:

npm uninstall @openrouter/sdk
npm install @openrouter/agent

If you also use non-agent SDK features (models list, chat completions, credits, OAuth, API keys), keep both packages installed.

Step 2: Update Imports

The OpenRouter client class and client.callModel() pattern work identically. Only the import source changes:

- import OpenRouter from '@openrouter/sdk';
+ import { OpenRouter } from '@openrouter/agent';

The rest of your code stays the same:

const client = new OpenRouter({ apiKey: process.env.OPENROUTER_API_KEY });

const result = client.callModel({
  model: 'openai/gpt-5-nano',
  input: 'Hello!',
});

const text = await result.getText();

Complete Import Mapping

Client & callModel
OldNew
import OpenRouter from '@openrouter/sdk'import { OpenRouter } from '@openrouter/agent'
import OpenRouter, { tool, stepCountIs } from '@openrouter/sdk'import { OpenRouter } from '@openrouter/agent'
import { tool } from '@openrouter/agent/tool'
import { stepCountIs } from '@openrouter/agent/stop-conditions'

A standalone callModel function is also available for advanced use cases where a pre-existing OpenRouterCore instance is available:

import { callModel } from '@openrouter/agent/call-model';

// Requires an OpenRouterCore instance (from @openrouter/sdk/core)
const result = callModel(coreInstance, { model: 'openai/gpt-5-nano', input: 'Hello' });

For most use cases, prefer the client.callModel() method shown above.

Tool Creation
OldNew
import { tool } from '@openrouter/sdk'import { tool } from '@openrouter/agent/tool'
Stop Conditions
OldNew
import { stepCountIs, hasToolCall, maxCost } from '@openrouter/sdk'import { stepCountIs, hasToolCall, maxCost } from '@openrouter/agent/stop-conditions'
import { maxTokensUsed, finishReasonIs } from '@openrouter/sdk'import { maxTokensUsed, finishReasonIs } from '@openrouter/agent/stop-conditions'
Types
OldNew
import type { Tool, ToolWithExecute, ToolWithGenerator, ManualTool } from '@openrouter/sdk/lib/tool-types'import type { Tool, ToolWithExecute, ToolWithGenerator, ManualTool } from '@openrouter/agent/tool-types'
import type { CallModelInput } from '@openrouter/sdk/lib/async-params'import type { CallModelInput } from '@openrouter/agent/async-params'
import { ModelResult } from '@openrouter/sdk/lib/model-result'import { ModelResult } from '@openrouter/agent/model-result'
Format Converters
OldNew
import { fromClaudeMessages, toClaudeMessage } from '@openrouter/sdk'import { fromClaudeMessages, toClaudeMessage } from '@openrouter/agent'
import { fromChatMessages, toChatMessage } from '@openrouter/sdk'import { fromChatMessages, toChatMessage } from '@openrouter/agent'
Type Guards
OldNew
import { hasExecuteFunction, isGeneratorTool, isRegularExecuteTool } from '@openrouter/sdk'import { hasExecuteFunction, isGeneratorTool, isRegularExecuteTool } from '@openrouter/agent/tool-types'

Before & After Example

Before (using @openrouter/sdk)
import OpenRouter, { tool, stepCountIs, hasToolCall } from '@openrouter/sdk';
import { z } from 'zod';

const client = new OpenRouter({
  apiKey: process.env.OPENROUTER_API_KEY,
});

const searchTool = tool({
  name: 'web_search',
  description: 'Search the web',
  inputSchema: z.object({ query: z.string() }),
  outputSchema: z.object({ results: z.array(z.string()) }),
  execute: async ({ query }) => {
    return { results: ['Result 1', 'Result 2'] };
  },
});

const finishTool = tool({
  name: 'finish',
  description: 'Complete the task',
  inputSchema: z.object({ answer: z.string() }),
  execute: async ({ answer }) => ({ answer }),
});

const result = client.callModel({
  model: 'openai/gpt-5-nano',
  instructions: 'You are a research assistant.',
  input: 'What are the latest AI developments?',
  tools: [searchTool, finishTool],
  stopWhen: [stepCountIs(10), hasToolCall('finish')],
});

const text = await result.getText();
After (using @openrouter/agent)
import { OpenRouter } from '@openrouter/agent';
import { tool } from '@openrouter/agent/tool';
import { stepCountIs, hasToolCall } from '@openrouter/agent/stop-conditions';
import { z } from 'zod';

const client = new OpenRouter({
  apiKey: process.env.OPENROUTER_API_KEY,
});

const searchTool = tool({
  name: 'web_search',
  description: 'Search the web',
  inputSchema: z.object({ query: z.string() }),
  outputSchema: z.object({ results: z.array(z.string()) }),
  execute: async ({ query }) => {
    return { results: ['Result 1', 'Result 2'] };
  },
});

const finishTool = tool({
  name: 'finish',
  description: 'Complete the task',
  inputSchema: z.object({ answer: z.string() }),
  execute: async ({ answer }) => ({ answer }),
});

const result = client.callModel({
  model: 'openai/gpt-5-nano',
  instructions: 'You are a research assistant.',
  input: 'What are the latest AI developments?',
  tools: [searchTool, finishTool],
  stopWhen: [stepCountIs(10), hasToolCall('finish')],
});

const text = await result.getText();

The only changes are the three import lines at the top.


When to Keep @openrouter/sdk

Keep @openrouter/sdk installed if you use any of these non-agent features:

FeatureAccess
Model listingclient.models.list()
Chat completionsclient.chat.send()
Legacy completionsclient.completions.generate()
Usage analyticsclient.analytics.getUserActivity()
Credit balanceclient.credits.getCredits()
API key managementclient.apiKeys.list(), .create(), etc.
OAuth PKCE flowclient.oAuth.createAuthCode(), .exchangeAuthCodeForAPIKey()

For mixed projects, use @openrouter/sdk for these features and @openrouter/agent for agent features:

import OpenRouter from '@openrouter/sdk';               // SDK client for models, credits, etc.
import { OpenRouter as Agent } from '@openrouter/agent'; // Agent client for callModel
import { tool } from '@openrouter/agent/tool';
import { stepCountIs } from '@openrouter/agent/stop-conditions';

// Use SDK client for non-agent features
const sdkClient = new OpenRouter({ apiKey: process.env.OPENROUTER_API_KEY });
const models = await sdkClient.models.list();
const credits = await sdkClient.credits.getCredits();

// Use Agent client for callModel
const agent = new Agent({ apiKey: process.env.OPENROUTER_API_KEY });
const result = agent.callModel({
  model: 'openai/gpt-5-nano',
  input: 'Hello!',
  tools: [myTool],
  stopWhen: stepCountIs(5),
});

New Features in @openrouter/agent

These features are only available in @openrouter/agent, not in @openrouter/sdk:

Shared Context Schema

Type-safe shared state across all tools in a conversation:

import { OpenRouter } from '@openrouter/agent';
import { z } from 'zod';

const client = new OpenRouter({ apiKey: '...' });

const result = client.callModel({
  model: 'openai/gpt-5-nano',
  input: 'Process this data',
  sharedContextSchema: z.object({
    userId: z.string(),
    sessionData: z.record(z.unknown()),
  }),
  context: {
    shared: { userId: '123', sessionData: {} },
  },
  tools: [myTool],
});
Tool Context

Tools can declare their own typed context and access shared context:

import { tool } from '@openrouter/agent/tool';
import { z } from 'zod';

const myTool = tool({
  name: 'my_tool',
  description: 'A tool with context',
  inputSchema: z.object({ query: z.string() }),
  contextSchema: z.object({ apiKey: z.string() }),
  execute: async (params, context) => {
    // context.local — this tool's own context
    // context.shared — shared context across all tools
    // context.setContext({ ... }) — update this tool's context
    // context.setSharedContext({ ... }) — update shared context
    return { result: 'done' };
  },
});
Tool Approval Flow

Require user approval before tool execution:

const dangerousTool = tool({
  name: 'delete_file',
  description: 'Delete a file',
  inputSchema: z.object({ path: z.string() }),
  requireApproval: true, // or a function: (toolCall, context) => boolean
  execute: async ({ path }) => { /* ... */ },
});
Turn Lifecycle Callbacks
const result = client.callModel({
  model: 'openai/gpt-5-nano',
  input: 'Complex task',
  tools: [myTool],
  onTurnStart: async (context) => {
    console.log(`Starting turn ${context.numberOfTurns}`);
  },
  onTurnEnd: async (context, response) => {
    console.log(`Turn ${context.numberOfTurns} complete`);
  },
});

All Subpath Exports

@openrouter/agent provides granular subpath imports:

SubpathExports
@openrouter/agentBarrel: all exports below
@openrouter/agent/clientOpenRouter class
@openrouter/agent/call-modelcallModel standalone function
@openrouter/agent/tooltool() factory function
@openrouter/agent/tool-typesTool, ToolWithExecute, ToolWithGenerator, ManualTool, type guards
@openrouter/agent/stop-conditionsstepCountIs, hasToolCall, maxCost, maxTokensUsed, finishReasonIs
@openrouter/agent/model-resultModelResult response wrapper
@openrouter/agent/async-paramsCallModelInput, hasAsyncFunctions, resolveAsyncFunctions
@openrouter/agent/anthropic-compatfromClaudeMessages, toClaudeMessage
@openrouter/agent/chat-compatfromChatMessages, toChatMessage
@openrouter/agent/conversation-statecreateInitialState, updateState, appendToMessages
@openrouter/agent/next-turn-paramsnextTurnParams utilities
@openrouter/agent/stream-transformersextractUnsupportedContent, getUnsupportedContentSummary
@openrouter/agent/tool-contextbuildToolExecuteContext, ToolContextStore
@openrouter/agent/tool-event-broadcasterToolEventBroadcaster
@openrouter/agent/turn-contextbuildTurnContext

Comments

Markdown supported