Labsco
kyopark2014 logo

MCP Agent

โ˜… 11

from kyopark2014

A lightweight, local MCP server in Python that enables RAG search through AWS Lambda.

๐Ÿ”ฅ๐Ÿ”ฅโœ“ VerifiedFreeAdvanced setup

MCP Agent ํ™œ์šฉํ•˜๊ธฐ

MCP(Model Context Protocol)์€ ์ƒ์„ฑํ˜• AI application์ด ์™ธ๋ถ€ ๋ฐ์ดํ„ฐ๋ฅผ ํ™œ์šฉํ•˜๋Š” ์ฃผ์š”ํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ๋น ๋ฅด๊ฒŒ ํ™•์‚ฐ๋˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—์„œ MCP๋ฅผ ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•ด ๋ณผ์ˆ˜ ์žˆ๋„๋ก local ํ™˜๊ฒฝ์—์„œ MCP์™€ agent๋ฅผ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค. Cloud ๋ฐฐํฌ๋“ฑ ์กฐ๊ธˆ ๋” ์ƒ์„ธํ•œ ๋‚ด์šฉ์€ kyopark/mcp์„ ์ฐธ์กฐํ•ฉ๋‹ˆ๋‹ค.

MCP ํ™œ์šฉ

MCP Basic

์‚ฌ์šฉ์ž๋Š” ์ž์‹ ์˜ Computer์— ์„ค์น˜๋œ Claude Desktop, Cursor์™€ ๊ฐ™์€ AI ๋„๊ตฌ๋ฟ ์•„๋‹ˆ๋ผ ์ฃผ๋กœ Agentํ˜•ํƒœ๋กœ ๊ฐœ๋ฐœ๋œ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ํ†ตํ•ด MCP ์„œ๋ฒ„์— ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. MCP server๋Š” MCP client์˜ ์š”์ฒญ์— ์ž์‹ ์ด ํ• ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ capability๋กœ ์ œ๊ณตํ•˜๊ณ  client์˜ ์š”์ฒญ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. MCP server๋Š” local computer์˜ ํŒŒ์ผ์ด๋‚˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์กฐํšŒํ•  ์ˆ˜ ์žˆ์„๋ฟ ์•„๋‹ˆ๋ผ ์ธํ„ฐ๋„ท์— ์žˆ๋Š” ์™ธ๋ถ€ ์„œ๋ฒ„์˜ API๋ฅผ ์ด์šฉํ•ด ํ•„์š”ํ•œ ์ •๋ณด๋ฅผ ์กฐํšŒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. MCP Client๋Š” Server์™€ JSON-RPC 2.0 ํ”„๋กœํ† ์ฝœ์„ ์ด์šฉํ•ด ์—ฐ๊ฒฐ๋˜๋Š”๋ฐ, stdio๋‚˜ SSE (Server-Sent Events)์„ ์„ ํƒํ•˜์—ฌ, Host์˜ ์š”์ฒญ์„ MCP์— ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๊ณ , ์‘๋‹ต์„ ๋ฐ›์•„์„œ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

MCP์˜ ์ฃผ์š” ์š”์†Œ์˜ ์ •์˜์™€ ๋™์ž‘์€ ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • MCP Hosts: MCP ํ”„๋กœํ† ์ฝœ์„ ํ†ตํ•ด ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•˜๋Š” ํ”„๋กœ๊ทธ๋žจ/AI ๋„๊ตฌ๋กœ์„œ Claude Desktop, Cursor, User Agent Application์ด ํ•ด๋‹น๋ฉ๋‹ˆ๋‹ค.
  • MCP Clients: MCP Server์™€ 1:1๋กœ ์—ฐ๊ฒฐ์„ ์ˆ˜ํ–‰ํ•˜๋Š” Client๋กœ์„œ MCP Server์™€ stdio ๋˜๋Š” Streamable HTTP ๋ฐฉ์‹์œผ๋กœ ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • MCP Servers: ํ‘œ์ค€ํ™”๋œ MCP๋ฅผ ํ†ตํ•ด Client์— Tool์˜ Capability๋ฅผ ์•Œ๋ ค์ฃผ๋Š” ๊ฒฝ๋Ÿ‰ ํ”„๋กœ๊ทธ๋žจ์œผ๋กœ Local Computer์˜ ํŒŒ์ผ์ด๋‚˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๊ณ , ์™ธ๋ถ€ API๋ฅผ ์ด์šฉํ•ด ์ •๋ณด๋ฅผ ์กฐํšŒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • Local data sources: MCP ์„œ๋ฒ„๊ฐ€ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ๋กœ์ปฌ ๋ฐ์ดํ„ฐ
  • Remote services: API๋ฅผ ํ†ตํ•ด ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ ์™ธ๋ถ€ ์‹œ์Šคํ…œ

