Skip to main content
Glama

Excel MCP Server

MIT License
2,793
315
  • Linux
  • Apple
package excel import ( "fmt" "github.com/xuri/excelize/v2" ) // PagingStrategy はページング範囲の計算戦略を定義するインターフェース type PagingStrategy interface { // CalculatePagingRanges は利用可能なページング範囲のリストを返す CalculatePagingRanges() []string // ValidatePagingRange は指定された範囲が有効かどうかを検証する ValidatePagingRange(rangeStr string) error } // ExcelizeFixedSizePagingStrategy は固定サイズでページング範囲を計算する戦略 type ExcelizeFixedSizePagingStrategy struct { pageSize int worksheet *ExcelizeWorksheet dimension string } // NewExcelizeFixedSizePagingStrategy は新しいFixedSizePagingStrategyインスタンスを生成する func NewExcelizeFixedSizePagingStrategy(pageSize int, worksheet *ExcelizeWorksheet) (*ExcelizeFixedSizePagingStrategy, error) { if pageSize <= 0 { pageSize = 5000 // デフォルト値 } // シートの次元情報を取得 dimension, err := worksheet.GetDimention() if err != nil { return nil, err } return &ExcelizeFixedSizePagingStrategy{ pageSize: pageSize, worksheet: worksheet, dimension: dimension, }, nil } // CalculatePagingRanges は固定サイズに基づいてページング範囲のリストを生成する func (s *ExcelizeFixedSizePagingStrategy) CalculatePagingRanges() []string { startCol, startRow, endCol, endRow, err := ParseRange(s.dimension) if err != nil { return []string{} } totalCols := endCol - startCol + 1 cellsPerPage := s.pageSize // 1ページあたりの行数を計算 rowsPerPage := cellsPerPage / totalCols if rowsPerPage < 1 { rowsPerPage = 1 } var ranges []string currentRow := startRow for currentRow <= endRow { pageEndRow := currentRow + rowsPerPage - 1 if pageEndRow > endRow { pageEndRow = endRow } startRange, _ := excelize.CoordinatesToCellName(startCol, currentRow) endRange, _ := excelize.CoordinatesToCellName(endCol, pageEndRow) ranges = append(ranges, fmt.Sprintf("%s:%s", startRange, endRange)) currentRow = pageEndRow + 1 } return ranges } // ValidatePagingRange は指定された範囲が有効かどうかを検証する func (s *ExcelizeFixedSizePagingStrategy) ValidatePagingRange(rangeStr string) error { startCol, startRow, endCol, endRow, err := ParseRange(rangeStr) if err != nil { return fmt.Errorf("invalid range format: %v", err) } dimStartCol, dimStartRow, dimEndCol, dimEndRow, err := ParseRange(s.dimension) if err != nil { return fmt.Errorf("invalid dimension format: %v", err) } // 範囲がシートの次元内に収まっているか確認 if startCol < dimStartCol || startRow < dimStartRow || endCol > dimEndCol || endRow > dimEndRow { return fmt.Errorf("range %s is outside sheet dimensions %s", rangeStr, s.dimension) } // セル数が pageSize を超えていないか確認 cellCount := (endRow - startRow + 1) * (endCol - startCol + 1) if cellCount > s.pageSize { return fmt.Errorf("range contains %d cells, exceeding page size of %d", cellCount, s.pageSize) } return nil } func NewOlePagingStrategy(pageSize int, worksheet *OleWorksheet) (PagingStrategy, error) { if worksheet == nil { return nil, fmt.Errorf("worksheet is nil") } printAreaPagingStrategy, err := NewPrintAreaPagingStrategy(worksheet) if err != nil { return nil, err } printArea, err := printAreaPagingStrategy.getPrintArea() if err != nil { return nil, err } if printArea == "" { return NewGoxcelFixedSizePagingStrategy(pageSize, worksheet) } else { return printAreaPagingStrategy, nil } } // OleFixedSizePagingStrategy は Goxcel を使用した固定サイズでページング範囲を計算する戦略 type OleFixedSizePagingStrategy struct { pageSize int worksheet *OleWorksheet dimension string } // NewGoxcelFixedSizePagingStrategy は新しい GoxcelFixedSizePagingStrategy インスタンスを生成する func NewGoxcelFixedSizePagingStrategy(pageSize int, worksheet *OleWorksheet) (*OleFixedSizePagingStrategy, error) { if pageSize <= 0 { pageSize = 5000 // デフォルト値 } if worksheet == nil { return nil, fmt.Errorf("worksheet is nil") } // UsedRange を使用して使用範囲を取得 dimention, err := worksheet.GetDimention() if err != nil { return nil, fmt.Errorf("failed to get dimention: %w", err) } return &OleFixedSizePagingStrategy{ pageSize: pageSize, worksheet: worksheet, dimension: dimention, }, nil } // CalculatePagingRanges は固定サイズに基づいてページング範囲のリストを生成する func (s *OleFixedSizePagingStrategy) CalculatePagingRanges() []string { startCol, startRow, endCol, endRow, err := ParseRange(s.dimension) if err != nil { return []string{} } totalCols := endCol - startCol + 1 cellsPerPage := s.pageSize // 1ページあたりの行数を計算 rowsPerPage := cellsPerPage / totalCols if rowsPerPage < 1 { rowsPerPage = 1 } var ranges []string currentRow := startRow for currentRow <= endRow { pageEndRow := currentRow + rowsPerPage - 1 if pageEndRow > endRow { pageEndRow = endRow } startRange, _ := excelize.CoordinatesToCellName(startCol, currentRow) endRange, _ := excelize.CoordinatesToCellName(endCol, pageEndRow) ranges = append(ranges, fmt.Sprintf("%s:%s", startRange, endRange)) currentRow = pageEndRow + 1 } return ranges } // ValidatePagingRange は指定された範囲が有効かどうかを検証する func (s *OleFixedSizePagingStrategy) ValidatePagingRange(rangeStr string) error { startCol, startRow, endCol, endRow, err := ParseRange(rangeStr) if err != nil { return fmt.Errorf("invalid range format: %v", err) } dimStartCol, dimStartRow, dimEndCol, dimEndRow, err := ParseRange(s.dimension) if err != nil { return fmt.Errorf("invalid dimension format: %v", err) } // 範囲がシートの次元内に収まっているか確認 if startCol < dimStartCol || startRow < dimStartRow || endCol > dimEndCol || endRow > dimEndRow { return fmt.Errorf("range %s is outside sheet dimensions %s", rangeStr, s.dimension) } // セル数が pageSize を超えていないか確認 cellCount := (endRow - startRow + 1) * (endCol - startCol + 1) if cellCount > s.pageSize { return fmt.Errorf("range contains %d cells, exceeding page size of %d", cellCount, s.pageSize) } return nil } // PrintAreaPagingStrategy は印刷範囲とページ区切りに基づいてページング範囲を計算する戦略 type PrintAreaPagingStrategy struct { worksheet *OleWorksheet } // NewPrintAreaPagingStrategy は新しいPrintAreaPagingStrategyインスタンスを生成する func NewPrintAreaPagingStrategy(worksheet *OleWorksheet) (*PrintAreaPagingStrategy, error) { if worksheet == nil { return nil, fmt.Errorf("worksheet is nil") } return &PrintAreaPagingStrategy{ worksheet: worksheet, }, nil } // getPrintArea は印刷範囲を取得する func (s *PrintAreaPagingStrategy) getPrintArea() (string, error) { return s.worksheet.PrintArea() } // getHPageBreaksPositions はページ区切りの位置情報を取得する func (s *PrintAreaPagingStrategy) getHPageBreaksPositions() ([]int, error) { pageBreaks, err := s.worksheet.HPageBreaks() if err != nil { return nil, fmt.Errorf("failed to get HPageBreaks: %w", err) } return pageBreaks, nil } // calculateRangesFromBreaks は印刷範囲とページ区切りから範囲のリストを生成する func (s *PrintAreaPagingStrategy) calculateRangesFromBreaks(printArea string, breaks []int) []string { if printArea == "" { return []string{} } startCol, startRow, endCol, endRow, err := ParseRange(printArea) if err != nil { return []string{} } ranges := make([]string, 0) currentRow := startRow // ページ区切りがない場合は印刷範囲全体を1つの範囲として扱う if len(breaks) == 0 { startRange, _ := excelize.CoordinatesToCellName(startCol, startRow) endRange, _ := excelize.CoordinatesToCellName(endCol, endRow) ranges = append(ranges, fmt.Sprintf("%s:%s", startRange, endRange)) return ranges } // ページ区切りで範囲を分割 for _, breakRow := range breaks { if breakRow <= startRow || breakRow > endRow { continue } startRange, _ := excelize.CoordinatesToCellName(startCol, currentRow) endRange, _ := excelize.CoordinatesToCellName(endCol, breakRow-1) ranges = append(ranges, fmt.Sprintf("%s:%s", startRange, endRange)) currentRow = breakRow } // 最後の範囲を追加 if currentRow <= endRow { startRange, _ := excelize.CoordinatesToCellName(startCol, currentRow) endRange, _ := excelize.CoordinatesToCellName(endCol, endRow) ranges = append(ranges, fmt.Sprintf("%s:%s", startRange, endRange)) } return ranges } // CalculatePagingRanges は印刷範囲とページ区切りに基づいてページング範囲のリストを生成する func (s *PrintAreaPagingStrategy) CalculatePagingRanges() []string { printArea, err := s.getPrintArea() if err != nil { return []string{} } breaks, err := s.getHPageBreaksPositions() if err != nil { return []string{} } return s.calculateRangesFromBreaks(printArea, breaks) } // ValidatePagingRange は指定された範囲が印刷範囲内に収まっているか検証する func (s *PrintAreaPagingStrategy) ValidatePagingRange(rangeStr string) error { printArea, err := s.getPrintArea() if err != nil { return fmt.Errorf("failed to get print area: %w", err) } if printArea == "" { return fmt.Errorf("print area is not set") } // 印刷範囲を解析 paStartCol, paStartRow, paEndCol, paEndRow, err := ParseRange(printArea) if err != nil { return fmt.Errorf("invalid print area format: %w", err) } // 検証対象の範囲を解析 startCol, startRow, endCol, endRow, err := ParseRange(rangeStr) if err != nil { return fmt.Errorf("invalid range format: %w", err) } // 範囲が印刷範囲内に収まっているか確認 if startCol < paStartCol || startRow < paStartRow || endCol > paEndCol || endRow > paEndRow { return fmt.Errorf("range %s is outside print area %s", rangeStr, printArea) } // ページ区切り位置と一致するか確認 ranges := s.CalculatePagingRanges() for _, r := range ranges { if r == rangeStr { return nil } } return fmt.Errorf("range %s does not match any page break boundary", rangeStr) } // PagingRangeService はページング処理を提供するサービス type PagingRangeService struct { strategy PagingStrategy } // NewPagingRangeService は新しいPagingRangeServiceインスタンスを生成する func NewPagingRangeService(strategy PagingStrategy) *PagingRangeService { return &PagingRangeService{strategy: strategy} } // GetPagingRanges は利用可能なページング範囲のリストを返す func (s *PagingRangeService) GetPagingRanges() []string { return s.strategy.CalculatePagingRanges() } // ValidatePagingRange は指定された範囲が有効かどうかを検証する func (s *PagingRangeService) ValidatePagingRange(rangeStr string) error { return s.strategy.ValidatePagingRange(rangeStr) } // FilterRemainingPagingRanges は未読の範囲のリストを返す func (s *PagingRangeService) FilterRemainingPagingRanges(allRanges []string, knownRanges []string) []string { if len(knownRanges) == 0 { return allRanges } // knownRanges をマップに変換 knownMap := make(map[string]bool) for _, r := range knownRanges { knownMap[r] = true } // 未読の範囲を抽出 remaining := make([]string, 0) for _, r := range allRanges { if !knownMap[r] { remaining = append(remaining, r) } } return remaining } // FindNextRange returns the next range in the sequence after the current range func (s *PagingRangeService) FindNextRange(allRanges []string, currentRange string) string { for i, r := range allRanges { if r == currentRange && i+1 < len(allRanges) { return allRanges[i+1] } } return "" }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/negokaz/excel-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server