AgentKit Frameworks: Framework-specific AgentKit examples — LangChain, Vercel AI, Anthropic, OpenAI, Google ADK, Mastra, Claude Managed Agents
---
# DOCUMENT BOUNDARY
---
# Overview
> Learn how AgentKit works: tool calling with pre-built connectors and authentication for AI agents acting on behalf of users.
AgentKit gives your AI agents authenticated access to third-party apps: sending emails, reading calendars, creating tickets, querying databases, and more. Your agent calls a tool; Scalekit handles the OAuth flow, token storage, and API call. ## Authentication [Section titled “Authentication”](#authentication) **Connections** are configurations you create once in the Scalekit Dashboard. A connection holds the credentials Scalekit needs to authenticate with a connector (OAuth app credentials, API keys, or service account details). One connection serves all your users. **Connected accounts** are per-user instances of a connection. When a user authorizes, Scalekit creates a connected account that stores their tokens and tracks their auth state. Your agent uses a connected account to act on that specific user’s behalf. Scalekit supports OAuth 2.0, API keys, RSA key pairs, and service accounts across all connectors. ## Tool calling [Section titled “Tool calling”](#tool-calling) **Connectors** are the pre-built integrations your agent can use: GitHub, Gmail, Slack, Salesforce, Snowflake, and many others. Each connector exposes a library of tools ready for your agent to call. **Tools** are connector-specific actions: `github_repo_star`, `salesforce_create_record`, `slack_send_message`. Scalekit provides the tool schemas and handles the authenticated API call. Your agent passes inputs; Scalekit injects the user’s credentials and returns structured output. ## How they fit together [Section titled “How they fit together”](#how-they-fit-together) You configure connections once. Your users authenticate to create connected accounts. Your agent calls tools; Scalekit handles the rest. ## Works with your framework [Section titled “Works with your framework”](#works-with-your-framework) AgentKit is framework-agnostic. Tool schemas work with any LLM API. Native adapters are available for [LangChain](/agentkit/examples/langchain/), [Google ADK](/agentkit/examples/google-adk/), and [Virtual MCP Servers](/agentkit/mcp/overview/). ## Get started [Section titled “Get started”](#get-started) [Quickstart](/agentkit/quickstart)Build a working agent with authenticated tool calls in minutes. [Configure a connection](/agentkit/connections)Set up your first connection in the Scalekit Dashboard. [Connectors](/agentkit/connectors/)Browse the pre-built connectors and their tool libraries. [Examples](/agentkit/examples/)Full working examples for LangChain, Google ADK, Anthropic, OpenAI, and more.
---
# DOCUMENT BOUNDARY
---
# AgentKit: Connect my agent to apps
> Build a working agent that makes authenticated tool calls on behalf of users, using GitHub as the example connector.
 By the end of this guide, you’ll have a working agent that stars a repository on GitHub on behalf of a user (authenticated with their real account). Scalekit manages the OAuth flow, token storage, and API proxy so you focus on agent logic. ## Before you start [Section titled “Before you start”](#before-you-start) Complete these steps in the Scalekit dashboard before writing any code: 1. **Create a Scalekit account** at [app.scalekit.com](https://app.scalekit.com). 2. **Confirm the GitHub connection** at Dashboard → **AgentKit** > **Connections**. New Scalekit environments include a default GitHub connection named `github-connect`, pre-configured with Scalekit’s managed credentials and the `user:email`, `repo`, and `public_repo` scopes — no connector setup is needed for this quickstart. Copy the exact **Connection name** from that connection and use that value in your code. It must match the dashboard exactly; in older environments or renamed connections the value can differ from `github-connect`. To connect to other services, create a connection for each app under **AgentKit** > **Connections** > **Create Connection**. 3. **Copy your API credentials** at Dashboard → **Developers → Settings → API Credentials**. Save these values as environment variables: * `SCALEKIT_CLIENT_ID` * `SCALEKIT_CLIENT_SECRET` * `SCALEKIT_ENV_URL` * `GITHUB_CONNECTION_NAME` (copy the exact Connection name from **AgentKit** > **Connections** — `github-connect` in new environments) ## Build your agent [Section titled “Build your agent”](#build-your-agent) * Using a coding agent Install the authstack plugin for your coding agent with `npx @scalekit-inc/cli setup` (or install globally with `npm install -g @scalekit-inc/cli` then `scalekit setup`), complete the browser authorization when prompted, then paste the implementation prompt. The agent scaffolds connected account setup, the OAuth flow, and tool execution. Terminal
