> **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/)

---

# Python SDK reference

Complete API reference for the Scalekit Python SDK: actions client, MCP server provisioning, framework adapters, tools client, and modifiers.
`scalekit_client.actions` is the primary interface for AgentKit. It handles connected account management, MCP server provisioning, tool execution, and framework integrations.

## Install and initialize

```bash
pip install scalekit-sdk-python
```

```python

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"),
)

actions = scalekit_client.actions
```

---

## Actions client

### Authentication

#### get_authorization_link

Generates a time-limited OAuth magic link to authorize a user's connection.

### Input schema

- `identifier` (`str`, optional): User identifier (e.g. email)
- `connection_name` (`str`, optional): Connector slug (e.g. gmail)
- `connected_account_id` (`str`, optional): Direct connected account ID (ca_...)
- `state` (`str`, optional): Opaque value passed through to the redirect URL
- `user_verify_url` (`str`, optional): App redirect URL for user verification

### Response schema

Type: `MagicLinkResponse`

- `link` (`str`): OAuth magic link URL. Redirect the user here to start the authorization flow.
- `expiry` (`datetime`): Link expiry timestamp

```python title="Example"
magic_link = actions.get_authorization_link(
    identifier="user@example.com",
    connection_name="gmail",
    user_verify_url="https://your-app.com/verify",
)
# Redirect the user to magic_link.link
```

#### verify_connected_account_user

Verifies the user after OAuth callback. Call this from your redirect URL handler.

### Input schema

- `auth_request_id` (`str`, required): Token from the redirect URL query params
- `identifier` (`str`, required): Current user identifier

### Response schema

Type: `VerifyConnectedAccountUserResponse`

- `post_user_verify_redirect_url` (`str`): URL to redirect the user to after successful verification

```python title="Example"
result = actions.verify_connected_account_user(
    auth_request_id=request.args["auth_request_id"],
    identifier="user@example.com",
)
# Redirect to result.post_user_verify_redirect_url
```

---

### Connected accounts

#### get_or_create_connected_account

Fetches an existing connected account or creates one if none exists. Use this as the default when setting up a user.

### Input schema

- `connection_name` (`str`, required): Connector slug
- `identifier` (`str`, required): User's identifier
- `authorization_details` (`dict`, optional): OAuth token or static auth details
- `organization_id` (`str`, optional): Organization tenant ID when your app scopes auth and accounts by org
- `user_id` (`str`, optional): Your application user ID when you map Scalekit accounts to internal users
- `api_config` (`dict`, optional): Connector-specific options (for example scopes or static auth fields)

### Response schema

Type: `CreateConnectedAccountResponse`

- `connected_account.id` (`str`): Account ID (ca_...)
- `connected_account.identifier` (`str`): User's identifier
- `connected_account.provider` (`str`): Provider slug
- `connected_account.status` (`str`): ACTIVE, INACTIVE, or PENDING
- `connected_account.authorization_type` (`str`): OAuth, API_KEY, etc.
- `connected_account.token_expires_at` (`datetime`): OAuth token expiry

```python title="Example"
account = actions.get_or_create_connected_account(
    connection_name="gmail",
    identifier="user@example.com",
)
print(account.connected_account.id)
```

#### get_connected_account

Fetches auth details for a connected account. Returns sensitive credentials. Protect access to this method.

Use this when you know the connected account already exists and you need its credential payload. For first-time setup or general application flows, prefer `get_or_create_connected_account` so new users do not hit a not-found error.

Requires `connected_account_id` **or** `connection_name` + `identifier`.

### Input schema

- `connection_name` (`str`, optional): Connector slug. Use with identifier when you do not pass connected_account_id.
- `identifier` (`str`, optional): End-user or workspace identifier. Use with connection_name.
- `connected_account_id` (`str`, optional): Connected account ID (ca_...) when resolving by ID instead of name + identifier

### Response schema

Type: `GetConnectedAccountAuthResponse`

