mcp-ai-agent-guidelines
Version:
A comprehensive Model Context Protocol server providing professional tools, resources, and prompts for implementing AI agent best practices
426 lines • 17.5 kB
JavaScript
import { z } from "zod";
import { logger } from "../shared/logger.js";
import { buildMetadataSection, buildReferencesSection, slugify, } from "../shared/prompt-utils.js";
/**
* Schema for a flow node - represents a single step/operation in the prompt flow.
* Inspired by claude-flow's node-based architecture for declarative AI workflows.
*
* Node types:
* - prompt: Execute a prompt and capture output
* - condition: Branch execution based on boolean expression
* - loop: Repeat execution with iteration limit or condition
* - parallel: Execute multiple branches concurrently
* - merge: Synchronization point for parallel branches
* - transform: Apply data transformation to flow state
*/
const FlowNodeSchema = z.object({
id: z.string(),
type: z.enum([
"prompt",
"condition",
"loop",
"parallel",
"merge",
"transform",
]),
name: z.string(),
description: z.string().optional(),
config: z.record(z.any()).optional(),
});
/**
* Schema for flow edges - defines connections between nodes with optional conditions.
* Edges create the control flow graph that determines execution order.
*/
const FlowEdgeSchema = z.object({
from: z.string(),
to: z.string(),
condition: z.string().optional(), // Boolean expression for conditional edges
label: z.string().optional(), // Human-readable label for visualization
});
/**
* Main schema for prompt flow configuration.
* Defines a declarative, graph-based approach to complex AI workflows.
*
* Key features:
* - Declarative flow definition with nodes and edges
* - Support for branching, loops, and parallel execution
* - Automatic visualization generation (Mermaid diagrams)
* - Built-in validation and error handling
*/
const PromptFlowSchema = z.object({
flowName: z.string(),
description: z.string().optional(),
nodes: z.array(FlowNodeSchema).min(1),
edges: z.array(FlowEdgeSchema).optional().default([]),
entryPoint: z.string().optional(), // If not specified, uses first node
variables: z.record(z.string()).optional().default({}), // Flow-level variables
includeMetadata: z.boolean().optional().default(true),
includeReferences: z.boolean().optional().default(true),
includeExecutionGuide: z.boolean().optional().default(true),
outputFormat: z
.enum(["markdown", "mermaid", "both"])
.optional()
.default("both"),
});
/**
* Prompt Flow Builder - Creates declarative, graph-based AI workflows
*
* Inspired by claude-flow's architecture, this tool enables complex prompting strategies through:
* - Declarative flow definition with nodes and edges
* - Conditional branching and loops for adaptive behavior
* - Parallel execution for independent operations
* - Automatic visualization and execution guides
*
* Integration with Serena patterns:
* - Memory-aware execution (stores flow results for context retention)
* - Mode-appropriate flow design (planning vs. execution flows)
* - Symbol-based operations (flows can reference code symbols)
*
* @param args - Flow configuration (validated against PromptFlowSchema)
* @returns Formatted flow specification with visualization and execution guide
*/
export async function promptFlowBuilder(args) {
const input = PromptFlowSchema.parse(args);
// Validate flow structure (edges, reachability, node configs)
validateFlow(input);
// Build comprehensive flow specification
const flowSpec = buildFlowSpecification(input);
// Generate Mermaid visualization (unless markdown-only format)
const visualization = input.outputFormat !== "markdown" ? buildFlowDiagram(input) : "";
// Include execution guide with best practices
const executionGuide = input.includeExecutionGuide
? buildExecutionGuide(input)
: "";
// Add metadata for traceability
const metadata = input.includeMetadata
? buildMetadataSection({
sourceTool: "mcp_ai-agent-guid_prompt-flow-builder",
filenameHint: `${slugify(input.flowName)}.flow.md`,
})
: "";
// Include references to flow-based programming resources
const references = input.includeReferences ? buildFlowReferences() : "";
return {
content: [
{
type: "text",
text: `# 🌊 Prompt Flow: ${input.flowName}\n\n${metadata}${input.description ? `## Description\n${input.description}\n\n` : ""}${flowSpec}\n\n${visualization}${executionGuide}${references}`,
},
],
};
}
/**
* Validates flow structure for correctness and safety.
*
* Validation checks:
* 1. Edge integrity - all edges reference existing nodes
* 2. Entry point validity - entry point exists in node set
* 3. Reachability analysis - detect unreachable nodes (warnings only)
* 4. Node configuration - type-specific config requirements
*
* @param input - Flow configuration to validate
* @throws Error if validation fails (invalid edges, missing configs)
*/
function validateFlow(input) {
const nodeIds = new Set(input.nodes.map((n) => n.id));
// Validate edges reference existing nodes (critical for execution)
for (const edge of input.edges) {
if (!nodeIds.has(edge.from)) {
throw new Error(`Edge references non-existent node: ${edge.from}`);
}
if (!nodeIds.has(edge.to)) {
throw new Error(`Edge references non-existent node: ${edge.to}`);
}
}
// Validate entry point if specified
if (input.entryPoint && !nodeIds.has(input.entryPoint)) {
throw new Error(`Entry point references non-existent node: ${input.entryPoint}`);
}
// Detect unreachable nodes (warning only - may be intentional for documentation)
const entryNode = input.entryPoint || input.nodes[0]?.id;
if (entryNode) {
const reachable = findReachableNodes(entryNode, input.edges);
const unreachable = Array.from(nodeIds).filter((id) => !reachable.has(id));
if (unreachable.length > 0) {
logger.warn("Unreachable nodes detected in flow", {
unreachableNodes: unreachable,
entryPoint: entryNode,
});
}
}
// Validate node type-specific configurations
for (const node of input.nodes) {
validateNodeConfig(node);
}
}
/**
* Validates node-specific configuration requirements.
*
* Each node type has different configuration requirements:
* - condition: must have 'expression' (boolean logic)
* - loop: must have 'condition' or 'iterations' (safety limit)
* - prompt: must have 'prompt' (the actual prompt text)
* - Others: no strict requirements (merge, parallel, transform are structural)
*
* @param node - Flow node to validate
* @throws Error if required configuration is missing
*/
function validateNodeConfig(node) {
switch (node.type) {
case "condition":
if (!node.config?.expression) {
throw new Error(`Condition node "${node.id}" must have an expression in config`);
}
break;
case "loop":
if (!node.config?.condition && !node.config?.iterations) {
throw new Error(`Loop node "${node.id}" must have either condition or iterations in config`);
}
break;
case "prompt":
if (!node.config?.prompt) {
throw new Error(`Prompt node "${node.id}" must have a prompt in config`);
}
break;
default:
// Other node types (merge, parallel, transform) don't require specific config validation
// They are primarily structural/control flow nodes
break;
}
}
/**
* Performs breadth-first search to find all reachable nodes from the start node.
* Used to detect unreachable nodes that may indicate flow design issues.
*
* @param startNode - Entry point node ID
* @param edges - Flow edges defining connections
* @returns Set of reachable node IDs
*/
function findReachableNodes(startNode, edges) {
const reachable = new Set([startNode]);
const queue = [startNode];
while (queue.length > 0) {
const current = queue.shift();
if (!current)
break; // Safety check for empty queue
for (const edge of edges) {
if (edge.from === current && !reachable.has(edge.to)) {
reachable.add(edge.to);
queue.push(edge.to);
}
}
}
return reachable;
}
/**
* Builds human-readable flow specification with all node and edge details.
*
* Output includes:
* - Flow variables (context shared across all nodes)
* - Entry point (where execution begins)
* - Node specifications (type, config, connections)
* - Edge information (conditions, labels)
*
* @param input - Validated flow configuration
* @returns Markdown-formatted flow specification
*/
function buildFlowSpecification(input) {
let output = "";
// Add variables if any (flow-level context available to all nodes)
if (Object.keys(input.variables).length > 0) {
output += "## Flow Variables\n";
for (const [key, value] of Object.entries(input.variables)) {
output += `- **${key}**: ${value}\n`;
}
output += "\n";
}
// Entry point (determines where flow execution begins)
const entry = input.entryPoint || input.nodes[0]?.id;
output += `## Entry Point\nFlow begins at node: **${entry}**\n\n`;
// Node specifications (detailed view of each node's purpose and configuration)
output += "## Flow Nodes\n\n";
for (const node of input.nodes) {
output += `### ${node.id}: ${node.name}\n\n`;
output += `**Type**: ${node.type}\n\n`;
if (node.description) {
output += `**Description**: ${node.description}\n\n`;
}
// Node configuration (type-specific parameters)
if (node.config && Object.keys(node.config).length > 0) {
output += "**Configuration**:\n";
output += "```json\n";
output += JSON.stringify(node.config, null, 2);
output += "\n```\n\n";
}
// Find outgoing edges (shows control flow from this node)
const outgoing = input.edges.filter((e) => e.from === node.id);
if (outgoing.length > 0) {
output += "**Outgoing Edges**:\n";
for (const edge of outgoing) {
output += `- To **${edge.to}** ${edge.condition ? `(if: ${edge.condition})` : ""} ${edge.label ? `[${edge.label}]` : ""}\n`;
}
output += "\n";
}
output += "---\n\n";
}
return output;
}
/**
* Generates Mermaid flowchart visualization of the prompt flow.
*
* Features:
* - Node shapes based on type (diamonds for conditions, etc.)
* - Color-coded node types for easy identification
* - Edge styling (dashed for conditional, solid for unconditional)
* - Comprehensive legend for interpretation
*
* Mermaid syntax reference: https://mermaid.js.org/syntax/flowchart.html
*
* @param input - Validated flow configuration
* @returns Mermaid diagram in markdown code block with legend
*/
function buildFlowDiagram(input) {
let mermaid = "## Flow Visualization\n\n```mermaid\nflowchart TD\n";
// Add nodes with appropriate shapes based on type
for (const node of input.nodes) {
const shape = getNodeShape(node.type);
const label = node.name || node.id;
mermaid += ` ${node.id}${shape[0]}"${label}"${shape[1]}\n`;
}
// Add edges with conditional vs. unconditional styling
for (const edge of input.edges) {
const arrow = edge.condition ? "-." : "-->"; // Dashed for conditional, solid for unconditional
const label = edge.label || edge.condition || "";
const labelText = label ? `|"${label}"| ` : " ";
mermaid += ` ${edge.from} ${arrow}${labelText}${edge.to}\n`;
}
// Style different node types with distinct colors
const conditionNodes = input.nodes
.filter((n) => n.type === "condition")
.map((n) => n.id);
const loopNodes = input.nodes
.filter((n) => n.type === "loop")
.map((n) => n.id);
const parallelNodes = input.nodes
.filter((n) => n.type === "parallel")
.map((n) => n.id);
if (conditionNodes.length > 0) {
mermaid += ` style ${conditionNodes.join(",")} fill:#ffe6cc,stroke:#d79b00\n`;
}
if (loopNodes.length > 0) {
mermaid += ` style ${loopNodes.join(",")} fill:#dae8fc,stroke:#6c8ebf\n`;
}
if (parallelNodes.length > 0) {
mermaid += ` style ${parallelNodes.join(",")} fill:#e1d5e7,stroke:#9673a6\n`;
}
mermaid += "```\n\n";
// Add legend for node shape interpretation
mermaid += "### Legend\n";
mermaid += "- **Rectangle**: Prompt node\n";
mermaid += "- **Diamond**: Condition node\n";
mermaid += "- **Rounded**: Loop node\n";
mermaid += "- **Parallelogram**: Parallel execution\n";
mermaid += "- **Circle**: Merge point\n";
mermaid += "- **Trapezoid**: Transform node\n\n";
return mermaid;
}
/**
* Maps node types to Mermaid shape syntax.
*
* Shape semantics:
* - Rectangles: Standard operations (prompts)
* - Diamonds: Decision points (conditions)
* - Rounded: Iteration (loops)
* - Parallelograms: Parallel processing
* - Circles: Synchronization (merge)
* - Trapezoids: Data transformation
*
* @param type - Node type from FlowNodeSchema
* @returns Tuple of [opening, closing] shape delimiters
*/
function getNodeShape(type) {
switch (type) {
case "prompt":
return ["[", "]"]; // Rectangle
case "condition":
return ["{", "}"]; // Diamond
case "loop":
return ["(", ")"]; // Rounded
case "parallel":
return ["[/", "/]"]; // Parallelogram
case "merge":
return ["((", "))"]; // Circle
case "transform":
return ["[\\", "/]"]; // Trapezoid
default:
return ["[", "]"]; // Default to rectangle
}
}
/**
* Generates comprehensive execution guide for flow implementation.
*
* Includes:
* - Step-by-step execution instructions
* - Error handling strategies per node type
* - Best practices for flow design and debugging
* - Performance optimization tips
*
* Integrates Serena best practices:
* - Planning-first approach (analyze before executing)
* - Context management (memory optimization for long flows)
* - Incremental execution (test nodes individually)
*
* @param input - Validated flow configuration
* @returns Markdown-formatted execution guide
*/
function buildExecutionGuide(input) {
let guide = "## Execution Guide\n\n";
guide += "### How to Execute This Flow\n\n";
guide += "1. **Initialize** flow variables and context\n";
guide += `2. **Start** at the entry point (${input.entryPoint || input.nodes[0]?.id})\n`;
guide += "3. **Execute** each node according to its type:\n";
guide += " - **Prompt nodes**: Execute the prompt and capture output\n";
guide +=
" - **Condition nodes**: Evaluate condition and branch accordingly\n";
guide +=
" - **Loop nodes**: Repeat until condition is met or max iterations reached\n";
guide += " - **Parallel nodes**: Execute branches concurrently\n";
guide += " - **Merge nodes**: Wait for all inputs before proceeding\n";
guide += " - **Transform nodes**: Apply transformation to data\n";
guide += "4. **Track** intermediate results and state\n";
guide += "5. **Handle** errors gracefully with fallbacks\n";
guide += "6. **Log** execution path for debugging\n\n";
guide += "### Error Handling\n\n";
guide += "- Implement try-catch blocks around each node execution\n";
guide += "- For condition nodes, ensure boolean evaluation is safe\n";
guide +=
"- For loop nodes, enforce max iteration limits to prevent infinite loops\n";
guide += "- For parallel nodes, handle partial failures gracefully\n\n";
guide += "### Best Practices\n\n";
guide += "- Keep prompts modular and focused on single responsibilities\n";
guide += "- Use descriptive node names and edge labels\n";
guide += "- Document complex conditions and transformations\n";
guide += "- Test flows with edge cases and error scenarios\n";
guide += "- Monitor performance and optimize bottlenecks\n";
guide +=
"- **Memory optimization**: Use context summarization for long flows (see memory-context-optimizer)\n";
guide +=
"- **Mode switching**: Select appropriate mode (planning/execution) before flow start (see mode-switcher)\n";
guide +=
"- **Project context**: Load relevant project memories for better results (see project-onboarding)\n\n";
return guide;
}
/**
* Builds references section with flow-based programming resources.
*
* @returns Markdown-formatted references section
*/
function buildFlowReferences() {
return buildReferencesSection([
"Flow-based Programming: https://en.wikipedia.org/wiki/Flow-based_programming",
"AI Workflow Patterns: https://www.anthropic.com/research/prompting-patterns",
"Claude Flow Architecture: https://github.com/ruvnet/claude-flow",
"Prompt Engineering Workflows: https://www.promptingguide.ai/techniques",
]);
}
//# sourceMappingURL=prompt-flow-builder.js.map