MCP๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์€ ์žฅ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • ํ‘œ์ค€ํ™”๋œ ๋ฐฉ์‹์œผ๋กœ ๋‹ค์–‘ํ•œ ๋ฐ์ดํ„ฐ ์†Œ์Šค์— ์ ‘๊ทผ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”๋“œ ๋ณ€๊ฒฝ ์—†์ด MCP ์„œ๋ฒ„ ์—…๋ฐ์ดํŠธ๋ฅผ ํ†ตํ•œ ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์กฐ์ง ์ „๋ฐ˜์— ๊ฑธ์ณ AI ์ง€์› ๋ฐ ํ™•์žฅ์ด ์šฉ์ดํ•ฉ๋‹ˆ๋‹ค.

MCP Server Components์—๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ํ•ญ๋ชฉ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • Tools (Model-controlled): LLM์ด ํŠน์ • ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ(๋„๊ตฌ)์œผ๋กœ์„œ, API์™€ ๊ฐ™์ด ํŠน์ •ํ•œ action์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
tools = await session.list_tools()
  • Resources (Application-controlled): ์ƒ์„ฑํ˜• AI ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ ‘๊ทผ ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐ์ดํ„ฐ ์†Œ์Šค์ž…๋‹ˆ๋‹ค. ๋ณต์žกํ•œ ๊ณ„์‚ฐ(significant computation)์ด๋‚˜ ๋ถ€์ž‘์šฉ(side effect)์—†์ด ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
resources = await session.list_resources()
  • Prompts (User-controlled): tool๋‚˜ resource๋ฅผ ์‚ฌ์šฉํ• ๋•Œ์— ์ด์šฉํ•˜๋Š” ์‚ฌ์ „ ์ •์˜๋œ ํ…œํ”Œ๋ ›์œผ๋กœ์„œ ์ถ”๋ก (inference)์ „์— ์„ ํƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
prompts = await session.list_prompts()

LangChain MCP Adapter

LangChain MCP Adapter๋Š” MCP๋ฅผ LangGraph agent์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๊ฒฝ๋Ÿ‰์˜ ๋žฉํผ(lightweight wrapper)๋กœ์„œ MIT ๊ธฐ๋ฐ˜์˜ ์˜คํ”ˆ์†Œ์Šค์ž…๋‹ˆ๋‹ค. MCP Adapter์˜ ์ฃผ๋œ ์—ญํ• ์€ MCP server๋ฅผ ์œ„ํ•œ tool๋“ค์„ ์ •์˜ํ•˜๊ณ , MCP client์—์„œ tools์˜ ์ •๋ณด๋ฅผ ์กฐํšŒํ•˜๊ณ  LangGraph์˜ tool node๋กœ ์ •์˜ํ•˜์—ฌ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ค๋‹ˆ๋‹ค.

MCP Server

RAG ๊ฒ€์ƒ‰์„ ์œ„ํ•œ MCP server๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์ •์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Server์˜ transport๋ฅผ "stdio"๋กœ ์ง€์ •ํ•˜๋ฉด server๋ฅผ ์ง€์† ์‹คํ–‰์‹œํ‚ค์ง€ ์•Š๋”๋ผ๋„, client๊ฐ€ server์˜ python code๋ฅผ ์ง์ ‘ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์–ด์„œ ํŽธ๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

from mcp.server.fastmcp import FastMCP 

mcp = FastMCP(
    name = "Search",
    instructions=(
        "You are a helpful assistant. "
        "You can search the documentation for the user's question and provide the answer."
    ),
) 

@mcp.tool()
def search(keyword: str) -> str:
    "search keyword"

    return retrieve_knowledge_base(keyword)

if __name__ =="__main__":
    print(f"###### main ######")
    mcp.run(transport="stdio")

