본문으로 건너뛰기

© 2026 Molayo

Zenn헤드라인2026. 05. 27. 22:49

ASP.NET Core로 간단한 MCP 서버를 만들어 보았다

요약

ASP.NET Core와 ModelContextProtocol.AspNetCore SDK를 사용하여 REST API와 MCP 서버 기능을 동시에 제공하는 TODO 리스트 서버 구현 방법을 소개합니다. 하나의 비즈니스 로직을 공유 서비스로 통합하여 HTTP API와 MCP 도구로 모두 활용하는 구조를 제안합니다.

핵심 포인트

  • ASP.NET Core Minimal API와 MCP SDK를 활용한 서버 구축
  • REST API와 MCP 엔드포인트를 하나의 애플리케이션에 통합
  • TodoStore를 통한 비즈니스 로직 및 데이터 상태 공유
  • WithHttpTransport를 이용한 Streamable HTTP 전송 활성화

서론

ASP.NET Core로 MCP 서버를 구축해 보고 싶어서, TODO 리스트를 조작하는 최소 구성의 샘플을 만들어 보았습니다.

이번 샘플은 다음과 같습니다.

  • REST API로서 TODO의 등록, 갱신, 삭제, 목록 조회를 공개한다
  • 동일한 비즈니스 로직을 MCP 도구 (Tool)로도 공개한다
  • ASP.NET Core의 Minimal API와 ModelContextProtocol.AspNetCore를 사용한다

MCP 서버 단독으로 만드는 것뿐만 아니라, Web API로서도 그대로 확인할 수 있도록 구성했습니다.

환경 정보

이번에 확인한 주요 환경 정보는 다음과 같습니다.

항목버전
Target Framework.NET 10 (net10.0)
MCP C# SDKModelContextProtocol.AspNetCore 1.3.0
MCP C# SDK(Core)ModelContextProtocol 1.3.0

이번에 만드는 구성

하나의 ASP.NET Core 애플리케이션 안에서 일반적인 HTTP API와 MCP 엔드포인트 (Endpoint)를 모두 공개합니다.

  • REST API: /api/todos
  • MCP 엔드포인트 (Endpoint): /mcp
  • TODO 데이터: 인메모리 (In-memory) 유지

REST와 MCP가 각각 별도의 구현을 갖는 것이 아니라, TodoStore에 처리를 집약하여 양쪽 모두에서 호출하는 형태로 만들었습니다.

MCP 전용의 별도 프로세스로 분리하는 구성도 가능하지만, 우선은 ASP.NET Core 웹 애플리케이션에 그대로 얹어버리는 형태가 동작 확인이나 디버깅이 훨씬 수월했습니다.

ASP.NET Core에 MCP 서버를 통합하기

먼저 AddMcpServer()WithHttpTransport()를 등록하고, 마지막에 MapMcp("/mcp")를 설정합니다.

using McpTodoServerSample;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<TodoStore>();
...

여기서 포인트가 되는 것은 다음 두 가지입니다.

  • WithHttpTransport()로 Streamable HTTP transport를 활성화한다
  • WithTools<TodoMcpTools>()로 공개할 MCP 도구 (Tool)를 등록한다

이렇게 하면 TodoMcpTools 내의 McpServerTool 속성 (Attribute)이 붙은 메서드가 list_tools / call_tool의 대상이 됩니다.

TODO 로직은 공유 서비스로 모은다

REST API와 MCP 도구 양쪽에서 동일한 로직을 사용하고 싶으므로, TODO의 상태는 TodoStore에 모읍니다.

public sealed class TodoStore
{
    private readonly Lock _lock = new();
    ...
}

이번 샘플에서는 다음과 같이 구현했습니다.

  • 영속화 (Persistence)는 사용하지 않고 인메모리 (In-memory)로만 완결한다
  • LockList<T>를 사용하여 최소한의 배타 제어 (Exclusive control)를 넣는다
  • 유효성 검사 (Validation)는 TodoStore 측에서 공통화한다

