Skip to content

Lang2SQL on Chat service

DShomin edited this page May 23, 2025 · 6 revisions

Lang2SQL 프로젝트를 진행하면서 고성능 프론티어 모델이나 값비싼 API를 활용하지 못하는 점이 늘 아쉬웠습니다. 이러한 고민은 "어떻게 하면 경제적으로 Lang2SQL을 사용할 수 있을까?"라는 질문으로 이어졌고, 별도의 API 비용 없이 많은 사람이 사용하는 채팅 서비스에서 동작한다면 저렴하게 서비스를 운영할 수 있겠다는 아이디어로 발전했습니다.

MCP.-.1080WebShareName.mov

#MCP #Supabase #Agentic #MCP_super_assistant

RAG with Model Context Protocol

기존 Lang2SQL에서는 데이터베이스의 테이블 정보를 검색할 수 있는 RAG(Retrieval Augmented Generation) 기능이 구현되어 있었습니다. 당시 RAG의 Vector DB는 Faiss로 구현되었지만, 이번 기회에 Supabase의 PostgreSQL을 활용하여 Vector DB를 구축하고, RAG를 수행하는 코드를 MCP(Model Context Protocol)로 만들어 활용성을 높였습니다.

하지만 아쉽게도 현재 MCP를 공식적으로 지원하는 서비스는 Anthropic의 Claude가 유일하여 ChatGPT, Gemini, Grok 등 다른 LLM 사용자들은 MCP의 이점을 누리기 어려운 상황입니다. 이러한 문제를 해결하기 위해 MCP SuperAssistant 서비스가 등장했습니다.

MCP SuperAssistant 소개

MCP SuperAssistant는 Chrome 확장 프로그램으로 설치하여 사용할 수 있습니다. 이 확장 프로그램은 각 채팅 서비스(예: ChatGPT) 내에서 클라이언트 역할을 수행하며, 서버에서 사용 가능한 MCP 도구들을 확인하고 연동하는 기능을 제공합니다.

작동 원리를 간단히 설명하자면, 채팅 서비스 내에서 직접 MCP가 동작하는 것이 아니라, MCP 활용을 위한 프롬프트를 자동으로 텍스트 파일 형태로 생성하고 이를 채팅에 첨부하여 MCP 도구 호출(tool calling)을 유도합니다. 이후 도구 호출이 감지되면 확장 프로그램이 이를 인지하고 사용자에게 함수 실행 권한을 요청합니다. 사용자가 승인하면 함수가 실행되고, 그 결과 역시 프롬프트 형태로 변환되어 다음 명령으로 이어져 최종적으로 사용자가 요청한 내용이 해결되는 방식입니다.

설정 과정

image

1. Supabase 기반 RAG 구축

Supabase는 애플리케이션 백엔드를 손쉽게 구축하고 관리할 수 있도록 지원하는 오픈소스 플랫폼입니다. 백엔드의 핵심 요소인 데이터베이스를 시작으로 Edge function, Authentication 등 다양한 기능을 별도의 서버 구축 없이 구현할 수 있게 돕습니다. 이번 프로젝트에서는 Supabase의 데이터베이스 기능을 활용하며, 특히 PostgreSQL 기반이므로 pgvector 확장 기능을 사용하여 RAG를 위한 Vector DB를 구성합니다.

기존 Vector DB(Faiss)에서 Documents 추출

기존에 langchain으로 저장된 Faiss Vector DB를 로드할 때는 임베딩 함수가 필요합니다. 여기서는 실제 임베딩을 수행하지 않고 Document 객체만 추출하기 위해 fake_embeddings 함수를 정의하여 활용합니다. 이후 추출된 Document 객체들을 리스트에 저장합니다.

from typing import List
from langchain_community.vectorstores import FAISS
from langchain.schema import Document # Document 타입을 명시적으로 import

def fake_embeddings(texts: List[str] = None, embeddings: List[float] = None): # 파라미터명과 타입을 보다 명확히 수정
    # 이 함수는 실제 임베딩을 반환하지 않고, FAISS 로드 시 형식적으로만 사용됩니다.
    return None

# <vector_db_path>를 실제 Faiss DB가 저장된 경로로 변경해야 합니다.
index = FAISS.load_local(
    "<vector_db_path>", fake_embeddings, allow_dangerous_deserialization=True
)
docstore_dict = index.docstore._dict

docs = list()
for doc_id, document in docstore_dict.items():
    docs.append(document)

추출된 Document를 임베딩하여 Supabase에 업로드

먼저 필요한 라이브러리를 설치합니다.

pip install supabase langchain-openai

이제 추출한 docs를 OpenAI 임베딩 모델을 사용해 임베딩하고 Supabase DB documents Table에 업로드합니다.

Supabase에 documents Table을 생성하는 내용은 아래 확장버튼을 눌러 확인하세요

