Excel MCP stdio server — read and edit .xlsx files via the Model Context Protocol
Project description
excelmcp — Excel MCP Server (Sprint P) v0.6.0
excelmcp is a Python MCP stdio server for reading and editing .xlsx files using
openpyxl as the backend. It is part of the
MCP Office repository and operates entirely at the file level — no
Excel application is required for the core openpyxl tool surface; optional COM-backed Sprint O tools add live recalculation, PDF export, template hydration, pivot-table operations, and live formula evaluation via evaluate_formula(live=True) on Windows when EXCEL_ENABLE_COM=true. Write operations are gated
behind EXCEL_ENABLE_WRITE=true (environment variable) and a confirm=True parameter
for destructive calls, preventing accidental mutations.
Installation
pip install excelmcp
Or with COM extras (Windows only, for live recalculation and PDF export):
pip install excelmcp[com]
Running tests (for contributors)
# Clone the repo and install with dev dependencies
pip install -e "./excelmcp[dev]"
# Run tests
pytest excelmcp/tests/ -m "not integration" -v --tb=short
VS Code mcp.json Registration
Add the following entry to
%APPDATA%\Code - Insiders\User\mcp.json (or Code\User\mcp.json for stable VS Code):
"excel-excelmcp": {
"type": "stdio",
"command": "python",
"args": ["-m", "excelmcp.server"],
"env": {
"PYTHONPATH": "C:\\path\\to\\mcp-office\\excelmcp\\src",
"EXCEL_ALLOWLIST_ROOTS": "C:\\Workbooks,D:\\ExcelData",
"EXCEL_ENABLE_WRITE": "true"
}
}
Environment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
EXCEL_ALLOWLIST_ROOTS |
yes | — | Comma-separated absolute folder paths the server may access. Any path outside these roots is blocked with a "Path not in allowlist" error. |
EXCEL_ENABLE_WRITE |
no | false |
Must be true to enable any mutating tool. If unset or false, all write calls return an error even when confirm=True is passed. |
EXCEL_MAX_RANGE_CELLS |
no | 5000 |
Maximum number of cells returned by a single read_range or write_range call. Prevents accidental oversized reads. |
EXCEL_ALLOW_FILE_HYPERLINKS |
no | false |
Allow file:// scheme in bulk_add_hyperlinks. Disabled by default — Windows UNC paths (file://attacker.com/share) can trigger NTLM credential relay. Enable only in fully trusted environments. |
EXCEL_MAX_METADATA_BYTES |
no | 32768 |
Maximum UTF-8 payload size accepted by workbook_metadata and the metadata resource. Payloads must also fit Excel's single-cell text limit (32767 chars). |
EXCEL_DA_COM_PROBE |
no | off |
Stage-4 COM validation pass after DA formula patching. off = skipped; auto = runs if win32com is available; required = runs and fails if win32com unavailable. Windows-only. |
EXCEL_DA_FINALIZE_MODE |
no | warn |
DA finalization pipeline strictness. warn = log failures and continue; strict = raise DAFinalizeError on any failed stage. |
EXCEL_ENABLE_COM |
no | false |
Must be true to activate COM-backed operations: recalculate_workbook, export_as_pdf, hydrate_template, pivot_table, and evaluate_formula(live=True). Windows-only; requires pywin32. |
EXCEL_EXPORT_ROOTS |
no | — | Comma-separated absolute folder paths permitted as PDF output destinations. Required when export_as_pdf is used. |
EXCEL_METADATA_POLICY |
no | strict |
Metadata validation policy. strict (default): any missing or unreadable field in build_workbook_metadata() is a hard error. lenient: optional fields may be empty; only path and workbook_name are required. Injected into the /metadata and /meta resource responses as the policy field. |
EXCEL_CSV_EXPORT_ROOTS |
no | — | Comma-separated absolute folder paths permitted as CSV output destinations when export_range_as_csv writes to disk. Required when output_path is supplied. Separate from EXCEL_ALLOWLIST_ROOTS (source read allowlist) and EXCEL_EXPORT_ROOTS (PDF output allowlist). |
EXCEL_SESSION_MODE |
no | false |
Set to true to enable session-mode routing for the active-workbook bridge. When true but EXCEL_ENABLE_COM is not true, a UserWarning is emitted at runtime and the bridge tools return errors. Has no effect without EXCEL_ENABLE_COM=true; safe to set on non-Windows where COM is unavailable. |
EXCEL_ENABLE_MACROS |
no | false |
Must be true to permit the run_macro tool. When unset or false, run_macro returns an error. list_macros does not require this variable. Used in _ensure_macros_gate(). Requires EXCEL_ENABLE_COM=true. |
Dynamic Array Formula Support
openpyxl does not emit the OOXML metadata required by Excel 365 for dynamic array (spill) formulas. Without it, Excel's repair dialog removes every spill formula on first open. excelmcp applies a post-save OOXML patch automatically when saving workbooks that contain any of the following functions:
FILTER, SORT, SORTBY, UNIQUE, SEQUENCE, RANDARRAY, EXPAND, TAKE,
DROP, TOROW, TOCOL, WRAPROWS, WRAPCOLS, LET, XLOOKUP, XMATCH,
MAKEARRAY
The patch runs as part of the 4-stage finalization pipeline (_da_finalize):
| Stage | Description |
|---|---|
| 1 — patch | OOXML metadata injection: cm="1" linkage on DA cells, xl/metadata.xml, Content-Type override, legacy CSE attribute strip |
| 2 — classify | Outcome classification (patched / no_da_detected) |
| 3 — validate | Invariant validation (skipped until a future sprint) |
| 4 — probe | Optional COM round-trip via _da_com.probe_excel_reopen — controlled by EXCEL_DA_COM_PROBE. Requires EXCEL_ENABLE_COM=true; raises NotAllowedError otherwise. Non-temp paths validated against EXCEL_ALLOWLIST_ROOTS (denied by default when unset). AutomationSecurity = 3 enforced before any workbook open — macro execution blocked. |
COM Formula2 Preservation (LET+FILTER Gap — Fixed)
Previously a Known Open Gap. LET() wrapping a spill function (e.g.
=LET(src, FILTER(...), INDEX(src, SEQUENCE(ROWS(src)), {cols}))) written via
Excel COM .Formula2 now survives Excel MCP (openpyxl) save round-trips.
Root cause: openpyxl's load/save cycle silently drops the t="array" aca="1" ref="..." ca="1" attributes from <f> elements. These attributes are required by
Excel 365 to recognise LET+FILTER cells as dynamic-array anchors. The cm="1"
approach alone is insufficient for deeply nested LET expressions.
Fix implemented in _core.py + _ooxml.py:
- Pre-save snapshot — Before
wb.save(),snapshot_formula2_cells(path)reads the OOXML zip and captures all cells withaca="1"on their<f>element (the Excel 365 Formula2 marker written by COM). - Post-patcher restore — After the DA patcher runs,
restore_formula2_f_attrs(path, snapshots)re-injects the originalt="array" aca="1" ref caattributes into those cells. Thecm="1"added by the patcher is preserved alongside them. - Strip-pass guard —
_strip_legacy_f_attrsin_ooxml.pyhas anaca="1"early-return guard: cells carryingaca="1"are skipped entirely by the legacy-CSE strip pass, preventing accidental attribute removal before the restore runs.
Both _save_and_evict and _flush_if_dirty in _core.py follow this
snapshot → save → patch → restore sequence.
Compatibility table:
| Formula type | Engine | Result after MCP save |
|---|---|---|
=FILTER(...) plain |
openpyxl patcher | ✅ cm="1" + _xlfn.FILTER |
=LET(x, FILTER(...), x) via COM .Formula2 |
snapshot/restore | ✅ t="array" aca="1" ref preserved |
=LET(src, ..., INDEX(FILTER(...), SEQUENCE(...), {...})) via COM |
snapshot/restore | ✅ full attributes preserved |
Legacy CSE {=SUM(...)} |
patcher strips t="array" |
✅ treated as scalar DA |
Plain spill functions (non-LET) continue to use the existing cm="1" path.
Tools Reference
64 tools: server_io.py (18), server_ops.py (16), server_format.py (8), server_batch.py (6), server_com.py (7), server_chart.py (1), server_review.py (3), server_acp.py (1), server_snapshot.py (2), server_com_vba.py (2).
Unified dispatch tools (e.g. sheet, cell, range_io) consolidate related operations behind one endpoint — pass operation= to select the mode. Read modes need no write gate; write modes require EXCEL_ENABLE_WRITE=true + confirm=True.
capabilities also returns a live prompt inventory from the shared MCP registry via prompts.count and prompts.names.
Read Tools (10)
No write-gate required. Safe to call without EXCEL_ENABLE_WRITE.
| Tool | Description | Key response fields |
|---|---|---|
capabilities |
Returns server version, phase, backend, tool list, live prompt inventory, and metadata governance fields | version, phase, backend, tools[], prompts.count, prompts.names[], governance, metadata_contract_version, metadata_policy |
get_used_range |
Bounding box of the used cells on a sheet; empty: true for blank sheets |
sheet, min_row, max_row, min_col, max_col, empty |
read_sheet_all |
Full used range of a worksheet as a 2-D value array | sheet, values[][] |
get_cell_metadata |
Font, fill, number format, comment, and hyperlink for a single cell | font, fill, number_format, comment, hyperlink |
evaluate_formula |
Formula string and value for a cell: cached openpyxl value by default, or live Excel COM evaluation when live=True (EXCEL_ENABLE_COM=true, Windows-only) |
cached path: sheet, address, formula, cached_value; live path also returns ok, note, value |
list_tables |
All Excel tables in the workbook; optionally filter by sheet | tables[].name, tables[].sheet, tables[].ref |
read_table |
Headers and all data rows from a named Excel table | table, sheet, headers[], rows[][] |
validate_formula_syntax |
Check formula syntax without writing; path is optional |
valid, error |
diff_workbooks |
Compare two workbooks cell-by-cell and return differences; read-only (data_only=True) |
path_a, path_b, sheets_compared[], diffs[], total_diffs, truncated |
consolidate_ranges |
Read multiple ranges from workbooks and merge into one table; max 20 range tuples, 500 rows | ok, ranges_read, total_rows, truncated, rows[] |
Unified Dispatch Tools (10)
Pass operation= to select the mode. Read operations need no write gate; write operations require EXCEL_ENABLE_WRITE=true + confirm=True.
| Tool | Operations | Replaces | Description |
|---|---|---|---|
sheet |
list · add · rename · delete |
list_sheets, add_sheet, rename_sheet, delete_sheet |
Manage workbook sheets; add/rename/delete require confirm=True |
cell |
read · write |
read_cell, write_cell |
Read or write a single cell value; write mode persists immediately on success. session_mode=True on the write path routes via COM to the active workbook (requires EXCEL_ENABLE_COM=true + EXCEL_ENABLE_WRITE=true + confirm=True; formula values rejected) |
range_io |
read · write |
read_range, write_range |
Read or write a rectangular range; capped at EXCEL_MAX_RANGE_CELLS; write mode persists immediately on success. session_mode=True on the write path routes via COM to the active workbook (same guards as cell; ragged rows padded to n_cols with None for COM SAFEARRAY safety) |
named_range |
list · read · write |
get_named_ranges, read_named_range, write_named_range |
List, read, or write named range definitions |
merge |
list · merge · unmerge |
list_merged_cells, merge_cells, unmerge_cells |
List, merge, or unmerge cell ranges |
cell_comment |
get · delete |
get_cell_comment, delete_cell_comment |
Get (read-only) or delete a cell comment |
sheet_properties |
get · set |
get_sheet_properties, set_sheet_properties |
Get or set tab color, visibility state, gridlines, zoom level |
data_validation |
get · add |
get_data_validation_info, bulk_add_data_validation |
Get or add data validation rules |
hyperlink |
get · add · remove |
get_hyperlinks, add_hyperlink, bulk_add_hyperlinks, remove_hyperlink |
Manage hyperlinks; URL scheme allowlist: https, http, mailto; file:// requires EXCEL_ALLOW_FILE_HYPERLINKS=true; javascript:, data:, vbscript: always blocked |
workbook_metadata |
read · write |
— | Read or write workbook-level metadata stored on hidden sheet _MCP_META; read returns presence, version, payload, and byte size; write requires EXCEL_ENABLE_WRITE=true + confirm=True |
Write / Structural Tools (25)
All require EXCEL_ENABLE_WRITE=true + confirm=True.
Save semantics:
cell(write),range_io(write), andappend_rowspersist immediately on success via the shared save-and-evict path.saveremains available as an explicit flush and returns a safe no-op when no write-mode workbook is cached.
| Tool | Save mode | Description |
|---|---|---|
create_workbook |
cache | Create a new .xlsx; optional sheets list; overwrite=False guards accidental replacement |
save |
— | Flush in-memory cache to disk; no-op if nothing pending |
append_rows |
auto | Append rows after the last used row; table-aware — expands tbl.ref + syncs autofilter |
find_replace |
auto | Find-and-replace strings across all cells; whole_cell + match_case options |
import_csv_to_sheet |
auto | Import CSV/TSV into a sheet; skip_header=False default; overwrite=True replaces data |
rows |
auto | Insert (operation='insert') or delete (operation='delete') rows |
cols |
auto | Insert (operation='insert') or delete (operation='delete') columns |
protect |
auto | Protect/unprotect a sheet (scope='sheet') or workbook structure (scope='workbook') |
set_print_area |
auto | Set or clear the print area; address=None to clear |
set_column_visibility |
auto | Show or hide columns by letter list; hidden=True to hide |
set_workbook_calc_mode |
auto | Set auto/manual/autoNoTable calc mode + fullCalcOnLoad flag |
copy_range |
auto | Copy values + formats from src_address to dst_address within the same sheet |
create_table |
auto | Create a native Excel table (ListObject); default style TableStyleMedium9 |
freeze_panes |
auto | Freeze rows/columns at a cell; address=None to unfreeze |
auto_filter |
auto | Apply or clear Excel AutoFilter on a range; address=None to clear |
bulk_copy_sheet |
auto | Copy one source sheet to multiple named new sheets in a single call; note: charts/images not copied |
set_row_heights |
auto | Set row heights: uniform (row_range + height) or per-row (row_specs list) |
set_column_widths |
auto | Explicit width or autofit=True (content approximation); col_range or columns list |
apply_format_to_sheet_list |
auto | Apply style/number_format/alignment/column_widths/row_heights to same address across multiple sheets |
bulk_add_comments |
auto | Add multiple cell comments in one call; fail-fast validation |
fill_formula_range |
auto | Fill a formula across a range with automatic reference relativisation via Translator |
bulk_range_write |
auto | Write multiple non-contiguous cells in one call using a writes list of {address, value} items; rejects invalid/out-of-bounds addresses and formula-injection strings |
copy_range_format |
auto | Format-painter: copy formatting (not values) from source to destination range; cross-sheet capable |
write_range_with_format |
auto | Write a 2-D value array with style/number_format/alignment atomically |
apply_conditional_format |
auto | Apply CellIsRule or FormulaRule conditional formatting; formula injection guard active |
snapshot_workbook |
auto | Copy a workbook to a timestamped snapshot file (shutil.copy2); dest validated against EXCEL_ALLOWLIST_ROOTS; path traversal guard on filename |
Formatting Tools (4)
All require EXCEL_ENABLE_WRITE=true + confirm=True. apply_style auto-saves on success; apply_number_format, apply_alignment, and add_border do NOT auto-save — call save() to persist.
| Tool | Auto-save? | Description |
|---|---|---|
apply_number_format |
no | Apply Excel format codes (e.g. #,##0.00, YYYY-MM-DD, +0.0%;-0.0%) to a cell or range |
apply_style |
yes | Set bold, italic, font size, font color, and/or fill; pass address (preferred) or ranges list |
apply_alignment |
no | Set horizontal (left/center/right/fill/justify), vertical alignment, and/or wrap_text |
add_border |
no | Apply a border to all edges; style: thin/medium/thick/dashed/dotted/double; optional hex color |
COM Tools — Sprint O + Phase 2 (5)
All require EXCEL_ENABLE_COM=true. recalculate_workbook, export_as_pdf, hydrate_template, pivot_table(operation='create'|'refresh'), and export_range_as_csv (write path) also require confirm=True; pivot_table(operation='read') and export_range_as_csv (console-only path) do not. Windows-only — requires pywin32 (pip install pywin32). A licensed Excel installation must be present.
Security:
AutomationSecurityset tomsoAutomationSecurityForceDisable(3) before any workbook opens — macro execution blocked at COM layer.hydrate_templatevalidates allcell_writesstrings against a formula-injection guard before COM entry (OWASP A03).- PDF output paths validated against
EXCEL_EXPORT_ROOTS(separate fromEXCEL_ALLOWLIST_ROOTS) before write.pivot_tablecreate/refresh: onlyxlDatabaselocal sources accepted; external ODBC/OData/web sources blocked (SSRF guard).export_range_as_csvCSV output paths validated againstEXCEL_CSV_EXPORT_ROOTSbefore write; cell values sanitised against CSV injection (OWASP A03 — values starting with=,+,-,@are prefixed with')._ensure_com_gate()now includes asys.platformguard — raisesNotAllowedErroron non-Windows platforms before attempting any COM call.
| Tool | Save mode | EXCEL_ENABLE_WRITE? | Description |
|---|---|---|---|
recalculate_workbook |
in-place | yes | Force-recalculate via COM; full_calc=True rebuilds full dependency tree; saves in-place. Returns {ok, path, calc_mode, elapsed_ms} |
export_as_pdf |
read-only | no | Export sheet (scope='sheet') or workbook (scope='workbook') to PDF; both path and output_path must be absolute, scope must be exactly sheet or workbook, and output_path must be an absolute .pdf path in EXCEL_EXPORT_ROOTS; once validation succeeds, any stale target is cleared immediately before export so evidence always refers to fresh bytes |
hydrate_template |
SaveAs | yes | Write cell_writes ({ref, value} list) to template then SaveAs; recalculate=True default. Returns {ok, output_path, writes_applied, recalculated}; calling with cell_writes=[] performs a pure recalc/save-as flow |
pivot_table |
varies | varies | Create (operation='create'), read (operation='read'), or refresh (operation='refresh') pivot tables. Only xlDatabase local sources. create requires EXCEL_ENABLE_WRITE=true + confirm=True; read is COM-gated but read-only; refresh requires confirm=True |
export_range_as_csv |
varies | yes (write path) | Export a cell range to CSV. live=False (default) uses openpyxl — no COM required; live=True uses Excel COM for accurate cached values. Console-only mode (no output_path) requires only EXCEL_ENABLE_COM=true when live=True; writing to disk additionally requires EXCEL_ENABLE_WRITE=true + output_path inside EXCEL_CSV_EXPORT_ROOTS + confirm=True. CSV injection guard applied to all cell values via _sanitize_csv_value. |
Active-Workbook Bridge Tools — Sprint W4 (2)
Both tools require EXCEL_ENABLE_COM=true. Read-only — no write gate, no confirm parameter. Results are filtered to EXCEL_ALLOWLIST_ROOTS; workbooks outside the allowlist are silently excluded. Windows-only — requires pywin32. These tools attach to the user's running Excel instance via GetActiveObject; they do not launch a new hidden Excel process.
| Tool | Key response fields | Description |
|---|---|---|
get_active_workbook_context |
status, workbook.name, workbook.path, workbook.sheets[] |
Return name, path, and sheet list for the workbook currently active in Excel. Status values: ok (data returned), no_active_workbook (Excel has no open file), outside_allowlist (active workbook is outside EXCEL_ALLOWLIST_ROOTS), com_error (workbook metadata could not be read). Requires EXCEL_ENABLE_COM=true. |
get_all_open_workbooks |
status, workbooks[].name, workbooks[].path, workbooks[].sheets[], count |
List all workbooks open in the running Excel instance. Filters to EXCEL_ALLOWLIST_ROOTS only; workbooks outside the allowlist are silently excluded — their paths and names are not exposed, not even as a count or redacted entry. Returns {status: ok, workbooks: [...], count: N}. Requires EXCEL_ENABLE_COM=true. |
Session-mode writes — cell and range_io:
Setting session_mode=True on cell (write) or range_io (write) routes the write directly to the active Excel workbook via COM, bypassing openpyxl file I/O. Requires EXCEL_ENABLE_COM=true + EXCEL_ENABLE_WRITE=true + confirm=True. Guards: formula values (=, +, -, @ prefixes) are rejected before COM entry; xl_app.Ready is verified before access; the workbook ReadOnly flag is checked before the write. Ragged values rows in range_io are padded to n_cols with None for COM SAFEARRAY compatibility. The Excel instance is never terminated — Quit() is never called on the attached session.
Chart Tool — Sprint O (1)
| Tool | Operations | Description |
|---|---|---|
chart |
list · create · delete · update |
Manage charts: operation='list' (no confirm needed), 'create' (bar/line/scatter/pie), 'delete' (by 0-based index), 'update' (replace data range). 'create' accepts data_address, target_address (placement cell), anchor, width_px, height_px. |
Render Verification
After build iterations are complete, use export_as_pdf with scope='sheet' (one sheet
at a time) or scope='workbook' (full workbook) to render through a live Excel COM
session and visually confirm that print areas, conditional formatting, charts, and formula
values appear correctly. See docs/render-check-iterate.md
for the full iteration loop and export directory convention.
Render-Check-Iterate Tools (3)
All require EXCEL_ENABLE_COM=true. Auto-save on success. confirm=True required.
| Tool | Description |
|---|---|
review_workbook_render |
Run structural quality checks (print area, empty sheets, stale formulas) before PDF export. Returns passed, findings[], checks_run |
produce_export_evidence_bundle |
Aggregate export_as_pdf results + render review findings into a versioned JSON evidence bundle for issue tracker attachment |
export_changed_sheets_only |
Hash sheet content; re-export only changed sheets to a timestamped directory; returns exported_sheets[], new_hashes{} |
ACP (Artifact Context Packet) Tool (1)
Read-only. No write gate or confirm required.
| Tool | Description |
|---|---|
get_workbook_context |
Return a structured Artifact Context Packet (ACP) for a workbook at index, focused, or deep disclosure level. Never exposes cell values. Use before styling or export operations. |
VBA Tools — COM (2)
Both require EXCEL_ENABLE_COM=true. run_macro additionally requires EXCEL_ENABLE_MACROS=true + EXCEL_ENABLE_WRITE=true + confirm=True. Windows-only — requires pywin32. Only .xlsm files (macro-enabled workbooks) are accepted. list_macros requires Trust Center access to the VBA project object model.
| Tool | EXCEL_ENABLE_MACROS? | Description |
|---|---|---|
list_macros |
no | List all VBA components in a workbook via the VBA project object model; returns component names and types. .xlsm only. |
run_macro |
yes | Execute a named VBA macro in an .xlsm workbook via Excel COM; macro_name validated against ^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)?$; single-quote injection guard on workbook filename. Requires EXCEL_ENABLE_WRITE=true + confirm=True. |
MCP Resources
Nine read-only resources expose workbook structure and workbook-level inventories without requiring a tool call.
Most follow the excelmcp://workbook/{path}/... URI scheme; excelmcp://meta/contract is a path-free schema resource.
Path encoding:
{path}must be URL-encoded because Windows paths contain backslashes and colons. Useurllib.parse.quote(path, safe='')in Python or the equivalent in your client before constructing the URI. Example:C:\Temp\file.xlsx→C%3A%5CTemp%5Cfile.xlsx
| Resource URI template | Description | Key response fields |
|---|---|---|
excelmcp://workbook/{path}/sheets |
List all worksheets in the workbook with used-range dimensions | sheets[].name, sheets[].min_row, sheets[].max_row, sheets[].min_col, sheets[].max_col, sheets[].empty |
excelmcp://workbook/{path}/sheet/{name} |
Return structural info (dims, headers, tables) for a specific sheet | sheet, min_row, max_row, min_col, max_col, headers[], tables[] |
excelmcp://workbook/{path}/charts |
Return embedded chart inventory for the workbook | charts[].sheet, charts[].index, charts[].type, charts[].anchor, charts[].data_address |
excelmcp://workbook/{path}/tables |
Return all Excel tables with names, refs, and column names | tables[].sheet, tables[].name, tables[].ref, tables[].columns[] |
excelmcp://workbook/{path}/named_ranges |
Return all named ranges defined in the workbook | named_ranges[].name, named_ranges[].refers_to, named_ranges[].scope |
excelmcp://workbook/{path}/validations |
Return workbook data-validation inventory | validations[].sheet, validations[].sqref, validations[].type, validations[].formula1, validations[].formula2 |
excelmcp://workbook/{path}/metadata |
Return canonical structural WorkbookMetadata for the workbook (10 fields; rewired in Metadata Umbrella Sprint from _MCP_META to build_workbook_metadata()) |
path, workbook_name, sheet_names, active_sheet, sheet_count, has_formulas, last_modified, size_bytes, format, contract_version, policy |
excelmcp://workbook/{path}/meta |
Legacy alias for /metadata; identical response shape via the same build_workbook_metadata() call |
same as /metadata |
excelmcp://meta/contract |
WorkbookMetadata contract schema — field names, types, and descriptions. No path parameter. |
contract_version, fields[], canonical_field_order[] |
MCP Prompts
Thirty-five built-in prompts provide ready-to-use orchestration instructions covering 100% of the tool surface. Pipe raw JSON output from a read tool directly into the matching prompt parameter.
Phase 2.0 — Analysis and Design (8 prompts)
| Prompt | Parameters | Use case |
|---|---|---|
summarize_sheet |
sheet_json: str — raw JSON from read_sheet_all or the sheet/{name} resource |
Produce a concise business summary: metrics, trends, data quality issues, actionable insights |
find_anomalies |
range_json: str — raw JSON from read_range |
Flag statistical outliers, missing values, impossible values, and duplicate rows in a range |
write_formula |
description: str, column_layout: str |
Generate an Excel formula for the described calculation given the column layout |
compare_ranges |
range_a: str, range_b: str — two JSON range strings |
Summarise values added, removed, and changed between two ranges; group by significance |
extract_structured_table |
raw_range: str — raw JSON from read_range |
Convert a messy Excel range to a clean JSON table with inferred column types; output: {"headers": [...], "rows": [[...]]} |
design_workbook |
business_context: str |
Plan a new workbook structure from a business description: suggest sheet names, columns, data types, and examples ready to pass to create_workbook |
suggest_formatting |
sheet_json: str, report_purpose: str — purpose as plain text |
Given sheet data and report purpose, suggest a minimal professional formatting sequence (which tools to call, in which order, with what parameters) |
prepare_for_print |
sheet_description: str, page_size: str = "A4" |
Given a sheet description and target page size, suggest a professional print configuration: set_print_area range, freeze_panes cell, and set_column_width values so the data fits neatly on the page |
Phase 2.0 — Orchestration (5 prompts)
| Prompt | Parameters | Use case |
|---|---|---|
audit_workbook |
path: str |
Discovery prompt: orchestrate the core workbook resources to understand a workbook before editing — sheets, tables, charts, named ranges; the current resource surface totals seven read-only resources including validations and workbook metadata |
chart_from_data |
sheet_json: str, insight: str |
Guide for creating a chart to visualise a specific business insight; selects chart type from time/category/part-to-whole/correlation and calls chart create |
build_data_validation |
field_description: str |
Generate a complete data_validation add tool call JSON spec (address, type, operator, formula1/2, error/prompt messages) for the described field |
pivot_workflow |
source_description: str |
Step-by-step guide for creating a pivot table via COM; includes a SUMIFS/COUNTIFS fallback for environments where COM is unavailable |
bulk_format_report |
sheet_json: str, brand_colors: str = "" |
Orchestrate 7-step bulk styling for a report sheet using the consulting navy/grey palette (or custom hex colors) |
Phase 2.1 — Tool Surface Coverage (12 prompts)
| Prompt | Parameters | Use case |
|---|---|---|
manage_sheets |
workbook_description: str |
Audit and reorganise all sheets: add, rename, delete, copy, set tab color/visibility; orchestrates sheet, sheet_properties, bulk_copy_sheet, save |
reshape_table |
table_description: str, change_description: str |
Insert/delete rows and columns, set row heights and column widths, hide columns; orchestrates rows, cols, set_row_heights, set_column_widths, set_column_visibility |
style_dashboard_range |
range_description: str, purpose: str |
Apply polished dashboard formatting in the correct order (fill → align → number format → border → banding); orchestrates apply_style, apply_alignment, apply_number_format, add_border |
named_range_workflow |
intent: str |
List, create, and verify named ranges; orchestrates named_range (list/write/read) with best-practice naming conventions |
annotate_workbook |
items_json: str — JSON array of {cell, comment, url} objects |
Add cell comments and hyperlinks in bulk; orchestrates bulk_add_comments, bulk_add_hyperlinks, get_hyperlinks, remove_hyperlink |
format_header_section |
header_description: str |
Merge title row, style header row, and apply auto-filter; orchestrates merge, apply_style, apply_alignment, auto_filter |
import_and_style_csv |
csv_path: str, target_sheet: str |
Import a CSV, auto-fit columns, convert to an Excel Table, freeze the header row; orchestrates import_csv_to_sheet, set_column_widths, create_table, freeze_panes, save |
export_workbook_report |
path: str, sheet_names: str — comma-separated |
Finalise print areas and export each sheet plus the full workbook to PDF; orchestrates set_print_area, freeze_panes, export_as_pdf, save |
lock_template |
path: str, editable_ranges: str — comma-separated |
Protect all sheets and workbook structure while keeping specified ranges editable; orchestrates protect; includes password guidance |
grow_table |
path: str, sheet_name: str, table_name: str |
Discover table schema, append rows in correct column order, verify table auto-expansion; orchestrates list_tables, read_table, append_rows, get_used_range |
highlight_exceptions |
range_address: str, rule_description: str |
Apply CellIs or formula-based conditional formatting with traffic-light colours; orchestrates apply_conditional_format |
validate_and_recalculate |
path: str |
Validate formula syntax, force-recalculate via COM, inspect used ranges, and verify KPI cached values; orchestrates validate_formula_syntax, recalculate_workbook, get_used_range, evaluate_formula |
Phase 2.2+ — Extended Coverage (7 prompts)
| Prompt | Parameters | Use case |
|---|---|---|
read_write_and_inspect_range |
path: str, sheet_name: str, address: str |
Read, write, copy (values+formats), inspect cell metadata, and find-replace in a range; orchestrates range_io, copy_range, get_cell_metadata, find_replace |
annotate_and_configure_sheet |
path: str, sheet_name: str |
Manage cell comments and hyperlinks, and configure sheet display (gridlines, headers, zoom, tab color); orchestrates cell_comment, hyperlink, sheet_properties |
style_and_write_range |
range_description: str, style_intent: str |
Write data with inline formatting, apply style to non-contiguous ranges, or format-paint from a template; orchestrates write_range_with_format, apply_style, copy_range_format |
duplicate_sheet_template |
path: str, source_sheet: str, new_names: str — comma-separated |
Copy a template sheet to multiple named project sheets in one call; orchestrates bulk_copy_sheet |
discover_server_and_manage_named_ranges |
path: str |
Discover server capabilities and feature flags, then list/create/verify named ranges; orchestrates capabilities, named_range |
export_or_hydrate |
intent: str |
Export a sheet or workbook to PDF, or fill a template with project data and save as a new file; orchestrates export_as_pdf, hydrate_template (both require EXCEL_ENABLE_COM=true) |
configure_workbook_calculation |
path: str |
Set auto/manual/autoNoTable calculation mode and the full_calc_on_load flag; orchestrates set_workbook_calc_mode |
Metadata Umbrella Sprint — Governance Prompts (2 prompts)
| Prompt | Parameters | Use case |
|---|---|---|
workbook_metadata_workflow |
path: str — absolute workbook path |
4-step metadata-first workflow: load /metadata resource → review 10 canonical fields → act on strict/lenient policy → confirm before subsequent tool calls |
metadata_contract_reference |
(none) | Returns the WorkbookMetadata contract table (all 10 canonical fields with types and descriptions) plus the active EXCEL_METADATA_POLICY note |
Removed Parameter Aliases
Seven backward-compatible parameter aliases (freeze_at, src_range, dst_range, table_range, anchor_cell, end_cell, range_address) were removed in the Contract Stabilization sprint. Only the canonical parameter names (address, src_address, dst_address, start_address, end_address) are accepted. See the tool descriptions above for current parameter names.
Metadata Governance (audit_workbook update)
The audit_workbook prompt now references the updated /metadata resource which returns structural WorkbookMetadata (10 canonical fields) rather than the _MCP_META hidden-sheet payload. Use excelmcp://meta/contract to inspect the field contract before building automation that consumes metadata responses.
UX Patterns and Recipes
Recipe 1 — Read a Budget Spreadsheet
1. capabilities()
→ confirm version, phase, backend, available tools, and live prompts payload
2. sheet(path, operation="list")
→ {"sheets": ["Summary", "Q1", "Q2", "Q3", "Q4"], "count": 5}
3. get_used_range(path, sheet="Summary")
→ {"min_row": 1, "max_row": 42, "min_col": 1, "max_col": 8, "empty": false}
4. range_io(path, sheet="Summary", operation="read", address="A1:H42")
→ 2-D array of all budget data
5. named_range(path, operation="list")
→ ["TotalRevenue", "COGS", "GrossMargin"] with their cell references
Recipe 2 — Update Cells
1. cell(path, sheet="Q1", operation="write", address="C10", value=125000, confirm=True)
→ auto-saved immediately
2. range_io(path, sheet="Q1", operation="write", address="A2",
values=[["Jan", 10000], ["Feb", 12000], ["Mar", 11500]], confirm=True)
→ auto-saved immediately
3. save(path, confirm=True)
→ safe no-op if nothing pending in the write cache
Recipe 3 — Import Data from CSV
1. import_csv_to_sheet(path, sheet="RawData",
csv_path="C:/data/export.csv",
overwrite=True, confirm=True)
→ auto-saved immediately; existing data replaced
2. read_sheet_all(path, sheet="RawData")
→ verify the imported rows
3. list_tables(path)
→ check if any Excel tables are defined over the imported range
Recipe 4 — Sheet Management
1. sheet(path, operation="add", sheet_name="Q5 Forecast", position=5, confirm=True)
(auto-saved immediately)
2. bulk_copy_sheet(path, source_sheet="Q4", new_names=["Q5 Template"], confirm=True)
(auto-saved immediately)
3. sheet(path, operation="rename", sheet_name="Sheet1", new_name="Index", confirm=True)
(auto-saved immediately)
4. sheet(path, operation="delete", sheet_name="Temp", confirm=True)
(auto-saved immediately)
5. save(path, confirm=True) # optional defensive no-op if nothing is pending
Recipe 5 — Format a Summary Table
1. apply_style(path, sheet="Summary", address="A1:H1",
bold=True, bg_color="1F3864", font_color="FFFFFF", confirm=True)
(auto-saved)
2. apply_alignment(path, sheet="Summary", address="A1:H1",
horizontal="center", vertical="center", confirm=True)
3. apply_number_format(path, sheet="Summary", address="C2:H20",
format_code="#,##0.00", confirm=True)
4. set_column_widths(path, sheet="Summary", columns=["A"], width=12, confirm=True)
(auto-saved)
5. add_border(path, sheet="Summary", address="A1:H20",
border_style="thin", color="AAAAAA", confirm=True)
6. save(path, confirm=True)
→ persists apply_alignment, apply_number_format, add_border changes
Recipe 6 — Create and Delete a Chart
1. chart(path, sheet="Revenue", operation="create", chart_type="bar",
data_address="A1:B13",
title="Monthly Revenue 2026",
anchor="D1", confirm=True)
→ auto-saved; chart appears at column D, row 1
2. chart(path, sheet="Revenue", operation="list")
→ {"charts": [{"index": 0, "title": "Monthly Revenue 2026",
"type": "BarChart", "anchor": "3,0"}], "count": 1}
3. chart(path, sheet="Revenue", operation="delete", chart_index=0, confirm=True)
→ chart removed; auto-saved
Recipe 7 — Read Workbook Structure via MCP Resources
1. GET excelmcp://workbook/C%3A%5CTemp%5Cmyfile.xlsx/sheets
→ all sheets with their used-range dimensions (min/max row and col)
2. GET excelmcp://workbook/C%3A%5CTemp%5Cmyfile.xlsx/sheet/Sales
→ sheet dims + first-row headers + list of Excel tables
3. GET excelmcp://workbook/C%3A%5CTemp%5Cmyfile.xlsx/named_ranges
→ [{"name": "TotalRevenue", "refers_to": "Sales!$B$2:$B$13", "scope": "workbook"}, ...]
Recipe 8 — Analyse a Sheet with the summarize_sheet Prompt
1. read_sheet_all(path, sheet="Sales")
→ raw 2-D values JSON
2. Use prompt: summarize_sheet(sheet_json=<values from step 1>)
→ model produces: key metrics, trends, data quality issues, actionable insights
What Is NOT Available (Known Limitations)
| Feature | Status | Notes |
|---|---|---|
Creating a new .xlsx from scratch |
✅ Available | create_workbook(path, sheets=["Sheet1"], overwrite=False, confirm=True) |
| Formula recalculation | ✅ Available (COM, Sprint G) | recalculate_workbook — opens workbook in Excel COM, recalculates (dirty cells or full tree), saves in-place. Requires EXCEL_ENABLE_COM=true, EXCEL_ENABLE_WRITE=true, Windows-only. |
set_workbook_calc_mode |
✅ Available (Phase 2.6) | Set auto/manual calculation + fullCalcOnLoad flag |
| Creating charts (basic types) | ✅ Available | `chart(operation='create' |
| Advanced chart editing (series, axes) | ❌ Not available | Requires COM (Phase 2 win32com) |
| Pivot tables (create / refresh / read data) | ✅ Available (Sprint H COM) | `pivot_table(operation='create' |
| Named range definitions (write) | ✅ Available | named_range(path, operation='write', name=..., sheet=..., address=..., confirm=True) — Sprint O |
| Conditional formatting (write) | ✅ Available (Phase 2.8) | apply_conditional_format — CellIsRule / FormulaRule with injection guard; copy_range_format — format-painter across sheets |
| Cell borders | ✅ Available | add_border — thin/medium/thick/dashed/dotted/double with optional hex color |
| Gradient fills, pattern fills | ❌ Not available | Basic solid fills via apply_style; gradient/pattern fills require COM (Phase 2) |
| Writing merged cells | ✅ Available | `merge(path, sheet, operation='merge' |
| Row/column freeze (freeze panes) | ✅ Available | freeze_panes(path, sheet, address=..., confirm=True) — Phase 2.2. Pass address=None to unfreeze. |
| AutoFilter on a data range | ✅ Available | auto_filter(path, sheet, address, confirm=True) — Phase 2.2. Pass address=None explicitly to clear. |
| Insert rows / columns | ✅ Available | `rows(operation='insert' |
| Worksheet protection | ✅ Available | protect(path, scope='sheet', sheet=..., password=..., confirm=True) — Sprint O |
| Print area definition | ✅ Available | set_print_area(path, sheet, address, confirm=True) — Phase 2.3 |
| Cell comments (add / delete / read) | ✅ Available | bulk_add_comments (write) — Sprint D; `cell_comment(operation='get' |
| Sheet tab colour / visibility state | ✅ Available | `sheet_properties(path, sheet, operation='get' |
| Data validation rules | ✅ Available | bulk_add_data_validation (write) — Sprint D; `data_validation(operation='get' |
| Workbook structure protection | ✅ Available | protect(path, scope='workbook', password=..., confirm=True) — Sprint O |
| Password-protected workbooks | ❌ Not available | openpyxl cannot open encrypted files |
| Multiple workbooks open simultaneously | ❌ Not available | Server holds one workbook per process |
| Macros / VBA | ✅ Available (COM) | list_macros (enumerate VBA components), run_macro (execute a named macro); .xlsm only; requires EXCEL_ENABLE_COM=true; run_macro additionally requires EXCEL_ENABLE_MACROS=true + EXCEL_ENABLE_WRITE=true + confirm=True |
| Live COM / Excel.Application interaction | ✅ Available (Sprint O) | 4 COM tools: recalculate_workbook, export_as_pdf, hydrate_template, pivot_table; plus live formula evaluation via evaluate_formula(live=True). Requires EXCEL_ENABLE_COM=true, Windows-only (pywin32). |
| Insert / delete individual columns by letter | ✅ Available | `cols(path, sheet, operation='insert' |
set_column_visibility |
✅ Available (Phase 2.6) | Hide/show columns by letter |
| Streaming large files | ❌ Not available | Full workbook is loaded into memory; very large files may be slow |
Security & Governance
- All file paths are validated against
EXCEL_ALLOWLIST_ROOTSbefore any I/O. Traversal above an allowed root is blocked with"Path not in allowlist". - Write tools require
EXCEL_ENABLE_WRITE=trueAND (for destructive operations)confirm=True. The two-factor pattern prevents accidental mutation from misconfigured clients. stdoutcarries MCP JSON-RPC traffic only. All server logs go tostderr.- No network calls. All operations are local file I/O only.
- Formula injection guard (openpyxl tools):
write_range_with_formatandimport_csv_to_sheetvalidate every cell value against the injection guard — values starting with=,+,-,@are rejected (OWASP A03); plain negative numbers are exempt.hyperlink(add/bulk_addoperations) validatesdisplay_textagainst the same guard. - File size pre-check: workbook size is checked before load to prevent unexpectedly large files from consuming server memory.
- UNC path guard:
_check_path()and_check_csv_path()both reject paths starting with\\(backslash UNC, e.g.\\server\share) or//(forward-slash UNC, e.g.//server/share) beforePath.resolve()is called — preventing NTLM relay and path-normalisation bypass attacks. - COM tools (
EXCEL_ENABLE_COM=truerequired):AutomationSecurityis set tomsoAutomationSecurityForceDisable(3) on the Excel Application object before any workbook is opened — macro execution is blocked at the COM layer.hydrate_templatevalidates allcell_writesstring values against a formula-injection guard before entering the COM context (strings starting with=,+,-,@are rejected — OWASP A03). PDF export output paths are validated againstEXCEL_EXPORT_ROOTSbefore any write. - ACP adapter injection hardening (OWASP A03):
_acp_adapter.pysanitizes all user-controlled string fields embedded in ACP annotations —sheet_names,table_names, andlineage_summary["workbook_name"]— via_sanitize_text_fieldimported frommcpshared. The shared helper strips formula-prefix characters (=,+,-,@,\t,\r,\n), enforces a 255-character max length, and rejects NUL bytes. - UNC path guard (W2-005):
_check_pathand_check_csv_pathin_core.pyreject paths starting with\\— prevents NTLM credential relay via UNC network shares. - CSV import null-byte guard (R-002):
_check_csv_pathin_core.pyrejects paths containing\x00before allowlist evaluation — prevents null-byte injection in CSV import operations.
Performance Notes
PERF-OPT (v0.2.9) — LRU-Cached Style Factories
Internal optimization: apply_cell_style and apply_alignment now use
@functools.lru_cache-backed factory functions for Font, Alignment, and
PatternFill construction. Identical style parameter sets reuse the same
Python object across cells rather than allocating a new instance per cell.
Measured speedups (versus pre-optimization baseline):
| Operation | 1k cells | 5k cells | 10k cells |
|---|---|---|---|
apply_cell_style |
~1.2x | ~1.2x | ~1.7x |
apply_alignment |
~1.8x | ~2.1x | ~1.8x |
No API surface changes in PERF-OPT itself. Current README inventory is 64 tools. Historical totals: v0.3.0 raised total to 66; v0.4.0 raised total to 72; v0.5.0 raised total to 73; v0.6.0 raised total to 78; v0.7.0 raised total to 82; Sprint N consolidation reduced total to 76; Sprint O consolidation reduced total to 50; Sprint R + Metadata Umbrella raised total to 54; Phase 2 CSV Export raised total to 55; Active-Workbook Bridge Sprint raised total to 57; Governed Write + W4 additions raised total to 59; Snapshot + VBA sprint raised total to 64.
Governance overhead (amortized, µs/call): _check_write ~0.4 µs,
_check_confirm ~0.1 µs, _check_path varies by allowlist length.
Server Module Layout
The Refactor Sprint (2026-03-11) decomposed 4 monolithic files into 17 focused modules. server.py is the MCP entry point and imports all modules at startup via side-effect imports. Sprint W4 added server_com_session.py; Snapshot + VBA sprint added server_snapshot.py and server_com_vba.py (total: 64).
Server Entry Points
| Module | Lines | Role |
|---|---|---|
server.py |
~120L | FastMCP instance, resource registrations, imports prompt modules, main() |
server_com.py |
~31L | Thin re-exporter for COM tools (imports server_com_export, server_com_pivot, server_com_session) |
server_com_export.py |
~409L | export_as_pdf, export_range_as_csv tools |
server_com_pivot.py |
~425L | recalculate_workbook, hydrate_template, pivot_table tools |
server_com_session.py |
~120L | get_active_workbook_context, get_all_open_workbooks — active-workbook bridge tools |
server_com_vba.py |
— | list_macros, run_macro — VBA COM tools |
server_snapshot.py |
— | snapshot_workbook, diff_workbooks — snapshot/diff tools |
server_prompts_analysis.py |
~47L | Analysis prompt definitions |
server_prompts_format.py |
~319L | Formatting prompt definitions |
server_prompts_workflow.py |
~426L | Workflow prompt definitions |
server_prompts_mgmt.py |
~242L | Workbook management prompt definitions |
Tool-Group Server Modules (Sprint P — unchanged)
| Module | Tools | Role |
|---|---|---|
server_io.py |
18 | I/O tools: capabilities, create_workbook, sheet, get_used_range, cell, range_io, read_sheet_all, named_range, get_cell_metadata, evaluate_formula, list_tables, read_table, append_rows, find_replace, import_csv_to_sheet, save, workbook_metadata, consolidate_ranges |
server_format.py |
8 | Formatting tools: apply_number_format, apply_style, apply_alignment, add_border, apply_format_to_sheet_list, copy_range_format, write_range_with_format, apply_conditional_format |
server_ops.py |
16 | Operations tools: merge, freeze_panes, auto_filter, rows, cols, protect, set_print_area, cell_comment, sheet_properties, data_validation, validate_formula_syntax, copy_range, create_table, set_column_visibility, set_workbook_calc_mode, hyperlink |
server_batch.py |
6 | Batch tools: bulk_copy_sheet, set_row_heights, set_column_widths, bulk_add_comments, fill_formula_range, bulk_range_write |
server_chart.py |
1 | Chart tool (chart) — registers with the shared mcp instance via side-effect import |
server_review.py |
3 | Render-review tools (review_workbook_render, produce_export_evidence_bundle, export_changed_sheets_only) |
server_acp.py |
1 | ACP tool: get_workbook_context (Artifact Context Packet — index/focused/deep disclosure; never exposes cell values) |
server_com_session.py |
2 | Active-workbook bridge: get_active_workbook_context, get_all_open_workbooks — read-only COM session tools |
server_snapshot.py |
2 | Snapshot tools: snapshot_workbook, diff_workbooks |
server_com_vba.py |
2 | VBA COM tools: list_macros, run_macro |
_server_instance.py |
— | Holds the single shared mcp = FastMCP(...) instance; imported by all server_*.py modules to guarantee one registry |
Implementation Modules
| Module | Lines | Role |
|---|---|---|
_format_cell.py |
~251L | Cell style, font, fill, alignment, border helpers |
_format_dimensions.py |
~304L | Column/row width/height |
_format_bulk.py |
~402L | apply_style_to_ranges, apply_format_to_sheet_list, copy_range_format |
_format.py |
~8L | Thin re-export router |
_data_cell.py |
~127L | Cell metadata, formula evaluation |
_data_tables.py |
~214L | Tables + named ranges |
_data_annotations.py |
~392L | Comments + hyperlinks |
_data_write.py |
~188L | fill_formula_range, write_range_with_format |
_data.py |
~9L | Thin re-export router |
All implementation modules (_core.py, _ooxml.py, _com.py, _advanced.py, _sheet.py, etc.) contain no FastMCP imports — pure logic, callable by both the MCP layer and tests.
Changelog
See ../CHANGELOG.md for the full release history.
v0.3 — capabilities_v2 + server decomposition (5cd6dad)
capabilities() now returns a machine-readable capabilities_v2 object (TOOL_REGISTRY 64 entries,
17 dispatcher vocabularies, per-tool gate metadata, tool modes). format() dispatcher added.
Server layer decomposed into focused modules. session_mode stubs removed. See
../shared/design/capabilities_schema_v1.md for schema spec.
Governed Write — Active-Workbook Sprint (2026-03-13)
Governed COM write for cell and range_io; 35 new tests (D-01..D-12, E-01..E-11, F/G-series); tool count unchanged at 57
cell(write) andrange_io(write) extended withsession_mode: bool = Falseparameter (server_io.py,_io.py).session_mode=Trueroutes writes via COM to the active Excel workbook;session_mode=False(default) preserves existing openpyxl path — no behaviour change for existing callers.- Requires
EXCEL_ENABLE_COM=true+EXCEL_ENABLE_WRITE=true+confirm=Truewhensession_mode=True. _active_wb.pyextended:_write_cell_via_com(),_write_range_via_com(),_is_valid_a1()added.- Guards: formula values (
=,+,-,@prefixes) rejected;xl_app.Readychecked before access;ReadOnlyflag checked before write;Quit()never called on the attached session. - Ragged
valuesrows padded ton_colswithNonefor COM SAFEARRAY safety (range_iowrite path). - 35 new unit tests (
test_governed_write.py): D-01..D-12, E-01..E-10, E-11, F/G-series. - Total tool count: 57 (unchanged)
Active-Workbook Bridge Sprint — W4 (2026-03-13)
2 new COM session tools; 1161 tests passing; tool count 55 → 57
- New tool
get_active_workbook_context(server_com_session.py): returns{status, workbook: {name, path, sheets[]}}for the workbook active in the running Excel session. RequiresEXCEL_ENABLE_COM=true. Workbooks outsideEXCEL_ALLOWLIST_ROOTSreturnstatus: outside_allowlist. No write gate. - New tool
get_all_open_workbooks(server_com_session.py): lists all allowlisted workbooks open in Excel; out-of-scope workbooks are silently excluded. Returns{status, workbooks[], count}. No write gate. - New module
_active_wb.py: COM helper —_com_active_app()context manager (attaches to live Excel session viaGetActiveObject),_is_in_allowlist(),get_active_workbook_com(), and_check_file_not_open_in_excel()(LC-2 conflict guard). _io.pyand_sheet.pyupdated:_check_file_not_open_in_excel()added to all write paths (LC-2 guard — blocks openpyxl write when the target file is currently open in Excel).runtime_config.pyupdated:get_session_mode()added — readsEXCEL_SESSION_MODEenv var; emitsUserWarningwhen session mode is enabled withoutEXCEL_ENABLE_COM=true.- New env var:
EXCEL_SESSION_MODE(defaultfalse) — enables session-mode routing for the active-workbook bridge. - Total tool count: 57
Refactor Sprint (2026-03-11)
Monolith decomposition — no tools added or removed; 1069 tests passing; 4 project tracker tech-debt items closed
- Decomposed monolithic
server.py(was 1048L → ~120L) into 4server_prompts_*.pymodules (server_prompts_analysis.py,server_prompts_format.py,server_prompts_workflow.py,server_prompts_mgmt.py) - Decomposed
server_com.py(was 859L → ~31L re-export router) intoserver_com_export.py(~409L) +server_com_pivot.py(~425L) - Decomposed
_format.py(was 898L → ~8L re-export router) into_format_cell.py(~251L) +_format_dimensions.py(~304L) +_format_bulk.py(~402L) - Decomposed
_data.py(was 868L → ~9L re-export router) into_data_cell.py(~127L) +_data_tables.py(~214L) +_data_annotations.py(~392L) +_data_write.py(~188L) - All 4 project tracker tech-debt items closed
- Total tool count: 55 (unchanged)
Phase 2 — CSV Export + COM Hardening (2026-03-11)
1 new tool (export_range_as_csv); 2 new security helpers in _com.py; tool count 54 → 55
export_range_as_csv(server_com.py): dual-path CSV export —live=False(openpyxl, no COM required) orlive=True(Excel COM, requiresEXCEL_ENABLE_COM=true). Writing tooutput_pathrequiresEXCEL_ENABLE_WRITE=true+ path insideEXCEL_CSV_EXPORT_ROOTS+confirm=True. Returns{ok, sheet, address, rows_exported, output_path}or{ok, csv}for console-only mode._ensure_com_gate()hardened:sys.platformguard added — raisesNotAllowedErrorimmediately on non-Windows platforms before anywin32comimport is attempted.- New helper
_check_csv_export_path(_com.py): validates CSVoutput_pathagainstEXCEL_CSV_EXPORT_ROOTS; mirrors the_check_export_pathpattern used for PDF outputs. - New helper
_sanitize_csv_value(_com.py): CSV injection guard (OWASP A03) — cell string values starting with=,+,-,@are prefixed with'before serialisation. - New env var:
EXCEL_CSV_EXPORT_ROOTS(no default) — comma-separated absolute folder paths permitted as CSV output destinations. - Total tool count: 55
Metadata Umbrella Sprint (2026-03-11)
43 new tests (15+9+15+4); 143 total regression pass; tool count 54 unchanged; 2 new prompts; 2 new resources
- New module
_metadata_contract.py:WorkbookMetadataTypedDict SSOT,CONTRACT_VERSION="1.0", 10CANONICAL_FIELDS,build_workbook_metadata()— validates path via allowlist, stats filesystem, loads workbook read-only, scans for formulas - New module
runtime_config.py:EXCEL_METADATA_POLICYenv var ("strict"default /"lenient");load_runtime_config(),get_effective_policy(),is_strict(),get_contract_version() - New module
_lineage.py:FIELD_ALIASESdict (8 legacy→canonical mappings);normalize_metadata()/map_lineage_fields()— normalises dicts that use legacy key names to the canonicalWorkbookMetadatashape - New module
server_metadata_prompts.py:workbook_metadata_workflowprompt (4-step metadata-first flow),metadata_contract_referenceprompt (contract table),excelmcp://meta/contractresource (schema JSON, no path parameter) server_io.pyupdated:/metadataand/metaresources rewired tobuild_workbook_metadata();get_workbook_metadata()shared impl injectspolicyfromEXCEL_METADATA_POLICY;capabilities()now returnsmetadata_contract_versionandmetadata_policyfieldsserver.pyupdated: side-effect importexcelmcp.server_metadata_promptsadded- New test files:
test_metadata_contract.py(15),test_runtime_config.py(9),test_lineage.py(15),test_capabilities_drift.py(4)
DA Security Hardening — probe_excel_reopen (2026-03-06)
_da_com.py security hardening — no tools added or removed in that sprint; current README inventory is 51 tools
probe_excel_reopen: now raisesNotAllowedErrorwhenEXCEL_ENABLE_COM != "true"— was previously silently bypassedprobe_excel_reopen: non-temp paths denied by default whenEXCEL_ALLOWLIST_ROOTSis unset (explicit deny-by-default)probe_excel_reopen:AutomationSecurity = 3(msoAutomationSecurityForceDisable) set before any workbook open — macro execution blocked at COM layer- Return dict no longer includes
temp_pathkey - New test file:
test_unit_da_com.py(3 unit tests covering COM gate, allowlist gate, and security flags)
Phase 5 QA — Security Hardening Bug Sprint (2026-03-05)
Commit b694ac6 on staging — no tools added or removed in that sprint; current README inventory is 51 tools; 815 → 846 tests
write_range_with_format+import_csv_to_sheet: formula injection guard applied to all cell values before write; plain negative numbers exempt in CSV context (OWASP A03)hyperlink(add/bulk_add):display_textparameter now validated against the injection guard- File size pre-check added before opening workbooks
protect(sheet scope): password hashing corrected — openpyxl hash algorithm now applied correctlyfind_replace: emptyfind_textandNonecell values handled without raising- New test file:
test_security_guards.py(30 tests covering all injection-guard and file-size scenarios) - ruff lint clean
Sprint P — Server Decomposition (2026-03-04)
Structural refactor: server.py (1363 lines) decomposed into 4 tool-group modules. Current README inventory is 51 total tools after the later addition of workbook_metadata.
server_io.py(new, 17 tools):capabilities,create_workbook,sheet,get_used_range,cell,range_io,read_sheet_all,named_range,get_cell_metadata,evaluate_formula,list_tables,read_table,append_rows,find_replace,import_csv_to_sheet,save,workbook_metadataserver_format.py(new, 8 tools):apply_number_format,apply_style,apply_alignment,add_border,apply_format_to_sheet_list,copy_range_format,write_range_with_format,apply_conditional_formatserver_ops.py(new, 16 tools):merge,freeze_panes,auto_filter,rows,cols,protect,set_print_area,cell_comment,sheet_properties,data_validation,validate_formula_syntax,copy_range,create_table,set_column_visibility,set_workbook_calc_mode,hyperlinkserver_batch.py(new, 5 tools):bulk_copy_sheet,set_row_heights,set_column_widths,bulk_add_comments,fill_formula_range- At Sprint P refactor time,
server.pyslimmed from 1363 lines into a lean entry point containing resources, prompts, side-effect imports, andmain()only; the decomposition enforces ≤500-line threshold per tool module - Test file structural splits (no logic changes):
test_sprint_n_b.py(fromtest_sprint_n.py— CommitB+C tests),test_unit_com_p2.py(fromtest_unit_com.py— ExportWorkbook+Hydrate+OpenTemplate tests),test_unit_com_h2.py(fromtest_unit_com_h.py— CreatePivot+Refresh+ReadPivot tests) - Current total tool count: 51
Sprint O — Tool Consolidation (2026-03-04)
76 → 50 tools: 26 single-operation tools removed; superseded by unified dispatch tools
| Removed tools | Unified dispatch replacement |
|---|---|
list_sheets, add_sheet, rename_sheet, delete_sheet |
sheet(operation='list'|'add'|'rename'|'delete') |
read_cell, write_cell |
cell(operation='read'|'write') |
read_range, write_range |
range_io(operation='read'|'write') |
get_named_ranges, read_named_range, write_named_range |
named_range(operation='list'|'read'|'write') |
list_merged_cells, merge_cells, unmerge_cells |
merge(operation='list'|'merge'|'unmerge') |
get_cell_comment, delete_cell_comment |
cell_comment(operation='get'|'delete') |
get_sheet_properties, set_sheet_properties |
sheet_properties(operation='get'|'set') |
get_data_validation_info, bulk_add_data_validation |
data_validation(operation='get'|'add') |
get_hyperlinks, add_hyperlink, bulk_add_hyperlinks, remove_hyperlink |
hyperlink(operation='get'|'add'|'remove') |
apply_cell_style, apply_style_to_ranges |
apply_style(ranges=[...]) |
insert_rows, delete_rows |
rows(operation='insert'|'delete') |
insert_cols, delete_cols |
cols(operation='insert'|'delete') |
protect_sheet, protect_workbook |
protect(scope='sheet'|'workbook') |
create_chart, list_charts, delete_chart, update_chart_data |
chart(operation='create'|'list'|'delete'|'update') |
export_sheet_as_pdf, export_workbook_as_pdf |
export_as_pdf(scope='sheet'|'workbook') |
open_template_and_recalculate |
hydrate_template(cell_writes=[]) |
evaluate_formula_live |
evaluate_formula(live=True) |
create_pivot_table, refresh_pivot_tables, read_pivot_table |
pivot_table(operation='create'|'read'|'refresh') |
Tool count: 76 → 50 (server.py=45, server_com.py=4, server_chart.py=1). All unified dispatch tools accept the same payloads as their single-operation predecessors — no calling-pattern migration needed for existing valid inputs.
Sprint N — Tool Consolidation (2026-03-04)
82 → 76 tools: 6 single-operation tools removed; superseded by bulk counterparts
| Removed tool | Superseded by |
|---|---|
set_column_width |
set_column_widths |
set_row_height |
set_row_heights |
copy_sheet |
bulk_copy_sheet |
add_hyperlink |
bulk_add_hyperlinks |
add_cell_comment |
bulk_add_comments |
add_data_validation |
bulk_add_data_validation |
All bulk replacements handle single-item inputs — no calling-pattern change needed.
v0.6.0 — 2026-03-02
Sprint G — 78-tool release: 5 COM Phase 2 tools (Windows-only, pywin32)
- New infrastructure module:
_com.py— shared COM helpers (_com_excel_app,_open_workbook,_close_workbook,_ensure_com_gate,_check_export_path,_guard_cell_value);EXCEL_ENABLE_COMgate;AutomationSecurity=3macro-execution block - New server module:
server_com.py— 5 COM tools registered via side-effect import at startup (mirrorsserver_chart.pypattern) - New env vars:
EXCEL_ENABLE_COM(COM activation gate),EXCEL_EXPORT_ROOTS(PDF output allowlist) recalculate_workbook(server_com.py): force-recalculate workbook via COM;full_calc=False(dirty cells) orfull_calc=True(CalculateFull); saves in-place; returns{ok, path, calc_mode, elapsed_ms}export_sheet_as_pdf(server_com.py): export single sheet to PDF read-only;quality0/1; output validated againstEXCEL_EXPORT_ROOTS; returns{ok, sheet, output_path, quality}export_workbook_as_pdf(server_com.py): export entire workbook to PDF read-only; all visible sheets in print order; returns{ok, output_path, quality, source}hydrate_template(server_com.py): writecell_writes({ref, value} list) to template thenSaveAsnew file; formula-injection guard on all string values before COM entry;recalculate=Truedefault; returns{ok, output_path, writes_applied, recalculated}open_template_and_recalculate(server_com.py): open template,CalculateFull(),SaveAsnew file; for volatile function workbooks (TODAY, NOW, RAND, OFFSET); returns{ok, output_path, template, recalculated: true}- New test files:
test_unit_com.py(31 unit tests, mock-based, no Excel required),test_integration_com.py(integration,pytest -m integration) - E2E gap-fix (commits
2e7e178,4f75ff5): ruff lint fixes;recalculated: trueadded toopen_template_and_recalculatereturn;test_unit_com.pyexpanded 22 → 31 tests; total suite 602 passing - Total tool count: 78 (was 73)
v0.5.1 — 2026-03-02
Sprint F — Internal quality hardening: modular server architecture (no new tools)
_ooxml.py: v-before-f regex bugfix — corrected attribute ordering in<f>element OOXML output (internal, no user-visible change)_format.py: gate-order hardening forapply_cell_style+apply_alignment— write-enable check now always precedes confirm check (internal, no API change)- New files:
_server_instance.py(sharedmcpFastMCP instance) +server_chart.py(chart tools module);server.pyregisters chart tools via side-effect import at startup - New test:
test_server_chart_module.py— verifies modular import contract - Total tool count: 73 (unchanged)
v0.5.0 — 2026-03-02
Sprint E — 73-tool release: update_chart_data + create_chart size/anchor extensions (Phase 2.9)
update_chart_data(chart write, new): replace the data range of an existing chart by 0-basedchart_index(seelist_charts);data_rangee.g.'A1:B5'— must contain':'; requiresEXCEL_ENABLE_WRITE=true+confirm=True; returns{ok, sheet, chart_index, data_range}create_chart(chart write, extended): new optional paramsanchor(overridestarget_cell, backward-compatible alias),width_px,height_px(chart size in pixels, 1–5000); fully backward-compatible with existing callers- Total tool count: 73 (was 72)
- New test files:
test_charts_sprint_e.py,test_da_ooxml_strip.py(split fromtest_da_finalize.py) - Commit:
2a1115bonstaging
v0.4.0 — 2026-02-27
Sprint D — 72-tool release: 7 bulk-operation / formatting tools (Phase 2.8)
- Sprint D tools added (7):
fill_formula_range,write_range_with_format,bulk_add_comments,bulk_add_data_validation,apply_format_to_sheet_list(v2 extension),copy_range_format,apply_conditional_format fill_formula_range(_data.py): fill a formula across a range usingopenpyxl.formula.translate.Translatorfor correct cell-reference relativisation; returns{ok, sheet, anchor_cell, cells_filled}write_range_with_format(_data.py): write 2-D values + apply style/number_format/alignment atomically; returns{ok, sheet, start_cell, rows_written}bulk_add_comments(_data.py): add multiple cell comments in one call with fail-fast validation; returns{ok, sheet, comments_added}bulk_add_data_validation(_advanced.py): apply multiple validation rules at once;validation_typerestricted to type allowlist to prevent injection; returns{ok, sheet, rules_applied}apply_format_to_sheet_listv2 (_format.py): extends Sprint C tool withcolumn_widthsandrow_heightsdict params; fully backward-compatiblecopy_range_format(_format.py): format-painter — copies cell formatting (not values) from src_range to dst_range, cross-sheet supported; returns{ok, src_sheet, dst_sheet, cells_copied}apply_conditional_format(_advanced.py): applies CellIsRule or FormulaRule conditional formatting; formula strings validated against injection guard before write; returns{ok, sheet, range_address, rule_type}
v0.3.0 — 2026-02-26
Sprint C — 66-tool release: 6 bulk-operation tools + add_hyperlink security backport
- Sprint C tools added (6):
bulk_copy_sheet,set_row_heights,apply_style_to_ranges,set_column_widths,bulk_add_hyperlinks,apply_format_to_sheet_list bulk_copy_sheet(_sheet.py): copy a source sheet to multiple named copies in one call; validates unique names, ≤31 chars, no invalid chars; returns{ok, source_sheet, sheets_created, names}; charts/images/print settings NOT copied (openpyxl limitation)set_row_heights(_format.py): uniform mode (rows/row_range+height) or per-row mode (row_specs); returns{ok, sheet, rows_set}apply_style_to_ranges(_format.py): apply font/fill to non-contiguous ranges in one call;EXCEL_MAX_RANGE_CELLSenforced across all ranges; returns{ok, sheet, ranges_processed, cells_styled}set_column_widths(_format.py): explicit list orcol_range; manualwidthorautofit=True(content-length approximation); returns{ok, sheet, columns_set, widths}bulk_add_hyperlinks(_data.py): add multiple hyperlinks atomically; URL scheme allowlist{https, http, mailto};file://opt-in viaEXCEL_ALLOW_FILE_HYPERLINKS=true;javascript:/vbscript:/data:always blocked; returns{ok, sheet, links_added}apply_format_to_sheet_list(_format.py): apply style/number_format/alignment to the same address across multiple sheets;sheets=Noneauto-selects all non-_-prefixed sheets; returns{ok, sheets_processed, cells_formatted}- Security backport:
add_hyperlinknow validates URL scheme — rejectsjavascript:,vbscript:,data:(OWASP A03/A05);file://requiresEXCEL_ALLOW_FILE_HYPERLINKS=true - New env var:
EXCEL_ALLOW_FILE_HYPERLINKS(defaultfalse) — opt-in forfile://hyperlink scheme
v0.2.9 — 2026-02-26
- Engine:
apply_cell_style/apply_alignmentstyle factories wrapped with@functools.lru_cache— identical style param sets reuse cached objects, reducing per-cell Python allocation overhead. - Benchmarks: cProfile analysis added to benchmark suite; governance gate
overhead quantified;
profile_report.txtcommitted as evidence. - No new tools. No API changes.
v0.2.8 — 2026-02-26
Phase 2.7 — 60-tool release: hyperlink management + zoom_scale extension for set_sheet_properties
- Phase 2.7 tools added (3):
get_hyperlinks,add_hyperlink,remove_hyperlink get_hyperlinks(read): return all hyperlinks in a worksheet; returns{"sheet": ..., "count": ..., "hyperlinks": [{address, url, display_text, tooltip}]}add_hyperlink(write): add or update a hyperlink on a cell; optionaldisplay_text(replaces cell value) andtooltip; requiresEXCEL_ENABLE_WRITE=true+confirm=Trueremove_hyperlink(write): remove a hyperlink from a cell; requiresEXCEL_ENABLE_WRITE=true+confirm=Trueset_sheet_propertiesextended:zoom_scaleparameter added (integer 10–400) to set sheet zoom level- Commit:
4b03cafonstaging
v0.2.7 — 2026-02-26
Docs sync: document Phase 2.6 tools set_column_visibility + set_workbook_calc_mode; version bump
- README tool table updated to reflect all 57 tools delivered in Phase 2.6
- Added
set_column_visibilityto Write / Structural Tools: hide or show one or more columns by letter list; requiresEXCEL_ENABLE_WRITE=true+confirm=True - Added
set_workbook_calc_modeto Write / Structural Tools: setauto/manual/autoNoTablecalc mode andfullCalcOnLoadflag; requiresEXCEL_ENABLE_WRITE=true+confirm=True - No code changes; docs-only release
v0.2.6 — 2026-02-26
Phase 2.6 — 57-tool release: read data validations, read cell comment, optional path for formula validation; set_column_visibility, set_workbook_calc_mode
- Phase 2.6 tools added (2):
get_data_validation_info,get_cell_comment get_data_validation_info(read): return all data validation rules configured on a sheet; returns{"sheet": ..., "count": ..., "validations": [...]}get_cell_comment(read): return the comment on a cell, including text and author; returns{"address": ..., "has_comment": bool, "text": ..., "author": ...}- Ergonomic fix:
validate_formula_syntaxpathparameter is now optional (defaults to"") - 265 pytest tests green (unit + smoke)
v0.2.5 — 2026-02-26
Phase 2.5 — 53-tool release: formula validation, range copy, merged cell listing, native table creation
- Phase 2.5 tools added (4):
validate_formula_syntax,copy_range,list_merged_cells,create_table validate_formula_syntax(read): check formula string syntax without writing to the workbook; returns{"valid": bool, "error": str | null}copy_range(write): copy values and formats from a source range to a destination range within the same sheet; requiresEXCEL_ENABLE_WRITE=true+confirm=Truelist_merged_cells(read): list all merged cell ranges in a worksheet; returns{"sheet": ..., "merged_ranges": [...]}create_table(write): create a native Excel table (ListObject) over a range; applies TableStyleMedium9 with row stripes; enforces name uniqueness, min 2 rows, valid Excel identifier fortable_name; requiresEXCEL_ENABLE_WRITE=true+confirm=True- 243 pytest tests green (unit + smoke)
v0.2.4 — 2026-02-25
Phase 2.4 — 49-tool release: cell comments, sheet properties, data validation, workbook protection
- Phase 2.4 tools added (6):
add_cell_comment,delete_cell_comment,get_sheet_properties,set_sheet_properties,add_data_validation,protect_workbook - All tools implemented via openpyxl (file-based, no COM); full
confirm=True+ write-gate discipline - 197 pytest unit tests + 20 smoke tests green
v0.2.3 — 2026-02-24
Phase 2.3 — 43-tool, 8-prompt release: insert rows/cols, worksheet protection, print area
- Phase 2.3 structural tools added (4):
insert_rows,insert_cols,protect_sheet,set_print_area - Range address validation (N3):
write_named_rangenow rejects garbage input with a clearValidationError - Prompt added:
prepare_for_print— generates a professional print configuration from a sheet description - 186 pytest tests green (unit + smoke)
v0.2.0 — 2026-02-24
Phase 2.0 — 33-tool release: formatting + chart tools; list_charts bug fix
- Phase 2.0 formatting tools added (6):
apply_number_format,apply_cell_style,apply_alignment,set_column_width,set_row_height,add_border - Phase 2.0 chart tools added (3):
create_chart,list_charts,delete_chart - Bug fix (
list_charts): openpyxlTitleobjects are now correctly serialised to plain strings viatitle.tx.rich.p[0].r[0].t; addedindexfield; simplified anchor format to"col,row" - Showcase file built end-to-end exercising all 33 tools
(
C:\Temp\excelmcp_showcase.xlsx— UserStories, SalesData, Products sheets) - Issue-tracker bug entries created under Excel MCP epic (internal tracking references redacted in this public repository)
v0.1.5 — 2026-02-23
Phase 1.5 — 24-tool release + E2E UX sprint (20/20 PASS)
- Full write toolset added (12 tools):
write_cell,write_range,append_rows,add_sheet,save,find_replace,rename_sheet,delete_sheet,copy_sheet,delete_rows,delete_cols,import_csv_to_sheet - E2E UX sprint — 20 user stories validated, all PASS:
- GAP-03:
read_sheet_allresponse wrapped in{"sheet": ..., "values": [[...]]}dict - CODE-GAP-01: workbook cache key changed from
pathstring to(path, sheet)tuple to isolate per-sheet state correctly - GAP-02:
_flush_if_dirtyhelper added for consistent auto-save in write tools - GAP-07:
has_headerdead-code parameter removed fromimport_csv_to_sheet
- GAP-03:
- 77 pytest tests green (unit + smoke)
- Commits:
c2ac564,f7c49b8onstaging
v0.1.0 — 2026-02-23
Phase 1 initial release — 12 read tools
capabilities,list_sheets,get_used_range,read_cell,read_range,read_sheet_all,get_named_ranges,read_named_range,get_cell_metadata,evaluate_formula,list_tables,read_table- openpyxl backend, FastMCP stdio transport
- Path allowlist enforcement,
EXCEL_MAX_RANGE_CELLSguard - Unit and smoke test suite
- Commit:
d94d2cdonstaging
Backlog
Phase 2 — COM / win32com (Windows-only)
- Live formula recalculation via
Excel.Application - Pivot table create/read/refresh
- PDF export via
export_as_pdf - Advanced chart editing (series data, axis labels, chart type change)
Phase 1 / 2.0 Extensions (openpyxl)
- Create a new
.xlsxfrom a blank template - Conditional formatting read
- Streaming read for very large files (openpyxl read-only mode)
- Gradient and pattern fill write support
- Merge / unmerge cells
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mcp_office_excel-0.6.0.tar.gz.
File metadata
- Download URL: mcp_office_excel-0.6.0.tar.gz
- Upload date:
- Size: 361.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
51322dfc3c21bb7c4a4dfd2f934a1a83051fce34d671b3816284c39da01c47c0
|
|
| MD5 |
a4cb0b01830c9cf289f17d958726e872
|
|
| BLAKE2b-256 |
24d173b4cdb9fbe964b206301a34a11bb3d28617654b507646f565a8e86b53c9
|
Provenance
The following attestation bundles were made for mcp_office_excel-0.6.0.tar.gz:
Publisher:
publish.yml on dosev-ai/mcp-office
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mcp_office_excel-0.6.0.tar.gz -
Subject digest:
51322dfc3c21bb7c4a4dfd2f934a1a83051fce34d671b3816284c39da01c47c0 - Sigstore transparency entry: 1614652660
- Sigstore integration time:
-
Permalink:
dosev-ai/mcp-office@b9a02d23ae8d1c43d17f78c98a1890e63eb57b12 -
Branch / Tag:
refs/tags/v0.6.0 - Owner: https://github.com/dosev-ai
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b9a02d23ae8d1c43d17f78c98a1890e63eb57b12 -
Trigger Event:
release
-
Statement type:
File details
Details for the file mcp_office_excel-0.6.0-py3-none-any.whl.
File metadata
- Download URL: mcp_office_excel-0.6.0-py3-none-any.whl
- Upload date:
- Size: 199.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
641b19575591b98677d24743acd96f473d07948a90f550f80524c08198065b6a
|
|
| MD5 |
1df5a3fc4967640a6535da815188a258
|
|
| BLAKE2b-256 |
9d6803760bc55b3795c80563e117c906d71c083f09a490f76009ff78487723eb
|
Provenance
The following attestation bundles were made for mcp_office_excel-0.6.0-py3-none-any.whl:
Publisher:
publish.yml on dosev-ai/mcp-office
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mcp_office_excel-0.6.0-py3-none-any.whl -
Subject digest:
641b19575591b98677d24743acd96f473d07948a90f550f80524c08198065b6a - Sigstore transparency entry: 1614652667
- Sigstore integration time:
-
Permalink:
dosev-ai/mcp-office@b9a02d23ae8d1c43d17f78c98a1890e63eb57b12 -
Branch / Tag:
refs/tags/v0.6.0 - Owner: https://github.com/dosev-ai
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b9a02d23ae8d1c43d17f78c98a1890e63eb57b12 -
Trigger Event:
release
-
Statement type: