Skip to content

Commit be4fa61

Browse files
Domantasclaude
andcommitted
feat(marketplace): sign_out_marketplace_integration, the OAuth sign-out (route 30)
The Code module gains sign_out_marketplace_integration (POST .../integrations/{IntegrationViewId}/oauth/sign-out); sync and async; the marketplace route table gets its case (28 -> 29, run on both clients) and the docs table its row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent a10708d commit be4fa61

3 files changed

Lines changed: 32 additions & 4 deletions

File tree

‎docs/hub/code.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ Integration ids look like `int_…`.
2929

3030
## Marketplace
3131

32-
The 28 routes of the gateway contract `Code.Marketplace.md` §14: the catalog
32+
The 29 routes of the gateway contract `Code.Marketplace.md` §14: the catalog
3333
of listings, the installed connections (integration ids look like `int_…`),
3434
their secrets and token mappings, and the functions added on a connection
3535
(`func_…`). Every method, its verb, path, query and body are asserted in
@@ -60,6 +60,7 @@ their secrets and token mappings, and the functions added on a connection
6060
| `set_marketplace_integration_role_access` | `PUT` | `/{version}/code/marketplace/integrations/{IntegrationViewId}/access/roles` | `project` | path: `integration_view_id`; JSON body: `roleId`, `package` (`None`, `Viewer`, `FunctionsOnly`, `ReadOnly`, `FullAccess`), `manage`; returns the role id |
6161
| `save_marketplace_integration_custom_access` | `POST` | `/{version}/code/marketplace/integrations/{IntegrationViewId}/access/roles/custom` | `project` | path: `integration_view_id`; JSON body: `roleId`, `basePackage`, `tools`, `functions`, `manage`, `newPolicyForThisRoleOnly`; returns `policyId`, `created`, `usedByRoles` |
6262
| `start_marketplace_integration_sign_in` | `POST` | `/{version}/code/marketplace/integrations/{IntegrationViewId}/oauth/start` | `project` | path: `integration_view_id`; returns `authorizeUrl` (open it in a browser), `redirectUrl`, `expiresAt` |
63+
| `sign_out_marketplace_integration` | `POST` | `/{version}/code/marketplace/integrations/{IntegrationViewId}/oauth/sign-out` | `project` | path: `integration_view_id`; returns `wasSignedIn`, `revoke` |
6364
| `replace_marketplace_integration_secrets` | `PUT` | `/{version}/code/marketplace/integrations/{IntegrationViewId}/secrets` | `project` | path: `integration_view_id`; JSON body: `secrets` |
6465
| `reveal_marketplace_integration_secrets` | `POST` | `/{version}/code/marketplace/integrations/{IntegrationViewId}/secrets/reveal` | `project` | path: `integration_view_id` |
6566
| `set_marketplace_integration_token_mappings` | `PUT` | `/{version}/code/marketplace/integrations/{IntegrationViewId}/token-mappings` | `project` | path: `integration_view_id`; JSON body: `tokenMappings` |

‎src/norbix_python/hub/code.py‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,19 @@ def start_marketplace_integration_sign_in(self, integration_view_id: str, *, tim
438438
bearer_token=bearer_token,
439439
)
440440

