@adrata/adrata-mcp 1.0.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.
Files changed (41) hide show
  1. package/README.md +548 -0
  2. package/access/auth.js +289 -0
  3. package/access/oauth.js +1059 -0
  4. package/access/resource-metadata.js +167 -0
  5. package/access/tiers.js +422 -0
  6. package/analytics.js +634 -0
  7. package/api-bridge.js +499 -0
  8. package/governance/money.js +141 -0
  9. package/output-formatter.js +589 -0
  10. package/package.json +68 -0
  11. package/resources.js +246 -0
  12. package/security.js +690 -0
  13. package/server.js +2139 -0
  14. package/server.json +55 -0
  15. package/skills/backlog-triage/SKILL.md +115 -0
  16. package/skills/board-review/SKILL.md +96 -0
  17. package/skills/incident-to-card/SKILL.md +126 -0
  18. package/skills/log-outreach.md +62 -0
  19. package/skills/ship-the-card/SKILL.md +155 -0
  20. package/tool-annotations.js +269 -0
  21. package/tools/billing.js +149 -0
  22. package/tools/email-tools.js +652 -0
  23. package/tools/enterprise-tools.js +651 -0
  24. package/tools/free-search.js +160 -0
  25. package/tools/memory.js +440 -0
  26. package/tools/morning-brief.js +551 -0
  27. package/tools/paper-tools.js +563 -0
  28. package/tools/scheduling.js +322 -0
  29. package/tools/work-board-tools.js +758 -0
  30. package/toolsets/communications.js +276 -0
  31. package/toolsets/crm.js +495 -0
  32. package/toolsets/extensibility.js +1131 -0
  33. package/toolsets/infrastructure.js +757 -0
  34. package/toolsets/intelligence.js +232 -0
  35. package/toolsets/knowledge.js +154 -0
  36. package/toolsets/matrix.js +217 -0
  37. package/toolsets/outreach.js +432 -0
  38. package/toolsets/prospecting.js +314 -0
  39. package/toolsets/revenue/always-loaded.js +341 -0
  40. package/toolsets/revenue/sloan-tools.js +81 -0
  41. package/transport-http.js +505 -0
