Skip to main content
Glama
amin-ale

HubSpot CRM MCP Server

by amin-ale

HubSpot CRM MCP Server

CI License: MIT

HubSpot CRM MCP server for Claude Desktop and any MCP client, free-tier compatible. 15 tools over contacts, deals, and pipelines, authenticated with either a HubSpot private-app token or a full OAuth app. Writes are idempotent, so a retried tool call replays its first result instead of creating a duplicate record, and every tool call is written to a PII-redacted audit trail, including the calls denied for a missing scope and the calls that errored.

Related: QuickBooks Online MCP Server · MCP Audit Gateway · What production MCP actually requires

HubSpot runs a hosted MCP server of its own, with wider object coverage than this one. It is built for self-hosting: you run the process, the source is short enough to read in a sitting, and the audit trail, the cache, and the credentials never leave your infrastructure.

The rest is what a REST wrapper usually leaves out. It prompts for the exact missing scope instead of leaking a raw 403, retries rate limits with backoff, serves record reads from a local cache (TTL plus write-invalidation, with a signature-verified webhook handler you can wire to your own HTTP ingress for out-of-band changes), walks cursor pagination, and returns per-item results when a batch partially fails.

See docs/COMPARISON.md for side-by-side transcripts of a naive API-wrapper MCP versus this one. Every transcript is generated by running both against the mocked test suite.

Architecture

flowchart TD
    Agent["MCP client / agent"] -->|"stdio (JSON-RPC)"| Server["FastMCP server<br/>server.py"]
    Server --> Service["CrmService<br/>scope checks · audit · orchestration"]

    Service --> Cache["LocalCache<br/>TTL + write invalidation"]
    Service --> Idem["Idempotency store"]
    Service --> Audit["Audit log<br/>PII redaction"]
    Service --> Client["HubSpotClient<br/>retries · pagination · error mapping"]

    Client --> Auth["Token provider<br/>private-app · OAuth refresh"]
    Client -->|HTTPS| HubSpot["HubSpot CRM API"]

    Ingress["Your HTTP ingress<br/>(optional, host-provided)"] -->|"signed v3 payload"| Processor["WebhookProcessor<br/>verify_signature"]
    Processor -->|"invalidate(object)"| Cache

The stdio server speaks JSON-RPC only; it does not listen for webhooks. WebhookProcessor and verify_signature are shipped as a tested component you mount on your own HTTP ingress (see Webhook cache invalidation).

Related MCP server: hubspot-mcp

Tools

Every tool carries MCP annotations (readOnlyHint, destructiveHint, idempotentHint, openWorldHint), a described input schema, and an output schema.

Tool

What it does

Scope

crm_search_contacts

Search contacts by free-text term, one page at a time

crm.objects.contacts.read

crm_list_contacts

List contacts in id order with cursor pagination

crm.objects.contacts.read

crm_get_contact

Fetch one contact by id, served from the local cache

crm.objects.contacts.read

crm_create_contact

Create a contact, idempotent on a supplied or derived key

crm.objects.contacts.write

crm_update_contact

Overwrite the properties passed and invalidate the cache entry

crm.objects.contacts.write

crm_delete_contact

Archive (soft-delete) a contact

crm.objects.contacts.write

crm_batch_create_contacts

Create up to 100 contacts and report per-row failures

crm.objects.contacts.write

crm_search_deals

Search deals by free-text term with cursor pagination

crm.objects.deals.read

crm_get_deal

Fetch one deal by id, served from the local cache

crm.objects.deals.read

crm_create_deal

Create a deal, idempotent on a supplied or derived key

crm.objects.deals.write

crm_update_deal

Update deal properties, including moving it to another stage

crm.objects.deals.write

crm_delete_deal

Archive (soft-delete) a deal

crm.objects.deals.write

crm_list_pipelines

List deal pipelines with stages in display order (cached)

crm.schemas.deals.read

crm_get_pipeline

Fetch one pipeline with its ordered stages

crm.schemas.deals.read

crm_export_audit_log

Export the session audit trail as JSON Lines

none

Quickstart

Run it without installing anything permanent:

uvx mcp-hubspot

Or install it:

pip install mcp-hubspot
mcp-hubspot

Either form speaks MCP over stdio on stdin and stdout. Authenticate with either a private-app token (HUBSPOT_PRIVATE_APP_TOKEN) or an OAuth app (HUBSPOT_CLIENT_ID + HUBSPOT_CLIENT_SECRET + HUBSPOT_REFRESH_TOKEN). The server auto-detects which is present.

Credentials are resolved lazily. The server starts and answers initialize and tools/list with nothing configured, which is what lets a directory or a sandbox introspect it; the first tool call is where a missing credential turns into an actionable error.

Wiring into an MCP client

