OLLAMA+MCP Agent: 비용 걱정없는 AI agent #1
OLLAMA MCP Agent 시리즈 목차
- OLLAMA+MCP Agent: 비용 걱정없는 AI agent #1
- ollama-mcp-agent 설치와 MCP tool 구현 및 동작 테스트
- 소스코드 다운로드
- OLLAMA+MCP Agent: 비용 걱정없는 AI agent #2
- 외부 MCP 서버 연동 테스트 (+ smithery.ai)
MCP 가 화두로 떠오르면서 Cursor + MCP, Claude desktop + MCP 활용법이 급속히 확산되고 있습니다. Cursor, Cluade 뿐 아니라 Openweb-UI, python/javascript 등 다양한 AI 도구들이 앞다투어 MCP 를 지원하고 있습니다. Deepseek 의 충격이 얼마 지나지도 않아 MCP 의 충격이 오더니 이제는 A2A 와 같은 도구들도 덩달아 주목을 받는 상황입니다.

MCP 를 활용하면 기존 LLM 성능이 향상됨은 두말한 나위가 없지만서도 한 가지 아쉬운 점은 비용 문제입니다. 어떤 AI 서비스든 API 형태로 서비스를 사용하면 사용량에 비례해서 비용이 발생할 수 밖에 없습니다. MCP 와 같은 도구를 이용해서 작업을 구성하고, 작업의 input/output 을 처리하면서 reasoning 을 진행하면 API 사용량이 급증할 수 밖에 없습니다.
그래서 PC 에서 AI 를 만지작거린 분들은 ollama 에 설치된 모델을 대신 사용해서 비용 걱정없이 사용할 수 없을까라는 고민을 해보셨을 겁니다. 어쩌면 로컬 PC 에서 돌아가는 LLM 성능 한계를 MCP 가 상당부분 보완해 줄 수 있지 않을까라는 기대와 함께…
이런 생각을 해보신 분들이라면 이번 포스트에서 제공하는 소스코드가 유용하리라 생각됩니다. 앞서 언급한대로 이 포스트에는 ollama 에 설치된 LLM 모델과 MCP 를 활용하여 비용 걱정없이 AI 작업을 수행하는 python 예제코드 – ollama-mcp-agent 를 제공합니다. 비록 상용 AI 서비스에 비해 부족한 점이 많이 보이지만, 개인정보나 비용 등 AI 를 활용하는 케이스에 따라 매우 유용할 수도 있는 장점들을 제공할 것입니다.

