Skip to main content

StackOne plugin for Google ADK: Connect agents to 200+ SaaS providers

Project description

stackone-adk

Google ADK plugin for connecting agents to 200+ SaaS providers via StackOne.

StackOne provides a unified API gateway for HRIS, ATS, CRM, scheduling, and more. This plugin dynamically discovers available tools from StackOne's API and exposes them as native Google ADK tools, no hardcoded tool definitions needed.

Installation

pip install stackone-adk

Or with uv:

uv add stackone-adk

Configuration

StackOne API Key

Get your API key from the StackOne Dashboard.

# Environment variable (recommended)
export STACKONE_API_KEY="your-stackone-api-key"

Or pass directly:

plugin = StackOnePlugin(api_key="your-stackone-api-key")

Google API Key

Get your API key from Google AI Studio. This key is required to access Google's Gemini models.

export GOOGLE_API_KEY="your-google-api-key"

Quick Start

In standard ADK, you define tools as Python functions:

# Standard ADK — manual tool functions
def get_current_time(city: str) -> dict:
    """Returns the current time in a specified city."""
    return {"status": "success", "city": city, "time": "10:30 AM"}

root_agent = Agent(
    model="gemini-3.1-pro-preview",
    name="root_agent",
    tools=[get_current_time],
)

With StackOne, tools are discovered dynamically from your connected providers — no manual tool definitions needed:

import asyncio
from google.adk.agents import Agent
from google.adk.apps import App
from google.adk.runners import InMemoryRunner
from stackone_adk import StackOnePlugin

async def main():
    # StackOne replaces manual tool functions
    # Reads STACKONE_API_KEY from env and uses the specified account_id
    plugin = StackOnePlugin(account_id="STACKONE_ACCOUNT_ID")

    agent = Agent(
        model="gemini-3.1-pro-preview",
        name="stackone_agent",
        instruction="You are an HR assistant with access to tools via StackOne.",
        tools=plugin.get_tools(),  # instead of: tools=[get_current_time]
    )

    app = App(name="stackone_app", root_agent=agent, plugins=[plugin])

    async with InMemoryRunner(app=app) as runner:
        response = await runner.run_debug("List the first 3 workers.")
        print(response)

asyncio.run(main())

Usage Patterns

With App (Recommended)

from google.adk.apps import App
from google.adk.runners import InMemoryRunner

plugin = StackOnePlugin(account_id="STACKONE_ACCOUNT_ID")

agent = Agent(
    model="gemini-3.1-pro-preview",
    name="stackone_agent",
    tools=plugin.get_tools(),
)

app = App(name="stackone_app", root_agent=agent, plugins=[plugin])

async with InMemoryRunner(app=app) as runner:
    response = await runner.run_debug("List the first 3 workers")

With Runner Directly

from google.adk.runners import InMemoryRunner

plugin = StackOnePlugin(account_id="STACKONE_ACCOUNT_ID")

agent = Agent(
    model="gemini-3.1-pro-preview",
    name="stackone_agent",
    tools=plugin.get_tools(),
)

async with InMemoryRunner(app_name="stackone_app", agent=agent) as runner:
    response = await runner.run_debug("List the first 3 workers")

Plugin Configuration

