Mastra
Connect a Mastra agent to Scalekit tools over MCP, all in Node.js. Create a Virtual MCP server, mint a session token for the user, and run the agent.
Connect a Mastra agent to Scalekit tools using MCP. Mastra has native MCP support via @mastra/mcp. Pass a Scalekit Virtual MCP server URL and a session token, and Mastra handles tool discovery automatically.
The whole example runs in Node.js on your server, in two files. setup.mts creates the Virtual MCP server once. agent.mts connects the user, mints a session token and runs the agent, every run. Run each with npx tsx, for example npx tsx agent.mts.
Before you start
Section titled “Before you start”- Create a Gmail connection in AgentKit > Connections. The samples use the connection name
gmail, so change it if yours differs. See Set up a connection. - Have the
.envfile from the Quickstart, and add your OpenAI API key asOPENAI_API_KEYto it. - The samples load
.envwithimport 'dotenv/config'.
Install
Section titled “Install”npm install @scalekit-sdk/node @mastra/core@1 @mastra/mcp@2 @ai-sdk/openai@4 dotenvInitialize the Scalekit client
Section titled “Initialize the Scalekit client”Start both files with the client:
import 'dotenv/config';import { ScalekitClient } from '@scalekit-sdk/node';
const scalekit = new ScalekitClient( process.env.SCALEKIT_ENVIRONMENT_URL!, process.env.SCALEKIT_CLIENT_ID!, process.env.SCALEKIT_CLIENT_SECRET!,);Create the Virtual MCP server
Section titled “Create the Virtual MCP server”Create the server once per agent role, not once per user. Every user and every session reuses its mcpServerUrl. Server names are unique in an environment, so run this file once: a second run fails with config with name 'gmail-user-tools' already exists.
// Run once per agent role, not once per userconst { config } = await scalekit.actions.mcp.createConfig({ name: 'gmail-user-tools', connectionToolMappings: [{ connectionName: 'gmail', tools: ['gmail_fetch_mails'] }],});console.log('Virtual MCP server:', config!.id, config!.mcpServerUrl);Create a Virtual MCP server covers choosing tools, and listing, updating and deleting servers.
Connect the user to Gmail
Section titled “Connect the user to Gmail”The rest of the code goes in agent.mts and runs before each agent run.
The Virtual MCP server only calls tools for users who have authorized the connection. Before each agent run, make sure the user’s Gmail account is ACTIVE:
import { createInterface } from 'node:readline/promises';import { ConnectorStatus } from '@scalekit-sdk/node';
// Connect the user's Gmail account, and wait until it's ACTIVE before calling toolsconst connectionName = 'gmail';const identifier = 'user_123'; // your app's unique user ID
let { connectedAccount } = await scalekit.actions.getOrCreateConnectedAccount({ connectionName, identifier,});if (connectedAccount?.status !== ConnectorStatus.ACTIVE) { const { link } = await scalekit.actions.getAuthorizationLink({ connectionName, identifier }); console.log('Authorize Gmail:', link); const rl = createInterface({ input: process.stdin, output: process.stdout }); await rl.question('Press Enter after authorizing...'); rl.close(); // Fetch the account again to pick up the new status ({ connectedAccount } = await scalekit.actions.getOrCreateConnectedAccount({ connectionName, identifier, }));}
if (connectedAccount?.status !== ConnectorStatus.ACTIVE) { throw new Error(`Gmail is not ACTIVE (status ${connectedAccount?.status}). Authorize it and run again.`);}See Authorize a user for production auth handling.
Mint a session token for the user
Section titled “Mint a session token for the user”The server URL is the same for everyone. The session token carries the user’s identity. Mint a new token before each agent run, after the connect step:
import { ScalekitNotFoundException, ScalekitUnauthorizedException } from '@scalekit-sdk/node';
// Run before each agent session, after the connect steplet mcpServerUrl: string;let sessionToken: string;try { const { configs } = await scalekit.actions.mcp.listConfigs({ search: 'gmail-user-tools' }); const server = configs.find((c) => c.name === 'gmail-user-tools'); if (!server) throw new Error('Run setup.mts to create the gmail-user-tools server first'); mcpServerUrl = server.mcpServerUrl;
const session = await scalekit.actions.mcp.createSessionToken({ mcpConfigId: server.id, identifier, // the user who authorized Gmail above expirySeconds: 60 * 60, }); sessionToken = session.token;} catch (err) { if (err instanceof ScalekitUnauthorizedException) { // Scalekit client credentials are wrong or expired: fix the environment variables } else if (err instanceof ScalekitNotFoundException) { // The server was deleted: create it again, then retry } throw err; // don't start the agent without a token}Don’t start the agent when minting fails: every tool call would return 401. See Errors and rate limits for what an error response contains. Set expirySeconds above the expected run time; Mint session tokens covers when to mint again.
Build the agent
Section titled “Build the agent”Pass the server URL to MCPClient, and the user’s session token as a bearer header in requestInit. Mastra fetches the tool list and schemas automatically. It names each tool after the server key, so gmail_fetch_mails appears to the model as scalekit_gmail_fetch_mails.
import { Agent } from '@mastra/core/agent';import { MCPClient } from '@mastra/mcp';import { openai } from '@ai-sdk/openai';
const mcp = new MCPClient({ servers: { scalekit: { url: new URL(mcpServerUrl), requestInit: { headers: { Authorization: `Bearer ${sessionToken}` } }, }, },});
try { const tools = await mcp.listTools();
const agent = new Agent({ id: 'gmail-assistant', name: 'Gmail assistant', instructions: 'You are a helpful Gmail assistant.', model: openai('gpt-4o'), tools, });
const result = await agent.generate('Fetch my last 5 unread emails and summarize them'); console.log(result.text);} finally { await mcp.disconnect();}API reference
The endpoints this page's code calls, with every field and the SDK method for each: