Mastra
Connect a Mastra agent to Scalekit tools using MCP. Mastra's MCP client connects to a Virtual MCP Server URL with a session token.
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 Mastra agent runs in Node.js. Creating the Virtual MCP server and minting session tokens use the Python SDK, because the Node.js SDK doesn’t create session tokens yet. Run those parts on your backend.
Install
Section titled “Install”pip install scalekit-sdk-python python-dotenvnpm install @mastra/core@1 @mastra/mcp@2 @ai-sdk/openai@4 dotenvFor Node.js, save the agent code in a .mts file, such as agent.mts, and run it with npx tsx agent.mts.
Initialize the Scalekit client
Section titled “Initialize the Scalekit client”Both backend scripts below start with this client:
import osfrom scalekit import ScalekitClientfrom dotenv import load_dotenv
load_dotenv()
scalekit_client = ScalekitClient( env_url=os.environ["SCALEKIT_ENVIRONMENT_URL"], client_id=os.environ["SCALEKIT_CLIENT_ID"], client_secret=os.environ["SCALEKIT_CLIENT_SECRET"],)actions = scalekit_client.actionsCreate 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 static mcp_server_url.
# Backend (Python): run oncefrom scalekit.actions.models.mcp_config import McpConfigConnectionToolMapping
vmcp_response = actions.mcp.create_config( name="gmail-user-tools", connection_tool_mappings=[ McpConfigConnectionToolMapping( connection_name="gmail", tools=["gmail_fetch_mails"], ), ],)print("Virtual MCP ID:", vmcp_response.config.id)See Set up and connect a Virtual MCP server for the full setup, including how to choose which tools to expose.
Connect the user to Gmail
Section titled “Connect the user to Gmail”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:
# Connect the user's Gmail account, and wait until it's ACTIVE before calling toolsconnection_name = "gmail"identifier = "user_123" # your app's unique user ID
response = actions.get_or_create_connected_account( connection_name=connection_name, identifier=identifier)if response.connected_account.status != "ACTIVE": link = actions.get_authorization_link( connection_name=connection_name, identifier=identifier ) print("Authorize Gmail:", link.link) input("Press Enter after authorizing...") # Fetch the account again to pick up the new status response = actions.get_or_create_connected_account( connection_name=connection_name, identifier=identifier )
if response.connected_account.status != "ACTIVE": raise RuntimeError( f"Gmail is {response.connected_account.status}, not ACTIVE. 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 static. The session token carries the user identity. Mint a fresh token before each agent run and pass it to your Mastra app. Put this after the connect step in the same script:
# Backend (Python): run before each agent sessionfrom datetime import timedelta
from scalekit.common.exceptions import ( ScalekitNotFoundException, ScalekitUnauthorizedException, ScalekitServerException,)
try: list_response = actions.mcp.list_configs(filter_name="gmail-user-tools") mcp_server_url = list_response.configs[0].mcp_server_url config_id = list_response.configs[0].id
token_response = actions.mcp.create_session_token( mcp_config_id=config_id, identifier=identifier, # the user who authorized Gmail above expiry=timedelta(hours=1), )except ScalekitNotFoundException: # The server was deleted or renamed — recreate it, then retry raiseexcept ScalekitUnauthorizedException: # Scalekit client credentials are wrong or expired — fix the environment variables raiseexcept ScalekitServerException as e: # Unexpected platform error — do not start the agent without a token print(e.error_code, e.http_status) raise
# Hand these to the Mastra app for this user only. For local testing, add them to .env.print(f"SCALEKIT_MCP_SERVER_URL={mcp_server_url}")print(f"SCALEKIT_MCP_SESSION_TOKEN={token_response.token}")Do not start the agent when minting fails. An agent that runs without a token calls every tool and gets a 401. See Error handling for the full exception list.
Set expiry longer than the expected agent run. create_session_token also mints replacements. Call it again whenever you need a new token.
Build the agent
Section titled “Build the agent”Pass the static 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';import 'dotenv/config';
// Minted by your backend for the current user; never shared between usersconst mcpServerUrl = process.env.SCALEKIT_MCP_SERVER_URL;const mcpToken = process.env.SCALEKIT_MCP_SESSION_TOKEN;if (!mcpServerUrl || !mcpToken) { throw new Error('Set SCALEKIT_MCP_SERVER_URL and SCALEKIT_MCP_SESSION_TOKEN from the mint step');}
const mcp = new MCPClient({ servers: { scalekit: { url: new URL(mcpServerUrl), requestInit: { headers: { Authorization: `Bearer ${mcpToken}` }, }, }, },});
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();}