MCP 서버에 관심이 생겨서 조사해 보았습니다.
AI에게 물어보았습니다.
직접 만든 MCP(Model Context Protocol) 서버는 Python의 'FastMCP'라는 라이브러리를 사용하면 가장 쉽게 만들 수 있습니다.
MCP는 Claude Desktop이나 Cursor, ChatGPT와 같은 AI 호스트가 사용자의 PC 로컬에 있는 데이터나 직접 만든 함수(도구)를 안전하게 실행하고 제공하기 위한 공통 규격입니다.
여기서는 가장 손쉽게 만들 수 있는 'Python + FastMCP'를 이용한 자작 MCP 서버 구축 절차를 알기 쉽게 설명합니다.
Python의 모던 패키지 관리 도구인 'uv'를 사용하여, 가장 간단한 구성으로 도구를 제작하고 Claude Desktop 등에 인식시키는 순서입니다.
- 개발 환경 준비 | uv를 사용해 새로운 프로젝트를 생성하고 필요한 라이브러리를 설치합니다. |
uv init my-mcp-server cd my-mcp-server uv add fastmcp
- 서버 코드 구현 | 프로젝트 내의 main.py (또는 hello.py)를 수정하여 AI에게 제공하고 싶은 함수를 정의합니다. 아래 코드 블록을 참조하세요. | - 설정 파일 편집 | AI 클라이언트가 사용자의 자작 MCP를 읽어 들일 수 있도록 지정된 config.json에 경로를 등록합니다. 아래 'AI 호스트에 등록하는 방법'을 참조하세요.
- AI 호스트 재시작 | 설정을 반영하기 위해 Claude Desktop이나 Cursor 같은 앱을 완전히 종료했다가 다시 시작합니다. 앱을 마우스 오른쪽 버튼으로 클릭하여 종료하거나, 작업 관리자에서 강제 종료 후 재시작합니다.
- 동작 확인 | AI와의 채팅창에서 자작 함수(도구)가 호출되는지 테스트합니다. 채팅창에 '자작 도구를 사용해서 ◯◯해'라고 지시합니다.
main.py에는 다음과 같은 코드를 작성합니다. @mcp.tool()이라는 데코레이터(표식)를 붙인 함수는 그대로 AI의 '팔다리'처럼 기능하게 됩니다.
# main.py
from fastmcp import FastMCP
# MCP 서버 인스턴스를 생성
...
※함수 내부의 삼중 따옴표(""")로 둘러싸인 설명문(docstring)을 AI가 읽어 들여 '언제 이 도구를 사용해야 하는지' 스스로 판단합니다.
만든 서버를 AI 앱에 인식시키기 위해, 각 앱의 설정 파일(JSON)에 다음과 같이 추가합니다.
설정 파일 저장 위치 | Claude Desktop (Windows): %APPDATA%\Claude\claude_desktop_config.json
| Cursor: .cursor/mcp.json (프로젝트 루트 하위)
기술할 내용 (JSON) | ```{
"mcpServers": {
"my-mcp-server": {
...
※C:/사용자명/.../main.py 부분은 실제로 파일을 저장한 절대 경로로 수정해 주세요.
앱을 재시작하면, 채팅 화면에 '콘센트 아이콘'이나 'Tool이 이용 가능해졌다는 알림'이 표시되며, AI가 자동으로 사용자의 함수를 호출하게 됩니다.
Python에서 MCP(Model Context Protocol) 서버를 '호출'(즉, 사용하여 이용하는 것)하려면, MCP의 공식 Python SDK가 제공하는 Client 기능을 사용합니다.
서버 측을 직접 제작하고 구동할 뿐만 아니라, Python 스크립트 자체를 'MCP 클라이언트'처럼 동작시켜 서버가 제공하는 도구(Tools)나 리소스(Resources)를 실행하는 흐름이 됩니다.
?? 사전 준비: SDK 설치
먼저 MCP의 공식 라이브러리를 설치합니다.
pip install mcp
(만약 테스트용 등으로 CLI 도구도 사용하고 싶다면 pip install "mcp[cli]"를 선택해 주세요)
Python에서 MCP 서버를 호출하는 최소 코드 (Stdio 통신)
일반적인 MCP 서버는 표준 입출력(Stdio)을 통해 통신합니다. Python 스크립트에서 로컬의 MCP 서버를 구동하고, 그 도구를 호출하는 구현은 다음과 같습니다.
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
...
주요 통신 트랜스포트(연결 방식)의 사용 구분
MCP 서버가 어디에서 실행되는지(로컬 프로세스인지, 네트워크를 경유하는지)에 따라 연결용 객체를 다르게 사용합니다.
| 연결 타입 | 서버 형태 | Python 클라이언트 측에서 사용하는 함수 |
| :---: | :---: | :---: |
| Stdio (표준 입출력) | 명령어로 그 자리에서 실행되는 로컬 서버 | mcp.client.stdio.stdio_client |
| SSE (HTTP) | 상시 구동 중인 네트워크/WEB 서버 | mcp.client.sse.sse_client |
이 구성에서는 '서버 측(server.py)'에 C 언어 파일을 생성하는 툴을 구현하고, 이를 앞서 언급한 '클라이언트 측(로컬 LLM)'에서 호출함으로써, LLM이 자발적으로 C 언어 코드를 로컬 환경에 작성할 수 있게 됩니다.
Windows 환경에서도 문제없이 작동하도록 데스크톱 저장 및 확장자 처리에 대응하는 코드를 작성했습니다.
- 서버 측 구현 (server.py)
먼저, MCP 서버 본체를 만듭니다. 이 서버는 LLM으로부터 '파일명'과 'C 언어 코드 내용'을 받아 로컬 환경에 저장하는 generate_c_file이라는 툴을 제공합니다.
```python
# server.py
import os
import mcp.server.fastmcp as fastmcp
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
이전 통합 코드(클라이언트 측)의 시스템 프롬프트 부분을 'C 언어를 생성하여 툴로 저장'하는 사양에 최적화합니다.
방금 만든 클라이언트 코드의 system_prompt와 user_query 부분을 다음과 같이 수정하여 실행해 주세요.
user_query = "재귀 함수를 사용해서 팩토리얼을 계산하는 C 언어 프로그램을 만들고, 'factorial.c'라는 이름으로 저장해 주세요."
...
- 툴 실행 후 최종 답변에서는, 저장이 완료되었음을 알리고 생성한 코드에 대한 간단한 설명을 사용자에게 전달해 주세요.
실행 절차 및 동작 흐름
- 같은 폴더 내에 server.py와 수정된 클라이언트 측 스크립트를 배치합니다.
- 클라이언트 측 스크립트를 실행합니다.
- 동작 플로우:
- LLM (llm-jp-3)이 사용자 지시를 해석하고, generate_c_file 툴을 사용하기 위한 JSON(C 언어 코드 포함)을 출력합니다.
- 클라이언트 프로그램이 JSON을 파싱하여, 백그라운드에서 작동하는 server.py의 툴을 호출합니다.
- server.py가 로컬 폴더에 factorial.c를 자동 생성합니다.
- LLM이 '저장이 완료되었습니다!'라는 최종 답변을 출력합니다.
이제 LLM이 스스로 C 언어 코드를 파일로 작성할 수 있게 되었습니다.
C#에서도 MCP(Model Context Protocol) 서버를 직접 제작하여, stdio(표준 입출력) 방식으로 Claude Desktop 등과 연동하는 것이 가능합니다.
C#으로 개발하는 경우, 커뮤니티에서 개발된 완성도가 매우 높은 비공식 SDK인 'ModelContextProtocol.NET (McpDotNet)'을 이용하는 것이 가장 권장됩니다.
내부적으로 Console.SetOut()이나 Console.SetError() 등의 핸들링이 철저하게 되어 있기 때문에, 주의 깊게 구현하면 C# 특유의 'stdio 오염'도 방지할 수 있습니다.
C#에서의 최소한 MCP 서버 구현 절차와 stdio 오염을 완벽하게 방지하기 위한 포인트를 정리했습니다.
dotnet new console -n MyMcpServer
cd MyMcpServer
MCP 서버 기능을 제공하는 라이브러리(McpDotNet)를 추가합니다.
dotnet add package McpDotNet
C#의 최상위 레벨 문장(top-level statements)을 사용한, stdio 통신 기반 MCP 서버의 최소 코드입니다.
using McpDotNet.Server;
using System.Text.Json.Serialization;
// 1. 서버의 기본 정보를 설정하여 초기화
...
C#에서 'stdio 오염'에 대한 초중요 체크포인트
C# 콘솔 애플리케이션을 MCP 서버로 구동할 때 가장 주의해야 할 점은 Console.Write나 Console.WriteLine을 절대 그대로 사용하지 않는 것입니다.
-
Console.Out (표준 출력)이 라이브러리에 가로채임
server.StartStdioAsync()를 호출하면, SDK가 onsole.OpenStandardOutput()이나 Console.OpenStandardInput()을 사용하여 Claude와의 메시지 송수신 스레드를 설정합니다.
이 상태에서 개발자가 실수로 Console.WriteLine("Debug")를 실행할 경우, Claude에게 전달되어야 할 JSON 데이터 스트림에 'Debug'라는 단순 문자열이 혼입되어 즉시 파싱 오류(stdio 오염)가 발생합니다.
-
디버그 출력은 Console.Error.WriteLine으로 통일
위의 코드 예시에서 볼 수 있듯이, 로그를 남기고 싶을 때는 반드시 Console.Error.WriteLine (표준 에러 출력)을 사용해야 합니다. Claude Desktop은 MCP 서버의 표준 에러 출력(stderr)을 오류로 간주하지 않고 자체 로그 파일에 기록해주는 사양으로 되어 있어 통신을 방해하지 않습니다.
-
서드파티 라이브러리의 '임의 콘솔 출력' 주의
예를 들어, 데이터베이스(EF Core 등)나 HTTP 통신 라이브러리(HttpClient 등)를 통합할 때, 라이브러리의 로그 출력 위치가 기본적으로 Console (표준 출력)으로 설정되어 있다면, 백그라운드에서 SQL 쿼리나 요청 URL이 콘솔에 자동 출력되면서 stdio 오염을 일으킵니다.
대책: 외부 라이브러리를 도입할 때는 로깅(Microsoft.Extensions.Logging 등) 설정을 통해 출력 위치를 Console이 아닌 Debug, Trace 또는 파일(Serilog 등)로 반드시 지정해야 합니다.
Claude Desktop에 등록하는 방법
C#(.NET)으로 만든 서버는 빌드하여 생성된 .exe 파일을 직접 지정하여 구동할 수 있습니다.
dotnet publish -c Release -r win-x64 --self-contained
%APPDATA%\Claude\claude_desktop_config.json 파일을 열고 다음과 같이 추가합니다.
{
"mcpServers": {
"my-csharp-server": {
...
※exe를 직접 실행하므로, args(인수)는 비워도 무방합니다.
실용적인 MCP 툴의 구현 샘플 (Program.cs)
C#(.NET 8+)과 McpDotNet 라이브러리를 사용하여, 실제로 유용한 MCP Tool의 샘플 코드를 작성했습니다.
AI가 실제 업무에서 자주 사용하는 '파일 조작', 'Web API 연동(날씨 정보)', '시스템 상태 조회'라는 세 가지 실용적인 기능을 통합한, 그대로 복사하여 작동시킬 수 있는 Program.cs 전체 예제입니다.
using McpDotNet.Server;
using System.Text.Json.Serialization;
using System.ComponentModel;
...
C#으로 툴을 구현할 때의 포인트
- [JsonDescription] 속성이 생명:
인수(argument) 속성에 붙이는 [JsonDescription("...")]는 매우 중요합니다. AI는 이 설명문을 읽고 '이 인수에 무엇을 전달해야 하는지'를 이해합니다. 가능하다면 구체적이고 이해하기 쉬운 설명(예시 등을 포함하여)을 작성해 두면, AI의 오작동이 크게 줄어듭니다.
- 인수 클래스는 필수:
비록 '시스템 상태 가져오기'처럼 인수가 필요 없는 툴이라 하더라도, McpDotNet의 제네릭스(Generics) 제약상 빈 클래스(위의 SystemStatusRequest와 같은)를 정의하여 핸들러에 전달해야 합니다. - 예외 처리(try-catch) 철저히 하기:
툴 내부에서 오류(파일 접근 거부나 네트워크 연결 끊김 등)가 발생했을 때, 프로그램 자체가 충돌하며 종료되면 Claude와의 연결이 끊어지게 됩니다. 반드시 try-catch로 감싸서, 오류가 발생하더라도 '오류 메시지'를 텍스트 형태로 AI에게 반환하도록 설계하는 것이 현명합니다.
이를 빌드하고 앞서 언급한 claude_desktop_config.json에 설정하면, Claude Desktop에서 '파일을 조사해', '날씨를 알려줘', 'PC의 상태는?'과 같이 지시하기만 해도 이 C# 코드가 자동으로 실행되게 됩니다.
이는 MCP 서버 개발에서의 '테스트 벤치'라고 할 수 있습니다.
MCP에서의 테스트 벤치란, 직접 제작한 MCP 서버(이번 경우 C#으로 만든 것)가 AI 앱(Claude Desktop 등)과 올바르게 상호 작용하는지 여부를, AI 앱을 거치지 않고 개발자가 수동 또는 자동 스크립트로 시뮬레이션하고 검증하기 위한 환경을 말합니다.
C# 개발에서 가장 쉽고 강력한 테스트 벤치 구축 방법(MCP Inspector 활용)과 C# 코드 측에서 가능한 유닛 테스트적인 검증 방법을 정리했습니다.
- 공식 테스트 벤치 'MCP Inspector'를 C#으로 사용하기
앞서 언급했듯이, 공식의 'MCP Inspector'가 가장 강력한 비주얼 테스트 벤치가 됩니다. C# 빌드 결과물(.exe)을 직접 지정하여 브라우저 상에서 툴을 실행할 수 있습니다.
? 테스트 벤치 실행 절차 (Windows)
- 명령 프롬프트나 PowerShell을 엽니다.
- 다음 명령어를 실행하여, C#으로 빌드한 .exe를 인스펙터에 로드합니다.
npx -y @modelcontextprotocol/inspector C:\path\to\your\MyMcpServer.exe
-
브라우저에서 http://localhost:3000을 열면, C# 코드 내에 등록한 list_directory_files나 get_system_status와 같은 툴들이 화면에 나열됩니다. 인수를 넣고 버튼만 누르면, C#의 로직이 올바르게 작동하는지, C# 측에서 표준 입출력 오염(stdio pollution)이 발생하지 않았는지 완벽하게 디버깅할 수 있습니다.
-
C# 코드 내에서 완결되는 '셀프 테스트 벤치'
'외부 툴(npx)을 매번 실행하는 것이 번거롭다', 'Visual Studio의 디버그 실행(F5)만으로 툴 로직 테스트를 완료하고 싶다'와 같은 경우에는, C# 코드 내에 테스트 모드(디버깅용 시뮬레이션 로직)를 직접 구현하는 것이 가장 간단합니다.
아래와 같이, 시작 시 인자(args)에 ---test 등의 문자가 포함되어 있다면, 표준 입출력 통신을 시작하지 않고, 직접 툴의 함수를 호출하여 콘솔(또는 디버그 창)에 결과를 출력하는 '즉석 테스트 벤치'를 구현하는 방식입니다.
수정된 Program.cs 예시
using McpDotNet.Server;
using System.Text.Json.Serialization;
// --- 1. 툴의 로직(핸들러)을 변수로 분리해 두기 ---
...
Visual Studio에서의 디버깅 방법
-
프로젝트 속성을 열고, '디버그 시작 프로파일'의 '애플리케이션 인자'에 --test를 설정합니다.
-
이렇게 하면 F5 키를 눌러 실행할 때, Claude와 통신하지 않고 Visual Studio의 '출력 창'이나 중단점(Breakpoint)을 사용하여 툴 로직을 한 줄씩 단계별로 테스트할 수 있습니다.
-
Claude Desktop에 등록할 때는 인자(args)를 비워두면, 평소대로 MCP 서버로 작동합니다.
-
MCP 공식이 정의하는 '테스트 자동화'
만약 xUnit이나 NUnit 같은 테스팅 프레임워크를 사용하여 CI/CD 라인 등에서 엄격한 통합 테스트를 수행하고 싶다면, McpDotNet의 클라이언트 기능(McpClient)을 사용하여, 테스트 코드 측에서 직접 제작한 서버를 호출하는 진짜 테스트 벤치 환경을 구축하는 것도 가능합니다.
- 테스트 프로젝트 측에서 McpClient를 생성합니다.
- 직접 제작한 서버의 .exe를 자식 프로세스(child process)로 실행하고 stdio를 통해 연결합니다.
- client.CallToolAsync("list_directory_files", ...)를 실행하여 예상대로 JSON이 반환되는지 단언(assert/검증)합니다.
우선은 "1. MCP Inspector"에서 시각적으로 작동하는지 테스트하고, 개발 중인 세부 로직 수정은 "2. 셀프 테스트 벤치(Self-Test Bench, F5 디버그)"에서 진행하는 것이 C# 환경에서 가장 쉽고 효율적인 개발 주기입니다.
C# (McpDotNet)을 사용하여 FizzBuzz를 실행하는 MCP 도구와, 이를 검증하기 위한 테스트 벤치(자동 테스트/디버깅 환경)의 완벽한 구현 샘플을 작성했습니다.
이 코드에는 AI가 호출하는 "FizzBuzz 도구"와 C# 단독으로 동작 확인이 가능한 "셀프 테스트 벤치 기능" 두 가지가 하나의 파일에 포함되어 있습니다.
FizzBuzz 도구 & 테스트 벤치 내장 코드 (Program.cs)
using McpDotNet.Server;
using System.Text.Json.Serialization;
using System.ComponentModel;
...
이 코드를 사용한 두 가지 테스트 방법
테스트 방법 A: Visual Studio / VS Code에서의 "셀프 테스트"
- 프로젝트의 디버그 인자(실행 옵션)에 --test를 설정하여 실행(F5)합니다.
- 그러면 stdio 통신을 오염시키지 않고, 콘솔 상에서 FizzBuzz의 실행 결과나 에러 핸들링이 올바른지 직접 확인할 수 있습니다.
- fizzBuzzHandler 내부에 중단점(breakpoint)을 설정하면, 값이 어떻게 처리되는지 한 줄씩 단계 실행으로 디버깅할 수 있습니다.
테스트 방법 B: 공식 "MCP Inspector"를 이용한 "AI 관점의 테스트"
- --test 인자를 제외하고 빌드(또는 직접 실행)하여 MCP Inspector에서 호출합니다.
npx -y @modelcontextprotocol/inspector dotnet run
- 브라우저에서 http://localhost:3000을 엽니다.
- Tools 탭에 execute_fizzbuzz가 표시되므로, count에 20 등을 입력하고 "Execute"를 클릭합니다.
- 아래와 같은 깔끔한 JSON 응답이 stdio 오염으로 인한 파싱 에러 없이 돌아온다면 대성공입니다!
{
"isError": false,
"message": "1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz"
...
실례했습니다! "AI(Claude 등)로부터 요청된 수까지 C# 서버 자체가 테스트 벤치용 C# 소스 코드를 동적으로 자동 생성하여 파일로 저장하는 도구"라는 의미였습니다.
AI에게 count를 지정하면, "해당 수까지의 FizzBuzz를 검증하기 위한 정식 xUnit(또는 NUnit) 테스트 코드(.cs)"를 서버가 자동으로 생성하여 로컬 PC에 저장하는 초실용적인 MCP 도구 샘플을 작성했습니다.
서버가 소스 코트(테스트 벤치)를 생성하여 저장하는 구현 예시
using McpDotNet.Server;
using System.Text.Json.Serialization;
using System.ComponentModel;
...
이 "소스 생성 도구"의 작동 방식
- Claude Desktop과 같은 AI에게 "C:\McpTest 폴더에, 30까지의 FizzBuzz를 검증하는 C# 테스트 벤치 코드를 생성해줘"라고 지시합니다.
- AI가 자동으로 generate_fizzbuzz_testbench 도구를 선택하고, count: 30, outputDirectory: "C:\McpTest"를 지정하여 이 C# 서버를 호출합니다.
- 서버 내부의 로직이 실행되어, 지정된 폴더에 FizzBuzzTestBench_UpTo30.cs라는 실제 C# 파일이 작성됩니다.
실제로 생성되는 소스 코드(내용)
서버로부터는 아래와 같이 그대로 컴파일하여 dotnet test가 통과하는 깔끔한 xUnit 코드가 출력됩니다.
using Xunit;
namespace FizzBuzz.Tests
{
...
}
이처럼, MCP 서버 쪽에 템플릿이나 코드 조립 로직을 가지고 있다면, AI로부터의 간단한 파라미터 지시만으로 사양을 충족하는 정확한 프로그램 소스나 자동 테스트 코드를 로컬 디스크에 대량 생산(量産)할 수 있습니다.