Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
5 changes: 2 additions & 3 deletions docs/features/mcp.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,11 +143,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