Skip to main content

McpStdioClient

Communicates with an MCP server running as a local process via its stdin/stdout. Use this for the npm-distributed MCP servers (@modelcontextprotocol/server-filesystem, …server-github, etc.), Python-based servers, or your own CLI.

public sealed class McpStdioClient : IMcpClient
{
public McpStdioClient(string command, params string[] args);
public Task ConnectAsync(CancellationToken ct = default);
public Task<IList<McpToolInfo>> GetToolsAsync(CancellationToken ct = default);
public Task<string> CallToolAsync(string toolName, string argsJson, CancellationToken ct = default);
public McpServerInfo? ServerInfo { get; }
}

Example — file-system server

using LogicGrid.Core.Agents;
using LogicGrid.Core.Tools;
using LogicGrid.Mcp;

using var mcp = new McpStdioClient(
"npx",
"-y", "@modelcontextprotocol/server-filesystem", "/repo");

await mcp.ConnectAsync();

IList<ToolBase> tools = await McpToolAdapter.GetAllAsync(mcp);

IAgent fileBot = new Agent<string>(
"FileBot",
"Reads and edits files in the repo.",
"Use the file tools to answer the user's question. " +
"Never write outside /repo.",
llm,
tools: tools);

Console.WriteLine(
await fileBot.RunAsync("List the top-level directories in /repo.", new AgentContext()));

Example — Python server

using var mcp = new McpStdioClient(
"python",
"-m", "my_company.mcp_server", "--config", "config.toml");

await mcp.ConnectAsync();

Lifecycle

The constructor only stores the command and args. ConnectAsync starts the process and performs the MCP handshake. Dispose (or the end of the using block) terminates the child process.

For long-running services, connect once at startup and reuse the client across requests:

// startup
var mcp = new McpStdioClient("npx", "-y",
"@modelcontextprotocol/server-github", "--token", token);
await mcp.ConnectAsync();
var tools = await McpToolAdapter.GetAllAsync(mcp);

// per request — reuse `tools`
IAgent agent = new Agent<string>("…", "…", "…", llm, tools: tools);

// shutdown
mcp.Dispose();

Startup cost

Spawning npx typically takes 200–500 ms on a warm machine. Not a problem for a long-running service, noticeable for a one-shot CLI. For CLI tools, prefer McpHttpClient against an already-running daemon.

Common pitfalls

  • Forgetting -y on npx. Without it, npx may prompt for install confirmation and the handshake will time out.
  • Wrong working directory. The child inherits the parent's CWD. If your MCP server expects relative paths, set them in your args.
  • Spawning the server inside an agent's RunAsync. Don't — connect once, share the tool list. Per-request spawning kills performance.