Setup and Configuration
Prerequisites
Before running Flowon Logic Conductor, ensure the following are in place:
| Requirement | Details |
|---|---|
| .NET Runtime | .NET 10 or later (the server targets net10.0) |
| Logic Composer | Installed and licensed in the target Dataverse environment |
| Action artifacts | At least one Logic Flow or Logic Block marked as an action in the projects you want to expose |
| Dataverse Connection | A Dataverse connection string or service-principal credentials for the target environment |
| AI Client | An MCP-compatible AI assistant (Claude, GitHub Copilot, etc.) |
| Microsoft Entra app | Required only for Secure HTTP mode (JWT Bearer / On-Behalf-Of auth) |
Choosing Which Projects to Expose
The one setting that is specific to Flowon Logic Conductor is FLOWON_PROJECTS - the list of Flowon projects whose action artifacts should be turned into tools.
| Variable | Purpose |
|---|---|
FLOWON_PROJECTS | The Flowon project(s) to expose. Every action Logic Flow and Logic Block in these projects becomes an MCP tool. |
- With a single project, tool names are the action name (e.g.
approve_credit_limit). - With multiple projects, tool names are prefixed with the project name (e.g.
sales_approve_credit_limit) to keep them unique.
Scope this narrowly - expose only the projects whose logic you intend an agent to be able to run. See How Tool Generation Works for naming and type-mapping details.
Restart to refresh: Tools are generated at startup. After changing which projects are exposed - or changing the action artifacts inside them - restart the server so the tool catalogue is regenerated.
Host Modes
Flowon Logic Conductor runs in one of three host modes, selected by the --transport argument and the incoming-auth flag.
| Mode | When to use | Transport | Incoming auth |
|---|---|---|---|
| Stdio (default) | Local desktop MCP clients | Standard I/O streams | None (local process) |
| HTTP | Dev / testing only | POST /mcp | Disabled (--disable-http-incoming-auth); CORS restricted to localhost |
| Secure HTTP | Production / team-shared | POST /mcp | JWT Bearer (Microsoft Entra ID), with On-Behalf-Of user impersonation |
The HostBuilderFactory selects the mode automatically: --transport Http + --disable-http-incoming-auth → HTTP; --transport Http (without that flag) → Secure HTTP; otherwise → Stdio.
Running the Server
Configuration has two independent axes, plus the Conductor-specific FLOWON_PROJECTS:
- Transport + incoming auth — the host mode above (
--transport,--disable-http-incoming-auth). - Dataverse (outgoing) auth — how the server authenticates to Dataverse, set with
--dataverse-auth-strategy. Connection details are supplied through environment variables, chosen by the strategy. There is no connection-string argument.
CLI Arguments
| Argument | Values | Default |
|---|---|---|
--transport | Stdio, Http | Stdio |
--dataverse-auth-strategy | UseHostingEnvironmentIdentity, UseOnBehalfOf, UseClientCredentials, UseUserCredentials | auto |
--disable-http-incoming-auth | flag | false |
Log level is set with the LOG_LEVEL environment variable (Trace … Error, default Information).
Dataverse Authentication Strategies
| Strategy | How the server authenticates to Dataverse | Required environment variables |
|---|---|---|
UseHostingEnvironmentIdentity | The host's managed/ambient identity (like DefaultAzureCredential). Ideal for cloud/managed-identity hosting. | DATAVERSE_URL |
UseOnBehalfOf | Exchanges the incoming user's token for a Dataverse token — each Logic Flow/Block runs as the calling user. HTTP modes only. | DATAVERSE_URL + AzureAd__* |
UseClientCredentials | App-only, using a client id + secret (service principal). | DATAVERSE_URL, DATAVERSE_CLIENT_ID, DATAVERSE_CLIENT_SECRET |
UseUserCredentials | Username + password (on-prem Dynamics). | DYNAMICS_URL, DYNAMICS_APP_ID, DYNAMICS_USER_NAME, DYNAMICS_PASSWORD |
Environment Variables
| Variable | Purpose |
|---|---|
FLOWON_PROJECTS | The Flowon project(s) whose action artifacts become tools |
DATAVERSE_URL | Dataverse environment URL (client-credentials, OBO, and hosting-identity strategies) |
DATAVERSE_CLIENT_ID / DATAVERSE_CLIENT_SECRET | Service-principal credentials (UseClientCredentials) |
DYNAMICS_URL / DYNAMICS_APP_ID / DYNAMICS_USER_NAME / DYNAMICS_PASSWORD | On-prem Dynamics credentials (UseUserCredentials) |
AzureAd__ClientId, AzureAd__TenantId, AzureAd__Instance, AzureAd__ClientCredentials__0__SourceType, AzureAd__ClientCredentials__0__ClientSecret | Microsoft Entra app registration — required for incoming JWT auth (Secure HTTP) and the OBO token exchange |
AZURE_CLOUD | Azure sovereign-cloud override |
LOG_LEVEL | Trace, Debug, Information, Warning, Error (default Information) |
ASPNETCORE_URLS | Bind address/port, e.g. http://localhost:1033 |
FLOWON_MCP_COLLECT_TELEMETRY | Set true to emit OpenTelemetry traces/metrics |
FLOWON_MCP_DANGEROUSLY_DISABLE_HTTPS_REDIRECTION | Set true to skip HTTPS redirection (useful behind a reverse proxy) |
ALLOW_INSECURE_EXTERNAL_BINDING | Set true to allow wildcard/external binding in dev HTTP mode |
On-Behalf-Of needs its own app registration. For
UseOnBehalfOf, theAzureAdapp must expose the MCP API scope and be consented for the on-behalf-of exchange — typically a different registration than the one used for plain client-credentials runs.⚠️ Never commit real secrets. The values below are placeholders — supply them through your environment, a secret store, or container secrets.
Examples
# HTTP, unauthenticated incoming, app-only Dataverse auth (local dev)
FLOWON_PROJECTS="Sales" \
DATAVERSE_URL="https://yourorg.crm.dynamics.com/" \
DATAVERSE_CLIENT_ID="<app-id>" \
DATAVERSE_CLIENT_SECRET="<secret>" \
ASPNETCORE_URLS="http://localhost:1033" \
dotnet run -- --transport http --disable-http-incoming-auth --dataverse-auth-strategy UseClientCredentials
# Secure HTTP with On-Behalf-Of (each tool call runs as the calling user)
FLOWON_PROJECTS="Sales,Operations" \
DATAVERSE_URL="https://yourorg.crm.dynamics.com/" \
AzureAd__Instance="https://login.microsoftonline.com/" \
AzureAd__TenantId="<tenant-id>" \
AzureAd__ClientId="<obo-app-id>" \
AzureAd__ClientCredentials__0__SourceType="ClientSecret" \
AzureAd__ClientCredentials__0__ClientSecret="<secret>" \
dotnet run -- --transport http --dataverse-auth-strategy UseOnBehalfOf
Endpoints (HTTP modes)
| Endpoint | Description |
|---|---|
POST /mcp | The MCP endpoint. In Secure HTTP mode it requires the McpAccess authorization policy. |
GET /health | Anonymous health check (Secure HTTP mode only). |
GET /.well-known/oauth-protected-resource | OAuth Protected Resource metadata (RFC 9728) describing the resource, its authorization servers, and supported scopes ({clientId}/Mcp.Tools.ReadWrite). MCP clients use this for discovery. |
In Secure HTTP mode, every tool call - i.e. every Logic Flow run or Logic Block evaluation - executes under the calling user's Dynamics identity, resolved from the incoming JWT via the Microsoft Entra On-Behalf-Of (OBO) flow. Dataverse row-level security, business-unit scoping, and team membership apply to every call.
On-Behalf-Of (OBO)
UseOnBehalfOf is the recommended mode for shared, production deployments. Instead of a single service account, the server runs each generated tool as the calling user:
- The MCP client calls with the user's access token (
Authorization: Bearer …). - The server validates the Entra JWT and enforces the
McpAccesspolicy — the token must carry the delegated scopeMcp.Tools.ReadWrite(or, for app-only callers, the app permissionMcp.Tools.ReadWrite.All). - The server performs the On-Behalf-Of exchange to obtain a downstream Dataverse token for that same user (the token's tenant must match the user's
tidclaim). - The resulting Logic Flow run / Logic Block evaluation executes with the user's own identity — so a tool can only read and write what that user is permitted to, with row-level security, business-unit scoping, and team membership all applied.
App registration requirements
The AzureAd app used for OBO must be:
- A confidential client (has a client secret — supplied via
AzureAd__ClientCredentials__0__*). - Configured to expose an API scope
Mcp.Tools.ReadWrite(delegated) and/or an app roleMcp.Tools.ReadWrite.All(app-only). - Granted delegated permission to Dataverse (
user_impersonation) and admin-consented, so the OBO exchange can succeed.
Wire it up with AzureAd__ClientId, AzureAd__TenantId, AzureAd__Instance, and AzureAd__ClientCredentials__0__SourceType / AzureAd__ClientCredentials__0__ClientSecret. This is typically a different registration than one used for a plain client-credentials run.
Client discovery
MCP clients can discover the authorization server and scope from GET /.well-known/oauth-protected-resource, which advertises the authorization server ({instance}/{tenantId}/v2.0) and the supported scope ({clientId}/Mcp.Tools.ReadWrite). The WWW-Authenticate challenge on an unauthenticated request points clients here.
Running with Docker
Flowon Logic Conductor ships with a Dockerfile (Alpine-based) for containerised deployments:
# Build a publish image
dotnet publish -c Release
# Or build the Docker image directly, then run it
docker build -t flowon-logic-conductor .
docker run \
-e FLOWON_PROJECTS="Sales" \
-e DATAVERSE_URL="https://yourorg.crm.dynamics.com/" \
-e DATAVERSE_CLIENT_ID="<app-id>" \
-e DATAVERSE_CLIENT_SECRET="<secret>" \
-p 5260:5260 flowon-logic-conductor \
--transport http --disable-http-incoming-auth --dataverse-auth-strategy UseClientCredentials
The container listens on port 5260 by default (override with ASPNETCORE_URLS). Pass whichever environment variables match your chosen strategy — e.g. the AzureAd__* set for UseOnBehalfOf.
Connecting to Your AI Client
Once the server is running, point your AI client's MCP configuration at it.
Stdio Mode
The client launches the server process:
{
"mcpServers": {
"flowon-logic-conductor": {
"command": "dotnet",
"args": [
"run",
"--project", "FlowOn.Xrm.Conductor.Mcp.Server.csproj",
"--",
"--dataverse-auth-strategy", "UseClientCredentials"
],
"env": {
"FLOWON_PROJECTS": "Sales",
"DATAVERSE_URL": "https://yourorg.crm.dynamics.com/",
"DATAVERSE_CLIENT_ID": "<app-id>",
"DATAVERSE_CLIENT_SECRET": "<secret>"
}
}
}
}
HTTP Mode
Point the client at the /mcp endpoint of a running server:
{
"mcpServers": {
"flowon-logic-conductor": {
"url": "https://your-mcp-server-host:port/mcp"
}
}
}
One Instance Per Environment
┌─────────────────────────────────────────────────────────────────────┐
│ ENVIRONMENT ISOLATION PATTERN │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Conductor │ │ Conductor │ │ Conductor │ │
│ │ (Dev) │ │ (UAT) │ │ (Prod) │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Dataverse │ │ Dataverse │ │ Dataverse │ │
│ │ Dev Env │ │ UAT Env │ │ Prod Env │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
│ Each instance exposes the action artifacts of its own │
│ environment, executed under the calling user's identity. │
│ │
└─────────────────────────────────────────────────────────────────────┘
Run a separate Flowon Logic Conductor instance per Dataverse environment so agents only ever invoke the logic deployed to that environment.