Skip to main content
Glama
NitinSharma077-echo

Zoho CRM MCP Server

Zoho CRM MCP Server (FastAPI + FastMCP)

A production-grade Model Context Protocol (MCP) server built with FastAPI + FastMCP that gives Claude and other AI clients complete, authenticated access to the Zoho CRM REST API v8 โ€” from reading records to designing modules and authoring workflow automation.

211 MCP tools covering records, COQL, schema design, workflow rules and their actions, webhooks, bulk/mass operations, tags, notes, email, security settings, and bulk import/export โ€” plus a dashboard and report engine, function authoring and attachment to workflow rules, org hierarchy tools, and teamspaces โ€” and a generic zoho_api_request escape hatch for anything Zoho exposes that has no dedicated tool.


๐ŸŒŸ Key Features

  • FastAPI Web Framework: High-performance, production-ready ASGI app powered by Uvicorn.

  • Dual Transport: Runs as a Streamable HTTP MCP server (for remote/cloud hosting) and as a STDIO MCP server (for local Claude Desktop).

  • Full OAuth 2.0 Lifecycle: Automatic code exchange, browser redirect handler (/auth/callback), encrypted token storage, and a background loop that keeps the token fresh while the server runs.

  • Chat-Configurable Credentials: Supply a Zoho Client ID/Secret from chat (set_zoho_credentials, or inline on get_auth_url/exchange_auth_code) instead of .env โ€” useful for switching Zoho accounts without restarting.

  • Dashboard Report Home Page: / serves a live dashboard โ€” record counts per module, workflow and function inventory, org chart, teamspaces, pinned reports, and the activity feed โ€” all backed by GET /api/dashboard/summary. Panels load concurrently and fail independently, so one missing OAuth scope never blanks the page.

  • Report Engine: Save report definitions (list, count, summary with count/sum/avg/min/max and group-by) that compile to COQL and run against live CRM data. Pin one to the dashboard, re-run it on demand, or export it to CSV. Zoho has no Reports API, so this is the working substitute.

  • Function Authoring (v8 Functions API): Create, read, update, publish, and delete Deluge/Java/NodeJS/Python functions, and download their source.

  • Functions Attached to Workflows: functions is a first-class workflow action type, so a function can be wired into a rule as an instant or scheduled action โ€” and detached again โ€” without touching the Zoho UI.

  • Org Hierarchy: Role and user-reporting trees assembled from Zoho's flat lists, with an ASCII org chart, subordinate/manager lookups, and the writes that reparent a role or reassign a manager.

  • Teamspaces: Group modules and people into named teamspaces, optionally projected onto a real CRM user group so the same roster drives sharing and assignment in Zoho.

  • Complete Automation Authoring: Build workflow rules end to end โ€” create field-update, email-notification, task, and webhook actions, then wire them into a rule with triggers and criteria.

  • Schema Design: Create custom modules (with the profiles Zoho mandates), fields, global picklists, layouts, and sales pipelines.

  • Scoped Session Mode: ID-based safety filter (activate_scope) restricting operations to specific record IDs.

  • Human-In-The-Loop Approvals: Destructive actions queue a pending request instead of executing. Toggle with ZOHO_REQUIRE_APPROVAL.

  • Structured Activity Logging: Every auth event, API call, and approval decision logged as JSON, retrievable via get_logs() / GET /logs.

  • Encrypted Token Storage: OAuth tokens encrypted at rest (Fernet/AES), never plaintext.

  • Resilient Network Client: Pooled httpx client with automatic 401 refresh-and-retry, clamped 429 backoff, exponential 5xx retry, an outbound rate limiter, and partial-failure detection on Zoho's per-record responses.

  • Multi-Account Isolation: Each Zoho client_id is an isolated tenant - its own tokens, scope, approval queue, reports, and teamspaces - resolved per MCP session or per REST header. Concurrent callers on different accounts cannot read or write each other's org.

  • Automated Test Suite: 93 pytest tests covering the HTTP surface, tool registration, request-payload shapes, client guards, the COQL report compiler, hierarchy tree assembly, teamspace rostering, and function-to-workflow attachment.


Related MCP server: Zoho CRM MCP Server

๐Ÿ“ Repository Structure