Supabase 프로젝트 documents 테이블 만들기

documents라는 이름을 갖는 Table을 정의하는 SQL으로 Supabase dashboard의 SQL editor에서 아래 SQL을 실행하면 Table이 생성됩니다. 아래 SQL에는 Table 뿐만 아니라 RAG에서 사용하게 될 retrieve 함수도 같이 정의합니다. 필요에 따라 retrieve 함수를 변경하여 hybrid search도 가능합니다. image

-- Enable the pgvector extension to work with embedding vectors
create extension if not exists vector;

-- Create a table to store your documents
create table
  documents (
    id uuid primary key,
    content text, -- corresponds to Document.pageContent
    metadata jsonb, -- corresponds to Document.metadata
    embedding vector (1536) -- 1536 works for OpenAI embeddings, change as needed
  );

-- Create a function to search for documents
create function match_documents (
  query_embedding vector (1536),
  filter jsonb default '{}'
) returns table (
  id uuid,
  content text,
  metadata jsonb,
  similarity float
) language plpgsql as $$
#variable_conflict use_column
begin
  return query
  select
    id,
    content,
    metadata,
    1 - (documents.embedding <=> query_embedding) as similarity
  from documents
  where metadata @> filter
  order by documents.embedding <=> query_embedding;
end;
$$;
from supabase import create_client, Client # Client 타입을 명시적으로 import
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import SupabaseVectorStore

# Supabase 접속 정보 (실제 값으로 대체 필요)
supabase_url = "<insert_your_supabase_url>"
supabase_key = "<insert_your_supabase_key>"
supabase: Client = create_client(supabase_url, supabase_key)

# OpenAI API 키 (실제 값으로 대체 필요)
openai_api_key = "<insert_your_openai_api_key>" 
embeddings = OpenAIEmbeddings(
    model="text-embedding-3-small", openai_api_key=openai_api_key
)

# Document와 임베딩 값을 Supabase에 업로드
vectorstore = SupabaseVectorStore.from_documents(
    docs,
    embeddings,
    client=supabase,
    table_name="documents", # Supabase에 생성될 테이블명
)

위 절차를 마치면 Supabase의 Table Editor에서 업로드된 데이터와 벡터값을 확인할 수 있습니다.

image

2. RAG 도구를 MCP로 정의하기

업로드된 Vector DB를 활용하여 RAG의 검색(retrieve) 기능을 정의하고, LLM이 이 함수를 도구로 인식하고 호출할 수 있도록 docstring을 상세히 작성합니다. 이후, 정의된 도구를 활성화하는 MCP 서버 스크립트를 완성하면 MCP를 사용할 준비가 끝납니다.

MCP 서버 환경 설정은 uv를 활용하는 것이 권장됩니다. 작업 공간에서 uv init을 실행하여 초기화한 후, 다음 명령어로 필요한 라이브러리를 설치합니다. uv add "mcp[cli]" 이외에 MCP 설치가 필요하다면 uv add langchain과 같은 명령어로 설치하면 가능합니다.

mcp_server.py

from mcp.server.fastmcp import FastMCP
from dotenv import load_dotenv
from supabase import create_client, Client
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import SupabaseVectorStore
import os
from langchain.schema import Document

# .env 파일에서 환경 변수 로드 (OPENAI_API_KEY, SUPABASE_URL, SUPABASE_KEY 등)
load_dotenv()

# MCP 서버 이름 (자유롭게 정의 가능)
mcp = FastMCP("lang2SQL_MCP")

def docs_to_str(docs: list[Document]) -> str:
    """
    검색된 Document 리스트를 단일 문자열로 변환합니다.
    """
    return "\n".join([doc.page_content for doc in docs])

@mcp.tool()
async def retrieve_table(question: str) -> str:
    """
    RAG 시스템의 일부로, SQL 쿼리 생성을 위해 데이터베이스에서 테이블 정보를 검색합니다.

    Args:
        question: 테이블 정보를 검색하기 위한 사용자 질문입니다.

    Returns:
        데이터베이스에서 검색된 테이블 정보 문자열입니다.
    """
    supabase_url: str = os.getenv("SUPABASE_URL")
    supabase_key: str = os.getenv("SUPABASE_KEY")
    supabase: Client = create_client(supabase_url, supabase_key)

    embeddings = OpenAIEmbeddings(
        model="text-embedding-3-small",
        openai_api_key=os.getenv("OPENAI_API_KEY"), # 환경 변수명 일관성 있게 수정 (OPEN_AI_LLM_KEY -> OPENAI_API_KEY)
    )

    vector_db = SupabaseVectorStore(
        client=supabase, # 파라미터 순서 변경 (client가 먼저)
        embedding=embeddings, # 파라미터명 변경 (embeddings -> embedding)
        table_name="documents",
        query_name="match_documents", # Supabase에서 사용할 함수명 (pgvector 설치 시 자동 생성되는 함수 활용 가능)
    )

    retriever = vector_db.as_retriever()
    retrieved_docs = retriever.get_relevant_documents(question) # 변수명 변경 (docs -> retrieved_docs)

    return docs_to_str(retrieved_docs)

