salesforce-metadata-mcp 2.1.3 → 2.2.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.
@@ -1,66 +1,75 @@
1
- import { CreateAgentSchema, CreateAgentTopicSchema, CreateAgentActionSchema, CreateAgentPlannerSchema } from "../schemas/index.js";
2
- import { getAuth, createAgent, createAgentTopic, createAgentPlanner, activateAgent, deactivateAgent, API_VERSION } from "../services/salesforce.js";
1
+ import {
2
+ CreateAgentSchema, CreateAgentTopicSchema, CreateAgentActionSchema, CreateAgentPlannerSchema,
3
+ ActivateAgentSchema, DeactivateAgentSchema, GetAgentSchema, UpdateAgentTopicSchema, UpdateAgentActionSchema, DeleteAgentSchema,
4
+ ListAgentsSchema, TestAgentSchema, GetAgentLogsSchema,
5
+ } from "../schemas/index.js";
6
+ import {
7
+ getAuth, createAgent, createAgentTopic, createAgentPlanner,
8
+ activateAgent, deactivateAgent, getAgent, listAgents,
9
+ updateAgentTopic, deleteAgent, testAgent, getAgentLogs, API_VERSION,
10
+ } from "../services/salesforce.js";
3
11
  import { buildGenAiActionZip, deployZip, pollDeployStatus } from "../services/deployment.js";
4
12
  import { resultContent } from "./utils.js";
