StackOne
Use StackOne when an agent needs to work with one of a user’s linked business applications, such as BambooHR,
Salesforce, or Zendesk. Each instance is scoped to one linked account, which is one authenticated connection between
StackOne and a provider.
The API may change between releases. Where practical, breaking changes ship with a deprecation warning.
Follow the StackOne docs to:
- Configure a connector and link an account. For your first test, enable only the read actions you need.
- Copy the linked account ID from the StackOne dashboard.
- Create a StackOne API key that can execute actions.
You also need an API key for the model your agent uses.
uv add "pydantic-ai-harness[stackone]" "pydantic-ai-slim[openai,spec]"
The openai and spec extras support the model and agent-spec examples below. Install the provider extra for a
different model provider instead.
Set the credentials in the shell where you will run the example:
export STACKONE_API_KEY='your-stackone-api-key'
export STACKONE_ACCOUNT_ID='your-linked-account-id'
export OPENAI_API_KEY='your-openai-api-key'
StackOne reads STACKONE_API_KEY automatically. The example reads STACKONE_ACCOUNT_ID explicitly so the account
ID is not hard-coded. You can pass api_key= directly instead, but keep secrets out of source control.
import os
from pydantic_ai import Agent
from pydantic_ai_harness.stackone import StackOne
agent = Agent(
'openai:gpt-5',
capabilities=[
StackOne(account_id=os.environ['STACKONE_ACCOUNT_ID']),
],
)
result = agent.run_sync('List the first 5 employees')
print(result.output)
By default, the model receives two tools. It searches for an action that matches the request, then executes the returned action ID. The final output depends on the linked provider and its data.
StackOne controls which actions are enabled for the linked account. Treat that configuration as the primary access control.
Use actions when you also want to limit which tools the model sees. Patterns use Python
fnmatch syntax, where * is a wildcard. They ignore case and match
the full {connector}_{action}_{entity} tool name:
from pydantic_ai_harness.stackone import StackOne
StackOne(account_id='your-linked-account-id', actions=['*_list_*']) # All matching list tools
StackOne(account_id='your-linked-account-id', actions=['workday_get_worker']) # One exact tool
Passing actions selects individual mode automatically. Explicitly combining actions with
tool_mode='search_execute' raises an error because that mode registers only the search and execute tools.
| Mode | What the model receives | Use it when |
|---|---|---|
search_execute | Two tools: search for an action, then execute it by ID | The account has many enabled actions. This is the default when actions is omitted. |
individual | One tool and schema per enabled action | You need to select exact actions or add per-tool behavior. Passing actions selects this mode. |
In search_execute mode, action IDs are returned by the search tool at runtime and should not be guessed. In
individual mode, all selected tool schemas are sent to the model, so filter large action sets with actions.
To keep StackOne tools out of the model context until they are needed, pass defer_loading=True. The capability uses
id='stackone' by default so it can be loaded on demand. Give each instance a distinct id when one agent uses
multiple StackOne accounts:
from pydantic_ai_harness.stackone import StackOne
StackOne(account_id='your-linked-account-id', defer_loading=True)
Provider actions can return large exports. Combine StackOne with the Tool Output Limits capability to reduce oversized tool returns agent-wide:
from pydantic_ai import Agent
from pydantic_ai_harness.stackone import StackOne
from pydantic_ai_harness.tool_output_limits import ToolOutputLimits
agent = Agent(
'openai:gpt-5',
capabilities=[
StackOne(account_id='your-linked-account-id'),
ToolOutputLimits(),
],
)
Approval is not enabled automatically. For operations that need human confirmation, use the public
StackOneToolset with Pydantic AI’s tool approval:
import os
from pydantic_ai import Agent
from pydantic_ai_harness.stackone import StackOneToolset
stackone_tools = StackOneToolset(
account_id=os.environ['STACKONE_ACCOUNT_ID'],
actions=['workday_create_worker'],
).approval_required()
agent = Agent('openai:gpt-5', toolsets=[stackone_tools])
Handle the resulting deferred approval requests as described in the linked guide.
The capability also works with Pydantic AI’s agent spec format for YAML or JSON.
Keep the API key in STACKONE_API_KEY rather than storing it in the file:
# agent.yaml
model: openai:gpt-5
capabilities:
- StackOne:
account_id: 'your-linked-account-id'
actions: ['*_list_*']
from pydantic_ai import Agent
from pydantic_ai_harness.stackone import StackOne
agent = Agent.from_file('agent.yaml', custom_capability_types=[StackOne])
Pass custom_capability_types so the spec loader knows how to instantiate StackOne.
Use the lower-level StackOneToolset directly when you need
Agent(toolsets=[...]) or other toolset wrappers.
Custom base_url and URL-valued client values must use HTTPS. The toolset adds auth headers and appends the
tool-mode query parameter for URL values when it is absent. It raises an error when the URL’s tool-mode conflicts
with the configured mode because rewriting would invalidate signed URLs. When using search_execute with a signed
URL, include tool-mode=search_execute before signing. Prebuilt clients are used as-is; configure their HTTPS
transport, auth, account selection, and tool mode yourself.
Bases: AbstractCapability[AgentDepsT]
Actions on the user’s SaaS account (HRIS, ATS, CRM, and more) via StackOne.
Connects an agent to one linked account’s actions over StackOne’s MCP endpoint, with authentication, tool filtering, and usage instructions.
The linked account to act on (one account is one provider connection).
Type: str
Stable capability and toolset ID. Override it when one agent uses several StackOne accounts.
Type: str | None Default: _DEFAULT_ID
Routing description used when the capability is loaded on demand.
Type: str | None Default: _DEFAULT_DESCRIPTION
StackOne API key. Defaults to the STACKONE_API_KEY environment variable.
Type: str | None Default: field(default=None, repr=False)
HTTPS StackOne API host. Point at a regional or staging host if needed.
Type: str Default: STACKONE_BASE_URL
fnmatch globs over full tool names (case-insensitive), e.g. ['*_list_*'].
Giving actions switches the default tool_mode to individual, where the globs apply.
Type: str | Sequence[str] Default: ()
individual registers one tool per enabled action; search_execute registers two
server-side meta-tools (search the catalog, execute an action by id) whose prompt
footprint stays constant however large the catalog is. None picks search_execute,
or individual when actions are given.
Type: ToolMode | None Default: None
Inject StackOne usage instructions into the system prompt.
Type: bool Default: True
Metadata merged onto every tool, available to tool-selection machinery such as
CodeMode(tools={'code_mode': True}) or custom prepare_tools hooks.
Type: Mapping[str, object] | None Default: None
Replacement for the default {base_url}/mcp connection. URL values must use HTTPS;
prebuilt clients keep their own transport, auth, and account selection, so account_id
is not applied to them.
Type: MCPToolsetClient | None Default: field(default=None, repr=False)
def get_toolset() -> StackOneToolset[AgentDepsT]
Build the StackOne toolset.
StackOneToolset[AgentDepsT]
def get_instructions() -> str | None
StackOne usage guidance; the underlying MCP toolset provides none itself.
@classmethod
def from_spec(
cls,
account_id: str,
*,
id: str | None = _DEFAULT_ID,
description: str | None = _DEFAULT_DESCRIPTION,
defer_loading: bool = False,
api_key: str | None = None,
base_url: str = STACKONE_BASE_URL,
actions: str | Sequence[str] = (),
tool_mode: ToolMode | None = None,
include_instructions: bool = True,
metadata: Mapping[str, object] | None = None,
) -> StackOne[AgentDepsT]
Construct from serializable options, excluding the runtime-only client.
StackOne[AgentDepsT]
@classmethod
def get_serialization_name(cls) -> str
Return the agent-spec capability name.
Bases: MCPToolset[AgentDepsT]
StackOne actions on one linked SaaS account, as an agent toolset.
An MCPToolset connected to StackOne’s MCP endpoint. URL clients receive
StackOne auth and account headers. Action filtering and metadata merging
apply to the tools listed by the server. Prebuilt clients keep their own
transport configuration. Instances support the full MCPToolset surface.
Use the StackOne capability for usage instructions and agent-spec support.
Use this class for toolset combinators such as approval_required().
def __init__(
*,
account_id: str,
api_key: str | None = None,
base_url: str = STACKONE_BASE_URL,
actions: Sequence[str] = (),
tool_mode: ToolMode | None = None,
metadata: Mapping[str, object] | None = None,
client: MCPToolsetClient | None = None,
id: str = 'stackone',
) -> None
Build a StackOne MCP toolset.
account_id : str
Linked account used for StackOne requests.
API key, or STACKONE_API_KEY when omitted.
base_url : str Default: STACKONE_BASE_URL
HTTPS StackOne API host.
Case-insensitive globs over individual action tool names. Selects
individual; incompatible with explicit search_execute.
tool_mode : ToolMode | None Default: None
Individual tools or the search/execute pair. Inferred when omitted.
Metadata merged onto each tool definition.
client : MCPToolsetClient | None Default: None
URL, FastMCP, or prebuilt client accepted by MCPToolset.
Non-URL clients keep their own transport, auth, and account selection,
so account_id is not applied to them.
id : str Default: 'stackone'
Toolset ID; use distinct values for multiple accounts.