conduyt-mcp 4.3.1 → 4.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -45,7 +45,7 @@ if (!apiKey.startsWith("cdy_")) {
45
45
  const client = new ConduytClient({ apiUrl, apiKey });
46
46
  const server = new McpServer({
47
47
  name: "conduyt",
48
- version: "4.3.1",
48
+ version: "4.3.3",
49
49
  });
50
50
  registerDiscoveryTools(server, client);
51
51
  registerContactTools(server, client);
@@ -1,8 +1,8 @@
1
1
  import { z } from "zod";
2
2
  import { formatResult } from "../client.js";
3
3
  export function registerCustomFieldTools(server, client) {
4
- server.tool("conduyt_list_custom_fields", "List custom field definitions. Filter by entity type (contact or deal) to see available fields.", {
5
- entityType: z.enum(["contact", "deal"]).optional().describe("Filter by entity type"),
4
+ server.tool("conduyt_list_custom_fields", "List custom field definitions. Filter by entity type (contact, deal, or company) to see available fields.", {
5
+ entityType: z.enum(["contact", "deal", "company"]).optional().describe("Filter by entity type"),
6
6
  page: z.number().optional().describe("Page number (default 1)"),
7
7
  per_page: z.number().optional().describe("Results per page"),
8
8
  }, async ({ entityType, page, per_page }) => {
@@ -16,8 +16,8 @@ export function registerCustomFieldTools(server, client) {
16
16
  const result = await client.get(`/api/v1/custom-fields?${params}`);
17
17
  return formatResult(result);
18
18
  });
19
- server.tool("conduyt_create_custom_field", "Create a new custom field definition for contacts or deals. Field types: text, number, date, select, multiselect, boolean.", {
20
- entityType: z.enum(["contact", "deal"]).describe("Entity type this field applies to"),
19
+ server.tool("conduyt_create_custom_field", "Create a new custom field definition for contacts, deals, or companies. Field types: text, number, date, select, multiselect, boolean.", {
20
+ entityType: z.enum(["contact", "deal", "company"]).describe("Entity type this field applies to"),
21
21
  fieldKey: z.string().describe("Unique field key (alphanumeric + underscores, e.g. 'lead_score')"),
22
22
  label: z.string().describe("Human-readable label (e.g. 'Lead Score')"),
23
23
  fieldType: z.enum(["text", "number", "date", "select", "multiselect", "boolean"]).describe("Field data type"),
@@ -1,20 +1,57 @@
1
1
  import { z } from "zod";
2
2
  import { formatResult } from "../client.js";
3
3
  export function registerDealTools(server, client) {
4
- server.tool("conduyt_list_deals", "List deals with filtering by pipeline, stage, assignee, or status. Returns deal title, value, stage, contact, and dates.", {
5
- pipelineId: z.string().optional().describe("Filter by pipeline UUID"),
6
- stageId: z.string().optional().describe("Filter by stage UUID"),
4
+ server.tool("conduyt_list_deals", "List deals with filtering by pipeline, stage, assignee, or status. Returns deal title, value, stage, contact, and dates. Filter by pipeline/stage NAME (the same names you import with) or by UUID — both camelCase (pipelineName) and snake_case (pipeline_name) argument names are accepted. An unknown name returns a clear error, never the whole account.", {
5
+ pipeline: z.string().optional().describe("Filter by pipeline UUID (canonical). Aliases: pipelineId, pipeline_id."),
6
+ pipelineId: z.string().optional().describe("Filter by pipeline UUID (alias of pipeline)."),
7
+ pipeline_id: z.string().optional().describe("snake_case alias of pipelineId."),
8
+ pipelineName: z.string().optional().describe("Filter by pipeline NAME (the same name used in conduyt_import_deals/create) — resolved to its UUID; errors if not found. Alias: pipeline_name."),
9
+ pipeline_name: z.string().optional().describe("snake_case alias of pipelineName."),
10
+ stage: z.string().optional().describe("Filter by stage UUID (canonical). Aliases: stageId, stage_id."),
11
+ stageId: z.string().optional().describe("Filter by stage UUID (alias of stage)."),
12
+ stage_id: z.string().optional().describe("snake_case alias of stageId."),
13
+ stageName: z.string().optional().describe("Filter by stage NAME within the pipeline — resolved to its UUID; errors if not found. Alias: stage_name."),
14
+ stage_name: z.string().optional().describe("snake_case alias of stageName."),
7
15
  assignedTo: z.string().optional().describe("Filter by assigned user UUID"),
8
16
  status: z.enum(["open", "won", "lost"]).optional().describe("Filter by deal status"),
9
17
  search: z.string().optional().describe("Search deal titles"),
10
18
  page: z.number().optional().describe("Page number (default 1)"),
11
19
  per_page: z.number().optional().describe("Results per page (default 25, max 100)"),
12
- }, async ({ pipelineId, stageId, assignedTo, status, search, page, per_page }) => {
20
+ }, async (a) => {
13
21
  const params = new URLSearchParams();
14
- if (pipelineId)
15
- params.set("pipeline", pipelineId);
16
- if (stageId)
17
- params.set("stage", stageId);
22
+ // Accept every reasonable casing of each filter (canonical bare name +
23
+ // camelCase + snake_case) so an agent's provided filter is NEVER silently
24
+ // stripped by the schema. Coalesce each alias GROUP to a single value
25
+ // locally — conflicting non-blank values in a group are a contradictory
26
+ // request and raise a clear tool error (don't emit ambiguous duplicate
27
+ // query params and hope the parser picks the right one) — then send ONE
28
+ // canonical wire param (pipeline / stage / pipeline_name / stage_name, the
29
+ // params the live contract advertises). A blank value is forwarded so the
30
+ // server rejects it; an id/name cross-conflict is caught by the server.
31
+ const coalesce = (label, ...vals) => {
32
+ const present = vals.filter((v) => v !== undefined);
33
+ if (present.length === 0)
34
+ return undefined;
35
+ const distinct = [...new Set(present.map((v) => v.trim().toLowerCase()).filter((v) => v !== ""))];
36
+ if (distinct.length > 1) {
37
+ throw new Error(`Conflicting ${label} values — pass only one.`);
38
+ }
39
+ // prefer a non-blank original; else keep a blank so the server can reject it
40
+ return present.find((v) => v.trim() !== "") ?? present[0];
41
+ };
42
+ const { assignedTo, status, search, page, per_page } = a;
43
+ const pipelineV = coalesce("pipeline id", a.pipeline, a.pipelineId, a.pipeline_id);
44
+ const pipelineNameV = coalesce("pipeline name", a.pipelineName, a.pipeline_name);
45
+ const stageV = coalesce("stage id", a.stage, a.stageId, a.stage_id);
46
+ const stageNameV = coalesce("stage name", a.stageName, a.stage_name);
47
+ if (pipelineV !== undefined)
48
+ params.set("pipeline", pipelineV);
49
+ if (pipelineNameV !== undefined)
50
+ params.set("pipeline_name", pipelineNameV);
51
+ if (stageV !== undefined)
52
+ params.set("stage", stageV);
53
+ if (stageNameV !== undefined)
54
+ params.set("stage_name", stageNameV);
18
55
  if (assignedTo)
19
56
  params.set("assigned_to", assignedTo);
20
57
  if (status)
@@ -1,7 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { formatResult } from "../client.js";
3
3
  export function registerDiscoveryTools(server, client) {
4
- server.tool("conduyt_api_catalog", "Discover all Conduyt CRM API endpoints 409 endpoints across 32 domains. Call this first to learn what the CRM can do.", {}, async () => {
4
+ server.tool("conduyt_api_catalog", "Discover all Conduyt CRM API endpoints across 90+ domains, with full request and response schemas. Call this first to learn what the CRM can do — the response carries the exact, current endpoint counts.", {}, async () => {
5
5
  const result = await client.get("/api/v1/schema/api-catalog");
6
6
  return formatResult(result);
7
7
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "conduyt-mcp",
3
- "version": "4.3.1",
3
+ "version": "4.3.3",
4
4
  "description": "MCP server for Conduyt CRM — expose CRM operations as AI-accessible tools",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",