<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Juan Antonio Osorio</title>
    <description>The latest articles on DEV Community by Juan Antonio Osorio (@jaormx).</description>
    <link>https://dev.to/jaormx</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1401847%2Fc64740da-2e89-43ad-9dcb-e06e77fa6c68.jpeg</url>
      <title>DEV Community: Juan Antonio Osorio</title>
      <link>https://dev.to/jaormx</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jaormx"/>
    <language>en</language>
    <item>
      <title>Bringing AI Agents to CI/CD: Using ToolHive and Buildkite to Bring Intelligence to Vulnerability Scanning</title>
      <dc:creator>Juan Antonio Osorio</dc:creator>
      <pubDate>Mon, 18 Aug 2025 18:14:16 +0000</pubDate>
      <link>https://dev.to/stacklok/bringing-ai-agents-to-cicd-using-toolhive-and-buildkite-to-bring-intelligence-to-vulnerability-22hn</link>
      <guid>https://dev.to/stacklok/bringing-ai-agents-to-cicd-using-toolhive-and-buildkite-to-bring-intelligence-to-vulnerability-22hn</guid>
      <description>&lt;p&gt;Continuous Integration and Continuous Deployment (CI/CD) pipelines have traditionally relied on deterministic scripts and predefined workflows, offering predictable results. What if your CI/CD pipeline could think, analyze, and make intelligent decisions? What if it could adapt to complex scenarios, understand context, and provide insights beyond simple pass/fail results?&lt;/p&gt;

&lt;p&gt;This is where agentic workflows come in. With the new &lt;a href="https://github.com/StacklokLabs/toolhive-buildkite-plugin" rel="noopener noreferrer"&gt;ToolHive Buildkite Plugin&lt;/a&gt;, you can now seamlessly integrate AI agents into your CI/CD pipelines using the Model Context Protocol (MCP).&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Case: Agentic Vulnerability Scanning
&lt;/h2&gt;

&lt;p&gt;Traditional CI/CD treats all issues equally. A medium-severity CVE gets the same treatment whether it's in a critical path or an unused dependency. Agents change this by understanding:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Context&lt;/strong&gt;: Where and how a vulnerability can be exploited in your specific architecture.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Impact&lt;/strong&gt;: The actual risk to your application, not just a generic score.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Remediation&lt;/strong&gt;: Specific steps that work for your codebase, not generic advice.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of a binary pass/fail, you get nuanced analysis with actionable recommendations. The agent doesn't just tell you there's a problem – it explains why it matters and how to fix it.&lt;/p&gt;

&lt;p&gt;In the context of CI/CD, this means your pipeline can become more than just a series of tests – it becomes an intelligent system that understands your codebase, security posture, and deployment requirements. The technology stack for this approach: MCP, ToolHive, and Buildkite.&lt;/p&gt;

&lt;h2&gt;
  
  
  ToolHive: The MCP Engine
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.stacklok.com/toolhive" rel="noopener noreferrer"&gt;ToolHive&lt;/a&gt; is your starting point for running MCP in production. It handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Server Lifecycle&lt;/strong&gt;: Starting, stopping, and managing MCP server instances.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transport Methods&lt;/strong&gt;: Supporting multiple communication protocols (stdio, SSE, streamable-http).
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt;: Managing secrets, permissions, and isolation.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Discovery&lt;/strong&gt;: Providing a registry of available MCP servers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Buildkite: The CI/CD Platform
&lt;/h2&gt;

&lt;p&gt;Buildkite provides a flexible, scalable CI/CD platform that's perfect for running agentic workflows because of its:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Plugin Architecture&lt;/strong&gt;: Extensible system for adding functionality.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Container Support&lt;/strong&gt;: Native Docker/Podman integration.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parallel Execution&lt;/strong&gt;: Ability to run multiple agents simultaneously.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Artifact Management&lt;/strong&gt;: Built-in support for storing and sharing results.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How the ToolHive Buildkite Plugin Works
&lt;/h2&gt;

&lt;p&gt;To bring this use case — agentic vulnerability scanning — to life, we created the ToolHive Buildkite Plugin. It bridges these technologies and enables you to spawn MCP servers directly in your CI/CD pipeline. Here's how it works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Plugin Configuration&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In your Buildkite pipeline, you simply add the plugin to any step:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;steps:
  - label: "🔍 Security Analysis"
    command: "run-security-scan"
    plugins:
      - StacklokLabs/toolhive#v0.0.2:
          server: "osv"  # OSV vulnerability database server
          transport: "sse"
          proxy-port: 8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Automatic MCP Server Provisioning&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When the pipeline runs, the plugin:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Downloads ToolHive&lt;/strong&gt; if not already available.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spawns the MCP server&lt;/strong&gt; in a containerized environment.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configures networking&lt;/strong&gt; to make the server accessible to your agent.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manages lifecycle&lt;/strong&gt; ensuring proper cleanup after execution.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Agent Connection&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your AI agent can then connect to the MCP server and use its tools. Here's a snippet from a simple Python agent using the PydanticAI framework:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic_ai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic_ai.mcp&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;MCPServerSSE&lt;/span&gt;

&lt;span class="c1"&gt;# Connect to the MCP server spawned by the plugin
&lt;/span&gt;&lt;span class="n"&gt;osv_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MCPServerSSE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:8080/sse&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Create an agent with access to OSV tools
&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;mcp_servers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;osv_server&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;system_prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;You are a security analyst...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# The agent can now use OSV tools to analyze vulnerabilities
&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Analyze security vulnerabilities...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Real-World Example: Intelligent Vulnerability Scanning
&lt;/h2&gt;

&lt;p&gt;Let's look at a concrete example from our &lt;a href="https://github.com/JAORMX/buildkite-demo-agent" rel="noopener noreferrer"&gt;demo repository&lt;/a&gt; that showcases agentic vulnerability scanning, comparing a traditional approach to an agentic approach&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Traditional CI/CD Security Scanning:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Run a vulnerability scanner
osv-scanner --json &amp;gt; results.json

# Check if any high severity vulnerabilities exist
if grep -q '"severity": "HIGH"' results.json; then
  echo "High severity vulnerabilities found!"
  exit 1
fi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach is limited in several ways. There’s a binary pass/fail decision, no context or explanation, no intelligent categorization and no actionable recommendations. In short, it’s functional, but not very useful. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agentic Security Scanning:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;steps:
  - label: "🔍 Intelligent Vulnerability Analysis"
    command: |
      uv run buildkite-demo-agent \
        --packages-file examples/packages.json \
        --fail-on-vulnerabilities \
        --severity-threshold high
    plugins:
      - StacklokLabs/toolhive#v0.0.2:
          server: "osv"
          transport: "sse"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This agent provides a lot more value. It will deliver intelligent analysis by using Claude Code to understand vulnerability context; it will classify the severity of CVEs based on actual impact and not just CVSS scores; and it will offer detailed explanations and specific remediation steps. &lt;/p&gt;

&lt;h2&gt;
  
  
  The Agent's Intelligence in Action
&lt;/h2&gt;

&lt;p&gt;Here's what happens when the agent analyzes a vulnerability:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Query OSV Database&lt;/strong&gt;: The agent uses MCP tools to query vulnerability data.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Contextual Analysis&lt;/strong&gt;: Claude analyzes the vulnerability considering:

&lt;ul&gt;
&lt;li&gt;The specific package and version
&lt;/li&gt;
&lt;li&gt;The type of vulnerability (RCE, DoS, data exposure)
&lt;/li&gt;
&lt;li&gt;The ecosystem and common usage patterns
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Intelligent Categorization&lt;/strong&gt;: Instead of relying solely on CVSS scores, the agent considers:

&lt;ul&gt;
&lt;li&gt;Exploitability in your environment
&lt;/li&gt;
&lt;li&gt;Actual impact on your application
&lt;/li&gt;
&lt;li&gt;Availability of patches or workarounds
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structured Output&lt;/strong&gt;: Returns actionable information:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqahbozrlls2rr5i8dwj1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqahbozrlls2rr5i8dwj1.png" alt=" " width="800" height="699"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;There’s a lot of energy around MCP, and we need to channel that into putting the protocol to work in production environments. Agentic vulnerability scanning is a compelling use case and it hints at the potential of agentic workflows in CI/CD pipelines. If you want to give this a try, we created the &lt;a href="https://github.com/StacklokLabs/toolhive-buildkite-plugin" rel="noopener noreferrer"&gt;ToolHive Buildkite Plugin&lt;/a&gt; to be simple and secure. Of course, we also encourage you to check out ToolHive for other MCP use cases; you can explore our &lt;a href="https://github.com/stacklok/toolhive" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt; or reach out directly via &lt;a href="https://discord.gg/stacklok" rel="noopener noreferrer"&gt;Discord&lt;/a&gt;.   &lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>security</category>
      <category>ci</category>
    </item>
    <item>
      <title>Vibe Coding an MCP Server (with ToolHive)</title>
      <dc:creator>Juan Antonio Osorio</dc:creator>
      <pubDate>Fri, 30 May 2025 08:42:04 +0000</pubDate>
      <link>https://dev.to/stacklok/vibe-coding-an-mcp-server-with-toolhive-2pi3</link>
      <guid>https://dev.to/stacklok/vibe-coding-an-mcp-server-with-toolhive-2pi3</guid>
      <description>&lt;p&gt;I’ve been writing MCP servers for fun and profit lately, and noticed that with the right tools, it’s not just easy — it’s actually pretty fun!&lt;/p&gt;

