Skip to content

feat(mcp): expose Elsa workflows as MCP tools - #209

Draft
RalfvandenBurg wants to merge 2 commits into
elsa-workflows:mainfrom
RalfvandenBurg:feature/mcp-server
Draft

RalfvandenBurg wants to merge 2 commits into
elsa-workflows:mainfrom
RalfvandenBurg:feature/mcp-server

Conversation

@RalfvandenBurg

@RalfvandenBurg RalfvandenBurg commented Sep 18, 2026

Copy link
Copy Markdown

Closes #210.

Related to #58, but the other direction: that issue is about Elsa agents consuming MCP tools from elsewhere, this is about Elsa exposing its own workflows as tools to any MCP client.

What this adds

Two things a workflow engine needs before an agent can drive it, in four packages.

Elsa.Mcp.Server + Elsa.Mcp.Abstractions

Serves workflows as Model Context Protocol tools over the MCP HTTP transport.

  • A workflow opts in through custom properties (mcp:enabled), and the catalog turns every opted-in definition into a tool an agent can list and call.
  • A tool is named after the workflow, not its definition id. A workflow drawn in Studio is named after a generated hex string, so an agent picking by id picks blind — a request to review a pull request starts the work item assistant instead, with no error to show for it. Names are slugified to ^[a-zA-Z0-9_-]{1,64}$, accents folded rather than dropped, and a clash is disambiguated by appending the id.
  • mcp:instructions and mcp:input:<name> let an author write guidance a model needs and a person reading the designer does not.
  • A suspended instance can be resumed through the same tool, by answering what it is waiting on.
  • Elsa.Mcp.Abstractions holds those names and rules on their own, with no dependencies, so an editor that lets an author configure a workflow as a tool does not have to take the server with it.

Elsa.Bookmarks.Ui + Elsa.Bookmarks.Ui.Domain

Describes what a suspended workflow is waiting for, which is what the resume path needs to do anything better than pass raw JSON through.

  • Each feature registers an IBookmarkUiProvider for its own bookmark types; the provider says what to show, what to ask before resuming, and whether the bookmark is answerable at all. Providers for Delay, Event and RunTask ship with it, plus a fallback.
  • A bookmark waiting in a trigger is described as nothing on purpose: a workflow waiting for another system to act is not a task anyone can answer, and the MCP server refuses to resume it rather than firing it early.
  • Elsa.Bookmarks.Ui.Domain carries the models and contracts free of any Elsa dependency, so a client that only reads a described bookmark — a Blazor host, a mobile app — can take that package without the engine. DomainPackageTests pins that split; one stray using silently undoes it and nothing else would notice.

Layout

src/modules/mcp/Elsa.Mcp.Abstractions
src/modules/mcp/Elsa.Mcp.Server
src/modules/bookmarks/Elsa.Bookmarks.Ui
src/modules/bookmarks/Elsa.Bookmarks.Ui.Domain
test/modules/mcp/Elsa.Mcp.Abstractions.UnitTests
test/modules/mcp/Elsa.Mcp.Server.UnitTests
test/modules/bookmarks/Elsa.Bookmarks.Ui.UnitTests

One new central package version: ModelContextProtocol.AspNetCore 1.3.0. Everything else resolves through the existing $(ElsaVersion) entries.

On scope

This is arguably two concerns. Elsa.Bookmarks.Ui stands on its own and I am happy to split it into its own PR first and rebase this one on top — Elsa.Mcp.Server does not compile without it, which is why the two travel together here. Say the word.

Verification

All three test projects build and pass on net10.0:

Project Tests
Elsa.Mcp.Abstractions.UnitTests 28 passed
Elsa.Bookmarks.Ui.UnitTests 35 passed
Elsa.Mcp.Server.UnitTests 78 passed

The four source modules build clean across net8.0, net9.0 and net10.0 with no new compiler warnings.

One thing to look at

Beyond the seven new projects, dotnet sln add dropped 48 configuration lines from Elsa.Extensions.sln belonging to four GUIDs that have no Project entry in the solution. They were already orphaned, so nothing that builds today loses a configuration — but it is churn in this diff that is not mine, and I can restore it if you would rather keep the solution byte-identical apart from the additions.

🤖 Generated with Claude Code

Adds Elsa.Mcp.Server, which serves a workflow as a Model Context Protocol
tool: the catalog turns every opted-in definition into a tool an agent can
list and call, and a suspended instance can be resumed through the same
tool by answering what it is waiting on.

A workflow opts in through custom properties (mcp:enabled), and names
itself through its own name rather than its definition id - an agent
picking a tool by a generated hex string picks blind. Elsa.Mcp.Abstractions
holds those names and rules on their own, so an editor that lets an author
configure a workflow as a tool does not have to take the server with it.

Resuming needs to know what a bookmark is waiting for, which is what
Elsa.Bookmarks.Ui adds: each feature registers a provider for its own
bookmark types, and the provider says what to show, what to ask before
resuming, and whether the bookmark is answerable at all. Providers for
Delay, Event and RunTask ship with it, plus a fallback. Elsa.Bookmarks.Ui.Domain
carries the models and contracts free of any Elsa dependency, so a client
that only reads a described bookmark - a Blazor host, a mobile app - can
take that package without the engine. A unit test pins that split.

Elsa.Bookmarks.Ui is useful on its own and can be split off into its own
pull request if that reads better; Elsa.Mcp.Server does not compile without
it, which is why it travels here.

Note on Elsa.Extensions.sln: beyond the seven new projects, `dotnet sln add`
dropped 48 configuration lines belonging to four GUIDs that have no Project
entry in the solution. Those were already orphaned; nothing that builds today
loses a configuration.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Model Context Protocol (MCP) server integration

2 participants