Skip to content

feat: write-side chat tools and provider wire-encoding tests#1070

Merged
datlechin merged 2 commits into
mainfrom
feat/chat-write-tools-and-provider-tests
May 7, 2026
Merged

feat: write-side chat tools and provider wire-encoding tests#1070
datlechin merged 2 commits into
mainfrom
feat/chat-write-tools-and-provider-tests

Conversation

@datlechin

Copy link
Copy Markdown
Member

Summary

Two phase-4 followups bundled because they share a theme (filling out the chat-tool surface area):

  1. Write-side chat tools. Adds execute_query and confirm_destructive_operation as ChatTool wrappers, mirroring the existing MCP versions. The connection's safe-mode dialog flow gates execution, so the user is still the final approver for any state-changing query.
  2. Provider wire-encoding tests. Phase 4 shipped 1100 LOC of request/response encoding across Anthropic / OpenAI-compatible / Gemini with zero unit tests. This adds 23 round-trip tests that lock the JSON shape contracts.

Write-side tools

  • ExecuteQueryChatTool (Tools/ExecuteQueryChatTool.swift):

    • Decodes connection_id (or falls back to active connection), query, optional max_rows, timeout_seconds, database, schema.
    • Rejects multi-statement queries and queries over 100KB.
    • Routes through ToolConnectionMetadata.resolve, optional database/schema switch, QueryClassifier.classifyTier.
    • Destructive queries (DROP / TRUNCATE / ALTER...DROP) are blocked here — the AI gets a ChatToolResult(isError: true) directing it to the confirm tool.
    • Write queries trigger MCPAuthPolicy.checkSafeModeDialog — the connection's safe-mode policy decides whether to surface a native confirmation dialog before executing.
    • Executes via ToolQueryExecutor.executeAndLog with principalLabel: "AI Chat" so query history attribution is clear.
  • ConfirmDestructiveOperationChatTool (Tools/ConfirmDestructiveOperationChatTool.swift):

    • Requires confirmation_phrase to match "I understand this is irreversible" exactly.
    • Validates the query is destructive (rejects non-destructive queries with a hint to use execute_query).
    • Same safe-mode dialog gate before execution.

Both are registered in ChatToolBootstrap.register() so they ship live to all providers that emit tool calls.

ChatToolArgumentDecoder.optionalInt(_:key:default:clamp:) added — needed by execute_query for max_rows / timeout_seconds clamping.

Provider tests

Anthropic / OpenAICompatible / Gemini had private encoding helpers. Demoted to internal so tests can call them directly without mocking URLSession or the full streamChat pipeline.

  • AnthropicProviderEncodingTests (6 tests): tool spec uses input_schema snake_case key; plain text vs typed-block array dispatch; tool_use shape; tool_result with and without is_error; empty turn drops out.
  • OpenAICompatibleProviderEncodingTests (7 tests): tool spec wrapped in type: "function"; assistant turn with tool_calls and arguments-as-string (NOT object, per OpenAI spec); content: NSNull when no text; role: "tool" for results; multiple tool results expand to multiple messages.
  • GeminiProviderEncodingTests (6 tests): assistant maps to model role; functionCall.args is a JSON object (not string, unlike OpenAI); functionResponse.name resolves correctly even when the originating toolUse is two turns back; fallback to toolUseId as name when not found; system turns skipped.

Why bundle these in one PR

Both touch chat-tool plumbing. Demoting access on provider encoders is small enough not to need its own PR. Splitting would mean a write-tools PR with no test coverage and a tests-only PR — the combined story is "fill out the phase 4 surface area."

Risk

  • The two new tools execute SQL through the existing safe-mode flow, so user confirmation gates state changes. The fail-closed default (destructive queries blocked unless explicit phrase) matches MCP behavior.
  • Demoting provider encoder helpers from private to internal is a tiny widening of the same-module surface area. No external module is affected (we already demoted ChatTool foundation to internal in fix: demote ChatTool foundation to internal access level #1064).

Lint

swiftlint lint --strict clean across all 819 files.

Test plan

  • Open chat with a connection. Type "Update the orders table to set status='paid' where id=1". Verify AI calls execute_query. Verify the safe-mode dialog appears (if the connection has safe mode enabled).
  • Type "Drop the temp_log table". Verify AI calls confirm_destructive_operation with the phrase, and that the dialog still appears.
  • Without safe-mode confirmation, click Cancel. Verify the tool returns an error and the table is intact.
  • Run xcodebuild test -only-testing:TableProTests/AnthropicProviderEncodingTests and the OpenAI / Gemini equivalents.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@datlechin datlechin merged commit 2aa2c06 into main May 7, 2026
2 checks passed
@datlechin datlechin deleted the feat/chat-write-tools-and-provider-tests branch May 7, 2026 07:55
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.

1 participant