diff --git a/src/gete/catalog/connections/slack-mcp.yaml b/src/gete/catalog/connections/slack-mcp.yaml index 35cda3c..d65cf49 100644 --- a/src/gete/catalog/connections/slack-mcp.yaml +++ b/src/gete/catalog/connections/slack-mcp.yaml @@ -26,21 +26,20 @@ oauth: # answers with the user's token at the top level and nothing else to take. authorization_url: https://slack.com/oauth/v2_user/authorize # The metadata names client_secret_post as the only client authentication - # method. Whether Gemini Enterprise sends the secret that way is - # unconfirmed, and a connection cannot say: the Authorization resource has - # no field for it. + # method, and the code exchange goes through however Gemini Enterprise + # sends the secret. Which of the two it sends a connection cannot say: the + # Authorization resource has no field for it. token_url: https://slack.com/api/oauth.v2.user.access # scope_parameter stays at its default. The app pair needs user_scope - # because oauth/v2/authorize reads scope as the app's own permissions. This - # endpoint is reached by standard MCP clients as an RFC 8414 authorization - # server, and those send a plain scope parameter, so scope it is - - # unconfirmed; user_scope is the switch if the consent screen answers - # invalid_scope. + # because oauth/v2/authorize reads scope as the app's own permissions; this + # endpoint is reached as an RFC 8414 authorization server, where a plain + # scope parameter is what a standard MCP client sends, and the consent + # screen was built from the scopes sent that way. # pkce stays unset: S256 is supported here, not required. # No authorization_query either. One would keep access_type and prompt off # the URL, as notion-mcp does, but it would also fix the scopes, and the - # menu below could not be offered beside it. Slack is expected to ignore - # parameters it does not know; unconfirmed. + # menu below could not be offered beside it. Slack ignores the two: the + # consent screen was reached with both on the URL. # # Slack fixes a user token's scopes at consent. A scope added here later # reaches nobody already authorized until their authorization is reset, so @@ -90,27 +89,36 @@ setup: | URLs): the one named above. Put the app's client id and client secret in the secrets named above. + Turn Model Context Protocol on, under Features -> Agents & AI Apps. With it + off the server answers the initialize request with 400 and "App is not + enabled", pointing at the app's own settings page. That is a refusal made + after reading the token, not a refusal of it - a token the server does not + accept answers 401 invalid_token instead - so a correct token looks broken + until the toggle is on. Turning it on needs neither a reinstall nor a fresh + authorization: a token taken before the toggle works after it. + Where the workspace requires apps to be approved, an admin has to approve this one before anyone can authorize. Semantic search needs a plan that includes Slack AI search; keyword search works on any plan. - No authorization has been taken through Gemini Enterprise yet, so the - following is read off the server's metadata rather than seen to work: - - The token endpoint names client_secret_post as its only client - authentication method. Whether Gemini Enterprise sends the client secret - that way is unconfirmed. - - The scopes are sent under scope, the parameter a standard MCP client - uses. If the consent screen answers invalid_scope, set - oauth.scope_parameter to user_scope in gete.yaml. - - The authorization URL carries access_type and prompt, which are - Google's vocabulary. Whether Slack ignores them is unconfirmed. - - A standard MCP client sends resource (RFC 8707) with the code exchange; - Gemini Enterprise does not. Whether Slack requires it is unconfirmed. + An authorization taken through Gemini Enterprise settles what the server's + metadata alone could not: + - The code exchange goes through however Gemini Enterprise sends the client + secret, though the token endpoint names client_secret_post as its only + client authentication method. + - The consent screen is built from the scopes sent under scope, the + parameter a standard MCP client uses; user_scope, which the app pair + needs, is not wanted here. + - access_type and prompt on the authorization URL are ignored. + - resource (RFC 8707) is not required. A standard MCP client sends it with + the code exchange and Gemini Enterprise does not; the exchange goes + through either way. - The metadata offers the refresh_token grant, so tokens may expire. - Whether Gemini Enterprise refreshes them is its own doing. + Whether Gemini Enterprise refreshes one is unconfirmed: none issued here + has yet lived long enough to find out. -# verified is absent because no authorization has been taken through Gemini -# Enterprise yet. +verified: + gemini_enterprise: 2026-09-09 examples: accepts: diff --git a/tests/conformance/test_catalog.py b/tests/conformance/test_catalog.py index 07bb147..30e19c7 100644 --- a/tests/conformance/test_catalog.py +++ b/tests/conformance/test_catalog.py @@ -285,17 +285,36 @@ def test_slack_mcp_takes_user_tokens_and_refuses_bot_tokens() -> None: def test_slack_mcp_says_what_a_person_has_to_do_before_authorizing() -> None: - """The app's user scopes, the one redirect URI, and what has not been seen - to work yet.""" + """The app's user scopes, the one redirect URI, and the toggle the server + reads before it will hold a session.""" setup = CATALOG["slack-mcp"]["setup"] assert "User Token Scopes" in setup assert "Bot Token Scopes" in setup assert "redirect URI" in setup - assert "unconfirmed" in setup + assert "Model Context Protocol" in setup -def test_slack_mcp_is_not_verified_until_an_authorization_has_been_taken() -> None: - assert "verified" not in CATALOG["slack-mcp"] +def test_slack_mcp_names_the_toggle_that_leaves_a_good_token_refused() -> None: + """With Model Context Protocol off the server refuses the initialize + request, which reads like a bad token and is not one. Turning it on takes + back nothing already granted, so setup must say that too.""" + setup = CATALOG["slack-mcp"]["setup"] + assert "Agents & AI Apps" in setup + assert "400" in setup + assert "reinstall" in setup + + +def test_slack_mcp_leaves_only_refreshing_unconfirmed() -> None: + """The rest of setup was seen through Gemini Enterprise; no token issued + there has yet lived long enough to expire.""" + setup = CATALOG["slack-mcp"]["setup"] + unconfirmed = [line for line in setup.splitlines() if "unconfirmed" in line] + assert len(unconfirmed) == 1 + assert "refresh" in unconfirmed[0] + + +def test_slack_mcp_records_the_authorization_it_was_verified_with() -> None: + assert CATALOG["slack-mcp"]["verified"] == {"gemini_enterprise": "2026-09-09"} def test_slack_mcp_can_sit_beside_a_connection_accepted_by_elimination() -> None: