Skip to content
Merged
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
15 changes: 6 additions & 9 deletions docs/features/mcp.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,15 +113,13 @@ func main() {
}
defer client.Stop()

// MCPServerConfig is map[string]any for flexibility
session, err := client.CreateSession(ctx, &copilot.SessionConfig{
Model: "gpt-5",
MCPServers: map[string]copilot.MCPServerConfig{
"my-local-server": {
"type": "local",
"command": "node",
"args": []string{"./mcp-server.js"},
"tools": []string{"*"},
"my-local-server": copilot.MCPStdioServerConfig{
Command: "node",
Args: []string{"./mcp-server.js"},
Tools: []string{"*"},
},
},
})
Expand All @@ -143,11 +141,10 @@ await using var client = new CopilotClient();
await using var session = await client.CreateSessionAsync(new SessionConfig
{
Model = "gpt-5",
McpServers = new Dictionary<string, object>
McpServers = new Dictionary<string, McpServerConfig>
{
["my-local-server"] = new McpLocalServerConfig
["my-local-server"] = new McpStdioServerConfig
{
Type = "local",
Command = "node",
Args = new List<string> { "./mcp-server.js" },
Tools = new List<string> { "*" },
Expand Down
26 changes: 10 additions & 16 deletions docs/troubleshooting/mcp-debugging.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,19 +250,17 @@ public static class McpDotnetConfigExample
{
public static void Main()
{
var servers = new Dictionary<string, McpLocalServerConfig>
var servers = new Dictionary<string, McpServerConfig>
{
["my-dotnet-server"] = new McpLocalServerConfig
["my-dotnet-server"] = new McpStdioServerConfig
{
Type = "local",
Command = @"C:\Tools\MyServer\MyServer.exe",
Args = new List<string>(),
Cwd = @"C:\Tools\MyServer",
Tools = new List<string> { "*" },
},
["my-dotnet-tool"] = new McpLocalServerConfig
["my-dotnet-tool"] = new McpStdioServerConfig
{
Type = "local",
Command = "dotnet",
Args = new List<string> { @"C:\Tools\MyTool\MyTool.dll" },
Cwd = @"C:\Tools\MyTool",
Expand All @@ -275,19 +273,17 @@ public static class McpDotnetConfigExample
<!-- /docs-validate: hidden -->
```csharp
// Correct configuration for .NET exe
["my-dotnet-server"] = new McpLocalServerConfig
["my-dotnet-server"] = new McpStdioServerConfig
{
Type = "local",
Command = @"C:\Tools\MyServer\MyServer.exe", // Full path with .exe
Args = new List<string>(),
Cwd = @"C:\Tools\MyServer", // Set working directory
Tools = new List<string> { "*" },
}

// For dotnet tool (DLL)
["my-dotnet-tool"] = new McpLocalServerConfig
["my-dotnet-tool"] = new McpStdioServerConfig
{
Type = "local",
Command = "dotnet",
Args = new List<string> { @"C:\Tools\MyTool\MyTool.dll" },
Cwd = @"C:\Tools\MyTool",
Expand All @@ -305,11 +301,10 @@ public static class McpNpxConfigExample
{
public static void Main()
{
var servers = new Dictionary<string, McpLocalServerConfig>
var servers = new Dictionary<string, McpServerConfig>
{
["filesystem"] = new McpLocalServerConfig
["filesystem"] = new McpStdioServerConfig
{
Type = "local",
Command = "cmd",
Args = new List<string> { "/c", "npx", "-y", "@modelcontextprotocol/server-filesystem", "C:\\allowed\\path" },
Tools = new List<string> { "*" },
Expand All @@ -321,9 +316,8 @@ public static class McpNpxConfigExample
<!-- /docs-validate: hidden -->
```csharp
// Windows needs cmd /c for npx
["filesystem"] = new McpLocalServerConfig
["filesystem"] = new McpStdioServerConfig
{
Type = "local",
Command = "cmd",
Args = new List<string> { "/c", "npx", "-y", "@modelcontextprotocol/server-filesystem", "C:\\allowed\\path" },
Tools = new List<string> { "*" },
Expand Down Expand Up @@ -357,9 +351,9 @@ xattr -d com.apple.quarantine /path/to/mcp-server

<!-- docs-validate: hidden -->
```typescript
import { MCPLocalServerConfig } from "@github/copilot-sdk";
import { MCPStdioServerConfig } from "@github/copilot-sdk";

const mcpServers: Record<string, MCPLocalServerConfig> = {
const mcpServers: Record<string, MCPStdioServerConfig> = {
"my-server": {
command: "/opt/homebrew/bin/node",
args: ["/path/to/server.js"],
Expand Down
4 changes: 2 additions & 2 deletions dotnet/src/Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1637,7 +1637,7 @@ internal record CreateSessionRequest(
bool? Hooks,
string? WorkingDirectory,
bool? Streaming,
Dictionary<string, object>? McpServers,
Dictionary<string, McpServerConfig>? McpServers,
string? EnvValueMode,
List<CustomAgentConfig>? CustomAgents,
string? Agent,
Expand Down Expand Up @@ -1692,7 +1692,7 @@ internal record ResumeSessionRequest(
bool? EnableConfigDiscovery,
bool? DisableResume,
bool? Streaming,
Dictionary<string, object>? McpServers,
Dictionary<string, McpServerConfig>? McpServers,
string? EnvValueMode,
List<CustomAgentConfig>? CustomAgents,
string? Agent,
Expand Down
66 changes: 34 additions & 32 deletions dotnet/src/Types.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1413,27 +1413,44 @@ public class AzureOptions
// ============================================================================

/// <summary>
/// Configuration for a local/stdio MCP server.
/// Abstract base class for MCP server configurations.
/// </summary>
public class McpLocalServerConfig
[JsonPolymorphic(
TypeDiscriminatorPropertyName = "type",
IgnoreUnrecognizedTypeDiscriminators = true)]
[JsonDerivedType(typeof(McpStdioServerConfig), "stdio")]
[JsonDerivedType(typeof(McpHttpServerConfig), "http")]
public abstract class McpServerConfig
{
private protected McpServerConfig() { }

/// <summary>
/// List of tools to include from this server. Empty list means none. Use "*" for all.
/// </summary>
[JsonPropertyName("tools")]
public List<string> Tools { get; set; } = [];

/// <summary>
/// Server type. Defaults to "local".
/// The server type discriminator.
/// </summary>
[JsonPropertyName("type")]
public string? Type { get; set; }
[JsonIgnore]
public virtual string Type => "unknown";

/// <summary>
/// Optional timeout in milliseconds for tool calls to this server.
/// </summary>
[JsonPropertyName("timeout")]
public int? Timeout { get; set; }
}

/// <summary>
/// Configuration for a local/stdio MCP server.
/// </summary>
public sealed class McpStdioServerConfig : McpServerConfig
{
/// <inheritdoc />
[JsonIgnore]
public override string Type => "stdio";

/// <summary>
/// Command to run the MCP server.
Expand Down Expand Up @@ -1463,25 +1480,11 @@ public class McpLocalServerConfig
/// <summary>
/// Configuration for a remote MCP server (HTTP or SSE).
/// </summary>
public class McpRemoteServerConfig
public sealed class McpHttpServerConfig : McpServerConfig
{
/// <summary>
/// List of tools to include from this server. Empty list means none. Use "*" for all.
/// </summary>
[JsonPropertyName("tools")]
public List<string> Tools { get; set; } = [];

/// <summary>
/// Server type. Must be "http" or "sse".
/// </summary>
[JsonPropertyName("type")]
public string Type { get; set; } = "http";

/// <summary>
/// Optional timeout in milliseconds for tool calls to this server.
/// </summary>
[JsonPropertyName("timeout")]
public int? Timeout { get; set; }
/// <inheritdoc />
[JsonIgnore]
public override string Type => "http";

/// <summary>
/// URL of the remote server.
Expand Down Expand Up @@ -1539,7 +1542,7 @@ public class CustomAgentConfig
/// MCP servers specific to this agent.
/// </summary>
[JsonPropertyName("mcpServers")]
public Dictionary<string, object>? McpServers { get; set; }
public Dictionary<string, McpServerConfig>? McpServers { get; set; }

/// <summary>
/// Whether the agent should be available for model inference.
Expand Down Expand Up @@ -1608,7 +1611,7 @@ protected SessionConfig(SessionConfig? other)
Hooks = other.Hooks;
InfiniteSessions = other.InfiniteSessions;
McpServers = other.McpServers is not null
? new Dictionary<string, object>(other.McpServers, other.McpServers.Comparer)
? new Dictionary<string, McpServerConfig>(other.McpServers, other.McpServers.Comparer)
: null;
Model = other.Model;
ModelCapabilities = other.ModelCapabilities;
Expand Down Expand Up @@ -1740,9 +1743,9 @@ protected SessionConfig(SessionConfig? other)

/// <summary>
/// MCP server configurations for the session.
/// Keys are server names, values are server configurations (McpLocalServerConfig or McpRemoteServerConfig).
/// Keys are server names, values are server configurations (<see cref="McpStdioServerConfig"/> or <see cref="McpHttpServerConfig"/>).
/// </summary>
public Dictionary<string, object>? McpServers { get; set; }
public Dictionary<string, McpServerConfig>? McpServers { get; set; }

/// <summary>
/// Custom agent configurations for the session.
Expand Down Expand Up @@ -1836,7 +1839,7 @@ protected ResumeSessionConfig(ResumeSessionConfig? other)
Hooks = other.Hooks;
InfiniteSessions = other.InfiniteSessions;
McpServers = other.McpServers is not null
? new Dictionary<string, object>(other.McpServers, other.McpServers.Comparer)
? new Dictionary<string, McpServerConfig>(other.McpServers, other.McpServers.Comparer)
: null;
Model = other.Model;
ModelCapabilities = other.ModelCapabilities;
Expand Down Expand Up @@ -1972,9 +1975,9 @@ protected ResumeSessionConfig(ResumeSessionConfig? other)

/// <summary>
/// MCP server configurations for the session.
/// Keys are server names, values are server configurations (McpLocalServerConfig or McpRemoteServerConfig).
/// Keys are server names, values are server configurations (<see cref="McpStdioServerConfig"/> or <see cref="McpHttpServerConfig"/>).
/// </summary>
public Dictionary<string, object>? McpServers { get; set; }
public Dictionary<string, McpServerConfig>? McpServers { get; set; }

/// <summary>
/// Custom agent configurations for the session.
Expand Down Expand Up @@ -2519,8 +2522,7 @@ public class SystemMessageTransformRpcResponse
[JsonSerializable(typeof(GetForegroundSessionResponse))]
[JsonSerializable(typeof(GetModelsResponse))]
[JsonSerializable(typeof(GetStatusResponse))]
[JsonSerializable(typeof(McpLocalServerConfig))]
[JsonSerializable(typeof(McpRemoteServerConfig))]
[JsonSerializable(typeof(McpServerConfig))]
[JsonSerializable(typeof(MessageOptions))]
[JsonSerializable(typeof(ModelBilling))]
[JsonSerializable(typeof(ModelCapabilities))]
Expand Down
14 changes: 7 additions & 7 deletions dotnet/test/CloneTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public void SessionConfig_Clone_CopiesAllProperties()
ExcludedTools = ["tool3"],
WorkingDirectory = "/workspace",
Streaming = true,
McpServers = new Dictionary<string, object> { ["server1"] = new object() },
McpServers = new Dictionary<string, McpServerConfig> { ["server1"] = new McpStdioServerConfig { Command = "echo" } },
CustomAgents = [new CustomAgentConfig { Name = "agent1" }],
Agent = "agent1",
SkillDirectories = ["/skills"],
Expand Down Expand Up @@ -118,7 +118,7 @@ public void SessionConfig_Clone_CollectionsAreIndependent()
{
AvailableTools = ["tool1"],
ExcludedTools = ["tool2"],
McpServers = new Dictionary<string, object> { ["s1"] = new object() },
McpServers = new Dictionary<string, McpServerConfig> { ["s1"] = new McpStdioServerConfig { Command = "echo" } },
CustomAgents = [new CustomAgentConfig { Name = "a1" }],
SkillDirectories = ["/skills"],
DisabledSkills = ["skill1"],
Expand All @@ -129,7 +129,7 @@ public void SessionConfig_Clone_CollectionsAreIndependent()
// Mutate clone collections
clone.AvailableTools!.Add("tool99");
clone.ExcludedTools!.Add("tool99");
clone.McpServers!["s2"] = new object();
clone.McpServers!["s2"] = new McpStdioServerConfig { Command = "echo" };
clone.CustomAgents!.Add(new CustomAgentConfig { Name = "a2" });
clone.SkillDirectories!.Add("/more");
clone.DisabledSkills!.Add("skill99");
Expand All @@ -146,7 +146,7 @@ public void SessionConfig_Clone_CollectionsAreIndependent()
[Fact]
public void SessionConfig_Clone_PreservesMcpServersComparer()
{
var servers = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase) { ["server"] = new object() };
var servers = new Dictionary<string, McpServerConfig>(StringComparer.OrdinalIgnoreCase) { ["server"] = new McpStdioServerConfig { Command = "echo" } };
var original = new SessionConfig { McpServers = servers };

var clone = original.Clone();
Expand All @@ -161,7 +161,7 @@ public void ResumeSessionConfig_Clone_CollectionsAreIndependent()
{
AvailableTools = ["tool1"],
ExcludedTools = ["tool2"],
McpServers = new Dictionary<string, object> { ["s1"] = new object() },
McpServers = new Dictionary<string, McpServerConfig> { ["s1"] = new McpStdioServerConfig { Command = "echo" } },
CustomAgents = [new CustomAgentConfig { Name = "a1" }],
SkillDirectories = ["/skills"],
DisabledSkills = ["skill1"],
Expand All @@ -172,7 +172,7 @@ public void ResumeSessionConfig_Clone_CollectionsAreIndependent()
// Mutate clone collections
clone.AvailableTools!.Add("tool99");
clone.ExcludedTools!.Add("tool99");
clone.McpServers!["s2"] = new object();
clone.McpServers!["s2"] = new McpStdioServerConfig { Command = "echo" };
clone.CustomAgents!.Add(new CustomAgentConfig { Name = "a2" });
clone.SkillDirectories!.Add("/more");
clone.DisabledSkills!.Add("skill99");
Expand All @@ -189,7 +189,7 @@ public void ResumeSessionConfig_Clone_CollectionsAreIndependent()
[Fact]
public void ResumeSessionConfig_Clone_PreservesMcpServersComparer()
{
var servers = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase) { ["server"] = new object() };
var servers = new Dictionary<string, McpServerConfig>(StringComparer.OrdinalIgnoreCase) { ["server"] = new McpStdioServerConfig { Command = "echo" } };
var original = new ResumeSessionConfig { McpServers = servers };

var clone = original.Clone();
Expand Down
Loading
Loading