zoho-crm-mcp/
โ”œโ”€โ”€ server.py              # FastAPI app + all FastMCP tool definitions & REST endpoints
โ”œโ”€โ”€ auth_manager.py        # OAuth 2.0 flow, scopes & token refresh
โ”œโ”€โ”€ zoho_client.py         # Async HTTP client for Zoho CRM API v8 (193 methods)
โ”œโ”€โ”€ models.py              # Pydantic state & validation models
โ”œโ”€โ”€ token_store.py         # Encrypted (Fernet) token persistence
โ”œโ”€โ”€ approval_manager.py    # HITL approval queue for high-risk actions
โ”œโ”€โ”€ activity_log.py        # Structured JSON activity logger
โ”œโ”€โ”€ dashboard.py           # Dashboard aggregation (concurrent, per-panel errors)
โ”œโ”€โ”€ report_store.py        # Report definitions + COQL compiler, runner & CSV export
โ”œโ”€โ”€ hierarchy.py           # Role/user tree assembly and hierarchy mutations
โ”œโ”€โ”€ teamspace_store.py     # Teamspace registry + CRM user-group projection
โ”œโ”€โ”€ web_ui.py              # The dashboard home page (no CDN, no build step)
โ”œโ”€โ”€ tenancy.py             # Per-account isolation: tenant registry, ContextVar, proxies
โ”œโ”€โ”€ test_server.py         # pytest suite
โ”œโ”€โ”€ requirements.txt       # Dependencies
โ”œโ”€โ”€ .env.example           # Environment configuration template
โ”œโ”€โ”€ pyproject.toml         # Package metadata
โ””โ”€โ”€ README.md

โš™๏ธ Setup & Installation

1. Prerequisites

  • Python 3.10+

  • A Zoho CRM API Console app (Zoho API Console)

    • Client Type: Server-based Applications

    • Redirect URI: http://localhost:8000/auth/callback (or your deployment callback URL)

2. Environment Setup

cp .env.example .env

Minimum configuration:

ZOHO_CLIENT_ID=1000.xxxxxxx
ZOHO_CLIENT_SECRET=xxxxxxx
ZOHO_REDIRECT_URI=http://localhost:8000/auth/callback
ZOHO_DATA_CENTER=com
PORT=8000

See .env.example for every supported variable, including the approval gate, OAuth scope override, rate limit, and timeout settings.

Working with more than one Zoho account? ZOHO_CLIENT_ID/ZOHO_CLIENT_SECRET are optional. Leave them blank and ask Claude to call set_zoho_credentials(client_id, client_secret, redirect_uri?, data_center?), or pass client_id/client_secret directly to get_auth_url / exchange_auth_code. Switching client_id clears tokens saved for the previous account, avoiding Zoho's invalid_client error from reusing a refresh token issued to a different app.

3. Install Dependencies

pip install -r requirements.txt

๐Ÿš€ Running & Deploying

Option A: Local FastAPI Web Server

python server.py

Or with Uvicorn directly:

uvicorn server:app --host 0.0.0.0 --port 8000

Once running:

The MCP endpoint is served at the mount root, so the path is /mcp/ โ€” not /mcp/mcp. Keep the trailing slash: /mcp answers with a 307 redirect, which not every MCP client follows correctly on a POST.

Option B: Local STDIO

python server.py --stdio

Option C: Cloud Deployment (Render, Railway, Docker, AWS, Heroku)

  • Start Command: uvicorn server:app --host 0.0.0.0 --port $PORT

  • Health Check Path: /health

  • Environment Variables: set ZOHO_CLIENT_ID, ZOHO_CLIENT_SECRET, ZOHO_REDIRECT_URI, ZOHO_DATA_CENTER, and ZOHO_TOKEN_ENCRYPTION_KEY (so tokens survive restarts on ephemeral filesystems).

Live deployment

Dashboard

https://zoho-crm-mcp-07t7.onrender.com/

Health

https://zoho-crm-mcp-07t7.onrender.com/health

MCP endpoint

https://zoho-crm-mcp-07t7.onrender.com/mcp/

Staying authenticated on an ephemeral host. Render wipes the filesystem on every deploy and restart, taking ~/.zoho_crm_tokens.json with it โ€” and the Fernet key at ~/.zoho_crm_mcp.key is regenerated on each boot, so even a surviving token file could not be decrypted. Set both of these in the host's environment and the server re-authenticates itself instead of asking for tokens again:

  • ZOHO_REFRESH_TOKEN โ€” picked up on cold boot, so a fresh access token is minted automatically. Refresh tokens do not expire unless revoked.

  • ZOHO_TOKEN_ENCRYPTION_KEY โ€” a stable Fernet key, so the cached access token survives too. Generate one with:

    python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"

Token refresh itself is automatic and needs no configuration: get_valid_access_token() renews anything expiring within 5 minutes, and a background loop re-checks every ZOHO_TOKEN_REFRESH_INTERVAL seconds (default 120).


