salesforce-metadata-mcp 2.1.2 → 2.1.4

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,73 +1,285 @@
1
- import { CreateAgentSchema, CreateAgentTopicSchema, CreateAgentActionSchema } from "../schemas/index.js";
2
- import { getAuth, createAgent, createAgentTopic } from "../services/salesforce.js";
1
+ import {
2
+ CreateAgentSchema, CreateAgentTopicSchema, CreateAgentActionSchema, CreateAgentPlannerSchema,
3
+ 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";
11
+ import { buildGenAiActionZip, deployZip, pollDeployStatus } from "../services/deployment.js";
3
12
  import { resultContent } from "./utils.js";
13
+
4
14
  export function registerAgentforceTools(server) {
15
+ // ─── CREATE ──────────────────────────────────────────────────────────────
5
16
  server.registerTool("sf_create_agent", {
6
17
  title: "Create Agentforce Agent",
7
- description: `Creates an Agentforce Agent (Einstein Copilot) in Salesforce using the Bot metadata type. An agent is an AI assistant that can understand natural language, reason about user intent, and take actions using topics and linked flows/actions. Specify the agent's persona, tone, and system-level instructions to shape its behavior and communication style.`,
18
+ description: `Creates an Agentforce Agent (Einstein Copilot / Service Agent) in Salesforce using the Bot metadata type.
19
+
20
+ IMPORTANT: Follow this exact creation order for a fully working agent:
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
23
+ 3. Create agent topics (sf_create_agent_topic) — create GenAiPlugin topics referencing the actions
24
+ 4. Create agent planner (sf_create_agent_planner) — create GenAiPlannerBundle linking all topics (CRITICAL link)
25
+ 5. Create agent (sf_create_agent) — create the Bot referencing the planner
26
+ 6. Activate the agent (sf_activate_agent)
27
+
28
+ The agent type is IMMUTABLE after creation — choose carefully:
29
+ - EinsteinCopilot: internal employee-facing (Lightning sidebar)
30
+ - ExternalCopilot: customer-facing Agentforce Service Agent`,
8
31
  inputSchema: CreateAgentSchema,
9
32
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
10
33
  }, async (params) => {
11
34
  const auth = await getAuth();
12
- const result = await createAgent(auth, {
13
- agentName: params.agentName,
14
- label: params.label,
15
- description: params.description,
16
- type: params.type,
17
- company: params.company,
18
- persona: params.persona,
19
- tone: params.tone,
20
- instructions: params.instructions,
21
- });
35
+ const result = await createAgent(auth, params);
22
36
  return resultContent(result);
23
37
  });
38
+
24
39
  server.registerTool("sf_create_agent_topic", {
25
- title: "Create Agentforce Topic",
26
- description: `Creates a Topic for an Agentforce Agent. Topics define areas of expertise — what types of user requests the agent can handle. Each topic has a description (what it covers), scope (what kinds of questions it answers), and optional step-by-step instructions for how the agent should respond. Topics are linked to Actions that the agent can execute.`,
40
+ title: "Create Agentforce Topic (GenAiPlugin)",
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.
42
+
43
+ Create topics AFTER creating agent actions (sf_create_agent_action) since topics reference actions by API name.
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.`,
27
48
  inputSchema: CreateAgentTopicSchema,
28
49
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
29
50
  }, async (params) => {
30
51
  const auth = await getAuth();
31
- const result = await createAgentTopic(auth, {
32
- agentName: params.agentName,
33
- topicName: params.topicName,
34
- label: params.label,
35
- description: params.description,
36
- scope: params.scope,
37
- instructions: params.instructions,
38
- actions: params.actions,
39
- });
52
+ const result = await createAgentTopic(auth, params);
40
53
  return resultContent(result);
41
54
  });
55
+
42
56
  server.registerTool("sf_create_agent_action", {
43
- title: "Create Agentforce Action",
44
- description: `Creates an Agentforce Action linked to a Flow, Apex class, or Prompt Template. Actions are the concrete steps an agent takes when handling a topic. The agent decides which action to invoke based on the user's request and the topic's instructions. Flow actions are the most common — create a Flow first, then link it as an agent action.`,
57
+ title: "Create Agentforce Action (GenAiFunction)",
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.
59
+
60
+ IMPORTANT: Deploys a complete GenAiFunction bundle including:
61
+ - The GenAiFunction metadata (invocationTarget, invocationTargetType, mappingAttributes)
62
+ - input/schema.json — the LLM-facing JSON schema for input parameters (REQUIRED for invocation)
63
+ - output/schema.json — the LLM-facing JSON schema for output parameters
64
+
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.
69
+
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).`,
45
73
  inputSchema: CreateAgentActionSchema,
46
74
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
47
75
  }, async (params) => {
48
76
  const auth = await getAuth();
49
77
  try {
50
- // Agent actions are created via the GenAiPlanner/GenAiFunctionDef metadata
51
- const { upsertMetadata, x } = await import("../services/salesforce.js");
52
- const inputsXml = (params.inputs ?? []).map((inp) => `
53
- <met:inputs>
54
- <met:name>${x(inp.name)}</met:name>
55
- <met:value>${x(inp.value)}</met:value>
56
- </met:inputs>`).join("\n");
57
- const xml = `<met:metadata xsi:type="met:GenAiFunction" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
58
- <met:fullName>${x(params.agentName)}.${x(params.topicName)}.${x(params.actionName)}</met:fullName>
59
- <met:masterLabel>${x(params.label)}</met:masterLabel>
60
- <met:description>${x(params.description)}</met:description>
61
- <met:type>${x(params.type)}</met:type>
62
- <met:functionRef>${x(params.reference)}</met:functionRef>
63
- ${inputsXml}
64
- </met:metadata>`;
65
- const result = await upsertMetadata(auth, xml);
78
+ const base64Zip = await buildGenAiActionZip({
79
+ actionName: params.actionName,
80
+ label: params.label,
81
+ description: params.description,
82
+ invocationTargetType: params.invocationTargetType,
83
+ flowApiName: params.flowApiName,
84
+ apexClassName: params.apexClassName,
85
+ inputs: params.inputs ?? [],
86
+ outputs: params.outputs ?? [],
87
+ }, API_VERSION);
88
+ const deployId = await deployZip(auth, base64Zip);
89
+ const result = await pollDeployStatus(auth, deployId, 120_000);
66
90
  return resultContent(result);
67
91
  }
68
92
  catch (err) {
69
93
  return resultContent({ success: false, message: err instanceof Error ? err.message : String(err) });
70
94
  }
71
95
  });
96
+
97
+ server.registerTool("sf_create_agent_planner", {
98
+ title: "Create Agentforce Planner Bundle (GenAiPlannerBundle)",
99
+ description: `Creates a GenAiPlannerBundle that connects topics to the agent's planning engine.
100
+
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.
102
+
103
+ Create the planner bundle AFTER creating all topics (sf_create_agent_topic) and BEFORE creating the agent (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).`,
107
+ inputSchema: CreateAgentPlannerSchema,
108
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
109
+ }, async (params) => {
110
+ const auth = await getAuth();
111
+ const result = await createAgentPlanner(auth, params);
112
+ return resultContent(result);
113
+ });
114
+
115
+ server.registerTool("sf_activate_agent", {
116
+ title: "Activate Agentforce Agent",
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: { type: "object", properties: { agentApiName: { type: "string", description: "API name of the agent to activate, e.g. 'CRM_Record_Creator'" } }, required: ["agentApiName"] },
119
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
120
+ }, async (params) => {
121
+ const auth = await getAuth();
122
+ const result = await activateAgent(auth, params.agentApiName);
123
+ return resultContent(result);
124
+ });
125
+
126
+ server.registerTool("sf_deactivate_agent", {
127
+ title: "Deactivate Agentforce Agent",
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.`,
129
+ inputSchema: { type: "object", properties: { agentApiName: { type: "string", description: "API name of the agent to deactivate, e.g. 'CRM_Record_Creator'" } }, required: ["agentApiName"] },
130
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
131
+ }, async (params) => {
132
+ const auth = await getAuth();
133
+ const result = await deactivateAgent(auth, params.agentApiName);
134
+ return resultContent(result);
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
+ });
72
284
  }
73
- //# sourceMappingURL=agentforce.js.map
285
+ //# sourceMappingURL=agentforce.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "salesforce-metadata-mcp",
3
- "version": "2.1.2",
3
+ "version": "2.1.4",
4
4
  "description": "The most comprehensive Salesforce metadata and development MCP server: 60+ tools for custom objects, flows, Apex, LWC, Agentforce, deployments, and more",
5
5
  "license": "MIT",
6
6
  "private": false,