Skip to content
Merged
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
3 changes: 1 addition & 2 deletions .justfiles/build.just
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
# Build Commands
# Run `just build` to see all build commands

# invocation_directory() returns the directory from which just was called
PROJECT_DIR := invocation_directory()
PROJECT_DIR := justfile_directory()
BIN_DIR := PROJECT_DIR / "bin"

[private]
Expand Down
50 changes: 49 additions & 1 deletion .justfiles/demos.just
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,26 @@ subagents: build-subagents
@echo "Starting subagents demo..."
{{BIN_DIR}}/demo-subagents

# Build and run sessions demo
sessions: build-sessions
@echo "Starting sessions demo..."
{{BIN_DIR}}/demo-sessions

# Build and run MCP demo
mcp: build-mcp
@echo "Starting MCP demo..."
{{BIN_DIR}}/demo-mcp

# Build and run retry demo
retry: build-retry
@echo "Starting retry demo..."
{{BIN_DIR}}/demo-retry

# Build and run permissions demo
permissions: build-permissions
@echo "Starting permissions demo..."
{{BIN_DIR}}/demo-permissions

# Build and run dangerous features example
dangerous: _check-dangerous build-dangerous
@echo "Starting dangerous features example..."
Expand Down Expand Up @@ -84,6 +104,34 @@ build-subagents:
cd {{PROJECT_DIR}}/examples/demo/subagents && go mod tidy && go build -o {{BIN_DIR}}/demo-subagents ./cmd/demo
@echo "Built: {{BIN_DIR}}/demo-subagents"

# Build sessions demo
build-sessions:
@echo "Building sessions demo..."
@mkdir -p {{BIN_DIR}}
cd {{PROJECT_DIR}}/examples/demo/sessions && go mod tidy && go build -o {{BIN_DIR}}/demo-sessions ./cmd/demo
@echo "Built: {{BIN_DIR}}/demo-sessions"

# Build MCP demo
build-mcp:
@echo "Building MCP demo..."
@mkdir -p {{BIN_DIR}}
cd {{PROJECT_DIR}}/examples/demo/mcp && go mod tidy && go build -o {{BIN_DIR}}/demo-mcp ./cmd/demo
@echo "Built: {{BIN_DIR}}/demo-mcp"

# Build retry demo
build-retry:
@echo "Building retry demo..."
@mkdir -p {{BIN_DIR}}
cd {{PROJECT_DIR}}/examples/demo/retry && go mod tidy && go build -o {{BIN_DIR}}/demo-retry ./cmd/demo
@echo "Built: {{BIN_DIR}}/demo-retry"

# Build permissions demo
build-permissions:
@echo "Building permissions demo..."
@mkdir -p {{BIN_DIR}}
cd {{PROJECT_DIR}}/examples/demo/permissions && go mod tidy && go build -o {{BIN_DIR}}/demo-permissions ./cmd/demo
@echo "Built: {{BIN_DIR}}/demo-permissions"

# Build dangerous example
build-dangerous:
@echo "Building dangerous example..."
Expand All @@ -99,7 +147,7 @@ build-enhanced:
@echo "Built: {{BIN_DIR}}/enhanced-example"

# Build all demos
build-all: build-streaming build-basic build-budget build-plugins build-subagents build-dangerous build-enhanced
build-all: build-streaming build-basic build-budget build-plugins build-subagents build-sessions build-mcp build-retry build-permissions build-dangerous build-enhanced
@echo "All demos built"

# Check dangerous environment requirements
Expand Down
2 changes: 1 addition & 1 deletion .justfiles/releases.just
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ check:
# Create signed git tag (use -a instead of -s if GPG not configured)
tag VERSION:
@echo "Creating signed tag {{VERSION}}..."
git tag -s {{VERSION}} -m "Release {{VERSION}}"
git tag -a {{VERSION}} -m "Release {{VERSION}}"
@echo "Tag {{VERSION}} created"
@echo "Run 'just release push' to push to remote"

Expand Down
Binary file modified docs/gif/mcp.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/gif/permissions.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/gif/retry.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/gif/sessions.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
124 changes: 112 additions & 12 deletions examples/demo/mcp/cmd/demo/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"

