@loopops/mcp-server 3.39.0 → 3.41.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.
@@ -371,6 +371,7 @@ export function registerConfigTools(server, allowed) {
371
371
  if (allowed.has("preview_routing")) {
372
372
  server.tool("preview_routing", "Simulate Route loop resolution for a hypothetical lead. Walks config/route/rules.yaml, dispatches to territory/pool/queue/user/target_account handler, and returns the resolved owner + audit trail. Useful for testing rule changes before merging. Requires at least billing geography or email.", {
373
373
  email: z.string().optional().describe("Lead email — also extracts the domain for target_account lookup."),
374
+ company_name: z.string().optional().describe("Lead.Company. Drives Account Master fuzzy-name matching."),
374
375
  billing_country: z.string().optional().describe("ISO country code (US, GB, JP, etc.)."),
375
376
  billing_state: z.string().optional().describe("ISO state code (CA, NY, etc.)."),
376
377
  billing_city: z.string().optional().describe("City name."),
@@ -379,6 +380,24 @@ export function registerConfigTools(server, allowed) {
379
380
  source: z.string().optional().describe("Lead source (Web, Partner Referral, etc.)."),
380
381
  }, safeTool(async (input) => trpcMutation("mcp.previewRouting", input)));
381
382
  }
383
+ if (allowed.has("preview_contact_routing")) {
384
+ server.tool("preview_contact_routing", "Simulate Route loop resolution for a hypothetical Contact. Resolves the account (existing AccountId → Account Master matcher → SOQL domain fallback), walks the territory cascade for an owner, and applies the open-opportunity attach policy (single_only / most_recent / never). Useful for validating contact-routing setup without writing to SF.", {
385
+ email: z.string().optional().describe("Contact email — feeds AM matcher + SOQL fallback when no AccountId."),
386
+ company_name: z.string().optional().describe("Account name or company hint. Drives AM fuzzy_name when no AccountId."),
387
+ account_id: z.string().optional().describe("Existing SF Account.Id, if the contact is already linked. Bypasses matching."),
388
+ billing_country: z.string().optional().describe("ISO country code from Contact.MailingCountry."),
389
+ billing_state: z.string().optional().describe("ISO state code from Contact.MailingState."),
390
+ billing_city: z.string().optional().describe("City from Contact.MailingCity."),
391
+ hierarchy_mode: z
392
+ .enum(["exact", "ultimate_parent", "best_child_by_country"])
393
+ .optional()
394
+ .describe("Hierarchy walk after AM match. Default: exact."),
395
+ opp_attach_policy: z
396
+ .enum(["single_only", "most_recent", "never"])
397
+ .optional()
398
+ .describe("Open-opp attach policy. Default: most_recent (LeanData default)."),
399
+ }, safeTool(async (input) => trpcMutation("mcp.previewContactRouting", input)));
400
+ }
382
401
  // list_pending_territories + assign_territories retired 2026-05-10.
383
402
  // Account placement now uses the Phase 3 propose_account_assignments
384
403
  // chain (with commit_cycle for directory-shape scenarios).
package/dist/tools/crm.js CHANGED
@@ -7,19 +7,19 @@ export function registerCrmTools(server, allowed) {
7
7
  // view analytics.my_pipeline. Row policies on salesforce.opportunity
8
8
  // enforce per-user scoping (rep: own; manager: team; leadership/ops/eng:
9
9
  // all). Query via the Cloud Remote MCP server (mcp.clickhouse.cloud/mcp).
10
- // See docs/CH-ANALYTICS-VIEWS.md.
10
+ // See docs/data/analytics-views.md.
11
11
  // my_leads retired 2026-05-06 — replaced by ClickHouse view
12
12
  // analytics.my_leads. Row policies on salesforce.lead enforce per-user
13
- // scoping. Filter by created_date in the caller. See docs/CH-ANALYTICS-VIEWS.md.
13
+ // scoping. Filter by created_date in the caller. See docs/data/analytics-views.md.
14
14
  // pipeline_health retired 2026-05-06 — replaced by ClickHouse views
15
15
  // analytics.pipeline_by_stage + analytics.win_rate(range_days).
16
- // See docs/CH-ANALYTICS-VIEWS.md.
16
+ // See docs/data/analytics-views.md.
17
17
  // rep_performance retired 2026-05-06 — replaced by ClickHouse view
18
- // analytics.rep_performance(range_days). See docs/CH-ANALYTICS-VIEWS.md.
18
+ // analytics.rep_performance(range_days). See docs/data/analytics-views.md.
19
19
  // forecast retired 2026-05-06 — replaced by ClickHouse view
20
- // analytics.forecast(quarter_start, quarter_end). See docs/CH-ANALYTICS-VIEWS.md.
20
+ // analytics.forecast(quarter_start, quarter_end). See docs/data/analytics-views.md.
21
21
  // update_opportunity / update_lead removed 2026-05-09 (Phase 2 of
22
- // docs/SALESFORCE-MCP-PLAN.md). Opportunity + Lead writes go through
22
+ // docs/plans/salesforce-mcp.md). Opportunity + Lead writes go through
23
23
  // the `salesforce-hosted` MCP for every Loop role: per-user OAuth+PKCE
24
24
  // preserves LastModifiedById; sharing rules + FLS + validation rules
25
25
  // enforce server-side. Use mcp__salesforce-hosted__updateSobjectRecord.
@@ -4,7 +4,7 @@
4
4
  * Phase 1.2 (this file) ships the first user-placement tool:
5
5
  * propose_user_assignments — run the Python placement model on Modal,
6
6
  * persist proposals against an open
7
- * planning_cycle. See docs/DEPLOY-PLAN.md.
7
+ * planning_cycle. See docs/plans/deploy.md.
8
8
  *
9
9
  * The review/edit/approve/commit triplet (Phase 1.3–1.5) lands here too
10
10
  * as it ships. Quota + account placement (Phases 2–3) get separate
@@ -4,7 +4,7 @@
4
4
  * Phase 1.2 (this file) ships the first user-placement tool:
5
5
  * propose_user_assignments — run the Python placement model on Modal,
6
6
  * persist proposals against an open
7
- * planning_cycle. See docs/DEPLOY-PLAN.md.
7
+ * planning_cycle. See docs/plans/deploy.md.
8
8
  *
9
9
  * The review/edit/approve/commit triplet (Phase 1.3–1.5) lands here too
10
10
  * as it ships. Quota + account placement (Phases 2–3) get separate
@@ -455,13 +455,18 @@ export function registerDeployTools(server, allowed) {
455
455
  }
456
456
  if (allowed.has("propose_account_assignments")) {
457
457
  server.tool("propose_account_assignments", [
458
- "Ops/eng. Run the Phase 3 account-placement model for an open",
459
- "planning cycle. Continuity-preservation policy from",
460
- "config/deploy/account_placement.yaml is honored unless overridden.",
461
- "Three refusal gates: (1) Phase 1 (user → territory) must be fully",
462
- "committed/superseded; (2) capacity_config.active_scenario must",
463
- "be set; (3) a planning scope snapshot must exist for the cycle",
464
- "(run snapshot_planning_scope first). Default behavior: accounts",
458
+ "Manager+. Run the Phase 3 account-placement model for an open",
459
+ "planning cycle. Scope-aware: managers re-run for accounts in",
460
+ "their hierarchy subtree (sync; persist scoped to their accounts;",
461
+ "approved + committed rows preserved across the run). Ops/eng",
462
+ "run org-wide (async-by-default; poll via",
463
+ "propose_account_assignments_status). Continuity-preservation",
464
+ "policy from config/deploy/account_placement.yaml is honored",
465
+ "unless overridden. Three refusal gates: (1) Phase 1 (user →",
466
+ "territory) must be fully committed/superseded; (2)",
467
+ "capacity_config.active_scenario must be set; (3) a planning",
468
+ "scope snapshot must exist for the cycle (run",
469
+ "snapshot_planning_scope first). Default behavior: accounts",
465
470
  "with active current owners auto-ratify their current territory",
466
471
  "(continuity); only new or owner-departed accounts go through",
467
472
  "full match-rule + composition + balance modeling. Pass",
@@ -472,7 +477,7 @@ export function registerDeployTools(server, allowed) {
472
477
  preserveCurrentOwnershipOverride: z.boolean().optional().describe("Override config/deploy/account_placement.yaml's preserve_current_ownership policy."),
473
478
  accountIds: z.array(z.string().uuid()).optional().describe("Optional restriction; only model these accounts."),
474
479
  scenarioId: z.string().optional().describe("Override active scenario."),
475
- async: z.boolean().optional().describe("Run the placement model as a Modal background job (bypasses Vercel's 5-min function ceiling). Returns immediately with a job_id; poll status via propose_account_assignments_status. Recommended for cycles with >500 accounts. Default: false."),
480
+ async: z.boolean().optional().describe("Run the placement model as a Modal background job (bypasses Vercel's 5-min function ceiling). Returns immediately with a job_id; poll status via propose_account_assignments_status. Recommended for cycles with >500 accounts. Default: false. NOTE: silently downgraded to sync for manager calls — async scoping isn't plumbed through the Modal worker yet."),
476
481
  branch: z.string().optional(),
477
482
  }, safeTool(async (input) => trpcMutation("mcp.proposeAccountAssignments", input)));
478
483
  }
package/dist/tools/eng.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { execSync } from "node:child_process";
2
- import { trpcQuery } from "../api-client.js";
2
+ import { z } from "zod";
3
+ import { trpcMutation, trpcQuery } from "../api-client.js";
3
4
  import { safeTool } from "./_helpers.js";
4
5
  import { rangeSchema } from "./_schemas.js";
5
6
  export function registerEngTools(server, allowed) {
@@ -33,4 +34,35 @@ export function registerEngTools(server, allowed) {
33
34
  }
34
35
  }));
35
36
  }
37
+ if (allowed.has("promote_skill_to_tool")) {
38
+ server.tool("promote_skill_to_tool", "Scaffold a new MCP tool from a Claude skill (markdown). Generates Zod schema + tRPC procedure + MCP registration + role-gate entry on a feature branch, then opens a PR. Eng-only. See docs/plans/promote-skill-to-tool.md.", {
39
+ toolName: z
40
+ .string()
41
+ .describe("Snake_case MCP wire name, e.g. 'review_partner_pipeline'."),
42
+ skillContent: z
43
+ .string()
44
+ .describe("Full markdown content of the skill (frontmatter + body)."),
45
+ intendedRole: z
46
+ .enum(["rep", "manager", "leadership", "ops", "eng"])
47
+ .describe("MCP role gate — which role tier should see the tool."),
48
+ implementation: z
49
+ .enum(["agent", "code"])
50
+ .describe("'agent' = procedure delegates to Claude on the server. 'code' = procedure is hand-written TS; skill becomes a doc comment."),
51
+ category: z
52
+ .string()
53
+ .optional()
54
+ .describe("Tool category file under packages/mcp-server/src/tools/. Defaults to 'eng' for eng-tier tools, otherwise 'misc'."),
55
+ proposerEmail: z
56
+ .string()
57
+ .optional()
58
+ .describe("Email of the person who proposed the skill — for PR attribution."),
59
+ }, safeTool(async ({ toolName, skillContent, intendedRole, implementation, category, proposerEmail }) => trpcMutation("mcp.promoteSkillToTool", {
60
+ toolName,
61
+ skillContent,
62
+ intendedRole,
63
+ implementation,
64
+ category,
65
+ proposerEmail,
66
+ })));
67
+ }
36
68
  }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Pursue-loop hybrid analysis tools.
