Skip to content

Commit ad97aaa

Browse files
Edit MCP Word Edit and Excel Read Support (#424)
1 parent f199f06 commit ad97aaa

17 files changed

Lines changed: 1403 additions & 175 deletions

File tree

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
def get_worksheet_content_as_md_table(doc) -> str:
2+
"""
3+
Return the content of the worksheet formatted as a markdown table.
4+
"""
5+
# Assume the first worksheet is the one we want
6+
worksheet = doc.Worksheets(1)
7+
8+
# Get the used range of the worksheet
9+
used_range = worksheet.UsedRange
10+
11+
# Get the values as a tuple of tuples
12+
values = used_range.Value
13+
14+
# Convert to markdown table format
15+
if not values: # Handle empty worksheet
16+
return "| Empty worksheet |"
17+
18+
# Convert rows to lists and handle empty cells
19+
rows = []
20+
for row in values:
21+
row_data = [str(cell) if cell is not None else "" for cell in row]
22+
rows.append(row_data)
23+
24+
# Calculate column widths for better formatting
25+
col_widths = []
26+
for col in range(len(rows[0])):
27+
width = max(len(str(row[col])) for row in rows)
28+
col_widths.append(width)
29+
30+
# Build the markdown table
31+
md_table = []
32+
33+
# Header row
34+
header = "| " + " | ".join(str(rows[0][i]).ljust(col_widths[i]) for i in range(len(rows[0]))) + " |"
35+
md_table.append(header)
36+
37+
# Separator row
38+
separator = "| " + " | ".join("-" * width for width in col_widths) + " |"
39+
md_table.append(separator)
40+
41+
# Data rows
42+
for row in rows[1:]:
43+
row_str = "| " + " | ".join(str(row[i]).ljust(col_widths[i]) for i in range(len(row))) + " |"
44+
md_table.append(row_str)
45+
46+
return "\n".join(md_table)
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import sys
2+
from enum import Enum, auto
3+
from pathlib import Path
4+
5+
6+
class OfficeAppType(Enum):
7+
"""Enum representing Microsoft Office application types."""
8+
9+
WORD = auto()
10+
EXCEL = auto()
11+
POWERPOINT = auto()
12+
13+
14+
def open_document_in_office(file_path: Path, app_type: OfficeAppType) -> tuple[object, object]:
15+
"""
16+
Opens a document at the specified path in Word, Excel, or PowerPoint.
17+
Creates and saves the document if it doesn't exist.
18+
19+
Args:
20+
file_path: Path to the document
21+
app_type: The type of Office application to use
22+
23+
Returns:
24+
A tuple containing (app_instance, document_instance)
25+
26+
Raises:
27+
EnvironmentError: If not running on Windows
28+
ValueError: If an invalid app_type is provided
29+
"""
30+
if sys.platform != "win32":
31+
raise EnvironmentError("This function only works on Windows.")
32+
33+
import win32com.client as win32
34+
35+
# Map app types to their COM identifiers and document properties
36+
app_config = {
37+
OfficeAppType.WORD: {"app_name": "Word.Application", "collection_name": "Documents", "file_ext": ".docx"},
38+
OfficeAppType.EXCEL: {"app_name": "Excel.Application", "collection_name": "Workbooks", "file_ext": ".xlsx"},
39+
OfficeAppType.POWERPOINT: {
40+
"app_name": "PowerPoint.Application",
41+
"collection_name": "Presentations",
42+
"file_ext": ".pptx",
43+
},
44+
}
45+
46+
if app_type not in app_config:
47+
raise ValueError(f"Unsupported app type: {app_type}")
48+
49+
config = app_config[app_type]
50+
app_name = config["app_name"]
51+
collection_name = config["collection_name"]
52+
53+
# Ensure path is a Path object and resolve to absolute path
54+
doc_path = file_path.resolve()
55+
56+
# Add default extension if no extension is present
57+
if not doc_path.suffix:
58+
doc_path = doc_path.with_suffix(config["file_ext"])
59+
60+
# Create parent directories if they don't exist
61+
doc_path.parent.mkdir(parents=True, exist_ok=True)
62+
63+
# First try to get an existing app instance
64+
try:
65+
app = win32.GetActiveObject(app_name)
66+
# Check if the document is already open in this instance
67+
collection = getattr(app, collection_name)
68+
69+
# Handle different collection indexing methods for different Office apps
70+
if app_type == OfficeAppType.WORD or app_type == OfficeAppType.EXCEL:
71+
for i in range(1, collection.Count + 1):
72+
open_doc = collection(i)
73+
if open_doc.FullName.lower() == str(doc_path).lower():
74+
# Document is already open, just return it and its parent app
75+
return app, open_doc
76+
elif app_type == OfficeAppType.POWERPOINT:
77+
# PowerPoint uses a different approach for accessing presentations
78+
for open_doc in collection:
79+
if hasattr(open_doc, "FullName") and open_doc.FullName.lower() == str(doc_path).lower():
80+
return app, open_doc
81+
except Exception:
82+
# No running app instance found, create a new one
83+
app = win32.Dispatch(app_name)
84+
85+
# Make app visible
86+
app.Visible = True
87+
88+
# If the file exists, open it, otherwise create a new document and save it
89+
collection = getattr(app, collection_name)
90+
91+
if doc_path.exists():
92+
doc = collection.Open(str(doc_path))
93+
else:
94+
doc = collection.Add()
95+
doc.SaveAs(str(doc_path))
96+
97+
return app, doc

0 commit comments

Comments
 (0)