Jump to content
과거의 기술자료(읽기 전용): https://tech.devgear.co.kr ×
과거의 기술자료(읽기 전용): https://tech.devgear.co.kr

Recommended Posts

에 포스트됨

MCPConnect로 Delphi에서 MCP 서버 만들기

원문 정보 (최종 번역일: 2026년 10월 2일):

MCPConnect로 Delphi에서 MCP 서버 만들기

MCPConnect는 Model Context Protocol(MCP) 서버를 만들기 위한 프레임워크다. 엠바카데로(Embarcadero) Delphi(델파이)와 C++Builder(C++빌더)에서 사용할 수 있다. 이 프레임워크는 애트리뷰트(attribute)를 사용해 기존 Delphi 클래스와 비즈니스 로직을 MCP 도구, 리소스, 프롬프트로 바꿔 준다(C++Builder에서는 함수 기반 등록 메커니즘을 사용한다). 그래서,  기존의 Delphi 클래스와 비즈니스 로직을 MCP 도구, 리소스, 프롬프트로 바꿔준다. 한편, MCP 프로토콜의 서버 쪽의 직렬화, 라우팅, 컨텍스트 관리를 이 라이브러리가 맡아서 처리해 준다.

MCPConnect는 Paolo Rossi와 Luca Minuti가 개발했다. 오픈소스로 공개되어 있으며 MIT 라이선스가 적용된다. 개발은 2025년 7월에 시작했고, GitHub 첫 커밋은 8월 12일이다. 그 후, 커뮤니티의 기여 덕분에도, 빠르게 성장해 왔다.

ℹ️ 공식 문서

MCPConnect의 전체 문서집은 mcpconnect.delphiblocks.dev에서 확인할 수 있다.

목차

MCP 소개

Model Context Protocol(MCP)은 2024년에 Anthropic이 도입한 프로토콜이다. 대규모 언어 모델(LLM, large language model)이 외부 시스템과 대화하는 방식을 표준화하기 위해 고안되었다. LLM은 그 자체로는 텍스트(토큰)만 만들어낼 수 있다. 외부 세계에서 직접 행동할 수 없다. 또한 학습 과정에 배운 내용만 가지고 답할 수 있다.

OpenAI는 이와 비슷한 문제를 풀려고 2023년에 "함수 호출(function calling)"를 사용하려고 했다. 대부분의 다른 벤더들도 그 아이디어를 따라 했다. 하지만 각 구현체는 자신만의 API에 묶였다. 그래서 둘 이상의 모델을 지원하려는 사람은 똑같은 통합 작업을 여러 번 반복해야 했다. MCP는 이 문제를 단일 프로토콜로 해결한다. 그래서 벤더에 중립적이다. 여러분이 서버를 한 번만 작성하면, 호환되는 모든 호스트들이 그 서버를 사용할 수 있다.

MCP의 대표적인 활용 사례는 다음과 같다.

  • 데이터베이스: InterBase(인터베이스), PostgreSQL, Firebird, SQLite에게 질의해서 데이터를 분석하기
  • 파일시스템: 로컬 파일을 읽고, 검색하고, 분석하기
  • 브라우저 자동화: 웹 스크래핑, 자동화 테스트 수행, 웹 애플리케이션과의 상호작용
  • 써드-파티(Third-Party) 서비스: Slack, GitHub, Linear, Jira 등등
  • 내부 도구: LLM이 회사의 API와 시스템에 접근할 수 있도록 제공하기
  • 데이터 처리: Excel, CSV, 기타 형식에 여러분만의 로직을 반영해 처리하기
  • 기존 Delphi 애플리케이션: 이미 가지고 있는 코드(FireDAC 쿼리, 청구서 발행 모듈, 리포트 생성기 등)를 노출하기. 다른 언어로 다시 작성할 필요없음

MCP 서버와 클라이언트의 아키텍처

