Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
99 commits
Select commit Hold shift + click to select a range
d64bf2a
updates
aknysh Mar 22, 2026
6ea28fb
updates
aknysh Mar 22, 2026
fad5746
Merge remote-tracking branch 'origin/main' into aknysh/mcp-integratio…
aknysh Mar 22, 2026
69eba72
feat: implement MCP integrations — external MCP server management (Ph…
aknysh Mar 22, 2026
00548c2
docs: update MCP integrations PRD with Phase 1 implementation status
aknysh Mar 22, 2026
61a16dc
feat: integrate MCP tools into AI chat and exec (Phase 2)
aknysh Mar 23, 2026
fc70454
docs: update MCP integrations PRD — mark Phase 2 as shipped
aknysh Mar 23, 2026
0375456
feat: add MCP management commands — add, remove, status, restart (Pha…
aknysh Mar 23, 2026
b041413
feat: add auth integration and start options for MCP integrations (Ph…
aknysh Mar 23, 2026
fdb0241
docs: update MCP integrations PRD — mark Phase 4 auth as shipped
aknysh Mar 23, 2026
e79d67a
refactor: rename mcp.integrations to mcp.servers for ecosystem alignment
aknysh Mar 23, 2026
de5959f
docs: move stack-level overrides and composite server to future consi…
aknysh Mar 23, 2026
64bfc1e
feat: add toolchain integration for MCP server prerequisites (Phase 4)
aknysh Mar 23, 2026
fd0cc19
docs: mark all 4 phases as shipped, move remaining to future consider…
aknysh Mar 23, 2026
e43bc5a
test: improve MCP client and command test coverage
aknysh Mar 23, 2026
da4779c
refactor: organize MCP commands into server/ and client/ subfolders
aknysh Mar 23, 2026
9327687
docs: add embedded markdown help text for all MCP commands
aknysh Mar 23, 2026
00b8bb9
fix: align MCP code with atmos-errors, flag-handler, and tui-expert a…
aknysh Mar 23, 2026
f340687
fix: replace tabwriter with theme tables, use StandardParser for star…
aknysh Mar 23, 2026
9ad865a
docs: add MCP integrations example with 6 AWS MCP servers
aknysh Mar 23, 2026
42f43af
fix: use name_template instead of obsolete name_pattern in MCP example
aknysh Mar 23, 2026
3143fcb
chore: enable toolchain integration in MCP example for manual testing
aknysh Mar 23, 2026
fe0e379
docs: improve prerequisites section in MCP example atmos.yaml
aknysh Mar 23, 2026
05b9e27
updates
aknysh Mar 23, 2026
0453ebb
chore: remove deprecated aws-diagram MCP server from example
aknysh Mar 23, 2026
e5f4a13
docs: expand MCP example with ai ask, ai exec, --ai flag, and server …
aknysh Mar 23, 2026
af4733b
docs: expand aws-docs section with try-it-now examples
aknysh Mar 23, 2026
c091460
docs: add note about how MCP tools render docs in the terminal
aknysh Mar 23, 2026
3eb083f
fix: improve MCP example atmos.yaml consistency and correctness
aknysh Mar 23, 2026
7d6dfb5
docs: add website documentation for all MCP client commands
aknysh Mar 23, 2026
e039384
docs: update MCP configuration reference with server connections
aknysh Mar 23, 2026
53c8387
docs: add MCP server connections to AI landing page
aknysh Mar 23, 2026
532d33f
docs: add blog post for MCP server integrations feature
aknysh Mar 23, 2026
6b45da5
docs: broaden MCP blog post to mention GCP, Azure, and custom servers
aknysh Mar 23, 2026
006ef6c
fix: remove AWS from blog post title
aknysh Mar 23, 2026
1c9a000
docs: fix user-facing language in blog post
aknysh Mar 23, 2026
f1fff16
refactor: remove atmos mcp add/remove commands
aknysh Mar 23, 2026
ba3d090
docs: add MCP server connections milestone to roadmap
aknysh Mar 23, 2026
b2cc565
updates
aknysh Mar 23, 2026
fff86ea
docs: update MCP integrations PRD to reflect current state
aknysh Mar 23, 2026
5148b98
docs: fix blog intro to say 'any MCP server' not 'an AWS MCP server'
aknysh Mar 23, 2026
9735312
Merge remote-tracking branch 'origin/main' into aknysh/mcp-integratio…
aknysh Mar 24, 2026
addc78b
updates
aknysh Mar 24, 2026
0a06993
Merge remote-tracking branch 'origin/main' into aknysh/mcp-integratio…
aknysh Mar 24, 2026
af2c753
Merge remote-tracking branch 'origin/main' into aknysh/mcp-integratio…
aknysh Mar 25, 2026
1128829
updates
aknysh Mar 25, 2026
7d926fd
updates
aknysh Mar 25, 2026
3de6546
Merge remote-tracking branch 'origin/main' into aknysh/mcp-integratio…
aknysh Mar 26, 2026
87bf312
Merge remote-tracking branch 'origin/main' into aknysh/mcp-integratio…
aknysh Mar 26, 2026
77bf35d
Merge remote-tracking branch 'origin/main' into aknysh/mcp-integratio…
aknysh Mar 26, 2026
a077082
updates
aknysh Mar 26, 2026
7bd9723
feat: unified MCP experience — auth wiring, generate-config, read-onl…
aknysh Mar 26, 2026
32883a3
updates
aknysh Mar 26, 2026
8012acc
feat: add user-facing MCP server feedback and update docs
aknysh Mar 26, 2026
64bb69d
docs: clarify that AI tool usage is explicitly declared, not inferred
aknysh Mar 26, 2026
998a086
docs: add tool execution flow details to MCP PRD
aknysh Mar 26, 2026
3bb3848
docs: add MCP tool call routing details to PRD
aknysh Mar 26, 2026
a5727e7
fix: improve MCP error handling — use ui package, show error details
aknysh Mar 26, 2026
39623f9
Merge remote-tracking branch 'origin/main' into aknysh/mcp-integratio…
aknysh Mar 27, 2026
9f0dae2
feat: add cost analysis and security MCP servers to example
aknysh Mar 27, 2026
040abc5
docs: update blog post with 9 pre-configured MCP servers
aknysh Mar 27, 2026
114970e
chore: update all dependencies to latest versions
aknysh Mar 27, 2026
6939ba1
docs: add generate-config command doc, update MCP servers across all …
aknysh Mar 27, 2026
160368b
refactor: remove read_only field — all MCP servers available in all A…
aknysh Mar 27, 2026
a4582e8
updates
aknysh Mar 27, 2026
0ecd66c
updates
aknysh Mar 28, 2026
5620746
docs: fix consistency issues across blog post and example README
aknysh Mar 28, 2026
d9da928
Merge remote-tracking branch 'origin/main' into aknysh/mcp-integratio…
aknysh Mar 28, 2026
6831962
fix: wire toolchain in MCP CLI commands, improve tool display, suppre…
aknysh Mar 28, 2026
f598db3
fix: correct toolchain config, remove PYTHONWARNINGS, complete AI config
aknysh Mar 28, 2026
d108911
fix: tool name sanitization, stderr suppression, provider timeouts, t…
aknysh Mar 28, 2026
f59d4a5
fix: MCP tool result forwarding and human-readable tool names in ask …
aknysh Mar 28, 2026
0292f6a
test: add tests for MCP tool display names, sanitization, and cleanTo…
aknysh Mar 28, 2026
e995efa
updates
aknysh Mar 29, 2026
5072da8
fix: auth config docs, I/O anti-pattern, and description consistency
aknysh Mar 29, 2026
16b6591
feat: two-pass MCP server routing and --mcp flag for AI commands
aknysh Mar 29, 2026
b9d2168
fix: use configured AI provider for routing, add --mcp flag docs, inc…
aknysh Mar 29, 2026
c1da259
docs: add all AI and MCP environment variables to environment variabl…
aknysh Mar 29, 2026
693e8f0
docs: clean up PRD, remove --ai from MCP docs, fix tool name format
aknysh Mar 29, 2026
84cf055
updates
aknysh Mar 29, 2026
799fdc4
docs: add mcp-integrations to roadmap, mark ai-assistant as shipped
aknysh Mar 29, 2026
3dc11f2
docs: add routing config to MCP reference, fix examples, remove --ai …
aknysh Mar 29, 2026
1cffa9a
[autofix.ci] apply automated fixes
autofix-ci[bot] Mar 29, 2026
0b43443
fix: replace flaky spec.modelcontextprotocol.io link with modelcontex…
aknysh Mar 29, 2026
3ac2377
fix: address CodeRabbit review feedback across MCP implementation
aknysh Mar 29, 2026
06f4274
fix: address remaining CodeRabbit feedback and increase test coverage
aknysh Mar 29, 2026
13472b7
updates
aknysh Mar 29, 2026
ce4cd32
fix: configurable max_tool_iterations, redact identifiers, increase t…
aknysh Mar 29, 2026
93b085d
fix: clean up test data, add compile sentinel, negative iteration test
aknysh Mar 29, 2026
e316f58
refactor: rename auth_identity to identity on MCP server config
aknysh Mar 29, 2026
56cb550
fix: extract formatStatusRow for testability, add status row tests
aknysh Mar 29, 2026
d2dac1c
fix: use static error sentinels, enforce file permissions, fix auth c…
aknysh Mar 29, 2026
5d7d18f
refactor: migrate atmos mcp list to shared list abstraction
aknysh Mar 30, 2026
3f04e0d
fix: use backtick quoting in all MCP ui messages for consistency
aknysh Mar 30, 2026
8b85379
refactor: rename atmos mcp generate-config to atmos mcp export
aknysh Mar 30, 2026
a6128f2
refactor: unify AI tool initialization, validate routing, address rev…
aknysh Mar 30, 2026
4cad175
test: add tests for selectManualServers, JSON marshaling, and NoArgs
aknysh Mar 30, 2026
d3d9098
fix: address review feedback — log filtered names, debug logging, tim…
aknysh Mar 30, 2026
95f5d34
fix: fail fast when auth manager unavailable for identity-backed servers
aknysh Mar 30, 2026
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
144 changes: 72 additions & 72 deletions NOTICE

Large diffs are not rendered by default.

30 changes: 22 additions & 8 deletions cmd/ai/ask.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package ai

import (
"bytes"
"context"
_ "embed"
"fmt"
Expand All @@ -13,6 +14,7 @@ import (
errUtils "github.com/cloudposse/atmos/errors"
"github.com/cloudposse/atmos/pkg/ai"
"github.com/cloudposse/atmos/pkg/ai/executor"
"github.com/cloudposse/atmos/pkg/ai/formatter"
"github.com/cloudposse/atmos/pkg/ai/tools"
cfg "github.com/cloudposse/atmos/pkg/config"
"github.com/cloudposse/atmos/pkg/flags"
Expand Down Expand Up @@ -45,6 +47,7 @@ var askCmd = &cobra.Command{
excludePatterns := v.GetStringSlice("exclude")
noAutoContext := v.GetBool("no-auto-context")
noTools := v.GetBool("no-tools")
mcpServers := v.GetStringSlice("mcp")

// Initialize configuration.
configAndStacksInfo := schema.ConfigAndStacksInfo{}
Expand Down Expand Up @@ -92,15 +95,19 @@ var askCmd = &cobra.Command{
}
}

// Create read-only tool executor (if tools are enabled).
// The ask command uses only in-process, read-only tools (no subprocess execution).
// Create tool executor (if tools are enabled).
var toolExecutor *tools.Executor
if !noTools && atmosConfig.AI.Tools.Enabled {
_, toolExecutor, err = initializeAIReadOnlyTools(&atmosConfig)
if err != nil {
log.Warn("Failed to initialize tools", "error", err)
toolsResult, toolsErr := initializeAIToolsAndExecutor(&atmosConfig, mcpServers, question)
if toolsErr != nil {
log.Warn("Failed to initialize tools", "error", toolsErr)
// Continue without tools rather than failing.
toolExecutor = nil
}
if toolsResult != nil {
toolExecutor = toolsResult.Executor
if toolsResult.MCPMgr != nil {
defer toolsResult.MCPMgr.StopAll() //nolint:errcheck // Best-effort MCP server cleanup.
}
}
}

Expand Down Expand Up @@ -129,8 +136,13 @@ var askCmd = &cobra.Command{
return errUtils.ErrAIExecutionFailed
}

// Render response as Markdown for rich terminal output.
utils.PrintfMarkdown("%s", result.Response)
// Render response with tool execution details as Markdown.
var buf bytes.Buffer
mdFormatter := formatter.NewFormatter(formatter.FormatMarkdown)
if err := mdFormatter.Format(&buf, result); err != nil {
return fmt.Errorf("failed to format response: %w", err)
}
utils.PrintfMarkdown("%s", buf.String())

return nil
},
Expand All @@ -143,10 +155,12 @@ func init() {
flags.WithStringSliceFlag("exclude", "", nil, "Add glob patterns to exclude from context (can be repeated)"),
flags.WithBoolFlag("no-auto-context", "", false, "Disable automatic context discovery"),
flags.WithBoolFlag("no-tools", "", false, "Disable tool execution"),
flags.WithStringSliceFlag("mcp", "", nil, "MCP servers to use (comma-separated, skips auto-routing)"),
flags.WithEnvVars("include", "ATMOS_AI_INCLUDE"),
flags.WithEnvVars("exclude", "ATMOS_AI_EXCLUDE"),
flags.WithEnvVars("no-auto-context", "ATMOS_AI_NO_AUTO_CONTEXT"),
flags.WithEnvVars("no-tools", "ATMOS_AI_NO_TOOLS"),
flags.WithEnvVars("mcp", "ATMOS_AI_MCP"),
)

// Register flags on the command.
Expand Down
8 changes: 4 additions & 4 deletions cmd/ai/ask_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -558,8 +558,8 @@ func TestAskCommand_FlagCount(t *testing.T) {
count++
}
})
// Expected: include, exclude, no-auto-context, no-tools = 4 flags.
assert.Equal(t, 4, count, "ask command should have exactly 4 custom flags")
// Expected: include, exclude, no-auto-context, no-tools, mcp = 5 flags.
assert.Equal(t, 5, count, "ask command should have exactly 5 custom flags")
}

func TestAskCommand_AIEnabledButClientCreationFails(t *testing.T) {
Expand Down Expand Up @@ -2235,9 +2235,9 @@ ai:
})
}

