Skip to content

feat(ENG-733): support flat_prefixed MCP param style - #192

Merged
StuBehan merged 2 commits into
mainfrom
eng-733/flat-prefixed-mcp-param-style
Jul 27, 2026
Merged

feat(ENG-733): support flat_prefixed MCP param style#192
StuBehan merged 2 commits into
mainfrom
eng-733/flat-prefixed-mcp-param-style

Conversation

@StuBehan

@StuBehan StuBehan commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Problem

The SDK lists tools from /mcp without pinning a param-style, so the tool schema handed to the LLM follows the server default. Execution, however, goes through /actions/rpc with a hardcoded nested unwrap (path/query/headers/body). When the server default param-style flips nestedflat_prefixed, the LLM emits path_id/query_limit/… which the nested unwrap can't route: path and query params are dropped and the prefixed keys leak into the request body. The server-side MCP shim does not cover this — SDK execution bypasses the MCP transport and hits the actions RPC endpoint directly.

Fix

  • Pin ?param-style=flat_prefixed on the /mcp tool-listing endpoint, so the schema the LLM sees is stable regardless of the server default.
  • Replace the nested _extract_record unwrap with _split_envelope_params, which buckets <location>_<field> keys back into path/query/headers/body. The prefix carries the location, so no per-action schema is needed. Bare nested envelopes are still accepted (for clients holding a cached nested schema), and unprefixed keys fall through to the body.

Net effect: SDK tool calls use the higher-accuracy flat_prefixed schema and are immune to the server-side default flip.

Tests

  • test_fetch_tools_pins_flat_prefixed_param_style
  • test_routes_flat_prefixed_keys_by_location
  • test_buckets_nested_envelope_and_unprefixed_keys
  • Backward-compat preserved: existing nested/unprefixed execution tests unchanged. ty type-check and ruff (lint + format) clean.

Note

This protects SDK versions from this release forward. Already-published versions still break the moment the server default flips, so the server-side flip should be gated on quantifying old-version exposure by User-Agent.


Summary by cubic

Support the flat_prefixed MCP param style end-to-end so tool execution stays correct even if the server default changes. Addresses ENG-733 by pinning the /mcp schema and aligning RPC argument handling.

  • Bug Fixes
    • Pin /mcp?param-style=flat_prefixed so tools expose <location>_<field> args consistently; tests assert the exact endpoint.
    • Replace the nested unwrap with _split_envelope_params to bucket path_/query_/headers_/body_-prefixed keys into the RPC envelope; still accepts nested dicts and treats unprefixed keys as body for backward compatibility.

Written for commit b895af1. Summary will update on new commits.

Review in cubic

Copilot AI review requested due to automatic review settings July 24, 2026 14:29

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR stabilizes MCP tool schemas and RPC execution by pinning the /mcp tool-listing param style to flat_prefixed and adding an argument “envelope” splitter that routes <location>_<field> keys into the correct RPC buckets (path, query, headers, body), while preserving backward-compat for nested envelopes.

Changes:

  • Pin /mcp tool listing to ?param-style=flat_prefixed so LLM schemas remain stable across server default changes.
  • Replace the RPC argument unwrapping logic with _StackOneRpcTool._split_envelope_params to support flat_prefixed tool arguments.
  • Add targeted tests for param-style pinning and envelope splitting behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
stackone_ai/toolset.py Pins MCP listing param-style and introduces _split_envelope_params used by _StackOneRpcTool.execute() to correctly bucket flat-prefixed args into the RPC envelope.
tests/test_fetch_tools.py Adds tests asserting the pinned param-style in /mcp URL construction and validating envelope splitting for flat-prefixed + nested/unprefixed keys.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread stackone_ai/toolset.py
Comment on lines +503 to 505
return buckets

def _build_action_headers(self, additional_headers: dict[str, Any] | None) -> dict[str, str]:

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 2 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="stackone_ai/toolset.py">

<violation number="1" location="stackone_ai/toolset.py:482">
P2: The existing `tests/test_tool_calling.py` suite now fails because `_extract_record` was removed. Please retain a compatibility implementation (or update/remove the stale tests as part of this change) so the repository test suite does not raise `AttributeError`.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread stackone_ai/toolset.py
if isinstance(value, dict):
return dict(value)
return None
def _split_envelope_params(params: dict[str, Any]) -> dict[str, dict[str, Any]]:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The existing tests/test_tool_calling.py suite now fails because _extract_record was removed. Please retain a compatibility implementation (or update/remove the stale tests as part of this change) so the repository test suite does not raise AttributeError.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At stackone_ai/toolset.py, line 482:

<comment>The existing `tests/test_tool_calling.py` suite now fails because `_extract_record` was removed. Please retain a compatibility implementation (or update/remove the stale tests as part of this change) so the repository test suite does not raise `AttributeError`.</comment>

<file context>
@@ -477,10 +479,28 @@ def _parse_arguments(self, arguments: str | dict[str, Any] | None) -> dict[str,
-        if isinstance(value, dict):
-            return dict(value)
-        return None
+    def _split_envelope_params(params: dict[str, Any]) -> dict[str, dict[str, Any]]:
+        """Split LLM-supplied tool arguments into the RPC envelope (path/query/headers/body).
+
</file context>

Comment thread tests/test_fetch_tools.py Outdated
@StuBehan
StuBehan force-pushed the eng-733/flat-prefixed-mcp-param-style branch from 1aebf53 to d84f094 Compare July 24, 2026 14:42

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@StuBehan
StuBehan merged commit ff8fb9a into main Jul 27, 2026
16 checks passed
@StuBehan
StuBehan deleted the eng-733/flat-prefixed-mcp-param-style branch July 27, 2026 12:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants