AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
MCP unreviewed MIT Self-run

AI Sentinel

mcp-marcelroozekrans-ai-sentinel · by MarcelRoozekrans

Security monitoring middleware for IChatClient (Microsoft.Extensions.AI). 55 detectors for prompt injection, hallucination, PII leakage, and operational anomalies. Intervention engine, embedded dashboard, audit forwarders to Azure Sentinel + OpenTelemetry. Drop-in middleware for any LLM client.

No reviews yet
0 installs
29 views
0.0% view→install

Install

$ agentstack add mcp-marcelroozekrans-ai-sentinel

Open-source listing, not yet scanned by AgentStack. Follow the source repository for install instructions.

Security review

⚠ Flagged

1 finding(s); flagged for manual review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures
  • high Possible prompt-injection directive.

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution Used
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Reliability & compatibility

Not yet reviewed
0 installs to date
no reviews yet
2mo ago

Declared compatibility

Claude CodeClaude DesktopCursorWindsurf

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of AI Sentinel? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

AI.Sentinel

[](https://github.com/MarcelRoozekrans/AI.Sentinel/actions/workflows/ci.yml) [](https://www.nuget.org/packages/AI.Sentinel) [](https://www.nuget.org/packages/AI.Sentinel) [](LICENSE) [](https://marcelroozekrans.github.io/AI.Sentinel/) [](https://github.com/sponsors/MarcelRoozekrans)

Security monitoring middleware for IChatClient (Microsoft.Extensions.AI). Wraps any LLM client transparently, scans every prompt and response through 55 detectors, and blocks, alerts, or logs threats — with an embedded real-time dashboard.

> Not building on IChatClient? AI.Sentinel also ships as a drop-in hook for [Claude Code](#claude-code) (sentinel-hook), [GitHub Copilot](#github-copilot) (sentinel-copilot-hook), and any [MCP](#mcp-proxy) host — Cursor, Continue, Cline, Windsurf — via the sentinel-mcp stdio proxy. Same 55 detectors, same audit trail, zero code changes in the host.

> Approval workflows for high-stakes tool calls (delete_database, send_payment, rotate_secrets) — pluggable backends include in-memory, SQLite (persists across CLI invocations), and native Microsoft Entra PIM (approvers act in the portal they already use). Operators approve/deny from the dashboard or the PIM portal; the conversation resumes when approval lands. See the approvals docs.


Why you need it

When you connect an LLM to your application you inherit a new attack surface. Users can craft messages that override the model's instructions (prompt injection), the model can leak credentials or PII it saw in context (credential exposure), or return fabricated citations and wildly inconsistent numbers (hallucination). None of these are bugs in your code — they happen at the model boundary, which your existing middleware stack doesn't see.

AI.Sentinel sits at that boundary:

User prompt → [AI.Sentinel: scan] → LLM → [AI.Sentinel: scan] → Your app

It scans both directions on every call. If something looks wrong it can quarantine the message before it reaches the model, or quarantine the response before it reaches the user. If it only looks suspicious it alerts your logging/event system. Everything is stored in an in-process audit ring buffer and surfaced on a live dashboard.

The embedded dashboard ships in AI.Sentinel.AspNetCore. Mount it on any ASP.NET Core app with one line — no JavaScript framework, no extra service to run.


Packages

| Package | Description | |---|---| | AI.Sentinel | Core — pipeline, 55 detectors, intervention engine, audit store | | AI.Sentinel.Detectors.Sdk | SDK for writing and testing custom detectors — SentinelContextBuilder, FakeEmbeddingGenerator, worked examples | | AI.Sentinel.AspNetCore | Embedded dashboard (no JS framework, HTMX + SSE) | | AI.Sentinel.Cli | dotnet tool install AI.Sentinel.Cli — offline replay CLI for forensics + CI | | AI.Sentinel.ClaudeCode / AI.Sentinel.ClaudeCode.Cli | Claude Code native hook adapter — wire into settings.json hooks to scan UserPromptSubmit, PreToolUse, PostToolUse | | AI.Sentinel.Copilot / AI.Sentinel.Copilot.Cli | GitHub Copilot native hook adapter — wire into hooks.json to scan userPromptSubmitted, preToolUse, postToolUse | | AI.Sentinel.Mcp / AI.Sentinel.Mcp.Cli | dotnet tool install AI.Sentinel.Mcp.Cli — stdio MCP proxy that scans tools/call + prompts/get for any MCP-speaking host (Cursor, Continue, Cline, Windsurf, Copilot) |

dotnet add package AI.Sentinel
dotnet add package AI.Sentinel.AspNetCore   # optional, for the dashboard

Quick start

// Program.cs
builder.Services.AddAISentinel(opts =>
{
    opts.OnCritical = SentinelAction.Quarantine; // throw SentinelException
    opts.OnHigh     = SentinelAction.Alert;      // publish mediator notification
    opts.OnMedium   = SentinelAction.Log;
    opts.OnLow      = SentinelAction.Log;
});

builder.Services.AddChatClient(pipeline =>
    pipeline.UseAISentinel()
            .Use(new OpenAIChatClient(...)));

// optional dashboard
app.MapAISentinel("/ai-sentinel");

Catch quarantined messages:

try
{
    var response = await chatClient.GetResponseAsync(messages);
}
catch (SentinelException ex)
{
    // ex.PipelineResult has the full detection details
    logger.LogWarning("Blocked: {Severity}", ex.PipelineResult?.MaxSeverity);
}

Named pipelines

Register multiple isolated pipelines under string names; pick one per chat client at construction time. Useful for multi-LLM-endpoint apps and dev/staging/prod tier configurations.

// Default + two named variants
services.AddAISentinel(opts => opts.EmbeddingGenerator = realGen);
services.AddAISentinel("strict", opts =>
{
    opts.OnCritical = SentinelAction.Quarantine;
    opts.Configure(c => c.SeverityFloor = Severity.High);
});
services.AddAISentinel("lenient", opts =>
{
    opts.OnCritical = SentinelAction.Log;
    opts.Configure(c => c.Enabled = false);
});

// Pick one per chat client
services.AddChatClient("openai-strict", b =>
    b.UseAISentinel("strict").Use(new OpenAIChatClient(...)));
services.AddChatClient("openai-lenient", b =>
    b.UseAISentinel("lenient").Use(new OpenAIChatClient(...)));

Each named pipeline gets its own SentinelOptions, IDetectionPipeline, and InterventionEngine. The audit store, forwarders, and alert sink are shared — operational dashboards see all pipelines through one feed. User-added detectors via opts.AddDetector() register globally; per-pipeline detector tuning rides on opts.Configure(c => ...).

Each named pipeline starts from a fresh SentinelOptions() — no inheritance from the default. For shared base config, extract a helper:

Action baseCfg = opts => opts.EmbeddingGenerator = realGen;
services.AddAISentinel(baseCfg);
services.AddAISentinel("strict", opts => { baseCfg(opts); opts.OnCritical = SentinelAction.Quarantine; });

Phase A limitations (planned for Phase B when a real user need surfaces):

  • Always register the default unnamed AddAISentinel(...) first. The shared audit store, forwarders, alert sink, and tool-call guard are wired by the default call. Skipping it and registering only named pipelines causes the named chat client to throw a missing-shared-infrastructure error the first time it's resolved (during request handling).
  • Tool-call authorization is global, not per-name. opts.RequireToolPolicy(...) calls on named pipelines are silently ignored — only the default pipeline's bindings are consulted by IToolCallGuard. Configure tool policies on the default for now.
  • No request-time selector. The pipeline is fixed at chat-client construction time; multi-tenant routing where the tenant ID arrives with the request requires Phase B.

How it works

Every call to GetResponseAsync or GetStreamingResponseAsync runs two pipeline passes:

  1. Prompt scan — before the request reaches the LLM
  2. Response scan — after the LLM responds, before the result is returned

Each pass runs all enabled detectors in parallel (Task.WhenAll), aggregates a Threat Risk Score (0–100), and calls the Intervention Engine which takes the configured action for the highest severity found.

IChatClient.GetResponseAsync(messages)
  │
  ├─ [1] DetectionPipeline.RunAsync(prompt context)
  │       ├─ PromptInjectionDetector
  │       ├─ JailbreakDetector
  │       ├─ ... (28 more, parallel)
  │       └─ ThreatRiskScore + detections
  │
  ├─ InterventionEngine.Apply(result)   → Quarantine / Alert / Log / PassThrough
  ├─ AuditStore.AppendAsync(entry)
  │
  ├─ inner IChatClient.GetResponseAsync(messages)
  │
  ├─ [2] DetectionPipeline.RunAsync(response context)
  ├─ InterventionEngine.Apply(result)
  └─ AuditStore.AppendAsync(entry)

Detectors (55)

Detectors run in three modes:

  • Rule-based — fast regex or heuristic, always active, sub-microsecond per call
  • Semantic — uses embedding cosine similarity via EmbeddingGenerator. Language-agnostic. Active only with opts.EmbeddingGenerator configured.
  • LLM escalation — fires a second-pass LLM classifier (stub detectors, active only with opts.EscalationClient)

Security (31)

| ID | Detector | Type | Detects | |---|---|---|---| | SEC‑01 | PromptInjection | Rule-based | Override/injection phrase patterns (ignore all previous instructions, you are now a different AI, etc.) | | SEC‑02 | CredentialExposure | Rule-based | API keys, tokens, private keys, secrets in output | | SEC‑03 | ToolPoisoning | Rule-based | Suspicious tool-call manipulation patterns | | SEC‑04 | DataExfiltration | Rule-based | Base64 blobs, high-entropy encoded data | | SEC‑05 | Jailbreak | Rule-based | Jailbreak attempt phrases (DAN, roleplay exploits) | | SEC‑06 | PrivilegeEscalation | Rule-based | Role/permission escalation requests | | SEC‑07 | CovertChannel | Semantic | Encoding-based hidden payloads | | SEC‑08 | EntropyCovertChannel | LLM escalation | Statistical entropy anomalies in output | | SEC‑09 | IndirectInjection | Semantic | Injection via retrieved documents or tool results | | SEC‑10 | AgentImpersonation | Semantic | Model claiming to be a different agent or system | | SEC‑11 | MemoryCorruption | Semantic | Attempts to corrupt agent memory/context | | SEC‑12 | UnauthorizedAccess | Semantic | Attempts to access restricted resources | | SEC‑13 | ShadowServer | Semantic | Redirection to unauthorised endpoints | | SEC‑14 | InformationFlow | Semantic | Cross-context data leakage | | SEC‑15 | PhantomCitationSecurity | Semantic | Security-context hallucinated authority sources | | SEC‑16 | GovernanceGap | Semantic | Policy/compliance bypass attempts | | SEC‑17 | SupplyChainPoisoning | Semantic | Compromised dependency suggestions | | SEC‑18 | ToolDescriptionDivergence | Stub | Tool description changed at runtime vs. original declaration (requires tool-descriptor snapshot) | | SEC‑20 | SystemPromptLeakage | Rule-based | Verbatim fragments of the system prompt echoed in conversation history | | SEC‑23 | PiiLeakage | Rule-based | PII: SSN, credit card, IBAN, BSN, UK NINO, passport, DE tax ID, email+name, phone, DOB | | SEC‑24 | AdversarialUnicode | Rule-based | Zero-width spaces, homoglyphs, invisible characters used to smuggle hidden instructions | | SEC‑25 | CodeInjection | Rule-based | SQL injection, shell metacharacters, path traversal in LLM-generated code | | SEC‑26 | PromptTemplateLeakage | Rule-based | {{variable}}, `, [INST] and other prompt scaffolding markers | | SEC‑27 | LanguageSwitchAttack | Rule-based | Abrupt script/language switch mid-response — injection vector via non-Latin text | | SEC‑28 | RefusalBypass | Rule-based | Model complied with a request it should have refused (caller-supplied forbidden patterns) | | SEC‑19 | ToolCallFrequency | Rule-based | Counts ChatRole.Tool messages; flags sessions with excessive tool invocations | | SEC‑21 | ExcessiveAgency | Semantic | Detects autonomous-action language ("I deleted", "I deployed", "I executed") | | SEC‑22 | HumanTrustManipulation | Semantic | Spots rapport/authority manipulation ("you can trust me", "I am your advisor") | | SEC‑29 | OutputSchema | Rule-based | Response doesn't deserialize as the caller-supplied ExpectedResponseType (OWASP LLM05) | | SEC‑30 | ShorthandEmergence | Semantic | Counts unknown all-caps tokens that may signal emergent covert language | | SEC‑31` | VectorRetrievalPoisoning | Semantic | Detects malicious instructions embedded in RAG-retrieved document chunks (OWASP LLM08) |

Hallucination (9)

| ID | Detector | Type | Detects | |---|---|---|---| | HAL‑01 | PhantomCitation | Rule-based | Fake DOIs, arXiv IDs, .invalid/.nonexistent domains | | HAL‑02 | SelfConsistency | Rule-based | Numeric inconsistency (values differing by >10×) | | HAL‑03 | CrossAgentContradiction | Semantic | Contradictions between agents in a multi-agent session | | HAL‑04 | SourceGrounding | Semantic | Claims unsupported by provided context | | HAL‑05 | ConfidenceDecay | Semantic | Confidence degradation across turns | | HAL‑06 | StaleKnowledge | Semantic | Time-sensitive facts stated as current ("the latest version is X", "the current CEO is Y") | | HAL‑07 | IntraSessionContradiction | Semantic | Model contradicts itself within the same conversation | | HAL‑08 | GroundlessStatistic | Rule-based | Specific percentages or statistics asserted without any source in the provided context | | HAL‑09 | UncertaintyPropagation | Semantic | Flags hedged statements that contradict a definitive assertion in the same response |

Operational (15)

| ID | Detector | Type | Detects | |---|---|---|---| | OPS‑01 | BlankResponse | Rule-based | Empty or whitespace-only responses | | OPS‑02 | RepetitionLoop | Rule-based | Same sentence repeated 3+ times | | OPS‑03 | IncompleteCodeBlock | Rule-based | Unclosed code fences | | OPS‑04 | PlaceholderText | Rule-based | TODO, [INSERT HERE], Lorem ipsum leftovers | | OPS‑05 | ContextCollapse | Semantic | Loss of conversational context across turns | | OPS‑06 | AgentProbing | Semantic | Attempts to map agent capabilities or system prompt | | OPS‑07 | QueryIntent | Semantic | Malicious intent hidden in benign-looking queries | | OPS‑08 | ResponseCoherence | Semantic | Response that doesn't address the question asked | | OPS‑09 | TruncatedOutput | Rule-based | Detects mid-sentence truncation and unclosed code fences | | OPS‑10 | WaitingForContext | Semantic | Finds stall phrases when the user prompt was substantive | | OPS‑11 | UnboundedConsumption | Rule-based | Compares response length to prompt length; flags unbounded expansion | | OPS‑12 | SemanticRepetition | Semantic | Same idea restated with different wording — extends RepetitionLoop beyond literal string matching | | OPS‑13 | PersonaDrift | Semantic | Model's tone, persona, or stated identity shifts significantly across turns — context poisoning signal | | OPS‑14 | Sycophancy | Semantic | Model reverses a stated position purely because the user pushed back — epistemic cowardice | | OPS‑15 | WrongLanguage | Rule-based | Response language doesn't match the user's language (script/charset detection) |

> Semantic detectors are no-ops until opts.EmbeddingGenerator is configured. They use embedding cosine similarity and are language-agnostic — no LLM round-trip required.

> LLM escalation detectors are no-ops until opts.EscalationClient is configured. Set it to a cheap fast model (e.g. GPT-4o-mini) to activate them.

> Streaming: GetStreamingResponseAsync buffers the complete response before yielding tokens so the response scan can quarantine before any token reaches the application. Time-to-first-token equals full model response latency on this path.


OWASP LLM Top 10 (2025) Coverage

| OWASP | Threat | Detectors | |---|---|---| | LLM01 | Prompt Injection | PromptInjectionDetector, IndirectInjectionDetector, ToolPoisoningDetector | | LLM02 | Sensitive Info Disclosure | CredentialExposureDetector, PiiLeakageDetector, SystemPromptLeakageDetector, PromptTemplateLeakageDetector | | LLM03 | Supply Chain | SupplyChainPoisoningDetector | | LLM04 | Data & Model Poisoning | DataExfiltrationDetector, InformationFlowDetector | | LLM05 | Improper Output Handling | CodeInjectionDetector, OutputSchemaDetector | | LLM06 | Excessive Agency | ExcessiveAgencyDetector, ToolCallFrequencyDetector | | LLM07 | System Prompt Leakage | SystemPromptLeakageDetector, GovernanceGapDetector | | LLM08 | Vector & Embedding Weaknesses | VectorRetrievalPoisoningDetector | | LLM09 | Misinformation | PhantomCitationDetector, GroundlessStatisticDetector, StaleKnowledgeDetector, `UncertaintyPropagation

Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.