ローカル環境でMCPサーバーを構築 – OllamaとMCPHostの活用

MCP

以前、OllamaとMCPHostを使用してローカル環境にMCP環境を構築しました。
今回はこの環境に自作したMCPサーバーを追加してみようと思います。

ファイルの準備

MCPサーバーとして動作するプログラムを、AIに作成してもらいます。
MCPについての詳細をAIは知らないため、これを補うためのファイルを準備します。

必要なファイルは、MCPの仕様が書かれている「llms-full.txt」と、構築する言語に応じたSDKの「README.md」になります。
llms-full.txt」は https://modelcontextprotocol.io/llms-full.txt
README.md」は https://modelcontextprotocol.io/docs/sdk から構築したい言語を選択し、その先にあるファイルをダウンロードします。

今回はPythonで構築するので https://github.com/modelcontextprotocol/python-sdk/blob/main/README.md をダウンロードしておきます。

プログラム作成

次にプログラムを作成するためのプロンプトを作成します。

今回は、指定したローカルのExcelファイルを読み込んでくれるMCPサーバーが欲しいので、その旨をプロンプトに記載します。
一緒に、先程ダウンロードした「llms-full.txt」と「README.md」を添付します。

プロンプト
# 目的
入力されたExcelの全シートを読み取り、テキストに変換するMCPサーバーを作成したいです。
コードを作成して、設定方法・使用方法も教えて下さい。
添付したllms-full.txtは、MCPに関する全体仕様書です。
ToolsのMCPサーバーを作成してください。
# 使用する言語
Pythonを使ってMCPサーバーを作成してください。
MCPのSDKの仕様書READMEも添付したので、参照しながら作成してください。
# 機能の詳細
名前は、excel-to-text-mcpです。
引数でExcelファイルのパスを指定します。
Excelファイルの読込には、Pythonライブラリのopenpyxlを使用してください。
Excel内の全てのシートを読込対象としてください。
シート内の全てのセルを読込対象としてください。
一行全てが何も無いセルの場合は読み飛ばしてください。
## 補足
jsonファイルにはコメントは入れないでください。

プロンプトを入力すると、下記のようなプログラムを作成してくれました。

Python
# server.py
from __future__ import annotations
from typing import Any, Dict, List, Tuple
from mcp.server.fastmcp import FastMCP, Context
from mcp.server.session import ServerSession
from openpyxl import load_workbook
import os

mcp = FastMCP("excel-to-text-mcp")

def _row_is_empty(values: List[Any]) -> bool:
    for v in values:
        if v is None:
            continue
        if str(v).strip() != "":
            return False
    return True

def _sheet_to_lines(ws) -> List[str]:
    lines: List[str] = []
    for row in ws.iter_rows(values_only=True):
        row_vals = list(row)
        if _row_is_empty(row_vals):
            continue
        lines.append("\t".join("" if v is None else str(v) for v in row_vals))
    return lines

@mcp.tool()
def read_excel(file_path: str, ctx: Context[ServerSession, None] | None = None) -> Tuple[str, Dict[str, Any]]:
    if not file_path or not isinstance(file_path, str):
        raise ValueError("file_path is required")
    if not os.path.exists(file_path):
        raise FileNotFoundError(f"file not found: {file_path}")
    try:
        wb = load_workbook(filename=file_path, data_only=True, read_only=True)
    except InvalidFileException as e:
        raise ValueError(f"invalid excel file: {e}") from e

    sheets: List[Dict[str, Any]] = []
    text_blocks: List[str] = []

    for ws in wb.worksheets:
        lines = _sheet_to_lines(ws)
        sheet_header = f"# Sheet: {ws.title}"
        body = "\n".join(lines)
        text_blocks.append(f"{sheet_header}\n{body}")
        sheets.append({"name": ws.title, "lines": lines})

    combined_text = "\n\n---\n\n".join(text_blocks)
    structured = {"file_path": file_path, "sheet_count": len(sheets), "sheets": sheets}
    return combined_text, structured

def main() -> None:
    # Streamable HTTPで起動(デフォルトで0.0.0.0:8000)
    mcp.run(transport="streamable-http")

if __name__ == "__main__":
    main()

このプログラムを「server.py」として保存します。
(省略していますが、HTTP起動を追加してもらったりと何度か追加のプロンプトを入力しています。)

次にMCPサーバー用のプロジェクトフォルダを作り、セットアップを行います。

ターミナル
uv init excel-to-text-mcp
cd excel-to-text-mcp
uv add "mcp[cli]" openpyxl

このフォルダ内に、保存しておいた「server.py」を格納します。

作成したMCPサーバーのプログラムが正常に動作するかは、MCP Inspectorで動作確認ができます。

Bash
uv run mcp dev server.py

一旦、これでMCPサーバー側の作業は終了です。

MCPHostの設定

続いて先程作成したMCPサーバーを、MCPHostで参照できるように設定を追加します。
MCPHostディレクトリの「.mcphost.json」に、作成した「excel-to-text-mcp」を追加しています。

今回は作成したMCPサーバーを起動しておき、HTTPで通信する形式です。

.mcphost.json
{
    "mcpServers": {
        "excel-to-text-mcp": {
          "type": "remote",
          "url": "http://localhost:8000/mcp"
        }
    }
}

以上で設定作業は終了です。

実行

それでは実際に起動して、動作確認していきます。
先ずはMCPサーバーを起動するため、MCPサーバーのプロジェクトフォルダで下記を実行します。

ターミナル
python server.py

下記のように起動され、待受状態となります。

次にMCPHostを起動します。

ターミナル
mcphost -m ollama:gpt-oss

これでようやく使用できるようになりました。
ローカルの「詳細設計書_品目メンテナンス.xlsx」というExcelファイルが読み込めるか試してみます。

プロンプト
"E:\Project\MCPHost-Excel\詳細設計書_品目メンテナンス.xlsx"を読み込んで内容を教えて下さい。

まとめ

今回はMCPサーバーとして、Excelを読み込むプログラムを作成してみました。
構築も簡潔で、簡単にLLMに機能を追加することができました。

ただ、プロンプトによってはMCPサーバーへとつながらず、正しい回答が得られないことがたまにあります。
使用するMCPサーバーの名前を指定すると使用してくれたりするので、プロンプトには注意が必要そうです。

また、今回は別サービスとしてHTTP経由でMCPサーバーと通信しましたが、直接プログラムを実行してくれた方が使い勝手は良さそうです。

今後、このあたりを中心に調べてみようと思います。

コメント

タイトルとURLをコピーしました