Server๋Š” ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด, retrieve_knowledge_base()๋กœ RAG ๊ฒ€์ƒ‰์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. Server์˜ python code๋Š” ๊ฒฝ๋Ÿ‰(lightweight)์ด์–ด์•ผ ํ•˜๋ฏ€๋กœ, ์•„๋ž˜์™€ ๊ฐ™์ด lambda๋ฅผ triggerํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ตฌ์„ฑํ•˜์˜€์Šต๋‹ˆ๋‹ค. Lambda์—์„œ๋Š” retrieve, grade, generation์˜ ๋™์ž‘์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์•„๋ž˜์™€ ๊ฐ™์ด "model_name"์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๊ณ , ํ•„์š”์— ๋”ฐ๋ผ์„œ๋Š” "grading"์„ ์„ ํƒ์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ๋ณ‘๋ ฌ์ฒ˜๋ฆฌ๋กœ ์†๋„๋ฅผ ๋น ๋ฅด๊ฒŒ ํ•˜๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ์—์€ "multi_region"์„ "Enable"๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ์ƒ์„ธํ•œ ์ฝ”๋“œ๋Š” lambda-rag๋ฅผ ์ฐธ์กฐํ•ฉ๋‹ˆ๋‹ค.

def retrieve_knowledge_base(query):
    lambda_client = boto3.client(
        service_name='lambda',
        region_name=bedrock_region
    )
    functionName = f"lambda-rag-for-{projectName}"
    payload = {
        'function': 'search_rag',
        'knowledge_base_name': knowledge_base_name,
        'keyword': query,
        'top_k': numberOfDocs,
        'grading': "Enable",
        'model_name': model_name,
        'multi_region': multi_region
    }
    output = lambda_client.invoke(
        FunctionName=functionName,
        Payload=json.dumps(payload),
    )
    payload = json.load(output['Payload'])
    return payload['response'], []

MCP Client

MCP client์ด ํ•˜๋‚˜์˜ MCP server๋งŒ ๋ณผ ๊ฒฝ์šฐ์—๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด stdio_client์™€ StdioServerParameters๋ฅผ ์ด์šฉํ•ด ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. MCP server์— ๋Œ€ํ•œ ์ •๋ณด๋Š” config.json์—์„œ ์ฝ์–ด์˜ค๊ฑฐ๋‚˜ streamlit์—์„œ ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ์ •๋ณด๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. load_mcp_server_parameters()์—์„œ๋Š” mcp_json์„ ์ฝ์–ด์™€์„œ StdioServerParameters์„ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค. config.json์˜ MCP server์— ๋Œ€ํ•œ ์ •๋ณด๋Š” AWS CDK๋กœ ๋ฐฐํฌํ›„ ์ƒ์„ฑ๋˜๋Š” output์—์„œ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

from mcp import ClientSession, StdioServerParameters

def load_mcp_server_parameters():
    mcp_json = json.loads(mcp_config)
    mcpServers = mcp_json.get("mcpServers")

    command = ""
    args = []
    if mcpServers is not None:
        for server in mcpServers:
            config = mcpServers.get(server)
            if "command" in config:
                command = config["command"]
            if "args" in config:
                args = config["args"]
            break

    return StdioServerParameters(
        command=command,
        args=args
    )

์•„๋ž˜์™€ ๊ฐ™์ด MCP server์— ๋Œ€ํ•œ ์ •๋ณด๋กœ stdio_client๋ฅผ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์ด๋•Œ tools์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ load_mcp_tools๋กœ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. Agent์—์„œ๋Š” tool ์ •๋ณด๋ฅผ bindํ•˜๊ณ  ainvoke๋ฅผ ์ด์šฉํ•ด ์š”์ฒญ๋œ ๋™์ž‘์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

from mcp.client.stdio import stdio_client
from langchain_mcp_adapters.tools import load_mcp_tools

async def mcp_rag_agent_single(query, st):
    server_params = load_mcp_server_parameters()

    async with stdio_client(server_params) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()
            tools = await load_mcp_tools(session)
            with st.status("thinking...", expanded=True, state="running") as status:       
                agent = create_agent(tools)
                agent_response = await agent.ainvoke({"messages": query})                

                result = agent_response["messages"][-1].content
            st.markdown(result)
            st.session_state.messages.append({
                "role": "assistant", 
                "content": result
            })            
            return result

