stackone-ai 2.10.1__tar.gz → 3.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- stackone_ai-3.0.0/.gitignore +23 -0
- {stackone_ai-2.10.1 → stackone_ai-3.0.0}/CHANGELOG.md +102 -0
- stackone_ai-3.0.0/MIGRATION.md +444 -0
- stackone_ai-3.0.0/PKG-INFO +463 -0
- stackone_ai-3.0.0/README.md +425 -0
- {stackone_ai-2.10.1 → stackone_ai-3.0.0}/pyproject.toml +20 -35
- stackone_ai-3.0.0/stackone_ai/__init__.py +40 -0
- stackone_ai-3.0.0/stackone_ai/tools.py +1765 -0
- stackone_ai-3.0.0/stackone_ai/toolset.py +1246 -0
- stackone_ai-3.0.0/stackone_ai/types.py +181 -0
- stackone_ai-3.0.0/tests/vectors/README.md +144 -0
- stackone_ai-2.10.1/.claude/rules/development-workflow.md +0 -63
- stackone_ai-2.10.1/.claude/rules/examples-standards.md +0 -75
- stackone_ai-2.10.1/.claude/rules/git-workflow.md +0 -97
- stackone_ai-2.10.1/.claude/rules/nix-workflow.md +0 -84
- stackone_ai-2.10.1/.claude/rules/no-relative-imports.md +0 -27
- stackone_ai-2.10.1/.claude/rules/package-installation.md +0 -59
- stackone_ai-2.10.1/.claude/rules/release-please-standards.md +0 -63
- stackone_ai-2.10.1/.claude/rules/uv-scripts.md +0 -66
- stackone_ai-2.10.1/.cursor/rules/development-workflow.mdc +0 -1
- stackone_ai-2.10.1/.cursor/rules/examples-standards.mdc +0 -1
- stackone_ai-2.10.1/.cursor/rules/git-workflow.mdc +0 -1
- stackone_ai-2.10.1/.cursor/rules/no-relative-imports.mdc +0 -1
- stackone_ai-2.10.1/.cursor/rules/package-installation.mdc +0 -1
- stackone_ai-2.10.1/.cursor/rules/release-please-standards.mdc +0 -1
- stackone_ai-2.10.1/.cursor/rules/uv-scripts.mdc +0 -1
- stackone_ai-2.10.1/.env.example +0 -6
- stackone_ai-2.10.1/.envrc +0 -2
- stackone_ai-2.10.1/.github/actions/setup-nix/action.yaml +0 -50
- stackone_ai-2.10.1/.github/dependabot.yaml +0 -46
- stackone_ai-2.10.1/.github/workflows/ci.yaml +0 -100
- stackone_ai-2.10.1/.github/workflows/nix-flake-update.yaml +0 -63
- stackone_ai-2.10.1/.github/workflows/nix-flake.yaml +0 -35
- stackone_ai-2.10.1/.github/workflows/release.yaml +0 -66
- stackone_ai-2.10.1/.gitignore +0 -24
- stackone_ai-2.10.1/.gitleaks.toml +0 -9
- stackone_ai-2.10.1/.gitmodules +0 -3
- stackone_ai-2.10.1/.mcp.json +0 -12
- stackone_ai-2.10.1/.release-please-config.json +0 -15
- stackone_ai-2.10.1/.release-please-manifest.json +0 -3
- stackone_ai-2.10.1/CLAUDE.md +0 -106
- stackone_ai-2.10.1/PKG-INFO +0 -541
- stackone_ai-2.10.1/README.md +0 -506
- stackone_ai-2.10.1/examples/auth_management.py +0 -103
- stackone_ai-2.10.1/examples/crewai_integration.py +0 -69
- stackone_ai-2.10.1/examples/langchain_integration.py +0 -69
- stackone_ai-2.10.1/examples/langgraph_integration.py +0 -62
- stackone_ai-2.10.1/examples/openai_integration.py +0 -113
- stackone_ai-2.10.1/examples/pydantic_ai_integration.py +0 -53
- stackone_ai-2.10.1/examples/search_tools.py +0 -171
- stackone_ai-2.10.1/examples/test_examples.py +0 -61
- stackone_ai-2.10.1/flake.lock +0 -230
- stackone_ai-2.10.1/flake.nix +0 -153
- stackone_ai-2.10.1/justfile +0 -47
- stackone_ai-2.10.1/scripts/benchmark_search.py +0 -134
- stackone_ai-2.10.1/stackone_ai/__init__.py +0 -26
- stackone_ai-2.10.1/stackone_ai/constants.py +0 -8
- stackone_ai-2.10.1/stackone_ai/feedback/__init__.py +0 -5
- stackone_ai-2.10.1/stackone_ai/feedback/tool.py +0 -226
- stackone_ai-2.10.1/stackone_ai/integrations/__init__.py +0 -20
- stackone_ai-2.10.1/stackone_ai/integrations/langgraph.py +0 -86
- stackone_ai-2.10.1/stackone_ai/local_search.py +0 -170
- stackone_ai-2.10.1/stackone_ai/models.py +0 -685
- stackone_ai-2.10.1/stackone_ai/semantic_search.py +0 -205
- stackone_ai-2.10.1/stackone_ai/toolset.py +0 -1341
- stackone_ai-2.10.1/stackone_ai/utils/__init__.py +0 -1
- stackone_ai-2.10.1/stackone_ai/utils/normalize.py +0 -17
- stackone_ai-2.10.1/stackone_ai/utils/tfidf_index.py +0 -242
- stackone_ai-2.10.1/tests/conftest.py +0 -113
- stackone_ai-2.10.1/tests/mocks/serve.ts +0 -134
- stackone_ai-2.10.1/tests/test_agent_tools.py +0 -541
- stackone_ai-2.10.1/tests/test_feedback.py +0 -397
- stackone_ai-2.10.1/tests/test_fetch_tools.py +0 -682
- stackone_ai-2.10.1/tests/test_integrations_langgraph.py +0 -217
- stackone_ai-2.10.1/tests/test_integrations_pydantic_ai.py +0 -183
- stackone_ai-2.10.1/tests/test_local_search.py +0 -254
- stackone_ai-2.10.1/tests/test_models.py +0 -962
- stackone_ai-2.10.1/tests/test_semantic_search.py +0 -1039
- stackone_ai-2.10.1/tests/test_tfidf_index.py +0 -452
- stackone_ai-2.10.1/tests/test_tool_calling.py +0 -536
- stackone_ai-2.10.1/tests/test_toolset.py +0 -356
- stackone_ai-2.10.1/uv.lock +0 -5881
- stackone_ai-2.10.1/vendor/stackone-ai-node/.claude/rules/development-workflow.md +0 -29
- stackone_ai-2.10.1/vendor/stackone-ai-node/.claude/rules/file-operations.md +0 -134
- stackone_ai-2.10.1/vendor/stackone-ai-node/.claude/rules/git-workflow.md +0 -97
- stackone_ai-2.10.1/vendor/stackone-ai-node/.claude/rules/pnpm-usage.md +0 -54
- stackone_ai-2.10.1/vendor/stackone-ai-node/.claude/rules/typescript-patterns.md +0 -246
- stackone_ai-2.10.1/vendor/stackone-ai-node/.claude/rules/typescript-testing.md +0 -132
- stackone_ai-2.10.1/vendor/stackone-ai-node/.claude/skills/orama-integration/SKILL.md +0 -25
- stackone_ai-2.10.1/vendor/stackone-ai-node/.cursor/rules/development-workflow.mdc +0 -1
- stackone_ai-2.10.1/vendor/stackone-ai-node/.cursor/rules/file-operations.mdc +0 -1
- stackone_ai-2.10.1/vendor/stackone-ai-node/.cursor/rules/git-workflow.mdc +0 -1
- stackone_ai-2.10.1/vendor/stackone-ai-node/.cursor/rules/orama-integration.mdc +0 -1
- stackone_ai-2.10.1/vendor/stackone-ai-node/.cursor/rules/pnpm-usage.mdc +0 -1
- stackone_ai-2.10.1/vendor/stackone-ai-node/.cursor/rules/typescript-patterns.mdc +0 -1
- stackone_ai-2.10.1/vendor/stackone-ai-node/.cursor/rules/typescript-testing.mdc +0 -1
- stackone_ai-2.10.1/vendor/stackone-ai-node/.envrc +0 -1
- stackone_ai-2.10.1/vendor/stackone-ai-node/.github/CODEOWNERS +0 -6
- stackone_ai-2.10.1/vendor/stackone-ai-node/.github/actions/setup-nix/action.yaml +0 -19
- stackone_ai-2.10.1/vendor/stackone-ai-node/.github/workflows/check-title.yaml +0 -25
- stackone_ai-2.10.1/vendor/stackone-ai-node/.github/workflows/ci.yaml +0 -87
- stackone_ai-2.10.1/vendor/stackone-ai-node/.github/workflows/claude.yaml +0 -39
- stackone_ai-2.10.1/vendor/stackone-ai-node/.github/workflows/dry-publish.yaml +0 -17
- stackone_ai-2.10.1/vendor/stackone-ai-node/.github/workflows/nix-flake.yaml +0 -33
- stackone_ai-2.10.1/vendor/stackone-ai-node/.github/workflows/release.yaml +0 -52
- stackone_ai-2.10.1/vendor/stackone-ai-node/.gitignore +0 -183
- stackone_ai-2.10.1/vendor/stackone-ai-node/.gitleaks.toml +0 -11
- stackone_ai-2.10.1/vendor/stackone-ai-node/.mcp.json +0 -12
- stackone_ai-2.10.1/vendor/stackone-ai-node/.oxfmtrc.jsonc +0 -7
- stackone_ai-2.10.1/vendor/stackone-ai-node/.oxlintrc.jsonc +0 -179
- stackone_ai-2.10.1/vendor/stackone-ai-node/AGENTS.md +0 -1
- stackone_ai-2.10.1/vendor/stackone-ai-node/CHANGELOG.md +0 -373
- stackone_ai-2.10.1/vendor/stackone-ai-node/CLAUDE.md +0 -26
- stackone_ai-2.10.1/vendor/stackone-ai-node/LICENSE +0 -201
- stackone_ai-2.10.1/vendor/stackone-ai-node/README.md +0 -614
- stackone_ai-2.10.1/vendor/stackone-ai-node/examples/README.md +0 -295
- stackone_ai-2.10.1/vendor/stackone-ai-node/examples/ai-sdk-integration.test.ts +0 -52
- stackone_ai-2.10.1/vendor/stackone-ai-node/examples/ai-sdk-integration.ts +0 -52
- stackone_ai-2.10.1/vendor/stackone-ai-node/examples/anthropic-integration.ts +0 -63
- stackone_ai-2.10.1/vendor/stackone-ai-node/examples/claude-agent-sdk-integration.test.ts +0 -140
- stackone_ai-2.10.1/vendor/stackone-ai-node/examples/claude-agent-sdk-integration.ts +0 -88
- stackone_ai-2.10.1/vendor/stackone-ai-node/examples/fetch-tools-debug.ts +0 -297
- stackone_ai-2.10.1/vendor/stackone-ai-node/examples/fetch-tools.test.ts +0 -102
- stackone_ai-2.10.1/vendor/stackone-ai-node/examples/fetch-tools.ts +0 -82
- stackone_ai-2.10.1/vendor/stackone-ai-node/examples/meta-tools.ts +0 -280
- stackone_ai-2.10.1/vendor/stackone-ai-node/examples/openai-integration.test.ts +0 -72
- stackone_ai-2.10.1/vendor/stackone-ai-node/examples/openai-integration.ts +0 -69
- stackone_ai-2.10.1/vendor/stackone-ai-node/examples/openai-responses-integration.test.ts +0 -64
- stackone_ai-2.10.1/vendor/stackone-ai-node/examples/openai-responses-integration.ts +0 -63
- stackone_ai-2.10.1/vendor/stackone-ai-node/examples/package.json +0 -30
- stackone_ai-2.10.1/vendor/stackone-ai-node/examples/tanstack-ai-integration.test.ts +0 -77
- stackone_ai-2.10.1/vendor/stackone-ai-node/examples/tanstack-ai-integration.ts +0 -83
- stackone_ai-2.10.1/vendor/stackone-ai-node/examples/tsconfig.json +0 -16
- stackone_ai-2.10.1/vendor/stackone-ai-node/flake.lock +0 -61
- stackone_ai-2.10.1/vendor/stackone-ai-node/flake.nix +0 -49
- stackone_ai-2.10.1/vendor/stackone-ai-node/knip.config.ts +0 -21
- stackone_ai-2.10.1/vendor/stackone-ai-node/lefthook.yaml +0 -22
- stackone_ai-2.10.1/vendor/stackone-ai-node/mocks/handlers.example-api.ts +0 -59
- stackone_ai-2.10.1/vendor/stackone-ai-node/mocks/handlers.mcp.ts +0 -35
- stackone_ai-2.10.1/vendor/stackone-ai-node/mocks/handlers.openai.ts +0 -214
- stackone_ai-2.10.1/vendor/stackone-ai-node/mocks/handlers.stackone-ai.ts +0 -97
- stackone_ai-2.10.1/vendor/stackone-ai-node/mocks/handlers.stackone-rpc.ts +0 -85
- stackone_ai-2.10.1/vendor/stackone-ai-node/mocks/handlers.ts +0 -13
- stackone_ai-2.10.1/vendor/stackone-ai-node/mocks/handlers.utils.ts +0 -30
- stackone_ai-2.10.1/vendor/stackone-ai-node/mocks/mcp-server.ts +0 -275
- stackone_ai-2.10.1/vendor/stackone-ai-node/mocks/node.ts +0 -4
- stackone_ai-2.10.1/vendor/stackone-ai-node/package.json +0 -118
- stackone_ai-2.10.1/vendor/stackone-ai-node/pnpm-lock.yaml +0 -4506
- stackone_ai-2.10.1/vendor/stackone-ai-node/pnpm-workspace.yaml +0 -66
- stackone_ai-2.10.1/vendor/stackone-ai-node/src/consts.ts +0 -20
- stackone_ai-2.10.1/vendor/stackone-ai-node/src/feedback.test.ts +0 -300
- stackone_ai-2.10.1/vendor/stackone-ai-node/src/feedback.ts +0 -230
- stackone_ai-2.10.1/vendor/stackone-ai-node/src/headers.test.ts +0 -69
- stackone_ai-2.10.1/vendor/stackone-ai-node/src/headers.ts +0 -46
- stackone_ai-2.10.1/vendor/stackone-ai-node/src/index.ts +0 -29
- stackone_ai-2.10.1/vendor/stackone-ai-node/src/mcp-client.test.ts +0 -68
- stackone_ai-2.10.1/vendor/stackone-ai-node/src/mcp-client.ts +0 -55
- stackone_ai-2.10.1/vendor/stackone-ai-node/src/requestBuilder.test.ts +0 -778
- stackone_ai-2.10.1/vendor/stackone-ai-node/src/requestBuilder.ts +0 -377
- stackone_ai-2.10.1/vendor/stackone-ai-node/src/rpc-client.test.ts +0 -122
- stackone_ai-2.10.1/vendor/stackone-ai-node/src/rpc-client.ts +0 -107
- stackone_ai-2.10.1/vendor/stackone-ai-node/src/schema.ts +0 -64
- stackone_ai-2.10.1/vendor/stackone-ai-node/src/tool.test-d.ts +0 -53
- stackone_ai-2.10.1/vendor/stackone-ai-node/src/tool.test.ts +0 -1453
- stackone_ai-2.10.1/vendor/stackone-ai-node/src/tool.ts +0 -743
- stackone_ai-2.10.1/vendor/stackone-ai-node/src/toolsets.test-d.ts +0 -44
- stackone_ai-2.10.1/vendor/stackone-ai-node/src/toolsets.test.ts +0 -712
- stackone_ai-2.10.1/vendor/stackone-ai-node/src/toolsets.ts +0 -558
- stackone_ai-2.10.1/vendor/stackone-ai-node/src/types.ts +0 -203
- stackone_ai-2.10.1/vendor/stackone-ai-node/src/utils/array.test.ts +0 -48
- stackone_ai-2.10.1/vendor/stackone-ai-node/src/utils/array.ts +0 -13
- stackone_ai-2.10.1/vendor/stackone-ai-node/src/utils/error-stackone-api.test.ts +0 -208
- stackone_ai-2.10.1/vendor/stackone-ai-node/src/utils/error-stackone-api.ts +0 -130
- stackone_ai-2.10.1/vendor/stackone-ai-node/src/utils/error-stackone.test.ts +0 -15
- stackone_ai-2.10.1/vendor/stackone-ai-node/src/utils/error-stackone.ts +0 -9
- stackone_ai-2.10.1/vendor/stackone-ai-node/src/utils/tfidf-index.test.ts +0 -206
- stackone_ai-2.10.1/vendor/stackone-ai-node/src/utils/tfidf-index.ts +0 -192
- stackone_ai-2.10.1/vendor/stackone-ai-node/src/utils/try-import.test.ts +0 -23
- stackone_ai-2.10.1/vendor/stackone-ai-node/src/utils/try-import.ts +0 -25
- stackone_ai-2.10.1/vendor/stackone-ai-node/src/utils/type.ts +0 -4
- stackone_ai-2.10.1/vendor/stackone-ai-node/tsconfig.json +0 -28
- stackone_ai-2.10.1/vendor/stackone-ai-node/tsdown.config.ts +0 -20
- stackone_ai-2.10.1/vendor/stackone-ai-node/vitest.config.ts +0 -46
- stackone_ai-2.10.1/vendor/stackone-ai-node/vitest.setup.ts +0 -5
- {stackone_ai-2.10.1 → stackone_ai-3.0.0}/LICENSE +0 -0
- {stackone_ai-2.10.1 → stackone_ai-3.0.0}/stackone_ai/py.typed +0 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
.env
|
|
2
|
+
.env.*
|
|
3
|
+
!.env.example
|
|
4
|
+
.venv
|
|
5
|
+
|
|
6
|
+
.pytest_cache
|
|
7
|
+
.python-version
|
|
8
|
+
__pycache__
|
|
9
|
+
.coverage
|
|
10
|
+
coverage/
|
|
11
|
+
|
|
12
|
+
.DS_Store
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
*.egg-info
|
|
16
|
+
dist/
|
|
17
|
+
build/
|
|
18
|
+
|
|
19
|
+
# Node tooling for the MCP mock server
|
|
20
|
+
node_modules/
|
|
21
|
+
|
|
22
|
+
# Hypothesis test database
|
|
23
|
+
.hypothesis/
|
|
@@ -1,5 +1,107 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [3.0.0](https://github.com/StackOneHQ/stackone-ai-python/compare/stackone-ai-v2.10.1...stackone-ai-v3.0.0) (2026-10-29)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### ⚠ BREAKING CHANGES
|
|
7
|
+
|
|
8
|
+
* one search tool and one execute tool spanning the account scope (ENG-3175) ([#231](https://github.com/StackOneHQ/stackone-ai-python/issues/231))
|
|
9
|
+
* StackOneRpcTool, MCP_PARAM_STYLE, ParameterLocation, validate_method, is_json_content_type and filename_from_content_disposition are removed. Every tool fetch_tools() returns is a StackOneMcpTool that executes over MCP tools/call on the endpoint and account that listed it, with its arguments sent unchanged; nothing posts to /actions/rpc.
|
|
10
|
+
* **tools:** StackOneTool.execute() no longer makes HTTP requests. On the base class it raises StackOneError, so a hand-built tool must override execute().
|
|
11
|
+
* **types:** ExecuteConfig is extra="forbid" and keeps only name, headers and timeout. Passing method, url, body_type or parameter_locations raises a pydantic ValidationError.
|
|
12
|
+
* **toolset:** StackOneToolSet.execute() and tool.execute() return the server's {isError, result, defenderMetadata?, policyMetadata?} object rather than the unwrapped payload or the /actions/rpc body, so result["data"] becomes result["result"]["data"].
|
|
13
|
+
* **tools:** a file action returns {download_url, expires_at, file} as `result` instead of {content, content_type, status_code, headers, file_name}, and raises StackOneAPIError with status 501 when no link can be issued. Tools.execute_openai_tool_calls() no longer base64-encodes bytes.
|
|
14
|
+
* **toolset:** the /mcp URL no longer carries ?param-style=flat_prefixed, so per-action tools from fetch_tools() take whichever argument style the server serves by default instead of flat, prefixed keys such as body_variables.
|
|
15
|
+
* **tools:** StackOneMcpTool(headers=...) is now optional and means extra request headers, not the full header set. The SDK always sets Authorization, x-account-id and User-Agent itself, after them.
|
|
16
|
+
* **tools:** to_openai_function() no longer derives `required` from the per-property nullable markers. It emits the served root `required` list verbatim, in the served order, and omits it when absent or empty, so a hand-built ToolParameters without `required` now has no required fields.
|
|
17
|
+
* **toolset:** fetch_tools(), openai(), langchain() and pydantic_ai() include the stackone_submit_feedback tool, once, whenever the server serves it.
|
|
18
|
+
* **tools:** a top-level `headers_<name>` argument is dropped unless the tool's schema declares that property, and always for Authorization, x-account-id and User-Agent. A declared flat `headers_<name>` property no longer admits the same name inside a nested `headers` object. An open `headers` object, as `*_execute_action` serves it, forwards any other header, so StackOneToolSet.execute(..., {"headers": {...}}) passes host-set headers through where it previously dropped them all. A top-level `headers` argument that isn't a plain object is dropped too, unless the schema declares `headers` itself as a non-object field, in which case it is an ordinary argument sent as given. A declared flat `headers_<name>` argument whose value is a list or dict is dropped as well; only a string, number or boolean can be a header value.
|
|
19
|
+
* **tools:** when more than one account serves a tool name, Tools.get_tool() returns the first one listed (listings merge in sorted account order) rather than the last.
|
|
20
|
+
* Python 3.10 is no longer supported. stackone-ai requires Python 3.11 or later.
|
|
21
|
+
* the `stackone-ai[mcp]` extra no longer exists. mcp is a core dependency, so install plain `stackone-ai`.
|
|
22
|
+
* langchain-core is no longer installed with stackone-ai. StackOneTool.to_langchain(), Tools.to_langchain() and StackOneToolSet.langchain() need the `stackone-ai[langchain]` extra, which requires langchain-core 0.3.36 or later, and raise an ImportError that names it without it.
|
|
23
|
+
* stackone_ai.models, stackone_ai.constants and stackone_ai.utils no longer exist. Import StackOneTool and Tools from stackone_ai.tools; ToolParameters, ExecuteConfig, DEFAULT_BASE_URL and the errors from stackone_ai.types; or any public name from stackone_ai. ToolDefinition and DEFAULT_HYBRID_ALPHA are removed.
|
|
24
|
+
* **toolset:** client-side search is removed: SearchConfig, SearchMode, SearchTool, SemanticSearchClient, SemanticSearchResult, SemanticSearchResponse and SemanticSearchError, the `search=` constructor argument, search_tools(), search_action_names(), get_search_tool(), semantic_client, the tool_search and tool_execute meta tools, and the bm25s and numpy dependencies. StackOneToolSet.search() runs the server's *_search_actions tools instead.
|
|
25
|
+
* stackone_ai.integrations, with to_tool_node(), to_tool_executor(), bind_model_with_tools() and create_react_agent(), is removed. Pass tools.to_langchain() to LangGraph's own ToolNode and bind_tools().
|
|
26
|
+
* **tools:** the tool_feedback tool, stackone_ai.feedback, create_feedback_tool() and the implicit feedback_* execution options are removed, and StackOneTool.execute() and call() no longer take options=. Use StackOneToolSet.submit_feedback() and a search hit's session_id instead.
|
|
27
|
+
* **toolset:** StackOneToolSet.execute() takes (action_id, arguments=None, *, account_ids=None, session_id=None) instead of a meta tool name with arguments as a JSON string or dict. arguments must be a dict in the nested form an action's example_request shows, and a failure raises (ToolsetConfigError, ToolArgumentsError, ToolsetLoadError or StackOneAPIError) instead of returning {"error": ...}.
|
|
28
|
+
* **tools:** to_openai_function(), to_langchain() and to_pydantic_ai_tool() give the model the served JSON Schema instead of only type, description, enum and a shallow copy of items and properties, so format, pattern, default, bounds, oneOf/anyOf and nested required reach it. to_langchain() sets that schema as args_schema instead of a pydantic model built from it.
|
|
29
|
+
* **toolset:** openai(), langchain() and pydantic_ai() no longer take mode=. Set tool_mode="search_execute" on StackOneToolSet to give a model the server's search and execute tools.
|
|
30
|
+
* **toolset:** StackOneToolSet(account_id="") raises ToolsetConfigError instead of being treated as no account, which now lists every active account, so an account id read from an environment variable that is set but empty raises. An empty or non-string entry in account_ids, set_accounts() or execute={"account_ids": [...]} raises too, instead of being sent with no x-account-id.
|
|
31
|
+
* **toolset:** account_ids, set_accounts() and execute={"account_ids": ...} accept only a list of strings. A tuple or any other non-list, which 2.10.1 accepted, raises ToolsetConfigError; pass list(ids) instead.
|
|
32
|
+
* removed StackOneTool.connector property and Tools.get_connectors().
|
|
33
|
+
|
|
34
|
+
### Features
|
|
35
|
+
|
|
36
|
+
* export the feedback types and StackOneMcpTool, and take any sequence of tool names ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
37
|
+
* one search tool and one execute tool spanning the account scope (ENG-3175) ([#231](https://github.com/StackOneHQ/stackone-ai-python/issues/231)) ([691d13a](https://github.com/StackOneHQ/stackone-ai-python/commit/691d13a9c2021bcd6d1645bce7a489d0e705b2fc))
|
|
38
|
+
* retry HTTP 429 on every request, honouring Retry-After, and raise a 429 that outlasts the retries or the timeout instead of skipping the account ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
39
|
+
* **toolset:** add a headers option sent on every request ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
40
|
+
* **toolset:** add action_run_id to submit_feedback(), and send feedback through the lowest account id ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
41
|
+
* **toolset:** link calls with session_id and add submit_feedback() ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
42
|
+
* **toolset:** read STACKONE_BASE_URL ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
43
|
+
* **toolset:** send x-end-user-id for non-shared accounts ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
44
|
+
* **toolset:** skip non-shared accounts during discovery unless include_non_shared ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
45
|
+
* **toolset:** tag each search hit with its account_id and apply top_k to the merged ranking ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
46
|
+
* **toolset:** warn when STACKONE_ACCOUNT_ID is set but ignored ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
47
|
+
* **tools:** raise ToolArgumentsError, a StackOneError and a ValueError, for unusable tool arguments ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
### Bug Fixes
|
|
51
|
+
|
|
52
|
+
* **tools:** build every adapter from the first tool of each name ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
53
|
+
* **tools:** emit the served required list in served order ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
54
|
+
* **toolset:** accept account ids only as a list ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
55
|
+
* **toolset:** cache the healthy accounts when one fails, and retry a failed one after 30 seconds ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
56
|
+
* **toolset:** chain a failed end-user lookup onto the 400 it was looking up for ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
57
|
+
* **toolset:** hold a provider lookup's miss for the failed-account window in execute() ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
58
|
+
* **toolset:** learn a failed account's provider with one GET /accounts before execute() refuses ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
59
|
+
* **toolset:** look up a non-shared account's end user when the API asks for it ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
60
|
+
* **toolset:** move a pinned action_id and session_id to the end of the arguments ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
61
|
+
* **toolset:** name shared accounts in the STACKONE_ACCOUNT_ID warning ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
62
|
+
* **toolset:** raise the shared error when every account fails, and keep each failure ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
63
|
+
* **toolset:** raise when every active account on the key is non-shared and not opted in ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
64
|
+
* **toolset:** re-list only the failed accounts that could serve the action in execute() ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
65
|
+
* **toolset:** refuse an action whose connector is linked on more than one account ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
66
|
+
* **toolset:** refuse to execute while a matching account failed to list ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
67
|
+
* **toolset:** reject an empty or non-string account id ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
68
|
+
* **toolset:** route stackone_submit_feedback over MCP and list it once ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
69
|
+
* **toolset:** send feedback once, through the first account ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
70
|
+
* **tools:** guard every header argument by its own declaration ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
71
|
+
* **tools:** keep a retried 429 on record while another request in the exchange is answered ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
72
|
+
* **tools:** report an MCP timeout as a timeout ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
73
|
+
* **tools:** return structuredContent from a tools/call result with no text ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
74
|
+
* **tools:** return the first listing from get_tool(), as Node does ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
75
|
+
* **tools:** send tools/call without relisting the catalog first ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
76
|
+
* **tools:** write nested header values as Node writes them ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
77
|
+
* **types:** declare ToolParameters.required so a hand-built tool type-checks ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
### Code Refactoring
|
|
81
|
+
|
|
82
|
+
* execute every tool over MCP tools/call ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
83
|
+
* MCP-only toolset with account discovery, search/execute, and hardened security ([#199](https://github.com/StackOneHQ/stackone-ai-python/issues/199)) ([e53edf0](https://github.com/StackOneHQ/stackone-ai-python/commit/e53edf0c7cc1da6cc5376df9ca27c6b845597e22))
|
|
84
|
+
* remove stackone_ai.integrations ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
85
|
+
* split stackone_ai.models into stackone_ai.tools and stackone_ai.types ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
86
|
+
* **toolset:** remove client-side search ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
87
|
+
* **toolset:** remove mode= from openai(), langchain() and pydantic_ai() ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
88
|
+
* **toolset:** return every result exactly as the server wrote it ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
89
|
+
* **toolset:** run an action by id with execute() ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
90
|
+
* **toolset:** stop pinning param-style=flat_prefixed on the MCP endpoint ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
91
|
+
* **tools:** pass the served schema through to every adapter ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
92
|
+
* **tools:** remove the client-side feedback tool ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
93
|
+
* **tools:** remove the HTTP executor from the base StackOneTool ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
94
|
+
* **tools:** return a download link from file actions ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
95
|
+
* **tools:** treat StackOneMcpTool headers as extra headers ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
96
|
+
* **types:** reduce ExecuteConfig to name, headers and timeout ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
### Build System
|
|
100
|
+
|
|
101
|
+
* make langchain-core an optional dependency ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
102
|
+
* make mcp a core dependency and remove the mcp extra ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
103
|
+
* require Python 3.11 or later ([049abf5](https://github.com/StackOneHQ/stackone-ai-python/commit/049abf548c7f5448a89e7999e95336980d23afc3))
|
|
104
|
+
|
|
3
105
|
## [2.10.1](https://github.com/StackOneHQ/stackone-ai-python/compare/stackone-ai-v2.10.0...stackone-ai-v2.10.1) (2026-07-28)
|
|
4
106
|
|
|
5
107
|
|
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
# Migrating from 2.x to 3.0
|
|
2
|
+
|
|
3
|
+
3.0 is a thin client over StackOne's MCP endpoint. Every tool call goes over MCP
|
|
4
|
+
`tools/call`, and the only other request the SDK makes is `GET /accounts`, to find
|
|
5
|
+
your linked accounts. Search runs on the server. Anything MCP does not support has
|
|
6
|
+
been removed rather than kept on a second transport.
|
|
7
|
+
|
|
8
|
+
Work through the sections that apply to you. Each one gives the 2.x code and its 3.0
|
|
9
|
+
replacement.
|
|
10
|
+
|
|
11
|
+
- [Installation](#installation)
|
|
12
|
+
- [Imports](#imports)
|
|
13
|
+
- [Search and execute](#search-and-execute)
|
|
14
|
+
- [Results](#results)
|
|
15
|
+
- [Tool arguments and headers](#tool-arguments-and-headers)
|
|
16
|
+
- [Base URL](#base-url)
|
|
17
|
+
- [Accounts](#accounts)
|
|
18
|
+
- [Feedback](#feedback)
|
|
19
|
+
- [Schemas given to a model](#schemas-given-to-a-model)
|
|
20
|
+
- [Hand-built tools and `ExecuteConfig`](#hand-built-tools-and-executeconfig)
|
|
21
|
+
- [LangGraph helpers](#langgraph-helpers)
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
- **Python 3.11 or later** is required. 3.10 is no longer supported.
|
|
26
|
+
- **`mcp` is a core dependency.** The `stackone-ai[mcp]` extra no longer exists.
|
|
27
|
+
- **`langchain-core` is now optional.** `to_langchain()` and `toolset.langchain()`
|
|
28
|
+
need the `langchain` extra and raise an `ImportError` that says so without it.
|
|
29
|
+
- `bm25s` and `numpy` are no longer dependencies, because search now runs on the server.
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# 2.x
|
|
33
|
+
uv add 'stackone-ai[mcp]'
|
|
34
|
+
|
|
35
|
+
# 3.0: add the extras for the frameworks you use
|
|
36
|
+
uv add stackone-ai
|
|
37
|
+
uv add 'stackone-ai[langchain]' # LangChain and LangGraph
|
|
38
|
+
uv add 'stackone-ai[pydantic-ai]' # Pydantic AI
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Imports
|
|
42
|
+
|
|
43
|
+
`stackone_ai.models` is split into `stackone_ai.tools` and `stackone_ai.types`. Anything
|
|
44
|
+
public can also be imported from the package root.
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
# 2.x
|
|
48
|
+
from stackone_ai.models import ExecuteConfig, StackOneTool, ToolParameters, Tools
|
|
49
|
+
from stackone_ai.toolset import ToolsetConfigError
|
|
50
|
+
|
|
51
|
+
# 3.0
|
|
52
|
+
from stackone_ai import ExecuteConfig, StackOneTool, ToolParameters, Tools, ToolsetConfigError
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`ToolsetError`, `ToolsetConfigError` and `ToolsetLoadError` now subclass `StackOneError`,
|
|
56
|
+
so `except StackOneError` catches everything the SDK raises. Existing
|
|
57
|
+
`except ToolsetError` clauses behave as before.
|
|
58
|
+
|
|
59
|
+
Unusable tool arguments raise the new `ToolArgumentsError`, before any request: a JSON
|
|
60
|
+
string that does not parse or does not parse to an object, an argument that cannot be
|
|
61
|
+
encoded as JSON, or `tool.call()` given both positional and keyword arguments, or more
|
|
62
|
+
than one positional argument. Raised by `tool.execute()`, `tool.call()` and
|
|
63
|
+
`toolset.execute()` alike. It subclasses both `StackOneError` and `ValueError`, which is
|
|
64
|
+
what 2.x raised, so existing `except ValueError` clauses still catch it.
|
|
65
|
+
|
|
66
|
+
These names have been removed and have no direct replacement:
|
|
67
|
+
|
|
68
|
+
| Removed | Use instead |
|
|
69
|
+
|---|---|
|
|
70
|
+
| `SearchConfig`, `SearchMode`, `SearchTool`, `SemanticSearchClient`, `SemanticSearchResult`, `SemanticSearchResponse`, `SemanticSearchError` | `toolset.search()`. See [Search and execute](#search-and-execute) |
|
|
71
|
+
| `stackone_ai.feedback`, `create_feedback_tool` | `toolset.submit_feedback()`. See [Feedback](#feedback) |
|
|
72
|
+
| `stackone_ai.integrations` | LangGraph's own APIs. See [LangGraph helpers](#langgraph-helpers) |
|
|
73
|
+
| `ToolDefinition` | `StackOneTool` fields: `name`, `description`, `parameters` |
|
|
74
|
+
| `ParameterLocation`, `validate_method` | Nothing. `ExecuteConfig` no longer has a method or parameter locations |
|
|
75
|
+
| `StackOneTool.connector`, `Tools.get_connectors()` | `fetch_tools(providers=[...])` to filter by provider. A provider name can contain `_` (`browser_linkedin`), so do not split the tool name |
|
|
76
|
+
|
|
77
|
+
Some names were public on `main` for a while before 3.0 but never in a release:
|
|
78
|
+
`StackOneRpcTool`, `MCP_PARAM_STYLE`, `is_json_content_type` and
|
|
79
|
+
`filename_from_content_disposition`. They are gone as well.
|
|
80
|
+
|
|
81
|
+
## Search and execute
|
|
82
|
+
|
|
83
|
+
Client-side search is gone: the BM25/TF-IDF index, the semantic search client, the
|
|
84
|
+
`search=` constructor argument, and the `tool_search`/`tool_execute` meta tools. The
|
|
85
|
+
server's `stackone_search_actions` tool now does the search, and `toolset.execute()`
|
|
86
|
+
runs an action by id through the server's `stackone_execute_action` tool. Each is one
|
|
87
|
+
call spanning every account in scope.
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
# 2.x
|
|
91
|
+
toolset = StackOneToolSet(search={"method": "auto"})
|
|
92
|
+
tools = toolset.search_tools("list employees", top_k=5)
|
|
93
|
+
result = tools[0].execute({"query_page_size": 25})
|
|
94
|
+
|
|
95
|
+
names = toolset.search_action_names("time off requests", top_k=5)
|
|
96
|
+
|
|
97
|
+
# 3.0
|
|
98
|
+
toolset = StackOneToolSet()
|
|
99
|
+
hits = toolset.search("list employees", top_k=5) # a list of action dicts, best first
|
|
100
|
+
hit = hits[0]
|
|
101
|
+
result = toolset.execute(
|
|
102
|
+
hit["action_id"],
|
|
103
|
+
{"query": {"page_size": 25}}, # the nested form in hit["input_schema"]
|
|
104
|
+
account_ids=[hit["available_accounts"][0]["id"]],
|
|
105
|
+
session_id=hit.get("session_id"),
|
|
106
|
+
)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
`search()` returns the server's hits: dicts carrying `action_id`, `provider` and
|
|
110
|
+
`available_accounts` (each `{"id", "name"}`), plus `description`, `similarity_score`,
|
|
111
|
+
`input_schema` and `example_request` when the server sends them, and the search's
|
|
112
|
+
`session_id`. `top_k` caps the whole result, as in 2.x; the same action linked on two
|
|
113
|
+
accounts is one hit naming both. When an action's connector is linked on more than one
|
|
114
|
+
account in scope, the server refuses to pick one and `execute()` raises
|
|
115
|
+
`StackOneAPIError` unless `account_ids` names one, such as
|
|
116
|
+
`[hit["available_accounts"][0]["id"]]`.
|
|
117
|
+
|
|
118
|
+
`toolset.execute()` has a new signature. It used to take a meta tool name, such as
|
|
119
|
+
`"tool_execute"`, with arguments as a JSON string or a dict. It now takes
|
|
120
|
+
`(action_id, arguments=None, *, account_ids=None, session_id=None)`, and `arguments`
|
|
121
|
+
must be a dict. It raises instead of returning `{"error": ...}`: `ToolsetConfigError`
|
|
122
|
+
before any request when `action_id`, `arguments` or `session_id` is malformed,
|
|
123
|
+
`ToolArgumentsError` when the arguments cannot be encoded as JSON, `ToolsetLoadError` when
|
|
124
|
+
the server did not serve `stackone_execute_action` (the project's policy disables
|
|
125
|
+
search_execute), and `StackOneAPIError` when the action fails, including when the server
|
|
126
|
+
rejects the arguments or the action id.
|
|
127
|
+
|
|
128
|
+
To give a model the search and execute tools, set the tool mode on the toolset.
|
|
129
|
+
`openai()`, `langchain()` and `pydantic_ai()` no longer take `mode=`.
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
# 2.x
|
|
133
|
+
toolset = StackOneToolSet(search={"method": "auto"})
|
|
134
|
+
openai_tools = toolset.openai(mode="search_and_execute")
|
|
135
|
+
# ...for each tool call the model makes:
|
|
136
|
+
toolset.execute(call.function.name, call.function.arguments)
|
|
137
|
+
|
|
138
|
+
# 3.0: stackone_search_actions and stackone_execute_action, served by the server and
|
|
139
|
+
# spanning every account in scope (at most 50), with stackone_list_accounts for several
|
|
140
|
+
toolset = StackOneToolSet(tool_mode="search_execute")
|
|
141
|
+
tools = toolset.fetch_tools()
|
|
142
|
+
openai_tools = tools.to_openai()
|
|
143
|
+
# ...then run the model's tool calls and get the messages to send back:
|
|
144
|
+
messages.extend(tools.execute_openai_tool_calls(message.tool_calls))
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
For a single account the server serves that account's own pair,
|
|
148
|
+
`<provider>_<account>_search_actions` and `_execute_action`; the SDK presents them as
|
|
149
|
+
`stackone_search_actions` and `stackone_execute_action`, so code and prompts use the same
|
|
150
|
+
names for one account or many. A scope of more than 50 accounts raises
|
|
151
|
+
`ToolsetConfigError` before any request.
|
|
152
|
+
|
|
153
|
+
`get_search_tool()` and `semantic_client` are removed. Call `toolset.search()` instead.
|
|
154
|
+
|
|
155
|
+
## Results
|
|
156
|
+
|
|
157
|
+
**Every tool returns the server's result as the server wrote it.** For an action,
|
|
158
|
+
that is `{"isError": False, "result": ..., "defenderMetadata"?, "policyMetadata"?}`.
|
|
159
|
+
Search results are bare JSON. This applies to `tool.execute()`, `tool.call()` and
|
|
160
|
+
`toolset.execute()`.
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
tool = toolset.fetch_tools(account_ids=[account_id]).get_tool("hibob_list_employees")
|
|
164
|
+
|
|
165
|
+
# 2.x
|
|
166
|
+
employees = tool.execute({})["data"]
|
|
167
|
+
|
|
168
|
+
# 3.0
|
|
169
|
+
employees = tool.execute({})["result"]["data"]
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
A result with `isError` set raises `StackOneAPIError`, which carries the status from the
|
|
173
|
+
payload in `status_code` and the body in `response_body`.
|
|
174
|
+
|
|
175
|
+
**Integers above 2^53 keep their exact value.** This SDK parses the server's JSON
|
|
176
|
+
itself, so it is not limited to the integer precision of IEEE 754 doubles; the Node
|
|
177
|
+
SDK's result can differ for a field that size, because its JSON parser has already
|
|
178
|
+
rounded it by the time the SDK sees it.
|
|
179
|
+
|
|
180
|
+
**File actions return a download link, not bytes.** The SDK does not follow the link.
|
|
181
|
+
When no link can be issued, the call raises `StackOneAPIError` with `status_code` 501.
|
|
182
|
+
|
|
183
|
+
```python
|
|
184
|
+
# 2.x
|
|
185
|
+
result = download.execute({"id": "file-id"})
|
|
186
|
+
with open(result["file_name"] or "download.bin", "wb") as f:
|
|
187
|
+
f.write(result["content"])
|
|
188
|
+
|
|
189
|
+
# 3.0
|
|
190
|
+
from pathlib import Path
|
|
191
|
+
|
|
192
|
+
import httpx
|
|
193
|
+
|
|
194
|
+
link = download.execute({"id": "file-id"})["result"]
|
|
195
|
+
# {"download_url": ..., "expires_at": ..., "file": {"name", "content_type", "content_length"}}
|
|
196
|
+
name = Path(link["file"]["name"] or "download.bin").name # chosen by the provider: keep only the basename
|
|
197
|
+
Path(name).write_bytes(httpx.get(link["download_url"]).content)
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Tool arguments and headers
|
|
201
|
+
|
|
202
|
+
**Arguments are sent exactly as given**, as `tools/call` arguments. The SDK no longer
|
|
203
|
+
splits flat `path_`/`query_`/`body_` keys into a request envelope, because the server
|
|
204
|
+
maps them itself.
|
|
205
|
+
|
|
206
|
+
**`fetch_tools()` tools have the server's own argument shape.** In 2.x the SDK asked
|
|
207
|
+
the server for the flat, prefixed style (`?param-style=flat_prefixed`). That request is
|
|
208
|
+
gone, so the argument names are whatever the server serves. Read them from
|
|
209
|
+
`tool.parameters.properties` rather than hard-coding them:
|
|
210
|
+
|
|
211
|
+
```python
|
|
212
|
+
# 2.x
|
|
213
|
+
tool.execute({"body_variables": {"first": 25}})
|
|
214
|
+
|
|
215
|
+
# 3.0: use the keys the schema names, for example
|
|
216
|
+
print(tool.parameters.properties)
|
|
217
|
+
tool.execute({"body": {"variables": {"first": 25}}})
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
**Header arguments are allowlisted.** A header argument is an entry of a `headers`
|
|
221
|
+
object argument, or a top-level `headers_<name>` argument. Each one is forwarded only
|
|
222
|
+
if the tool's schema declares it in the same form: under `headers.properties`, or as a
|
|
223
|
+
`headers_<name>` property. An open `headers` object, `"type": "object"` with no
|
|
224
|
+
`properties` and `additionalProperties` not `false`, declares every name; without
|
|
225
|
+
`"type": "object"`, or with `additionalProperties: false` and no `properties`, it
|
|
226
|
+
declares none. `Authorization`, `x-account-id`, `User-Agent` and `x-end-user-id` are
|
|
227
|
+
never forwarded, even when declared, because the SDK sets them itself. A dropped header argument is
|
|
228
|
+
logged as a warning, except a null value, which is omitted silently. Every other
|
|
229
|
+
argument is sent unchanged.
|
|
230
|
+
|
|
231
|
+
A top-level `headers` argument that isn't a plain object is dropped with a warning,
|
|
232
|
+
unless the schema declares `headers` itself as a non-object field, in which case it's
|
|
233
|
+
an ordinary argument that happens to be named `headers`: it skips header filtering and is
|
|
234
|
+
sent as given, subject to the same JSON-value check as every other argument. A
|
|
235
|
+
`headers_<name>` argument is dropped with a warning when its value is a list or dict.
|
|
236
|
+
|
|
237
|
+
`stackone_execute_action` serves an open `headers` object, so `toolset.execute()` passes your
|
|
238
|
+
own headers on to the action, with the exception of those four:
|
|
239
|
+
|
|
240
|
+
```python
|
|
241
|
+
toolset.execute("linear_list_comments", {"headers": {"x-request-id": "abc"}})
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Base URL
|
|
245
|
+
|
|
246
|
+
**`STACKONE_BASE_URL` is now read.** `base_url` still takes precedence when given;
|
|
247
|
+
otherwise the SDK now falls back to `STACKONE_BASE_URL` before the default,
|
|
248
|
+
`https://api.stackone.com`. An empty `base_url` argument or an empty
|
|
249
|
+
`STACKONE_BASE_URL` is treated as not set, not as a literal empty host.
|
|
250
|
+
|
|
251
|
+
## Accounts
|
|
252
|
+
|
|
253
|
+
**With no account id, the SDK discovers your accounts.** In 2.x, calling
|
|
254
|
+
`fetch_tools()` with no account listed tools without an `x-account-id`. In 3.0 it asks
|
|
255
|
+
`GET /accounts` and lists the catalog of every active shared account (non-shared ones
|
|
256
|
+
too with `include_non_shared=True`). If you have many
|
|
257
|
+
accounts, pass `account_id=`, `account_ids=` or call `set_accounts()` so the SDK does
|
|
258
|
+
not fetch every catalog.
|
|
259
|
+
|
|
260
|
+
**Discovery skips non-shared accounts.** An account with `shared: false` belongs to a
|
|
261
|
+
single end user, so discovery leaves it out and logs a warning naming it; when every
|
|
262
|
+
active account is non-shared it raises `ToolsetConfigError` instead. Pass its id, or
|
|
263
|
+
construct the toolset with `include_non_shared=True`, to use it.
|
|
264
|
+
|
|
265
|
+
**A non-shared account's end user is sent for you.** The API requires every MCP request
|
|
266
|
+
for a non-shared account to carry the account's end-user id in `x-end-user-id`. Whenever
|
|
267
|
+
the SDK calls `GET /accounts` (discovery, or `fetch_accounts()`), it records the
|
|
268
|
+
`origin_username` of each account with `shared: false` and sends it as `x-end-user-id` on
|
|
269
|
+
every request for that account. With explicit account ids it makes no `GET /accounts` up
|
|
270
|
+
front: the first request for a non-shared account is refused, and the SDK then calls
|
|
271
|
+
`GET /accounts` once and retries it with the end user it records. Call
|
|
272
|
+
`toolset.fetch_accounts()` once first to skip that round trip.
|
|
273
|
+
|
|
274
|
+
**`STACKONE_ACCOUNT_ID` is not read.** Pass the account id as `account_id=` or
|
|
275
|
+
`execute={"account_ids": [...]}`. A toolset constructed with neither while
|
|
276
|
+
`STACKONE_ACCOUNT_ID` is set to a non-empty value logs a warning that it is ignored.
|
|
277
|
+
|
|
278
|
+
**An empty account id raises.** `StackOneToolSet(account_id="")` raises
|
|
279
|
+
`ToolsetConfigError`, where it used to be treated as no account at all. An account id
|
|
280
|
+
read from an environment variable that is set but empty now raises too, rather than
|
|
281
|
+
quietly reaching every active shared account:
|
|
282
|
+
|
|
283
|
+
```python
|
|
284
|
+
# Raises when STACKONE_ACCOUNT_ID is set to ""
|
|
285
|
+
toolset = StackOneToolSet(account_id=os.getenv("STACKONE_ACCOUNT_ID"))
|
|
286
|
+
|
|
287
|
+
# Falls back to discovery only when the variable is unset or empty, deliberately
|
|
288
|
+
toolset = StackOneToolSet(account_id=os.getenv("STACKONE_ACCOUNT_ID") or None)
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
An empty or non-string entry in `account_ids`, `set_accounts()` or
|
|
292
|
+
`execute={"account_ids": [...]}` raises `ToolsetConfigError` as well, and so does
|
|
293
|
+
passing a tuple (or any other non-list) there: pass a list, `list(ids)`, instead. A falsy entry
|
|
294
|
+
(`None`, `0`, `""`) used to produce an unscoped request with no `x-account-id`; a
|
|
295
|
+
truthy non-string entry used to be sent as the header's value.
|
|
296
|
+
|
|
297
|
+
**`get_tool()` returns the first of any duplicates.** When two accounts serve the same
|
|
298
|
+
tool name, `Tools.get_tool()` now returns the first one listed, where 2.x returned the
|
|
299
|
+
last. Listings are merged in sorted account order, and a warning names the clashing
|
|
300
|
+
tools. `execute_openai_tool_calls()`, `to_openai()`, `to_langchain()` and
|
|
301
|
+
`to_pydantic_ai()` use the same one: each builds one tool per name. Pass `account_ids`
|
|
302
|
+
to choose the account yourself.
|
|
303
|
+
|
|
304
|
+
**A failing account is skipped, and left out for 30 seconds.** In a multi-account scope, an
|
|
305
|
+
account whose listing fails with a non-429 error is skipped with a warning and left out of
|
|
306
|
+
the cached catalog for 30 seconds, unless every account fails: then the accounts' shared
|
|
307
|
+
`StackOneAPIError` is raised when they all failed with one status, and otherwise a
|
|
308
|
+
`ToolsetLoadError` with each account's error in `failures`. A 429 that outlasts its retries
|
|
309
|
+
is never skipped — it aborts the whole call, even when other accounts are healthy, since
|
|
310
|
+
it is the key's rate limit rather than that account's failure. A single-account scope
|
|
311
|
+
raises its failure rather than skipping or caching it. `execute()` does not wait the 30
|
|
312
|
+
seconds for an account that could serve the action, one whose provider is the action's
|
|
313
|
+
connector or unknown after one `GET /accounts` to learn it: it lists it again straight
|
|
314
|
+
away, and refuses to run the action on another account while it still fails.
|
|
315
|
+
|
|
316
|
+
## Feedback
|
|
317
|
+
|
|
318
|
+
The client-side `tool_feedback` tool, `create_feedback_tool()` and the implicit
|
|
319
|
+
`feedback_*` execution options have been removed. `execute()` and `call()` no longer
|
|
320
|
+
take `options=`.
|
|
321
|
+
|
|
322
|
+
```python
|
|
323
|
+
# 2.x
|
|
324
|
+
feedback_tool = toolset.fetch_tools(actions=["tool_*"]).get_tool("tool_feedback")
|
|
325
|
+
feedback_tool.call(feedback="Worked well", account_id="acc_123", tool_names=["workday_list_workers"])
|
|
326
|
+
tool.execute({"id": "1"}, options={"feedback_session_id": "chat-42"})
|
|
327
|
+
|
|
328
|
+
# 3.0
|
|
329
|
+
hit = toolset.search("list workers")[0]
|
|
330
|
+
toolset.execute(hit["action_id"], session_id=hit.get("session_id"))
|
|
331
|
+
toolset.submit_feedback(
|
|
332
|
+
"positive",
|
|
333
|
+
[hit["action_id"]],
|
|
334
|
+
feedback="Worked well",
|
|
335
|
+
session_id=hit.get("session_id"),
|
|
336
|
+
)
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
When feedback is enabled for your project, the server also serves a
|
|
340
|
+
`stackone_submit_feedback` tool. **`fetch_tools()`, `openai()`, `langchain()` and
|
|
341
|
+
`pydantic_ai()` include it**, once, however many accounts are linked. If you don't want
|
|
342
|
+
a model to call it, filter it out:
|
|
343
|
+
|
|
344
|
+
```python
|
|
345
|
+
from stackone_ai import Tools
|
|
346
|
+
|
|
347
|
+
tools = Tools([t for t in toolset.fetch_tools() if t.name != "stackone_submit_feedback"])
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
`submit_feedback()` raises `ToolsetLoadError` when feedback is not enabled. It calls the
|
|
351
|
+
tool once, on the lowest account id in scope, and sends `action_run_id` when given.
|
|
352
|
+
|
|
353
|
+
## Schemas given to a model
|
|
354
|
+
|
|
355
|
+
**`to_openai_function()` passes the served schema through.** 2.x kept only `type`,
|
|
356
|
+
`description`, `enum` and a shallow copy of `items`/`properties`. Now `format`,
|
|
357
|
+
`pattern`, `default`, bounds, `oneOf`/`anyOf` and nested `required` reach the model as
|
|
358
|
+
the server served them. `to_langchain()` and `to_pydantic_ai_tool()` hand over the
|
|
359
|
+
same schema.
|
|
360
|
+
|
|
361
|
+
**`required` is the served list.** 2.x derived `required` from each property's
|
|
362
|
+
internal `nullable` marker, which made every property of a hand-built tool with no
|
|
363
|
+
markers required. 3.0 emits the schema's own `required` list, in the order the server
|
|
364
|
+
sent it, and leaves it out when that list is missing or empty. Give a hand-built tool
|
|
365
|
+
its `required` list directly:
|
|
366
|
+
|
|
367
|
+
```python
|
|
368
|
+
# 2.x: required, because "nullable" is absent
|
|
369
|
+
ToolParameters(type="object", properties={"id": {"type": "string"}})
|
|
370
|
+
|
|
371
|
+
# 3.0
|
|
372
|
+
ToolParameters(type="object", properties={"id": {"type": "string"}}, required=["id"])
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
## Hand-built tools and `ExecuteConfig`
|
|
376
|
+
|
|
377
|
+
**`StackOneTool` no longer makes HTTP requests.** Its `execute()` raises `StackOneError`
|
|
378
|
+
on the base class, so a hand-built tool must override it. Tools from `fetch_tools()` are
|
|
379
|
+
`StackOneMcpTool` instances, which execute over MCP. `_api_key` is now optional on the
|
|
380
|
+
base class.
|
|
381
|
+
|
|
382
|
+
```python
|
|
383
|
+
# 2.x
|
|
384
|
+
tool = StackOneTool(
|
|
385
|
+
description="Get an employee",
|
|
386
|
+
parameters=ToolParameters(type="object", properties={"id": {"type": "string"}}),
|
|
387
|
+
_execute_config=ExecuteConfig(
|
|
388
|
+
name="get_employee", method="GET", url="https://api.example.com/employees/{id}",
|
|
389
|
+
parameter_locations={"id": ParameterLocation.PATH},
|
|
390
|
+
),
|
|
391
|
+
_api_key="...",
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
# 3.0
|
|
395
|
+
class GetEmployee(StackOneTool):
|
|
396
|
+
def __init__(self) -> None:
|
|
397
|
+
super().__init__(
|
|
398
|
+
description="Get an employee",
|
|
399
|
+
parameters=ToolParameters(type="object", properties={"id": {"type": "string"}}, required=["id"]),
|
|
400
|
+
_execute_config=ExecuteConfig(name="get_employee"),
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
def execute(self, arguments=None):
|
|
404
|
+
args = json.loads(arguments) if isinstance(arguments, str) else dict(arguments or {})
|
|
405
|
+
return httpx.get(f"https://api.example.com/employees/{args['id']}").json()
|
|
406
|
+
|
|
407
|
+
tool = GetEmployee()
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
Define `__init__` on the subclass, as above. `StackOneTool` is a pydantic model, so a
|
|
411
|
+
type checker gives a subclass without its own `__init__` one built from the model's
|
|
412
|
+
fields, which rejects `_execute_config` even though the call works at runtime.
|
|
413
|
+
|
|
414
|
+
**`ExecuteConfig` keeps only `name`, `headers` and `timeout`**, and rejects any other
|
|
415
|
+
field. Passing `method`, `url`, `body_type` or `parameter_locations` raises a pydantic
|
|
416
|
+
`ValidationError` rather than being silently ignored.
|
|
417
|
+
|
|
418
|
+
**`StackOneMcpTool(headers=...)` adds headers; it does not replace them.** The SDK
|
|
419
|
+
always sets `Authorization`, `x-account-id` and `User-Agent` itself, after your headers,
|
|
420
|
+
and drops any case variant of those names you pass. An `x-end-user-id` you pass is sent as
|
|
421
|
+
given. `headers` is optional.
|
|
422
|
+
|
|
423
|
+
**`StackOneToolSet(headers=...)` is new.** It sends extra headers on every request the
|
|
424
|
+
toolset makes, `GET /accounts` included, with the same rule: `Authorization`, `x-account-id`
|
|
425
|
+
and `User-Agent` are ignored with a warning, and `x-end-user-id` is passed through unless
|
|
426
|
+
`GET /accounts` reported the account's end user.
|
|
427
|
+
|
|
428
|
+
## LangGraph helpers
|
|
429
|
+
|
|
430
|
+
`stackone_ai.integrations` has been removed. Pass the LangChain tools to LangGraph
|
|
431
|
+
directly:
|
|
432
|
+
|
|
433
|
+
```python
|
|
434
|
+
# 2.x
|
|
435
|
+
from stackone_ai.integrations import to_tool_node
|
|
436
|
+
node = to_tool_node(tools)
|
|
437
|
+
|
|
438
|
+
# 3.0
|
|
439
|
+
from langgraph.prebuilt import ToolNode
|
|
440
|
+
node = ToolNode(tools.to_langchain())
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
See [examples/langgraph_integration.py](examples/langgraph_integration.py) for a
|
|
444
|
+
complete agent.
|