&lt;p&gt;A few people have asked for a guide on how I set things up. So here it is: my day-to-day stack, a working philosophy, and an example that you can reuse. This isn’t just a tech guide — it’s how to vibe while building real stuff.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;🐝 The Stack I Use&lt;/strong&gt;
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;🔧 ToolHive (core runtime)&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Disclaimer: I am a maintainer in the ToolHive project. That being said, I do use the tool in my day-to-day, so I consider it a foundational piece of my workflow. ToolHive is a lightweight, developer-friendly MCP runtime I use to run, test, and deploy MCP servers — whether they’re containerized or not. It handles authentication, transports, and common runtime concerns so I can focus on building servers, not plumbing.&lt;/p&gt;

&lt;p&gt;Servers I use ToolHive to run every day:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;fetch&lt;/strong&gt;: Lets the AI client fetch URLs and embed markdown into context.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;github&lt;/strong&gt;: Lets the AI client read GitHub repositories and create PRs.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;ToolHive&lt;/strong&gt; is open source and flexible — it can run your server locally or as a container without requiring a Dockerfile. Just point it to a Go binary or MCP server image and go.&lt;/p&gt;

&lt;p&gt;🛠️ &lt;a href="https://github.com/StacklokLabs/toolhive" rel="noopener noreferrer"&gt;Check out ToolHive&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;💻 Golang + &lt;code&gt;mcp-go&lt;/code&gt;&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Go makes writing MCP servers minimal and straightforward. I’ve been using &lt;a href="https://pkg.go.dev/github.com/mark3labs/mcp-go" rel="noopener noreferrer"&gt;&lt;code&gt;mcp-go&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://pkg.go.dev/github.com/mark3labs/mcp-go/server" rel="noopener noreferrer"&gt;&lt;code&gt;mcp-go/server&lt;/code&gt;&lt;/a&gt; to implement the protocol, and it’s been incredibly smooth.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;🐳 ko (build tool)&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;I use &lt;a href="https://github.com/ko-build/ko" rel="noopener noreferrer"&gt;&lt;code&gt;ko&lt;/code&gt;&lt;/a&gt; to build container images for my Go projects without needing Dockerfiles. It produces minimal, secure, and production-ready containers — great for MCP servers.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;🔍 Testing &amp;amp; Linting&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pkg.go.dev/github.com/stretchr/testify" rel="noopener noreferrer"&gt;&lt;code&gt;testify&lt;/code&gt;&lt;/a&gt; – for expressive Go unit tests&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://golangci-lint.run" rel="noopener noreferrer"&gt;&lt;code&gt;golangci-lint&lt;/code&gt;&lt;/a&gt; – for enforcing style and catching issues early&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🌐 &lt;strong&gt;Anthropic’s Claude Sonnet 4&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I’ve been using Claude Sonnet 4 with a lot of success lately. It’s great for coding!&lt;/p&gt;

&lt;p&gt;🦘 &lt;strong&gt;Roo Code&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Roo Code provides just what I need in terms of an LLM client. I don’t have a very complex setup, just MCP servers with the LLM I mentioned above, plus some custom prompts to take my Fedora Silverblue + toolbox Linux setup into account.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;🌱 Philosophy: Vibe Coding Principles&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Here are the core values I follow when writing MCP servers:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Principle&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Minimalism&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Don’t over-engineer. Use the fewest tools that do the job well.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Observability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Understand every layer: protocols, requests, and failures.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Security&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Prefer non-root containers, minimal base images, and validated input.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;KISS + DRY&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Simplicity and reuse always win over clever hacks.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rapid feedback&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Run tests and linters constantly to stay in flow.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dev-First UX&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Use tools like ToolHive that reduce friction and empower iteration.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;💡 Sample Prompt&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Here's the kind of prompt I give myself when starting a new MCP server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I'd like to start a new golang project. It'll be an MCP server that queries an OCI registry and image references. It'll be an SSE MCP server. We'll be using https://pkg.go.dev/github.com/mark3labs/mcp-go/mcp and https://pkg.go.dev/github.com/mark3labs/mcp-go/server so query those before going forward. Also, query https://pkg.go.dev/github.com/google/go-containerregistry and its subpackages since I'd like to use that library to query the registry. Let's gather all the information and plan ahead first. I'd like to have a very minimal and handy set of tools only. Let's also write tests using testify and run those often. Finally, in the container use golangci-lint to lint.

Run linting tools and unit tests often to ensure changes are correct.

Please think step by step about whether there exists a less over-engineered and yet simpler, more elegant, and more robust solution to the problem that accords with KISS and DRY principles.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can copy this and adjust it for your own project — it's like setting your mindset before you even write the first &lt;code&gt;main()&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;🏗️ Vibe Coding Dev Loop&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;I wish I could tell you that I just press enter and forget; But this is not the case. While I do allow the client to read files in the repository (I never have secrets in the repository anyways), I don’t allow any execute or write operations. So, I tend to read all diffs, and verify what the LLM is trying to execute. I look at this as more of a pair-programming exercise, where I have a conversation with the LLM and ensure it’s going the right way and following best practices. Sometimes this may be very fast, sometimes, not so much.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;🧪 Dev Loop with ToolHive&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;ToolHive makes it easy to go from idea → tested server → deployed runtime:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Write your MCP server in Go using mcp-go
2. Add tests with testify
3. Add linting with golangci-lint
4. Build with ko (or just run it directly via ToolHive!)
5. Test locally with ToolHive:
   $ thv run go://./cmd/my-server
6. Iterate fast, deploy when ready
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can run SSE servers, HTTP-based ones, or even non-containerized ones locally using just a single &lt;code&gt;thv run&lt;/code&gt; command.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;🔒Lock it down!&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Finally, don't forget that the regular software engineering best practices apply:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do automatic updates&lt;/li&gt;
&lt;li&gt;Use minimal container images&lt;/li&gt;
&lt;li&gt;Run vulnerability scanning constantly&lt;/li&gt;
&lt;li&gt;Set up vulnerability disclosure&lt;/li&gt;
&lt;li&gt;Publish signatures and provenance&lt;/li&gt;
&lt;li&gt;Publish an SBOM&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these things are easy to do and you can even leverage an LLM to help you implement these!&lt;/p&gt;

&lt;p&gt;Don't worry! In ToolHive we'll soon publish a list of heuristics for you to follow to publish a quality MCP server... Stay tuned!&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;🧭 TL;DR&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Leverage MCP servers to enhance your workflow. &lt;code&gt;fetch&lt;/code&gt; and &lt;code&gt;github&lt;/code&gt; are great!&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Write servers in &lt;strong&gt;Go&lt;/strong&gt; with &lt;code&gt;mcp-go&lt;/code&gt; and &lt;code&gt;go-containerregistry&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Build minimal, secure images with &lt;strong&gt;ko&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Keep dev feedback tight: &lt;strong&gt;testify&lt;/strong&gt; + &lt;strong&gt;golangci-lint&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Start with a clear &lt;strong&gt;prompt&lt;/strong&gt; and let simplicity guide the implementation&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use &lt;strong&gt;ToolHive&lt;/strong&gt; to simplify running and testing MCP servers&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>vibecoding</category>
      <category>go</category>
    </item>
    <item>
      <title>No Dockerfile? No problem! Running Node and Python MCPs with ToolHive</title>
      <dc:creator>Juan Antonio Osorio</dc:creator>
      <pubDate>Thu, 24 Apr 2025 06:23:40 +0000</pubDate>
      <link>https://dev.to/stacklok/no-dockerfile-no-problem-running-node-and-python-mcps-with-toolhive-1khi</link>
      <guid>https://dev.to/stacklok/no-dockerfile-no-problem-running-node-and-python-mcps-with-toolhive-1khi</guid>
      <description>&lt;p&gt;Containerizing your MCP server is great—until it isn’t. Maybe you're moving fast, testing a new idea, or you just don’t want to write a Dockerfile. We’ve been there.&lt;/p&gt;

&lt;p&gt;That’s why ToolHive now supports running MCP servers powered by &lt;strong&gt;npm&lt;/strong&gt; or &lt;strong&gt;uv&lt;/strong&gt;, &lt;em&gt;without needing them to be containerized up front&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;ToolHive will now &lt;strong&gt;dynamically build a container image&lt;/strong&gt; for you based on the MCP reference. That means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;No Dockerfile.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No need to install &lt;code&gt;uv&lt;/code&gt; or &lt;code&gt;npm&lt;/code&gt; on your system.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Just write your code and run.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;We’ve added support for special transport-prefixed references to MCP servers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For Node.js projects:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;thv run npx://my-cool-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;For Python projects using &lt;code&gt;uv&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;thv run uvx://my-python-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When ToolHive sees one of these prefixes (&lt;code&gt;npx://&lt;/code&gt; or &lt;code&gt;uvx://&lt;/code&gt;), it knows to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Build a container image dynamically behind the scenes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Include the appropriate runtime (Node.js or Python + uv)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Execute your server just like any other MCP—no container authoring required&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This all happens without you needing to install &lt;code&gt;npm&lt;/code&gt;, &lt;code&gt;npx&lt;/code&gt;, or &lt;code&gt;uv&lt;/code&gt; on your local machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters
&lt;/h2&gt;