13
+
5
14
  export function registerAgentforceTools(server) {
15
+ // ─── CREATE ──────────────────────────────────────────────────────────────
6
16
  server.registerTool("sf_create_agent", {
7
17
  title: "Create Agentforce Agent",
8
18
  description: `Creates an Agentforce Agent (Einstein Copilot / Service Agent) in Salesforce using the Bot metadata type.
9
19
 
10
20
  IMPORTANT: Follow this exact creation order for a fully working agent:
11
- 1. Create flows (sf_create_flow) — the AutoLaunchedFlows that perform actions
12
- 2. Create agent actions (sf_create_agent_action) — link each flow as a GenAiFunction
21
+ 1. Create flows (sf_create_flow) — AutoLaunchedFlows that perform actions, must be Active with runInMode=SystemModeWithoutSharing
22
+ 2. Create agent actions (sf_create_agent_action) — link each flow as a GenAiFunction with LLM-facing schemas
13
23
  3. Create agent topics (sf_create_agent_topic) — create GenAiPlugin topics referencing the actions
14
- 4. Create agent planner (sf_create_agent_planner) — create GenAiPlannerBundle linking all topics
24
+ 4. Create agent planner (sf_create_agent_planner) — create GenAiPlannerBundle linking all topics (CRITICAL link)
15
25
  5. Create agent (sf_create_agent) — create the Bot referencing the planner
16
26
  6. Activate the agent (sf_activate_agent)
17
27
 
18
- The agent type is IMMUTABLE after creation — choose carefully (EinsteinCopilot for internal, ExternalCopilot for customer-facing).`,
28
+ The agent type is IMMUTABLE after creation — choose carefully:
29
+ - EinsteinCopilot: internal employee-facing (Lightning sidebar)
30
+ - ExternalCopilot: customer-facing Agentforce Service Agent`,
19
31
  inputSchema: CreateAgentSchema,
20
32
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
21
33
  }, async (params) => {
22
34
  const auth = await getAuth();
23
- const result = await createAgent(auth, {
24
- agentName: params.agentName,
25
- label: params.label,
26
- description: params.description,
27
- type: params.type,
28
- });
35
+ const result = await createAgent(auth, params);
29
36
  return resultContent(result);
30
37
  });
38
+
31
39
  server.registerTool("sf_create_agent_topic", {
32
40
  title: "Create Agentforce Topic (GenAiPlugin)",
33
- description: `Creates a Topic (GenAiPlugin) for an Agentforce Agent. Topics define the agent's areas of expertise — what types of user requests it handles. Each topic has a description (routing criteria), scope (what it covers), step-by-step instructions, and references to agent actions.
41
+ description: `Creates a Topic (GenAiPlugin) for an Agentforce Agent. Topics define the agent's areas of expertise — what types of user requests it handles. Each topic has a routing description, scope, step-by-step instructions, and references to agent actions.
34
42
 
35
43
  Create topics AFTER creating agent actions (sf_create_agent_action) since topics reference actions by API name.
36
- Then create the planner bundle (sf_create_agent_planner) to link all topics to the agent.`,
44
+ Then create the planner bundle (sf_create_agent_planner) to link all topics to the agent.
45
+
46
+ Set escalationEnabled: true to allow this topic to escalate to a human agent.
47
+ Set fallbackTopic to route to another topic when this one cannot handle the request.`,
37
48
  inputSchema: CreateAgentTopicSchema,
38
49
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
39
50
  }, async (params) => {
40
51
  const auth = await getAuth();
41
- const result = await createAgentTopic(auth, {
42
- topicName: params.topicName,
43
- label: params.label,
44
- description: params.description,
45
- scope: params.scope,
46
- instructions: params.instructions,
47
- actions: params.actions,
48
- });
52
+ const result = await createAgentTopic(auth, params);
49
53
  return resultContent(result);
50
54
  });
55
+
51
56
  server.registerTool("sf_create_agent_action", {
52
57
  title: "Create Agentforce Action (GenAiFunction)",
53
- description: `Creates an Agentforce Action (GenAiFunction) linked to a Flow. Actions are the concrete steps an agent takes when handling a topic. The LLM invokes actions based on the topic's instructions and the user's request.
58
+ description: `Creates an Agentforce Action (GenAiFunction) linked to a Flow or Apex class. Actions are the concrete steps an agent takes when handling a topic.
54
59
 
55
- IMPORTANT: This deploys a complete GenAiFunction bundle including:
60
+ IMPORTANT: Deploys a complete GenAiFunction bundle including:
56
61
  - The GenAiFunction metadata (invocationTarget, invocationTargetType, mappingAttributes)
57
- - input/schema.json — the LLM-facing JSON schema for input parameters (REQUIRED for action invocation)
62
+ - input/schema.json — the LLM-facing JSON schema for input parameters (REQUIRED for invocation)
58
63
  - output/schema.json — the LLM-facing JSON schema for output parameters
59
64
 
60
- Without the schema files, the LLM cannot invoke the action. This tool handles all three files automatically.
65
+ Without schema files the LLM cannot invoke the action. This tool generates all three files automatically.
66
+
67
+ Set invocationTargetType to "ApexClass" for Apex-backed actions (provide apexClassName instead of flowApiName).
68
+ For Flow actions (default): the flow must be Active with runInMode=SystemModeWithoutSharing.
61
69
 
62
- Create actions BEFORE topics (topics reference actions by API name).
63
- The referenced flow MUST exist and be Active with runInMode=SystemModeWithoutSharing.`,
70
+ Type matching: inputs/outputs type MUST match the flow variable type — use "Currency" for Currency fields, "Date" for Date fields, etc. Using "Text" for a Currency field causes "incorrect format" errors.
71
+
72
+ Create actions BEFORE topics (topics reference actions by API name).`,
64
73
  inputSchema: CreateAgentActionSchema,
65
74
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
66
75
  }, async (params) => {
@@ -70,7 +79,9 @@ The referenced flow MUST exist and be Active with runInMode=SystemModeWithoutSha
70
79
  actionName: params.actionName,
71
80
  label: params.label,
72
81
  description: params.description,
82
+ invocationTargetType: params.invocationTargetType,
73
83
  flowApiName: params.flowApiName,
84
+ apexClassName: params.apexClassName,
74
85
  inputs: params.inputs ?? [],
75
86
  outputs: params.outputs ?? [],
76
87
  }, API_VERSION);
@@ -82,6 +93,7 @@ The referenced flow MUST exist and be Active with runInMode=SystemModeWithoutSha
82
93
  return resultContent({ success: false, message: err instanceof Error ? err.message : String(err) });
83
94
  }
84
95
  });
96
+
85
97
  server.registerTool("sf_create_agent_planner", {
86
98
  title: "Create Agentforce Planner Bundle (GenAiPlannerBundle)",
87
99
  description: `Creates a GenAiPlannerBundle that connects topics to the agent's planning engine.
@@ -89,38 +101,185 @@ The referenced flow MUST exist and be Active with runInMode=SystemModeWithoutSha
89
101
  The planner bundle is the CRITICAL link between the Bot and its topics (GenAiPlugins). Without it, the agent will have 0 topics visible and cannot route requests or invoke actions.
90
102
 
91
103
  Create the planner bundle AFTER creating all topics (sf_create_agent_topic) and BEFORE creating the agent (sf_create_agent).
92
- Then reference this planner's API name when calling sf_create_agent.`,
104
+ Then reference this planner's API name when calling sf_create_agent.
105
+
106
+ For Knowledge-grounded agents, set dataLibraryName to the API name of a Data Library (Knowledge Base).`,
93
107
  inputSchema: CreateAgentPlannerSchema,
94
108
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
95
109
  }, async (params) => {
96
110
  const auth = await getAuth();
97
- const result = await createAgentPlanner(auth, {
98
- plannerName: params.plannerName,
99
- label: params.label,
100
- description: params.description,
101
- topicNames: params.topicNames,
102
- });
111
+ const result = await createAgentPlanner(auth, params);
103
112
  return resultContent(result);
104
113
  });
114
+
105
115
  server.registerTool("sf_activate_agent", {
106
116
  title: "Activate Agentforce Agent",
107
- description: `Activates an Agentforce Agent so it is available to users. The agent must be inactive (deactivated) before you can make configuration changes like adding/removing topics or actions. After making changes, re-activate with this tool.`,
108
- inputSchema: { type: "object", properties: { agentApiName: { type: "string", description: "API name of the agent to activate, e.g. 'CRM_Record_Creator'" } }, required: ["agentApiName"] },
117
+ description: `Activates an Agentforce Agent so it is available to users. After making configuration changes (adding/removing topics or actions), re-activate with this tool.`,
118
+ inputSchema: ActivateAgentSchema,
109
119
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
110
120
  }, async (params) => {
111
121
  const auth = await getAuth();
112
122
  const result = await activateAgent(auth, params.agentApiName);
113
123
  return resultContent(result);
114
124
  });
125
+
115
126
  server.registerTool("sf_deactivate_agent", {
116
127
  title: "Deactivate Agentforce Agent",
117
128
  description: `Deactivates an Agentforce Agent so configuration changes can be made. You must deactivate an agent before modifying its topics, actions, or planner configuration. After making changes, re-activate with sf_activate_agent.`,
118
- inputSchema: { type: "object", properties: { agentApiName: { type: "string", description: "API name of the agent to deactivate, e.g. 'CRM_Record_Creator'" } }, required: ["agentApiName"] },
129
+ inputSchema: DeactivateAgentSchema,
119
130
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
120
131
  }, async (params) => {
121
132
  const auth = await getAuth();
122
133
  const result = await deactivateAgent(auth, params.agentApiName);
123
134
  return resultContent(result);
124
135
  });
136
+
137
+ // ─── READ ────────────────────────────────────────────────────────────────
138
+ server.registerTool("sf_get_agent", {
139
+ title: "Get Agentforce Agent Configuration",
140
+ description: `Retrieves the full configuration of an Agentforce Agent including activation status, all GenAiPlugin topics, GenAiFunction actions, and GenAiPlannerBundle planners in the org.
141
+
142
+ Returns:
143
+ - agent.status: Active / Inactive / Unknown
144
+ - topics[]: all GenAiPlugin topics (name, label, lastModified)
145
+ - actions[]: all GenAiFunction actions (name, label, lastModified)
146
+ - planners[]: all GenAiPlannerBundle planners (name, label, lastModified)
147
+
148
+ Note: topics/actions/planners list all components in the org (not filtered to this agent). Use sf_get_agent_logs to see which are actively routing.`,
149
+ inputSchema: GetAgentSchema,
150
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
151
+ }, async (params) => {
152
+ const auth = await getAuth();
153
+ const result = await getAgent(auth, params.agentApiName);
154
+ return resultContent(result);
155
+ });
156
+
157
+ server.registerTool("sf_list_agents", {
158
+ title: "List All Agentforce Agents",
159
+ description: `Lists all Agentforce Agents (Bots) in the org with their API name, label, activation status, and last modified date.
160
+
161
+ Use this to discover agents before updating, testing, or deleting them.`,
162
+ inputSchema: ListAgentsSchema,
163
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
164
+ }, async (_params) => {
165
+ const auth = await getAuth();
166
+ const result = await listAgents(auth);
167
+ return resultContent(result);
168
+ });
169
+
170
+ // ─── UPDATE ──────────────────────────────────────────────────────────────
171
+ server.registerTool("sf_update_agent_topic", {
172
+ title: "Update Agentforce Topic (GenAiPlugin)",
173
+ description: `Updates an existing GenAiPlugin topic in-place without deleting and recreating it. Automatically deactivates the agent before the update and reactivates it after.
174
+
175
+ Use this to change a topic's routing description, scope, instructions, or linked actions.
176
+ All fields provided replace the existing values (instructions and actions arrays are replaced entirely).
177
+
178
+ The agent must exist and be accessible — provide agentApiName for automatic deactivate/reactivate.`,
179
+ inputSchema: UpdateAgentTopicSchema,
180
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
181
+ }, async (params) => {
182
+ const auth = await getAuth();
183
+ const { agentApiName, ...topicParams } = params;
184
+ const result = await updateAgentTopic(auth, topicParams, agentApiName);
185
+ return resultContent(result);
186
+ });
187
+
188
+ server.registerTool("sf_update_agent_action", {
189
+ title: "Update Agentforce Action (GenAiFunction)",
190
+ description: `Updates an existing GenAiFunction action and regenerates its LLM-facing input/output schema files. Uses zip-based metadata deploy (same as sf_create_agent_action) since GenAiFunction bundles require schema files.
191
+
192
+ Use this to change an action's description, input/output parameters, or switch from one flow to another.
193
+ All fields provided replace the existing values.
194
+
195
+ After updating an action, the topics that reference it will automatically use the new schema on the next agent invocation.`,
196
+ inputSchema: UpdateAgentActionSchema,
197
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
198
+ }, async (params) => {
199
+ const auth = await getAuth();
200
+ try {
201
+ const base64Zip = await buildGenAiActionZip({
202
+ actionName: params.actionName,
203
+ label: params.label,
204
+ description: params.description,
205
+ invocationTargetType: params.invocationTargetType,
206
+ flowApiName: params.flowApiName,
207
+ apexClassName: params.apexClassName,
208
+ inputs: params.inputs ?? [],
209
+ outputs: params.outputs ?? [],
210
+ }, API_VERSION);
211
+ const deployId = await deployZip(auth, base64Zip);
212
+ const result = await pollDeployStatus(auth, deployId, 120_000);
213
+ return resultContent(result);
214
+ }
215
+ catch (err) {
216
+ return resultContent({ success: false, message: err instanceof Error ? err.message : String(err) });
217
+ }
218
+ });
219
+
220
+ // ─── DELETE ──────────────────────────────────────────────────────────────
221
+ server.registerTool("sf_delete_agent", {
222
+ title: "Delete Agentforce Agent",
223
+ description: `Cleanly deletes an Agentforce Agent (Bot) and optionally its related GenAiPlugin topics and GenAiFunction actions.
224
+
225
+ Deletion order (to avoid dependency errors):
226
+ 1. Deactivates the agent
227
+ 2. Reads the Bot → finds GenAiPlannerBundle name
228
+ 3. Reads the GenAiPlannerBundle → finds GenAiPlugin topic names (if deleteTopics: true)
229
+ 4. Reads each GenAiPlugin → finds GenAiFunction action names (if deleteActions: true)
230
+ 5. Deletes GenAiFunctions → GenAiPlugins → GenAiPlannerBundle → Bot
231
+
232
+ Set deleteTopics: true to also delete the agent's topics.
233
+ Set deleteActions: true to also delete the actions (requires deleteTopics: true since action names are discovered via topics).
234
+
235
+ WARNING: Deleting actions/topics affects all agents that reference them.`,
236
+ inputSchema: DeleteAgentSchema,
237
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
238
+ }, async (params) => {
239
+ const auth = await getAuth();
240
+ const result = await deleteAgent(auth, params);
241
+ return resultContent(result);
242
+ });
243
+
244
+ // ─── TEST & DEBUG ─────────────────────────────────────────────────────────
245
+ server.registerTool("sf_test_agent", {
246
+ title: "Send Test Message to Agentforce Agent",
247
+ description: `Sends a test message to an Agentforce Agent via the Einstein Agent API and returns the agent's response. Useful for end-to-end testing without opening the Salesforce UI.
248
+
249
+ Flow:
250
+ 1. Bootstraps an Agentforce session (POST /agentforce/bootstrap/nameduser)
251
+ 2. Creates a session (POST /einstein/ai-agent/v1/agents/{id}/sessions)
252
+ 3. Sends the message and returns the agent's reply text
253
+
254
+ The agent must be Active. Set orgUrl if your My Domain URL differs from SF_INSTANCE_URL.
255
+
256
+ Returns: response text, sessionId, and success status.`,
257
+ inputSchema: TestAgentSchema,
258
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
259
+ }, async (params) => {
260
+ const auth = await getAuth();
261
+ const result = await testAgent(auth, params);
262
+ return resultContent(result);
263
+ });
264
+
265
+ server.registerTool("sf_get_agent_logs", {
266
+ title: "Get Agentforce Debug Logs",
267
+ description: `Queries ConversationDefinitionEventLog for Agentforce agent debug information. Shows topic routing decisions, action executions, and errors.
268
+
269
+ Key event types to look for:
270
+ - TopicClassificationSuccess — agent routed to a topic (routing is working)
271
+ - ActionExecuted — agent invoked an action (full pipeline working)
272
+ - TopicClassificationFail / Error — routing or execution failed
273
+
274
+ If you see TopicClassificationSuccess but NO ActionExecuted events, the agent is routing correctly but cannot invoke actions — check that GenAiFunction schema files exist (redeploy with sf_create_agent_action).
275
+
276
+ Requires ConversationDefinitionEventLog access (Event Monitoring or Agentforce debug enabled in the org).`,
277
+ inputSchema: GetAgentLogsSchema,
278
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
279
+ }, async (params) => {
280
+ const auth = await getAuth();
281
+ const result = await getAgentLogs(auth, params);
282
+ return resultContent(result);
283
+ });
125
284
  }
126
285
  //# sourceMappingURL=agentforce.js.map
@@ -1,6 +1,6 @@
1
- import { CreateApexClassSchema, CreateApexTriggerSchema, CreateApexTestClassSchema, RunApexTestsSchema, ExecuteAnonymousApexSchema, } from "../schemas/index.js";
2
- import { getAuth } from "../services/salesforce.js";
3
- import { buildApexClassZip, buildApexTriggerZip, deployZip, pollDeployStatus, } from "../services/deployment.js";
1
+ import { CreateApexClassSchema, CreateApexTriggerSchema, CreateApexTestClassSchema, RunApexTestsSchema, ExecuteAnonymousApexSchema, UpdateApexClassSchema, GetApexClassSchema, GetCodeCoverageSchema, CreateVisualforcePageSchema, CreateAuraComponentSchema, } from "../schemas/index.js";
2
+ import { getAuth, getApexClass, getCodeCoverage, API_VERSION } from "../services/salesforce.js";
3
+ import { buildApexClassZip, buildApexTriggerZip, buildVFPageZip, buildAuraZip, deployZip, pollDeployStatus, } from "../services/deployment.js";
4
4
  import { executeAnonymousApex, runApexTests, } from "../services/tooling.js";
5
5
  import { resultContent } from "./utils.js";
6
6
  export function registerApexTools(server) {
@@ -91,5 +91,73 @@ export function registerApexTools(server) {
91
91
  const result = await executeAnonymousApex(auth, params.apexCode);
92
92
  return resultContent(result);
93
93
  });
94
+ server.registerTool("sf_update_apex_class", {
95
+ title: "Update Apex Class",
96
+ description: `Updates an existing Apex class by redeploying it with new source code. The class body is fully replaced — provide the complete class source. The deploy uses zip-based Metadata API for immediate deployment.`,
97
+ inputSchema: UpdateApexClassSchema,
98
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
99
+ }, async (params) => {
100
+ const auth = await getAuth();
101
+ try {
102
+ const base64Zip = await buildApexClassZip(params.className, params.classBody, API_VERSION);
103
+ const deployId = await deployZip(auth, base64Zip);
104
+ const result = await pollDeployStatus(auth, deployId, 120_000);
105
+ return resultContent(result);
106
+ } catch (err) {
107
+ return resultContent({ success: false, message: err instanceof Error ? err.message : String(err) });
108
+ }
109
+ });
110
+ server.registerTool("sf_get_apex_class", {
111
+ title: "Get Apex Class Source",
112
+ description: `Retrieves the source code and metadata of an Apex class from the org using the Tooling API. Returns the class body, API version, and status. Useful for reviewing existing code before making changes.`,
113
+ inputSchema: GetApexClassSchema,
114
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
115
+ }, async (params) => {
116
+ const auth = await getAuth();
117
+ const result = await getApexClass(auth, params);
118
+ return resultContent(result);
119
+ });
120
+ server.registerTool("sf_get_code_coverage", {
121
+ title: "Get Apex Code Coverage",
122
+ description: `Retrieves Apex code coverage percentages from the last test run using the Tooling API. Returns covered lines, uncovered lines, and coverage percentage per class. Filter by class name or retrieve all classes.`,
123
+ inputSchema: GetCodeCoverageSchema,
124
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
125
+ }, async (params) => {
126
+ const auth = await getAuth();
127
+ const result = await getCodeCoverage(auth, params);
128
+ return resultContent(result);
129
+ });
130
+ server.registerTool("sf_create_visualforce_page", {
131
+ title: "Create Visualforce Page",
132
+ description: `Deploys a Visualforce page to the org using zip-based Metadata API. Provide the page name (no spaces) and the full Visualforce markup. Visualforce pages use the apex: and c: tag libraries and can reference Apex controllers.`,
133
+ inputSchema: CreateVisualforcePageSchema,
134
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
135
+ }, async (params) => {
136
+ const auth = await getAuth();
137
+ try {
138
+ const base64Zip = await buildVFPageZip(params.pageName, params.markup, API_VERSION);
139
+ const deployId = await deployZip(auth, base64Zip);
140
+ const result = await pollDeployStatus(auth, deployId, 120_000);
141
+ return resultContent(result);
142
+ } catch (err) {
143
+ return resultContent({ success: false, message: err instanceof Error ? err.message : String(err) });
144
+ }
145
+ });
146
+ server.registerTool("sf_create_aura_component", {
147
+ title: "Create Aura Component Bundle",
148
+ description: `Deploys an Aura (Lightning) component bundle using zip-based Metadata API. Provide the component name and markup (.cmp file). Optionally include a controller, helper, and CSS style. Aura components are the predecessor to LWC and still supported for complex use cases.`,
149
+ inputSchema: CreateAuraComponentSchema,
150
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
151
+ }, async (params) => {
152
+ const auth = await getAuth();
153
+ try {
154
+ const base64Zip = await buildAuraZip(params.componentName, params.markup, params.controller, params.helper, params.style, API_VERSION);
155
+ const deployId = await deployZip(auth, base64Zip);
156
+ const result = await pollDeployStatus(auth, deployId, 120_000);
157
+ return resultContent(result);
158
+ } catch (err) {
159
+ return resultContent({ success: false, message: err instanceof Error ? err.message : String(err) });
160
+ }
161
+ });
94
162
  }
95
163
  //# sourceMappingURL=apex.js.map
@@ -1,5 +1,5 @@
1
- import { CreateEmailAlertSchema, CreatePlatformEventSchema, CreateAssignmentRuleSchema, CreateEscalationRuleSchema, CreateAutoResponseRuleSchema, CreateMatchingRuleSchema, CreateDuplicateRuleSchema, CreateApexEmailServiceSchema, CreateScheduledJobSchema, } from "../schemas/index.js";
2
- import { getAuth, createEmailAlert, createPlatformEvent, createAssignmentRule, createAutoResponseRule, createMatchingRule, createDuplicateRule, } from "../services/salesforce.js";
1
+ import { CreateEmailAlertSchema, CreatePlatformEventSchema, CreateAssignmentRuleSchema, CreateEscalationRuleSchema, CreateAutoResponseRuleSchema, CreateMatchingRuleSchema, CreateDuplicateRuleSchema, CreateApexEmailServiceSchema, CreateScheduledJobSchema, ActivateFlowSchema, CreateQuickActionSchema, } from "../schemas/index.js";
2
+ import { getAuth, createEmailAlert, createPlatformEvent, createAssignmentRule, createAutoResponseRule, createMatchingRule, createDuplicateRule, activateFlow, createQuickAction, } from "../services/salesforce.js";
3
3
  import { createApexEmailService, createScheduledJob, } from "../services/tooling.js";
4
4
  import { resultContent } from "./utils.js";
5
5
  export function registerAutomationTools(server) {
@@ -182,5 +182,28 @@ export function registerAutomationTools(server) {
182
182
  const result = await createScheduledJob(auth, params.className, params.jobName, params.cronExpression);
183
183
  return resultContent(result);
184
184
  });
185
+ server.registerTool("sf_activate_flow", {
186
+ title: "Activate a Flow",
187
+ description: `Activates the latest version of a Flow via the Tooling API. Flow must already exist in the org. The flow's latest version will be set as the active version. Use after creating or updating a flow to make it available.`,
188
+ inputSchema: ActivateFlowSchema,
189
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
190
+ }, async (params) => {
191
+ const auth = await getAuth();
192
+ const result = await activateFlow(auth, params);
193
+ return resultContent(result);
194
+ });
195
+ server.registerTool("sf_create_quick_action", {
196
+ title: "Create Quick Action",
197
+ description: `Creates a Quick Action (global or object-specific) for record creation, updates, logging calls, sending emails, or launching flows from record pages and the global action bar.
198
+
199
+ Action types: Create, Update, LogACall, SendEmail, Flow, Visualforce.
200
+ For object-specific actions, provide objectApiName. For global actions, omit it.`,
201
+ inputSchema: CreateQuickActionSchema,
202
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
203
+ }, async (params) => {
204
+ const auth = await getAuth();
205
+ const result = await createQuickAction(auth, params);
206
+ return resultContent(result);
207
+ });
185
208
  }
