Skip to content
Scalekit Docs

Claude Managed Agents

Run Claude Managed Agents with Scalekit-authenticated tools using Virtual MCP Servers and Anthropic vaults.

Run a background agent that reads Gmail and creates Google Calendar events — without managing any agent loop. Anthropic handles tool discovery, execution, retries, and session state. You provide the task.

Scalekit connects the agent to user-authorized tools via a Virtual MCP Server. Before each run, you mint a short-lived session token and store it in an Anthropic vault. The agent accesses the MCP server using the vault credential.

  • A Scalekit account with Gmail and Google Calendar connections configured. See Configure a connection.
  • An Anthropic API key with access to the Managed Agents beta.
  • An Anthropic environment ID set as ANTHROPIC_ENVIRONMENT_ID.

The flow has three phases:

  1. Build (one-time) — Create a Virtual MCP Server and a Claude Managed Agent. Save mcp_id and agent_id.
  2. Authorize user for external connections (once per user) — Authorize the user’s Gmail and Google Calendar accounts.
  3. Run a session (per agent run) — Check connections, mint a session token, store it in an Anthropic vault, and start a session.
Terminal window
pip install anthropic scalekit-sdk-python python-dotenv

Run this once to create your Virtual MCP Server and Claude Managed Agent. Save the returned mcp_id and agent_id — you reuse them for every user and every session.

builder.py
import os
import anthropic
from scalekit import ScalekitClient
from scalekit.actions.models.mcp_config import McpConfigConnectionToolMapping
from dotenv import load_dotenv
load_dotenv()
anthropic_client = anthropic.Anthropic()
scalekit_client = ScalekitClient(
env_url=os.environ["SCALEKIT_ENVIRONMENT_URL"],
client_id=os.environ["SCALEKIT_CLIENT_ID"],
client_secret=os.environ["SCALEKIT_CLIENT_SECRET"],
)
GMAIL_TOOLS = ["gmail_fetch_mails"]
GCAL_TOOLS = [
"googlecalendar_list_calendars",
"googlecalendar_list_events",
"googlecalendar_get_event_by_id",
"googlecalendar_create_event",
"googlecalendar_update_event",
]
vmcp_response = scalekit_client.actions.mcp.create_config(
name="email-calendar-demo",
connection_tool_mappings=[
McpConfigConnectionToolMapping(
connection_name="gmail",
tools=GMAIL_TOOLS,
),
McpConfigConnectionToolMapping(
connection_name="googlecalendar",
tools=GCAL_TOOLS,
),
],
)
mcp_id = vmcp_response.config.id
mcp_server_url = vmcp_response.config.mcp_server_url
agent = anthropic_client.beta.agents.create(
name="Email Meeting Manager",
model="claude-haiku-4-5-20251001",
system=(
"You are an email and calendar assistant. When invoked, you will:\n"
"1. Fetch the single most recent unread email from Gmail.\n"
"2. Summarize it in 2-3 sentences.\n"
"3. Create a Google Calendar event titled 'Action Required: <email subject>' "
"with your summary as the description."
),
mcp_servers=[
{
"type": "url",
"name": "email-calendar-mcp",
"url": mcp_server_url,
}
],
tools=[
{"type": "agent_toolset_20260401", "default_config": {"enabled": True}},
{
"type": "mcp_toolset",
"mcp_server_name": "email-calendar-mcp",
"default_config": {
"enabled": True,
"permission_policy": {"type": "always_allow"},
},
},
],
)
print("Add these to your .env file:")
print(f"SCALEKIT_MCP_CONFIG_ID={mcp_id}")
print(f"ANTHROPIC_AGENT_ID={agent.id}")

Run python builder.py once and add the two printed lines to your .env file. The next two scripts read them from there.

The agent definition references the mcp_server_url but carries no auth credentials. Authentication is injected at runtime via the Anthropic vault.

Each user authorizes their Gmail and Google Calendar accounts once. All future agent sessions for that user reuse those connections.

executor_setup.py
import os
from scalekit import ScalekitClient
from 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"],
)
mcp_id = os.environ["SCALEKIT_MCP_CONFIG_ID"] # printed by builder.py
identifier = "user_123" # your app's unique user ID
accounts_response = scalekit_client.actions.mcp.list_mcp_connected_accounts(
config_id=mcp_id,
identifier=identifier,
)
for account in accounts_response.connected_accounts:
status = (account.connected_account_status or "").upper()
if status != "ACTIVE":
auth_response = scalekit_client.actions.get_authorization_link(
identifier=identifier,
connection_name=account.connection_name,
)
print(f"{account.connection_name} needs auth: {auth_response.link}")
else:
print(f"✓ {account.connection_name} is ACTIVE")

