Skip to content

Commit c1c7a85

Browse files
stephentoubCopilot
andcommitted
Refactor MCP server config types across all SDK languages
Introduce an abstract McpServerConfig base class in C# with a private protected constructor and sealed derived types McpStdioServerConfig and McpHttpServerConfig. Shared properties (Tools, Type, Timeout) are deduplicated into the base class. The Type property uses the JsonPolymorphic discriminator pattern consistent with SessionEvent. All Dictionary<string, object> McpServers properties are now strongly typed as Dictionary<string, McpServerConfig>. Rename Local/Remote to Stdio/Http across all four SDK languages to match MCP protocol terminology: - C#: McpStdioServerConfig / McpHttpServerConfig - TypeScript: MCPStdioServerConfig / MCPHttpServerConfig - Go: MCPStdioServerConfig / MCPHTTPServerConfig - Python: MCPStdioServerConfig / MCPHttpServerConfig Update documentation examples accordingly. Fixes #245 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 6029b37 commit c1c7a85

11 files changed

Lines changed: 89 additions & 101 deletions

File tree

docs/features/mcp.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,11 +143,10 @@ await using var client = new CopilotClient();
143143
await using var session = await client.CreateSessionAsync(new SessionConfig
144144
{
145145
Model = "gpt-5",
146-
McpServers = new Dictionary<string, object>
146+
McpServers = new Dictionary<string, McpServerConfig>
147147
{
148-
["my-local-server"] = new McpLocalServerConfig
148+
["my-local-server"] = new McpStdioServerConfig
149149
{
150-
Type = "local",
151150
Command = "node",
152151
Args = new List<string> { "./mcp-server.js" },
153152
Tools = new List<string> { "*" },

docs/troubleshooting/mcp-debugging.md

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -250,19 +250,17 @@ public static class McpDotnetConfigExample
250250
{
251251
public static void Main()
252252
{
253-
var servers = new Dictionary<string, McpLocalServerConfig>
253+
var servers = new Dictionary<string, McpServerConfig>
254254
{
255-
["my-dotnet-server"] = new McpLocalServerConfig
255+
["my-dotnet-server"] = new McpStdioServerConfig
256256
{
257-
Type = "local",
258257
Command = @"C:\Tools\MyServer\MyServer.exe",
259258
Args = new List<string>(),
260259
Cwd = @"C:\Tools\MyServer",
261260
Tools = new List<string> { "*" },
262261
},
263-
["my-dotnet-tool"] = new McpLocalServerConfig
262+
["my-dotnet-tool"] = new McpStdioServerConfig
264263
{
265-
Type = "local",
266264
Command = "dotnet",
267265
Args = new List<string> { @"C:\Tools\MyTool\MyTool.dll" },
268266
Cwd = @"C:\Tools\MyTool",
@@ -275,19 +273,17 @@ public static class McpDotnetConfigExample
275273
<!-- /docs-validate: hidden -->
276274
```csharp
277275
// Correct configuration for .NET exe
278-
["my-dotnet-server"] = new McpLocalServerConfig
276+
["my-dotnet-server"] = new McpStdioServerConfig
279277
{
280-
Type = "local",
281278
Command = @"C:\Tools\MyServer\MyServer.exe", // Full path with .exe
282279
Args = new List<string>(),
283280
Cwd = @"C:\Tools\MyServer", // Set working directory
284281
Tools = new List<string> { "*" },
285282
}
286283

287284
// For dotnet tool (DLL)
288-
["my-dotnet-tool"] = new McpLocalServerConfig
285+
["my-dotnet-tool"] = new McpStdioServerConfig
289286
{
290-
Type = "local",
291287
Command = "dotnet",
292288
Args = new List<string> { @"C:\Tools\MyTool\MyTool.dll" },
293289
Cwd = @"C:\Tools\MyTool",
@@ -305,11 +301,10 @@ public static class McpNpxConfigExample
305301
{
306302
public static void Main()
307303
{
308-
var servers = new Dictionary<string, McpLocalServerConfig>
304+
var servers = new Dictionary<string, McpServerConfig>
309305
{
310-
["filesystem"] = new McpLocalServerConfig
306+
["filesystem"] = new McpStdioServerConfig
311307
{
312-
Type = "local",
313308
Command = "cmd",
314309
Args = new List<string> { "/c", "npx", "-y", "@modelcontextprotocol/server-filesystem", "C:\\allowed\\path" },
315310
Tools = new List<string> { "*" },
@@ -321,9 +316,8 @@ public static class McpNpxConfigExample
321316
<!-- /docs-validate: hidden -->
322317
```csharp
323318
// Windows needs cmd /c for npx
324-
["filesystem"] = new McpLocalServerConfig
319+
["filesystem"] = new McpStdioServerConfig
325320
{
326-
Type = "local",
327321
Command = "cmd",
328322
Args = new List<string> { "/c", "npx", "-y", "@modelcontextprotocol/server-filesystem", "C:\\allowed\\path" },
329323
Tools = new List<string> { "*" },
@@ -357,9 +351,9 @@ xattr -d com.apple.quarantine /path/to/mcp-server
357351

358352
<!-- docs-validate: hidden -->
359353
```typescript
360-
import { MCPLocalServerConfig } from "@github/copilot-sdk";
354+
import { MCPStdioServerConfig } from "@github/copilot-sdk";
361355

362-
const mcpServers: Record<string, MCPLocalServerConfig> = {
356+
const mcpServers: Record<string, MCPStdioServerConfig> = {
363357
"my-server": {
364358
command: "/opt/homebrew/bin/node",
365359
args: ["/path/to/server.js"],

dotnet/src/Client.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1637,7 +1637,7 @@ internal record CreateSessionRequest(
16371637
bool? Hooks,
16381638
string? WorkingDirectory,
16391639
bool? Streaming,
1640-
Dictionary<string, object>? McpServers,
1640+
Dictionary<string, McpServerConfig>? McpServers,
16411641
string? EnvValueMode,
16421642
List<CustomAgentConfig>? CustomAgents,
16431643
string? Agent,
@@ -1692,7 +1692,7 @@ internal record ResumeSessionRequest(
16921692
bool? EnableConfigDiscovery,
16931693
bool? DisableResume,
16941694
bool? Streaming,
1695-
Dictionary<string, object>? McpServers,
1695+
Dictionary<string, McpServerConfig>? McpServers,
16961696
string? EnvValueMode,
16971697
List<CustomAgentConfig>? CustomAgents,
16981698
string? Agent,

dotnet/src/Types.cs

Lines changed: 34 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1413,27 +1413,44 @@ public class AzureOptions
14131413
// ============================================================================
14141414

14151415
/// <summary>
1416-
/// Configuration for a local/stdio MCP server.
1416+
/// Abstract base class for MCP server configurations.
14171417
/// </summary>
1418-
public class McpLocalServerConfig
1418+
[JsonPolymorphic(
1419+
TypeDiscriminatorPropertyName = "type",
1420+
IgnoreUnrecognizedTypeDiscriminators = true)]
1421+
[JsonDerivedType(typeof(McpStdioServerConfig), "stdio")]
1422+
[JsonDerivedType(typeof(McpHttpServerConfig), "http")]
1423+
public abstract class McpServerConfig
14191424
{
1425+
private protected McpServerConfig() { }
1426+
14201427
/// <summary>
14211428
/// List of tools to include from this server. Empty list means none. Use "*" for all.
14221429
/// </summary>
14231430
[JsonPropertyName("tools")]
14241431
public List<string> Tools { get; set; } = [];
14251432

14261433
/// <summary>
1427-
/// Server type. Defaults to "local".
1434+
/// The server type discriminator.
14281435
/// </summary>
1429-
[JsonPropertyName("type")]
1430-
public string? Type { get; set; }
1436+
[JsonIgnore]
1437+
public virtual string Type => "unknown";
14311438

14321439
/// <summary>
14331440
/// Optional timeout in milliseconds for tool calls to this server.
14341441
/// </summary>
14351442
[JsonPropertyName("timeout")]
14361443
public int? Timeout { get; set; }
1444+
}
1445+
1446+
/// <summary>
1447+
/// Configuration for a local/stdio MCP server.
1448+
/// </summary>
1449+
public sealed class McpStdioServerConfig : McpServerConfig
1450+
{
1451+
/// <inheritdoc />
1452+
[JsonIgnore]
1453+
public override string Type => "stdio";
14371454

14381455
/// <summary>
14391456
/// Command to run the MCP server.
@@ -1463,25 +1480,11 @@ public class McpLocalServerConfig
14631480
/// <summary>
14641481
/// Configuration for a remote MCP server (HTTP or SSE).
14651482
/// </summary>
1466-
public class McpRemoteServerConfig
1483+
public sealed class McpHttpServerConfig : McpServerConfig
14671484
{
1468-
/// <summary>
1469-
/// List of tools to include from this server. Empty list means none. Use "*" for all.
1470-
/// </summary>
1471-
[JsonPropertyName("tools")]
1472-
public List<string> Tools { get; set; } = [];
1473-
1474-
/// <summary>
1475-
/// Server type. Must be "http" or "sse".
1476-
/// </summary>
1477-
[JsonPropertyName("type")]
1478-
public string Type { get; set; } = "http";
1479-
1480-
/// <summary>
1481-
/// Optional timeout in milliseconds for tool calls to this server.
1482-
/// </summary>
1483-
[JsonPropertyName("timeout")]
1484-
public int? Timeout { get; set; }
1485+
/// <inheritdoc />
1486+
[JsonIgnore]
1487+
public override string Type => "http";
14851488

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

15441547
/// <summary>
15451548
/// Whether the agent should be available for model inference.
@@ -1608,7 +1611,7 @@ protected SessionConfig(SessionConfig? other)
16081611
Hooks = other.Hooks;
16091612
InfiniteSessions = other.InfiniteSessions;
16101613
McpServers = other.McpServers is not null
1611-
? new Dictionary<string, object>(other.McpServers, other.McpServers.Comparer)
1614+
? new Dictionary<string, McpServerConfig>(other.McpServers, other.McpServers.Comparer)
16121615
: null;
16131616
Model = other.Model;
16141617
ModelCapabilities = other.ModelCapabilities;
@@ -1740,9 +1743,9 @@ protected SessionConfig(SessionConfig? other)
17401743

17411744
/// <summary>
17421745
/// MCP server configurations for the session.
1743-
/// Keys are server names, values are server configurations (McpLocalServerConfig or McpRemoteServerConfig).
1746+
/// Keys are server names, values are server configurations (<see cref="McpStdioServerConfig"/> or <see cref="McpHttpServerConfig"/>).
17441747
/// </summary>
1745-
public Dictionary<string, object>? McpServers { get; set; }
1748+
public Dictionary<string, McpServerConfig>? McpServers { get; set; }
17461749

17471750
/// <summary>
17481751
/// Custom agent configurations for the session.
@@ -1836,7 +1839,7 @@ protected ResumeSessionConfig(ResumeSessionConfig? other)
18361839
Hooks = other.Hooks;
18371840
InfiniteSessions = other.InfiniteSessions;
18381841
McpServers = other.McpServers is not null
1839-
? new Dictionary<string, object>(other.McpServers, other.McpServers.Comparer)
1842+
? new Dictionary<string, McpServerConfig>(other.McpServers, other.McpServers.Comparer)
18401843
: null;
18411844
Model = other.Model;
18421845
ModelCapabilities = other.ModelCapabilities;
@@ -1972,9 +1975,9 @@ protected ResumeSessionConfig(ResumeSessionConfig? other)
19721975

19731976
/// <summary>
19741977
/// MCP server configurations for the session.
1975-
/// Keys are server names, values are server configurations (McpLocalServerConfig or McpRemoteServerConfig).
1978+
/// Keys are server names, values are server configurations (<see cref="McpStdioServerConfig"/> or <see cref="McpHttpServerConfig"/>).
19761979
/// </summary>
1977-
public Dictionary<string, object>? McpServers { get; set; }
1980+
public Dictionary<string, McpServerConfig>? McpServers { get; set; }
19781981

19791982
/// <summary>
19801983
/// Custom agent configurations for the session.
@@ -2519,8 +2522,7 @@ public class SystemMessageTransformRpcResponse
25192522
[JsonSerializable(typeof(GetForegroundSessionResponse))]
25202523
[JsonSerializable(typeof(GetModelsResponse))]
25212524
[JsonSerializable(typeof(GetStatusResponse))]
2522-
[JsonSerializable(typeof(McpLocalServerConfig))]
2523-
[JsonSerializable(typeof(McpRemoteServerConfig))]
2525+
[JsonSerializable(typeof(McpServerConfig))]
25242526
[JsonSerializable(typeof(MessageOptions))]
25252527
[JsonSerializable(typeof(ModelBilling))]
25262528
[JsonSerializable(typeof(ModelCapabilities))]

dotnet/test/CloneTests.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ public void SessionConfig_Clone_CopiesAllProperties()
8686
ExcludedTools = ["tool3"],
8787
WorkingDirectory = "/workspace",
8888
Streaming = true,
89-
McpServers = new Dictionary<string, object> { ["server1"] = new object() },
89+
McpServers = new Dictionary<string, McpServerConfig> { ["server1"] = new McpStdioServerConfig { Command = "echo" } },
9090
CustomAgents = [new CustomAgentConfig { Name = "agent1" }],
9191
Agent = "agent1",
9292
SkillDirectories = ["/skills"],
@@ -118,7 +118,7 @@ public void SessionConfig_Clone_CollectionsAreIndependent()
118118
{
119119
AvailableTools = ["tool1"],
120120
ExcludedTools = ["tool2"],
121-
McpServers = new Dictionary<string, object> { ["s1"] = new object() },
121+
McpServers = new Dictionary<string, McpServerConfig> { ["s1"] = new McpStdioServerConfig { Command = "echo" } },
122122
CustomAgents = [new CustomAgentConfig { Name = "a1" }],
123123
SkillDirectories = ["/skills"],
124124
DisabledSkills = ["skill1"],
@@ -129,7 +129,7 @@ public void SessionConfig_Clone_CollectionsAreIndependent()
129129
// Mutate clone collections
130130
clone.AvailableTools!.Add("tool99");
131131
clone.ExcludedTools!.Add("tool99");
132-
clone.McpServers!["s2"] = new object();
132+
clone.McpServers!["s2"] = new McpStdioServerConfig { Command = "echo" };
133133
clone.CustomAgents!.Add(new CustomAgentConfig { Name = "a2" });
134134
clone.SkillDirectories!.Add("/more");
135135
clone.DisabledSkills!.Add("skill99");
@@ -146,7 +146,7 @@ public void SessionConfig_Clone_CollectionsAreIndependent()
146146
[Fact]
147147
public void SessionConfig_Clone_PreservesMcpServersComparer()
148148
{
149-
var servers = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase) { ["server"] = new object() };
149+
var servers = new Dictionary<string, McpServerConfig>(StringComparer.OrdinalIgnoreCase) { ["server"] = new McpStdioServerConfig { Command = "echo" } };
150150
var original = new SessionConfig { McpServers = servers };
151151

152152
var clone = original.Clone();
@@ -161,7 +161,7 @@ public void ResumeSessionConfig_Clone_CollectionsAreIndependent()
161161
{
162162
AvailableTools = ["tool1"],
163163
ExcludedTools = ["tool2"],
164-
McpServers = new Dictionary<string, object> { ["s1"] = new object() },
164+
McpServers = new Dictionary<string, McpServerConfig> { ["s1"] = new McpStdioServerConfig { Command = "echo" } },
165165
CustomAgents = [new CustomAgentConfig { Name = "a1" }],
166166
SkillDirectories = ["/skills"],
167167
DisabledSkills = ["skill1"],
@@ -172,7 +172,7 @@ public void ResumeSessionConfig_Clone_CollectionsAreIndependent()
172172
// Mutate clone collections
173173
clone.AvailableTools!.Add("tool99");
174174
clone.ExcludedTools!.Add("tool99");
175-
clone.McpServers!["s2"] = new object();
175+
clone.McpServers!["s2"] = new McpStdioServerConfig { Command = "echo" };
176176
clone.CustomAgents!.Add(new CustomAgentConfig { Name = "a2" });
177177
clone.SkillDirectories!.Add("/more");
178178
clone.DisabledSkills!.Add("skill99");
@@ -189,7 +189,7 @@ public void ResumeSessionConfig_Clone_CollectionsAreIndependent()
189189
[Fact]
190190
public void ResumeSessionConfig_Clone_PreservesMcpServersComparer()
191191
{
192-
var servers = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase) { ["server"] = new object() };
192+
var servers = new Dictionary<string, McpServerConfig>(StringComparer.OrdinalIgnoreCase) { ["server"] = new McpStdioServerConfig { Command = "echo" } };
193193
var original = new ResumeSessionConfig { McpServers = servers };
194194

195195
var clone = original.Clone();

0 commit comments

Comments
 (0)