0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

CursorでMCPサーバーを使用する方法:詳細ガイド

Posted at

はじめに

Cursor(カーソル)はAI駆動のコードエディタで、コーディングの効率を大幅に向上させるツールとして注目を集めています。一方、Model Context Protocol(MCP)は、AIモデルが外部ツールやデータソースと標準化された方法で対話するためのオープンプロトコルです。このプロトコルを活用することで、CursorのAI機能を拡張し、カスタムツールやローカルモデルとの連携が可能になります。

本チュートリアルでは、CursorとMCPサーバーを連携させる方法を詳しく解説します。特に、コーディング支援やデータ分析、ドキュメント生成など、開発者の日常業務を効率化するための実践的な使い方に焦点を当てていきます。

MCPサーバーの実装例については、以下のGitHubリポジトリが参考になります:

1. MCPとCursorの基本概念

MCPとは何か?

Model Context Protocol(MCP)は、AI言語モデルが外部ツールやシステムと安全かつ標準化された方法で通信するためのプロトコルです。主な特徴は:

  • クライアント・サーバーモデルに基づくアーキテクチャ
  • JSON形式でのメッセージのやり取り
  • ツール検出とツール呼び出しのための標準インターフェース
  • セキュリティと認証の仕組み

Cursorとは?

Cursorは、VS Codeをベースにした次世代のコードエディタで、AIを駆使したコーディング支援機能が統合されています。以下の機能が特徴です:

  • コード生成・修正のAI支援
  • リアルタイムコードレビューと提案
  • 自然言語での指示によるコード操作
  • 拡張機能によるカスタマイズ性

2. 環境準備

必要なソフトウェア

  • Node.js (v16.0以上)
  • Cursor (最新版)
  • Git (バージョン管理用)

Node.jsのインストール

# バージョン確認
node -v

# 古い場合は更新するか、公式サイトからダウンロード
# https://nodejs.org/ja/download/

Cursorのインストール

  1. Cursor公式サイトからインストーラーをダウンロード
  2. インストーラーを実行し、指示に従ってインストール
  3. インストール完了後、Cursorを起動

3. 基本的なMCPサーバーの作成

まず、シンプルなMCPサーバーを作成しましょう。

プロジェクトの初期化

# プロジェクトディレクトリの作成
mkdir cursor-mcp-server
cd cursor-mcp-server

# npmプロジェクトの初期化
npm init -y

# 必要なパッケージのインストール
npm install @anthropic-ai/sdk @modelcontextprotocol/server dotenv

基本的なMCPサーバーの実装

以下のコードをindex.jsとして保存します:

const { McpServer } = require('@modelcontextprotocol/server');
const { StdioServerTransport } = require('@modelcontextprotocol/server');
const fs = require('fs');
const path = require('path');
require('dotenv').config();

// MCPサーバーの初期化
const server = new McpServer({
  name: 'cursor-helper',
  version: '1.0.0',
});

// ファイル一覧を取得するツール
server.tool(
  'list-files',
  'ディレクトリ内のファイル一覧を表示',
  {
    directory: {
      type: 'string',
      description: '検索するディレクトリパス',
    }
  },
  async ({ directory }) => {
    try {
      const files = fs.readdirSync(directory);
      
      return {
        content: [{
          type: 'text',
          text: `# ${directory}内のファイル一覧\n\n${files.join('\n')}`
        }]
      };
    } catch (error) {
      return {
        isError: true,
        content: [{
          type: 'text',
          text: `エラー: ${error.message}`
        }]
      };
    }
  }
);

// ファイルの内容を読み取るツール
server.tool(
  'read-file',
  'ファイルの内容を読み取って表示',
  {
    filepath: {
      type: 'string',
      description: '読み取るファイルのパス',
    }
  },
  async ({ filepath }) => {
    try {
      const content = fs.readFileSync(filepath, 'utf8');
      const extension = path.extname(filepath).substring(1);
      
      return {
        content: [{
          type: 'text',
          text: `# ${filepath}の内容\n\n\`\`\`${extension}\n${content}\n\`\`\``
        }]
      };
    } catch (error) {
      return {
        isError: true,
        content: [{
          type: 'text',
          text: `エラー: ${error.message}`
        }]
      };
    }
  }
);

// サーバーの起動
async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error('Cursor MCP サーバーが起動しました');
}

main().catch(error => {
  console.error('エラー:', error);
  process.exit(1);
});

4. CursorとMCPサーバーの連携

Cursor設定ファイルの編集

Cursorの設定ディレクトリを見つけて、MCPサーバーを登録します:

Macの場合

mkdir -p ~/Library/Application\ Support/Cursor/mcp

Windowsの場合

mkdir %APPDATA%\Cursor\mcp

MCP設定ファイルの作成

mcp/config.jsonファイルを作成し、以下の内容を追加します:

{
  "servers": {
    "cursor-helper": {
      "command": "node",
      "args": ["/絶対パス/cursor-mcp-server/index.js"]
    }
  }
}

/絶対パス/ は実際のプロジェクトディレクトリパスに置き換えてください。

Cursorの再起動

設定を反映させるために、Cursorを完全に終了して再起動します。

5. 応用的なMCPサーバーの実装

より実践的な機能を持つMCPサーバーを実装しましょう。

コード分析ツールの実装

以下のコードを新しいファイルadvanced-server.jsとして保存します:

const { McpServer } = require('@modelcontextprotocol/server');
const { StdioServerTransport } = require('@modelcontextprotocol/server');
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
require('dotenv').config();

const server = new McpServer({
  name: 'code-analyzer',
  version: '1.0.0',
});

// プロジェクト構造を分析するツール
server.tool(
  'analyze-project',
  'プロジェクト構造を分析して要約',
  {
    projectPath: {
      type: 'string',
      description: 'プロジェクトのルートパス',
    }
  },
  async ({ projectPath }) => {
    try {
      // ファイル数とタイプを集計
      const stats = {
        totalFiles: 0,
        byExtension: {}
      };
      
      function scanDirectory(dir) {
        const entries = fs.readdirSync(dir, { withFileTypes: true });
        
        for (const entry of entries) {
          const fullPath = path.join(dir, entry.name);
          
          // 隠しファイルやnode_modulesをスキップ
          if (entry.name.startsWith('.') || entry.name === 'node_modules') {
            continue;
          }
          
          if (entry.isDirectory()) {
            scanDirectory(fullPath);
          } else {
            stats.totalFiles++;
            
            const ext = path.extname(entry.name).substring(1) || 'no-extension';
            stats.byExtension[ext] = (stats.byExtension[ext] || 0) + 1;
          }
        }
      }
      
      scanDirectory(projectPath);
      
      // package.jsonがあればその情報も取得
      let packageInfo = '';
      const packagePath = path.join(projectPath, 'package.json');
      if (fs.existsSync(packagePath)) {
        const packageData = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
        packageInfo = `
## パッケージ情報
- 名前: ${packageData.name || 'N/A'}
- バージョン: ${packageData.version || 'N/A'}
- 説明: ${packageData.description || 'N/A'}
- 主な依存パッケージ: ${Object.keys(packageData.dependencies || {}).join(', ') || 'なし'}
`;
      }
      
      // 結果の整形
      const extensionStats = Object.entries(stats.byExtension)
        .map(([ext, count]) => `- .${ext}: ${count}ファイル`)
        .join('\n');
      
      return {
        content: [{
          type: 'text',
          text: `# プロジェクト分析結果

## 基本情報
- 総ファイル数: ${stats.totalFiles}
- プロジェクトパス: ${projectPath}

## ファイル種類
${extensionStats}
${packageInfo}
`
        }]
      };
    } catch (error) {
      return {
        isError: true,
        content: [{
          type: 'text',
          text: `エラー: ${error.message}`
        }]
      };
    }
  }
);

// Gitの変更を分析するツール
server.tool(
  'analyze-git-changes',
  'Gitの最近の変更を分析',
  {
    repoPath: {
      type: 'string',
      description: 'Gitリポジトリのパス',
    },
    days: {
      type: 'number',
      description: '過去何日分の変更を取得するか',
      default: 7
    }
  },
  async ({ repoPath, days }) => {
    try {
      // カレントディレクトリを一時的に変更
      const originalDir = process.cwd();
      process.chdir(repoPath);
      
      // 過去X日のコミット統計を取得
      const since = new Date();
      since.setDate(since.getDate() - days);
      const sinceStr = since.toISOString().split('T')[0];
      
      const gitLog = execSync(
        `git log --since="${sinceStr}" --numstat --pretty=format:"%h|%an|%ad|%s" --date=short`
      ).toString();
      
      // 結果の解析
      const commits = [];
      const stats = {
        totalCommits: 0,
        filesChanged: 0,
        insertions: 0,
        deletions: 0,
        byAuthor: {}
      };
      
      const commitChunks = gitLog.split('\n\n');
      for (const chunk of commitChunks) {
        if (!chunk.trim()) continue;
        
        const lines = chunk.split('\n');
        if (lines.length === 0) continue;
        
        const [hash, author, date, subject] = lines[0].split('|');
        stats.totalCommits++;
        
        stats.byAuthor[author] = (stats.byAuthor[author] || 0) + 1;
        
        // ファイル変更の集計
        for (let i = 1; i < lines.length; i++) {
          const line = lines[i].trim();
          if (!line) continue;
          
          const parts = line.split('\t');
          if (parts.length !== 3) continue;
          
          const [insertions, deletions] = [
            Number(parts[0] === '-' ? 0 : parts[0]),
            Number(parts[1] === '-' ? 0 : parts[1])
          ];
          
          stats.filesChanged++;
          stats.insertions += insertions;
          stats.deletions += deletions;
        }
        
        commits.push({ hash, author, date, subject });
      }
      
      // 元のディレクトリに戻る
      process.chdir(originalDir);
      
      // 著者ごとの統計
      const authorStats = Object.entries(stats.byAuthor)
        .map(([name, count]) => `- ${name}: ${count}コミット`)
        .join('\n');
      
      // 最近のコミット一覧
      const recentCommits = commits
        .slice(0, 5)
        .map(c => `- ${c.date} **${c.author}**: ${c.subject} (${c.hash})`)
        .join('\n');
      
      return {
        content: [{
          type: 'text',
          text: `# Git変更分析 (過去${days}日間)

## 概要
- 総コミット数: ${stats.totalCommits}
- 変更ファイル数: ${stats.filesChanged}
- 追加行数: ${stats.insertions}
- 削除行数: ${stats.deletions}

## 著者別コミット
${authorStats}

## 最近のコミット
${recentCommits}
`
        }]
      };
    } catch (error) {
      return {
        isError: true,
        content: [{
          type: 'text',
          text: `エラー: ${error.message || '不明なエラー'}`
        }]
      };
    }
  }
);