- `connected_account.id` (`str`): Account ID (ca_...)
- `connected_account.identifier` (`str`): User's identifier
- `connected_account.provider` (`str`): Provider slug
- `connected_account.status` (`str`): ACTIVE, INACTIVE, or PENDING
- `connected_account.authorization_type` (`str`): OAuth, API_KEY, etc.
- `connected_account.authorization_details` (`dict`): Credential payload (access token, API key, etc.)
- `connected_account.token_expires_at` (`datetime`): OAuth token expiry
- `connected_account.last_used_at` (`datetime`): Last time this account was used
- `connected_account.updated_at` (`datetime`): Last update timestamp

#### list_connected_accounts

### Input schema

- `connection_name` (`str`, optional): Filter by connector
- `identifier` (`str`, optional): Filter by user identifier
- `provider` (`str`, optional): Filter by provider

### Response schema

Type: `ListConnectedAccountsResponse`

- `connected_accounts` (`list`): List of ConnectedAccountForList objects (excludes authorization_details and api_config)
- `total_count` (`int`): Total number of matching accounts
- `next_page_token` (`str`): Token for the next page, if any
- `previous_page_token` (`str`): Token for the previous page, if any

#### create_connected_account

Creates a connected account with explicit auth details.

### Input schema

- `connection_name` (`str`, required): Connector slug. Must match a connection configured in your environment.
- `identifier` (`str`, required): Stable ID for this end user or workspace (email, user_id, or custom string)
- `authorization_details` (`dict`, required): OAuth token payload, API key, or other credentials for this connector
- `organization_id` (`str`, optional): Organization tenant ID when your app scopes auth and accounts by org
- `user_id` (`str`, optional): Your application user ID when you map Scalekit accounts to internal users
- `api_config` (`dict`, optional): Connector-specific options (for example scopes or static auth fields)

Returns CreateConnectedAccountResponse. Same shape as `get_or_create_connected_account`.

#### update_connected_account

Requires `connected_account_id` **or** `connection_name` + `identifier`.

### Input schema

- `connection_name` (`str`, optional): Connector slug. Use with identifier when you do not pass connected_account_id.
- `identifier` (`str`, optional): End-user or workspace identifier. Use with connection_name.
- `connected_account_id` (`str`, optional): Connected account ID (ca_...) when updating by ID instead of name + identifier
- `authorization_details` (`dict`, optional): Replace or merge stored credentials (OAuth tokens, API keys, etc.)
- `organization_id` (`str`, optional): Organization tenant ID when your app scopes auth and accounts by org
- `user_id` (`str`, optional): Your application user ID when you map Scalekit accounts to internal users
- `api_config` (`dict`, optional): Connector-specific configuration to persist on the account

Returns UpdateConnectedAccountResponse.

#### delete_connected_account

Deletes a connected account and revokes its credentials. Requires `connected_account_id` **or** `connection_name` + `identifier`.

### Input schema

- `connection_name` (`str`, optional): Connector slug. Use with identifier when you do not pass connected_account_id.
- `identifier` (`str`, optional): End-user or workspace identifier. Use with connection_name.
- `connected_account_id` (`str`, optional): Connected account ID (ca_...) when deleting by ID instead of name + identifier

Returns DeleteConnectedAccountResponse.

---

### Tool execution

#### execute_tool

Executes a named tool via Scalekit. Pre- and post-modifiers run automatically if registered.

### Input schema

- `tool_name` (`str`, required): Tool name (e.g. gmail_fetch_emails)
- `tool_input` (`dict`, required): Parameters the tool expects
- `identifier` (`str`, optional): User's identifier
- `connected_account_id` (`str`, optional): Direct connected account ID

### Response schema

Type: `ExecuteToolResponse`

- `data` (`dict`): Tool structured output
- `execution_id` (`str`): Unique ID for this execution

```python title="Example"
result = actions.execute_tool(
    tool_name="gmail_fetch_emails",
    tool_input={"max_results": 5, "label": "UNREAD"},
    identifier="user@example.com",
)
emails = result.data
```

---

### Proxied API calls

#### request

Makes a REST API call on behalf of a connected account. Scalekit injects the user's OAuth token automatically.

### Input schema

