Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions cli/azd/.vscode/cspell.yaml
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import: ../../../.vscode/cspell.global.yaml
words:
- appcode
- azcloud
- azdext
- azurefd
- backoff
- bestpractices
- bicepschema
- Canonicalize
- chinacloudapi
- cloudarchitect
- Codespace
- Codespaces
- devcontainers
Expand Down
72 changes: 52 additions & 20 deletions cli/azd/cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -437,26 +437,18 @@ Do not stop until all tasks are complete and fully resolved.
}
}

// Run Step
// Run Step with retry logic
i.console.Message(ctx, color.MagentaString(step.Name))
fullTaskInput := fmt.Sprintf(taskInput, strings.Join([]string{
step.Description,
"Provide a very brief summary in markdown format that includes any files generated during this step.",
}, "\n"))

agentOutput, err := azdAgent.SendMessage(ctx, fullTaskInput)
if err != nil {
if agentOutput != "" {
i.console.Message(ctx, output.WithMarkdown(agentOutput))
}

if err := i.sendMessageWithRetry(ctx, azdAgent, fullTaskInput); err != nil {
return err
}

i.console.Message(ctx, "")
i.console.Message(ctx, fmt.Sprintf("%s:", output.AzdAgentLabel()))
i.console.Message(ctx, output.WithMarkdown(agentOutput))
i.console.Message(ctx, "")
}

// Post-completion feedback loop
Expand All @@ -467,6 +459,29 @@ Do not stop until all tasks are complete and fully resolved.
return nil
}

// handleErrorWithRetryPrompt displays an error and prompts user for retry
func (i *initAction) handleErrorWithRetryPrompt(ctx context.Context, err error) bool {
// Display error in error format
i.console.Message(ctx, "")
i.console.Message(ctx, output.WithErrorFormat("Error occurred: %s", err.Error()))
i.console.Message(ctx, "")

// Prompt user if they want to try again
retryPrompt := uxlib.NewConfirm(&uxlib.ConfirmOptions{
Message: "Would you like to try again?",
DefaultValue: uxlib.Ptr(true),
HelpMessage: "Choose 'yes' to retry the current step, or 'no' to stop the initialization.",
})

shouldRetry, promptErr := retryPrompt.Ask(ctx)
if promptErr != nil {
// If we can't prompt, don't retry
return false
}

return shouldRetry != nil && *shouldRetry
}

