@loopops/mcp-server 3.22.0 → 3.24.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/dist/index.js CHANGED
@@ -13,6 +13,7 @@ import { registerDeployTools } from "./tools/deploy.js";
13
13
  import { registerEngTools } from "./tools/eng.js";
14
14
  import { registerCrmTools } from "./tools/crm.js";
15
15
  import { registerEngageTools } from "./tools/engage.js";
16
+ import { registerPursueTools } from "./tools/pursue.js";
16
17
  import { registerAccountMasterTools } from "./tools/account-master.js";
17
18
  import { registerPeopleMasterTools } from "./tools/people-master.js";
18
19
  import { registerScoringTools } from "./tools/scoring.js";
@@ -59,6 +60,7 @@ registerConfigTools(server, allowedSkills);
59
60
  registerEngTools(server, allowedSkills);
60
61
  registerCrmTools(server, allowedSkills);
61
62
  registerEngageTools(server, allowedSkills);
63
+ registerPursueTools(server, allowedSkills);
62
64
  registerAccountMasterTools(server, allowedSkills);
63
65
  registerPeopleMasterTools(server, allowedSkills);
64
66
  registerDeployTools(server, allowedSkills);
package/dist/tools/crm.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { trpcMutation } from "../api-client.js";
3
3
  import { safeTool } from "./_helpers.js";
