MCP Vendor Intelligence
Third-PartyWraps HubSpot

peakmojo/mcp-hubspot

by peakmojo (Bary Huang)v0.2.0 (per pyproject.toml)Updated 2025-11-11

A Python-based HubSpot MCP that adds vector storage and persistent local caching of CRM data on top of HubSpot's API — features marketed as "context retention" for AI assistants. The headline concern is that the local cache is full plaintext JSON of HubSpot record content, with no encryption, a 7-day rolling window, and no documented retention policy. CLI-flag credential exposure in the host process list, an exact-pinned dependency with known CVEs, and tool-count discrepancies (16 tools registered, 7 documented) round out the concerns. If your organization has data residency requirements, encryption-at-rest mandates, or audit constraints on CRM data, this MCP places that data outside the control plane you've reasoned about.

6 findings7 BTI-M codes
Max severity (buyer-facing)
HIGH

How To Read This Profile

BLACKOUT performed static analysis of the published npm package and supporting documentation, classified each behavior using our BTI-M (Behavioral Threat Intelligence — MCP runtime) taxonomy, and recorded findings with severity calibrated from a buyer's perspective. Anomalies below the findings list are hygiene observations that may not directly affect security but contribute to the overall trust posture.

Overview

What This MCP Does

Who built it. PeakMojo (Bary Huang) — independent publisher. Distributed as a Python package (not npm) and as a Docker image, with source on GitHub at peakmojo/mcp-hubspot. Version 0.2.0.

What it does. Connects an LLM agent to HubSpot CRM, with an additional layer the package describes as "context retention": persistent local caching of every record the agent touches and FAISS-based vector storage for semantic search across previous interactions. Required HubSpot OAuth scopes include read/write on contacts and companies plus sales-email-read.

