> **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**: `/plugin marketplace add scalekit-inc/claude-code-authstack` then `/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/)

---

# Give your agent tool access via MCP

Create a per-user MCP server with whitelisted, pre-authenticated tools; then hand your agent a single URL.
When your agent needs to act on behalf of a user (reading their email, creating calendar events), each user must authenticate to each service separately. Managing those credentials in your agent adds complexity and security risk.

Scalekit solves this with per-user MCP servers. You define which tools and connections a server exposes, and Scalekit gives you a unique, pre-authenticated URL for each user. Hand that URL to your agent; it calls tools through MCP, Scalekit handles the auth. MCP servers only support Streamable HTTP transport.

> caution: Testing only: not for production
>
> This feature is in beta and intended for testing purposes only. Do not use it in production environments.

## How it works

Two objects are central to this model:

| Object | What it is | Created |
|---|---|---|
| **MCP config** | A reusable template that defines which connections and tools are exposed | Once, by your app |
| **MCP instance** | A per-user instantiation of a config, with its own URL | Once per user |

Your app creates a config once, then calls `ensure_instance` whenever a new user needs access. Scalekit generates a unique URL for that user. When the agent calls tools through that URL, Scalekit routes each call using the user's pre-authorized credentials.

> Image: Architecture diagram showing how Scalekit MCP works: app creates a config, Scalekit creates per-user instances with unique URLs, users authorize OAuth connections, and the agent connects via MCP URL

## Prerequisites

Before you start, make sure you have:

- **Scalekit API credentials**: go to **Dashboard → Settings** and copy your `environment_url`, `client_id` and `client_secret`
- **Gmail and Google Calendar connections configured in Scalekit:**
  - **Gmail**: Dashboard → **AgentKit** > **Connections** > **Create Connection** → select **Gmail** → set `Connection Name = MY_GMAIL` → Save
  - **Google Calendar**: Dashboard → **AgentKit** > **Connections** > **Create Connection** → select **Google Calendar** → set `Connection Name = MY_CALENDAR` → Save

1. ## Install the SDK and initialize the client

   Install the Scalekit Python SDK:

   ```sh showLineNumbers=false frame="none"
pip install scalekit-sdk-python python-dotenv>=1.0.1
```

   Initialize the client using your environment credentials:

   ```python showLineNumbers=false frame="none"
import os
import scalekit.client
from scalekit.actions.models.mcp_config import McpConfigConnectionToolMapping

scalekit_client = scalekit.client.ScalekitClient(
    client_id=os.getenv("SCALEKIT_CLIENT_ID"),
    client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"),
    env_url=os.getenv("SCALEKIT_ENV_URL"),
)
my_mcp = scalekit_client.actions.mcp
```

2. ## Create an MCP config

   An MCP config is a reusable template. It declares which connections and tools your server exposes. Create it once, not once per user.

   ```python showLineNumbers=false frame="none"
cfg_response = my_mcp.create_config(
    name="reminder-manager",
    description="Summarizes latest email and creates a reminder event",
    connection_tool_mappings=[
        McpConfigConnectionToolMapping(
            connection_name="MY_GMAIL",
            tools=["gmail_fetch_mails"],
        ),
        McpConfigConnectionToolMapping(
            connection_name="MY_CALENDAR",
            tools=["googlecalendar_create_event"],
        ),
    ],
)
config_name = cfg_response.config.name
```

3. ## Get a per-user MCP URL

   Call `ensure_instance` to get a unique MCP URL for a specific user. If an instance already exists for that user, Scalekit returns it; it's safe to call on every login.

   ```python showLineNumbers=false frame="none"
inst_response = my_mcp.ensure_instance(
    config_name=config_name,
    user_identifier="john-doe",
)
mcp_url = inst_response.instance.url
print("MCP URL:", mcp_url)
```

   Before the agent can use this URL, the user must authorize each connection. Retrieve the auth links and surface them to the user:

   ```python showLineNumbers=false frame="none"
auth_state_response = my_mcp.get_instance_auth_state(
    instance_id=inst_response.instance.id,
    include_auth_links=True,
)
for conn in getattr(auth_state_response, "connections", []):
    print("Connection:", conn.connection_name,
          "| Status:", conn.connected_account_status,
          "| Auth link:", conn.authentication_link)
```

   > note: Complete authentication
>
> Open the printed links in your browser and complete authentication for each connection.
>
> In production, surface these links to users via your app UI, email, or a Slack message. Poll `get_instance_auth_state` (without `include_auth_links`) to check when a user has completed authorization before passing the URL to your agent.

   At this point you have a per-user MCP URL. You can pass it to any spec-compliant MCP client: MCP Inspector, Claude Desktop, or an agent framework. The next step shows an example using LangChain.

4. ## Connect an agent (LangChain example)

   Install the LangChain dependencies:

   ```sh showLineNumbers=false frame="none"
pip install langgraph>=0.6.5 langchain-mcp-adapters>=0.1.9 openai>=1.53.0
```

   Set your OpenAI API key:

   ```sh showLineNumbers=false frame="none"
export OPENAI_API_KEY=your-openai-api-key
```

   Pass the MCP URL to a LangChain agent. The agent discovers available tools automatically; no additional auth configuration required:

   ```python showLineNumbers=false frame="none"
import asyncio
from langgraph.prebuilt import create_react_agent
from langchain_mcp_adapters.client import MultiServerMCPClient

async def run_agent(mcp_url: str):
    client = MultiServerMCPClient(
        {
            "reminder_demo": {
                "transport": "streamable_http",
                "url": mcp_url,
            },
        }
    )
    tools = await client.get_tools()
    agent = create_react_agent("openai:gpt-4.1", tools)
    response = await agent.ainvoke({
        "messages": "Get my latest email and create a calendar reminder in the next 15 minutes."
    })
    print(response)

asyncio.run(run_agent(mcp_url))
```

   > note: MCP client compatibility
>
> This MCP server works with MCP Inspector, Claude Desktop, and any spec-compliant MCP client. ChatGPT’s beta connector may not work correctly; it is still in beta and does not fully implement the MCP specification.

Full working code for all steps above is on [GitHub](https://github.com/scalekit-inc/python-connect-demos/tree/main/mcp).

## Next steps


---

## 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 |