// collectAndApplyFeedback prompts for user feedback and applies it using the agent in a loop
func (i *initAction) collectAndApplyFeedback(
ctx context.Context,
Expand Down Expand Up @@ -508,24 +523,41 @@ func (i *initAction) collectAndApplyFeedback(
if userInput != "" {
i.console.Message(ctx, color.MagentaString("Feedback"))

feedbackOutput, err := azdAgent.SendMessage(ctx, userInput)
if err != nil {
if feedbackOutput != "" {
i.console.Message(ctx, output.WithMarkdown(feedbackOutput))
}
// Apply feedback with retry logic
if err := i.sendMessageWithRetry(ctx, azdAgent, userInput); err != nil {
return err
}

i.console.Message(ctx, "")
i.console.Message(ctx, fmt.Sprintf("%s:", output.AzdAgentLabel()))
i.console.Message(ctx, output.WithMarkdown(feedbackOutput))
i.console.Message(ctx, "")
}
}

return nil
}

// sendMessageWithRetry sends a message to the agent with retry logic for error recovery
func (i *initAction) sendMessageWithRetry(ctx context.Context, azdAgent agent.Agent, input string) error {
for {
agentOutput, err := azdAgent.SendMessage(ctx, input)
if err != nil {
if agentOutput != "" {
i.console.Message(ctx, output.WithMarkdown(agentOutput))
}

// Display error and ask if user wants to retry
if shouldRetry := i.handleErrorWithRetryPrompt(ctx, err); shouldRetry {
continue // Retry the same operation
}
return err // User chose not to retry, return original error
}

// Success - display output and return
i.console.Message(ctx, "")
i.console.Message(ctx, fmt.Sprintf("%s:", output.AzdAgentLabel()))
i.console.Message(ctx, output.WithMarkdown(agentOutput))
i.console.Message(ctx, "")
return nil
}
}

// postCompletionFeedbackLoop provides a final opportunity for feedback after all steps complete
func (i *initAction) postCompletionFeedbackLoop(
ctx context.Context,
Expand Down
9 changes: 9 additions & 0 deletions cli/azd/cmd/mcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,11 +154,20 @@ func (a *mcpStartAction) Run(ctx context.Context) (*actions.ActionResult, error)

allTools := []server.ServerTool{
tools.NewAzdPlanInitTool(),
tools.NewAzdIdentifyUserIntentTool(),
tools.NewAzdSelectStackTool(),
tools.NewAzdListStackResourcesTool(),
tools.NewAzdNewProjectTool(),
tools.NewAzdModernizeProjectTool(),
tools.NewAzdDiscoveryAnalysisTool(),
tools.NewAzdArchitecturePlanningTool(),
tools.NewAzdArtifactGenerationTool(),
tools.NewAzdAppCodeGenerationTool(),
tools.NewGenerateProjectSpecTemplateTool(),
tools.NewAzdAzureYamlGenerationTool(),
tools.NewAzdDockerGenerationTool(),
tools.NewAzdInfrastructureGenerationTool(),
tools.NewAzdGenerateInfraModuleTool(),
tools.NewAzdIacGenerationRulesTool(),
tools.NewAzdProjectValidationTool(),
tools.NewAzdYamlSchemaTool(),
Expand Down
22 changes: 18 additions & 4 deletions cli/azd/internal/agent/agent_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,17 @@ func (f *AgentFactory) Create(opts ...AgentCreateOption) (Agent, error) {
// Add more excluded tools here as needed
}

allTools := []common.AnnotatedTool{}
includedTools := map[string]bool{
"bestpractices": true,
"bicepschema": true,
"cloudarchitect": true,
"documentation": true,
"group": true,
"role": true,
"subscription": true,
}

filteredTools := []common.AnnotatedTool{}

for _, toolLoader := range toolLoaders {
categoryTools, err := toolLoader.LoadTools()
Expand All @@ -102,14 +112,18 @@ func (f *AgentFactory) Create(opts ...AgentCreateOption) (Agent, error) {

// Filter out excluded tools
for _, tool := range categoryTools {
if !excludedTools[tool.Name()] {
allTools = append(allTools, tool)
if excludedTools[tool.Name()] {
continue
}

if tool.Server() == "azd" || tool.Server() == "built-in" || includedTools[tool.Name()] {
filteredTools = append(filteredTools, tool)
}
}
}

// Wraps all tools in consent workflow
protectedTools := f.consentManager.WrapTools(allTools)
protectedTools := f.consentManager.WrapTools(filteredTools)

// Finalize agent creation options
allOptions := []AgentCreateOption{}
Expand Down
24 changes: 9 additions & 15 deletions cli/azd/internal/agent/prompts/conversational.txt
Original file line number Diff line number Diff line change
@@ -1,21 +1,15 @@
You are an Azure Developer CLI (AZD) agent.
You are an expert in building, provisioning, and deploying Azure applications.
Always use Azure best practices and automation wherever possible.
You are an expert in packaging, provisioning, and deploying Azure applications.

---

## Pre-Task Expectations

Before beginning your work:

* Review all available tools.
* If a tool exists for best practices or required inputs, you MUST invoke it before taking further steps.
* Integrate any learned knowledge from tools into your workflow.

When generating code, infrastructure, or configurations:
You must always seek out and follow established best practices and rules provided by available tools.
Before generating any code, infrastructure, or configuration, you must consult and apply guidance from tools whenever they are present.
If a tool defines rules, patterns, or validations, you must integrate those into your outputs and strictly align with them.
Only fall back to your own reasoning if no relevant tool or rule is available.

* You MUST ALWAYS save the content to files using the `write_file` tool.
* If no filename is provided, generate a meaningful and descriptive name.
You must be extremely thorough and cautious in all reasoning and actions.
Always follow the given instructions exactly, without skipping or relaxing any requirements.
Before producing a final answer, carefully verify that every step aligns with the rules, best practices, and tool guidance provided.
If there is any uncertainty, pause and re-check rather than making assumptions.

---

Expand Down
41 changes: 41 additions & 0 deletions cli/azd/internal/mcp/tools/azd_appcode_generation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package tools

import (
"context"

"github.com/azure/azure-dev/cli/azd/internal/mcp/tools/prompts"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)

// NewAzdAppCodeGenerationTool creates a new azd application code generation tool
func NewAzdAppCodeGenerationTool() server.ServerTool {
return server.ServerTool{
Tool: mcp.NewTool(
"azd_appcode_generation",
mcp.WithReadOnlyHintAnnotation(false),
mcp.WithIdempotentHintAnnotation(false),
mcp.WithDestructiveHintAnnotation(false),
mcp.WithOpenWorldHintAnnotation(false),
mcp.WithDescription(
`Provides instructions for generating production-ready application scaffolding and starter code for all application components with Azure SDK integrations and deployment-ready configurations.

Check failure on line 24 in cli/azd/internal/mcp/tools/azd_appcode_generation.go

View workflow job for this annotation

GitHub Actions / azd-lint (ubuntu-latest)

The line is 207 characters long, which exceeds the maximum of 125 characters. (lll)

This tool returns detailed instructions that the LLM agent should follow using available code generation tools.

Use this tool when:
- Application components and technology stack have been defined in the application spec
- Ready to create code structure in src/<component> directories
- Need to generate framework-specific project files with Azure integrations
- Application architecture planning is complete and ready for implementation`,
),
),
Handler: handleAzdAppCodeGeneration,
}
}

func handleAzdAppCodeGeneration(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
return mcp.NewToolResultText(prompts.AzdAppCodeGenerationPrompt), nil
}
13 changes: 6 additions & 7 deletions cli/azd/internal/mcp/tools/azd_architecture_planning.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,15 @@
mcp.WithDestructiveHintAnnotation(false),
mcp.WithOpenWorldHintAnnotation(false),
mcp.WithDescription(
`Returns instructions for selecting appropriate Azure services for discovered application components and
designing infrastructure architecture.
`Provides instructions for consolidating all previously gathered context (requirements, stack selection, discovered components) into a complete application architecture design with Azure service mappings and implementation strategy.

Check failure on line 24 in cli/azd/internal/mcp/tools/azd_architecture_planning.go

View workflow job for this annotation

GitHub Actions / azd-lint (ubuntu-latest)

The line is 248 characters long, which exceeds the maximum of 125 characters. (lll)

The LLM agent should execute these instructions using available tools.
This tool returns detailed instructions that the LLM agent should follow using available planning and documentation tools.

Use this tool when:
- Discovery analysis has been completed and azd-arch-plan.md exists
- Application components have been identified and classified
- Need to map components to Azure hosting services
- Ready to plan containerization and database strategies`,
- Discovery analysis has been completed and application components are identified
- Technology stack selection is complete
- Ready to map components to Azure hosting services and design infrastructure
- Need to create comprehensive architecture documentation in the application spec`,
),
),
Handler: handleAzdArchitecturePlanning,
Expand Down
41 changes: 41 additions & 0 deletions cli/azd/internal/mcp/tools/azd_artifact_generation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package tools

import (
"context"

"github.com/azure/azure-dev/cli/azd/internal/mcp/tools/prompts"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)

// NewAzdArtifactGenerationTool creates a new azd artifact generation orchestration tool
func NewAzdArtifactGenerationTool() server.ServerTool {
return server.ServerTool{
Tool: mcp.NewTool(
"azd_artifact_generation",
mcp.WithReadOnlyHintAnnotation(false),
mcp.WithIdempotentHintAnnotation(false),
mcp.WithDestructiveHintAnnotation(false),
mcp.WithOpenWorldHintAnnotation(false),
mcp.WithDescription(
`Provides instructions for orchestrating the complete artifact generation process for AZD projects, generating infrastructure templates, application scaffolding, Docker configurations, and azure.yaml in the correct order with proper dependencies.

Check failure on line 24 in cli/azd/internal/mcp/tools/azd_artifact_generation.go

View workflow job for this annotation

GitHub Actions / azd-lint (ubuntu-latest)

The line is 262 characters long, which exceeds the maximum of 125 characters. (lll)

This tool returns detailed instructions that the LLM agent should follow using available generation tools.

Use this tool when:
- Application architecture design is complete with all service mappings
- Ready to generate all project artifacts (infrastructure, code, Docker, azure.yaml)
- Need to coordinate multiple generation processes in proper dependency order
- Project specification contains complete requirements for artifact generation`,
),
),
Handler: handleAzdArtifactGeneration,
}
}

func handleAzdArtifactGeneration(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
return mcp.NewToolResultText(prompts.AzdArtifactGenerationPrompt), nil
}
5 changes: 2 additions & 3 deletions cli/azd/internal/mcp/tools/azd_azure_yaml_generation.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,9 @@
mcp.WithDestructiveHintAnnotation(false),
mcp.WithOpenWorldHintAnnotation(false),
mcp.WithDescription(
`Returns instructions for generating the azure.yaml configuration file with proper service hosting,
build, and deployment settings for AZD projects.
`Provides instructions for creating a complete and valid azure.yaml file in the root directory that maps all application services to their Azure hosting services with proper build and deployment configurations.

Check failure on line 24 in cli/azd/internal/mcp/tools/azd_azure_yaml_generation.go

View workflow job for this annotation

GitHub Actions / azd-lint (ubuntu-latest)

The line is 226 characters long, which exceeds the maximum of 125 characters. (lll)

The LLM agent should execute these instructions using available tools.
This tool returns detailed instructions that the LLM agent should follow using available file creation and validation tools.

Use this tool when:
- Architecture planning has been completed and Azure services selected
Expand Down
7 changes: 3 additions & 4 deletions cli/azd/internal/mcp/tools/azd_discovery_analysis.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,15 @@
mcp.WithDestructiveHintAnnotation(false),
mcp.WithOpenWorldHintAnnotation(false),
mcp.WithDescription(
`Returns instructions for performing comprehensive discovery and analysis of application components
to prepare for Azure Developer CLI (AZD) initialization.
`Provides instructions for scanning and analyzing the current workspace to identify all application components, technologies, dependencies, and communication patterns, documenting findings in the application specification.

Check failure on line 24 in cli/azd/internal/mcp/tools/azd_discovery_analysis.go

View workflow job for this annotation

GitHub Actions / azd-lint (ubuntu-latest)

The line is 238 characters long, which exceeds the maximum of 125 characters. (lll)

The LLM agent should execute these instructions using available tools.
This tool returns detailed instructions that the LLM agent should follow using available analysis and documentation tools.

Use this tool when:
- Starting Phase 1 of AZD migration process
- Need to identify all application components and dependencies
- Codebase analysis required before architecture planning
- azd-arch-plan.md does not exist or needs updating`,
- Application spec does not exist or needs updating`,
),
),
Handler: handleAzdDiscoveryAnalysis,
Expand Down
13 changes: 6 additions & 7 deletions cli/azd/internal/mcp/tools/azd_docker_generation.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,15 @@
mcp.WithDestructiveHintAnnotation(false),
mcp.WithOpenWorldHintAnnotation(false),
mcp.WithDescription(
`Returns instructions for generating optimized Dockerfiles and container configurations for containerizable
services in AZD projects.
`Provides instructions for generating optimized Dockerfiles and .dockerignore files for all containerizable services based on the Docker File Generation Checklist from the application spec.

Check failure on line 24 in cli/azd/internal/mcp/tools/azd_docker_generation.go

View workflow job for this annotation

GitHub Actions / azd-lint (ubuntu-latest)

The line is 205 characters long, which exceeds the maximum of 125 characters. (lll)

The LLM agent should execute these instructions using available tools.
This tool returns detailed instructions that the LLM agent should follow using available file creation tools.

Use this tool when:
- Architecture planning identified services requiring containerization
- azd-arch-plan.md shows Container Apps or AKS as selected hosting platform
- Need Dockerfiles for microservices, APIs, or containerized web applications
- Ready to implement containerization strategy`,
- Application components requiring containerization have been identified
- Docker File Generation Checklist exists in the application spec
- Ready to create production-ready Dockerfiles with multi-stage builds and security best practices
- Need to generate .dockerignore files for build optimization`,
),
),
Handler: handleAzdDockerGeneration,
Expand Down
Loading
Loading