๐Ÿ–ฅ๏ธ Claude Desktop Integration

Mode 1: HTTP / Remote MCP Connection

Local:

{
  "mcpServers": {
    "zoho-crm": {
      "url": "http://localhost:8000/mcp/"
    }
  }
}

Deployed:

{
  "mcpServers": {
    "zoho-crm": {
      "url": "https://zoho-crm-mcp-07t7.onrender.com/mcp/"
    }
  }
}

The server has no MCP-level authentication, so leave any "OAuth Client ID" field in the connector settings blank. The Zoho OAuth inside the server authenticates it to Zoho; it does not gate the MCP endpoint. Pointing a client at the wrong path (/mcp/mcp) returns 404, which some clients report as a misleading sign-in or registration failure rather than as a bad URL.

Mode 2: Local STDIO Connection

{
  "mcpServers": {
    "zoho-crm": {
      "command": "python",
      "args": ["C:/Users/Lenovo/Desktop/zoho MCP/server.py", "--stdio"],
      "env": {
        "ZOHO_CLIENT_ID": "1000.YOUR_CLIENT_ID",
        "ZOHO_CLIENT_SECRET": "YOUR_CLIENT_SECRET",
        "ZOHO_REDIRECT_URI": "http://localhost:8000/auth/callback",
        "ZOHO_DATA_CENTER": "com"
      }
    }
  }
}

๐Ÿ”‘ First-Run OAuth Flow

  1. Start the server: python server.py

  2. Open http://localhost:8000/auth/url, or ask Claude to run get_auth_url().

  3. Open the returned URL, sign in to Zoho CRM, click Accept.

  4. Zoho redirects to /auth/callback?code=...; the server exchanges the code and saves encrypted tokens to ~/.zoho_crm_tokens.json.


๐Ÿงฉ Building Automation: The Workflow Recipe

Zoho models a workflow rule as a trigger plus conditions, where each condition points at pre-created action objects. Build them in that order:

1. get_workflow_configurations(module="Leads")
   -> see which triggers, comparators, and action types this org supports

2. create_field_update_action(
       name="Mark as Hot", module="Leads",
       field_api_name="Rating", value="Hot")
   -> returns the action id

3. create_workflow(
       name="Hot Lead Router",
       module="Leads",
       execute_when={"type": "create_or_edit"},
       conditions=[{
           "sequence_number": 1,
           "criteria_details": {"criteria": {"group_operator": "and", "group": [
               {"comparator": "equal",
                "field": {"api_name": "Lead_Source"},
                "value": "Web Form"}]}},
           "instant_actions": {"actions": [
               {"id": "<action id from step 2>", "type": "field_updates"}]}}])

4. activate_workflow(workflow_id="...")

The same pattern applies with create_email_notification_action, create_automation_task, and create_webhook as the action source.



๐Ÿ“Š Dashboard & Report Home Page

GET / is a live dashboard, not a status card. It shows record counts per module, the workflow and function inventory (broken down by runtime and category), the org chart, teamspaces, every pinned report, and the recent activity feed โ€” with an in-page report runner for ad-hoc queries.

Panels are fetched concurrently and each carries its own error, so a missing OAuth scope costs you one panel rather than the whole page. The same payload is available as JSON:

curl localhost:8000/api/dashboard/summary
curl "localhost:8000/api/dashboard/summary?modules=Deals,Leads&include_hierarchy=false"

From an MCP client:

get_dashboard_summary()                       # everything
get_module_record_counts(["Deals", "Leads"])   # just the counts

Reports

A report is a definition stored on this server that compiles to COQL and runs against live CRM data. Three shapes:

# 1. list โ€” rows of fields
create_report(name="Big open deals", module="Deals", type="list",
              fields=["Deal_Name", "Stage", "Amount"],
              criteria="(Amount > 50000) and (Stage != 'Closed Won')",
              sort_by="Amount", sort_order="desc")

# 2. count โ€” how many match
create_report(name="Web leads", module="Leads", type="count",
              criteria="(Lead_Source = 'Web Form')")

# 3. summary โ€” aggregate, grouped; pinned to the dashboard home page
create_report(name="Pipeline by stage", module="Deals", type="summary",
              group_by=["Stage"],
              aggregates=[{"function": "sum", "field": "Amount", "alias": "pipeline"}],
              pinned=True)

run_report("Pipeline by stage")                    # by name or id
run_adhoc_report(module="Deals", type="count")     # iterate without saving
export_report("Pipeline by stage", save_to="./pipeline.csv")