- `connection_name` (`str`, required): Connector slug
- `identifier` (`str`, required): User's identifier
- `path` (`str`, required): API path (e.g. /gmail/v1/users/me/messages)
- `method` (`str`, optional): HTTP method. Default: GET
- `query_params` (`dict`, optional): URL query parameters appended to path
- `body` (`any`, optional): JSON-serializable body for POST, PUT, PATCH, or similar methods
- `form_data` (`dict`, optional): Multipart form fields when the upstream API expects form data instead of JSON
- `headers` (`dict`, optional): Extra HTTP headers merged with Scalekit-injected auth headers

Returns `requests.Response`. Use `.json()`, `.status_code`, and standard response attributes.

```python title="Example"
response = actions.request(
    connection_name="gmail",
    identifier="user@example.com",
    path="/gmail/v1/users/me/messages",
    query_params={"maxResults": 5, "q": "is:unread"},
)
messages = response.json()["messages"]
```

---

## MCP server provisioning

`actions.mcp` generates per-user MCP-compatible server URLs. Any MCP-compatible agent framework (LangChain, Google ADK, Anthropic, OpenAI, and others) can connect to these URLs directly.

**Two-step model:** Create a **config** once (defines which connectors and tools to expose), then call `ensure_instance` per user to get their personal MCP server URL.

### Configs

#### actions.mcp.create_config

### Input schema

- `name` (`str`, required): Config name
- `description` (`str`, optional): Human-readable summary of what this MCP config exposes
- `connection_tool_mappings` (`list`, optional): List of McpConfigConnectionToolMapping objects

### Response schema

Type: `CreateMcpConfigResponse`

- `config.id` (`str`): Config ID
- `config.name` (`str`): Config name
- `config.connection_tool_mappings` (`list`): Connector-to-tools mappings

```python title="Example"
from scalekit.actions.types import McpConfigConnectionToolMapping

config = actions.mcp.create_config(
    name="email-agent",
    connection_tool_mappings=[
        McpConfigConnectionToolMapping(
            connection_name="gmail",
            tools=["gmail_fetch_emails", "gmail_send_email"],
        )
    ],
)
```

#### actions.mcp.list_configs

### Input schema

- `page_size` (`int`, optional): Maximum configs per page (server default if omitted)
- `page_token` (`str`, optional): Opaque cursor from a previous list response
- `filter_name` (`str`, optional): Filter by exact name
- `filter_provider` (`str`, optional): Filter by provider slug
- `search` (`str`, optional): Free-text search on name

Returns ListMcpConfigsResponse.

#### actions.mcp.update_config

### Input schema

- `config_id` (`str`, required): MCP config ID from create_config or list_configs
- `description` (`str`, optional): New human-readable description for this config
- `connection_tool_mappings` (`list`, optional): Replaces existing mappings

Returns UpdateMcpConfigResponse.

#### actions.mcp.delete_config

### Input schema

- `config_id` (`str`, required): MCP config ID to delete

Returns DeleteMcpConfigResponse.

### Instances

#### actions.mcp.ensure_instance

Creates an MCP instance for this user if one doesn't exist, or returns the existing one. Call this on every session; it's idempotent.

The `instance.url` field is the MCP server URL to give to the user's agent or IDE.

### Input schema

- `config_name` (`str`, required): Name of the config to instantiate
- `user_identifier` (`str`, required): User identifier (e.g. email)
- `name` (`str`, optional): Display name for the instance

### Response schema

Type: `EnsureMcpInstanceResponse`

- `instance.url` (`str`): MCP server URL for agent or IDE
- `instance.id` (`str`): Instance ID
- `instance.name` (`str`): Display name
- `instance.user_identifier` (`str`): User identifier
- `instance.config` (`object`): The config this instance was created from
- `instance.last_used_at` (`datetime`): Last usage timestamp
- `instance.updated_at` (`datetime`): Last update timestamp

```python title="Example"
instance = actions.mcp.ensure_instance(
    config_name="email-agent",
    user_identifier="user@example.com",
)
mcp_url = instance.instance.url
# Give mcp_url to the user's agent or IDE
```

