@components-kit/open-workbook 0.1.5 → 0.1.7

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.
@@ -5,7 +5,7 @@
5
5
  xmlns:ov="http://schemas.microsoft.com/office/taskpaneappversionoverrides"
6
6
  xsi:type="TaskPaneApp">
7
7
  <Id>6f2d2ac1-69b0-4eb6-a256-0a1fcb000001</Id>
8
- <Version>0.1.5.0</Version>
8
+ <Version>0.1.7.0</Version>
9
9
  <ProviderName>ComponentsKit</ProviderName>
10
10
  <DefaultLocale>en-US</DefaultLocale>
11
11
  <DisplayName DefaultValue="OpenWorkbook" />
@@ -25,6 +25,7 @@ If the add-in is disconnected, ask the user to start their agent UI so it launch
25
25
  - Use `excel.table.*` for structured table rows, filters, sorts, totals, and table resizing.
26
26
  - Use `excel.template.*`, `excel.style.*`, and `excel.formula.*` when preserving or repairing templates matters.
27
27
  - Use `excel.plan.*` for previewable multi-step changes that need rollback and stale-target checks.
28
+ - Use `excel.batch.preflight` before large generated writes, then choose direct apply, queued submit, or `excel.batch.submit_chunked` with job progress based on the recommendation.
28
29
  - Use `excel.batch.*` for compact, direct range mutations that still need backups, fingerprints, permissions, and transaction logging.
29
30
  - Use `excel.workflow.create_formula_sheet` for standard sheet creation with values, formulas, number formats, and formula validation in one response.
30
31
  - Use `excel.workflow.repair_formula_errors` for formula error repair when you have an error range plus a source formula range or exact formulas to write.
@@ -33,7 +34,7 @@ If the add-in is disconnected, ask the user to start their agent UI so it launch
33
34
  - Use `excel.workflow.create_pivot_chart_summary` for standard PivotTable plus chart summary tasks that need create, refresh, and validation in one response.
34
35
  Combined mutating workflows include a read-only preflight payload before mutation; still prefer `excel.workflow.prepare_session` first when possible.
35
36
  - Use `excel.validate.*` before and after risky changes.
36
- - Use `excel.backup.*`, `excel.snapshot.*`, and `excel.transaction.*` for recovery, audit, rollback previews, and rollback chains.
37
+ - Use `excel.backup.*`, `excel.snapshot.*`, `excel.transaction.*`, and `excel.job.*` for recovery, audit, long-running progress, rollback previews, and rollback chains.
37
38
  - Use `excel.task.*`, `excel.lock.*`, `excel.collab.*`, and `excel.conflict.*` for multi-agent workbook work.
38
39
 
39
40
  For detailed routing, read `references/tool-selection.md`.
@@ -51,12 +52,12 @@ For detailed routing, read `references/tool-selection.md`.
51
52
  ## Critical Recipes
52
53
 
53
54
  - Sheet with formulas: prefer `excel.workflow.create_formula_sheet`; if using separate tools, create the sheet, write labels/constants with `excel.range.write_values`, write formulas with `excel.range.write_formulas`, write number formats with `excel.range.write_number_formats`, then validate with `excel.formula.validate` or `excel.validate.no_formula_errors`. Do not put formula strings into `write_values`.
54
- - Large table reorder/filter/sort/append/update: inspect with `excel.table.get_info` and bounded `excel.table.read` only when row data is needed, then use `excel.table.reorder_columns`, `excel.table.apply_filters`, `excel.table.sort`, `excel.table.append_rows`, or `excel.table.update_rows`. Never clear, recreate, or rewrite the whole table for these tasks.
55
+ - Large table reorder/filter/sort/append/update: always inspect the table first with `excel.table.get_info` or `excel.workbook.get_workbook_map`. Use bounded `excel.table.read` only when row data is needed, then use `excel.table.reorder_columns`, `excel.table.apply_filters`, `excel.table.sort`, `excel.table.append_rows`, or `excel.table.update_rows`. For table filters, prefer `excel.table.apply_filters` over generic filter tools. After mutation, validate with `excel.validate.tables` for table changes and `excel.validate.filters` for filter/sort changes. Never clear, recreate, or rewrite the whole table for these tasks.
55
56
  - Formula repair: prefer `excel.workflow.repair_formula_errors` when the error range and source formula range are known. Otherwise find errors, read nearby formula patterns, inspect dependencies, repair with `excel.formula.repair_patterns`, `excel.formula.fill_down`, `excel.formula.fill_right`, or scoped `excel.range.write_formulas`, recalculate if needed, then validate. Never convert formulas to values unless explicitly requested.
