{"slug": "stackone-is-now-a-pydantic-ai-capability", "title": "StackOne is now a Pydantic AI capability", "summary": "StackOne, the integration gateway for AI agents, is now a capability in Pydantic AI Harness, allowing agents to access actions on linked accounts such as Workday, BambooHR, Salesforce, and Zendesk with a single line of code. The capability, added via the capabilities array, leverages StackOne's Search & Execute to avoid serializing its catalog of thousands of actions into prompts. Developers can get started by installing the harness with the stackone extra and configuring a StackOne connector, linked account, and API key.", "body_md": "[StackOne](https://www.stackone.com/?utm_source=pydantic&utm_medium=referral&utm_campaign=pydantic-harness-launch&utm_content=pydantic-homepage), the integration gateway for AI agents, is now a capability in [Pydantic AI Harness](https://pydantic.dev/docs/ai/harness/overview/). Add StackOne(account_id=...) to an agent's capabilities list and it can work with the actions on a linked account: Workday, BambooHR, Salesforce, Zendesk, and the rest of the StackOne connector catalog.\n\nThis post covers the problem this new capability solves, how it works, and what to configure before an agent starts writing to a system of record.\n\nThe AI integration problem with SaaS tools\n\nAn agent that answers questions about your company's data needs to reach the systems that hold it. In practice, there are two usual ways to do that, and each has a cost.\n\nThe first is to hand-write a tool per endpoint: a `list_employees`\n\nwrapper here, a `create_ticket`\n\nwrapper there, each with its own auth handling, its own pagination quirks, and its own schema to keep in sync when the vendor changes something. Do that across three or four providers and the integration code outgrows the agent.\nThe second is the obvious shortcut, and it backfires too: dump every action you might need into the tool list and you spend your context window on schemas the model will never call, while tool selection gets worse as the list grows.\n\nStackOne handles both. It is one gateway in front of hundreds of SaaS systems, with thousands of executable actions behind it, and [Search & Execute](https://docs.stackone.com/optimize/search-and-execute?utm_source=pydantic&utm_medium=referral&utm_campaign=pydantic-harness-launch&utm_content=pydantic-docs-search-execute) running on the gateway so a catalog that size never has to be serialized into a prompt. The capability in Pydantic AI Harness is the front door to it from an agent, and it is one line.\n\nWhat a capability is\n\nPydantic AI Harness is the official capability library for Pydantic AI. A capability is a self-contained battery: tools, hooks, instructions, and settings bundled together, added to an agent through the `capabilities=[...]`\n\narray, and composable with the other capabilities in the library. `StackOne`\n\nis one of those, alongside code execution, memory, planning, and guardrails.\n\nAn agent can work across as many accounts as it needs. Each StackOne instance is scoped to a StackOne [linked account](https://docs.stackone.com/gateway/concepts/linked-accounts?utm_source=pydantic&utm_medium=referral&utm_campaign=pydantic-harness-launch&utm_content=pydantic-docs-linked-accounts), meaning one end user's authenticated connection to one underlying system, whether that is their Workday, their Salesforce, or their Zendesk.\n\nGetting started\n\nInstall the harness with the `stackone`\n\nextra, plus the model provider you want. The `spec`\n\nextra covers the YAML agent example further down and logfire covers the tracing calls in every snippet:\n\n```\nuv add \"pydantic-ai-harness[stackone]\" \"pydantic-ai-slim[openai,spec,logfire]\"\n```\n\nBefore the first run you need to configure a connector in StackOne, link an account, copy the linked account ID from the dashboard, and create an API key that can execute actions. For a first test, enable only the read actions you need.\n\n```\nexport STACKONE_API_KEY='your-stackone-api-key'\nexport STACKONE_ACCOUNT_ID='your-linked-account-id'\nexport OPENAI_API_KEY='your-openai-api-key'\n```\n\n`StackOne`\n\nreads `STACKONE_API_KEY`\n\non its own. The account ID is read explicitly in the example below so it stays out of the source:\n\n``` python\nimport os\n\nimport logfire\nfrom pydantic_ai import Agent\nfrom pydantic_ai_harness.stackone import StackOne\n\nlogfire.configure()\nlogfire.instrument_pydantic_ai()\n\nagent = Agent(\n    'openai:gpt-5',\n    capabilities=[\n        StackOne(account_id=os.environ['STACKONE_ACCOUNT_ID']),\n    ],\n)\nresult = agent.run_sync('List the first 5 employees')\nprint(result.output)\n```\n\nThat is the whole setup. The model receives two tools by default: one to search for an action matching the request, one to execute the action it found by ID.\n\nTwo ways to expose actions\n\nThe search-then-execute pair is the interesting design decision, so it is worth understanding both modes before you pick one.\n\n| Mode | What the model receives | Use it when |\n|---|---|---|\n`search_execute` |\nTwo tools: search for an action, then execute it by ID | The account has many enabled actions. This is the default. |\n`individual` |\nOne tool and schema per enabled action | You need exact action selection or per-tool behavior. |\n\n`search_execute`\n\nkeeps the context cost flat no matter how many actions the account has enabled, because the catalog is queried at runtime instead of being serialized into the prompt. Action IDs come back from the search tool and should not be guessed.\n\n`individual`\n\nmode sends every selected schema to the model, which is what you want when the set is small and you care about exactly which actions are reachable. Filter it with `actions`\n\n, using [ fnmatch](https://docs.python.org/3/library/fnmatch.html) patterns that ignore case and match the full\n\n`{connector}_{action}_{entity}`\n\ntool name:\n\n``` python\nfrom pydantic_ai_harness.stackone import StackOne\n\nStackOne(account_id='your-linked-account-id', actions=['*_list_*'])            # All matching list tools\nStackOne(account_id='your-linked-account-id', actions=['workday_get_worker'])  # One exact tool\n```\n\nPassing `actions`\n\nselects `individual`\n\nmode for you. Combining it with an explicit `tool_mode='search_execute'`\n\nraises an error, because that mode only ever registers the search and execute tools.\n\nOne thing to be clear about: `actions`\n\nis a context-management tool, not an access control. StackOne controls which actions are enabled for the linked account, and that configuration is the real boundary. Treat the pattern list as a way to shape what the model sees, and the StackOne dashboard as the place where permissions live.\n\nIf you want the tools kept out of context entirely until the agent needs them, defer the load:\n\n``` python\nfrom pydantic_ai_harness.stackone import StackOne\n\nStackOne(account_id='your-linked-account-id', defer_loading=True)\n```\n\nThe capability uses `id='stackone'`\n\nby default so it can be loaded on demand. Give each instance a distinct `id`\n\nwhen one agent manages more than one linked account.\n\nBefore you let it write\n\nTwo settings matter as soon as the agent does more than read.\n\nProvider actions can return large exports, and a full employee list will happily eat a context window. `ToolOutputLimits`\n\nbounds oversized returns agent-wide, and composes with `StackOne`\n\nlike any other capability:\n\n``` python\nimport logfire\nfrom pydantic_ai import Agent\nfrom pydantic_ai_harness.stackone import StackOne\nfrom pydantic_ai_harness.tool_output_limits import ToolOutputLimits\n\nlogfire.configure()\nlogfire.instrument_pydantic_ai()\n\nagent = Agent(\n    'openai:gpt-5',\n    capabilities=[\n        StackOne(account_id='your-linked-account-id'),\n        ToolOutputLimits(),\n    ],\n)\n```\n\nApproval is not enabled automatically, and it should be your default for anything that mutates a system of record. Use the public `StackOneToolset`\n\nwith Pydantic AI's [tool approval](https://pydantic.dev/docs/ai/tools-toolsets/toolsets/#requiring-tool-approval):\n\n``` python\nimport os\n\nimport logfire\nfrom pydantic_ai import Agent\nfrom pydantic_ai_harness.stackone import StackOneToolset\n\nlogfire.configure()\nlogfire.instrument_pydantic_ai()\n\nstackone_tools = StackOneToolset(\n    account_id=os.environ['STACKONE_ACCOUNT_ID'],\n    actions=['workday_create_employee'],\n).approval_required()\n\nagent = Agent('openai:gpt-5', toolsets=[stackone_tools])\n```\n\nThat returns deferred approval requests for your application to resolve, so a human sits between the model and the write. `StackOneToolset`\n\nis the lower-level entry point in general: reach for it when you need `Agent(toolsets=[...])`\n\nor another toolset wrapper rather than the capability's defaults.\n\nDefining it in YAML\n\nThe capability works with Pydantic AI's [agent spec](https://pydantic.dev/docs/ai/core-concepts/agent-spec/) format, so the configuration can live outside the code. Keep the key in `STACKONE_API_KEY`\n\nrather than in the file:\n\n```\n# agent.yaml\nmodel: openai:gpt-5\ncapabilities:\n    - StackOne:\n          account_id: 'your-linked-account-id'\n          actions: ['*_list_*']\npython\nimport logfire\nfrom pydantic_ai import Agent\nfrom pydantic_ai_harness.stackone import StackOne\n\nlogfire.configure()\nlogfire.instrument_pydantic_ai()\n\nagent = Agent.from_file('agent.yaml', custom_capability_types=[StackOne])\n```\n\nPass `custom_capability_types`\n\nso the spec loader knows how to instantiate `StackOne`\n\n.\n\nWhy this pairing works\n\nPydantic AI brings the parts that make an agent debuggable: typed tool definitions, validated outputs, and tracing through [Pydantic Logfire](https://pydantic.dev/logfire). StackOne brings the connector surface, so the agent's business logic is not buried in HTTP plumbing per vendor.\n\nThe combination matters most in the failure cases. When an agent picks the wrong action, or a provider returns a payload shaped differently than last week, a trace shows you which action ID was searched, what was executed, and what came back.\n\nA few caveats worth reading before you ship:\n\n- Harness uses 0.x versioning, so the API may change between releases. Breaking changes ship with a deprecation warning where that is practical.\n- Custom\n`base_url`\n\nand URL-valued`client`\n\nvalues must use HTTPS. - For URL values, the toolset appends the\n`tool-mode`\n\nquery parameter when it is absent for the search_execute path. It raises an error when a URL's`tool-mode`\n\nconflicts with the configured mode, because rewriting it would invalidate a signed URL. If you use`search_execute`\n\nwith a signed URL, include`tool-mode=search_execute`\n\nbefore signing. - Prebuilt clients are used as-is, so configure their transport, auth, account selection, and tool mode yourself.\n\nTry it\n\nLink an account in [StackOne](https://docs.stackone.com/guides/introduction?utm_source=pydantic&utm_medium=referral&utm_campaign=pydantic-harness-launch&utm_content=pydantic-docs-get-started), export the two environment variables, and add `StackOne(account_id=...)`\n\nto an agent's capabilities. Then:\n\n- The\n[StackOne capability docs](https://pydantic.dev/docs/ai/harness/stackone/)carry the full API reference for`StackOne`\n\nand`StackOneToolset`\n\n. - The\n[Harness overview](https://pydantic.dev/docs/ai/harness/overview/)lists the other capabilities you can compose with it, including memory, planning, and guardrails. - All examples call\n`logfire.configure()`\n\n. That is[Pydantic Logfire](https://pydantic.dev/logfire), and it turns each run into a trace you can open: tool searches, action calls, retries, and token costs, queryable with SQL. Try[Logfire](/logfire)'s[MCP Server](https://pydantic.dev/docs/logfire/guides/mcp-server/)for debugging. - The\n[Pydantic AI integration docs](https://pydantic.dev/docs/logfire/integrations/llms/pydanticai/)cover the setup for other parts of your application. [Pydantic Evals](https://pydantic.dev/docs/ai/evals/evals/)is what you want once the agent is choosing between actions on its own, so a prompt change that improves one workflow does not quietly break another.\n\nTogether, these are pieces of [the Pydantic Stack](https://pydantic.dev/).", "url": "https://wpnews.pro/news/stackone-is-now-a-pydantic-ai-capability", "canonical_source": "https://pydantic.dev/articles/stackone-pydantic-ai-harness", "published_at": "2026-08-13 09:00:00+00:00", "updated_at": "2026-08-13 20:08:18.110310+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "developer-tools"], "entities": ["StackOne", "Pydantic AI Harness", "Pydantic AI", "Workday", "BambooHR", "Salesforce", "Zendesk"], "alternates": {"html": "https://wpnews.pro/news/stackone-is-now-a-pydantic-ai-capability", "markdown": "https://wpnews.pro/news/stackone-is-now-a-pydantic-ai-capability.md", "text": "https://wpnews.pro/news/stackone-is-now-a-pydantic-ai-capability.txt", "jsonld": "https://wpnews.pro/news/stackone-is-now-a-pydantic-ai-capability.jsonld"}}