Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions dotnet/src/Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,23 @@ public async Task<List<ModelInfo>> ListModelsAsync(CancellationToken cancellatio
}
}

/// <summary>
/// Lists available built-in tools with their metadata.
/// </summary>
/// <param name="model">Optional model ID to get model-specific tool overrides.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
/// <returns>A task that resolves with a list of available tools.</returns>
/// <exception cref="InvalidOperationException">Thrown when the client is not connected.</exception>
public async Task<List<ToolInfoItem>> ListToolsAsync(string? model = null, CancellationToken cancellationToken = default)
{
var connection = await EnsureConnectedAsync(cancellationToken);

var response = await InvokeRpcAsync<GetToolsResponse>(
connection.Rpc, "tools.list", [new ListToolsRequest { Model = model }], cancellationToken);

return response.Tools;
}

/// <summary>
/// Gets the ID of the most recently used session.
/// </summary>
Expand Down Expand Up @@ -1385,6 +1402,11 @@ internal record UserInputRequestResponse(
internal record HooksInvokeResponse(
object? Output);

internal record ListToolsRequest
{
public string? Model { get; init; }
}

/// <summary>Trace source that forwards all logs to the ILogger.</summary>
internal sealed class LoggerTraceSource : TraceSource
{
Expand Down Expand Up @@ -1439,6 +1461,7 @@ public override void WriteLine(string? message) =>
[JsonSerializable(typeof(GetLastSessionIdResponse))]
[JsonSerializable(typeof(HooksInvokeResponse))]
[JsonSerializable(typeof(ListSessionsResponse))]
[JsonSerializable(typeof(ListToolsRequest))]
[JsonSerializable(typeof(PermissionRequestResponse))]
[JsonSerializable(typeof(PermissionRequestResult))]
[JsonSerializable(typeof(ProviderConfig))]
Expand Down
37 changes: 37 additions & 0 deletions dotnet/src/Types.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1063,6 +1063,41 @@ public class GetModelsResponse
public List<ModelInfo> Models { get; set; } = new();
}

/// <summary>
/// Information about an available built-in tool
/// </summary>
public class ToolInfoItem
{
/// <summary>Tool identifier (e.g., "bash", "grep", "str_replace_editor")</summary>
[JsonPropertyName("name")]
public string Name { get; set; } = string.Empty;

/// <summary>Optional namespaced name for declarative filtering (e.g., "playwright/navigate" for MCP tools)</summary>
[JsonPropertyName("namespacedName")]
public string? NamespacedName { get; set; }

/// <summary>Description of what the tool does</summary>
[JsonPropertyName("description")]
public string Description { get; set; } = string.Empty;

/// <summary>JSON Schema for the tool's input parameters</summary>
[JsonPropertyName("parameters")]
public JsonElement? Parameters { get; set; }

/// <summary>Optional instructions for how to use this tool effectively</summary>
[JsonPropertyName("instructions")]
public string? Instructions { get; set; }
}

/// <summary>
/// Response from tools.list
/// </summary>
public class GetToolsResponse
{
[JsonPropertyName("tools")]
public List<ToolInfoItem> Tools { get; set; } = new();
}

// ============================================================================
// Session Lifecycle Types (for TUI+server mode)
// ============================================================================
Expand Down Expand Up @@ -1143,6 +1178,7 @@ public class SetForegroundSessionResponse
[JsonSerializable(typeof(GetAuthStatusResponse))]
[JsonSerializable(typeof(GetForegroundSessionResponse))]
[JsonSerializable(typeof(GetModelsResponse))]
[JsonSerializable(typeof(GetToolsResponse))]
[JsonSerializable(typeof(GetStatusResponse))]
[JsonSerializable(typeof(McpLocalServerConfig))]
[JsonSerializable(typeof(McpRemoteServerConfig))]
Expand All @@ -1165,6 +1201,7 @@ public class SetForegroundSessionResponse
[JsonSerializable(typeof(SetForegroundSessionResponse))]
[JsonSerializable(typeof(SystemMessageConfig))]
[JsonSerializable(typeof(ToolBinaryResult))]
[JsonSerializable(typeof(ToolInfoItem))]
[JsonSerializable(typeof(ToolInvocation))]
[JsonSerializable(typeof(ToolResultObject))]
[JsonSerializable(typeof(JsonElement))]
Expand Down
29 changes: 29 additions & 0 deletions dotnet/test/ClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,35 @@ public async Task Should_List_Models_When_Authenticated()
}
}

[Fact]
public async Task Should_List_Tools()
{
using var client = new CopilotClient(new CopilotClientOptions { UseStdio = true });

try
{
await client.StartAsync();

var tools = await client.ListToolsAsync();
Assert.NotNull(tools);
Assert.True(tools.Count > 0, "Expected at least one tool");
if (tools.Count > 0)
{
var tool = tools[0];
Assert.NotNull(tool.Name);
Assert.NotEmpty(tool.Name);
Assert.NotNull(tool.Description);
Assert.NotEmpty(tool.Description);
}

await client.StopAsync();
}
finally
{
await client.ForceStopAsync();
}
}

