{"slug": "building-enterprise-multi-agent-workflows-in-net-with-mistral", "title": "building enterprise multi-agent workflows in .net with mistral", "summary": "A developer built Mistral.Agents.Net, an open-source .NET library that wraps Mistral's Agents API to enable enterprise multi-agent workflows. The library supports persistent agents, stateful conversations, tool integrations, and agent handoffs, filling a gap in existing .NET SDKs that only cover chat completions and embeddings.", "body_md": "most people know [Mistral](https://mistral.ai) for its chat models. the part i find more interesting for enterprise work is the [Agents API](https://docs.mistral.ai/studio-api/agents/introduction): persistent agents with instructions and tools, stateful conversations you can resume, built-in connectors (web search, code interpreter, document library), and handoffs so one agent can delegate to another.\n\nthe .net story stops short of this. the community sdks (tghamm's is genuinely good) cover chat completions, embeddings and function calling. they don't cover the agentic layer. so if you're a .net shop that wants to build a multi-agent workflow on mistral, you're writing raw http. i didn't want to, so i built [Mistral.Agents.Net](https://github.com/ivanjurina/mistral-agents-dotnet). here's the design and the one wire-format detail that cost me a debugging session.\n\na chat completion is stateless: you send messages, you get a reply, you manage all the history yourself. an agent is a stored object with instructions and tools, and a conversation is a stateful thread you can continue by id. that difference matters for enterprise workflows, where a \"session\" spans many turns and you want the platform to hold the state.\n\n``` js\nvar agent = await client.CreateAgentAsync(new CreateAgentRequest\n{\n    Model = \"mistral-medium-latest\",\n    Name = \"Financial Analyst\",\n    Instructions = \"Use the code interpreter for math and web search for current facts.\",\n    Tools = { AgentTool.CodeInterpreter(), AgentTool.WebSearch() },\n});\n\nusing var turn = await client.StartConversationAsync(new StartConversationRequest\n{\n    AgentId = agent.Id,\n    Inputs = \"what was 15% of last quarter's revenue if it was 12.4M?\",\n});\nConsole.WriteLine(turn.OutputText);\n```\n\nthe response isn't a single message. it's a list of outputs: tool executions, message chunks, function calls, handoffs. the library gives you `OutputText`\n\nfor the common case and `Outputs`\n\nfor the raw stream, plus a `Root`\n\nJsonElement escape hatch for anything the typed model doesn't cover yet. same philosophy i used for the serpapi and elevenlabs clients: typed where it helps, raw where you need it.\n\nthe enterprise-interesting feature is handoffs. you give an agent the ids of other agents it may delegate to, and the platform routes work between them.\n\n``` js\nvar researcher = await client.CreateAgentAsync(new CreateAgentRequest\n{\n    Model = \"mistral-medium-latest\",\n    Name = \"Research Agent\",\n    Tools = { AgentTool.WebSearch() },\n});\n\nvar analyst = await client.CreateAgentAsync(new CreateAgentRequest\n{\n    Model = \"mistral-medium-latest\",\n    Name = \"Financial Analyst\",\n    Handoffs = new List<string> { researcher.Id! }, // delegate research\n    Tools = { AgentTool.CodeInterpreter() },\n});\n```\n\nask the analyst a question that needs current market data and it hands off to the researcher, which uses web search, and the answer comes back through the analyst. you orchestrate multiple specialized agents without writing the routing yourself.\n\nbuilt-in connectors are great, but enterprise value is in your private data. a function tool exposes your c# to the agent: it decides when to call, you run it, you return the result.\n\n```\nrequest.Tools.Add(AgentTool.FunctionTool(FunctionDefinition.FromJsonSchema(\n    \"get_internal_metric\", \"Returns a private company metric.\",\n    \"\"\"{\"type\":\"object\",\"properties\":{\"name\":{\"type\":\"string\"}}}\"\"\")));\n```\n\nthen, when the agent calls it:\n\n``` js\nforeach (var call in turn.FunctionCalls)\n{\n    using var args = call.ParseArguments();\n    var result = LookUp(call.FunctionName!, args.RootElement);\n    using var next = await client.SubmitToolResultAsync(turn.ConversationId!, call.ToolCallId!, result);\n}\n```\n\ni tested this end to end: asked \"what is 15% of our q3 revenue?\", watched the agent call `get_internal_metric`\n\n, feed the private number back, and compute 15% with the code interpreter. the whole loop, from .net.\n\nhere's the debugging session. the docs show function-call arguments as a json object. the live api returns them as a json **string** — a stringified object you have to parse. my first live run threw `element has type 'String'`\n\nthe instant the agent called a function, because i was calling `GetProperty`\n\non what i thought was an object.\n\nthis is a common llm-api quirk (openai does the same), but it's exactly the kind of thing you only learn by running against the real service, not by reading the reference. so the library handles it for you:\n\n``` js\npublic JsonDocument ParseArguments()\n{\n    var v = Arguments;\n    return v.ValueKind switch\n    {\n        JsonValueKind.String => JsonDocument.Parse(string.IsNullOrEmpty(v.GetString()) ? \"{}\" : v.GetString()!),\n        JsonValueKind.Object or JsonValueKind.Array => JsonDocument.Parse(v.GetRawText()),\n        _ => JsonDocument.Parse(\"{}\"),\n    };\n}\n```\n\n`ParseArguments()`\n\nnormalizes both forms, so your code never sees the difference. every wrinkle like this that the library absorbs is a wrinkle your users don't hit.\n\nagents, conversations, connectors, handoffs and function tools are all live-verified against the real api — the revenue demo above actually runs. the library is net8.0, zero dependencies, async-first with cancellation, typed requests with a raw escape hatch, and an offline test suite that replays captured api frames (including the string-arguments case) so ci needs no key or credits.\n\nit's on nuget as `Mistral.Agents.Net`\n\nand the source is on my github. .net is a large enterprise audience for agentic ai and right now it has no official path to mistral's agents platform. if you're at mistral and reading this: happy to help close that gap properly.\n\n*originally published at ivanjurina.com*", "url": "https://wpnews.pro/news/building-enterprise-multi-agent-workflows-in-net-with-mistral", "canonical_source": "https://dev.to/ivan_jurina_708793b01312e/building-enterprise-multi-agent-workflows-in-net-with-mistral-1ik", "published_at": "2026-07-22 12:54:01+00:00", "updated_at": "2026-07-22 13:00:27.837824+00:00", "lang": "en", "topics": ["artificial-intelligence", "developer-tools", "ai-agents", "large-language-models"], "entities": ["Mistral", "Mistral.Agents.Net", "Ivan Jurina"], "alternates": {"html": "https://wpnews.pro/news/building-enterprise-multi-agent-workflows-in-net-with-mistral", "markdown": "https://wpnews.pro/news/building-enterprise-multi-agent-workflows-in-net-with-mistral.md", "text": "https://wpnews.pro/news/building-enterprise-multi-agent-workflows-in-net-with-mistral.txt", "jsonld": "https://wpnews.pro/news/building-enterprise-multi-agent-workflows-in-net-with-mistral.jsonld"}}