Surface the auth link in your app UI or send it via email. Users only need to do this once.

Run executor.py for each agent run. It stops before starting a session if any connection isn’t ACTIVE, so the agent never runs with a tool it can’t call.

  1. executor.py
    import os
    import sys
    from datetime import timedelta
    import anthropic
    from dotenv import load_dotenv
    from scalekit import ScalekitClient
    load_dotenv()
    anthropic_client = anthropic.Anthropic()
    scalekit_client = ScalekitClient(
    env_url=os.environ["SCALEKIT_ENVIRONMENT_URL"],
    client_id=os.environ["SCALEKIT_CLIENT_ID"],
    client_secret=os.environ["SCALEKIT_CLIENT_SECRET"],
    )
    mcp_id = os.environ["SCALEKIT_MCP_CONFIG_ID"] # printed by builder.py
    agent_id = os.environ["ANTHROPIC_AGENT_ID"] # printed by builder.py
    identifier = "user_123" # your app's unique user ID
    prompt = "Process my most recent unread email."
    # Created on the first run; add the printed IDs to .env to reuse them
    vault_id = os.getenv("ANTHROPIC_VAULT_ID")
    credential_id = os.getenv("ANTHROPIC_CREDENTIAL_ID")
  2. OAuth tokens for connected accounts can expire or be revoked, so check every connection before minting a token. If any isn’t ACTIVE, stop and send the user back through executor_setup.py.

    executor.py
    accounts_response = scalekit_client.actions.mcp.list_mcp_connected_accounts(
    config_id=mcp_id,
    identifier=identifier,
    )
    inactive = [
    a.connection_name
    for a in accounts_response.connected_accounts
    if (a.connected_account_status or "").upper() != "ACTIVE"
    ]
    if inactive:
    sys.exit(f"Not ACTIVE: {', '.join(inactive)}. Run executor_setup.py to reauthorize.")
  3. Mint a session token and store it in the vault

    Section titled “Mint a session token and store it in the vault”

    Mint a short-lived session token and store it in an Anthropic vault. Claude Managed Agents access the MCP server using the vault credential, not a direct bearer header.

    executor.py
    configs_response = scalekit_client.actions.mcp.list_configs(filter_id=mcp_id)
    mcp_server_url = configs_response.configs[0].mcp_server_url
    token_response = scalekit_client.actions.mcp.create_session_token(
    mcp_config_id=mcp_id,
    identifier=identifier,
    expiry=timedelta(hours=1),
    )
    token = token_response.token
    # Create the vault and credential on the first run; update the token on later runs
    if vault_id and credential_id:
    anthropic_client.beta.vaults.credentials.update(
    credential_id,
    vault_id=vault_id,
    auth={"type": "static_bearer", "token": token},
    )
    else:
    vault = anthropic_client.beta.vaults.create(display_name="email-calendar-vault")
    vault_id = vault.id
    credential = anthropic_client.beta.vaults.credentials.create(
    vault_id,
    display_name="email-calendar-credential",
    auth={
    "type": "static_bearer",
    "mcp_server_url": mcp_server_url,
    "token": token,
    },
    )
    credential_id = credential.id
    print("Add these to your .env file to reuse the vault:")
    print(f"ANTHROPIC_VAULT_ID={vault_id}")
    print(f"ANTHROPIC_CREDENTIAL_ID={credential_id}")
  4. Pass vault_ids to the session so the agent can authenticate against the MCP server.

    executor.py
    session = anthropic_client.beta.sessions.create(
    agent=agent_id,
    environment_id=os.environ["ANTHROPIC_ENVIRONMENT_ID"],
    vault_ids=[vault_id],
    )
    with anthropic_client.beta.sessions.events.stream(session_id=session.id) as stream:
    anthropic_client.beta.sessions.events.send(
    session_id=session.id,
    events=[{"type": "user.message", "content": [{"type": "text", "text": prompt}]}],
    )
    for event in stream:
    if event.type == "agent.message":
    for block in event.content:
    if block.type == "text":
    print(block.text, end="", flush=True)
    elif event.type == "agent.mcp_tool_use":
    print(f"\n→ {event.name}", flush=True)
    elif event.type in ("session.status_idle", "session.status_terminated"):
    break