From 663046a862ff2c77c970ecbbe2866bc26940270d Mon Sep 17 00:00:00 2001 From: ppippi Date: Tue, 23 Sep 2025 23:10:07 +0900 Subject: [PATCH 1/2] =?UTF-8?q?mcp=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _posts/2025-09-15-mcp-one-page.md | 38 ++++++++----------------------- 1 file changed, 10 insertions(+), 28 deletions(-) diff --git a/_posts/2025-09-15-mcp-one-page.md b/_posts/2025-09-15-mcp-one-page.md index 71ffc34..2119c77 100644 --- a/_posts/2025-09-15-mcp-one-page.md +++ b/_posts/2025-09-15-mcp-one-page.md @@ -6,11 +6,13 @@ feature-img: "assets/img/2025-09-15-mcp-one-page/0.png" tags: [LLMOps] --- -2024년 11월, Anthropic이 Claude에서 MCP(Model Context Protocol)를 오픈소스로 공개했다. 출시 직후부터 주변에서는 "MCP 써봤냐"는 질문이 잦았고, "써보면 정말 편하다"라는 이야기도 들려왔다. +2024년 11월, Anthropic이 Claude에서 MCP(Model Context Protocol)를 오픈소스로 공개했다. 출시 직후부터 주변에서는 "MCP 써봤냐"는 질문이 잦았고, "써보면 정말 편하다"라는 이야기도 들려왔다. + +솔직히 처음에는 큰 감흥이 없었다. OpenAI Function Calling 혹은 Gemini Function Calling과 크게 다르지 않아 보였고, 이미 LLM 확장 기능을 만들 수 있는 도구는 많았기 때문이다. 하지만 불과 몇 달 사이에 MCP는 OpenAI, Google 등 주요 플레이어들의 제품에도 빠르게 통합되며 사실상 표준 후보로 자리 잡았다. ChatGPT UI에서도 [베타 기능으로 MCP를 직접 등록](https://platform.openai.com/docs/guides/developer-mode)할 수 있게 된 것이 좋은 예이며, 그 외에 많은 Agent Docs에서도 MCP 관련 튜토리얼을 제공하고 있다. + +이 글에서는 간단한 MCP를 직접 구현해보고자 한다. -솔직히 처음에는 큰 감흥이 없었다. OpenAI Function Calling과 크게 다르지 않아 보였고, 이미 LLM 확장 기능을 만들 수 있는 도구는 많았기 때문이다. 하지만 불과 몇 달 사이에 MCP는 OpenAI, Google 등 주요 플레이어들의 제품에도 빠르게 통합되며 사실상 표준 후보로 자리 잡았다. ChatGPT UI에서도 [베타 기능으로 MCP를 직접 등록](https://platform.openai.com/docs/guides/developer-mode)할 수 있게 된 것이 좋은 예다. -이 글에서는 MCP를 직접 구현해보고자 한다. ## 왜 MCP를 다시 바라보게 되었나 @@ -29,7 +31,7 @@ tags: [LLMOps] Claude는 MCP를 ["AI 애플리케이션을 외부 시스템에 연결하기위한 오픈 소스 표준"](https://modelcontextprotocol.io/docs/getting-started/intro) 이라고 소개하고 있다. -특히 (질리도록 들어본...?) 자주 사용하는 표현 중 하나인 "AI 애플리케이션 용 USB-C 포트처럼 MCP를 생각해보십시오." 라는 내용이 등장한다. 즉, MCP는 AI 애플리케이션을 외부 시스템과 연결해주는 프로토콜이다. +특히 질리도록 들어본 표현 중 하나인 "AI 애플리케이션 용 USB-C 포트처럼 MCP를 생각해보십시오." 라는 내용이 등장한다. 즉, MCP는 AI 애플리케이션을 외부 시스템과 연결해주는 프로토콜이다. Function Calling과 비교하면, 단일 벤더가 정한 JSON 스키마를 따르는 대신, MCP는 연결·초기화·도구 목록·리소스 접근까지 전 과정을 표준화했다. 덕분에 서버 입장에서는 "어떤 클라이언트가 붙더라도 동일한 핸드셰이크"를 기대할 수 있고, 클라이언트는 새로운 기능을 발견(List)하고 설명(Description)만으로 호출할 수 있다. @@ -41,11 +43,7 @@ Function Calling과 비교하면, 단일 벤더가 정한 JSON 스키마를 따 간단한 예시를 들면, GPT에게 오늘 나의 캘린더 일정을 조회 해달라고 요청하는 것이다. -MCP가 없다면, GPT는 이를 수행할 수 없다. 하지만 MCP를 통해 캘린더를 조회하는 API를 호출하고, 이를 통해 데이터를 조회할 수 있다. 물론 GPT의 Function calling을 통해서도 가능하지만, MCP는 오픈소스 프로토콜로 설정해서, GPT에서도 호출 할 수 있고 Gemini에서도 호출할 수 있는 그야말로 표준을 만든 셈이다. - - - -나의 경우 [Cursor에서 MCP 서버를 등록](https://docs.cursor.com/en/context/mcp)해두고, 주로 사용하곤 한다. +MCP가 없다면, GPT는 이를 수행할 수 없다. 하지만 MCP를 통해 캘린더를 조회하는 API를 호출하고, 이를 통해 데이터를 조회할 수 있다. 물론 Function calling을 통해서도 가능하지만, MCP는 오픈소스 프로토콜로 설정해서, GPT에서도 호출 할 수 있고 Gemini에서도 호출할 수 있는 그야말로 표준을 만든 셈이다. @@ -68,9 +66,9 @@ MCP는 크게 2가지로 나눌 수 있다. Client와 Server이다. MCP Host 라 주목해야 할 부분은 MCP에서 클라이언트와 서버가 1:1 세션 연결을 권장한다는 점이다. -MCP 서버는 로컬 또는 원격으로 실행할 수 있다. 로컬에서는 STDIO 전송을 사용하며 클라이언트와 동일한 시스템에서 돌리게 되는데, 이를 로컬 MCP라고 부른다. +MCP 서버는 로컬 또는 원격으로 클라이언트와 연결 할 수 있다. 로컬에서는 STDIO를 사용하며 클라이언트와 동일한 시스템(운영체제)에서 돌리게 되는데, 이를 로컬 MCP라고 부른다. -반대로 외부 플랫폼에서 실행하며 SSE 전송으로 연결하면 원격 MCP 서버가 된다. +반대로 외부 플랫폼에서 실행하며 SSE 전송으로 연결하면 원격 MCP 서버가 되며 네트워크를 통한 통신이 된다. @@ -111,7 +109,6 @@ from mcp.server.fastmcp import FastMCP mcp = FastMCP( name="stock-mcp", ) - ``` @@ -171,7 +168,6 @@ def get_stock_price(args: dict[str, Any]) -> dict[str, Any]: } except Exception as e: return {"success": False, "error": str(e)} - ``` @@ -197,14 +193,12 @@ async def main() -> None: {"args": {"ticker": "005930.KS"}}, ) print(result.content) - ``` 해당 코드를 실행시키면 아래와 같은 로그가 출력된다. ```shell [TextContent(type='text', text='{\n "success": true,\n "company_name": "Samsung Electronics Co., Ltd.",\n "ticker": "005930.KS",\n "current_price": 78200.0,\n "previous_close": 79400.0,\n "change": "-1200.00 (-1.51%)"\n}', annotations=None, meta=None)] - ``` 흐름을 다시 정리하면 아래와 같다. @@ -263,7 +257,6 @@ async def main() -> None: {"args": {"ticker": "005930.KS"}}, ) print(result.content) - ``` @@ -287,19 +280,14 @@ PROMPT_MESSAGE: If you don't know the answer, return 'None' GPT Response: get_stock_price - ``` Prompt를 직접 만들어도 되지만, 프로덕션에서는 "도구 선택 → 인자 생성 → 호출 결과 해석" 과정을 프레임워크가 대신해 주는 경우가 많다. 다만 한 번은 로우레벨 흐름을 경험해 보는 것이 좋다. 어떤 메시지가 오가고 실패했을 때 어떤 예외가 터지는지 감을 잡을 수 있기 때문이다. 물론 직접 이렇게 LLM을 구현할 필요는 없다. 마지막에 간단하게 소개하겠지만, OpenAI SDK나 Google ADK에서 MCP와의 연동을 함께 제공하고 있어 훨씬 간단하게 통합할 수 있다. - - 이제 다음으로 Resource를 살펴보자. - - ### Resource ```python @@ -314,7 +302,6 @@ def help_resource() -> str: " - stock-answer: Message template for composing a stock response\n" "\n(Invoke the LLM from the client.)\n" ) - ``` Resource는 에이전트가 참고할 수 있는 정적인 자료를 노출하는 용도로 활용한다. @@ -341,8 +328,7 @@ async def main() -> None: for r in resources.resources: read_result = await session.read_resource(r.uri) - print(read_result.contents) - + print(read_result.contents) ``` Resource도 Tools와 동일하게 MCP에서 List 혹은 Read 관련 함수를 제공하며, 클라이언트가 필요 시 적절한 URI로 `read_resource`를 호출한다. @@ -356,7 +342,6 @@ Resource도 Tools와 동일하게 MCP에서 List 혹은 Read 관련 함수를 - file://help.md/: Server usage guide and available tools [TextResourceContents(uri=AnyUrl('file://help.md/'), mimeType='text/plain', meta=None, text='# stock-mcp Help\n\n- get_stock_price(ticker): Look up a Yahoo Finance ticker (e.g., 005930.KS)\n- prompts:\n - extract-stock-code: Message template for extracting a 6-digit stock code\n - stock-answer: Message template for composing a stock response\n\n(Invoke the LLM from the client.)\n')] - ``` @@ -383,7 +368,6 @@ def extract_stock_code_prompt(user_input: str) -> dict[str, Any]: f"Question: {user_input}" ), } - ``` LLM에서 자주 쓰는 프롬프트를 MCP 서버에서 관리한다고 생각하면 된다. @@ -428,7 +412,6 @@ async def main() -> None: ) print("GPT Response: ", response.choices[0].message.content) - ``` 위와 같이 Prompt를 MCP 서버에서 받아온 뒤 LLM에 그대로 전달하면, 서버에서 정의한 템플릿과 인자가 조합된 최종 메시지를 재사용할 수 있다. 복잡한 Few-shot 예제를 서버에 묶어 두고, 클라이언트는 필요한 변수만 넘기는 방식이 깔끔하게 정리된다. @@ -465,7 +448,6 @@ async def main(): result = await Runner.run(agent, "삼성전자 주가 얼마야?") print(result) - ``` From 262333da6aa0f95f76d136ed9f8c89ed6b4360a1 Mon Sep 17 00:00:00 2001 From: ppippi-dev <61408680+ppippi-dev@users.noreply.github.com> Date: Tue, 23 Sep 2025 14:14:05 +0000 Subject: [PATCH 2/2] chore: add English translations for PR #8 --- _posts_en/2025-09-15-mcp-one-page.md | 152 +++++++++++---------------- 1 file changed, 63 insertions(+), 89 deletions(-) diff --git a/_posts_en/2025-09-15-mcp-one-page.md b/_posts_en/2025-09-15-mcp-one-page.md index ba92b2d..f2d04a4 100644 --- a/_posts_en/2025-09-15-mcp-one-page.md +++ b/_posts_en/2025-09-15-mcp-one-page.md @@ -8,32 +8,34 @@ title: MCP - Model Context Protocol --- -In November 2024, Anthropic open-sourced MCP (Model Context Protocol) in Claude. Right after launch, I kept getting asked “Have you tried MCP?” and heard “Once you use it, it’s really convenient.” +In November 2024, Anthropic open-sourced MCP (Model Context Protocol) from Claude. Right after launch, I kept hearing “Have you tried MCP?” and “It’s super convenient once you use it.” -Honestly, it didn’t impress me at first. It didn’t look that different from OpenAI Function Calling, and there were already plenty of ways to extend LLMs. But within just a few months, MCP was quickly integrated into products from major players like OpenAI and Google, becoming a de facto standard candidate. A good example is that you can now [register MCP directly in the ChatGPT UI as a beta feature](https://platform.openai.com/docs/guides/developer-mode). +Honestly, at first I wasn’t impressed. It didn’t look very different from OpenAI or Gemini Function Calling, and there were already plenty of tools to extend LLMs. But within just a few months, MCP was rapidly integrated into products from major players like OpenAI and Google, becoming a de facto standard candidate. A good example: you can now [register an MCP directly in the ChatGPT UI as a beta feature](https://platform.openai.com/docs/guides/developer-mode). Many agent docs now include MCP tutorials as well. -In this post, I’ll implement MCP myself. +In this post, we’ll implement a simple MCP server and client. -## Why revisit MCP -First, let’s see how MCP differs from OpenAI/Gemini connectors or Function Calling. -- Standardized interface: Function Calling specs differ by platform, whereas MCP provides a JSON-RPC–based common contract, so multiple LLMs/agents can connect to the same server. -- Vendor neutrality: Being open source first, the ecosystem grew fast. Cursor, Claude, OpenAI, Gemini, and more can share one server implementation. -- Tool/Resource/Prompt management: Beyond simple API calls, servers can declaratively manage the resources and prompts an agent should reference. -- Local/remote transport options: From STDIO to SSE, it supports multiple transports, so you can scale from local experiments to SaaS with the same codebase. +## Why I took another look at MCP -Thanks to these strengths, MCP has become less “another take on Function Calling” and more like a standard port for agent architectures. Let’s look at how it’s structured and how it actually works. +First, how does MCP differ from OpenAI/Gemini connectors or Function Calling? + +- Standardized interface: Function Calling specs vary by platform, but MCP provides a shared JSON-RPC-based convention so multiple LLMs/agents can connect to the same server. +- Vendor-neutral: Being open source accelerated ecosystem growth. Clients like Cursor, Claude, OpenAI, and Gemini can all share one server implementation. +- Tools, resources, and prompt management: Beyond plain API calls, the server can declaratively expose resources and prompts for the agent to use. +- Local and remote transport options: It supports multiple transports, from STDIO to SSE, so you can scale from local experiments to SaaS with the same codebase. + +Thanks to these strengths, MCP isn’t just “another Function Calling flavor.” It’s closer to a standard port for agent architectures. Let’s see how it’s structured and how it works in practice. ### MCP (Model Context Protocol) -Claude introduces MCP as an “[open standard for connecting AI apps to external systems](https://modelcontextprotocol.io/docs/getting-started/intro).” +Claude introduces MCP as an “[open-source standard for connecting AI applications to external systems](https://modelcontextprotocol.io/docs/getting-started/intro).” -You’ll often see the phrase “Think of MCP like a USB‑C port for AI applications.” In other words, MCP is the protocol that connects AI apps to external systems. +You’ll also see the familiar analogy: “Think of MCP as a USB-C port for AI applications.” In short, MCP is a protocol that connects AI apps to external systems. -Compared to Function Calling, which follows a vendor-defined JSON schema, MCP standardizes the full flow: connection, initialization, tool listing, and resource access. Servers can expect “the same handshake regardless of the client,” and clients can discover (list) and invoke functions based on descriptions. +Compared to Function Calling, instead of following a single vendor’s JSON schema, MCP standardizes the full flow: connect, initialize, list tools, access resources, etc. Servers can expect the same handshake from any client, and clients can discover features via List and call them using their Description. @@ -41,36 +43,32 @@ Compared to Function Calling, which follows a vendor-defined JSON schema, MCP st -A simple example: asking GPT to fetch today’s calendar events. - -Without MCP, GPT can’t do it on its own. With MCP, it can call an API that reads the calendar and retrieve the data. While Function Calling could also make this work, MCP, as an open protocol, lets both GPT and Gemini call the same capability—effectively a shared standard. +A simple example: ask GPT to fetch today’s calendar events. -I usually [register MCP servers in Cursor](https://docs.cursor.com/en/context/mcp) and use them there. +Without MCP, GPT can’t do that on its own. With MCP, it can call a calendar API and fetch the data. Sure, Function Calling can also do this, but MCP is an open protocol you can set up so both GPT and Gemini can call the same server—a true cross-vendor standard. -## Quick look at MCP terminology +## Key MCP terms -Let’s keep the terminology lightweight. +Let’s keep terminology light and just cover the essentials. ### Participants -MCP has two main roles: Client and Server. MCP docs also mention an MCP Host—think of it as an app like Cursor that manages multiple clients. +MCP has two main roles: Client and Server. MCP Docs also mention an MCP Host, which you can think of as an app like Cursor that manages multiple clients. ![image-20250916001157470](/assets/img/2025-09-15-mcp-one-page/image-20250916001157470.png) -Client and server roles: +Roles: -- Client: LLM apps/agents/IDEs, etc. They connect to an MCP server to use its capabilities and resources. -- Server: Wraps external systems and exposes them via the MCP interface. Examples: calendars, issue trackers, databases, filesystems. +- Client: LLM apps/agents/IDEs, etc. They connect to MCP servers to use capabilities and resources. +- Server: Wraps external systems and exposes them via the MCP interface. Examples: calendar, issue tracker, database, filesystem. -Notably, MCP recommends a 1:1 client–server session. +Note that MCP recommends a 1:1 session between client and server. -An MCP server can run locally or remotely. Locally, it uses STDIO transport and runs on the same machine as the client—this is a local MCP. - -If it runs on an external platform and connects over SSE, that’s a remote MCP server. +An MCP server can connect locally or remotely. Locally, it uses STDIO and runs on the same OS as the client—this is a local MCP. If it runs on an external platform and connects over SSE, it becomes a remote MCP server communicating over the network. @@ -78,31 +76,29 @@ If it runs on an external platform and connects over SSE, that’s a remote MCP There are two major layers: Data Layer and Transport Layer. -The Data Layer defines the JSON-RPC–based client–server protocol, lifecycle, and core primitives like tools, resources, prompts, and notifications. When building an MCP server, you’ll frequently work with tools/resources/prompts—more on these later. +The Data Layer defines the JSON-RPC-based client–server protocol and lifecycle, plus core primitives like tools, resources, prompts, and notifications. When building an MCP server, you’ll often work with tools, resources, and prompts—we’ll dig into those shortly. -The Transport Layer handles connection setup per transport, message framing, and authentication to ensure data can flow. This is where you choose between STDIO (Standard Input/Output), SSE (Server-Sent Events), etc. +The Transport Layer handles the connection method, message framing, and authentication so data can flow: e.g., STDIO (standard input/output) or SSE (server-sent events). -See [MCP Docs Architecture](https://modelcontextprotocol.io/docs/learn/architecture) for details. This post focuses on hands-on work; we’ll keep it at that and dive into code. +For details, see the [MCP Docs Architecture](https://modelcontextprotocol.io/docs/learn/architecture). We’ll focus on hands-on practice here and revisit specifics in code. -## Setup +## Environment To follow along, you’ll need: -- Python 3.11+ and a virtual env via uv (or pip) +- Python 3.11+ and a virtualenv via uv (or pip) - Libraries used in the examples: fastmcp, openai, yfinance, etc. -- An OpenAI API key (only if you want to try the LLM calls; client–server interactions work without it) - +- OpenAI API key (for LLM calls; not required to test client–server interaction) -## Let’s look at it in code -The theory can feel abstract, so let’s walk through code. +## Let’s look at code -We’ll start with the MCP server. I migrated a stock price lookup I’ve used before to an MCP-based server. +The theory can be abstract—walking through code helps. We’ll start with the MCP server. I ported a stock price lookup I’ve used before to MCP. -With Python’s FastMCP library, you can spin up a server quickly. Create a FastMCP instance, then register features with the @mcp.tool decorator. +With Python’s FastMCP, you can spin up a server quickly. Instantiate FastMCP and register functions with @mcp.tool. ```python from mcp.server.fastmcp import FastMCP @@ -111,16 +107,15 @@ from mcp.server.fastmcp import FastMCP mcp = FastMCP( name="stock-mcp", ) - ``` ### Tools -Tools belong to the Data Layer and are what you’ll use most on the server. +Tools belong to the Data Layer and are the part you’ll use most on the server. -The basic pattern: define a normal Python function and expose it with @mcp.tool. Provide a name and description so the client can read metadata via list_tools and know what inputs to provide. +The basic pattern: define a normal Python function and expose it with @mcp.tool. If you include a name and description, clients can read the metadata via list_tools to learn what inputs to provide. ```python # ===== Tools ===== @@ -130,12 +125,9 @@ def change_hello_to_hi(args: dict[str, Any]) -> dict[str, Any]: return {"output": text.replace("Hello", "Hi")} ``` -We accept args as a dict because it’s serialized directly into JSON-RPC messages. If you add type hints, FastMCP auto-generates a JSON Schema to tell agents which fields are required. Pydantic works nicely here, but for the example we’ll stick to a simple dict. +args is a dict so it can serialize directly to/from JSON-RPC messages. With type hints, FastMCP can auto-generate a JSON Schema to tell agents which fields are required. You could use Pydantic here, but we’ll keep it to a simple dict in the example. - -Now a more practical example. - -Below, we fetch stock info for a given ticker using yfinance. +A more practical example: fetch stock info for an input ticker using yfinance. ```python # mcp_simple_stdio/mcp_server.py @@ -171,10 +163,9 @@ def get_stock_price(args: dict[str, Any]) -> dict[str, Any]: } except Exception as e: return {"success": False, "error": str(e)} - ``` -It may feel abrupt to jump to client code, but this shows connecting to the MCP server over STDIO and initializing. Focus on session.call_tool. +You’ll now see some client code: it connects to the MCP server over STDIO and initializes the session. Focus on session.call_tool. ```python # mcp_simple_stdio/mcp_client.py @@ -195,23 +186,22 @@ async def main() -> None: {"args": {"ticker": "005930.KS"}}, ) print(result.content) - ``` -Running that prints logs like: +Running this prints something like: ```shell [TextContent(type='text', text='{\n "success": true,\n "company_name": "Samsung Electronics Co., Ltd.",\n "ticker": "005930.KS",\n "current_price": 78200.0,\n "previous_close": 79400.0,\n "change": "-1200.00 (-1.51%)"\n}', annotations=None, meta=None)] ``` Flow recap: -- ClientSession.initialize() negotiates protocol versions and retrieves server capabilities (tools/resources/prompts). -- session.call_tool() sends a JSON-RPC call_tool request; the server responds wrapped in standard types like TextContent. -- The ListToolsRequest you see in logs is issued automatically by FastMCP early in the session to cache the available tools. +- ClientSession.initialize() negotiates protocol version and fetches available tools/resources/prompts. +- session.call_tool() sends a JSON-RPC call_tool request; the server responds with a standardized content type like TextContent. +- The ListToolsRequest you see in logs is auto-invoked by FastMCP to cache tools at session start. -In short, the client receives standardized results from whatever functions the MCP server exposes. +In short, the client receives a standardized response based on the server’s functions. -That looked like a plain function call; here’s how it looks when you involve an LLM: +So far this looks like a direct function call. Here’s how it looks when you have an LLM select and call the tool: @@ -260,16 +250,13 @@ async def main() -> None: {"args": {"ticker": "005930.KS"}}, ) print(result.content) - ``` -Suppose the question is “How much is the stock price of Samsung Electronics?” - -MCP provides an API to list tools. I added a test-only fake_function alongside the real one. +Suppose the question is: “How much is the stock price of Samsung Electronics?” -We first list the tools, include them in the prompt, and have the model pick the one needed to answer the question. +MCP provides an API to list tools. I added a fake_function for testing alongside the real function. We list tools, feed them into the prompt, and ask the model to pick exactly one tool for the question. -GPT chooses get_stock_price and we execute it. (Argument inference is omitted to keep the code short.) +GPT chooses get_stock_price and we call it. (Argument inference is omitted to keep code short.) Logs: @@ -282,18 +269,13 @@ PROMPT_MESSAGE: If you don't know the answer, return 'None' GPT Response: get_stock_price - ``` -You can handcraft prompts, but in production, frameworks often handle “tool selection → argument construction → result interpretation” for you. Still, it’s worth experiencing the low-level flow once to see what messages are exchanged and what exceptions you get on failure. - -You also don’t have to build the LLM loop yourself. As I’ll briefly note at the end, OpenAI SDK and Google ADK include MCP integrations that make this much simpler. - - - -Now let’s look at Resources. +You can handcraft prompts, but in production a framework often handles “tool selection → argument creation → result interpretation.” Still, it’s worth experiencing the low-level flow at least once to see what messages are exchanged and what exceptions occur on failure. +You don’t have to implement LLM logic yourself either. As I’ll briefly note later, the OpenAI SDK and Google ADK already provide MCP integrations that make this even easier. +Next, let’s look at Resources. ### Resource @@ -309,10 +291,9 @@ def help_resource() -> str: " - stock-answer: Message template for composing a stock response\n" "\n(Invoke the LLM from the client.)\n" ) - ``` -Resources expose static materials an agent can reference. +Resources expose static references the agent can consult. @@ -336,13 +317,10 @@ async def main() -> None: for r in resources.resources: read_result = await session.read_resource(r.uri) - print(read_result.contents) - + print(read_result.contents) ``` -Like Tools, MCP provides list/read APIs for Resources, and the client calls read_resource with the appropriate URI when needed. - - +Like Tools, Resources have list and read methods. The client lists available resources and reads them via read_resource using their URI. Logs: @@ -357,9 +335,9 @@ Logs: ### Prompt -Finally, Prompts let the server define reusable message templates. +Prompts let the server define reusable message templates. -If you’ve worked with LLM APIs, this will feel familiar. Store prompts you call repeatedly with different variables on the server, and multiple clients can share the same logic while varying only the inputs. Code first: +If you’ve worked with LLM APIs, this pattern is familiar. When you need to call the same prompt with different variables, register it on the server so multiple clients can share the logic while passing different args. Let’s jump to code. @@ -377,14 +355,13 @@ def extract_stock_code_prompt(user_input: str) -> dict[str, Any]: f"Question: {user_input}" ), } - ``` Think of this as managing commonly used LLM prompts on the MCP server. -Why on the server instead of the client? Because tools, resources, and prompts are all managed by the MCP server, and clients “discover” and invoke them consistently. It also centralizes versioning for templates shared across a team. +Why manage them on the server instead of the client? Because Tools, Resources, and Prompts share a consistent “server-managed, client-discovers-and-calls” model. It also makes versioning and sharing team-wide templates easier. -You can list prompts with list_prompts and, similar to Tools, pass arguments to get the final prompt message. +You can fetch prompts with list_prompts and, like Tools, pass args to get the final message. @@ -403,7 +380,7 @@ async def main() -> None: async with ClientSession(read, write) as session: await session.initialize() - # extract-stock-code 프롬프트 사용 예시 + # extract-stock-code prompt example prompt_result = await session.get_prompt( "extract-stock-code", {"user_input": "How much is the stock price of Samsung Electronics?"}, @@ -422,10 +399,9 @@ async def main() -> None: ) print("GPT Response: ", response.choices[0].message.content) - ``` -Fetch the prompt from the MCP server and pass it straight to the LLM—the server-defined template and your arguments are combined into a final message you can reuse. You can keep complex few-shot examples on the server and have clients only supply variables. +By retrieving the prompt from the MCP server and sending it to the LLM as-is, you can reuse final messages composed from server-defined templates and input variables. You can also bundle complex few-shot prompts on the server, and clients just pass the variables they need. Logs: @@ -438,8 +414,7 @@ GPT Response: 005930 ## Wrap-up -At first glance, the code can look a bit complex. -These days, agent SDKs support MCP, so you can get the same effect with much simpler code, like below: +At first glance, the code can look a bit involved. These days, agent SDKs support MCP, so you can get the same effect with much simpler code: ```python @@ -459,9 +434,8 @@ async def main(): result = await Runner.run(agent, "삼성전자 주가 얼마야?") print(result) - ``` -The full example I used is in my [GitHub repo](https://github.com/ppippi-dev/LLMOps/tree/main/mcp_test). +All example code used here is in my [GitHub repository](https://github.com/ppippi-dev/LLMOps/tree/main/mcp_test). -As an aside, there are still plenty of security concerns around MCP. Keep this in mind and be especially careful when connecting to remote MCP servers. \ No newline at end of file +As a side note, there are still many security issues to consider with MCP. Be especially careful when connecting to remote MCP servers. \ No newline at end of file