&lt;p&gt;This feature is a huge win for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Quick iterations:&lt;/strong&gt; Skip the Dockerfile and get straight to testing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Lightweight environments:&lt;/strong&gt; Don’t want to install runtime tools globally? No problem.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Clean CI/CD pipelines:&lt;/strong&gt; ToolHive encapsulates the runtime without polluting your environment.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s never been easier to run an MCP server with ToolHive, even if all you’ve got is a &lt;code&gt;main.py&lt;/code&gt; or &lt;code&gt;index.js&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Behind the magic
&lt;/h2&gt;

&lt;p&gt;Under the hood, ToolHive builds a container image on-the-fly using the MCP reference you pass in. It wraps your package with the right runtime environment and proxies traffic into it, just like any other server launched through ToolHive.&lt;/p&gt;

&lt;p&gt;This means you get all the benefits of the ToolHive runtime—clean shutdown, traffic proxying, process supervision—&lt;em&gt;without needing to containerize anything yourself&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get started
&lt;/h2&gt;

&lt;p&gt;Just run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;thv run npx://&amp;lt;your-npm-package&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;thv run uvx://&amp;lt;your-python-package&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ToolHive will take it from there.&lt;/p&gt;

&lt;p&gt;E.g. If you’re interested in running the &lt;a href="https://playwright.dev/" rel="noopener noreferrer"&gt;Playwright&lt;/a&gt; &lt;a href="https://github.com/microsoft/playwright-mcp" rel="noopener noreferrer"&gt;MCP&lt;/a&gt;, simply do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;thv run npx://@playwright/mcp@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more details, check out the &lt;a href="https://github.com/StacklokLabs/toolhive/blob/main/README.md" rel="noopener noreferrer"&gt;README&lt;/a&gt; .&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Try it out and join the fun!&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;We're excited to keep making ToolHive easier to use, especially for devs who want to focus on building—not on container plumbing. Got feedback or ideas? &lt;a href="https://github.com/StacklokLabs/toolhive/issues" rel="noopener noreferrer"&gt;Open an issue&lt;/a&gt; or &lt;a href="https://discord.gg/stacklok" rel="noopener noreferrer"&gt;talk to us on Discord&lt;/a&gt;. We’d love to hear from you.&lt;/p&gt;

&lt;p&gt;Happy hacking 🐝&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>security</category>
      <category>containers</category>
    </item>
    <item>
      <title>Secure-by-Default Authorization for MCP Servers powered by ToolHive</title>
      <dc:creator>Juan Antonio Osorio</dc:creator>
      <pubDate>Wed, 16 Apr 2025 05:50:44 +0000</pubDate>
      <link>https://dev.to/stacklok/secure-by-default-authorization-for-mcp-servers-powered-by-toolhive-1hp6</link>
      <guid>https://dev.to/stacklok/secure-by-default-authorization-for-mcp-servers-powered-by-toolhive-1hp6</guid>
      <description>&lt;p&gt;As more teams start deploying MCP servers to power tool-calling agents, one question comes up fast: how do you control who can call what? It’s not just about verifying identity, it’s about enforcing the right permissions, without bloating every server with bespoke auth logic. &lt;a href="https://github.com/StacklokLabs/toolhive" rel="noopener noreferrer"&gt;ToolHive&lt;/a&gt; was built to solve exactly this problem. It separates authentication from authorization, integrates cleanly with existing identity providers, and uses Amazon’s Cedar policy language to define clear, auditable access rules. The result is a simple but powerful way to lock down tool access without embedding auth logic into every server you run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authentication vs Authorization: Separate Concerns
&lt;/h2&gt;

