Skip to content
Closed
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
637 changes: 485 additions & 152 deletions dotnet/src/Generated/Rpc.cs

Large diffs are not rendered by default.

668 changes: 570 additions & 98 deletions dotnet/src/Generated/SessionEvents.cs

Large diffs are not rendered by default.

72 changes: 72 additions & 0 deletions dotnet/test/Unit/SerializationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,78 @@ namespace GitHub.Copilot.Test.Unit;
/// </summary>
public class SerializationTests
{
[Fact]
public void CatalogSearchResult_DeserializesTypedCandidatesHandlesAndRefusals()
{
var options = GetSerializerOptions();
var json = """
{
"kind": "succeeded",
"searchId": "search-1",
"candidates": [
{
"kind": "mcp-server",
"handle": "mcp-handle",
"handleExpiresAt": "2026-09-02T12:00:00Z",
"mediaType": "application/mcp-server-card+json",
"installability": "installable",
"displayName": "Example MCP",
"source": { "kind": "url", "url": "https://example.com/mcp.json" },
"provenance": {
"authority": "example.com",
"observedAt": "2026-09-02T11:00:00Z",
"mediaType": "application/mcp-server-card+json"
}
},
{
"kind": "ai-skill",
"handle": "skill-handle",
"handleExpiresAt": "2026-09-02T12:00:00Z",
"mediaType": "application/ai-skill",
"installability": "not-installable-kind",
"displayName": "Example skill",
"source": { "kind": "embedded" },
"provenance": {
"authority": "example.com",
"observedAt": "2026-09-02T11:00:00Z",
"mediaType": "application/ai-skill"
}
}
],
"truncated": false,
"negotiated": {
"runtimeProtocolVersion": 1,
"grantedCapabilities": ["mcp-server-card", "ai-skill-discovery"]
}
}
""";

var result = JsonSerializer.Deserialize<CatalogSearchResult>(json, options);

var success = Assert.IsType<CatalogSearchResultSucceeded>(result);
var mcpCandidate = Assert.IsType<CatalogCandidateMcpServer>(success.Candidates[0]);
Assert.Equal("mcp-handle", mcpCandidate.Handle);
Assert.IsType<CatalogCandidateSourceUrl>(mcpCandidate.Source);
var skillCandidate = Assert.IsType<CatalogCandidateAiSkill>(success.Candidates[1]);
Assert.Equal("skill-handle", skillCandidate.Handle);
Assert.IsType<CatalogCandidateSourceEmbedded>(skillCandidate.Source);
Assert.Equal(
"""{"kind":"embedded"}""",
JsonSerializer.Serialize<CatalogCandidateSource>(skillCandidate.Source, options));

var refusal = JsonSerializer.Deserialize<CatalogSearchResult>(
"""
{
"kind": "unsupported-kind",
"message": "AI skills are unavailable",
"requestedKinds": ["ai-skill"],
"supportedKinds": ["mcp-server"]
}
""",
options);
Assert.IsType<CatalogSearchResultUnsupportedKind>(refusal);
}

[Fact]
public void ProviderConfig_CanSerializeHeaders_WithSdkOptions()
{
Expand Down
91 changes: 91 additions & 0 deletions go/rpc/generated_rpc_union_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ func TestExternalToolResultJSONUnion(t *testing.T) {
if err != nil {
t.Fatalf("marshal string result: %v", err)
}

if string(raw) != `"tool result"` {
t.Fatalf("marshal string result = %s", raw)
}
Expand Down Expand Up @@ -46,6 +47,96 @@ func TestExternalToolResultJSONUnion(t *testing.T) {
}
}

func TestCatalogSearchResultJSONUnion(t *testing.T) {
result, err := unmarshalCatalogSearchResult([]byte(`{
"kind":"succeeded",
"searchId":"search-1",
"candidates":[
{
"kind":"mcp-server",
"handle":"mcp-handle",
"handleExpiresAt":"2026-09-02T12:00:00Z",
"mediaType":"application/mcp-server-card+json",
"installability":"installable",
"displayName":"Example MCP",
"source":{"kind":"url","url":"https://example.com/mcp.json"},
"provenance":{
"authority":"example.com",
"observedAt":"2026-09-02T11:00:00Z",
"mediaType":"application/mcp-server-card+json"
}
},
{
"kind":"ai-skill",
"handle":"skill-handle",
"handleExpiresAt":"2026-09-02T12:00:00Z",
"mediaType":"application/ai-skill",
"installability":"not-installable-kind",
"displayName":"Example skill",
"source":{"kind":"embedded"},
"provenance":{
"authority":"example.com",
"observedAt":"2026-09-02T11:00:00Z",
"mediaType":"application/ai-skill"
}
}
],
"truncated":false,
"negotiated":{
"runtimeProtocolVersion":1,
"grantedCapabilities":["mcp-server-card","ai-skill-discovery"]
}
}`))
if err != nil {
t.Fatalf("unmarshal successful catalogue search: %v", err)
}
success, ok := result.(*CatalogSearchSucceeded)
if !ok {
t.Fatalf("catalogue search result = %T, want *CatalogSearchSucceeded", result)
}
mcpCandidate, ok := success.Candidates[0].(*CatalogMCPServerCandidate)
if !ok {
t.Fatalf("first candidate = %T, want *CatalogMCPServerCandidate", success.Candidates[0])
}
if mcpCandidate.Handle != "mcp-handle" {
t.Fatalf("MCP candidate handle = %q, want mcp-handle", mcpCandidate.Handle)
}
if _, ok := mcpCandidate.Source.(*CatalogCandidateSourceURL); !ok {
t.Fatalf("MCP candidate source = %T, want *CatalogCandidateSourceURL", mcpCandidate.Source)
}
skillCandidate, ok := success.Candidates[1].(*CatalogAiSkillCandidate)
if !ok {
t.Fatalf("second candidate = %T, want *CatalogAiSkillCandidate", success.Candidates[1])
}
if skillCandidate.Handle != "skill-handle" {
t.Fatalf("skill candidate handle = %q, want skill-handle", skillCandidate.Handle)
}
embedded, ok := skillCandidate.Source.(*CatalogCandidateSourceEmbedded)
if !ok {
t.Fatalf("skill candidate source = %T, want *CatalogCandidateSourceEmbedded", skillCandidate.Source)
}
encodedSource, err := json.Marshal(embedded)
if err != nil {
t.Fatalf("marshal embedded candidate source: %v", err)
}
if string(encodedSource) != `{"kind":"embedded"}` {
t.Fatalf("embedded candidate source = %s, want kind only", encodedSource)
}

refusal, err := unmarshalCatalogSearchResult([]byte(`{
"kind":"unsupported-kind",
"message":"AI skills are unavailable",
"requestedKinds":["ai-skill"],
"supportedKinds":["mcp-server"]
}`))
if err != nil {
t.Fatalf("unmarshal catalogue refusal: %v", err)
}
if _, ok := refusal.(*CatalogUnsupportedKindError); !ok {
t.Fatalf("catalogue refusal = %T, want *CatalogUnsupportedKindError", refusal)
}
}

func TestFilterMappingJSONUnion(t *testing.T) {
var mapping FilterMapping = FilterMappingEnumMap{"secret": ContentFilterModeHiddenCharacters}
raw, err := json.Marshal(mapping)
Expand Down
Loading
Loading