Aggregates take count, sum, avg, min, and max; alias is the column name you get back. criteria is a COQL where clause, and omitting it matches every record. Rows cap at 2000 per report โ€” use bulk_read_create_job for a full export.

create_dashboard_widget(...) is the same thing pinned in one step.


โšก Functions, and Attaching Them to Workflows

v8 exposes a real Functions API, so functions are authored over the wire. Deluge source goes inline; Java, NodeJS, and Python ship as a ZIP and then need publishing.

get_function_runtimes()     # what this org allows, and what's deprecated
get_function_categories()   # 'Automation' is the workflow-callable one

create_function(
    name="Flag big deal",
    api_name="flag_big_deal",
    category="Automation",
    runtime="Deluge 1.0",
    code='''void automation.flag_big_deal(Map deal)
{
    // Deluge body
}''')

get_function_code("flag_big_deal")               # read it back
update_function("flag_big_deal", code="...")     # Deluge auto-publishes
publish_function("nightly_sync")                 # Java/NodeJS/Python drafts

api_name, runtime, and category are immutable in Zoho โ€” to change any of them, create a new function.

functions is a first-class workflow action type, so a function can be wired straight into a rule:

# instant: runs the moment the rule fires
attach_function_to_workflow(workflow_id="...", function="flag_big_deal", module="Deals")

# scheduled: runs two days later
attach_function_to_workflow(workflow_id="...", function="flag_big_deal", module="Deals",
                            scheduled_after={"period": "days", "unit": 2})

list_workflow_actions(workflow_id="...", module="Deals")
detach_function_from_workflow(workflow_id="...", function="flag_big_deal", module="Deals")

A rule's actions hang off its conditions, so attaching one is read-modify-write on the target condition โ€” existing actions on that condition are preserved. condition_sequence picks which condition (default 1). attach_action_to_workflow does the same for the other action types: pass action_id for an existing field update, email notification, task, or webhook, or details for an inline action like add_tags or create_record. Check get_workflow_configurations(module=...) first โ€” a trigger only accepts certain actions.


๐Ÿข Hierarchy & Teamspaces

Hierarchy

Zoho returns roles and users as flat lists with reporting_to pointers and no tree endpoint, so these tools assemble one:

get_hierarchy_preference()      # Role_Hierarchy or Reporting_Hierarchy (read-only in Zoho)
get_role_hierarchy()            # nested tree + an ASCII org chart + depth
get_user_hierarchy()            # same, from each user's reporting_to
get_subordinates("4876...001", of="user", deep=True)   # + the managers above them

Both trees come with an ascii org chart, which is what an AI client can actually read back:

CEO
โ”œโ”€โ”€ VP Sales
โ”‚   โ”œโ”€โ”€ Manager EU
โ”‚   โ”‚   โ”œโ”€โ”€ AE Berlin
โ”‚   โ”‚   โ””โ”€โ”€ AE Paris
โ”‚   โ””โ”€โ”€ Manager US
โ””โ”€โ”€ VP Marketing

A role whose parent is outside your permission scope still appears, listed under orphans rather than silently dropped. The mutations are real Zoho writes:

create_role_under(name="Manager EU", parent_role_id="...")
set_role_parent(role_id="...", parent_role_id="...")   # moves the whole subtree
set_user_manager(user_id="...", manager_user_id="...")  # omit to clear
assign_user_role(user_id="...", role_id="...")

Teamspaces

Teamspaces group modules and people. Zoho's REST API has no teamspaces endpoint, so the roster lives on this server โ€” and can be projected onto a real CRM user group, which is Zoho's own way to name a set of people for sharing and assignment:

create_teamspace(
    name="EMEA Sales",
    modules=["Deals", "Contacts"],
    members=["4876...001", {"user_id": "4876...002", "role": "viewer"}],
    lead_user_id="4876...001",
    create_crm_user_group=True)     # also creates the Zoho user group

add_teamspace_members("EMEA Sales", [{"user_id": "...", "role": "member"}])
set_teamspace_modules("EMEA Sales", add=["Tasks"], remove=["Contacts"])
sync_teamspace_to_crm("EMEA Sales")   # push the roster onto the linked group
get_user_teamspaces("4876...001")

Member roles are lead, member, or viewer. If the CRM user group can't be created (a duplicate name, say), the teamspace is still kept and the response says the CRM-side binding failed โ€” the registry is the source of truth, the group is its projection.

The underlying Zoho user groups are also directly addressable: get_user_groups, create_user_group, update_user_group, delete_user_group, get_user_group_associations.


