From 7cd5588b93371d00c0ed60df784ddf325c9fda9a Mon Sep 17 00:00:00 2001 From: jpelletier1 Date: Sat, 25 Jul 2026 12:16:26 +0000 Subject: [PATCH] feat(integrations): save MCP tokens as secrets Co-authored-by: openhands --- integrations/catalog/linear.json | 4 +++- integrations/catalog/ordinal.json | 4 +++- integrations/catalog/posthog.json | 4 +++- integrations/catalog/stripe.json | 4 +++- tests/test_catalogs.py | 17 +++++++++++++++++ 5 files changed, 29 insertions(+), 4 deletions(-) diff --git a/integrations/catalog/linear.json b/integrations/catalog/linear.json index b43ba08d..5a467b88 100644 --- a/integrations/catalog/linear.json +++ b/integrations/catalog/linear.json @@ -52,7 +52,9 @@ "apiKeyOptional": true, "credentialLabel": "Linear API key", "credentialPlaceholder": "Paste your Linear API key", - "credentialHelp": "Create a personal API key (grant at least read access) under Security & access in [Linear settings](https://linear.app/settings/account/security). Sent as Authorization: Bearer ; optional when the endpoint accepts your OAuth session." + "credentialHelp": "Create a personal API key (grant at least read access) under Security & access in [Linear settings](https://linear.app/settings/account/security). Sent as Authorization: Bearer ; optional when the endpoint accepts your OAuth session.", + "credentialSecretName": "LINEAR_API_KEY", + "saveCredentialAsSecretByDefault": true } } ], diff --git a/integrations/catalog/ordinal.json b/integrations/catalog/ordinal.json index 96b8295e..09f2a0d2 100644 --- a/integrations/catalog/ordinal.json +++ b/integrations/catalog/ordinal.json @@ -22,7 +22,9 @@ ], "credentialLabel": "Ordinal API key", "credentialPlaceholder": "Paste your Ordinal API key", - "credentialHelp": "API key from your Ordinal workspace settings, sent as a Bearer token in the Authorization header." + "credentialHelp": "API key from your Ordinal workspace settings, sent as a Bearer token in the Authorization header.", + "credentialSecretName": "ORDINAL_API_KEY", + "saveCredentialAsSecretByDefault": true }, "transport": { "kind": "shttp", diff --git a/integrations/catalog/posthog.json b/integrations/catalog/posthog.json index 9b572d04..f8de01d0 100644 --- a/integrations/catalog/posthog.json +++ b/integrations/catalog/posthog.json @@ -31,7 +31,9 @@ "strategy": "bearer", "credentialLabel": "PostHog personal API key", "credentialPlaceholder": "Paste your PostHog personal API key", - "credentialHelp": "Create one under PostHog → User settings → API keys (MCP Server preset). Sent as Authorization: Bearer ." + "credentialHelp": "Create one under PostHog → User settings → API keys (MCP Server preset). Sent as Authorization: Bearer .", + "credentialSecretName": "POSTHOG_PERSONAL_API_KEY", + "saveCredentialAsSecretByDefault": true } } ] diff --git a/integrations/catalog/stripe.json b/integrations/catalog/stripe.json index d7a77b8d..5763b98f 100644 --- a/integrations/catalog/stripe.json +++ b/integrations/catalog/stripe.json @@ -22,7 +22,9 @@ "strategy": "bearer", "credentialLabel": "Stripe restricted key", "credentialPlaceholder": "rk_live_...", - "credentialHelp": "Create a Stripe restricted key for MCP access and paste it here." + "credentialHelp": "Create a Stripe restricted key for MCP access and paste it here.", + "credentialSecretName": "STRIPE_RESTRICTED_KEY", + "saveCredentialAsSecretByDefault": true } }, { diff --git a/tests/test_catalogs.py b/tests/test_catalogs.py index f1875256..55aa68d7 100644 --- a/tests/test_catalogs.py +++ b/tests/test_catalogs.py @@ -213,6 +213,23 @@ def test_daily_workflow_entries_are_locally_installable(): assert required_env == {"SLACK_TEAM_ID", "SLACK_BOT_TOKEN"} +def test_direct_mcp_token_credentials_can_be_saved_as_secrets(): + for entry in load_catalog_entries("integrations/catalog"): + for option in entry["connectionOptions"]: + auth = option.get("auth", {}) + if option.get("provider") != "mcp" or not auth.get("credentialLabel"): + continue + if auth["strategy"] not in {"api_key", "bearer"}: + continue + + assert auth.get("credentialSecretName"), ( + f"{entry['id']}/{option['id']}: token credential needs a secret name" + ) + assert auth.get("saveCredentialAsSecretByDefault") is True, ( + f"{entry['id']}/{option['id']}: token credential should default to secret saving" + ) + + def test_daily_workflow_entries_state_scopes_and_setup_paths(): """The locally installable options must state the required scopes and link a public setup path (OSS-5193). The OAuth options' scope lists never reach a