meguro-mcp 0.2.7 → 0.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,25 @@
2
2
 
3
3
  All notable changes to `meguro-mcp` are recorded here. Versions follow Semantic Versioning.
4
4
 
5
+ ## 0.2.9 — 2026-08-10
6
+
7
+ - Keeps the full real-store history-run family in the public STDIO registry while the hosted OAuth
8
+ surface now teaches and exposes only the practice-world run lane.
9
+ - Adds immutable getting-started v7, product-guide v4, and documentation-index v2 resources with
10
+ hosted-first control-plane guidance, Gate-policy v2 citations, and one honest STDIO history-lane
11
+ bridge.
12
+ - Reconciles `usage_read` with the exempt auto-provisioned sample store shown by `stores_list`, while
13
+ preserving every existing tool name, input schema, and prior documentation byte stream.
14
+
15
+ ## 0.2.8 — 2026-08-09
16
+
17
+ - Adds `practice_runs_list` so a cold MCP-only evaluator can discover existing practice attempts
18
+ without guessing a store or run identifier.
19
+ - Exposes `admin_schema` through the hosted-safe registry, corrects hosted OAuth connection
20
+ guidance, and adds immutable documentation currency/index metadata.
21
+ - Changes no earlier immutable documentation byte stream and carries no unrelated tool or protocol
22
+ behavior beyond the landed MEG-768 cold-walk corrections.
23
+
5
24
  ## 0.2.7 — 2026-08-08
6
25
 
7
26
  - Adds the optional `storeId` selector to `gate_evaluate` and `gate_verdict`, preserving every
package/README.md CHANGED
@@ -43,7 +43,7 @@ Console provides fast trusted proof and evidence inspection; the agency agent no
43
43
 
44
44
  <!-- BEGIN GENERATED MCP TOOL TABLE -->
45
45
  <!-- Run `npm run docs:tools --workspace meguro-mcp` to refresh this section from `tools/list`. -->
46
- This table is generated from the server's live `tools/list` response (46 tools).
46
+ This table is generated from the server's live `tools/list` response (47 tools).
47
47
 
48
48
  | Tool | Title | Behavior annotations |
49
49
  |---|---|---|
@@ -84,6 +84,7 @@ This table is generated from the server's live `tools/list` response (46 tools).
84
84
  | `exam_status` | Read Shopify Exam status | read-only · non-destructive · idempotent · closed-world |
85
85
  | `exam_report` | Read a Shopify Exam receipt | read-only · non-destructive · idempotent · closed-world |
86
86
  | `practice_run_start` | Start a practice run | read-write · non-destructive · non-idempotent · closed-world |
87
+ | `practice_runs_list` | List practice runs | read-only · non-destructive · idempotent · closed-world |
87
88
  | `practice_run_status` | Read practice-run status | read-only · non-destructive · idempotent · closed-world |
88
89
  | `practice_run_checkpoint` | Capture a practice-run checkpoint | read-write · non-destructive · non-idempotent · closed-world |
89
90
  | `practice_run_advance` | Advance store time | read-write · non-destructive · non-idempotent · closed-world |
