@fluentcommerce/fluent-mcp-extn 0.7.0 → 0.7.3

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,12 +2,12 @@
2
2
  * Multi-profile registry for simultaneous Fluent Commerce environment access.
3
3
  *
4
4
  * Supports lazy client initialization, runtime profile management
5
- * (connection.add/remove/list/switch), and per-tool-call profile resolution
5
+ * (connection_add/remove/list/switch), and per-tool-call profile resolution
6
6
  * via the optional `profile` parameter on every tool.
7
7
  *
8
8
  * Profiles can be:
9
9
  * - Pre-loaded at startup via FLUENT_ADDITIONAL_PROFILES env var (comma-separated names)
10
- * - Added at runtime via the connection.add tool
10
+ * - Added at runtime via the connection_add tool
11
11
  */
12
12
  import { detectAuthStrategy, loadConfigForProfile, buildExplicitConfig, } from "./config.js";
13
13
  import { initSDKClient } from "./sdk-client.js";
@@ -143,7 +143,7 @@ function deepShape(value, budget, stats) {
143
143
  // Object
144
144
  const obj = value;
145
145
  const keys = Object.keys(obj);
146
- // Summarize large objects (e.g., plugin.list with 597 rule keys)
146
+ // Summarize large objects (e.g., plugin_list with 597 rule keys)
147
147
  if (keys.length > budget.maxArrayElements && !hasProtectedStructure(obj)) {
148
148
  return summarizeObject(obj, keys, budget.sampleSize, stats);
149
149
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Settings management tools: setting.upsert, setting.bulkUpsert
2
+ * Settings management tools: setting_upsert, setting_bulkUpsert
3
3
  *
4
4
  * Closes the configuration loop — agents can create and update
5
5
  * settings that workflows depend on (webhook URLs, feature flags, thresholds).
@@ -63,9 +63,9 @@ export const SettingGetInputSchema = z.object({
63
63
  .number()
64
64
  .int()
65
65
  .min(1)
66
- .max(100)
67
- .default(10)
68
- .describe("Max results (default: 10, max: 100)."),
66
+ .max(500)
67
+ .default(25)
68
+ .describe("Max results per page (default: 25, max: 500). Auto-paginates wildcard queries using edge cursors."),
69
69
  digest: z
70
70
  .boolean()
71
71
  .default(false)
@@ -84,7 +84,7 @@ export const SettingBulkUpsertInputSchema = z.object({
84
84
  // ---------------------------------------------------------------------------
85
85
  export const SETTING_TOOL_DEFINITIONS = [
86
86
  {
87
- name: "setting.get",
87
+ name: "setting_get",
88
88
  description: [
89
89
  "Fetch settings by name (% wildcards supported). Use outputFile to save large values to disk.",
90
90
  "Use digest=true for structural summary instead of raw content (Mystique-aware).",
@@ -98,7 +98,7 @@ export const SETTING_TOOL_DEFINITIONS = [
98
98
  },
99
99
  },
100
100
  {
101
- name: "setting.upsert",
101
+ name: "setting_upsert",
102
102
  description: "Create or update a setting. Use lobValue for large JSON. For fc.mystique.* settings, set valueType: \"JSON\".",
103
103
  annotations: {
104
104
  title: "Upsert Setting",
@@ -109,7 +109,7 @@ export const SETTING_TOOL_DEFINITIONS = [
109
109
  },
110
110
  },
111
111
  {
112
- name: "setting.bulkUpsert",
112
+ name: "setting_bulkUpsert",
113
113
  description: "Batch upsert up to 50 settings in one call. Returns per-setting results.",
114
114
  annotations: {
115
115
  title: "Bulk Upsert Settings",
@@ -122,7 +122,7 @@ export const SETTING_TOOL_DEFINITIONS = [
122
122
  ];
123
123
  function requireSettingClient(ctx) {
124
124
  if (!ctx.client) {
125
- throw new ToolError("CONFIG_ERROR", "SDK client is not available. Run config.validate and fix auth/base URL.");
125
+ throw new ToolError("CONFIG_ERROR", "SDK client is not available. Run config_validate and fix auth/base URL.");
126
126
  }
127
127
  return ctx.client;
128
128
  }
@@ -168,7 +168,7 @@ function isWildcardSettingName(name) {
168
168
  }
169
169
  function buildSettingGetCacheKey(input) {
170
170
  return [
171
- "setting:get",
171
+ "setting_get",
172
172
  input.name,
173
173
  input.context ?? "_all",
174
174
  input.contextId !== undefined ? String(input.contextId) : "_all",
@@ -320,7 +320,10 @@ function digestSetting(setting) {
320
320
  digest,
321
321
  };
322
322
  }
323
- function buildSettingGetResponse(parsed, settings, cacheMeta) {
323
+ function buildSettingGetResponse(parsed, settings, cacheMeta, truncated = false) {
324
+ const truncationWarning = truncated
325
+ ? `Results truncated at ${settings.length} settings. More exist on the server. Use graphql_queryAll with a settings query to fetch all, or narrow your filter.`
326
+ : undefined;
324
327
  if (settings.length === 0) {
325
328
  return {
326
329
  ok: true,
@@ -370,6 +373,7 @@ function buildSettingGetResponse(parsed, settings, cacheMeta) {
370
373
  count: settings.length,
371
374
  savedTo: outputDir,
372
375
  files: savedFiles,
376
+ ...(truncationWarning ? { warning: truncationWarning } : {}),
373
377
  ...(cacheMeta ? { _cache: cacheMeta } : {}),
374
378
  settings: settings.map((s) => ({
375
379
  id: s.id,
@@ -387,6 +391,7 @@ function buildSettingGetResponse(parsed, settings, cacheMeta) {
387
391
  ok: true,
388
392
  count: settings.length,
389
393
  digested: true,
394
+ ...(truncationWarning ? { warning: truncationWarning } : {}),
390
395
  ...(cacheMeta ? { _cache: cacheMeta } : {}),
391
396
  settings: settings.map((s) => digestSetting(s)),
392
397
  };
@@ -394,6 +399,7 @@ function buildSettingGetResponse(parsed, settings, cacheMeta) {
394
399
  return {
395
400
  ok: true,
396
401
  count: settings.length,
402
+ ...(truncationWarning ? { warning: truncationWarning } : {}),
397
403
  ...(cacheMeta ? { _cache: cacheMeta } : {}),
398
404
  settings: settings.map((s) => ({
399
405
  id: s.id,
@@ -554,7 +560,7 @@ async function updateSetting(client, input) {
554
560
  return setting;
555
561
  }
556
562
  /**
557
- * Handle setting.get tool call.
563
+ * Handle setting_get tool call.
558
564
  * Fetches settings by name (with wildcard support) and optionally writes to file.
559
565
  */
560
566
  export async function handleSettingGet(args, ctx) {
@@ -565,7 +571,7 @@ export async function handleSettingGet(args, ctx) {
565
571
  if (cacheable && ctx.cache) {
566
572
  const cached = await ctx.cache.get(cacheKey);
567
573
  if (cached.hit && Array.isArray(cached.data)) {
568
- return buildSettingGetResponse(parsed, cached.data, { hit: true, ageMs: cached.ageMs ?? 0 });
574
+ return buildSettingGetResponse(parsed, cached.data, { hit: true, ageMs: cached.ageMs ?? 0 }, false);
569
575
  }
570
576
  }
571
577
  // Build variables — only include context/contextId if provided
@@ -585,10 +591,11 @@ export async function handleSettingGet(args, ctx) {
585
591
  else if (parsed.context === "ACCOUNT") {
586
592
  variables.contextId = [0];
587
593
  }
588
- // Build query — include lobValue for full content
589
- const query = `query GetSettings($name: [String!], $context: [String!], $contextId: [Int!], $first: Int) {
590
- settings(name: $name, context: $context, contextId: $contextId, first: $first) {
594
+ // Build query — edge cursors for Fluent-style pagination
595
+ const query = `query GetSettings($name: [String!], $context: [String!], $contextId: [Int!], $first: Int, $after: String) {
596
+ settings(name: $name, context: $context, contextId: $contextId, first: $first, after: $after) {
591
597
  edges {
598
+ cursor
592
599
  node {
593
600
  id
594
601
  name
@@ -599,23 +606,52 @@ export async function handleSettingGet(args, ctx) {
599
606
  valueType
600
607
  }
601
608
  }
609
+ pageInfo {
610
+ hasNextPage
611
+ }
602
612
  }
603
613
  }`;
604
- const response = await client.graphql({
605
- query,
606
- variables: variables,
607
- });
608
- const data = response?.data;
609
- const connection = data?.settings;
610
- const edges = (connection?.edges ?? []);
611
- const settings = edges.map((e) => e.node);
614
+ // Auto-paginate wildcard queries using edge cursors; single-page for exact names
615
+ const MAX_TOTAL = 500;
616
+ const isWildcard = isWildcardSettingName(parsed.name);
617
+ const allSettings = [];
618
+ let cursor = null;
619
+ let hasNextPage = true;
620
+ while (hasNextPage && allSettings.length < MAX_TOTAL) {
621
+ const pageVars = { ...variables };
622
+ if (cursor)
623
+ pageVars.after = cursor;
624
+ const response = await client.graphql({
625
+ query,
626
+ variables: pageVars,
627
+ });
628
+ const data = response?.data;
629
+ const connection = data?.settings;
630
+ const edges = (connection?.edges ?? []);
631
+ const pageInfo = connection?.pageInfo;
632
+ for (const edge of edges) {
633
+ if (allSettings.length >= MAX_TOTAL)
634
+ break;
635
+ allSettings.push(edge.node);
636
+ }
637
+ hasNextPage = Boolean(pageInfo?.hasNextPage) && edges.length > 0;
638
+ // Fluent pagination: cursor lives on the edge, not in pageInfo
639
+ if (hasNextPage && edges.length > 0) {
640
+ cursor = edges[edges.length - 1].cursor ?? null;
641
+ }
642
+ // Non-wildcard: one page is enough
643
+ if (!isWildcard)
644
+ break;
645
+ }
646
+ const settings = allSettings;
647
+ const truncated = hasNextPage && allSettings.length >= MAX_TOTAL;
612
648
  if (cacheable && ctx.cache) {
613
649
  await ctx.cache.set(cacheKey, "setting", settings);
614
650
  }
615
- return buildSettingGetResponse(parsed, settings, cacheable && ctx.cache ? { hit: false, stored: true } : undefined);
651
+ return buildSettingGetResponse(parsed, settings, cacheable && ctx.cache ? { hit: false, stored: true } : undefined, truncated);
616
652
  }
617
653
  /**
618
- * Handle setting.upsert tool call.
654
+ * Handle setting_upsert tool call.
619
655
  */
620
656
  export async function handleSettingUpsert(args, ctx) {
621
657
  const parsed = SettingUpsertInputSchema.parse(args);
@@ -678,7 +714,7 @@ export async function handleSettingUpsert(args, ctx) {
678
714
  return result;
679
715
  }
680
716
  /**
681
- * Handle setting.bulkUpsert tool call.
717
+ * Handle setting_bulkUpsert tool call.
682
718
  */
683
719
  export async function handleSettingBulkUpsert(args, ctx) {
684
720
  const parsed = SettingBulkUpsertInputSchema.parse(args);
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Test tools: test.assert
2
+ * Test tools: test_assert
3
3
  *
4
4
  * Entity state assertion with optional polling for agentic verify loops.
5
5
  */
@@ -67,7 +67,7 @@ export const TestAssertInputSchema = z.object({
67
67
  // ---------------------------------------------------------------------------
68
68
  export const TEST_TOOL_DEFINITIONS = [
69
69
  {
70
- name: "test.assert",
70
+ name: "test_assert",
71
71
  description: "Assert entity state (status, type, attributes, edge counts). Set poll=true to retry until pass or timeout (for async state changes).",
72
72
  annotations: {
73
73
  title: "Assert Entity State",
@@ -224,7 +224,7 @@ function evaluateAssertions(entity, assertions) {
224
224
  }
225
225
  function requireTestClient(ctx) {
226
226
  if (!ctx.client) {
227
- throw new ToolError("CONFIG_ERROR", "SDK client is not available. Run config.validate and fix auth/base URL.");
227
+ throw new ToolError("CONFIG_ERROR", "SDK client is not available. Run config_validate and fix auth/base URL.");
228
228
  }
229
229
  return ctx.client;
230
230
  }
@@ -253,7 +253,7 @@ function sleep(ms) {
253
253
  return new Promise((resolve) => setTimeout(resolve, ms));
254
254
  }
255
255
  /**
256
- * Handle test.assert tool call.
256
+ * Handle test_assert tool call.
257
257
  */
258
258
  export async function handleTestAssert(args, ctx) {
259
259
  const parsed = TestAssertInputSchema.parse(args);