Conversation
Add HubSpot CRM as an OAuth provider with support for: - Contacts read/write - Companies read/write - Deals read/write - Owners read HubSpot uses form-encoded token requests and extracts user info from hub_id and user fields in the token response. Co-authored-by: Cursor <cursoragent@cursor.com>
Add 16 HubSpot MCP tools: - hubspot_status: Check connection status - hubspot_list_contacts, hubspot_get_contact, hubspot_create_contact, hubspot_update_contact, hubspot_search_contacts - hubspot_list_companies, hubspot_create_company, hubspot_search_companies - hubspot_list_deals, hubspot_create_deal, hubspot_update_deal, hubspot_search_deals - hubspot_list_owners - hubspot_associate: Create object associations Co-authored-by: Cursor <cursoragent@cursor.com>
Add standalone HubSpot MCP endpoint at /api/mcps/hubspot/ that allows the Eliza agent to interact with HubSpot CRM directly in conversations. Tools exposed to agent: - Contact management (list, get, create, update, search) - Company management (list, create, search) - Deal management (list, create, search) - Owner listing Also registers HubSpot in MCP_SERVER_CONFIGS so agent automatically connects when HubSpot OAuth is active. Co-authored-by: Cursor <cursoragent@cursor.com>
Enhance OAUTH_CONNECT and OAUTH_REVOKE actions: - Add HubSpot to similes (CONNECT_HUBSPOT, LINK_HUBSPOT) - Update descriptions to list HubSpot as available platform - Add HubSpot examples to action definitions This enables the agent to understand HubSpot connection commands like "connect hubspot" or "disconnect hubspot". Co-authored-by: Cursor <cursoragent@cursor.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Use the checkbox below for a quick retry:
Note 🎁 Summarized by CodeRabbit FreeYour organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login. Comment |
There was a problem hiding this comment.
Pull request overview
This pull request expands OAuth integration support by adding HubSpot alongside existing platforms (Google, Linear, Notion, GitHub, Slack). The PR includes comprehensive MCP (Model Context Protocol) tooling for HubSpot CRM operations, updates OAuth action handlers, improves Twitter callback robustness, and includes extensive code formatting improvements across the runtime factory.
Changes:
- Added HubSpot OAuth provider configuration with CRM scopes and endpoints
- Implemented HubSpot MCP tools for contacts, companies, deals, and owners management
- Updated OAuth connect/revoke actions to support HubSpot and other new platforms
- Enhanced Twitter OAuth callback to handle different cache return types
- Applied consistent code formatting across runtime factory and OAuth modules
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
tests/unit/mcp-tools.test.ts |
Added test assertion for HubSpot tools registration |
lib/services/oauth/provider-registry.ts |
Added HubSpot provider configuration and updated Linear, Slack, and Google configurations |
lib/eliza/runtime-factory.ts |
Added HubSpot MCP server configuration and applied formatting improvements |
lib/eliza/plugin-oauth/actions/oauth-revoke.ts |
Extended action to support HubSpot and other platforms with updated examples |
lib/eliza/plugin-oauth/actions/oauth-connect.ts |
Extended action to support HubSpot and other platforms with updated examples |
app/api/v1/twitter/callback/route.ts |
Improved state data handling to support both string and object formats from cache |
app/api/mcps/hubspot/[transport]/route.ts |
New standalone HubSpot MCP server endpoint with full CRM tooling |
app/api/mcp/tools/index.ts |
Exported HubSpot tools registration function |
app/api/mcp/tools/hubspot.ts |
Comprehensive HubSpot MCP tools implementation with 15 tools |
app/api/mcp/route.ts |
Registered HubSpot tools in main MCP handler with formatting fix |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…ubSpot connections Co-authored-by: Cursor <cursoragent@cursor.com>
…URLs - Cache key now includes connected MCP platforms (e.g. google,hubspot) so runtime is recreated when user connects HubSpot; otherwise cached runtime never gets HubSpot tools in action list. - MCP server URLs updated to /streamable-http path for Google and HubSpot. Co-authored-by: Cursor <cursoragent@cursor.com>
Address Copilot review comments on PR #283: - Slack: document auth.test response shape and why email is absent from mapping #283 (comment) - HubSpot: remove incorrect revoke URL, document local-only disconnect #283 (comment) - HubSpot: clarify userInfoMapping is for tokenInfo response, not userInfo #283 (comment) Co-authored-by: Cursor <cursoragent@cursor.com>
Address Copilot review comment on PR #283 requesting test coverage: #283 (comment) Covers tool registration (all 15 tools), status checks, contacts/companies/ deals/owners list operations, contact creation with error handling, association creation, network errors, and concurrent org isolation. Co-authored-by: Cursor <cursoragent@cursor.com>
Reverts non-HubSpot changes that were mixed into this branch: - Twitter callback: revert cache string/object handling (unrelated fix) - Linear: revert tokenContentType from form back to json (unrelated fix) - Slack: revert auth.test back to users.identity with original scopes/mapping - runtime-factory: remove ~50 trailing comma formatting changes, keep only HubSpot MCP config and MCP platform cache key logic Co-authored-by: Cursor <cursoragent@cursor.com>
Return distinct status when HubSpot OAuth connection is expired, prompting the user to reconnect instead of a generic "not connected". Co-authored-by: Cursor <cursoragent@cursor.com>
Resolved conflicts by taking dev's changes and adding HubSpot on top: - mcp/route.ts, tools/index.ts: added HubSpot alongside dev's Linear/Notion/GitHub - twitter/callback: took dev's version with validation - oauth-connect/revoke: took dev's format, added hubspot to platform lists - runtime-factory: took dev's version, added hubspot to MCP_SERVER_CONFIGS - provider-registry: took dev's Slack mapping Co-authored-by: Cursor <cursoragent@cursor.com>
Resolved migration conflict: kept 0028_add_missing_apps_columns, renumbered upstream 0028_add_user_platform_oauth_constraint to 0029. Co-authored-by: Cursor <cursoragent@cursor.com>
Code Review: PR #283 - HubSpot OAuth IntegrationOverviewThis PR adds comprehensive HubSpot OAuth integration with CRM tools (contacts, companies, deals, owners). The implementation follows established project patterns well and is nearly production-ready. Overall Quality: 8/10 ✅ Approve with minor changes requested 🔴 Critical Issues (Must Fix)1. Type Mismatch in Deal Amount FieldFile: The // route.ts:419 (WRONG)
amount: z.string().optional().describe("Deal amount"),
// hubspot.ts:550 (CORRECT)
amount: z.number().optional().describe("Deal amount"),Impact: Runtime errors when creating deals with numeric amounts via standalone endpoint. Fix: Change line 419 to 2. Inconsistent Default LimitsThe two implementations have different default limits:
Fix: Standardize on 20 for consistency.
|
Code Review: PR #283 - HubSpot Integration & Multi-Platform OAuth SupportSummary: High-quality implementation with excellent security foundations and comprehensive test coverage. Recommended for merge with minor monitoring items noted below. Stats: 14 files changed (+2,245/-39 lines) ✅ Strengths1. Security - Excellent Implementation
2. Code Quality
3. Twitter Callback Improvements
|
- Fix deal amount type mismatch (z.string -> z.number) in standalone endpoint - Standardize default limits from 10 to 20 across all standalone endpoint tools - Add pagination (paging) to search operations in both implementations - Improve error response parsing with logger.warn fallback in hubspotFetch Co-authored-by: Cursor <cursoragent@cursor.com>
PR Review: HubSpot OAuth IntegrationSummaryThis PR adds comprehensive HubSpot CRM integration with OAuth support. The implementation includes MCP tools for contacts, companies, deals, owners, and associations. Overall, the code quality is good with proper error handling and test coverage. ✅ Strengths1. Well-Structured Implementation
2. OAuth Configuration
3. Test Coverage
4. Security
🔍 Issues & RecommendationsHigh Priority1. Type Safety - Deal Amount Field amount: z.number().optional().describe("Deal amount"),However, in the standalone endpoint, line 555 might have a mismatch. Ensure consistency between both implementations. HubSpot's API typically expects 2. Error Response Parsing if (!response.ok && response.status !== 204) {
const error = await response.json().catch(() => {
logger.warn("[HubSpot] Failed to parse error response", { status: response.status });
return { message: `HTTP ${response.status}` };
});Good fallback handling, but consider logging the original error response body for debugging. 3. Missing Rate Limit Handling
Medium Priority4. Pagination Improvements
5. Association Type Hardcoding const associationTypeMap: Record<string, Record<string, number>> = {
contacts: { companies: 1, deals: 3 },
companies: { contacts: 2, deals: 5 },
deals: { contacts: 4, companies: 6 },
};These are HubSpot's default association types but may break if custom associations are needed. Consider:
6. Default Limits Inconsistency
Low Priority7. Property Selection
8. Input Validation
9. Logging Consistency
🔐 Security Assessment✅ Pass - No critical security issues found:
Minor note: Ensure HubSpot API responses don't contain sensitive data that shouldn't be logged. 🧪 TestingGood coverage of:
Missing:
📝 Code StyleAdheres to CLAUDE.md conventions:
Minor: Remove trailing commas that were inadvertently changed (e.g., 🎯 RecommendationApprove with minor revisions The implementation is solid and follows best practices. Address the high-priority items before merging:
The medium and low priority items can be follow-up improvements. 📊 Metrics
Great work on this integration! 🚀 |
|
Claude says: Addressed the critical and important items from the Claude CI review:
Items 5-11 (association type docs, rate limiting docs, JSDoc, test expansion, code dedup) are acknowledged as nice-to-haves and can be addressed in follow-up work. Claude |
Code Review: HubSpot OAuth Integration (PR #283)Thanks for this comprehensive addition of HubSpot OAuth integration! I've reviewed all 2,235 lines of changes across 14 files. Overall, the implementation follows the existing patterns well, but I've identified several critical issues that should be addressed before merging. Critical Security Issues 🔴1. Token Exposure in Error ResponsesFile: The const url = `${baseUrl.replace(/\/$/, "")}/${encodeURIComponent(accessToken)}`;Risk: If the request fails, error messages may contain the token. Fix: Redact tokens from error messages before logging or sanitize the URL. 2. Race Condition in OAuth Connection StorageFile: The const existingByPlatformUser = await dbWrite.select(...).where(...);
// ... code that could take time ...
if (existingByPlatformUser.length > 0 && existingByPlatformUser[0].user_id !== userId) {
throw new Error("OAUTH_ACCOUNT_ALREADY_LINKED");
}
// Later: transaction startsRisk: Concurrent OAuth callbacks could bypass the duplicate check. Fix: Move the duplicate check into a transaction using 3. Secret Cleanup Before Transaction CommitFile: if (pendingRevocation) {
await revokeConnectionsSecrets(pendingRevocation.connections, pendingRevocation.reason);
}
// Then transaction runs...Risk: If the transaction fails after secrets are deleted, database records will reference non-existent secrets. Fix: Move secret revocation to AFTER successful transaction commit, or include it in the same transaction. High Priority Issues 🟡4. No Rate Limiting on HubSpot API CallsFiles: HubSpot has strict rate limits (15 requests/10 seconds per token), but the tools make direct API calls without throttling. Fix: Implement rate limiting middleware or add backoff/retry logic for 429 responses. 5. N+1 Query Pattern in OAuth CallbackFile: Multiple sequential database queries for each OAuth callback:
Fix: Combine queries or use a single transaction with CTEs. 6. Type Safety Issues with
|
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 6 potential issues.
Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.
| actor: "user", | ||
| }, | ||
| tokenContentType: "form", // Linear requires x-www-form-urlencoded | ||
| tokenContentType: "json", |
There was a problem hiding this comment.
Linear token exchange content type breaks OAuth flow
High Severity
The Linear provider's tokenContentType was changed from "form" to "json", removing the comment that explicitly stated "Linear requires x-www-form-urlencoded". Linear's token endpoint (https://api.linear.app/oauth/token) requires application/x-www-form-urlencoded content type per their documentation. With "json", the exchangeCodeForTokens function sends application/json, which will cause the token exchange to fail, completely breaking Linear OAuth.
| revoke: "https://slack.com/api/auth.revoke", | ||
| }, | ||
| // Bot scopes only - these must also be added in Slack app's OAuth & Permissions | ||
| defaultScopes: ["chat:write", "channels:read", "users:read"], |
There was a problem hiding this comment.
Slack userInfoMapping incompatible with new endpoint
High Severity
The Slack userInfo endpoint was changed from auth.test to users.identity, but the userInfoMapping still uses auth.test field names (id: "user_id", displayName: "user"). The users.identity endpoint returns a nested response where user data is under user.id and user.name, not at the top level. This means user ID extraction will return undefined, breaking Slack OAuth user identification. The stale comment about "Bot tokens" also confirms the mapping wasn't updated.
| try { | ||
| const orgId = getOrgId(); | ||
| const properties: Record<string, string> = { dealname }; | ||
| if (amount) properties.amount = amount; |
There was a problem hiding this comment.
Deal amount zero skipped and type mismatch
Medium Severity
In the standalone hubspot_create_deal, properties is typed as Record<string, string> but amount is z.number(), causing a type mismatch on assignment. More importantly, if (amount) is falsy when amount === 0, silently dropping a valid zero-amount deal. The main hubspot.ts correctly uses Record<string, string | number> and if (amount !== undefined).
| }, | ||
| { capabilities: { tools: {} } }, | ||
| { basePath: "/api/mcps/hubspot", maxDuration: 60 } | ||
| ); |
There was a problem hiding this comment.
Duplicate HubSpot tools across two files
Medium Severity
The HubSpot CRM tool logic is fully duplicated between hubspot.ts (15 tools via registerHubSpotTools) and the standalone route.ts (~12 tools). The two implementations already diverge — route.ts has the amount type bug, fewer tools (missing hubspot_update_deal, hubspot_associate), and different response shapes. Shared business logic (API calls, response mapping) could be extracted to avoid these inconsistencies.
Additional Locations (1)
|
|
||
| const googleServer = mcpSettings?.servers?.google as { url?: string; type?: string }; | ||
| expect(googleServer.url).toContain("/api/mcps/google/mcp"); | ||
| expect(googleServer.url).toContain("/api/mcps/google/streamable-http"); |
There was a problem hiding this comment.
Test expects URL path not matching server config
Medium Severity
The test assertion was changed to expect the Google MCP server URL to contain /api/mcps/google/streamable-http, but MCP_SERVER_CONFIGS in runtime-factory.ts still defines the URL as /api/mcps/google/mcp. The transformMcpSettings method only prepends the base URL to relative paths — it doesn't replace mcp with the transport type. This test will fail.
| if (domain) properties.domain = domain; | ||
| if (industry) properties.industry = industry; | ||
| if (numberofemployees) properties.numberofemployees = numberofemployees; | ||
| if (annualrevenue) properties.annualrevenue = annualrevenue; |
There was a problem hiding this comment.
Falsy check drops zero for numeric company fields
Low Severity
if (numberofemployees) and if (annualrevenue) use falsy checks on numeric values, silently dropping 0. This is inconsistent within the same file, where hubspot_create_deal correctly uses if (amount !== undefined). A startup with 0 annual revenue would have the field silently omitted.


