🐷

相対パスを使用したFileSystem MCP Serverパッケージを作成・公開してみた

に公開

1. 概要

Model Context Protocol (MCP) 用のセキュアなファイルシステムサーバーを開発し、npmパッケージとして公開しました。自分がファイルシステムMCPを使う際には、ワークディレクトリを指定することが多く、エージェントコードと同じパスでないため、指定パスが間違ってしまうことが多々ありました。エージェントがカレントディレクトリとの違いを意識せず、相対パスで実行できるようにしました。

この記事では、パッケージの紹介と使い方、そしてnpmパッケージの公開手順を簡潔にまとめます。

2. 公開したパッケージの紹介 & 使い方

パッケージ概要

パッケージ名: @m_sea_bass/relpath-filesystem-mcp
機能: セキュアなMCPファイルシステムサーバー
リンク: @m_sea_bass/relpath-filesystem-mcp

主な特徴

  • 🔒 セキュリティ強化: パストラバーサル攻撃防止
  • 📁 相対パス対応: 指定ディレクトリからの相対パス操作
  • 🛠️ 豊富なツール: 11種類のファイル・ディレクトリ操作
  • 🚫 アクセス制御: ベースディレクトリ外へのアクセスを防止

使い方

コマンドライン

npx @m_sea_bass/relpath-filesystem-mcp /path/to/directory

Pythonクライアントの例

以下のように、サーバーパラメータを設定して、MCPクライアントを作成します。

server_params = StdioServerParameters(
    command="npx",              # npxを使用
    args=["@m_sea_bass/relpath-filesystem-mcp", "path-to-your-directory"]  # パッケージ名とベースディレクトリ
)
サンプルコード
#!/usr/bin/env python3
"""
包括的なMCPクライアントテストスイート

このテストスイートは、MCPファイルシステムサーバーのすべての機能をテストします。
- サーバーに接続
- すべてのツールを順次テスト
- エラーハンドリングとログ出力

使用方法:
    python simple_mcp_client.py
"""

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


async def test_tool(session, tool_name, arguments, description):
    """
    個別ツールをテストするヘルパー関数
    """
    print(f"\n🧪 テスト実行: {tool_name}")
    print(f"📋 説明: {description}")
    print(f"📤 引数: {arguments}")
    
    try:
        result = await session.call_tool(tool_name, arguments=arguments)
        if not result.isError:
            print(f"✅ {tool_name} 成功")
            print(f"📄 結果: {result.content[0].text[:200]}{'...' if len(result.content[0].text) > 200 else ''}")
            return True
        else:
            print(f"❌ {tool_name} 失敗: {result.content[0].text}")
            return False
    except Exception as e:
        print(f"❌ {tool_name} エラー: {e}")
        return False


