sheetforge-mcp 0.2.1__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.
@@ -0,0 +1,6 @@
1
+ .venv/
2
+ dist/
3
+ excel_files/
4
+ __pycache__/
5
+ .notes/
6
+ *.log
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Haris
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.
@@ -0,0 +1,178 @@
1
+ Metadata-Version: 2.4
2
+ Name: sheetforge-mcp
3
+ Version: 0.2.1
4
+ Summary: SheetForge MCP for reading, writing, and reshaping Excel workbooks
5
+ Author: SheetForge
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.10
8
+ Requires-Dist: fastmcp<3.0.0,>=2.0.0
9
+ Requires-Dist: mcp[cli]>=1.10.1
10
+ Requires-Dist: openpyxl>=3.1.5
11
+ Requires-Dist: typer>=0.16.0
12
+ Provides-Extra: dev
13
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
14
+ Description-Content-Type: text/markdown
15
+
16
+ # SheetForge MCP
17
+
18
+ SheetForge MCP exposes `.xlsx` workbook operations over the Model Context Protocol. It uses `openpyxl` under the hood, so MCP clients can inspect and modify Excel files without launching Microsoft Excel or LibreOffice.
19
+
20
+ Package name: `sheetforge-mcp`
21
+ CLI command: `sheetforge-mcp`
22
+
23
+ ## What This Project Covers
24
+
25
+ - workbook creation and metadata
26
+ - worksheet creation, renaming, copying, and deletion
27
+ - structured reads, compact table reads, and cell search
28
+ - row, column, and range mutations
29
+ - formulas and validation checks
30
+ - formatting, merges, and conditional formatting
31
+ - native Excel tables, charts, and pivot summaries
32
+ - `stdio`, `streamable-http`, and deprecated `sse` transports
33
+
34
+ ## Requirements
35
+
36
+ - Python `3.10+`
37
+ - `.xlsx` workbooks
38
+ - either `uvx` or a local package install
39
+
40
+ ## Quick Start
41
+
42
+ ### Stdio
43
+
44
+ Use `stdio` when the MCP client starts the server locally.
45
+
46
+ ```bash
47
+ uvx sheetforge-mcp stdio
48
+ ```
49
+
50
+ ```json
51
+ {
52
+ "mcpServers": {
53
+ "excel": {
54
+ "command": "uvx",
55
+ "args": ["sheetforge-mcp", "stdio"]
56
+ }
57
+ }
58
+ }
59
+ ```
60
+
61
+ ### Streamable HTTP
62
+
63
+ Use `streamable-http` when you want a long-running local or remote server process.
64
+
65
+ ```bash
66
+ EXCEL_FILES_PATH=/path/to/excel-files uvx sheetforge-mcp streamable-http
67
+ ```
68
+
69
+ Default endpoint:
70
+
71
+ ```text
72
+ http://127.0.0.1:8017/mcp
73
+ ```
74
+
75
+ Example client config:
76
+
77
+ ```json
78
+ {
79
+ "mcpServers": {
80
+ "excel": {
81
+ "url": "http://127.0.0.1:8017/mcp"
82
+ }
83
+ }
84
+ }
85
+ ```
86
+
87
+ ### SSE
88
+
89
+ SSE is kept for compatibility, but new integrations should prefer `streamable-http`.
90
+
91
+ ```bash
92
+ EXCEL_FILES_PATH=/path/to/excel-files uvx sheetforge-mcp sse
93
+ ```
94
+
95
+ Default endpoint:
96
+
97
+ ```text
98
+ http://127.0.0.1:8017/sse
99
+ ```
100
+
101
+ ## File Path Rules
102
+
103
+ - In `stdio` mode, `filepath` values must be absolute paths.
104
+ - In `streamable-http` and `sse` mode, relative paths are resolved under `EXCEL_FILES_PATH`.
105
+ - Absolute paths are accepted in every transport.
106
+ - In `streamable-http` and `sse` mode, the server creates `EXCEL_FILES_PATH` automatically if it does not exist.
107
+
108
+ ## Environment Variables
109
+
110
+ | Variable | Default | Used by | Purpose |
111
+ | --- | --- | --- | --- |
112
+ | `FASTMCP_HOST` | `127.0.0.1` | HTTP and SSE | Bind address for the server process |
113
+ | `FASTMCP_PORT` | `8017` | HTTP and SSE | Port for the server process |
114
+ | `EXCEL_FILES_PATH` | `./excel_files` | HTTP and SSE | Base directory for relative workbook paths |
115
+
116
+ ## Tooling Overview
117
+
118
+ The server currently registers 28 MCP tools across these groups:
119
+
120
+ - workbook overview: `create_workbook`, `create_worksheet`, `get_workbook_metadata`, `list_all_sheets`
121
+ - data access: `read_data_from_excel`, `read_excel_as_table`, `search_in_sheet`, `write_data_to_excel`
122
+ - worksheet and range changes: `copy_worksheet`, `delete_worksheet`, `rename_worksheet`, `copy_range`, `delete_range`, `insert_rows`, `insert_columns`, `delete_sheet_rows`, `delete_sheet_columns`
123
+ - formatting and layout: `format_range`, `merge_cells`, `unmerge_cells`, `get_merged_cells`
124
+ - formulas and validation: `apply_formula`, `validate_formula_syntax`, `validate_excel_range`, `get_data_validation_info`
125
+ - analysis and structure: `create_table`, `create_chart`, `create_pivot_table`
126
+
127
+ The three most agent-friendly read tools are:
128
+
129
+ - `list_all_sheets`: quick workbook inventory with sheet sizes and emptiness flags
130
+ - `read_excel_as_table`: compact `headers + rows` output for structured datasets
131
+ - `search_in_sheet`: exact or partial value search across a worksheet
132
+
133
+ See [TOOLS.md](TOOLS.md) for the full reference.
134
+
135
+ ## Development
136
+
137
+ Install dependencies:
138
+
139
+ ```bash
140
+ uv sync --extra dev
141
+ ```
142
+
143
+ Run tests:
144
+
145
+ ```bash
146
+ uv run --extra dev pytest -q
147
+ ```
148
+
149
+ Run the package locally:
150
+
151
+ ```bash
152
+ uv run sheetforge-mcp stdio
153
+ ```
154
+
155
+ ## Release Flow
156
+
157
+ - GitHub releases run a build verification workflow only.
158
+ - PyPI publishing is a separate manual workflow, so releases do not create a failing deployment before Trusted Publisher is configured for the package.
159
+
160
+ ## Repository Layout
161
+
162
+ - `src/excel_mcp/server.py`: MCP server, transport setup, and tool registration
163
+ - `src/excel_mcp/workbook.py`: workbook lifecycle helpers and workbook metadata
164
+ - `src/excel_mcp/data.py`: read, write, table, and search helpers
165
+ - `src/excel_mcp/sheet.py`: worksheet and range mutations
166
+ - `tests/`: regression tests for workbook handling and public behavior
167
+ - `manifest.json`: packaged MCP bundle metadata
168
+ - `docs/index.html`: static project landing page
169
+
170
+ ## Notes For Integrators
171
+
172
+ - `stdio` mode is careful not to write non-protocol text to `stdout`.
173
+ - Most read-oriented tools return JSON strings, but a few older tools still return plain string representations for compatibility.
174
+ - `read_data_from_excel(..., preview_only=True)` limits the response to the first 10 rows in the selected range and marks the payload as truncated when applicable.
175
+
176
+ ## License
177
+
178
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,163 @@
1
+ # SheetForge MCP
2
+
3
+ SheetForge MCP exposes `.xlsx` workbook operations over the Model Context Protocol. It uses `openpyxl` under the hood, so MCP clients can inspect and modify Excel files without launching Microsoft Excel or LibreOffice.
4
+
5
+ Package name: `sheetforge-mcp`
6
+ CLI command: `sheetforge-mcp`
7
+
8
+ ## What This Project Covers
9
+
10
+ - workbook creation and metadata
11
+ - worksheet creation, renaming, copying, and deletion
12
+ - structured reads, compact table reads, and cell search
13
+ - row, column, and range mutations
14
+ - formulas and validation checks
15
+ - formatting, merges, and conditional formatting
16
+ - native Excel tables, charts, and pivot summaries
17
+ - `stdio`, `streamable-http`, and deprecated `sse` transports
18
+
19
+ ## Requirements
20
+
21
+ - Python `3.10+`
22
+ - `.xlsx` workbooks
23
+ - either `uvx` or a local package install
24
+
25
+ ## Quick Start
26
+
27
+ ### Stdio
28
+
29
+ Use `stdio` when the MCP client starts the server locally.
30
+
31
+ ```bash
32
+ uvx sheetforge-mcp stdio
33
+ ```
34
+
35
+ ```json
36
+ {
37
+ "mcpServers": {
38
+ "excel": {
39
+ "command": "uvx",
40
+ "args": ["sheetforge-mcp", "stdio"]
41
+ }
42
+ }
43
+ }
44
+ ```
45
+
46
+ ### Streamable HTTP
47
+
48
+ Use `streamable-http` when you want a long-running local or remote server process.
49
+
50
+ ```bash
51
+ EXCEL_FILES_PATH=/path/to/excel-files uvx sheetforge-mcp streamable-http
52
+ ```
53
+
54
+ Default endpoint:
55
+
56
+ ```text
57
+ http://127.0.0.1:8017/mcp
58
+ ```
59
+
60
+ Example client config:
61
+
62
+ ```json
63
+ {
64
+ "mcpServers": {
65
+ "excel": {
66
+ "url": "http://127.0.0.1:8017/mcp"
67
+ }
68
+ }
69
+ }
70
+ ```
71
+
72
+ ### SSE
73
+
74
+ SSE is kept for compatibility, but new integrations should prefer `streamable-http`.
75
+
76
+ ```bash
77
+ EXCEL_FILES_PATH=/path/to/excel-files uvx sheetforge-mcp sse
78
+ ```
79
+
80
+ Default endpoint:
81
+
82
+ ```text
83
+ http://127.0.0.1:8017/sse
84
+ ```
85
+
86
+ ## File Path Rules
87
+
88
+ - In `stdio` mode, `filepath` values must be absolute paths.
89
+ - In `streamable-http` and `sse` mode, relative paths are resolved under `EXCEL_FILES_PATH`.
90
+ - Absolute paths are accepted in every transport.
91
+ - In `streamable-http` and `sse` mode, the server creates `EXCEL_FILES_PATH` automatically if it does not exist.
92
+
93
+ ## Environment Variables
94
+
95
+ | Variable | Default | Used by | Purpose |
96
+ | --- | --- | --- | --- |
97
+ | `FASTMCP_HOST` | `127.0.0.1` | HTTP and SSE | Bind address for the server process |
98
+ | `FASTMCP_PORT` | `8017` | HTTP and SSE | Port for the server process |
99
+ | `EXCEL_FILES_PATH` | `./excel_files` | HTTP and SSE | Base directory for relative workbook paths |
100
+
101
+ ## Tooling Overview
102
+
103
+ The server currently registers 28 MCP tools across these groups:
104
+
105
+ - workbook overview: `create_workbook`, `create_worksheet`, `get_workbook_metadata`, `list_all_sheets`
106
+ - data access: `read_data_from_excel`, `read_excel_as_table`, `search_in_sheet`, `write_data_to_excel`
107
+ - worksheet and range changes: `copy_worksheet`, `delete_worksheet`, `rename_worksheet`, `copy_range`, `delete_range`, `insert_rows`, `insert_columns`, `delete_sheet_rows`, `delete_sheet_columns`
108
+ - formatting and layout: `format_range`, `merge_cells`, `unmerge_cells`, `get_merged_cells`
109
+ - formulas and validation: `apply_formula`, `validate_formula_syntax`, `validate_excel_range`, `get_data_validation_info`
110
+ - analysis and structure: `create_table`, `create_chart`, `create_pivot_table`
111
+
112
+ The three most agent-friendly read tools are:
113
+
114
+ - `list_all_sheets`: quick workbook inventory with sheet sizes and emptiness flags
115
+ - `read_excel_as_table`: compact `headers + rows` output for structured datasets
116
+ - `search_in_sheet`: exact or partial value search across a worksheet
117
+
118
+ See [TOOLS.md](TOOLS.md) for the full reference.
119
+
120
+ ## Development
121
+
122
+ Install dependencies:
123
+
124
+ ```bash
125
+ uv sync --extra dev
126
+ ```
127
+
128
+ Run tests:
129
+
130
+ ```bash
131
+ uv run --extra dev pytest -q
132
+ ```
133
+
134
+ Run the package locally:
135
+
136
+ ```bash
137
+ uv run sheetforge-mcp stdio
138
+ ```
139
+
140
+ ## Release Flow
141
+
142
+ - GitHub releases run a build verification workflow only.
143
+ - PyPI publishing is a separate manual workflow, so releases do not create a failing deployment before Trusted Publisher is configured for the package.
144
+
145
+ ## Repository Layout
146
+
147
+ - `src/excel_mcp/server.py`: MCP server, transport setup, and tool registration
148
+ - `src/excel_mcp/workbook.py`: workbook lifecycle helpers and workbook metadata
149
+ - `src/excel_mcp/data.py`: read, write, table, and search helpers
150
+ - `src/excel_mcp/sheet.py`: worksheet and range mutations
151
+ - `tests/`: regression tests for workbook handling and public behavior
152
+ - `manifest.json`: packaged MCP bundle metadata
153
+ - `docs/index.html`: static project landing page
154
+
155
+ ## Notes For Integrators
156
+
157
+ - `stdio` mode is careful not to write non-protocol text to `stdout`.
158
+ - Most read-oriented tools return JSON strings, but a few older tools still return plain string representations for compatibility.
159
+ - `read_data_from_excel(..., preview_only=True)` limits the response to the first 10 rows in the selected range and marks the payload as truncated when applicable.
160
+
161
+ ## License
162
+
163
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,47 @@
1
+ import sys
2
+ import traceback
3
+ from collections.abc import Callable
4
+
5
+ import typer
6
+
7
+ from .server import run_sse, run_stdio, run_streamable_http
8
+
9
+ app = typer.Typer(help="SheetForge MCP")
10
+
11
+
12
+ def _run_server(start_fn: Callable[[], None]) -> None:
13
+ """Run a server command without writing protocol-breaking output to stdout."""
14
+ exit_code = 0
15
+ try:
16
+ start_fn()
17
+ except KeyboardInterrupt:
18
+ typer.echo("Shutting down server...", err=True)
19
+ exit_code = 130
20
+ except Exception as e:
21
+ typer.echo(f"Error: {e}", err=True)
22
+ traceback.print_exc(file=sys.stderr)
23
+ exit_code = 1
24
+ finally:
25
+ typer.echo("Service stopped.", err=True)
26
+
27
+ if exit_code:
28
+ raise typer.Exit(code=exit_code)
29
+
30
+
31
+ @app.command()
32
+ def sse():
33
+ """Start SheetForge MCP in SSE mode."""
34
+ _run_server(run_sse)
35
+
36
+ @app.command()
37
+ def streamable_http():
38
+ """Start SheetForge MCP in streamable HTTP mode."""
39
+ _run_server(run_streamable_http)
40
+
41
+ @app.command()
42
+ def stdio():
43
+ """Start SheetForge MCP in stdio mode."""
44
+ _run_server(run_stdio)
45
+
46
+ if __name__ == "__main__":
47
+ app()
@@ -0,0 +1,55 @@
1
+ from typing import Any
2
+ import logging
3
+
4
+ from .workbook import safe_workbook
5
+ from .cell_utils import validate_cell_reference
6
+ from .exceptions import ValidationError, CalculationError
7
+ from .validation import validate_formula
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+ def apply_formula(
12
+ filepath: str,
13
+ sheet_name: str,
14
+ cell: str,
15
+ formula: str
16
+ ) -> dict[str, Any]:
17
+ """Apply any Excel formula to a cell."""
18
+ try:
19
+ if not validate_cell_reference(cell):
20
+ raise ValidationError(f"Invalid cell reference: {cell}")
21
+
22
+ # Ensure formula starts with =
23
+ if not formula.startswith('='):
24
+ formula = f'={formula}'
25
+
26
+ # Validate formula syntax
27
+ is_valid, message = validate_formula(formula)
28
+ if not is_valid:
29
+ raise CalculationError(f"Invalid formula syntax: {message}")
30
+
31
+ with safe_workbook(filepath, save=True) as wb:
32
+ if sheet_name not in wb.sheetnames:
33
+ raise ValidationError(f"Sheet '{sheet_name}' not found")
34
+
35
+ sheet = wb[sheet_name]
36
+
37
+ try:
38
+ # Apply formula to the cell
39
+ cell_obj = sheet[cell]
40
+ cell_obj.value = formula
41
+ except Exception as e:
42
+ raise CalculationError(f"Failed to apply formula to cell: {str(e)}")
43
+
44
+ return {
45
+ "message": f"Applied formula '{formula}' to cell {cell}",
46
+ "cell": cell,
47
+ "formula": formula
48
+ }
49
+
50
+ except (ValidationError, CalculationError) as e:
51
+ logger.error(str(e))
52
+ raise
53
+ except Exception as e:
54
+ logger.error(f"Failed to apply formula: {e}")
55
+ raise CalculationError(str(e))
@@ -0,0 +1,54 @@
1
+ import re
2
+
3
+ from openpyxl.utils import column_index_from_string
4
+
5
+ def parse_cell_range(
6
+ cell_ref: str,
7
+ end_ref: str | None = None
8
+ ) -> tuple[int, int, int | None, int | None]:
9
+ """Parse Excel cell reference into row and column indices."""
10
+ if end_ref:
11
+ start_cell = cell_ref
12
+ end_cell = end_ref
13
+ else:
14
+ start_cell = cell_ref
15
+ end_cell = None
16
+
17
+ match = re.match(r"([A-Z]+)([0-9]+)", start_cell.upper())
18
+ if not match:
19
+ raise ValueError(f"Invalid cell reference: {start_cell}")
20
+ col_str, row_str = match.groups()
21
+ start_row = int(row_str)
22
+ start_col = column_index_from_string(col_str)
23
+
24
+ if end_cell:
25
+ match = re.match(r"([A-Z]+)([0-9]+)", end_cell.upper())
26
+ if not match:
27
+ raise ValueError(f"Invalid cell reference: {end_cell}")
28
+ col_str, row_str = match.groups()
29
+ end_row = int(row_str)
30
+ end_col = column_index_from_string(col_str)
31
+ else:
32
+ end_row = None
33
+ end_col = None
34
+
35
+ return start_row, start_col, end_row, end_col
36
+
37
+ def validate_cell_reference(cell_ref: str) -> bool:
38
+ """Validate Excel cell reference format (e.g., 'A1', 'BC123')"""
39
+ if not cell_ref:
40
+ return False
41
+
42
+ # Split into column and row parts
43
+ col = row = ""
44
+ for c in cell_ref:
45
+ if c.isalpha():
46
+ if row: # Letters after numbers not allowed
47
+ return False
48
+ col += c
49
+ elif c.isdigit():
50
+ row += c
51
+ else:
52
+ return False
53
+
54
+ return bool(col and row)