Add to your MCP host config (for example Claude Desktop's claude_desktop_config.json):

{
  "mcpServers": {
    "hubspot": {
      "command": "uvx",
      "args": ["mcp-hubspot"],
      "env": { "HUBSPOT_PRIVATE_APP_TOKEN": "pat-na1-..." }
    }
  }
}

Docker

docker build -t mcp-hubspot .
docker run --rm -i -e HUBSPOT_PRIVATE_APP_TOKEN=pat-na1-... mcp-hubspot

From source

Requires Python 3.12+ and uv.

uv venv
uv pip install -e ".[dev]"

cp .env.example .env   # then fill in your HubSpot credentials
uv run mcp-hubspot

OAuth authorization URL

For the OAuth flow, mcp_crm.auth.build_authorization_url(...) builds the consent URL with the scopes the tools need; exchange the returned code for a refresh token and set HUBSPOT_REFRESH_TOKEN.

Webhook cache invalidation

The stdio server does not receive webhooks. To invalidate the cache on changes made outside this process (edits in the HubSpot UI, other integrations), mount WebhookProcessor on your own HTTP endpoint and verify HubSpot's v3 signature with verify_signature (using the HUBSPOT_WEBHOOK_SECRET you configure). Point it at the same LocalCache your CrmService uses:

from mcp_crm.webhooks import WebhookProcessor, verify_signature

processor = WebhookProcessor(cache)

def handle_hubspot_webhook(request):
    ok = verify_signature(
        secret=webhook_signing_secret,
        method="POST",
        uri=request.url,
        body=request.raw_body,
        signature=request.headers["X-HubSpot-Signature-v3"],
        timestamp=request.headers["X-HubSpot-Request-Timestamp"],
    )
    if not ok:
        return 401
    processor.process(request.json())
    return 200

verify_signature rejects tampered bodies, wrong secrets, and stale timestamps; WebhookProcessor.process maps each subscription to the object it invalidates and reports what it touched.

Design decisions

  • Cache scope. Only object-detail reads (crm_get_contact, crm_get_deal) and the pipeline list are cached; list/search results are query-dependent and left uncached to avoid serving stale result sets. Writes invalidate the relevant object immediately. For out-of-band changes, a signature-verified WebhookProcessor ships as a component you mount on your own HTTP ingress (see Webhook cache invalidation); the stdio server itself does not listen for webhooks.

  • Idempotency is client-side. HubSpot's create endpoints are not natively idempotent, so a key (supplied or derived from the payload) is stored and replayed. This makes at-least-once tool retries safe without duplicating records. The store lives in the process, so it covers retries within a session rather than across restarts, and crm_batch_create_contacts deliberately does not use it; both facts are stated in the tool descriptions.

  • Scope prompting happens twice. The service pre-checks granted scopes (via token introspection) for a fast, actionable error, and the HTTP client also maps a server-side MISSING_SCOPES 403 to the same typed error (belt and suspenders).

  • Credentials load lazily. Nothing reads a token at import or at startup. A missing credential surfaces as a typed error on the first tool call, and that failure is audited like any other, so the server is still introspectable in a sandbox with an empty environment.

  • Backoff and clocks are injectable. Retry sleep, RNG jitter, and time sources are constructor parameters, which is why the whole suite runs offline in well under a second.

Testing

Every external HubSpot call is served by an in-memory fake (tests/fake_hubspot.py) backed by JSON fixtures (tests/fixtures/), wired in through httpx.MockTransport. No network, no credentials, deterministic.

uv run pytest -q

Regenerate the comparison document (CI also checks it stays in sync):

uv run python scripts/generate_comparison.py

Registry metadata

server.json describes this package for the Model Context Protocol registry (io.github.amin-ale/hubspot-crm-mcp, PyPI mcp-hubspot, stdio transport). .mcp.json is the minimal client config for tools that auto-detect MCP servers from a repository root.

Scope and safety

This is a client for HubSpot data you own or are authorized to access. Point it only at HubSpot accounts you control or have written permission to operate. The audit log redacts emails and phone numbers before writing records; treat exported audit logs as sensitive regardless. Behaviour documented here is point-in-time against the included fixtures, not a guarantee about any live HubSpot account.

Hire me

I build MCP servers and API integrations that survive a senior-dev code review: auth, retries, idempotency, and audit trails included, not bolted on later. Portfolio and contact: https://amin-ale.github.io/portfolio-site · amin.ale.business@gmail.com.

Available Tools

15 tools
crm_batch_create_contactsA

Create many HubSpot contacts in one request and report the outcome of every row.

Use this for bulk loads. Use crm_create_contact instead when a retry must not risk duplicates.

Writes, additively. Needs the crm.objects.contacts.write scope. Returns 'succeeded' with the created records and 'failed' with the input index, HubSpot status code, category, message, and original row for every rejection, so a partial failure never hides the rows that did save. This path does not go through the idempotency store: running the same batch twice can create duplicates, so re-send only the rows listed in 'failed'. Cached contact reads are invalidated.

ParametersJSON Schema
NameRequiredDescriptionDefault
contactsYesContacts to create, up to 100 per call. Each item is a flat property map such as {'email': 'ada@example.com', 'firstname': 'Ada'}. Keys other than email, firstname, lastname, phone, and company are sent through as custom HubSpot properties.

Output Schema

ParametersJSON Schema
NameRequiredDescription
failedNoRows HubSpot rejected, with the reason for each.
succeededNoRecords HubSpot created, each with 'id'.

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

It discloses the required write scope, the response shape with 'succeeded' and 'failed' arrays, the non-idempotent behavior, and cache invalidation — all beyond the annotations. This gives the agent a full behavioral model.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is about five sentences, each with a distinct purpose: purpose, usage, behavior, idempotency caveat, cache effect. No fluff.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a batch mutation tool, it covers permissions, partial failure semantics, retry guidance, and response structure, so the agent can invoke it safely. The output schema is not shown but the description adequately describes the return value.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema already describes the contacts array with 100% coverage, and the description adds that failures include the input index and original row, making the parameter's ordering meaningful. It also refers to 'the same batch' which contextualizes the array as a unit.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with 'Create many HubSpot contacts in one request and report the outcome of every row,' which is a specific action and resource. It also contrasts with crm_create_contact by emphasizing 'many... in one request,' making the distinction clear.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly states 'Use this for bulk loads' and advises using crm_create_contact when a retry must not risk duplicates, giving clear when-to-use and when-not-to-use guidance. No other tool needs that exclusion.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

crm_create_contactA
Idempotent

Create one HubSpot contact from the properties given and return the new record.

Use this for single creates, especially ones a retry might repeat. Use crm_batch_create_contacts to load many rows at once, and crm_update_contact to change a record that already exists.

Writes, additively. Needs the crm.objects.contacts.write scope. The call is keyed by 'idempotency_key', or by a hash of the properties when none is given, and a repeat of the same key replays the stored record instead of creating a second one. That key store lives in this process, so it covers retries within a session and not restarts. Cached contact reads are invalidated. At least one property must be supplied or the call fails validation.

ParametersJSON Schema
NameRequiredDescriptionDefault
emailNoPrimary email address. HubSpot deduplicates contacts on this value.
phoneNoPhone number in any format HubSpot accepts, e.g. '+1-202-555-0101'.
companyNoCompany name stored on the contact. This is a text property, not an association to a company record.
lastnameNoFamily name. HubSpot property 'lastname'.
firstnameNoGiven name. HubSpot property 'firstname'.
idempotency_keyNoCaller-chosen key that makes a retry replay the first result instead of creating a second record. Omit to derive one from the properties.

Output Schema

ParametersJSON Schema
NameRequiredDescription
idYesHubSpot record id, stable for the life of the record.
archivedNoTrue when the record is archived (soft-deleted).
propertiesNoProperty values HubSpot returned for the requested property set.

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the annotations (readOnlyHint=false, idempotentHint=true), the description discloses the required OAuth scope, the session-scoped idempotency store, cache invalidation on writes, and the validation requirement of at least one property. No contradiction with annotations; it meaningfully enriches them.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three tight paragraphs: purpose, usage guidance, and behavioral details. Every sentence carries information, with no filler or repetition of the schema. Front-loaded with the core purpose.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present, the description appropriately avoids re-explaining return values. It covers authentication, idempotency semantics, cache effects, and validation constraints, making it sufficient for an agent to invoke correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3. The description adds valuable semantic context beyond the schema: the 'at least one property' validation rule and the idempotency key derivation/replay behavior. This justifies a 4.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: 'Create one HubSpot contact from the properties given and return the new record.' It explicitly contrasts with sibling tools like crm_batch_create_contacts and crm_update_contact, making the tool's unique role unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit when-to-use guidance: 'Use this for single creates, especially ones a retry might repeat.' It also names alternatives: crm_batch_create_contacts for bulk loading and crm_update_contact for existing records, giving clear decision criteria.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

crm_create_dealA
Idempotent

Create one HubSpot deal from the properties given and return the new record.

Call crm_list_pipelines first when setting 'pipeline' or 'dealstage', because HubSpot only accepts a stage id that belongs to the chosen pipeline. Use crm_update_deal to move an existing deal.

Writes, additively. Needs the crm.objects.deals.write scope. The call is keyed by 'idempotency_key', or by a hash of the properties when none is given, and a repeat of the same key replays the stored record instead of creating a second one. That key store lives in this process, so it covers retries within a session and not restarts. Cached deal reads are invalidated. At least one property must be supplied.

ParametersJSON Schema
NameRequiredDescriptionDefault
amountNoDeal value as a decimal string in the account currency, e.g. '2500.00'.
dealnameNoDeal title shown on the pipeline board.
pipelineNoPipeline id from crm_list_pipelines. HubSpot uses the account's default pipeline when this is omitted.
dealstageNoStage id taken from the chosen pipeline's 'stages'. HubSpot rejects a stage id that belongs to a different pipeline.
close_dateNoExpected close date as an ISO 8601 date or timestamp, e.g. '2026-09-30'. Maps to HubSpot's 'closedate' property.
idempotency_keyNoCaller-chosen key that makes a retry replay the first result instead of creating a second record. Omit to derive one from the properties.

Output Schema

ParametersJSON Schema
NameRequiredDescription
idYesHubSpot record id, stable for the life of the record.
archivedNoTrue when the record is archived (soft-deleted).
propertiesNoProperty values HubSpot returned for the requested property set.

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description discloses substantial behavioral traits beyond the annotations: it states the operation is 'Writes, additively,' explains the idempotency key mechanism, the in-process key store limitation ('covers retries within a session and not restarts'), cache invalidation, and the minimum property requirement. These details go well beyond the annotations' idempotentHint and destructiveHint flags.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is effectively structured into three paragraphs: purpose, prerequisite guidance, and behavioral details. Every sentence provides necessary information with zero fluff. It is appropriately sized for the complexity of the tool.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity—6 optional parameters, external dependency on crm_list_pipelines, OAuth scope requirements, idempotency semantics, and cache invalidation—the description covers all essential context. Return values are covered by the output schema, so no further explanation is needed.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema already provides strong descriptions for all 6 parameters (100% coverage), so the baseline is 3. The description adds critical cross-parameter semantics, noting that HubSpot 'only accepts a stage id that belongs to the chosen pipeline' and explaining idempotency_key behavior ('or by a hash of the properties when none is given'). This adds meaningful meaning beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's function: 'Create one HubSpot deal from the properties given and return the new record.' It uses a specific verb and resource, and distinguishes itself from the sibling crm_update_deal by explicitly saying 'Use crm_update_deal to move an existing deal.'

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit when-to-use guidance with a prerequisite call: 'Call crm_list_pipelines first when setting pipeline or dealstage,' and directs users to an alternative tool: 'Use crm_update_deal to move an existing deal.' Also states the required OAuth scope and the condition 'At least one property must be supplied.'

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

crm_delete_contactA
DestructiveIdempotent

Archive (soft-delete) a HubSpot contact so it drops out of active views.

Use this instead of asking a human to delete in the UI. This server never issues a hard delete; HubSpot keeps archived records restorable from its own UI for a limited window.

Writes, and destructive from an agent's point of view. Needs the crm.objects.contacts.write scope. Archiving the same id again leaves it archived. The record is dropped from the read cache and stops appearing in list and search results. Raises a not-found error for an id that never existed.

ParametersJSON Schema
NameRequiredDescriptionDefault
contact_idYesHubSpot contact record id, as returned by search, list, or create.

Output Schema

ParametersJSON Schema
NameRequiredDescription
idYesId of the record that was archived.
archivedYesAlways true. The record was archived, not permanently deleted.

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description goes far beyond the annotations (readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true) by disclosing the soft-delete nature, required OAuth scope ('crm.objects.contacts.write'), idempotent behavior, cache invalidation, and not-found error handling. This gives the agent a full understanding of side effects and prerequisites.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with a clear one-sentence summary, followed by usage guidance, behavioral details, permissions, and error cases. Each sentence adds unique value; there is no fluff or repetition. Despite being a bit long, it's well-organized and efficient for a destructive tool.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's simplicity (one parameter), the description covers all critical aspects: purpose, when to use, safety, permissions, idempotency, cache effects, and error behavior. The output schema exists, so return details are not needed. This is thoroughly complete for an agent to safely invoke the tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema already fully describes contact_id as 'HubSpot contact record id, as returned by search, list, or create,' achieving 100% schema description coverage. The description adds no new parameter semantics beyond noting that an unknown id raises a not-found error, which is minor.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with 'Archive (soft-delete) a HubSpot contact so it drops out of active views,' which precisely states the action (archive/soft-delete), resource (HubSpot contact), and effect. It clearly distinguishes from sibling tools like crm_get_contact or crm_update_contact by focusing on deletion behavior.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says 'Use this instead of asking a human to delete in the UI,' giving a clear when-to-use directive. It also notes the tool 'never issues a hard delete' and that records are restorable for a limited window, which helps the agent decide if this is appropriate for the task.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

crm_delete_dealA
DestructiveIdempotent

Archive (soft-delete) a HubSpot deal so it drops off the pipeline board.

Prefer moving a lost deal to a closed-lost stage with crm_update_deal, which keeps it in reporting. Archive only when the record should not have existed.

Writes, and destructive from an agent's point of view. Needs the crm.objects.deals.write scope. Archiving the same id again leaves it archived. The record is dropped from the read cache and stops appearing in searches. Raises a not-found error for an id that never existed.

ParametersJSON Schema
NameRequiredDescriptionDefault
deal_idYesHubSpot deal record id, as returned by search or create.

Output Schema

ParametersJSON Schema
NameRequiredDescription
idYesId of the record that was archived.
archivedYesAlways true. The record was archived, not permanently deleted.

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the annotations, the description exposes key behaviors: soft-delete semantics, dropping from the read cache and search results, idempotency (archiving again leaves it archived), and not-found errors for never-existing IDs. It also reinforces the destructive nature from the agent's perspective while aligning with the destructiveHint and idempotentHint annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is structured with the core action first, then usage guidance, then behavioral details. Every sentence earns its place; the length is justified for a destructive operation with important caveats.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's destructive nature, annotations, and full schema coverage, the description covers all essential context: scope requirements, error behavior, idempotency, and cache/search effects. The presence of an output schema means returning the archive confirmation is already handled, so no return-value explanation is needed.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The single parameter is fully documented in the schema (100% coverage), so the description doesn't need to add parameter syntax. It does add useful context by explaining repeated calls with the same id are idempotent, but the schema already provides the essential meaning.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool archives (soft-deletes) a HubSpot deal so it disappears from the pipeline board. It distinguishes itself from the sibling crm_update_deal by explicitly contrasting archiving with moving to a closed-lost stage.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives explicit guidance: prefer crm_update_deal for moving to closed-lost, and only archive when the record should not have existed. It also notes the required OAuth scope, providing clear when-to-use and when-not-to-use context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

crm_export_audit_logA
Read-onlyIdempotent

Export this session's audit trail as JSON Lines, one record per line.

Use this to hand a reviewer evidence of what the agent did, or to attach the trail to a ticket or change record.

Read-only and entirely local: it reads an in-memory buffer, makes no HubSpot call, and needs no scope. Every other tool call in the session is present, including calls denied for a missing scope and calls that raised an error, each with an id, timestamp, actor, tool name, outcome, the arguments, a short result summary, and the idempotency key where one applied. Email addresses and phone numbers are redacted before a record is stored. The buffer is not persisted, so it starts empty every time the server starts and an export after a restart covers only the new session.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description goes far beyond the annotations. It discloses that the tool is entirely local and reads an in-memory buffer, makes no HubSpot call, requires no scope, includes even denied/error calls, redacts email/phone, and explains the buffer is not persisted across restarts. These details fully characterize behavior, aligning with and expanding upon the read-only and idempotent hints.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured and efficient. The first sentence states the core function, the second provides use cases, and the remaining sentences deliver behavioral details without redundancy. Every sentence earns its place, making it concise despite being detailed.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Even with no parameters and an output schema present, the description covers all necessary context: purpose, use cases, input (none), output format and content, behavioral traits, redaction, and session-scope limitations. It is fully self-contained and leaves no significant gaps for the agent.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With zero parameters, the baseline is 4. The description doesn't need to explain parameter semantics; it adds value by describing the output format and content, which is appropriate for a no-input tool.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool exports the session's audit trail as JSON Lines with one record per line. The verb 'export' and resource 'audit trail' are specific, and the format 'JSON Lines' adds clarity. This is distinct from sibling CRM tools that focus on contacts, deals, and pipelines.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides explicit when-to-use guidance: 'Use this to hand a reviewer evidence... or to attach the trail to a ticket or change record.' It does not mention exclusions or alternative tools, though no alternatives likely exist in the given sibling set, so the absence is not a significant gap.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

crm_get_contactA
Read-onlyIdempotent

Fetch one HubSpot contact by record id with its email, name, phone, and company.

Use this once an id is known. Use crm_search_contacts to find the id first.

Read-only. Needs the crm.objects.contacts.read scope. Served from an in-process cache with a 300 second default TTL; writes made through this server invalidate the entry immediately, and a cache hit is recorded in the audit log as a cache hit rather than a live read. Raises a not-found error for an unknown or archived id.

ParametersJSON Schema
NameRequiredDescriptionDefault
contact_idYesHubSpot contact record id, as returned by search, list, or create.

Output Schema

ParametersJSON Schema
NameRequiredDescription
idYesHubSpot record id, stable for the life of the record.
archivedNoTrue when the record is archived (soft-deleted).
propertiesNoProperty values HubSpot returned for the requested property set.

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the annotations (readOnly, idempotent, not destructive), the description adds crucial operational context: required OAuth scope, in-process cache with 300s TTL, write-invalidation behavior, audit log implications, and the not-found error for unknown/archived ids. No contradiction with annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the core action, then adds usage guidance and operational details. Every sentence contributes essential information—retrieval, prerequisite usage, permissions, caching, invalidation, and error behavior—with no redundancy or fluff.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the simple one-parameter schema, available output schema, and strong annotations, the description covers all necessary operational nuances: when to use, required scope, caching behavior, and error handling. The agent has complete guidance without needing to infer undefined behavior.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema already fully describes the single parameter (contact_id) with its type and source ('as returned by search, list, or create'). The description's phrase 'by record id' adds no additional meaning beyond the schema, so a baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool fetches one HubSpot contact by record id, specifying the returned fields (email, name, phone, company). It distinguishes itself from sibling tools like search, list, create, and delete by focusing on retrieval of a known id.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly instructs to use the tool only after an id is known and names the alternative (crm_search_contacts) to find the id first. This provides clear sequencing and differentiates from the search tool.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

crm_get_dealA
Read-onlyIdempotent

Fetch one HubSpot deal by record id with its name, amount, pipeline, and stage.

Use this once an id is known; use crm_search_deals to find the id first.

Read-only. Needs the crm.objects.deals.read scope. Served from an in-process cache with a 300 second default TTL, and writes made through this server invalidate the entry immediately. Raises a not-found error for an unknown or archived id.

ParametersJSON Schema
NameRequiredDescriptionDefault
deal_idYesHubSpot deal record id, as returned by search or create.

Output Schema

ParametersJSON Schema
NameRequiredDescription
idYesHubSpot record id, stable for the life of the record.
archivedNoTrue when the record is archived (soft-deleted).
propertiesNoProperty values HubSpot returned for the requested property set.

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the annotations (readOnlyHint, idempotentHint, destructiveHint), the description adds important context: required OAuth scope, in-process caching with 300s TTL, cache invalidation on writes, and not-found behavior for unknown/archived ids. These details significantly exceed annotation coverage.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise and well-structured, using short paragraphs to separate purpose, usage guidance, and operational details. Every sentence adds value, with no redundancy or filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple one-parameter read tool with a rich output schema, the description covers all essential context: purpose, usage, auth requirements, caching behavior, and error conditions. Nothing important is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With only one parameter and 100% schema description coverage, the schema already fully documents deal_id. The description adds no further semantic detail beyond what is in the schema, so the baseline score of 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb ('Fetch') and resource ('one HubSpot deal by record id'), and explicitly lists the fields returned (name, amount, pipeline, stage). It clearly distinguishes from crm_search_deals by stating this tool is used once an id is known.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly states when to use the tool ('once an id is known') and directs users to use crm_search_deals to find the id first, naming the alternative. This is clear and actionable guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

crm_get_pipelineA
Read-onlyIdempotent

Fetch one deal pipeline by id with its stages sorted by display order.

Use this when the pipeline id is already known and only its stages are needed. Use crm_list_pipelines to discover ids.

Read-only. Needs the crm.schemas.deals.read scope. Unlike the pipeline list, this call is not cached and always reaches HubSpot. Raises a not-found error for an unknown id.

ParametersJSON Schema
NameRequiredDescriptionDefault
pipeline_idYesDeal pipeline id, as returned by crm_list_pipelines.

Output Schema

ParametersJSON Schema
NameRequiredDescription
idYesPipeline id, the value to pass as a deal's 'pipeline'.
labelYesPipeline name as shown in the HubSpot UI.
stagesNoStages sorted by display order, first to last.
display_orderYesPosition of the pipeline in the account.

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint/idempotentHint, but the description adds valuable context beyond that: requires 'crm.schemas.deals.read' scope, is 'not cached and always reaches HubSpot' (unlike the pipeline list), and 'Raises a not-found error for an unknown id.' These are meaningful behavioral traits not present in annotations. No contradiction.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise and front-loaded with the core purpose. Each sentence adds value: purpose, usage guidance, discovery alternative, and behavioral caveats. No filler or redundancy. Despite several sentences, all are necessary for correct tool use.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple one-parameter tool with an output schema and strong annotations, the description covers purpose, when to use, auth scope, caching/network behavior, and error handling. It is fully complete for the task context; nothing critical is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema has 100% coverage for the single parameter with description 'Deal pipeline id, as returned by crm_list_pipelines.' The tool description repeats this context ('pipeline id is already known', 'by id') but adds little beyond the schema. With high schema coverage, baseline is 3; description offers no extra semantics.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description starts with 'Fetch one deal pipeline by id with its stages sorted by display order,' a specific verb+resource combination that clearly states the tool's function. It also distinguishes from sibling tool crm_list_pipelines by focusing on fetching a single pipeline by ID, not listing all pipelines.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit when-to-use guidance: 'Use this when the pipeline id is already known and only its stages are needed.' Also names the alternative: 'Use crm_list_pipelines to discover ids.' This directly addresses tool selection and differentiates from the list sibling.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

crm_list_contactsA
Read-onlyIdempotent

List HubSpot contacts in record-id order, one page at a time.

Use this to walk the whole contact set. Prefer crm_search_contacts when there is a term to match on, since listing everything is slower and burns rate limit.

Read-only. Needs the crm.objects.contacts.read scope; a missing scope is denied locally and recorded in the audit log. Not cached. Pass the returned 'after' cursor back for the next page and stop when it comes back null. Archived contacts are excluded.

ParametersJSON Schema
NameRequiredDescriptionDefault
afterNoCursor copied from the previous response's 'after' field. Omit for the first page.
limitNoHow many records this page may contain, 1 to 100. HubSpot caps it at 100.

Output Schema

ParametersJSON Schema
NameRequiredDescription
afterNoCursor for the next page, or null when this is the last page.
resultsYesRecords in this page, each with 'id' and 'properties'.

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the annotations (readOnlyHint, idempotentHint, openWorldHint, destructiveHint false), it adds meaningful behavioral context: required OAuth scope, local denial with audit logging, no caching, archived contacts excluded, and cursor-based pagination termination. This goes well beyond the structured metadata.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact and front-loaded, with each sentence earning its place. It mixes usage guidance, auth requirements, caching behavior, and pagination instructions without redundancy or filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Despite having an output schema (so return values are covered), the description covers all operational essentials: iteration pattern, auth, rate-limit trade-off, archived-contact filtering, and pagination. This is complete for a paged list tool of moderate complexity.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3. The description adds value by explaining how the 'after' cursor is used across pages and when to stop ('stop when it comes back null'), which complements the schema. It does not add much for 'limit', but the schema already documents its range and default.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: 'List HubSpot contacts in record-id order, one page at a time.' It clearly identifies the operation and distinguishes itself from crm_search_contacts by explicitly framing this as the paged walk-all tool.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description says exactly when to use it ('to walk the whole contact set') and when not to: 'Prefer crm_search_contacts when there is a term to match on, since listing everything is slower and burns rate limit.' It names the alternative and gives the reason.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

crm_list_pipelinesA
Read-onlyIdempotent

List every deal pipeline in the account with its stages in display order.

Call this before creating or moving a deal: it is the source of the 'pipeline' and 'dealstage' ids those tools need, and HubSpot rejects a stage id from the wrong pipeline.

Read-only. Needs the crm.schemas.deals.read scope. Served from an in-process cache with a 300 second default TTL, since pipeline definitions rarely change. Returns an empty list only if the account genuinely has no deal pipelines.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the readOnlyHint annotation, the description adds valuable context: required OAuth scope, in-process cache with 300-second TTL, and empty-list semantics. It also explicitly says 'Read-only' reinforcing the safe nature of the call.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact yet information-dense. The first sentence states the purpose, the second gives usage guidance, and the third covers behavioral details. Each sentence earns its place with no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple zero-parameter tool, the description fully covers what the tool returns, when to use it, auth requirements, caching behavior, and edge cases. The existence of an output schema covers return format details, so nothing critical is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes no parameters, so schema coverage is trivially 100%. The description adds no parameter details because none exist, but the baseline of 4 is appropriate for a zero-parameter tool.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: 'List every deal pipeline in the account with its stages in display order.' It clearly distinguishes from sibling tools like crm_get_pipeline by indicating it covers all pipelines and includes stage ordering.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly states when to use this tool ('Call this before creating or moving a deal') and explains the necessity for pipeline and dealstage ids. It doesn't explicitly mention alternatives (e.g., crm_get_pipeline for a single pipeline), but the use case is clearly defined, making it a strong 4.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

crm_search_contactsA
Read-onlyIdempotent

Search HubSpot contacts by free-text term and return one page of matches.

Use this to find contacts by name, email, company, or any other indexed property. Prefer crm_list_contacts when there is no term to match on, and crm_get_contact when the record id is already known.

Read-only. Needs the crm.objects.contacts.read scope; without it the call is denied before any CRM data request is made and the denial is written to the audit log. Search results are never cached, so every call reaches HubSpot. Pass the returned 'after' cursor back to walk further pages; a null 'after' means this was the last page. Archived contacts are excluded.

ParametersJSON Schema
NameRequiredDescriptionDefault
afterNoCursor copied from the previous response's 'after' field. Omit for the first page.
limitNoHow many records this page may contain, 1 to 100. HubSpot caps it at 100.
queryNoFree-text term matched against the record's indexed properties. Omit to match every record.

Output Schema

ParametersJSON Schema
NameRequiredDescription
afterNoCursor for the next page, or null when this is the last page.
resultsYesRecords in this page, each with 'id' and 'properties'.

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the readOnly/idempotent annotations, the description discloses the required OAuth scope, denial/audit behavior, lack of caching, pagination cursor semantics, and archived contact exclusion. No contradictions with annotations; the description adds substantial behavioral context.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact and front-loaded: the first sentence states the core purpose, the second gives usage context, the third covers auth and caching, and the fourth explains pagination. Every sentence earns its place with no redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With rich schema annotations, output schema present, and the description covering auth, safety, pagination, and filtering behavior, the agent has everything needed to select and invoke the tool correctly. No critical gaps identified.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema already describes all three parameters fully (100% coverage), including defaults and meanings. The description reinforces free-text search and 'after' cursor usage but does not add new semantics beyond the schema, so a baseline of 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a clear verb+resource+scope: 'Search HubSpot contacts by free-text term and return one page of matches.' It explicitly contrasts with sibling tools in the usage guidance, distinguishing it from crm_list_contacts (no term) and crm_get_contact (known ID).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit when-to-use guidance: 'Use this to find contacts by name, email, company, or any other indexed property.' It names alternatives: 'Prefer crm_list_contacts when there is no term to match on, and crm_get_contact when the record id is already known.' Also includes operational guidance on pagination and caching.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

crm_search_dealsA
Read-onlyIdempotent

Search HubSpot deals by free-text term and return one page of matches.

Use this to find deals by name, amount, or stage text. Use crm_get_deal when the record id is already known, and crm_list_pipelines to translate the returned pipeline and stage ids into readable labels.

Read-only. Needs the crm.objects.deals.read scope; a missing scope is denied locally and recorded in the audit log. Not cached. Pass the returned 'after' cursor back for the next page and stop when it is null. Archived deals are excluded.

ParametersJSON Schema
NameRequiredDescriptionDefault
afterNoCursor copied from the previous response's 'after' field. Omit for the first page.
limitNoHow many records this page may contain, 1 to 100. HubSpot caps it at 100.
queryNoFree-text term matched against the record's indexed properties. Omit to match every record.

Output Schema

ParametersJSON Schema
NameRequiredDescription
afterNoCursor for the next page, or null when this is the last page.
resultsYesRecords in this page, each with 'id' and 'properties'.

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description adds significant behavioral context beyond the annotations: required OAuth scope ('crm.objects.deals.read'), failure behavior ('denied locally and recorded in the audit log'), caching behavior ('Not cached'), pagination mechanism ('Pass the returned after cursor back for the next page and stop when it is null'), and filtering ('Archived deals are excluded'). No contradiction with the readOnlyHint annotation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is four sentences, each serving a clear purpose: main capability, usage guidance, permission/pagination/filtering details. There is no fluff; it is front-loaded with the core purpose and tightly packed with actionable information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's moderate complexity, the description covers all necessary context: it explains pagination, permission requirements, exclusions, and the distinction from sibling tools. An output schema exists, so not detailing return values is acceptable. This is a complete description for an AI agent to select and invoke the tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3. The description goes beyond the schema by explaining what 'query' matches ('name, amount, or stage text') and how to use the 'after' cursor for pagination. This extra context justifies a 4, though the schema already documents the parameters well.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The first sentence states a specific action: 'Search HubSpot deals by free-text term and return one page of matches,' clearly distinguishing this from siblings by specifying the resource (deals) and method (free-text search). It also explicitly contrasts with crm_get_deal and crm_list_pipelines, making the tool's unique role unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides explicit guidance: 'Use this to find deals by name, amount, or stage text' and then names alternatives: 'Use crm_get_deal when the record id is already known, and crm_list_pipelines to translate the returned pipeline and stage ids into readable labels.' This clearly answers when to use this tool versus others.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

crm_update_contactA
DestructiveIdempotent

Update properties on an existing HubSpot contact and return the updated record.

Use this to change a known record. Use crm_create_contact for records that do not exist yet.

Writes. Only the properties passed are sent, and each one overwrites the value HubSpot held before, so the previous value is lost; properties left out are untouched. Applying the same values again produces the same record, which makes a retry safe. Needs the crm.objects.contacts.write scope. Invalidates this contact's cache entry. Raises a not-found error for an unknown or archived id, and a validation error when no property is supplied.

ParametersJSON Schema
NameRequiredDescriptionDefault
emailNoPrimary email address. HubSpot deduplicates contacts on this value.
phoneNoPhone number in any format HubSpot accepts, e.g. '+1-202-555-0101'.
companyNoCompany name stored on the contact. This is a text property, not an association to a company record.
lastnameNoFamily name. HubSpot property 'lastname'.
firstnameNoGiven name. HubSpot property 'firstname'.
contact_idYesHubSpot contact record id, as returned by search, list, or create.

Output Schema

ParametersJSON Schema
NameRequiredDescription
idYesHubSpot record id, stable for the life of the record.
archivedNoTrue when the record is archived (soft-deleted).
propertiesNoProperty values HubSpot returned for the requested property set.

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Adds significant detail beyond annotations: states it's a write operation, only passed properties are sent, previous values are lost, omitted properties are untouched, retries are safe, requires the write scope, invalidates cache, and raises specific errors. Consistent with annotations; no contradiction.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact yet comprehensive, covering purpose, usage, behavior, scope, cache, and errors in a logical order without redundancy. Every sentence adds value.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a mutation tool with 6 parameters and an output schema, the description is complete. It addresses when to use, how it behaves, required permissions, error cases, and side effects. The output schema covers return details, so no need to elaborate on that.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so descriptions already exist per parameter. The tool description adds general semantics about partial updates and overwrite behavior, which enriches understanding of how all parameters behave.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states 'Update properties on an existing HubSpot contact and return the updated record,' with a specific verb and resource. It also distinguishes from the sibling create tool, saying 'Use crm_create_contact for records that do not exist yet.'

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says 'Use this to change a known record' and directs to crm_create_contact for new records. Error cases (unknown/archived id, no property supplied) provide additional context on when the tool is appropriate.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

crm_update_dealA
DestructiveIdempotent

Update properties on an existing HubSpot deal, including moving it to a new stage.

Use this to advance a deal through its pipeline. The deal's pipeline itself is not changeable here; create the deal in the right pipeline instead.

Writes. Only the properties passed are sent, and each one overwrites the value HubSpot held before; properties left out are untouched. Applying the same values again produces the same record, which makes a retry safe. Needs the crm.objects.deals.write scope. Invalidates this deal's cache entry. Raises a not-found error for an unknown or archived id, and a validation error when no property is supplied.

ParametersJSON Schema
NameRequiredDescriptionDefault
amountNoDeal value as a decimal string in the account currency, e.g. '2500.00'.
deal_idYesHubSpot deal record id, as returned by search or create.
dealnameNoDeal title shown on the pipeline board.
dealstageNoStage id taken from the chosen pipeline's 'stages'. HubSpot rejects a stage id that belongs to a different pipeline.
close_dateNoExpected close date as an ISO 8601 date or timestamp, e.g. '2026-09-30'. Maps to HubSpot's 'closedate' property.

Output Schema

ParametersJSON Schema
NameRequiredDescription
idYesHubSpot record id, stable for the life of the record.
archivedNoTrue when the record is archived (soft-deleted).
propertiesNoProperty values HubSpot returned for the requested property set.

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond annotations (readOnlyHint=false, destructiveHint=true, idempotentHint=true), the description adds rich behavior: 'Writes' status, partial overwrite semantics, retry safety, required OAuth scope 'crm.objects.deals.write', cache invalidation, and specific error conditions (not-found for unknown/archived id, validation for no property). This fully complements and expands on structured hints without contradicting them.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is appropriately dense, starting with a clear one-sentence purpose, followed by usage guidance, then a concise paragraph on behavior. Every sentence adds distinct value—no repetition of schema or annotation data, no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present, return values need not be described. The description covers auth requirements, cache effects, error scenarios, idempotency, and partial-update behavior, making it fully sufficient for an agent to invoke the tool correctly. The tool's complexity is moderate, and all relevant context is provided.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema covers 100% of parameters, so the baseline is 3. The description adds meaningful context beyond schemas: dealstage must come from the chosen pipeline and HubSpot rejects mismatched stages, and close_date maps to HubSpot's 'closedate' property. This extra information elevates the score.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states 'Update properties on an existing HubSpot deal' with a specific verb and resource, and explicitly distinguishes from other operations by noting the pipeline cannot be changed here and to 'create the deal in the right pipeline instead'. This separates it from create, delete, get, and search siblings.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit guidance: 'Use this to advance a deal through its pipeline' and warns that changing the pipeline itself requires creating a new deal. Also explains partial-update semantics (only passed properties are sent, omitted ones untouched), which helps the agent decide when to use this tool. Does not explicitly list all alternatives but covers the main use case.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

  1. 15 tool updatesv0.1.0
    • First observedcrm_batch_create_contacts
    • First observedcrm_create_contact
    • First observedcrm_create_deal
    • First observedcrm_delete_contact
    • First observedcrm_delete_deal
    • First observedcrm_export_audit_log
    • First observedcrm_get_contact
    • First observedcrm_get_deal
    • First observedcrm_get_pipeline
    • First observedcrm_list_contacts
    • First observedcrm_list_pipelines
    • First observedcrm_search_contacts
    • First observedcrm_search_deals
    • First observedcrm_update_contact
    • First observedcrm_update_deal

TDQS

A4.8/5.0
Disambiguation5/5

Each tool targets a distinct resource and action: contacts, deals, pipelines, or audit log. Overlapping search/get/list tools are clearly separated with explicit guidance on when to prefer each.

Naming Consistency5/5

All tools follow a consistent crm_verb_noun pattern with snake_case throughout. The compound verb 'batch_create' fits the convention as cleanly as simple verbs like 'search' or 'list'.

Tool Count5/5

15 tools is right at the upper bound of the ideal 3-15 range. The count matches the server's stated CRM scope: two primary objects (contacts, deals) plus supporting pipelines and audit coverage.

Completeness4/5

Contacts have full search/list/get/create/update/delete/batch-create coverage. Deals lack a list-all-deals counterpart to crm_list_contacts, though search can approximate it. Pipeline and audit support are adequate. Minor gap only.

Maintenance

ActivitySlowing
ResponsivenessSyncing

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • A
    license
    B
    quality
    F
    maintenance
    Enables AI models to interact with HubSpot CRM data and operations through a standardized interface, supporting contact and company management.
    16
    128
    MIT
  • A
    license
    A
    quality
    C
    maintenance
    A read-only MCP server that exposes HubSpot CRM data (contacts, deals, companies, quotes) to AI agents, enabling natural language queries.
    9
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    A Model Context Protocol (MCP) server that provides AI assistants with full access to HubSpot CRM. Manage contacts, companies, deals, pipelines, and associations directly from Claude, Cursor, or any MCP-compatible client.
    15
    MIT

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/amin-ale/hubspot-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server