This pull request introduces expanded support for OAuth integrations, particularly adding HubSpot, Linear, Notion, GitHub, and Slack alongside Google. The changes enhance both the connection and revocation flows, update example usage, and add HubSpot-specific server configuration. Additionally, minor code cleanups and formatting improvements are made across several files.
OAuth integration enhancements:
lib/eliza/runtime-factory.ts, enabling HubSpot-specific OAuth flows.oauthConnectActionandoauthRevokeActionto support HubSpot, Linear, Notion, GitHub, and Slack, updating similes, descriptions, parameters, and example flows for both actions. [1] [2] [3] [4] [5] [6] [7] [8]Code and formatting improvements:
lib/eliza/runtime-factory.ts,oauth-connect.ts, andoauth-revoke.ts. [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17]Twitter callback robustness:
Error handling fixes:
Note
Medium Risk
Adds a new OAuth provider and multiple HubSpot MCP endpoints, and changes OAuth token/user-info retrieval for some providers (incl. Linear/Slack), which could break existing connection flows if misconfigured. Runtime cache key behavior also changes based on connected MCP platforms, which may affect cache hit rates and invalidation.
Overview
Introduces HubSpot integration end-to-end: a new
hubspotOAuth provider (with scopes and token-metadata lookup), HubSpot MCP tools for CRM objects (contacts/companies/deals/owners + associations), and a standalone HubSpot MCP server underapp/api/mcps/hubspot/[transport].Updates the main MCP handler to register HubSpot tools, extends Eliza’s
OAUTH_CONNECT/OAUTH_REVOKEactions to recognize HubSpot (copy + examples), and adjusts runtime creation/caching so MCP server injection varies by connected OAuth platforms (avoiding stale cached runtimes missing newly connected tools).Enhances the OAuth2 adapter/provider registry with an optional
tokenInfoendpoint (used for HubSpot) and tweaks provider configs (notably Linear token exchange content type, Slack user info endpoint/scopes, and Google scope set). Adds a helper script to purge HubSpot connections and new tests covering HubSpot tool behavior and MCP server URL expectations.Written by Cursor Bugbot for commit 39bb5e7. This will update automatically on new commits. Configure here.