MCP client๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ๋น„๋™๊ธฐ์ ์œผ๋กœ ์‹คํ–‰ํ•˜๊ธฐ ์œ„ํ•ด์„œ asyncio๋ฅผ ์ด์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค. ์ดํ›„ ์‚ฌ์šฉ์ž๊ฐ€ UI์—์„œ MCP Config๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋ฉด ์ •๋ณด๋ฅผ ์—…๋ฐ์ดํŠธ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

asyncio.run(mcp_rag_agent_single(query, st))

์„œ๋ฒ„ ์ •๋ณด๊ฐ€ ์—ฌ๋Ÿฟ์ธ ๊ฒฝ์šฐ์— langchain-mcp-adapters์—์„œ ์ œ๊ณตํ•˜๋Š” MultiServerMCPClient์„ ์ด์šฉํ•ฉ๋‹ˆ๋‹ค. ๋จผ์ €, ์•„๋ž˜์™€ ๊ฐ™์ด ์„œ๋ฒ„ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

def load_multiple_mcp_server_parameters():
    mcp_json = json.loads(mcp_config)
    mcpServers = mcp_json.get("mcpServers")

    server_info = {}
    if mcpServers is not None:
        command = ""
        args = []
        for server in mcpServers:
            config = mcpServers.get(server)
            if "command" in config:
                command = config["command"]
            if "args" in config:
                args = config["args"]

            server_info[server] = {
                "command": command,
                "args": args,
                "transport": "stdio"
            }
    return server_info

์ดํ›„ ์•„๋ž˜์™€ ๊ฐ™์ด MCP server์ •๋ณด์™€ MultiServerMCPClient๋กœ client๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. MCP server๋กœ ๋ถ€ํ„ฐ ๊ฐ€์ ธ์˜จ tool ์ •๋ณด๋Š” client.get_tools()๋กœ ๊ฐ€์ ธ์™€์„œ agent๋ฅผ ์ƒ์„ฑํ•  ๋•Œ์— ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. Single MCP server์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ainvoke๋กœ ์‹คํ–‰ํ•˜์—ฌ ๊ฒฐ๊ณผ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

from langchain_mcp_adapters.client import MultiServerMCPClient
asyncio.run(mcp_rag_agent_multiple(query, st))

async def mcp_rag_agent_multiple(query, st):
    server_params = load_multiple_mcp_server_parameters()
    async with  MultiServerMCPClient(server_params) as client:
        with st.status("thinking...", expanded=True, state="running") as status:                       
            tools = client.get_tools()
            agent = create_agent(tools)
            response = await agent.ainvoke({"messages": query})
            result = response["messages"][-1].content

        st.markdown(result)
        st.session_state.messages.append({
            "role": "assistant", 
            "content": result
        })
    return result

์—ฌ๊ธฐ์„œ๋Š” customize๊ฐ€ ์šฉ์ดํ•˜๋„๋ก agent๋ฅผ ์ •์˜ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

def create_agent(tools):
    tool_node = ToolNode(tools)

    chatModel = get_chat(extended_thinking="Disable")
    model = chatModel.bind_tools(tools)

    class State(TypedDict):
        messages: Annotated[list, add_messages]

    def call_model(state: State, config):
        system = (
            "๋‹น์‹ ์˜ ์ด๋ฆ„์€ ์„œ์—ฐ์ด๊ณ , ์งˆ๋ฌธ์— ์นœ๊ทผํ•œ ๋ฐฉ์‹์œผ๋กœ ๋Œ€๋‹ตํ•˜๋„๋ก ์„ค๊ณ„๋œ ๋Œ€ํ™”ํ˜• AI์ž…๋‹ˆ๋‹ค."
            "์ƒํ™ฉ์— ๋งž๋Š” ๊ตฌ์ฒด์ ์ธ ์„ธ๋ถ€ ์ •๋ณด๋ฅผ ์ถฉ๋ถ„ํžˆ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค."
            "๋ชจ๋ฅด๋Š” ์งˆ๋ฌธ์„ ๋ฐ›์œผ๋ฉด ์†”์งํžˆ ๋ชจ๋ฅธ๋‹ค๊ณ  ๋งํ•ฉ๋‹ˆ๋‹ค."
            "ํ•œ๊ตญ์–ด๋กœ ๋‹ต๋ณ€ํ•˜์„ธ์š”."
        )
        try:
            prompt = ChatPromptTemplate.from_messages(
                [
                    ("system", system),
                    MessagesPlaceholder(variable_name="messages"),
                ]
            )
            chain = prompt | model                
            response = chain.invoke(state["messages"])
        return {"messages": [response]}

    def should_continue(state: State) -> Literal["continue", "end"]:
        messages = state["messages"]    
        last_message = messages[-1]
        if isinstance(last_message, AIMessage) and last_message.tool_calls:
            return "continue"        
        else:
            return "end"

    def buildChatAgent():
        workflow = StateGraph(State)
        workflow.add_node("agent", call_model)
        workflow.add_node("action", tool_node)
        workflow.add_edge(START, "agent")
        workflow.add_conditional_edges(
            "agent",
            should_continue,
            {
                "continue": "action",
                "end": END,
            },
        )
        workflow.add_edge("action", "agent")
        return workflow.compile() 
    
    return buildChatAgent()

