resolveModel
Turns one string, "<provider>/<model>", into an AI SDK LanguageModel for any frontier model.
Turns one string, "<provider>/<model>", into an AI SDK LanguageModel for any frontier model. Use it on the server when you build the model yourself; createChatHandler uses it for its model option and for each agent’s model. The provider’s own AI SDK package is used when its key is set (the package is an optional peer, loaded only then); otherwise the AI Gateway built into ai serves any a/b id with AI_GATEWAY_API_KEY. A bare claude-… id still means Anthropic.
Import
import {
displayName,
ModelUnavailableError,
modelAvailability,
parseModelId,
PROVIDERS,
providerOptionsFor,
resolveModel,
} from "@webmcp-agents/chat/server";
The types:
import type {
ModelAvailability,
ParsedModelId,
ProviderOptions,
ProviderSpec,
ReasoningLevel,
} from "@webmcp-agents/chat/server";
Example
Check that a model can run, build it, and spell a reasoning level the provider’s way.
import {
modelAvailability,
providerOptionsFor,
resolveModel,
} from "@webmcp-agents/chat/server";
// Keys stay on the server: .env.local has OPENAI_API_KEY=…
const id = "openai/gpt-5.5";
// Synchronous; loads no SDK. Says which variable to set.
const check = modelAvailability(id, process.env);
if (!check.enabled) throw new Error(check.message);
// Loads @ai-sdk/openai once and builds the model.
const { model, provider } = await resolveModel(id, process.env);
// The reasoning level, spelled the provider's way.
console.log(providerOptionsFor(provider, "low"));
// { openai: { reasoningEffort: "low", reasoningSummary: "auto" } }
console.log(provider, model);
In .env.local (server side only), a model and its key:
WEB_AGENTS_MODEL=openrouter/anthropic/claude-opus-5.5
OPENROUTER_API_KEY=sk-or-…
Options
resolveModel(id, env):
| Name | Type | Default | Description |
|---|---|---|---|
id |
string |
required | The model, "<provider>/<model>" (for example openai/gpt-5.5), or a bare claude-… id for Anthropic. |
env |
Record<string, string | undefined> |
required | Where the keys are read, usually process.env. |
Providers
The prefix picks the provider. The provider’s key wins; its package is an optional peer you install on the server.
| Prefix | Package | Env var | Reasoning option |
|---|---|---|---|
anthropic/ (and bare claude-*) |
@ai-sdk/anthropic |
ANTHROPIC_API_KEY |
effort + fallbacks: "default" |
openai/ |
@ai-sdk/openai |
OPENAI_API_KEY |
reasoningEffort (minimal for off), reasoningSummary: "auto" |
google/ |
@ai-sdk/google |
GOOGLE_GENERATIVE_AI_API_KEY |
thinkingConfig.thinkingLevel (minimal for off), includeThoughts |
deepseek/ |
@ai-sdk/deepseek |
DEEPSEEK_API_KEY |
reasoningEffort for low and high only |
xai/ |
@ai-sdk/xai |
XAI_API_KEY |
reasoningEffort (none for off), reasoningSummary: "auto" |
mistral/ |
@ai-sdk/mistral |
MISTRAL_API_KEY |
reasoningEffort (none for off) |
openrouter/<vendor>/<model> |
@openrouter/ai-sdk-provider |
OPENROUTER_API_KEY |
reasoning: { effort } (none for off) |
any other a/b id |
none: createGateway from ai |
AI_GATEWAY_API_KEY (or VERCEL_OIDC_TOKEN) |
the upstream provider’s object, keyed by the prefix |
Option names were checked against the installed SDKs on 2026-11-08 (@ai-sdk/openai 4.0, google 4.0, xai 5.0, deepseek 3.0, mistral 4.0, @openrouter/ai-sdk-provider 3.1). PROVIDERS exports this table as Record<string, ProviderSpec> (id, pkg, envKey, label, load, reasoning); adding a provider means adding a spec to it in this package.
The other functions
| Function | Description |
|---|---|
modelAvailability(id, env) |
ModelAvailability, synchronously and without loading an SDK: { enabled, provider, missingEnv, message, viaGateway }. message names the env var(s) that would enable the id ("Set OPENAI_API_KEY or AI_GATEWAY_API_KEY on the server to use openai/gpt-5.5."). With only OPENROUTER_API_KEY set and a bare vendor id, it suggests the openrouter/ form. |
parseModelId(id) |
ParsedModelId: { provider, model, id }. Splits at the first slash, so openrouter/anthropic/claude-opus-5.5 is provider openrouter, model anthropic/claude-opus-5.5. A bare id is Anthropic’s; an unknown prefix is provider "gateway". Throws a TypeError when nothing follows the slash. |
providerOptionsFor(provider, level, id?) |
The providerOptions for a ReasoningLevel ("off", "low", "medium" or "high"), spelled the provider’s way and keyed by its id; undefined when the provider has no such knob. Through the gateway (provider is "gateway"), pass id: the upstream provider’s object is keyed by the id’s prefix. |
displayName(id) |
A readable name: anthropic/claude-opus-5-5 is “Claude Opus 5.5”, openai/gpt-5.5 is “GPT-5.5”, openrouter/deepseek/deepseek-v4 is “DeepSeek V4 via OpenRouter”. Also exported from @webmcp-agents/chat; the cloud adapter labels itself with it. |
Returns
resolveModel resolves to { model: LanguageModel; provider: string }. It rejects with a ModelUnavailableError (code: "missing_env", "missing_package" or "unknown_provider") when the key is missing, the package is not installed ("Install @ai-sdk/openai on the server to use openai/gpt-5.5 …") or the prefix is unknown and no gateway key is set.
Notes
- The provider’s key wins. Without it, a gateway key serves any slash id, except
openrouter/ids (OpenRouter’s own catalog) and bare ids: gateway ids use dots (anthropic/claude-opus-5.5) and SDK ids use dashes (claude-opus-5-5), so an id is never rerouted between the two catalogs. resolveModelloads a provider’s package with a literalimport()once per key and caches the factory, so a bundler and Vercel’s tracer follow it.createChatHandlerreports the resolvedproviderin itsGETanswer.
See also
- Add the chat, where the model is chosen
createChatHandler, themodelandreasoningoptionscreateCloudAdapter, which labels itself withdisplayName- Deploy, the env vars