441+
def sign_out_marketplace_integration(self, integration_view_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any:
442+
"""POST /{version}/code/marketplace/integrations/{IntegrationViewId}/oauth/sign-out — sign an MCP connection out of OAuth; returns `wasSignedIn` and `revoke` (`Revoked`, `NoRevocationEndpoint`, `Failed` or None)."""
443+
return self._transport.send(
444+
target="hub",
445+
path="/{version}/code/marketplace/integrations/{IntegrationViewId}/oauth/sign-out",
446+
method="POST",
447+
path_params={"IntegrationViewId": integration_view_id},
448+
request=request,
449+
scope="project",
450+
timeout=timeout,
451+
bearer_token=bearer_token,
452+
)
453+
441454
def replace_marketplace_integration_secrets(self, integration_view_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any:
442455
"""PUT /{version}/code/marketplace/integrations/{IntegrationViewId}/secrets"""
443456
return self._transport.send(
@@ -1002,6 +1015,19 @@ async def start_marketplace_integration_sign_in(self, integration_view_id: str,
10021015
bearer_token=bearer_token,
10031016
)
10041017

1018+
async def sign_out_marketplace_integration(self, integration_view_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any:
1019+
"""POST /{version}/code/marketplace/integrations/{IntegrationViewId}/oauth/sign-out — sign an MCP connection out of OAuth; returns `wasSignedIn` and `revoke` (`Revoked`, `NoRevocationEndpoint`, `Failed` or None)."""
1020+
return await self._transport.send(
1021+
target="hub",
1022+
path="/{version}/code/marketplace/integrations/{IntegrationViewId}/oauth/sign-out",
1023+
method="POST",
1024+
path_params={"IntegrationViewId": integration_view_id},
1025+
request=request,
1026+
scope="project",
1027+
timeout=timeout,
1028+
bearer_token=bearer_token,
1029+
)
1030+
10051031
async def replace_marketplace_integration_secrets(self, integration_view_id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any:
10061032
"""PUT /{version}/code/marketplace/integrations/{IntegrationViewId}/secrets"""
10071033
return await self._transport.send(

‎tests/hub/test_code.py‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,7 @@ def test_invoke_code_function_sends_the_function_ref_in_the_body() -> None:
295295
("set_marketplace_integration_role_access", "PUT", "/v3/code/marketplace/integrations/int_6Qp1dW2kRm8NsT4vYc0Hb/access/roles", None, {'roleId': 'acc_6Qp1dW2kRm8NsT4vYc0Hb_nr_3kLm9QwErTyU', 'package': 'ReadOnly', 'manage': False}, lambda m: m.set_marketplace_integration_role_access("int_6Qp1dW2kRm8NsT4vYc0Hb", roleId='acc_6Qp1dW2kRm8NsT4vYc0Hb_nr_3kLm9QwErTyU', package='ReadOnly', manage=False)),
296296
("save_marketplace_integration_custom_access", "POST", "/v3/code/marketplace/integrations/int_6Qp1dW2kRm8NsT4vYc0Hb/access/roles/custom", None, {'roleId': 'acc_6Qp1dW2kRm8NsT4vYc0Hb_nr_3kLm9QwErTyU', 'basePackage': 'ReadOnly', 'tools': ['create_issue'], 'functions': ['func_4hT2b9QkLm3XcT1yRfGh7a'], 'manage': False, 'newPolicyForThisRoleOnly': True}, lambda m: m.save_marketplace_integration_custom_access("int_6Qp1dW2kRm8NsT4vYc0Hb", roleId='acc_6Qp1dW2kRm8NsT4vYc0Hb_nr_3kLm9QwErTyU', basePackage='ReadOnly', tools=['create_issue'], functions=['func_4hT2b9QkLm3XcT1yRfGh7a'], manage=False, newPolicyForThisRoleOnly=True)),
297297
("start_marketplace_integration_sign_in", "POST", "/v3/code/marketplace/integrations/int_6Qp1dW2kRm8NsT4vYc0Hb/oauth/start", None, None, lambda m: m.start_marketplace_integration_sign_in("int_6Qp1dW2kRm8NsT4vYc0Hb")),
298+
("sign_out_marketplace_integration", "POST", "/v3/code/marketplace/integrations/int_6Qp1dW2kRm8NsT4vYc0Hb/oauth/sign-out", None, None, lambda m: m.sign_out_marketplace_integration("int_6Qp1dW2kRm8NsT4vYc0Hb")),
298299
("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'})),
299300
("reveal_marketplace_integration_secrets", "POST", "/v3/code/marketplace/integrations/int_6Qp1dW2kRm8NsT4vYc0Hb/secrets/reveal", None, None, lambda m: m.reveal_marketplace_integration_secrets("int_6Qp1dW2kRm8NsT4vYc0Hb")),
300301
("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'}])),
@@ -355,9 +356,9 @@ async def run() -> None:
355356

356357

357358
def test_marketplace_surface_size() -> None:
358-
"""28 marketplace routes in the gateway (Code.Marketplace.md §14; 24 = MCP tool catalog, 25–27 = connection access, 28 = OAuth sign-in start; 29, the callback, is not in the metadata). A new one changes this count."""
359-
assert len(MARKETPLACE_CASES) == 28
360-
assert len({case[0] for case in MARKETPLACE_CASES}) == 28
359+
"""29 marketplace routes in the gateway (Code.Marketplace.md §14; 24 = MCP tool catalog, 25–27 = connection access, 28 = OAuth sign-in start, 30 = OAuth sign-out; 29, the callback, is not in the metadata). A new one changes this count."""
360+
assert len(MARKETPLACE_CASES) == 29
361+
assert len({case[0] for case in MARKETPLACE_CASES}) == 29
361362
client, _ = make_client()
362363
for name, *_ in MARKETPLACE_CASES:
363364
assert callable(getattr(client.hub.code, name)), name

0 commit comments

Comments
 (0)