
Search activities, optionally scoped to one record with parent, e.g. {'id': 185332522, 'type': 'person'} — this is how you read a contact's history. Set fullresult true to get every match rather than the latest per record. Use listactivity_types to filter by type.
Fetch one activity by ID, including its type, the record it belongs to and its text body. Use list_activities to find the ID.
Log an activity (note, call, meeting) against a record. Copper requires a type, a parent and details. Call listactivitytypes first and pick a 'user' category type — 'system' types are read-only. Returns the created activity including its ID.
Update a logged activity's text or date. Copper requires the type on every update even when unchanged — call listactivitytypes or get_activity to supply it. Only 'user' category activities can be updated.
Permanently delete a logged activity by ID. This cannot be undone. Only 'user' category activities can be deleted; Copper's own 'system' audit entries cannot. Use list_activities to find the ID.
Log up to 10 activities in one call, each with a type, a parent and details. Call listactivitytypes first for valid 'user' category type IDs. Copper answers 200 even when individual records fail, so check the per-record entries.
Search companies with server-side filters (name, email domains, assignee, contact type, tags, location, interaction, created- and modified-date ranges) and sorting. Use listcontacttypes and list_users to find filter IDs.
Fetch one company by ID, including address, websites, tags and custom fields. Use list_companies to find the ID.
Create a company. Only a name is required. emaildomain must be unique across the account or the call fails. Call listcontacttypes, listusers and listcustomfield_definitions first for valid IDs. Returns the created company including its ID.
Update a company by ID. Only the fields you supply change, but list fields such as websites and tags replace the existing list rather than appending. Use list_companies to find the ID.
Permanently delete a company by ID. This cannot be undone and does not delete the people linked to it. Use listcompanies to find the ID. Copper answers 200 even when no such record existed, returning the same body either way, so isdeleted true is not proof the record was there — confirm with a get or list call first if that matters.
Create up to 10 companies in one call. Copper answers 200 even when individual records fail, so check the per-record entries: a success carries an id, a failure carries a message. Email domains must be unique across the account.
Update up to 10 companies in one call, each identified by companyid. Copper answers 200 even when individual records fail, so check the per-record entries. Use listcompanies to find IDs.
Search leads with server-side filters (name, emails, company, status, source, assignee, tags, location and value ranges) and sorting. Use listleadstatuses, listcustomersources and list_users to find filter IDs. Fails if the Leads module is off for the account.
Fetch one lead by ID, including email, company name, status, value and custom fields. Use list_leads to find the ID.
Create a lead. Only a name is required. Call listleadstatuses, listcustomersources, listusers and listcustomfielddefinitions first for valid IDs. A lead carries a single email, not a list. Returns the created lead including its ID.
Update a lead by ID — change its status, owner, value or details. Only the fields you supply change, but list fields such as tags replace the existing list. Use listleads for the ID and listlead_statuses for status IDs.
Permanently delete a lead by ID. This cannot be undone — to qualify a lead instead, use convertlead. Use listleads to find the ID.
Convert a qualified lead into a person, and optionally a company and an opportunity. The lead is consumed and can no longer be fetched. Copper honours opportunitydetails but decides the person and company itself, so always read personname and companyname off the result to see what it actually made. Pass companydetails.id to control the company. Use listpipelinestages for the pipeline IDs opportunity_details takes.
Create up to 10 leads in one call. Copper answers 200 even when individual records fail, so check the per-record entries: a success carries an id, a failure carries a message.
Update up to 10 leads in one call, each identified by leadid. Copper answers 200 even when individual records fail, so check the per-record entries. Use listleads to find IDs.
Fetch the Copper account this credential belongs to: its ID, name, timezone and which optional modules are enabled. Check settings.settingenableleads here before using any lead tool — the lead endpoints fail when it is false.
List the users in the Copper account. Call this first to get the user IDs that assigneeid takes on every create and update tool, and that the assigneeids filter takes on every list tool.
List the opportunity pipelines in the account. Call this to get the pipelineid that createopportunity and updateopportunity take. Some plans return an empty list here even though pipelines exist: in that case read pipelineid off listpipelinestages, then get_pipeline for its details.
Get one opportunity pipeline by ID, including its stages and whether it tracks revenue. Use this when listpipelines returns an empty list, which some plans do even though pipelines exist. Read pipelineid from listpipelinestages or from an opportunity.
List every opportunity pipeline stage in the account, each naming the pipeline it belongs to. Call this to get the pipelinestageid that createopportunity and updateopportunity take when moving a deal.
List the activity types in the account, split into the 'user' types an integration may write and the read-only 'system' types Copper writes itself. Call this before createactivity or updateactivity and pick a user type.
List the contact types in the account, e.g. 'Potential Customer'. Call this to get the contacttypeid that createperson, updateperson, createcompany and updatecompany take, and that the contacttypeids filter takes.
List the customer sources in the account, e.g. 'Cold Call'. Call this to get the customersourceid that createopportunity, updateopportunity, createlead and updatelead take.
List the loss reasons in the account. Call this to get the lossreasonid to pass to update_opportunity when setting an opportunity's status to Lost.
List the lead statuses in the account. Call this to get the statusid that createlead and updatelead take. Fails if the Leads module is off for the account — check getaccount first.
List the custom field definitions configured on the account, with each field's data type, the entities it applies to, and its dropdown options. Call this to get the customfielddefinitionid every customfields value needs.
List the tags already in use across the account. Call this before setting tags on a record so you reuse an existing tag instead of creating a near-duplicate.
Search opportunities with server-side filters (pipeline, stage, status, priority, assignee, company, value, close-date and stage-change-date ranges) and sorting. Use listpipelinestages (it carries each stage's pipelineid) and listusers to find filter IDs; list_pipelines returns empty on some plans.
Fetch one opportunity by ID, including pipeline, stage, value, status and custom fields. Use list_opportunities to find the ID.
Create an opportunity. Copper requires a name and a primarycontactid — use listpeople or getpersonbyemail to get one. Call listpipelines and listpipeline_stages first to place it in a specific stage, or omit them to use the default pipeline. Returns the created opportunity including its ID.
Update an opportunity by ID — move it to another stage, change its value, or close it by setting status to Won or Lost. Pair status 'Lost' with a lossreasonid from listlossreasons. Use listpipelinestages for stage IDs.
Permanently delete an opportunity by ID. This cannot be undone — to close a deal instead, use updateopportunity with status Won, Lost or Abandoned. Use listopportunities to find the ID.
Create up to 10 opportunities in one call. Each needs a name and a primarycontactid. Copper answers 200 even when individual records fail, so check the per-record entries: a success carries an id, a failure a message.
Search people with server-side filters (name, emails, company, assignee, contact type, tags, location, interaction, created- and modified-date ranges) and sorting. Use this to browse or filter; use getpersonbyemail for an exact single lookup. Use listcontacttypes and listusers to find filter IDs.
Fetch one person by ID, including emails, phone numbers, company, tags and custom fields. Use listpeople or getpersonbyemail to find the ID.
Fetch one person by their exact email address. Use this when you have an email and need the person's ID; use list_people to browse or filter instead.
Create a person. Only a name is required. Call listcontacttypes, listusers, listcompanies and listcustomfielddefinitions first to get valid IDs for contacttypeid, assigneeid, company_id and custom fields. Returns the created person including its ID.
Update a person by ID. Only the fields you supply change; omitted fields are left alone, but list fields such as emails and tags replace the existing list rather than appending. Use list_people to find the ID.
Permanently delete a person by ID. This cannot be undone. Use listpeople to find the ID and confirm the record before deleting. Copper answers 200 even when no such record existed, returning the same body either way, so isdeleted true is not proof the record was there — confirm with a get or list call first if that matters.
Create up to 10 people in one call. Copper answers 200 even when individual records fail, so check the per-record entries: a success carries an id, a failure carries a message. Email addresses must be unique across the account.
Update up to 10 people in one call, each identified by personid. Copper answers 200 even when individual records fail, so check the per-record entries. Use listpeople to find IDs.
Search projects with server-side filters (name, assignee, status, tags and created/modified date ranges) and sorting. Use list_users to find assignee IDs.
Fetch one project by ID, including its assignee, status, tags and custom fields. Use list_projects to find the ID.
Create a project. Only a name is required. Set an owner with assigneeid from listusers, and use listcustomfield_definitions for custom field IDs. Returns the created project including its ID.
Update a project by ID — reassign it, rename it, or close it by setting status to Completed. Only the fields you supply change, but tags replace the existing list. Use list_projects to find the ID.
Permanently delete a project by ID. This cannot be undone — to close a project instead, use updateproject with status Completed. Use listprojects to find the ID. Copper answers 200 even when no such record existed, returning the same body either way, so isdeleted true is not proof the record was there — confirm with a get or list_ call first if that matters.
List the records linked to one record — for example every opportunity and project attached to a person. Narrow with relatedentitytype (plural) to one kind of link. Use the matching list tool (listpeople, listcompanies) for entity_id.
Link two existing records, e.g. attach an opportunity to a person. Both must already exist — this creates the link, not the records. relatedentitytype is singular here ('opportunity'), unlike listrelateditems. Use the matching list tools to find both IDs.
Remove the link between two records. The records themselves are not deleted, only the relationship. relatedentitytype is singular here ('opportunity'). Use listrelateditems to confirm the link exists first.
Search tasks with server-side filters (assignee, status, priority, the project or opportunity a task hangs off, tags, and due/reminder/created/modified/completed date ranges) and sorting. Use listusers, listprojects and list_opportunities to find filter IDs.
Fetch one task by ID, including its assignee, status, due date and the record it is attached to. Use list_tasks to find the ID.
Create a task. Only a name is required. Attach it to a record with relatedresource, e.g. {'id': 185332522, 'type': 'person'}, and set an owner with assigneeid from list_users. Dates are Unix epoch seconds. Returns the created task including its ID.
Update a task by ID — reassign it, change its due date, or close it by setting status to Completed. Only the fields you supply change, but tags replace the existing list. Use list_tasks to find the ID.
Permanently delete a task by ID. This cannot be undone — to close a task instead, use updatetask with status Completed. Use listtasks to find the ID. Copper answers 200 even when no such record existed, returning the same body either way, so isdeleted true is not proof the record was there — confirm with a get or list_ call first if that matters.
Validate Copper credentials by fetching the account. Returns success status and the account name. Requires both the API key and the email address of the user who generated it.
List the webhook subscriptions registered on the account, each with the entity type and event it watches and the URL it posts to. Call this before creating one to avoid registering a duplicate.
Fetch one webhook subscription by ID, including its target URL, watched entity type and event. Use list_webhooks to find the ID.
Register a webhook subscription so Copper posts to your URL when a record changes. The target must already be a live HTTPS listener you control — Copper does not verify it and drops notifications to a dead URL. One subscription watches one entity type and one event.
Update a webhook subscription by ID: its target URL, secret, or customfieldcomputedvalues. Copper cannot change the entity type or event a subscription watches — it answers 200 and ignores them — so use deletewebhook and createwebhook for that. Use listwebhooks to find the ID.
Delete a webhook subscription by ID. Copper stops posting notifications immediately. Use list_webhooks to find the ID.

In an mcp.json file, add the configuration below, and restart Cursor.
Learn more in the official documentation ↗
1{
2 "mcpServers": {
3 "agent-handler": {
4 "url": "https://ah-api-develop.merge.dev/api/v1/tool-packs/{TOOL_PACK_ID}/registered-users/{REGISTERED_USER_ID}/mcp",
5 "headers": {
6 "Authorization": "Bearer yMt*****"
7 }
8 }
9 }
10}
11Open your Claude Desktop configuration file and add the server configuration below. You'll also need to restart the application for the changes to take effect.
Make sure Claude is using the Node v20+.
Learn more in the official documentation ↗
1{
2 "mcpServers": {
3 "agent-handler": {
4 "command": "npx",
5 "args": [
6 "-y",
7 "mcp-remote@latest",
8 "https://ah-api-develop.merge.dev/api/v1/tool-packs/{TOOL_PACK_ID}/registered-users/{REGISTERED_USER_ID}/mcp",
9 "--header",
10 "Authorization: Bearer ${AUTH_TOKEN}"
11 ],
12 "env": {
13 "AUTH_TOKEN": "yMt*****"
14 }
15 }
16 }
17}Open your Windsurf MCP configuration file and add the server configuration below.
Click on the refresh button in the top right of the Manage MCP server page or in the top right of the chat box in the box icon.
Learn more in the official documentation ↗
1{
2 "mcpServers": {
3 "agent-handler": {
4 "command": "npx",
5 "args": [
6 "-y",
7 "mcp-remote@latest",
8 "https://ah-api.merge.dev/api/v1/tool-packs/<tool-pack-id>/registered-users/<registered-user-id>/mcp",
9 "--header",
10 "Authorization: Bearer ${AUTH_TOKEN}"
11 ],
12 "env": {
13 "AUTH_TOKEN": "<ah-production-access-key>"
14 }
15 }
16 }
17 }In Command Palette (Cmd+Shift+P on macOS, Ctrl+Shift+P on Windows), run "MCP: Open User Configuration".
You can then add the configuration below and press "start" right under servers. Enter the auth token when prompted.
Learn more in the official documentation ↗
1{
2 "inputs": [
3 {
4 "type": "promptString",
5 "id": "agent-handler-auth",
6 "description": "Agent Handler AUTH_TOKEN", // "yMt*****" when prompt
7 "password": true
8 }
9 ],
10 "servers": {
11 "agent-handler": {
12 "type": "stdio",
13 "command": "npx",
14 "args": [
15 "-y",
16 "mcp-remote@latest",
17 "https://ah-api-develop.merge.dev/api/v1/tool-packs/{TOOL_PACK_ID}/registered-users/{REGISTERED_USER_ID}/mcp",
18 "--header",
19 "Authorization: Bearer ${input:agent-handler-auth}"
20 ]
21 }
22 }
23}Whether you're an engineer experimenting with agents or a product manager looking to add tools, you can get started for free now