// サーバーの起動
async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error('コード分析MCPサーバーが起動しました');
}

main().catch(error => {
  console.error('エラー:', error);
  process.exit(1);
});

設定ファイルの更新

mcp/config.jsonを更新して、新しいサーバーを追加します:

{
  "servers": {
    "cursor-helper": {
      "command": "node",
      "args": ["/絶対パス/cursor-mcp-server/index.js"]
    },
    "code-analyzer": {
      "command": "node",
      "args": ["/絶対パス/cursor-mcp-server/advanced-server.js"]
    }
  }
}

6. Cursorでのツール使用方法

MCPツールの確認

  1. Cursorを開く
  2. 新しいチャットを開始する(AI機能を使用)
  3. 以下のようにツールの存在を確認する:
利用可能なMCPツールを教えてください。

基本ツールの使用例

ファイル一覧の取得

現在のディレクトリのファイル一覧を表示してください。

ファイルの内容を読み取る

package.jsonファイルの内容を表示してください。

高度なツールの使用例

プロジェクト分析

このプロジェクトの構造を分析して、概要を教えてください。

Git変更分析

このGitリポジトリの過去14日間の変更を分析してください。

7. ユースケース別の活用例

コーディング支援

CursorとMCPサーバーを組み合わせると、以下のようなコーディング支援が可能になります:

  1. コード生成の文脈理解を強化

    現在のプロジェクト構造を分析した上で、ユーザー認証機能のコードを提案してください。
    
  2. 自動リファクタリング提案

    このファイルのコードを分析し、改善点を具体的なコード例と共に提案してください。
    

デバッグサポート

  1. エラーログの分析

    エラーログを分析して、問題の原因と解決策を提案してください。
    
  2. コードレビュー支援

    最近のGit変更を分析し、潜在的な問題点やベストプラクティスの観点から改善点を指摘してください。
    

8. 応用:独自ツールの開発

より専門的なニーズに応えるカスタムMCPツールを開発することも可能です。

APIクライアントツール例

以下は外部APIを呼び出すツールの例です:

// 天気情報取得ツール
server.tool(
  'get-weather',
  '指定した都市の天気情報を取得',
  {
    city: {
      type: 'string',
      description: '都市名',
    }
  },
  async ({ city }) => {
    try {
      // 注: 実際には適切なAPIキーを使用してください
      const API_KEY = process.env.WEATHER_API_KEY;
      const response = await fetch(
        `https://api.openweathermap.org/data/2.5/weather?q=${encodeURIComponent(city)}&appid=${API_KEY}&units=metric&lang=ja`
      );
      
      if (!response.ok) {
        throw new Error(`API error: ${response.status}`);
      }
      
      const data = await response.json();
      
      return {
        content: [{
          type: 'text',
          text: `# ${city}の天気情報

- 現在の天気: ${data.weather[0].description}
- 気温: ${data.main.temp}°C
- 体感温度: ${data.main.feels_like}°C
- 湿度: ${data.main.humidity}%
- 風速: ${data.wind.speed} m/s
`
        }]
      };
    } catch (error) {
      return {
        isError: true,
        content: [{
          type: 'text',
          text: `エラー: ${error.message}`
        }]
      };
    }
  }
);

9. トラブルシューティング

よくある問題と解決策

  1. MCPツールが表示されない

    • Cursorを再起動する
    • 設定ファイルのパスが正しいか確認
    • サーバーが正常に起動しているか確認
  2. 実行時エラー

    • コンソール出力をチェック
    • 必要な依存パッケージがインストールされているか確認
    • 絶対パスが正しいか確認
  3. ファイルアクセスエラー

    • 適切な権限でサーバーを実行しているか確認
    • パスに日本語や特殊文字が含まれていないか確認

10. まとめ

本チュートリアルでは、CursorとMCPサーバーを連携させる方法について解説しました。基本的なファイル操作ツールから、プロジェクト分析、Git変更の追跡など、開発作業を効率化するための様々なツールを実装する方法を紹介しました。

MCPの柔軟性を活かすことで、Cursorの機能を大幅に拡張し、より効率的な開発環境を構築することができます。チームの特定のニーズに合わせたカスタムツールを作成することで、コーディングワークフローをさらに向上させることができるでしょう。

今後もMCPプロトコルは進化を続けると思われますので、新しい機能や改善点をチェックしながら、より高度なツールを開発していくことをお勧めします。

参考リンク

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?