mirror of
https://github.com/haris-musa/excel-mcp-server.git
synced 2025-12-31 18:25:27 +08:00
feat: Add tool annotations for improved LLM tool understanding (#110)
Add readOnlyHint and destructiveHint annotations to all 25 tools to help LLMs better understand tool behavior and make safer decisions. Changes: - Added readOnlyHint: true to 6 read-only tools (validate_formula_syntax, read_data_from_excel, get_workbook_metadata, get_merged_cells, validate_excel_range, get_data_validation_info) - Added destructiveHint: true to 19 tools that modify data - Added title annotations for human-readable display This improves tool safety metadata for MCP clients. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: triepod-ai <noreply@github.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -3,6 +3,7 @@ import os
|
|||||||
from typing import Any, List, Dict, Optional
|
from typing import Any, List, Dict, Optional
|
||||||
|
|
||||||
from mcp.server.fastmcp import FastMCP
|
from mcp.server.fastmcp import FastMCP
|
||||||
|
from mcp.types import ToolAnnotations
|
||||||
|
|
||||||
# Import exceptions
|
# Import exceptions
|
||||||
from excel_mcp.exceptions import (
|
from excel_mcp.exceptions import (
|
||||||
@ -92,7 +93,12 @@ def get_excel_path(filename: str) -> str:
|
|||||||
# In SSE mode, if it's a relative path, resolve it based on EXCEL_FILES_PATH
|
# In SSE mode, if it's a relative path, resolve it based on EXCEL_FILES_PATH
|
||||||
return os.path.join(EXCEL_FILES_PATH, filename)
|
return os.path.join(EXCEL_FILES_PATH, filename)
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool(
|
||||||
|
annotations=ToolAnnotations(
|
||||||
|
title="Apply Formula",
|
||||||
|
destructiveHint=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
def apply_formula(
|
def apply_formula(
|
||||||
filepath: str,
|
filepath: str,
|
||||||
sheet_name: str,
|
sheet_name: str,
|
||||||
@ -120,7 +126,12 @@ def apply_formula(
|
|||||||
logger.error(f"Error applying formula: {e}")
|
logger.error(f"Error applying formula: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool(
|
||||||
|
annotations=ToolAnnotations(
|
||||||
|
title="Validate Formula Syntax",
|
||||||
|
readOnlyHint=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
def validate_formula_syntax(
|
def validate_formula_syntax(
|
||||||
filepath: str,
|
filepath: str,
|
||||||
sheet_name: str,
|
sheet_name: str,
|
||||||
@ -138,7 +149,12 @@ def validate_formula_syntax(
|
|||||||
logger.error(f"Error validating formula: {e}")
|
logger.error(f"Error validating formula: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool(
|
||||||
|
annotations=ToolAnnotations(
|
||||||
|
title="Format Range",
|
||||||
|
destructiveHint=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
def format_range(
|
def format_range(
|
||||||
filepath: str,
|
filepath: str,
|
||||||
sheet_name: str,
|
sheet_name: str,
|
||||||
@ -192,7 +208,12 @@ def format_range(
|
|||||||
logger.error(f"Error formatting range: {e}")
|
logger.error(f"Error formatting range: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool(
|
||||||
|
annotations=ToolAnnotations(
|
||||||
|
title="Read Data from Excel",
|
||||||
|
readOnlyHint=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
def read_data_from_excel(
|
def read_data_from_excel(
|
||||||
filepath: str,
|
filepath: str,
|
||||||
sheet_name: str,
|
sheet_name: str,
|
||||||
@ -234,7 +255,12 @@ def read_data_from_excel(
|
|||||||
logger.error(f"Error reading data: {e}")
|
logger.error(f"Error reading data: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool(
|
||||||
|
annotations=ToolAnnotations(
|
||||||
|
title="Write Data to Excel",
|
||||||
|
destructiveHint=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
def write_data_to_excel(
|
def write_data_to_excel(
|
||||||
filepath: str,
|
filepath: str,
|
||||||
sheet_name: str,
|
sheet_name: str,
|
||||||
@ -262,7 +288,12 @@ def write_data_to_excel(
|
|||||||
logger.error(f"Error writing data: {e}")
|
logger.error(f"Error writing data: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool(
|
||||||
|
annotations=ToolAnnotations(
|
||||||
|
title="Create Workbook",
|
||||||
|
destructiveHint=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
def create_workbook(filepath: str) -> str:
|
def create_workbook(filepath: str) -> str:
|
||||||
"""Create new Excel workbook."""
|
"""Create new Excel workbook."""
|
||||||
try:
|
try:
|
||||||
@ -276,7 +307,12 @@ def create_workbook(filepath: str) -> str:
|
|||||||
logger.error(f"Error creating workbook: {e}")
|
logger.error(f"Error creating workbook: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool(
|
||||||
|
annotations=ToolAnnotations(
|
||||||
|
title="Create Worksheet",
|
||||||
|
destructiveHint=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
def create_worksheet(filepath: str, sheet_name: str) -> str:
|
def create_worksheet(filepath: str, sheet_name: str) -> str:
|
||||||
"""Create new worksheet in workbook."""
|
"""Create new worksheet in workbook."""
|
||||||
try:
|
try:
|
||||||
@ -290,7 +326,12 @@ def create_worksheet(filepath: str, sheet_name: str) -> str:
|
|||||||
logger.error(f"Error creating worksheet: {e}")
|
logger.error(f"Error creating worksheet: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool(
|
||||||
|
annotations=ToolAnnotations(
|
||||||
|
title="Create Chart",
|
||||||
|
destructiveHint=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
def create_chart(
|
def create_chart(
|
||||||
filepath: str,
|
filepath: str,
|
||||||
sheet_name: str,
|
sheet_name: str,
|
||||||
@ -321,7 +362,12 @@ def create_chart(
|
|||||||
logger.error(f"Error creating chart: {e}")
|
logger.error(f"Error creating chart: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool(
|
||||||
|
annotations=ToolAnnotations(
|
||||||
|
title="Create Pivot Table",
|
||||||
|
destructiveHint=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
def create_pivot_table(
|
def create_pivot_table(
|
||||||
filepath: str,
|
filepath: str,
|
||||||
sheet_name: str,
|
sheet_name: str,
|
||||||
@ -350,7 +396,12 @@ def create_pivot_table(
|
|||||||
logger.error(f"Error creating pivot table: {e}")
|
logger.error(f"Error creating pivot table: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool(
|
||||||
|
annotations=ToolAnnotations(
|
||||||
|
title="Create Table",
|
||||||
|
destructiveHint=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
def create_table(
|
def create_table(
|
||||||
filepath: str,
|
filepath: str,
|
||||||
sheet_name: str,
|
sheet_name: str,
|
||||||
@ -375,7 +426,12 @@ def create_table(
|
|||||||
logger.error(f"Error creating table: {e}")
|
logger.error(f"Error creating table: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool(
|
||||||
|
annotations=ToolAnnotations(
|
||||||
|
title="Copy Worksheet",
|
||||||
|
destructiveHint=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
def copy_worksheet(
|
def copy_worksheet(
|
||||||
filepath: str,
|
filepath: str,
|
||||||
source_sheet: str,
|
source_sheet: str,
|
||||||
@ -392,7 +448,12 @@ def copy_worksheet(
|
|||||||
logger.error(f"Error copying worksheet: {e}")
|
logger.error(f"Error copying worksheet: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool(
|
||||||
|
annotations=ToolAnnotations(
|
||||||
|
title="Delete Worksheet",
|
||||||
|
destructiveHint=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
def delete_worksheet(
|
def delete_worksheet(
|
||||||
filepath: str,
|
filepath: str,
|
||||||
sheet_name: str
|
sheet_name: str
|
||||||
@ -408,7 +469,12 @@ def delete_worksheet(
|
|||||||
logger.error(f"Error deleting worksheet: {e}")
|
logger.error(f"Error deleting worksheet: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool(
|
||||||
|
annotations=ToolAnnotations(
|
||||||
|
title="Rename Worksheet",
|
||||||
|
destructiveHint=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
def rename_worksheet(
|
def rename_worksheet(
|
||||||
filepath: str,
|
filepath: str,
|
||||||
old_name: str,
|
old_name: str,
|
||||||
@ -425,7 +491,12 @@ def rename_worksheet(
|
|||||||
logger.error(f"Error renaming worksheet: {e}")
|
logger.error(f"Error renaming worksheet: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool(
|
||||||
|
annotations=ToolAnnotations(
|
||||||
|
title="Get Workbook Metadata",
|
||||||
|
readOnlyHint=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
def get_workbook_metadata(
|
def get_workbook_metadata(
|
||||||
filepath: str,
|
filepath: str,
|
||||||
include_ranges: bool = False
|
include_ranges: bool = False
|
||||||
@ -441,7 +512,12 @@ def get_workbook_metadata(
|
|||||||
logger.error(f"Error getting workbook metadata: {e}")
|
logger.error(f"Error getting workbook metadata: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool(
|
||||||
|
annotations=ToolAnnotations(
|
||||||
|
title="Merge Cells",
|
||||||
|
destructiveHint=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
def merge_cells(filepath: str, sheet_name: str, start_cell: str, end_cell: str) -> str:
|
def merge_cells(filepath: str, sheet_name: str, start_cell: str, end_cell: str) -> str:
|
||||||
"""Merge a range of cells."""
|
"""Merge a range of cells."""
|
||||||
try:
|
try:
|
||||||
@ -454,7 +530,12 @@ def merge_cells(filepath: str, sheet_name: str, start_cell: str, end_cell: str)
|
|||||||
logger.error(f"Error merging cells: {e}")
|
logger.error(f"Error merging cells: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool(
|
||||||
|
annotations=ToolAnnotations(
|
||||||
|
title="Unmerge Cells",
|
||||||
|
destructiveHint=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
def unmerge_cells(filepath: str, sheet_name: str, start_cell: str, end_cell: str) -> str:
|
def unmerge_cells(filepath: str, sheet_name: str, start_cell: str, end_cell: str) -> str:
|
||||||
"""Unmerge a range of cells."""
|
"""Unmerge a range of cells."""
|
||||||
try:
|
try:
|
||||||
@ -467,7 +548,12 @@ def unmerge_cells(filepath: str, sheet_name: str, start_cell: str, end_cell: str
|
|||||||
logger.error(f"Error unmerging cells: {e}")
|
logger.error(f"Error unmerging cells: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool(
|
||||||
|
annotations=ToolAnnotations(
|
||||||
|
title="Get Merged Cells",
|
||||||
|
readOnlyHint=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
def get_merged_cells(filepath: str, sheet_name: str) -> str:
|
def get_merged_cells(filepath: str, sheet_name: str) -> str:
|
||||||
"""Get merged cells in a worksheet."""
|
"""Get merged cells in a worksheet."""
|
||||||
try:
|
try:
|
||||||
@ -479,7 +565,12 @@ def get_merged_cells(filepath: str, sheet_name: str) -> str:
|
|||||||
logger.error(f"Error getting merged cells: {e}")
|
logger.error(f"Error getting merged cells: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool(
|
||||||
|
annotations=ToolAnnotations(
|
||||||
|
title="Copy Range",
|
||||||
|
destructiveHint=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
def copy_range(
|
def copy_range(
|
||||||
filepath: str,
|
filepath: str,
|
||||||
sheet_name: str,
|
sheet_name: str,
|
||||||
@ -507,7 +598,12 @@ def copy_range(
|
|||||||
logger.error(f"Error copying range: {e}")
|
logger.error(f"Error copying range: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool(
|
||||||
|
annotations=ToolAnnotations(
|
||||||
|
title="Delete Range",
|
||||||
|
destructiveHint=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
def delete_range(
|
def delete_range(
|
||||||
filepath: str,
|
filepath: str,
|
||||||
sheet_name: str,
|
sheet_name: str,
|
||||||
@ -533,7 +629,12 @@ def delete_range(
|
|||||||
logger.error(f"Error deleting range: {e}")
|
logger.error(f"Error deleting range: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool(
|
||||||
|
annotations=ToolAnnotations(
|
||||||
|
title="Validate Excel Range",
|
||||||
|
readOnlyHint=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
def validate_excel_range(
|
def validate_excel_range(
|
||||||
filepath: str,
|
filepath: str,
|
||||||
sheet_name: str,
|
sheet_name: str,
|
||||||
@ -552,7 +653,12 @@ def validate_excel_range(
|
|||||||
logger.error(f"Error validating range: {e}")
|
logger.error(f"Error validating range: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool(
|
||||||
|
annotations=ToolAnnotations(
|
||||||
|
title="Get Data Validation Info",
|
||||||
|
readOnlyHint=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
def get_data_validation_info(
|
def get_data_validation_info(
|
||||||
filepath: str,
|
filepath: str,
|
||||||
sheet_name: str
|
sheet_name: str
|
||||||
@ -596,7 +702,12 @@ def get_data_validation_info(
|
|||||||
logger.error(f"Error getting validation info: {e}")
|
logger.error(f"Error getting validation info: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool(
|
||||||
|
annotations=ToolAnnotations(
|
||||||
|
title="Insert Rows",
|
||||||
|
destructiveHint=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
def insert_rows(
|
def insert_rows(
|
||||||
filepath: str,
|
filepath: str,
|
||||||
sheet_name: str,
|
sheet_name: str,
|
||||||
@ -614,7 +725,12 @@ def insert_rows(
|
|||||||
logger.error(f"Error inserting rows: {e}")
|
logger.error(f"Error inserting rows: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool(
|
||||||
|
annotations=ToolAnnotations(
|
||||||
|
title="Insert Columns",
|
||||||
|
destructiveHint=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
def insert_columns(
|
def insert_columns(
|
||||||
filepath: str,
|
filepath: str,
|
||||||
sheet_name: str,
|
sheet_name: str,
|
||||||
@ -632,7 +748,12 @@ def insert_columns(
|
|||||||
logger.error(f"Error inserting columns: {e}")
|
logger.error(f"Error inserting columns: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool(
|
||||||
|
annotations=ToolAnnotations(
|
||||||
|
title="Delete Rows",
|
||||||
|
destructiveHint=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
def delete_sheet_rows(
|
def delete_sheet_rows(
|
||||||
filepath: str,
|
filepath: str,
|
||||||
sheet_name: str,
|
sheet_name: str,
|
||||||
@ -650,7 +771,12 @@ def delete_sheet_rows(
|
|||||||
logger.error(f"Error deleting rows: {e}")
|
logger.error(f"Error deleting rows: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool(
|
||||||
|
annotations=ToolAnnotations(
|
||||||
|
title="Delete Columns",
|
||||||
|
destructiveHint=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
def delete_sheet_columns(
|
def delete_sheet_columns(
|
||||||
filepath: str,
|
filepath: str,
|
||||||
sheet_name: str,
|
sheet_name: str,
|
||||||
|
|||||||
Reference in New Issue
Block a user