← Discover MCPs and Agents
s
MCPAI & MLGitHub

skunit

skUnit is a testing tool for AI units, such as IChatClient, MCP Servers and agents.

Links

README

From the repo.

skUnit

Build and Deploy NuGet version (skUnit) NuGet downloads Ask DeepWiki

skUnit is a semantic testing framework for .NET — write AI tests in plain Markdown, run them with xUnit, NUnit, or MSTest.

Test anything that talks to AI:

  • AI Agents (Microsoft Agent Framework, A2A)
  • IChatClient (Microsoft Extensions AI)
  • MCP servers (Model Context Protocol)
  • Custom AI integrations
dotnet add package skUnit

Quick Start

Imagine you have an AIAgent configured to answer questions about a bank account. Here's a test scenario in Markdown, balance-test.md:

# SCENARIO Balance Test

## [USER]
What is the account balance for John Doe?

## [ASSISTANT]

### ASSERT SemanticCondition
The answer mentions that the account balance is $1,234.56.

And the C# test:

[Fact]
public async Task SimpleTest()
{
    var markdown = File.ReadAllText("balance-test.md");
    var scenario = ChatScenario.Parse(markdown);

    await agent.RunAsync(scenario);
}

That's it. skUnit drives the conversation, calls your AI, and semantically verifies the response.

Tip — Parameterized scenarios: Use {placeholders} in your Markdown and replace them with C# string manipulation to run the same scenario with different inputs.

## [USER]
What is the account balance for {CustomerName}?

## [ASSISTANT]

### ASSERT SemanticCondition
The answer mentions that the account balance is {CustomerBalance}.

Key Features

1. Function Call Assertion

Verify that your agent calls the right tool with the right arguments:

# SCENARIO Balance Test

## [USER]
What is the account balance for John Doe?

## [ASSISTANT]

### ASSERT SemanticCondition
The answer mentions that the account balance is $1,234.56.

### ASSERT FunctionCall
{
  "function_name": "get-account-balance"
}

You can also assert on the arguments passed to the function, using exact or semantic matching:

{
  "function_name": "get-account-balance",
  "arguments": {
    "accountHolder": ["Equals", "John Doe"]
  }
}
{
  "function_name": "get-account-balance",
  "arguments": {
    "accountHolder": ["SemanticSimilar", "John Doe"]
  }
}

See ASSERT JsonCheck for full JSON schema validation.


2. Multi-Turn Conversations

Test realistic back-and-forth conversations — each turn gets its own assertions:

# SCENARIO Multi-Turn Balance Test

## [USER]
What is the account balance for John Doe?

## [ASSISTANT]
The account balance for John Doe is $1,234.56.

### ASSERT SemanticCondition
The answer mentions that the account balance is $1,234.56.

## [USER]
What about his credit score?

## [ASSISTANT]
His credit score is 750.

### ASSERT SemanticCondition
It mentions that the credit score is 750.

skUnit Chat Scenario Structure

Each [ASSISTANT] turn can carry multiple ASSERT statements to verify different aspects of the response independently.


3. MCP Server Testing

Test Model Context Protocol servers end-to-end — same Markdown format, no extra APIs to learn:

# SCENARIO MCP Time Server

## [USER]
What time is it?

## [ASSISTANT]
It's currently 2:30 PM PST

### ASSERT FunctionCall
{
  "function_name": "current_time"
}

### ASSERT SemanticCondition
It mentions a specific time

Wire up your MCP server in the test setup:

var mcp = await McpClientFactory.CreateAsync(clientTransport);
var tools = await mcp.ListToolsAsync();

var baseChatClient = // your IChatClient (Azure OpenAI, OpenAI, etc.)

var agent = baseChatClient.AsAgent(
    instructions: "You are a helpful assistant that can answer questions and call tools.",
    tools: tools.ToArray());

await agent.ExecuteScenarioAsync(scenario, assertionClient: baseChatClient);
// assertionClient = the AI that evaluates responses semantically
// agent           = the system under test that actually calls tools

Need to test multiple MCP servers together? Merge their tool lists before creating the agent — see MCP Testing Guide.


4. Hallucination Mitigation with ScenarioRunOptions

LLM outputs vary between runs. Don't let a single flaky response break your build.

Run each scenario multiple times and require a minimum number of passes:

var options = new ScenarioRunOptions
{
    TotalRuns = 3,           // Run the scenario 3 times
    RequiredSuccessRuns = 2  // At least 2 must pass
};

await agent.ExecuteScenarioAsync(scenario, assertionClient: baseChatClient, options: options);

Recommended thresholds:

Scenario typeTotalRunsRequiredSuccessRuns
Deterministic / low-temp11
Function / tool invocation32
Creative generation53
Critical CI gating54

When a scenario falls below the threshold, you get a clear failure message:

Only 2 of 5 runs passed, which is below the required success runs of 4.

See Scenario Run Options for the full guide.


Setup

public class BankAccountTests
{
    private readonly ChatScenarioRunner ScenarioRunner;
    private readonly IChatClient systemUnderTestClient;

    public BankAccountTests(ITestOutputHelper output)  // xUnit
    {
        var assertionClient = new AzureOpenAIClient(endpoint, credential)
            .GetChatClient(deploymentName)
            .AsIChatClient();

        ScenarioRunner = new ChatScenarioRunner(assertionClient, output.WriteLine);
    }

    [Fact]
    public async Task TestBalance()
    {
        var agentUnderTest = /* the client/agent you are testing */
        var scenarios = ChatScenario.Parse(File.ReadAllText("balance-test.md"));
        await ScenarioRunner.RunAsync(scenarios, systemUnderTestClient);
    }
}

Two clients, two roles:

  • assertionClient — evaluates semantic assertions (SemanticCondition, SemanticSimilar, etc.)
  • agentUnderTest — the agent/client whose behavior you are testing

MSTest / NUnit: Replace ITestOutputHelper.WriteLine with TestContext.WriteLine. Everything else stays the same. See Test Framework Integration.


Documentation

DocDescription
Chat Scenario SpecComplete guide to writing scenarios
ASSERT Statement SpecAll available assertion types
Test Framework IntegrationxUnit, MSTest, NUnit setup
MCP Testing GuideTesting MCP servers
Multi-Modal SupportImages and other media
Scenario Run OptionsMulti-run hallucination mitigation

Examples

See the /demos folder for complete, runnable projects:

  • Moody Chef AI: A step-by-step tutorial project that shows how to build and test an AI agent using skUnit.

Requirements

  • .NET 8.0 or higher
  • An AI provider (Azure OpenAI, OpenAI, Anthropic, ...) for semantic assertions
  • A test framework of your choice — xUnit, NUnit, or MSTest

Contributing

Contributions are welcome! Check open issues or submit a PR.

Collected info

  • 179 stars
  • 21 forks
  • Language: C#
  • Source updated: 7/30/2026

Config for your environment

Replace {MCP_ENDPOINT_URL} with this MCP’s endpoint URL (from its repo or docs above). No API key — you connect directly.

Tool

OS

Config file: ~/.cursor/mcp.json

{
  "mcpServers": {
    "mcp-server": {
      "url": "{MCP_ENDPOINT_URL}"
    }
  }
}

Paste into mcpServers in the config file. Restart Cursor after saving.

If this MCP is also published on mcpchannel.ai, you can subscribe from Browse and use the gateway config there instead.