Skip to content
Scalekit Docs
Talk to an Engineer Dashboard

Use built-in tools

Find the tools a user can call, run one with execute_tool, and pass the tool list to your model. Python, Node.js and cURL.

Every connector ships prebuilt tools, such as github_user_repos_list or slack_send_message. Each has a JSON Schema for its input and returns structured output. Your code names the tool and the user; Scalekit adds that user’s credentials, calls the app’s API and returns the result. This page finds a tool, runs it, and hands the tool list to your model.

  • An ACTIVE connected account for the user. Authorize a user shows how to get one.

  • The connection name from AgentKit > Connections. These examples use github-connect, the GitHub connection new environments include.

  • A client set up as in the Quickstart: actions in Python, scalekit in Node.js. For cURL, get an access token and keep it in TOKEN:

    Terminal window
    TOKEN=$(curl -sS "$SCALEKIT_ENVIRONMENT_URL/oauth/token" \
    -d grant_type=client_credentials \
    -d client_id="$SCALEKIT_CLIENT_ID" \
    -d client_secret="$SCALEKIT_CLIENT_SECRET" | jq -r .access_token)
  1. Search by the job you want done. Pass the user’s identifier to see, for each tool, whether that user can call it now.

    from scalekit.v1.tools.tools_pb2 import ToolReadinessState
    results, _ = actions.tools.search_tools(
    query="list my GitHub repositories",
    identifier="user_123",
    top_k=5,
    )
    for tool in results.tools:
    ready = [
    c.connected_account_id
    for c in tool.connections
    if c.readiness_state == ToolReadinessState.TOOL_READINESS_STATE_READY
    ]
    print(tool.name, round(tool.score, 2), "ready" if ready else "not connected")

    Each result has a score (higher is closer) and a connections list with a readiness_state per connection:

    • READY: the user has an ACTIVE account on that connection. Call the tool now.
    • NEEDS_CONNECTION: the account exists but isn’t active. Send a new authorization link.
    • NEEDS_REAUTH: the token expired. Send a new authorization link.

    A connector the user has never connected isn’t in connections at all.

    To get the tools of specific connections instead, list the scoped tools. Pass the connections your agent works with, such as the ones the user picked in your app, and get the tools of those connections in one list:

    • The user needs a connected account on each connection you pass. Otherwise the call fails.
    • Pass one connection per connector. Two connections of the same connector, such as two GitHub connections, fail with INVALID_ARGUMENT.
    • Pages hold up to 100 tools. Pass next_page_token back until it’s empty.

    The samples list the tools of github-connect and of a Gmail connection named gmail. Change gmail to a connection in your environment, or remove it.

    from google.protobuf.json_format import MessageToDict
    definitions = []
    page_token = None
    while True:
    page, _ = actions.tools.list_scoped_tools(
    identifier="user_123",
    filter={"connection_names": ["github-connect", "gmail"]},
    page_size=100,
    page_token=page_token,
    )
    definitions += [MessageToDict(t.tool)["definition"] for t in page.tools]
    page_token = page.next_page_token
    if not page_token:
    break
    print(len(definitions), [d["name"] for d in definitions[:5]])
  2. Pass the tool name, its input, and the user. Identify the user with identifier plus the connection name, or with connected_account_id alone, which you get from a search result’s connections or from get_connected_account. When you pass connected_account_id, Scalekit ignores identifier.

    from scalekit.common.exceptions import ScalekitServerException
    try:
    result = actions.execute_tool(
    tool_name="github_user_repos_list",
    identifier="user_123",
    connection_name="github-connect",
    tool_input={"per_page": 5, "sort": "updated"},
    )
    print(result.execution_id)
    print(result.data)
    except ScalekitServerException as e:
    print("Tool call failed:", e.error_code, e.message)
  3. The response wraps the app’s output. Read the tool’s fields from data, and keep execution_id to find the call in your logs.

    Some apps report an error in a successful (2xx) response. Then the call still returns, and data holds the app’s error. Slack, for example, returns:

    { "ok": false, "error": "missing_scope", "needed": "users:read" }

    Check data for the app’s error shape before you use it. When the app answers with an error status instead, such as 401, 403, 429 or another 4xx or 5xx, the call fails with an error whose message starts with tool execution failed. See Common problems.

  4. Tool definitions carry more than a model needs. Keep name, description and input_schema, a JSON Schema object for the tool’s input. Anthropic’s API takes it as input_schema unchanged, and OpenAI’s takes it as a function’s parameters. This continues from the list in step 1.

    llm_tools = [
    {
    "name": d["name"],
    "description": d.get("description", ""),
    "input_schema": d.get("input_schema", {"type": "object", "properties": {}}),
    }
    for d in definitions
    ]
    read_only = [d["name"] for d in definitions if d.get("annotations", {}).get("read_only_hint")]
    print(len(llm_tools), "tools,", len(read_only), "read-only")

    Each definition’s annotations include read_only_hint and destructive_hint. Use them to start an agent with read-only tools, or to ask the user before a destructive call.

    When the model asks for a tool, call it as in step 2 and send data back as the tool result. LangChain and Google ADK get native tool objects from actions.langchain.get_tools and actions.google.get_tools. Choose a framework has a complete agent for each framework.

The call returns an execution_id and a data object with the app’s response, such as a list of repositories for github_user_repos_list. Search results for the user show READY for the connection you called.

That tool name doesn’t exist in your environment. Use a name from search or from the scoped list, exactly as returned.

The connection name doesn’t match AgentKit > Connections exactly. Connection names can differ between environments, so read them from configuration rather than hard-coding them.

No connected account exists for this identifier on this connection. Check that you pass the same identifier you used when the user authorized, or authorize the user first.

The user hasn’t finished authorizing, or the account expired or was disconnected. Send a new authorization link and wait until the status is ACTIVE.

The input doesn’t match the tool’s input_schema. Check required fields and types in the tool’s definition.

The call returns, but data holds an error such as missing_scope

Section titled “The call returns, but data holds an error such as missing_scope”

The app refused the call in a 2xx response. For a missing scope, add the scope to the connection, then have the user authorize again so their token includes it.

tool execution failed - unauthorized access

Section titled “tool execution failed - unauthorized access”

The app answered 401: it rejected the user’s token. Check the connected account’s status, and if the user revoked access, send a new authorization link.

The app answered 403. The user’s token is usually missing a scope the tool needs, or the user lacks permission in the app. Add the scope to the connection, then have the user authorize again.

The app answered 429. Wait before you retry, and slow down calls for this user.

The app answered with another error status. The rest of the message is the app’s own error. Check the input against the tool’s input_schema and the app’s API reference.

To serve these tools to an MCP client instead, create a Virtual MCP server.

API reference

The endpoints this page's code calls, with every field and the SDK method for each: