> **Building with AI coding agents?** If you're using an AI coding agent, install the official Scalekit plugin. It gives your agent full awareness of the Scalekit API — reducing hallucinations and enabling faster, more accurate code generation.
>
> - **Claude Code**: `claude plugin marketplace add scalekit-inc/claude-code-authstack && claude plugin install <auth-type>@scalekit-auth-stack`
> - **GitHub Copilot CLI**: `copilot plugin marketplace add scalekit-inc/github-copilot-authstack` then `copilot plugin install <auth-type>@scalekit-auth-stack`
> - **Codex**: run the bash installer, restart, then open Plugin Directory and enable `<auth-type>`
> - **Skills CLI** (Windsurf, Cline, 40+ agents): `npx skills add scalekit-inc/skills --list` then `--skill <skill-name>`
>
> `<auth-type>` / `<skill-name>`: `agentkit`, `full-stack-auth`, `mcp-auth`, `modular-sso`, `modular-scim` — [Full setup guide](https://docs.scalekit.com/dev-kit/build-with-ai/)

---

# Claude Managed Agents

Run Claude Managed Agents with Scalekit-authenticated tools using Virtual MCP Servers and Anthropic vaults.
> tip: 🚀 Complete working demo on GitHub
>
> Browse the full source for this guide at [Github](https://github.com/scalekit-inc/python-connect-demos/tree/main/claude-managed-agents).

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](/agentkit/mcp/overview/). 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.

## Prerequisites

- A Scalekit account with Gmail and Google Calendar connections configured. See [Configure a connection](/agentkit/connections/).
- An [Anthropic API key](https://platform.anthropic.com/settings/keys) with access to the Managed Agents beta.
- An Anthropic environment ID set as `ANTHROPIC_ENVIRONMENT_ID`.

## How it works

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.

## Install

```sh
pip install anthropic scalekit-sdk-python python-dotenv
```

## Build

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.

```python title="builder.py"

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_ENV_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("Virtual MCP ID:", mcp_id)
print("Agent ID: ", agent.id)
```

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

## Authorize user for external connections

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

```python title="executor_setup.py"

from scalekit import ScalekitClient
from dotenv import load_dotenv

load_dotenv()

scalekit_client = ScalekitClient(
    env_url=os.environ["SCALEKIT_ENV_URL"],
    client_id=os.environ["SCALEKIT_CLIENT_ID"],
    client_secret=os.environ["SCALEKIT_CLIENT_SECRET"],
)

# Retrieve mcp_id by listing Virtual MCP Servers filtered by name to use below
accounts_response = scalekit_client.actions.mcp.list_mcp_connected_accounts(
    config_id=mcp_id,
    identifier=identifier,    # your app's unique user ID
)

for account in accounts_response.connected_accounts:
    if account.connected_account_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} — {account.connected_account_status}")
```

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

## Run a session

Run the following for each agent execution.

1. ## Check that connections are active

   Before minting a token, confirm all connections are still `"ACTIVE"`. OAuth tokens for connected accounts can expire or be revoked.

   ```python title="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 != "ACTIVE"
   ]
   if inactive:
       print("Inactive connections:", inactive)
       # Prompt the user to re-authorize before proceeding
   ```

2. ## Mint a session token and store in 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.

   ```python title="executor.py"
   from datetime import timedelta

   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 vault and credential on first run; update the token on subsequent 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
   ```

3. ## Start the session

   Pass `vault_ids` to the session so the agent can authenticate against the MCP server.

   ```python title="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
   ```


---

## More Scalekit documentation

| Resource | What it contains | When to use it |
|----------|-----------------|----------------|
| [/llms.txt](/llms.txt) | Structured index with routing hints per product area | Start here — find which documentation set covers your topic before loading full content |
| [/llms-full.txt](/llms-full.txt) | Complete documentation for all Scalekit products in one file | Use when you need exhaustive context across multiple products or when the topic spans several areas |
| [sitemap-0.xml](https://docs.scalekit.com/sitemap-0.xml) | Full URL list of every documentation page | Use to discover specific page URLs you can fetch for targeted, page-level answers |