What it collects. A HubSpot access token, passed via a CLI flag (which exposes it in the host's process list — see findings). Then every CRM record the LLM accesses gets cached locally as plaintext JSON, including full record content. Vector embeddings of past interactions are also persisted on disk.

What it stores. Plaintext JSON copies of HubSpot CRM records in a configurable storage directory, retained on a 7-day rolling window, with no encryption at rest. The storage location is also inconsistently configured across multiple files in the package, so data can land in unexpected paths depending on which code path is invoked.

Where data goes. HubSpot's API for live tool calls. Critically, the local cache means CRM data leaves HubSpot's control plane and lives wherever the MCP is running — including any backups, snapshots, or filesystem-replication systems that capture that directory.

Findings

6 Findings

7 BTI-M CODES

Each finding maps to one or more BTI-M codes. Buyer-facing severity reflects how a reasonable buyer should weigh the issue when deciding whether to deploy this MCP. Code review severity (when shown separately) is the analyst's view of the underlying defect or design choice.

Finding 1BTI-M02MCP-S-PEAKMOJO-001

Persistent local plaintext cache of HubSpot CRM data — no encryption, 7-day window, undocumented retention

HIGH

The package caches every CRM record the LLM accesses to local disk as plaintext JSON, including the full record content (not just IDs or summaries). The cache rolls over after 7 days but the README doesn't disclose this retention behavior or warn that records are stored unencrypted. For organizations with data residency requirements, encryption-at-rest mandates, or audit controls on CRM data, this places that data outside the control plane those policies cover. The cache is a feature — it enables the vector-search context retention the package markets — but the privacy implications aren't surfaced.

Exploit precondition
None. The behavior is not an exploit — it's the documented design of the server, but with material understatement about what's persisted and for how long.
Evidence (6 references)
  • pyproject.toml:7 — production dependencies include faiss-cpu>=1.7.4, numpy>=1.24.0, sentence-transformers>=2.2.2, huggingface-hub==0.14.1. The vector-storage stack is unconditional.
    • src/mcp_server_hubspot/utils.py:25-71 (store_in_faiss) — receives a list of CRM data items and stores both the embedding and the full record content in metadata: metadata = {"type": data_type, "data": item} (line 58-61). The data field is the unmodified HubSpot record (contact properties, company details, message bodies, etc.).
    • src/mcp_server_hubspot/faiss_manager.py:15def __init__(self, storage_dir: str = "/storage", max_days: int = 7, embedding_dimension: int = 384) — the rolling window is hardcoded at 7 days.
    • src/mcp_server_hubspot/faiss_manager.py:208-214 — vector index serialized to index_YYYY-MM-DD.faiss, metadata serialized as plain JSON to metadata_YYYY-MM-DD.json. Plaintext at rest, no encryption.
    • src/mcp_server_hubspot/core/storage.py:46-50 (ThreadStorage.save_cache) — separate plaintext JSON cache for conversation threads at storage/conversation_threads.json. Conversation message bodies, including customer support exchanges, written verbatim.
    • README.md:47 — claim: "Data persists between sessions in configurable storage directory." No mention of: what data, what format (plaintext JSON), what retention period (7 days), or encryption posture (none).
Also cites:BTI-X04
Finding 2BTI-M16bMCP-S-PEAKMOJO-002

An exact-pinned 24-month-old version of `huggingface-hub` carries known CVEs

MEDIUM

The package depends on huggingface-hub==0.14.1, exact-pinned to a version released two years prior. Multiple security advisories have been published against that version since, none of which are addressed in the package's pinned dependency. Standard pip-audit runs will flag this; buyers in environments with vulnerability-management requirements will need to either fork or wait for the publisher to update.

Exploit precondition
Either (a) a maliciously-crafted model on HuggingFace Hub that the server downloads, or (b) a pip install -e . install path (non-Docker) where the model is fetched at first run rather than pre-baked
Evidence (2 references)
  • pyproject.toml:7huggingface-hub==0.14.1 (note the exact pin, not >=0.14.1)
    • Release date of 0.14.1: April 2023. Current huggingface-hub is 0.30+ (May 2026).
    • Multiple CVEs filed against huggingface-hub between releases 0.14 and 0.30 — including CVE-2024-42476 and others. Buyer should run pip-audit for live exposure list.
    • Dockerfile:13-15 — pre-downloads all-MiniLM-L6-v2 at build time, mitigating runtime download in Docker mode.
    • src/mcp_server_hubspot/server.py:88-90 — non-Docker code path: embedding_model = SentenceTransformer('all-MiniLM-L6-v2') triggers HuggingFace Hub download.
Also cites:BTI-M16a
Finding 3BTI-M01MCP-S-PEAKMOJO-003

Tool count documented in the README is less than half what the package actually registers

LOW

The README enumerates 7 tools. The package's runtime tool list has 16. Buyers who scope their LLM agent's permissions based on the documented tool set may find the agent has access to operations they didn't anticipate.

Exploit precondition
None
Evidence (2 references)
  • README.md:32-40 — "Available Tools" table lists 7 tools: hubspot_create_contact, hubspot_create_company, hubspot_get_company_activity, hubspot_get_active_companies, hubspot_get_active_contacts, hubspot_get_recent_conversations, hubspot_search_data.
    • src/mcp_server_hubspot/server.py:189-281 — registers 16 tools: 5 company tools (create/get_activity/get_active/get/update), 4 contact tools (create/get_active/get/update), 1 conversation tool, 2 ticket tools (get/get_threads), 1 search tool, 3 property tools (get/update/create).
    • 9 undocumented tools: hubspot_get_company, hubspot_update_company, hubspot_get_contact, hubspot_update_contact, hubspot_get_tickets, hubspot_get_ticket_conversation_threads, hubspot_get_property, hubspot_update_property, hubspot_create_property.
Finding 4BTI-M08MCP-S-PEAKMOJO-004

Authentication flag passes the HubSpot access token through the host process list

MEDIUM

The package accepts the HubSpot access token via a --access-token command-line flag. On Linux and macOS, command-line arguments are visible to any user on the host via ps, /proc/<pid>/cmdline, or process-list scrapers. Containerized environments often expose process lists across containers in the same pod. Buyers should pass credentials via environment variable instead, but the package documents the CLI-flag path as the primary option.

Exploit precondition
Another user on the same host has read access to /proc/<pid>/cmdline (Linux) or equivalent process-listing facility on macOS / Windows
Evidence (2 references)
  • src/mcp_server_hubspot/__init__.py:31-37argparse adds --access-token flag
    • src/mcp_server_hubspot/server.py:354 — same pattern in the alternate entry point
    • HubSpot Private App tokens passed as CLI args land in ps aux output and /proc/<pid>/cmdline, readable by any local user with appropriate filesystem permissions.
Finding 5BTI-M01MCP-S-PEAKMOJO-005

Storage directory is configured inconsistently across three files in the package

LOW

The location where the local cache (Finding 1) is written is set in three different places in the package, with three different defaults. Depending on which code path is invoked, cached CRM data may land in any of those three locations. Buyers attempting to lock down the storage directory with file-system permissions will need to lock down all three, and may not realize they exist.

Exploit precondition
None
Evidence (3 references)
  • src/mcp_server_hubspot/__init__.py:21os.environ["HUBSPOT_STORAGE_DIR"] = "/storage" (sets an env var)
    • src/mcp_server_hubspot/server.py:99storage_dir = os.getenv("HUBSPOT_STORAGE_DIR_LOCAL", "/storage") — reads a different env var name (HUBSPOT_STORAGE_DIR_LOCAL, not HUBSPOT_STORAGE_DIR)
    • src/mcp_server_hubspot/hubspot_client.py:40storage_dir = pathlib.Path("storage") — uses a third path: relative "storage/" from CWD
    • Net effect: The env var set in __init__.py is never read. ThreadStorage writes to a different location than FaissManager. A user trying to relocate "the storage" via the documented -v Docker volume only relocates the FAISS data, not the conversation threads cache.
Finding 6BTI-X12MCP-S-PEAKMOJO-006

No SECURITY.md, no PRIVACY.md, no documented retention or purge mechanism

MEDIUM

The package's persistent storage feature stores CRM data on disk indefinitely (subject to the 7-day rolling window in Finding 1) without any documented mechanism for the buyer to purge it. There's no PRIVACY.md describing what data is collected, how long it's kept, or how to delete it. There's no SECURITY.md describing the threat model or vulnerability disclosure process.

Exploit precondition
None — this is a documentation and process gap, not an attack vector
Evidence (1 reference)
  • No SECURITY.md in repo root
    • No PRIVACY.md or equivalent
    • No CHANGELOG.md
    • No documented purge command or "clear cache" tool surfaced via MCP
    • No retention configuration surfaced via env var or config file (max_days = 7 is hardcoded in faiss_manager.py:15)
BTI-M Code Index

Codes Cited In This Profile

Each link goes to the BTI-M code reference page where you can see every MCP server in the corpus that triggered the same code.