MCP 아키텍처는 세 부분으로 이루어져 있다.

  • MCP 호스트: MCP를 사용하는 AI 애플리케이션이다. 예를 들면 Claude Desktop이 있다.
  • MCP 클라이언트: 호스트 안에서 실행된다. 하나의 MCP 서버와 연결을 유지한다. 서버로부터 받은 데이터를 호스트로 전달한다. 호스트 안에서 여러 서버들에 연결되려면, 각 서버에 해당되는 클라이언트가 서버의 종류만큼 호스트 안에서 실행된다.
  • MCP 서버: 컨텍스트를 제공하는 프로그램이다. 호스트가 클라이언트를 통해 요청을 보내면, 응답한다.

MCP 아키텍처 다이어그램

MCPConnect가 MCP 버전과 여러 MCP 스펙들을 다루는 방법

MCP는 버전이 있는 프로토콜이다. 스펙의 각 개정판은 발표일이 이름에 붙는다. 클라이언트와 서버는 연결할 때 어떤 버전을 사용할지 합의한다. 그래서 어떤 MCPConnect 버전을 고를지는, 여러분이 지원하고 싶은 스펙에 따라 달라진다.

ℹ️ 참고

이 글의 예제는 GetIt 버전(2025-11-25)을 기준으로 한다.

나만의 MCP 서버를 만드는 방법

MCP 서버는 실제 작업을 수행하는 부분이다. 즉, 문서를 읽고, 데이터베이스 쿼리를 실행하고, 이메일이나 메시지를 보내고, 캘린더를 동기화한다.

이를 위해, MCP 프로토콜은 세 가지 빌딩 블록을 정의한다. MCPConnect는 이 세 가지를 모두 지원한다.

  • 도구(Tools): LLM이 호출하겠다고 결정할 수 있는 함수들이다. 도구는 데이터베이스를 질의하거나, 외부 API를 호출하거나, 파일을 수정하거나, 기타 코드를 실행할 수 있다.
  • 리소스(Resources): LLM이 요청할 수 있고, 자신의 컨텍스트 안에 추가할 수 있는 읽기 전용 데이터다. 문서, 파일, 기타 구조화된 데이터 등이 해당된다.
  • 프롬프트(Prompts): 사용자가 호출할 수 있는 템플릿이다(보통 슬래시 뒤에 이름을 입력한다). 특정 작업을 위한 프롬프트를 생성한다.

만약 여러분에게 TDocumentService 클래스가 있어서, 몇 가지 도구를 구현한다면, 그 서버 구성은 다음과 같은 모습이다.

uses
  MCPConnect.JRPC.Server,
  MCPConnect.MCP.Server.Api, // 표준 MCP API를 등록하는 역할
  MCPConnect.Configuration.MCP,

  Demo.DocumentService; // 여러분의 MCP 클래스들이 담겨있는 유닛

// 해당 JSON-RPC 서버를 생성한다
FJRPCServer := TJRPCServer.Create(Self);
FJRPCServer
  .Plugin.Configure<IMCPConfig>
    .Server
      .SetName('delphi-mcp-server')
      .SetVersion('2.0.0')
    .BackToMCP
    .Tools
      .RegisterClass(TDocumentService)  // Register your tool class here
    .BackToMCP
  .ApplyConfig;

이 구성은 여러 섹션으로 나뉜다(.Server, .Tools, .Resources, .Prompts 등). .BackToMCP는 한 섹션을 닫고 MCP 구성으로 돌아간다. .ApplyConfig는 플러그인 전체를 닫고 서버로 돌아간다. 그래서 그 뒤에 또 다른 .Plugin.Configure<...>를 이어서 연결할 수 있다.

MCP 트랜스포트 프로토콜의 세부 사항

MCP 메시지는 JSON-RPC로 인코딩된다. 이 프로토콜은 두 가지 트랜스포트를 지원한다.

  • Stdio: 서버가 콘솔 애플리케이션이다. 표준 입력에서 요청을 읽고 표준 출력에 응답을 쓴다. 예전 CGI와 다소 비슷하다.
  • Streamable HTTP: 일반 HTTP다. 연결을 계속 열어둘 수 있는 옵션을 갖추고 있다. 그래서 서버가 폴링 없이도 클라이언트에게 알림을 보낼 수 있다.

ℹ️ 참고