@@ -0,0 +1,269 @@
1
+ /**
2
+ * Honest MCP tool annotations + per-domain scoping for the Adrata MCP server.
3
+ *
4
+ * Two jobs:
5
+ * 1. Annotate every exposed tool with the MCP `ToolAnnotations` behaviour
6
+ * hints (readOnlyHint / destructiveHint / idempotentHint / openWorldHint)
7
+ * so a host (Claude, Cursor, ...) can truthfully decide what needs
8
+ * confirmation. These hints are advisory per the MCP spec and MUST be
9
+ * treated as untrusted by clients — we derive them from the tool's real
10
+ * behaviour, never from a model-supplied description.
11
+ * 2. Tag each tool with a domain "pack" (crm, email, calendar, enrichment,
12
+ * ...) so a client can request least-privilege scopes. When
13
+ * ADRATA_MCP_ENABLED_DOMAINS is set, tools outside the enabled packs are
14
+ * refused at dispatch — a lightweight scoped-tool-pack enforcement that
15
+ * avoids shipping N separate servers.
16
+ *
17
+ * Pure/stateless so it is unit-testable.
18
+ */
19
+
20
+ // ---------------------------------------------------------------------------
21
+ // Domain packs
22
+ // ---------------------------------------------------------------------------
23
+
24
+ /**
25
+ * Explicit tool -> domain overrides. Anything not listed falls back to the
26
+ * keyword/prefix heuristic in getToolDomain().
27
+ */
28
+ const DOMAIN_OVERRIDES = {
29
+ // Meta / workspace lifecycle — always available regardless of scope.
30
+ connect_workspace: 'workspace',
31
+ disconnect_workspace: 'workspace',
32
+ workspace_status: 'workspace',
33
+ list_workspaces: 'workspace',
34
+ switch_workspace: 'workspace',
35
+ get_workspace_settings: 'workspace',
36
+ list_toolsets: 'workspace',
37
+ enable_toolset: 'workspace',
38
+ who_am_i: 'workspace',
39
+ get_current_user: 'workspace',
40
+
41
+ // Governed API bridge.
42
+ adrata_api_catalog: 'bridge',
43
+ adrata_api_request: 'bridge',
44
+ adrata_ai_tool_catalog: 'bridge',
45
+ adrata_ai_tool_execute: 'bridge',
46
+
47
+ // Enrichment / external intelligence.
48
+ enrich_company: 'enrichment',
49
+ enrich_person: 'enrichment',
50
+ get_intent_signals: 'enrichment',
51
+ list_customer_signals: 'enrichment',
52
+ get_competitor_intel: 'enrichment',
53
+ get_company_firmographics: 'enrichment',
54
+ score_company_icp: 'intelligence',
55
+ list_icp_profiles: 'intelligence',
56
+ rank_companies_by_icp: 'intelligence',
57
+ rank_people_by_icp: 'intelligence',
58
+ get_icp_distribution: 'intelligence',
59
+ get_deal_authority: 'intelligence',
60
+ rank_paths_to_power: 'intelligence',
61
+ recommend_deal_move: 'intelligence',
62
+ get_account_read: 'intelligence',
63
+ build_pursuit_command_center: 'intelligence',
64
+ get_pipeline_metrics: 'intelligence',
65
+ get_forecast_data: 'intelligence',
66
+ get_speedrun_list: 'intelligence',
67
+ get_priority_pursuits: 'intelligence',
68
+ morning_brief: 'intelligence',
69
+
70
+ // Networking / warm intros.
71
+ find_intro_path: 'network',
72
+ get_network_stats: 'network',
73
+ create_intro_request: 'network',
74
+ update_intro_request: 'network',
75
+ list_intro_requests: 'network',
76
+ get_intro_pipeline: 'network',
77
+
78
+ // Meetings / calendar.
79
+ schedule_demo: 'calendar',
80
+ schedule_meeting: 'calendar',
81
+ get_demo_availability: 'calendar',
82
+ list_meetings: 'meetings',
83
+ get_meeting: 'meetings',
84
+ get_meeting_summary: 'meetings',
85
+ get_meeting_action_items: 'meetings',
86
+
87
+ // Memory.
88
+ save_memory: 'memory',
89
+ recall: 'memory',
90
+ forget: 'memory',
91
+
92
+ // Billing.
93
+ upgrade_account: 'billing',
94
+ check_subscription: 'billing',
95
+
96
+ // Analytics.
97
+ get_mcp_analytics: 'analytics',
98
+ track_conversion: 'analytics',
99
+
100
+ // Finance: the customer's own ERP invoices / installed base. Its own pack so
101
+ // a least-privilege scope can grant CRM reads without granting revenue data.
102
+ // get_company_invoices would otherwise fall through to the 'crm' keyword rule.
103
+ get_installed_base: 'finance',
104
+ get_company_invoices: 'finance',
105
+
106
+ // Adrata Cloud: the bitemporal record substrate read.
107
+ get_cloud_records: 'cloud',
108
+ };
109
+
110
+ const DOMAIN_PREFIX_RULES = [
111
+ [/^(create_email_account|list_email_accounts|warmup_email|get_email_health)/, 'infra'],
112
+ [/^(search_domains|purchase_domain|setup_domain|verify_domain|list_domains)/, 'infra'],
113
+ [/^(create_sequence|list_sequences|get_sequence|add_sequence_step|activate_sequence|pause_sequence|get_sequence_analytics|enroll_contacts|list_campaigns|get_campaign)/, 'sequences'],
114
+ [/^(search_emails|get_email|count_emails|check_inbox|log_interaction)/, 'email'],
115
+ [/webhook/, 'webhooks'],
116
+ [/(buyer_group)/, 'buyergroup'],
117
+ [/^(create_action|update_action|complete_action|delete_action|get_action|list_actions|list_overdue_actions|list_today_actions|get_activity_summary)/, 'actions'],
118
+ [/note/, 'notes'],
119
+ [/opportunit|move_pipeline_card/, 'pipeline'],
120
+ [/agent_task/, 'agent'],
121
+ [/(external_pipeline|external_companies|import_external|check_batch_import|bulk_import|export_data)/, 'admin'],
122
+ [/(custom_field|describe_fields|manage_data|manage_workspace)/, 'admin'],
123
+ [/(company|companies)/, 'crm'],
124
+ [/(person|people|lead|contact)/, 'crm'],
125
+ [/^search/, 'search'],
126
+ [/^find/, 'search'],
127
+ ];
128
+
129
+ /** Domains that are always callable regardless of ADRATA_MCP_ENABLED_DOMAINS. */
130
+ const ALWAYS_ENABLED_DOMAINS = new Set(['workspace', 'billing']);
131
+
132
+ /** Resolve the domain pack for a tool. */
133
+ export function getToolDomain(name) {
134
+ if (DOMAIN_OVERRIDES[name]) return DOMAIN_OVERRIDES[name];
135
+ for (const [re, domain] of DOMAIN_PREFIX_RULES) {
136
+ if (re.test(name)) return domain;
137
+ }
138
+ return 'general';
139
+ }
140
+
141
+ /**
142
+ * Enforce per-domain scoping. Returns { allowed } or { allowed:false, reason }.
143
+ * Enabled only when ADRATA_MCP_ENABLED_DOMAINS is set (comma list of packs).
144
+ */
145
+ export function checkDomainScope(name) {
146
+ const raw = process.env.ADRATA_MCP_ENABLED_DOMAINS;
147
+ if (!raw) return { allowed: true };
148
+ const enabled = new Set(raw.split(',').map(s => s.trim()).filter(Boolean));
149
+ const domain = getToolDomain(name);
150
+ if (ALWAYS_ENABLED_DOMAINS.has(domain) || enabled.has(domain)) return { allowed: true };
151
+ return { allowed: false, reason: 'domain_not_in_scope', domain, enabled: [...enabled] };
152
+ }
153
+
154
+ // ---------------------------------------------------------------------------
155
+ // Behaviour classification (readOnly / destructive / idempotent / openWorld)
156
+ // ---------------------------------------------------------------------------
157
+
158
+ // Tools that mutate but do not read (idempotent updates vs. creates vs. deletes).
159
+ //
160
+ // Destructive is derived from a PATTERN first, so every future `delete_*`,
161
+ // `bulk_delete_*`, `purchase_*` (irreversible spend) or `*send_email`
162
+ // (irreversible external effect) tool is destructive by construction. The
163
+ // explicit set below covers names the pattern cannot see (mid-name deletes
164
+ // like paper_delete_document, revokes, composite manage_* delete actions).
165
+ const DESTRUCTIVE_PATTERN = /^(delete_|bulk_delete_|purchase_)|send_email$/;
166
+
167
+ const DESTRUCTIVE = new Set([
168
+ 'delete_company', 'delete_person', 'delete_opportunity', 'delete_action',
169
+ 'delete_note', 'delete_webhook', 'disconnect_workspace', 'forget',
170
+ // Buyer rooms. delete_buyer_group cascades to the room's member seats, and
171
+ // bulk_delete_buyer_groups does that for up to 50 rooms in one call.
172
+ 'delete_buyer_group', 'bulk_delete_buyer_groups', 'remove_buyer_group_member',
173
+ // Paper: mid-name delete + share revocation (cuts off external access).
174
+ 'paper_delete_document', 'paper_revoke_share',
175
+ // The composite manage_* tools each expose a `delete` action. They were
176
+ // previously unclassified, so isReadOnly() fell through to a false
177
+ // destructiveHint — a host would not have confirmation-gated a delete.
178
+ 'manage_company', 'manage_person', 'manage_opportunity', 'manage_activity',
179
+ 'manage_buyer_group',
180
+ ]);
181
+
182
+ /** True when a tool is destructive (irreversible delete, spend, or send). */
183
+ export function isDestructive(name) {
184
+ return DESTRUCTIVE.has(name) || DESTRUCTIVE_PATTERN.test(name);
185
+ }
186
+
187
+ // Non-read tools that are safe to retry (PATCH/PUT semantics, toggles).
188
+ const IDEMPOTENT_WRITES = new Set([
189
+ 'update_company', 'update_person', 'update_opportunity', 'update_action',
190
+ 'update_note', 'update_webhook', 'update_intro_request', 'complete_action',
191
+ 'pause_sequence', 'activate_sequence', 'verify_domain', 'move_pipeline_card',
192
+ 'add_to_company_list', 'find_or_create_person', 'find_or_create_company',
193
+ 'save_memory', 'manage_custom_fields', 'build_pursuit_command_center',
194
+ 'update_buyer_group', 'update_buyer_group_member',
195
+ ]);
196
+
197
+ // Non-read tools that create new state / have side effects each call.
198
+ const NON_IDEMPOTENT_WRITES = new Set([
199
+ 'create_company', 'create_person', 'create_opportunity', 'create_action',
200
+ 'create_note', 'create_webhook', 'create_buyer_group', 'add_buyer_group_member',
201
+ 'create_company_list', 'create_intro_request', 'create_sequence',
202
+ 'add_sequence_step', 'enroll_contacts', 'create_email_account',
203
+ 'create_agent_task', 'cancel_agent_task', 'bulk_import', 'import_external_companies',
204
+ 'import_external_pipeline_members', 'purchase_domain', 'setup_domain',
205
+ 'warmup_email', 'schedule_demo', 'schedule_meeting', 'connect_workspace',
206
+ 'enable_toolset', 'track_conversion', 'log_interaction', 'test_webhook',
207
+ 'replay_webhook_delivery', 'upgrade_account',
208
+ ]);
209
+
210
+ // Tools that reach beyond the workspace (external enrichment, web, providers).
211
+ const OPEN_WORLD = new Set([
212
+ 'enrich_company', 'enrich_person', 'get_intent_signals', 'get_competitor_intel',
213
+ 'get_company_firmographics', 'search_domains', 'purchase_domain',
214
+ 'find_intro_path', 'search_leads', 'get_demo_availability', 'schedule_demo',
215
+ ]);
216
+
217
+ /** True when a tool only reads state. */
218
+ function isReadOnly(name) {
219
+ if (isDestructive(name) || IDEMPOTENT_WRITES.has(name) || NON_IDEMPOTENT_WRITES.has(name)) {
220
+ return false;
221
+ }
222
+ return /^(get_|list_|search_|find_(?!or_create)|count_|describe_|check_|inspect_|rank_|score_|who_am_i|recall|workspace_status|morning_brief|export_data)/.test(name)
223
+ || name.endsWith('_catalog')
224
+ || name.endsWith('_analytics')
225
+ || name.endsWith('_status');
226
+ }
227
+
228
+ /**
229
+ * Build honest MCP annotations for a tool. Derived only from the tool's real
230
+ * behaviour class + domain — NEVER from an untrusted description string.
231
+ *
232
+ * @param {string} name
233
+ * @param {string} [description]
234
+ * @returns {{ title:string, readOnlyHint:boolean, destructiveHint:boolean,
235
+ * idempotentHint:boolean, openWorldHint:boolean, domain:string,
236
+ * requiresConfirmation:boolean }}
237
+ */
238
+ export function buildToolAnnotations(name) {
239
+ const domain = getToolDomain(name);
240
+ const readOnly = isReadOnly(name);
241
+ const destructive = isDestructive(name);
242
+ // Deletes/revokes are retry-safe; purchases and email sends are destructive
243
+ // but NOT idempotent — retrying re-spends money or re-sends the email.
244
+ const nonRetryable = NON_IDEMPOTENT_WRITES.has(name) || /^purchase_|send_email$/.test(name);
245
+ const idempotent = readOnly
246
+ || IDEMPOTENT_WRITES.has(name)
247
+ || (destructive && !nonRetryable);
248
+ const openWorld = OPEN_WORLD.has(name);
249
+
250
+ return {
251
+ title: titleFor(name),
252
+ readOnlyHint: readOnly,
253
+ destructiveHint: destructive,
254
+ idempotentHint: idempotent,
255
+ openWorldHint: openWorld,
256
+ // Adrata extensions (allowed additional properties on ToolAnnotations).
257
+ domain,
258
+ // Write/destructive tools should be confirmation-gated by the host and are
259
+ // additionally tier-gated + audit-logged server-side.
260
+ requiresConfirmation: !readOnly,
261
+ };
262
+ }
263
+
264
+ function titleFor(name) {
265
+ return name
266
+ .split('_')
267
+ .map(w => (w ? w[0].toUpperCase() + w.slice(1) : w))
268
+ .join(' ');
269
+ }
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Billing tools for Adrata MCP Server.
3
+ *
4
+ * Provides two tools:
5
+ * - upgrade_account: Opens browser to Adrata Edition signup page
6
+ * - check_subscription: Returns current plan, usage, and trial status
7
+ */
8
+
9
+ import { z } from 'zod';
10
+ import { execFile } from 'node:child_process';
11
+
12
+ const SIGNUP_URL = 'https://adrata.com/edition/signup';
13
+
14
+ function openBrowser(url) {
15
+ if (process.platform === 'darwin') execFile('open', [url], () => {});
16
+ else if (process.platform === 'win32') {
17
+ execFile('rundll32.exe', ['url.dll,FileProtocolHandler', url], () => {});
18
+ } else execFile('xdg-open', [url], () => {});
19
+ }
20
+
21
+ /**
22
+ * Register billing tools on the MCP server instance.
23
+ *
24
+ * @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server
25
+ * @param {object} options
26
+ * @param {Function} options.api - HTTP helper for API calls
27
+ * @param {Function} options.ok - Response formatter
28
+ * @param {object} options.auth - Authentication context from authenticate()
29
+ */
30
+ export function registerBillingTools(server, { api, ok, auth }) {
31
+ // --------------------------------------------------------------------------
32
+ // upgrade_account — opens browser to signup/upgrade page
33
+ // --------------------------------------------------------------------------
34
+ server.tool(
35
+ 'upgrade_account',
36
+ 'Open the Adrata Edition signup page in your browser to upgrade your account. ' +
37
+ 'Free users get a 14-day Pro trial (no credit card required). ' +
38
+ 'Pro users can manage their subscription. ' +
39
+ 'Enterprise users are directed to schedule a demo.',
40
+ {
41
+ plan: z.enum(['pro', 'enterprise']).optional()
42
+ .describe('Target plan: "pro" for self-serve upgrade, "enterprise" for sales contact. Defaults to "pro".'),
43
+ },
44
+ async (args) => {
45
+ const targetPlan = args.plan || 'pro';
46
+
47
+ if (targetPlan === 'enterprise') {
48
+ return ok({
49
+ action: 'schedule_demo',
50
+ message: 'Enterprise plans require a conversation with our team. Schedule a demo to get started.',
51
+ url: 'https://adrata.com/demo',
52
+ calendar: 'https://cal.com/adrata/enterprise-demo',
53
+ next_steps: [
54
+ 'Click the demo link above to schedule a call',
55
+ 'Our team will customize a plan for your organization',
56
+ 'Enterprise includes SSO, custom integrations, and dedicated support',
57
+ ],
58
+ });
59
+ }
60
+
61
+ // Pro upgrade / signup
62
+ const url = auth.authenticated
63
+ ? `${SIGNUP_URL}?upgrade=true`
64
+ : SIGNUP_URL;
65
+
66
+ // Attempt to open browser
67
+ let browserOpened = false;
68
+ try {
69
+ openBrowser(url);
70
+ browserOpened = true;
71
+ } catch {
72
+ browserOpened = false;
73
+ }
74
+
75
+ return ok({
76
+ action: 'upgrade_to_pro',
77
+ url,
78
+ browser_opened: browserOpened,
79
+ message: browserOpened
80
+ ? 'Opening signup page in your browser...'
81
+ : `Open this URL to sign up: ${url}`,
82
+ plan: {
83
+ name: 'Adrata Pro',
84
+ price: '$2,000/seat/year',
85
+ trial: '14-day free trial, no credit card required',
86
+ features: [
87
+ 'Full company and contact intelligence',
88
+ 'Buying intent signals and deal authority mapping',
89
+ 'Pipeline analytics and revenue forecasting',
90
+ 'Competitive intelligence briefs',
91
+ 'Priority Pursuits ranked daily action lists',
92
+ 'Meeting summaries and action items',
93
+ 'AI-powered enrichment and scoring',
94
+ ],
95
+ },
96
+ });
97
+ }
98
+ );
99
+
100
+ // --------------------------------------------------------------------------
101
+ // check_subscription — returns current plan, usage, and trial status
102
+ // --------------------------------------------------------------------------
103
+ server.tool(
104
+ 'check_subscription',
105
+ 'Check your current Adrata subscription plan, usage limits, trial status, and billing details. ' +
106
+ 'Works at all tiers: free users see upgrade options, pro users see usage and trial countdown, ' +
107
+ 'enterprise users see full workspace subscription details.',
108
+ {},
109
+ async () => {
110
+ // Free tier — no API call possible
111
+ if (!auth.authenticated) {
112
+ return ok({
113
+ plan: 'free',
114
+ status: 'active',
115
+ usage: {
116
+ tools_available: 'Search and email tools only',
117
+ tools_used_today: 'N/A (no tracking in free tier)',
118
+ },
119
+ upgrade: {
120
+ message: 'Upgrade to Pro for full intelligence and analytics.',
121
+ url: SIGNUP_URL,
122
+ trial: '14-day free trial, no credit card required',
123
+ },
124
+ });
125
+ }
126
+
127
+ // Pro or Enterprise — fetch subscription from API
128
+ try {
129
+ const data = await api('GET', '/api/v1/mcp/subscription');
130
+ return ok({
131
+ plan: data.plan || auth.tier,
132
+ status: data.status || 'active',
133
+ trial: data.trial || null,
134
+ usage: data.usage || null,
135
+ billing: data.billing || null,
136
+ manage_url: data.manage_url || null,
137
+ });
138
+ } catch (err) {
139
+ // Fallback if endpoint not available yet
140
+ return ok({
141
+ plan: auth.tier,
142
+ status: 'active',
143
+ message: 'Subscription details are available in your Adrata dashboard.',
144
+ dashboard_url: 'https://app.adrata.com/settings/billing',
145
+ });
146
+ }
147
+ }
148
+ );
149
+ }