MCPFlow 1.0.9
dotnet add package MCPFlow --version 1.0.9
NuGet\Install-Package MCPFlow -Version 1.0.9
<PackageReference Include="MCPFlow" Version="1.0.9" />
<PackageVersion Include="MCPFlow" Version="1.0.9" />
<PackageReference Include="MCPFlow" />
paket add MCPFlow --version 1.0.9
#r "nuget: MCPFlow, 1.0.9"
#:package MCPFlow@1.0.9
#addin nuget:?package=MCPFlow&version=1.0.9
#tool nuget:?package=MCPFlow&version=1.0.9
MCPFlow
<div align="center">
Orchestrate MCP tools into safe, guided workflows
A lightweight .NET library for returning structured plans from your MCP tools so agents like Cursor can: plan β ask for approval β execute β follow up.
</div>
π Table of Contents
- π Quick Start
- π Success Example
- β οΈ Common Error Patterns
- π¬ Message Snippets
- π Field Glossary
- π οΈ Using the Builder & Serializer
- π― Why This Helps
- π Use with Cursor
- π€ Use with GitHub Copilot Agent (generic)
- π§ͺ Quick Smoke Test Plan
- π License
π Quick Start
Installation
dotnet add package MCPFlow
TL;DR - What you return from your MCP tool
using MCPFlow;
var plan = FlowBuilder.Orchestrated(PlanStatus.Ok)
.WithInstruction("Scaffold Payroll screen and components.")
.WithFollowUp("Wire the new components to the API now?")
.WithSafety(writesCode: true, touchesFiles: new[] { "src/modules/payroll/**" })
.AddNextAction("create_screen", new {
screen = "Payroll",
components = new[] { "OvertimeStartDaySelector", "PayPeriodConfigurator", "PTCHoursOverride" }
})
.Build();
// Return a structured JSON object (nulls omitted):
return FlowResult.Payload(plan);
Agent flow you'll typically pair with this:
- Show the instruction as a plan and ask "Proceed?"
- On "Yes", run next_actions in order.
- On success, ask follow_up.
π Success Example
Here's the JSON your tool returns:
{
"kind": "orchestrated_plan",
"version": "1",
"status": "ok",
"instruction": "Scaffold Payroll screen and components.",
"next_actions": [
{
"tool": "create_screen",
"args": {
"screen": "Payroll",
"components": ["OvertimeStartDaySelector", "PayPeriodConfigurator", "PTCHoursOverride"]
}
}
],
"follow_up": "Wire the new components to the API now?",
"safety": {
"writes_code": true,
"touches_files": ["src/modules/payroll/**"]
}
}
β οΈ Common Error Patterns
1οΈβ£ Recoverable "ID not found"
{
"kind": "orchestrated_plan",
"version": "1",
"status": "error",
"instruction": "Cannot proceed: componentId 'abc123' not found.",
"errors": [
{
"code": "NOT_FOUND",
"field": "componentId",
"message": "No match for 'abc123'",
"hint": "Select from detected components",
"recoverable": true
}
],
"missing": ["componentId"],
"ask": {
"prompt": "Choose a valid component or create a placeholder.",
"fields": [{ "name": "componentId", "type": "select", "required": true }]
},
"recovery_actions": [
{ "tool": "list_detected_components", "args": { "screen": "Payroll" } },
{
"tool": "create_placeholder_component",
"args": { "name": "NewComponent" },
"label": "Create placeholder (safe)",
"confirm": true
}
],
"follow_up": "After fixing the component, wire API connections?"
}
2οΈβ£ Invalid input (schema/validation)
{
"kind": "orchestrated_plan",
"version": "1",
"status": "invalid",
"instruction": "Input failed validation.",
"errors": [
{ "code": "REQUIRED", "field": "screen", "message": "Missing 'screen'" },
{ "code": "TYPE", "field": "components", "message": "Expected array of strings" }
],
"ask": {
"prompt": "Provide the missing/invalid fields.",
"fields": [
{ "name": "screen", "type": "string", "required": true },
{ "name": "components", "type": "string" }
]
},
"follow_up": "Proceed to scaffolding once valid?"
}
3οΈβ£ Blocked (external dependency/permission)
{
"kind": "orchestrated_plan",
"version": "1",
"status": "blocked",
"instruction": "Blocked: target path is read-only.",
"errors": [{
"code": "PERMISSION",
"message": "Read-only file system",
"recoverable": false
}],
"recovery_actions": [{ "tool": "select_output_directory", "args": {} }],
"follow_up": "Retry scaffolding after selecting a writable path?"
}
4οΈβ£ Partial progress
{
"kind": "orchestrated_plan",
"version": "1",
"status": "partial",
"instruction": "Scaffolded some components; a few failed.",
"partial_results": [
{ "step": "create_screen", "outcome": "ok", "detail": "Base page created" },
{ "step": "create_component:PTCHoursOverride", "outcome": "failed", "detail": "Template missing" }
],
"recovery_actions": [{
"tool": "create_placeholder_component",
"args": { "name": "PTCHoursOverride" }
}],
"follow_up": "Continue by wiring API for the completed components?"
}
π¬ Message Snippets
Here's what your agent typically says:
Approval prompt:
Plan: Scaffold Payroll screen with 3 components.
Changes:src/modules/payroll/**
Proceed?
Done message:
Done. Created screen + components. Wire the new components to the API now?
Recoverable error:
Couldn't find
componentId="abc123". Select an existing component or create a placeholder.
π Field Glossary
Here's what each field meansβsuper short:
| Field | Description |
|---|---|
kind |
Payload type. "orchestrated_plan" tells the agent this is a plan to execute. |
version |
Schema version for this envelope. Use to evolve safely. |
status |
Plan state enum. ok = 0, error = 1, invalid = 2, blocked = 3, partial = 4. |
instruction |
Human-readable summary of what the plan intends to do (for the approval prompt). |
next_actions |
Ordered steps (tools) to run on user approval. |
follow_up |
Question to ask after all next_actions succeed (to continue the flow). |
errors |
Machine/human diagnostics when something went wrong. |
warnings |
Non-fatal issues worth showing (e.g., deprecations, guesses). |
missing |
Names of required inputs not provided (e.g., "componentId"). |
partial_results |
What already happened in this session (per step: ok/skipped/failed, with detail). |
ask |
A mini form spec to unblock (prompt + fields to collect) before re-planning or continuing. |
recovery_actions |
Safe alternatives to fix/retry (e.g., list components, create placeholder). |
safety |
Risk preview to show before execution. |
affected_paths |
Specific files/paths actually touched/created (fill after execution if you track them). |
correlation_id |
Trace/debug id to tie tool logs, chat steps, and telemetry together. |
Next Actions Fields
| Field | Description |
|---|---|
tool |
MCP tool name to invoke. |
args |
Arguments for that tool (free-form JSON object). |
label |
Optional UI label for the step (nice to have). |
confirm |
If true, force an extra "are you sure?" before this step. |
Safety Fields
| Field | Description |
|---|---|
writes_code |
Will this write/modify code? |
touches_files |
Glob(s)/paths likely to be changed (for approval UI). |
Note: In your serializer, null fields are omitted, keeping responses clean.
π οΈ Using the Builder & Serializer
// Build success
var ok = FlowBuilder.Orchestrated(PlanStatus.Ok)
.WithInstruction("Scaffold Payroll screen and components.")
.WithFollowUp("Wire the new components to the API now?")
.AddNextAction("create_screen", new {
screen = "Payroll",
components = new[] { "OvertimeStartDaySelector", "PayPeriodConfigurator", "PTCHoursOverride" }
})
.Build();
// Build recoverable error
var err = FlowBuilder.Orchestrated(PlanStatus.Error)
.WithInstruction("Cannot proceed: componentId 'abc123' not found.")
.NotFound("componentId", "abc123", "Select from detected components.")
.AddMissing("componentId")
.WithAsk("Choose a valid component or create a placeholder.",
new[] { new AskField { Name = "componentId", Type = AskFieldType.Select, Required = true } })
.AddRecoveryAction("list_detected_components", new { screen = "Payroll" })
.AddRecoveryAction("create_placeholder_component", new { name = "NewComponent" },
label: "Create placeholder (safe)", confirm: true)
.WithFollowUp("After fixing the component, wire API connections?")
.Build();
// Return structured JSON (preferred for MCP tools)
return FlowResult.Payload(ok); // or FlowResult.Payload(err);
// Or string form if you truly need it
var json = FlowResult.Json(ok, indented: true);
Enum Strings (Recommended)
Add JsonStringEnumConverter(JsonNamingPolicy.CamelCase) to get "ok", "error", etc. Or use an EnumMember converter for custom literals.
π― Why This Helps
- β Predictable, testable plans instead of ad-hoc strings
- β Enforces approval gates and follow-ups
- β Clean JSON with nulls omitted; easy to diff and log
- β
Works great with a simple Cursor rule or a
workflow_state.mdstate machine
π Use with Cursor
1οΈβ£ Your MCP tool returns an MCPFlow plan
return FlowResult.Payload(
FlowBuilder.Orchestrated(PlanStatus.Ok)
.WithInstruction("Scaffold Payroll screen and components.")
.WithFollowUp("Wire the new components to the API now?")
.WithSafety(writesCode: true, touchesFiles: new[] { "src/modules/payroll/**" })
.AddNextAction("create_screen", new {
screen = "Payroll",
components = new[] { "OvertimeStartDaySelector","PayPeriodConfigurator","PTCHoursOverride" }
})
.Build()
);
2οΈβ£ Minimal Cursor Rule (.cursor/rules/mcpflow.md)
---
type: Always
description: "MCPFlow: plan β approval β execute β follow-up"
alwaysApply: true
---
If the last tool result contains `"kind":"orchestrated_plan"`:
1) Print a 1β3 line PLAN from `instruction`. Show `safety.writes_code` and `safety.touches_files`.
2) Ask: **Proceed?**
3) If **Yes**:
- For each `next_actions` item:
- Briefly say what will run; then call the tool with `args`.
- When done, if `follow_up` exists, ask it.
4) If **No**: Ask what to change and request a new plan.
3οΈβ£ Optional: State machine (workflow_state.md)
# Workflow State
## State
NEEDS_PLAN_APPROVAL
## Plan
(none)
## Rules
- If State=NEEDS_PLAN_APPROVAL and a plan appears:
- Summarize `instruction`; ask "Proceed?"
- On Yes β State=EXECUTING; copy `next_actions` into ## Plan
- On No β ask for changes; re-run planning tool
- If State=EXECUTING:
- Run steps in ## Plan; if all succeed β FOLLOW_UP_PENDING; on error β BLOCKED
- If State=FOLLOW_UP_PENDING:
- Ask `follow_up`; Yes β EXECUTING with a new Plan; No β IDLE
## Log
- Initialized
(Optional) Driver rule to enforce the loop (.cursor/rules/state-driver.md)
---
type: Always
description: "Readβdecideβactβupdate using workflow_state.md"
alwaysApply: true
---
Before any action:
- Read ./workflow_state.md (## State/Plan/Rules)
- Decide next step per ## Rules
- Act (ask user or call tool)
- Update ./workflow_state.md (## State/Plan/Log)
That's it! Cursor will now: show plan β get approval β run tools β ask follow-up.
π€ Use with GitHub Copilot Agent (generic)
Copilot doesn't use Cursor rules, but you can achieve the same flow via your agent/extension instructions and a small action that calls your tool and returns MCPFlow JSON.
1οΈβ£ Expose an HTTP action (or CLI) that returns MCPFlow
TypeScript example (Express), but your server can be C#; just return the JSON from FlowResult.Payload(plan).
app.post("/mcpflow/create_screen", async (req, res) => {
const plan = /* call your C# MCP tool or service β returns MCPFlow JSON */;
res.json(plan);
});
2οΈβ£ Agent/Extension instructions (paste into your Copilot agent's system/instructions)
When an API response contains {"kind":"orchestrated_plan"}:
1. Summarize 'instruction' in β€3 lines and ask the user "Proceed?"
2. If user approves:
- For each item in 'next_actions', call its tool/endpoint with 'args'.
- After all succeed, ask the 'follow_up' question if present.
3. On errors/invalid:
- Display 'errors' and 'ask' fields; collect required inputs.
- Optionally call 'recovery_actions' to unblock, then request a fresh plan.
Always show which files may change (safety.touches_files) before executing.
3οΈβ£ Minimal success flow (Copilot chat)
- You: "Create the Payroll screen from this screenshot."
- Agent: Shows plan from
instruction+ safety. "Proceed?" - You: "Yes."
- Agent: Runs each
next_actionscall, then "Wire the new components to the API now?"
π§ͺ Quick Smoke Test Plan
Return this from your tool to verify the end-to-end loop:
{
"kind": "orchestrated_plan",
"version": "1",
"status": "ok",
"instruction": "Create a Demo page and one component.",
"next_actions": [
{ "tool": "create_screen", "args": { "screen": "Demo", "components": ["HelloBox"] } }
],
"follow_up": "Add API wiring for HelloBox?",
"safety": { "writes_code": true, "touches_files": ["src/modules/demo/**"] }
}
If you see: plan β approval β tool run β follow-up, you're good! π
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.