2026-07-28 버전에서는, JSON-RPC 레이어가 더 이상 MCPConnect에 내장되어 있지 않다. 이제는 별도 라이브러리인 Delphi-JRPC다. Delphi-JRPC는 JSON-RPC 2.0(서버와 클라이언트 모두)을 구현한다. 더 이상, MCP에 의존하지 않는다. 그래서 MCP와 상관없이, 원격 API를 노출하거나 호출해야 하는 Delphi 애플리케이션에서 사용할 수 있다. MCPConnect는 이 라이브러리를 의존성으로 사용한다. 그리고 그 유닛들은 MCPConnect. 접두사를 잃었다(예를 들어 MCPConnect.JRPC.Server는 JRPC.Server가 된다).

측면 stdio Streamable HTTP
범위 로컬 로컬 + 원격
배포 호스트가 실행 독립형 서비스로 실행
확장성 프로세스당 클라이언트 하나 여러 클라이언트 동시 지원
보안 암묵적(로컬 프로세스) 구성 필요(토큰, OAuth)
서버 알림 제한적 완전 지원
디버깅 더 어려움 더 쉬움
일반적인 용도 개발 / 로컬 도구 운영 / 클라우드

MCPConnect는 둘 다 지원한다. 어떤 것을 사용할지는, 프로젝트의 종류와 개발 단계에 따라 달라진다. 예를 들어, HTTP 서버를 디버깅하는 것은 stdio 서버를 디버깅하는 것보다 훨씬 쉽다. 서버 구성은 트랜스포트에 의존하지 않는다. 그래서 같은 애플리케이션이 둘 다 지원하면서, 설정이나 커맨드라인 파라미터를 통해 하나를 고를 수 있다.

MCP 트랜스포트 다이어그램

Stdio를 통해 Claude Desktop 그리고 다른 AI 엔진들을 내 MCP 서버에 연결하는 방법

stdio를 사용하면, 호스트(예를 들어 Claude Desktop)가 여러분의 실행 파일을 시작하고, 표준 입출력을 통해 그 실행 파일과 대화한다. MCPConnect 안에서 여러분에게 필요한 것은 오직 TJRPCStdioServer를 가진 콘솔 애플리케이션이다.

program DocumentServer;

