Install
openclaw skills install @sprintcx/zoho-crm-mcp-openclaw-skillConnect your OpenClaw agent to Zoho CRM via MCP. Search contacts, list accounts, query records with COQL, and manage CRM data using mcporter. Includes ready-to-use Python scripts with pagination and custom-field support for common CRM operations.
openclaw skills install @sprintcx/zoho-crm-mcp-openclaw-skillConnect your OpenClaw agent to Zoho CRM through the Model Context Protocol (MCP). This skill provides everything you need to search, read, and manage CRM data using mcporter.
GitHub source: sprintberlin/openclaw-zoho-crm-mcp-skill
| Requirement | Details |
|---|---|
| Zoho CRM MCP Server | A configured endpoint from mcp.zoho.eu |
| mcporter | MCP client CLI (pre-installed with OpenClaw) |
| Environment variable | ZOHO_MCP_URL must be set (see below) |
This skill requires the ZOHO_MCP_URL environment variable. Without it, the Python scripts will not work.
Add this to your shell profile (e.g. ~/.bashrc or ~/.zshrc):
export ZOHO_MCP_URL="https://your-org-zoho-crm-xxxxx.zohomcp.eu/mcp/YOUR_TOKEN/message"
Or set it per session:
ZOHO_MCP_URL="https://your-org-zoho-crm-xxxxx.zohomcp.eu/mcp/YOUR_TOKEN/message" python3 scripts/list_contacts.py
To verify it's set:
echo $ZOHO_MCP_URL
https://your-org-zoho-crm-xxxxx.zohomcp.eu/mcp/abc123def456/message
ZOHO_MCP_URL as shown above.If you manage multiple Zoho CRM orgs, each gets its own MCP endpoint. You can:
ZOHO_MCP_URL.env file per projectmcporter list $ZOHO_MCP_URL
cat << 'EOF' > /tmp/zoho_search.json
{
"path_variables": {"module": "Contacts"},
"query_params": {"criteria": "(Last_Name:equals:Smith)"}
}
EOF
mcporter call "$ZOHO_MCP_URL.ZohoCRM_searchRecords" --args "$(< /tmp/zoho_search.json)"
cat << 'EOF' > /tmp/zoho_record.json
{
"path_variables": {"module": "Accounts", "recordID": "1234567890"}
}
EOF
mcporter call "$ZOHO_MCP_URL.ZohoCRM_getRecord" --args "$(< /tmp/zoho_record.json)"
cat << 'EOF' > /tmp/zoho_coql.json
{
"body": {"select_query": "SELECT Id, Account_Name, Website FROM Accounts WHERE Website != '' ORDER BY Account_Name LIMIT 50"}
}
EOF
mcporter call "$ZOHO_MCP_URL.ZohoCRM_executeCOQLQuery" --args "$(< /tmp/zoho_coql.json)"
Ready-to-use scripts for common CRM operations. All scripts require ZOHO_MCP_URL to be set.
The bundled Python scripts call mcporter directly through subprocess.run([...]) and do not invoke a shell. This avoids shell expansion of the credential-bearing ZOHO_MCP_URL.
list_contacts.py and list_accounts.py paginate automatically (they follow more_records until the full result set is retrieved) and normalize the different Zoho CRM MCP response shapes, so large modules are never silently truncated.
Every Zoho org has different custom fields. Instead of hard-coding them, the list scripts accept:
--fields F1,F2,... — request any Zoho field API names (comma-separated), including org-specific custom fields (e.g. Google_Drive_URL, Trello_URL). Unknown fields appear under their API name in the table header.--where "<COQL clause>" (list_accounts.py only) — override the default WHERE filter, e.g. --where "Google_Drive_URL != ''".# Contacts with custom columns
python3 scripts/list_contacts.py --fields First_Name,Last_Name,Email,Designation
# Accounts filtered on a custom field, showing custom columns
python3 scripts/list_accounts.py \
--where "Google_Drive_URL != ''" \
--fields Account_Name,Website,Google_Drive_URL,Trello_URL,Trello_ID
Use getFields (see below) to discover the exact API names of your custom fields.
# All contacts as table
python3 scripts/list_contacts.py
# Search by last name
python3 scripts/list_contacts.py --search "Smith"
# Raw JSON output
python3 scripts/list_contacts.py --json
# Full record data
python3 scripts/list_contacts.py --search "Smith" --json --full
# All accounts with website
python3 scripts/list_accounts.py
# Search by company name
python3 scripts/list_accounts.py --search "Acme"
# All accounts (including those without website)
python3 scripts/list_accounts.py --all
# JSON output
python3 scripts/list_accounts.py --json
# Search any module by name
python3 scripts/search_records.py Contacts "Smith"
python3 scripts/search_records.py Accounts "Acme Corp"
python3 scripts/search_records.py Deals "Project X"
# COQL query on any module
python3 scripts/search_records.py Contacts --coql "Email != ''" --json
When calling mcporter directly from a shell, escaping can break inline JSON. Write arguments to a temp file:
cat << 'EOF' > /tmp/args.json
{
"path_variables": {"module": "Contacts"},
"query_params": {"criteria": "(Email:equals:test@example.com)"}
}
EOF
mcporter call "$ZOHO_MCP_URL.ZohoCRM_searchRecords" --args "$(< /tmp/args.json)"
For large result sets, use page and per_page:
{
"path_variables": {"module": "Contacts"},
"query_params": {"page": 2, "per_page": 200}
}
Get all field names for a module (useful before writing scripts or Deluge code):
cat << 'EOF' > /tmp/args.json
{
"query_params": {"module": "Contacts", "include": "allowed_permissions_to_update"}
}
EOF
mcporter call "$ZOHO_MCP_URL.ZohoCRM_getFields" --args "$(< /tmp/args.json)"
For a fully capable CRM agent, enable these actions on your Zoho MCP server at mcp.zoho.eu:
getModules - List all CRM modulesgetFields - Get field definitions for any modulegetRecord / getRecords - Read individual or lists of recordssearchRecords - Search by criteria (email, name, etc.)executeCOQLQuery - SQL-like queries across modulesgetRecordCount - Count records per modulegetRelatedRecords - Read linked records (e.g., contacts of an account)getPickListValues - Get dropdown options for fieldscreateRecords - Create new records in any moduleupdateRecord - Update a single record by IDupsertRecords - Insert or update (upsert)createNotes - Add notes to recordscreateEventsRecords - Create calendar eventscreateTags / postRemoveTags - Manage tagsdeleteRecord / deleteRecords - Only enable when specifically neededCOQL (Zoho's SQL-like query language) differs from standard SQL in several ways:
WHERE Last_Name = 'Smith'2026-01-01T00:00:00+01:00LIMIT 20 OFFSET 0true / false (lowercase)-- All contacts with email
SELECT Id, Full_Name, Email FROM Contacts WHERE Email != '' LIMIT 100
-- Deals from last 3 months
SELECT Id, Deal_Name, Amount, Stage FROM Deals WHERE Created_Time >= '2026-04-01T00:00:00+01:00'
-- Accounts by city
SELECT Id, Account_Name, Billing_City FROM Accounts WHERE Billing_City = 'Berlin'
Set the environment variable with your MCP endpoint URL. See "Environment Variable Setup" above.
Use the temp-file approach with --args "$(< /tmp/args.json)" instead of inline JSON.
The MCP connection token may have expired. Go to mcp.zoho.eu, revoke and reconnect the affected app to get a fresh token.
Zoho CRM shows display labels in the UI, but the API uses api_name values (e.g., Account_Name not "Account Name"). Always check field names with getFields before writing scripts or Deluge code.