56
57
  - Template report: prefer `excel.workflow.create_template_report`; if using separate tools, create the sheet from template, clear/fill declared regions, compare styles, repair style drift, and validate against the template.
57
58
  - Snapshot/diff/rollback preview: after discovery, prefer `excel.workflow.preview_risky_edit` for a scoped risky edit with a non-empty minimal operation list. If using separate tools, create a before snapshot, make the scoped change, create an after snapshot, call `excel.diff.summarize` or `excel.snapshot.compare` with both snapshot IDs, then use rollback preview tools without actually rolling back unless asked.
58
59
  - Pivot/chart summary: prefer `excel.workflow.create_pivot_chart_summary`; if using separate tools, check capability, create the PivotTable, refresh it, create/update the chart, refresh/update the chart source, and validate the PivotTable source before reporting success.
59
- - Multi-agent work: read collaboration status, create or inspect the task, acquire the narrowest lock, follow conflict guidance, perform or plan the scoped operation, then release locks.
60
+ - Multi-agent work: start with `excel.collab.get_status`, create or inspect the task, acquire the narrowest lock, follow conflict guidance, perform or plan the scoped operation, then release locks.
60
61
 
61
62
  ## Workflow References
62
63
 
@@ -24,12 +24,16 @@ Avoid workbook-wide reads unless the task is search, validation, audit, or disco
24
24
  ## Writes
25
25
 
26
26
  - Use one batch or plan for related edits.
27
+ - Preflight large generated batches before applying them.
27
28
  - Keep matrix shapes exact: rows and columns must match the target range.
28
- - Let Open Workbook chunk large values/formulas/number formats through its batch compiler.
29
+ - Let Open Workbook chunk large values/formulas/number formats through safe row-based chunk plans.
29
30
  - Avoid alternating read/write/read/write loops. Read once, compute, apply once, validate once.
31
+ - If work is queued or applying, report the progress message to the user and wait/poll the job or transaction rather than starting parallel mutations.
30
32
 
31
33
  For very large writes, consider a plan preview first so the agent can expose scope, chunk count, and rollback coverage before applying.
32
34
 
35
+ Automatic timeout retry is limited to style-only batches because repeating the same style is safe. Values, formulas, and number formats should be chunked before execution when preflight recommends it. For tables, pivots, charts, and structure, inspect transaction status before retrying.
36
+
33
37
  ## Formulas
34
38
 
35
39
  - Prefer formula pattern tools for repeated formula layouts.
@@ -32,17 +32,24 @@ Use explicit sheet/address ranges whenever possible. Use used-range or workbook-
32
32
  - Simple formulas: `excel.range.write_formulas`
33
33
  - Number formats: `excel.range.write_number_formats`
34
34
  - Styles: `excel.range.write_styles`
35
+ - Grouped styles: `excel.range.write_styles_many`
35
36
  - Combined formula sheet creation: `excel.workflow.create_formula_sheet`
36
37
  - Combined formula error repair: `excel.workflow.repair_formula_errors`
37
- - Multiple related range edits: `excel.batch.validate`, `excel.batch.dry_run`, `excel.batch.apply`
38
+ - Multiple related range edits: `excel.batch.validate`, `excel.batch.preflight`, `excel.batch.dry_run`, `excel.batch.apply`
39
+ - Long-running related range edits: `excel.batch.submit`
40
+ - Preflighted chunked range edits: `excel.batch.submit_chunked`
41
+ - Chunked long-running jobs: `excel.job.get`, `excel.job.wait`, `excel.job.cancel`
38
42
  - Scoped risky edit with diff and rollback preview: `excel.workflow.preview_risky_edit`
39
43
  - Previewable or user-reviewable edits: `excel.plan.create`, `excel.plan.preview`, `excel.plan.apply`
40
44
  - Stale plan handling: `excel.plan.refresh_preview`, `excel.plan.rebase`
45
+ - Queued mutation status: `excel.transaction.get`, `excel.transaction.wait`, `excel.transaction.cancel`
41
46
 