이렇게 하면 REST와 MCP 중 어느 쪽에서 호출하더라도 동일한 등록·갱신 규칙이 적용됩니다.

TODO 조작을 MCP 도구로 공개하기

MCP 도구 (Tool)는 TodoMcpTools에 모아두었습니다.

using System.ComponentModel;
using ModelContextProtocol;
using ModelContextProtocol.Server;
...

※ 일부만 발췌했습니다.

TodoStore store가 그대로 인자 (Argument)로 들어가는 이유는, MCP SDK 측에서 DI 컨테이너 (DI Container)로부터 서비스를 해결 (Resolve)해 주기 때문입니다.

이번에는 4개의 도구 (Tool)를 공개하고 있습니다.

도구 이름용도
list_todosTODO 목록을 반환한다
create_todoTODO를 추가한다
update_todoTODO를 갱신한다
delete_todoTODO를 삭제한다

Description은 매우 중요하다

이곳은 이번에 직접 구동해보며 실감한 포인트입니다.

MCP 도구의 Description (설명)

은 단순한 설명문이 아니라, 모델이 "어떤 도구를 호출해야 하는가"를 판단하기 위한 중요한 단서가 됩니다. 특히 create_todo와 같은 쓰기 계열 도구는 설명이 너무 짧으면 모델이 용도를 오해하거나, 목록 조회만 하고 멈추거나, 다른 도구를 선택하는 등 동작이 불안정했습니다.

여러 가지 시도를 해본 결과, 각 도구의 Description (설명)에 다음 정보를 명시하고 있습니다.

  • 해당 도구가 무엇 전용인지
  • 어떤 요청이 있을 때 사용해야 하는지
  • 반대로 무엇에는 사용하지 않는지
  • 실행 전에 list_todos를 호출해야 하는 상황인지

예를 들어 create_todo에는 "TODO를 1건 신규 등록하는 전용 도구", "TODO를 추가하거나, 새로운 태스크를 등록하거나, 할 일을 작성하는 등의 요청 시에는 반드시 사용", "기존 TODO의 변경이나 삭제에는 사용하지 않음"이라고 적어두었습니다.

이 정도 수준까지 상세히 작성해두면 도구 선택의 정밀도가 상당히 높아집니다.

그렇다고 해도 자유도도 필요하므로, 실무에서는 도구의 Description (설명)뿐만 아니라 Skill (스킬)과 함께 사용법을 에이전트에게 설명하는 방식의 운용이 좋을 것 같습니다.

참고로, 검증되지 않은(wild) MCP 서버에서도 시도해 보았습니다만, 검색 계열 이외의 도구는 그대로 두면 잘 작동하지 않는 경우가 많았습니다. (MCP는 AI에게 있어 USB-C와 같다고들 하지만, Skill (스킬)과 같은 드라이버에 해당하는 개념도 필수적이라는 느낌이 듭니다. 꽂기만 한다고 해서 바로 잘 작동하지는 않습니다.)

REST API로도 동시에 공개하기

동일한 TodoStore를 사용하여 일반적인 HTTP API도 제공합니다.

