From eb2aad66daa421e364096fd3bccdca4807f745bf Mon Sep 17 00:00:00 2001 From: CodeMash Date: Tue, 13 Oct 2026 12:03:12 +0300 Subject: [PATCH 1/2] chore(types): regenerate hub types from gateway refactoringV2 e011478fe (MCP tool catalog, route 24) Adds GetMarketplaceIntegrationTools, GetMarketplaceIntegrationToolsResponse and MarketplaceMcpToolDto. No type removed. Co-Authored-By: Claude Opus 5.5 --- src/Norbix.Sdk.Types/Generated/Hub.dtos.cs | 41 ++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/Norbix.Sdk.Types/Generated/Hub.dtos.cs b/src/Norbix.Sdk.Types/Generated/Hub.dtos.cs index 0e921cc..c456187 100644 --- a/src/Norbix.Sdk.Types/Generated/Hub.dtos.cs +++ b/src/Norbix.Sdk.Types/Generated/Hub.dtos.cs @@ -4128,6 +4128,28 @@ public partial class MarketplaceListingProjection public virtual string? VideoUrl { get; set; } } + [DataContract] + public partial class MarketplaceMcpToolDto + { + [DataMember] + public virtual string Name { get; set; } + + [DataMember] + public virtual string? Title { get; set; } + + [DataMember] + public virtual string? Description { get; set; } + + [DataMember] + public virtual string InputSchema { get; set; } + + [DataMember] + public virtual bool? ReadOnlyHint { get; set; } + + [DataMember] + public virtual bool? DestructiveHint { get; set; } + } + public enum MarketplaceParameterLocation { Body, @@ -11598,6 +11620,25 @@ public partial class GetMarketplaceIntegrationsResponse public virtual PaginatedResponse? List { get; set; } } + [NorbixRoute("/{version}/code/marketplace/integrations/{IntegrationViewId}/tools", "GET")] + public partial class GetMarketplaceIntegrationTools + : CodeMashRequestBase, INorbixRequest + { + /// + ///Integration view id, from get_marketplace_integrations. + /// + public virtual string IntegrationViewId { get; set; } + } + + public partial class GetMarketplaceIntegrationToolsResponse + : ResponseBase + { + public virtual IReadOnlyList Tools { get; set; } + public virtual DateTimeOffset? ListedAt { get; set; } + public virtual string? CatalogHash { get; set; } + public virtual bool Truncated { get; set; } + } + [NorbixRoute("/{version}/code/marketplace/integrations/{IntegrationViewId}/secrets", "PUT")] [DataContract] public partial class ReplaceMarketplaceIntegrationSecretsRequest From c3095a60dc3be351c388c83c76039b0fec8c2ff6 Mon Sep 17 00:00:00 2001 From: CodeMash Date: Tue, 13 Oct 2026 12:05:27 +0300 Subject: [PATCH 2/2] feat(marketplace): GetMarketplaceIntegrationToolsAsync, the MCP tool catalog (route 24) The method comes from the regenerated Hub types (source generator). Adds MarketplaceMcpToolsTests (id in the path, no body, catalog read back), accepts the Hub.Code coverage snapshot (38 -> 39 endpoints) and a README section. Co-Authored-By: Claude Opus 5.5 --- README.md | 20 +++++++ .../MarketplaceMcpToolsTests.cs | 57 +++++++++++++++++++ ...ndpointCoverageTests.Hub.Code.verified.txt | 37 +++++++++++- ...n_id_in_the_path_with_no_body.verified.txt | 23 ++++++++ 4 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 tests/Norbix.Hub.Tests/MarketplaceMcpToolsTests.cs create mode 100644 tests/Norbix.Hub.Tests/test_results/MarketplaceMcpToolsTests.Tools_put_the_connection_id_in_the_path_with_no_body.verified.txt diff --git a/README.md b/README.md index dfade1d..6786ea9 100644 --- a/README.md +++ b/README.md @@ -1028,6 +1028,26 @@ foreach (var run in runs?.Runs ?? []) } ``` +## Marketplace MCP connections — tool catalog + +An MCP connection (a Marketplace connection whose listing uses the MCP +transport) stores its server's tool catalog when it is validated. +`Code.GetMarketplaceIntegrationToolsAsync` reads it +(`GET /{version}/code/marketplace/integrations/{IntegrationViewId}/tools`): +each tool has `Name`, `Title`, `Description`, `InputSchema` (JSON Schema as +text), `ReadOnlyHint` and `DestructiveHint`; the response also carries +`ListedAt`, `CatalogHash` and `Truncated`. A connection that was never +validated answers `CM-ERRORS-MARKETPLACE-020`. A function on a tool is saved +with `SaveMarketplaceFunction` (`Function.FunctionKey` = the tool name). + +```csharp +var catalog = await hub.Code.GetMarketplaceIntegrationToolsAsync( + new GetMarketplaceIntegrationTools { IntegrationViewId = "int_…" }); + +foreach (var tool in catalog?.Tools ?? []) + Console.WriteLine($"{tool.Name} {(tool.ReadOnlyHint == true ? "read-only" : "writes")}"); +``` + ## Coverage Notes Generated coverage tracks the API and Hub DTO contract files. Some flows are not generated automatically: diff --git a/tests/Norbix.Hub.Tests/MarketplaceMcpToolsTests.cs b/tests/Norbix.Hub.Tests/MarketplaceMcpToolsTests.cs new file mode 100644 index 0000000..661103a --- /dev/null +++ b/tests/Norbix.Hub.Tests/MarketplaceMcpToolsTests.cs @@ -0,0 +1,57 @@ +using Norbix.Sdk.Tests.Helpers; +using Norbix.Sdk.Types.Hub; + +using NUnit.Framework; + +using VerifyNUnit; + +namespace Norbix.Hub.Tests; + +/// +/// GET /{version}/code/marketplace/integrations/{IntegrationViewId}/tools returns +/// the stored tool catalog of an MCP connection. The coverage snapshot sends a +/// placeholder id; this case pins a real connection id in the path, no query +/// value for it, no body, and the catalog read back from the generated response. +/// Nothing leaves the process. +/// +[TestFixture] +public sealed class MarketplaceMcpToolsTests +{ + private static NorbixTestFixture NewFixture() => + NorbixTestFixture.Create(o => + { + o.AccountId = "test-account"; + o.BearerToken = "test-bearer"; + }); + + [Test] + public async Task Tools_put_the_connection_id_in_the_path_with_no_body() + { + using var fixture = NewFixture(); + fixture.RespondNext(new + { + tools = new[] + { + new + { + name = "create_issue", + title = "Create issue", + description = "Open a new issue in a repository", + inputSchema = "{\"type\":\"object\",\"properties\":{\"title\":{\"type\":\"string\"}},\"required\":[\"title\"]}", + readOnlyHint = false, + destructiveHint = false, + }, + }, + listedAt = "2026-10-13T08:00:00Z", + catalogHash = "4ce4259bec02f283", + truncated = false, + }); + + var response = await fixture.Client.Code.GetMarketplaceIntegrationToolsAsync( + new GetMarketplaceIntegrationTools { IntegrationViewId = "int_7Hq2mcpServer" }); + + await Verifier.Verify( + new { Sent = fixture.LastRequest, response!.Tools, response.CatalogHash, response.Truncated }, + VerifyConfig.VerifySettings); + } +} diff --git a/tests/Norbix.Hub.Tests/test_results/EndpointCoverageTests.Hub.Code.verified.txt b/tests/Norbix.Hub.Tests/test_results/EndpointCoverageTests.Hub.Code.verified.txt index d18ee68..1a9d872 100644 --- a/tests/Norbix.Hub.Tests/test_results/EndpointCoverageTests.Hub.Code.verified.txt +++ b/tests/Norbix.Hub.Tests/test_results/EndpointCoverageTests.Hub.Code.verified.txt @@ -1,7 +1,7 @@ { Target: Hub, Group: code, - Count: 38, + Count: 39, Endpoints: [ { Target: Hub, @@ -1060,6 +1060,41 @@ } } }, + { + Target: Hub, + Group: code, + MethodName: GetMarketplaceIntegrationToolsAsync, + RequestType: GetMarketplaceIntegrationTools, + HttpMethod: GET, + Path: /{version}/code/marketplace/integrations/{IntegrationViewId}/tools, + PathParams: [ + IntegrationViewId + ], + IsAccountScoped: false, + IsUnauthenticated: false, + Sent: { + Method: GET, + Path: /v2/code/marketplace/integrations/{guid}/tools, + Query: ?env=test-env, + Headers: { + Authorization: Bearer test-bearer, + norbix-account-id: test-account, + norbix-project-id: test-project + } + }, + ResponseType: GetMarketplaceIntegrationToolsResponse, + Response: { + Tools: [ + {} + ], + ListedAt: DateTimeOffset_1, + CatalogHash: test-catalogHash, + Truncated: true, + ResponseStatus: { + IsSuccess: true + } + } + }, { Target: Hub, Group: code, diff --git a/tests/Norbix.Hub.Tests/test_results/MarketplaceMcpToolsTests.Tools_put_the_connection_id_in_the_path_with_no_body.verified.txt b/tests/Norbix.Hub.Tests/test_results/MarketplaceMcpToolsTests.Tools_put_the_connection_id_in_the_path_with_no_body.verified.txt new file mode 100644 index 0000000..b460bff --- /dev/null +++ b/tests/Norbix.Hub.Tests/test_results/MarketplaceMcpToolsTests.Tools_put_the_connection_id_in_the_path_with_no_body.verified.txt @@ -0,0 +1,23 @@ +{ + Sent: { + Method: GET, + Path: /v2/code/marketplace/integrations/int_7Hq2mcpServer/tools, + Headers: { + Authorization: Bearer test-bearer, + norbix-account-id: test-account, + norbix-project-id: test-project + } + }, + Tools: [ + { + Name: create_issue, + Title: Create issue, + Description: Open a new issue in a repository, + InputSchema: {"type":"object","properties":{"title":{"type":"string"}},"required":["title"]}, + ReadOnlyHint: false, + DestructiveHint: false + } + ], + CatalogHash: 4ce4259bec02f283, + Truncated: false +} \ No newline at end of file