// TestAskCommand_StandardParserIntegration tests that the ask command uses StandardParser
// TestAskCommand_StandardParserServer tests that the ask command uses StandardParser
// with proper Viper binding for flag precedence (CLI > ENV > defaults).
func TestAskCommand_StandardParserIntegration(t *testing.T) {
func TestAskCommand_StandardParserServer(t *testing.T) {
t.Run("askParser is initialized", func(t *testing.T) {
require.NotNil(t, askParser, "askParser should be initialized by init()")
})
Expand Down
16 changes: 13 additions & 3 deletions cmd/ai/chat.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ var chatCmd = &cobra.Command{
return err
}

mcpServers := v.GetStringSlice("mcp")
Comment thread
aknysh marked this conversation as resolved.

// Initialize configuration.
configAndStacksInfo := schema.ConfigAndStacksInfo{}
atmosConfig, err := cfg.InitCliConfig(configAndStacksInfo, true)
Expand Down Expand Up @@ -128,9 +130,15 @@ var chatCmd = &cobra.Command{
// Initialize tool registry and executor if tools are enabled.
var executor *tools.Executor
if atmosConfig.AI.Tools.Enabled {
_, executor, err = initializeAIToolsAndExecutor(&atmosConfig)
if err != nil {
log.Warnf("Failed to initialize AI tools: %v", err)
toolsResult, toolsErr := initializeAIToolsAndExecutor(&atmosConfig, mcpServers, "")
if toolsErr != nil {
log.Warnf("Failed to initialize AI tools: %v", toolsErr)
}
if toolsResult != nil {
executor = toolsResult.Executor
if toolsResult.MCPMgr != nil {
defer toolsResult.MCPMgr.StopAll() //nolint:errcheck // Best-effort MCP server cleanup.
}
}
}

Expand Down Expand Up @@ -179,7 +187,9 @@ func init() {
// Create parser with chat-specific flags using functional options.
chatParser = flags.NewStandardParser(
flags.WithStringFlag("session", "", "", "Resume or create a named session"),
flags.WithStringSliceFlag("mcp", "", nil, "MCP servers to use (comma-separated, skips auto-routing)"),
flags.WithEnvVars("session", "ATMOS_AI_SESSION"),
flags.WithEnvVars("mcp", "ATMOS_AI_MCP"),
)

// Register flags on the command.
Expand Down
4 changes: 2 additions & 2 deletions cmd/ai/chat_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2170,9 +2170,9 @@ func TestChatCmd_RunE_OllamaToolsInitWarn(t *testing.T) {
assert.Contains(t, err.Error(), "chat session failed")
}

// TestChatCommand_StandardParserIntegration tests that the chat command uses StandardParser
// TestChatCommand_StandardParserServer tests that the chat command uses StandardParser
// with proper Viper binding for flag precedence (CLI > ENV > defaults).
func TestChatCommand_StandardParserIntegration(t *testing.T) {
func TestChatCommand_StandardParserServer(t *testing.T) {
t.Run("chatParser is initialized", func(t *testing.T) {
require.NotNil(t, chatParser, "chatParser should be initialized by init()")
})
Expand Down
17 changes: 12 additions & 5 deletions cmd/ai/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ var execCmd = &cobra.Command{
includePatterns := v.GetStringSlice("include")
excludePatterns := v.GetStringSlice("exclude")
noAutoContext := v.GetBool("no-auto-context")
mcpServers := v.GetStringSlice("mcp")

// Initialize configuration.
configAndStacksInfo := schema.ConfigAndStacksInfo{}
Expand Down Expand Up @@ -104,11 +105,15 @@ var execCmd = &cobra.Command{
var toolExecutor *tools.Executor
if !noTools && atmosConfig.AI.Tools.Enabled {
// Use shared initialization function.
_, toolExecutor, err = initializeAIToolsAndExecutor(&atmosConfig)
if err != nil {
log.Warn("Failed to initialize tools", "error", err)
// Continue without tools rather than failing.
toolExecutor = nil
toolsResult, toolsErr := initializeAIToolsAndExecutor(&atmosConfig, mcpServers, prompt)
if toolsErr != nil {
log.Warn("Failed to initialize tools", "error", toolsErr)
}
if toolsResult != nil {
toolExecutor = toolsResult.Executor
if toolsResult.MCPMgr != nil {
defer toolsResult.MCPMgr.StopAll() //nolint:errcheck // Best-effort MCP server cleanup.
}
}
}

Expand Down Expand Up @@ -235,6 +240,7 @@ func init() {
flags.WithStringFlag("format", "f", "text", "Output format: text, json, markdown"),
flags.WithStringFlag("output", "o", "", "Output file (default: stdout)"),
flags.WithBoolFlag("no-tools", "", false, "Disable tool execution"),
flags.WithStringSliceFlag("mcp", "", nil, "MCP servers to use (comma-separated, skips auto-routing)"),
flags.WithBoolFlag("context", "", false, "Include stack context in prompt"),
flags.WithStringFlag("provider", "p", "", "Override AI provider (anthropic, openai, gemini, etc.)"),
flags.WithStringFlag("session", "s", "", "Session ID for conversation context"),
Expand All @@ -244,6 +250,7 @@ func init() {
flags.WithEnvVars("format", "ATMOS_AI_FORMAT"),
flags.WithEnvVars("output", "ATMOS_AI_OUTPUT"),
flags.WithEnvVars("no-tools", "ATMOS_AI_NO_TOOLS"),
flags.WithEnvVars("mcp", "ATMOS_AI_MCP"),
flags.WithEnvVars("context", "ATMOS_AI_CONTEXT"),
flags.WithEnvVars("provider", "ATMOS_AI_PROVIDER"),
flags.WithEnvVars("session", "ATMOS_AI_SESSION"),
Expand Down
14 changes: 7 additions & 7 deletions cmd/ai/exec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -775,8 +775,8 @@ func TestExecCommand_ErrorMessages(t *testing.T) {
})
}

// TestExecCommand_IntegrationStyle tests the overall flow in a more integration-style manner.
func TestExecCommand_IntegrationStyle(t *testing.T) {
// TestExecCommand_ServerStyle tests the overall flow in a more server-style manner.
func TestExecCommand_ServerStyle(t *testing.T) {
t.Run("full flag setup works", func(t *testing.T) {
// Create a copy of the command for isolated testing.
testCmd := &cobra.Command{
Expand Down Expand Up @@ -1493,8 +1493,8 @@ ai:
return tmpDir
}

// TestFormatterIntegration tests actual formatter usage with ExecutionResult.
func TestFormatterIntegration(t *testing.T) {
// TestFormatterServer tests actual formatter usage with ExecutionResult.
func TestFormatterServer(t *testing.T) {
t.Run("text formatter writes response", func(t *testing.T) {
result := &formatter.ExecutionResult{
Success: true,
Expand Down Expand Up @@ -1899,7 +1899,7 @@ func TestExecCommand_TimeoutConfiguration(t *testing.T) {
t.Run("custom timeout from config", func(t *testing.T) {
// Test that timeout_seconds config is read at lines 143-146.
extraConfig := `
timeout_seconds: 300
timeout_seconds: 120
`
tmpDir := createValidAtmosConfig(t, true, extraConfig)

Expand Down Expand Up @@ -3313,9 +3313,9 @@ func TestGetPrompt_WithMockStdin(t *testing.T) {
})
}

// TestExecCommand_StandardParserIntegration tests that the exec command uses StandardParser
// TestExecCommand_StandardParserServer tests that the exec command uses StandardParser
// with proper Viper binding for flag precedence (CLI > ENV > defaults).
func TestExecCommand_StandardParserIntegration(t *testing.T) {
func TestExecCommand_StandardParserServer(t *testing.T) {
t.Run("execParser is initialized", func(t *testing.T) {
require.NotNil(t, execParser, "execParser should be initialized by init()")
})
Expand Down
Loading
Loading