Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions docs/mcp_excel_server.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# MCP Server to Analyze and Work with Excel files

Excel file analysis and automation is a major task
done by the knowledge workers across the
organisations. The data is transferred between the
users, services and their managers in form of
table.

MCP servers have the tools, and resources that can
be made to work with excel sheets and extract
analysis from it.

We are implementing the MCP Server not just to
analyse the excel file. The tools in the server
can manipulate the excel files and the data in the
cells. The mcp implementation will use uv package
management tool. You can find more info about it
in this [video](https://youtu.be/0Lz_oXjqicE)

## Project Details:

- Project Path: ../mcp_excel_server/

The above project folder alone can be downloaded
using
[github-download-directory](https://download-directory.github.io/)

### Project setup commands:

The tool is executed in the terminal or command
prompt. If you have never used the terminal, the
video explaining the process will be available in
the below playlist.

```bash
# install uv tool, which is the python package manager
curl -LsSf https://astral.sh/uv/install.sh | sh

cd mcp_excel_server
# pyproject.toml will contain the libraries that are used in the project

uv run mcpclient.py server.py

After the client start, you will be prompted for the query.

Query: Who are you?.
```

### Available MCP Server Tools:

These tool are using mcp resources that are
manipulating and analysing the excel files.

- xlsx_to_html_table : Used for analysing the data
- create_excel_file : Create excel file
- read_excel_file_cell : Reads the excel file
designated cell
- write_to_excel_file_cell: Writes data to a
designated cell
- write_formula_to_excel_file_cell: Writes formula
to a designated cell
- delete_value_at_cell: Deletes data from a
designated cell
- search_excel_sheet_for_value: Searches for a
value in given excel file
- write_analysis_md: Write data to the markdown
file
- remove_file: Removes a file from the system

When you need use mcp inspector to debug the code,
use the command below. Ensure you have
[npx](https://docs.npmjs.com/cli/v8/commands/npx)
and [node](https://nodejs.org/en/download)
installed

npx @modelcontextprotocol/inspector uv run
server.py

### Project References

- Server code referred from :
[MCP Installation](https://github.com/modelcontextprotocol/python-sdk?tab=readme-ov-file#installation)

- Client code referred from:
[Introducing Clients](https://modelcontextprotocol.io/quickstart/client)

- Notion Code referred from:
[Notion Example code](../fw_ex/notionapi_spike/)

```

```
1 change: 1 addition & 0 deletions mcp_excel_server/.python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.11
Empty file added mcp_excel_server/README.md
Empty file.
Binary file added mcp_excel_server/exercise_data.xlsx
Binary file not shown.
168 changes: 168 additions & 0 deletions mcp_excel_server/mcpclient.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import asyncio
from typing import Optional
from contextlib import AsyncExitStack

# pyright: reportMissingImports=false
# pyright: reportOptionalSubscript=false

from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

from anthropic import Anthropic
from dotenv import load_dotenv

# uncomment this when running in your local environment
# ensure you have updated the .env file with the Anthropic API Key
# load_dotenv() # load environment variables from .env


class MCPClient:
def __init__(self):
# Initialize session and client objects
self.session: Optional[ClientSession] = None
self.exit_stack = AsyncExitStack()
self.anthropic = Anthropic()

# methods will go here
async def connect_to_server(self, server_script_path: str):
"""Connect to notion MCP server

Args:
server_script_path: Path to the server script (.py or .js)
"""
is_python = server_script_path.endswith(".py")
is_js = server_script_path.endswith(".js")
if not (is_python or is_js):
raise ValueError("Server script must be a .py or .js file")

command = "python" if is_python else "node"
server_params = StdioServerParameters(
command=command, args=[server_script_path], env=None
)

stdio_transport = await self.exit_stack.enter_async_context(
stdio_client(server_params)
)
self.stdio, self.write = stdio_transport
self.session = await self.exit_stack.enter_async_context(
ClientSession(self.stdio, self.write)
)

await self.session.initialize()

# List available tools
response = await self.session.list_tools()
tools = response.tools
print(
"\nConnected to Excel Analysis MCP server with tools:",
[tool.name for tool in tools],
)

async def process_query(self, query: str) -> str:
"""Process a query using Claude and available tools"""

hal_system = """You are hal3025, an expert in working on filesystem and excel sheets.
You have access to a local filesystem with read and write access.
You are very good in analysing xlsx files and you can use the available
tools with you and return the results to the user.
Just use the tools, and provide the updates the tools are giving.
Do not use external python packages for the analysis.
Use the tools that are available to you.
Do not apologize. Do not provide guidance or examples. Do not share what you cannot do.
Please do not provide the python code for the user requests.
Do not explain how something can be done."""

messages = [{"role": "user", "content": query}]

response = await self.session.list_tools()
available_tools = [
{
"name": tool.name,
"description": tool.description,
"input_schema": tool.inputSchema,
}
for tool in response.tools
]

# Initial Claude API call
response = self.anthropic.messages.create(
model="claude-3-5-haiku-20241022",
max_tokens=1000,
system=hal_system,
messages=messages,
tools=available_tools,
)

# Process response and handle tool calls
tool_results = []
final_text = []

for content in response.content:
if content.type == "text":
final_text.append(content.text)
elif content.type == "tool_use":
tool_name = content.name
tool_args = content.input

# Execute tool call
result = await self.session.call_tool(tool_name, tool_args)
tool_results.append({"call": tool_name, "result": result})
final_text.append(f"[Calling tool {tool_name} with args {tool_args}]")

# Continue conversation with tool results
if hasattr(content, "text") and content.text:
messages.append({"role": "assistant", "content": content.text})
messages.append({"role": "user", "content": result.content})

# Get next response from Claude
response = self.anthropic.messages.create(
model="claude-3-5-haiku-20241022",
system=hal_system,
max_tokens=1000,
messages=messages,
)

final_text.append(response.content[0].text)

return "\n".join(final_text)

async def chat_loop(self):
"""Run an interactive chat loop"""
print("\nMCP Client Started!")
print("Type your queries or 'quit' to exit.")

while True:
try:
query = input("\nInteract with Excel File here: ").strip()

if query.lower() == "quit":
break

response = await self.process_query(query)
print("\n" + response)

except Exception as e:
print(f"\nError: {str(e)}")

async def cleanup(self):
"""Clean up resources"""
await self.exit_stack.aclose()


async def main():
if len(sys.argv) < 2:
print("Usage: python mcpclient.py mcpserver.py")
sys.exit(1)

client = MCPClient()
try:
await client.connect_to_server(sys.argv[1])
await client.chat_loop()
finally:
await client.cleanup()


if __name__ == "__main__":
import sys

asyncio.run(main())
12 changes: 12 additions & 0 deletions mcp_excel_server/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[project]
name = "mcp-excel-server"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"anthropic>=0.50.0",
"mcp[cli]>=1.6.0",
"openpyxl>=3.1.5",
"python-dotenv>=1.1.0",
]
Loading