Method CRM MCP by usefulapi
Query Method CRM customers, leads, activities and invoices; create and update records.
我该使用它吗
质量与安全性
基于对工具定义和协议合规性的自动分析。
上下文开销
这是每次将服务器的工具加载到模型上下文窗口时所消耗的大致 token 数。数值越高,可用于其他任务的注意力就越少。
安装
一键安装
将以下内容添加到你的 `claude_desktop_config.json` 文件中:
{
"mcpServers": {
"method-crm": {
"url": "https://method-crm.usefulapi.io/mcp"
}
}
}远程端点
https://method-crm.usefulapi.io/mcpstreamable-http它能做什么
工具清单
工具(16)
🟢method_get_account
Who the API key acts as: the Method user behind it (id and name; use the id as an assignee). Also a cheap check that the key works. GET /api/v1/me.
输入模式
{
"type": "object",
"properties": {},
"$schema": "https://json-schema.org/draft/2020-12/schema"
}🟢method_list_tables
List every table on this Method account (standard QuickBooks/Xero/Method tables and custom ones) with its source and whether it can be created, updated or deleted. Tables differ per account: call this before querying a table you are unsure of. GET /api/v1/tables.
输入模式
{
"type": "object",
"properties": {},
"$schema": "https://json-schema.org/draft/2020-12/schema"
}🟢method_get_table_fields(table)
The exact field names of a table with type, whether required, creatable and mutable, and for dropdowns the table they reference. Use it to pick `select` fields and to name fields in create/update calls (names are case-sensitive and wrong ones are silently ignored). GET /api/v1/tables/{table}/fields.
输入模式
{
"type": "object",
"properties": {
"table": {
"type": "string",
"pattern": "^[A-Za-z][A-Za-z0-9_]{0,63}$",
"description": "Table name exactly as method_list_tables shows it, e.g. Customer, Contacts, Activity, Invoice, Estimate."
}
},
"required": [
"table"
],
"$schema": "https://json-schema.org/draft/2020-12/schema"
}🟢method_query_records(table, select, filter, orderby, apply, ...)
Query any table (Customer, Contacts, Activity, Invoice, Estimate, Item, Users, custom tables ...). Always pass `select`. `filter` uses OData-style syntax: strings in single quotes, dates as '2026-05-26T00:00:00Z' with Z or an offset, operators eq ne lt gt le ge and or not, functions contains(Name,'x') startswith() endswith(); e.g. "BalanceRemaining gt 0 and DueDate lt '2026-10-01T00:00:00Z'". `apply` runs server-side aggregation instead of listing rows, e.g. "aggregate(BalanceRemaining with sum as Outstanding)" or "groupby((Customer), aggregate(Amount with sum as Total))". Default order is RecordID ascending (stable paging); page with next_skip. GET /api/v1/tables/{table}.
输入模式
{
"type": "object",
"properties": {
"table": {
"type": "string",
"pattern": "^[A-Za-z][A-Za-z0-9_]{0,63}$",
"description": "Table name exactly as method_list_tables shows it, e.g. Customer, Contacts, Activity, Invoice, Estimate."
},
"select": {
"minItems": 1,
"maxItems": 60,
"type": "array",
"items": {
"type": "string",
"pattern": "^[A-Za-z][A-Za-z0-9_]{0,63}$"
},
"description": "Field names to return (case-sensitive, from method_get_table_fields). Without a select Method returns hundreds of columns per row."
},
"filter": {
"description": "Filter expression, e.g. \"IsLeadStatusOnly eq true and contains(Name,'Smith')\".",
"type": "string",
"minLength": 1,
"maxLength": 1500
},
"orderby": {
"description": "Sort, e.g. \"RecordID desc\" or \"TotalBalance desc,RecordID\".",
"type": "string",
"minLength": 1,
"maxLength": 300,
"pattern": "^[A-Za-z0-9_, ]+$"
},
"apply": {
"description": "Aggregation, e.g. \"aggregate($count as Total)\"; ignores paging.",
"type": "string",
"minLength": 1,
"maxLength": 1500
},
"top": {
"description": "Rows per page, 1-100 (default 25).",
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 100
},
"skip": {
"description": "Rows to skip (default 0). Pass the previous reply's next_skip.",
"type": "integer",
"minimum": 0,
"maximum": 10000000
}
},
"required": [
"table"
],
"$schema": "https://json-schema.org/draft/2020-12/schema"
}🟢method_count_records(table, filter)
How many records a table has, optionally matching a filter (same filter syntax as method_query_records). Cheap: call it before paging through a big table. GET /api/v1/tables/{table}/count.
输入模式
{
"type": "object",
"properties": {
"table": {
"type": "string",
"pattern": "^[A-Za-z][A-Za-z0-9_]{0,63}$",
"description": "Table name exactly as method_list_tables shows it, e.g. Customer, Contacts, Activity, Invoice, Estimate."
},
"filter": {
"description": "Filter expression.",
"type": "string",
"minLength": 1,
"maxLength": 1500
}
},
"required": [
"table"
],
"$schema": "https://json-schema.org/draft/2020-12/schema"
}🟢method_get_record(table, record_id, select, expand)
One record by RecordID from any table. Pass `select` to limit the columns; `expand` names a related CHILD table whose rows are embedded (e.g. InvoiceLine on Invoice; at most 50 rows); a parent such as a contact's Entity is not expanded: read it with its own RecordID (Entity_RecordID). GET /api/v1/tables/{table}/{recordId}.
输入模式
{
"type": "object",
"properties": {
"table": {
"type": "string",
"pattern": "^[A-Za-z][A-Za-z0-9_]{0,63}$",
"description": "Table name exactly as method_list_tables shows it, e.g. Customer, Contacts, Activity, Invoice, Estimate."
},
"record_id": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 2147483647,
"description": "The record's RecordID."
},
"select": {
"minItems": 1,
"maxItems": 60,
"type": "array",
"items": {
"type": "string",
"pattern": "^[A-Za-z][A-Za-z0-9_]{0,63}$"
},
"description": "Field names to return (case-sensitive, from method_get_table_fields). Without a select Method returns hundreds of columns per row."
},
"expand": {
"description": "A child table to embed (a table with a dropdown field to this one), e.g. InvoiceLine on Invoice. At most 50 child rows.",
"type": "string",
"pattern": "^[A-Za-z][A-Za-z0-9_]{0,63}$"
}
},
"required": [
"table",
"record_id"
],
"$schema": "https://json-schema.org/draft/2020-12/schema"
}🟢method_search_customers(text, kind, active_only, select, top, ...)
Find customers and leads (the Customer table: Customers & Leads in Method) by text in the name or email, by lead/customer type, or list them all. Returns RecordID, name, email, phone, lead flag and status; override `select` for other fields (see method_get_table_fields with table Customer). Page with next_skip. GET /api/v1/tables/Customer.
输入模式
{
"type": "object",
"properties": {
"text": {
"description": "Matches records whose Name or Email contains this text (case-insensitive).",
"type": "string",
"minLength": 1,
"maxLength": 200
},
"kind": {
"description": "leads = IsLeadStatusOnly true, customers = false (default all).",
"type": "string",
"enum": [
"all",
"leads",
"customers"
]
},
"active_only": {
"description": "Only active records (IsActive true).",
"type": "boolean"
},
"select": {
"minItems": 1,
"maxItems": 60,
"type": "array",
"items": {
"type": "string",
"pattern": "^[A-Za-z][A-Za-z0-9_]{0,63}$"
},
"description": "Field names to return (case-sensitive, from method_get_table_fields). Without a select Method returns hundreds of columns per row."
},
"top": {
"description": "Rows per page, 1-100 (default 25).",
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 100
},
"skip": {
"description": "Rows to skip (default 0). Pass the previous reply's next_skip.",
"type": "integer",
"minimum": 0,
"maximum": 10000000
}
},
"$schema": "https://json-schema.org/draft/2020-12/schema"
}🟢method_list_contacts(entity_id, text, select, top, skip)
List contacts (people), optionally those of one customer/lead entity (entity_id = the Customer's RecordID) or matching text in name/email/phone. Returns RecordID, name, email, phone, the entity link and active flag. Use a Contact's RecordID (not the Customer's) when linking activities. GET /api/v1/tables/Contacts.
输入模式
{
"type": "object",
"properties": {
"entity_id": {
"description": "Only contacts of this entity (the Customer/Vendor RecordID).",
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 2147483647
},
"text": {
"description": "Matches contacts whose Name, Email or Phone contains this text.",
"type": "string",
"minLength": 1,
"maxLength": 200
},
"select": {
"minItems": 1,
"maxItems": 60,
"type": "array",
"items": {
"type": "string",
"pattern": "^[A-Za-z][A-Za-z0-9_]{0,63}$"
},
"description": "Field names to return (case-sensitive, from method_get_table_fields). Without a select Method returns hundreds of columns per row."
},
"top": {
"description": "Rows per page, 1-100 (default 25).",
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 100
},
"skip": {
"description": "Rows to skip (default 0). Pass the previous reply's next_skip.",
"type": "integer",
"minimum": 0,
"maximum": 10000000
}
},
"$schema": "https://json-schema.org/draft/2020-12/schema"
}🟢method_list_activities(entity_id, assigned_to_id, due_after, due_before, select, ...)
List activities (calls, emails, to-dos, notes), newest first. Filter by the entity they belong to, the assigned user or a due-date window. The note text is in Comments. Override `select` for other fields (method_get_table_fields with table Activity). GET /api/v1/tables/Activity.
输入模式
{
"type": "object",
"properties": {
"entity_id": {
"description": "Only activities of this entity (Customer RecordID).",
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 2147483647
},
"assigned_to_id": {
"description": "Only activities assigned to this user id (method_get_account or the Users table).",
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 2147483647
},
"due_after": {
"type": "string",
"description": "ISO 8601 date-time WITH a timezone (Z or +hh:mm), e.g. 2026-10-01T00:00:00Z."
},
"due_before": {
"type": "string",
"description": "ISO 8601 date-time WITH a timezone (Z or +hh:mm), e.g. 2026-10-01T00:00:00Z."
},
"select": {
"minItems": 1,
"maxItems": 60,
"type": "array",
"items": {
"type": "string",
"pattern": "^[A-Za-z][A-Za-z0-9_]{0,63}$"
},
"description": "Field names to return (case-sensitive, from method_get_table_fields). Without a select Method returns hundreds of columns per row."
},
"top": {
"description": "Rows per page, 1-100 (default 25).",
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 100
},
"skip": {
"description": "Rows to skip (default 0). Pass the previous reply's next_skip.",
"type": "integer",
"minimum": 0,
"maximum": 10000000
}
},
"$schema": "https://json-schema.org/draft/2020-12/schema"
}🟢method_list_invoices(customer_id, unpaid_only, created_since, select, top, ...)
List invoices, newest first, optionally only unpaid ones (BalanceRemaining > 0), one customer's (customer_id = Customer RecordID) or created since a time. Returns reference number, customer, dates, amount and balance; override `select` for other fields. For totals use method_query_records with `apply`. GET /api/v1/tables/Invoice.
输入模式
{
"type": "object",
"properties": {
"customer_id": {
"description": "Only invoices of this customer (Customer RecordID).",
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 2147483647
},
"unpaid_only": {
"description": "Only invoices with an outstanding balance.",
"type": "boolean"
},
"created_since": {
"type": "string",
"description": "ISO 8601 date-time WITH a timezone (Z or +hh:mm), e.g. 2026-10-01T00:00:00Z."
},
"select": {
"minItems": 1,
"maxItems": 60,
"type": "array",
"items": {
"type": "string",
"pattern": "^[A-Za-z][A-Za-z0-9_]{0,63}$"
},
"description": "Field names to return (case-sensitive, from method_get_table_fields). Without a select Method returns hundreds of columns per row."
},
"top": {
"description": "Rows per page, 1-100 (default 25).",
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 100
},
"skip": {
"description": "Rows to skip (default 0). Pass the previous reply's next_skip.",
"type": "integer",
"minimum": 0,
"maximum": 10000000
}
},
"$schema": "https://json-schema.org/draft/2020-12/schema"
}🟢method_list_files(table, record_id)
List the files attached to a record (name, size, who added it, id). Use the id with method_get_file_url. GET /api/v1/files.
输入模式
{
"type": "object",
"properties": {
"table": {
"type": "string",
"pattern": "^[A-Za-z][A-Za-z0-9_]{0,63}$",
"description": "Table name exactly as method_list_tables shows it, e.g. Customer, Contacts, Activity, Invoice, Estimate."
},
"record_id": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 2147483647,
"description": "The record's RecordID."
}
},
"required": [
"table",
"record_id"
],
"$schema": "https://json-schema.org/draft/2020-12/schema"
}🟢method_get_file_url(file_id)
A temporary download link (valid 20 minutes) for an attached file, by the file id from method_list_files. The link works without the API key: share it only with someone allowed to see the file. GET /api/v1/files/{id}/url.
输入模式
{
"type": "object",
"properties": {
"file_id": {
"type": "string",
"pattern": "^[A-Za-z0-9-]{8,64}$",
"description": "File id (a GUID)."
}
},
"required": [
"file_id"
],
"$schema": "https://json-schema.org/draft/2020-12/schema"
}🔴method_create_customer(name, is_lead, first_name, last_name, email, ...)
WRITE: creates a record in Method's Customer table. is_lead true = a Customer Lead (does not sync to the accounting system); is_lead false = a full customer, which Method syncs to QuickBooks/Xero (it appears there too). Method also creates the primary Contact; its RecordID is returned as contact_id when it could be found (use it, not the customer id, to link activities). Name is required (for a person with no company use "First Last"). `extra_fields` sets other fields by their exact names (see method_get_table_fields, table Customer). Not idempotent: calling twice creates two records, so search first (method_search_customers). POST /api/v1/tables/Customer.
输入模式
{
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1,
"maxLength": 200,
"description": "Display name of the customer or lead."
},
"is_lead": {
"type": "boolean",
"description": "true = lead (not synced to accounting), false = customer (synced to QuickBooks/Xero)."
},
"first_name": {
"type": "string",
"maxLength": 100
},
"last_name": {
"type": "string",
"maxLength": 100
},
"email": {
"type": "string",
"maxLength": 254
},
"phone": {
"type": "string",
"maxLength": 50
},
"lead_status": {
"description": "Account-defined lead status, e.g. Open (leads only).",
"type": "string",
"maxLength": 100
},
"assigned_to_id": {
"description": "Owner: a Method user id (method_get_account, or the Users table).",
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 2147483647
},
"extra_fields": {
"type": "object",
"propertyNames": {
"type": "string",
"pattern": "^[A-Za-z][A-Za-z0-9_]{0,63}$"
},
"additionalProperties": {
"anyOf": [
{
"type": "string",
"maxLength": 8000
},
{
"type": "number"
},
{
"type": "boolean"
},
{
"type": "null"
}
]
},
"description": "Field name -> value, exactly as method_get_table_fields names them (case-sensitive; a misspelled name is silently ignored by Method). For a dropdown/linked field send the integer id in its `_RecordID` field (e.g. Customer_RecordID: 1234), not the caption."
}
},
"required": [
"name",
"is_lead"
],
"$schema": "https://json-schema.org/draft/2020-12/schema"
}🔴method_create_activity(entity_id, comments, activity_type_id, activity_type, contact_id, ...)
WRITE: logs an activity (call, email, to-do, note) on a customer/lead. entity_id is the Customer RecordID; contact_id is the Contact RecordID (not the Customer's; method_list_contacts finds it). The text goes in Comments. Prefer activity_type_id (read it from an existing activity of that type) over the caption activity_type: a caption that matches zero or several options is silently dropped. Method requires an assignee on standard accounts. The reply re-reads the activity so you can check the links were stored (field names are account-specific; method_get_table_fields with table Activity shows them). It does not email anyone. Not idempotent. POST /api/v1/tables/Activity.
输入模式
{
"type": "object",
"properties": {
"entity_id": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 2147483647,
"description": "The Customer (entity) RecordID the activity belongs to, so it shows in its history."
},
"comments": {
"type": "string",
"minLength": 1,
"maxLength": 8000,
"description": "The note body."
},
"activity_type_id": {
"description": "ActivityType_RecordID from an existing activity of the wanted type.",
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 2147483647
},
"activity_type": {
"description": "Activity type caption, e.g. Call. Use only if you have no id.",
"type": "string",
"minLength": 1,
"maxLength": 100
},
"contact_id": {
"description": "The Contact RecordID.",
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 2147483647
},
"assigned_to_id": {
"description": "Method user id of the owner (method_get_account).",
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 2147483647
},
"status_id": {
"description": "ActivityStatus_RecordID (account-defined, e.g. Not Started / Completed).",
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 2147483647
},
"due": {
"description": "Due/start time, YYYY-MM-DDTHH:mm:ss.",
"type": "string"
}
},
"required": [
"entity_id",
"comments"
],
"$schema": "https://json-schema.org/draft/2020-12/schema"
}🔴method_create_record(table, fields)
WRITE: creates a record in any table that allows it (method_list_tables shows canCreate), e.g. Estimate, Invoice, Item, Vendor. For customers/leads and activities use the dedicated tools. Field names are case-sensitive and a wrong one is silently ignored: read them with method_get_table_fields first, and send dropdown links as their `_RecordID` integer. Creating accounting records (Invoice, Estimate, Customer ...) makes Method sync them to QuickBooks/Xero. Child lines (InvoiceLine ...) are not supported here. Not idempotent. Returns the new RecordID. POST /api/v1/tables/{table}.
输入模式
{
"type": "object",
"properties": {
"table": {
"type": "string",
"pattern": "^[A-Za-z][A-Za-z0-9_]{0,63}$",
"description": "Table name exactly as method_list_tables shows it, e.g. Customer, Contacts, Activity, Invoice, Estimate."
},
"fields": {
"type": "object",
"propertyNames": {
"type": "string",
"pattern": "^[A-Za-z][A-Za-z0-9_]{0,63}$"
},
"additionalProperties": {
"anyOf": [
{
"type": "string",
"maxLength": 8000
},
{
"type": "number"
},
{
"type": "boolean"
},
{
"type": "null"
}
]
},
"description": "Field name -> value, exactly as method_get_table_fields names them (case-sensitive; a misspelled name is silently ignored by Method). For a dropdown/linked field send the integer id in its `_RecordID` field (e.g. Customer_RecordID: 1234), not the caption."
}
},
"required": [
"table",
"fields"
],
"$schema": "https://json-schema.org/draft/2020-12/schema"
}🔴method_update_record(table, record_id, fields)
WRITE: changes fields on one record (PATCH: only the fields you pass change). Field names are case-sensitive and Method silently ignores wrong or read-only names, so the reply re-reads the record and shows the stored values of the fields you sent: check them. Linked fields are changed through their `_RecordID` integer field. Updating accounting records (Customer, Invoice ...) makes Method sync the change to QuickBooks/Xero; setting IsLeadStatusOnly to false converts a lead into a synced customer. There is no delete tool. PATCH /api/v1/tables/{table}/{recordId}.
输入模式
{
"type": "object",
"properties": {
"table": {
"type": "string",
"pattern": "^[A-Za-z][A-Za-z0-9_]{0,63}$",
"description": "Table name exactly as method_list_tables shows it, e.g. Customer, Contacts, Activity, Invoice, Estimate."
},
"record_id": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 2147483647,
"description": "The record's RecordID."
},
"fields": {
"type": "object",
"propertyNames": {
"type": "string",
"pattern": "^[A-Za-z][A-Za-z0-9_]{0,63}$"
},
"additionalProperties": {
"anyOf": [
{
"type": "string",
"maxLength": 8000
},
{
"type": "number"
},
{
"type": "boolean"
},
{
"type": "null"
}
]
},
"description": "Field name -> value, exactly as method_get_table_fields names them (case-sensitive; a misspelled name is silently ignored by Method). For a dropdown/linked field send the integer id in its `_RecordID` field (e.g. Customer_RecordID: 1234), not the caption."
}
},
"required": [
"table",
"record_id",
"fields"
],
"$schema": "https://json-schema.org/draft/2020-12/schema"
}社区
证据