.NETで簡単なMCPサーバー作ってみた

# 最近話題のMCP
最近話題のMCP（Model Context Protocol）、なんかおもしろそうだけどよく分からない。
どうやらAIと外部ツールをつなぐ新しい仕組みらしく、すごいらしい。

そんなMCPを少しでも理解するために、<a href="https://engincanveske.substack.com/p/building-your-first-mcp-server-with">こちらの記事</a>を参考にしながら、.NETで「現在時刻を返すだけ」のシンプルなMCPサーバーを実装してみました。

この記事では、その手順をまとめていきます。

# 前提
Cursorのチャット機能で、特定の都市、タイムゾーンの現在時刻を聞いてみると、以下のような回答が得られます。
![Screenshot 2025-04-14 at 23.40.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956628/513ead8f-d4dd-478a-b2d3-a00d2add748e.png)
LLMはデフォルトでインターネット接続を持たないため、リアルタイムな情報を取得することはできません。
これを補うために、現在時刻を提供する MCP Server を構築し、Cursorに統合します。

# MCPサーバーを作成する

## ステップ1：プロジェクトを作成する
```c#
dotnet new console -n McpTimeServer
cd McpTimeServer
```

## ステップ2：必要となるパッケージをインストールする
```c#
dotnet add package ModelContextProtocol --prerelease
dotnet add package Microsoft.Extensions.Hosting
```

:::note info
ModelContextProtocol パッケージは、MCPサーバーに接続するクライアントを作成するAPI、MCPサーバーを作成するAPI、および`Microsoft.Extensions.AI`を通じてLLMと統合するためのAIヘルパーライブラリの利用を可能にするAPIへのアクセスを提供します。
:::

## ステップ3：MCPサーバーを実装する

``` Program.cs
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection;

var builder = Host.CreateEmptyApplicationBuilder(null);

builder.Services
    .AddMcpServer() // MCPサーバーを追加
    .WithStdioServerTransport() // 標準入出力を通じて通信を行うように設定
    .WithToolsFromAssembly(); // アセンブリからMCPツールを読み込む

var app = builder.Build();

await app.RunAsync();
```

`.WithToolsFromAssembly()`についてですが、これを設定することにより、関連する属性が付与されているクラス、メソッドを自動で検知して、**MCPツール**として読み込むようになります。

:::note info
MCPツールって？（ChatGPTに聞いてみた）
MCPツールは、AIが外部システムと連携するための「機能の窓口」として機能します。例えば、以下のような操作が可能です。
* 天気情報の取得：指定された都市の現在の天気を取得する
* カレンダー操作：予定の追加や確認を行う
* ファイル検索；特定の条件に合致するファイルを検索する
* データベースクエリ：指定された条件でデータベースを検索する

これらのツールは、AIがユーザーの指示に基づいて適切な外部リソースと連携し、必要な情報を取得・操作するための手段を提供します。
:::

次に、MCPサーバー上で呼び出される**ツール**を実装します。

``` TimeTools.cs
using System.ComponentModel;
using ModelContextProtocol.Server;

namespace McpTimeServer;

[McpServerToolType]
public static class TimeTools
{
    [McpServerTool, Description("現在の時刻を取得します。")]
    public static string GetCurrentTime()
    {
        return DateTimeOffset.Now.ToString();
    }

    [McpServerTool, Description("指定されたタイムゾーンの現在の時刻を取得します。")]
    public static string GetTimeInTimeZone(string timeZone)
    {
        try
        {
            var tz = TimeZoneInfo.FindSystemTimeZoneById(timeZone);
            return TimeZoneInfo.ConvertTime(DateTimeOffset.Now, tz).ToString();
        }
        catch
        {
            return "指定されたタイムゾーンが見つかりません。";
        }
    }
}
```
`[McpServerToolType]`はこのクラスにMCPツールを含んでいることを表します。
`[McpServerTool]`を付与された二つのメソッドは、MCPクライアント（今回でいうCursor）から利用可能になります。

# MCPクライアント（Cursor）と統合する
MCPサーバーが完了したので、Cursorに設定します。

## ステップ1：CursorにMCPサーバーを設定する
多分この辺にある「Cursor Settings」を開きます。
![Screenshot 2025-04-15 at 2.17.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956628/2c439d51-0924-44bc-ba19-3430566f41ae.png)

「Cursor Settings」を開くと、「MCP」のセクションがあるので、そこから「Add new global MCP server」のボタンを押します。

![Screenshot 2025-04-15 at 0.00.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956628/c5b373b1-1c5a-44cd-b4b4-08b492562283.png)

するとmcp.jsonが開くので、以下のように設定します。

```json
{
  "mcpServers": {
    "timeServer": {
      "command": "dotnet",
      "args": [
        "run",
        "--project",
        "path/to/your/McpTimeServer",
        "--no-build"
      ]
    }
  }
}
```

## ステップ2：作成したMCPサーバーをテスト
MCPサーバーの設定が完了して、「Cursor Settings」の「MCP」セクションに戻ると、MCPサーバーが利用可能になっています。（「Refresh」を押してください。）

![Screenshot 2025-04-15 at 1.00.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956628/551facb9-726c-4934-9d5d-4c612c524f7c.png)

チャット機能を使って、もう一度パリの現在時刻を聞いてみます。

![Screenshot 2025-04-15 at 1.02.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956628/f0e61496-b11e-479b-bd5f-08a4b2911d2a.png)

作成したMCPサーバーを利用して、LLMが特定の都市、タイムゾーンの現在時刻を取得できるようになりました。また、どのツールを使ったのか、そのツールを利用する時に渡された値も確認することができます。
すごい。

# 参考
* https://engincanveske.substack.com/p/building-your-first-mcp-server-with