Skip to content

Make tool callbacks optional across SDKs#1308

Open
stephentoub wants to merge 3 commits into
mainfrom
stephentoub/optional-tool-callbacks
Open

Make tool callbacks optional across SDKs#1308
stephentoub wants to merge 3 commits into
mainfrom
stephentoub/optional-tool-callbacks

Conversation

@stephentoub
Copy link
Copy Markdown
Collaborator

Tools previously assumed SDK-managed callbacks were always available, which made it difficult for consumers to pause/resume work and manually resolve permission or external tool requests from events. This PR makes callback-backed execution optional while preserving the existing automatic behavior whenever a callback or handler is supplied.

Summary

  • Allows create/resume sessions to omit permission handlers so permission requests can remain pending for manual resolution.
  • Supports declaration-only external tools across .NET, Node, Python, Go, and Rust while still auto-invoking tools with callbacks.
  • Adds end-to-end manual permission/tool-result resume samples for every SDK.
  • Updates docs and focused tests to cover both SDK-managed callbacks and consumer-managed pending request flows.

Notes

The .NET samples are file-based apps using dotnet run --file, so the existing chat sample and new manual resume sample can live side by side without project glob conflicts.

Validation

  • dotnet build dotnet\GitHub.Copilot.SDK.slnx --nologo
  • dotnet build dotnet\samples\Chat.cs --nologo
  • dotnet build dotnet\samples\ManualToolResume.cs --nologo
  • cd nodejs && npm run typecheck -- --pretty false
  • cd python && python -m py_compile samples\manual_tool_resume.py
  • cd go\samples && go test ./...
  • cd rust && cargo check --all-features --example manual_tool_resume

Copilot AI review requested due to automatic review settings May 15, 2026 21:05
@stephentoub stephentoub requested a review from a team as a code owner May 15, 2026 21:05
Comment thread python/copilot/tools.py Fixed
@stephentoub
Copy link
Copy Markdown
Collaborator Author

@SteveSandersonMS I'd appreciate your eyes on this one.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Makes permission handlers and external-tool handlers optional across all five SDKs (Node, Python, Go, .NET, Rust). Previously, omitting these handlers caused errors; now requests are surfaced as events and left pending for the consumer to resolve via the new pending permission/tool RPCs. Adds an end-to-end "manual tool resume" sample for each SDK and updates docs/tests accordingly.

Changes:

  • Drops "required" validation on OnPermissionRequest/onPermissionRequest/on_permission_request and makes the handler argument nullable; tool definitions also accept a missing handler so tools can be declaration-only.
  • Adds a new Rust NoopHandler (replacing DenyAllHandler as the default), introduces HandlerResponse::NoResult, and routes external-tool requests/notifications without handlers to leave them pending.
  • Adds samples/manual_tool_resume.* for all SDKs and converts the .NET samples to dotnet run --file style (removing Chat.csproj from the solution).
Show a summary per file
File Description
rust/src/types.rs Doc updates and switch from DenyAllHandler to NoopHandler as the default in builder paths.
rust/src/session.rs Default handler swapped to NoopHandler; new notification_tool_payload/direct_tool_result helpers handle NoResult for external tool calls.
rust/src/handler.rs Adds HandlerResponse::NoResult and the new NoopHandler implementation with tests.
rust/README.md Reformats tables and documents NoopHandler as the default.
rust/examples/manual_tool_resume.rs New end-to-end example for manual permission/tool resume.
python/copilot/{client,session,tools,init}.py Permission handler optional in create/resume; declaration-only tool support; Tool/ToolResult re-exported.
python/test_client.py, python/test_tools.py Tests updated for optional handlers and declaration-only tools.
python/samples/manual_tool_resume.py, python/README.md New sample and updated docs.
nodejs/src/{client,session,types}.ts Removes required-handler checks, makes Tool.handler optional, only registers tools that have handlers.
nodejs/test/client.test.ts Tests updated to use offline client/session for the new optional behavior.
nodejs/samples/manual-tool-resume.ts, nodejs/README.md New sample and updated docs.
go/{client,session,types}.go Removes required-handler validation; Tool.Handler documented as optional.
go/client_test.go Renames/repurposes tests for optional handler behavior.
go/samples/manual_tool_resume/main.go, go/README.md New sample and updated docs.
dotnet/src/{Client,Session,Types}.cs Removes required-handler validation; Tools switched to ICollection<AIFunctionDeclaration>; only declarations exposing an AIFunction get auto-invoked.
dotnet/test/E2E/ClientE2ETests.cs Tests updated to assert successful create/resume without permission handler.
dotnet/samples/{Chat.cs,ManualToolResume.cs}, Chat.csproj, GitHub.Copilot.SDK.slnx, dotnet/README.md Switch samples to dotnet run --file; new manual-resume sample; remove Chat.csproj.

Copilot's findings

  • Files reviewed: 34/34 changed files
  • Comments generated: 0

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

Copilot AI added 3 commits May 18, 2026 07:26
Allow SDK consumers to provide declaration-only tools and manually resolve permission and tool requests when callbacks are omitted. Preserve automatic SDK handling when callbacks are supplied, and add manual resume samples across languages.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use explicit pass bodies for define_tool overload declarations so code-quality checks do not flag no-op ellipsis statements.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Assert create and resume sessions work without permission callbacks now that those callbacks are optional.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@stephentoub stephentoub force-pushed the stephentoub/optional-tool-callbacks branch from 309b7b9 to 79d9c47 Compare May 18, 2026 12:55
@github-actions
Copy link
Copy Markdown
Contributor

Cross-SDK Consistency Review ✅

This PR implements the optional permission handler and declaration-only tool features consistently across all five SDK implementations (Node.js/TypeScript, Python, Go, .NET, and Rust). Here's a summary of what was verified:

Feature parity confirmed

Feature Node.js Python Go .NET Rust
Optional onPermissionRequest
Declaration-only tools (no handler)
Manual resume sample

Implementation consistency

Optional permission handler: All SDKs removed the validation that threw when onPermissionRequest was missing, and updated documentation to describe the new "leave pending" semantics. The Rust implementation correctly replaced DenyAllHandler with the new NoopHandler as the default, which aligns with the other SDKs' "leave pending" behavior rather than the old "deny immediately" behavior.

Declaration-only tools: Each SDK made the handler field optional in its idiomatic way:

  • Node.js/TypeScript: handler?: ToolHandler<TArgs>
  • Python: handler: ToolHandler | None = None
  • Go: documented Handler ToolHandler as optional (nil-safe in registerTools)
  • .NET: Changed ICollection<AIFunction>?ICollection<AIFunctionDeclaration>? (backing AIFunction is accessed via GetService<AIFunction>())
  • Rust: Tool struct was already declaration-only; NoopHandler now correctly returns HandlerResponse::NoResult for unhandled external tool calls

ResumeSessionConfig coverage: All SDKs consistently apply the changes to both create-session and resume-session paths. The Node.js ResumeSessionConfig inherits the optional onPermissionRequest correctly via Pick<SessionConfig, ...>.

One behavioral note worth being aware of

The Rust default handler change from DenyAllHandlerNoopHandler is intentional but worth calling out explicitly: existing Rust consumers who relied on the implicit "safe deny" behavior (no handler = all permissions denied, no stalling) will now see sessions left pending instead of being denied. The PR documents this change, but consumers upgrading should be aware of this behavioral shift.

Overall, the cross-SDK implementation is thorough and well-aligned. No additional changes needed for consistency.

Generated by SDK Consistency Review Agent for issue #1308 · ● 1.9M ·

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.

3 participants