{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  MCPConnect.JRPC.Server,
  MCPConnect.MCP.Server.Api,
  MCPConnect.Configuration.MCP,
  MCPConnect.Transport.Stdio,

  Demo.DocumentService;

var
  LServer: TJRPCStdioServer;
begin
  LServer := TJRPCStdioServer.Create(nil);
  try
    LServer.JRPCServer
      .Plugin.Configure<IMCPConfig>
        // ... 동일한 구성 (앞에 있는대로)
      .ApplyConfig;

    // 해당 클라이언트가 이 파이프들을 닫을 때 까지
    LServer.StartServerAndWait;
  finally
    LServer.Free;
  end;
end.

⚠ 경고

stdio 서버에서는, 표준 출력이 프로토콜의 몫이다. 뭔가를 로깅해야 한다면, ErrOutput이나 파일에 써야 한다. 절대로 일반 Writeln을 쓰면 안 된다.

가장 쉬운 방법은, Logify를 사용하는 것이다. MCPConnect는 이미 이 로깅 라이브러리에 의존하고 있다. Logify는 로깅 파사드(facade)다. 여러분의 코드(그리고 MCPConnect 자신도 자체 프로토콜 트레이스와 타이밍을 위해 Logify를 사용하고 있다)는 로그 기록을 단일 Logger에 한다. 실제 목적지는, 시작할 때 여러분이 등록하는 어댑터에 의해 결정된다. 예를 들어, 디버그 어댑터는 메시지를 OutputDebugString으로 보낸다. 그래서 디버깅하는 동안 RAD Studio 이벤트 로그에서 그 메시지를 읽을 수 있다. 표준 출력은 전혀 건드리지 않는다.

uses
  Logify, Logify.Adapter.Debug;

// 기동 시점: route all the log messages to OutputDebugString
TLoggerAdapterRegistry.Instance.RegisterFactory(
  TLogifyAdapterDebugFactory.CreateAdapterFactory('Debug log', TLogLevel.Debug));

// 여러분의 코드 안 어디에서든 (tools 도 마찬가지로)
Logger.LogDebug('Listing documents for category %s', [ACategory]);

디버거 밖에 로그를 남기고 싶다면, 다른 어댑터를 등록하면 된다(예를 들어 파일에 쓰는 어댑터). 로깅 코드는 그대로 유지된다. 레벨을 Debug 대신 Info로 설정하면, MCPConnect의 요청별 메시지가 숨겨진다.

MCP 통신에 Streamable HTTP를 사용하는 방법

HTTP의 경우, Indy(TJRPCIndyServer, 독립형 HTTP 서버)와 WebBroker(TJRPCDispatcher, 독립형·ISAPI·CGI WebBroker 애플리케이션용) 중에서 고를 수 있다. 다음은 Indy 버전이다.

uses
  MCPConnect.JRPC.Server,
  MCPConnect.MCP.Server.Api, // 표준 MCP API를 등록하는 역할
  MCPConnect.Transport.Indy, // TJRPCIndyServer
  MCPConnect.Configuration.MCP,

  Demo.DocumentService; // 여러분의 MCP 클래스들이 담겨있는 유닛

// 해당 HTTP server를 생성한다: it creates and owns its own JSON-RPC server
FServer := TJRPCIndyServer.CreateMCPServer(Self);

// 해당 JSON-RPC Server를 구성한다 (same code as before)
FServer.JRPCServer
  .Plugin.Configure<IMCPConfig>
    // ...
  .ApplyConfig;

// Start listening
FServer.DefaultPort := 8080;
FServer.Active := True;

CreateMCPServer는 Indy HTTP 서버를 그 TJRPCServer와 함께 생성하고, MCP 요청 처리를 연결한다. POST를 통한 JSON-RPC, 알림용 SSE 스트림, CORS, 세션, 인증이 여기에 포함된다. 서버가 활성화되면, 클라이언트는 그 주소로 연결할 수 있다. 예를 들면 http://localhost:8080/mcp다. TJRPCIndyServer는 Indy의 HTTP 서버를 상속한다. 그래서 일반적인 Indy 프로퍼티들(Bindings, DefaultPort, HTTPS용 IOHandler 등)을 모두 사용할 수 있다.

MCP 사용 예시: 글쓰기 도구 MCP 도구 만들기

MCPConnect를 사용하면, 프로토콜의 세부 사항을 다루지 않고도 MCP 서버를 작성할 수 있다. 특정 카테고리의 문서 목록을 LLM에게 제공하고 싶다고 해보자. Delphi에서는 다음과 같이 작성하면 된다.

TDocumentService = class
public
  function ListDocument(const ACategory: string): string;
end;

MCPConnect는 더 복잡한 반환 타입(TArray<TDocument>, TStringList, TImage 등)도 처리할 수 있다. 하지만 결과는 어차피 LLM의 컨텍스트 안으로 들어간다. 그래서 문자열 하나로 충분한 경우가 많다.

ListDocument가 결과를 어떻게 만드는지는 중요하지 않다. MCPConnect가 그 메서드를 LLM에게 설명해 주고, LLM이 언제 어떻게 그 메서드를 호출할지 결정한다.

하지만 그러려면, 그 클래스가 무엇을 위한 것이고 각 메서드가 무엇을 하는지 LLM에게 알려줘야 한다. MCPConnect에서는, 이 작업을 대부분 애트리뷰트(attribute)로 한다.

TDocumentService = class
public
  // 이 메서드는 published이다. 하나의 MCP tool이 된다
  [McpTool('doclist', 'List all the available documents')]
  function ListDocument(
    [McpParam('category', 'Document Category')] const ACategory: string
  ): string;

  // 이 메서드는 노출되지 "않는다" 왜냐하면 [McpTool] 애트리뷰트가 없기 때문이다
  procedure InternalStuff;
end;

McpTool과 McpParam은 두 개의 파라미터를 받는다. 이름과 설명이다. 이름은 LLM이 보는 값이다(예제에서는 doclist와 category다). Delphi 메서드나 파라미터 이름과 일치할 필요는 없다. 그래서 코드는 일반적인 Pascal 네이밍 규칙을 그대로 유지할 수 있다.

💡 팁 — 설명은 필수다

LLM은 도구나 파라미터가 무엇을 위한 것인지, 사용해야 할지, 어떻게 사용해야 할지 이해하기 위해 설명에 의존한다. 그러니 시간을 들여 잘 작성하자. 그 도구가 무엇을 하는지, 무엇을 반환하는지, 언제 사용해야 하는지를 적는다. "List all the available documents"(사용 가능한 모든 문서를 나열)도 작동은 한다. 하지만 "Returns the title and date of the documents in a category; use it before reading a document"(카테고리 안 문서들의 제목과 날짜를 반환한다. 문서를 읽기 전에 사용한다)는 LLM에게 훨씬 더 많은 것을 알려준다.

클래스가 등록되면, MCPConnect는 RTTI를 통해 이 애트리뷰트들을 읽어서, 클라이언트가 받아 LLM에게 전달하는 도구 정의를 만든다.

MCPConnect 애트리뷰트가 도구 정의로 만들어지는 과정

런타임에, LLM이 그 도구를 사용하기로 결정하면, 호출은 호스트에서 여러분의 Delphi 메서드로 갔다가 다시 돌아온다.

MCP 도구 호출 흐름

MCP 도구가 결과를 AI 모델에게 돌려보내는 방법

예제에서는 도구가 문자열을 반환한다. 이것으로 충분한 경우가 많다. 하지만 도구가 무엇을 하는지에 따라 달라진다. 대략적으로, 도구는 두 그룹으로 나뉜다.

  • 작업형(Operational): 파일을 쓰거나, 데이터베이스 데이터를 변경하거나, 이메일을 보내는 등 무언가를 수행하는 도구다. 결과는 그 작업의 결과만으로 충분할 수 있다. 불리언 값이나, 오류를 설명하는 문자열이다.
  • 정보형(Informational): 데이터를 조회해서 컨텍스트에 추가하는 도구다. 데이터에 따라, 문자열로 충분할 수도 있고, 구조화된 형식이 필요할 수도 있다.

MCPConnect가 변환을 대신 처리해 준다. 함수가 오브젝트를 반환하면, 알맞은 형식으로 자동 직렬화된다.

한 가지 경우는 자세히 살펴볼 만하다. TContentList 클래스다. 이 클래스는, MCP가 정의한 도구 결과 형식에 직접 대응한다. 그 형식에서는, 응답이 여러 부분으로 이루어질 수 있고, 각 부분은 자신만의 타입(텍스트, 이미지, 오디오, 링크, BLOB)을 가진다. TContentList는 각 타입마다 플루언트(fluent) 메서드를 가지고 있다(AddText, AddImage, AddAudio, AddLink, AddBlob).

function TDelphiDayTool.BuyTicket(AId, AQuantity: Integer): TContentList;
begin
  var LTicketStream := FCart.BuyTicket(AId, AQuantity);
  try
    // TContentList 해제는 이 프레임워크가 직렬화 후에 수행한다
    Result := TContentList.Create
      .AddText('Purchase completed successfully.')
      .AddImage('image/png', LTicketStream); // 이 스트림이 읽혀지고, base64로 인코딩된다
  finally
    LTicketStream.Free;
  end;
end;

내 코드에서 MCP 세션 관리를 다루는 방법

MCP에는 세션이라는 개념이 있다. stdio에서는 암묵적이고, Streamable HTTP에서는 명시적이다.

⚠ 경고

MCPConnect의 다음 버전(2026-07-28 스펙용)은 세션을 완전히 제거한다. 코드가 계속 작동하길 원한다면, 꼭 필요한 경우가 아니라면 고정 세션(sticky session)에 의존하지 말자. 도구를 상태 없이(stateless) 유지하고, 필요한 상태는 도구 파라미터로, 또는 여러분이 직접 저장하는 데이터를 가리키는 식별자로(예를 들어 이전 도구 호출이 반환한 카트 ID) 명시적으로 전달하자.

MCPConnect는 세션을 대신 관리해 준다. 모든 도구는 현재 세션의 데이터를 읽고 쓸 수 있다. 세션은 TMCPSessionData(데이터를 JSON으로 저장)일 수도 있고, TMCPSessionBase에서 파생된 어떤 클래스든 될 수 있다.

다음 코드는, 사용자가 주문을 만드는 동안 장바구니를 유지하는 커스텀 세션 클래스(TShoppingSession)를 구성한다.

uses
  ...
  MCPConnect.Configuration.Session,
  MCPConnect.Session.Core;

type
  TShoppingSession = class(TMCPSessionBase)
  private
    FCart: TObjectDictionary<string, TCartItem>;
  public
    constructor Create;
    destructor Destroy; override;
    property Cart: TObjectDictionary<string, TCartItem> read FCart;
  end;

begin
  ...
  FJRPCServer
    .Plugin.Configure<ISessionConfig>
      .SetLocation(TSessionIdLocation.Header) // 디펄트
      .SetHeaderName('Mcp-Session-Id') // 디펄트
      .SetTimeout(30)  // 타임아웃 30 분 (디펄트)
      .SetSessionClass(TShoppingSession)  // 사용자 정의 타입 세션(custom typed session) 사용
    .ApplyConfig;

도구 클래스 안에서는, [Context] 애트리뷰트가 붙은 필드에 현재 세션이 주입된다.

  TDelphiDayTool = class
  private
    [Context] FSession: TShoppingSession;
  public
    [McpTool('cart_clear', 'Removes all the tickets from the shopping cart')]
    procedure ClearCart;
  end;

//-----

procedure TDelphiDayTool.ClearCart;
begin
  FSession.Cart.Clear;
end;

MCP 세션 다이어그램

맺음말 – MCP와 MCPConnect 도구에 대해 배운 내용 정리

MCPConnect는 아직 젊은 라이브러리다. 하지만 이미 많은 것을 다루고 있다. 이 글에서는 주요 주제들만 살짝 건드렸을 뿐이다. 전체 문서는 MCPConnect 문서에서, 소스 코드는 이 프로젝트의 GitHub 홈에서 확인할 수 있다. 저장소 안의 데모들은 다른 여러 사례를 다룬다. 각 요소들이 어떻게 맞물리는지 보기에 좋은 방법이다.

시작하려면, Demo/MCPServerSimple/Stdio에 있는 stdio 데모를 열고 Claude Desktop에 연결해 보자. 몇 분 안에, LLM이 여러분의 Delphi 코드를 호출하는 모습을 볼 수 있다.

이 글에서 다루지 않았지만 곧 다룰 예정인 MCPConnect의 다른 기능들은 다음과 같다.

  • 리소스, 리소스 템플릿, 프롬프트
  • MCP 앱(호스트가 렌더링하는 인터랙티브 HTML UI)
  • 컨텍스트 주입(Context injection)
  • 플루언트 구성(Fluent configuration)
  • 가비지 컬렉션
  • 도구 네임스페이싱(namespacing)
  • 보안(토큰과 OAuth 인증)

이 글은 MCP Connect의 제작자인 MVP Paolo Rossi와 Luca Minuti가 기고한 게스트 블로그 글이다.

이 토의에 참여하세요

지금 바로 의견을 남길 수 있습니다. 그리고 나서 가입해도 됩니다. 이미 회원이라면, 지금 로그인하고 본인 계정으로 의견을 남기세요.

Guest
이 토픽(기고/질문)에 답하기

×   서식있는 텍스트로 붙여넣기.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   이전에 작성한 콘텐츠가 복원되었습니다..   편집창 비우기

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

중요한 정보

이용약관 개인정보보호정책 이용규칙 이 사이트가 더 잘 작동하기 위해 방문자의 컴퓨터에 쿠키가 배치됩니다. 쿠키 설정 변경에서 원하는 설정을 할 수 있습니다. 변경하지 않으면 쿠키를 허용하는 것으로 이해합니다.