42
- Prefer `excel.workflow.create_formula_sheet` for standard new sheets that need values, formulas, number formats, and validation. Prefer `excel.workflow.repair_formula_errors` for formula error repair when an error range and source formula range are known. Prefer `excel.workflow.preview_risky_edit` when a risky scoped edit should apply and return before/after snapshots, a diff, transaction id, and rollback preview in one response. Provide at least one minimal scoped operation and leave `apply` enabled unless the user asked for preview only. Prefer `excel.plan.*` when a user should review a diff before applying, when formulas/templates are at risk, or when rollback clarity matters. Prefer `excel.batch.*` for compact, well-scoped range mutations.
47
+ Prefer `excel.workflow.create_formula_sheet` for standard new sheets that need values, formulas, number formats, and validation. Prefer `excel.workflow.repair_formula_errors` for formula error repair when an error range and source formula range are known. Prefer `excel.workflow.preview_risky_edit` when a risky scoped edit should apply and return before/after snapshots, a diff, transaction id, and rollback preview in one response. Provide at least one minimal scoped operation and leave `apply` enabled unless the user asked for preview only. Prefer `excel.plan.*` when a user should review a diff before applying, when formulas/templates are at risk, or when rollback clarity matters. Prefer `excel.batch.*` for compact, well-scoped range mutations. Preflight large generated batches before applying them.
43
48
  Combined mutating workflows include an internal read-only preflight payload before mutation; still prefer `excel.workflow.prepare_session` first when possible.
44
49
  Values, formulas, formats, and styles are separate workbook facets. Use `excel.range.write_values` for constants only, `excel.range.write_formulas` for formulas beginning with `=`, and `excel.range.write_number_formats` for display formats. Do not merge formulas into a values write just because the matrix is convenient.
45
50
  Do not pad a large range write with `null`, blank strings, or unchanged cells when only one cell or a smaller rectangle should change. Use the smallest changed target range, or use `excel.range.clear_values_keep_format` for intentional clearing.
51
+ Use `excel.range.write_styles_many` for report styling that touches many ranges, such as title bands, headers, zebra rows, status colors, or type colors. Use single `excel.range.write_styles` only for one contiguous range. Large grouped style updates may return queued parent jobs; wait or poll their job IDs.
52
+ If a mutation returns queued or applying transaction or job state, tell the user the workbook update is still running, then call `excel.job.wait`, `excel.job.get`, `excel.transaction.wait`, or `excel.transaction.get` instead of launching parallel writes. Use cancel tools only for queued work that should not start.
46
53
 
47
54
  When a noninteractive agent run cannot apply a mutation, create an `excel.plan.create` draft with operations that use actual MCP tool names and nested `args`. Example operations should look like `{ "tool": "excel.range.write_formulas", "args": { ... } }`, not vague prose or broad workbook rewrites.
48
55
 
