duduexcel 0.2.0__tar.gz
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.
- duduexcel-0.2.0/CHANGELOG.md +85 -0
- duduexcel-0.2.0/LICENSE +21 -0
- duduexcel-0.2.0/MANIFEST.in +9 -0
- duduexcel-0.2.0/PKG-INFO +227 -0
- duduexcel-0.2.0/README.md +178 -0
- duduexcel-0.2.0/README_EN.md +173 -0
- duduexcel-0.2.0/duduexcel/__init__.py +4 -0
- duduexcel-0.2.0/duduexcel/__main__.py +8 -0
- duduexcel-0.2.0/duduexcel/advanced.py +447 -0
- duduexcel-0.2.0/duduexcel/analytics.py +590 -0
- duduexcel-0.2.0/duduexcel/excel_ops.py +684 -0
- duduexcel-0.2.0/duduexcel/recalc.py +303 -0
- duduexcel-0.2.0/duduexcel/safety.py +163 -0
- duduexcel-0.2.0/duduexcel/server.py +570 -0
- duduexcel-0.2.0/duduexcel/styling.py +259 -0
- duduexcel-0.2.0/duduexcel.egg-info/PKG-INFO +227 -0
- duduexcel-0.2.0/duduexcel.egg-info/SOURCES.txt +25 -0
- duduexcel-0.2.0/duduexcel.egg-info/dependency_links.txt +1 -0
- duduexcel-0.2.0/duduexcel.egg-info/entry_points.txt +2 -0
- duduexcel-0.2.0/duduexcel.egg-info/requires.txt +10 -0
- duduexcel-0.2.0/duduexcel.egg-info/top_level.txt +1 -0
- duduexcel-0.2.0/pyproject.toml +56 -0
- duduexcel-0.2.0/setup.cfg +4 -0
- duduexcel-0.2.0/skill/duduexcel/SKILL.md +83 -0
- duduexcel-0.2.0/skill/duduexcel/references/charts.md +54 -0
- duduexcel-0.2.0/skill/duduexcel/references/formulas.md +60 -0
- duduexcel-0.2.0/skill/duduexcel/references/style.md +56 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to duduExcel. This project uses [Semantic Versioning](https://semver.org/).
|
|
4
|
+
|
|
5
|
+
## [0.2.0] — 2026-09-05
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
#### M7 — Format semantics, hidden content, embedded images (inspired by VOYAGER-Inc/excel-vision-mcp)
|
|
10
|
+
- **Format semantics markers** in `read_range`: `[B]` bold, `[I]` italic, `[S]` strikethrough (cancelled),
|
|
11
|
+
`[HL:color]` highlight (needs review), `[C:color]` font color, `[M]` merged cell
|
|
12
|
+
- Markers are attached **only when formatting is actually used** — plain sheets cost zero extra tokens
|
|
13
|
+
- Includes `marker_legend` and a `formatted_view` for quick reading
|
|
14
|
+
- **Hidden content handling**: hidden rows/columns skipped by default; cells referenced by visible
|
|
15
|
+
formulas are kept and tagged `[HIDDEN-REF]`; skip counts always reported (never silent)
|
|
16
|
+
- **`list_images`**: lists embedded images via zero-dependency `xl/media/` scan (count, filename, size, dimensions)
|
|
17
|
+
- `workbook_info` now reports merged-cell count, hidden row/col counts, and embedded image count
|
|
18
|
+
- **Atomic save**: write to temp → swap, so a failed write can never corrupt the original
|
|
19
|
+
- **Multi-directory sandbox**: `DUDU_EXCEL_ROOT` accepts several paths (`;` on Windows, `:` elsewhere)
|
|
20
|
+
|
|
21
|
+
#### M6 — Advanced operations
|
|
22
|
+
- `create_pivot` (static summary table; honestly labelled as non-interactive)
|
|
23
|
+
- `add_conditional_format` (data bar, color scale, greater/less/equal, between, duplicate)
|
|
24
|
+
- `compare_sheets` (keyed diff: only-in-left / only-in-right / value differences)
|
|
25
|
+
- `join_sheets` (left/right/inner/outer, returns first N rows only)
|
|
26
|
+
|
|
27
|
+
#### Packaging & docs
|
|
28
|
+
- `LICENSE` (MIT), `MANIFEST.in`, English `README_EN.md`
|
|
29
|
+
- GitHub Actions CI (Python 3.10–3.12 × Ubuntu/Windows) + build job
|
|
30
|
+
- `scripts/install_libreoffice.py`, `scripts/check_libreoffice.py`, `scripts/health_check.py`
|
|
31
|
+
- `docs/SUBMISSION.md` — ready-to-paste copy for MCP directories
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
- **Recalculation failed on Chinese paths** — LibreOffice cannot overwrite a file in place when the path
|
|
35
|
+
contains non-ASCII characters (`SfxBaseModel::impl_store failed: 0x4c0c`).
|
|
36
|
+
Now converts into an ASCII temp directory and moves the result back. Verified on `E:\工作类\研发\`.
|
|
37
|
+
- Non-zero soffice exit now surfaces stderr instead of only reporting "file not rewritten"
|
|
38
|
+
- **`_as_bool` internal helper was mistakenly decorated with `@mcp.tool()`**, registering it as a bogus
|
|
39
|
+
19th tool and polluting `tools/list`. Removed the decorator (back to 18 tools) with a guard comment.
|
|
40
|
+
- `test_m34.py` adapts to environment: asserts real recalculation when LibreOffice exists,
|
|
41
|
+
degradation path when it does not
|
|
42
|
+
|
|
43
|
+
## [0.1.0] — 2026-09-03
|
|
44
|
+
|
|
45
|
+
### Added
|
|
46
|
+
|
|
47
|
+
#### M1 — Core + safe read/write
|
|
48
|
+
- `workbook_info`, `read_range` (pagination + honest truncation), `write_cells` (bulk), `revert_last_write`
|
|
49
|
+
- Path sandbox (`DUDU_EXCEL_ROOT`), backup-before-write, auto rollback on failure
|
|
50
|
+
|
|
51
|
+
#### M2 — Server-side analysis
|
|
52
|
+
- `sheet_profile` (one call replaces a dozen probes), `filter_count`, `aggregate` (incl. `group_by`), `top_n`
|
|
53
|
+
- `_meta.tokens_saved` self-reporting, equivalent Excel formulas and TSV output on results
|
|
54
|
+
|
|
55
|
+
#### M3 — Formula verification
|
|
56
|
+
- `recalculate` with external-link circuit breaker and file-fingerprint silent-failure guard
|
|
57
|
+
- `scan_formula_errors` (7 Excel error types, honest truncation reporting)
|
|
58
|
+
|
|
59
|
+
#### M4 — Chinese scenarios & charts
|
|
60
|
+
- `apply_chinese_style` (YaHei headings, CJK-aware autofit, freeze panes), `set_number_format` (`¥#,##0`, `0.0%`, `0.0x`)
|
|
61
|
+
- `add_chart` (bar/line/pie/scatter)
|
|
62
|
+
|
|
63
|
+
#### M5 — Skill layer
|
|
64
|
+
- `SKILL.md` + `references/{style,formulas,charts}.md`
|
|
65
|
+
|
|
66
|
+
### Fixed
|
|
67
|
+
- Type-handling crashes found by boundary probing:
|
|
68
|
+
1. `TypeError` when comparing text columns with `>`/`<` — now a readable error suggesting text operators
|
|
69
|
+
2. `TypeError: bad operand type for unary -: 'str'` when aggregating text columns with `group_by` —
|
|
70
|
+
replaced with a type-safe sort key
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## Design lineage
|
|
75
|
+
|
|
76
|
+
Ideas borrowed (never code) from the ecosystem:
|
|
77
|
+
|
|
78
|
+
| Idea | Source |
|
|
79
|
+
|---|---|
|
|
80
|
+
| Server-side atomic operations ("results, not rows") | jwadow/mcp-excel |
|
|
81
|
+
| `_meta.tokens_saved`, destructive-action preflight | jgravelle/jdatamunch-mcp |
|
|
82
|
+
| Bulk tools, explicit "don't loop the single version" | knorq-ai/xlsx-mcp-server |
|
|
83
|
+
| Path sandbox (`EXCEL_FILES_PATH`) | haris-musa/excel-mcp-server |
|
|
84
|
+
| External-link breaker, honest truncation, validate-before-deliver | Anthropic official xlsx skill (Proprietary — engineering ideas only) |
|
|
85
|
+
| Format semantics, hidden-content handling, embedded images, atomic save | VOYAGER-Inc/excel-vision-mcp |
|
duduexcel-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Shine8592
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
duduexcel-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: duduexcel
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Excel MCP server for AI agents: context-efficient (server-side aggregation), safe writes (atomic save + backup + rollback), format semantics (strikethrough/highlight), formula recalculation, and first-class Chinese support.
|
|
5
|
+
Author: Shine8592
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026 Shine8592
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Project-URL: Homepage, https://github.com/Shine8592/duduExcel
|
|
29
|
+
Project-URL: Repository, https://github.com/Shine8592/duduExcel
|
|
30
|
+
Project-URL: Issues, https://github.com/Shine8592/duduExcel/issues
|
|
31
|
+
Project-URL: Changelog, https://github.com/Shine8592/duduExcel/blob/main/CHANGELOG.md
|
|
32
|
+
Keywords: excel,xlsx,spreadsheet,mcp,model-context-protocol,ai-agent,llm,openpyxl,pandas,automation,chinese
|
|
33
|
+
Classifier: Development Status :: 4 - Beta
|
|
34
|
+
Classifier: Intended Audience :: Developers
|
|
35
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
36
|
+
Classifier: Programming Language :: Python :: 3
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
40
|
+
Classifier: Topic :: Office/Business :: Financial :: Spreadsheet
|
|
41
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
42
|
+
Requires-Python: >=3.10
|
|
43
|
+
Description-Content-Type: text/markdown
|
|
44
|
+
License-File: LICENSE
|
|
45
|
+
Requires-Dist: openpyxl>=3.1.0
|
|
46
|
+
Requires-Dist: mcp>=2.0.0
|
|
47
|
+
Provides-Extra: analysis
|
|
48
|
+
Requires-Dist: pandas>=2.0.0; extra == "analysis"
|
|
49
|
+
Provides-Extra: dev
|
|
50
|
+
Requires-Dist: pandas>=2.0.0; extra == "dev"
|
|
51
|
+
Requires-Dist: build>=1.0.0; extra == "dev"
|
|
52
|
+
Requires-Dist: twine>=5.0.0; extra == "dev"
|
|
53
|
+
Dynamic: license-file
|
|
54
|
+
|
|
55
|
+
# 📊 duduExcel
|
|
56
|
+
|
|
57
|
+
**Excel MCP server built for AI agents** — context-efficient, safe to write, and it understands what the author *meant*, not just cell values.
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
Agent ──MCP (stdio)──► duduExcel ──► openpyxl / pandas ──► .xlsx
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
*Read this in [中文](README.md)*
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Why another Excel MCP?
|
|
68
|
+
|
|
69
|
+
Most Excel MCP servers make you choose between two things:
|
|
70
|
+
|
|
71
|
+
| Approach | Examples | Can write? | Context-friendly? |
|
|
72
|
+
|---|---|---|---|
|
|
73
|
+
| Full read/write | haris-musa, knorq | ✅ | ❌ dumps whole sheets |
|
|
74
|
+
| Atomic analysis | jwadow, jdatamunch | ❌ read-only | ✅ |
|
|
75
|
+
| Methodology only | Anthropic's xlsx skill | ✅ (via generated code) | ✅ but relies on the model writing Python |
|
|
76
|
+
|
|
77
|
+
**duduExcel does all three**: server-side aggregation (cheap), full read/write (capable), and a bundled Skill (reliable).
|
|
78
|
+
|
|
79
|
+
### And it sees what others silently drop
|
|
80
|
+
|
|
81
|
+
> A strikethrough row means *cancelled*. A yellow-highlighted cell means *needs review*. A pasted flowchart in `B12` holds half the spec.
|
|
82
|
+
> **Every other Excel MCP flattens all of that into plain strings** — so a cancelled row looks identical to an active one.
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
# Other MCP servers
|
|
86
|
+
[["Legacy export", "Cancelled"], ["Driver roll call", "Needs review"]]
|
|
87
|
+
|
|
88
|
+
# duduExcel
|
|
89
|
+
A3: Legacy export [S] | B3: Cancelled [S] ← [S] = strikethrough
|
|
90
|
+
A4: Driver roll call | B4: Needs review [HL:yellow] ← [HL:] = highlight
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## ✨ 18 Tools
|
|
96
|
+
|
|
97
|
+
| Layer | Tools |
|
|
98
|
+
|---|---|
|
|
99
|
+
| **Inspect** | `workbook_info` · `sheet_profile` |
|
|
100
|
+
| **Analyze** (server-side) | `filter_count` · `aggregate` · `top_n` |
|
|
101
|
+
| **Read / Write** | `read_range` · `write_cells` · `revert_last_write` |
|
|
102
|
+
| **Verify** ⭐ | `recalculate` · `scan_formula_errors` |
|
|
103
|
+
| **Chinese** ⭐ | `apply_chinese_style` · `set_number_format` |
|
|
104
|
+
| **Charts** ⭐ | `add_chart` |
|
|
105
|
+
| **Advanced** | `create_pivot` · `add_conditional_format` · `compare_sheets` · `join_sheets` · `list_images` |
|
|
106
|
+
|
|
107
|
+
⭐ = capabilities competitors commonly lack
|
|
108
|
+
|
|
109
|
+
### Highlights
|
|
110
|
+
|
|
111
|
+
- **Context-efficient by design** — `read_range` paginates (default `limit=200`) and reports `truncated` honestly. Analysis runs server-side: you get `{"sum": 575}`, not 5,000 rows. Every response carries `_meta.tokens_saved`.
|
|
112
|
+
- **Format semantics** — `[B]` bold · `[I]` italic · `[S]` strikethrough (cancelled) · `[HL:color]` highlight (needs review) · `[C:color]` font color · `[M]` merged · `[HIDDEN-REF]` hidden-but-referenced. **Markers are only attached when formatting is actually used**, so plain sheets cost zero extra tokens.
|
|
113
|
+
- **Hidden content, handled** — hidden rows/cols are skipped by default (an author hiding them signals they're not for review), **except** cells referenced by visible formulas, which are kept and tagged `[HIDDEN-REF]`. Skipped counts are always reported — nothing disappears silently.
|
|
114
|
+
- **Embedded images** — `list_images` scans `xl/media/` with zero extra dependencies, so flowcharts pasted into cells no longer vanish.
|
|
115
|
+
- **Formula recalculation** — via LibreOffice, with **external-link circuit breaker**: if a workbook has external links whose cached values were stripped by openpyxl, recalculation would turn them into `#NAME?` **and delete the links permanently**. duduExcel refuses by default and tells you why; pass `force=true` only if you accept the loss.
|
|
116
|
+
- **Safe writes** — atomic save (temp file → swap), `.bak` backup before every write, auto-rollback on failure, `revert_last_write` to undo.
|
|
117
|
+
- **Chinese-first** — Microsoft YaHei headings, CJK-aware column autofit (2 units per CJK char), `¥#,##0` currency, `0.0%`, `0.0x` multiples.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## 🚀 Install
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
pip install "duduexcel[analysis]" # analysis extra enables pandas-backed tools
|
|
125
|
+
# or run without installing:
|
|
126
|
+
uvx duduexcel
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Requirements:** Python 3.10+. Formula recalculation (`recalculate`) additionally needs [LibreOffice](https://www.libreoffice.org/) installed; without it the tool degrades explicitly with install instructions — it never silently pretends to succeed.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## 🔌 MCP Configuration
|
|
134
|
+
|
|
135
|
+
### opencode (`~/.config/opencode/opencode.jsonc`)
|
|
136
|
+
|
|
137
|
+
```jsonc
|
|
138
|
+
"duduexcel": {
|
|
139
|
+
"type": "local",
|
|
140
|
+
"command": ["python", "-u", "-m", "duduexcel"],
|
|
141
|
+
"enabled": true
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Claude Desktop / Cursor / Cline
|
|
146
|
+
|
|
147
|
+
```json
|
|
148
|
+
{
|
|
149
|
+
"mcpServers": {
|
|
150
|
+
"duduexcel": {
|
|
151
|
+
"command": "uvx",
|
|
152
|
+
"args": ["duduexcel"]
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Restart your client after editing the config.
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## 🔒 Security
|
|
163
|
+
|
|
164
|
+
- **Path sandbox** — set `DUDU_EXCEL_ROOT` to one or more directories (`;` on Windows, `:` elsewhere); absolute paths and `..` traversal outside them are rejected.
|
|
165
|
+
- **Atomic save** — a failed write can never corrupt your original file.
|
|
166
|
+
- **Auto backup / rollback** — `.bak` before every write, auto-restore on exception, `revert_last_write` to undo.
|
|
167
|
+
- **External-link circuit breaker** — refuses irreversible recalculation unless you force it.
|
|
168
|
+
- **Local-first** — stdio transport; your files never leave the machine.
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## ⚠️ Known Limitations (honest list)
|
|
173
|
+
|
|
174
|
+
- **Interactive PivotTables are not supported.** `create_pivot` produces a **static summary table** (group-and-aggregate written back) — numerically equivalent and verified correct (e.g. East 365 / South 210), but not clickable. openpyxl cannot create real PivotTable objects (`ws._pivots` is empty).
|
|
175
|
+
- ~~Conditional formatting can be written, not read~~ — **corrected**: it *can* be read. `list_conditional_formats` returns range, type, operator, thresholds, fill color, and priority (verified: 4/4 rules read back). This limitation was mistakenly copied from a competitor's README without verification.
|
|
176
|
+
- **`.xlsm` macros** are preserved on read; not guaranteed on write.
|
|
177
|
+
- Max **100,000 cells** per write.
|
|
178
|
+
- `recalculate` needs LibreOffice (verified working on 26.8, ~4s for a typical sheet).
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## 🧪 Tests
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
python tests/test_smoke.py # read/write, pagination, safety
|
|
186
|
+
python tests/test_m2.py # server-side analysis
|
|
187
|
+
python tests/test_m34.py # recalc degradation + Chinese styling + charts
|
|
188
|
+
python tests/test_m6.py # pivot / conditional format / multi-sheet
|
|
189
|
+
python tests/test_edge_cases.py # regression guards for past bugs
|
|
190
|
+
python tests/test_m7.py # format semantics / hidden / images / atomic save
|
|
191
|
+
python tests/test_m8_recalc.py # real recalculation (skips if no LibreOffice)
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## 📚 Bundled Agent Skill
|
|
197
|
+
|
|
198
|
+
`skill/duduexcel/` ships methodology the agent can auto-load:
|
|
199
|
+
|
|
200
|
+
```
|
|
201
|
+
SKILL.md # tool routing + 5 hard rules + formula constraints
|
|
202
|
+
references/style.md # financial-model color semantics, number formats
|
|
203
|
+
references/formulas.md # function allowlist, _xlfn. prefixes, 7 error types
|
|
204
|
+
references/charts.md # chart recipes and fidelity caveats
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
> Anthropic's official xlsx skill is **Proprietary** (derivative works prohibited). duduExcel borrows only its *engineering ideas* (validate-before-deliver, honest truncation, external-link breaker); all text and code are written independently.
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## 🗺️ Roadmap
|
|
212
|
+
|
|
213
|
+
- [x] M1 core + safe read/write
|
|
214
|
+
- [x] M2 server-side analysis + token self-reporting
|
|
215
|
+
- [x] M3 formula recalculation + external-link breaker
|
|
216
|
+
- [x] M4 Chinese styling + charts
|
|
217
|
+
- [x] M5 Skill layer
|
|
218
|
+
- [x] M6 pivot / conditional format / multi-sheet joins
|
|
219
|
+
- [x] M7 format semantics / hidden content / embedded images / atomic save
|
|
220
|
+
- [x] Conditional-formatting reading (`list_conditional_formats`)
|
|
221
|
+
- [ ] Interactive PivotTables (blocked by openpyxl)
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## License
|
|
226
|
+
|
|
227
|
+
MIT © Shine8592
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# 📊 duduExcel
|
|
2
|
+
|
|
3
|
+
面向 AI Agent 的 Excel MCP 服务 —— **上下文高效 + 安全读写 + 中文场景 + 公式验证**。
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
Agent ──MCP(stdio)──► duduExcel ──► openpyxl/pandas ──► .xlsx
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## 为什么又一个 Excel MCP?
|
|
10
|
+
|
|
11
|
+
调研了 GitHub 上 10+ 个同类项目后,发现它们分裂成两个对立阵营,没人同时做到"能写"和"不炸上下文":
|
|
12
|
+
|
|
13
|
+
| 阵营 | 代表 | 能写 | 上下文友好 | 问题 |
|
|
14
|
+
|---|---|---|---|---|
|
|
15
|
+
| 读写全能型 | haris-musa、knorq | ✅ | ❌ | 整表读进上下文就爆了 |
|
|
16
|
+
| 原子分析型 | jwadow、jdatamunch | ❌ 只读 | ✅ | 改不了文件 |
|
|
17
|
+
| 方法论型 | Anthropic 官方 xlsx Skill | ✅(靠写代码) | ✅ | 靠 Agent 自己写 Python,不稳定 |
|
|
18
|
+
|
|
19
|
+
**duduExcel:把三者优点合到一个项目** —— 服务端原子分析(省 token)+ 完整读写 + 内置方法论 Skill。
|
|
20
|
+
|
|
21
|
+
## ✨ 能力(17 个工具)
|
|
22
|
+
|
|
23
|
+
| 层 | 工具 | 说明 |
|
|
24
|
+
|---|---|---|
|
|
25
|
+
| **探查** | `workbook_info` | 表清单、行列数、文件大小 |
|
|
26
|
+
| | `sheet_profile` | 列画像:类型/空值率/唯一数/Top值/统计(一次替代十几次调用) |
|
|
27
|
+
| **分析** | `filter_count` | 条件计数(14 种运算符),只回传数字 |
|
|
28
|
+
| | `aggregate` | sum/mean/count/… 支持分组与过滤 |
|
|
29
|
+
| | `top_n` | 排行榜,只回传前 N 行 |
|
|
30
|
+
| **读写** | `read_range` | 分页读取,默认 limit=200 防上下文溢出 |
|
|
31
|
+
| | `write_cells` | **批量**写入,一次调用完成;`=` 开头即公式 |
|
|
32
|
+
| **验证** ⭐ | `recalculate` | 公式重算 + 外链熔断(差异化,竞品多不支持) |
|
|
33
|
+
| | `scan_formula_errors` | 扫描 7 类公式错误 |
|
|
34
|
+
| **中文** ⭐ | `apply_chinese_style` | 微软雅黑表头、中文列宽自适应、冻结首行 |
|
|
35
|
+
| | `set_number_format` | `¥#,##0` / `0.0%` / `0.0x` 等内置格式 |
|
|
36
|
+
| **图表** ⭐ | `add_chart` | bar/line/pie/scatter(官方 skill 无图表指导,knorq 明确不支持) |
|
|
37
|
+
| **高级** | `create_pivot` | 透视汇总表(静态,诚实标注不可交互) |
|
|
38
|
+
| | `add_conditional_format` | 数据条/色阶/阈值高亮/重复值(openpyxl 原生规则) |
|
|
39
|
+
| | `compare_sheets` | 两表按关键列比对,只回差异摘要 |
|
|
40
|
+
| | `join_sheets` | 两表关联(left/right/inner/outer),只回前 N 行 |
|
|
41
|
+
| **安全** | `revert_last_write` | 回滚最近一次写入 |
|
|
42
|
+
|
|
43
|
+
⭐ = 差异化能力
|
|
44
|
+
|
|
45
|
+
## 🔑 汲取的设计(附来源)
|
|
46
|
+
|
|
47
|
+
| 设计点 | 来源 | 落地 |
|
|
48
|
+
|---|---|---|
|
|
49
|
+
| 一次调用替代 N 次试探 | jwadow `get_data_profile` | `sheet_profile` |
|
|
50
|
+
| 服务端原子操作(results, not rows) | jwadow | `filter_count`/`aggregate`/`top_n` |
|
|
51
|
+
| 分页 + 截断诚实报告 | jwadow、官方 `recalc.py` | `read_range` 返回 `truncated`+`hint` |
|
|
52
|
+
| `_meta.tokens_saved` 自报节省 | jdatamunch | 每个分析工具的 `_meta` |
|
|
53
|
+
| 结果附 Excel 公式(可复现) | jwadow | `filter_count`/`aggregate`/`top_n` |
|
|
54
|
+
| TSV 输出(便于粘回 Excel) | jwadow | `aggregate`/`top_n` |
|
|
55
|
+
| 批量接口,禁止循环调用 | knorq | `write_cells` |
|
|
56
|
+
| 路径白名单 + 拒目录穿越 | haris-musa | `DUDU_EXCEL_ROOT` |
|
|
57
|
+
| 外链熔断(不可逆保护) | 官方 `recalc.py` | `recalculate` 的 force 机制 |
|
|
58
|
+
| 文件指纹防静默失败 | 官方 `recalc.py` | `recalculate` |
|
|
59
|
+
| 诚实的 Limitations | knorq | 见下 |
|
|
60
|
+
|
|
61
|
+
## 🚀 安装与挂载
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
cd duduExcel
|
|
65
|
+
pip install -e ".[analysis]" # analysis 可选(提供 pandas)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
opencode(`~/.config/opencode/opencode.jsonc`):
|
|
69
|
+
|
|
70
|
+
```jsonc
|
|
71
|
+
"duduexcel": {
|
|
72
|
+
"type": "local",
|
|
73
|
+
"command": ["<你的python路径>", "-u", "-m", "duduexcel"],
|
|
74
|
+
"enabled": true,
|
|
75
|
+
"cwd": "E:\\工作类\\研发\\duduExcel"
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
其他 MCP 客户端(Claude Code / Cursor / Cline)同理,指向 `python -m duduexcel`。
|
|
80
|
+
**配置改完需重启客户端生效。**
|
|
81
|
+
|
|
82
|
+
## 🔒 安全
|
|
83
|
+
|
|
84
|
+
- **路径白名单**:设 `DUDU_EXCEL_ROOT` 后,所有路径必须在根目录内,拒绝绝对路径与 `..` 穿越
|
|
85
|
+
- **写前自动备份**:任何写入前生成 `.xlsx.bak`,备份失败则中止写入
|
|
86
|
+
- **失败自动回滚**:写入异常时自动还原,不留半成品
|
|
87
|
+
- **外链熔断**:检测到外链缓存丢失时拒绝重算(否则会永久破坏外链),需显式 `force=true`
|
|
88
|
+
- **本地优先**:stdio 传输,文件不出本机
|
|
89
|
+
|
|
90
|
+
## 🔄 汲取的新设计(第二轮调研)
|
|
91
|
+
|
|
92
|
+
调研 `VOYAGER-Inc/excel-vision-mcp` 后发现一个 openpyxl 生态的普遍盲区,
|
|
93
|
+
而 duduExcel 原本也有:**内嵌图片、格式语义、隐藏行列全部丢失**。
|
|
94
|
+
作者用删除线表示"已取消"、黄底表示"待审阅",而纯文本读取会把它们抹平——
|
|
95
|
+
模型看到的是"字符串表格",不是"作者想表达的意思"。
|
|
96
|
+
|
|
97
|
+
已补齐:
|
|
98
|
+
|
|
99
|
+
| 汲取点 | 来源 | 落地 |
|
|
100
|
+
|---|---|---|
|
|
101
|
+
| **格式语义标记** | excel-vision-mcp | `read_range` 返回 `[B]/[I]/[S]/[HL:色]/[C:色]/[M]`;**只在真用了格式时才附加**,朴素表零额外 token |
|
|
102
|
+
| **隐藏内容智能处理** | excel-vision-mcp | 默认跳过隐藏行列(作者隐藏=不想让你看),但**被可见公式引用的隐藏单元格仍保留**并标 `[HIDDEN-REF]`;始终报告跳过数量,绝不静默丢弃 |
|
|
103
|
+
| **内嵌图片不丢失** | excel-vision-mcp | 新增 `list_images`(零依赖扫描 `xl/media/`),`workbook_info` 也报告图片数 |
|
|
104
|
+
| **原子保存** | excel-vision-mcp | 写临时文件成功后再替换目标,失败的写入永不损坏原文件 |
|
|
105
|
+
| **多目录沙箱** | excel-vision-mcp | `DUDU_EXCEL_ROOT` 支持 `;` 分隔多个目录 |
|
|
106
|
+
|
|
107
|
+
## ⚠️ 已知限制(诚实清单)
|
|
108
|
+
|
|
109
|
+
- **公式重算需要 LibreOffice**:未安装时 `recalculate` 明确降级并给出安装指引(不静默假装成功)。
|
|
110
|
+
此时写入的公式无缓存值,`read_range` 读回 `None` —— 这是预期行为,非数据丢失。
|
|
111
|
+
|
|
112
|
+
✅ **已在本机打通验证**(LibreOffice 26.8):`recalculate` 实测 **4 秒**完成重算,
|
|
113
|
+
`=SUM(销售!C2:C7)` 正确算出 **575** 并落盘(回归测试见 `tests/test_m8_recalc.py`)。
|
|
114
|
+
|
|
115
|
+
⚠️ **中文路径坑(已修复)**:LibreOffice 在中文路径下**原地覆盖**文件会失败
|
|
116
|
+
(`SfxBaseModel::impl_store failed: 0x4c0c`)。本实现改为先输出到纯 ASCII 临时目录再移回,
|
|
117
|
+
因此在 `E:\工作类\研发\` 这类中文路径下也能正常重算。
|
|
118
|
+
- **不支持可交互透视表**:`create_pivot` 生成的是静态汇总表(数值正确且已验证,如华东 365 / 华南 210),
|
|
119
|
+
但 openpyxl 无法创建真正的 PivotTable 对象(实测 `ws._pivots` 为空),因此不可点击交互。
|
|
120
|
+
- ~~条件格式只能写入不能读取~~ —— **已更正**:实测条件格式**可以完整读回**。
|
|
121
|
+
`list_conditional_formats` 能返回作用区域/类型/运算符/阈值/填充色/优先级(实测 4 条规则全部读回)。
|
|
122
|
+
此前我照抄了竞品 knorq 的 Known Limitations 而未亲自验证,这是错误的。
|
|
123
|
+
- **`.xlsm` 宏**:读取保留 VBA,写入不保证
|
|
124
|
+
- 单次写入上限 **10 万单元格**
|
|
125
|
+
- 小表上 `_meta.tokens_saved` 节省不明显属正常(省 token 的收益随表增大而放大)
|
|
126
|
+
|
|
127
|
+
## 🧪 测试
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
python tests/test_smoke.py # M1 读写与分页
|
|
131
|
+
python tests/test_m2.py # M2 服务端分析
|
|
132
|
+
python tests/test_m34.py # M3 重算降级 + M4 中文样式与图表
|
|
133
|
+
python tests/test_m6.py # M6 透视表/条件格式/多表关联
|
|
134
|
+
python tests/test_edge_cases.py # 边界回归(防 BUG 复发)
|
|
135
|
+
python tests/test_m7.py # M7 格式语义/隐藏处理/图片/原子保存
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
6 个测试文件 **全部通过**,另有 **20 项 MCP stdio 端到端验证**(覆盖全部 19 个工具)。
|
|
139
|
+
|
|
140
|
+
## 🧠 踩过的坑(值得记住)
|
|
141
|
+
|
|
142
|
+
1. **openpyxl `read_only=True` 模式不加载行列维度** —— `ws.row_dimensions` 为空,
|
|
143
|
+
隐藏检测会**静默失效**。改为直接解析 sheet XML(零内存且准确)。
|
|
144
|
+
2. **mcp 2.x 的 schema 生成器会丢弃 `str | None`(PEP 604)标注的参数** ——
|
|
145
|
+
表现为"工具传了参数却不生效"。统一改用 `Optional[str]` 才正常。
|
|
146
|
+
3. **图表 `Reference(range_string="B2:B5")` 要求 `表名!A1:B2` 形式** ——
|
|
147
|
+
中文表名还需引号包裹。改用 `range_boundaries()` 解析成行列参数。
|
|
148
|
+
4. **重复 `@mcp.tool()` 注册同名函数**会触发 `Tool already exists` 警告且行为异常。
|
|
149
|
+
|
|
150
|
+
## 📚 Skill 层
|
|
151
|
+
|
|
152
|
+
`skill/duduexcel/` 提供 Agent 方法论(非 MCP 工具):
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
SKILL.md # 工具路由表 + 5 条铁律 + 公式约束
|
|
156
|
+
references/style.md # 配色语义、数字格式码、结构规范
|
|
157
|
+
references/formulas.md # 函数白名单、_xlfn. 前缀、7 类错误速查
|
|
158
|
+
references/charts.md # 图表做法与保真度
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
> 合规说明:Anthropic 官方 xlsx skill 为 Proprietary(禁止衍生作品)。
|
|
162
|
+
> 本 Skill 仅借鉴其**工程思想**(验证优先、截断诚实性、外链熔断),
|
|
163
|
+
> 文字与代码均为独立撰写。
|
|
164
|
+
|
|
165
|
+
## 🗺️ 路线图
|
|
166
|
+
|
|
167
|
+
| 阶段 | 状态 |
|
|
168
|
+
|---|---|
|
|
169
|
+
| M1 骨架 + 安全读写 | ✅ |
|
|
170
|
+
| M2 服务端分析 + token 自报 | ✅ |
|
|
171
|
+
| M3 公式重算 + 外链熔断 | ✅ |
|
|
172
|
+
| M4 中文样式 + 图表 | ✅ |
|
|
173
|
+
| M5 Skill 层 | ✅ |
|
|
174
|
+
| 透视表 / 条件格式 / 多表关联 | 待做 |
|
|
175
|
+
|
|
176
|
+
## License
|
|
177
|
+
|
|
178
|
+
MIT
|