LangGraph + MCP Agent Study Notes
핵심: Claude/GPT를 단순 챗봇으로 쓰는 것이 아니라, LangGraph가 LLM-tool-LLM 루프를 실행하고 MCP server가 실제 외부 함수를 제공하는 agent runtime을 구성한다.
Metadata
- Source codebase
- braincrew-lab/langgraph-mcp-agents
- Source material
- Teo's uploaded Korean study note:
langgraph_mcp_agent_study_notes.md - Category
- Agent architectures · Agent operations · MCP tool use
- Main files to study
app.py,mcp_server_time.py,mcp_server_rag.py,utils.py,config.json- Keywords
- LangGraph, ReAct, MCP, Streamlit, MultiServerMCPClient, tool calling, RAG, MemorySaver, RunnableConfig
Mental model
LLM is the controller; MCP tools are the action space
이 프로젝트는 LLM을 답변 생성기만으로 쓰지 않는다. LLM은 어떤 tool을 어떤 인자로 호출할지 결정하고, LangGraph는 그 호출을 실행하며, MCP server는 실제 Python 함수나 외부 API 로직을 수행한다.
Agent runtime은 inference orchestration 코드다
Vision 코드에 비유하면 LLM은 backbone, MCP/RAG/API는 dataloader 또는 external input, LangGraph는 inference pipeline, MemorySaver와 thread_id는 checkpoint/memory 역할을 한다.
MCP JSON은 tool 본체가 아니라 server 연결 설정이다
config.json이나 UI의 Tool JSON은 어떤 MCP server를 어떤 command 또는 URL로 연결할지 설명한다. 실제 tool은 MCP server 안의 @mcp.tool() 함수나 외부 MCP package가 제공한다.
Runtime flow
app.py는 UI, config, MCP lifecycle, model selection, agent creation, streaming, memory thread를 한 파일에서 오케스트레이션한다. 학습할 때는 아래 흐름을 먼저 고정해두면 코드가 읽힌다.
Streamlit UI
→ load .env and config.json
→ MultiServerMCPClient(mcp_config)
→ await client.__aenter__()
→ tools = client.get_tools()
→ ChatAnthropic or ChatOpenAI
→ create_react_agent(model, tools, prompt, MemorySaver)
→ HumanMessage(user_query)
→ LangGraph ReAct loop
→ optional MCP tool call
→ ToolMessage result
→ final AIMessage answer
Add Tool과 Apply Settings는 다른 단계다. Add Tool은 JSON을 파싱해 pending config에 넣는 단계이고, Apply Settings를 눌러야 MCP client가 실제 server에 연결하고 tool schema를 읽어온다.
MCP tool registration
MCP server는 local stdio 프로세스일 수도 있고, 이미 떠 있는 remote/local SSE server일 수도 있다.
{
"time": {
"command": "python",
"args": ["./mcp_server_time.py"],
"transport": "stdio"
},
"weather": {
"url": "http://localhost:8005/sse",
"transport": "sse"
}
}
- stdio:
command + args를 실행해 local MCP server 프로세스를 띄우고 표준입출력으로 MCP protocol을 주고받는다. - sse:
url에 접속해 HTTP/SSE 기반 MCP server와 통신한다. - custom tool: 꼭 Python일 필요는 없지만, 직접 만들 경우에는
FastMCP와@mcp.tool()로 함수를 노출하는 구조가 가장 단순하다. - multiple tools: 파일 하나에 여러
@mcp.tool()함수를 둘 수 있다. Tool 하나당 Python 파일 하나가 필요한 것은 아니다.
LangGraph ReAct agent
create_react_agent()는 LLM, tools, prompt, checkpointer를 묶어 ReAct graph를 만든다. 핵심은 LLM이 tool call을 제안하고, graph runtime이 tool을 실행한 뒤, 결과를 다시 LLM context로 넣는 것이다.
agent = create_react_agent(
model,
tools,
checkpointer=MemorySaver(),
prompt=SYSTEM_PROMPT,
)
메시지 흐름은 아래처럼 분리해서 이해하면 좋다.
- HumanMessage: 사용자 질문이 agent state에 들어간 형태.
- AIMessage / AIMessageChunk: LLM이 생성하는 답변 또는 streaming 조각. 필요하면 tool call을 포함한다.
- ToolMessage: MCP tool 실행 결과가 다시 agent state에 들어간 형태.
- RunnableConfig:
thread_id와recursion_limit같은 실행 옵션을 전달한다.
Recommended study order
mcp_server_time.py: 가장 작은 MCP server.FastMCP,@mcp.tool(),mcp.run(transport="stdio")를 확인한다.initialize_session(): MCP tools, LLM wrapper, LangGraph agent가 합쳐지는 핵심 함수다.process_query(): 사용자 입력이HumanMessage로 graph에 들어가는 지점이다.get_streaming_callback(): 최종 답변보다 중요한 debugging surface다. 어떤 tool을 어떤 argument로 불렀는지 확인한다.mcp_server_rag.py: PDF → chunking → embedding → FAISS → retriever → MCP tool 구조를 본다.
Practical pitfalls and improvements
app.py가 너무 많은 책임을 가짐: UI, auth, config, MCP lifecycle, model factory, streaming, history를 나누면 유지보수가 쉬워진다.- Arbitrary command 입력은 위험함: 공개 서비스에서는 UI에서 임의 command JSON을 받지 말고 allowlist, secret manager, audit log가 필요하다.
- Tool description 품질이 중요함: 이름, docstring, argument schema가 약하면 LLM이 언제 tool을 써야 할지 모른다.
- RAG는 source metadata를 반환해야 함: prompt가 출처를 요구해도 retriever가
source,page를 반환하지 않으면 제대로 citation할 수 없다. - Retriever cache 필요: 학습용 구현처럼 query마다 vectorstore를 다시 만들면 느리고 비싸다. Persistent index와 caching이 필요하다.
가장 중요한 학습 포인트는 역할 분리다: LLM은 action selector, LangGraph는 action executor, MCP server는 function implementation이다.