Create your own connector
Choose an auth type, build the connector payload, and create or manage custom connectors in Scalekit.
Create a custom connector to bring an unsupported API or MCP server into Scalekit’s secure access model. This guide walks you through building the connector payload, creating the connector, and managing it over its lifecycle - list, update, and delete - with the management API.
Check out the examplesPrerequisites
You need three credentials from your Scalekit environment:
SCALEKIT_ENVIRONMENT_URL- the base URL of your Scalekit environmentSCALEKIT_CLIENT_ID- your environment’s client IDSCALEKIT_CLIENT_SECRET- your environment’s client secret
Find these in the Scalekit Dashboard under Developers → Settings → API Credentials.
Create a connector
Section titled “Create a connector”Create a connector in the Scalekit Dashboard or with the management API. The dashboard provides a guided form for MCP connectors; the management API gives you scriptable control over every connector type and auth pattern.
Create an MCP connector in the dashboard
Section titled “Create an MCP connector in the dashboard”Add an MCP connector through a guided form - no payload required.
-
In the Scalekit Dashboard, switch to AgentKit.
-
Select Connectors.
-
Select Create custom connector.
-
Complete the Add MCP connector form:
- Display name: a name for the connector, such as
Example MCP. - Description: a short description of what the connector connects to.
- Icon URL (optional): an icon for the connector. Must start with
https://. For best results, use an 800×800px SVG image, such ashttps://cdn.example.com/icon.svg. - Server URL: the base URL of the MCP server. Must start with
https://, such ashttps://app.example.com/mcp. - Metadata (optional): key-value pairs for the connector. Values must be plain strings; nested objects are not supported.
For Auth type, choose how users authenticate when they connect their account:
- OAuth: users authorize access through the provider’s OAuth flow, and Scalekit handles the token exchange.
- Bearer token: users provide a long-lived token issued by the provider.
- API key: users provide an API key issued by the provider.
- No authentication: the server is public and requires no credentials. Use this only when the server intentionally allows unauthenticated access and exposes no user-specific data or privileged operations, since every user shares the same anonymous access.
- Display name: a name for the connector, such as
-
Select Save. The connector is ready to use when you create a connection.
Create a connector with the management API
Section titled “Create a connector with the management API”Build the connector payload using the reference and examples that follow, then create the connector with the management API.
Understand the connector payload
Supported auth types are OAUTH, BASIC, BEARER, and API_KEY. Use OAUTH when the upstream API or MCP server requires a user authorization flow and token exchange. Use BASIC, BEARER, or API_KEY when it accepts static credentials or long-lived tokens.
MCP providers use the same four auth types as REST API providers, with is_mcp: true set in each auth_patterns[] entry. OAuth MCP connectors use a simplified oauth_config: {"pkce_enabled": true} - the MCP server handles authorization via Dynamic Client Registration. Non-OAuth MCP connectors omit oauth_config entirely. MCP connectors can also use NO_AUTH for public servers that require no credentials - set is_mcp: true, use an empty fields: [], and omit oauth_config. Use NO_AUTH only when the upstream intentionally allows unauthenticated public access and exposes no user-specific data or privileged operations; every user of the connector shares the same anonymous access.
The connector payload uses these common top-level fields:
display_name: Human-readable name for the custom connectordescription: Short description of what the connector connects toauth_patterns: Authentication options supported by the connectorproxy_url: Base URL the proxy should call for the upstream API (mandatory)proxy_enabled: Whether the proxy is enabled for the connector (mandatory, should be true)
proxy_url can also include templated fields when the upstream API requires account-specific values, for example https://{{domain}}/api.
Within auth_patterns, the most common fields are:
type: The auth type, such as OAUTH, BASIC, BEARER, or API_KEYdisplay_name: Label shown for that auth optiondescription: Short explanation of the auth methodfields: Inputs collected for static auth providers such as BASIC, BEARER, and API_KEY. These usually store values such asusername,password,token,api_key,domain, orversion.account_fields: Inputs collected for OAUTH connectors when account-scoped values are needed. This is typically used for values tied to a connected account, such as named path parameters.oauth_config: OAuth-specific configuration, such as authorize and token endpointsauth_header_key_override: Custom header name when the upstream does not useAuthorization. For example, some APIs expect auth in a header such asX-API-Keyinstead of the standardAuthorizationheader.auth_field_mutations: Value transformations applied before the credential is sent. This is useful when the upstream expects a prefix, suffix, or default companion value, such as adding a token prefix or setting a fallback password value for Basic auth.is_mcp: Set totruewhen the upstream is an MCP server. Tells Scalekit to route the connector through MCP tool calling instead of the HTTP proxy.
Below are example payloads for API and MCP connectors across all supported auth patterns.
{ "display_name": "My Asana", "description": "Connect to Asana. Manage tasks, projects, teams, and workflow automation", "auth_patterns": [ { "type": "OAUTH", "display_name": "OAuth 2.0", "description": "Authenticate with Asana using OAuth 2.0 for comprehensive project management", "fields": [], "oauth_config": { "authorize_uri": "https://app.asana.com/-/oauth_authorize", "token_uri": "https://app.asana.com/-/oauth_token", "user_info_uri": "https://app.asana.com/api/1.0/users/me", "available_scopes": [ { "scope": "profile", "display_name": "Profile", "description": "Access user profile information", "required": true }, { "scope": "email", "display_name": "Email", "description": "Access user email address", "required": true } ] } } ], "proxy_url": "https://app.asana.com/api", "proxy_enabled": true}{ "display_name": "My Bearer Token Provider", "description": "Connect to an API that accepts a static bearer token", "auth_patterns": [ { "type": "BEARER", "display_name": "Bearer Token", "description": "Authenticate with a static bearer token", "fields": [ { "field_name": "token", "label": "Bearer Token", "input_type": "password", "hint": "Your long-lived bearer token", "required": true } ] } ], "proxy_url": "https://api.example.com", "proxy_enabled": true}{ "display_name": "My Freshdesk", "description": "Connect to Freshdesk. Manage tickets, contacts, companies, and customer support workflows", "auth_patterns": [ { "type": "BASIC", "display_name": "Basic Auth", "description": "Authenticate with Freshdesk using Basic Auth with username and password for comprehensive helpdesk management", "fields": [ { "field_name": "domain", "label": "Freshdesk Domain", "input_type": "text", "hint": "Your Freshdesk domain (e.g., yourcompany.freshdesk.com)", "required": true }, { "field_name": "username", "label": "API Key", "input_type": "text", "hint": "Your Freshdesk API Key", "required": true } ] } ], "proxy_url": "https://{{domain}}/api", "proxy_enabled": true}{ "display_name": "My Attention", "description": "Connect to Attention for AI insights, conversations, teams, and workflows", "auth_patterns": [ { "type": "API_KEY", "display_name": "API Key", "description": "Authenticate with Attention using an API Key", "fields": [ { "field_name": "api_key", "label": "Integration Token", "input_type": "password", "hint": "Your Attention API Key", "required": true } ] } ], "proxy_url": "https://api.attention.tech", "proxy_enabled": true}{ "display_name": "Github MCP", "description": "Connect to Github MCP", "auth_patterns": [ { "description": "Authenticate with Github MCP using browser OAuth.", "display_name": "OAuth 2.1/DCR", "fields": [], "is_mcp": true, "oauth_config": { "pkce_enabled": true }, "type": "OAUTH" } ], "proxy_url": "https://api.githubcopilot.com/mcp/", "proxy_enabled": true}{ "display_name": "Apify MCP", "description": "Connect to Apify MCP to run web scraping, browser automation, and data extraction Actors directly from your AI workflows.", "auth_patterns": [ { "description": "Authenticate with Apify using your API Token.", "display_name": "Apify Token", "fields": [ { "field_name": "token", "hint": "Your Apify API Token", "input_type": "password", "label": "Apify Token", "required": true } ], "is_mcp": true, "type": "BEARER" } ], "proxy_url": "https://mcp.apify.com", "proxy_enabled": true}{ "display_name": "My Internal MCP", "description": "Connect to an internal MCP server that authenticates with a username and password", "auth_patterns": [ { "type": "BASIC", "display_name": "Basic Auth", "description": "Authenticate with a username and password", "is_mcp": true, "fields": [ { "field_name": "username", "label": "Username", "input_type": "text", "hint": "Your username", "required": true }, { "field_name": "password", "label": "Password", "input_type": "password", "hint": "Your password", "required": true } ] } ], "proxy_url": "https://mcp.internal.example.com", "proxy_enabled": true}{ "display_name": "My API Key MCP", "description": "Connect to an MCP server that authenticates with a static API key", "auth_patterns": [ { "type": "API_KEY", "display_name": "API Key", "description": "Authenticate with a static API key", "is_mcp": true, "fields": [ { "field_name": "api_key", "label": "API Key", "input_type": "password", "hint": "Your API key", "required": true } ] } ], "proxy_url": "https://mcp.example.com", "proxy_enabled": true}{ "display_name": "Public Docs MCP", "description": "Connect to a public MCP server that requires no credentials", "auth_patterns": [ { "type": "NO_AUTH", "display_name": "No Auth", "description": "Public server - no credentials required.", "is_mcp": true, "fields": [] } ], "proxy_url": "https://mcp.example.com", "proxy_enabled": true}Before submitting, review the final payload carefully:
display_nameanddescription- The selected auth
type - Required
fieldsandaccount_fields - OAuth endpoints and scopes, if the connector uses OAuth
proxy_url- Whether
is_mcpis set totruefor MCP providers
Generate an access token
All API requests require a short-lived access token. Generate one using your SCALEKIT_CLIENT_ID and SCALEKIT_CLIENT_SECRET:
curl --location "$SCALEKIT_ENVIRONMENT_URL/oauth/token" \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'grant_type=client_credentials' \ --data-urlencode "client_id=$SCALEKIT_CLIENT_ID" \ --data-urlencode "client_secret=$SCALEKIT_CLIENT_SECRET"Use the access_token value from the response as $env_access_token in the curl commands below.
Use the payload for your auth type as the request body in the create request:
# $env_access_token and $SCALEKIT_CLIENT_SECRET are secrets - keep them server-side and out of source control.# --fail-with-body makes curl exit non-zero and print the error body on a non-2xx response.curl --fail-with-body --location "$SCALEKIT_ENVIRONMENT_URL/api/v1/custom-providers" \ --header "Authorization: Bearer $env_access_token" \ --header "Content-Type: application/json" \ --data '{...}'The Python SDK builds the payload with typed request objects and authenticates using your client credentials - no separate access token step is needed. It covers MCP connector auth types: OAuth (via Dynamic Client Registration), Bearer, API key, and No Auth. The example below creates an OAuth MCP connector; swap the AuthPattern for the auth type you need.
import scalekit.client, osfrom dotenv import load_dotenvfrom scalekit.actions.types import AuthPattern, OAuthConfig, CreateCustomProviderRequestfrom scalekit.common.exceptions import ScalekitExceptionload_dotenv()
# Load credentials from the environment. Keep SCALEKIT_CLIENT_SECRET server-side -# never commit it or expose it in client-side code.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"),)
try: response = scalekit_client.actions.providers.create_custom_provider( CreateCustomProviderRequest( display_name="Github MCP", description="Connect to Github MCP", proxy_url="https://api.githubcopilot.com/mcp/", proxy_enabled=True, auth_patterns=[ AuthPattern( type="OAUTH", display_name="OAuth 2.1/DCR", description="Authenticate with Github MCP using browser OAuth.", is_mcp=True, oauth_config=OAuthConfig(), # pkce_enabled=True by default ) ], # Optional: icon_src="https://cdn.example.com/icon.svg", # Optional: metadata={"team": "platform"}, ) ) print("Created connector:", response.provider.identifier)except ScalekitException as err: # Handle validation errors, conflicts (duplicate name), auth failures, etc. print("Failed to create connector:", err) raiseA successful request returns the created connector. Next, create a connection in the Scalekit Dashboard, then continue with the standard connector flow to authorize users and call tools.
List connectors
Section titled “List connectors”List existing connectors before you create one, to confirm whether a connector for the upstream already exists. You also need the list to find a connector’s identifier for update and delete requests.
# $env_access_token is a secret - keep it server-side and out of source control.curl --fail-with-body --location "$SCALEKIT_ENVIRONMENT_URL/api/v1/providers?filter.provider_type=CUSTOM&page_size=1000" \ --header "Authorization: Bearer $env_access_token"import scalekit.client, osfrom dotenv import load_dotenvfrom scalekit.actions.types import ListProvidersRequestfrom scalekit.v1.providers.providers_pb2 import ProviderTypefrom scalekit.common.exceptions import ScalekitExceptionload_dotenv()
# Keep SCALEKIT_CLIENT_SECRET server-side - never commit it or expose it client-side.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"),)
try: response = scalekit_client.actions.providers.list_providers( ListProvidersRequest(provider_type=ProviderType.CUSTOM, page_size=1000) ) for provider in response.providers: print(provider.identifier, provider.display_name)except ScalekitException as err: print("Failed to list connectors:", err) raiseUpdate a connector
Section titled “Update a connector”Use the List connectors API to get the connector identifier, then send the updated payload. Include display_name, proxy_url, and auth_patterns on every update, and echo back any other fields you want to keep - omitted fields are not preserved, so read the current connector first and change only what you need.
# $env_access_token and $SCALEKIT_CLIENT_SECRET are secrets - keep them server-side and out of source control.curl --fail-with-body --location --request PUT "$SCALEKIT_ENVIRONMENT_URL/api/v1/custom-providers/$PROVIDER_IDENTIFIER" \ --header "Authorization: Bearer $env_access_token" \ --header "Content-Type: application/json" \ --data '{...}'import scalekit.client, osfrom dotenv import load_dotenvfrom scalekit.actions.types import ListProvidersRequest, UpdateCustomProviderRequestfrom scalekit.common.exceptions import ScalekitExceptionload_dotenv()
# Keep SCALEKIT_CLIENT_SECRET server-side - never commit it or expose it client-side.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"),)
provider_identifier = "prov_..." # from list_providers
try: # Read the current state, then echo back every field you want to keep. current = scalekit_client.actions.providers.list_providers( ListProvidersRequest(identifier=provider_identifier) ).providers[0]
response = scalekit_client.actions.providers.update_custom_provider( UpdateCustomProviderRequest( identifier=current.identifier, display_name=current.display_name, proxy_url=current.proxy_url, description="Updated description", auth_patterns=current.auth_patterns, metadata=dict(current.metadata), ) ) print("Updated connector:", response.provider.identifier)except ScalekitException as err: print("Failed to update connector:", err) raiseDelete a connector
Section titled “Delete a connector”Use the List connectors API to get the connector identifier. If the connector is still in use, remove the related connections or connected accounts first.
# $env_access_token is a secret - keep it server-side and out of source control.curl --fail-with-body --location --request DELETE "$SCALEKIT_ENVIRONMENT_URL/api/v1/custom-providers/$PROVIDER_IDENTIFIER" \ --header "Authorization: Bearer $env_access_token"import scalekit.client, osfrom dotenv import load_dotenvfrom scalekit.actions.types import DeleteCustomProviderRequestfrom scalekit.common.exceptions import ScalekitExceptionload_dotenv()
# Keep SCALEKIT_CLIENT_SECRET server-side - never commit it or expose it client-side.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"),)
try: scalekit_client.actions.providers.delete_custom_provider( DeleteCustomProviderRequest(identifier="prov_...") ) print("Connector deleted.")except ScalekitException as err: # e.g. not found, or forbidden if the connector is still in use. print("Failed to delete connector:", err) raiseNext steps
Section titled “Next steps”With the connector created and a connection in place, authorize a user and start calling the upstream:
- Making tool calls - call the upstream API or MCP server through your connector.