fix(toolset): honour STACKONE_BASE_URL and remove documented fiction - #197
willleeney wants to merge 2 commits into
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
Two moderate toolset issues and one documentation nit remain unresolved.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Aligns the Python SDK with Node SDK behavior for base URLs, feedback tools, dependency constraints, and documentation.
Changes:
- Adds
STACKONE_BASE_URLfallback and automatic feedback-tool injection. - Pins
mcpbelow version 2. - Updates tests for feedback-tool behavior.
- Removes or corrects stale documentation.
File summaries
| File | Summary |
|---|---|
uv.lock |
Records the MCP dependency constraint. |
tests/test_semantic_search.py |
Updates semantic-search expectations. |
tests/test_fetch_tools.py |
Covers automatic feedback-tool inclusion. |
stackone_ai/toolset.py |
Adds base URL fallback and feedback-tool injection. Two moderate issues remain: trailing slashes can produce double-slash feedback URLs, and the pseudo-connector may trigger unnecessary semantic-search requests. |
README.md |
Removes obsolete feedback documentation. |
pyproject.toml |
Pins mcp to <2.0.0. |
CLAUDE.md |
Corrects the tool-filtering example. Nit: the example still lacks standalone imports and initialization. |
Review details
Suppressed comments (1)
stackone_ai/toolset.py:597
- This new environment fallback has no regression test: the existing initialization tests cover the default and explicit
base_url, but notSTACKONE_BASE_URLor its precedence over the constructor argument. Add coverage for both cases so this parity behavior is verified.
self.base_url = base_url or os.getenv("STACKONE_BASE_URL") or DEFAULT_BASE_URL
- Files reviewed: 6/7 changed files
- Comments generated: 3
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| self.api_key: str = api_key_value | ||
| self.account_id = account_id | ||
| self.base_url = base_url or DEFAULT_BASE_URL | ||
| self.base_url = base_url or os.getenv("STACKONE_BASE_URL") or DEFAULT_BASE_URL |
| if actions: | ||
| all_tools = [tool for tool in all_tools if self._filter_by_action(tool.name, actions)] | ||
|
|
||
| all_tools.append(self._create_feedback_tool()) |
| ```python | ||
| # Use glob patterns for tool selection | ||
| tools = StackOneToolSet(include_tools=["bamboohr_*", "!bamboohr_create_*"]) | ||
| tools = toolset.fetch_tools(actions=["bamboohr_*"]) |
There was a problem hiding this comment.
1 issue found across 7 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="CLAUDE.md">
<violation number="1" location="CLAUDE.md:60">
P3: Make this example standalone by importing and initializing `StackOneToolSet` before calling `fetch_tools`; as written, copying the block raises `NameError` for `toolset`.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| ```python | ||
| # Use glob patterns for tool selection | ||
| tools = StackOneToolSet(include_tools=["bamboohr_*", "!bamboohr_create_*"]) | ||
| tools = toolset.fetch_tools(actions=["bamboohr_*"]) |
There was a problem hiding this comment.
P3: Make this example standalone by importing and initializing StackOneToolSet before calling fetch_tools; as written, copying the block raises NameError for toolset.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At CLAUDE.md, line 60:
<comment>Make this example standalone by importing and initializing `StackOneToolSet` before calling `fetch_tools`; as written, copying the block raises `NameError` for `toolset`.</comment>
<file context>
@@ -57,7 +57,7 @@ StackOne AI SDK is a Python library that provides a unified interface for access
```python
# Use glob patterns for tool selection
-tools = StackOneToolSet(include_tools=["bamboohr_*", "!bamboohr_create_*"])
+tools = toolset.fetch_tools(actions=["bamboohr_*"])
</file context>
</details>
```suggestion
from stackone_ai import StackOneToolSet
toolset = StackOneToolSet(api_key="your-api-key")
tools = toolset.fetch_tools(actions=["bamboohr_*"])
There was a problem hiding this comment.
0 issues found across 3 files (changes from recent commits).
Requires human review: Auto-approval blocked by 1 unresolved issue from previous reviews.
Re-trigger cubic
There was a problem hiding this comment.
0 issues found across 2 files (changes from recent commits).
Requires human review: Auto-approval blocked by 1 unresolved issue from previous reviews.
Re-trigger cubic
STACKONE_BASE_URL was ignored entirely, so a consumer pointing at staging silently talked to production. Now read as a fallback, matching the STACKONE_API_KEY handling directly above it and node's behaviour. Precedence is explicit arg > env var > default; the docstring says so rather than leaving the env var undocumented. Also removes three documented-but-untrue claims (consumer contract §3): - `configure_implicit_feedback` and the whole "Implicit Feedback (Beta)" section. None of it exists in the source; models.py carries a `# Implicit feedback removed` marker where it used to live. - Action-filter "exclusions" like `"!*_delete_*"`. `_filter_by_action` is a bare `any(fnmatch(...))` with no negation, so the pattern matches nothing and excludes nothing. - "This tool is automatically included in the toolset." The feedback tool is never injected client-side, so the worked example returned None and the next line raised AttributeError. Replaced with the MCP-served `stackone_submit_feedback` and the `create_feedback_tool` path that actually works — verified by running it.
e4a5dac to
f67e461
Compare
The env-var read had no regression cover: reverting it passed the suite clean, so the headline behaviour change of this PR was unprotected. Three tests pin the contract — explicit argument > env var > default. test_init_with_api_key also asserted the default base_url without clearing the environment, so this change made it fail for anyone with STACKONE_BASE_URL exported. It now clears the env for that assertion.
There was a problem hiding this comment.
0 issues found across 1 file (changes from recent commits).
Requires human review: Auto-approval blocked by 1 unresolved issue from previous reviews.
Re-trigger cubic
Summary
Two Phase 1 patch fixes from the SDK platform rewrite proposal. Rebased onto 2.10.1.
STACKONE_BASE_URLis now honoured. It was ignored entirely, so a consumer pointing at staging silently talked to production (consumer contract §2.4, defect 7). Precedence isexplicit arg > env var > default, matching theSTACKONE_API_KEYhandling on the line above and node's behaviour. The docstring documents it rather than leaving a constructor that reads an undocumented env var.Three documented-but-untrue claims removed (consumer contract §3), each verified against the source first:
configure_implicit_feedback+ the "Implicit Feedback (Beta)" sectionmodels.pycarries a# Implicit feedback removedmarker where it used to live."!*_delete_*"_filter_by_actionis a bareany(fnmatch(...))— no negation.fnmatch("sf_delete_lead", "!*_delete_*")isFalse, so it matches nothing and excludes nothing.get_tool("tool_feedback")returnedNone, so the next line of the shipped example raisedAttributeError.The feedback docs now point at the MCP-served
stackone_submit_feedbackand thecreate_feedback_toolpath that actually works — verified by executing it.Scope changes after review
Three things were dropped from earlier revisions of this branch:
mcp<2.0.0pin — already on main via fix(deps): Avoid DOA-pin mcp below 2.0.0 ahead of upstream MCP v2 release #194. My version was byte-identical. Removed.pydantic-ai-slimto<3.0.0— removed. See below.stackone_submit_feedbackas a global, always-present tool (mcp.service.ts:448, unscoped to account or tool mode), so injecting a secondtool_feedbackgave one capability two names, two schemas and two routes, and it bypassed the provider/action filters. Node's auto-append is the same defect rather than the target state; both hand-built tools go together in the MCP-execution rewrite.On the pydantic-ai cap
I initially widened
pydantic-ai-slimto<3.0.0, having measured that the adapter works fine on 2.x:Tool.from_schemais signature-identical on 1.83.0 and 2.42.0, and all 11 integration tests (including a liveAgent.run_sync) pass on both majors.That is still true, but widening it here was wrong. #195 capped the
pydantic-aiandexamplesextras together deliberately, and the resolver shows why they cannot move apart:So moving to pydantic-ai 2.x means widening both extras and verifying the example scripts (crewai / langgraph / openai) on 2.x — a coupled dependency migration, not a patch. It also reds
uv lock --check, which CI runs. Worth doing as its own PR; the evidence above is the starting point.One related gap worth recording:
pydantic_aiis in no dependency group, sopytest.importorskipskips all 11 integration tests silently in CI. Whatever upper bound we settle on, nothing currently exercises it.Verified
origin/main(langgraph andmcpoptional deps absent locally).uv lock --check: passes (this is what CI'suv sync --all-extras --lockedenforces).ruff check: clean.https://x.example.comwins over env; envhttps://staging.example.comused when no arg; default otherwise.stackone-adk-plugin32/32 green against this branch.pydantic-ai-harness[stackone]does not depend onstackone-aiat all (it usesfastmcp+pydantic-ai-slimdirectly), so it is unaffected.Noted, not fixed
Out of scope for this change, but found while verifying it:
StackOneTool.call()accepts anoptionskwarg and drops it — neithermodels.py:396nor:398forwards it toexecute().create_feedback_toolis absent from__all__; it is only reachable asstackone_ai.feedback.tool.create_feedback_tool.__init__.pyhardcodes__version__whiletoolset.py:84derives it fromimportlib.metadata— the same drift class as the hardcodedstackone-python/1.0.0User-Agent in contract §7..hypothesis/is not in.gitignore.🤖 Generated with Claude Code