186
209
  //# sourceMappingURL=automation.js.map
@@ -0,0 +1,86 @@
1
+ import { CreateUserSchema, UpdateUserSchema, AssignQueueMemberSchema, CreatePublicGroupSchema, QueryRecordsSchema, CreateRecordSchema, UpdateRecordSchema, BulkImportRecordsSchema, } from "../schemas/index.js";
2
+ import { getAuth, createUser, updateUser, assignQueueMember, createPublicGroup, queryRecords, createRecord, updateRecord, bulkImportRecords, } from "../services/salesforce.js";
3
+ import { resultContent } from "./utils.js";
4
+ export function registerDataTools(server) {
5
+ server.registerTool("sf_create_user", {
6
+ title: "Create Salesforce User",
7
+ description: `Creates a new Salesforce user via the REST API. Requires username (must be unique and email-like), lastName, email, and profileName. The profile must already exist. Optionally assign a role by roleApiName (DeveloperName of the UserRole). The user will receive a welcome email unless email confirmations are suppressed in org settings.`,
8
+ inputSchema: CreateUserSchema,
9
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
10
+ }, async (params) => {
11
+ const auth = await getAuth();
12
+ const result = await createUser(auth, params);
13
+ return resultContent(result);
14
+ });
15
+ server.registerTool("sf_update_user", {
16
+ title: "Update Salesforce User",
17
+ description: `Updates an existing Salesforce user's properties via the REST API. Look up the user by username and update fields like firstName, lastName, email, title, department, phone, or isActive (to deactivate/reactivate). Only fields you provide are updated.`,
18
+ inputSchema: UpdateUserSchema,
19
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
20
+ }, async (params) => {
21
+ const auth = await getAuth();
22
+ const result = await updateUser(auth, params);
23
+ return resultContent(result);
24
+ });
25
+ server.registerTool("sf_assign_queue_member", {
26
+ title: "Add User to Queue",
27
+ description: `Adds a user to an existing Queue (GroupMember SObject) by username and queue DeveloperName. The queue must already exist (create via sf_create_queue). Users in queues can be assigned records and receive queue notification emails.`,
28
+ inputSchema: AssignQueueMemberSchema,
29
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
30
+ }, async (params) => {
31
+ const auth = await getAuth();
32
+ const result = await assignQueueMember(auth, params);
33
+ return resultContent(result);
34
+ });
35
+ server.registerTool("sf_create_public_group", {
36
+ title: "Create Public Group",
37
+ description: `Creates a Public Group (Group SObject with Type=Regular) for sharing rules, email distribution, or queue membership. Public groups can include users, roles, and other groups. Use as a sharing target in sf_create_sharing_rule.`,
38
+ inputSchema: CreatePublicGroupSchema,
39
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
40
+ }, async (params) => {
41
+ const auth = await getAuth();
42
+ const result = await createPublicGroup(auth, params);
43
+ return resultContent(result);
44
+ });
45
+ server.registerTool("sf_query_records", {
46
+ title: "Query Records (SOQL)",
47
+ description: `Executes a SOQL query against the org and returns matching records. Provide either a full SOQL string (soql param) or individual parameters (objectApiName, fields, whereClause, orderBy, limit). Use for reading data, checking existing records before creating, or verifying changes.`,
48
+ inputSchema: QueryRecordsSchema,
49
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
50
+ }, async (params) => {
51
+ const auth = await getAuth();
52
+ const result = await queryRecords(auth, params);
53
+ return resultContent(result);
54
+ });
55
+ server.registerTool("sf_create_record", {
56
+ title: "Create SObject Record",
57
+ description: `Creates a single SObject record via the Salesforce REST API. Provide the object API name and a fields object with field API names and values. For bulk creation (100+ records), use sf_bulk_import_records instead.`,
58
+ inputSchema: CreateRecordSchema,
59
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
60
+ }, async (params) => {
61
+ const auth = await getAuth();
62
+ const result = await createRecord(auth, params);
63
+ return resultContent(result);
64
+ });
65
+ server.registerTool("sf_update_record", {
66
+ title: "Update SObject Record",
67
+ description: `Updates an existing SObject record by record ID via the Salesforce REST API. Provide the object API name, the 15 or 18 character record ID, and the fields to update. Only provided fields are changed — omitted fields retain their current values.`,
68
+ inputSchema: UpdateRecordSchema,
69
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
70
+ }, async (params) => {
71
+ const auth = await getAuth();
72
+ const result = await updateRecord(auth, params);
73
+ return resultContent(result);
74
+ });
75
+ server.registerTool("sf_bulk_import_records", {
76
+ title: "Bulk Import Records (Bulk API 2.0)",
77
+ description: `Bulk imports records using the Salesforce Bulk API 2.0. Supports insert, upsert, update, and delete operations on large datasets (thousands to millions of records). Provide CSV data with a header row. For upsert, set externalIdField to the field used for matching. Polls until the job completes and returns success/failure counts.`,
78
+ inputSchema: BulkImportRecordsSchema,
79
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
80
+ }, async (params) => {
81
+ const auth = await getAuth();
82
+ const result = await bulkImportRecords(auth, params);
83
+ return resultContent(result);
84
+ });
85
+ }
86
+ //# sourceMappingURL=data.js.map
@@ -1,5 +1,5 @@
1
- import { CreateOutboundChangeSetSchema, AddToChangeSetSchema, DeployMetadataSchema, CheckDeployStatusSchema, RetrieveMetadataSchema, } from "../schemas/index.js";
2
- import { getAuth } from "../services/salesforce.js";
1
+ import { CreateOutboundChangeSetSchema, AddToChangeSetSchema, DeployMetadataSchema, CheckDeployStatusSchema, RetrieveMetadataSchema, ValidateDeploymentSchema, ListMetadataSchema, } from "../schemas/index.js";
2
+ import { getAuth, listMetadataForTool } from "../services/salesforce.js";
3
3
  import { buildGenericDeployZip, deployZip, pollDeployStatus, checkDeployStatus, retrieveMetadata, } from "../services/deployment.js";