async def run_comprehensive_mcp_test():
    """
    包括的なMCPクライアントテストの実行
    """
    print("🚀 包括的MCPクライアントテスト開始")
    print("=" * 60)
    
    # サーバーパラメータの設定
    server_params = StdioServerParameters(
        command="npx",
        args=["@m_sea_bass/relpath-filesystem-mcp", "./work"]
    )
    
    test_results = {}
    
    try:
        # サーバーとの接続を確立
        async with stdio_client(server_params) as (read, write):
            async with ClientSession(read, write) as session:
                
                # 1. 初期化
                print("🔌 サーバーに接続中...")
                init_result = await session.initialize()
                print(f"✅ 接続成功!")
                
                # 2. 利用可能なツールを一覧表示
                print("\n📋 利用可能なツール:")
                tools = await session.list_tools()
                for tool in tools.tools:
                    print(f"  🔧 {tool.name}: {tool.description[:80]}...")
                
                print(f"\n📊 合計 {len(tools.tools)} 個のツールをテストします")
                print("=" * 60)
                
                # 3. 各ツールのテスト実行
                
                # 3.1 list_allowed_directory - 許可されたディレクトリの確認
                test_results["list_allowed_directory"] = await test_tool(
                    session, "list_allowed_directory", {},
                    "許可されたベースディレクトリの確認"
                )
                
                # 3.2 list_directory - 現在のディレクトリ一覧
                test_results["list_directory"] = await test_tool(
                    session, "list_directory", {"path": "."},
                    "現在のディレクトリ一覧表示"
                )
                
                # 3.3 create_directory - テスト用ディレクトリ作成
                test_results["create_directory"] = await test_tool(
                    session, "create_directory", {"path": "test_suite"},
                    "テスト用ディレクトリの作成"
                )
                
                # 3.4 write_file - ファイル作成
                test_results["write_file"] = await test_tool(
                    session, "write_file", {
                        "path": "test_file.txt",
                        "content": "Hello from Comprehensive MCP Test!\nこんにちは、世界!"
                    },
                    "テストファイルの作成"
                )
                
                # 3.5 read_file - ファイル読み込み
                test_results["read_file"] = await test_tool(
                    session, "read_file", {"path": "test_file.txt"},
                    "作成したファイルの読み込み"
                )
                
                # 3.6 write_file - 追加ファイル作成(複数ファイル読み込みテスト用)
                await test_tool(
                    session, "write_file", {
                        "path": "test_suite/test_file2.txt",
                        "content": "Second test file content.\nMultiline content for testing."
                    },
                    "2番目のテストファイル作成"
                )
                
                # 3.7 read_multiple_files - 複数ファイル読み込み
                test_results["read_multiple_files"] = await test_tool(
                    session, "read_multiple_files", {
                        "paths": ["test_file.txt", "test_suite/test_file2.txt"]
                    },
                    "複数ファイルの同時読み込み"
                )
                
                # 3.8 get_file_info - ファイル情報取得
                test_results["get_file_info"] = await test_tool(
                    session, "get_file_info", {"path": "test_file.txt"},
                    "ファイルメタデータの取得"
                )
                
                # 3.9 edit_file - ファイル編集(dry run)
                test_results["edit_file_dry"] = await test_tool(
                    session, "edit_file", {
                        "path": "test_file.txt",
                        "edits": [{
                            "oldText": "Hello from Comprehensive MCP Test!",
                            "newText": "Hello from EDITED MCP Test!"
                        }],
                        "dryRun": True
                    },
                    "ファイル編集のプレビュー(dry run)"
                )
                
                # 3.10 edit_file - 実際のファイル編集
                test_results["edit_file"] = await test_tool(
                    session, "edit_file", {
                        "path": "test_file.txt",
                        "edits": [{
                            "oldText": "Hello from Comprehensive MCP Test!",
                            "newText": "Hello from EDITED MCP Test!"
                        }]
                    },
                    "実際のファイル編集"
                )
                
                # 3.11 directory_tree - ディレクトリツリー表示
                test_results["directory_tree"] = await test_tool(
                    session, "directory_tree", {"path": "test_suite"},
                    "ディレクトリツリーのJSON表示"
                )
                
                # 3.12 search_files - ファイル検索
                test_results["search_files"] = await test_tool(
                    session, "search_files", {
                        "path": ".",
                        "pattern": "test_file",
                        "excludePatterns": []
                    },
                    "ファイル名パターン検索"
                )
                
                # 3.13 move_file - ファイル移動/リネーム
                test_results["move_file"] = await test_tool(
                    session, "move_file", {
                        "source": "test_suite/test_file2.txt",
                        "destination": "test_suite/renamed_test_file.txt"
                    },
                    "ファイルのリネーム/移動"
                )
                
                # 3.14 最終確認 - 変更後のディレクトリ確認
                await test_tool(
                    session, "list_directory", {"path": "test_suite"},
                    "最終的なディレクトリ構造の確認"
                )
                
                # 3.15 セキュリティテスト - パストラバーサル攻撃防御テスト
                print("\n🔒 セキュリティテスト: パストラバーサル攻撃防御")
                security_test_result = await test_tool(
                    session, "create_directory", {"path": "../security_test"},
                    "パストラバーサル攻撃防御テスト(失敗が期待される)"
                )
                test_results["security_test"] = not security_test_result  # 失敗が期待される
                if not security_test_result:
                    print("✅ セキュリティテスト成功: パストラバーサル攻撃が正しく防がれました")
                
                # 4. テスト結果サマリー
                print("\n" + "=" * 60)
                print("📊 テスト結果サマリー")
                print("=" * 60)
                
                successful_tests = sum(1 for result in test_results.values() if result)
                total_tests = len(test_results)
                
                print(f"✅ 成功: {successful_tests}/{total_tests} テスト")
                print(f"❌ 失敗: {total_tests - successful_tests}/{total_tests} テスト")
                
                print("\n📋 詳細結果:")
                for tool_name, result in test_results.items():
                    status = "✅ PASS" if result else "❌ FAIL"
                    print(f"  {status} {tool_name}")
                
                if successful_tests == total_tests:
                    print("\n🎉 すべてのテストが成功しました!")
                else:
                    print(f"\n⚠️ {total_tests - successful_tests} 個のテストが失敗しました。")
                
                print("=" * 60)
                print("🏁 包括的MCPクライアントテスト完了")
    
    except Exception as e:
        print(f"❌ 重大なエラーが発生しました: {e}")
        import traceback
        print(f"🔍 詳細なエラー情報:")
        traceback.print_exc()


if __name__ == "__main__":
    # 非同期関数を実行
    asyncio.run(run_comprehensive_mcp_test())

利用可能なツール

  • ファイル読み書き (read_file, write_file)
  • ディレクトリ操作 (create_directory, list_directory)
  • ファイル検索 (search_files)
  • ファイル移動 (move_file)
  • ファイル情報取得 (get_file_info)
  • など11種類

3. npmパッケージを公開する手順

前提条件

  • Node.js環境
  • npmアカウント: npmjs.comで無料作成可能

公開手順

1. ファイル構成の確認

以下のファイルが正しく配置されていることを確認してください:

your-project/
├── dist/
│   └── index.js          # MCPサーバーの本体
├── package.json      # パッケージ設定
└── README.md         # パッケージの説明

1. package.jsonの準備

{
  "name": "@your-username/package-name",
  "version": "1.0.0",
  "main": "dist/index.js",
  "bin": {
    "your-command": "./dist/index.js"
  },
  "type": "module"
}

2. npmにログイン

npm login

3. パッケージ公開

# 初回公開
npm publish --access public

# アップデート時
npm version patch  # or minor, major
npm publish

公開時の注意点

  • スコープ付きパッケージ (@username/package-name) は --access public が必要
  • バージョン管理npm version コマンドを使用
  • テスト は公開前に npm pack で確認

4. まとめ

MCPファイルシステムサーバーパッケージを開発・公開することで、以下を実現しました:

  • 相対パスを使用したファイル操作環境の提供
  • 簡単インストール(npxで即利用可能)
  • 再利用可能なコンポーネントとしての公開

npmパッケージの公開は思っているより簡単で、適切な設定とテストがあれば数分で完了します。
この情報がご活用いただければ幸いです。

Discussion