Expand All @@ -31,9 +32,47 @@ func isExitCommand(input string) bool {

func displayMCPStatus() {
fmt.Println("\n┌─────────────────────────────────────────────────────────────┐")
fmt.Printf("│ MCP Config: %-48s │\n", truncatePath(configFile))
fmt.Printf("│ Strict Mode: %-47v │\n", strictMode)
fmt.Printf("│ Allowed Tools: %-45d │\n", len(allowedTools))

// MCP Status
if configFile != "" {
fmt.Println("│ MCP Status: 🟢 Configured │")
fmt.Printf("│ Config File: %-47s │\n", truncatePath(configFile))
} else {
fmt.Println("│ MCP Status: ⚪ Not configured │")
fmt.Println("│ (Use /example to create sample config) │")
}

// Strict Mode
strictIcon := "⚪"
strictDesc := "off"
if strictMode {
strictIcon = "🔒"
strictDesc = "on (MCP servers only)"
}
fmt.Printf("│ Strict Mode: %s %-44s │\n", strictIcon, strictDesc)

// Tool Allowlist
if len(allowedTools) > 0 {
fmt.Printf("│ Allowed Tools: %-45d │\n", len(allowedTools))
// Show first 3 tools
maxShow := 3
for i, tool := range allowedTools {
if i >= maxShow {
remaining := len(allowedTools) - maxShow
fmt.Printf("│ ... and %d more%-42s │\n", remaining, "")
break
}
// Truncate long tool names
displayTool := tool
if len(displayTool) > 50 {
displayTool = displayTool[:47] + "..."
}
fmt.Printf("│ • %-56s │\n", displayTool)
}
} else {
fmt.Println("│ Allowed Tools: All tools enabled │")
}

fmt.Println("└─────────────────────────────────────────────────────────────┘")
}

Expand All @@ -60,6 +99,30 @@ func displayHelp() {
fmt.Println(" exit - Exit the demo")
}

func checkPrerequisites() error {
// Check for npx
fmt.Print("🔍 Checking for npx... ")
_, err := exec.LookPath("npx")
if err != nil {
fmt.Println("❌")
return fmt.Errorf("npx not found - install Node.js first")
}
fmt.Println("✅")

// Check for Node.js
fmt.Print("🔍 Checking Node.js version... ")
cmd := exec.Command("node", "--version")
output, err := cmd.Output()
if err != nil {
fmt.Println("❌")
return fmt.Errorf("node not found")
}
fmt.Printf("✅ %s", strings.TrimSpace(string(output)))

fmt.Println("✅ Prerequisites met")
return nil
}

func createExampleConfig() (string, error) {
// Create a filesystem MCP server config
config := map[string]interface{}{
Expand Down Expand Up @@ -112,19 +175,35 @@ func handleCommand(cmd string) bool {
return true

case "/example":
// Check prerequisites but don't block on failure
fmt.Println("\n📝 Creating MCP configuration...")
if err := checkPrerequisites(); err != nil {
fmt.Println("⚠️ Note: Prerequisites not met (for actual MCP server usage):")
fmt.Printf(" %v\n", err)
fmt.Println(" (Creating config anyway for demonstration)")
} else {
fmt.Println("✅ Prerequisites verified")
}

// Always create the config, regardless of prerequisites
path, err := createExampleConfig()
if err != nil {
fmt.Printf("✗ Failed to create example config: %v\n", err)
return true
}

configFile = path
allowedTools = []string{
"mcp__filesystem__list_directory",
"mcp__filesystem__read_file",
}
fmt.Printf("✓ Created example config at: %s\n", path)
fmt.Println(" Configured filesystem MCP server")
fmt.Println(" Allowed tools: list_directory, read_file")
fmt.Println(" 📦 Server: filesystem (via npx)")
fmt.Println(" 🔧 Command: npx -y @modelcontextprotocol/server-filesystem .")
fmt.Println(" 🛠️ Configured tools:")
fmt.Println(" • mcp__filesystem__list_directory")
fmt.Println(" • mcp__filesystem__read_file")
fmt.Println("\n💡 Try: 'List files in the current directory'")
return true

case "/strict":
Expand All @@ -150,10 +229,19 @@ func handleCommand(cmd string) bool {
case "/tools":
fmt.Println("\n🔧 Allowed MCP Tools:")
if len(allowedTools) == 0 {
fmt.Println(" (all tools allowed)")
}
for i, tool := range allowedTools {
fmt.Printf(" %d. %s\n", i+1, tool)
fmt.Println(" ⚠️ No allowlist configured - all tools are permitted")
fmt.Println("\n💡 Tip: Use /allow <tool> to restrict to specific tools")
} else {
for i, tool := range allowedTools {
// Parse tool name for better display
if strings.HasPrefix(tool, "mcp__") {
serverName := extractServerName(tool)
toolName := strings.TrimPrefix(tool, "mcp__"+serverName+"__")
fmt.Printf(" %d. 🔌 %s (server: %s)\n", i+1, toolName, serverName)
} else {
fmt.Printf(" %d. 🛠️ %s (built-in)\n", i+1, tool)
}
}
}
return true

Expand Down Expand Up @@ -185,6 +273,16 @@ func handleCommand(cmd string) bool {
return false
}

func extractServerName(toolName string) string {
// MCP tool names follow pattern: mcp__<server>__<tool>
// Example: mcp__filesystem__list_directory -> filesystem
parts := strings.Split(toolName, "__")
if len(parts) >= 2 {
return parts[1]
}
return "unknown"
}

func displayStreamingMessage(msg claude.Message) {
switch msg.Type {
case "system":
Expand All @@ -203,11 +301,13 @@ func displayStreamingMessage(msg claude.Message) {
}
} else if itemMap["type"] == "tool_use" {
if name, ok := itemMap["name"].(string); ok {
// Highlight MCP tools
// Highlight MCP tools with server info
if strings.HasPrefix(name, "mcp__") {
fmt.Printf("🔌 MCP Tool: %s\n", name)
serverName := extractServerName(name)
toolName := strings.TrimPrefix(name, "mcp__"+serverName+"__")
fmt.Printf("🔌 MCP Tool: %s (server: %s)\n", toolName, serverName)
} else {
fmt.Printf("🔧 Tool: %s\n", name)
fmt.Printf("🛠️ SDK Tool: %s (built-in)\n", name)
}
}
}
Expand Down
Loading
Loading