Skip to content
Scalekit Docs
Talk to an Engineer Dashboard

Mint session tokens

Mint a session token so an agent can call a Virtual MCP server's tools as one user, and decide when to mint again for scheduled and long-running agents.

A session token lets an MCP client call a Virtual MCP server’s tools as one user. Mint one before each run: check that the user’s connections are active, mint the token, and pass it to the agent as a bearer token.

  • A Virtual MCP server and its ID. For cURL, keep the ID in CONFIG_ID and the URL in MCP_SERVER_URL.

  • The user’s identifier: your app’s ID for that user, the same value you passed when they authorized their connections. Use an ID that’s stable and hard to guess, not an email address. Choose an identifier explains why.

  • A client set up as in the Quickstart: actions in Python, scalekit in Node.js. For cURL, get an access token and keep it in TOKEN:

    Terminal window
    TOKEN=$(curl -sS "$SCALEKIT_ENVIRONMENT_URL/oauth/token" \
    -d grant_type=client_credentials \
    -d client_id="$SCALEKIT_CLIENT_ID" \
    -d client_secret="$SCALEKIT_CLIENT_SECRET" | jq -r .access_token)
  1. List the user’s connected account for each connection on the server. For any that isn’t ACTIVE, send the user its authorization link before the run.

    response = actions.mcp.list_mcp_connected_accounts(
    config_id=config_id,
    identifier="user_123",
    include_auth_link=True,
    )
    not_ready = [a for a in response.connected_accounts if a.connected_account_status != "ACTIVE"]
    for account in not_ready:
    print(f"{account.connection_name} is {account.connected_account_status}: {account.authentication_link}")

    If the user has no account yet for a connection, this call creates one in PENDING_AUTH and returns its link.

  2. Set expiry a little above how long the run takes. It must be between 60 seconds and 24 hours, and defaults to one hour.

    from datetime import timedelta
    session = actions.mcp.create_session_token(
    mcp_config_id=config_id,
    identifier="user_123",
    expiry=timedelta(minutes=30),
    )
    session_token = session.token
    print(session.expires_at)
  3. Pass the server URL and Authorization: Bearer <session token> to your MCP client. Most frameworks take them as the server’s URL and headers. With the MCP SDKs, or directly over HTTP:

    import asyncio
    import httpx2
    from mcp import ClientSession
    from mcp.client.streamable_http import streamable_http_client
    async def list_tools():
    headers = {"Authorization": f"Bearer {session_token}"}
    async with httpx2.AsyncClient(headers=headers, timeout=30) as http_client:
    async with streamable_http_client(mcp_server_url, http_client=http_client) as (read, write):
    async with ClientSession(read, write) as mcp_session:
    await mcp_session.initialize()
    result = await mcp_session.list_tools()
    print([tool.name for tool in result.tools])
    asyncio.run(list_tools())

    The Python example uses mcp 2.x (pip install "mcp>=2"), and the Node.js example uses @modelcontextprotocol/sdk. For clients without an MCP SDK, Connect any MCP client has the full HTTP contract, including tools/call and the response format. Choose a framework shows the setup for CrewAI, Mastra and Claude Managed Agents.

A token can’t be refreshed, so decide when your code mints a new one:

Your agentWhen to mint
Runs once, for minutesOnce, right before the run. Set expiry above the longest run you expect.
Chat session with a userWhen the session starts. Mint again if the session outlives the token, or when a call returns 401.
Scheduled job, such as a daily briefingAt the start of each run. Don’t store a token between runs.
Many runs at onceOne token per run. Tokens are independent, and minting a new one doesn’t revoke the others.
Long-running host or gatewayOn a schedule, before expires_at. Give the host the new token, then reload it. A host with an expired token fails at its next tool call, not at startup.

A token lasts at most 24 hours, so a client that can’t update its headers, such as a config file you edit by hand, stops working within a day.

tools/list returns the tools you chose for the server, and a tool call returns the app’s response under data.

The token expired, is for a different server, or was copied incompletely. Mint a new token for this server.

Set expiry between 60 seconds and 24 hours. In REST, send it in seconds, such as "1800s".

A tool call fails because the account isn’t connected

Section titled “A tool call fails because the account isn’t connected”

The user’s account for that connection isn’t ACTIVE. Run the check in step 1 and send the authorization link. If the server lists connect_account, the agent can get the link itself.

API reference

The endpoints this page's code calls, with every field and the SDK method for each: