stackone-ai 0.3.1__tar.gz → 0.3.2__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.
Files changed (74) hide show
  1. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/.github/workflows/lint.yml +11 -3
  2. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/.github/workflows/test.yml +12 -2
  3. stackone_ai-0.3.2/.release-please-manifest.json +3 -0
  4. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/CHANGELOG.md +7 -0
  5. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/PKG-INFO +34 -5
  6. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/README.md +23 -1
  7. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/pyproject.toml +15 -6
  8. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/stackone_ai/__init__.py +1 -1
  9. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/stackone_ai/meta_tools.py +2 -1
  10. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/stackone_ai/models.py +31 -22
  11. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/stackone_ai/server.py +23 -8
  12. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/stackone_ai/specs/parser.py +4 -1
  13. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/stackone_ai/toolset.py +3 -0
  14. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/uv.lock +1365 -247
  15. stackone_ai-0.3.1/.release-please-manifest.json +0 -3
  16. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/.cursor/rules/cursor-rules-location.mdc +0 -0
  17. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/.cursor/rules/examples-standards.mdc +0 -0
  18. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/.cursor/rules/no-relative-imports.mdc +0 -0
  19. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/.cursor/rules/package-installation.mdc +0 -0
  20. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/.cursor/rules/release-please-standards.mdc +0 -0
  21. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/.cursor/rules/uv-scripts.mdc +0 -0
  22. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/.env_example +0 -0
  23. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/.github/workflows/docs.yml +0 -0
  24. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/.github/workflows/release.yml +0 -0
  25. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/.gitignore +0 -0
  26. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/.mcp.json +0 -0
  27. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/.pre-commit-config.yaml +0 -0
  28. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/.release-please-config.json +0 -0
  29. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/.vscode/settings.json +0 -0
  30. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/CLAUDE.md +0 -0
  31. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/LICENSE +0 -0
  32. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/Makefile +0 -0
  33. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/examples/available_tools.py +0 -0
  34. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/examples/crewai_integration.py +0 -0
  35. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/examples/custom_base_url.py +0 -0
  36. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/examples/error_handling.py +0 -0
  37. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/examples/file_uploads.py +0 -0
  38. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/examples/index.py +0 -0
  39. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/examples/langchain_integration.py +0 -0
  40. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/examples/langgraph_tool_node.py +0 -0
  41. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/examples/mcp_server.py +0 -0
  42. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/examples/meta_tools_example.py +0 -0
  43. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/examples/openai_integration.py +0 -0
  44. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/examples/stackone_account_ids.py +0 -0
  45. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/examples/test_examples.py +0 -0
  46. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/mkdocs.yml +0 -0
  47. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/py.typed +0 -0
  48. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/scripts/build_docs.py +0 -0
  49. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/scripts/pull_oas.py +0 -0
  50. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/scripts/update_version.py +0 -0
  51. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/stackone_ai/constants.py +0 -0
  52. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/stackone_ai/oas/ats.json +0 -0
  53. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/stackone_ai/oas/core.json +0 -0
  54. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/stackone_ai/oas/crm.json +0 -0
  55. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/stackone_ai/oas/documents.json +0 -0
  56. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/stackone_ai/oas/hris.json +0 -0
  57. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/stackone_ai/oas/iam.json +0 -0
  58. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/stackone_ai/oas/lms.json +0 -0
  59. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/stackone_ai/oas/marketing.json +0 -0
  60. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/stackone_ai/py.typed +0 -0
  61. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/stackone_ai/specs/loader.py +0 -0
  62. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/tests/snapshots/test_parser/test_parse_all_oas_specs/ats_tools.json +0 -0
  63. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/tests/snapshots/test_parser/test_parse_all_oas_specs/core_tools.json +0 -0
  64. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/tests/snapshots/test_parser/test_parse_all_oas_specs/crm_tools.json +0 -0
  65. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/tests/snapshots/test_parser/test_parse_all_oas_specs/documents_tools.json +0 -0
  66. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/tests/snapshots/test_parser/test_parse_all_oas_specs/hris_tools.json +0 -0
  67. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/tests/snapshots/test_parser/test_parse_all_oas_specs/iam_tools.json +0 -0
  68. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/tests/snapshots/test_parser/test_parse_all_oas_specs/lms_tools.json +0 -0
  69. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/tests/snapshots/test_parser/test_parse_all_oas_specs/marketing_tools.json +0 -0
  70. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/tests/test_meta_tools.py +0 -0
  71. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/tests/test_models.py +0 -0
  72. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/tests/test_parser.py +0 -0
  73. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/tests/test_tool_calling.py +0 -0
  74. {stackone_ai-0.3.1 → stackone_ai-0.3.2}/tests/test_toolset.py +0 -0
@@ -8,17 +8,25 @@ on:
8
8
  jobs:
9
9
  lint:
10
10
  runs-on: ubuntu-latest
11
+ strategy:
12
+ matrix:
13
+ python-version: ["3.9", "3.10"]
14
+ include:
15
+ - python-version: "3.9"
16
+ sync-extras: "--all-extras --no-extra mcp"
17
+ - python-version: "3.10"
18
+ sync-extras: "--all-extras"
11
19
  steps:
12
20
  - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
13
21
 
14
22
  - name: Install uv
15
23
  uses: astral-sh/setup-uv@e92bafb6253dcd438e0484186d7669ea7a8ca1cc # v6.4.3
16
24
  with:
17
- python-version: "3.11"
25
+ python-version: ${{ matrix.python-version }}
18
26
  enable-cache: true
19
27
 
20
28
  - name: Install dependencies
21
- run: uv sync --all-extras
29
+ run: uv sync ${{ matrix.sync-extras }}
22
30
 
23
31
  - name: Run Ruff
24
32
  uses: astral-sh/ruff-action@0c50076f12c38c3d0115b7b519b54a91cb9cf0ad # v3.5.0
@@ -26,4 +34,4 @@ jobs:
26
34
  args: check .
27
35
 
28
36
  - name: Run Mypy
29
- run: uv run mypy stackone_ai
37
+ run: uv run mypy stackone_ai --exclude stackone_ai/server.py
@@ -8,6 +8,16 @@ on:
8
8
  jobs:
9
9
  test:
10
10
  runs-on: ubuntu-latest
11
+ strategy:
12
+ matrix:
13
+ python-version: ["3.9", "3.10", "3.13"]
14
+ include:
15
+ - python-version: "3.9"
16
+ test-extras: "--all-extras --no-extra mcp"
17
+ - python-version: "3.10"
18
+ test-extras: "--all-extras"
19
+ - python-version: "3.13"
20
+ test-extras: "--all-extras"
11
21
  env:
12
22
  STACKONE_API_KEY: ${{ secrets.STACKONE_API_KEY }}
13
23
  OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
@@ -17,11 +27,11 @@ jobs:
17
27
  - name: Install uv
18
28
  uses: astral-sh/setup-uv@e92bafb6253dcd438e0484186d7669ea7a8ca1cc # v6.4.3
19
29
  with:
20
- python-version: "3.11"
30
+ python-version: ${{ matrix.python-version }}
21
31
  enable-cache: true
22
32
 
23
33
  - name: Install dependencies
24
- run: uv sync --all-extras
34
+ run: uv sync ${{ matrix.test-extras }}
25
35
 
26
36
  - name: Run tests