@@ -181,11 +182,11 @@ See [the agency quickstart](../../docs/agency-practice-run-quickstart.md) and [H
181
182
  ## Advanced: CI or local STDIO
182
183
 
183
184
  Use the public STDIO package for CI, local automation, or clients without hosted Streamable HTTP and
184
- OAuth support. It requires a workspace-bound `meg_sk_…` API key and exposes the full 46-tool local
185
+ OAuth support. It requires a workspace-bound `meg_sk_…` API key and exposes the full 47-tool local
185
186
  surface. Pin the exact public version in client configuration so a quickstart stays reproducible:
186
187
 
187
188
  ```bash
188
- npx -y meguro-mcp@0.2.7
189
+ npx -y meguro-mcp@0.2.9
189
190
  ```
190
191
 
191
192
  ### Environment
@@ -203,7 +204,7 @@ claude mcp add meguro \
203
204
  -e MEGURO_API_BASE_URL=https://api-dev.meguro.io \
204
205
  -e MEGURO_API_TOKEN=meg_sk_... \
205
206
  -e MEGURO_DASHBOARD_URL=https://... \
206
- -- npx -y meguro-mcp@0.2.7
207
+ -- npx -y meguro-mcp@0.2.9
207
208
  ```
208
209
 
209
210
  ### Register — Cursor (`.cursor/mcp.json`)
@@ -213,7 +214,7 @@ claude mcp add meguro \
213
214
  "mcpServers": {
214
215
  "meguro": {
215
216
  "command": "npx",
216
- "args": ["-y", "meguro-mcp@0.2.7"],
217
+ "args": ["-y", "meguro-mcp@0.2.9"],
217
218
  "env": {
218
219
  "MEGURO_API_BASE_URL": "https://api-dev.meguro.io",
219
220
  "MEGURO_API_TOKEN": "meg_sk_...",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "meguro-mcp",
3
- "version": "0.2.7",
3
+ "version": "0.2.9",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Meguro control-plane MCP server: drive worlds, runs, and verdicts from your own AI tools.",
package/src/docs.mjs CHANGED
@@ -20,9 +20,11 @@ const GETTING_STARTED_LATEST_VERSION = 3;
20
20
  const GETTING_STARTED_V4_VERSION = 4;
21
21
  const GETTING_STARTED_V5_VERSION = 5;
22
22
  const GETTING_STARTED_V6_VERSION = 6;
23
+ const GETTING_STARTED_V7_VERSION = 7;
23
24
  const PRODUCT_GUIDE_VERSION = 1;
24
25
  const PRODUCT_GUIDE_V2_VERSION = 2;
25
26
  const PRODUCT_GUIDE_V3_VERSION = 3;
27
+ const PRODUCT_GUIDE_V4_VERSION = 4;
26
28
  const RECEIPT_GUIDE_URI = `meguro://docs/receipt-guide/v${RECEIPT_GUIDE_VERSION}`;
27
29
  const RECEIPT_GUIDE_V2_URI = `meguro://docs/receipt-guide/v${RECEIPT_GUIDE_V2_VERSION}`;
28
30
  const RECEIPT_GUIDE_V3_URI = `meguro://docs/receipt-guide/v${RECEIPT_GUIDE_V3_VERSION}`;
@@ -38,11 +40,61 @@ const GETTING_STARTED_LATEST_URI = `meguro://docs/getting-started/v${GETTING_STA
38
40
  const GETTING_STARTED_V4_URI = `meguro://docs/getting-started/v${GETTING_STARTED_V4_VERSION}`;
39
41
  const GETTING_STARTED_V5_URI = `meguro://docs/getting-started/v${GETTING_STARTED_V5_VERSION}`;
40
42
  const GETTING_STARTED_V6_URI = `meguro://docs/getting-started/v${GETTING_STARTED_V6_VERSION}`;
43
+ const GETTING_STARTED_V7_URI = `meguro://docs/getting-started/v${GETTING_STARTED_V7_VERSION}`;
41
44
  const PRODUCT_GUIDE_URI = `meguro://docs/product-guide/v${PRODUCT_GUIDE_VERSION}`;
42
45
  const PRODUCT_GUIDE_V2_URI = `meguro://docs/product-guide/v${PRODUCT_GUIDE_V2_VERSION}`;
43
46
  const PRODUCT_GUIDE_V3_URI = `meguro://docs/product-guide/v${PRODUCT_GUIDE_V3_VERSION}`;
47
+ const PRODUCT_GUIDE_V4_URI = `meguro://docs/product-guide/v${PRODUCT_GUIDE_V4_VERSION}`;
48
+ export const DOCUMENTATION_INDEX_URI = 'meguro://docs/index/v1';
49
+ export const DOCUMENTATION_INDEX_V2_URI = 'meguro://docs/index/v2';
50
+
51
+ const DOCUMENTATION_CURRENCY_V1 = deepFreeze({
52
+ indexVersion: 1,
53
+ currentUriByTopic: {
54
+ 'gate-policy': GATE_POLICY_V2_URI,
55
+ 'getting-started': GETTING_STARTED_V6_URI,
56
+ index: DOCUMENTATION_INDEX_URI,
57
+ 'product-guide': PRODUCT_GUIDE_V3_URI,
58
+ 'receipt-guide': RECEIPT_GUIDE_V7_URI,
59
+ },
60
+ });
61
+ const DOCUMENTATION_CURRENCY_V2 = deepFreeze({
62
+ indexVersion: 2,
63
+ currentUriByTopic: {
64
+ 'gate-policy': GATE_POLICY_V2_URI,
65
+ 'getting-started': GETTING_STARTED_V7_URI,
66
+ index: DOCUMENTATION_INDEX_V2_URI,
67
+ 'product-guide': PRODUCT_GUIDE_V4_URI,
68
+ 'receipt-guide': RECEIPT_GUIDE_V7_URI,
69
+ },
70
+ });
71
+ const CURRENT_DOCUMENTATION_CURRENCY = DOCUMENTATION_CURRENCY_V2;
72
+
73
+ function documentationVersionFromUri(uri) {
74
+ const match = /\/v([1-9][0-9]*)$/u.exec(uri);
75
+ if (!match) throw new Error(`Documentation URI has no immutable version: ${uri}`);
76
+ return Number(match[1]);
77
+ }
78
+
79
+ function documentationIndexText(currency, publishedUri = DOCUMENTATION_INDEX_URI) {
80
+ const rows = Object.entries(currency.currentUriByTopic).sort(([left], [right]) => codepointCompare(left, right));
81
+ return [
82
+ `# Meguro documentation index v${currency.indexVersion}`,
83
+ '',
84
+ `Published URI: \`${publishedUri}\``,
85
+ '',
86
+ 'This immutable index records the current published version of every documentation family at issuance.',
87
+ 'Use the `meguro/status` annotations from `resources/list` for live currency; cited versioned resources never change.',
88
+ '',
89
+ '| Family | Current version | Current resource |',
90
+ '| --- | ---: | --- |',
91
+ ...rows.map(([topic, uri]) => `| ${topic} | v${documentationVersionFromUri(uri)} | ${uri} |`),
92
+ ].join('\n');
93
+ }
44
94
 
45
- export const MCP_INITIALIZE_INSTRUCTIONS = `Start with \`${GETTING_STARTED_V6_URI}\` or call \`docs_read({ topic: "getting-started", version: 6 })\`; for general Meguro questions, read \`${PRODUCT_GUIDE_V3_URI}\` or call \`docs_read({ topic: "product-guide", version: 3 })\`.`;
95
+ const CURRENT_GETTING_STARTED_URI = CURRENT_DOCUMENTATION_CURRENCY.currentUriByTopic['getting-started'];
96
+ const CURRENT_PRODUCT_GUIDE_URI = CURRENT_DOCUMENTATION_CURRENCY.currentUriByTopic['product-guide'];
97
+ export const MCP_INITIALIZE_INSTRUCTIONS = `Start with \`${CURRENT_GETTING_STARTED_URI}\` or call \`docs_read({ topic: "getting-started", version: ${documentationVersionFromUri(CURRENT_GETTING_STARTED_URI)} })\`; for general Meguro questions, read \`${CURRENT_PRODUCT_GUIDE_URI}\` or call \`docs_read({ topic: "product-guide", version: ${documentationVersionFromUri(CURRENT_PRODUCT_GUIDE_URI)} })\`.`;
46
98
  export const PRODUCT_GUIDE_SHA256 = 'e5450c1b4181cec4d6ed0a498dbff1cb25d2e223ff2228928aecbbce9674f074';
47
99
 
48
100
  function deepFreeze(value) {
@@ -1211,6 +1263,38 @@ a plan, consent for the human, or reveal a credential that the human has not gra
1211
1263
  ## Question-to-source routing`,
1212
1264
  )
1213
1265
  .replace('Read \`meguro://docs/getting-started/v1\`.', `Read \`${GETTING_STARTED_V6_URI}\`.`);
1266
+ const REAL_STORE_HISTORY_LANE_BRIDGE = 'Meguro\'s separate real-store history lane still exists in the public STDIO distribution for internal and CI use; hosted OAuth MCP does not expose or teach that lane.';
1267
+ const GETTING_STARTED_V7_TEXT = GETTING_STARTED_V6_TEXT
1268
+ .replace('# Getting started with Meguro v6', '# Getting started with Meguro v7')
1269
+ .replace(`Published URI: \`${GETTING_STARTED_V6_URI}\``, `Published URI: \`${GETTING_STARTED_V7_URI}\``)
1270
+ .replaceAll('meguro://docs/gate-policy/v1', 'meguro://docs/gate-policy/v2')
1271
+ .replace(
1272
+ `The local \`meguro-mcp\` server is the Meguro control plane: it discovers stores and drives run
1273
+ lifecycle. The practice store's returned Shopify-shaped Admin endpoint is the data plane used by the
1274
+ commerce agent under evaluation. A practice store's storefront MCP endpoint is neither of those and
1275
+ cannot replace the local control plane or the Shopify-shaped Admin endpoint.`,
1276
+ `Hosted OAuth MCP is the recommended control plane for interactive agents. The public STDIO
1277
+ distribution is the CI and internal fallback. The practice store's returned Shopify-shaped Admin
1278
+ endpoint is the data plane used by the commerce agent under evaluation; its storefront MCP endpoint
1279
+ is neither control plane nor a substitute for the Shopify-shaped Admin endpoint.
1280
+
1281
+ ${REAL_STORE_HISTORY_LANE_BRIDGE}`,
1282
+ );
1283
+ const PRODUCT_GUIDE_V4_TEXT = PRODUCT_GUIDE_V3_TEXT
1284
+ .replace('# Meguro product guide v3', '# Meguro product guide v4')
1285
+ .replace(`Published URI: \`${PRODUCT_GUIDE_V3_URI}\``, `Published URI: \`${PRODUCT_GUIDE_V4_URI}\``)
1286
+ .replaceAll('meguro://docs/gate-policy/v1', 'meguro://docs/gate-policy/v2')
1287
+ .replace(`Read \`${GETTING_STARTED_V6_URI}\`.`, `Read \`${GETTING_STARTED_V7_URI}\`.`)
1288
+ .replace(
1289
+ '- **Public STDIO MCP** is the local and CI fallback.',
1290
+ `- **Public STDIO MCP** is the CI and internal fallback.
1291
+
1292
+ ${REAL_STORE_HISTORY_LANE_BRIDGE}`,
1293
+ )
1294
+ .replace(
1295
+ '| What runs exist or what happened? | Call \`runs_list\` and the relevant status, report, or impact tool. |',
1296
+ '| What practice runs exist or what happened? | Call \`practice_runs_list\` and the relevant \`practice_run_status\`, \`practice_run_report\`, or \`practice_run_impact\` tool. |',
1297
+ );
1214
1298
  const RECEIPT_GUIDE_V6_TEXT = simulationVocabularyText(
1215
1299
  RECEIPT_GUIDE_V5_TEXT
1216
1300
  .replace('# Meguro receipt guide v5', '# Meguro receipt guide v6')
@@ -1262,13 +1346,13 @@ function entry(input) {
1262
1346
  name: input.name,
1263
1347
  title: input.title,
1264
1348
  description: input.description,
1265
- mimeType: 'text/markdown',
1349
+ mimeType: input.mimeType ?? 'text/markdown',
1266
1350
  text,
1267
1351
  sha256: digest,
1268
1352
  });
1269
1353
  }