4
- import { leadStatusSchema, salesforceLeadIdSchema, salesforceOpportunityIdSchema, targetAccountTierSchema, } from "./_schemas.js";
4
+ import { targetAccountTierSchema } from "./_schemas.js";
5
5
  export function registerCrmTools(server, allowed) {
6
6
  // my_pipeline retired 2026-05-06 — replaced by ClickHouse parameterized
7
7
  // view analytics.my_pipeline. Row policies on salesforce.opportunity
@@ -12,58 +12,17 @@ export function registerCrmTools(server, allowed) {
12
12
  // analytics.my_leads. Row policies on salesforce.lead enforce per-user
13
13
  // scoping. Filter by created_date in the caller. See docs/CH-ANALYTICS-VIEWS.md.
14
14
  // pipeline_health retired 2026-05-06 — replaced by ClickHouse views
15
- // analytics.pipeline_by_stage + analytics.win_rate(range_days). Query via
16
- // the Cloud Remote MCP server (mcp.clickhouse.cloud/mcp). Row policies on
17
- // salesforce.opportunity enforce per-user scoping. See docs/CH-ANALYTICS-VIEWS.md.
15
+ // analytics.pipeline_by_stage + analytics.win_rate(range_days).
16
+ // See docs/CH-ANALYTICS-VIEWS.md.
18
17
  // rep_performance retired 2026-05-06 — replaced by ClickHouse view
19
18
  // analytics.rep_performance(range_days). See docs/CH-ANALYTICS-VIEWS.md.
20
19
  // forecast retired 2026-05-06 — replaced by ClickHouse view
21
- // analytics.forecast(quarter_start, quarter_end). Caller computes the
22
- // quarter window. See docs/CH-ANALYTICS-VIEWS.md.
23
- if (allowed.has("update_opportunity")) {
24
- server.tool("update_opportunity", "Update an opportunity's close date, next step, or description. You can identify the deal by Salesforce Opportunity ID or exact opportunity name. Reps can only update their own deals. Requires a reason for the change.", {
25
- opportunityId: salesforceOpportunityIdSchema
26
- .optional()
27
- .describe("Salesforce Opportunity ID (006…). Preferred if known."),
28
- opportunityName: z
29
- .string()
30
- .optional()
31
- .describe("Exact opportunity name, used when you don't have the Salesforce ID."),
32
- close_date: z
33
- .string()
34
- .regex(/^\d{4}-\d{2}-\d{2}$/, { message: "Use YYYY-MM-DD." })
35
- .optional()
36
- .describe("New close date in YYYY-MM-DD format."),
37
- next_step: z.string().optional().describe("Next step text."),
38
- description: z.string().optional().describe("Opportunity description."),
39
- reason: z
40
- .string()
41
- .min(1)
42
- .describe("Why this change is being made (required, logged)."),
43
- }, safeTool(async ({ opportunityId, opportunityName, close_date, next_step, description, reason }) => trpcMutation("mcp.updateOpportunity", {
44
- opportunityId,
45
- opportunityName,
46
- fields: { close_date, next_step, description },
47
- reason,
48
- })));
49
- }
50
- if (allowed.has("update_lead")) {
51
- server.tool("update_lead", "Update a lead's status or description. Reps can only update their own leads. Requires a reason for the change.", {
52
- leadId: salesforceLeadIdSchema.describe("Salesforce Lead ID (00Q…)."),
53
- status: leadStatusSchema
54
- .optional()
55
- .describe("New Salesforce Lead status."),
56
- description: z.string().optional().describe("Lead description."),
57
- reason: z
58
- .string()
59
- .min(1)
60
- .describe("Why this change is being made (required, logged)."),
61
- }, safeTool(async ({ leadId, status, description, reason }) => trpcMutation("mcp.updateLead", {
62
- leadId,
63
- fields: { status, description },
64
- reason,
65
- })));
66
- }
20
+ // analytics.forecast(quarter_start, quarter_end). See docs/CH-ANALYTICS-VIEWS.md.
21
+ // update_opportunity / update_lead removed 2026-05-09 (Phase 2 of
22
+ // docs/SALESFORCE-MCP-PLAN.md). Opportunity + Lead writes go through
23
+ // the `salesforce-hosted` MCP for every Loop role: per-user OAuth+PKCE
24
+ // preserves LastModifiedById; sharing rules + FLS + validation rules
25
+ // enforce server-side. Use mcp__salesforce-hosted__updateSobjectRecord.
67
26
  if (allowed.has("push_target_account_to_sf")) {
68
27
  server.tool("push_target_account_to_sf", "Create Salesforce Account records from the target account list. Pushes accounts that don't yet have an SF Account ID. Ops+ only.", {
69
28
  domain: z
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Pursue-loop hybrid analysis tools.
3
+ *
4
+ * Home for hybrid-tools-pattern (docs/HYBRID-TOOLS.md) tools that operate
5
+ * on the deal-progression / pipeline-health surface — composing CH +
6
+ * Salesforce + Postgres reads to deliver deterministic structured analysis
7
+ * an operator can act on. By rule, these tools are read-only; they emit
8
+ * recommendations + copy-pasteable salesforce-hosted commands. The
9
+ * operator runs the actual writes through `salesforce-hosted`.
10
+ *
11
+ * First instance: review_stale_pipeline. As more hybrid pursue tools land
12
+ * (forecast_risk_review, manager_coaching_brief, etc.), they go here.
13
+ */
14
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
15
+ export declare function registerPursueTools(server: McpServer, allowed: Set<string>): void;
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Pursue-loop hybrid analysis tools.
3
+ *
4
+ * Home for hybrid-tools-pattern (docs/HYBRID-TOOLS.md) tools that operate
5
+ * on the deal-progression / pipeline-health surface — composing CH +
6
+ * Salesforce + Postgres reads to deliver deterministic structured analysis
7
+ * an operator can act on. By rule, these tools are read-only; they emit
8
+ * recommendations + copy-pasteable salesforce-hosted commands. The
9
+ * operator runs the actual writes through `salesforce-hosted`.
10
+ *
11
+ * First instance: review_stale_pipeline. As more hybrid pursue tools land
12
+ * (forecast_risk_review, manager_coaching_brief, etc.), they go here.
13
+ */
14
+ import { z } from "zod";
15
+ import { trpcQuery } from "../api-client.js";
16
+ import { safeTool } from "./_helpers.js";
17
+ export function registerPursueTools(server, allowed) {
18
+ if (allowed.has("review_stale_pipeline")) {
19
+ server.tool("review_stale_pipeline", [
20
+ "Deterministic stale-pipeline review. Composes ClickHouse reads (salesforce.opportunity + user + account) against a per-caller scope.",
21
+ "Default scope: rep → own opps; manager+ → recursive reporting subtree (any depth). Manager+ can pass {repId} or {managerId} for nodes within their subtree.",
22
+ "Returns Markdown — table of stale opps with classifier-driven recommended actions, plus copy-pasteable commands you run via the salesforce-hosted MCP under your real user identity.",
23
+ "Read-only. Recommends; does not execute writes.",
24
+ ].join(" "), {
25
+ scope: z
26
+ .union([
27
+ z.literal("me"),
28
+ z.literal("my_team"),
29
+ z.literal("subtree"),
30
+ z.object({ repId: z.string().min(1) }),
31
+ z.object({ managerId: z.string().min(1) }),
32
+ ])
33
+ .optional()
34
+ .describe("Optional scope override. Default: rep → 'me'; manager+ → 'subtree'. " +
35
+ "Reps cannot escalate. Managers can scope to {repId} or {managerId} within their subtree only."),
36
+ staleness_days: z
37
+ .number()
38
+ .int()
39
+ .min(1)
40
+ .max(365)
41
+ .optional()
42
+ .describe("Days since LastActivityDate (or LastModifiedDate if no activity logged) threshold. Default 14."),
43
+ stage_filter: z
44
+ .array(z.string())
45
+ .optional()
46
+ .describe("Optional list of stage names to restrict the review (e.g. ['Negotiation/Review','Proposal/Price Quote']). Default: all open stages."),
47
+ min_amount: z
48
+ .number()
49
+ .min(0)
50
+ .optional()
51
+ .describe("Minimum opp amount in dollars. Default 0 (no filter)."),
52
+ }, safeTool(async ({ scope, staleness_days, stage_filter, min_amount }) => trpcQuery("mcp.reviewStalePipeline", {
53
+ scope,
54
+ staleness_days,
55
+ stage_filter,
56
+ min_amount,
57
+ })));
58
+ }
59
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loopops/mcp-server",
3
- "version": "3.22.0",
3
+ "version": "3.24.0",
4
4
  "description": "Loop Operations MCP Server — AI skills for RevOps",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",