From a690c4bbf69fb0b52d1db6400558f857f9460dec Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Wed, 8 Apr 2026 11:15:59 -0400 Subject: [PATCH 1/2] Expose enableConfigDiscovery in all SDK languages Add enableConfigDiscovery option to session config types in Node.js, Python, Go, and .NET SDKs. When set to true, the runtime automatically discovers MCP server configurations (.mcp.json, .vscode/mcp.json) and skill directories from the working directory, merging them with any explicitly provided values (explicit takes precedence on name collision). This surfaces a capability already implemented in copilot-agent-runtime's resolveDiscoveredConfig() for SDK consumers. Changes per SDK: - Node.js: SessionConfig + ResumeSessionConfig pick + client passthrough - Python: create_session/resume_session params + payload serialization - Go: config structs + wire request structs + client passthrough - .NET: config classes + clone constructors + wire records + client passthrough Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- dotnet/src/Client.cs | 4 ++++ dotnet/src/Types.cs | 28 ++++++++++++++++++++++++++++ go/client.go | 6 ++++++ go/types.go | 16 ++++++++++++++++ nodejs/src/client.ts | 2 ++ nodejs/src/types.ts | 14 ++++++++++++++ python/copilot/client.py | 22 ++++++++++++++++++++++ 7 files changed, 92 insertions(+) diff --git a/dotnet/src/Client.cs b/dotnet/src/Client.cs index d5cb6707b..aad44e4eb 100644 --- a/dotnet/src/Client.cs +++ b/dotnet/src/Client.cs @@ -502,6 +502,7 @@ public async Task CreateSessionAsync(SessionConfig config, Cance config.CustomAgents, config.Agent, config.ConfigDir, + config.EnableConfigDiscovery, config.SkillDirectories, config.DisabledSkills, config.InfiniteSessions, @@ -618,6 +619,7 @@ public async Task ResumeSessionAsync(string sessionId, ResumeSes hasHooks ? true : null, config.WorkingDirectory, config.ConfigDir, + config.EnableConfigDiscovery, config.DisableResume is true ? true : null, config.Streaming is true ? true : null, config.McpServers, @@ -1640,6 +1642,7 @@ internal record CreateSessionRequest( List? CustomAgents, string? Agent, string? ConfigDir, + bool? EnableConfigDiscovery, List? SkillDirectories, List? DisabledSkills, InfiniteSessionConfig? InfiniteSessions, @@ -1686,6 +1689,7 @@ internal record ResumeSessionRequest( bool? Hooks, string? WorkingDirectory, string? ConfigDir, + bool? EnableConfigDiscovery, bool? DisableResume, bool? Streaming, Dictionary? McpServers, diff --git a/dotnet/src/Types.cs b/dotnet/src/Types.cs index 2f81f3b4c..d8262e140 100644 --- a/dotnet/src/Types.cs +++ b/dotnet/src/Types.cs @@ -1603,6 +1603,7 @@ protected SessionConfig(SessionConfig? other) CustomAgents = other.CustomAgents is not null ? [.. other.CustomAgents] : null; Agent = other.Agent; DisabledSkills = other.DisabledSkills is not null ? [.. other.DisabledSkills] : null; + EnableConfigDiscovery = other.EnableConfigDiscovery; ExcludedTools = other.ExcludedTools is not null ? [.. other.ExcludedTools] : null; Hooks = other.Hooks; InfiniteSessions = other.InfiniteSessions; @@ -1660,6 +1661,19 @@ protected SessionConfig(SessionConfig? other) /// public string? ConfigDir { get; set; } + /// + /// When , automatically discovers MCP server configurations + /// (e.g. .mcp.json, .vscode/mcp.json) and skill directories from + /// the working directory and merges them with any explicitly provided + /// and , with explicit + /// values taking precedence on name collision. + /// + /// Custom instruction files (.github/copilot-instructions.md, AGENTS.md, etc.) + /// are always loaded from the working directory regardless of this setting. + /// + /// + public bool? EnableConfigDiscovery { get; set; } + /// /// Custom tool functions available to the language model during the session. /// @@ -1817,6 +1831,7 @@ protected ResumeSessionConfig(ResumeSessionConfig? other) Agent = other.Agent; DisabledSkills = other.DisabledSkills is not null ? [.. other.DisabledSkills] : null; DisableResume = other.DisableResume; + EnableConfigDiscovery = other.EnableConfigDiscovery; ExcludedTools = other.ExcludedTools is not null ? [.. other.ExcludedTools] : null; Hooks = other.Hooks; InfiniteSessions = other.InfiniteSessions; @@ -1929,6 +1944,19 @@ protected ResumeSessionConfig(ResumeSessionConfig? other) /// public string? ConfigDir { get; set; } + /// + /// When , automatically discovers MCP server configurations + /// (e.g. .mcp.json, .vscode/mcp.json) and skill directories from + /// the working directory and merges them with any explicitly provided + /// and , with explicit + /// values taking precedence on name collision. + /// + /// Custom instruction files (.github/copilot-instructions.md, AGENTS.md, etc.) + /// are always loaded from the working directory regardless of this setting. + /// + /// + public bool? EnableConfigDiscovery { get; set; } + /// /// When true, the session.resume event is not emitted. /// Default: false (resume event is emitted). diff --git a/go/client.go b/go/client.go index f8d29cc98..ebea33209 100644 --- a/go/client.go +++ b/go/client.go @@ -578,6 +578,9 @@ func (c *Client) CreateSession(ctx context.Context, config *SessionConfig) (*Ses req.ClientName = config.ClientName req.ReasoningEffort = config.ReasoningEffort req.ConfigDir = config.ConfigDir + if config.EnableConfigDiscovery { + req.EnableConfigDiscovery = Bool(true) + } req.Tools = config.Tools wireSystemMessage, transformCallbacks := extractTransformCallbacks(config.SystemMessage) req.SystemMessage = wireSystemMessage @@ -754,6 +757,9 @@ func (c *Client) ResumeSessionWithOptions(ctx context.Context, sessionID string, } req.WorkingDirectory = config.WorkingDirectory req.ConfigDir = config.ConfigDir + if config.EnableConfigDiscovery { + req.EnableConfigDiscovery = Bool(true) + } if config.DisableResume { req.DisableResume = Bool(true) } diff --git a/go/types.go b/go/types.go index d80a80f54..5df2c1a7d 100644 --- a/go/types.go +++ b/go/types.go @@ -465,6 +465,13 @@ type SessionConfig struct { // ConfigDir overrides the default configuration directory location. // When specified, the session will use this directory for storing config and state. ConfigDir string + // EnableConfigDiscovery, when true, automatically discovers MCP server configurations + // (e.g. .mcp.json, .vscode/mcp.json) and skill directories from the working directory + // and merges them with any explicitly provided MCPServers and SkillDirectories, with + // explicit values taking precedence on name collision. + // Custom instruction files (.github/copilot-instructions.md, AGENTS.md, etc.) are + // always loaded from the working directory regardless of this setting. + EnableConfigDiscovery bool // Tools exposes caller-implemented tools to the CLI Tools []Tool // SystemMessage configures system message customization @@ -692,6 +699,13 @@ type ResumeSessionConfig struct { WorkingDirectory string // ConfigDir overrides the default configuration directory location. ConfigDir string + // EnableConfigDiscovery, when true, automatically discovers MCP server configurations + // (e.g. .mcp.json, .vscode/mcp.json) and skill directories from the working directory + // and merges them with any explicitly provided MCPServers and SkillDirectories, with + // explicit values taking precedence on name collision. + // Custom instruction files (.github/copilot-instructions.md, AGENTS.md, etc.) are + // always loaded from the working directory regardless of this setting. + EnableConfigDiscovery bool // Streaming enables streaming of assistant message and reasoning chunks. // When true, assistant.message_delta and assistant.reasoning_delta events // with deltaContent are sent as the response is generated. @@ -909,6 +923,7 @@ type createSessionRequest struct { CustomAgents []CustomAgentConfig `json:"customAgents,omitempty"` Agent string `json:"agent,omitempty"` ConfigDir string `json:"configDir,omitempty"` + EnableConfigDiscovery *bool `json:"enableConfigDiscovery,omitempty"` SkillDirectories []string `json:"skillDirectories,omitempty"` DisabledSkills []string `json:"disabledSkills,omitempty"` InfiniteSessions *InfiniteSessionConfig `json:"infiniteSessions,omitempty"` @@ -948,6 +963,7 @@ type resumeSessionRequest struct { Hooks *bool `json:"hooks,omitempty"` WorkingDirectory string `json:"workingDirectory,omitempty"` ConfigDir string `json:"configDir,omitempty"` + EnableConfigDiscovery *bool `json:"enableConfigDiscovery,omitempty"` DisableResume *bool `json:"disableResume,omitempty"` Streaming *bool `json:"streaming,omitempty"` MCPServers map[string]MCPServerConfig `json:"mcpServers,omitempty"` diff --git a/nodejs/src/client.ts b/nodejs/src/client.ts index 5fdbf0358..6941598b8 100644 --- a/nodejs/src/client.ts +++ b/nodejs/src/client.ts @@ -742,6 +742,7 @@ export class CopilotClient { customAgents: config.customAgents, agent: config.agent, configDir: config.configDir, + enableConfigDiscovery: config.enableConfigDiscovery, skillDirectories: config.skillDirectories, disabledSkills: config.disabledSkills, infiniteSessions: config.infiniteSessions, @@ -873,6 +874,7 @@ export class CopilotClient { hooks: !!(config.hooks && Object.values(config.hooks).some(Boolean)), workingDirectory: config.workingDirectory, configDir: config.configDir, + enableConfigDiscovery: config.enableConfigDiscovery, streaming: config.streaming, mcpServers: config.mcpServers, envValueMode: "direct", diff --git a/nodejs/src/types.ts b/nodejs/src/types.ts index 13367631f..c2d095234 100644 --- a/nodejs/src/types.ts +++ b/nodejs/src/types.ts @@ -1074,6 +1074,19 @@ export interface SessionConfig { */ configDir?: string; + /** + * When true, automatically discovers MCP server configurations (e.g. `.mcp.json`, + * `.vscode/mcp.json`) and skill directories from the working directory and merges + * them with any explicitly provided `mcpServers` and `skillDirectories`, with + * explicit values taking precedence on name collision. + * + * Note: custom instruction files (`.github/copilot-instructions.md`, `AGENTS.md`, etc.) + * are always loaded from the working directory regardless of this setting. + * + * @default false + */ + enableConfigDiscovery?: boolean; + /** * Tools exposed to the CLI server */ @@ -1226,6 +1239,7 @@ export type ResumeSessionConfig = Pick< | "hooks" | "workingDirectory" | "configDir" + | "enableConfigDiscovery" | "mcpServers" | "customAgents" | "agent" diff --git a/python/copilot/client.py b/python/copilot/client.py index 8be8b8220..d260dcc91 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -1202,6 +1202,7 @@ async def create_session( custom_agents: list[CustomAgentConfig] | None = None, agent: str | None = None, config_dir: str | None = None, + enable_config_discovery: bool | None = None, skill_directories: list[str] | None = None, disabled_skills: list[str] | None = None, infinite_sessions: InfiniteSessionConfig | None = None, @@ -1238,6 +1239,13 @@ async def create_session( custom_agents: Custom agent configurations. agent: Agent to use for the session. config_dir: Override for the configuration directory. + enable_config_discovery: When True, automatically discovers MCP server + configurations (e.g. ``.mcp.json``, ``.vscode/mcp.json``) and skill + directories from the working directory and merges them with any + explicitly provided ``mcp_servers`` and ``skill_directories``, with + explicit values taking precedence on name collision. Custom instruction + files (``.github/copilot-instructions.md``, ``AGENTS.md``, etc.) are + always loaded regardless of this setting. skill_directories: Directories to search for skills. disabled_skills: Skills to disable. infinite_sessions: Infinite session configuration. @@ -1362,6 +1370,10 @@ async def create_session( if config_dir: payload["configDir"] = config_dir + # Add config discovery flag if provided + if enable_config_discovery is not None: + payload["enableConfigDiscovery"] = enable_config_discovery + # Add skill directories configuration if provided if skill_directories: payload["skillDirectories"] = skill_directories @@ -1455,6 +1467,7 @@ async def resume_session( custom_agents: list[CustomAgentConfig] | None = None, agent: str | None = None, config_dir: str | None = None, + enable_config_discovery: bool | None = None, skill_directories: list[str] | None = None, disabled_skills: list[str] | None = None, infinite_sessions: InfiniteSessionConfig | None = None, @@ -1491,6 +1504,13 @@ async def resume_session( custom_agents: Custom agent configurations. agent: Agent to use for the session. config_dir: Override for the configuration directory. + enable_config_discovery: When True, automatically discovers MCP server + configurations (e.g. ``.mcp.json``, ``.vscode/mcp.json``) and skill + directories from the working directory and merges them with any + explicitly provided ``mcp_servers`` and ``skill_directories``, with + explicit values taking precedence on name collision. Custom instruction + files (``.github/copilot-instructions.md``, ``AGENTS.md``, etc.) are + always loaded regardless of this setting. skill_directories: Directories to search for skills. disabled_skills: Skills to disable. infinite_sessions: Infinite session configuration. @@ -1588,6 +1608,8 @@ async def resume_session( payload["workingDirectory"] = working_directory if config_dir: payload["configDir"] = config_dir + if enable_config_discovery is not None: + payload["enableConfigDiscovery"] = enable_config_discovery # TODO: disable_resume is not a keyword arg yet; keeping for future use if mcp_servers: From 59efcda2d299b2e65f0dbb3d81234c95fddf4be3 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Wed, 8 Apr 2026 11:23:36 -0400 Subject: [PATCH 2/2] Fix gofmt alignment in wire request structs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- go/types.go | 114 ++++++++++++++++++++++++++-------------------------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/go/types.go b/go/types.go index 5df2c1a7d..c26f075e3 100644 --- a/go/types.go +++ b/go/types.go @@ -903,34 +903,34 @@ type SessionLifecycleHandler func(event SessionLifecycleEvent) // createSessionRequest is the request for session.create type createSessionRequest struct { - Model string `json:"model,omitempty"` - SessionID string `json:"sessionId,omitempty"` - ClientName string `json:"clientName,omitempty"` - ReasoningEffort string `json:"reasoningEffort,omitempty"` - Tools []Tool `json:"tools,omitempty"` - SystemMessage *SystemMessageConfig `json:"systemMessage,omitempty"` - AvailableTools []string `json:"availableTools"` - ExcludedTools []string `json:"excludedTools,omitempty"` - Provider *ProviderConfig `json:"provider,omitempty"` - ModelCapabilities *rpc.ModelCapabilitiesOverride `json:"modelCapabilities,omitempty"` - RequestPermission *bool `json:"requestPermission,omitempty"` - RequestUserInput *bool `json:"requestUserInput,omitempty"` - Hooks *bool `json:"hooks,omitempty"` - WorkingDirectory string `json:"workingDirectory,omitempty"` - Streaming *bool `json:"streaming,omitempty"` - MCPServers map[string]MCPServerConfig `json:"mcpServers,omitempty"` - EnvValueMode string `json:"envValueMode,omitempty"` - CustomAgents []CustomAgentConfig `json:"customAgents,omitempty"` - Agent string `json:"agent,omitempty"` - ConfigDir string `json:"configDir,omitempty"` - EnableConfigDiscovery *bool `json:"enableConfigDiscovery,omitempty"` - SkillDirectories []string `json:"skillDirectories,omitempty"` - DisabledSkills []string `json:"disabledSkills,omitempty"` - InfiniteSessions *InfiniteSessionConfig `json:"infiniteSessions,omitempty"` - Commands []wireCommand `json:"commands,omitempty"` - RequestElicitation *bool `json:"requestElicitation,omitempty"` - Traceparent string `json:"traceparent,omitempty"` - Tracestate string `json:"tracestate,omitempty"` + Model string `json:"model,omitempty"` + SessionID string `json:"sessionId,omitempty"` + ClientName string `json:"clientName,omitempty"` + ReasoningEffort string `json:"reasoningEffort,omitempty"` + Tools []Tool `json:"tools,omitempty"` + SystemMessage *SystemMessageConfig `json:"systemMessage,omitempty"` + AvailableTools []string `json:"availableTools"` + ExcludedTools []string `json:"excludedTools,omitempty"` + Provider *ProviderConfig `json:"provider,omitempty"` + ModelCapabilities *rpc.ModelCapabilitiesOverride `json:"modelCapabilities,omitempty"` + RequestPermission *bool `json:"requestPermission,omitempty"` + RequestUserInput *bool `json:"requestUserInput,omitempty"` + Hooks *bool `json:"hooks,omitempty"` + WorkingDirectory string `json:"workingDirectory,omitempty"` + Streaming *bool `json:"streaming,omitempty"` + MCPServers map[string]MCPServerConfig `json:"mcpServers,omitempty"` + EnvValueMode string `json:"envValueMode,omitempty"` + CustomAgents []CustomAgentConfig `json:"customAgents,omitempty"` + Agent string `json:"agent,omitempty"` + ConfigDir string `json:"configDir,omitempty"` + EnableConfigDiscovery *bool `json:"enableConfigDiscovery,omitempty"` + SkillDirectories []string `json:"skillDirectories,omitempty"` + DisabledSkills []string `json:"disabledSkills,omitempty"` + InfiniteSessions *InfiniteSessionConfig `json:"infiniteSessions,omitempty"` + Commands []wireCommand `json:"commands,omitempty"` + RequestElicitation *bool `json:"requestElicitation,omitempty"` + Traceparent string `json:"traceparent,omitempty"` + Tracestate string `json:"tracestate,omitempty"` } // wireCommand is the wire representation of a command (name + description only, no handler). @@ -948,35 +948,35 @@ type createSessionResponse struct { // resumeSessionRequest is the request for session.resume type resumeSessionRequest struct { - SessionID string `json:"sessionId"` - ClientName string `json:"clientName,omitempty"` - Model string `json:"model,omitempty"` - ReasoningEffort string `json:"reasoningEffort,omitempty"` - Tools []Tool `json:"tools,omitempty"` - SystemMessage *SystemMessageConfig `json:"systemMessage,omitempty"` - AvailableTools []string `json:"availableTools"` - ExcludedTools []string `json:"excludedTools,omitempty"` - Provider *ProviderConfig `json:"provider,omitempty"` - ModelCapabilities *rpc.ModelCapabilitiesOverride `json:"modelCapabilities,omitempty"` - RequestPermission *bool `json:"requestPermission,omitempty"` - RequestUserInput *bool `json:"requestUserInput,omitempty"` - Hooks *bool `json:"hooks,omitempty"` - WorkingDirectory string `json:"workingDirectory,omitempty"` - ConfigDir string `json:"configDir,omitempty"` - EnableConfigDiscovery *bool `json:"enableConfigDiscovery,omitempty"` - DisableResume *bool `json:"disableResume,omitempty"` - Streaming *bool `json:"streaming,omitempty"` - MCPServers map[string]MCPServerConfig `json:"mcpServers,omitempty"` - EnvValueMode string `json:"envValueMode,omitempty"` - CustomAgents []CustomAgentConfig `json:"customAgents,omitempty"` - Agent string `json:"agent,omitempty"` - SkillDirectories []string `json:"skillDirectories,omitempty"` - DisabledSkills []string `json:"disabledSkills,omitempty"` - InfiniteSessions *InfiniteSessionConfig `json:"infiniteSessions,omitempty"` - Commands []wireCommand `json:"commands,omitempty"` - RequestElicitation *bool `json:"requestElicitation,omitempty"` - Traceparent string `json:"traceparent,omitempty"` - Tracestate string `json:"tracestate,omitempty"` + SessionID string `json:"sessionId"` + ClientName string `json:"clientName,omitempty"` + Model string `json:"model,omitempty"` + ReasoningEffort string `json:"reasoningEffort,omitempty"` + Tools []Tool `json:"tools,omitempty"` + SystemMessage *SystemMessageConfig `json:"systemMessage,omitempty"` + AvailableTools []string `json:"availableTools"` + ExcludedTools []string `json:"excludedTools,omitempty"` + Provider *ProviderConfig `json:"provider,omitempty"` + ModelCapabilities *rpc.ModelCapabilitiesOverride `json:"modelCapabilities,omitempty"` + RequestPermission *bool `json:"requestPermission,omitempty"` + RequestUserInput *bool `json:"requestUserInput,omitempty"` + Hooks *bool `json:"hooks,omitempty"` + WorkingDirectory string `json:"workingDirectory,omitempty"` + ConfigDir string `json:"configDir,omitempty"` + EnableConfigDiscovery *bool `json:"enableConfigDiscovery,omitempty"` + DisableResume *bool `json:"disableResume,omitempty"` + Streaming *bool `json:"streaming,omitempty"` + MCPServers map[string]MCPServerConfig `json:"mcpServers,omitempty"` + EnvValueMode string `json:"envValueMode,omitempty"` + CustomAgents []CustomAgentConfig `json:"customAgents,omitempty"` + Agent string `json:"agent,omitempty"` + SkillDirectories []string `json:"skillDirectories,omitempty"` + DisabledSkills []string `json:"disabledSkills,omitempty"` + InfiniteSessions *InfiniteSessionConfig `json:"infiniteSessions,omitempty"` + Commands []wireCommand `json:"commands,omitempty"` + RequestElicitation *bool `json:"requestElicitation,omitempty"` + Traceparent string `json:"traceparent,omitempty"` + Tracestate string `json:"tracestate,omitempty"` } // resumeSessionResponse is the response from session.resume