1270
1354
 
1271
- export function buildDocumentationCatalog(inputs) {
1355
+ export function buildDocumentationCatalog(inputs, currency) {
1272
1356
  const rows = inputs.map(entry).sort((left, right) => codepointCompare(left.uri, right.uri));
1273
1357
  const byUri = new Map();
1274
1358
  const byTopicVersion = new Map();
@@ -1279,6 +1363,19 @@ export function buildDocumentationCatalog(inputs) {
1279
1363
  byUri.set(row.uri, row);
1280
1364
  byTopicVersion.set(key, row);
1281
1365
  }
1366
+ const currentUriByTopic = currency?.currentUriByTopic ?? Object.fromEntries(
1367
+ [...new Set(rows.map((row) => row.topic))].map((topic) => {
1368
+ const current = rows.filter((row) => row.topic === topic).sort((left, right) => left.version - right.version).at(-1);
1369
+ return [topic, current.uri];
1370
+ }),
1371
+ );
1372
+ for (const topic of new Set(rows.map((row) => row.topic))) {
1373
+ const currentUri = currentUriByTopic[topic];
1374
+ const current = byUri.get(currentUri);
1375
+ if (!current || current.topic !== topic) {
1376
+ throw new Error(`Documentation currency has no valid current resource for ${topic}`);
1377
+ }
1378
+ }
1282
1379
  return deepFreeze({
1283
1380
  list() {
1284
1381
  return rows.map((row) => deepFreeze({
@@ -1292,6 +1389,8 @@ export function buildDocumentationCatalog(inputs) {
1292
1389
  'meguro/topic': row.topic,
1293
1390
  'meguro/version': row.version,
1294
1391
  'meguro/sha256': row.sha256,
1392
+ 'meguro/status': row.uri === currentUriByTopic[row.topic] ? 'current' : 'superseded',
1393
+ 'meguro/current-uri': currentUriByTopic[row.topic],
1295
1394
  },
1296
1395
  }));
1297
1396
  },
@@ -1363,6 +1462,15 @@ const CATALOG = buildDocumentationCatalog([
1363
1462
  description: `World-first discovery sequence for Shopify Admin GraphQL ${ADMIN_API_SUPPORTED_VERSIONS.join(' and ')} with default ${ADMIN_API_DEFAULT_VERSION}.`,
1364
1463
  text: GETTING_STARTED_V6_TEXT,
1365
1464
  },
1465
+ {
1466
+ topic: 'getting-started',
1467
+ version: GETTING_STARTED_V7_VERSION,
1468
+ uri: GETTING_STARTED_V7_URI,
1469
+ name: 'Getting started with Meguro v7',
1470
+ title: 'Getting started with Meguro v7',
1471
+ description: `Hosted-first practice-store sequence for Shopify Admin GraphQL ${ADMIN_API_SUPPORTED_VERSIONS.join(' and ')} with default ${ADMIN_API_DEFAULT_VERSION}.`,
1472
+ text: GETTING_STARTED_V7_TEXT,
1473
+ },
1366
1474
  {
1367
1475
  topic: 'product-guide',
1368
1476
  version: PRODUCT_GUIDE_VERSION,
@@ -1391,6 +1499,33 @@ const CATALOG = buildDocumentationCatalog([
1391
1499
  description: 'World-first product concepts, evidence boundaries, surface responsibilities, and question-to-source routing.',
1392
1500
  text: PRODUCT_GUIDE_V3_TEXT,
1393
1501
  },
1502
+ {
1503
+ topic: 'product-guide',
1504
+ version: PRODUCT_GUIDE_V4_VERSION,
1505
+ uri: PRODUCT_GUIDE_V4_URI,
1506
+ name: 'Meguro product guide v4',
1507
+ title: 'Meguro product guide v4',
1508
+ description: 'Hosted-first product concepts, practice-run vocabulary, evidence boundaries, and question-to-source routing.',
1509
+ text: PRODUCT_GUIDE_V4_TEXT,
1510
+ },
1511
+ {
1512
+ topic: 'index',
1513
+ version: DOCUMENTATION_CURRENCY_V1.indexVersion,
1514
+ uri: DOCUMENTATION_INDEX_URI,
1515
+ name: 'Meguro documentation index v1',
1516
+ title: 'Meguro documentation index v1',
1517
+ description: 'Immutable current-version pointers for every published Meguro documentation family.',
1518
+ text: documentationIndexText(DOCUMENTATION_CURRENCY_V1),
1519
+ },
1520
+ {
1521
+ topic: 'index',
1522
+ version: DOCUMENTATION_CURRENCY_V2.indexVersion,
1523
+ uri: DOCUMENTATION_INDEX_V2_URI,
1524
+ name: 'Meguro documentation index v2',
1525
+ title: 'Meguro documentation index v2',
1526
+ description: 'Immutable current-version pointers for every published Meguro documentation family after the hosted run-lane separation.',
1527
+ text: documentationIndexText(DOCUMENTATION_CURRENCY_V2, DOCUMENTATION_INDEX_V2_URI),
1528
+ },
1394
1529
  {
1395
1530
  topic: 'receipt-guide',
1396
1531
  version: RECEIPT_GUIDE_VERSION,
@@ -1472,7 +1607,7 @@ const CATALOG = buildDocumentationCatalog([
1472
1607
  description: 'Immutable named checks and flip conditions republished with the simulation-run vocabulary.',
1473
1608
  text: GATE_POLICY_V2_TEXT,
1474
1609
  },
1475
- ]);
1610
+ ], CURRENT_DOCUMENTATION_CURRENCY);
1476
1611
 
1477
1612
  export function documentationResources() {
1478
1613
  return CATALOG.list();
package/src/server.mjs CHANGED
@@ -14,7 +14,7 @@ import {
14
14
  } from './protocol.mjs';
15
15
  import { createTools, redactSecrets } from './tools.mjs';
16
16
 
17
- const SERVER_INFO = { name: 'meguro', version: '0.2.7' };
17
+ const SERVER_INFO = { name: 'meguro', version: '0.2.9' };
18
18
  const PROTOCOL_VERSION = '2025-03-26';
19
19
 
20
20
  const tools = createTools({
package/src/tools.mjs CHANGED
@@ -26,6 +26,7 @@ const DOCUMENTATION_TOOL_CONTRACT = documentationToolContract();
26
26
  const DOCUMENTATION_TOPICS = new Set(DOCUMENTATION_TOOL_CONTRACT.topics);
27
27
  const FLEET_TOOL_NAMES = new Set([
28
28
  'templates_list', 'stores_list', 'store_create', 'store_delete', 'store_passport',
29
+ 'practice_runs_list',
29
30
  'workspaces_list', 'workspace_create', 'workspace_archive', 'workspace_unarchive',
30
31
  'share_create', 'shares_list', 'share_status', 'share_publish', 'share_revoke',
31
32
  'catalog_slice_read', 'catalog_slice_snapshot', 'catalog_slices_saved',
@@ -69,6 +70,7 @@ const TOOL_PRESENTATION = Object.freeze({
69
70
  exam_status: { title: 'Read Shopify Exam status', readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
70
71
  exam_report: { title: 'Read a Shopify Exam receipt', readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
71
72
  practice_run_start: { title: 'Start a practice run', readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
73
+ practice_runs_list: { title: 'List practice runs', readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
72
74
  practice_run_status: { title: 'Read practice-run status', readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
73
75
  practice_run_checkpoint: { title: 'Capture a practice-run checkpoint', readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
74
76
  practice_run_advance: { title: 'Advance store time', readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
@@ -832,13 +834,94 @@ function runsListProjection(value, lifecycle) {
832
834
  ...(visible.length === 0 ? {
833
835
  emptyState: {
834
836
  message: `No ${lifecycle ?? 'matching'} Shopify dev-store history runs were found. This says nothing about practice simulation attempts.`,
835
- practiceAttemptRecovery: 'For a known practice simulation attemptId, call practice_run_report and practice_run_impact. To discover an attemptId, open the Console Runs/Receipts view; MCP has no practice-attempt list tool today.',
837
+ practiceAttemptRecovery: 'Call practice_runs_list to discover practice simulation attempts, then pass one exact attemptId to practice_run_report or practice_run_impact.',
836
838
  },
837
839
  } : {}),
838
840
  clockDiscipline: 'runClock names lifecycle timestamps; worldClock names scenario coordinates and does not invent an unreported Store-time.',
839
841
  });
840
842
  }
841
843
 
844
+ function practiceRunsSelectionError(storeIds) {
845
+ const multiple = storeIds.length > 1;
846
+ const meaning = multiple
847
+ ? `This workspace has more than one practice store (${storeIds.length}), so Meguro cannot infer which store's practice runs to list`
848
+ : 'This workspace has no practice store, so there are no store-owned practice runs to list';
849
+ const nextStep = multiple
850
+ ? 'Call stores_list({}), choose one exact storeId, then call practice_runs_list({ storeId: "<exact storeId>" }). You may instead provide a known receiptId so Meguro can derive its owning store.'
851
+ : 'Create a practice store first, then call practice_runs_list with its storeId or with a receiptId from one of its runs.';
852
+ return {
853
+ content: [{
854
+ type: 'text',
855
+ text: JSON.stringify({
856
+ code: 'store-selection-required',
857
+ storeIds,
858
+ errors: [{ code: 'store-selection-required', message: `Store selection required. ${meaning}. ${nextStep}`, meaning, nextStep }],
859
+ }, null, 2),
860
+ }],
861
+ isError: true,
862
+ };
863
+ }
864
+
865
+ function practiceRunReceiptSelectionError(receiptId) {
866
+ return {
867
+ content: [{
868
+ type: 'text',
869
+ text: JSON.stringify({
870
+ code: 'receipt-unavailable',
871
+ errors: [{
872
+ code: 'receipt-unavailable',
873
+ message: `Practice receipt ${receiptId} is not available in this workspace.`,
874
+ meaning: 'The receipt does not identify a retained practice run owned by this workspace.',
875
+ nextStep: 'Call practice_runs_list with an exact current storeId, or use a receiptId returned by a prior practice_runs_list or practice_run_report response.',
876
+ }],
877
+ }, null, 2),
878
+ }],
879
+ isError: true,
880
+ };
881
+ }
882
+
883
+ function practiceRunReceiptStoreMismatch(receiptId, receiptStoreId, requestedStoreId) {
884
+ return {
885
+ content: [{
886
+ type: 'text',
887
+ text: JSON.stringify({
888
+ code: 'receipt-store-mismatch',
889
+ errors: [{
890
+ code: 'receipt-store-mismatch',
891
+ message: `Receipt and storeId identify different practice stores. Receipt ${receiptId} belongs to ${receiptStoreId}, not ${requestedStoreId}.`,
892
+ meaning: `Receipt ${receiptId} belongs to store ${receiptStoreId}, not ${requestedStoreId}.`,
893
+ nextStep: `Retry with storeId ${receiptStoreId}, or omit storeId and let Meguro derive it from receiptId ${receiptId}.`,
894
+ }],
895
+ }, null, 2),
896
+ }],
897
+ isError: true,
898
+ };
899
+ }
900
+
901
+ function practiceRunsProjection(value, storeId, storeSelection) {
902
+ const attempts = Array.isArray(value?.playbacks) ? value.playbacks : [];
903
+ return secretSafe({
904
+ schemaVersion: 'meguro.practice-run-list.v1',
905
+ storeId,
906
+ storeSelection,
907
+ attempts: attempts
908
+ .filter((attempt) => attempt?.storeId === storeId || attempt?.worldId === storeId)
909
+ .map((attempt) => ({
910
+ attemptId: attempt.attemptId,
911
+ state: attempt.status,
912
+ clock: {
913
+ mode: attempt.clock?.mode ?? null,
914
+ baselineDay: attempt.baselineDay ?? null,
915
+ watermarkDay: attempt.watermarkDay ?? null,
916
+ simulationDays: attempt.simulationDays ?? attempt.clock?.simulationDays ?? null,
917
+ elapsedSimulationDays: attempt.elapsedSimulationDays ?? null,
918
+ remainingSimulationDays: attempt.remainingSimulationDays ?? null,
919
+ },
920
+ receiptRef: { attemptId: attempt.attemptId },
921
+ })),
922
+ });
923
+ }
924
+
842
925
  function practiceAttemptComparisonGuidanceFor(toolName, a, b) {
843
926
  const attemptIds = [a, b].filter((id) => PRACTICE_ATTEMPT_ID.test(id));
844
927
  const calls = attemptIds.flatMap((attemptId) => [
@@ -900,6 +983,21 @@ function practiceStoreConnectionNotFoundGuidance(storeId) {
900
983
  };
901
984
  }
902
985
 
986
+ function practiceRunsStoreNotFoundGuidance(storeId) {
987
+ return {
988
+ content: [{
989
+ type: 'text',
990
+ text: JSON.stringify({
991
+ code: 'practice-store-not-found',
992
+ message: `practice_runs_list could not find practice store ${storeId} in this account or the store is no longer active.`,
993
+ nextAction: 'Call stores_list({}) and use an exact current storeId from the returned stores array.',
994
+ stopCondition: `If stores_list({}) returns no current stores, stop: there is no practice store whose attempts can be listed. Do not retry ${JSON.stringify(storeId)}.`,
995
+ }, null, 2),
996
+ }],
997
+ isError: true,
998
+ };
999
+ }
1000
+
903
1001
  function legacyGateArtifactNotFoundGuidance(runId) {
904
1002
  return {
905
1003
  content: [{
@@ -957,6 +1055,11 @@ function usageProjection(value) {
957
1055
  },
958
1056
  limits: {
959
1057
  activeStores,
1058
+ activeStoreAccounting: {
1059
+ basis: 'metered-active-stores',
1060
+ exemptionMarker: 'auto-provisioned-sample-store',
1061
+ note: 'activeStores counts metered stores. A stores_list row marked auto-provisioned-sample-store is an exempt sample store and is not counted here.',
1062
+ },
960
1063
  activeStoreLimit,
961
1064
  activeWorkspaces,
962
1065
  activeWorkspaceLimit,
@@ -1881,7 +1984,7 @@ export function createTools(config) {
1881
1984
  },
1882
1985
  {
1883
1986
  name: 'runs_list',
1884
- description: 'List Shopify dev-store history runs created by run_start so an agent can re-find that family of prior work. It does not list practice simulation attempts returned by practice_run_start; those use pa-* ids, practice_run_report, and practice_run_impact, while attempt discovery lives in the Console Runs/Receipts view today. Optional history-run lifecycle filtering supports running, paused, completed, failed, or cleaning. An empty result explicitly says it is only about history runs. The payload names run-clock lifecycle timestamps separately from world-clock scenario coordinates and returns no filter counts.',
1987
+ description: 'List Shopify dev-store history runs created by run_start so an agent can re-find that family of prior work. It does not list practice simulation attempts returned by practice_run_start; call practice_runs_list for those pa-* ids, then use practice_run_report or practice_run_impact. Optional history-run lifecycle filtering supports running, paused, completed, failed, or cleaning. An empty result explicitly says it is only about history runs. The payload names run-clock lifecycle timestamps separately from world-clock scenario coordinates and returns no filter counts.',
1885
1988
  inputSchema: {
1886
1989
  type: 'object',
1887
1990
  additionalProperties: false,
@@ -1992,6 +2095,20 @@ export function createTools(config) {
1992
2095
  anyOf: [{ required: ['storeId'] }, { required: ['worldId'] }],
1993
2096
  },
1994
2097
  },
2098
+ {
2099
+ name: 'practice_runs_list',
2100
+ description: 'List bounded practice simulation attempt facts for one exact practice store: attemptId, lifecycle state, Store-clock summary, and a receiptRef that can be passed unchanged to practice_run_report. Store selection follows one fail-closed ladder: receiptId derives its owning store; otherwise an explicit storeId wins; omission is allowed only when the workspace has exactly one current store; zero or multiple stores return a structured store-selection-required error and never choose silently. An OAuth grant may select an owned workspace; an account API key remains bound to its own workspace.',
2101
+ inputSchema: {
2102
+ type: 'object',
2103
+ additionalProperties: false,
2104
+ properties: {
2105
+ workspaceId: { type: 'string', minLength: 1, description: 'Optional OAuth selector for an account-owned non-default workspace. An account API key can name only its bound workspace.' },
2106
+ storeId: { ...PRACTICE_STORE_ID_INPUT_SCHEMA, description: 'Optional exact practice-store id from stores_list. Omit only with receiptId or when the workspace has exactly one current store.' },
2107
+ receiptId: { type: 'string', minLength: 4, maxLength: 128, pattern: '^pa-[a-z0-9][a-z0-9-]{0,124}$', description: 'Optional known pa-* practice receipt identity. Meguro derives its owning store and refuses a conflicting storeId.' },
2108
+ },
2109
+ required: [],
2110
+ },
2111
+ },
1995
2112
  {
1996
2113
  name: 'practice_run_status',
1997
2114
  description: 'Read the server-authoritative state of an external-agent practice simulation run, including its announced simulation-run deadlines and immutable simulation run receipt after any exit. Use Console for quick proof; the tested agent remains in the caller\'s environment.',
@@ -2059,7 +2176,7 @@ export function createTools(config) {
2059
2176
  },
2060
2177
  {
2061
2178
  name: 'get_connection_details',
2062
- description: `Fetch connection material for supported Shopify Admin GraphQL versions ${ADMIN_API_SUPPORTED_VERSION_LABEL}; the default is ${ADMIN_API_DEFAULT_VERSION}. Returns adminApiVersion (default), adminApiVersions (supported set), adminVersions (exact versioned URLs), the default URL, access token, and shop domain for an existing Meguro practice store so an assistant can configure a user agent without opening the dashboard. Inspect the selected version with admin_schema before the first Admin call; an agent targeting a newer Shopify release outside the supported set must treat that behavior as not established. Takes the canonical practice-store id (storeId) and returns it back, so the result passes directly into practice_run_start({ storeId }) — no identifier translation. The practice-store id is not an attemptId (run identity). Requires an account API key because it reveals the per-store token.`,
2179
+ description: `Fetch connection material for supported Shopify Admin GraphQL versions ${ADMIN_API_SUPPORTED_VERSION_LABEL}; the default is ${ADMIN_API_DEFAULT_VERSION}. Returns adminApiVersion (default), adminApiVersions (supported set), adminVersions (exact versioned URLs), the default URL, access token, and shop domain for an existing Meguro practice store so an assistant can configure a user agent without opening the dashboard. Inspect the selected version with admin_schema before the first Admin call; an agent targeting a newer Shopify release outside the supported set must treat that behavior as not established. Takes the canonical practice-store id (storeId) and returns it back, so the result passes directly into practice_run_start({ storeId }) — no identifier translation. The practice-store id is not an attemptId (run identity). The hosted MCP path uses the caller's OAuth grant; the public STDIO path uses its workspace-bound account API key.`,
2063
2180
  inputSchema: {
2064
2181
  type: 'object',
2065
2182
  additionalProperties: false,
@@ -2630,6 +2747,50 @@ export function createTools(config) {
2630
2747
  ...consoleUrlFields(started.consoleRef, started.consoleRefExpiresAt),
2631
2748
  }));
2632
2749
  }
2750
+ case 'practice_runs_list': {
2751
+ const workspaceId = optionalWorkspaceId(args);
2752
+ const requestedStoreId = args.storeId === undefined
2753
+ ? undefined
2754
+ : validatedStoreId(requiredString(args, 'storeId'), 'storeId');
2755
+ const receiptId = args.receiptId === undefined
2756
+ ? undefined
2757
+ : requiredPracticeAttemptId(args, 'receiptId');
2758
+ if (receiptId) {
2759
+ const listed = await fleetRequest(name, 'GET', '/practice/playbacks', undefined, { workspaceId });
2760
+ if ('error' in listed) return listed.error;
2761
+ const playbacks = Array.isArray(listed.value?.playbacks) ? listed.value.playbacks : [];
2762
+ const referenced = playbacks.find((attempt) => attempt?.attemptId === receiptId);
2763
+ if (!referenced) return practiceRunReceiptSelectionError(receiptId);
2764
+ const receiptStoreId = String(referenced.storeId ?? referenced.worldId ?? '');
2765
+ if (requestedStoreId && requestedStoreId !== receiptStoreId) {
2766
+ return practiceRunReceiptStoreMismatch(receiptId, receiptStoreId, requestedStoreId);
2767
+ }
2768
+ return textResult(practiceRunsProjection(listed.value, receiptStoreId, 'receipt-derived'));
2769
+ }
2770
+
2771
+ const fleet = await fleetRequest(name, 'GET', '/practice/stores', undefined, { workspaceId });
2772
+ if ('error' in fleet) return fleet.error;
2773
+ const storeIds = [...new Set((Array.isArray(fleet.value?.stores) ? fleet.value.stores : [])
2774
+ .map((store) => String(store?.storeId ?? store?.worldId ?? '').trim())
2775
+ .filter((storeId) => /^[a-z0-9-]+$/u.test(storeId)))].sort();
2776
+
2777
+ if (requestedStoreId && !storeIds.includes(requestedStoreId)) {
2778
+ return practiceRunsStoreNotFoundGuidance(requestedStoreId);
2779
+ }
2780
+ if (!requestedStoreId && storeIds.length !== 1) {
2781
+ return practiceRunsSelectionError(storeIds);
2782
+ }
2783
+
2784
+ const listPath = queryPath('/practice/playbacks', { worldId: requestedStoreId ?? storeIds[0] });
2785
+ const listed = await fleetRequest(name, 'GET', listPath, undefined, { workspaceId });
2786
+ if ('error' in listed) return listed.error;
2787
+ const selectedStoreId = requestedStoreId ?? storeIds[0];
2788
+ return textResult(practiceRunsProjection(
2789
+ listed.value,
2790
+ selectedStoreId,
2791
+ requestedStoreId ? 'explicit-store-id' : 'single-store',
2792
+ ));
2793
+ }
2633
2794
  case 'practice_run_status': {
2634
2795
  const attemptId = requiredPracticeAttemptId(args);
2635
2796
  const response = await practiceApi('GET', `/practice/playbacks/${encodeURIComponent(attemptId)}/state`);