MCP Servers์˜ ํ™œ์šฉ

Model Context Protocol servers์—์„œ๋„ ์•„๋ž˜์™€ ๊ฐ™์€ ์„œ๋ฒ„๋“ค์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

Smithery์—์„œ MCP server๋ฅผ ์ฐพ์•„๋ณด๊ณ  ํ•„์š”ํ•œ ์„œ๋ฒ„๋ฅผ ์ฐพ์œผ๋ฉด ์ ‘์†ํ•  ์ˆ˜ ์žˆ๋Š” MCP ์„œ๋ฒ„ ์ •๋ณด๋ฅผ JSON ํ˜•ํƒœ๋กœ ์กฐํšŒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Smithery - Google Search Server์—์„œ ํ™•์ธํ•œ ๊ตฌ๊ธ€ ๊ฒ€์ƒ‰์šฉ MCP ์„œ๋ฒ„ ์ •๋ณด๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๊ฒ€์ƒ‰์—”์ง„ ID์™€ API Key๋ฅผ ํ•„์š”๋กœ ํ•ฉ๋‹ˆ๋‹ค.

{
  "mcpServers": {
    "google-search-mcp-server": {
      "command": "npx",
      "args": [
        "-y",
        "@smithery/cli@latest",
        "run",
        "@gradusnikov/google-search-mcp-server",
        "--config",
        "{\"googleCseId\":\"b5cd8c527fbd64b72\",\"googleApiKey\":\"AIzbSyDQlYpck8-9TbBSuxoew1luOGVB6unRPNk\"}"
      ]
    }
  }
}

์•„๋ž˜์™€ ๊ฐ™์ด json ํ˜•์‹์˜ ์„œ๋ฒ„์ •๋ณด๋ฅผ ์—…๋ฐ์ดํŠธ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜์—์„œ๋Š” mcp-server.py์—์„œ ์ •์˜ํ•œ search๋ฅผ ์ด์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

{
  "mcpServers": {
    "search": {
      "command": "python",
      "args": [
        "application/mcp-server.py"
      ]
    }
  }
}

Local์—์„œ ์‹คํ–‰ํ•˜๊ธฐ (MAC)

  1. ํ•„์ˆ˜๋Š” ์•„๋‹ˆ์ง€๋งŒ ์ •์ƒ์ ์ธ ์ง„ํ–‰์„ ์œ„ํ•ด์„œ๋Š” AWS CLI๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์ตœ์‹  ๋ฒ„์ „์˜ AWS CLI ์„ค์น˜ ๋˜๋Š” ์—…๋ฐ์ดํŠธ์— ๋”ฐ๋ผ ์„ค์น˜ํ›„ "aws configure" ๋ช…๋ น์œผ๋กœ AWS credential์„ ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค.

  2. venv๋กœ ํ™˜๊ฒฝ์„ ๊ตฌ์„ฑํ•˜๋ฉด ํŽธ๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ์ ๋‹นํ•œ ํด๋”๋ฅผ ๋งŒ๋“ค๊ณ  ์•„๋ž˜์™€ ๊ฐ™์ด ํ™˜๊ฒฝ์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

python -m venv venv
source venv/bin/activate
  1. ์†Œ์Šค๋ฅผ ๋‹ค์šด๋กœ๋“œ ํ•ฉ๋‹ˆ๋‹ค.