```bash
npx @scalekit-inc/cli setup
```
The wizard sets up the right plugins and skills for your editors. Complete any browser authorization for the Scalekit MCP server when prompted. Then use the prompt below (or describe your goal in natural language). Implementation prompt
```md
Configure Scalekit agent authentication for GitHub. Provide code to create a connected account, generate an authorization link, and, once the user authorizes, star Scalekit's SDK repo (scalekit-inc/scalekit-sdk-python) using Scalekit's tool API.
```
Review generated code before deploying Verify that token validation logic, error handling, and environment variable references match your application’s requirements. * Step by step ### 1. Set up your environment [Section titled “1. Set up your environment”](#1-set-up-your-environment) Install the Scalekit SDK and initialize the client with your API credentials: * Python
```sh
pip install scalekit-sdk-python python-dotenv
```
* Node.js
```sh
npm install @scalekit-sdk/node
```
- Python
```python
import os
from scalekit import ScalekitClient
from dotenv import load_dotenv
load_dotenv()
# Constructor: env_url, client_id, client_secret
scalekit_client = ScalekitClient(
os.environ["SCALEKIT_ENV_URL"],
os.environ["SCALEKIT_CLIENT_ID"],
os.environ["SCALEKIT_CLIENT_SECRET"],
)
actions = scalekit_client.actions
connection_name = os.getenv("GITHUB_CONNECTION_NAME") # must match the Connection name in the dashboard exactly
```
- Node.js
```typescript
import { ScalekitClient } from '@scalekit-sdk/node';
import { ConnectorStatus } from '@scalekit-sdk/node/lib/pkg/grpc/scalekit/v1/connected_accounts/connected_accounts_pb';
import 'dotenv/config';
// Constructor: envUrl, clientId, clientSecret
const scalekit = new ScalekitClient(
process.env.SCALEKIT_ENV_URL!,
process.env.SCALEKIT_CLIENT_ID!,
process.env.SCALEKIT_CLIENT_SECRET!
);
const actions = scalekit.actions;
const connectionName = process.env.GITHUB_CONNECTION_NAME!; // must match the Connection name in the dashboard exactly
```
### 2. Create a connected account [Section titled “2. Create a connected account”](#2-create-a-connected-account) Scalekit tracks each user’s third-party connection as a connected account. This is the record that holds their OAuth tokens. Creating it tells Scalekit to start managing the user’s GitHub access on your behalf. This step fails if the GitHub connection does not exist in **AgentKit** > **Connections**, or if `connection_name` / `connectionName` does not match the dashboard exactly. * Python
```python
# Create or retrieve the user's connected GitHub account
response = actions.get_or_create_connected_account(
connection_name=connection_name,
identifier="user_123" # Replace with your system's unique user ID
)
connected_account = response.connected_account
print(f'Connected account created: {connected_account.id}')
```
* Node.js
```typescript
// Create or retrieve the user's connected GitHub account
const response = await actions.getOrCreateConnectedAccount({
connectionName,
identifier: 'user_123', // Replace with your system's unique user ID
});
let connectedAccount = response.connectedAccount;
console.log('Connected account created:', connectedAccount?.id);
```
### 3. Authenticate the user [Section titled “3. Authenticate the user”](#3-authenticate-the-user) Your agent can’t act on behalf of a user until they authorize access. Generate an authorization link, send it to the user, and Scalekit handles the rest: token exchange, storage, and automatic refresh. Once they complete the flow, the connected account status becomes `ACTIVE`. * Python
```python
# Generate authorization link if user hasn't authorized or token is expired.
# Do not call tools until status is ACTIVE — wait for the user to finish OAuth first.
if connected_account.status != "ACTIVE":
print(f"GitHub is not connected: {connected_account.status}")
link_response = actions.get_authorization_link(
connection_name=connection_name,
identifier="user_123",
)
print("🔗 click on the link to authorize GitHub", link_response.link)
input("⎆ Press Enter after authorizing GitHub...")
# Re-fetch so connected_account reflects ACTIVE status and a valid id
response = actions.get_or_create_connected_account(
connection_name=connection_name,
identifier="user_123",
)
connected_account = response.connected_account
# In production, redirect the user to this URL and resume after the OAuth callback
if connected_account.status != "ACTIVE":
raise RuntimeError(
"GitHub is still not ACTIVE. Complete authorization and try again."
)
```
* Node.js
```typescript
// Generate authorization link if user hasn't authorized or token is expired.
// Do not call tools until status is ACTIVE — wait for the user to finish OAuth first.
if (connectedAccount?.status !== ConnectorStatus.ACTIVE) {
console.log('GitHub is not connected:', connectedAccount?.status);
const linkResponse = await actions.getAuthorizationLink({
connectionName,
identifier: 'user_123',
});
console.log('🔗 click on the link to authorize GitHub', linkResponse.link);
console.log('Press Enter after authorizing GitHub...');
await new Promise((resolve) => {
process.stdin.resume();
process.stdin.once('data', () => {
process.stdin.pause();
resolve();
});
});
// Re-fetch so connectedAccount reflects ACTIVE status and a valid id
const refreshed = await actions.getOrCreateConnectedAccount({
connectionName,
identifier: 'user_123',
});
connectedAccount = refreshed.connectedAccount;
// In production, redirect the user to this URL and resume after the OAuth callback
}
if (connectedAccount?.status !== ConnectorStatus.ACTIVE) {
throw new Error('GitHub is still not ACTIVE. Complete authorization and try again.');
}
```
Open the link in a browser and authorize the GitHub connection. Once complete, the connected account status updates to `ACTIVE` and your agent can act on the user’s behalf. In CLI samples, wait for that step (for example with `input()` or stdin) before calling tools — otherwise the first run fails because the account is still inactive. ### 4. Star a repo via tool call [Section titled “4. Star a repo via tool call”](#4-star-a-repo-via-tool-call) Pass the tool name and your inputs to Scalekit. It handles the request to GitHub and returns a structured response your agent can reason over directly: no endpoint URLs, auth headers, or response parsing required. The example stars the Scalekit SDK repo for your language. * Python
```python
# Prefer connected_account_id after authorize. If you use identifier instead,
# also pass connection_name — the pair is required for account resolution.
tool_response = actions.execute_tool(
tool_name="github_repo_star",
connected_account_id=connected_account.id,
tool_input={
"owner": "scalekit-inc",
"repo": "scalekit-sdk-python",
},
)
# Tool output lives under data
print(tool_response.data)
```
* Node.js
```typescript
const toolResponse = await actions.executeTool({
toolName: 'github_repo_star',
connectedAccountId: connectedAccount?.id,
toolInput: {
owner: 'scalekit-inc',
repo: 'scalekit-sdk-node',
},
});
// Tool output lives under data
console.log('Starred the repo:', toolResponse.data);
```
* Python
```sh
pip install scalekit-sdk-python python-dotenv
```
* Node.js
```sh
npm install @scalekit-sdk/node
```
* Python
```python
import os
from scalekit import ScalekitClient
from dotenv import load_dotenv
load_dotenv()
# Constructor: env_url, client_id, client_secret
scalekit_client = ScalekitClient(
os.environ["SCALEKIT_ENV_URL"],
os.environ["SCALEKIT_CLIENT_ID"],
os.environ["SCALEKIT_CLIENT_SECRET"],
)
actions = scalekit_client.actions
connection_name = os.getenv("GITHUB_CONNECTION_NAME") # must match the Connection name in the dashboard exactly
```
* Node.js
```typescript
import { ScalekitClient } from '@scalekit-sdk/node';
import { ConnectorStatus } from '@scalekit-sdk/node/lib/pkg/grpc/scalekit/v1/connected_accounts/connected_accounts_pb';
import 'dotenv/config';
// Constructor: envUrl, clientId, clientSecret
const scalekit = new ScalekitClient(
process.env.SCALEKIT_ENV_URL!,
process.env.SCALEKIT_CLIENT_ID!,
process.env.SCALEKIT_CLIENT_SECRET!
);
const actions = scalekit.actions;
const connectionName = process.env.GITHUB_CONNECTION_NAME!; // must match the Connection name in the dashboard exactly
```
* Python
```python
# Create or retrieve the user's connected GitHub account
response = actions.get_or_create_connected_account(
connection_name=connection_name,
identifier="user_123" # Replace with your system's unique user ID
)
connected_account = response.connected_account
print(f'Connected account created: {connected_account.id}')
```
* Node.js
```typescript
// Create or retrieve the user's connected GitHub account
const response = await actions.getOrCreateConnectedAccount({
connectionName,
identifier: 'user_123', // Replace with your system's unique user ID
});
let connectedAccount = response.connectedAccount;
console.log('Connected account created:', connectedAccount?.id);
```
* Python
```python
# Generate authorization link if user hasn't authorized or token is expired.
# Do not call tools until status is ACTIVE — wait for the user to finish OAuth first.
if connected_account.status != "ACTIVE":
print(f"GitHub is not connected: {connected_account.status}")
link_response = actions.get_authorization_link(
connection_name=connection_name,
identifier="user_123",
)
print("🔗 click on the link to authorize GitHub", link_response.link)
input("⎆ Press Enter after authorizing GitHub...")
# Re-fetch so connected_account reflects ACTIVE status and a valid id
response = actions.get_or_create_connected_account(
connection_name=connection_name,
identifier="user_123",
)
connected_account = response.connected_account
# In production, redirect the user to this URL and resume after the OAuth callback
if connected_account.status != "ACTIVE":
raise RuntimeError(
"GitHub is still not ACTIVE. Complete authorization and try again."
)
```
* Node.js
```typescript
// Generate authorization link if user hasn't authorized or token is expired.
// Do not call tools until status is ACTIVE — wait for the user to finish OAuth first.
if (connectedAccount?.status !== ConnectorStatus.ACTIVE) {
console.log('GitHub is not connected:', connectedAccount?.status);
const linkResponse = await actions.getAuthorizationLink({
connectionName,
identifier: 'user_123',
});
console.log('🔗 click on the link to authorize GitHub', linkResponse.link);
console.log('Press Enter after authorizing GitHub...');
await new Promise((resolve) => {
process.stdin.resume();
process.stdin.once('data', () => {
process.stdin.pause();
resolve();
});
});
// Re-fetch so connectedAccount reflects ACTIVE status and a valid id
const refreshed = await actions.getOrCreateConnectedAccount({
connectionName,
identifier: 'user_123',
});
connectedAccount = refreshed.connectedAccount;
// In production, redirect the user to this URL and resume after the OAuth callback
}
if (connectedAccount?.status !== ConnectorStatus.ACTIVE) {
throw new Error('GitHub is still not ACTIVE. Complete authorization and try again.');
}
```
* Python
```python
# Prefer connected_account_id after authorize. If you use identifier instead,
# also pass connection_name — the pair is required for account resolution.
tool_response = actions.execute_tool(
tool_name="github_repo_star",
connected_account_id=connected_account.id,
tool_input={
"owner": "scalekit-inc",
"repo": "scalekit-sdk-python",
},
)
# Tool output lives under data
print(tool_response.data)
```
* Node.js
```typescript
const toolResponse = await actions.executeTool({
toolName: 'github_repo_star',
connectedAccountId: connectedAccount?.id,
toolInput: {
owner: 'scalekit-inc',
repo: 'scalekit-sdk-node',
},
});
// Tool output lives under data
console.log('Starred the repo:', toolResponse.data);
```
## Verify it works [Section titled “Verify it works”](#verify-it-works) Run your agent and confirm: * The connected account status is `ACTIVE` after the user completes the GitHub OAuth flow. * The tool call returns success and the star appears on the Scalekit SDK repo on GitHub. Starring is idempotent — GitHub returns success even if the repo is already starred — so re-runs are safe. To confirm programmatically, call `github_starred_repos_list` and check the repo is in the list. If the connected account stays in a `non-ACTIVE` state, the user has not completed the OAuth flow. Regenerate the authorization link and try again. ## Next steps [Section titled “Next steps”](#next-steps) * [Secure user verification](/agentkit/user-verification/): Confirm the OAuth identity matches your logged-in user before activating a connected account. Required for production. * [Connected accounts](/agentkit/connected-accounts/): Manage user connections across multiple providers. * [Tool calling](/agentkit/tools/scalekit-optimized-tools/): Use Scalekit’s optimized tools to call APIs without managing endpoints yourself.
---
# DOCUMENT BOUNDARY
---
# AgentKit code samples
> Full working examples showing how to integrate AgentKit with popular AI frameworks and agent platforms.
Each example builds a working agent that reads a user’s Gmail inbox using Scalekit-authenticated tools. ## No agent loop to build [Section titled “No agent loop to build”](#no-agent-loop-to-build) These platforms manage the agent harness for you. Pass a Scalekit MCP URL, describe the task, and the platform handles tool discovery, execution, and session state. [Claude Managed Agents](/agentkit/examples/claude-managed-agents/)Anthropic runs the agent loop. Pass a Scalekit MCP URL, describe a task, and Claude handles tool discovery, execution, and retries. [OpenClaw](/agentkit/openclaw/)Conversational agent platform. No code required to connect 50+ services including Gmail, Slack, Notion, and LinkedIn. ## Build your own agent loop [Section titled “Build your own agent loop”](#build-your-own-agent-loop) These integrations give you full control. Fetch Scalekit tool schemas, wire them into your framework, and run the tool-use loop yourself. | Framework | Language | Integration | Notes | | ---------------------------------------------- | --------------- | -------------------- | -------------------------------------------------------------------------------- | | [LangChain](/agentkit/examples/langchain/) | Python | SDK, native adapter | Scalekit returns native LangChain tool objects. No schema reshaping needed. | | [Google ADK](/agentkit/examples/google-adk/) | Python | SDK, native adapter | Scalekit returns native ADK tool objects. No schema reshaping needed. | | [Anthropic](/agentkit/examples/anthropic/) | Python, Node.js | SDK, direct | Tool schemas use `input_schema`, which matches Anthropic’s format exactly. | | [OpenAI](/agentkit/examples/openai/) | Python, Node.js | SDK, direct | Rename `input_schema` to `parameters` to match OpenAI’s function format. | | [Vercel AI SDK](/agentkit/examples/vercel-ai/) | Node.js | SDK, `tool()` helper | Wrap tools with `tool()` and `jsonSchema()`. No manual schema conversion needed. | | [CrewAI](/agentkit/examples/crewai/) | Python | MCP | `MCPServerAdapter` connects to a Scalekit MCP URL. Tool discovery is automatic. | | [Mastra](/agentkit/examples/mastra/) | Node.js | MCP | Native MCP support via `@mastra/mcp`. Tool discovery is automatic. | ## Working examples on GitHub [Section titled “Working examples on GitHub”](#working-examples-on-github) ### [Connect LangChain agents to Gmail](https://github.com/scalekit-inc/sample-langchain-agent) [Securely connect a LangChain agent to Gmail using Scalekit for authentication. Python example for tool authorization.](https://github.com/scalekit-inc/sample-langchain-agent) ### [Connect Google GenAI agents to Gmail](https://github.com/scalekit-inc/google-adk-agent-example) [Build a Google ADK agent that securely accesses Gmail tools. Python example demonstrating Scalekit auth integration.](https://github.com/scalekit-inc/google-adk-agent-example) ### [Connect agents to Slack tools](https://github.com/scalekit-inc/python-connect-demos/tree/main/direct) [Authorize Python agents to use Slack tools with Scalekit. Direct integration example for secure tool access.](https://github.com/scalekit-inc/python-connect-demos/tree/main/direct) ### [Connect CrewAI agents to Gmail](https://github.com/scalekit-developers/crewai-scalekit-example) [Multi-agent email triage crew using CrewAI with Scalekit-authenticated Gmail tools via MCP.](https://github.com/scalekit-developers/crewai-scalekit-example) ### [Meeting prep agent](https://github.com/scalekit-inc/meeting-prep-agent-example) [Pulls context from Google Cal, Gmail, HubSpot, and Slack before each external meeting. Delivers a structured brief in under 60 seconds using delegated user identity.](https://github.com/scalekit-inc/meeting-prep-agent-example) ### [Browse all agent auth examples](https://github.com/scalekit-developers/agent-auth-examples) [A curated collection of working examples showing how to build agents that authenticate and access tools using Scalekit.](https://github.com/scalekit-developers/agent-auth-examples)
---
# DOCUMENT BOUNDARY
---
# Anthropic
> Build an Anthropic agent with Scalekit-authenticated tools. Scalekit returns tool schemas in Anthropic's native format; no conversion needed.
Build an agent using Anthropic’s Claude that reads a user’s Gmail inbox. Scalekit returns tool schemas with `input_schema`, the exact format Anthropic’s tool use API expects. ## Install [Section titled “Install”](#install) * Python
```sh
1
pip install scalekit-sdk-python anthropic
```
* Node.js
```sh
1
npm install @scalekit-sdk/node @anthropic-ai/sdk
```
## Initialize [Section titled “Initialize”](#initialize) * Python
```python
1
import os
2
import scalekit.client
3
import anthropic
4
from google.protobuf.json_format import MessageToDict
5
6
scalekit_client = scalekit.client.ScalekitClient(
7
client_id=os.getenv("SCALEKIT_CLIENT_ID"),
8
client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"),
9
env_url=os.getenv("SCALEKIT_ENV_URL"),
10
)
11
actions = scalekit_client.actions
12
client = anthropic.Anthropic()
```
* Node.js
```typescript
1
import { ScalekitClient } from '@scalekit-sdk/node';
2
import { ConnectorStatus } from '@scalekit-sdk/node/lib/pkg/grpc/scalekit/v1/connected_accounts/connected_accounts_pb';
3
import Anthropic from '@anthropic-ai/sdk';
4
5
const scalekit = new ScalekitClient(
6
process.env.SCALEKIT_ENV_URL!,
7
process.env.SCALEKIT_CLIENT_ID!,
8
process.env.SCALEKIT_CLIENT_SECRET!,
9
);
10
const anthropic = new Anthropic();
```
## Connect the user to Gmail [Section titled “Connect the user to Gmail”](#connect-the-user-to-gmail) * Python
```python
1
response = actions.get_or_create_connected_account(
2
connection_name="gmail",
3
identifier="user_123",
4
)
5
if response.connected_account.status != "ACTIVE":
6
link = actions.get_authorization_link(connection_name="gmail", identifier="user_123")
7
print("Authorize Gmail:", link.link)
8
input("Press Enter after authorizing...")
```
* Node.js
```typescript
1
const { connectedAccount } = await scalekit.actions.getOrCreateConnectedAccount({
2
connectionName: 'gmail',
3
identifier: 'user_123',
4
});
5
if (connectedAccount?.status !== ConnectorStatus.ACTIVE) {
6
const { link } = await scalekit.actions.getAuthorizationLink({ connectionName: 'gmail', identifier: 'user_123' });
7
console.log('Authorize Gmail:', link);
8
}
```
See [Authorize a user](/agentkit/tools/authorize/) for production auth handling. ## Run the agent [Section titled “Run the agent”](#run-the-agent) Fetch tools scoped to this user, then run the full Claude tool-use loop: * Python
```python
1
# Fetch tools scoped to this user
2
scoped_response, _ = actions.tools.list_scoped_tools(
3
identifier="user_123",
4
filter={"connection_names": ["gmail"]},
5
page_size=100, # fetch beyond the default page so no connector tools are missed
6
)
7
llm_tools = [
8
{
9
"name": MessageToDict(t.tool).get("definition", {}).get("name"),
10
"description": MessageToDict(t.tool).get("definition", {}).get("description", ""),
11
"input_schema": MessageToDict(t.tool).get("definition", {}).get("input_schema", {}),
12
}
13
for t in scoped_response.tools
14
]
15
16
# Run the agent loop
17
messages = [{"role": "user", "content": "Fetch my last 5 unread emails and summarize them"}]
18
19
while True:
20
response = client.messages.create(
21
model="claude-sonnet-4-6",
22
max_tokens=1024,
23
tools=llm_tools,
24
messages=messages,
25
)
26
if response.stop_reason == "end_turn":
27
print(response.content[0].text)
28
break
29
30
tool_results = []
31
for block in response.content:
32
if block.type == "tool_use":
33
result = actions.execute_tool(
34
tool_name=block.name,
35
identifier="user_123",
36
tool_input=block.input,
37
)
38
tool_results.append({
39
"type": "tool_result",
40
"tool_use_id": block.id,
41
"content": str(result.data),
42
})
43
44
messages.append({"role": "assistant", "content": response.content})
45
messages.append({"role": "user", "content": tool_results})
```
* Node.js
```typescript
1
// Fetch tools scoped to this user
2
const { tools } = await scalekit.tools.listScopedTools('user_123', {
3
filter: { connectionNames: ['gmail'] },
4
pageSize: 100, // fetch beyond the default page so no connector tools are missed
5
});
6
const llmTools = tools.map(t => ({
7
name: t.tool.definition.name,
8
description: t.tool.definition.description,
9
input_schema: t.tool.definition.input_schema,
10
}));
11
12
// Run the agent loop
13
const messages: Anthropic.MessageParam[] = [
14
{ role: 'user', content: 'Fetch my last 5 unread emails and summarize them' },
15
];
16
17
while (true) {
18
const response = await anthropic.messages.create({
19
model: 'claude-sonnet-4-6',
20
max_tokens: 1024,
21
tools: llmTools,
22
messages,
23
});
24
25
if (response.stop_reason === 'end_turn') {
26
const text = response.content.find(b => b.type === 'text');
27
if (text?.type === 'text') console.log(text.text);
28
break;
29
}
30
31
const toolResults: Anthropic.ToolResultBlockParam[] = [];
32
for (const block of response.content) {
33
if (block.type === 'tool_use') {
34
const result = await scalekit.actions.executeTool({
35
toolName: block.name,
36
identifier: 'user_123',
37
toolInput: block.input as Record,
38
});
39
toolResults.push({ type: 'tool_result', tool_use_id: block.id, content: JSON.stringify(result.data) });
40
}
41
}
42
messages.push({ role: 'assistant', content: response.content });
43
messages.push({ role: 'user', content: toolResults });
44
}
```
## Use MCP instead [Section titled “Use MCP instead”](#use-mcp-instead) Claude Desktop and other Anthropic-compatible MCP hosts connect directly to Scalekit MCP URLs. Add the URL to your MCP host config:
```json
1
{
2
"mcpServers": {
3
"scalekit": {
4
"transport": "streamable-http",
5
"url": "your-scalekit-mcp-url"
6
}
7
}
8
}
```
For programmatic use, connect via any MCP client library and pass tools to `anthropic.messages.create`. See [Virtual MCP Servers](/agentkit/mcp/overview/) for setup details and the URL.
---
# DOCUMENT BOUNDARY
---
# 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](/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 [Section titled “Prerequisites”](#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 [Section titled “How it works”](#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 [Section titled “Install”](#install)
```sh
1
pip install anthropic scalekit-sdk-python python-dotenv
```
## Build [Section titled “Build”](#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. builder.py
```python
1
import os
2
import anthropic
3
from scalekit import ScalekitClient
4
from scalekit.actions.models.mcp_config import McpConfigConnectionToolMapping
5
from dotenv import load_dotenv
6
7
load_dotenv()
8
9
anthropic_client = anthropic.Anthropic()
10
scalekit_client = ScalekitClient(
11
env_url=os.environ["SCALEKIT_ENV_URL"],
12
client_id=os.environ["SCALEKIT_CLIENT_ID"],
13
client_secret=os.environ["SCALEKIT_CLIENT_SECRET"],
14
)
15
16
GMAIL_TOOLS = ["gmail_fetch_mails"]
17
GCAL_TOOLS = [
18
"googlecalendar_list_calendars",
19
"googlecalendar_list_events",
20
"googlecalendar_get_event_by_id",
21
"googlecalendar_create_event",
22
"googlecalendar_update_event",
23
]
24
25
vmcp_response = scalekit_client.actions.mcp.create_config(
26
name="email-calendar-demo",
27
connection_tool_mappings=[
28
McpConfigConnectionToolMapping(
29
connection_name="gmail",
30
tools=GMAIL_TOOLS,
31
),
32
McpConfigConnectionToolMapping(
33
connection_name="googlecalendar",
34
tools=GCAL_TOOLS,
35
),
36
],
37
)
38
39
mcp_id = vmcp_response.config.id
40
mcp_server_url = vmcp_response.config.mcp_server_url
41
42
agent = anthropic_client.beta.agents.create(
43
name="Email Meeting Manager",
44
model="claude-haiku-4-5-20251001",
45
system=(
46
"You are an email and calendar assistant. When invoked, you will:\n"
47
"1. Fetch the single most recent unread email from Gmail.\n"
48
"2. Summarize it in 2-3 sentences.\n"
49
"3. Create a Google Calendar event titled 'Action Required: ' "
50
"with your summary as the description."
51
),
52
mcp_servers=[
53
{
54
"type": "url",
55
"name": "email-calendar-mcp",
56
"url": mcp_server_url,
57
}
58
],
59
tools=[
60
{"type": "agent_toolset_20260401", "default_config": {"enabled": True}},
61
{
62
"type": "mcp_toolset",
63
"mcp_server_name": "email-calendar-mcp",
64
"default_config": {
65
"enabled": True,
66
"permission_policy": {"type": "always_allow"},
67
},
68
},
69
],
70
)
71
72
print("Virtual MCP ID:", mcp_id)
73
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 [Section titled “Authorize user for external connections”](#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. executor\_setup.py
```python
1
import os
2
from scalekit import ScalekitClient
3
from dotenv import load_dotenv
4
5
load_dotenv()
6
7
scalekit_client = ScalekitClient(
8
env_url=os.environ["SCALEKIT_ENV_URL"],
9
client_id=os.environ["SCALEKIT_CLIENT_ID"],
10
client_secret=os.environ["SCALEKIT_CLIENT_SECRET"],
11
)
12
13
# Retrieve mcp_id by listing Virtual MCP Servers filtered by name to use below
14
accounts_response = scalekit_client.actions.mcp.list_mcp_connected_accounts(
15
config_id=mcp_id,
16
identifier=identifier, # your app's unique user ID
17
)
18
19
for account in accounts_response.connected_accounts:
20
if account.connected_account_status != "ACTIVE":
21
auth_response = scalekit_client.actions.get_authorization_link(
22
identifier=identifier,
23
connection_name=account.connection_name,
24
)
25
print(f"{account.connection_name} needs auth: {auth_response.link}")
26
else:
27
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 [Section titled “Run a session”](#run-a-session) Run the following for each agent execution. 1. ## Check that connections are active [Section titled “Check that connections are active”](#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. executor.py
```python
1
accounts_response = scalekit_client.actions.mcp.list_mcp_connected_accounts(
2
config_id=mcp_id,
3
identifier=identifier,
4
)
5
inactive = [
6
a.connection_name
7
for a in accounts_response.connected_accounts
8
if a.connected_account_status != "ACTIVE"
9
]
10
if inactive:
11
print("Inactive connections:", inactive)
12
# Prompt the user to re-authorize before proceeding
```
2. ## Mint a session token and store in vault [Section titled “Mint a session token and store in vault”](#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. executor.py
```python
1
from datetime import timedelta
2
3
configs_response = scalekit_client.actions.mcp.list_configs(filter_id=mcp_id)
4
mcp_server_url = configs_response.configs[0].mcp_server_url
5
6
token_response = scalekit_client.actions.mcp.create_session_token(
7
mcp_config_id=mcp_id,
8
identifier=identifier,
9
expiry=timedelta(hours=1),
10
)
11
token = token_response.token
12
13
# Create vault and credential on first run; update the token on subsequent runs
14
if vault_id and credential_id:
15
anthropic_client.beta.vaults.credentials.update(
16
credential_id,
17
vault_id=vault_id,
18
auth={"type": "static_bearer", "token": token},
19
)
20
else:
21
vault = anthropic_client.beta.vaults.create(display_name="email-calendar-vault")
22
vault_id = vault.id
23
credential = anthropic_client.beta.vaults.credentials.create(
24
vault_id,
25
display_name="email-calendar-credential",
26
auth={
27
"type": "static_bearer",
28
"mcp_server_url": mcp_server_url,
29
"token": token,
30
},
31
)
32
credential_id = credential.id
```
3. ## Start the session [Section titled “Start the session”](#start-the-session) Pass `vault_ids` to the session so the agent can authenticate against the MCP server. executor.py
```python
1
session = anthropic_client.beta.sessions.create(
2
agent=agent_id,
3
environment_id=os.environ["ANTHROPIC_ENVIRONMENT_ID"],
4
vault_ids=[vault_id],
5
)
6
7
with anthropic_client.beta.sessions.events.stream(session_id=session.id) as stream:
8
anthropic_client.beta.sessions.events.send(
9
session_id=session.id,
10
events=[{"type": "user.message", "content": [{"type": "text", "text": prompt}]}],
11
)
12
for event in stream:
13
if event.type == "agent.message":
14
for block in event.content:
15
if block.type == "text":
16
print(block.text, end="", flush=True)
17
elif event.type == "agent.mcp_tool_use":
18
print(f"\n→ {event.name}", flush=True)
19
elif event.type in ("session.status_idle", "session.status_terminated"):
20
break
```
---
# DOCUMENT BOUNDARY
---
# CrewAI
> Build a CrewAI agent with Scalekit-authenticated Gmail tools via MCP. CrewAI's MCPServerAdapter connects to a Scalekit MCP URL for automatic tool discovery.
Build a CrewAI agent that reads a user’s Gmail inbox. Scalekit handles OAuth, token storage, and exposes tools over MCP. CrewAI’s `MCPServerAdapter` discovers the tools automatically — no manual schema conversion needed. [Full code on GitHub](https://github.com/scalekit-developers/crewai-scalekit-example) ## Install [Section titled “Install”](#install)
```sh
1
pip install crewai crewai-tools scalekit-sdk-python python-dotenv
```
## Initialize [Section titled “Initialize”](#initialize)
```python
1
import os
2
from scalekit import ScalekitClient
3
from dotenv import find_dotenv, load_dotenv
4
5
load_dotenv(find_dotenv())
6
7
scalekit_client = ScalekitClient(
8
env_url=os.environ["SCALEKIT_ENV_URL"],
9
client_id=os.environ["SCALEKIT_CLIENT_ID"],
10
client_secret=os.environ["SCALEKIT_CLIENT_SECRET"],
11
)
12
actions = scalekit_client.actions
```
## Connect the user to Gmail [Section titled “Connect the user to Gmail”](#connect-the-user-to-gmail)
```python
1
response = actions.get_or_create_connected_account(
2
connection_name="gmail",
3
identifier="user_123",
4
)
5
if response.connected_account.status != "ACTIVE":
6
link = actions.get_authorization_link(connection_name="gmail", identifier="user_123")
7
print("Authorize Gmail:", link.link)
8
input("Press Enter after authorizing...")
```
See [Authorize a user](/agentkit/tools/authorize/) for production auth handling. ## Build and run the agent [Section titled “Build and run the agent”](#build-and-run-the-agent) Get the Virtual MCP Server URL and mint a session token, then pass both to `MCPServerAdapter`. CrewAI discovers all available Gmail tools from the MCP server:
```python
1
from crewai import Agent, Crew, LLM, Task
2
from crewai_tools import MCPServerAdapter
3
from datetime import timedelta
4
5
# Retrieve config_id by listing Virtual MCP Servers filtered by name
6
list_response = actions.mcp.list_configs(filter_name="gmail-user-tools")
7
mcp_server_url = list_response.configs[0].mcp_server_url
8
mcp_id = list_response.configs[0].id
9
10
token_response = actions.mcp.create_session_token(
11
mcp_config_id=mcp_id,
12
identifier="user_123",
13
expiry=timedelta(hours=1),
14
)
15
16
with MCPServerAdapter({
17
"url": mcp_server_url,
18
"headers": {"Authorization": f"Bearer {token_response.token}"},
19
"transport": "streamable-http",
20
}) as tools:
21
agent = Agent(
22
role="Email Assistant",
23
goal="Fetch and summarize the user's unread emails",
24
backstory="You are a helpful assistant with access to the user's Gmail inbox.",
25
tools=tools,
26
llm=LLM(
27
model=os.getenv("LLM_MODEL", "gpt-4o"),
28
base_url=os.getenv("OPENAI_BASE_URL"),
29
api_key=os.getenv("OPENAI_API_KEY"),
30
),
31
verbose=True,
32
)
33
34
task = Task(
35
description="Fetch the last 5 unread emails and provide a brief summary of each.",
36
expected_output="A list of 5 unread emails with subject, sender, and a one-sentence summary.",
37
agent=agent,
38
)
39
40
result = Crew(agents=[agent], tasks=[task]).kickoff()
41
print(result)
```
## Multi-agent crew [Section titled “Multi-agent crew”](#multi-agent-crew) CrewAI’s real strength is multi-agent orchestration. For a full example that splits email triage across three specialized agents (scanner, prioritizer, drafter), see the [CrewAI email triage cookbook](/cookbooks/crewai-agentkit-email-triage/). ## Get the MCP server URL [Section titled “Get the MCP server URL”](#get-the-mcp-server-url) The code above reads `mcp_server_url` from a Virtual MCP Server config. Create a config in the Scalekit Dashboard under **AgentKit → MCP Configs**. See [Virtual MCP Servers](/agentkit/mcp/overview/) for setup details.
---
# DOCUMENT BOUNDARY
---
# Google ADK
> Build a Google ADK agent with Scalekit-authenticated Gmail tools. Scalekit returns native ADK tool objects; no schema reshaping needed.
Build a Google ADK agent that reads a user’s Gmail inbox. Scalekit handles OAuth, token storage, and returns tools as native ADK tool objects compatible with any ADK agent. [Full code on GitHub](https://github.com/scalekit-inc/google-adk-agent-example) ## Install [Section titled “Install”](#install)
```sh
1
pip install scalekit-sdk-python google-adk
```
## Initialize [Section titled “Initialize”](#initialize)
```python
1
import os
2
import asyncio
3
import scalekit.client
4
5
scalekit_client = scalekit.client.ScalekitClient(
6
client_id=os.getenv("SCALEKIT_CLIENT_ID"),
7
client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"),
8
env_url=os.getenv("SCALEKIT_ENV_URL"),
9
)
10
actions = scalekit_client.actions
```
## Connect the user to Gmail [Section titled “Connect the user to Gmail”](#connect-the-user-to-gmail)
```python
1
response = actions.get_or_create_connected_account(
2
connection_name="gmail",
3
identifier="user_123",
4
)
5
if response.connected_account.status != "ACTIVE":
6
link = actions.get_authorization_link(connection_name="gmail", identifier="user_123")
7
print("Authorize Gmail:", link.link)
8
input("Press Enter after authorizing...")
```
See [Authorize a user](/agentkit/tools/authorize/) for production auth handling. ## Build and run the agent [Section titled “Build and run the agent”](#build-and-run-the-agent) `actions.google.get_tools()` returns native ADK tool objects. Pass them directly to a Google ADK `Agent`:
```python
1
from google.adk.agents import Agent
2
from google.adk.runners import Runner
3
from google.adk.sessions import InMemorySessionService
4
from google.genai import types
5
6
tools = actions.google.get_tools(
7
identifier="user_123",
8
connection_names=["gmail"],
9
page_size=100, # avoid missing tools when a connector has more than the default page
10
)
11
12
agent = Agent(
13
name="gmail_assistant",
14
model="gemini-2.0-flash",
15
instruction="You are a helpful Gmail assistant.",
16
tools=tools,
17
)
18
19
async def main():
20
session_service = InMemorySessionService()
21
runner = Runner(agent=agent, app_name="gmail_app", session_service=session_service)
22
session = await session_service.create_session(app_name="gmail_app", user_id="user_123")
23
24
message = types.Content(
25
role="user",
26
parts=[types.Part(text="Fetch my last 5 unread emails and summarize them")],
27
)
28
async for event in runner.run_async(
29
user_id="user_123",
30
session_id=session.id,
31
new_message=message,
32
):
33
if event.is_final_response():
34
print(event.response.text)
35
36
asyncio.run(main())
```
## Use MCP instead [Section titled “Use MCP instead”](#use-mcp-instead) Google ADK supports MCP via `MCPToolset`. Connect to a Scalekit-generated MCP URL to skip tool setup:
```python
1
from google.adk.agents import Agent
2
from google.adk.tools.mcp_tool.mcp_toolset import MCPToolset, StreamableHTTPConnectionParams
3
4
agent = Agent(
5
name="gmail_assistant",
6
model="gemini-2.0-flash",
7
instruction="You are a helpful Gmail assistant.",
8
tools=[
9
MCPToolset(
10
connection_params=StreamableHTTPConnectionParams(url=mcp_url)
11
)
12
],
13
)
```
See [Virtual MCP Servers](/agentkit/mcp/overview/) to get `mcp_url`.
---
# DOCUMENT BOUNDARY
---
# LangChain
> Build a LangChain agent with Scalekit-authenticated Gmail tools. Scalekit returns native LangChain tool objects; no schema reshaping needed.
Build a LangChain agent that reads a user’s Gmail inbox. Scalekit handles OAuth, token storage, and returns tools in native LangChain format. Your agent code needs no Scalekit-specific logic beyond initialization. [Full code on GitHub](https://github.com/scalekit-inc/sample-langchain-agent) ## Install [Section titled “Install”](#install)
```sh
1
pip install scalekit-sdk-python langchain-openai
```
## Initialize [Section titled “Initialize”](#initialize)
```python
1
import os
2
import scalekit.client
3
4
scalekit_client = scalekit.client.ScalekitClient(
5
client_id=os.getenv("SCALEKIT_CLIENT_ID"),
6
client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"),
7
env_url=os.getenv("SCALEKIT_ENV_URL"),
8
)
9
actions = scalekit_client.actions
```
## Connect the user to Gmail [Section titled “Connect the user to Gmail”](#connect-the-user-to-gmail)
```python
1
response = actions.get_or_create_connected_account(
2
connection_name="gmail",
3
identifier="user_123",
4
)
5
if response.connected_account.status != "ACTIVE":
6
link = actions.get_authorization_link(connection_name="gmail", identifier="user_123")
7
print("Authorize Gmail:", link.link)
8
input("Press Enter after authorizing...")
```
See [Authorize a user](/agentkit/tools/authorize/) for production auth handling. ## Build and run the agent [Section titled “Build and run the agent”](#build-and-run-the-agent) `actions.langchain.get_tools()` returns native `StructuredTool` objects. Bind them to your LLM and run the tool-calling loop:
```python
1
from langchain_openai import ChatOpenAI
2
from langchain_core.messages import HumanMessage, ToolMessage
3
4
tools = actions.langchain.get_tools(
5
identifier="user_123",
6
connection_names=["gmail"],
7
page_size=100, # avoid missing tools when a connector has more than the default page
8
)
9
tool_map = {t.name: t for t in tools}
10
11
llm = ChatOpenAI(model="gpt-4o").bind_tools(tools)
12
messages = [HumanMessage("Fetch my last 5 unread emails and summarize them")]
13
14
while True:
15
response = llm.invoke(messages)
16
messages.append(response)
17
if not response.tool_calls:
18
print(response.content)
19
break
20
for tc in response.tool_calls:
21
result = tool_map[tc["name"]].invoke(tc["args"])
22
messages.append(ToolMessage(content=str(result), tool_call_id=tc["id"]))
```
## Use MCP instead [Section titled “Use MCP instead”](#use-mcp-instead) LangChain supports MCP via `langchain-mcp-adapters`. Install it, then connect to a Scalekit-generated MCP URL:
```sh
1
pip install langchain-mcp-adapters
```
```python
1
import asyncio
2
from langchain_mcp_adapters.client import MultiServerMCPClient
3
from langchain_openai import ChatOpenAI
4
from langchain_core.messages import HumanMessage, ToolMessage
5
6
async def run(mcp_url: str):
7
async with MultiServerMCPClient(
8
{"scalekit": {"transport": "streamable_http", "url": mcp_url}}
9
) as client:
10
tools = client.get_tools()
11
tool_map = {t.name: t for t in tools}
12
llm = ChatOpenAI(model="gpt-4o").bind_tools(tools)
13
messages = [HumanMessage("Fetch my last 5 unread emails and summarize them")]
14
15
while True:
16
response = await llm.ainvoke(messages)
17
messages.append(response)
18
if not response.tool_calls:
19
print(response.content)
20
break
21
for tc in response.tool_calls:
22
result = await tool_map[tc["name"]].ainvoke(tc["args"])
23
messages.append(ToolMessage(content=str(result), tool_call_id=tc["id"]))
24
25
asyncio.run(run(mcp_url))
```
See [Virtual MCP Servers](/agentkit/mcp/overview/) to get `mcp_url`.
---
# DOCUMENT BOUNDARY
---
# Mastra
> Connect a Mastra agent to Scalekit-authenticated tools using MCP. Mastra's native MCP client connects directly to a Scalekit-generated MCP URL.
Connect a Mastra agent to Scalekit tools using MCP. Mastra has native MCP support via `@mastra/mcp`. Pass a Scalekit-generated URL and Mastra handles tool discovery automatically. ## Install [Section titled “Install”](#install)
```sh
1
npm install @scalekit-sdk/node @mastra/core @mastra/mcp @ai-sdk/openai
```
## Get a per-user MCP URL [Section titled “Get a per-user MCP URL”](#get-a-per-user-mcp-url) Generate a Scalekit MCP URL for the user. This requires the Python SDK. Call this from your backend for the **current user**, then pass that URL into your Mastra app (API response, session store, or request-scoped config).
```python
1
# Backend (Python): generate once per user session
2
inst_response = actions.mcp.ensure_instance(
3
config_name="your-mcp-config",
4
user_identifier="user_123",
5
)
6
mcp_url = inst_response.instance.url
7
# Return mcp_url to the Mastra app for this user only
```
See [Virtual MCP Servers](/agentkit/mcp/overview/) to set up the config and generate the URL. Do not share one MCP URL across users Each URL is pre-authenticated for a single user. In multi-user deployments, look up the URL for the authenticated user on the server. A process-wide `SCALEKIT_MCP_URL` is only safe for single-user demos; sharing it runs every request as that user. ## Build the agent [Section titled “Build the agent”](#build-the-agent) Pass the **current user’s** MCP URL to `MCPClient`. Mastra fetches the tool list and schemas automatically:
```typescript
1
import { Agent } from '@mastra/core/agent';
2
import { MCPClient } from '@mastra/mcp';
3
import { openai } from '@ai-sdk/openai';
4
5
// From your backend for the authenticated user — not a shared process-wide secret
6
const mcpUrl = await getMcpUrlForUser(currentUserId);
7
8
const mcp = new MCPClient({
9
servers: {
10
scalekit: { url: new URL(mcpUrl) },
11
},
12
});
13
14
const tools = await mcp.getTools();
15
16
const agent = new Agent({
17
name: 'gmail_assistant',
18
instructions: 'You are a helpful Gmail assistant.',
19
model: openai('gpt-4o'),
20
tools,
21
});
22
23
const result = await agent.generate('Fetch my last 5 unread emails and summarize them');
24
console.log(result.text);
25
26
await mcp.disconnect();
```
---
# DOCUMENT BOUNDARY
---
# OpenAI
> Build an OpenAI agent with Scalekit-authenticated tools. Convert Scalekit's tool schemas to OpenAI's function calling format in one step.
Build an agent using OpenAI’s GPT models that reads a user’s Gmail inbox. Scalekit’s tool schemas use `input_schema`: rename it to `parameters` and wrap it in OpenAI’s function format. ## Install [Section titled “Install”](#install) * Python
```sh
1
pip install scalekit-sdk-python openai
```
* Node.js
```sh
1
npm install @scalekit-sdk/node openai
```
## Initialize [Section titled “Initialize”](#initialize) * Python
```python
1
import os, json
2
import scalekit.client
3
from openai import OpenAI
4
from google.protobuf.json_format import MessageToDict
5
6
scalekit_client = scalekit.client.ScalekitClient(
7
client_id=os.getenv("SCALEKIT_CLIENT_ID"),
8
client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"),
9
env_url=os.getenv("SCALEKIT_ENV_URL"),
10
)
11
actions = scalekit_client.actions
12
client = OpenAI()
```
* Node.js
```typescript
1
import { ScalekitClient } from '@scalekit-sdk/node';
2
import { ConnectorStatus } from '@scalekit-sdk/node/lib/pkg/grpc/scalekit/v1/connected_accounts/connected_accounts_pb';
3
import OpenAI from 'openai';
4
5
const scalekit = new ScalekitClient(
6
process.env.SCALEKIT_ENV_URL!,
7
process.env.SCALEKIT_CLIENT_ID!,
8
process.env.SCALEKIT_CLIENT_SECRET!,
9
);
10
const openai = new OpenAI();
```
## Connect the user to Gmail [Section titled “Connect the user to Gmail”](#connect-the-user-to-gmail) * Python
```python
1
response = actions.get_or_create_connected_account(
2
connection_name="gmail",
3
identifier="user_123",
4
)
5
if response.connected_account.status != "ACTIVE":
6
link = actions.get_authorization_link(connection_name="gmail", identifier="user_123")
7
print("Authorize Gmail:", link.link)
8
input("Press Enter after authorizing...")
```
* Node.js
```typescript
1
const { connectedAccount } = await scalekit.actions.getOrCreateConnectedAccount({
2
connectionName: 'gmail',
3
identifier: 'user_123',
4
});
5
if (connectedAccount?.status !== ConnectorStatus.ACTIVE) {
6
const { link } = await scalekit.actions.getAuthorizationLink({ connectionName: 'gmail', identifier: 'user_123' });
7
console.log('Authorize Gmail:', link);
8
}
```
See [Authorize a user](/agentkit/tools/authorize/) for production auth handling. ## Run the agent [Section titled “Run the agent”](#run-the-agent) Fetch tools scoped to this user, convert to OpenAI’s function format, then run the tool-calling loop: * Python
```python
1
# Fetch and convert tools to OpenAI format
2
scoped_response, _ = actions.tools.list_scoped_tools(
3
identifier="user_123",
4
filter={"connection_names": ["gmail"]},
5
page_size=100, # fetch beyond the default page so no connector tools are missed
6
)
7
llm_tools = [
8
{
9
"type": "function",
10
"function": {
11
"name": MessageToDict(t.tool).get("definition", {}).get("name"),
12
"description": MessageToDict(t.tool).get("definition", {}).get("description", ""),
13
"parameters": MessageToDict(t.tool).get("definition", {}).get("input_schema", {}),
14
},
15
}
16
for t in scoped_response.tools
17
]
18
19
# Run the agent loop
20
messages = [{"role": "user", "content": "Fetch my last 5 unread emails and summarize them"}]
21
22
while True:
23
response = client.chat.completions.create(
24
model="gpt-4o",
25
tools=llm_tools,
26
messages=messages,
27
)
28
message = response.choices[0].message
29
if not message.tool_calls:
30
print(message.content)
31
break
32
33
messages.append(message)
34
for tc in message.tool_calls:
35
result = actions.execute_tool(
36
tool_name=tc.function.name,
37
identifier="user_123",
38
tool_input=json.loads(tc.function.arguments),
39
)
40
messages.append({
41
"role": "tool",
42
"tool_call_id": tc.id,
43
"content": str(result.data),
44
})
```
* Node.js
```typescript
1
// Fetch and convert tools to OpenAI format
2
const { tools } = await scalekit.tools.listScopedTools('user_123', {
3
filter: { connectionNames: ['gmail'] },
4
pageSize: 100, // fetch beyond the default page so no connector tools are missed
5
});
6
const llmTools: OpenAI.ChatCompletionTool[] = tools.map(t => ({
7
type: 'function',
8
function: {
9
name: t.tool.definition.name,
10
description: t.tool.definition.description,
11
parameters: t.tool.definition.input_schema,
12
},
13
}));
14
15
// Run the agent loop
16
const messages: OpenAI.ChatCompletionMessageParam[] = [
17
{ role: 'user', content: 'Fetch my last 5 unread emails and summarize them' },
18
];
19
20
while (true) {
21
const response = await openai.chat.completions.create({
22
model: 'gpt-4o',
23
tools: llmTools,
24
messages,
25
});
26
const message = response.choices[0].message;
27
if (!message.tool_calls?.length) {
28
console.log(message.content);
29
break;
30
}
31
messages.push(message);
32
for (const tc of message.tool_calls) {
33
const result = await scalekit.actions.executeTool({
34
toolName: tc.function.name,
35
identifier: 'user_123',
36
toolInput: JSON.parse(tc.function.arguments),
37
});
38
messages.push({ role: 'tool', tool_call_id: tc.id, content: JSON.stringify(result.data) });
39
}
40
}
```
## Use the Responses API [Section titled “Use the Responses API”](#use-the-responses-api) OpenAI’s [Responses API](https://platform.openai.com/docs/api-reference/responses) is a stateful alternative to Chat Completions. Instead of managing conversation history yourself, you pass `previous_response_id` to continue a session. The tool schema format is the same. * Python
```python
1
response = client.responses.create(
2
model="gpt-4o",
3
input="Fetch my last 5 unread emails and summarize them",
4
tools=llm_tools,
5
)
6
7
while any(item.type == "function_call" for item in response.output):
8
tool_results = [
9
{
10
"type": "function_call_output",
11
"call_id": item.call_id,
12
"output": str(actions.execute_tool(
13
tool_name=item.name,
14
identifier="user_123",
15
tool_input=json.loads(item.arguments),
16
).data),
17
}
18
for item in response.output
19
if item.type == "function_call"
20
]
21
response = client.responses.create(
22
model="gpt-4o",
23
previous_response_id=response.id,
24
input=tool_results,
25
tools=llm_tools,
26
)
27
28
for item in response.output:
29
if item.type == "message":
30
print(item.content[0].text)
```
* Node.js
```typescript
1
let response = await openai.responses.create({
2
model: 'gpt-4o',
3
input: 'Fetch my last 5 unread emails and summarize them',
4
tools: llmTools,
5
});
6
7
while (response.output.some(item => item.type === 'function_call')) {
8
const toolResults = await Promise.all(
9
response.output
10
.filter(item => item.type === 'function_call')
11
.map(async item => {
12
const result = await scalekit.actions.executeTool({
13
toolName: item.name,
14
identifier: 'user_123',
15
toolInput: JSON.parse(item.arguments),
16
});
17
return {
18
type: 'function_call_output' as const,
19
call_id: item.call_id,
20
output: JSON.stringify(result.data),
21
};
22
})
23
);
24
response = await openai.responses.create({
25
model: 'gpt-4o',
26
previous_response_id: response.id,
27
input: toolResults,
28
tools: llmTools,
29
});
30
}
31
32
const message = response.output.find(item => item.type === 'message');
33
if (message?.type === 'message') console.log(message.content[0].text);
```
## Use MCP instead [Section titled “Use MCP instead”](#use-mcp-instead) If you prefer the MCP approach, connect your OpenAI agent via the [Vercel AI SDK + MCP](/agentkit/examples/vercel-ai#use-mcp-instead) or LangChain’s MCP client with a Scalekit-generated URL. See [Virtual MCP Servers](/agentkit/mcp/overview/) for the URL setup.
---
# DOCUMENT BOUNDARY
---
# Vercel AI SDK
> Build a Vercel AI SDK agent with Scalekit-authenticated tools using the tool() helper and jsonSchema() adapter.
Build an agent using the Vercel AI SDK that reads a user’s Gmail inbox. Use `tool()` and `jsonSchema()` from the `ai` package to wrap Scalekit tools. No manual schema conversion needed. ## Install [Section titled “Install”](#install)
```sh
1
npm install @scalekit-sdk/node ai @ai-sdk/openai
```
## Initialize [Section titled “Initialize”](#initialize)
```typescript
1
import { ScalekitClient } from '@scalekit-sdk/node';
2
import { ConnectorStatus } from '@scalekit-sdk/node/lib/pkg/grpc/scalekit/v1/connected_accounts/connected_accounts_pb';
3
4
const scalekit = new ScalekitClient(
5
process.env.SCALEKIT_ENV_URL!,
6
process.env.SCALEKIT_CLIENT_ID!,
7
process.env.SCALEKIT_CLIENT_SECRET!,
8
);
```
## Connect the user to Gmail [Section titled “Connect the user to Gmail”](#connect-the-user-to-gmail)
```typescript
1
const { connectedAccount } = await scalekit.actions.getOrCreateConnectedAccount({
2
connectionName: 'gmail',
3
identifier: 'user_123',
4
});
5
if (connectedAccount?.status !== ConnectorStatus.ACTIVE) {
6
const { link } = await scalekit.actions.getAuthorizationLink({ connectionName: 'gmail', identifier: 'user_123' });
7
console.log('Authorize Gmail:', link);
8
}
```
See [Authorize a user](/agentkit/tools/authorize/) for production auth handling. ## Run the agent [Section titled “Run the agent”](#run-the-agent)
```typescript
1
import { generateText, jsonSchema, stepCountIs, tool } from 'ai';
2
import { openai } from '@ai-sdk/openai';
3
4
const { tools: scopedTools } = await scalekit.tools.listScopedTools('user_123', {
5
filter: { connectionNames: ['gmail'] },
6
pageSize: 100, // fetch beyond the default page so no connector tools are missed
7
});
8
9
const tools = Object.fromEntries(
10
scopedTools.map(t => [
11
t.tool.definition.name,
12
tool({
13
description: t.tool.definition.description,
14
parameters: jsonSchema(t.tool.definition.input_schema ?? { type: 'object', properties: {} }),
15
execute: async (args) => {
16
const result = await scalekit.actions.executeTool({
17
toolName: t.tool.definition.name,
18
identifier: 'user_123',
19
toolInput: args,
20
});
21
return result.data;
22
},
23
}),
24
]),
25
);
26
27
const { text } = await generateText({
28
model: openai('gpt-4o'),
29
tools,
30
stopWhen: stepCountIs(5),
31
prompt: 'Fetch my last 5 unread emails and summarize them',
32
});
33
console.log(text);
```
## Use MCP instead [Section titled “Use MCP instead”](#use-mcp-instead) The Vercel AI SDK supports MCP via `experimental_createMCPClient`. Pass the Virtual MCP Server URL and a session token to connect without any tool schema setup:
```typescript
1
import { experimental_createMCPClient, generateText } from 'ai';
2
import { openai } from '@ai-sdk/openai';
3
4
const mcpClient = await experimental_createMCPClient({
5
transport: {
6
type: 'streamable-http',
7
url: mcpUrl, // mcp_server_url from Virtual MCP Server config
8
headers: { Authorization: `Bearer ${mcpToken}` },
9
},
10
});
11
12
const tools = await mcpClient.tools();
13
14
const { text } = await generateText({
15
model: openai('gpt-4o'),
16
tools,
17
stopWhen: stepCountIs(5),
18
prompt: 'Fetch my last 5 unread emails and summarize them',
19
});
20
await mcpClient.close();
21
console.log(text);
```
See [Virtual MCP Servers](/agentkit/mcp/overview/) for setup details and how to get `mcpUrl` and `mcpToken`.
---
# DOCUMENT BOUNDARY
---
# Node.js SDK
> Install and initialize the Scalekit Node.js SDK for AgentKit.
Install the Node.js SDK, create a `ScalekitClient`, then use the sidebar clients for AgentKit. * **Connected accounts** (`scalekit.actions`) — connect end-user accounts and execute tools * **Tool calling** (`scalekit.tools`) — raw tool definitions for custom adapters * **Error handling** — typed exceptions for API failures ## Install [Section titled “Install”](#install)
```bash
1
npm install @scalekit-sdk/node
```
## Initialize [Section titled “Initialize”](#initialize)
```ts
import { ScalekitClient } from '@scalekit-sdk/node'
// Security: load credentials from environment variables — never hard-code secrets
const scalekit = new ScalekitClient(
process.env.SCALEKIT_ENVIRONMENT_URL!,
process.env.SCALEKIT_CLIENT_ID!,
process.env.SCALEKIT_CLIENT_SECRET!
)
```
## Next steps [Section titled “Next steps”](#next-steps) 1. [Connected accounts](/agentkit/sdks/node/actions/) — authorization links, connected accounts, `executeTool` 2. [Tool calling](/agentkit/sdks/node/tools/) — list tools for custom adapters 3. [Error handling](/agentkit/sdks/node/errors/) — catch `ScalekitNotFoundException` and related types
---
# DOCUMENT BOUNDARY
---
# Connected accounts
> Connect accounts, start auth, and execute tools with scalekit.actions
`scalekit.actions` is the primary AgentKit client for connecting end-user accounts, starting OAuth, and executing tools on their behalf. **Common path:** create or look up a connected account → get an authorization link → verify the user after redirect → run tools with `executeTool`. For raw tool schemas used by custom adapters, see [Tool calling](/agentkit/sdks/node/tools/). For exception types, see [Error handling](/agentkit/sdks/node/errors/). ### verifyConnectedAccountUser [Section titled “verifyConnectedAccountUser”](#verifyconnectedaccountuser) classActionsClienthttps\://github.com/scalekit-inc/scalekit-sdk-node/blob/main/src/actions.ts ### listConnectedAccounts [Section titled “listConnectedAccounts”](#listconnectedaccounts) classActionsClienthttps\://github.com/scalekit-inc/scalekit-sdk-node/blob/main/src/actions.ts ### executeTool [Section titled “executeTool”](#executetool) classActionsClienthttps\://github.com/scalekit-inc/scalekit-sdk-node/blob/main/src/actions.ts ### getAuthorizationLink [Section titled “getAuthorizationLink”](#getauthorizationlink) classActionsClienthttps\://github.com/scalekit-inc/scalekit-sdk-node/blob/main/src/actions.ts ### listConnections [Section titled “listConnections”](#listconnections) classActionsClienthttps\://github.com/scalekit-inc/scalekit-sdk-node/blob/main/src/actions.ts ### deleteConnectedAccount [Section titled “deleteConnectedAccount”](#deleteconnectedaccount) classActionsClienthttps\://github.com/scalekit-inc/scalekit-sdk-node/blob/main/src/actions.ts ### getConnectedAccount [Section titled “getConnectedAccount”](#getconnectedaccount) classActionsClienthttps\://github.com/scalekit-inc/scalekit-sdk-node/blob/main/src/actions.ts ### createConnectedAccount [Section titled “createConnectedAccount”](#createconnectedaccount) classActionsClienthttps\://github.com/scalekit-inc/scalekit-sdk-node/blob/main/src/actions.ts ### getOrCreateConnectedAccount [Section titled “getOrCreateConnectedAccount”](#getorcreateconnectedaccount) classActionsClienthttps\://github.com/scalekit-inc/scalekit-sdk-node/blob/main/src/actions.ts ### updateConnectedAccount [Section titled “updateConnectedAccount”](#updateconnectedaccount) classActionsClienthttps\://github.com/scalekit-inc/scalekit-sdk-node/blob/main/src/actions.ts ### request [Section titled “request”](#request) classActionsClienthttps\://github.com/scalekit-inc/scalekit-sdk-node/blob/main/src/actions.ts
---
# DOCUMENT BOUNDARY
---
# Error handling
> Catch Scalekit exceptions from AgentKit calls and handle not-found, auth, and server failures
AgentKit methods on `scalekit.actions` and `scalekit.tools` throw typed exceptions when the API returns an error. Catch the specific type first, then fall back to the base server exception. ## Catch exceptions [Section titled “Catch exceptions”](#catch-exceptions)
```ts
import {
ScalekitNotFoundException,
ScalekitUnauthorizedException,
ScalekitForbiddenException,
ScalekitServerException,
} from '@scalekit-sdk/node'
try {
const account = await scalekit.actions.getConnectedAccount({
connectionName: 'gmail',
identifier: 'user@example.com',
})
} catch (err) {
if (err instanceof ScalekitNotFoundException) {
// No connected account yet — create one or send the user through OAuth
} else if (err instanceof ScalekitUnauthorizedException) {
// Invalid or expired client credentials / tokens
} else if (err instanceof ScalekitForbiddenException) {
// Caller is authenticated but not allowed for this resource
} else if (err instanceof ScalekitServerException) {
// Unexpected API or platform error — log status and code
console.error(err.message)
} else {
throw err
}
}
```
## Exception types [Section titled “Exception types”](#exception-types) | Exception | When it is raised | Typical response | | ------------------------------- | --------------------------------------------------------- | ----------------------------------------------------------- | | `ScalekitNotFoundException` | Resource does not exist (connected account, tool, config) | Create the resource or return a clear not-found to the user | | `ScalekitUnauthorizedException` | Missing or invalid credentials | Refresh tokens or fix client ID/secret | | `ScalekitForbiddenException` | Authenticated but not permitted | Adjust scopes, org, or role | | `ScalekitServerException` | Base class for Scalekit HTTP/API failures | Log, retry when safe, surface a generic error | `ScalekitServerException` is the base type. Prefer checking subclasses first so not-found and auth failures get the right UX. ## Related [Section titled “Related”](#related) * [Connected accounts](/agentkit/sdks/node/actions/) — connect accounts and execute tools * [Tool calling](/agentkit/sdks/node/tools/) — list tool definitions * [Install](/agentkit/sdks/node/) — create the Scalekit client
---
# DOCUMENT BOUNDARY
---
# Tool calling
> List raw tool schemas for custom adapters
`scalekit.tools` returns raw tool schemas so you can build custom agent adapters instead of using `scalekit.actions.executeTool` directly. Use this client when you need tool definitions (name, parameters, connector) for frameworks or your own executor. For connect + execute flows, prefer [Connected accounts](/agentkit/sdks/node/actions/). See [Error handling](/agentkit/sdks/node/errors/) for exceptions. ### listTools [Section titled “listTools”](#listtools) classToolsClienthttps\://github.com/scalekit-inc/scalekit-sdk-node/blob/main/src/tools.ts ### listScopedTools [Section titled “listScopedTools”](#listscopedtools) classToolsClienthttps\://github.com/scalekit-inc/scalekit-sdk-node/blob/main/src/tools.ts ### listAvailableTools [Section titled “listAvailableTools”](#listavailabletools) classToolsClienthttps\://github.com/scalekit-inc/scalekit-sdk-node/blob/main/src/tools.ts ### executeTool [Section titled “executeTool”](#executetool) classToolsClienthttps\://github.com/scalekit-inc/scalekit-sdk-node/blob/main/src/tools.ts
---
# DOCUMENT BOUNDARY
---
# Python SDK
> Install and initialize the Scalekit Python SDK for AgentKit.
Install the Python SDK, create a `ScalekitClient`, then use the sidebar for AgentKit clients. * **Connections** (`scalekit_client.connection`) — create, list, get, and update environment-level connections * **Connected accounts** (`scalekit_client.actions`) — connect end-user accounts and execute tools * **Tool calling** — raw tool definitions for custom adapters * **MCP server**, **Frameworks**, **Request modifiers**, **Custom OAuth** — advanced surfaces * **Error handling** — typed exceptions for API failures ## Install [Section titled “Install”](#install) **Requires Python 3.8 or later.** The public SDK on PyPI does not support Python 3.5–3.7. If you run a legacy Python environment, contact [support](mailto:support@scalekit.com) to discuss alternatives.
```bash
1
pip install scalekit-sdk-python
```
## Initialize [Section titled “Initialize”](#initialize)
```python
import os
from scalekit import ScalekitClient
scalekit_client = ScalekitClient(
env_url=os.environ["SCALEKIT_ENVIRONMENT_URL"],
client_id=os.environ["SCALEKIT_CLIENT_ID"],
client_secret=os.environ["SCALEKIT_CLIENT_SECRET"],
)
actions = scalekit_client.actions
```
## Next steps [Section titled “Next steps”](#next-steps) * [Connections](/agentkit/sdks/python/connections/) * [Connected accounts](/agentkit/sdks/python/actions/) * [Tool calling](/agentkit/sdks/python/tools/) * [MCP](/agentkit/sdks/python/mcp/) * [Framework adapters](/agentkit/sdks/python/framework-adapters/) * [Error handling](/agentkit/sdks/python/errors/)
---
# DOCUMENT BOUNDARY
---
# Connected accounts
> Connect accounts, start auth, and execute tools with scalekit.actions
`scalekit.actions` is the primary AgentKit client for connecting end-user accounts, starting OAuth, and executing tools on their behalf. **Common path:** create or look up a connected account → get an authorization link → verify the user after redirect → run tools. For raw tool schemas, see [Tool calling](/agentkit/sdks/python/tools/). For exceptions, see [Error handling](/agentkit/sdks/python/errors/). ### get\_authorization\_link [Section titled “get\_authorization\_link”](#get_authorization_link) classActionsClient ### verify\_connected\_account\_user [Section titled “verify\_connected\_account\_user”](#verify_connected_account_user) classActionsClient ### get\_or\_create\_connected\_account [Section titled “get\_or\_create\_connected\_account”](#get_or_create_connected_account) classActionsClient ### get\_connected\_account [Section titled “get\_connected\_account”](#get_connected_account) classActionsClient ### get\_connected\_account\_details [Section titled “get\_connected\_account\_details”](#get_connected_account_details) classActionsClient ### list\_connected\_accounts [Section titled “list\_connected\_accounts”](#list_connected_accounts) classActionsClient ### create\_connected\_account [Section titled “create\_connected\_account”](#create_connected_account) classActionsClient ### update\_connected\_account [Section titled “update\_connected\_account”](#update_connected_account) classActionsClient ### delete\_connected\_account [Section titled “delete\_connected\_account”](#delete_connected_account) classActionsClient ### execute\_tool [Section titled “execute\_tool”](#execute_tool) classActionsClient ### request [Section titled “request”](#request) classActionsClient
---
# DOCUMENT BOUNDARY
---
# Connections
> Manage environment-level AgentKit connections with scalekit_client.connection
`scalekit_client.connection` manages the environment-level connections that AgentKit connectors run on. A connection holds the OAuth app credentials, scopes, and redirect URI Scalekit uses when your users authorize a connector, and one connection serves every user in that environment. **Common path:** create the connection once → attach OAuth credentials with `update_environment_connection` → connect end-user accounts with [`scalekit_client.actions`](/agentkit/sdks/python/actions/) → run tools. Most teams create connections in the Scalekit Dashboard — see [Configure a connection](/agentkit/connections/). Use these methods when you provision or manage environments in code, such as seeding a new environment from a setup script or CI job. These methods cover environment-level **app** connections (`Flags(is_app=True)`). Organization-scoped SSO connection APIs stay on the [SaaSKit connection reference](/saaskit/sdks/python/connection/). ### create\_environment\_connection [Section titled “create\_environment\_connection”](#create_environment_connection) clientConnectionhttps\://github.com/scalekit-inc/scalekit-sdk-python/blob/main/scalekit/connection.py ### list\_app\_connections [Section titled “list\_app\_connections”](#list_app_connections) clientConnectionhttps\://github.com/scalekit-inc/scalekit-sdk-python/blob/main/scalekit/connection.py ### get\_environment\_connection [Section titled “get\_environment\_connection”](#get_environment_connection) clientConnectionhttps\://github.com/scalekit-inc/scalekit-sdk-python/blob/main/scalekit/connection.py ### update\_environment\_connection [Section titled “update\_environment\_connection”](#update_environment_connection) clientConnectionhttps\://github.com/scalekit-inc/scalekit-sdk-python/blob/main/scalekit/connection.py ## Next steps [Section titled “Next steps”](#next-steps) * [Connected accounts](/agentkit/sdks/python/actions/) — connect end-user accounts and run tools on their behalf * [Configure a connection](/agentkit/connections/) — create connections in the Scalekit Dashboard
---
# DOCUMENT BOUNDARY
---
# Custom OAuth
> AgentKit custom providers
`actions.providers` manages custom providers used with bring-your-own connectors. Methods take typed request objects and return typed responses. Working end-to-end examples for OAuth, API key, bearer, and other auth types live in the [custom connectors demos](https://github.com/scalekit-inc/python-connect-demos/tree/main/custom-connectors) repo. Use those samples for field-complete setup rather than partial request shapes here. See [Bring your own connector](/agentkit/bring-your-own-connector/overview/) for the product flow. | Method | Purpose | | ------------------------------------------ | ---------------------------------------------------------------------------- | | `actions.providers.create_custom_provider` | Create a custom provider (`CreateCustomProviderRequest`) | | `actions.providers.update_custom_provider` | Partial update (`UpdateCustomProviderRequest`; only non-`None` fields apply) | | `actions.providers.list_providers` | List or filter providers (`ListProvidersRequest`) | | `actions.providers.delete_custom_provider` | Permanent delete by identifier (`DeleteCustomProviderRequest`) | ***
---
# DOCUMENT BOUNDARY
---
# Error handling
> Catch Scalekit exceptions from AgentKit calls and handle not-found, auth, and server failures
AgentKit methods throw typed exceptions when the API returns an error. Catch the specific type first, then fall back to the base server exception. ## Catch exceptions [Section titled “Catch exceptions”](#catch-exceptions)
```python
from scalekit.common.exceptions import (
ScalekitNotFoundException,
ScalekitUnauthorizedException,
ScalekitForbiddenException,
ScalekitServerException,
)
try:
account = scalekit_client.actions.get_connected_account(
connection_name="gmail",
identifier="user@example.com",
)
except ScalekitNotFoundException:
# No connected account yet — create one or send the user through OAuth
pass
except ScalekitUnauthorizedException:
# Invalid or expired client credentials / tokens
pass
except ScalekitForbiddenException:
# Caller is authenticated but not allowed for this resource
pass
except ScalekitServerException as e:
# Unexpected API or platform error
print(e.error_code, e.http_status)
```
## Exception types [Section titled “Exception types”](#exception-types) | Exception | When it is raised | Typical response | | ------------------------------- | --------------------------------------------------------- | ----------------------------------------------------------- | | `ScalekitNotFoundException` | Resource does not exist (connected account, tool, config) | Create the resource or return a clear not-found to the user | | `ScalekitUnauthorizedException` | Missing or invalid credentials | Refresh tokens or fix client ID/secret | | `ScalekitForbiddenException` | Authenticated but not permitted | Adjust scopes, org, or role | | `ScalekitServerException` | Base class for Scalekit HTTP/API failures | Log, retry when safe, surface a generic error | `ScalekitServerException` is the base type. Prefer checking subclasses first so not-found and auth failures get the right UX. ## Related [Section titled “Related”](#related) * [Connected accounts](/agentkit/sdks/python/actions/) — connect accounts and execute tools * [Tool calling](/agentkit/sdks/python/tools/) — list tool definitions * [Install](/agentkit/sdks/python/) — create the Scalekit client
---
# DOCUMENT BOUNDARY
---
# Frameworks
> Adapters for agent frameworks
Framework adapters map Scalekit tools into popular agent frameworks so you can register tools without hand-writing schema conversion. Pick the adapter for your stack, pass the Scalekit client, and register tools on the agent. See [Connected accounts](/agentkit/sdks/python/actions/) for the underlying client and [Error handling](/agentkit/sdks/python/errors/) for exceptions. ### actions.langchain.get\_tools [Section titled “actions.langchain.get\_tools”](#actionslangchainget_tools) classLangChainhttps\://github.com/scalekit-inc/scalekit-sdk-python/blob/main/scalekit/actions/frameworks/langchain.py
---
# DOCUMENT BOUNDARY
---
# MCP configurations
> Expose AgentKit tools over MCP.
Expose AgentKit tools over MCP so hosts like Claude Desktop or Cursor can call connected-account tools through a standard protocol. Configure an MCP server, attach connected accounts, and issue tokens for clients. See [Error handling](/agentkit/sdks/python/errors/) for API exceptions. ### scalekit\_client.actions.mcp.create\_config [Section titled “scalekit\_client.actions.mcp.create\_config”](#scalekit_clientactionsmcpcreate_config) classMcpClient ### scalekit\_client.actions.mcp.list\_configs [Section titled “scalekit\_client.actions.mcp.list\_configs”](#scalekit_clientactionsmcplist_configs) classMcpClient ### scalekit\_client.actions.mcp.update\_config [Section titled “scalekit\_client.actions.mcp.update\_config”](#scalekit_clientactionsmcpupdate_config) classMcpClient ### scalekit\_client.actions.mcp.delete\_config [Section titled “scalekit\_client.actions.mcp.delete\_config”](#scalekit_clientactionsmcpdelete_config) classMcpClient ### scalekit\_client.actions.mcp.list\_mcp\_connected\_accounts [Section titled “scalekit\_client.actions.mcp.list\_mcp\_connected\_accounts”](#scalekit_clientactionsmcplist_mcp_connected_accounts) classMcpClient ### scalekit\_client.actions.mcp.create\_session\_token [Section titled “scalekit\_client.actions.mcp.create\_session\_token”](#scalekit_clientactionsmcpcreate_session_token) classMcpClient
---
# DOCUMENT BOUNDARY
---
# Request modifiers
> AgentKit modifiers
Modifiers intercept tool calls to transform inputs or outputs, useful for validation, enrichment, or logging.
```python
1
# actions comes from the Scalekit client (or your framework adapter)
2
actions = scalekit_client.actions
3
4
@actions.pre_modifier(tool_names=["gmail_fetch_emails"])
5
def add_default_label(tool_input):
6
tool_input.setdefault("label", "UNREAD")
7
return tool_input
8
9
@actions.post_modifier(tool_names=["gmail_fetch_emails"])
10
def filter_attachments(tool_output):
11
tool_output["emails"] = [e for e in tool_output["emails"] if not e.get("has_attachment")]
12
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. ***
---
# DOCUMENT BOUNDARY
---
# Tool calling
> List raw tool schemas for custom adapters
`scalekit.tools` returns raw tool schemas for custom agent adapters. Use this when you need tool definitions for frameworks or your own executor. For connect + execute, use [Connected accounts](/agentkit/sdks/python/actions/). See [Error handling](/agentkit/sdks/python/errors/). ### tools.list\_tools [Section titled “tools.list\_tools”](#toolslist_tools) classToolsClient ### tools.list\_scoped\_tools [Section titled “tools.list\_scoped\_tools”](#toolslist_scoped_tools) classToolsClient ### tools.execute\_tool [Section titled “tools.execute\_tool”](#toolsexecute_tool) classToolsClient