Table of Contents

MCP Server Integration

The WorkflowFramework.Extensions.Agents.Mcp package connects to Model Context Protocol servers, exposing their tools and resources as native IToolProvider and IContextSource instances.

Installation

dotnet add package WorkflowFramework.Extensions.Agents.Mcp

Key Types

Type Purpose
IMcpTransport Transport layer (stdio or HTTP)
StdioMcpTransport Communicates via stdin/stdout of a child process
HttpMcpTransport Communicates via streamable HTTP with SSE
McpClient Protocol client with typed methods
McpToolProvider Wraps McpClient as IToolProvider
McpResourceProvider Wraps McpClient as IContextSource
McpServerConfig Server connection configuration

IMcpTransport

Two built-in transports:

public interface IMcpTransport : IDisposable
{
    Task ConnectAsync(CancellationToken ct = default);
    Task DisconnectAsync(CancellationToken ct = default);
    Task SendAsync(McpJsonRpcMessage message, CancellationToken ct = default);
    Task<McpJsonRpcMessage> ReceiveAsync(CancellationToken ct = default);
}

StdioMcpTransport — Launches a child process and communicates via stdin/stdout (one JSON-RPC message per line):

var transport = new StdioMcpTransport(
    command: "npx",
    args: new[] { "-y", "@modelcontextprotocol/server-filesystem", "/home/user/docs" },
    env: new Dictionary<string, string> { ["NODE_ENV"] = "production" }
);

HttpMcpTransport — Posts JSON-RPC to an HTTP endpoint, parses SSE or plain JSON responses:

var transport = new HttpMcpTransport(
    url: "https://mcp.example.com/rpc",
    headers: new Dictionary<string, string> { ["Authorization"] = "Bearer token123" }
);

McpClient

The protocol client handles the MCP handshake (initialize → initialized) and provides typed methods:

using var client = new McpClient(transport, "filesystem");

// Connect and handshake
await client.ConnectAsync();

// List available tools
IReadOnlyList<McpToolInfo> tools = await client.ListToolsAsync();
foreach (var tool in tools)
{
    Console.WriteLine($"{tool.Name}: {tool.Description}");
    // tool.InputSchema contains the JSON Schema string
}

// Call a tool
McpToolCallResult result = await client.CallToolAsync(
    "read_file",
    """{"path": "/home/user/docs/readme.md"}"""
);
Console.WriteLine(result.Content);

// List and read resources
IReadOnlyList<McpResourceInfo> resources = await client.ListResourcesAsync();
McpResourceContent content = await client.ReadResourceAsync(resources[0].Uri);
Console.WriteLine(content.Text);

// Disconnect
await client.DisconnectAsync();

McpToolProvider

Wraps an McpClient as an IToolProvider so MCP tools appear in the ToolRegistry:

var transport = new StdioMcpTransport("npx", new[] { "-y", "@mcp/server-github" });
var client = new McpClient(transport, "github");
await client.ConnectAsync();

var toolProvider = new McpToolProvider(client);
registry.Register(toolProvider);

// Now "github" tools are available in agent loops
var tools = await registry.ListAllToolsAsync();
// Each tool's Metadata["source"] = "mcp:github"

McpResourceProvider

Wraps an McpClient as an IContextSource for resources:

var resourceProvider = new McpResourceProvider(client);

// Use as context source in agent loops
var workflow = new WorkflowBuilder()
    .AgentLoop(provider, registry, options =>
    {
        options.ContextSources.Add(resourceProvider);
    })
    .Build();

Resources are fetched and injected into the agent prompt as ContextDocument objects.

McpServerConfig

Declarative configuration for MCP servers:

// Stdio server
var stdioConfig = new McpServerConfig
{
    Name = "filesystem",
    Transport = "stdio",
    Command = "npx",
    Args = new[] { "-y", "@modelcontextprotocol/server-filesystem", "/data" },
    Env = new Dictionary<string, string> { ["HOME"] = "/home/user" }
};

// HTTP server
var httpConfig = new McpServerConfig
{
    Name = "remote-api",
    Transport = "http",
    Url = "https://mcp.example.com/rpc",
    Headers = new Dictionary<string, string> { ["Authorization"] = "Bearer secret" }
};

DI Registration

Register MCP servers via dependency injection with AddMcpServer():

services.AddMcpServer(new McpServerConfig
{
    Name = "filesystem",
    Transport = "stdio",
    Command = "npx",
    Args = new[] { "-y", "@modelcontextprotocol/server-filesystem", "/data" }
});

services.AddAgentTooling(); // Auto-discovers MCP tool providers

AddMcpServer() registers both an IToolProvider and an IContextSource for the server.

Builder Extensions

// Call a specific MCP tool in a workflow step
var workflow = new WorkflowBuilder()
    .CallMcpTool("filesystem", "read_file", """{"path":"{FilePath}"}""", registry)
    .Build();

Complete Example

// 1. Configure MCP servers
var transport = new StdioMcpTransport(
    "npx", new[] { "-y", "@modelcontextprotocol/server-filesystem", "/workspace" });
using var client = new McpClient(transport, "filesystem");
await client.ConnectAsync();

// 2. Set up registry with MCP + local tools
var registry = new ToolRegistry();
registry.Register(new McpToolProvider(client));
registry.Register(new CustomToolProvider());

// 3. Build agent workflow with MCP resources as context
var workflow = new WorkflowBuilder()
    .AgentLoop(provider, registry, options =>
    {
        options.SystemPrompt = "You have access to the filesystem. Help the user manage their files.";
        options.ContextSources.Add(new McpResourceProvider(client));
        options.MaxIterations = 20;
    })
    .Build();

await workflow.RunAsync(context);