RouteGroupBuilder todos = app.MapGroup("/api/todos");
todos.MapGet("/", (TodoStore store) => Results.Ok(store.List()));
todos.MapPost("/", (CreateTodoRequest request, TodoStore store) =>
...

이렇게 하면 HTTP 클라이언트에서는 일반적인 Web API로 사용할 수 있고, MCP 클라이언트에서는 도구 집합(toolset)으로 사용할 수 있습니다.

실행 방법

실행 명령어는 다음과 같습니다.

dotnet run --project .\McpTodoServerSample\ --urls http://127.0.0.1:5088

실행 후 루트(root) 경로에 접속하면 공개 대상의 개요를 반환합니다.

{
"service": "todo-mcp-server",
"restApi": "/api/todos",
...

MCP 클라이언트에서 사용하려면

이 샘플은 http://127.0.0.1:5088/mcp에 MCP endpoint (엔드포인트)를 공개하므로, MCP 클라이언트 측에서 이 URL을 등록하면 사용할 수 있습니다.

GitHub Copilot에서 사용하는 경우

VS Code의 Copilot Chat에서 사용하는 경우, GitHub Docs에 나와 있는 대로 .vscode/mcp.json에 서버를 정의할 수 있습니다. 이번 서버라면 예를 들어 다음과 같습니다.

{
"servers": {
"todoServer": {
...

설정 후에는 Agent mode (에이전트 모드)로 변경하여 서버를 활성화하면 사용할 수 있습니다.

REST API 확인

PowerShell에서 확인하려면 다음과 같이 호출할 수 있습니다.

$created = Invoke-RestMethod -Uri 'http://127.0.0.1:5088/api/todos' `
-Method Post `
-ContentType 'application/json' `
...

이번 확인 과정에서는 아래와 같이 등록 → 목록 조회 → 갱신 → 삭제까지 모두 통과했습니다.

{
"created": {
"id": 1,
...

MCP 공개 도구 확인

ModelContextProtocol.Client를 사용한 간이 프로브(probe)로 /mcp에 접속하여 list_tools를 호출하면 아래의 4개가 반환되었습니다.

create_todo|TODO를 1건 신규 등록하는 전용 도구입니다. 사용자가 "TODO를 추가해줘", "새로운 태스크를 등록해줘", "할 일을 만들어줘"와 같이 요청한 경우에는 반드시 이 도구를 사용하세요. 기존 TODO의 변경이나 삭제에는 사용하지 않습니다.
delete_todo|기존의 TODO를 1건 삭제하는 전용 도구입니다. 사용자가 "삭제해줘", "불필요하니 지워줘", "이 태스크를 취소해줘"라고 명시한 경우에만 사용합니다. 신규 생성이나 업데이트에는 사용하지 않습니다. 통상적으로 사전에 list_todos로 대상 ID를 확인한 후 사용합니다.
list_todos|TODO 목록을 가져오는 전용 도구입니다. 기존의 TODO를 확인하고 싶을 때, 업데이트나 삭제 전에 대상 ID를 조사하고 싶을 때, 현재 등록 상황을 보고 싶을 때 사용합니다. 신규 생성, 업데이트, 삭제는 할 수 없습니다.
...

ASP.NET Core 앱이 MCP 서버로서 도구(tool)를 올바르게 공개하고 있는지와

각 도구의 용도가 클라이언트에게 전달되는 형태로 공개되고 있음을 확인할 수 있었습니다.

요약

이번에는 ASP.NET Core로 간이 MCP 서버를 만들고, TODO 리스트 조작을 REST API와 MCP 도구 양쪽 모두로 공개했습니다.

확인할 수 있었던 포인트는 다음과 같습니다.

  • AddMcpServer(), WithHttpTransport(), MapMcp("/mcp")를 통해 ASP.NET Core에 MCP 엔드포인트(endpoint)를 추가할 수 있음
  • WithTools<T>()를 통해 일반적인 C# 메서드를 MCP 도구로 공개할 수 있음
  • REST API와 MCP 도구에서 동일한 TodoStore를 공유하면 구현을 이중화하지 않아도 됨
  • list_tools를 통해 create_todo / delete_todo / list_todos / update_todo를 가져올 수 있음
  • 도구의 Description은 모델의 도구 선택 정확도에 크게 영향을 미치므로, 용도, 사용하는 상황, 사용하지 않는 상황까지 작성하는 것이 좋음

실운영을 목표로 한다면 인메모리(in-memory) 구현을 SQLite나 Cosmos DB로 교체하거나, 인증(authentication) 및 인가(authorization) 기능을 추가하는 형태가 될 것입니다.

소스

Program.cs

using McpTodoServerSample;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<TodoStore>();
...

TodoMcpTools.cs

using System.ComponentModel;
using ModelContextProtocol;
using ModelContextProtocol.Server;
...

TodoStore.cs

namespace McpTodoServerSample;
public sealed class TodoStore
{
...

Discussion

AI 자동 생성 콘텐츠

본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0