[Fact]
public void Should_Accept_GithubToken_Option()
{
Expand Down
21 changes: 21 additions & 0 deletions go/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -970,6 +970,27 @@ func (c *Client) ListModels(ctx context.Context) ([]ModelInfo, error) {
return models, nil
}

// ListTools returns available built-in tools with their metadata.
//
// When a model is provided, the returned tool list reflects model-specific overrides.
func (c *Client) ListTools(ctx context.Context, model string) ([]ToolInfo, error) {
if c.client == nil {
return nil, fmt.Errorf("client not connected")
}

result, err := c.client.Request("tools.list", listToolsRequest{Model: model})
if err != nil {
return nil, err
}

var response listToolsResponse
if err := json.Unmarshal(result, &response); err != nil {
return nil, fmt.Errorf("failed to unmarshal tools response: %w", err)
}

return response.Tools, nil
}

// verifyProtocolVersion verifies that the server's protocol version matches the SDK's expected version
func (c *Client) verifyProtocolVersion(ctx context.Context) error {
expectedVersion := GetSdkProtocolVersion()
Expand Down
33 changes: 33 additions & 0 deletions go/internal/e2e/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,4 +225,37 @@ func TestClient(t *testing.T) {

client.Stop()
})

t.Run("should list tools", func(t *testing.T) {
client := copilot.NewClient(&copilot.ClientOptions{
CLIPath: cliPath,
UseStdio: copilot.Bool(true),
})
t.Cleanup(func() { client.ForceStop() })

if err := client.Start(t.Context()); err != nil {
t.Fatalf("Failed to start client: %v", err)
}

tools, err := client.ListTools(t.Context(), "")
if err != nil {
t.Fatalf("Failed to list tools: %v", err)
}

if len(tools) == 0 {
t.Error("Expected at least one tool")
}

if len(tools) > 0 {
tool := tools[0]
if tool.Name == "" {
t.Error("Expected tool.Name to be non-empty")
}
if tool.Description == "" {
t.Error("Expected tool.Description to be non-empty")
}
}

client.Stop()
})
}
19 changes: 19 additions & 0 deletions go/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,15 @@ type ModelInfo struct {
DefaultReasoningEffort string `json:"defaultReasoningEffort,omitempty"`
}

// ToolInfo contains information about an available built-in tool
type ToolInfo struct {
Name string `json:"name"`
NamespacedName string `json:"namespacedName,omitempty"`
Description string `json:"description"`
Parameters map[string]interface{} `json:"parameters,omitempty"`
Instructions string `json:"instructions,omitempty"`
}

// SessionMetadata contains metadata about a session
type SessionMetadata struct {
SessionID string `json:"sessionId"`
Expand Down Expand Up @@ -733,6 +742,16 @@ type listModelsResponse struct {
Models []ModelInfo `json:"models"`
}

// listToolsRequest is the request for tools.list
type listToolsRequest struct {
Model string `json:"model,omitempty"`
}

// listToolsResponse is the response from tools.list
type listToolsResponse struct {
Tools []ToolInfo `json:"tools"`
}

// sessionGetMessagesRequest is the request for session.getMessages
type sessionGetMessagesRequest struct {
SessionID string `json:"sessionId"`
Expand Down
56 changes: 28 additions & 28 deletions nodejs/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion nodejs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"author": "GitHub",
"license": "MIT",
"dependencies": {
"@github/copilot": "^0.0.405",
"@github/copilot": "^0.0.407",
"vscode-jsonrpc": "^8.2.1",
"zod": "^4.3.6"
},
Expand Down
19 changes: 19 additions & 0 deletions nodejs/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import type {
ToolCallRequestPayload,
ToolCallResponsePayload,
ToolHandler,
ToolInfo,
ToolResult,
ToolResultObject,
TypedSessionLifecycleHandler,
Expand Down Expand Up @@ -721,6 +722,24 @@ export class CopilotClient {
}
}

/**
* List available built-in tools with their metadata.
*
* Returns the list of tools available in the runtime, optionally filtered
* by model-specific overrides when a model ID is provided.
*
* @param model - Optional model ID to get model-specific tool overrides
*/
async listTools(model?: string): Promise<ToolInfo[]> {
if (!this.connection) {
throw new Error("Client not connected");
}

const result = await this.connection.sendRequest("tools.list", { model });
const response = result as { tools: ToolInfo[] };
return response.tools;
}

/**
* Verify that the server's protocol version matches the SDK's expected version
*/
Expand Down
1 change: 1 addition & 0 deletions nodejs/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export type {
SystemMessageReplaceConfig,
Tool,
ToolHandler,
ToolInfo,
ToolInvocation,
ToolResultObject,
TypedSessionEventHandler,
Expand Down
Loading
Loading