Parameter Type Default Description
api_key str | None None StackOne API key. Falls back to STACKONE_API_KEY env var.
account_id str | None None Default account ID for all tools.
base_url str | None None API URL override (default: https://api.stackone.com).
plugin_name str "stackone_plugin" Plugin identifier for ADK.
providers list[str] | None None Filter by provider names.
actions list[str] | None None Filter by action patterns (supports globs).
account_ids list[str] | None None Scope tools to specific account IDs.
mode Literal["search_and_execute"] | None None Tool registration strategy. See Search and Execute mode.
search SearchConfig | None None Search backend config. Only used when mode="search_and_execute".
execute ExecuteToolsConfig | None None Execution config (account scoping, timeout). Only used when mode="search_and_execute".
timeout float | None None Per-request timeout in seconds for tool execution.

Search and Execute Mode

By default, StackOnePlugin registers every discovered tool with the agent — fine for one or two providers, but the LLM context balloons quickly when many SaaS accounts are connected. With mode="search_and_execute", the plugin registers exactly two meta tools (tool_search and tool_execute) and lets the model discover and invoke real tools on demand:

from stackone_adk import StackOnePlugin

plugin = StackOnePlugin(
    mode="search_and_execute",
    search={"method": "auto", "top_k": 5},
)

agent = Agent(
    model="gemini-3.1-pro-preview",
    name="multi_saas_agent",
    tools=plugin.get_tools(),
)

When to use: more than ~5 connected providers, or whenever the full tool list would dominate the model's context window.

How it works: the LLM calls tool_search("...") to get a short list of candidate tools (name, description, parameter schema), then calls tool_execute(tool_name, parameters) with the chosen one. Both calls round-trip to StackOne's AI Integration Gateway via the SDK.

Notes:

  • providers and actions filters are ignored in this mode (scoping happens via account_ids and the LLM's search query). A warning is logged if you pass them.
  • Search defaults to {"method": "auto"} (semantic search with local BM25+TF-IDF fallback). Pass search={"method": "semantic"} or search={"method": "local"} to force one backend.
  • The connector list embedded in the meta tools' descriptions is captured at plugin construction time. If you link new accounts after the plugin starts, restart the agent to pick them up.

See examples/search_and_execute_agent.py for a complete runnable example.

Tool Filtering

By Account ID (Recommended)

Scope tools to specific connected accounts. This is the recommended approach to ensure your agent only accesses the intended accounts:

# Single account
plugin = StackOnePlugin(account_id="STACKONE_ACCOUNT_ID")

# Multiple accounts
plugin = StackOnePlugin(account_ids=["acct-hibob-1", "acct-bamboohr-1"])

By Provider

Filter tools to specific SaaS providers:

# Single provider
plugin = StackOnePlugin(providers=["workday"])

# Multiple providers
plugin = StackOnePlugin(providers=["hibob", "bamboohr"])

By Action Pattern

Use glob patterns to filter specific actions:

# Read-only operations
plugin = StackOnePlugin(actions=["*_list_*", "*_get_*"])

# Specific actions
plugin = StackOnePlugin(actions=["workday_list_workers", "workday_get_worker*"])

Combining Filters

plugin = StackOnePlugin(
    providers=["hibob", "bamboohr"],
    actions=["*_list_*", "*_get_*"],
    account_ids=["acct-hibob-1", "acct-bamboohr-1"],
)

Available Tools

Unlike plugins with a fixed set of tools, StackOne tools are dynamically discovered from your connected providers via the StackOne API. The available tools depend on which SaaS providers you have connected in your StackOne Dashboard. Print discovered tools:

plugin = StackOnePlugin(account_id="STACKONE_ACCOUNT_ID", providers=["workday"])
for tool in plugin.get_tools():
    print(f"{tool.name}: {tool.description}")

Examples

See the examples/ directory:

Example Description
workday_agent.py Default mode (registers all matching tools); demo uses actions=[...] to scope to 3 Workday actions
search_and_execute_agent.py LLM-driven discovery via mode="search_and_execute", registers 2 tools

Development

git clone https://github.com/StackOneHQ/stackone-adk-plugin.git
cd stackone-adk-plugin
pip install -e ".[dev]"

Run tests:

pytest

Lint and type check:

ruff check stackone_adk/ tests/
mypy stackone_adk/

Try an example (requires at least one provider connected in your StackOne Dashboard, e.g. Workday):

export STACKONE_API_KEY="your-stackone-api-key"
export STACKONE_ACCOUNT_ID="account-id"
export GOOGLE_API_KEY="your-google-api-key"
uv run examples/workday_agent.py

Releasing

Bump version in pyproject.toml and stackone_adk/__init__.py, update CHANGELOG.md, then:

rm -rf dist/ && uv build && UV_PUBLISH_TOKEN=<pypi-token> uv publish
git tag stackone-adk-vX.Y.Z && git push origin stackone-adk-vX.Y.Z

License

Apache 2.0 — see LICENSE.

References

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

stackone_adk-0.2.0.tar.gz (268.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

stackone_adk-0.2.0-py3-none-any.whl (12.5 kB view details)

Uploaded Python 3

File details

Details for the file stackone_adk-0.2.0.tar.gz.

File metadata

  • Download URL: stackone_adk-0.2.0.tar.gz
  • Upload date:
  • Size: 268.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for stackone_adk-0.2.0.tar.gz
Algorithm Hash digest
SHA256 948696b98bdfa818e8efd8e80e688e0b3e0ef651be241177d8f1eecf3144194c
MD5 b244d146b3a810f651503b1bcf68b7ca
BLAKE2b-256 42e00f2823326dd930db5c4ade5d492276a6755cf40c85b345c31ff0508a3398

See more details on using hashes here.

File details

Details for the file stackone_adk-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: stackone_adk-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 12.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for stackone_adk-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 341c185549eb921b1bc7d31d777a63a35f251dd99329b287f299ddbd30079e33
MD5 8186fe2e82e81c7711961ab295dfc226
BLAKE2b-256 a511164e4caaad05194393b8642086288cbed5e45ab9b9c1e6c7b7520e43aa55

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page