예제코드 실행을 위해서는 PC에 ollama 가 설치되어 있어야 합니다. 아래 링크를 참고해서 ollama 설치를 먼저 진행해주세요.
- ollama official page (download install file)
ollama-mcp-agent 설치
ollama-mcp-agent python 예제코드는 아래 github 링크에서 다운로드 받을 수 있습니다.
- https://github.com/godstale/ollama-mcp-agent
- 압축 파일 다운로드 –> 압축 해제 후 생성된 폴더로 이동
# 또는 terminal 에서 아래 명령어 수행해서 repository clone > git clone https://github.com/godstale/ollama-mcp-agent > cd ollama-mcp-agent
예제코드 실행에 필요한 라이브러리 설치가 필요합니다. ollama-mcp-agent 예제는 uv 패키지 관리자를 지원합니다. termial 또는 PowerShell 환경에서 아래 명령어를 실행합니다.
# Using pip pip install uv # Or using curl (Unix-like systems) curl -LsSf https://astral.sh/uv/install.sh | sh # Or using PowerShell (Windows) powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
uv 를 설치, 예제코드를 실행하기 위한 가상환경을 만들고 실행합니다. 그리고 아래 순서대로 프로젝트를 설치할 수 있습니다. (terminal 환경에서 실행)
# Create and activate virtual environment uv venv source .venv/bin/activate # For Unix-like systems # Or .venv\Scripts\activate # For Windows # Install dependencies uv sync
ollama 에 LLM 모델을 다운로드해야 합니다. 이때 주의할 점이 하나가 있습니다. 반드시 tools calling 기능을 지원하는 모델을 받아야 합니다. 그렇지 않으면 MCP 설정을 추가해도 LLM 이 도구를 사용하지 않을 것입니다.
- 2025.04 기준으로 ollama 에서는 MFDoom/deepseek-r1-tool-calling 모델이 적합해 보입니다.
- 그래픽카드 메모리 용량에 맞춰 모델을 선택세요. (ollama 모델 페이지 참고)
- 이 포스트에서는 RTX4070 SUPER 12G 그래픽카드에 맞춰 14b (9.0GB) 모델을 사용합니다.
# Install Ollama (refer to https://ollama.ai for platform-specific installation) # Download LLM model which supports Tool calling feature ollama pull MFDoom/deepseek-r1-tool-calling:14b
PC 종료 후 다시 실행할 때는 activate 명령어로 가상환경을 다시 실행할 수 있습니다.
source .venv/bin/activate # For Unix-like systems # Or .venv\Scripts\activate # For Windows
MCP 테스트
LLM 설치가 끝나면 예제코드를 사용할 준비는 완료되었습니다. MCP 의 효용성을 비교하기 위해 먼저 ollama 를 직접 실행해서 “오늘 서울의 날씨”를 질문해 보겠습니다.
> ollama list NAME ID SIZE MODIFIED gemma3:12b f4031aab637d 8.1 GB 6 days ago MFDoom/deepseek-r1-tool-calling:14b cc00307e31da 9.0 GB 12 days ago deepseek-r1:14b ea35dfe18182 9.0 GB 2 months ago paraphrase-multilingual:latest ba13c2e06707 562 MB 4 months ago nomic-embed-text:latest 0a109f422b47 274 MB 8 months ago llama3.1:latest 62757c860e01 4.7 GB 8 months ago > ollama run MFDoom/deepseek-r1-tool-calling:14b >>> 오늘 서울의 날씨는? </think> 죄송해요, 현재 날씨 정보에 접근할 수 없습니다. 날씨를 확인하시려면晷/moment/weather 사이트나 관련 앱을 사용해 주세요.
LLM 은 학습된 데이터만을 기억하고 있을테니 오늘 서울의 날씨는 알지 못하는 것이 당연합니다. 이런 단점들을 MCP 도구를 추가해서 보완할 수 있습니다. LLM 이 날씨에 대한 질문을 받는 경우 MCP server – weather 도구를 사용해서 오늘 날씨에 정보를 얻어서 답변을 하면 됩니다.
MCP server – weather 도구는 fastmcp 라이브러리를 이용해 파이썬으로 간단히 구현해 보겠습니다.
- ./mcp_server/mcp_server_weather.py
from mcp.server.fastmcp import FastMCP mcp = FastMCP( "Weather", # Name of the MCP server instructions="You are a weather assistant that can answer questions about the weather in a given location.", host="0.0.0.0", # Host address (0.0.0.0 allows connections from any IP) port=8005, # Port number for the server ) @mcp.tool() async def get_weather(location: str) -> str: print(f"\n[DEBUG] MCP: get_weather called: {location}\n") return f"It's sunny in the morning, cloudy in the afternoon and snowing in the evening in {location}" if __name__ == "__main__": mcp.run(transport="stdio")
주석을 제거하고 보면 코드는 굉장히 단순합니다.
- FastMCP()
- fastmcp 라이브러리에서 제공하는 MCP server 생성 함수입니다.
- 이 함수를 사용하면 내가 원하는 기능의 MCP server 를 만들 수 있습니다.
- 추후 예제가 실행될 때 FastMCP() 에 정의된 대로 MCP 서버가 만들어지고 LLM 이 사용합니다.
- @mcp.tool()
- FastMCP() 로 생성한 MCP 서버에서 제공할 도구를 정의합니다.
- 이 annotation 이 달린 함수를 LLM 이 도구로 인식합니다.
- 몇 가지 다른 type 들도 있는데 당장은 mcp.tool 만 사용해도 됩니다.
- mcp.tool 함수는 여러개를 정의할 수 있습니다.
- weather 도구는 위치를 입력받아서 답변을 출력합니다.
- 테스트를 위해 함수는 위치에 상관없이 정해진 대답만을 출력합니다.
MCP server – weather 도구를 생성했다면, 이 도구를 예제코드에서 사용할 수 있도록 설정을 추가해줘야 합니다. 프로젝트 폴더의 mcp_config.json 파일을 열어보세요.
{ "mcpServers": { "weather": { "command": "python", "args": ["./mcp_server/mcp_server_weather.py"], "transport": "stdio" }, "local_file_manager": { "command": "python", "args": ["./mcp_server/mcp_server_file_manager.py"], "transport": "stdio" } } }
mcp 설정파일에는 weather python 코드의 위치가 정의되어 있습니다. mcp_config.json 설정과 실행 파일을 참고해서 실행 가능한 MCP 서버를 만들고 LLM 은 필요할 때 호출해서 답을 구할 것입니다.
이제 main.py 파일을 실행해서 예제코드가 “오늘 서울의 날씨”에 대한 답변을 제대로 생성하는지 보겠습니다.
C:\Workspace\ollama-mcp-agent> uv run main.py === Initializing MCP client... === Loaded 3 MCP tools. [Tool] get_weather [Tool] get_local_file_list [Tool] write_text_to_file === Starting Ollama Chat === Enter 'quit', 'exit', or 'bye' to exit. ======================================== User: 오늘 서울의 날씨는? AI: Processing tool: get_weather --------------------- </think> 서울의 오늘 날씨는 오전에 맑고 오후에는 흐려지며 저녁에는 눈이 내리고 있습니다. User: exit Chat ended. Goodbye! C:\Workspace\ollama-mcp-agent>
ollama 로 LLM 을 직접 실행했을 때와는 달리 MCP 를 사용하니 도구를 통해 얻은 정보를 사용해서 대답을 해줍니다. 예제코드에서는 고정된 답변만을 하도록 간단히 만들었지만, weather API service 와 연동하면 실제 “오늘 서울의 날씨” 정보를 알려주도록 만들 수 있겠죠.
중요한 것은 LLM 이 답변하지 못하던 부분들을 MCP 를 활용해서 보완해주고 있다는 점입니다!!
MCP 서버 추가와 reasoning 성능 확인
이제는 MCP server 를 더 추가해서 LLM 이 사용자의 질문에 대한 답변을 구성하기 위해 여러개의 도구를 활용할 수 있는지 확인해 보겠습니다. 이 예제를 통해 LLM 이 사용자의 질문에 대한 답변을 구성하기 위해 필요한 도구들을 파악하고, 도구들을 적절한 순서로 호출해서 답변을 완성해 나갈 수 있는지를 확인해 볼 겁니다.
앞서 살펴본 mcp_config.json 파일에는 file manager 도구도 정의되어 있었습니다. file manager 는 내 PC 의 특정 폴더에 담기 파일 목록을 조회하거나 텍스트 파일을 만드는 도구를 제공합니다.
- mcp_server/mcp_server_file_manager.py 파일을 열어보면…
import os from datetime import datetime from pathlib import Path from mcp.server.fastmcp import FastMCP # Initialize FastMCP server with configuration mcp = FastMCP( "File manager", # Name of the MCP server instructions="You are a local file manager that can operate on the local file system.", host="0.0.0.0", # Host address (0.0.0.0 allows connections from any IP) port=8006, # Port number for the server ) # Get list of files and directories in a specified path @mcp.tool() async def get_local_file_list(path: str) -> str: try: if not os.path.exists(path): return f"Error: Path '{path}' does not exist" file_list = [] # Collect file/directory information for entry in os.scandir(path): stats = entry.stat() size = stats.st_size modified_time = datetime.fromtimestamp(stats.st_mtime).strftime( "%Y-%m-%d %H:%M:%S" ) # Convert size unit size_str = f"{size:,} bytes" if size > 1024 * 1024 * 1024: size_str = f"{size/(1024*1024*1024):.2f} GB" elif size > 1024 * 1024: size_str = f"{size/(1024*1024):.2f} MB" elif size > 1024: size_str = f"{size/1024:.2f} KB" # File/directory distinction type_str = "[DIR]" if entry.is_dir() else "[FILE]" # Generate result string file_info = f"{type_str} {entry.name:<50} {size_str:<15} {modified_time}" file_list.append(file_info) # Sort and return results return "\n".join(sorted(file_list)) except Exception as e: return f"Error: {str(e)}" # Write specified text to a file @mcp.tool() async def write_text_to_file(file_name: str, text: str) -> str: try: path = os.path.join(os.path.expanduser("~"), "Downloads", file_name) with open(path, "w", encoding="utf-8") as f: f.write(text) result_text = f"Successfully wrote to file: {path}\n{text}" return result_text except Exception as e: return f"Error: {str(e)}" if __name__ == "__main__": # Start the MCP server with stdio transport mcp.run(transport="stdio")
- 두 개의 도구가 정의되어 있습니다.
- get_local_file_list : 특정 폴더에 포함된 폴더와 파일 목록을 반환합니다.
- write_text_to_file : 문자열을 txt 파일로 저장합니다.
- FastMCP() 를 이용해 MCP server 를 만듭니다. port 는 weather 와 겹치지 않게 다른 포트를 사용했습니다.
- 텍스트를 파일로 저장할 때 ~\Downloads 폴더에 파일을 생성하도록 했습니다.
mcp_config.json 파일에는 이미 file manager 도구가 정의되어 있으므로 main.py 파일을 다시 실행합니다. 그리고 이번에는 “오늘 서울의 날씨 정보를 텍스트 파일로 저장” 하도록 해보겠습니다.
C:\Workspace\ollama-mcp-agent> uv run main.py === Initializing MCP client... === Loaded 3 MCP tools. [Tool] get_weather [Tool] get_local_file_list [Tool] write_text_to_file === Starting Ollama Chat === Enter 'quit', 'exit', or 'bye' to exit. ======================================== User: 오늘 서울의 날씨를 검색하고 그 결과를 파일 도구를 사용해서 텍스트 파일로 저장해줘. AI: Processing tool: get_weather --------------------- Processing tool: write_text_to_file --------------------- </think> 성공적으로 파일에 써bió. User: exit Chat ended. Goodbye! C:\Workspace\ollama-mcp-agent>
오늘 서울의 날씨 정보가 텍스트 파일로 저장됩니다. “다운로드” 폴더에서 새로 생성된 파일을 찾아 저장된 내용을 확인하세요.
weather 도구가 생성하는 날씨 정보가 파일로 저장되었다면 성공입니다! LLM 은 사용자의 질문을 분석해서 사용해야 할 도구들을 구분하고, 도구를 호출하는 순서를 구성한 뒤 실행했습니다. 단계별로 도구를 호출해서 얻은 정보로 다음 단계를 실행하는데 사용하기도 했습니다. reasoning 과정을 꽤 잘 수행했다고 볼 수 있겠네요.
위 테스트에서는 정상적으로 답변이 나왔지만 본인이 사용하는 PC의 상태, LLM 종류나 질문, 프롬프트 등 여러 변수들에 의해서 답변은 다르게 나올수도 있으며, 어떤 경우에는 도구를 호출하지 못하거나 잘못된 정보를 알려주기도 합니다.
이런 경우라면 아래 내용들을 하나씩 살펴보면서 성능을 튜닝해 나가야합니다.
- LLM 설정 변경
- temperature 값을 조금씩 조절해보면 응답이 변경됩니다.
- 도구 호출 정보 확인
- main.py – get_streaming_callback(0 함수에서 도구 호출 정보를 더 상세히 표시하도록 로그를 추가합니다.
- 더 상세한 정보를 출력하기 위해서는 astream_graph() 함수를 확인하거나 LangSmith 를 사용하는 것이 좋습니다.
# Return streaming callback function and accumulated data # This function processes the output chunks from LangGraph's streaming def get_streaming_callback(): accumulated_text = [] accumulated_tool_info = [] # Store tool name and response separately # data receives chunk.ops[0]['value'] def callback_func(data: Any): nonlocal accumulated_text, accumulated_tool_info if isinstance(data, dict): # Try to find the key associated with the agent step containing messages agent_step_key = next( ( k for k in data if isinstance(data.get(k), dict) and "messages" in data[k] ), None, ) if agent_step_key: messages = data[agent_step_key].get("messages", []) for message in messages: if isinstance(message, AIMessage): # Check if it's an intermediate message (tool call) or final answer chunk if message.tool_calls: # Tool call requested by the model (won't print content yet) pass # Or log if needed: print(f"DEBUG: Tool call requested: {message.tool_calls}") elif message.content and isinstance( message.content, str ): # Check content is a string # Append and print final response chunks from AIMessage content_chunk = message.content.encode( "utf-8", "replace" ).decode("utf-8") if content_chunk: # Avoid appending/printing empty strings accumulated_text.append(content_chunk) print(content_chunk, end="", flush=True) elif isinstance(message, ToolMessage): # Result of a tool execution tool_info = f"Tool Used: {message.name}\nResult: {message.content}\n---------------------" print( f"\n[Tool Execution Result: {message.name}]" ) # Indicate tool use clearly # print(message.content) # Optionally print the full tool result accumulated_tool_info.append(tool_info) return None # Callback doesn't need to return anything return callback_func, accumulated_text, accumulated_tool_info
- System 프롬프트 수정
- main.py 상단에 query 를 수행할 때 사용할 system 프롬프트가 정의되어 있습니다. 프롬프트를 더 명확하게 정의함으로써 다른 결과를 기대할 수 있습니다.
MCP_CHAT_PROMPT = """ You are a helpful AI assistant that can use tools to answer questions. You have access to the following tools: {tools} Use the following format: Question: the input question you must answer Thought: you should always think about what to do Action: the action to take, should be one of [{tool_names}] Action Input: the input to the action Observation: the result of the action ... (this Thought/Action/Action Input/Observation can repeat N times) Thought: I now know the final answer Final Answer: the final answer to the original input question When using tools, think step by step: 1. Understand the question and what information is needed. 2. Look at the available tools ({tool_names}) and their descriptions ({tools}). 3. Decide which tool, if any, is most appropriate to find the needed information. 4. Determine the correct input parameters for the chosen tool based on its description. 5. Call the tool with the determined input. 6. Analyze the tool's output (Observation). 7. If the answer is found, formulate the Final Answer. If not, decide if another tool call is needed or if you can answer based on the information gathered. 8. Only provide the Final Answer once you are certain. Do not use a tool if it's not necessary to answer the question. """
- MCP 도구 구현 및 설정 확인
- mcp_manager.py 파일에서 test_mcp_tool() 함수를 활성화하여 MCP 도구를 테스트 해볼 수 있습니다.
- mcp server 를 설정하는 FastMCP() 함수의 설정값도 확인합니다.
# Test MCP tool calls async def test_mcp_tool(mcp_tools): try: # Test calls for tool in mcp_tools: print(f"[Tool] {tool.name}") # print(f" - Description: {tool.description}") # print(f" - Schema: {tool.args_schema}") # Check MCP server status # if tool.name == "get_weather": # try: # # Test call # test_response = await tool.ainvoke({"location": "Seoul"}) # print("\n=== MCP Server Status ===") # print("✅ MCP server is running normally.") # print(f"Test response: {test_response}") # except Exception as e: # print("\n❌ MCP server status check failed:") # print(f"- Error message: {str(e)}") # return except Exception as e: print(f"Error occurred during test call: {str(e)}")
- LLM 모델 확인
- 현재 사용중인 모델이 tools calling 을 지원하는지 확인해야 합니다.
- tool 지원 모델이라 하더라도 실제 실행해보면 tool 을 호출하지 못하는 경우도 있습니다.
- LLM 모델 최신 업데이트 확인
- 예제에서 사용중인 deepseek-r1-tool-calling 모델은 tool 사용을 위해 튜닝 된 모델이며, 아직 tool 호출 버그들이 있다고 합니다. 이어질 업데이트에서 상당수가 수정될 예정이라고 하니 모델을 최신 상태로 유지하는 것이 좋습니다.
- LLM 모델 업그레이드
- 그래픽카드가 허용한다면 더 큰 모델을 사용하는 것도 성능 향상에 도움이 될 것입니다.
- 현재 사용중인 모델이 tools calling 을 지원하는지 확인해야 합니다.
이외에도 코드에서 사용중인 prebuilt 된 (범용적인) ReAct Agent 를 사용자가 직접 수정해서 사용하는 방법도 있겠습니다.
이 방법은 LangGraph 를 이용해서 작업을 수행하는 프로세스를 미리 정의함으로써 기대하는 결과를 더 잘 얻도록 유도합니다. 하지만 처음 볼때는 다소 어려운 사용법을 익힐 필요가 있습니다.


이번 포스트에서는 ollama 와 MCP 를 연동해서 사용하는 가장 기본적인 예제를 사용했습니다. 다음 포스트에서는 smithery.ai 와 같은 외부 MCP repository 에서 제공하는 MCP 설정을 추가하고 예제를 실행해 보겠습니다.
OLLAMA MCP Agent 시리즈 목차
- OLLAMA+MCP Agent: 비용 걱정없는 AI agent #1
- ollama-mcp-agent 설치와 MCP tool 구현 및 동작 테스트
- 소스코드 다운로드
- OLLAMA+MCP Agent: 비용 걱정없는 AI agent #2
- 외부 MCP 서버 연동 테스트 (+ smithery.ai)
uv sync 를 사용하여 패키지 설치할 수 있도록 코드가 변경되었습니다.
main.py 파일 실행은 uv run main.py 를 사용하면 됩니다.