27
37
  run: uv run pytest
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "0.3.2"
3
+ }
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.3.2](https://github.com/StackOneHQ/stackone-ai-python/compare/stackone-ai-v0.3.1...stackone-ai-v0.3.2) (2025-08-26)
4
+
5
+
6
+ ### Features
7
+
8
+ * support Python 3.9+ with optional MCP server support ([#28](https://github.com/StackOneHQ/stackone-ai-python/issues/28)) ([1a37776](https://github.com/StackOneHQ/stackone-ai-python/commit/1a377768c15223e25dbaf1e0bcd0c0e8bb0df2e8))
9
+
3
10
  ## [0.3.1](https://github.com/StackOneHQ/stackone-ai-python/compare/stackone-ai-v0.3.0...stackone-ai-v0.3.1) (2025-08-19)
4
11
 
5
12
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stackone-ai
3
- Version: 0.3.1
3
+ Version: 0.3.2
4
4
  Summary: agents performing actions on your SaaS
5
5
  Author-email: StackOne <support@stackone.com>
6
6
  License-File: LICENSE
@@ -8,24 +8,31 @@ Classifier: Development Status :: 4 - Beta
8
8
  Classifier: Intended Audience :: Developers
9
9
  Classifier: License :: OSI Approved :: MIT License
10
10
  Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.9
12
+ Classifier: Programming Language :: Python :: 3.10
11
13
  Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
12
16
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
13
- Requires-Python: >=3.11
17
+ Requires-Python: >=3.9
14
18
  Requires-Dist: bm25s>=0.2.2
19
+ Requires-Dist: eval-type-backport; python_version < '3.10'
15
20
  Requires-Dist: langchain-core>=0.1.0
16
- Requires-Dist: mcp[cli]>=1.3.0
17
21
  Requires-Dist: numpy>=1.24.0
18
22
  Requires-Dist: pydantic>=2.10.6
19
23
  Requires-Dist: requests>=2.32.3
24
+ Requires-Dist: typing-extensions>=4.0.0
20
25
  Provides-Extra: docs
21
26
  Requires-Dist: mkdocs-terminal>=4.7.0; extra == 'docs'
22
27
  Requires-Dist: pygments>=2.12; extra == 'docs'
23
28
  Requires-Dist: pymdown-extensions; extra == 'docs'
24
29
  Provides-Extra: examples
25
- Requires-Dist: crewai>=0.102.0; extra == 'examples'
30
+ Requires-Dist: crewai>=0.102.0; (python_version >= '3.10') and extra == 'examples'
26
31
  Requires-Dist: langchain-openai>=0.3.6; extra == 'examples'
27
32
  Requires-Dist: openai>=1.63.2; extra == 'examples'
28
33
  Requires-Dist: python-dotenv>=1.0.1; extra == 'examples'
34
+ Provides-Extra: mcp
35
+ Requires-Dist: mcp[cli]>=1.3.0; (python_version >= '3.10') and extra == 'mcp'
29
36
  Description-Content-Type: text/markdown
30
37
 
31
38
  # StackOne AI SDK
@@ -48,12 +55,32 @@ StackOne AI provides a unified interface for accessing various SaaS tools throug
48
55
  - CrewAI Tools
49
56
  - LangGraph Tool Node
50
57
 
58
+ ## Requirements
59
+
60
+ - Python 3.9+ (core SDK functionality)
61
+ - Python 3.10+ (for MCP server and CrewAI examples)
62
+
51
63
  ## Installation
52
64
 
65
+ ### Basic Installation
66
+
53
67
  ```bash
54
68
  pip install stackone-ai
55
69
  ```
56
70
 
71
+ ### Optional Features
72
+
73
+ ```bash
74
+ # Install with MCP server support (requires Python 3.10+)
75
+ pip install 'stackone-ai[mcp]'
76
+
77
+ # Install with CrewAI examples (requires Python 3.10+)
78
+ pip install 'stackone-ai[examples]'
79
+
80
+ # Install everything
81
+ pip install 'stackone-ai[mcp,examples]'
82
+ ```
83
+
57
84
  ## Quick Start
58
85
 
59
86
  ```python
@@ -112,10 +139,12 @@ for tool_call in response.tool_calls:
112
139
  </details>
113
140
 
114
141
  <details>
115
- <summary>CrewAI Integration</summary>
142
+ <summary>CrewAI Integration (Python 3.10+)</summary>
116
143
 
117
144
  CrewAI uses LangChain tools natively, making integration seamless:
118
145
 
146
+ > **Note**: CrewAI requires Python 3.10+. Install with `pip install 'stackone-ai[examples]'`
147
+
119
148
  ```python
120
149
  from crewai import Agent, Crew, Task
121
150
  from stackone_ai import StackOneToolSet
@@ -18,12 +18,32 @@ StackOne AI provides a unified interface for accessing various SaaS tools throug
18
18
  - CrewAI Tools
19
19
  - LangGraph Tool Node
20
20
 
21
+ ## Requirements
22
+
23
+ - Python 3.9+ (core SDK functionality)
24
+ - Python 3.10+ (for MCP server and CrewAI examples)
25
+
21
26
  ## Installation
22
27
 
28
+ ### Basic Installation
29
+
23
30
  ```bash
24
31
  pip install stackone-ai
25
32
  ```
26
33
 
34
+ ### Optional Features
35
+
36
+ ```bash
37
+ # Install with MCP server support (requires Python 3.10+)
38
+ pip install 'stackone-ai[mcp]'
39
+
40
+ # Install with CrewAI examples (requires Python 3.10+)
41
+ pip install 'stackone-ai[examples]'
42
+
43
+ # Install everything
44
+ pip install 'stackone-ai[mcp,examples]'
45
+ ```
46
+
27
47
  ## Quick Start
28
48
 
29
49
  ```python
@@ -82,10 +102,12 @@ for tool_call in response.tool_calls:
82
102
  </details>
83
103
 
84
104
  <details>
85
- <summary>CrewAI Integration</summary>
105
+ <summary>CrewAI Integration (Python 3.10+)</summary>
86
106
 
87
107
  CrewAI uses LangChain tools natively, making integration seamless:
88
108
 
109
+ > **Note**: CrewAI requires Python 3.10+. Install with `pip install 'stackone-ai[examples]'`
110
+
89
111
  ```python
90
112
  from crewai import Agent, Crew, Task
91
113
  from stackone_ai import StackOneToolSet
@@ -1,9 +1,9 @@
1
1
  [project]
2
2
  name = "stackone-ai"
3
- version = "0.3.1"
3
+ version = "0.3.2"
4
4
  description = "agents performing actions on your SaaS"
5
5
  readme = "README.md"
6
- requires-python = ">=3.11"
6
+ requires-python = ">=3.9"
7
7
  authors = [
8
8
  { name = "StackOne", email = "support@stackone.com" }
9
9
  ]
@@ -12,16 +12,21 @@ classifiers = [
12
12
  "Intended Audience :: Developers",
13
13
  "License :: OSI Approved :: MIT License",
14
14
  "Programming Language :: Python :: 3",
15
+ "Programming Language :: Python :: 3.9",
16
+ "Programming Language :: Python :: 3.10",
15
17
  "Programming Language :: Python :: 3.11",
18
+ "Programming Language :: Python :: 3.12",
19
+ "Programming Language :: Python :: 3.13",
16
20
  "Topic :: Software Development :: Libraries :: Python Modules",
17
21
  ]
18
22
  dependencies = [
19
23
  "pydantic>=2.10.6",
20
24
  "requests>=2.32.3",
21
25
  "langchain-core>=0.1.0",
22
- "mcp[cli]>=1.3.0",
23
26
  "bm25s>=0.2.2",
24
27
  "numpy>=1.24.0",
28
+ "typing-extensions>=4.0.0",
29
+ "eval-type-backport; python_version<'3.10'", # TODO: Remove when Python 3.9 support is dropped
25
30
  ]
26
31
 
27
32
  [project.scripts]
@@ -41,8 +46,12 @@ packages = ["stackone_ai"]
41
46
  "py.typed" = "py.typed"
42
47
 
43
48
  [project.optional-dependencies]
49
+ # TODO: Remove python_version conditions when Python 3.9 support is dropped
50
+ mcp = [
51
+ "mcp[cli]>=1.3.0; python_version>='3.10'",
52
+ ]
44
53
  examples = [
45
- "crewai>=0.102.0",
54
+ "crewai>=0.102.0; python_version>='3.10'",
46
55
  "langchain-openai>=0.3.6",
47
56
  "openai>=1.63.2",
48
57
  "python-dotenv>=1.0.1",
@@ -82,7 +91,7 @@ markers = [
82
91
 
83
92
  [tool.ruff]
84
93
  line-length = 110
85
- target-version = "py311"
94
+ target-version = "py39"
86
95
 
87
96
  [tool.ruff.lint]
88
97
  select = [
@@ -96,7 +105,7 @@ select = [
96
105
  ]
97
106
 
98
107
  [tool.mypy]
99
- python_version = "3.11"
108
+ python_version = "3.9"
100
109
  disallow_untyped_defs = true
101
110
  disallow_incomplete_defs = true
102
111
  check_untyped_defs = true
@@ -4,4 +4,4 @@ from .models import StackOneTool, Tools
4
4
  from .toolset import StackOneToolSet
5
5
 
6
6
  __all__ = ["StackOneToolSet", "StackOneTool", "Tools"]
7
- __version__ = "0.3.1"
7
+ __version__ = "0.3.2"
@@ -82,7 +82,8 @@ class ToolIndex:
82
82
 
83
83
  # Process results
84
84
  search_results = []
85
- for idx, score in zip(results[0], scores[0], strict=False):
85
+ # TODO: Add strict=False when Python 3.9 support is dropped
86
+ for idx, score in zip(results[0], scores[0]):
86
87
  if score < min_score:
87
88
  continue
88
89
 
@@ -1,16 +1,23 @@
1
+ # TODO: Remove when Python 3.9 support is dropped
2
+ from __future__ import annotations
3
+
1
4
  import asyncio
2
5
  import base64
3
6
  import json
4
7
  from collections.abc import Sequence
5
8
  from enum import Enum
6
9
  from functools import partial
7
- from typing import Annotated, Any, TypeAlias, cast
10
+ from typing import Annotated, Any, cast
11
+ from urllib.parse import quote
8
12
 
9
13
  import requests
10
14
  from langchain_core.tools import BaseTool
11
15
  from pydantic import BaseModel, BeforeValidator, Field, PrivateAttr
12
16
  from requests.exceptions import RequestException
13
17
 
18
+ # TODO: Remove when Python 3.9 support is dropped
19
+ from typing_extensions import TypeAlias
20
+
14
21
  # Type aliases for common types
15
22
  JsonDict: TypeAlias = dict[str, Any]
16
23
  Headers: TypeAlias = dict[str, str]
@@ -140,21 +147,24 @@ class StackOneTool(BaseModel):
140
147
  for key, value in kwargs.items():
141
148
  param_location = self._execute_config.parameter_locations.get(key)
142
149
 
143
- match param_location:
144
- case ParameterLocation.PATH:
145
- url = url.replace(f"{{{key}}}", str(value))
146
- case ParameterLocation.QUERY:
150
+ if param_location == ParameterLocation.PATH:
151
+ # Safely encode path parameters to prevent SSRF attacks
152
+ encoded_value = quote(str(value), safe="")
153
+ url = url.replace(f"{{{key}}}", encoded_value)
154
+ elif param_location == ParameterLocation.QUERY:
155
+ query_params[key] = value
156
+ elif param_location in (ParameterLocation.BODY, ParameterLocation.FILE):
157
+ body_params[key] = value
158
+ else:
159
+ # Default behavior
160
+ if f"{{{key}}}" in url:
161
+ # Safely encode path parameters to prevent SSRF attacks
162
+ encoded_value = quote(str(value), safe="")
163
+ url = url.replace(f"{{{key}}}", encoded_value)
164
+ elif self._execute_config.method in {"GET", "DELETE"}:
147
165
  query_params[key] = value
148
- case ParameterLocation.BODY | ParameterLocation.FILE:
166
+ else:
149
167
  body_params[key] = value
150
- case _:
151
- # Default behavior
152
- if f"{{{key}}}" in url:
153
- url = url.replace(f"{{{key}}}", str(value))
154
- elif self._execute_config.method in {"GET", "DELETE"}:
155
- query_params[key] = value
156
- else:
157
- body_params[key] = value
158
168
 
159
169
  return url, body_params, query_params
160
170
 
@@ -355,13 +365,12 @@ class StackOneTool(BaseModel):
355
365
  python_type: type = str # Default to str
356
366
  if isinstance(details, dict):
357
367
  type_str = details.get("type", "string")
358
- match type_str:
359
- case "number":
360
- python_type = float
361
- case "integer":
362
- python_type = int
363
- case "boolean":
364
- python_type = bool
368
+ if type_str == "number":
369
+ python_type = float
370
+ elif type_str == "integer":
371
+ python_type = int
372
+ elif type_str == "boolean":
373
+ python_type = bool
365
374
 
366
375
  field = Field(description=details.get("description", ""))
367
376
  else:
@@ -480,7 +489,7 @@ class Tools:
480
489
  """
481
490
  return [tool.to_langchain() for tool in self.tools]
482
491
 
483
- def meta_tools(self) -> "Tools":
492
+ def meta_tools(self) -> Tools:
484
493
  """Return meta tools for tool discovery and execution
485
494
 
486
495
  Meta tools enable dynamic tool discovery and execution based on natural language queries.
@@ -1,3 +1,6 @@
1
+ # TODO: Remove when Python 3.9 support is dropped
2
+ from __future__ import annotations
3
+
1
4
  import argparse
2
5
  import asyncio
3
6
  import logging
@@ -5,12 +8,24 @@ import os
5
8
  import sys
6
9
  from typing import Any, TypeVar
7
10
 
8
- import mcp.types as types
9
- from mcp.server import NotificationOptions, Server
10
- from mcp.server.models import InitializationOptions
11
- from mcp.server.stdio import stdio_server
12
- from mcp.shared.exceptions import McpError
13
- from mcp.types import EmbeddedResource, ErrorData, ImageContent, TextContent, Tool
11
+ # Check Python version for MCP server functionality
12
+ if sys.version_info < (3, 10):
13
+ raise RuntimeError(
14
+ "MCP server functionality requires Python 3.10+. Current version: {}.{}.{}".format(
15
+ *sys.version_info[:3]
16
+ )
17
+ )
18
+
19
+ try: # type: ignore[unreachable]
20
+ import mcp.types as types
21
+ from mcp.server import NotificationOptions, Server
22
+ from mcp.server.models import InitializationOptions
23
+ from mcp.server.stdio import stdio_server
24
+ from mcp.shared.exceptions import McpError
25
+ from mcp.types import EmbeddedResource, ErrorData, ImageContent, TextContent, Tool
26
+ except ImportError as e:
27
+ raise ImportError("MCP dependencies not found. Install with: pip install 'stackone-ai[mcp]'") from e
28
+
14
29
  from pydantic import ValidationError
15
30
 
16
31
  from stackone_ai import StackOneToolSet
@@ -41,7 +56,7 @@ def tool_needs_account_id(tool_name: str) -> bool:
41
56
  return True
42
57
 
43
58
 
44
- @app.list_tools() # type: ignore[misc]
59
+ @app.list_tools()
45
60
  async def list_tools() -> list[Tool]:
46
61
  """List all available StackOne tools as MCP tools."""
47
62
  if not toolset:
@@ -99,7 +114,7 @@ async def list_tools() -> list[Tool]:
99
114
  ) from e
100
115
 
101
116
 
102
- @app.call_tool() # type: ignore[misc]
117
+ @app.call_tool()
103
118
  async def call_tool(
104
119
  name: str, arguments: dict[str, Any]
105
120
  ) -> list[TextContent | ImageContent | EmbeddedResource]:
@@ -1,3 +1,6 @@
1
+ # TODO: Remove when Python 3.9 support is dropped
2
+ from __future__ import annotations
3
+
1
4
  import json
2
5
  from pathlib import Path
3
6
  from typing import Any
@@ -73,7 +76,7 @@ class OpenAPIParser:
73
76
  visited = set()
74
77
 
75
78
  # Handle primitive types (str, int, etc)
76
- if not isinstance(schema, dict | list):
79
+ if not isinstance(schema, (dict, list)):
77
80
  return schema
78
81
 
79
82
  if isinstance(schema, list):
@@ -1,3 +1,6 @@
1
+ # TODO: Remove when Python 3.9 support is dropped
2
+ from __future__ import annotations
3
+
1
4
  import fnmatch
2
5
  import os
3
6
  import warnings