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
9 changes: 8 additions & 1 deletion cmd/app/clone.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,12 +176,19 @@ func runClone(cmd *cobra.Command) error {

// Generate env file
cmd.Println("\nGenerating .env file...")
envFilePath, _, err := generateEnvFile(finalDir)
envFilePath, envVars, err := generateEnvFile(finalDir)
if err != nil {
return errors.WrapError("failed to generate .env file", err)
}
cmd.Printf("Successfully generated .env file at: %s\n", envFilePath)

// Generate .mcp.json for Claude Code
if _, err := utils.GenerateMcpConfig(finalDir, envVars); err != nil {
cmd.Printf("Warning: Failed to generate .mcp.json: %v\n", err)
} else {
cmd.Println("✓ Generated .mcp.json for Claude Code")
}

cmd.Println("\n✓ Application clone complete!")

printSuccessMessage(cmd, finalDir)
Expand Down
9 changes: 8 additions & 1 deletion cmd/app/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,11 +210,18 @@ func runCreate(cobraCmd *cobra.Command) error {

// Generate .env file
cobraCmd.Println("\nGenerating .env file...")
envFilePath, _, err := generateEnvFile(targetDir)
envFilePath, envVars, err := generateEnvFile(targetDir)
if err != nil {
cobraCmd.Printf("Warning: Failed to generate .env file: %v\n", err)
} else {
cobraCmd.Printf("✓ Generated .env file at: %s\n", envFilePath)

// Generate .mcp.json for Claude Code
if _, err := utils.GenerateMcpConfig(targetDir, envVars); err != nil {
cobraCmd.Printf("Warning: Failed to generate .mcp.json: %v\n", err)
} else {
cobraCmd.Println("✓ Generated .mcp.json for Claude Code")
}
}

printSuccessMessage(cobraCmd, appName)
Expand Down
14 changes: 7 additions & 7 deletions cmd/app/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,25 +203,25 @@ func ensureGitRepositoryWithRetries(cmd *cobra.Command, workingDir, sshURL, http

// generateEnvFile generates a .env file for the application in the specified directory.
// If targetDir is empty, it uses the current git repository root.
// Returns the path to the generated file and the number of variables written.
func generateEnvFile(targetDir string) (string, int, error) {
// Returns the path to the generated file and the env vars map.
func generateEnvFile(targetDir string) (string, map[string]string, error) {
applicationID, orgID, err := getApplicationAndOrgIDFromDir(targetDir)
if err != nil {
return "", 0, errors.WrapError("failed to get application ID", err)
return "", nil, errors.WrapError("failed to get application ID", err)
}

apiClient := singletons.GetAPIClient()

envVars, err := apiClient.GetApplicationEnv(orgID, applicationID)
if err != nil {
return "", 0, errors.WrapError("failed to get environment variables", err)
return "", nil, errors.WrapError("failed to get environment variables", err)
}

gitRoot := targetDir
if gitRoot == "" {
gitRoot, err = git.GetRepoRoot()
if err != nil {
return "", 0, errors.WrapError("failed to get git repository root", err)
return "", nil, errors.WrapError("failed to get git repository root", err)
}
}

Expand All @@ -237,8 +237,8 @@ func generateEnvFile(targetDir string) (string, int, error) {
// Write to .env file
err = os.WriteFile(envFilePath, []byte(envContent.String()), 0644)
if err != nil {
return "", 0, errors.WrapError("failed to write .env file", err)
return "", nil, errors.WrapError("failed to write .env file", err)
}

return envFilePath, len(envVars), nil
return envFilePath, envVars, nil
}
9 changes: 8 additions & 1 deletion cmd/app/link.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,19 @@ func runLink(cmd *cobra.Command, applicationID string) error {

// Step 4: Generate .env file
cmd.Println("Generating .env file...")
envFilePath, _, err := generateEnvFile(workingDir)
envFilePath, envVars, err := generateEnvFile(workingDir)
if err != nil {
return errors.WrapError("failed to generate .env file", err)
}
cmd.Printf("✓ Generated .env file at: %s\n", envFilePath)

// Generate .mcp.json for Claude Code
if _, err := utils.GenerateMcpConfig(workingDir, envVars); err != nil {
cmd.Printf("Warning: Failed to generate .mcp.json: %v\n", err)
} else {
cmd.Println("✓ Generated .mcp.json for Claude Code")
}

// Step 5: Print success and run start
printLinkSuccessMessage(cmd, workingDir, appInfo.Name)

Expand Down
8 changes: 7 additions & 1 deletion cmd/app/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"path/filepath"

"github.com/major-technology/cli/errors"
"github.com/major-technology/cli/utils"
"github.com/spf13/cobra"
)

Expand All @@ -21,11 +22,16 @@ var startCmd = &cobra.Command{

func runStart(cobraCmd *cobra.Command) error {
// Generate .env file
_, _, err := generateEnvFile("")
_, envVars, err := generateEnvFile("")
if err != nil {
return errors.WrapError("failed to generate .env file", err)
}

// Generate .mcp.json for Claude Code
if _, err := utils.GenerateMcpConfig("", envVars); err != nil {
cobraCmd.Printf("Warning: Failed to generate .mcp.json: %v\n", err)
}

// Run start in current directory
return RunStartInDir(cobraCmd, "")
}
Expand Down
17 changes: 12 additions & 5 deletions cmd/demo/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,11 +153,18 @@ func runCreate(cobraCmd *cobra.Command) error {

// Generate .env file
cobraCmd.Println("\nGenerating .env file...")
envFilePath, _, err := generateEnvFile(targetDir, orgID, createResp.ApplicationID)
envFilePath, envVars, err := generateEnvFile(targetDir, orgID, createResp.ApplicationID)
if err != nil {
cobraCmd.Printf("Warning: Failed to generate .env file: %v\n", err)
} else {
cobraCmd.Printf("✓ Generated .env file at: %s\n", envFilePath)

// Generate .mcp.json for Claude Code
if _, err := utils.GenerateMcpConfig(targetDir, envVars); err != nil {
cobraCmd.Printf("Warning: Failed to generate .mcp.json: %v\n", err)
} else {
cobraCmd.Println("✓ Generated .mcp.json for Claude Code")
}
}

printSuccessMessage(cobraCmd, createResp.RepositoryName)
Expand Down Expand Up @@ -228,12 +235,12 @@ func printSuccessMessage(cobraCmd *cobra.Command, appName string) {
}

// generateEnvFile generates a .env file for the application in the specified directory.
func generateEnvFile(targetDir, orgID, applicationID string) (string, int, error) {
func generateEnvFile(targetDir, orgID, applicationID string) (string, map[string]string, error) {
apiClient := singletons.GetAPIClient()

envVars, err := apiClient.GetApplicationEnv(orgID, applicationID)
if err != nil {
return "", 0, errors.WrapError("failed to get environment variables", err)
return "", nil, errors.WrapError("failed to get environment variables", err)
}

// Create .env file path
Expand All @@ -248,8 +255,8 @@ func generateEnvFile(targetDir, orgID, applicationID string) (string, int, error
// Write to .env file
err = os.WriteFile(envFilePath, []byte(envContent.String()), 0644)
if err != nil {
return "", 0, errors.WrapError("failed to write .env file", err)
return "", nil, errors.WrapError("failed to write .env file", err)
}

return envFilePath, len(envVars), nil
return envFilePath, envVars, nil
}
91 changes: 91 additions & 0 deletions utils/mcp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package utils

import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"

"github.com/major-technology/cli/clients/git"
)

// GenerateMcpConfig generates a .mcp.json file for Claude Code in the specified directory.
// If targetDir is empty, it uses the current git repository root.
// It uses the env vars from the application env endpoint to construct the MCP server config
// pointing to the Go API's resource MCP endpoint.
func GenerateMcpConfig(targetDir string, envVars map[string]string) (string, error) {
apiBaseURL := envVars["MAJOR_API_BASE_URL"]
jwtToken := envVars["MAJOR_JWT_TOKEN"]
applicationID := envVars["APPLICATION_ID"]

if apiBaseURL == "" || jwtToken == "" || applicationID == "" {
return "", fmt.Errorf("missing required env vars for MCP config (MAJOR_API_BASE_URL, MAJOR_JWT_TOKEN, APPLICATION_ID)")
}

if targetDir == "" {
var err error
targetDir, err = git.GetRepoRoot()
if err != nil {
return "", fmt.Errorf("failed to get git repository root: %w", err)
}
}

mcpURL := fmt.Sprintf("%s/internal/apps/v1/%s/mcp", apiBaseURL, applicationID)

config := map[string]any{
"mcpServers": map[string]any{
"major": map[string]any{
"type": "http",
"url": mcpURL,
"headers": map[string]string{
"x-major-jwt": jwtToken,
},
},
},
}

jsonBytes, err := json.MarshalIndent(config, "", " ")
if err != nil {
return "", fmt.Errorf("failed to marshal MCP config: %w", err)
}

jsonBytes = append(jsonBytes, '\n')

mcpPath := filepath.Join(targetDir, ".mcp.json")
if err := os.WriteFile(mcpPath, jsonBytes, 0644); err != nil {
return "", fmt.Errorf("failed to write .mcp.json file: %w", err)
}

// Ensure .mcp.json is in .gitignore
ensureGitignoreEntry(targetDir, ".mcp.json")

return mcpPath, nil
}

// ensureGitignoreEntry appends an entry to .gitignore if it's not already present.
func ensureGitignoreEntry(dir, entry string) {
gitignorePath := filepath.Join(dir, ".gitignore")

content, err := os.ReadFile(gitignorePath)
if err != nil {
// No .gitignore file, create one
_ = os.WriteFile(gitignorePath, []byte(entry+"\n"), 0644)
return
}

// Check if entry already exists
lines := strings.Split(string(content), "\n")
for _, line := range lines {
if strings.TrimSpace(line) == entry {
return
}
}

// Append the entry with a preceding newline if file doesn't end with one
suffix := entry + "\n"
if len(content) > 0 && content[len(content)-1] != '\n' {
suffix = "\n" + suffix
}
_ = os.WriteFile(gitignorePath, append(content, []byte(suffix)...), 0644)
}