Skip to content

fix(hubspot): re-sync to HubSpot's replaced MCP tool catalog + RFC 6749 scope fallback on OAuth callback#428

Merged
angel-manuel merged 1 commit into
devfrom
fix/hubspot-mcp-catalog-and-scope-fallback
Jul 5, 2026
Merged

fix(hubspot): re-sync to HubSpot's replaced MCP tool catalog + RFC 6749 scope fallback on OAuth callback#428
angel-manuel merged 1 commit into
devfrom
fix/hubspot-mcp-catalog-and-scope-fallback

Conversation

@angel-manuel

Copy link
Copy Markdown
Contributor

Problem

Two dev-environment failures on the HubSpot connector (debugged live against overslash-dev and mcp.hubspot.com):

  1. Every "Try it" call 502'd. HubSpot replaced its remote-MCP tool catalog: the hubspot-list-objects-style tools that services/hubspot.yaml was authored against no longer exist. The audit log proved Overslash sent the correct per-template name (hubspot-list-objects), and the server answered JSON-RPC -32603 "Unknown tool: invalid_tool_name" → mapped to 502. A live tools/list (with the real connection token) shows a completely new 17-tool catalog (search_crm_objects, manage_crm_objects, query_crm_data, …).

  2. Connections landed with scopes = {} after a full grant. HubSpot's /oauth/v1/token response never includes a scope field. The callback treated that as "no scopes granted" and recorded a known-empty set — while introspection (GET /oauth/v1/access-tokens/{token}) shows the token actually carries the app's full ~40-scope grant.

Fix

  • services/hubspot.yaml re-authored against the live catalog (verified 2026-07-05): CRM search/read + SQL query, property metadata, owners, campaign/content analytics, tool_guidance, and manage_crm_objects as the single write tool with disclose declarations (operation, object type, truncated payload). Connector-internal tools (submit_feedback, render_landing_page_ui) and the landing-page CMS editor are intentionally not exposed. New names are valid action keys, so the mcp_tool dash-aliasing is gone.
  • RFC 6749 §5.1 scope fallback: when the token response omits scope entirely, the granted set is by definition the requested set — the callback now records the flow row's requested scopes (TokenResponse::granted_scopes_or_requested). A present-but-empty scope is still honored verbatim as "nothing granted", and refresh reconciliation is unchanged (a no-echo refresh still never clobbers).

Tests

  • The hubspot fake MCP server now pins a LIVE_CATALOG snapshot and rejects unknown names with HubSpot's exact -32603 error, so catalog drift fails CI instead of 502ing in prod.
  • New: template↔catalog sync test (every shipped tool must exist in the snapshot), drift → 502 test (RPC code preserved in the body for diagnosis), end-to-end callback test proving a HubSpot-shaped token response (no scope field) records the requested scopes, and unit tests for the three fallback semantics (omitted / echoed-narrower / explicit-empty).
  • Existing suites re-run green: hubspot (6/6), oauth_x, oauth_return_url, connection_webhooks, approval_disclosure, actions_reauth_return_url, connection_credential_source, overslash-core (incl. shipped_services_validate_clean).

Notes

  • No migration; no dashboard change needed (the API Explorer renders the template's action list dynamically).
  • The existing dev connection was already corrected in-place (account_email set, scopes = NULL), so "Try it" works immediately after deploy without a reconnect — the token was always fully scoped.

🤖 Generated with Claude Code

… requested scopes when /token omits scope

HubSpot's remote MCP (mcp.hubspot.com) replaced its entire tool catalog:
the hubspot-list-objects-style tools the shipped template was authored
against no longer exist, so every call answered JSON-RPC -32603
"Unknown tool" and surfaced as a 502 from /v1/actions/call. Verified
live via tools/list (17 new tools) and re-authored services/hubspot.yaml
against it: CRM search/read/SQL-query tools, property metadata, owners,
campaign/content analytics, and manage_crm_objects as the single write
tool with disclose declarations. Connector-internal tools and the
landing-page CMS editor are intentionally not exposed.

Separately, HubSpot's token endpoint never echoes `scope`, so
connections landed with a known-empty scopes set ({}) even after a full
grant. Per RFC 6749 §5.1 an omitted scope means "granted as requested" —
the callback now records the flow row's requested set in that case
(TokenResponse::granted_scopes_or_requested). A present-but-empty scope
is still honored verbatim, and refresh reconciliation is unchanged.

Tests: the hubspot fake now pins a LIVE_CATALOG snapshot and rejects
unknown names with HubSpot's exact -32603 error, so catalog drift fails
CI instead of 502ing in prod; new tests cover template↔catalog sync,
drift → 502 with the RPC code preserved, and the callback scope
fallback end-to-end (plus unit tests for the fallback semantics).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
overslash Ready Ready Preview, Comment Jul 5, 2026 1:07pm

Request Review

@codecov

codecov Bot commented Jul 5, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@angel-manuel angel-manuel merged commit 6660857 into dev Jul 5, 2026
15 checks passed
@angel-manuel angel-manuel deleted the fix/hubspot-mcp-catalog-and-scope-fallback branch July 5, 2026 13:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant