@fluentcommerce/fluent-mcp-extn 0.7.0 → 0.7.2

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.
@@ -2,11 +2,11 @@
2
2
  * Batch Ingestion Tools
3
3
  *
4
4
  * Five batch lifecycle tools:
5
- * batch.create — create a batch ingestion job
6
- * batch.send — send records to an existing job
7
- * batch.status — check overall job status
8
- * batch.batchStatus — check a specific batch inside a job
9
- * batch.results — get per-record outcomes for a completed job
5
+ * batch_create — create a batch ingestion job
6
+ * batch_send — send records to an existing job
7
+ * batch_status — check overall job status
8
+ * batch_batchStatus — check a specific batch inside a job
9
+ * batch_results — get per-record outcomes for a completed job
10
10
  */
11
11
  import { z } from "zod";
12
12
  import { ToolError } from "./errors.js";
@@ -50,8 +50,8 @@ export const BatchDescribePayloadInputSchema = z.object({
50
50
  // ---------------------------------------------------------------------------
51
51
  export const BATCH_TOOL_DEFINITIONS = [
52
52
  {
53
- name: "batch.create",
54
- description: "Create a batch ingestion job. Next: batch.send -> batch.status -> batch.results.",
53
+ name: "batch_create",
54
+ description: "Create a batch ingestion job. Next: batch_send -> batch_status -> batch_results.",
55
55
  annotations: {
56
56
  title: "Create Batch Job",
57
57
  readOnlyHint: false,
@@ -61,8 +61,8 @@ export const BATCH_TOOL_DEFINITIONS = [
61
61
  },
62
62
  },
63
63
  {
64
- name: "batch.send",
65
- description: "Send records to an existing batch job. Requires jobId from batch.create.",
64
+ name: "batch_send",
65
+ description: "Send records to an existing batch job. Requires jobId from batch_create.",
66
66
  annotations: {
67
67
  title: "Send Batch",
68
68
  readOnlyHint: false,
@@ -72,7 +72,7 @@ export const BATCH_TOOL_DEFINITIONS = [
72
72
  },
73
73
  },
74
74
  {
75
- name: "batch.status",
75
+ name: "batch_status",
76
76
  description: "Check overall job status for polling loops. Repeat until terminal status.",
77
77
  annotations: {
78
78
  title: "Batch Job Status",
@@ -83,7 +83,7 @@ export const BATCH_TOOL_DEFINITIONS = [
83
83
  },
84
84
  },
85
85
  {
86
- name: "batch.batchStatus",
86
+ name: "batch_batchStatus",
87
87
  description: "Check a specific batch inside a job (jobId + batchId). Use for troubleshooting partial failures.",
88
88
  annotations: {
89
89
  title: "Batch Status",
@@ -94,7 +94,7 @@ export const BATCH_TOOL_DEFINITIONS = [
94
94
  },
95
95
  },
96
96
  {
97
- name: "batch.results",
97
+ name: "batch_results",
98
98
  description: "Get per-record batch outcomes for a completed job.",
99
99
  annotations: {
100
100
  title: "Batch Results",
@@ -105,8 +105,8 @@ export const BATCH_TOOL_DEFINITIONS = [
105
105
  },
106
106
  },
107
107
  {
108
- name: "batch.describePayload",
109
- description: "Returns entity-type-specific payload shape, field requirements, and example rows for batch.send. No API call.",
108
+ name: "batch_describePayload",
109
+ description: "Returns entity-type-specific payload shape, field requirements, and example rows for batch_send. No API call.",
110
110
  annotations: {
111
111
  title: "Describe Batch Payload",
112
112
  readOnlyHint: true,
@@ -121,7 +121,7 @@ export const BATCH_TOOL_DEFINITIONS = [
121
121
  // ---------------------------------------------------------------------------
122
122
  function requireClient(ctx) {
123
123
  if (!ctx.client) {
124
- throw new ToolError("CONFIG_ERROR", "SDK client is not available. Run config.validate and fix auth/base URL.");
124
+ throw new ToolError("CONFIG_ERROR", "SDK client is not available. Run config_validate and fix auth/base URL.");
125
125
  }
126
126
  return ctx.client;
127
127
  }
@@ -221,7 +221,7 @@ const BATCH_PAYLOAD_DESCRIPTORS = {
221
221
  // Handlers
222
222
  // ---------------------------------------------------------------------------
223
223
  /**
224
- * Handle batch.create tool call.
224
+ * Handle batch_create tool call.
225
225
  */
226
226
  export async function handleBatchCreate(args, ctx) {
227
227
  const client = requireClient(ctx);
@@ -243,7 +243,7 @@ export async function handleBatchCreate(args, ctx) {
243
243
  return { ok: true, job: result };
244
244
  }
245
245
  /**
246
- * Handle batch.send tool call.
246
+ * Handle batch_send tool call.
247
247
  */
248
248
  export async function handleBatchSend(args, ctx) {
249
249
  const client = requireClient(ctx);
@@ -254,7 +254,7 @@ export async function handleBatchSend(args, ctx) {
254
254
  return { ok: true, batch: result };
255
255
  }
256
256
  /**
257
- * Handle batch.status tool call.
257
+ * Handle batch_status tool call.
258
258
  */
259
259
  export async function handleBatchStatus(args, ctx) {
260
260
  const client = requireClient(ctx);
@@ -263,7 +263,7 @@ export async function handleBatchStatus(args, ctx) {
263
263
  return { ok: true, status: result };
264
264
  }
265
265
  /**
266
- * Handle batch.batchStatus tool call.
266
+ * Handle batch_batchStatus tool call.
267
267
  */
268
268
  export async function handleBatchBatchStatus(args, ctx) {
269
269
  const client = requireClient(ctx);
@@ -272,7 +272,7 @@ export async function handleBatchBatchStatus(args, ctx) {
272
272
  return { ok: true, batchStatus: result };
273
273
  }
274
274
  /**
275
- * Handle batch.results tool call.
275
+ * Handle batch_results tool call.
276
276
  */
277
277
  export async function handleBatchResults(args, ctx) {
278
278
  const client = requireClient(ctx);
@@ -281,7 +281,7 @@ export async function handleBatchResults(args, ctx) {
281
281
  return { ok: true, results: result };
282
282
  }
283
283
  /**
284
- * Handle batch.describePayload tool call.
284
+ * Handle batch_describePayload tool call.
285
285
  */
286
286
  export async function handleBatchDescribePayload(args, _ctx) {
287
287
  const parsed = BatchDescribePayloadInputSchema.parse(args);
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Cache management tools: cache.status and cache.clear.
2
+ * Cache management tools: cache_status and cache_clear.
3
3
  * No API calls — operates on local filesystem cache only.
4
4
  */
5
5
  import { z } from "zod";
@@ -13,14 +13,14 @@ export const CacheClearInputSchema = z.object({
13
13
  pattern: z
14
14
  .string()
15
15
  .optional()
16
- .describe('Glob pattern to clear. Omit to clear all. Examples: "workflow:*", "setting:get:fc.mystique.*", "introspect:*".'),
16
+ .describe('Glob pattern to clear. Omit to clear all. Examples: "workflow:get:*", "workflow:list:*", "setting:get:fc.mystique.*", "plugin:list:*", "introspect:*".'),
17
17
  });
18
18
  // ---------------------------------------------------------------------------
19
19
  // Tool definitions
20
20
  // ---------------------------------------------------------------------------
21
21
  export const CACHE_TOOL_DEFINITIONS = [
22
22
  {
23
- name: "cache.status",
23
+ name: "cache_status",
24
24
  description: "Local cache statistics: entry count, size, hit/miss rate, per-category breakdown.",
25
25
  annotations: {
26
26
  title: "Cache Status",
@@ -31,8 +31,8 @@ export const CACHE_TOOL_DEFINITIONS = [
31
31
  },
32
32
  },
33
33
  {
34
- name: "cache.clear",
35
- description: "Clear cached entries by pattern (e.g. workflow:*, setting:get:*, introspect:*) or all.",
34
+ name: "cache_clear",
35
+ description: "Clear cached entries by pattern (e.g. workflow:get:*, workflow:list:*, setting:get:*, plugin:list:*, introspect:*) or all.",
36
36
  annotations: {
37
37
  title: "Clear Cache",
38
38
  readOnlyHint: false,
package/dist/config.js CHANGED
@@ -345,7 +345,7 @@ export function loadConfigForProfile(profileName, retailer, base) {
345
345
  }
346
346
  /**
347
347
  * Build a RuntimeConfig from explicit credentials (not a CLI profile).
348
- * Used by connection.add when the caller provides baseUrl + OAuth/token.
348
+ * Used by connection_add when the caller provides baseUrl + OAuth/token.
349
349
  */
350
350
  export function buildExplicitConfig(params, base) {
351
351
  return {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Entity lifecycle tools: entity.create, entity.update, entity.get
2
+ * Entity lifecycle tools: entity_create, entity_update, entity_get
3
3
  *
4
4
  * Type-safe entity CRUD with built-in validation, gotcha knowledge,
5
5
  * and audit trail for agentic workflows.
@@ -63,7 +63,7 @@ export const EntityPlanUpdateInputSchema = z.object({
63
63
  // ---------------------------------------------------------------------------
64
64
  export const ENTITY_TOOL_DEFINITIONS = [
65
65
  {
66
- name: "entity.create",
66
+ name: "entity_create",
67
67
  description: `Type-safe entity creation with field validation, compound key encoding, and retailerId auto-resolve. Supports dryRun. Types: ${SUPPORTED_ENTITY_TYPES.join(", ")}`,
68
68
  annotations: {
69
69
  title: "Create Entity",
@@ -74,8 +74,8 @@ export const ENTITY_TOOL_DEFINITIONS = [
74
74
  },
75
75
  },
76
76
  {
77
- name: "entity.update",
78
- description: "Update entity fields/status. Optional validateTransition checks workflow.transitions before status change.",
77
+ name: "entity_update",
78
+ description: "Update entity fields/status. Optional validateTransition checks workflow_transitions before status change.",
79
79
  annotations: {
80
80
  title: "Update Entity",
81
81
  readOnlyHint: false,
@@ -85,7 +85,7 @@ export const ENTITY_TOOL_DEFINITIONS = [
85
85
  },
86
86
  },
87
87
  {
88
- name: "entity.get",
88
+ name: "entity_get",
89
89
  description: "Look up an entity by ID or ref. Use includeEdges to fetch related entities.",
90
90
  annotations: {
91
91
  title: "Get Entity",
@@ -96,7 +96,7 @@ export const ENTITY_TOOL_DEFINITIONS = [
96
96
  },
97
97
  },
98
98
  {
99
- name: "entity.planCreate",
99
+ name: "entity_planCreate",
100
100
  description: `Plan an entity creation. Returns required fields, variables skeleton, GraphQL mutation, gotchas, and auto-inject info. No API call. Types: ${SUPPORTED_ENTITY_TYPES.join(", ")}.`,
101
101
  annotations: {
102
102
  title: "Plan Entity Create",
@@ -107,8 +107,8 @@ export const ENTITY_TOOL_DEFINITIONS = [
107
107
  },
108
108
  },
109
109
  {
110
- name: "entity.planUpdate",
111
- description: `Plan an entity update. Returns updatable fields, variables skeleton, GraphQL mutation, gotchas, and status-change guidance. No API call. Types: ${SUPPORTED_ENTITY_TYPES.join(", ")}.`,
110
+ name: "entity_planUpdate",
111
+ description: `Plan an entity_update. Returns updatable fields, variables skeleton, GraphQL mutation, gotchas, and status-change guidance. No API call. Types: ${SUPPORTED_ENTITY_TYPES.join(", ")}.`,
112
112
  annotations: {
113
113
  title: "Plan Entity Update",
114
114
  readOnlyHint: true,
@@ -120,7 +120,7 @@ export const ENTITY_TOOL_DEFINITIONS = [
120
120
  ];
121
121
  function requireEntityClient(ctx) {
122
122
  if (!ctx.client) {
123
- throw new ToolError("CONFIG_ERROR", "SDK client is not available. Run config.validate and fix auth/base URL.");
123
+ throw new ToolError("CONFIG_ERROR", "SDK client is not available. Run config_validate and fix auth/base URL.");
124
124
  }
125
125
  return ctx.client;
126
126
  }
@@ -178,7 +178,7 @@ function optionalPlanFields(meta) {
178
178
  return Array.from(base);
179
179
  }
180
180
  /**
181
- * Handle entity.create tool call.
181
+ * Handle entity_create tool call.
182
182
  */
183
183
  export async function handleEntityCreate(args, ctx) {
184
184
  const parsed = EntityCreateInputSchema.parse(args);
@@ -245,7 +245,7 @@ export async function handleEntityCreate(args, ctx) {
245
245
  };
246
246
  }
247
247
  /**
248
- * Handle entity.update tool call.
248
+ * Handle entity_update tool call.
249
249
  */
250
250
  export async function handleEntityUpdate(args, ctx) {
251
251
  const parsed = EntityUpdateInputSchema.parse(args);
@@ -326,7 +326,7 @@ export async function handleEntityUpdate(args, ctx) {
326
326
  };
327
327
  }
328
328
  /**
329
- * Handle entity.get tool call.
329
+ * Handle entity_get tool call.
330
330
  */
331
331
  export async function handleEntityGet(args, ctx) {
332
332
  const parsed = EntityGetInputSchema.parse(args);
@@ -399,7 +399,7 @@ export async function handleEntityGet(args, ctx) {
399
399
  };
400
400
  }
401
401
  /**
402
- * Handle entity.planCreate tool call.
402
+ * Handle entity_planCreate tool call.
403
403
  */
404
404
  export function handleEntityPlanCreate(args, ctx) {
405
405
  const parsed = EntityPlanCreateInputSchema.parse(args);
@@ -454,7 +454,7 @@ export function handleEntityPlanCreate(args, ctx) {
454
454
  };
455
455
  }
456
456
  /**
457
- * Handle entity.planUpdate tool call.
457
+ * Handle entity_planUpdate tool call.
458
458
  */
459
459
  export function handleEntityPlanUpdate(args, _ctx) {
460
460
  const parsed = EntityPlanUpdateInputSchema.parse(args);
@@ -498,8 +498,8 @@ export function handleEntityPlanUpdate(args, _ctx) {
498
498
  hasRef: meta.hasRef,
499
499
  },
500
500
  _hints: [
501
- "Required input field: id (ID!). Add status, attributes, and any other changed fields before calling entity.update.",
502
- "Use entity.update for direct mutations. When changing status, validateTransition can warn when no workflow transition exists from the current state.",
501
+ "Required input field: id (ID!). Add status, attributes, and any other changed fields before calling entity_update.",
502
+ "Use entity_update for direct mutations. When changing status, validateTransition can warn when no workflow transition exists from the current state.",
503
503
  ],
504
504
  };
505
505
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Environment tools: environment.discover, environment.validate
2
+ * Environment tools: environment_discover, environment_validate
3
3
  *
4
4
  * Closes the setup loop — single-call environment snapshot and pre-flight checks
5
5
  * for agentic workflows that need to understand what they're working with.
@@ -44,7 +44,7 @@ export const EnvironmentValidateInputSchema = z.object({
44
44
  // ---------------------------------------------------------------------------
45
45
  export const ENVIRONMENT_TOOL_DEFINITIONS = [
46
46
  {
47
- name: "environment.discover",
47
+ name: "environment_discover",
48
48
  description: [
49
49
  "Full environment snapshot. Sections opt-in via include array: retailer, locations, networks, catalogues, workflows, settings, modules, users.",
50
50
  "Returns refs, IDs, statuses — enough to construct valid mutations. First 100 items per section.",
@@ -58,7 +58,7 @@ export const ENVIRONMENT_TOOL_DEFINITIONS = [
58
58
  },
59
59
  },
60
60
  {
61
- name: "environment.validate",
61
+ name: "environment_validate",
62
62
  description: "Pre-flight validation checks: auth, retailer, locations, inventory, workflows, settings, modules. Returns pass/fail per check with severity.",
63
63
  annotations: {
64
64
  title: "Validate Environment",
@@ -123,7 +123,7 @@ const QUERIES = {
123
123
  };
124
124
  function requireEnvClient(ctx) {
125
125
  if (!ctx.client) {
126
- throw new ToolError("CONFIG_ERROR", "SDK client is not available. Run config.validate and fix auth/base URL.");
126
+ throw new ToolError("CONFIG_ERROR", "SDK client is not available. Run config_validate and fix auth/base URL.");
127
127
  }
128
128
  return ctx.client;
129
129
  }
@@ -152,7 +152,7 @@ function extractNodes(connection) {
152
152
  .filter((n) => n !== null);
153
153
  }
154
154
  /**
155
- * Handle environment.discover tool call.
155
+ * Handle environment_discover tool call.
156
156
  */
157
157
  export async function handleEnvironmentDiscover(args, ctx) {
158
158
  const parsed = EnvironmentDiscoverInputSchema.parse(args);
@@ -187,7 +187,7 @@ export async function handleEnvironmentDiscover(args, ctx) {
187
187
  const locPageInfo = data.locations?.pageInfo;
188
188
  result.locations = locationNodes;
189
189
  if (locPageInfo?.hasNextPage) {
190
- result.locationsNote = `Showing first ${locationNodes.length} locations. Use graphql.queryAll for complete list.`;
190
+ result.locationsNote = `Showing first ${locationNodes.length} locations. Use graphql_queryAll for complete list.`;
191
191
  }
192
192
  break;
193
193
  }
@@ -211,7 +211,7 @@ export async function handleEnvironmentDiscover(args, ctx) {
211
211
  }
212
212
  case "workflows": {
213
213
  // Workflows are not directly queryable via standard GraphQL.
214
- // We use the workflow.transitions API to discover deployed workflow types.
214
+ // We use the workflow_transitions API to discover deployed workflow types.
215
215
  const retailerId = ctx.config.retailerId;
216
216
  if (retailerId) {
217
217
  try {
@@ -259,12 +259,12 @@ export async function handleEnvironmentDiscover(args, ctx) {
259
259
  const settPageInfo = data.settings?.pageInfo;
260
260
  result.settings = settingNodes;
261
261
  if (settPageInfo?.hasNextPage) {
262
- result.settingsNote = `Showing first ${settingNodes.length} settings. Use graphql.queryAll for complete list.`;
262
+ result.settingsNote = `Showing first ${settingNodes.length} settings. Use graphql_queryAll for complete list.`;
263
263
  }
264
264
  break;
265
265
  }
266
266
  case "modules": {
267
- // Modules discoverable via plugin.list
267
+ // Modules discoverable via plugin_list
268
268
  try {
269
269
  const plugins = await client.getPlugins();
270
270
  if (plugins && typeof plugins === "object") {
@@ -305,7 +305,7 @@ export async function handleEnvironmentDiscover(args, ctx) {
305
305
  return { ok: true, ...result };
306
306
  }
307
307
  /**
308
- * Handle environment.validate tool call.
308
+ * Handle environment_validate tool call.
309
309
  */
310
310
  export async function handleEnvironmentValidate(args, ctx) {
311
311
  const parsed = EnvironmentValidateInputSchema.parse(args);
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Event lifecycle tools: event.build, event.send, event.get, event.list, event.flowInspect
2
+ * Event lifecycle tools: event_build, event_send, event_get, event_list, event_flowInspect
3
3
  *
4
4
  * Extracted from tools.ts — follows the same pattern as entity-tools.ts.
5
5
  */
@@ -46,7 +46,7 @@ export const EventPublishInputSchema = z.object({
46
46
  export const EventGetInputSchema = z.object({
47
47
  eventId: z.string().min(1),
48
48
  });
49
- /** Tool input for event.list. Supports canonical keys and backward-compatible aliases. */
49
+ /** Tool input for event_list. Supports canonical keys and backward-compatible aliases. */
50
50
  export const EventListInputSchema = z
51
51
  .object({
52
52
  // Canonical keys (SDK FluentEventQueryParams)
@@ -113,31 +113,31 @@ export const EventFlowInspectInputSchema = z.object({
113
113
  // ---------------------------------------------------------------------------
114
114
  export const EVENT_TOOL_DEFINITIONS = [
115
115
  {
116
- name: "event.build",
117
- description: "Payload-only builder for Event API input. Use before event.send to verify defaults and required context (no API call).",
116
+ name: "event_build",
117
+ description: "Payload-only builder for Event API input. Use before event_send to verify defaults and required context (no API call).",
118
118
  schema: EventBuildInputSchema,
119
119
  annotations: { title: "Build Event Payload", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
120
120
  },
121
121
  {
122
- name: "event.send",
123
- description: "Builds and sends a Fluent event (supports dryRun). dryRun=true includes _workflowPreview with matching rulesets, predicted target status, and rule analysis when workflow is cached. Call workflow.get first for full preview.",
122
+ name: "event_send",
123
+ description: "Builds and sends a Fluent event (supports dryRun). dryRun=true includes _workflowPreview with matching rulesets, predicted target status, and rule analysis when workflow is cached. Call workflow_get first for full preview.",
124
124
  schema: EventPublishInputSchema,
125
125
  annotations: { title: "Send Event", readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
126
126
  },
127
127
  {
128
- name: "event.get",
129
- description: "Fetch one event by ID. Use after event.send if response contains eventId, or after event.list resolves eventId.",
128
+ name: "event_get",
129
+ description: "Fetch one event by ID. Use after event_send if response contains eventId, or after event_list resolves eventId.",
130
130
  schema: EventGetInputSchema,
131
131
  annotations: { title: "Get Event", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
132
132
  },
133
133
  {
134
- name: "event.list",
135
- description: "List/filter events via SDK getEvents with filters and pagination. Use to verify sends, discover eventId, then call event.get for full details.",
134
+ name: "event_list",
135
+ description: "List/filter events via SDK getEvents with filters and pagination. Use to verify sends, discover eventId, then call event_get for full details.",
136
136
  schema: EventListInputSchema,
137
137
  annotations: { title: "List Events", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
138
138
  },
139
139
  {
140
- name: "event.flowInspect",
140
+ name: "event_flowInspect",
141
141
  description: "One-call event forensics for a root entity. Collects ORCHESTRATION events, extracts timelines, mutations, webhooks, NO_MATCH diagnostics. Compact mode (default) returns pre-analyzed summary.",
142
142
  schema: EventFlowInspectInputSchema,
143
143
  annotations: { title: "Inspect Event Flow", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
@@ -145,7 +145,7 @@ export const EVENT_TOOL_DEFINITIONS = [
145
145
  ];
146
146
  function requireEventClient(ctx) {
147
147
  if (!ctx.client) {
148
- throw new ToolError("CONFIG_ERROR", "SDK client is not available. Run config.validate and fix auth/base URL.");
148
+ throw new ToolError("CONFIG_ERROR", "SDK client is not available. Run config_validate and fix auth/base URL.");
149
149
  }
150
150
  return ctx.client;
151
151
  }
@@ -255,7 +255,7 @@ export function toEventQueryParams(input) {
255
255
  // Handlers
256
256
  // ---------------------------------------------------------------------------
257
257
  /**
258
- * Handle event.build tool call.
258
+ * Handle event_build tool call.
259
259
  */
260
260
  export async function handleEventBuild(args, ctx) {
261
261
  const parsed = EventBuildInputSchema.parse(args);
@@ -268,14 +268,14 @@ export async function handleEventBuild(args, ctx) {
268
268
  export function buildEventDryRunEnrichment(eventName, entityType, entitySubtype, cache, retailerId) {
269
269
  const hints = [];
270
270
  if (!cache || !retailerId) {
271
- hints.push("Call workflow.get with entityType + entitySubtype to enable workflow simulation preview in dryRun.");
271
+ hints.push("Call workflow_get with entityType + entitySubtype to enable workflow simulation preview in dryRun.");
272
272
  return { _hints: hints };
273
273
  }
274
274
  const subtype = entitySubtype ?? "DEFAULT";
275
275
  const cacheKey = `workflow:get:${retailerId}:${entityType}::${subtype}:latest`;
276
276
  const cached = cache.get(cacheKey);
277
277
  if (!cached.hit || !cached.data || typeof cached.data !== "object") {
278
- hints.push(`No cached workflow for ${entityType}::${subtype}. Call workflow.get first to enable simulation preview.`);
278
+ hints.push(`No cached workflow for ${entityType}::${subtype}. Call workflow_get first to enable simulation preview.`);
279
279
  return { _hints: hints };
280
280
  }
281
281
  const wf = cached.data;
@@ -346,7 +346,7 @@ export function buildEventDryRunEnrichment(eventName, entityType, entitySubtype,
346
346
  });
347
347
  }
348
348
  if (matchingRulesets.length === 0) {
349
- hints.push(`No ruleset named "${eventName}" found in workflow ${entityType}::${subtype}. The event may trigger by entity status match instead. Use workflow.simulate for full analysis.`);
349
+ hints.push(`No ruleset named "${eventName}" found in workflow ${entityType}::${subtype}. The event may trigger by entity status match instead. Use workflow_simulate for full analysis.`);
350
350
  return { _hints: hints };
351
351
  }
352
352
  // Deduplicated aggregates
@@ -371,7 +371,7 @@ export function buildEventDryRunEnrichment(eventName, entityType, entitySubtype,
371
371
  if (uniqueFollowOnEvents.length > 0) {
372
372
  hints.push(`Follow-on event(s) will be emitted: ${uniqueFollowOnEvents.join(", ")}.`);
373
373
  }
374
- hints.push("Use event.flowInspect after sending to verify execution results.");
374
+ hints.push("Use event_flowInspect after sending to verify execution results.");
375
375
  return {
376
376
  _workflowPreview: {
377
377
  workflowName: wf.name ?? `${entityType}::${subtype}`,
@@ -388,7 +388,7 @@ export function buildEventDryRunEnrichment(eventName, entityType, entitySubtype,
388
388
  };
389
389
  }
390
390
  /**
391
- * Handle event.send tool call.
391
+ * Handle event_send tool call.
392
392
  */
393
393
  export async function handleEventSend(args, ctx) {
394
394
  const parsed = EventPublishInputSchema.parse(args);
@@ -406,7 +406,7 @@ export async function handleEventSend(args, ctx) {
406
406
  : {}),
407
407
  _hints: [
408
408
  ...enrichment._hints,
409
- "Use workflow.simulate with currentStatus + eventName for full static analysis.",
409
+ "Use workflow_simulate with currentStatus + eventName for full static analysis.",
410
410
  ],
411
411
  };
412
412
  }
@@ -419,17 +419,17 @@ export async function handleEventSend(args, ctx) {
419
419
  _hints: parsed.mode === "async"
420
420
  ? [
421
421
  "Event sent asynchronously. Processing happens in the background.",
422
- "Use event.list with context.entityRef + name to find the event and check eventStatus.",
423
- "Use event.flowInspect with rootEntityRef for full execution forensics.",
422
+ "Use event_list with context.entityRef + name to find the event and check eventStatus.",
423
+ "Use event_flowInspect with rootEntityRef for full execution forensics.",
424
424
  ]
425
425
  : [
426
- "Use event.get with the returned eventId to check detailed status.",
427
- "Use event.flowInspect with rootEntityRef for full execution forensics.",
426
+ "Use event_get with the returned eventId to check detailed status.",
427
+ "Use event_flowInspect with rootEntityRef for full execution forensics.",
428
428
  ],
429
429
  };
430
430
  }
431
431
  /**
432
- * Handle event.get tool call.
432
+ * Handle event_get tool call.
433
433
  */
434
434
  export async function handleEventGet(args, ctx) {
435
435
  const parsed = EventGetInputSchema.parse(args);
@@ -438,7 +438,7 @@ export async function handleEventGet(args, ctx) {
438
438
  return { ok: true, event };
439
439
  }
440
440
  /**
441
- * Handle event.list tool call.
441
+ * Handle event_list tool call.
442
442
  */
443
443
  export async function handleEventList(args, ctx) {
444
444
  const parsed = EventListInputSchema.parse(args);
@@ -476,7 +476,7 @@ export async function handleEventList(args, ctx) {
476
476
  return { ok: true, events };
477
477
  }
478
478
  /**
479
- * Handle event.flowInspect tool call.
479
+ * Handle event_flowInspect tool call.
480
480
  */
481
481
  export async function handleEventFlowInspect(args, ctx) {
482
482
  const parsed = EventFlowInspectInputSchema.parse(args);
@@ -170,6 +170,26 @@ export class FluentClientAdapter {
170
170
  return FluentClientAdapter.unwrapResponse(response);
171
171
  });
172
172
  }
173
+ /**
174
+ * Upload (deploy) a workflow JSON to the Fluent environment.
175
+ * PUT /api/v4.1/workflow/{retailerId}
176
+ * Uses PUT for idempotent upsert — replaces the workflow definition
177
+ * rather than creating a new version on every call (which POST does).
178
+ * Write operation — executeOnce (timeout, no retry).
179
+ *
180
+ * NOTE: This does NOT update the CLI workflowlog cache.
181
+ */
182
+ async uploadWorkflow(retailerId, workflow) {
183
+ const requestClient = this.requireRequestClient("Workflow REST upload API");
184
+ return this.executeOnce("uploadWorkflow", async () => {
185
+ const response = await requestClient.request(`/api/v4.1/workflow/${retailerId}`, {
186
+ method: "PUT",
187
+ headers: { "Content-Type": "application/json" },
188
+ body: workflow,
189
+ });
190
+ return FluentClientAdapter.unwrapResponse(response);
191
+ });
192
+ }
173
193
  /**
174
194
  * Query Prometheus metrics via GraphQL metricInstant / metricRange queries.
175
195
  * The Fluent platform does NOT expose raw Prometheus REST endpoints