@@ -56,6 +63,7 @@ When a noninteractive agent run cannot apply a mutation, create an `excel.plan.c
56
63
  - Sorts: `excel.sort.apply`, `excel.sort.clear`
57
64
 
58
65
  Use table tools instead of range tools when the target is an Excel table. This preserves headers, totals rows, filters, structured references, and table styles.
66
+ Before table reorder, filter, sort, append, or update operations, call `excel.table.get_info` or `excel.workbook.get_workbook_map`. For table filters, use `excel.table.apply_filters` rather than generic filter tools. After table structure or row mutations, call `excel.validate.tables`; after filter/sort changes, call `excel.validate.filters`.
59
67
  Do not clear and recreate a table to reorder columns; use `excel.table.reorder_columns`, or stop for confirmation before any destructive rebuild.
60
68
  For large tables, avoid full `excel.table.read` unless the user asks for all rows. Pass `rowLimit`, `rowOffset`, and `columns` for targeted discovery, then mutate with table-native tools.
61
69
 
@@ -45,7 +45,7 @@ const STYLE_COPY_TOOL_DIMENSIONS = {
45
45
  "excel.style.copy_hidden_rows_columns": "hiddenRowsColumns"
46
46
  };
47
47
  const runtime = await createRuntimeFacade();
48
- const runtimeVersion = process.env.OPEN_WORKBOOK_VERSION ?? "0.1.5";
48
+ const runtimeVersion = process.env.OPEN_WORKBOOK_VERSION ?? "0.1.7";
49
49
  const server = new McpServer({
50
50
  name: "open-workbook",
51
51
  version: runtimeVersion
@@ -73,6 +73,7 @@ registerCollaborationTools(server);
73
73
  registerLockTools(server);
74
74
  registerConflictTools(server);
75
75
  registerTransactionTools(server);
76
+ registerJobTools(server);
76
77
  registerPermissionsTools(server);
77
78
  registerCleanTools(server);
78
79
  registerValidateTools(server);
@@ -1118,6 +1119,35 @@ function registerRangeTools(mcp) {
1118
1119
  destructiveLevel: "format",
1119
1120
  reason: "MCP range write styles"
1120
1121
  }));
1122
+ registerMcpTool(mcp, "excel.range.write_styles_many", {
1123
+ title: "Write many Excel range styles",
1124
+ description: "Apply styles to multiple ranges through one reversible batch transaction. Use this for grouped report styling instead of many parallel write_styles calls.",
1125
+ inputSchema: {
1126
+ workbookId: z.string(),
1127
+ reason: z.string().optional(),
1128
+ entries: z.array(z.object({
1129
+ sheetName: z.string(),
1130
+ address: z.string(),
1131
+ style: z.record(z.string(), z.any()),
1132
+ reason: z.string().optional()
1133
+ })).min(1)
1134
+ },
1135
+ annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false }
1136
+ }, async ({ workbookId, reason, entries }) => {
1137
+ const typedWorkbookId = workbookId;
1138
+ const operations = styleEntriesToOperations(typedWorkbookId, entries, reason);
1139
+ const chunkSize = styleBatchChunkSize();
1140
+ if (operations.length > chunkSize) {
1141
+ return jsonResult(runtime.submitChunkedBatch({
1142
+ workbookId: typedWorkbookId,
1143
+ mode: "apply",
1144
+ operations,
1145
+ retryStrategy: "split_style_entries",
1146
+ progressMessage: reason ?? "Apply bulk range styles"
1147
+ }, { goal: reason ?? "Apply bulk range styles", retryStrategy: "split_style_entries" }));
1148
+ }
1149
+ return jsonResult(await runtime.applyBatch({ workbookId: typedWorkbookId, mode: "apply", operations }));
1150
+ });
1121
1151
  for (const name of ["excel.range.clear", "excel.range.clear_values", "excel.range.clear_formats", "excel.range.clear_values_keep_format"]) {
1122
1152
  registerRangeOperation(mcp, name, {
1123
1153
  workbookId: z.string(),
@@ -1205,6 +1235,26 @@ function registerBatchTools(mcp) {
1205
1235
  };
1206
1236
  return jsonResult(await runtime.compileBatch(request));
1207
1237
  });
1238
+ registerMcpTool(mcp, "excel.batch.preflight", {
1239
+ title: "Preflight Excel batch",
1240
+ description: "Estimate batch size and recommend apply, submit, or chunked submit before sending work to Excel.",
1241
+ inputSchema: {
1242
+ workbookId: z.string(),
1243
+ operations: z.array(z.any())
1244
+ },
1245
+ annotations: {
1246
+ readOnlyHint: true,
1247
+ destructiveHint: false,
1248
+ openWorldHint: false
1249
+ }
1250
+ }, async ({ workbookId, operations }) => {
1251
+ const request = {
1252
+ workbookId: workbookId,
1253
+ mode: "validate",
1254
+ operations: operations
1255
+ };
1256
+ return jsonResult(runtime.preflightBatch(request));
1257
+ });
1208
1258
  registerMcpTool(mcp, "excel.batch.dry_run", {
1209
1259
  title: "Dry-run Excel batch",
1210
1260
  description: "Compile a batch and report backups, touched ranges, and estimated changes.",
@@ -1269,6 +1319,98 @@ function registerBatchTools(mcp) {
1269
1319
  }
1270
1320
  return jsonResult(await runtime.applyBatch(request));
1271
1321
  });
1322
+ registerMcpTool(mcp, "excel.batch.submit", {
1323
+ title: "Submit Excel batch",
1324
+ description: "Queue a batch mutation and return transaction progress immediately instead of waiting for Excel execution to finish.",
1325
+ inputSchema: {
1326
+ workbookId: z.string(),
1327
+ operations: z.array(z.any()),
1328
+ confirmationToken: z.string().optional(),
1329
+ expectedTargetFingerprints: z.array(z.any()).optional(),
1330
+ agentId: z.string().optional(),
1331
+ agentName: z.string().optional(),
1332
+ taskId: z.string().optional(),
1333
+ role: z.string().optional()
1334
+ },
1335
+ annotations: {
1336
+ readOnlyHint: false,
1337
+ destructiveHint: true,
1338
+ openWorldHint: false
1339
+ }
1340
+ }, async ({ workbookId, operations, confirmationToken, expectedTargetFingerprints, agentId, agentName, taskId, role }) => {
1341
+ const request = {
1342
+ workbookId: workbookId,
1343
+ mode: "apply",
1344
+ operations: operations
1345
+ };
1346
+ if (confirmationToken !== undefined) {
1347
+ request.confirmationToken = confirmationToken;
1348
+ }
1349
+ if (expectedTargetFingerprints !== undefined) {
1350
+ request.expectedTargetFingerprints = expectedTargetFingerprints;
1351
+ }
1352
+ if (agentId !== undefined) {
1353
+ request.agentId = agentId;
1354
+ }
1355
+ if (agentName !== undefined) {
1356
+ request.agentName = agentName;
1357
+ }
1358
+ if (taskId !== undefined) {
1359
+ request.taskId = taskId;
1360
+ }
1361
+ if (role !== undefined) {
1362
+ request.role = role;
1363
+ }
1364
+ return jsonResult(runtime.submitBatch(request));
1365
+ });
1366
+ registerMcpTool(mcp, "excel.batch.submit_chunked", {
1367
+ title: "Submit chunked Excel batch",
1368
+ description: "Preflight a large batch, split safely chunkable operations, queue child transactions, and return one parent job.",
1369
+ inputSchema: {
1370
+ workbookId: z.string(),
1371
+ operations: z.array(z.any()),
1372
+ goal: z.string().optional(),
1373
+ confirmationToken: z.string().optional(),
1374
+ expectedTargetFingerprints: z.array(z.any()).optional(),
1375
+ agentId: z.string().optional(),
1376
+ agentName: z.string().optional(),
1377
+ taskId: z.string().optional(),
1378
+ role: z.string().optional()
1379
+ },
1380
+ annotations: {
1381
+ readOnlyHint: false,
1382
+ destructiveHint: true,
1383
+ openWorldHint: false
1384
+ }
1385
+ }, async ({ workbookId, operations, goal, confirmationToken, expectedTargetFingerprints, agentId, agentName, taskId, role }) => {
1386
+ const request = {
1387
+ workbookId: workbookId,
1388
+ mode: "apply",
1389
+ operations: operations
1390
+ };
1391
+ if (goal !== undefined) {
1392
+ request.progressMessage = goal;
1393
+ }
1394
+ if (confirmationToken !== undefined) {
1395
+ request.confirmationToken = confirmationToken;
1396
+ }
1397
+ if (expectedTargetFingerprints !== undefined) {
1398
+ request.expectedTargetFingerprints = expectedTargetFingerprints;
1399
+ }
1400
+ if (agentId !== undefined) {
1401
+ request.agentId = agentId;
1402
+ }
1403
+ if (agentName !== undefined) {
1404
+ request.agentName = agentName;
1405
+ }
1406
+ if (taskId !== undefined) {
1407
+ request.taskId = taskId;
1408
+ }
1409
+ if (role !== undefined) {
1410
+ request.role = role;
1411
+ }
1412
+ return jsonResult(runtime.submitChunkedBatch(request, { goal }));
1413
+ });
1272
1414
  }
1273
1415
  function registerWorkflowTools(mcp) {
1274
1416
  registerMcpTool(mcp, "excel.workflow.prepare_session", {
@@ -3284,6 +3426,22 @@ function registerTransactionTools(mcp) {
3284
3426
  inputSchema: { transactionId: z.string() },
3285
3427
  annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false }
3286
3428
  }, async ({ transactionId }) => jsonResult(runtime.getTransaction(transactionId)));
3429
+ registerMcpTool(mcp, "excel.transaction.wait", {
3430
+ title: "Wait for Excel transaction",
3431
+ description: "Wait for a queued or applying workbook transaction to reach a terminal status.",
3432
+ inputSchema: {
3433
+ transactionId: z.string(),
3434
+ timeoutMs: z.number().int().positive().optional(),
3435
+ pollMs: z.number().int().positive().optional()
3436
+ },
3437
+ annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false }
3438
+ }, async ({ transactionId, timeoutMs, pollMs }) => jsonResult(await runtime.waitTransaction(transactionId, timeoutMs, pollMs)));
3439
+ registerMcpTool(mcp, "excel.transaction.cancel", {
3440
+ title: "Cancel Excel transaction",
3441
+ description: "Cancel a queued workbook transaction before it starts applying in Excel.",
3442
+ inputSchema: { transactionId: z.string() },
3443
+ annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false }
3444
+ }, async ({ transactionId }) => jsonResult(runtime.cancelTransaction(transactionId)));
3287
3445
  registerMcpTool(mcp, "excel.transaction.preview_rollback", {
3288
3446
  title: "Preview transaction rollback",
3289
3447
  description: "Check whether a transaction can be rolled back without overwriting later work.",
@@ -3309,6 +3467,36 @@ function registerTransactionTools(mcp) {
3309
3467
  annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false }
3310
3468
  }, async ({ transactionId, confirmationToken }) => jsonResult(await runtime.rollbackTransactionChain(transactionId, confirmationToken)));
3311
3469
  }
3470
+ function registerJobTools(mcp) {
3471
+ registerMcpTool(mcp, "excel.job.list", {
3472
+ title: "List Excel jobs",
3473
+ description: "List parent jobs that group multiple queued workbook transactions.",
3474
+ inputSchema: { workbookId: z.string().optional() },
3475
+ annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false }
3476
+ }, async ({ workbookId }) => jsonResult(runtime.listJobs(workbookId)));
3477
+ registerMcpTool(mcp, "excel.job.get", {
3478
+ title: "Get Excel job",
3479
+ description: "Return one parent workbook job with aggregate progress across child transactions.",
3480
+ inputSchema: { jobId: z.string() },
3481
+ annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false }
3482
+ }, async ({ jobId }) => jsonResult(runtime.getJob(jobId)));
3483
+ registerMcpTool(mcp, "excel.job.wait", {
3484
+ title: "Wait for Excel job",
3485
+ description: "Wait for a parent workbook job to reach a terminal status.",
3486
+ inputSchema: {
3487
+ jobId: z.string(),
3488
+ timeoutMs: z.number().int().positive().optional(),
3489
+ pollMs: z.number().int().positive().optional()
3490
+ },
3491
+ annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false }
3492
+ }, async ({ jobId, timeoutMs, pollMs }) => jsonResult(await runtime.waitJob(jobId, timeoutMs, pollMs)));
3493
+ registerMcpTool(mcp, "excel.job.cancel", {
3494
+ title: "Cancel Excel job",
3495
+ description: "Cancel queued child transactions for a parent workbook job.",
3496
+ inputSchema: { jobId: z.string() },
3497
+ annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false }
3498
+ }, async ({ jobId }) => jsonResult(runtime.cancelJob(jobId)));
3499
+ }
3312
3500
  function registerPermissionsTools(mcp) {
3313
3501
  registerMcpTool(mcp, "excel.permissions.get", {
3314
3502
  title: "Get permissions",
@@ -4364,6 +4552,26 @@ function workflowDestructiveLevel(kind) {
4364
4552
  }
4365
4553
  return "workbook";
4366
4554
  }
4555
+ function styleEntriesToOperations(workbookId, entries, reason) {
4556
+ return entries.map((entry) => ({
4557
+ kind: "range.write_styles",
4558
+ operationId: makeId("op"),
4559
+ workbookId,
4560
+ target: {
4561
+ workbookId,
4562
+ sheetName: entry.sheetName,
4563
+ address: entry.address
4564
+ },
4565
+ style: entry.style,
4566
+ preserveValues: true,
4567
+ destructiveLevel: "format",
4568
+ reason: entry.reason ?? reason ?? "MCP bulk range write styles"
4569
+ }));
4570
+ }
4571
+ function styleBatchChunkSize() {
4572
+ const value = Number(process.env.OPEN_WORKBOOK_STYLE_BATCH_CHUNK_SIZE ?? 25);
4573
+ return Number.isFinite(value) && value > 0 ? Math.round(value) : 25;
4574
+ }
4367
4575
  function targetFromArgs(args) {
4368
4576
  return {
4369
4577
  workbookId: args.workbookId,