Sentinel: Using MCP Sampling to Analyze Your Own Tool Security

If you connect enough MCP servers to your AI client, you eventually start wondering: is this combination of tools actually safe? You’ve got a database connector, an email sender, a file system reader, and a code executor all available to the same LLM. What could go wrong?

I built Sentinel to answer that question, and the approach turned out to be more interesting than the answer itself.

The Problem

Modern AI workflows stitch together a growing number of MCP servers. Each one is fine on its own: a Gmail connector just sends email, a Postgres connector just queries a database. But when an LLM has access to both simultaneously, new risks emerge that neither server was designed to handle.

Consider this setup:

graph LR
    LLM["AI Model"] --> DB["Postgres MCP<br/>reads customer PII"]
    LLM --> Email["Gmail MCP<br/>sends to any address"]
    LLM --> FS["Filesystem MCP<br/>reads local files"]
    LLM --> Code["Code Runner MCP<br/>executes Python"]

    style DB fill:#ffe0e0
    style Email fill:#ffe0e0

A tool that reads PII from a database combined with a tool that sends email creates a data exfiltration path. A tool that fetches untrusted web content combined with a tool that executes code creates a prompt injection vector. These are emergent risks, invisible when you look at each server in isolation.

The Architectural Question

My first instinct was to build a proper security scanner: parse MCP manifests, build a permission graph, run rules against it. I wrote a PRD, designed Pydantic models, planned a 40-task roadmap. Classic over-engineering.

Then I stopped and asked: how would this server actually get the information it needs? MCP is hub-and-spoke. Servers are isolated from each other. Sentinel, as just another spoke, has no native way to see the other spokes.

There’s no list_other_servers() in the MCP spec.

The Sampling Approach

The solution came from a feature I’d been reading about: MCP sampling. Sampling lets an MCP server ask the client’s LLM to generate a completion. The LLM knows what tools it has because they’re in its context. So Sentinel can just ask.

The entire architecture collapses to two sampling calls:

sequenceDiagram
    participant User
    participant LLM as Client LLM
    participant Sentinel as Sentinel MCP

    User->>LLM: "Assess my security posture"
    LLM->>Sentinel: Call assess()
    Sentinel->>LLM: sampling: "What tools do you have?"
    LLM->>Sentinel: Tool inventory
    Sentinel->>LLM: sampling: "Analyze these for risks"
    LLM->>Sentinel: Risk assessment
    Sentinel->>LLM: Return analysis
    LLM->>User: Security report
  1. Discover: Ask the client LLM to describe all its available MCP tools and their capabilities
  2. Analyze: Feed that inventory back to the LLM and ask it to identify security risks

No manifest parsing. No permission graphs. No API keys. The client’s own LLM does both the discovery and the analysis.

The Implementation

The whole thing is one Python file. Here’s the core of it:

from fastmcp import FastMCP, Context

mcp = FastMCP("sentinel")

@mcp.tool
async def assess(ctx: Context) -> str:
    """Analyze the security posture of your MCP tool setup."""

    # Step 1: Ask the client LLM to describe its tools
    ctx.info("Discovering connected MCP tools...")
    discovery = await ctx.sample(
        messages="List all MCP tools you currently have access to, "
                 "with their capabilities.",
        system_prompt=(
            "You are cataloging MCP tools. List every tool you have "
            "access to (excluding sentinel tools). For each, provide: "
            "tool name, what it can read/access, what actions it can "
            "take, and what auth/permissions it requires."
        ),
        max_tokens=8192,
    )

    tool_inventory = discovery.text
    if not tool_inventory or not tool_inventory.strip():
        return "Could not discover any MCP tools."

    # Step 2: Analyze the inventory for security risks
    ctx.info("Analyzing tool configuration for security risks...")
    analysis = await ctx.sample(
        messages=ANALYSIS_PROMPT.format(tools=tool_inventory),
        system_prompt=(
            "You are a security analyst specializing in AI tool chain "
            "security. Analyze the provided tool inventory for concrete "
            "security risks. Be specific and actionable."
        ),
        max_tokens=8192,
    )

    return analysis.text

The analysis prompt asks the LLM to look for specific risk categories:

  • Data exfiltration paths: Can a tool that reads sensitive data combine with a tool that sends data externally?
  • Prompt injection vectors: Can a tool that ingests untrusted content feed into a tool that takes privileged actions?
  • Overly broad access: Does any tool have more access than it likely needs?
  • Missing authentication: Are any tools accessing sensitive resources without auth?
  • Lateral movement: Could compromising one tool’s access lead to escalated access through another?

For each risk, the LLM provides a severity rating, the specific tools involved, and a recommendation to mitigate it.

Installation

Sentinel is packaged for uvx, so you can add it to any MCP client:

{
  "mcpServers": {
    "sentinel": {
      "command": "uvx",
      "args": ["sentinel-security-advisor"]
    }
  }
}

Then just ask your AI: “Assess my MCP security posture.”

What It Actually Finds

The quality of the analysis depends entirely on the client’s LLM, but in practice it does a surprisingly good job of identifying real risks. Given a typical developer setup with filesystem, database, email, and code execution tools, it consistently flags:

  • The database-to-email exfiltration path (PII leakage)
  • Untrusted web content flowing into code execution (injection)
  • Filesystem tools with overly broad access (no path restrictions)
  • Tools without authentication requirements

It’s essentially threat modeling, done conversationally. Not a scanner, not a compliance tool, more like having a security-minded colleague look at your setup and point out the obvious problems.

The Tradeoffs

This approach has real limitations:

The LLM only sees tool names and descriptions, not implementations. It can infer that a query_database tool probably reads data and a send_email tool probably sends email, but it can’t see the actual permissions, authentication config, or data classification. The analysis is based on inferred capabilities, not declared ones.

It’s as good as the LLM. A more capable model gives better analysis. A smaller model might miss subtle risks or flag false positives.

Client support for sampling is limited. As of early 2026, Claude Desktop and Claude Code don’t support sampling yet. VS Code with Copilot does. FastMCP offers a fallback handler if you want to provide your own API key, but that adds friction.

It can’t see runtime behavior. A tool might have database access in its description but only use it for read-only aggregations. Sentinel can’t tell the difference.

Why This Architecture Is Interesting

Beyond the security use case, Sentinel demonstrates a pattern that I think will become more common: MCP servers that use sampling to introspect the client’s environment.

The LLM sitting at the center of an MCP setup knows things that no individual server can know. It sees the full tool landscape, the user’s context, and the relationships between capabilities. Sampling gives servers a way to tap into that knowledge.

You could imagine the same pattern applied to:

  • Workflow optimization: “What tools do you have? Here’s how to combine them more efficiently.”
  • Compatibility checking: “Which of your tools have conflicting requirements?”
  • Onboarding: “Here’s what your tool setup can and can’t do.”

The LLM becomes a shared information layer that servers can query about the broader system they’re part of.

Getting Started

The project is on GitHub. It’s deliberately minimal, just fastmcp as a dependency and about a hundred lines of Python. If you want to try it:

# Run directly
uvx sentinel-security-advisor

# Or install from source
git clone https://github.com/wdm0006/mcp-sentinel
cd mcp-sentinel
uv sync
uv run sentinel

If you’re interested in the sampling pattern more broadly, check out my guide on MCP sampling and elicitation for the technical details of how it works under the hood.