git clone https://github.com/kyopark2014/mcp-agent
  1. ์ดํ›„ ๋‹ค์šด๋กœ๋“œ ๋ฐ›์€ github ํด๋”๋กœ ์ด๋™ํ•œ ํ›„์— ์•„๋ž˜์™€ ๊ฐ™์ด ํ•„์š”ํ•œ ํŒจํ‚ค์ง€๋ฅผ ์ถ”๊ฐ€๋กœ ์„ค์น˜ ํ•ฉ๋‹ˆ๋‹ค.
cd mcp-agent && python -m pip install -r requirements.txt
  1. ์‹ค์Šต์— ํ•„์š”ํ•œ Key ์„ค์ •์— ๋”ฐ๋ผ์„œ, ์ธํ„ฐ๋„ท๊ณผ ๋‚ ์”จ์กฐํšŒ API์— ๋Œ€ํ•œ key๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ์„ค์ •์ด ๋˜๋ฉด, application/config.json์„ ์•„๋ž˜์™€ ๊ฐ™์€ json ํŒŒ์ผ์ด ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.
{
    "WEATHER_API_KEY": "fbd00245cabcedefghijkd3e94905f7049",
    "TAVILY_API_KEY": "tvly-1234567890U3imZFs4LNO2g0Qv1LoE"
}
  1. ์ด์ œ ์ค€๋น„๊ฐ€ ๋˜์—ˆ์œผ๋ฏ€๋กœ, ์•„๋ž˜์™€ ๊ฐ™์€ ๋ช…๋ น์–ด๋กœ streamlit์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. MCP Tool ์‚ฌ์šฉ ๋ฐฉ๋ฒ•์„ ์ฐธ์กฐํ•˜์—ฌ ๋™์ž‘ ํ…Œ์ŠคํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
streamlit run application/app.py

Local์—์„œ Docker๋กœ ์‹คํ–‰ํ•˜๊ธฐ

์•„๋ž˜์™€ ๊ฐ™์ด docker๋ฅผ ์„ค์น˜ํ•˜๊ณ  ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

brew install --cask docker

์ด์ œ ์•„๋ž˜์™€ ๊ฐ™์ด ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ด์šฉํ•ด ๋นŒ๋“œํ•ฉ๋‹ˆ๋‹ค. build.sh๋Š” aws credential์„ ์กฐํšŒํ•ด์„œ build ํ• ๋•Œ์— ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.

./build.sh

์ด์ œ ์•„๋ž˜์™€ ๊ฐ™์ด ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์•„๋ž˜์—์„œ๋Š” ํŽธ์˜์ƒ docker์˜ ํฌํŠธ๋ฅผ 8502๋กœ ์„ค์ •ํ•˜์˜€๋Š”๋ฐ ์ž์‹ ์˜ ํ™˜๊ฒฝ์— ๋”ฐ๋ผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

docker run -p 8502:8501 mcp-agent

๋ธŒ๋ผ์šฐ์ €์—์„œ ์•„๋ž˜ URL๋กœ ์ ‘์†ํ•ฉ๋‹ˆ๋‹ค.

http://0.0.0.0:8502

์‹คํ–‰ ์ค€๋น„

Diagram์„ ๊ทธ๋ฆฌ๊ธฐ ์œ„ํ•ด์„œ๋Š” Graphviz๋ฅผ ๋”ฐ๋ผ์„œ graphviz๋ฅผ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค. Mac์—์„œ๋Š” ์•„๋ž˜ ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

brew install graphviz

์‹คํ–‰ ๊ฒฐ๊ณผ

"์„œ์šธ์—์„œ ๋ถ€์‚ฐ์„ ๊ฑฐ์ณ์„œ ์ œ์ฃผ์— ๊ฐ€๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ€๋Š” ๋™์•ˆ์˜ ๋‚ ์”จ์™€ ๋ง›์ง‘์€?"์™€ ๊ฐ™์ด ๋ณต์žก ์งˆ๋ฌธ์„ ์ˆ˜ํ–‰ํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ์—ฌ๋Ÿฌ๊ฐ€์ง€ tool๋“ค์„ ์ด์šฉํ•˜์—ฌ ์ •๋ณด๋ฅผ ์ˆ˜์ง‘ํ•ฉ๋‹ˆ๋‹ค.

image

๊ฒฐ๊ณผ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ๋ณต์žก ์งˆ๋ฌธ์— ๋Œ€ํ•ด ๋‹ต๋ณ€์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

image

Reference

MCP Python SDK

LangChain MCP Adapters