conduyt-mcp 4.99.0 → 4.100.0

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/README.md CHANGED
@@ -36,6 +36,7 @@ MCP server for [Conduyt CRM](https://conduyt.app) — expose your CRM as AI-acce
36
36
  | `conduyt_get_stage_rules` / `conduyt_set_stage_rules` | Read / set one stage's rule (`requiredFields` incl. `custom:<fieldKey>`, `requiredDocuments` 0-20, lost-reason label and options, `lostReasonAllowOther`, `entryByAutomationOnly`: only an automation can move or create a deal into the stage, every other entry is refused 422 `stage_automation_only`; `conduyt_list_pipelines` shows it on each stage; `closesOtherOpenDeals` (+ `closesOtherOpenDealsReason`, 1-200 characters): a deal entering the stage marks the contact's other open deals lost; `assignsContactOwner`: the entering deal's owner becomes the contact owner, the previous contact owner stays a collaborator); other stages untouched; revision-aware, retried once on a 409 |
37
37
  | `conduyt_get_calling_hours` / `conduyt_update_calling_hours` | Read the window the dialer enforces (`effectiveCallingWindow`: `source`, `enabled`, `start`/`end` HH:MM, `timezone` or null = contact zone, then number area code, then account) / set `settings.callingWindow` (`enabled`, `start`, `end` always sent, `end` after `start`, `timezone` trimmed and recognised by the API (omit keeps the stored zone, null clears it); `enabled: false` needs `acknowledgeAnyHour: true`, else 400 `calling_hours_acknowledgement_required`) |
38
38
  | `conduyt_list_email_opt_outs` / `conduyt_add_email_opt_out` / `conduyt_resubscribe_email_opt_out` | The email opt-out list (owner / admin key; hash-only rows `{ id, source, reason, createdAt }`): list (`page`, `perPage` 1-100), add an address by hand (`reason` 1-200 required; 201 new row, 200 when an unsubscribe-link or Mailchimp row is promoted to manual, 409 with the existing row when already listed by hand) and take an address off (`{ removed }`) |
39
+ | `conduyt_get_my_profile` | Read the caller's own identity (`GET /users/me`, same as `/auth/me`): `data.user` incl. `ringtone` (the incoming-ring slug, null = Conduyt Default), `data.account`, role, permissions, `apiKey`, `accounts`. Editing the profile is a signed-in person's action and is not exposed to API keys |
39
40
  | `conduyt_create_short_link` | Make (or reuse) the workspace's Conduyt short link for a public link-shortener address the Message check flagged (`POST /short-links`, body `{ url }`; key needs the `messages` scope, member needs communications.create). Not shortenable: our own host, merge fields, `mailto:`/`tel:`/`sms:`, or any address the Message check does not flag as a link shortener (400 `not_shortenable`). Other errors verbatim, never retried: 400 `invalid_body`, 403 `forbidden`, 413 `body_too_large`, 429 `rate_limited` (with Retry-After), 503 `short_link_unavailable` (nothing changed) |
40
41
  | `conduyt_get_deal_card_fields` / `conduyt_set_deal_card_fields` | Read / replace the kanban card fields (1-8, built-ins or `custom:<fieldKey>`) |
41
42
  | `conduyt_get_settings_revision` | The account `settingsRevision` (plus stored stage rules and rot days): pass it as `expectedSettingsRevision` to `conduyt_archive_pipeline` / `conduyt_delete_stage` so a stale one is refused 409 `stale_settings_revision` |
@@ -33,6 +33,7 @@ import { registerBatchOperationTools } from "./tools/batch-operations.js";
33
33
  import { registerDncTools } from "./tools/dnc.js";
34
34
  import { registerEmailOptOutTools } from "./tools/email-opt-out.js";
35
35
  import { registerShortLinkTools } from "./tools/short-links.js";
36
+ import { registerMyProfileTools } from "./tools/my-profile.js";
36
37
  import { registerSavedFilterTools } from "./tools/saved-filters.js";
37
38
  import { registerDripCampaignTools } from "./tools/drip-campaigns.js";
38
39
  import { registerScoringTools } from "./tools/scoring.js";
@@ -93,6 +94,7 @@ export function registerAllTools(server, client) {
93
94
  registerDncTools(server, client);
94
95
  registerEmailOptOutTools(server, client);
95
96
  registerShortLinkTools(server, client);
97
+ registerMyProfileTools(server, client);
96
98
  registerSavedFilterTools(server, client);
97
99
  registerDripCampaignTools(server, client);
98
100
  registerScoringTools(server, client);
@@ -221,6 +221,7 @@ export const TOOL_ANNOTATIONS = {
221
221
  conduyt_list_email_opt_outs: ro("List Email Opt-Outs"),
222
222
  conduyt_add_email_opt_out: update("Add Email Opt-Out"), // a repeat is refused 409 (already listed by hand) before any write, so it lands on the same state; adding a block is protective
223
223
  conduyt_resubscribe_email_opt_out: destroy("Resubscribe Email Address"), // consent change: removing the row re-enables every customer-facing email to the address
224
+ conduyt_get_my_profile: ro("Get My Profile"),
224
225
  conduyt_create_short_link: update("Create Short Link"), // one link per account and target: a repeat returns the same link (idempotent), nothing existing is changed
225
226
  conduyt_list_saved_filters: ro("List Saved Filters"),
226
227
  conduyt_create_saved_filter: create("Create Saved Filter"),
@@ -0,0 +1,8 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { ConduytClient } from "../client.js";
3
+ /**
4
+ * The caller's own profile read (app route GET /api/v1/users/me, the same handler as GET /auth/me). data.user carries the
5
+ * incoming-ring slug (#385) as `ringtone`. PATCH /users/me is deliberately not a tool: it is a signed-in person's action
6
+ * (profile, ringtone, password) and the API refuses an API key, so a tool for it would only ever return 403.
7
+ */
8
+ export declare function registerMyProfileTools(server: McpServer, client: ConduytClient): void;
@@ -0,0 +1,10 @@
1
+ import { formatResult } from "../client.js";
2
+ import { strictInput } from "./strict.js";
3
+ /**
4
+ * The caller's own profile read (app route GET /api/v1/users/me, the same handler as GET /auth/me). data.user carries the
5
+ * incoming-ring slug (#385) as `ringtone`. PATCH /users/me is deliberately not a tool: it is a signed-in person's action
6
+ * (profile, ringtone, password) and the API refuses an API key, so a tool for it would only ever return 403.
7
+ */
8
+ export function registerMyProfileTools(server, client) {
9
+ strictInput(server.tool("conduyt_get_my_profile", "Read the caller's own identity (GET /api/v1/users/me, identical to GET /auth/me): data.user (id, email, firstName, lastName, avatarUrl, phone, birthdayMonth, birthdayDay, tourSeenAt, and ringtone: the slug of the incoming ring this person hears, or null = Conduyt Default; the slug is one of classic, soft-bell, marimba, digital-pulse, rising-chime, office-trill, double-ring, pocket-melody, retro-cell, kalimba, uplift), data.account (the current account, name at data.account.name), role, permissions, leadVisibilityMode, apiKey (the key's own metadata when called with an API key, else null) and accounts. Any authenticated caller, including a scoped key, may read it. Editing the profile (name, phone, birthday, ringtone, password) is a signed-in person's action in the app and is not available to an API key.", {}, async () => formatResult(await client.get("/api/v1/users/me"))));
10
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "conduyt-mcp",
3
- "version": "4.99.0",
3
+ "version": "4.100.0",
4
4
  "mcpName": "com.conduyt/mcp",
5
5
  "description": "MCP server for Conduyt CRM — expose CRM operations as AI-accessible tools",
6
6
  "type": "module",
@@ -200,6 +200,11 @@
200
200
  "status": "planned",
201
201
  "reason": "company subroutes beyond core CRUD"
202
202
  },
203
+ {
204
+ "match": "PATCH /api/v1/users/me",
205
+ "status": "excluded",
206
+ "reason": "a signed-in person's own profile edit (name, phone, birthday, ringtone, password): session only (assertSessionOnly), never an API key's; GET /users/me is covered by conduyt_get_my_profile"
207
+ },
203
208
  {
204
209
  "match": "/api/v1/users",
205
210
  "status": "planned",
@@ -1,11 +1,11 @@
1
1
  {
2
- "generatedAt": "2026-10-25T02:48:35.925Z",
3
- "toolCount": 365,
2
+ "generatedAt": "2026-10-25T09:09:54.777Z",
3
+ "toolCount": 366,
4
4
  "totalEndpoints": 1107,
5
5
  "counts": {
6
- "covered": 321,
7
- "excluded": 330,
8
- "planned": 456,
6
+ "covered": 322,
7
+ "excluded": 331,
8
+ "planned": 454,
9
9
  "uncategorized": 0
10
10
  },
11
11
  "matrix": {
@@ -4854,12 +4854,14 @@
4854
4854
  ]
4855
4855
  },
4856
4856
  "GET /api/v1/users/me": {
4857
- "status": "planned",
4858
- "reason": "team management subroutes beyond list/invite"
4857
+ "status": "covered",
4858
+ "tools": [
4859
+ "conduyt_get_my_profile"
4860
+ ]
4859
4861
  },
4860
4862
  "PATCH /api/v1/users/me": {
4861
- "status": "planned",
4862
- "reason": "team management subroutes beyond list/invite"
4863
+ "status": "excluded",
4864
+ "reason": "a signed-in person's own profile edit (name, phone, birthday, ringtone, password): session only (assertSessionOnly), never an API key's; GET /users/me is covered by conduyt_get_my_profile"
4863
4865
  },
4864
4866
  "GET /api/v1/users/me/tour": {
4865
4867
  "status": "planned",