From 31527eafe246dcc5d8834461d09d13d14acb6e6e 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 --- references/hub_dtos.py | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/references/hub_dtos.py b/references/hub_dtos.py index 4ec2226..01def68 100644 --- a/references/hub_dtos.py +++ b/references/hub_dtos.py @@ -1,5 +1,5 @@ """ Options: -Date: 2026-10-13 06:06:37 +Date: 2026-10-13 09:01:23 Version: 10.20 Tip: To override a DTO option, remove "#" prefix before updating BaseUrl: http://localhost:5001 @@ -5945,6 +5945,17 @@ class MarketplaceIntegrationListProjection(IHasViewId): requires_human_delivery_confirmation: bool = False +@dataclass_json(letter_case=LetterCase.CAMEL, undefined=Undefined.EXCLUDE) +@dataclass +class MarketplaceMcpToolDto: + name: Optional[str] = None + title: Optional[str] = None + description: Optional[str] = None + input_schema: Optional[str] = None + read_only_hint: Optional[bool] = None + destructive_hint: Optional[bool] = None + + @dataclass_json(letter_case=LetterCase.CAMEL, undefined=Undefined.EXCLUDE) @dataclass class MarketplaceFunctionProjection(IHasViewId): @@ -8051,6 +8062,15 @@ class GetMarketplaceIntegrationsResponse(ResponseBase): list: Optional[PaginatedResponse[MarketplaceIntegrationListProjection]] = None +@dataclass_json(letter_case=LetterCase.CAMEL, undefined=Undefined.EXCLUDE) +@dataclass +class GetMarketplaceIntegrationToolsResponse(ResponseBase): + tools: Optional[IReadOnlyList[MarketplaceMcpToolDto]] = None + listed_at: Optional[datetime.datetime] = None + catalog_hash: Optional[str] = None + truncated: bool = False + + @dataclass_json(letter_case=LetterCase.CAMEL, undefined=Undefined.EXCLUDE) @dataclass class EmptyMarketplaceSecretsResponse(ResponseBase): @@ -15312,6 +15332,17 @@ class GetSmsCampaignMessagesRequest(CodeMashListPaginationRequestBase, IReturn[G """ +# @Route("/{version}/code/marketplace/integrations/{IntegrationViewId}/tools", "GET") +@dataclass_json(letter_case=LetterCase.CAMEL, undefined=Undefined.EXCLUDE) +@dataclass +class GetMarketplaceIntegrationTools(CodeMashRequestBase, IReturn[GetMarketplaceIntegrationToolsResponse]): + # @ApiMember(Description="Integration view id, from get_marketplace_integrations.", IsRequired=true) + integration_view_id: Optional[str] = None + """ + Integration view id, from get_marketplace_integrations. + """ + + # @Route("/{version}/code/marketplace/integrations/{IntegrationViewId}/secrets", "PUT") @dataclass_json(letter_case=LetterCase.CAMEL, undefined=Undefined.EXCLUDE) @dataclass From eafcecb56098acefa5b3b09c7b0c41edd72e79f9 Mon Sep 17 00:00:00 2001 From: CodeMash Date: Tue, 13 Oct 2026 12:06:53 +0300 Subject: [PATCH 2/2] feat(marketplace): get_marketplace_integration_tools, the MCP tool catalog (route 24) Sync and async CodeModule gain GET /{version}/code/marketplace/ integrations/{IntegrationViewId}/tools; the marketplace route table gets its case (23 -> 24, run on the sync and the async client) and the docs table its row. Co-Authored-By: Claude Opus 5.5 --- docs/hub/code.md | 3 ++- src/norbix_python/hub/code.py | 26 ++++++++++++++++++++++++++ tests/hub/test_code.py | 7 ++++--- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/docs/hub/code.md b/docs/hub/code.md index 101b14d..871574f 100644 --- a/docs/hub/code.md +++ b/docs/hub/code.md @@ -29,7 +29,7 @@ Integration ids look like `int_…`. ## Marketplace -The 23 routes of the gateway contract `Code.Marketplace.md` §14: the catalog +The 24 routes of the gateway contract `Code.Marketplace.md` §14: the catalog of listings, the installed connections (integration ids look like `int_…`), their secrets and token mappings, and the functions added on a connection (`func_…`). Every method, its verb, path, query and body are asserted in @@ -55,6 +55,7 @@ their secrets and token mappings, and the functions added on a connection | `invoke_marketplace_function` | `POST` | `/{version}/code/marketplace/functions/{FunctionViewId}/invoke` | `project` | path: `function_view_id`; JSON body: `payload` | | `get_marketplace_listing` | `GET` | `/{version}/code/marketplace/listings/{ListingViewId}` | `project` | path: `listing_view_id` | | `test_marketplace_integration` | `POST` | `/{version}/code/marketplace/integrations/{IntegrationViewId}/test` | `project` | path: `integration_view_id` | +| `get_marketplace_integration_tools` | `GET` | `/{version}/code/marketplace/integrations/{IntegrationViewId}/tools` | `project` | path: `integration_view_id`; returns the stored MCP tool catalog (`tools`, `listedAt`, `catalogHash`, `truncated`) | | `replace_marketplace_integration_secrets` | `PUT` | `/{version}/code/marketplace/integrations/{IntegrationViewId}/secrets` | `project` | path: `integration_view_id`; JSON body: `secrets` | | `reveal_marketplace_integration_secrets` | `POST` | `/{version}/code/marketplace/integrations/{IntegrationViewId}/secrets/reveal` | `project` | path: `integration_view_id` | | `set_marketplace_integration_token_mappings` | `PUT` | `/{version}/code/marketplace/integrations/{IntegrationViewId}/token-mappings` | `project` | path: `integration_view_id`; JSON body: `tokenMappings` | diff --git a/src/norbix_python/hub/code.py b/src/norbix_python/hub/code.py index 2248bd4..9e5cc95 100644 --- a/src/norbix_python/hub/code.py +++ b/src/norbix_python/hub/code.py @@ -373,6 +373,19 @@ def test_marketplace_integration(self, integration_view_id: str, *, timeout: flo bearer_token=bearer_token, ) + def get_marketplace_integration_tools(self, integration_view_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """GET /{version}/code/marketplace/integrations/{IntegrationViewId}/tools — the stored MCP tool catalog of a connection.""" + return self._transport.send( + target="hub", + path="/{version}/code/marketplace/integrations/{IntegrationViewId}/tools", + method="GET", + path_params={"IntegrationViewId": integration_view_id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + def replace_marketplace_integration_secrets(self, integration_view_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: """PUT /{version}/code/marketplace/integrations/{IntegrationViewId}/secrets""" return self._transport.send( @@ -872,6 +885,19 @@ async def test_marketplace_integration(self, integration_view_id: str, *, timeou bearer_token=bearer_token, ) + async def get_marketplace_integration_tools(self, integration_view_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: + """GET /{version}/code/marketplace/integrations/{IntegrationViewId}/tools — the stored MCP tool catalog of a connection.""" + return await self._transport.send( + target="hub", + path="/{version}/code/marketplace/integrations/{IntegrationViewId}/tools", + method="GET", + path_params={"IntegrationViewId": integration_view_id}, + request=request, + scope="project", + timeout=timeout, + bearer_token=bearer_token, + ) + async def replace_marketplace_integration_secrets(self, integration_view_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any: """PUT /{version}/code/marketplace/integrations/{IntegrationViewId}/secrets""" return await self._transport.send( diff --git a/tests/hub/test_code.py b/tests/hub/test_code.py index eb75c37..4326c62 100644 --- a/tests/hub/test_code.py +++ b/tests/hub/test_code.py @@ -290,6 +290,7 @@ def test_invoke_code_function_sends_the_function_ref_in_the_body() -> None: ("invoke_marketplace_function", "POST", "/v3/code/marketplace/functions/func_4hT2b9QkLm3XcT1yRfGh7a/invoke", None, {'payload': {'text': 'Order 6ac54a59 shipped'}}, lambda m: m.invoke_marketplace_function("func_4hT2b9QkLm3XcT1yRfGh7a", payload={'text': 'Order 6ac54a59 shipped'})), ("get_marketplace_listing", "GET", "/v3/code/marketplace/listings/ml_slack_v1", None, None, lambda m: m.get_marketplace_listing("ml_slack_v1")), ("test_marketplace_integration", "POST", "/v3/code/marketplace/integrations/int_6Qp1dW2kRm8NsT4vYc0Hb/test", None, None, lambda m: m.test_marketplace_integration("int_6Qp1dW2kRm8NsT4vYc0Hb")), + ("get_marketplace_integration_tools", "GET", "/v3/code/marketplace/integrations/int_6Qp1dW2kRm8NsT4vYc0Hb/tools", None, None, lambda m: m.get_marketplace_integration_tools("int_6Qp1dW2kRm8NsT4vYc0Hb")), ("replace_marketplace_integration_secrets", "PUT", "/v3/code/marketplace/integrations/int_6Qp1dW2kRm8NsT4vYc0Hb/secrets", None, {'secrets': {'botToken': 'xoxb-new'}}, lambda m: m.replace_marketplace_integration_secrets("int_6Qp1dW2kRm8NsT4vYc0Hb", secrets={'botToken': 'xoxb-new'})), ("reveal_marketplace_integration_secrets", "POST", "/v3/code/marketplace/integrations/int_6Qp1dW2kRm8NsT4vYc0Hb/secrets/reveal", None, None, lambda m: m.reveal_marketplace_integration_secrets("int_6Qp1dW2kRm8NsT4vYc0Hb")), ("set_marketplace_integration_token_mappings", "PUT", "/v3/code/marketplace/integrations/int_6Qp1dW2kRm8NsT4vYc0Hb/token-mappings", None, {'tokenMappings': [{'key': 'channel', 'value': '#orders', 'resolver': 'Custom'}]}, lambda m: m.set_marketplace_integration_token_mappings("int_6Qp1dW2kRm8NsT4vYc0Hb", tokenMappings=[{'key': 'channel', 'value': '#orders', 'resolver': 'Custom'}])), @@ -350,9 +351,9 @@ async def run() -> None: def test_marketplace_surface_size() -> None: - """23 marketplace routes in the gateway (Code.Marketplace.md §14). A new one changes this count.""" - assert len(MARKETPLACE_CASES) == 23 - assert len({case[0] for case in MARKETPLACE_CASES}) == 23 + """24 marketplace routes in the gateway (Code.Marketplace.md §14; 24 = MCP tool catalog). A new one changes this count.""" + assert len(MARKETPLACE_CASES) == 24 + assert len({case[0] for case in MARKETPLACE_CASES}) == 24 client, _ = make_client() for name, *_ in MARKETPLACE_CASES: assert callable(getattr(client.hub.code, name)), name