&lt;p&gt;In my &lt;a href="https://dev.to/stacklok/getting-authentication-right-is-critical-to-running-mcp-servers-39fk"&gt;previous blog post&lt;/a&gt;, I outlined a fundamental design choice in ToolHive: the &lt;strong&gt;strong separation of authentication (authN) and authorization (authZ)&lt;/strong&gt;. Authentication verifies identity (proving &lt;em&gt;who&lt;/em&gt; you are), while authorization determines &lt;em&gt;what&lt;/em&gt; that identity is allowed to do. ToolHive treats these as two distinct steps – first authenticate the caller, then check what they can access. In practice, ToolHive uses OpenID Connect (OIDC) to handle authentication (e.g., verifying a user's JWT from a trusted identity provider) and only afterwards applies its own permission rules for MCP actions. By explicitly separating authN and authZ, ToolHive can rely on well-proven identity systems and avoid conflating identity with access control, leading to a cleaner, more secure design. This separation matters because it lets ToolHive plug into existing single sign-on/IdP solutions for identifying users, while maintaining a clear, flexible policy model for what those users can do within MCP servers.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://github.com/StacklokLabs/toolhive" rel="noopener noreferrer"&gt;ToolHive&lt;/a&gt;’s Authorization Framework Overview
&lt;/h2&gt;

&lt;p&gt;A few weeks ago, I had lunch with &lt;a href="https://www.linkedin.com/in/luxas/" rel="noopener noreferrer"&gt;Lucas Käldström&lt;/a&gt; and had a good chat about authorization primitives and the work he’s been doing with Amazon’s Cedar policy language in relation to Kubernetes. Let’s just say that chat was exciting enough for me to try it out, and an authorization framework for ToolHive was born!&lt;/p&gt;

&lt;p&gt;As mentioned before, ToolHive’s authorization system is built on &lt;strong&gt;Amazon’s Cedar policy language&lt;/strong&gt; and is designed as a layer on top of the base MCP server. This authorization layer is tightly integrated with ToolHive’s existing JWT-based authentication middleware. In a typical deployment, the request flow is: a JWT validator middleware first verifies the user's identity token, and then the Cedar authorization middleware runs next. This means ToolHive acts as a &lt;strong&gt;gateway&lt;/strong&gt; in front of the MCP server, handling auth on the server’s behalf. The MCP server itself doesn’t need to implement OAuth or check tokens – ToolHive has it covered.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Authorization Works in ToolHive
&lt;/h2&gt;

&lt;p&gt;Once an MCP server is launched with ToolHive’s authZ enabled (by passing an &lt;code&gt;--authz-config&lt;/code&gt; file), every client request goes through an authorization check before reaching the server logic. At a high level, the process is as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Authenticate&lt;/strong&gt; – ToolHive’s JWT middleware verifies the client’s token (e.g., an OIDC ID token or other JWT) and, if valid, attaches the user’s identity and claims to the request context. This tells ToolHive &lt;em&gt;who&lt;/em&gt; is making the request (the principal).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Extract Request Info&lt;/strong&gt; – The authorization middleware then examines the incoming MCP request. It determines what operation is being attempted and on what resource. For example, it might see a JSON-RPC call for the method &lt;code&gt;tools/call&lt;/code&gt; with tool name "weather". From this, ToolHive derives the &lt;strong&gt;Action&lt;/strong&gt; (like &lt;code&gt;"call_tool"&lt;/code&gt;) and the &lt;strong&gt;Resource&lt;/strong&gt; (like the &lt;code&gt;"weather"&lt;/code&gt; tool) being requested.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Policy Evaluation&lt;/strong&gt; – ToolHive invokes the Cedar authorizer with the gathered context (the &lt;strong&gt;principal&lt;/strong&gt; identity, the &lt;strong&gt;action&lt;/strong&gt; type, and the &lt;strong&gt;resource&lt;/strong&gt; target). The Cedar engine checks the loaded policies to decide if this principal is permitted to perform that action on that resource. This evaluation considers any relevant policy rules defined in the config file, including conditions based on user attributes or tool attributes (more on this below).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Allow or Deny&lt;/strong&gt; – If the policies &lt;strong&gt;permit&lt;/strong&gt; the action, the request is forwarded to the MCP server’s normal processing. If not (no policy allowed it, or an explicit deny policy matches), ToolHive stops the request and returns an HTTP 403 Forbidden error (or an error in the SSE/JSON-RPC response) to the client. The MCP server never sees unauthorized requests.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In essence, ToolHive acts as a policy enforcement point in front of the MCP server. Unauthorized tool invocations, prompt fetches, etc., are blocked at the gate. This design greatly reduces the risk of a malicious or unauthorized request ever hitting the actual model server logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Policy Model: Cedar in ToolHive
&lt;/h2&gt;

&lt;p&gt;The heart of ToolHive’s authorization is the &lt;a href="https://www.cedarpolicy.com/" rel="noopener noreferrer"&gt;Cedar policy language&lt;/a&gt;. Cedar is a flexible, expressive, and formally verified language for access control, supporting both role-based and attribute-based rules. ToolHive leverages Cedar to define who can do what in terms of MCP operations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Principals, Actions, Resources:&lt;/strong&gt; In Cedar (and ToolHive’s usage), every access policy involves these three entities. ToolHive defines them in the context of MCP as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Principal&lt;/strong&gt; – the client or user making the request. In ToolHive, principals are identified by the OIDC/JWT subject. For example, a user with ID "user123" would be represented as &lt;code&gt;Client::user123&lt;/code&gt; in policies. (ToolHive uses a &lt;code&gt;Client&lt;/code&gt; entity type for principals.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Action&lt;/strong&gt; – the operation being performed. ToolHive categorizes MCP operations like calling a tool, reading a resource, listing prompts, etc. Examples include &lt;code&gt;Action::"call_tool"&lt;/code&gt;, &lt;code&gt;Action::"get_prompt"&lt;/code&gt;, &lt;code&gt;Action::"list_tools"&lt;/code&gt;, and so on. The authorization middleware translates each incoming request to the appropriate action tag.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Resource&lt;/strong&gt; – the target object of the action. This could be a specific tool name, a prompt name, or a resource identifier. In policies, resources are namespaced by type, like &lt;code&gt;Tool::"weather"&lt;/code&gt; for the "weather" tool, &lt;code&gt;Prompt::"greeting"&lt;/code&gt; for the "greeting" prompt, &lt;code&gt;Resource::"data"&lt;/code&gt; for a resource called "data", or even a category like &lt;code&gt;FeatureType::"tool"&lt;/code&gt; to represent “any tool” in a list operation.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using these, you can write fine-grained rules. A Cedar policy generally looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;permit(principal, action, resource) when { &amp;lt;conditions&amp;gt; };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means "allow this principal performing this action on this resource if certain conditions hold." (Cedar also supports &lt;code&gt;forbid&lt;/code&gt; rules to explicitly disallow actions.) In ToolHive’s config, policies are defined as strings. For example, the ToolHive docs show a simple policy set that permits any user to call the "weather" tool, get the "greeting" prompt, and read the "data" resource:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cedarv1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cedar"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"policies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"permit(principal, action == Action::&lt;/span&gt;&lt;span class="se"&gt;\\\"&lt;/span&gt;&lt;span class="s2"&gt;call_tool&lt;/span&gt;&lt;span class="se"&gt;\\\"&lt;/span&gt;&lt;span class="s2"&gt;, resource == Tool::&lt;/span&gt;&lt;span class="se"&gt;\\\"&lt;/span&gt;&lt;span class="s2"&gt;weather&lt;/span&gt;&lt;span class="se"&gt;\\\"&lt;/span&gt;&lt;span class="s2"&gt;);"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"permit(principal, action == Action::&lt;/span&gt;&lt;span class="se"&gt;\\\"&lt;/span&gt;&lt;span class="s2"&gt;get_prompt&lt;/span&gt;&lt;span class="se"&gt;\\\"&lt;/span&gt;&lt;span class="s2"&gt;, resource == Prompt::&lt;/span&gt;&lt;span class="se"&gt;\\\"&lt;/span&gt;&lt;span class="s2"&gt;greeting&lt;/span&gt;&lt;span class="se"&gt;\\\"&lt;/span&gt;&lt;span class="s2"&gt;);"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"permit(principal, action == Action::&lt;/span&gt;&lt;span class="se"&gt;\\\"&lt;/span&gt;&lt;span class="s2"&gt;read_resource&lt;/span&gt;&lt;span class="se"&gt;\\\"&lt;/span&gt;&lt;span class="s2"&gt;, resource == Resource::&lt;/span&gt;&lt;span class="se"&gt;\\\"&lt;/span&gt;&lt;span class="s2"&gt;data&lt;/span&gt;&lt;span class="se"&gt;\\\"&lt;/span&gt;&lt;span class="s2"&gt;);"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"entities_json"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[]"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each string in the &lt;code&gt;policies&lt;/code&gt; array is a Cedar rule. For instance, the first rule above says any principal can &lt;code&gt;call_tool&lt;/code&gt; on the &lt;code&gt;weather&lt;/code&gt; tool. If a request comes in to invoke the weather tool, this policy would allow it (assuming no conflicting denies).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Attributes and Conditions:&lt;/strong&gt; One of the strengths of Cedar (and ToolHive’s approach) is the ability to use attributes in policies for richer conditions – this is essentially &lt;strong&gt;attribute-based access control (ABAC)&lt;/strong&gt;. ToolHive’s middleware automatically makes certain attributes available to policies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;JWT Claims&lt;/strong&gt; – All standard claims from the authenticated user's JWT are added as attributes on the principal, prefixed with &lt;code&gt;claim_&lt;/code&gt;. For example, if a user's token has a claim &lt;code&gt;roles: ["admin"]&lt;/code&gt;, then in policy you can refer to &lt;code&gt;principal.claim_roles&lt;/code&gt; to check that. A policy might require &lt;code&gt;principal.claim_roles.contains("admin")&lt;/code&gt; in its condition to restrict an action to admins only. Similarly, &lt;code&gt;principal.claim_name&lt;/code&gt; could be their name, etc., allowing policies to target specific users or user properties.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tool Arguments&lt;/strong&gt; – If the MCP action involves calling a tool with arguments (for example, a calculator tool with an operation argument "add" vs "subtract"), those arguments are exposed as attributes on the resource, prefixed with &lt;code&gt;arg_&lt;/code&gt; . This means policies can even inspect the parameters of a tool invocation. For instance, you could allow use of a tool &lt;em&gt;only for certain argument values&lt;/em&gt;. The ToolHive docs show a policy that permits calling the &lt;code&gt;"calculator"&lt;/code&gt; tool &lt;strong&gt;only&lt;/strong&gt; if the operation is "add" or "subtract", by checking &lt;code&gt;resource.arg_operation&lt;/code&gt; in the policy condition.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For convenience, these attributes are also exposed in the authorizer’s context. That is, you can access them via the &lt;code&gt;context&lt;/code&gt; key.&lt;/p&gt;

&lt;p&gt;Additionally, administrators can define custom &lt;strong&gt;resource attributes&lt;/strong&gt; via the &lt;code&gt;entities_json&lt;/code&gt; in the config. This JSON string can enumerate entities (like specific tools) with custom attributes (like an owner or sensitivity label). At runtime, the Cedar authorizer will include those entity definitions. For example, you could mark the &lt;code&gt;"weather"&lt;/code&gt; tool entity with an &lt;code&gt;"owner": "user123"&lt;/code&gt; attribute, and then write a policy that permits access only if &lt;code&gt;resource.owner == principal.claim_sub&lt;/code&gt; . This would mean only the owner of a tool can use it, effectively implementing per-tool ownership controls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Policy Decision Logic:&lt;/strong&gt; ToolHive’s policy evaluation follows a &lt;strong&gt;secure-by-default&lt;/strong&gt; logic: if nothing explicitly allows a request, it’s denied. In fact, Cedar policies can be either permit or forbid rules, and ToolHive applies them with &lt;strong&gt;deny precedence&lt;/strong&gt; – any matching forbid rule will override permits. During evaluation: (1) if any forbid policy matches, the request is denied; (2) otherwise, if any permit policy matches, the request is allowed; (3) if no policies match, the request is denied by default. This default-deny approach ensures that in the absence of a rule, access is not allowed (principle of least privilege). As a ToolHive admin, you therefore list out what &lt;em&gt;is&lt;/em&gt; permitted (and optionally what is explicitly forbidden), and everything else is automatically blocked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example: Writing and Using Policies
&lt;/h2&gt;

&lt;p&gt;Let’s illustrate a few typical authorization rules you might implement in ToolHive (using Cedar syntax):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Allow everyone to use a specific tool:&lt;/strong&gt; For instance, to open up a weather info tool to all users, you’d add a policy:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;permit(principal, action == Action::"call_tool", resource == Tool::"weather");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means any authenticated principal can call the &lt;code&gt;weather&lt;/code&gt; tool. (You might pair this with other policies to keep other tools locked down.)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Restrict a tool to certain users:&lt;/strong&gt; Suppose only the user with ID "alice123" should use an internal admin tool. You could write:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;permit(principal == Client::"alice123", action == Action::"call_tool", resource == Tool::"admin_tool");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This permits only that specific user (principal) to call the &lt;code&gt;admin_tool&lt;/code&gt;. No one else’s principal ID will match, so others will be denied (by default).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Role-based rule:&lt;/strong&gt; If your JWT tokens include roles, you can use them. For example:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;permit(principal, action == Action::"call_tool", resource) 
  when { principal.claim_roles.contains("premium") };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This would allow any user with the role "premium" to call any tool (maybe to grant paying users access to certain features), while denying calls from users without that role.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tool argument condition:&lt;/strong&gt; As mentioned, you can gate based on tool parameters. For a calculator tool that has an argument &lt;code&gt;"operation"&lt;/code&gt;, you might write:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;permit(principal, action == Action::"call_tool", resource == Tool::"calculator") 
  when { resource.arg_operation == "add" || resource.arg_operation == "subtract" };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures the calculator tool can only be used for addition or subtraction, and would block other operations like "multiply" if the tool supports them.&lt;/p&gt;

&lt;p&gt;These examples show the expressive power of ToolHive’s authorization policies. This approach makes it easy for developers and operators to reason about &lt;strong&gt;who can do what&lt;/strong&gt; on their MCP servers without modifying the server code itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  ToolHive vs. MCP Specification’s Authorization Guidance
&lt;/h2&gt;

&lt;p&gt;It’s worth noting how ToolHive’s approach compares to the official Model Context Protocol (MCP) specification’s guidance on authorization. The &lt;a href="https://modelcontextprotocol.io/specification/2025-03-26" rel="noopener noreferrer"&gt;MCP spec&lt;/a&gt; (as of the 2025-03-26 revision) outlines an &lt;strong&gt;OAuth 2.1-based authorization model&lt;/strong&gt; for HTTP transports. In other words, an MCP server &lt;strong&gt;could&lt;/strong&gt; implement a standard OAuth flow: clients obtain an access token (via some OAuth authorization server), present that token to the MCP server, and the server validates it and checks scopes/permissions. The spec makes this kind of transport-level authorization optional but recommended for HTTP-based MCP servers. It even describes a scenario where an MCP server might delegate to an external OAuth provider (like redirecting the user to sign in with a third-party, then exchanging tokens).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ToolHive takes a different path.&lt;/strong&gt; Rather than turning every MCP server into an OAuth resource server (or running a full OAuth authorization dance for each tool), ToolHive centralizes auth in its proxy layer. It uses OIDC for authentication (OAuth2 under the hood, but handled by an external IdP) and then its Cedar policy engine for fine-grained authorization. This means as a developer or admin, you don't have to implement OAuth flows in each MCP server or manage scope definitions for each tool. Instead, you configure ToolHive with the identity provider of your choice (Google, GitHub, corporate SSO, etc. for OIDC login) and write straightforward policies to declare what’s allowed. The heavy lifting of token verification is done once in ToolHive’s front door, and the permission logic is under your control via Cedar policies.&lt;/p&gt;

&lt;p&gt;In summary, the MCP spec focuses on a &lt;strong&gt;standardized mechanism (OAuth 2.1)&lt;/strong&gt; to allow clients to access restricted servers – great for interoperability, but it can be complex to implement directly. ToolHive’s approach is &lt;strong&gt;built for convenience and control&lt;/strong&gt;: it uses standard OIDC to know who the user is, but then gives you a powerful, code-driven way to define authorization rules without a separate OAuth service for each server. This approach trades the spec’s one-size-fits-all OAuth solution for a more flexible policy model that is easily tailored to the needs of different tools and deployments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security and Usability
&lt;/h2&gt;

&lt;p&gt;ToolHive’s approach to authorization within MCP emphasizes secure defaults, integration with existing identity systems, and a developer-friendly policy model. By separating authN and authZ, using a powerful policy language, and acting as a smart proxy, ToolHive achieves a balance of security and usability. Developers can spin up MCP servers with confidence that only the right people (or services) can invoke the right tools, all configured through a clear and versionable policy file. This makes running MCP servers in production (especially in enterprise environments) much more manageable and auditable, without sacrificing the flexibility that makes the Model Context Protocol so powerful.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>security</category>
      <category>api</category>
    </item>
    <item>
      <title>Getting Authentication Right is Critical to Running MCP Servers</title>
      <dc:creator>Juan Antonio Osorio</dc:creator>
      <pubDate>Mon, 14 Apr 2025 06:16:35 +0000</pubDate>
      <link>https://dev.to/stacklok/getting-authentication-right-is-critical-to-running-mcp-servers-39fk</link>
      <guid>https://dev.to/stacklok/getting-authentication-right-is-critical-to-running-mcp-servers-39fk</guid>
      <description>&lt;p&gt;Any time you’re connecting APIs, you have to consider security implications. The same is true for Model Context Protocol Servers. In our experience, it’s important to have  a strong separation of &lt;strong&gt;authentication&lt;/strong&gt; (verifying &lt;em&gt;who&lt;/em&gt; you are) and &lt;strong&gt;authorization&lt;/strong&gt; (deciding &lt;em&gt;what&lt;/em&gt; you can do).&lt;/p&gt;

&lt;p&gt;We built &lt;a href="https://github.com/StacklokLabs/toolhive" rel="noopener noreferrer"&gt;ToolHive&lt;/a&gt; to make it easier and more secure to run MCP Servers. In this post, we’ll explore why ToolHive leverages &lt;strong&gt;OpenID Connect (OIDC)&lt;/strong&gt; for authentication, how it distinguishes authn vs authz, and real-world scenarios it enables – from Google sign-ins to Kubernetes service tokens. (A follow-up post will dive into ToolHive’s built-in authorization framework in detail.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Authentication vs Authorization in &lt;a href="https://github.com/StacklokLabs/toolhive" rel="noopener noreferrer"&gt;ToolHive&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;It’s crucial to understand the difference between &lt;em&gt;authentication&lt;/em&gt; (authn) and &lt;em&gt;authorization&lt;/em&gt; (authz). Authentication is about &lt;strong&gt;identity&lt;/strong&gt; – proving &lt;em&gt;who&lt;/em&gt; is making a request (e.g. via a login or token). Authorization is about &lt;strong&gt;permissions&lt;/strong&gt; – determining if that identity is allowed to perform a given action. ToolHive treats these as separate concerns.&lt;/p&gt;

&lt;p&gt;In practice, this means ToolHive first ensures it knows &lt;em&gt;who&lt;/em&gt; the caller is (authenticating via OIDC), and only then applies rules about &lt;em&gt;what&lt;/em&gt; that caller can do (ToolHive’s own permission model). By explicitly separating authn and authz, ToolHive can rely on well-proven identity systems for authentication and avoid conflating identity with access control. This leads to a cleaner, more secure design.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why OpenID Connect for Authentication?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;OpenID Connect (OIDC)&lt;/strong&gt; is a widely adopted protocol for user authentication built on OAuth 2.0. It provides a standard way to get an identity token (usually a JWT) that confirms a user’s identity, issued by a trusted &lt;strong&gt;Identity Provider&lt;/strong&gt; (IdP). In other words, OIDC lets ToolHive outsource the heavy lifting of verifying identities to providers like Google, GitHub, or corporate SSO systems, and then consume a &lt;strong&gt;verifiable assertion of the user’s identity&lt;/strong&gt; (&lt;a href="https://openid.net/developers/how-connect-works/#:~:text=OpenID%20Connect%20enables%20application%20and,OpenID%20Providers%2C%20and%20session%20logout" rel="noopener noreferrer"&gt;How OpenID Connect Works - OpenID Foundation&lt;/a&gt;). This has several advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Standard and Interoperable&lt;/strong&gt; – OIDC is an interoperable auth protocol based on OAuth2, simplifying identity verification. Because of this, ToolHive can integrate with any OIDC-compliant identity provider without custom code. The protocol even supports features like discovery and JWT signature verification out-of-the-box, making integration easier and less error-prone.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Proven and Secure&lt;/strong&gt; – By using OIDC, ToolHive leverages the security of battle-tested identity systems. OIDC providers handle the user login UI, multi-factor auth, password storage, etc., so ToolHive itself never sees raw passwords or credentials. ToolHive simply trusts the ID token from the provider if it’s correctly signed and not expired.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Decoupled Identity Management&lt;/strong&gt; – Adopting OIDC means ToolHive doesn’t have to be its own identity service. Instead of reinventing user management, it “piggybacks” on existing identity solutions. For organizations, this is huge: you can plug ToolHive into your existing SSO/IdP (Google, Microsoft Entra ID, Okta, etc.) and instantly use those identities.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flexible for Humans and Services&lt;/strong&gt; – OIDC isn’t just for interactive user login. It also works great for service-to-service auth via JWTs. ToolHive’s use of OIDC means it can authenticate &lt;em&gt;both&lt;/em&gt; end-users &lt;em&gt;and&lt;/em&gt; other services in a unified way (more on this below). The common factor is that identity is conveyed through a trusted OIDC token, whether it came from an OAuth web flow or from a service account in Kubernetes.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  OIDC Use Cases in &lt;a href="https://github.com/StacklokLabs/toolhive" rel="noopener noreferrer"&gt;ToolHive&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;How does this OIDC-based authentication play out in practice? Here are two common scenarios where ToolHive’s approach shines:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔐 User Login via Google (OIDC)&lt;/strong&gt; – Imagine a developer wants to run an MCP server that requires user authentication, and they prefer using their Google credentials. Because Google’s identity platform supports OpenID Connect, ToolHive can delegate the login to Google. The user would be redirected to sign in with Google, Google would then provide an &lt;strong&gt;ID token&lt;/strong&gt; to ToolHive representing the user’s Google identity (email, user ID, etc.). ToolHive trusts this token (after verifying its signature and issuer). The end result: the user is authenticated and can use the MCP server using their Google account.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🤖 Service-to-Service Auth with Kubernetes&lt;/strong&gt; – In some cases, you might want to call an MCP server from a service running in a Kubernetes cluster. With ToolHive’s OIDC support this can be done fairly seamlessly. Kubernetes can issue &lt;strong&gt;service account tokens&lt;/strong&gt; that are actually &lt;strong&gt;OIDC JWTs&lt;/strong&gt; (specifically, &lt;em&gt;bound service account tokens&lt;/em&gt; are OIDC identity token). This means a microservice in your cluster can present its Kubernetes JWT to ToolHive; ToolHive will recognize and validate it as an OIDC token. The beauty here is that the service doesn’t need a separate API key or password – its cluster-issued token is enough to prove its identity. This is perfect for scenarios like an internal app calling an MCP server managed by ToolHive; the auth is secure, token-based, and fully automated by existing infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  How ToolHive Builds on the MCP Spec’s Recommendations
&lt;/h2&gt;

&lt;p&gt;If you’ve read the MCP specification, you might know that the MCP world has been gravitating towards OAuth 2.x for securing tool access. The latest MCP spec (as of early 2025) actually standardizes an OAuth 2.1-based authorization flow for MCP servers. In essence, the spec suggests that an MCP server should use OAuth 2.1 so that clients can obtain tokens to access it, rather than using simplistic API keys. This is a great step for security!&lt;/p&gt;

&lt;p&gt;However, the spec’s approach couples authentication and authorization together in the OAuth flow. It essentially envisions the MCP server itself acting as both the &lt;strong&gt;Authorization Server and Resource Server&lt;/strong&gt; in OAuth terms. This means an MCP server developer would need to implement OAuth endpoints (authorization, token, client registration, etc.) and manage tokens, which is non-trivial. In other words, every MCP server might end up reinventing identity and auth if following the spec naively – a recipe for inconsistent implementations and potential security issues.&lt;/p&gt;

&lt;p&gt;ToolHive’s philosophy is to leverage existing best practices instead of coming up with our own thing. Rather than turning every MCP tool into an OAuth mini-server, ToolHive acts as the gateway that handles auth on behalf of the MCP servers it runs. It can use OIDC for &lt;em&gt;authentication&lt;/em&gt; (as described earlier) to validate the user or service’s identity, and then applies its own &lt;em&gt;authorization&lt;/em&gt; logic to decide if that identity can access a given tool. This clean separation aligns with best practices and the principle of least privilege. The MCP spec’s OAuth guidance is not thrown away. In fact, ToolHive aligns with it by using the OIDC/OAuth standards.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does it look like?
&lt;/h2&gt;

&lt;p&gt;ToolHive has built-in support for OIDC token validation. Enabling it in your server would look as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ thv run \
    --oidc-audience "some-audience" \
    --oidc-client-id "some-client" \
    --oidc-issuer https://some-oidc-issuer.com/ \
    --oidc-jwks-url https://some-oidc-issuer.com/path/to/jws \
    $MY_MCP_SERVER
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using those flags, the MCP server proxy that ToolHive spawns will validate the OIDC token with the relevant information.&lt;/p&gt;

&lt;p&gt;Let’s look at this in a Kubernetes setup. Let’s say our deployment looks as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Service account name&lt;/strong&gt;: &lt;code&gt;my-service&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Namespace&lt;/strong&gt;: &lt;code&gt;mcp-servers&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audience expected by the MCP server&lt;/strong&gt;: &lt;code&gt;toolhive&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;OIDC issuer&lt;/strong&gt; for your cluster (usually the Kubernetes API server): &lt;code&gt;https://kubernetes.default.svc&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;JWKS URL&lt;/strong&gt; (location of public keys used to verify the JWT signature): &lt;code&gt;https://kubernetes.default.svc/openid/v1/jwks&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The ToolHive pod would look as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: v1       
kind: Pod                    
metadata:        
  labels:                                    
    app: thv-everything          
    app.kubernetes.io/name: thv-everything  
  name: thv-everything
  namespace: mcp-servers          
spec:                                        
  containers:    
    - args:                          
        - run                
        - --foreground=true                      
        - --port=8080  
        - --transport=stdio
        - --name=mcp-everything
        - --oidc-audience "toolhive"
        - --oidc-issuer=https://kubernetes.default.svc
        - --oidc-client-id=my-service.mcp-servers.svc.cluster.local
        - --oidc-jwks-url=https://kubernetes.default.svc/openid/v1/jwks
        - docker.io/mcp/everything               
      image: ghcr.io/stackloklabs/toolhive:latest
      name: toolhive     
      serviceAccountName: toolhive-sa
      ports:                                 
        - containerPort: 8080
          name: http                                             
          protocol: TCP
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will only allow requests coming from a pod that has the &lt;strong&gt;my-service&lt;/strong&gt; service account in the &lt;strong&gt;mcp-servers&lt;/strong&gt; namespace.&lt;/p&gt;

&lt;p&gt;Did we ever mention we have Kubernetes support? We do! In fact, this will spawn an MCP server instance in Kubernetes. We’ll talk about this in detail later on.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s next?
&lt;/h2&gt;

&lt;p&gt;With OIDC, ToolHive is able to validate identities, which is a great first step. However, this is not the end of the road when locking down a deployment. Authorization or Access Control is a very important piece that needs to be considered. Authorization itself can be challenging, but the ToolHive team decided to give it a go anyways!&lt;/p&gt;

&lt;p&gt;Stay tuned for a follow-up post, where we’ll dive into ToolHive’s built-in authorization framework. We’ll see how ToolHive defines and checks permissions for MCP servers, ensuring that once a user/service is authenticated via OIDC, they only get to do what they’re allowed to. By separating authn and authz, ToolHive makes it easier to reason about security in your AI toolchain – and ultimately, to keep your MCP servers both &lt;strong&gt;easy and secure&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>mcp</category>
      <category>identity</category>
    </item>
    <item>
      <title>ToolHive: Making MCP Servers Easy, Secure, and Fun</title>
      <dc:creator>Juan Antonio Osorio</dc:creator>
      <pubDate>Wed, 09 Apr 2025 08:08:50 +0000</pubDate>
      <link>https://dev.to/stacklok/toolhive-making-mcp-servers-easy-secure-and-fun-7hi</link>
      <guid>https://dev.to/stacklok/toolhive-making-mcp-servers-easy-secure-and-fun-7hi</guid>
      <description>&lt;p&gt;We are excited about the potential of Model Context Protocol (MCP) servers and inspired to make it more consistent to set up, easier to configure and overall secure. So, we're releasing &lt;a href="https://github.com/StacklokLabs/toolhive" rel="noopener noreferrer"&gt;ToolHive&lt;/a&gt; as an open-source project that uses existing technology (e.g. Docker and Kubernetes) for better packaging, security utilities, and more. Let's build on MCP together!&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;ToolHive helps you run MCP servers in an &lt;strong&gt;easy&lt;/strong&gt;, &lt;strong&gt;secure,&lt;/strong&gt; and &lt;strong&gt;opinionated&lt;/strong&gt; way.&lt;/li&gt;
&lt;li&gt;ToolHive leverages container runtimes (e.g. the Docker runtime) to do so.&lt;/li&gt;
&lt;li&gt;ToolHive is open source, and we welcome contributions!&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/StacklokLabs/toolhive" rel="noopener noreferrer"&gt;Here's the repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://discord.gg/stacklok" rel="noopener noreferrer"&gt;Join us in Discord!&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How it started
&lt;/h2&gt;

&lt;p&gt;MCP has generated a lot of hype recently in the AI ecosystem. When we first heard about it, it felt just right: a formalization of how AI clients and agents can interact with external APIs, a potential ecosystem, and a lot of opportunities. So, to get myself better acquainted with this, I decided to try it out. There were already a lot of folks building MCP servers and many tutorials on setting them up, so this part wasn’t too hard. However, when I started going through the tutorials, some things felt off. I was sure I wasn’t the only one, so I kicked off an exploration phase with a colleague, and we started gathering information. We landed on three main points of friction:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Search &amp;amp; Discovery Challenges&lt;/li&gt;
&lt;li&gt;Inconsistency &amp;amp; Installation Friction&lt;/li&gt;
&lt;li&gt;Credential &amp;amp; Permission Concerns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s go through the three friction points and expand.&lt;/p&gt;

&lt;h3&gt;
  
  
  Search &amp;amp; Discovery Challenges
&lt;/h3&gt;

&lt;p&gt;When exploring the tooling, I and everybody I talked to had different answers to how we found servers. Docker provides a set of official MCP servers in a dedicated namespace, Anthropic has a set of examples, there are a bunch of “Awesome MCP” lists, and nowadays there are even services that allow you to use hosted MCPs. But a lot of questions lingered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which MCP is the right one for me?&lt;/li&gt;
&lt;li&gt;How do I know if I can trust it?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finding MCP servers is not as simple as it could be, and finding the right one has its challenges. We clearly aren’t the only ones thinking about this; the recent disclosure of &lt;a href="https://invariantlabs.ai/blog/mcp-security-notification-tool-poisoning-attacks" rel="noopener noreferrer"&gt;Tool Poisoning Attacks&lt;/a&gt; presents issues relating to this realm.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inconsistency &amp;amp; Installation Friction
&lt;/h3&gt;

&lt;p&gt;Once you’ve found a subset of MCP servers that work for your use case, the next issue is learning how to install and use them.&lt;/p&gt;

&lt;p&gt;At the time of writing, we found several implementations that didn’t work. Don’t get me wrong, software quality issues are common, especially in a community as new and fast-moving as MCP. But it is challenging for us early adopters. Before going forward, I must give props to Docker and Anthropic for their curated lists of MCP servers since they’re what’s worked the best for me so far.&lt;/p&gt;

&lt;p&gt;So, when looking at MCP installation instructions, they’d often look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"some-mcp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"@namespace/mcp-name"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I have to install npx? I’m one of those people who run an immutable Linux distribution (Fedora Silverblue) as my daily driver. If I need a tool, I’ll run a dedicated container or a toolbox for that. I’m not keen on allowing my IDE to run npx commands…&lt;/p&gt;

&lt;p&gt;But anyway, let’s see other implementations and figure out how to run them. And this is where I saw a lot of divergence. Some use &lt;code&gt;uv&lt;/code&gt;, some use docker, and there are a lot more invocation styles for this!&lt;/p&gt;

&lt;h3&gt;
  
  
  Credential &amp;amp; Permission Concerns
&lt;/h3&gt;

&lt;p&gt;Finally, when looking at other implementations, I stumbled upon something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"some-mcp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"@namespace/mcp-name"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"MCP_API_KEY"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YOUR_API_KEY_HERE"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do I have to copy my API key into a clear text configuration file? As a security engineer, this feels like a no-go for me. And, once I run it, how do I know if it’s not sending my keys elsewhere?&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter ToolHive
&lt;/h2&gt;

&lt;p&gt;To address this, we decided to start project ToolHive to run MCP servers in an easy, consistent, and secure manner. This is not a problem that a single company can and should fix alone. And, given that security and community are our backbone, we’re open-sourcing it!&lt;/p&gt;

&lt;p&gt;ToolHive is an opinionated way of running MCP servers that takes best practices and packages them in a simple utility.&lt;/p&gt;

&lt;p&gt;Simply use the command &lt;code&gt;thv run&lt;/code&gt; to get an MCP server running locally.&lt;/p&gt;

&lt;p&gt;ToolHive can also help configure your clients by automatically inserting the MCP entry into the right config file. Today we support Cursor, Roo Code, and GitHub Copilot in VS Code, with more to come.&lt;/p&gt;

&lt;h3&gt;
  
  
  Docker (OCI) images are here to stay
&lt;/h3&gt;

&lt;p&gt;Packaging software can be challenging, but we already have solutions to this. And, coming from the Cloud Native community, we believe that Docker images sure get us a lot closer to the state we’d like in packaging MCP servers. They allow for better consistency in packaging plus they allow us to leverage a lot of the security utilities that have been developed over recent years: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Container signatures&lt;/li&gt;
&lt;li&gt;SBOMs tied to image tags&lt;/li&gt;
&lt;li&gt;Attestations&lt;/li&gt;
&lt;li&gt;Vulnerability Scanning&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There’s a lot we can inherit from the ecosystem by simply converging on this format.&lt;/p&gt;

&lt;h3&gt;
  
  
  Containers, containers, containers
&lt;/h3&gt;

&lt;p&gt;ToolHive itself does not re-invent the wheel. It simply is a wrapper that leverages existing technologies to do its job. In a local experience, ToolHive uses Docker (or any other container runtime of your choice) to spawn containers that run the server. It then spawns a proxy that ensures one way of serving that utility. Today, we’ve implemented &lt;a href="https://spec.modelcontextprotocol.io/specification/2024-11-05/basic/transports/#http-with-sse" rel="noopener noreferrer"&gt;HTTP SSE&lt;/a&gt; as the transport protocol of choice. This was done given current client support for this. However, when more clients start adopting the new &lt;a href="https://spec.modelcontextprotocol.io/specification/2025-03-26/basic/transports/#streamable-http" rel="noopener noreferrer"&gt;Streamable HTTP transport&lt;/a&gt;, we’ll likely &lt;a href="https://github.com/StacklokLabs/toolhive/issues/54" rel="noopener noreferrer"&gt;migrate to that&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;So, you end up with one endpoint per MCP server you run.&lt;/p&gt;

&lt;p&gt;We’re aware that a local experience is not going to be the only one that folks want to leverage, so we also added a Kubernetes runtime! This way, you can run your servers as you see fit, where you see fit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Let secrets be secret
&lt;/h3&gt;

&lt;p&gt;We’re not very keen on API keys leaking, and thus we decided to implement secret storage as part of this. Simply use the command &lt;code&gt;thv secret set &amp;lt;key&amp;gt;&lt;/code&gt;, to store an API key securely. To use it, you can reference the key as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;thv run &lt;span class="nt"&gt;--secret&lt;/span&gt; secret-key,target&lt;span class="o"&gt;=&lt;/span&gt;ENV_API_KEY &amp;lt;MCP name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is no need to copy secrets across configuration files!&lt;/p&gt;

&lt;p&gt;Here’s how they’d look in the end:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"fetch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:23971/sse#fetch"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"brave-search"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:23872/sse#brave-search"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sequentialthinking"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:29313/sse#sequentialthinking"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ToolHive encrypts your secrets to keep them safe, including keyring support for Linux and macOS! We’ll dedicate a later blog post to this topic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Find your tools
&lt;/h3&gt;

&lt;p&gt;We’ve also implemented a basic MCP registry to allow you to easily find the tools you need depending on your use case. The &lt;code&gt;thv search&lt;/code&gt; command allows you to search by tag or keyword, and the &lt;code&gt;thv registry list&lt;/code&gt; allows you to view all the registry entries we’ve packaged so far.&lt;/p&gt;

&lt;p&gt;The intention is not for us to be the ultimate gatekeepers of this. We’re happy to help curate entries, and we’re also glad to keep adding entries, but this is somewhere we welcome the community to engage. We’d like to make this more pluggable, and we’d like to have the community help in curation. This tool is meant for everyone, and we believe the community can do a better job in shepherding this effort.&lt;/p&gt;

&lt;h3&gt;
  
  
  Let’s lock things down
&lt;/h3&gt;

&lt;p&gt;To address some of the security concerns we’ve encountered with MCP servers, we decided to propose a permission system for ToolHive. A sample would look as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"read"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"write"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"network"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"outbound"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"insecure_allow_all"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"allow_transport"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"tcp"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"allow_host"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"docs.github.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"github.com"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"allow_port"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="mi"&gt;443&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows the MCP to access GitHub through port 443 and nothing else.&lt;/p&gt;

&lt;p&gt;With this, we’re hoping to address concerns of keys leaking to external and unauthorized servers. Implementation of this is still in progress, so it’s a good time to join and chime in!&lt;/p&gt;

&lt;h3&gt;
  
  
  Wait… But there’s more!
&lt;/h3&gt;

&lt;p&gt;We added support for running MCPs over Kubernetes, which allows you to host your tools in your own cluster.&lt;/p&gt;

&lt;p&gt;We are also looking into authentication and authorization, bringing end-to-end security for the framework.&lt;/p&gt;

&lt;p&gt;We’ll be talking about these in detail in further blog posts. There are a lot of things to come! &lt;/p&gt;

&lt;h2&gt;
  
  
  Join the fun!
&lt;/h2&gt;

&lt;p&gt;ToolHive is open source, and we welcome contributions!&lt;/p&gt;

&lt;p&gt;Whether you want to propose new MCP servers for the registry, add new functionality and features, or help us with documentation, we welcome everybody to join! It is only as a community that we can thrive.&lt;/p&gt;

&lt;p&gt;Join us on &lt;a href="https://discord.gg/stacklok" rel="noopener noreferrer"&gt;Discord&lt;/a&gt; and check out &lt;a href="https://github.com/StacklokLabs/toolhive" rel="noopener noreferrer"&gt;the repo&lt;/a&gt;! &lt;/p&gt;

&lt;p&gt;I’ll take some time to write further about the challenges we’re solving and dig deeper into each of the friction points, as well as the solutions that ToolHive proposes. Stay tuned!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>security</category>
      <category>containers</category>
    </item>
    <item>
      <title>Shield Your Agents: Integrating LangGraph’s workflows with CodeGate's Security Layer</title>
      <dc:creator>Juan Antonio Osorio</dc:creator>
      <pubDate>Tue, 11 Mar 2025 10:52:26 +0000</pubDate>
      <link>https://dev.to/stacklok/shield-your-agents-integrating-langgraphs-workflows-with-codegates-security-layer-2iik</link>
      <guid>https://dev.to/stacklok/shield-your-agents-integrating-langgraphs-workflows-with-codegates-security-layer-2iik</guid>
      <description>&lt;p&gt;Authors:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/in/jaosorior/" rel="noopener noreferrer"&gt;Juan Antonio "Ozz" Osorio&lt;/a&gt; is a Mexican software engineer living in Finland. He has worked in security with cloud-related open source projects such as OpenStack and Kubernetes, as well as security for bare metal environments. He's currently working at Stacklok building tools to make software supply chain and AI security easier and friendlier.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/in/radoslavdimitrov/" rel="noopener noreferrer"&gt;Radoslav Dimitrov&lt;/a&gt; is a Senior Software Engineer at Stacklok with a background in supply chain security. Previously at VMware, he is an open-source maintainer of &lt;a href="https://github.com/theupdateframework/go-tuf" rel="noopener noreferrer"&gt;go-tuf&lt;/a&gt; and is currently working on CodeGate, a gateway that enhances AI coding assistants by improving privacy, cost efficiency, and performance across multiple models.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Challenge: Securing Your AI Agents&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;As AI developers build increasingly sophisticated LLM-powered applications, two critical challenges emerge:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Security vulnerabilities&lt;/strong&gt; - LLMs can inadvertently expose sensitive data, recommend insecure code, or suggest vulnerable dependencies
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model management complexity&lt;/strong&gt; - Juggling multiple AI providers, API keys, and model configurations across applications creates friction&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These challenges are especially pronounced when building multi-agent systems with frameworks like LangGraph, where multiple LLM calls occur within complex workflows. What if there was a simple way to add a security layer without disrupting your existing architecture?&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Enter CodeGate + LangGraph: A Powerful Combination&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/stacklok/codegate" rel="noopener noreferrer"&gt;CodeGate&lt;/a&gt; acts as a protective gateway between your &lt;a href="https://www.langchain.com/langgraph" rel="noopener noreferrer"&gt;LangGraph&lt;/a&gt; applications and AI providers, handling security and model management while preserving your application's core logic. The integration requires minimal code changes but delivers substantial benefits.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;What is CodeGate?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffudq5clbb3nor0b4uy0q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffudq5clbb3nor0b4uy0q.png" alt="CodeGate Logo" width="800" height="118"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;CodeGate is a local AI gateway that enhances security and streamlines management of other AI assistants, models and applications. CodeGate includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Custom instructions:&lt;/strong&gt; Set up workspaces with prompts engineered for specific stages of your workflow
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model routing (muxing)&lt;/strong&gt;: Route requests to different models based on criteria like file types or project needs
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security features&lt;/strong&gt;: Automatic redaction of secrets and PII from prompts
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Session insights&lt;/strong&gt;: Track all interactions in a centralized dashboard including prompt history and security alerts&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;What is LangGraph?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F34o75h0yw8ws3mg15qmx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F34o75h0yw8ws3mg15qmx.png" alt="LangGraph logo" width="800" height="123"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;LangGraph extends LangChain to enable stateful, multi-agent applications with LLMs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build directed graphs defining how agents communicate
&lt;/li&gt;
&lt;li&gt;Manage state across multiple interactions
&lt;/li&gt;
&lt;li&gt;Create complex workflows with conditional logic
&lt;/li&gt;
&lt;li&gt;Combine different LLM-powered components&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Real-World Example: HAIstings Security Assistant&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;To demonstrate how simple the integration is, we’ll use HAIstings as an example, an AI-powered companion that prioritizes Kubernetes vulnerabilities using LangGraph - &lt;a href="https://github.com/StacklokLabs/HAIstings" rel="noopener noreferrer"&gt;https://github.com/StacklokLabs/HAIstings&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;HAIstings analyzes vulnerability reports from Kubernetes clusters, including potentially sensitive infrastructure details, making it a perfect candidate for CodeGate's security features.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Integration: Step-by-Step&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;1. Install and run CodeGate&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;First, deploy CodeGate using Docker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run --name codegate -d -p 8989:8989 -p 9090:9090 \
  --mount type=volume,src=codegate_volume,dst=/app/codegate_volume \
  --restart unless-stopped ghcr.io/stacklok/codegate:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once running, CodeGate’s gateway is available at &lt;a href="http://localhost:8989/" rel="noopener noreferrer"&gt;http://localhost:8989&lt;/a&gt; and you can access the CodeGate dashboard at &lt;a href="http://localhost:9090/" rel="noopener noreferrer"&gt;http://localhost:9090&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;2. Configure your LangChain app to use CodeGate&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In your LangGraph application, point your LLM initialization to CodeGate's muxing endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.chat_models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;init_chat_model&lt;/span&gt;
&lt;span class="n"&gt;llm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;init_chat_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-4&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# Model name is handled by CodeGate's muxing
&lt;/span&gt;    &lt;span class="n"&gt;model_provider&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;openai&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;not-needed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# CodeGate manages API keys
&lt;/span&gt;    &lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://127.0.0.1:8989/v1/mux&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;  &lt;span class="c1"&gt;# CodeGate's muxing endpoint
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This single change routes all LLM calls through CodeGate, instantly adding security features without changing your application's logic.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;3. Create your LangGraph workflow (no changes needed)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Your LangGraph workflow remains unchanged! Here's how HAIstings implements its conversation flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Define graph nodes and connections
&lt;/span&gt;&lt;span class="n"&gt;graph_builder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;StateGraph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;graph_builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;retrieve&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;retrieve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;graph_builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;generate_initial&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;generate_initial&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;graph_builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;extra_userinput&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;extra_userinput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Add edges
&lt;/span&gt;&lt;span class="n"&gt;graph_builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_edge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;START&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;retrieve&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;graph_builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_edge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;retrieve&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;generate_initial&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;graph_builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_edge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;generate_initial&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;extra_userinput&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Add conditional edges
&lt;/span&gt;&lt;span class="n"&gt;graph_builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_conditional_edges&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;extra_userinput&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;needs_more_info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;extra_userinput&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;END&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Compile the graph
&lt;/span&gt;&lt;span class="n"&gt;graph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;graph_builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;checkpointer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;4. Setup your workspaces&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Access the CodeGate dashboard at &lt;a href="http://localhost:9090" rel="noopener noreferrer"&gt;http://localhost:9090&lt;/a&gt; to configure your LLM provider and set up a workspace with your desired muxing rules (&lt;a href="https://docs.codegate.ai/features/muxing" rel="noopener noreferrer"&gt;see the docs for more&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;In our case, this looks like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fec395zwhzxcr4hyaju7v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fec395zwhzxcr4hyaju7v.png" alt="Workspace setup screenshot" width="800" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;5. Run your application&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Now when you run your application, all LLM interactions pass through CodeGate, which:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Checks for and redacts sensitive information, i.e. secrets or PII
&lt;/li&gt;
&lt;li&gt;Routes to the appropriate model based on your desired workspace settings
&lt;/li&gt;
&lt;li&gt;Logs interactions for audit and monitoring
&lt;/li&gt;
&lt;li&gt;Scans for security issues in generated code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For more details, check out the &lt;a href="https://docs.codegate.ai/" rel="noopener noreferrer"&gt;CodeGate documentation&lt;/a&gt; and &lt;a href="https://langchain-ai.github.io/langgraph/" rel="noopener noreferrer"&gt;LangGraph documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;CodeGate dashboard view:&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The CodeGate dashboard now displays all interactions, showing redacted secrets and providing a complete audit trail:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9zqcp9s37vv94e6ifqiu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9zqcp9s37vv94e6ifqiu.png" alt="security dashboard" width="800" height="357"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Benefits of the Integration&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Adding CodeGate to your LangGraph applications provides several key advantages:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Enhanced security&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Automatic secrets detection&lt;/strong&gt;: API keys, passwords, and credentials are identified and redacted
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PII protection&lt;/strong&gt;: Personal identifiable information is kept secure
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Safe code generation&lt;/strong&gt;: Generated code is scanned for security issues&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Simplified model management&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Centralized configuration&lt;/strong&gt;: Manage all API keys and provider settings in one place
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model flexibility&lt;/strong&gt;: Change models without modifying application code
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Workspace isolation&lt;/strong&gt;: Select different models for different projects&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Improved observability&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Centralized logging&lt;/strong&gt;: View all LLM interactions across applications
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security alerting&lt;/strong&gt;: Get notified about potential issues
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prompt history&lt;/strong&gt;: Review past interactions for debugging&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Operational advantages&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Privacy-first&lt;/strong&gt;: All processing happens locally
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minimized vendor lock-in&lt;/strong&gt;: Switch between AI providers easily
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost optimization&lt;/strong&gt;: Route to different models based on needs&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Common Questions&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q: Will CodeGate slow down my LangGraph application?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A: CodeGate adds minimal latency while providing significant security benefits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: What if I'm using different model providers?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A: CodeGate supports OpenAI, Anthropic, Ollama, and more. You can mix providers across workspaces.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: How does model muxing work with LangGraph?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A: CodeGate's muxing routes requests to different models based on rules you define. Your LangGraph code remains unchanged.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Integrating CodeGate with LangGraph provides a powerful security layer for your AI applications with minimal effort. The HAIstings example demonstrates how seamlessly these technologies work together to create secure, sophisticated AI systems.&lt;/p&gt;

&lt;p&gt;By focusing on security from the beginning, you can build LangGraph applications that protect sensitive data while remaining flexible and maintainable.&lt;/p&gt;

&lt;p&gt;Start building more secure multi-agent systems today by adding CodeGate to your LangGraph applications! We invite you to &lt;a href="https://discord.gg/3wHfdYya" rel="noopener noreferrer"&gt;join our Discord&lt;/a&gt; to stay up to date on dev-focused AI news, get notified of CodeGate releases, or send us a note with what you’d like to see next.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>langchain</category>
      <category>security</category>
      <category>python</category>
    </item>
  </channel>
</rss>