๐ŸŒ REST Surface

Beyond the MCP endpoint, the dashboard's data is plain JSON:

Endpoint

Purpose

GET /api/dashboard/summary

The whole dashboard (?modules=, ?include_reports=, ?include_hierarchy=)

GET /api/dashboard/modules

Record counts per module

GET /api/dashboard/automation

Workflow rules, limits, and the function inventory

GET /api/reports ยท POST /api/reports

List and save report definitions

POST /api/reports/run

Run an ad-hoc definition

GET /api/reports/{ref} ยท POST /api/reports/{ref}/run ยท DELETE /api/reports/{ref}

One saved report

GET /api/hierarchy/preference ยท /roles ยท /users

Hierarchy model and trees

GET /api/hierarchy/subordinates/{id}

Subordinates (?of=user|role, ?deep=)

GET /api/teamspaces ยท POST /api/teamspaces

List and create teamspaces

GET /api/teamspaces/{ref} ยท DELETE /api/teamspaces/{ref}

One teamspace (?resolve_members=true)

GET /api/functions

The org's functions

GET /api/workflows/{id}/actions

Every action attached to a rule

GET /tenants

Resident accounts and their state, credentials masked

Every endpoint accepts an X-Zoho-Client-Id header to select which account it acts for.


๐Ÿ‘ฅ Multi-Account Isolation (Tenancy)

This server is safe to share between many Zoho accounts and many AI clients at once. Each Zoho client_id is a tenant with its own tokens, scope, approval queue, reports, and teamspaces. Concurrent callers using different credentials cannot read or write each other's org.

How a caller picks its account

MCP clients โ€” call set_zoho_credentials once; that session is bound to the account for its lifetime, and every later tool call routes there automatically:

set_zoho_credentials(client_id="1000.AAAA", client_secret="...")
get_tenant_info()      # confirm which account you are pointed at
create_record(...)     # goes to 1000.AAAA, whatever other sessions are doing

REST callers โ€” send the account's client_id as a header:

curl localhost:8000/api/reports -H 'X-Zoho-Client-Id: 1000.AAAA'
curl localhost:8000/tenants     # resident accounts, credentials masked

Resolution order, first match wins: an explicit binding on the current task โ†’ the calling MCP session's binding โ†’ the X-Zoho-Client-Id header โ†’ the .env account. A deployment that never calls set_zoho_credentials behaves exactly as a single-account server, on the same file paths as before.

What is isolated, and why each one matters

Per tenant

Why

AuthManager + token file

Otherwise a second caller's credentials overwrite the first's mid-request, and calls execute against the wrong org

ZohoClient

Its _module_id_cache holds Zoho's numeric module ids, which are per-org โ€” sharing it corrupts workflow, webhook, and field-update payloads

Rate limiter

One account's burst would otherwise throttle all the others

ScopeState

A scope activated by one account would filter and block another's records

Approval queue

Approvals are per-account, and a queued action executes pinned to the tenant that raised it

Reports & teamspaces

Definitions reference one org's modules and users

Activity log reads

One shared audit trail, tagged per tenant; reads are filtered to the caller (all_tenants=True for diagnostics)

How the isolation works

The current tenant lives in a contextvars.ContextVar. ContextVars are copied per asyncio task, so two interleaved requests each see their own value โ€” isolation by construction rather than by locking. The module-level names (zoho_client, auth_manager, โ€ฆ) are thin proxies that resolve the calling task's tenant on every attribute access, which is why all 211 tool bodies are unchanged.

OAuth needed one extra step: a browser callback carries no session, so get_auth_url puts the tenant key in the OAuth state parameter and /auth/callback reads it back to store the tokens against the right account.

Operating notes

  • Tenant state lives under ZOHO_STATE_DIR (default ~/.zoho_crm_mcp/tenants/<hash>/), one directory per account, named by a hash of the client_id rather than the client_id itself. The .env account keeps the original home-directory paths.

  • Residency is capped by ZOHO_MAX_TENANTS (default 250); idle tenants past the cap have their connection pools closed by the background loop. Tenants still bound to a live session are never evicted.

  • Token refresh runs per tenant inside that tenant's context, so a refresh can never write one account's token into another's store.

  • get_tenant_info() reports which account a session resolved to and how. Worth calling before anything destructive.

๐ŸŽฏ Scoped Session Mode (Safety Filter)