3
3
  *
4
- * Home for hybrid-tools-pattern (docs/HYBRID-TOOLS.md) tools that operate
4
+ * Home for hybrid-tools-pattern (docs/engineering/hybrid-tools.md) tools that operate
5
5
  * on the deal-progression / pipeline-health surface — composing CH +
6
6
  * Salesforce + Postgres reads to deliver deterministic structured analysis
7
7
  * an operator can act on. By rule, these tools are read-only; they emit
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Pursue-loop hybrid analysis tools.
3
3
  *
4
- * Home for hybrid-tools-pattern (docs/HYBRID-TOOLS.md) tools that operate
4
+ * Home for hybrid-tools-pattern (docs/engineering/hybrid-tools.md) tools that operate
5
5
  * on the deal-progression / pipeline-health surface — composing CH +
6
6
  * Salesforce + Postgres reads to deliver deterministic structured analysis
7
7
  * an operator can act on. By rule, these tools are read-only; they emit
@@ -4,11 +4,11 @@ import { rangeSchema, salesforceLeadIdSchema, } from "./_schemas.js";
4
4
  export function registerReportingTools(server, allowed) {
5
5
  // loop_health retired 2026-05-06 — replaced by ClickHouse parameterized
6
6
  // view analytics.loop_health(range_days). Query via the Cloud Remote MCP
7
- // server (mcp.clickhouse.cloud/mcp). See docs/CH-ANALYTICS-VIEWS.md.
7
+ // server (mcp.clickhouse.cloud/mcp). See docs/data/analytics-views.md.
8
8
  // scoring_distribution retired 2026-05-06 — replaced by ClickHouse view
9
- // analytics.scoring_distribution(range_days). See docs/CH-ANALYTICS-VIEWS.md.
9
+ // analytics.scoring_distribution(range_days). See docs/data/analytics-views.md.
10
10
  // routing_report retired 2026-05-06 — replaced by ClickHouse view
11
- // analytics.routing_report(range_days). See docs/CH-ANALYTICS-VIEWS.md.
11
+ // analytics.routing_report(range_days). See docs/data/analytics-views.md.
12
12
  if (allowed.has("enrichment_coverage")) {
13
13
  server.tool("enrichment_coverage", "Analyze lead enrichment coverage: enrichment rate, top industries, headcount distribution.", {
14
14
  range: rangeSchema
@@ -71,7 +71,7 @@ export function registerSfdcSyncTools(server, allowed) {
71
71
  " 4. Add a reason on each approve=yes row (audit log; required, ≥3 chars)",
72
72
  " 5. Run `process_approvals_csv` with the edited CSV content",
73
73
  "",
74
- "See docs/csv-templates/pending-approvals.csv for the canonical shape.",
74
+ "See docs/operations/tools/csv-templates/pending-approvals.csv for the canonical shape.",
75
75
  ].join("\n"), {
76
76
  limit: z
77
77
  .number()
@@ -102,7 +102,7 @@ export function registerSfdcSyncTools(server, allowed) {
102
102
  "the corrected CSV any time; already-approved accounts return 'not_in_review' and are",
103
103
  "reported as failures (idempotent — no double-approval).",
104
104
  "",
105
- "See docs/csv-templates/pending-approvals.csv for the canonical shape.",
105
+ "See docs/operations/tools/csv-templates/pending-approvals.csv for the canonical shape.",
106
106
  ].join("\n"), {
107
107
  csv: z
108
108
  .string()
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Target Account List (TAL) MCP tool wrappers.
3
3
  *
4
- * Phase 3.5.5 of docs/ACCOUNT-MASTER-PLAN.md.
4
+ * Phase 3.5.5 of docs/plans/account-master.md.
5
5
  *
6
6
  * Tools (ops + eng):
7
7
  * list_tals — catalog of every TAL with member counts
package/dist/tools/tal.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Target Account List (TAL) MCP tool wrappers.
3
3
  *
4
- * Phase 3.5.5 of docs/ACCOUNT-MASTER-PLAN.md.
4
+ * Phase 3.5.5 of docs/plans/account-master.md.
5
5
  *
6
6
  * Tools (ops + eng):
7
7
  * list_tals — catalog of every TAL with member counts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loopops/mcp-server",
3
- "version": "3.39.0",
3
+ "version": "3.41.0",
4
4
  "description": "Loop Operations MCP Server — AI skills for RevOps",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",