metorial
v2.1.1
Published
The official Node.js/TypeScript SDK for [Metorial](https://metorial.com) - Connect your AI agents to any MCP server with a single line of code. Deploy tools like Slack, GitHub, SAP, and hundreds more without managing infrastructure.
Readme
Metorial Node.js SDK
The official Node.js/TypeScript SDK for Metorial - Connect your AI agents to any MCP server with a single line of code. Deploy tools like Slack, GitHub, SAP, and hundreds more without managing infrastructure.
Sign up for a free account to get started.
Complete API Documentation
API Documentation - Complete API reference and guides
Available Providers
| Provider | Import | Format | Models (non-exhaustive) |
| ----------------- | ----------------------------- | ---------------------------- | -------------------------------------------- |
| AI SDK | @metorial/ai-sdk | Framework tools | Any model via Vercel AI SDK |
| OpenAI | @metorial/openai | OpenAI function calling | gpt-4.1, gpt-4o, o1, o3 |
| Anthropic | @metorial/anthropic | Claude tool format | claude-sonnet-4-5, claude-opus-4 |
| Google | @metorial/google | Gemini function declarations | gemini-2.5-pro, gemini-2.5-flash |
| Mistral | @metorial/mistral | Mistral function calling | mistral-large-latest, codestral-latest |
| DeepSeek | @metorial/deepseek | OpenAI-compatible | deepseek-chat, deepseek-reasoner |
| TogetherAI | @metorial/togetherai | OpenAI-compatible | Llama-4, Qwen-3 |
| XAI | @metorial/xai | OpenAI-compatible | grok-3, grok-3-mini |
| LangChain | @metorial/langchain | LangChain tools | Any model via LangChain |
| OpenAI-Compatible | @metorial/openai-compatible | OpenAI-compatible | Any OpenAI-compatible API |
Quick Start
import { anthropic } from "@ai-sdk/anthropic";
import { metorialAiSdk } from "@metorial/ai-sdk";
import { Metorial } from "@metorial/sdk";
import { stepCountIs, streamText } from "ai";
// Get your API key at https://app.metorial.com
let metorial = new Metorial({ apiKey: "your-metorial-api-key" });
let result = await metorial.withProviderSession(
metorialAiSdk,
{
serverDeployments: [
{ serverDeploymentId: "your-server-deployment-id" },
],
streaming: true, // Required for streaming with tool calls
},
async ({ tools, closeSession }) => {
let result = streamText({
model: anthropic("claude-sonnet-4-5"),
prompt: "Research what makes Metorial so special.",
stopWhen: stepCountIs(25),
tools: tools,
onStepFinish: (step: any) => {
if (step.toolCalls) {
console.log(`🔧 Using tools: ${step.toolCalls.map((tc: any) => tc.toolName).join(", ")}`);
}
},
onFinish: async () => {
console.log("\n🎯 Stream completed!");
await closeSession();
},
});
return result;
}
);
console.log("🤖 AI Response:\n");
for await (const textPart of result.textStream) {
process.stdout.write(textPart);
}Installation
npm install metorial
# or
yarn add metorial
# or
pnpm add metorial
# or
bun add metorialOAuth Integration
When working with services that require user authentication (like Google Calendar, Slack, etc.), Metorial provides OAuth session management to handle the authentication flow:
import { Metorial } from 'metorial';
import { metorialAnthropic } from '@metorial/anthropic';
import Anthropic from '@anthropic-ai/sdk';
let metorial = new Metorial({ apiKey: 'your-metorial-api-key' });
let anthropic = new Anthropic({ apiKey: 'your-anthropic-api-key' });
// Create OAuth sessions for services that require user authentication
// this just needs to be done once per user
let [googleCalOAuthSession, slackOAuthSession] = await Promise.all([
metorial.oauth.sessions.create({
serverDeploymentId: 'your-google-calendar-server-deployment-id',
// Optional: callback URL after OAuth completion
// callbackUri: "https://your-app.com/oauth/callback",
}),
metorial.oauth.sessions.create({
serverDeploymentId: 'your-slack-server-deployment-id',
// Optional: callback URL after OAuth completion
// callbackUri: "https://your-app.com/oauth/callback",
})
]);
// Give user OAuth URLs for authentication
console.log('OAuth URLs for user authentication:');
console.log(` Google Calendar: ${googleCalOAuthSession.url}`);
console.log(` Slack: ${slackOAuthSession.url}`);
// Wait for user to complete OAuth flow
await metorial.oauth.waitForCompletion([googleCalOAuthSession, slackOAuthSession]);
console.log('OAuth sessions completed!');
// Now use the authenticated sessions
await metorial.withProviderSession(
metorialAnthropic,
{
serverDeployments: [
{
serverDeploymentId: 'your-google-calendar-server-deployment-id',
oauthSessionId: googleCalOAuthSession.id
},
{
serverDeploymentId: 'your-slack-server-deployment-id',
oauthSessionId: slackOAuthSession.id
},
{
serverDeploymentId: 'your-exa-server-deployment-id' // No OAuth needed for Exa
}
],
// streaming: true, // Optional: enable for streaming with tool calls
},
async ({ tools, callTools, closeSession }) => {
let messages: Anthropic.Messages.MessageParam[] = [
{
role: 'user',
content: `Look in Slack for mentions of potential partners. Use Exa to research their background,
company, and email. Schedule a 30-minute intro call with them for an open slot on Dec 13th, 2025
SF time and send me the calendar link.`
}
];
// Dedupe tools by name
let uniqueTools = Array.from(new Map(tools.map(t => [t.name, t])).values());
for (let i = 0; i < 10; i++) {
let response = await anthropic.messages.create({
model: 'claude-sonnet-4-5',
max_tokens: 1024,
messages,
tools: uniqueTools
});
let toolCalls = response.content.filter(
(c): c is Anthropic.Messages.ToolUseBlock => c.type === 'tool_use'
);
if (toolCalls.length === 0) {
let finalText = response.content
.filter((c): c is Anthropic.Messages.TextBlock => c.type === 'text')
.map(c => c.text)
.join('');
console.log(finalText);
await closeSession();
return;
}
let toolResponses = await callTools(toolCalls);
messages.push({ role: 'assistant', content: response.content as any }, toolResponses);
}
await closeSession();
}
);OAuth Flow Explained
- Create OAuth Sessions: Call
metorial.oauth.sessions.create()for each service requiring user authentication (only once per user) - Send URLs: Show the OAuth URLs to users so they can authenticate in their browser
- Wait for Completion: Use
metorial.oauth.waitForCompletion()to wait for users to complete the OAuth flow - Use Authenticated Sessions: Pass the
oauthSessionIdwhen configuringserverDeployments
Examples
Check out the examples/ directory for more comprehensive examples:
examples/typescript-ai-sdk/- Vercel AI SDK integration (recommended)examples/typescript-openai/- OpenAI integrationexamples/typescript-anthropic/- Anthropic integrationexamples/typescript-google/- Google Gemini integrationexamples/typescript-deepseek/- DeepSeek integration
Provider Examples
OpenAI
import { Metorial } from 'metorial';
import { metorialOpenAI } from '@metorial/openai';
import OpenAI from 'openai';
let metorial = new Metorial({ apiKey: 'your-metorial-api-key' });
let openai = new OpenAI({ apiKey: 'your-openai-api-key' });
await metorial.withProviderSession(
metorialOpenAI.chatCompletions,
{
serverDeployments: ['your-server-deployment-id'],
// streaming: true, // Optional: enable for streaming with tool calls
},
async ({ tools, callTools, closeSession }) => {
let messages: OpenAI.Chat.Completions.ChatCompletionMessageParam[] = [
{ role: 'user', content: 'What are the latest commits?' }
];
for (let i = 0; i < 10; i++) {
let response = await openai.chat.completions.create({
model: 'gpt-4o',
messages,
tools: tools
});
let choice = response.choices[0]!;
let toolCalls = choice.message.tool_calls;
if (!toolCalls) {
console.log(choice.message.content);
await closeSession();
return;
}
let toolResponses = await callTools(toolCalls);
messages.push(
{ role: 'assistant', tool_calls: choice.message.tool_calls },
...toolResponses
);
}
await closeSession();
}
);Anthropic (Claude)
import { Metorial } from 'metorial';
import { metorialAnthropic } from '@metorial/anthropic';
import Anthropic from '@anthropic-ai/sdk';
let metorial = new Metorial({ apiKey: 'your-metorial-api-key' });
let anthropic = new Anthropic({ apiKey: 'your-anthropic-api-key' });
await metorial.withProviderSession(
metorialAnthropic,
{
serverDeployments: ['your-server-deployment-id'],
// streaming: true, // Optional: enable for streaming with tool calls
},
async ({ tools, callTools, closeSession }) => {
let messages: Anthropic.Messages.MessageParam[] = [
{ role: 'user', content: 'Help me with this GitHub task: ...' }
];
// Dedupe tools by name
let uniqueTools = Array.from(new Map(tools.map(t => [t.name, t])).values());
let response = await anthropic.messages.create({
model: 'claude-sonnet-4-5',
max_tokens: 1024,
messages,
tools: uniqueTools
});
// Handle tool calls
let toolCalls = response.content.filter(
(c): c is Anthropic.Messages.ToolUseBlock => c.type === 'tool_use'
);
if (toolCalls.length > 0) {
let toolResponses = await callTools(toolCalls);
messages.push({ role: 'assistant', content: response.content as any }, toolResponses);
}
await closeSession();
}
);Google (Gemini)
import { Metorial } from 'metorial';
import { metorialGoogle } from '@metorial/google';
import { GoogleGenerativeAI } from '@google/generative-ai';
let metorial = new Metorial({ apiKey: 'your-metorial-api-key' });
let genAI = new GoogleGenerativeAI('your-google-api-key');
await metorial.withProviderSession(
metorialGoogle,
{
serverDeployments: ['your-server-deployment-id'],
// streaming: true, // Optional: enable for streaming with tool calls
},
async ({ tools, closeSession }) => {
let model = genAI.getGenerativeModel({
model: 'gemini-2.5-pro',
tools: tools
});
let response = await model.generateContent('What can you help me with?');
// Handle function calls if present
// ... tool call handling logic
await closeSession();
}
);OpenAI-Compatible (DeepSeek, TogetherAI, XAI)
import { Metorial } from 'metorial';
import { metorialDeepSeek } from '@metorial/deepseek';
import OpenAI from 'openai';
// Works with any OpenAI-compatible API
let deepseekClient = new OpenAI({
apiKey: 'your-deepseek-key',
baseURL: 'https://api.deepseek.com'
});
let metorial = new Metorial({ apiKey: 'your-metorial-api-key' });
await metorial.withProviderSession(
metorialDeepSeek.chatCompletions,
{
serverDeployments: ['your-server-deployment-id'],
// streaming: true, // Optional: enable for streaming with tool calls
},
async ({ tools, closeSession }) => {
let response = await deepseekClient.chat.completions.create({
model: 'deepseek-chat',
messages: [{ role: 'user', content: 'Help me code' }],
tools: tools
});
// ... handle response
await closeSession();
}
);Core API
Metorial Class
import { Metorial } from 'metorial';
let metorial = new Metorial({
apiKey: 'your-api-key'
});Session Management
// Provider session (recommended)
await metorial.withProviderSession(
provider.chatCompletions,
{
serverDeployments: ['deployment-id'],
// streaming: true, // Optional: enable for streaming with tool calls
},
async ({ tools, callTools, closeSession }) => {
// Your session logic here
await closeSession(); // Close when done
}
);
// Direct session management
await metorial.withSession(['deployment-id'], async session => {
// Your session logic here
});Session Callback
The callback provides:
async ({ tools, callTools, closeSession }) => {
// tools: Tool definitions formatted for your provider
// callTools: Execute tools and get responses
// closeSession: Close the session when done (always call this!)
}Error Handling
import { MetorialAPIError } from 'metorial';
try {
await metorial.withProviderSession(/* ... */);
} catch (error) {
if (error instanceof MetorialAPIError) {
console.error(`API Error: ${error.message} (Status: ${error.status})`);
} else {
console.error(`Unexpected error:`, error);
}
}License
MIT License - see LICENSE file for details.
