@loopops/mcp-server 3.6.0 → 3.7.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 +2 -0
- package/dist/tools/sfdc-sync.d.ts +13 -0
- package/dist/tools/sfdc-sync.js +91 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -14,6 +14,7 @@ import { registerCrmTools } from "./tools/crm.js";
|
|
|
14
14
|
import { registerEngageTools } from "./tools/engage.js";
|
|
15
15
|
import { registerAccountMasterTools } from "./tools/account-master.js";
|
|
16
16
|
import { registerScoringTools } from "./tools/scoring.js";
|
|
17
|
+
import { registerSfdcSyncTools } from "./tools/sfdc-sync.js";
|
|
17
18
|
import { registerTalTools } from "./tools/tal.js";
|
|
18
19
|
// Read our own package.json at runtime so the version baked into MCP
|
|
19
20
|
// initialize-handshake `serverInfo.version` matches the published npm
|
|
@@ -59,5 +60,6 @@ registerEngageTools(server, allowedSkills);
|
|
|
59
60
|
registerAccountMasterTools(server, allowedSkills);
|
|
60
61
|
registerTalTools(server, allowedSkills);
|
|
61
62
|
registerScoringTools(server, allowedSkills);
|
|
63
|
+
registerSfdcSyncTools(server, allowedSkills);
|
|
62
64
|
const transport = new StdioServerTransport();
|
|
63
65
|
await server.connect(transport);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SFDC sync MCP tool wrappers (Phase 4).
|
|
3
|
+
*
|
|
4
|
+
* Tools (ops + eng):
|
|
5
|
+
* deploy_account — push one account immediately
|
|
6
|
+
* approve_initial_deployment — promote pending_initial_deployment_review
|
|
7
|
+
* to pending_update_deployment
|
|
8
|
+
* deployment_status — top-line counts + initial-review queue
|
|
9
|
+
* drift_report — drifted accounts
|
|
10
|
+
* resolve_drift — operator picks accept_am or accept_sf
|
|
11
|
+
*/
|
|
12
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
13
|
+
export declare function registerSfdcSyncTools(server: McpServer, allowed: Set<string>): void;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SFDC sync MCP tool wrappers (Phase 4).
|
|
3
|
+
*
|
|
4
|
+
* Tools (ops + eng):
|
|
5
|
+
* deploy_account — push one account immediately
|
|
6
|
+
* approve_initial_deployment — promote pending_initial_deployment_review
|
|
7
|
+
* to pending_update_deployment
|
|
8
|
+
* deployment_status — top-line counts + initial-review queue
|
|
9
|
+
* drift_report — drifted accounts
|
|
10
|
+
* resolve_drift — operator picks accept_am or accept_sf
|
|
11
|
+
*/
|
|
12
|
+
import { z } from "zod";
|
|
13
|
+
import { trpcMutation, trpcQuery } from "../api-client.js";
|
|
14
|
+
import { safeTool } from "./_helpers.js";
|
|
15
|
+
const accountIdentifierShape = {
|
|
16
|
+
accountId: z
|
|
17
|
+
.string()
|
|
18
|
+
.uuid()
|
|
19
|
+
.optional()
|
|
20
|
+
.describe("Account Master account_id (UUID). Use account_lookup if you don't have it."),
|
|
21
|
+
domain: z.string().min(1).optional().describe("Account's primary domain (e.g. 'acme.com')."),
|
|
22
|
+
sfAccountId: z.string().min(3).optional().describe("Salesforce Account ID."),
|
|
23
|
+
accountName: z.string().min(1).optional().describe("Company name."),
|
|
24
|
+
};
|
|
25
|
+
export function registerSfdcSyncTools(server, allowed) {
|
|
26
|
+
if (allowed.has("deploy_account")) {
|
|
27
|
+
server.tool("deploy_account", [
|
|
28
|
+
"Push one account's golden record + score to Salesforce immediately. Builds the SF payload",
|
|
29
|
+
"from config/account_master/sfdc_field_map.yaml, PATCHes the SF Account record, and",
|
|
30
|
+
"updates salesforce_mapping with the canonical hash for drift detection.",
|
|
31
|
+
"",
|
|
32
|
+
"Use after a manual override or attribute backfill when you want SF updated immediately",
|
|
33
|
+
"instead of waiting for the next account_master_deploy_orchestrator cron fire (every 15m).",
|
|
34
|
+
"",
|
|
35
|
+
"Initial-deployment review accounts (no SF mapping yet, mode=review in YAML) are NOT",
|
|
36
|
+
"pushed by this tool. Approve them via approve_initial_deployment first.",
|
|
37
|
+
].join("\n"), accountIdentifierShape, safeTool(async (input) => trpcMutation("mcp.deployAccount", input)));
|
|
38
|
+
}
|
|
39
|
+
if (allowed.has("approve_initial_deployment")) {
|
|
40
|
+
server.tool("approve_initial_deployment", [
|
|
41
|
+
"Approve an account currently in `pending_initial_deployment_review` (the queue gate when",
|
|
42
|
+
"config/account_master/sfdc_deploy.yaml has initial_deployment.mode=review). Promotes the",
|
|
43
|
+
"account to pending_update_deployment so the next deploy run pushes it to SF.",
|
|
44
|
+
"",
|
|
45
|
+
"Use this for the first push of every new account until you flip mode=auto in the YAML",
|
|
46
|
+
"(once you trust the eligibility filter + field map).",
|
|
47
|
+
].join("\n"), accountIdentifierShape, safeTool(async (input) => trpcMutation("mcp.approveInitialDeployment", input)));
|
|
48
|
+
}
|
|
49
|
+
if (allowed.has("deployment_status")) {
|
|
50
|
+
server.tool("deployment_status", "Top-line counts of accounts in each deployment lifecycle state (pending review, pending update, deployed, deployment_failed, sync_drift). Includes a sample of the initial-review queue so ops can spot-check what's waiting for approval.", {
|
|
51
|
+
sampleSize: z
|
|
52
|
+
.number()
|
|
53
|
+
.int()
|
|
54
|
+
.positive()
|
|
55
|
+
.max(100)
|
|
56
|
+
.optional()
|
|
57
|
+
.describe("How many initial-review samples to surface (1-100). Default: 10."),
|
|
58
|
+
}, safeTool(async (input) => trpcQuery("mcp.deploymentStatus", input)));
|
|
59
|
+
}
|
|
60
|
+
if (allowed.has("drift_report")) {
|
|
61
|
+
server.tool("drift_report", "List accounts in `sync_drift` — where SF state diverged from the AM golden record. Each row shows the SF Account ID, when drift was detected, and the last-pushed hash. Resolve with `resolve_drift accountId=X choice=accept_am|accept_sf`.", {
|
|
62
|
+
limit: z
|
|
63
|
+
.number()
|
|
64
|
+
.int()
|
|
65
|
+
.positive()
|
|
66
|
+
.max(500)
|
|
67
|
+
.optional()
|
|
68
|
+
.describe("Max drifted accounts to return. Default 50."),
|
|
69
|
+
}, safeTool(async (input) => trpcQuery("mcp.driftReport", input)));
|
|
70
|
+
}
|
|
71
|
+
if (allowed.has("resolve_drift")) {
|
|
72
|
+
server.tool("resolve_drift", [
|
|
73
|
+
"Resolve a drifted account by picking the canonical winner.",
|
|
74
|
+
"",
|
|
75
|
+
" accept_am → re-queue for deployment; AM values overwrite SF",
|
|
76
|
+
" accept_sf → treat SF as authoritative for the diverged fields; write them back into AM",
|
|
77
|
+
" via manual overrides (v1 path requires a follow-up step — tool emits the",
|
|
78
|
+
" recipe)",
|
|
79
|
+
"",
|
|
80
|
+
"Always include a `reason` — it lands on the lifecycle event for audit.",
|
|
81
|
+
].join("\n"), {
|
|
82
|
+
...accountIdentifierShape,
|
|
83
|
+
choice: z.enum(["accept_am", "accept_sf"]).describe("Which side wins."),
|
|
84
|
+
reason: z
|
|
85
|
+
.string()
|
|
86
|
+
.min(3)
|
|
87
|
+
.max(500)
|
|
88
|
+
.describe("Operator note for the lifecycle event audit log."),
|
|
89
|
+
}, safeTool(async (input) => trpcMutation("mcp.resolveDrift", input)));
|
|
90
|
+
}
|
|
91
|
+
}
|