Restrict every operation to specific record IDs:

  • Activate: activate_scope(module="Deals", record_ids=["4153...001", "4153...002"])

  • REST: POST /scope/activate with {"module": "Leads", "record_ids": ["123", "456"]}

  • Deactivate: deactivate_scope() or POST /scope/deactivate

While active, reads on that module are filtered to those IDs, and writes to any other ID are refused with OUT_OF_SCOPE.


โœ… Human-In-The-Loop (HITL) Approvals

By default, destructive actions queue a pending request and return a request_id instead of running:

delete_record, bulk_update_records, bulk_delete_records, mass_update_records, mass_delete_records, change_owner, mass_change_owner, merge_records, delete_workflow, delete_workflows, execute_blueprint, update_layout, activate_layout, delete_layout, delete_field, delete_user, delete_tag, bulk_write_create_job.

  • Review: list_pending_approvals() or GET /approvals

  • Approve & execute: approve_action(request_id="...") or POST /approvals/{id}/approve

  • Reject & discard: reject_action(request_id="...") or POST /approvals/{id}/reject

  • Disable the gate entirely: set ZOHO_REQUIRE_APPROVAL=false so these tools execute immediately.

Every request, approval, and rejection is written to the activity log.


๐Ÿ“œ Activity Logging

Auth events, outbound Zoho API calls, function executions, and approval decisions are recorded as {timestamp, action, status, details} entries โ€” held in memory and appended to ~/.zoho_crm_mcp_activity.log.jsonl.

  • Retrieve: get_logs(limit=50, action=None, status=None) or GET /logs


๐Ÿ” Token Security

  • Tokens are encrypted at rest (Fernet/AES) in ~/.zoho_crm_tokens.json.

  • The key is auto-generated into ~/.zoho_crm_mcp.key on first run (user-only permissions on POSIX), or set explicitly via ZOHO_TOKEN_ENCRYPTION_KEY for a stable key across container restarts.

  • Tokens are tagged with the client_id that issued them and discarded on mismatch, which prevents Zoho's invalid_client error after switching accounts.

  • Outbound calls are self-throttled (ZOHO_RATE_LIMIT_PER_SEC, default 10/sec) on top of 429/5xx backoff.


๐Ÿงช Testing

pytest -v

