Skip to content

feat: Auto-exclude built-in tools overridden by user-registered tools#523

Draft
Copilot wants to merge 7 commits intomainfrom
copilot/review-code-changes
Draft

feat: Auto-exclude built-in tools overridden by user-registered tools#523
Copilot wants to merge 7 commits intomainfrom
copilot/review-code-changes

Conversation

Copy link
Contributor

Copilot AI commented Feb 20, 2026

When users register tools whose names collide with CLI built-ins (e.g. edit_file, read_file), the CLI would intercept those calls instead of dispatching to the user's handler. The fix: automatically merge user-registered tool names into excludedTools on session.create/session.resume, so the CLI skips its built-in and the SDK dispatches locally.

Changes

  • Node.jsmergeExcludedTools() helper; applied in createSession + resumeSession
  • Python — inline merge using dict.fromkeys() for dedup; applied in create_session + resume_session
  • GomergeExcludedTools() helper with map-based dedup; applied in CreateSession + ResumeSessionWithOptions
  • .NETMergeExcludedTools() internal static helper using Union(); applied in CreateSessionAsync + ResumeSessionAsync; added InternalsVisibleTo for test project
  • Tests — unit tests in all 4 SDKs covering: names added, deduplication, no-op (no tools), and resume path
  • Docs — "Overriding Built-in Tools" subsection added to all 4 READMEs

Example (Node.js)

// Register a tool named after a CLI built-in — your handler is called instead
const session = await client.createSession({
  tools: [defineTool("edit_file", {
    description: "Custom editor with project-specific validation",
    parameters: z.object({ path: z.string(), content: z.string() }),
    handler: async ({ path, content }) => { /* your logic */ },
  })],
  // No need to manually add "edit_file" to excludedTools — SDK does it automatically
});

