diff --git a/.github/workflows/translate-to-english.yml b/.github/workflows/translate-to-english.yml index c6042e3..a075998 100644 --- a/.github/workflows/translate-to-english.yml +++ b/.github/workflows/translate-to-english.yml @@ -69,4 +69,5 @@ jobs: uses: stefanzweifel/git-auto-commit-action@v5 with: commit_message: "chore: add English translations for PR #${{ github.event.pull_request.number }}" - file_pattern: src/content/blog-en/**/*.md + file_pattern: 'src/content/blog-en/*.md' + add_options: '-A' diff --git a/public/img/mcp-context-forge/image-20251218010740484.png b/public/img/mcp-context-forge/image-20251218010740484.png new file mode 100644 index 0000000..8dce71c Binary files /dev/null and b/public/img/mcp-context-forge/image-20251218010740484.png differ diff --git a/public/img/mcp-context-forge/image-20251219001059140.png b/public/img/mcp-context-forge/image-20251219001059140.png new file mode 100644 index 0000000..3a69e16 Binary files /dev/null and b/public/img/mcp-context-forge/image-20251219001059140.png differ diff --git a/public/img/mcp-context-forge/image-20251219001536178.png b/public/img/mcp-context-forge/image-20251219001536178.png new file mode 100644 index 0000000..6794572 Binary files /dev/null and b/public/img/mcp-context-forge/image-20251219001536178.png differ diff --git a/public/img/mcp-context-forge/image-20251219001727809.png b/public/img/mcp-context-forge/image-20251219001727809.png new file mode 100644 index 0000000..672f6bb Binary files /dev/null and b/public/img/mcp-context-forge/image-20251219001727809.png differ diff --git a/public/img/mcp-context-forge/image-20251219001743938.png b/public/img/mcp-context-forge/image-20251219001743938.png new file mode 100644 index 0000000..ad054b8 Binary files /dev/null and b/public/img/mcp-context-forge/image-20251219001743938.png differ diff --git a/public/img/mcp-context-forge/image-20251219002025981.png b/public/img/mcp-context-forge/image-20251219002025981.png new file mode 100644 index 0000000..33e1bcd Binary files /dev/null and b/public/img/mcp-context-forge/image-20251219002025981.png differ diff --git a/public/img/mcp-context-forge/image-20251219002100211.png b/public/img/mcp-context-forge/image-20251219002100211.png new file mode 100644 index 0000000..97a659a Binary files /dev/null and b/public/img/mcp-context-forge/image-20251219002100211.png differ diff --git a/public/img/mcp-context-forge/image-20251219002139585.png b/public/img/mcp-context-forge/image-20251219002139585.png new file mode 100644 index 0000000..656be5f Binary files /dev/null and b/public/img/mcp-context-forge/image-20251219002139585.png differ diff --git a/public/img/mcp-context-forge/image-20251219002209987.png b/public/img/mcp-context-forge/image-20251219002209987.png new file mode 100644 index 0000000..1fbf797 Binary files /dev/null and b/public/img/mcp-context-forge/image-20251219002209987.png differ diff --git a/public/img/mcp-context-forge/image-20251219002222560.png b/public/img/mcp-context-forge/image-20251219002222560.png new file mode 100644 index 0000000..3b93d4a Binary files /dev/null and b/public/img/mcp-context-forge/image-20251219002222560.png differ diff --git a/public/img/mcp-context-forge/image-20251219002234945.png b/public/img/mcp-context-forge/image-20251219002234945.png new file mode 100644 index 0000000..658e38a Binary files /dev/null and b/public/img/mcp-context-forge/image-20251219002234945.png differ diff --git a/public/img/mcp-context-forge/image-20251219002305321.png b/public/img/mcp-context-forge/image-20251219002305321.png new file mode 100644 index 0000000..c366abe Binary files /dev/null and b/public/img/mcp-context-forge/image-20251219002305321.png differ diff --git a/public/img/mcp-context-forge/image-20251219002426793.png b/public/img/mcp-context-forge/image-20251219002426793.png new file mode 100644 index 0000000..dba646e Binary files /dev/null and b/public/img/mcp-context-forge/image-20251219002426793.png differ diff --git a/scripts/translate_to_en.py b/scripts/translate_to_en.py index 7b54587..c8ca0dd 100644 --- a/scripts/translate_to_en.py +++ b/scripts/translate_to_en.py @@ -20,15 +20,38 @@ POSTS_EN_DIR = REPO_ROOT / "src" / "content" / "blog-en" -PROMPT_SYSTEM = ( - "You are an expert technical writer and translator. Translate the given Korean Astro blog post content into natural, concise American English.\n" - "- Preserve front matter keys; translate 'title' and any 'description' or 'summary' fields to English.\n" - "- Keep Markdown structure, headings, code blocks, links, images, and footnotes intact.\n" - "- Do not hallucinate code or change code semantics.\n" - "- Maintain YAML front matter formatting.\n" - "- If there is mixed language, prefer English.\n" - "- Do NOT change the file name or slug. The output will be saved using the exact same filename as the source.\n" -) +PROMPT_SYSTEM = """You are an expert technical writer and translator specializing in MLOps, LLMOps, and AI infrastructure content. + +## Task +Translate the given Korean Astro blog post into professional, SEO-optimized American English. + +## SEO & Front Matter Guidelines +- **title**: Create a compelling, keyword-rich title (50-60 characters ideal). Include primary keywords like tool names, technologies, or concepts. +- **description**: Write a clear meta description (150-160 characters) that includes target keywords and encourages clicks. Summarize the value proposition. +- Preserve all other front matter keys (pubDate, tags, etc.) exactly as they are. + +## Content Translation Guidelines +- Write in a professional yet approachable tone suitable for a senior engineer audience. +- Preserve technical accuracy - do NOT change code, commands, configurations, or technical terms. +- Keep the author's voice and personal experiences intact (e.g., "In my experience...", "I found that..."). +- Maintain all Markdown structure: headings, code blocks, links, images, lists, blockquotes. +- Translate Korean technical jargon to commonly accepted English equivalents. +- Keep product names, tool names, and proper nouns unchanged (e.g., mcp-context-forge, Kubernetes, PostgreSQL). +- For headings, use clear and descriptive phrases that work well for SEO and readability. + +## Quality Standards +- Use active voice where possible. +- Be concise - remove filler words common in Korean-to-English translation. +- Ensure the translation reads naturally, as if originally written in English. +- Do NOT add or remove content - translate faithfully. +- Do NOT change the filename or slug. + +## Output Format +Return the complete translated Markdown file with YAML front matter. +- IMPORTANT: The title and description MUST be translated to English. Do NOT keep them in Korean. +- IMPORTANT: Always wrap title and description values in double quotes to handle special YAML characters like colons. Example: title: "My Title: A Subtitle" +- Do NOT wrap the output in markdown code blocks (no ``` markers). +- Start directly with --- and the YAML front matter.""" def normalize_repo_path(path_like: str | Path) -> Path: @@ -86,20 +109,86 @@ def call_openai(model: str, system_prompt: str, user_prompt: str) -> str: return resp.choices[0].message.content +def strip_markdown_codeblock(text: str) -> str: + """Strip markdown code block wrappers if present.""" + text = text.strip() + if text.startswith("```"): + lines = text.split("\n") + if lines[0].startswith("```"): + lines = lines[1:] + if lines and lines[-1].strip() == "```": + lines = lines[:-1] + text = "\n".join(lines) + return text.strip() + + +def contains_korean(text: str) -> bool: + """Check if text contains Korean characters (Hangul).""" + for char in str(text): + if "\uac00" <= char <= "\ud7a3" or "\u1100" <= char <= "\u11ff": + return True + return False + + def split_front_matter(translated_markdown: str) -> tuple[dict, str]: - m = re.match(r"^---\n([\s\S]+?)\n---\n?([\s\S]*)$", translated_markdown.strip()) + text = strip_markdown_codeblock(translated_markdown) + + patterns = [ + r"^---\s*\n([\s\S]+?)\n---\s*\n?([\s\S]*)$", + r"^---\s*\n([\s\S]+?)\n---\s*([\s\S]*)$", + r"^-{3,}\s*\n([\s\S]+?)\n-{3,}\s*\n?([\s\S]*)$", + ] + + m = None + for pattern in patterns: + m = re.match(pattern, text) + if m: + break + if not m: - return {}, translated_markdown + print(f"[translate] Debug: Could not match frontmatter. First 300 chars:") + print(text[:300]) + return {}, text + fm_text, body = m.group(1), m.group(2) try: fm = yaml.safe_load(fm_text) or {} if not isinstance(fm, dict): + print(f"[translate] Debug: Parsed YAML is not a dict: {type(fm)}") + fm = {} + except yaml.YAMLError as e: + print(f"[translate] Debug: YAML parse error, trying to fix: {e}") + fixed_fm_text = fix_yaml_special_chars(fm_text) + try: + fm = yaml.safe_load(fixed_fm_text) or {} + if not isinstance(fm, dict): + fm = {} + else: + print("[translate] Debug: Fixed YAML successfully") + except yaml.YAMLError: + print(f"[translate] Debug: Could not fix YAML:\n{fm_text[:300]}") fm = {} - except yaml.YAMLError: - fm = {} return fm, body +def fix_yaml_special_chars(fm_text: str) -> str: + """Fix YAML values that contain special characters like colons.""" + lines = fm_text.split("\n") + fixed_lines = [] + for line in lines: + if line.startswith("title:") or line.startswith("description:"): + key, _, value = line.partition(":") + value = value.strip() + if value and not (value.startswith('"') and value.endswith('"')): + if value.startswith("'") and value.endswith("'"): + pass + elif ":" in value or "#" in value: + value = '"' + value.replace('"', '\\"') + '"' + line = f"{key}: {value}" + fixed_lines.append(line) + return "\n".join(fixed_lines) + + def ensure_posts_en_dir(): POSTS_EN_DIR.mkdir(parents=True, exist_ok=True) @@ -135,11 +224,20 @@ def main() -> int: prompt = build_prompt(post) translated = call_openai(model, PROMPT_SYSTEM, prompt) fm, body = split_front_matter(translated) + original_fm = dict(post) if not fm: - fm = dict(post) - if "title" not in fm or not fm["title"]: - fm["title"] = dict(post).get("title", "") + print(f"[translate] Warning: Could not parse frontmatter, using original") + fm = original_fm.copy() + + for key in original_fm: + if key not in fm: + fm[key] = original_fm[key] + + if contains_korean(fm.get("title", "")): + print(f"[translate] Warning: title still contains Korean: {fm.get('title')}") + if contains_korean(fm.get("description", "")): + print(f"[translate] Warning: description still contains Korean") en_path = to_en_filename(src) en_path.parent.mkdir(parents=True, exist_ok=True) diff --git a/src/content/blog-en/mcp-context-forge.md b/src/content/blog-en/mcp-context-forge.md new file mode 100644 index 0000000..5b7ee0a --- /dev/null +++ b/src/content/blog-en/mcp-context-forge.md @@ -0,0 +1,165 @@ +--- +description: Hands-on experience running IBM’s open-source MCP Gateway, mcp-context-forge, + in production. Learn Virtual Server-based tool authorization, the MCP Catalog, metrics, + and more. +pubDate: '2025-12-19' +tags: +- LLMOps +- MCP +- Agent +title: 'MCP Gateway Recommendation: mcp-context-forge in Production' +--- + +## TL;DR + +> I consider an MCP Gateway one of the essential tools for building an Agentic AI platform. If you’re accumulating more MCP servers than you can manage—or you keep spinning up redundant MCP servers—consider adopting the open-source project mcp-context-forge. + + + +Recently, while working on a project to set up agent infrastructure, I got hands-on experience with several open-source projects, and I wanted to share what I learned. + +Building agent infrastructure isn’t just about creating a system that calls an LLM. It means turning it into a **software platform** that can be operated sustainably. I’d like to introduce a few open-source projects that helped a lot with the architecture design, and this is the first post in that series. I decided to start with the tool I’m most attached to—one I’ve even contributed to and use heavily. + +It’s the MCP Gateway open source project: [**mcp-context-forge**](https://github.com/IBM/mcp-context-forge). Let’s walk through it step by step. + +As of writing, it’s V1.0.0-BETA-1. + + + +## Why Do You Need an MCP Gateway? + +Before explaining mcp-context-forge, it’s worth addressing **“Why do you need an MCP Gateway?”** first. Before adopting an MCP Gateway, I think it’s good to ask yourself: + +1. Do I have enough MCP servers and tools to justify needing an MCP Gateway? +2. Will agents need to call the same MCP? + +In early-stage or small projects, you may not feel the need for an MCP Gateway. It can even feel like over-engineering since it adds management overhead and can become a single point of failure. + +But once you hit the point where **the number of agents and MCPs grows and the system starts scaling**, the story changes. From that moment, the benefits can be so strong that the initial effort to introduce a gateway feels trivial. + +The biggest advantage is the MCP Catalog. When everyone is churning out MCP servers because “MCP is great,” understanding what MCP servers and tools exist inside your organization becomes an important problem. + +I’ve also run into situations where multiple MCP tools were created that do the exact same thing. In that kind of environment, being able to view a list of what MCP servers and tools exist is a major win. + + +## What Is mcp-context-forge? + +Now let’s look at mcp-context-forge more directly. + +`mcp-context-forge` is an MCP Gateway open-sourced by IBM. It efficiently brokers increasingly complex connections between agents and tools. + +To be honest, there aren’t many MCP Gateway tools out there. When I was doing research, mcp-context-forge and the kgateway + Agentgateway combination looked appealing. But Agentgateway felt relatively complex to operate, and I felt it wasn’t mature enough for production use at the time. We also already use a different gateway stack internally. + +For those reasons, I chose mcp-context-forge. Of course, mcp-context-forge itself says it shouldn’t be used at the production level yet since it’s not a stable release. In my case, I *am* running it in production, and I haven’t felt any major issues. + + + +### Try Running It + +My production environment is Kubernetes, but in this post I’ll use Docker. + +Given how many GitHub stars it already has, you can test it without much trouble. + +```bash +git clone https://github.com/IBM/mcp-context-forge +docker-compose up +``` + +The setup is mainly composed of the Gateway, a main DB (Postgres or MariaDB), Redis, and `fast_time_server`. +It’s not a complicated architecture, so it’s easy to operate. + +There’s a separate time-related server called `fast_time_server`. I’m planning to look into that later (it doesn’t feel very impactful so far). + + + +The start screen looks like this. You get a colorful UI, and it also provides a useful login experience—including optional SSO integration. + +The default email address and password are `admin@example.com`, `changeme`. + +![image-20251218010740484](/img/mcp-context-forge/image-20251218010740484.png) + + + +After logging in, you’ll see a screen like the following. I’ve been watching this project since v0.8, and as of v1.0.0-BETA-1, you can really feel how much the design has evolved. + +![image-20251219001536178](/img/mcp-context-forge/image-20251219001536178.png) + +In the sidebar, there are various options: + +- MCP Servers: In mcp-context-forge, these are labeled as “Gateways,” but you can think of them as MCP Servers. When you register one here, the tools, prompts, and resources on that MCP server are automatically registered. +- Virtual Servers: I consider this mcp-context-forge’s core feature. You can create a virtual gateway and select the tools, prompts, and resources you need—then use it just like an MCP server. This is a very useful feature for configuring per-agent toolsets and separating tool permissions. +- Tools, Prompts, Resources: Same meaning as in MCP. +- Roots: I’m not sure what this does... I haven’t used it. It looks like a feature for pulling content from storage on the server, but I don’t really know. +- MCP Registry: mcp-context-forge runs a registry you can register from easily. You can add a registry you like with one click. +- Agents: You can register not only MCP servers but also agents. +- Metrics: You can view mcp-context-forge metrics. You can see which tools are called frequently, when they were used most recently, and identify unused tools. +- Teams / Users: You can manage mcp-context-forge user permissions. + +![image-20251219001059140](/img/mcp-context-forge/image-20251219001059140.png) + +The screen above is the MCP Registry. If you click Add Server for a few entries for testing, you’ll see them registered like in the screenshot below. + +Of course, for most tools, adding the server isn’t the end—you still need to use whatever authentication each server requires. + +![image-20251219001727809](/img/mcp-context-forge/image-20251219001727809.png) + +The screen above shows what gets created when you register GitHub, for example. + +![image-20251219002025981](/img/mcp-context-forge/image-20251219002025981.png) + +I also registered a few registries for testing besides GitHub. Once configuration is complete, you can browse the tools provided by each MCP server in Tools. + +![image-20251219002100211](/img/mcp-context-forge/image-20251219002100211.png) + +As shown above, you can test a registered tool. + +![image-20251219002139585](/img/mcp-context-forge/image-20251219002139585.png) + +This is the Virtual Servers screen—the core feature I emphasized earlier. As you can see, you can select the MCP Servers and the Tools / Resources / Prompts you want. + +![image-20251219002209987](/img/mcp-context-forge/image-20251219002209987.png) + +After selecting a Virtual Server, you can confirm it gets created like this. + +![image-20251219002222560](/img/mcp-context-forge/image-20251219002222560.png) + +When you view Config, you can choose as shown above. It supports Stdio, SSE, and HTTP, so there’s no issue using it for MCP. + +![image-20251219002234945](/img/mcp-context-forge/image-20251219002234945.png) + +This is an example of the SSE method, showing the familiar MCP registration configuration. If you look at the headers, you’ll see it requires a Bearer token—mcp-context-forge uses tokens to control authorization. + +![image-20251219002305321](/img/mcp-context-forge/image-20251219002305321.png) + +The token configuration screen looks like this. For each token, you can specify which Virtual Servers it can access. There’s also an IP restriction feature, but I haven’t used it. You can also configure permissions, but in my case I typically just define which tools each agent can access, so I haven’t used that either. + +Now I’ll wrap up by summarizing the pros and cons of mcp-context-forge. + +## Pros + +The biggest advantage is that it serves as an MCP Catalog. You can understand at a glance what MCP servers and tools exist in your organization, which helps prevent redundant MCP servers from being created. It also makes cross-team collaboration easier because you can quickly answer questions like, “Do we have a tool for this?” + +Permission separation through Virtual Servers is also compelling. You can specify only the tools needed for each agent, which prevents unnecessary tool calls and provides peace of mind from a security standpoint. The metrics feature also makes it easy to see which tools are actually being used versus which are being neglected. + +The simple architecture is another plus. You can operate it with just PostgreSQL or MariaDB and Redis, so the operational burden is low. Since it supports Stdio, SSE, and HTTP, there are no compatibility issues with existing MCP clients. + +## Cons + +I didn’t really feel any major functional downsides. If I had to pick one, it’s that it’s still a beta version, so some features are not implemented yet. + +Also, compared to connecting directly from an agent to an MCP, it seems there are a few authentication methods it doesn’t support. In my case, I remember trying to connect using FastMCP Client and failing, so I switched to the mcp library instead. This may have been resolved by now. + +Finally, since it’s a gateway, you should consider that it can become a Single Point of Failure. That said, this is a common characteristic of all gateways. + +## Conclusion + +An MCP Gateway becomes mandatory infrastructure once the number of MCP servers and tools starts growing. mcp-context-forge is still in beta, but based on my experience running it in a real production environment, it worked reliably without major issues. + +Most importantly, it’s great that IBM has open-sourced it and is actively developing it. I’m using it while contributing, and issue turnaround is fairly fast. + +If you’re considering adopting an MCP Gateway, I recommend taking a look at mcp-context-forge. + +## References + +- [mcp-context-forge GitHub](https://github.com/IBM/mcp-context-forge) +- [mcp-context-forge official documentation](https://ibm.github.io/mcp-context-forge/) \ No newline at end of file diff --git a/src/content/blog/mcp-context-forge.md b/src/content/blog/mcp-context-forge.md new file mode 100644 index 0000000..5af28c9 --- /dev/null +++ b/src/content/blog/mcp-context-forge.md @@ -0,0 +1,200 @@ +--- +description: IBM에서 오픈소스로 공개한 MCP Gateway, mcp-context-forge를 프로덕션 환경에서 운영한 경험을 공유합니다. Virtual Server를 통한 툴 권한 분리, MCP Catalog, 메트릭 기능 등 핵심 기능을 소개합니다. +pubDate: '2025-12-19' +tags: +- LLMOps +- MCP +- Agent +title: MCP Gateway 추천, mcp-context-forge 사용기 +--- + +## TL;DR + +> MCP Gateway는 Agentic AI 플랫폼을 만드는데 꼭 필요한 툴 중 하나라고 생각합니다. MCP가 점점 많아지고 관리하기 힘든 경우, 중복된 MCP 서버를 계속해서 찍어내고 있는 경우, mcp-context-forge라는 오픈소스 도입을 검토해보세요. + + + +저는 최근, Agent 인프라를 셋팅하는 프로젝트를 하면서 여러 오픈소스를 경험했고, 그 경험을 글로나마 공유하고자 합니다. + +Agent 인프라를 구축한다는 것은 단순히 LLM을 호출하는 시스템을 만드는 것이 아닙니다. 지속적으로 운영 가능한 **소프트웨어 플랫폼**으로 만들어야 한다는 것을 의미합니다. 이러한 아키텍처 설계를 위해 큰 도움이 되었던 오픈소스들을 소개하고자 하고, 오늘이 그 첫번째입니다. 첫번째로 제가 가장 애정을 가지고 사용하고 있는 툴을 소개하기로 했습니다. 실제로 컨트리뷰트까지하면서 애용하고 있습니다. + +바로, MCP Gateway인 [**mcp-context-forge**](https://github.com/IBM/mcp-context-forge) 라는 오픈소스입니다. 이제 하나씩 알아보는 시간을 가지면 좋을 것 같습니다. + +글을 작성하는 현 시점은 V1.0.0-BETA-1인점 말씀드립니다. + + + +## 왜 MCP Gateway가 필요한가? + +mcp-context-forge를 설명하기 전에, 먼저 **"왜 MCP Gateway가 필요한가?"**부터 짚고 넘어가는 것이 좋을 것 같습니다. 저는 MCP Gateway를 도입하기 전에, 아래의 질문을 먼저 해보는게 좋다고 생각합니다. + +1. 현재 내가 MCP Gateway가 필요할 만큼, 관리해야하는 MCP 서버 및 툴이 많은가? +2. Agent가 동일한 MCP를 호출할 일이 있는가? + +사실 초기 단계나 작은 규모의 프로젝트에서는 MCP Gateway의 필요성이 잘 느껴지지 않을 수 있습니다. 오히려 관리 포인트 증가 및 단일 실패 지점이 될 수 있어, 오버 엔지니어링으로 느껴지기도 합니다. + +하지만, **Agent와 MCP의 개수가 늘어나고 시스템이 확장되는 시점**이 오면 이야기는 달라집니다. 이때부터는 Gateway 도입에 들어가는 수고가 우스울 정도로 강력한 효과를 발휘하게 됩니다. + +가장 큰 장점은 MCP Catalog입니다. 모두가 MCP가 좋다며 MCP 서버를 찍어내고 있는 상황에서, 현재 사내에 어떤 MCP 서버와 툴이 있는지 파악하는 것은 중요한 문제입니다. + +저 또한, 동일한 기능을 하는 MCP tool들이 여러개 생성되어 있는 것을 경험하기도 했고요. 이런 상황에서 어떤 MCP 서버 및 툴이 있는지 리스트를 볼 수 있는 건 큰 장점일 것입니다. + + +## mcp-context-forge란 무엇인가? + +이제 본격적으로 mcp-context-forge를 알아보면 좋을 것 같습니다. + +`mcp-context-forge`는 IBM에서 오픈소스로 공개한 MCP Gateway입니다. 복잡해지는 Agent와 도구(Tool) 간의 연결을 효율적으로 중개해주는 역할을 합니다. + +사실 MCP Gateway 툴이 많지 않습니다. 제가 리서치를 하던 시점에는, mcp-context-forge와 kgateway + Agentgateway 조합이 매력적으로 보였는데, agentgateway는 상대적으로 운영이 복잡하다고 느꼈고, 프로덕션 레벨에서 사용하기에 완성도가 아직 미흡하다고 느꼈습니다. 이미 사내에서 다른 gateway스택을 사용하고 있는 부분도 있습니다. + +이러한 이유들로 인해 mcp-context-forge를 택하게 되었습니다. 물론 아직 mcp-context-forge는 정식버전 출시전으로 프로덕션 레벨에서 사용하지말라고는 내용이 있기도 합니다. 저의 경우 프로덕션 레벨에서 사용하고 있는데, 큰 문제점을 느끼진 않았습니다. + + + +### 실행시켜보기 + +제가 운영중인 환경은 쿠버네티스이지만 해당 글에서는 docker 기반으로 진행할 예정입니다. + +이미 많은 깃헙 star를 받은 만큼 크게 어려움 없이 테스트해볼 수 있습니다. + +```bash +git clone https://github.com/IBM/mcp-context-forge +docker-compose up +``` + +구성은 크게 Gateway, 메인db(postgres or mariadb), Redis, fast_time_server 으로 되어있습니다. +복잡한 구조는 아니라, 쉽게 운영할 수 있습니다. + +fast_time_server라고, 시간 관련 서버가 따로 있는데, 이거에 대해서는 이후에 알아보려고 합니다. (크게 체감되지는 않습니다.) + + + +시작화면은 다음과 같습니다. 알록달록한 화면을 볼 수 있고, 필요에 따라, SSO도 연결할 수 있어서, 유용한 로그인 기능을 제공합니다. + +기본 Email address 및 Password는 `admin@example.com`, `changeme` 로 설정되어 있으니 참고하세요. + +![image-20251218010740484](/img/mcp-context-forge/image-20251218010740484.png) + + + +로그인 이후 들어가면, 다음과 같은 화면을 볼 수 있습니다. 저는 v0.8부터 해당 오픈소스를 보고있는데, v1.0.0-BETA-1인 현시점에서 보면, 디자인이 많이 진화 했다는 걸 느낄 수 있습니다. + + + +![image-20251219001536178](/img/mcp-context-forge/image-20251219001536178.png) + +사이드바를 보면, 다양한 옵션이 있습니다. + +- MCP Servers: mcp-context-forge에서는 이를 Gateway라는 표현으로 사용하는데, 그냥 MCP Server라고 보면 되고, 여기서 등록을하면 해당 MCP 서버에 있는 tools, prompts, resources 가 자동으로 등록됩니다. +- Virtual Servers: mcp-context-forge의 핵심기능이라고 생각합니다. 가상의 gateway를 두고, 필요한 tools, prompts, resources 들을 지정할 수 있는데, 이후 이를 기존 MCP 서버처럼 사용할 수 있습니다. 에이전트마다 필요한 툴들을 설정하고, 툴 권한 분리를 할 수 있는 유용한 기능입니다. +- Tools, Prompts, Resources: MCP에서 말하는 내용과 동일합니다. +- Roots: 무슨 역할인지 잘 모르겠습니다... 사용해본적도 없습니다. 아마 서버에 있는 저장공간에 있는 내용을 가져다 쓸 수 있는 기능같아 보이는데 잘 모르겠네요. +- MCP Registry: mcp-context-forge에서 편하게 등록할 수 있는 registry를 운영하고 있습니다. 마음에 드는 registry를 원클릭으로 등록할 수 있습니다. +- Agents: MCP 서버들 뿐만 아니라 Agent도 등록할 수 있습니다. +- Metrics: mcp-context-forge의 메트릭을 볼 수 있습니다. 어떤 툴이 많이 호출되는지, 가장 최근이 언제인지 파악할 수 있으며, 사용되지 않는 툴이 어떤 건지 파악하기 좋습니다. +- Teams / Users: mcp-context-forge 유저의 권한 관리를 할 수 있습니다. + + + + + +![image-20251219001059140](/img/mcp-context-forge/image-20251219001059140.png) + +위의 화면은 MCP Registry입니다. 테스트를 위해 몇가지 Add Server를 해보면 아래의 사진처럼 등록되는 걸 알 수 있습니다. + +물론 대부분의 툴은 Add Server만 한다고 끝은 아니고, 각 서버들이 요구하는 인증을 사용해야합니다. + + + +![image-20251219001727809](/img/mcp-context-forge/image-20251219001727809.png) + +위의 화면은 Github을 등록했을 때, 다음과 같이 생성되는 걸 볼 수 있습니다. + + + +![image-20251219002025981](/img/mcp-context-forge/image-20251219002025981.png) + +Github 외에도 몇개의 Registry를 테스트로 등록했는데, 설정이 끝나면, Tools에 MCP 서버가 가진 툴들을 조회할 수 있습니다. + + + + + +![image-20251219002100211](/img/mcp-context-forge/image-20251219002100211.png) + +위의 화면처럼, 등록된 Tool을 테스트해볼 수 있습니다. + + + + + +![image-20251219002139585](/img/mcp-context-forge/image-20251219002139585.png) + +위에가 아까 가장 핵심 기능이라고 강조한 Virtual Servers의 모습입니다. 위와같이 원하는 MCP Server 및 Tools / Resources / Prompts 를 선택할 수 있습니다. + + + + + +![image-20251219002209987](/img/mcp-context-forge/image-20251219002209987.png) + +Virtual Server를 선택하면 위와 같이 생성되는걸 확인할 수 있습니다. + + + +![image-20251219002222560](/img/mcp-context-forge/image-20251219002222560.png) + +Config를 조회하면 위와 같이 선택할 수 있습니다. Stdio, SSE, HTTP를 지원해서 MCP를 이용하는데 문제 없습니다. + + + +![image-20251219002234945](/img/mcp-context-forge/image-20251219002234945.png) + +위의 화면은 SSE방식의 예시인데, 다음과 같이 익숙한 MCP 등록 설정입니다. headers를 보면 Bearer token을 요구하는 것을 알 수 있는데, mcp-context-forge의 권한을 컨트롤 하기 위해 토큰을 사용합니다. + + + + + +![image-20251219002305321](/img/mcp-context-forge/image-20251219002305321.png) + +토큰 설정화면은 위와 같습니다. 토큰마다 접근할 수있는, Virtual Server를 지정할 수 있습니다. IP로 제한거는 기능도 있는데 이건 사용해본적은 없네요. 권한또한 지정할 수 있는데, 보통은 에이전트마다 접근할 수 있는 툴들만 지정해서 이것도 사용해 본적은 없습니다. + + + +이제 mcp-context-forge의 장점과 단점을 말하고 마무리하려고 합니다. + +## 장점 + +가장 큰 장점은 MCP Catalog 역할입니다. 사내에 어떤 MCP 서버와 툴이 있는지 한눈에 파악할 수 있어서, 중복된 MCP 서버가 생성되는 것을 방지할 수 있습니다. 팀 간 협업 시에도 "이런 툴 있어?"라는 질문에 바로 답할 수 있게 됩니다. + +Virtual Server를 통한 권한 분리도 매력적입니다. 에이전트마다 필요한 툴만 지정할 수 있어서, 불필요한 툴 호출을 막고 보안적으로도 안심이 됩니다. 메트릭 기능 덕분에 어떤 툴이 실제로 사용되고 있는지, 혹은 방치되고 있는지 파악하기도 좋습니다. + +구성이 간단한 것도 장점입니다. PostgreSQL이나 MariaDB, 그리고 Redis만 있으면 운영할 수 있어서 부담이 적습니다. Stdio, SSE, HTTP를 모두 지원하기 때문에 기존 MCP 클라이언트와 호환성 문제도 없습니다. + + +## 단점 + +기능적인 단점은 사실 크게 못 느꼈습니다. 굳이 뽑자면 아직 베타 버전이라 미구현된 기능들이 있다는 점 정도입니다. + +그리고 Agent에서 MCP로 직접 연결할 때와 비교해서, 지원하지 않는 인증 방식이 몇 가지 있는 것으로 보입니다. 저의 경우 FastMCP Client를 사용해서 연결하는 방법을 시도했었는데 실패해서, mcp 라이브러리로 변경했던 기억이 있습니다. 지금은 해결되었을 수도 있겠네요. + +또한 Gateway라는 특성상 단일 실패 지점(Single Point of Failure)이 될 수 있다는 점은 고려해야 합니다. 다만 이건 모든 Gateway의 공통적인 특성이기도 합니다. + + + +## 마무리 + +MCP Gateway는 MCP 서버와 툴이 많아지기 시작하면 반드시 필요해지는 인프라입니다. mcp-context-forge는 아직 베타 버전이지만, 실제 프로덕션 환경에서 운영해본 결과 큰 문제 없이 잘 동작했습니다. + +무엇보다 IBM에서 오픈소스로 공개하고 활발하게 개발 중이라는 점이 좋습니다. 저도 컨트리뷰트하면서 사용하고 있는데, 이슈 대응도 빠른 편입니다. + +MCP Gateway 도입을 고민하고 계시다면, mcp-context-forge를 한번 검토해보시길 추천드립니다. + + + +## 참고 링크 + +- [mcp-context-forge GitHub](https://github.com/IBM/mcp-context-forge) +- [mcp-context-forge 공식 문서](https://ibm.github.io/mcp-context-forge/) diff --git a/src/styles/global.css b/src/styles/global.css index e96bd16..1a89645 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -44,15 +44,21 @@ h1, h2, h3, h4, h5, h6 { line-height: 1.3; font-weight: 600; } +h2 { + margin-top: 2.5rem; +} +h3 { + margin-top: 2rem; +} strong, b { font-weight: 600; } a { - color: var(--text-primary); + color: #2563eb; text-decoration: none; } a:hover { - color: var(--text-secondary); + color: #1d4ed8; } p { margin-bottom: 1em;