excel-mcp-server 0.1.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.

Potentially problematic release.


This version of excel-mcp-server might be problematic. Click here for more details.

@@ -0,0 +1,36 @@
1
+ # Build and Distribution
2
+ __pycache__/
3
+ *.py[cod]
4
+ build/
5
+ dist/
6
+ src/*.egg-info/
7
+
8
+ # Development Environment
9
+ .venv/
10
+ .env
11
+
12
+ # IDE
13
+ .vscode/
14
+ .idea/
15
+ .cursor/
16
+ .cursorignore
17
+ .cursorrules
18
+ .specstory
19
+
20
+ # Testing and Linting
21
+ .coverage
22
+ .pytest_cache/
23
+ .ruff_cache/
24
+ .mypy_cache/
25
+ htmlcov/
26
+ tests/
27
+
28
+ # Project Files
29
+ extras/
30
+ .notes/
31
+ logs/
32
+ output/
33
+ *.xlsx
34
+ *.xls
35
+ *.log
36
+ excel_files/
@@ -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,126 @@
1
+ Metadata-Version: 2.4
2
+ Name: excel-mcp-server
3
+ Version: 0.1.0
4
+ Summary: MCP server for Excel file manipulation
5
+ Author-email: haris <haris.musa@outlook.com>
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.10
8
+ Requires-Dist: mcp[cli]>=1.2.0
9
+ Requires-Dist: openpyxl>=3.1.2
10
+ Description-Content-Type: text/markdown
11
+
12
+ # Excel MCP Server
13
+ [![smithery badge](https://smithery.ai/badge/@haris-musa/excel-mcp-server)](https://smithery.ai/server/@haris-musa/excel-mcp-server)
14
+
15
+ A Model Context Protocol (MCP) server implementation that provides Excel file manipulation capabilities without requiring Microsoft Excel installation. This server enables workbook creation, data manipulation, formatting, and advanced Excel features.
16
+
17
+ ## Requirements
18
+
19
+ - Python 3.10+
20
+ - MCP SDK 1.2.0+
21
+ - OpenPyXL 3.1.2+
22
+
23
+ ## Components
24
+
25
+ ### Resources
26
+
27
+ The server provides Excel workbook manipulation through OpenPyXL:
28
+
29
+ - Creates and modifies Excel workbooks
30
+ - Manages worksheets and ranges
31
+ - Handles formatting and styles
32
+ - Supports charts and pivot tables
33
+
34
+ ### Tools
35
+
36
+ This server provides a comprehensive set of Excel manipulation tools. For detailed documentation of all available tools, their parameters, and usage examples, please refer to [TOOLS.md](TOOLS.md).
37
+
38
+ The tools include capabilities for:
39
+
40
+ - Workbook and worksheet management
41
+ - Data reading and writing
42
+ - Formatting and styling
43
+ - Charts and visualizations
44
+ - Pivot tables and data analysis
45
+
46
+ See [TOOLS.md](TOOLS.md) for complete documentation.
47
+
48
+ ## Features
49
+
50
+ - Full Excel Support: Comprehensive Excel functionality
51
+ - Data Manipulation: Read, write, and transform data
52
+ - Advanced Features: Charts, pivot tables, and formatting
53
+ - Error Handling: Comprehensive error handling with clear messages
54
+
55
+ ## Usage
56
+
57
+ ### Environment Configuration
58
+
59
+ The server can be configured using the following environment variables:
60
+
61
+ - `EXCEL_FILES_PATH`: Directory where Excel files will be stored (default: `./excel_files`)
62
+
63
+ You can set this in different ways:
64
+
65
+ Windows CMD:
66
+
67
+ ```cmd
68
+ set EXCEL_FILES_PATH=C:\path\to\excel\files
69
+ uv run excel-mcp-server
70
+ ```
71
+
72
+ Windows PowerShell:
73
+
74
+ ```powershell
75
+ $env:EXCEL_FILES_PATH="C:\path\to\excel\files"
76
+ uv run excel-mcp-server
77
+ ```
78
+
79
+ Linux/MacOS:
80
+
81
+ ```bash
82
+ export EXCEL_FILES_PATH=/path/to/excel/files
83
+ uv run excel-mcp-server
84
+ ```
85
+
86
+ Or in Claude Desktop config:
87
+
88
+ ```json
89
+ {
90
+ "mcpServers": {
91
+ "excel": {
92
+ "command": "uv run excel-mcp-server",
93
+ "transport": "sse",
94
+ "env": {
95
+ "EXCEL_FILES_PATH": "/path/to/excel/files"
96
+ }
97
+ }
98
+ }
99
+ }
100
+ ```
101
+
102
+ ### Starting the Server
103
+
104
+ Start the server:
105
+
106
+ ```bash
107
+ uv run excel-mcp-server
108
+ ```
109
+
110
+ The server will start in SSE mode and wait for connections from MCP clients.
111
+
112
+ ### Connecting in Cursor IDE
113
+
114
+ After starting the server, connect to the SSE endpoint in Cursor IDE:
115
+
116
+ ```
117
+ http://localhost:8000/sse
118
+ ```
119
+
120
+ The Excel MCP tools will be available through the agent.
121
+
122
+ For available tools and their usage, please refer to [TOOLS.md](TOOLS.md).
123
+
124
+ ## License
125
+
126
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
@@ -0,0 +1,115 @@
1
+ # Excel MCP Server
2
+ [![smithery badge](https://smithery.ai/badge/@haris-musa/excel-mcp-server)](https://smithery.ai/server/@haris-musa/excel-mcp-server)
3
+
4
+ A Model Context Protocol (MCP) server implementation that provides Excel file manipulation capabilities without requiring Microsoft Excel installation. This server enables workbook creation, data manipulation, formatting, and advanced Excel features.
5
+
6
+ ## Requirements
7
+
8
+ - Python 3.10+
9
+ - MCP SDK 1.2.0+
10
+ - OpenPyXL 3.1.2+
11
+
12
+ ## Components
13
+
14
+ ### Resources
15
+
16
+ The server provides Excel workbook manipulation through OpenPyXL:
17
+
18
+ - Creates and modifies Excel workbooks
19
+ - Manages worksheets and ranges
20
+ - Handles formatting and styles
21
+ - Supports charts and pivot tables
22
+
23
+ ### Tools
24
+
25
+ This server provides a comprehensive set of Excel manipulation tools. For detailed documentation of all available tools, their parameters, and usage examples, please refer to [TOOLS.md](TOOLS.md).
26
+
27
+ The tools include capabilities for:
28
+
29
+ - Workbook and worksheet management
30
+ - Data reading and writing
31
+ - Formatting and styling
32
+ - Charts and visualizations
33
+ - Pivot tables and data analysis
34
+
35
+ See [TOOLS.md](TOOLS.md) for complete documentation.
36
+
37
+ ## Features
38
+
39
+ - Full Excel Support: Comprehensive Excel functionality
40
+ - Data Manipulation: Read, write, and transform data
41
+ - Advanced Features: Charts, pivot tables, and formatting
42
+ - Error Handling: Comprehensive error handling with clear messages
43
+
44
+ ## Usage
45
+
46
+ ### Environment Configuration
47
+
48
+ The server can be configured using the following environment variables:
49
+
50
+ - `EXCEL_FILES_PATH`: Directory where Excel files will be stored (default: `./excel_files`)
51
+
52
+ You can set this in different ways:
53
+
54
+ Windows CMD:
55
+
56
+ ```cmd
57
+ set EXCEL_FILES_PATH=C:\path\to\excel\files
58
+ uv run excel-mcp-server
59
+ ```
60
+
61
+ Windows PowerShell:
62
+
63
+ ```powershell
64
+ $env:EXCEL_FILES_PATH="C:\path\to\excel\files"
65
+ uv run excel-mcp-server
66
+ ```
67
+
68
+ Linux/MacOS:
69
+
70
+ ```bash
71
+ export EXCEL_FILES_PATH=/path/to/excel/files
72
+ uv run excel-mcp-server
73
+ ```
74
+
75
+ Or in Claude Desktop config:
76
+
77
+ ```json
78
+ {
79
+ "mcpServers": {
80
+ "excel": {
81
+ "command": "uv run excel-mcp-server",
82
+ "transport": "sse",
83
+ "env": {
84
+ "EXCEL_FILES_PATH": "/path/to/excel/files"
85
+ }
86
+ }
87
+ }
88
+ }
89
+ ```
90
+
91
+ ### Starting the Server
92
+
93
+ Start the server:
94
+
95
+ ```bash
96
+ uv run excel-mcp-server
97
+ ```
98
+
99
+ The server will start in SSE mode and wait for connections from MCP clients.
100
+
101
+ ### Connecting in Cursor IDE
102
+
103
+ After starting the server, connect to the SSE endpoint in Cursor IDE:
104
+
105
+ ```
106
+ http://localhost:8000/sse
107
+ ```
108
+
109
+ The Excel MCP tools will be available through the agent.
110
+
111
+ For available tools and their usage, please refer to [TOOLS.md](TOOLS.md).
112
+
113
+ ## License
114
+
115
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
@@ -0,0 +1,21 @@
1
+ import asyncio
2
+ from .server import run_server
3
+
4
+ def main():
5
+ """Start the Excel MCP server."""
6
+ try:
7
+ print("Excel MCP Server")
8
+ print("---------------")
9
+ print("Starting server... Press Ctrl+C to exit")
10
+ asyncio.run(run_server())
11
+ except KeyboardInterrupt:
12
+ print("\nShutting down server...")
13
+ except Exception as e:
14
+ print(f"\nError: {e}")
15
+ import traceback
16
+ traceback.print_exc()
17
+ finally:
18
+ print("Server stopped.")
19
+
20
+ if __name__ == "__main__":
21
+ main()
@@ -0,0 +1,60 @@
1
+ from typing import Any
2
+ import logging
3
+
4
+ from .workbook import get_or_create_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
+ wb = get_or_create_workbook(filepath)
23
+ if sheet_name not in wb.sheetnames:
24
+ raise ValidationError(f"Sheet '{sheet_name}' not found")
25
+
26
+ sheet = wb[sheet_name]
27
+
28
+ # Ensure formula starts with =
29
+ if not formula.startswith('='):
30
+ formula = f'={formula}'
31
+
32
+ # Validate formula syntax
33
+ is_valid, message = validate_formula(formula)
34
+ if not is_valid:
35
+ raise CalculationError(f"Invalid formula syntax: {message}")
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
+ try:
45
+ wb.save(filepath)
46
+ except Exception as e:
47
+ raise CalculationError(f"Failed to save workbook after applying formula: {str(e)}")
48
+
49
+ return {
50
+ "message": f"Applied formula '{formula}' to cell {cell}",
51
+ "cell": cell,
52
+ "formula": formula
53
+ }
54
+
55
+ except (ValidationError, CalculationError) as e:
56
+ logger.error(str(e))
57
+ raise
58
+ except Exception as e:
59
+ logger.error(f"Failed to apply formula: {e}")
60
+ 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)
@@ -0,0 +1,237 @@
1
+ from typing import Any, Optional, Dict
2
+ import logging
3
+ from enum import Enum
4
+
5
+ from openpyxl import load_workbook
6
+ from openpyxl.chart import (
7
+ BarChart, LineChart, PieChart, ScatterChart,
8
+ AreaChart, Reference, Series
9
+ )
10
+ from openpyxl.chart.label import DataLabelList
11
+ from openpyxl.chart.legend import Legend
12
+ from openpyxl.chart.axis import ChartLines
13
+ from openpyxl.drawing.spreadsheet_drawing import (
14
+ AnchorMarker, OneCellAnchor, SpreadsheetDrawing
15
+ )
16
+ from openpyxl.utils import column_index_from_string
17
+
18
+ from .cell_utils import parse_cell_range
19
+ from .exceptions import ValidationError, ChartError
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+ class ChartType(str, Enum):
24
+ """Supported chart types"""
25
+ LINE = "line"
26
+ BAR = "bar"
27
+ PIE = "pie"
28
+ SCATTER = "scatter"
29
+ AREA = "area"
30
+ BUBBLE = "bubble"
31
+ STOCK = "stock"
32
+ SURFACE = "surface"
33
+ RADAR = "radar"
34
+
35
+ class ChartStyle:
36
+ """Chart style configuration"""
37
+ def __init__(
38
+ self,
39
+ title_size: int = 14,
40
+ title_bold: bool = True,
41
+ axis_label_size: int = 12,
42
+ show_legend: bool = True,
43
+ legend_position: str = "r",
44
+ show_data_labels: bool = True,
45
+ grid_lines: bool = False,
46
+ style_id: int = 2
47
+ ):
48
+ self.title_size = title_size
49
+ self.title_bold = title_bold
50
+ self.axis_label_size = axis_label_size
51
+ self.show_legend = show_legend
52
+ self.legend_position = legend_position
53
+ self.show_data_labels = show_data_labels
54
+ self.grid_lines = grid_lines
55
+ self.style_id = style_id
56
+
57
+ def create_chart_in_sheet(
58
+ filepath: str,
59
+ sheet_name: str,
60
+ data_range: str,
61
+ chart_type: str,
62
+ target_cell: str,
63
+ title: str = "",
64
+ x_axis: str = "",
65
+ y_axis: str = "",
66
+ style: Optional[Dict] = None
67
+ ) -> dict[str, Any]:
68
+ """Create chart in sheet with enhanced styling options"""
69
+ try:
70
+ wb = load_workbook(filepath)
71
+ if sheet_name not in wb.sheetnames:
72
+ logger.error(f"Sheet '{sheet_name}' not found")
73
+ raise ValidationError(f"Sheet '{sheet_name}' not found")
74
+
75
+ worksheet = wb[sheet_name]
76
+
77
+ # Initialize collections if they don't exist
78
+ if not hasattr(worksheet, '_drawings'):
79
+ worksheet._drawings = []
80
+ if not hasattr(worksheet, '_charts'):
81
+ worksheet._charts = []
82
+
83
+ # Parse the data range
84
+ if "!" in data_range:
85
+ range_sheet_name, cell_range = data_range.split("!")
86
+ if range_sheet_name not in wb.sheetnames:
87
+ logger.error(f"Sheet '{range_sheet_name}' referenced in data range not found")
88
+ raise ValidationError(f"Sheet '{range_sheet_name}' referenced in data range not found")
89
+ else:
90
+ cell_range = data_range
91
+
92
+ try:
93
+ start_cell, end_cell = cell_range.split(":")
94
+ start_row, start_col, end_row, end_col = parse_cell_range(start_cell, end_cell)
95
+ except ValueError as e:
96
+ logger.error(f"Invalid data range format: {e}")
97
+ raise ValidationError(f"Invalid data range format: {str(e)}")
98
+
99
+ # Validate chart type
100
+ chart_classes = {
101
+ "line": LineChart,
102
+ "bar": BarChart,
103
+ "pie": PieChart,
104
+ "scatter": ScatterChart,
105
+ "area": AreaChart
106
+ }
107
+
108
+ chart_type_lower = chart_type.lower()
109
+ ChartClass = chart_classes.get(chart_type_lower)
110
+ if not ChartClass:
111
+ logger.error(f"Unsupported chart type: {chart_type}")
112
+ raise ValidationError(
113
+ f"Unsupported chart type: {chart_type}. "
114
+ f"Supported types: {', '.join(chart_classes.keys())}"
115
+ )
116
+
117
+ chart = ChartClass()
118
+
119
+ # Basic chart settings
120
+ chart.title = title
121
+ if hasattr(chart, "x_axis"):
122
+ chart.x_axis.title = x_axis
123
+ if hasattr(chart, "y_axis"):
124
+ chart.y_axis.title = y_axis
125
+
126
+ try:
127
+ # Create data references
128
+ if chart_type_lower == "scatter":
129
+ # For scatter charts, create series for each pair of columns
130
+ for col in range(start_col + 1, end_col + 1):
131
+ x_values = Reference(
132
+ worksheet,
133
+ min_row=start_row + 1,
134
+ max_row=end_row,
135
+ min_col=start_col
136
+ )
137
+ y_values = Reference(
138
+ worksheet,
139
+ min_row=start_row + 1,
140
+ max_row=end_row,
141
+ min_col=col
142
+ )
143
+ series = Series(y_values, x_values, title_from_data=True)
144
+ chart.series.append(series)
145
+ else:
146
+ # For other chart types
147
+ data = Reference(
148
+ worksheet,
149
+ min_row=start_row,
150
+ max_row=end_row,
151
+ min_col=start_col + 1,
152
+ max_col=end_col
153
+ )
154
+ cats = Reference(
155
+ worksheet,
156
+ min_row=start_row + 1,
157
+ max_row=end_row,
158
+ min_col=start_col
159
+ )
160
+ chart.add_data(data, titles_from_data=True)
161
+ chart.set_categories(cats)
162
+ except Exception as e:
163
+ logger.error(f"Failed to create chart data references: {e}")
164
+ raise ChartError(f"Failed to create chart data references: {str(e)}")
165
+
166
+ # Apply style if provided
167
+ try:
168
+ if style:
169
+ if style.get("show_legend", True):
170
+ chart.legend = Legend()
171
+ chart.legend.position = style.get("legend_position", "r")
172
+ else:
173
+ chart.legend = None
174
+
175
+ if style.get("show_data_labels", False):
176
+ chart.dataLabels = DataLabelList()
177
+ chart.dataLabels.showVal = True
178
+
179
+ if style.get("grid_lines", False):
180
+ if hasattr(chart, "x_axis"):
181
+ chart.x_axis.majorGridlines = ChartLines()
182
+ if hasattr(chart, "y_axis"):
183
+ chart.y_axis.majorGridlines = ChartLines()
184
+ except Exception as e:
185
+ logger.error(f"Failed to apply chart style: {e}")
186
+ raise ChartError(f"Failed to apply chart style: {str(e)}")
187
+
188
+ # Set chart size
189
+ chart.width = 15
190
+ chart.height = 7.5
191
+
192
+ # Create drawing and anchor
193
+ try:
194
+ drawing = SpreadsheetDrawing()
195
+ drawing.chart = chart
196
+
197
+ # Validate target cell format
198
+ if not target_cell or not any(c.isalpha() for c in target_cell) or not any(c.isdigit() for c in target_cell):
199
+ raise ValidationError(f"Invalid target cell format: {target_cell}")
200
+
201
+ # Create anchor
202
+ col = column_index_from_string(target_cell[0]) - 1
203
+ row = int(target_cell[1:]) - 1
204
+ anchor = OneCellAnchor()
205
+ anchor._from = AnchorMarker(col=col, row=row)
206
+ drawing.anchor = anchor
207
+
208
+ # Add to worksheet
209
+ worksheet._drawings.append(drawing)
210
+ worksheet._charts.append(chart)
211
+ except ValueError as e:
212
+ logger.error(f"Invalid target cell: {e}")
213
+ raise ValidationError(f"Invalid target cell: {str(e)}")
214
+ except Exception as e:
215
+ logger.error(f"Failed to create chart drawing: {e}")
216
+ raise ChartError(f"Failed to create chart drawing: {str(e)}")
217
+
218
+ try:
219
+ wb.save(filepath)
220
+ except Exception as e:
221
+ logger.error(f"Failed to save workbook: {e}")
222
+ raise ChartError(f"Failed to save workbook with chart: {str(e)}")
223
+
224
+ return {
225
+ "message": f"{chart_type.capitalize()} chart created successfully",
226
+ "details": {
227
+ "type": chart_type,
228
+ "location": target_cell,
229
+ "data_range": data_range
230
+ }
231
+ }
232
+
233
+ except (ValidationError, ChartError):
234
+ raise
235
+ except Exception as e:
236
+ logger.error(f"Unexpected error creating chart: {e}")
237
+ raise ChartError(f"Unexpected error creating chart: {str(e)}")