Backward-compatible: users with no name collisions see no behavior change.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • https://api.github.com/copilot_internal/user
    • Triggering command: /opt/hostedtoolcache/node/22.22.0/x64/bin/node /opt/hostedtoolcache/node/22.22.0/x64/bin/node /home/REDACTED/work/copilot-sdk/copilot-sdk/nodejs/node_modules/@github/copilot/index.js --headless --no-auto-update --log-level debug --stdio ache/node/22.22.0/x64/bin/git (http block)
    • Triggering command: /opt/hostedtoolcache/node/22.22.0/x64/bin/node /opt/hostedtoolcache/node/22.22.0/x64/bin/node /home/REDACTED/work/copilot-sdk/copilot-sdk/nodejs/node_modules/@github/copilot/index.js --headless --no-auto-update --log-level debug --stdio cal/bin/git (http block)
    • Triggering command: /opt/hostedtoolcache/node/22.22.0/x64/bin/node /opt/hostedtoolcache/node/22.22.0/x64/bin/node /home/REDACTED/work/copilot-sdk/copilot-sdk/nodejs/node_modules/@github/copilot/index.js --headless --no-auto-update --log-level debug --stdio rgo/bin/git (http block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

i want to review the code in this branch

Chronological Review: 1. User asked about branch changes → on main, no changes 2. User asked for 10 recent issues → listed, recommended #518 3. User asked to find issue about overriding tools → found #411 4. User entered plan mode → analyzed codebase extensively via explore agents, created plan with 5 todos 5. User entered fleet mode → dispatched 4 parallel agents for SDK implementations 6. All 4 agents completed, then docs agent dispatched and completed 7. User asked to commit, push, and open PR → done successfully

Intent Mapping:

Technical Inventory:

  • github/copilot-sdk repo with 4 SDKs: Node.js, Python, Go, .NET
  • Issue Support overriding built-in tools #411: Support overriding built-in tools
  • Solution: auto-add user tool names to excludedTools in RPC payload
  • mergeExcludedTools helper pattern across SDKs

Code changes across 13 files, all committed and pushed.

Recent Commands: git commit, git push, gh pr create → PR #522 created

1. Conversation Overview: - Primary Objectives: User wanted to explore the copilot-sdk repo, find a good issue to work on, then plan and implement Issue #411 ("Support overriding built-in tools"), and finally commit/push/open a PR. - Session Context: Started with repo exploration → issue review → found #411 about tool overrides → planned implementation → executed across all 4 SDKs in parallel → committed and opened PR #522. - User Intent Evolution: Browse issues → focus on #411 → plan → execute → ship.
  1. Technical Foundation:

    • Repo: github/copilot-sdk at /Users/patrick/projects/copilot-sdk
    • 4 SDKs: Node.js (nodejs/), Python (python/), Go (go/), .NET (dotnet/)
    • Issue Support overriding built-in tools #411: Users registering tools with names colliding with built-in CLI tools (e.g., Bash, Read, Edit) couldn't override them — CLI handled them instead of dispatching to user's handler.
    • Solution: Auto-add user-registered tool names to excludedTools in the session.create/session.resume RPC payload so CLI excludes its built-in and SDK dispatches locally.
    • Key architecture: SessionConfig.tools → mapped to RPC payload → CLI uses excludedTools to skip built-ins → SDK's tool.call handler dispatches to user's registered handler.
  2. Codebase Status:

    • nodejs/src/client.ts:
      • Added mergeExcludedTools() helper function (~L57)
      • Called in createSession (~L545) and resumeSession (~L623)
    • nodejs/test/client.test.ts:
      • 4 new tests: tools added to excludedTools, deduplication, no-op, resumeSession
    • python/copilot/client.py:
      • Inline merge logic in both create_session (~L483) and resume_session (~L658)
      • Uses dict.fromkeys() for deduplication
    • python/test_client.py:
      • 4 new tests in TestExcludedToolsFromRegisteredTools class; all 26 tests pass
    • go/client.go:
      • Added mergeExcludedTools() helper (~L1355)
      • Called in CreateSession (~L464) and ResumeSessionWithOptions (~L561)
    • go/client_test.go:
      • TestMergeExcludedTools with 4 sub-tests
    • dotnet/src/Client.cs:
      • Added MergeExcludedTools() internal static helper (~L855)
      • Called in CreateSessionAsync (~L385) and ResumeSessionAsync (~L470)
    • dotnet/src/GitHub.Copilot.SDK.csproj:
      • Added InternalsVisibleTo for test project
    • dotnet/test/MergeExcludedToolsTests.cs:
      • New file with 5 tests; all pass
    • All 4 READMEs updated with "Overriding Built-in Tools" subsection + code examples
  3. Problem Resolution:

    • Issue Support overriding built-in tools #411 comments confirmed: maintainer @SteveSandersonMS approved "override by name" approach
    • No externalToolDefinitions/externalToolDispatcher fields exist despite issue description — the SessionConfig.tools + excludedTools approach is the correct mechanism
    • Backward-compatible: users not naming tools after built-ins see no change
  4. Progress Tracking:

    • ✅ node-override — done
    • ✅ python-override — done
    • ✅ go-override — done
    • ✅ dotnet-override — done
    • ✅ docs-update — done
    • ✅ Committed, pushed, PR opened
  5. Active Work State:

  6. Recent Operations:

    • Last Agent Commands:
      1. git checkout -b support-tool-overrides && git add -A → branch created, all changes staged
      2. git commit -m "feat: auto-exclude built-in tools overridden by user-registered tools..." → committed as dc83960
      3. git push -u origin support-tool-overrides → pushed successfully
      4. gh pr create --title "feat: Support overriding built-in tools" --body "..." --base main → created PR feat: Support overriding built-in tools #522
    • Tool Results Summary: All succeeded. PR URL: https://github.com/git...

Created from Copilot CLI via the copilot delegate command.


🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

Auto-add user-registered tool names to excludedTools in session.create/resume
RPC payloads so that SDK-registered tools override CLI built-in tools.

- Node.js: mergeExcludedTools() helper + createSession/resumeSession updates
- Python: inline merge logic in create_session/resume_session
- Go: mergeExcludedTools() helper + CreateSession/ResumeSessionWithOptions updates
- .NET: MergeExcludedTools() helper + CreateSessionAsync/ResumeSessionAsync updates
- Tests added for all 4 SDKs
- All 4 READMEs updated with "Overriding Built-in Tools" documentation

Co-authored-by: patniko <26906478+patniko@users.noreply.github.com>
Copilot AI changed the title [WIP] Review code changes in branch feat: Auto-exclude built-in tools overridden by user-registered tools Feb 20, 2026
Copilot AI requested a review from patniko February 20, 2026 05:24
Resolve merge conflicts in 4 files:
- dotnet/src/Client.cs: Keep MergeExcludedTools with main's non-nullable config access
- go/client.go: Keep mergeExcludedTools with main's direct config access (no nil guard)
- go/client_test.go: Keep both MergeExcludedTools tests and permission handler tests
- python/test_client.py: Merge imports (both define_tool and PermissionHandler)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
patniko and others added 3 commits February 24, 2026 19:37
Add E2E tests across all 4 SDKs verifying that registering a custom tool
with the same name as a built-in tool (e.g., 'grep') causes the custom
tool to be invoked instead of the built-in. This validates the
mergeExcludedTools feature end-to-end.

- Add 'overrides built-in tool with custom tool' test to Node, Python, Go, .NET
- Add YAML snapshot for the replay proxy
- Add test/scenarios/tools/tool-overrides/ with all 4 language implementations

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Fix Python tests: add missing required argument to create_session() calls
- Fix Go: separate misplaced buildUnsupportedToolResult doc comment from mergeExcludedTools
- Fix Go sample: whitespace alignment from merge

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions
Copy link

✅ Cross-SDK Consistency Review: PASSED

I've reviewed PR #523 for consistency across all four SDK implementations (Node.js/TypeScript, Python, Go, and .NET). This PR demonstrates exemplary cross-SDK consistency in implementing the feature to auto-exclude built-in tools overridden by user-registered tools.

Feature Implementation Consistency

All four SDKs implement the same core functionality with appropriate language-specific idioms:

1️⃣ Helper Function Pattern

  • Node.js: mergeExcludedTools() - camelCase function ✅
  • Python: Inline implementation using dict.fromkeys() for deduplication ✅
  • Go: mergeExcludedTools() - camelCase (unexported/private) ✅
  • .NET: MergeExcludedTools() - PascalCase (internal static) ✅

All implementations properly deduplicate and merge tool names with existing excludedTools.

2️⃣ Integration Points

All SDKs apply the merge logic in both:

  • createSession / create_session / CreateSession / CreateSessionAsync
  • resumeSession / resume_session / ResumeSessionWithOptions / ResumeSessionAsync

3️⃣ Unit Test Coverage

Each SDK includes comprehensive unit tests covering:

  • ✅ Adding tool names to excludedTools
  • ✅ Deduplication with existing excludedTools
  • ✅ No-op when no tools provided
  • ✅ Resume session path

Test locations:

  • Node.js: nodejs/test/client.test.ts (4 tests in describe("excludedTools merging with config.tools"))
  • Python: python/test_client.py (TestExcludedToolsFromRegisteredTools class, 4 tests)
  • Go: go/client_test.go (TestMergeExcludedTools with 4 sub-tests)
  • .NET: dotnet/test/MergeExcludedToolsTests.cs (5 tests)

4️⃣ E2E Test Coverage

All SDKs include E2E tests verifying the feature works end-to-end:

  • ✅ Node.js: nodejs/test/e2e/tools.test.ts - "overrides built-in tool with custom tool"
  • ✅ Python: python/e2e/test_tools.py - test_overrides_built_in_tool_with_custom_tool
  • ✅ Go: go/internal/e2e/tools_test.go - "overrides built-in tool with custom tool"
  • ✅ .NET: dotnet/test/ToolsTests.cs - Overrides_Built_In_Tool_With_Custom_Tool

All E2E tests use the same approach: register a custom grep tool and verify it returns CUSTOM_GREP_RESULT instead of calling the built-in.

5️⃣ Documentation Consistency

All four READMEs include an identical "Overriding Built-in Tools" subsection with:

  • ✅ Clear explanation of the feature
  • ✅ Language-appropriate code example
  • ✅ Consistent messaging about automatic behavior

6️⃣ Test Scenarios & Snapshots

The PR includes cross-language example implementations in test/scenarios/tools/tool-overrides/:

  • ✅ TypeScript implementation
  • ✅ Python implementation
  • ✅ Go implementation
  • ✅ C# implementation
  • ✅ Shared verification script (verify.sh)
  • ✅ Shared snapshot (test/snapshots/tools/overrides_built_in_tool_with_custom_tool.yaml)

Language-Specific Differences (All Appropriate)

The only differences are idiomatic language conventions, which are correctly applied:

  1. Naming conventions: camelCase (TS/Python), camelCase for private/PascalCase for public (Go), PascalCase (.NET)
  2. Visibility modifiers:
    • Node.js: module-private function
    • Python: inline (no separate function needed)
    • Go: unexported function (lowercase)
    • .NET: internal static method with InternalsVisibleTo for testing
  3. Deduplication approach:
    • Node.js: Set constructor
    • Python: dict.fromkeys()
    • Go: map[string]bool for seen tracking
    • .NET: LINQ Union()

All approaches are idiomatic and achieve the same result.

Conclusion

🎉 This PR maintains perfect feature parity across all four SDK implementations. The feature is:

  • Implemented consistently with appropriate language idioms
  • Thoroughly tested with both unit and E2E tests
  • Well-documented in all READMEs
  • Includes comprehensive example scenarios

No cross-SDK consistency issues found. Excellent work! 👏

AI generated by SDK Consistency Review Agent

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions
Copy link

✅ Cross-SDK Consistency Review: PASSED

This PR demonstrates excellent cross-language consistency in implementing the auto-exclude feature for user-registered tools that override built-ins. All four SDK implementations maintain feature parity with properly adapted API patterns.

Summary of Changes

The PR adds functionality across all SDKs to automatically merge user-registered tool names into excludedTools when calling session.create or session.resume, ensuring custom tools override CLI built-ins with the same name.

Consistency Analysis

✅ Core Implementation Pattern

All SDKs implement the same logic with language-appropriate patterns:

SDK Helper Function Deduplication Method Location
Node.js mergeExcludedTools() Set for deduplication nodejs/src/client.ts:57
Python Inline logic dict.fromkeys() for deduplication python/copilot/client.py:498-501, 679-682
Go mergeExcludedTools() Map-based deduplication go/client.go:1358
.NET MergeExcludedTools() Union() for deduplication dotnet/src/Client.cs:865

✅ Integration Points

Consistently applied in both createSession/create_session/CreateSession/CreateSessionAsync and resumeSession/resume_session/ResumeSessionWithOptions/ResumeSessionAsync across all SDKs.

✅ Test Coverage

All SDKs include comprehensive unit and E2E tests:

Unit Tests (testing the merge logic):

  • Node.js: 4 tests in nodejs/test/client.test.ts
  • Python: 4 tests in python/test_client.py (TestExcludedToolsFromRegisteredTools)
  • Go: 4 sub-tests in go/client_test.go (TestMergeExcludedTools)
  • .NET: 5 tests in dotnet/test/MergeExcludedToolsTests.cs

E2E Tests (testing actual override behavior):

  • Node.js: nodejs/test/e2e/tools.test.ts - "overrides built-in tool with custom tool"
  • Python: python/e2e/test_tools.py - test_overrides_built_in_tool_with_custom_tool
  • Go: go/internal/e2e/tools_test.go - "overrides built-in tool with custom tool"
  • .NET: dotnet/test/ToolsTests.cs - Overrides_Built_In_Tool_With_Custom_Tool

All E2E tests use the same pattern: register a custom grep tool and verify it returns CUSTOM_GREP_RESULT.

✅ Documentation

Identical "Overriding Built-in Tools" subsections added to all four READMEs with language-appropriate code examples, consistent messaging, and proper placement in the "Custom Tools" section.

✅ Naming Conventions

Properly adapted to language idioms:

  • Node.js: excludedTools (camelCase)
  • Python: excluded_tools (snake_case)
  • Go: ExcludedTools (PascalCase - public field)
  • .NET: ExcludedTools (PascalCase)

Edge Cases Handled Consistently

All implementations correctly handle:

  • ✅ Null/empty tools list (no-op)
  • ✅ Null/empty excludedTools (returns tool names)
  • ✅ Deduplication when tool names appear in both lists
  • ✅ Both create and resume session flows

No Issues Found

This PR maintains excellent cross-SDK consistency. The feature is implemented uniformly across all languages with appropriate adaptations for each ecosystem's conventions and idioms. Great work! 🎉

AI generated by SDK Consistency Review Agent

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