Covers the HTTP surface (/health, /, /auth/*, /scope/*, /approvals/*, /logs, /api/*), registration of all 211 MCP tools plus a regression guard that the original 167 are still there, the exact request payloads sent for workflows/modules/notes/calls/webhooks/merges/locks and the Functions API's multipart body, the COQL compiler (including its limit clamp and the mandatory WHERE clause), aggregate-alias relabelling, hierarchy tree assembly (orphans and parent cycles included), per-panel dashboard error isolation, teamspace membership rules, function-to-workflow attach/detach across instant and scheduled actions, and multi-account isolation - including a concurrency test that drives overlapping interleaved calls from many accounts and asserts every request carried its own credentials (the old shared-singleton design failed that on 96 of 100 calls).

Tests run entirely offline โ€” no Zoho credentials required.


๐Ÿ› ๏ธ MCP Tool Reference

Category

Tools

OAuth & Auth

get_auth_url, exchange_auth_code, set_zoho_credentials, get_auth_status, get_access_token, refresh_access_token, validate_token, get_token_expiry

Scoped Mode

activate_scope, deactivate_scope, get_scope_status

Tenancy

get_tenant_info โ€” which Zoho account this session is bound to, and how it resolved

HITL & Logging

list_pending_approvals, approve_action, reject_action, get_logs

Escape Hatch

zoho_api_request โ€” call any Zoho v8 endpoint with full auth/retry handling

Record CRUD

create_record, get_record, update_record, delete_recordโ€ , list_records, search_records, upsert_record, clone_record, get_record_count, get_deleted_records, get_record_timeline

Bulk (โ‰ค100/call)

bulk_create_records, bulk_update_recordsโ€ , bulk_upsert_records, bulk_delete_recordsโ€ 

Mass (async jobs)

mass_update_recordsโ€ , get_mass_update_status, mass_delete_recordsโ€ , get_mass_delete_status, change_ownerโ€ , mass_change_ownerโ€ , merge_recordsโ€ 

Locking & Sharing

lock_record, unlock_record, get_record_locking_info, share_record, get_shared_record_details, revoke_shared_record

Related Records

get_related_records, get_related_records_count, link_related_records, delink_related_record

Query

execute_coql, composite_request

Metadata & Discovery

get_modules, get_module_details, get_fields, get_field_details, get_picklist_values, get_layouts, get_layout_structure, get_related_lists, get_custom_views, get_custom_view_details, get_features, get_organizations, get_business_hours, get_currencies, get_email_templates, get_recycle_bin

Schema Design

create_module, update_module, create_field, create_fields, update_field, delete_fieldโ€ , get_global_picklists, create_global_picklist, update_layoutโ€ , activate_layoutโ€ , deactivate_layout, delete_layoutโ€ , get_pipelines, create_pipeline, update_pipeline

Workflow Rules

get_workflows, get_workflow, get_workflow_configurations, create_workflow, update_workflow, activate_workflow, deactivate_workflow, delete_workflowโ€ , delete_workflowsโ€ 

Workflow Actions

get_field_update_actions, create_field_update_action, update_field_update_action, delete_field_update_action, get_email_notification_actions, create_email_notification_action, delete_email_notification_action, get_automation_tasks, create_automation_task, update_automation_task, get_assignment_rules

Webhooks

create_webhook, get_webhooks, update_webhook, delete_webhook

Files

upload_attachment, get_attachments, download_attachment, delete_attachment, upload_photo, delete_photo

Notes, Calls & Email

create_note, get_notes, update_note, delete_note, create_call, send_mail, get_from_addresses, get_emails

Tags

get_tags, create_tags, update_tag, delete_tagโ€ , merge_tags, get_tag_record_count, add_tags, remove_tags, add_tags_to_multiple_records

Lead Conversion

get_lead_conversion_options, convert_lead, mass_convert_leads, get_mass_convert_status

Blueprint

get_blueprints, execute_blueprintโ€ , create_blueprint, update_blueprint

Bulk Read/Write

bulk_read_create_job, bulk_read_job_status, bulk_read_download_result, bulk_write_upload_file, bulk_write_create_jobโ€ , bulk_write_job_status

Security & Users

get_users, create_user, update_user, delete_userโ€ , get_profiles, create_profile, get_roles, create_role, update_role, get_territories, get_variables, create_variables

Notifications

get_notification_details, enable_notifications, disable_notifications

Functions (v8 API)

get_functions, get_function, get_function_code, get_function_runtimes, get_function_categories, create_function, update_function, publish_function, delete_functionโ€ , execute_function

Functions โ†’ Workflows

attach_function_to_workflow, detach_function_from_workflow, attach_action_to_workflow, detach_action_from_workflow, list_workflow_actions

Workflow Insight

get_workflow_usage_report, get_workflow_module_counts, get_workflow_limits

Reports โ€ก

create_report, get_reports, get_report, update_report, delete_report, run_report, run_adhoc_report, export_report

Dashboard โ€ก

get_dashboard_summary, get_dashboard, get_module_record_counts, create_dashboard_widget

Hierarchy โ€ก

get_hierarchy_preference, get_role_hierarchy, get_user_hierarchy, get_subordinates, set_user_manager, set_role_parent, create_role_under, assign_user_role

Teamspaces โ€ก

create_teamspace, get_teamspaces, get_teamspace, update_teamspace, delete_teamspace, add_teamspace_members, remove_teamspace_members, set_teamspace_modules, sync_teamspace_to_crm, get_user_teamspaces

User Groups

get_user_groups, create_user_group, update_user_group, delete_user_groupโ€ , get_user_group_associations

โ€  Approval-gated by default. Set ZOHO_REQUIRE_APPROVAL=false to execute immediately.

* Zoho CRM's public REST API has no endpoint for this operation โ€” blueprint authoring is UI-only. These tools return a clear NOT_SUPPORTED_BY_ZOHO_API message naming a working alternative, rather than failing against a URL that doesn't exist. (Driving records through an existing blueprint does work: get_blueprints / execute_blueprint.)

โ€ก Zoho exposes no Reports, Dashboards, Teamspaces, or hierarchy-tree endpoint, so these features are implemented on this server against real CRM data:

  • Reports are definitions stored here that compile to COQL (or to /actions/count) and execute live. get_reports(module=...) also returns that module's Custom Views, which are the closest thing Zoho itself exposes.

  • The dashboard is computed from live figures โ€” module counts, workflow and function inventory, hierarchy, teamspaces, pinned reports.

  • Hierarchy tools assemble a tree from /settings/roles and /users (Zoho returns flat lists with reporting_to pointers); the mutations are real Zoho writes. hierarchy_preferences is read-only in Zoho's API.

  • Teamspaces are stored here and can be projected onto a real CRM user group (/settings/user_groups) so the roster is usable inside Zoho.

Definitions persist as JSON beside the token store (~/.zoho_crm_mcp_reports.json, ~/.zoho_crm_mcp_teamspaces.json). On an ephemeral host, mount a disk or accept that they reset on redeploy โ€” the CRM data they read is never affected.


๐Ÿงญ Reaching Anything Not Listed

Zoho's API is larger than any hand-written wrapper. zoho_api_request covers the rest with the same auth, throttling, and retry handling:

zoho_api_request(
    method="GET",
    endpoint="settings/territories")

zoho_api_request(
    method="POST",
    endpoint="settings/automation/scoring_rules",
    body={"scoring_rules": [{...}]})

zoho_api_request(
    method="GET",
    endpoint="read/1234567890",
    api_root="bulk")

api_root selects the URL base: crm โ†’ {domain}/crm/v8 (default), bulk โ†’ {domain}/crm/bulk/v8, root โ†’ {domain}.

Related MCP Connectors

  • Search recruiting CRM records, manage candidates, jobs, pipelines, notes and tasks with OAuth.

  • The HubSpot MCP Server acts as a bridge that enables AI assistants and Large Language Models to securely interact with HubSpot CRM data through natural conversation, without requiring users to understand complex API structures. It provides read-only access to standard CRM objects (contacts, companies, deals, tickets, products, invoices, and more) and their associations, secured via OAuth 2.0, allowing AI agents to perform tasks like summarizing deals, fetching company updates, and looking up record changes.

  • Connect Claude to your Platform7n workspaces โ€” chat, links, and tasks. One-click OAuth.

  • xmagnet โ€” AI-powered B2B CRM for Claude. 35 tools that turn natural-language prompts into real CRM actions: prospect, enrich, score leads, manage deals, scan buying intent, run email campaigns and sequences, build forms and landing pages, refine ICP, and analyze performance โ€” all directly inside Claude. ๐Ÿš€ ONE-CLICK INSTALL: https://api.xmagnet.ai/claude The install page guides Claude users through 3 steps in under a minute: open Claude Connectors, paste the connector name, paste the server URL, sign in. A reviewer workspace is auto-provisioned on first sign-in with sample contacts, deals, campaigns, and ICP suggestions, so every tool works end-to-end with zero setup. No 2FA. No paid plan required. Free tier exposes all 35 tools. What you can do: โ€ข Prospecting โ€” search_contacts, search_companies, search_investors, find_contacts_at_companies, enrich_contact, validate_email, find_competitors, company_intelligence โ€ข Pipeline โ€” get_deals_pipeline, scan_deal_intent, get_ghost_pipeline, create_deal โ€ข Campaigns & sequences โ€” create_campaign, generate_campaign_content, get_campaign_stats, get_bounce_stats, get_unsub_stats, create_sequence_draft, list_sequences โ€ข Top of funnel โ€” suggest_icp, get_icp, create_form, list_forms, create_landing_page, list_landing_pages, show_suggestions โ€ข Operations โ€” analyze_contacts, get_contact_details, update_contact, save_contacts_to_crm, export_contacts, get_dashboard_stats, get_credit_balance Example prompts to try: โ€ข "Find C-suite contacts at fintech companies that raised Series A in the last 6 months." โ€ข "Scan my open deals for buying intent and prioritize follow-ups." โ€ข "Generate a re-engagement campaign for contacts who opened my last newsletter but didn't reply." โ€ข "Show me my deals pipeline by stage with weighted value and win rate." โ€ข "Generate a landing page for my Q2 webinar with a registration form." Built for founders, SDRs, RevOps, and growth teams who want their CRM to take action โ€” not just store records. Install: https://api.xmagnet.ai/claude ยท Site: https://xmagnet.ai ยท Privacy: https://xmagnet.ai/privacy-policy ยท Terms: https://xmagnet.ai/terms-of-service ยท Support: ashish.sinha@xmagnet.ai

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    Connects Claude to Zoho CRM with read-only access, enabling natural language queries to search records, list modules, retrieve field information, and count records using OAuth authentication.
    -
  • F
    license
    Not graded
    quality
    D
    maintenance
    Enables read-only interaction with Zoho CRM data through natural language queries, allowing users to search records, list modules, retrieve field information, and count records using secure OAuth authentication.
    2
    -
  • F
    license
    B
    quality
    D
    maintenance
    Exposes Zoho CRM v6 REST API as structured tools for LLM agents via MCP, enabling CRUD operations, search, COQL queries, and more.
    11
    3
    -
  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables AI assistants to interact with Zoho CRM data through secure OAuth authentication, supporting comprehensive CRM operations including record management, search, bulk operations, and lead conversion.
    3
    MIT