#### actions.mcp.get_instance_auth_state

Returns authorization status per connector. Use `include_auth_links=True` to generate fresh auth links for connections that need authorization or re-authorization.

### Input schema

- `instance_id` (`str`, required): Instance ID
- `include_auth_links` (`bool`, optional): Generate auth links for unauthorized connections

### Response schema

Type: `GetMcpInstanceAuthStateResponse`

- `connections` (`list`): List of McpInstanceConnectionAuthState, one per configured connector
- `connections[].connection_id` (`str`): Connection ID
- `connections[].connection_name` (`str`): Connector slug
- `connections[].provider` (`str`): Provider slug
- `connections[].connected_account_id` (`str`): Connected account ID, if authorized
- `connections[].connected_account_status` (`str`): ACTIVE, INACTIVE, or PENDING
- `connections[].authentication_link` (`str`): Auth link to send to the user when status is not ACTIVE

```python title="Example"
auth_state = actions.mcp.get_instance_auth_state(
    instance_id=instance.instance.id,
    include_auth_links=True,
)
for conn in auth_state.connections:
    if conn.connected_account_status != "ACTIVE":
        # Send conn.authentication_link to the user to authorize
        print(f"{conn.connection_name}: {conn.authentication_link}")
```

#### actions.mcp.get_instance

### Input schema

- `instance_id` (`str`, required): MCP instance ID from ensure_instance or list_instances

Returns GetMcpInstanceResponse.

#### actions.mcp.list_instances

### Input schema

- `page_size` (`int`, optional): Maximum instances per page (server default if omitted)
- `page_token` (`str`, optional): Opaque cursor from a previous list response
- `filter_user_identifier` (`str`, optional): Filter by user
- `filter_config_name` (`str`, optional): Filter by config name
- `filter_name` (`str`, optional): Filter by MCP instance display name
- `filter_id` (`str`, optional): Filter by MCP instance ID

Returns ListMcpInstancesResponse.

#### actions.mcp.update_instance

At least one of `name` or `config_name` is required.

### Input schema

- `instance_id` (`str`, required): MCP instance ID to update
- `name` (`str`, optional): New display name for this instance
- `config_name` (`str`, optional): Switch the instance to a different config by name

Returns UpdateMcpInstanceResponse.

#### actions.mcp.delete_instance

### Input schema

- `instance_id` (`str`, required): MCP instance ID to delete

Returns DeleteMcpInstanceResponse.

---

## Framework adapters

Pre-built integrations for LangChain and Google ADK. Use these when your agent runs in one of these frameworks and you prefer native tool objects over an MCP URL.

> note: MCP is the recommended path
>
> `actions.mcp.ensure_instance` generates a URL compatible with any MCP-supporting framework. Use framework adapters only when native tool objects are required.

### LangChain

```bash
pip install langchain
```

#### actions.langchain.get_tools

### Input schema

- `identifier` (`str`, required): User connected account identifier
- `providers` (`list`, optional): Filter by provider (e.g. ["google"])
- `tool_names` (`list`, optional): Filter by tool name
- `connection_names` (`list`, optional): Filter by connection name
- `page_size` (`int`, optional): Maximum tools per page. Use 100 for discovery so connectors with more than the default page are not truncated.
- `page_token` (`str`, optional): Opaque cursor from a previous list response

### Response schema

Type: `List[StructuredTool]`

- `[].name` (`str`): Tool name
- `[].description` (`str`): Tool description
- `[].args_schema` (`object`): Pydantic schema for the tool inputs

```python title="Example"
from langchain.agents import create_react_agent

tools = actions.langchain.get_tools(
    identifier="user@example.com",
    page_size=100,  # avoid missing tools when a connector has more than the default page
)
agent = create_react_agent(llm=llm, tools=tools, prompt=prompt)
```

### Google ADK

```bash
pip install google-adk
```

#### actions.google.get_tools

Same parameters as `actions.langchain.get_tools`.

Returns `List[ScalekitGoogleAdkTool]`. Pass it directly to a Google ADK agent.

