@adrata/adrata-mcp 1.0.46 → 1.0.49

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adrata/adrata-mcp",
3
- "version": "1.0.46",
3
+ "version": "1.0.49",
4
4
  "description": "Adrata MCP Server \u2014 connect Claude Code, Codex, Gemini, Cursor, and other AI tools to your CRM. About 275 tools registered at startup for companies, people, deals, actions, buyer groups, warm intros, webhooks and intelligence, plus 65 more behind eight named toolsets you load with enable_toolset.",
5
5
  "type": "module",
6
6
  "main": "server.js",
package/server.js CHANGED
@@ -1828,14 +1828,30 @@ server.tool('get_company_firmographics', 'Get employee count and revenue metadat
1828
1828
  { companyId: z.string() },
1829
1829
  async (a) => ok(await api('GET', `/api/v1/companies/${a.companyId}/firmographics`)));
1830
1830
 
1831
- server.tool('enrich_person', `Trigger enrichment for a person professional info, social profiles. SPENDS VENDOR CREDITS (a Coresignal collect is 20 credits per person).${governedWriteNote('write:people')}`,
1832
- { personId: z.string(), ...governedWriteArgs },
1833
- async ({ personId, ...g }) => runGovernedWrite(
1831
+ // `enrichPhone` and `forceRefresh` reach the route ONLY through the request
1832
+ // body, and this tool used to send none at all — no `body` key, so the POST went
1833
+ // out empty and the route's `enrich_phone` fell to its `unwrap_or(false)`
1834
+ // default on every call. The flag was therefore unreachable from MCP: an agent
1835
+ // could pass it, the schema would accept it, and no vendor was ever asked for a
1836
+ // phone. That is the "capability that nothing can reach" shape, and it is the
1837
+ // reason phone was never requested on any of the async jobs in production.
1838
+ server.tool('enrich_person', `Trigger enrichment for a person — email, phone, professional info, social profiles. SPENDS VENDOR CREDITS. Phone is a separate paid field (a BetterContact mobile is 10 credits against 1 for an email), so pass enrichPhone: true whenever the ask is for a mobile, cell, direct line or "contact details" — without it no vendor is asked for a number and the run still reports success.${governedWriteNote('write:people')}`,
1839
+ {
1840
+ personId: z.string(),
1841
+ enrichPhone: z.boolean().optional().describe('Also look up a mobile/direct phone. Paid field, explicit opt-in, defaults false.'),
1842
+ forceRefresh: z.boolean().optional().describe('Run even when fields are already populated; only for an explicitly stale or wrong record.'),
1843
+ ...governedWriteArgs,
1844
+ },
1845
+ async ({ personId, enrichPhone, forceRefresh, ...g }) => runGovernedWrite(
1834
1846
  g,
1835
1847
  {
1836
1848
  method: 'POST',
1837
1849
  path: `/api/v1/people/${personId}/enrich`,
1838
- preview: { entity: 'person', entityId: personId, operation: 'enrich', spendsVendorCredits: true },
1850
+ body: body({ enrichPhone, forceRefresh }),
1851
+ preview: {
1852
+ entity: 'person', entityId: personId, operation: 'enrich',
1853
+ spendsVendorCredits: true, enrichPhone: enrichPhone === true,
1854
+ },
1839
1855
  },
1840
1856
  ));
1841
1857
 
package/server.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "name": "com.adrata/adrata-mcp",
4
4
  "description": "Adrata revenue-intelligence MCP server: companies, people, opportunities, actions, buyer groups, enrichment, email, and workspace operations for AI agents.",
5
5
  "status": "active",
6
- "version": "1.0.46",
6
+ "version": "1.0.49",
7
7
  "websiteUrl": "https://adrata.com/developers",
8
8
  "repository": {
9
9
  "url": "https://github.com/adrata/adrata",
@@ -15,7 +15,7 @@
15
15
  "registryType": "npm",
16
16
  "registryBaseUrl": "https://registry.npmjs.org",
17
17
  "identifier": "@adrata/adrata-mcp",
18
- "version": "1.0.46",
18
+ "version": "1.0.49",
19
19
  "transport": {
20
20
  "type": "stdio"
21
21
  },
@@ -392,6 +392,42 @@ export function validateOnboardingArgs(args = {}) {
392
392
  return problems;
393
393
  }
394
394
 
395
+ /**
396
+ * Whether the adaptive payload (launch, preparedAccountIds, preparedNote,
397
+ * source) is durable, read FROM the invite response rather than assumed. The
398
+ * route returns `recorded` alongside `inviteUrl`: `true` means the
399
+ * workspace_invitation_records row exists; `false` means the link works but
400
+ * the record was not written. An absent `recorded` is reported as unknown —
401
+ * `null` — rather than asserted either way.
402
+ */
403
+ export function describeAdaptivePayloadDurability(payload = {}) {
404
+ const recorded = payload?.recorded;
405
+ if (recorded === true) {
406
+ return {
407
+ adaptivePayloadDurable: true,
408
+ adaptivePayloadNote:
409
+ 'The seat and the link are live, and the launch, preparedAccountIds, preparedNote and source'
410
+ + ' were stored with the invitation (recorded: true).',
411
+ };
412
+ }
413
+ if (recorded === false) {
414
+ return {
415
+ adaptivePayloadDurable: false,
416
+ adaptivePayloadNote:
417
+ 'The seat and the link are live, but the endpoint reported recorded: false — the invitation'
418
+ + ' record carrying launch, preparedAccountIds, preparedNote and source was not written, so it'
419
+ + ' will not appear in the issued list and cannot be revoked from it.',
420
+ };
421
+ }
422
+ return {
423
+ adaptivePayloadDurable: null,
424
+ adaptivePayloadNote:
425
+ 'The seat and the link are live. The endpoint did not confirm whether the invitation record'
426
+ + ' carrying launch, preparedAccountIds, preparedNote and source was written (no `recorded`'
427
+ + ' field in the response), so durability is unknown rather than asserted.',
428
+ };
429
+ }
430
+
395
431
  /** The exact body this tool would send. Kept in one place so preview and live write cannot drift. */
396
432
  export function buildInvitationBody(args = {}) {
397
433
  const body = {
@@ -404,19 +440,19 @@ export function buildInvitationBody(args = {}) {
404
440
  if (args.managerId) body.managerId = args.managerId;
405
441
 
406
442
  // ── The adaptive payload ────────────────────────────────────────────────
407
- // INTEGRATION POINT (2026-09-06): these four fields are sent and the
408
- // DEPLOYED endpoint ignores them. `InviteMemberRequest` in
409
- // `code/api/src/routes/v1/entities/org/workspace/members.rs` declares
410
- // exactly `email`, `role` and `manager_id`; serde drops unknown fields
411
- // silently, so the call succeeds, a link is minted, and the launch is
412
- // simply not recorded. They are sent anyway rather than withheld, because
413
- // the route change carrying `InvitationContext` is in flight on
414
- // `feature/onboarding-grant-record` and the wire shape should not need a
415
- // second edit when it lands.
443
+ // These four fields are stored by the deployed endpoint. `InviteMemberRequest`
444
+ // in `code/api/src/routes/v1/entities/org/workspace/members.rs` declares
445
+ // `launch`, `prepared_account_ids`, `prepared_note` and `source` with
446
+ // `#[serde(default)]` (camelCase via `alias`), and `mint_workspace_invitation`
447
+ // in `invitations.rs` binds them into `workspace_invitation_records`. The
448
+ // route answers with `recorded: true|false` saying whether that durable row
449
+ // was written, and the live-write result below reports durability FROM that
450
+ // field rather than from an assumption about what the server does.
416
451
  //
417
- // What this means for a caller TODAY, and it is stated in the tool's own
418
- // response rather than only here: the seat and the link are real, and the
419
- // adaptive configuration is not yet durable.
452
+ // (History: until 2026-09-06 the endpoint accepted only email, role and
453
+ // managerId and dropped the rest; the tool then said so in its response.
454
+ // That note outlived the fix and told an AE a stored configuration was not
455
+ // durable — hence the rule above: report what the response says.)
420
456
  //
421
457
  // Note also that `InviteMemberRequest` carries no
422
458
  // `#[serde(rename_all = "camelCase")]` — `manager_id` works only because it
@@ -625,12 +661,12 @@ export function registerProvisioningTools(
625
661
  preparedAccountIds: body.preparedAccountIds ?? [],
626
662
  preparedAccountCount: body.preparedAccountIds?.length ?? 0,
627
663
  preparedNote: body.preparedNote ?? null,
628
- adaptivePayloadDurable: false,
664
+ adaptivePayloadDurable: null,
629
665
  adaptivePayloadNote:
630
- 'INTEGRATION POINT: the deployed invite endpoint accepts email, role and managerId only, so'
631
- + ' launch, preparedAccountIds, preparedNote and source are sent but not yet stored. The seat'
632
- + ' and the link are real; the adaptive configuration becomes durable when the'
633
- + ' workspace_invitation_records route change ships.',
666
+ 'Not known until minted. The invite endpoint stores launch, preparedAccountIds, preparedNote'
667
+ + ' and source with the invitation (members.rs InviteMemberRequest → invitations.rs'
668
+ + ' mint_workspace_invitation) and reports `recorded` on the live write; the live result'
669
+ + ' carries that answer.',
634
670
  preview: validation,
635
671
  body,
636
672
  note:
@@ -712,10 +748,7 @@ export function registerProvisioningTools(
712
748
  inviteUrl,
713
749
  launch: body.launch ?? null,
714
750
  preparedAccountIds: body.preparedAccountIds ?? [],
715
- adaptivePayloadDurable: false,
716
- adaptivePayloadNote:
717
- 'The seat and the link are live. launch/preparedAccountIds/preparedNote were sent but the'
718
- + ' deployed endpoint does not yet store them — see the integration point in tools/provisioning.js.',
751
+ ...describeAdaptivePayloadDurability(payload),
719
752
  credentialWarning:
720
753
  'inviteUrl is a credential: anyone holding it can seat themselves in this workspace. Deliver it'
721
754
  + ' over a trusted channel and do not paste it into a log, a ticket, or a shared document.',