def run_server(transport: str = "stdio"):
    """MCP 서버를 실행합니다.

    Args:
        transport: 통신 방식 ("stdio" 또는 "sse")
    """
    mcp.run(transport=transport)

if __name__ == "__main__":
    # 일반적으로 웹 기반 상호작용을 위해 "sse" (Server-Sent Events)를 사용합니다.
    run_server(transport="sse")

3. MCP SuperAssistant 설정

MCP SuperAssistant를 활용하려면 Chrome 확장 프로그램 설치와 사용하려는 MCP의 환경 설정(config) 정의가 필요합니다. 설정이 완료되면 npx 명령어로 클라이언트를 실행합니다.

MCP SuperAssistant Chrome 확장 프로그램 설치

MCP SuperAssistant Chrome Extension 설치 링크를 통해 Chrome에 확장 프로그램을 추가합니다.

MCP Config 정의

MCP Config는 JSON 형태로 저장해야 하며, 이번 글에서 정의한 RAG MCP 외에도 외부 MCP를 함께 사용할 수 있습니다. lang2SQL_MCP 정의를 위해서는 MCP 서버 스크립트(mcp_server.py)가 위치한 경로(<insert_your_mcp_path>)를 정확히 명시해야 합니다.

rag_mcp_config.json

{
    "mcpServers": {
        "sequential-thinking": {
            "command": "npx",
            "args": [
                "-y",
                "@modelcontextprotocol/server-sequential-thinking"
            ]
        },
        "lang2SQL_MCP": {
            "command": "uv",
            "args": [
                "--directory",
                "<insert_your_mcp_path>",
                "run",
                "mcp_server.py"
            ]
        }
    }
}
MCP 클라이언트 실행

다음 명령어를 사용하여 MCP 클라이언트를 실행합니다.

npx @srbhptl39/mcp-superassistant-proxy@latest --config ./rag_mcp_config.json
MCP SuperAssistant와 채팅 서비스 연동 실행

Chrome 확장 프로그램 설치 후 Gemini 홈페이지 등에 접속하면, 채팅 입력창 근처에 MCP 버튼이 새롭게 생성된 것을 확인할 수 있습니다.

image

MCP 버튼을 클릭하면 팝업창이 나타나며, MCP 도구 호출을 위한 프롬프트가 자동으로 생성됩니다.

image

MCP를 활용하기 위해 "Attach" 버튼을 클릭하면, "Instructions" 아래에 보이는 프롬프트가 현재 사용 중인 채팅 서비스의 입력창에 자동으로 첨부됩니다.

image

이제 모든 준비가 완료되었습니다. 첨부된 프롬프트와 함께 질문을 입력하면, 다음과 같이 우리가 정의한 MCP 도구(retrieve_table)가 호출되는 것을 확인할 수 있습니다.

image

호출된 도구 옆의 "Run" 버튼을 누르면 함수가 실행되고, 실행이 완료되면 버튼이 "Insert"와 "Attach File" 버튼이 나타납니다. "Insert" 버튼을 클릭하면 검색 결과를 다음 명령어(LLM에게 전달되는 프롬프트)에 포함하여 활용할 수 있습니다.

image

위와 같이 검색 결과를 포함한 명령을 실행하면, 아래처럼 현재 사용 중인 데이터베이스 스키마에 맞는 SQL 쿼리가 생성되는 것을 확인할 수 있습니다.

image

요약 및 소감

과거에는 Anthropic의 Claude를 주로 사용하지 않아 Cursor 외에는 MCP를 활용할 기회가 많지 않아 아쉬움이 컸습니다. 하지만 MCP SuperAssistant 덕분에 다양한 환경에서 MCP를 경험해볼 수 있었습니다. Cursor에서의 MCP 경험도 훌륭했지만, Cursor는 '코딩'이라는 특정 작업에 초점을 맞춘 서비스이기에 MCP 기술의 다양한 활용 가능성을 직접적으로 체감하기에는 한계가 있었습니다. 이번 글을 통해 MCP가 코딩뿐만 아니라 다양한 분야와 서비스에서 유용하게 활용될 수 있다는 잠재력을 확인하게 되어 매우 의미 있는 경험이었습니다. 앞으로 MCP 생태계가 더욱 확장되어 더 많은 사용자가 그 이점을 누릴 수 있기를 기대합니다.

case 1: https://g.co/gemini/share/f9fb06338167

Clone this wiki locally