```python title="Example"
tools = actions.google.get_tools(
    identifier="user@example.com",
    page_size=100,  # avoid missing tools when a connector has more than the default page
)
```

---

## Tools client

`scalekit_client.actions.tools` gives access to raw tool schemas. Use this when building a custom adapter or passing schemas directly to an LLM API (e.g. Anthropic, OpenAI).

#### actions.tools.list_tools

### Input schema

- `filter` (`Filter`, optional): Filter by provider, identifier, or tool name
- `page_size` (`int`, optional): Maximum tools per page. Use 100 for discovery so connectors with more than the default page are not truncated.
- `page_token` (`str`, optional): Opaque cursor from a previous list response

### Response schema

Type: `ListToolsResponse`

- `tools` (`list`): List of tool schemas (name, description, input schema)
- `next_page_token` (`str`): Token for the next page, if any

#### actions.tools.list_scoped_tools

Lists tools scoped to a specific user. Use this method for tool discovery because it returns pagination metadata such as `next_page_token` and `total_size`; framework `get_tools()` helpers return framework-ready tool objects and do not expose that metadata.

### Input schema

- `identifier` (`str`, required): User connected account identifier
- `filter` (`ScopedToolFilter`, optional): Filter by providers, tool names, or connection names
- `page_size` (`int`, optional): Maximum tools per page. Use 100 for discovery so connectors with more than the default page are not truncated.
- `page_token` (`str`, optional): Opaque cursor from a previous list response

### Response schema

Type: `ListScopedToolsResponse`

- `tools` (`list`): List of tool schemas (name, description, input_schema)
- `tools[].name` (`str`): Tool name
- `tools[].description` (`str`): Tool description
- `tools[].input_schema` (`object`): JSON Schema for tool inputs. Pass directly to LLM API.
- `next_page_token` (`str`): Token for the next page, if any

```python title="Example"
tools_response = scalekit_client.actions.tools.list_scoped_tools(
    identifier="user@example.com",
    page_size=100,
)
# Pass tools_response.tools to your LLM's tool call API
```

#### actions.tools.execute_tool

Low-level tool execution. Bypasses modifiers. Prefer `actions.execute_tool` in most cases.

### Input schema

- `tool_name` (`str`, required): Registered tool name to execute
- `identifier` (`str`, required): End-user or workspace identifier used to resolve the connected account
- `params` (`dict`, optional): Tool arguments matching the tool input schema
- `connected_account_id` (`str`, optional): Connected account ID (ca_...) when you already know it

Returns ExecuteToolResponse. Same shape as `actions.execute_tool`.

---

## Modifiers

Modifiers intercept tool calls to transform inputs or outputs, useful for validation, enrichment, or logging.

```python
@actions.pre_modifier(tool_names=["gmail_fetch_emails"])
def add_default_label(tool_input):
    tool_input.setdefault("label", "UNREAD")
    return tool_input

@actions.post_modifier(tool_names=["gmail_fetch_emails"])
def filter_attachments(tool_output):
    tool_output["emails"] = [e for e in tool_output["emails"] if not e.get("has_attachment")]
    return tool_output
```

| Decorator | Receives | Returns |
|---|---|---|
| `@actions.pre_modifier(tool_names)` | `dict` | Modified `dict` |
| `@actions.post_modifier(tool_names)` | `dict` | Modified `dict` |

`tool_names` accepts a string or a list of strings. Multiple modifiers for the same tool chain in registration order.

---

## Error handling

```python
from scalekit.common.exceptions import ScalekitNotFoundException, ScalekitServerException

try:
    account = actions.get_connected_account(
        connection_name="gmail",
        identifier="user@example.com",
    )
except ScalekitNotFoundException:
    # Account does not exist: create it or redirect to auth
    pass
except ScalekitServerException as e:
    print(e.error_code, e.http_status)
```

| Exception | When raised |
|---|---|
| `ScalekitNotFoundException` | Resource not found |
| `ScalekitUnauthorizedException` | Invalid credentials |
| `ScalekitForbiddenException` | Insufficient permissions |
| `ScalekitServerException` | Base class for all server errors |


---

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