4
4
  import { createOutboundChangeSet, addComponentsToChangeSet, } from "../services/tooling.js";
5
5
  import { resultContent } from "./utils.js";
@@ -33,17 +33,18 @@ export function registerDeploymentTools(server) {
33
33
  });
34
34
  server.registerTool("sf_deploy_metadata", {
35
35
  title: "Deploy Metadata",
36
- description: `Deploys a set of metadata components directly to the org using the Metadata API SOAP deploy operation. Builds a package.xml and deployment zip in memory. Supports validate-only (checkOnly:true) for pre-deployment validation without making changes. Specify runTests to execute test classes during deployment (required for production). Polls until complete or timeout.`,
36
+ description: `Deploys a set of metadata components directly to the org using the Metadata API SOAP deploy operation. Builds a package.xml and deployment zip in memory. Supports validate-only (checkOnly:true) for pre-deployment validation without making changes. Specify runTests to execute test classes during deployment (required for production). Set testLevel to RunSpecifiedTests, RunLocalTests, or RunAllTestsInOrg. Polls until complete or timeout.`,
37
37
  inputSchema: DeployMetadataSchema,
38
38
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
39
39
  }, async (params) => {
40
40
  const auth = await getAuth();
41
41
  try {
42
- const base64Zip = await buildGenericDeployZip(params.components, "62.0");
42
+ const base64Zip = await buildGenericDeployZip(params.components, "66.0");
43
43
  const deployId = await deployZip(auth, base64Zip, {
44
44
  checkOnly: params.checkOnly,
45
45
  runTests: params.runTests,
46
46
  rollbackOnError: params.rollbackOnError,
47
+ testLevel: params.testLevel,
47
48
  });
48
49
  const result = await pollDeployStatus(auth, deployId, params.waitMinutes * 60 * 1000);
49
50
  return resultContent({
@@ -85,5 +86,40 @@ export function registerDeploymentTools(server) {
85
86
  const result = await retrieveMetadata(auth, params.components);
86
87
  return resultContent(result);
87
88
  });
89
+ server.registerTool("sf_validate_deployment", {
90
+ title: "Validate Deployment (Check-Only)",
91
+ description: `Runs a check-only deployment that validates metadata components against the org without making any changes. Equivalent to sf_deploy_metadata with checkOnly:true. Use before deploying to production to catch errors and test failures without risk. Specify testLevel to control which tests run during validation.`,
92
+ inputSchema: ValidateDeploymentSchema,
93
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
94
+ }, async (params) => {
95
+ const auth = await getAuth();
96
+ try {
97
+ const base64Zip = await buildGenericDeployZip(params.components, "66.0");
98
+ const deployId = await deployZip(auth, base64Zip, {
99
+ checkOnly: true,
100
+ runTests: params.runTests,
101
+ rollbackOnError: true,
102
+ testLevel: params.testLevel,
103
+ });
104
+ const result = await pollDeployStatus(auth, deployId, (params.waitMinutes ?? 10) * 60 * 1000);
105
+ return resultContent({
106
+ success: result.success,
107
+ message: `[VALIDATE ONLY] ${result.message}`,
108
+ ...(result.success ? { fullName: deployId, created: false } : {})
109
+ });
110
+ } catch (err) {
111
+ return resultContent({ success: false, message: err instanceof Error ? err.message : String(err) });
112
+ }
113
+ });
114
+ server.registerTool("sf_list_metadata", {
115
+ title: "List Metadata Components",
116
+ description: `Lists all metadata components of a given type in the org (e.g., ApexClass, CustomObject, Flow, PermissionSet). Returns full names, file names, and last modified dates. Useful for discovering what metadata exists in the org before retrieving or deploying.`,
117
+ inputSchema: ListMetadataSchema,
118
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
119
+ }, async (params) => {
120
+ const auth = await getAuth();
121
+ const result = await listMetadataForTool(auth, params.metadataType);
122
+ return resultContent(result);
123
+ });
88
124
  }
89
125
  //# sourceMappingURL=deployment.js.map