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
2 changes: 1 addition & 1 deletion api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

50 changes: 30 additions & 20 deletions cmd/kmcp/cmd/add_tool.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ package cmd

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

"kagent.dev/kmcp/pkg/templates"

"github.com/spf13/cobra"
"kagent.dev/kmcp/pkg/frameworks"
"kagent.dev/kmcp/pkg/manifest"
Expand Down Expand Up @@ -34,6 +37,7 @@ var (
addToolDescription string
addToolForce bool
addToolInteractive bool
addToolDir string
)

func init() {
Expand All @@ -42,6 +46,7 @@ func init() {
addToolCmd.Flags().StringVarP(&addToolDescription, "description", "d", "", "Tool description")
addToolCmd.Flags().BoolVarP(&addToolForce, "force", "f", false, "Overwrite existing tool file")
addToolCmd.Flags().BoolVarP(&addToolInteractive, "interactive", "i", false, "Interactive tool creation")
addToolCmd.Flags().StringVar(&addToolDir, "project-dir", "", "Project directory (default: current directory)")
}

func runAddTool(_ *cobra.Command, args []string) error {
Expand All @@ -52,12 +57,26 @@ func runAddTool(_ *cobra.Command, args []string) error {
return fmt.Errorf("invalid tool name: %w", err)
}

// Get project root and framework
projectRoot, err := findProjectRoot()
if err != nil {
return err
// Determine project directory
projectDirectory := addToolDir
if projectDirectory == "" {
var err error
projectDirectory, err = os.Getwd()
if err != nil {
return fmt.Errorf("failed to get current directory: %w", err)
}
} else {
// Convert relative path to absolute path
if !filepath.IsAbs(projectDirectory) {
cwd, err := os.Getwd()
if err != nil {
return fmt.Errorf("failed to get current directory: %w", err)
}
projectDirectory = filepath.Join(cwd, projectDirectory)
}
}
manifestManager := manifest.NewManager(projectRoot)

manifestManager := manifest.NewManager(projectDirectory)
projectManifest, err := manifestManager.Load()
if err != nil {
return fmt.Errorf("failed to load project manifest: %w", err)
Expand All @@ -78,10 +97,10 @@ func runAddTool(_ *cobra.Command, args []string) error {
}

if addToolInteractive {
return createToolInteractive(toolName, projectRoot, framework)
return createToolInteractive(toolName, projectDirectory, framework)
}

return createTool(toolName, projectRoot, framework)
return createTool(toolName, projectDirectory, framework)
}

func validateToolName(name string) error {
Expand Down Expand Up @@ -168,23 +187,14 @@ func generateTool(toolName, projectRoot, framework string) error {
return err
}

config := map[string]interface{}{
"description": addToolDescription,
config := templates.ToolConfig{
ToolName: toolName,
Description: addToolDescription,
}

if err := generator.GenerateTool(projectRoot, toolName, config); err != nil {
if err := generator.GenerateTool(projectRoot, config); err != nil {
return fmt.Errorf("failed to generate tool file: %w", err)
}

fmt.Printf("✅ Successfully created tool: %s\n", toolName)
fmt.Printf("📁 Generated file: src/tools/%s.py\n", toolName)
fmt.Printf("🔄 Updated tools/__init__.py with new tool import\n")

fmt.Printf("\nNext steps:\n")
fmt.Printf("1. Edit src/tools/%s.py to implement your tool logic\n", toolName)
fmt.Printf("2. Configure any required environment variables in kmcp.yaml\n")
fmt.Printf("3. Run 'uv run python src/main.py' to start the server\n")
fmt.Printf("4. Run 'uv run pytest tests/' to test your tool\n")

return nil
}
63 changes: 27 additions & 36 deletions cmd/kmcp/cmd/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,6 @@ const (

var deployCmd = &cobra.Command{
Use: "deploy",
Short: "Deploy to Kubernetes",
Long: `Deploy components to Kubernetes clusters.

This command provides functionality to deploy MCP servers and the KMCP controller
to Kubernetes clusters.`,
}

// deployMCPCmd deploys an MCP server to Kubernetes
var deployMCPCmd = &cobra.Command{
Use: "mcp [name]",
Short: "Deploy MCP server to Kubernetes",
Long: `Deploy an MCP server to Kubernetes by generating MCPServer CRDs.

Expand All @@ -53,15 +43,15 @@ Secret namespace will be overridden with the deployment namespace to avoid the n
to enable cross-namespace references.

Examples:
kmcp deploy mcp # Deploy with project name to cluster
kmcp deploy mcp my-server # Deploy with custom name
kmcp deploy mcp --namespace staging # Deploy to staging namespace
kmcp deploy mcp --dry-run # Generate manifest without applying to cluster
kmcp deploy mcp --image custom:tag # Use custom image
kmcp deploy mcp --transport http # Use HTTP transport
kmcp deploy mcp --output deploy.yaml # Save to file
kmcp deploy mcp --file /path/to/kmcp.yaml # Use custom kmcp.yaml file
kmcp deploy mcp --environment staging # Target environment for deployment (e.g., staging, production)`,
kmcp deploy # Deploy with project name to cluster
kmcp deploy my-server # Deploy with custom name
kmcp deploy --namespace staging # Deploy to staging namespace
kmcp deploy --dry-run # Generate manifest without applying to cluster
kmcp deploy --image custom:tag # Use custom image
kmcp deploy --transport http # Use HTTP transport
kmcp deploy --output deploy.yaml # Save to file
kmcp deploy --file /path/to/kmcp.yaml # Use custom kmcp.yaml file
kmcp deploy --environment staging # Target environment for deployment (e.g., staging, production)`,
Args: cobra.MaximumNArgs(1),
RunE: runDeployMCP,
}
Expand All @@ -86,23 +76,20 @@ var (
func init() {
rootCmd.AddCommand(deployCmd)

// Add subcommands
deployCmd.AddCommand(deployMCPCmd)

// MCP deployment flags
deployMCPCmd.Flags().StringVarP(&deployNamespace, "namespace", "n", "default", "Kubernetes namespace")
deployMCPCmd.Flags().BoolVar(&deployDryRun, "dry-run", false, "Generate manifest without applying to cluster")
deployMCPCmd.Flags().StringVarP(&deployOutput, "output", "o", "", "Output file for the generated YAML")
deployMCPCmd.Flags().StringVar(&deployImage, "image", "", "Docker image to deploy (overrides build image)")
deployMCPCmd.Flags().StringVar(&deployTransport, "transport", "", "Transport type (stdio, http)")
deployMCPCmd.Flags().IntVar(&deployPort, "port", 0, "Container port (default: from project config)")
deployMCPCmd.Flags().IntVar(&deployTargetPort, "target-port", 0, "Target port for HTTP transport")
deployMCPCmd.Flags().StringVar(&deployCommand, "command", "", "Command to run (overrides project config)")
deployMCPCmd.Flags().StringSliceVar(&deployArgs, "args", []string{}, "Command arguments")
deployMCPCmd.Flags().StringSliceVar(&deployEnv, "env", []string{}, "Environment variables (KEY=VALUE)")
deployMCPCmd.Flags().BoolVar(&deployForce, "force", false, "Force deployment even if validation fails")
deployMCPCmd.Flags().StringVarP(&deployFile, "file", "f", "", "Path to kmcp.yaml file (default: current directory)")
deployMCPCmd.Flags().StringVar(
deployCmd.Flags().StringVarP(&deployNamespace, "namespace", "n", "default", "Kubernetes namespace")
deployCmd.Flags().BoolVar(&deployDryRun, "dry-run", false, "Generate manifest without applying to cluster")
deployCmd.Flags().StringVarP(&deployOutput, "output", "o", "", "Output file for the generated YAML")
deployCmd.Flags().StringVar(&deployImage, "image", "", "Docker image to deploy (overrides build image)")
deployCmd.Flags().StringVar(&deployTransport, "transport", "", "Transport type (stdio, http)")
deployCmd.Flags().IntVar(&deployPort, "port", 0, "Container port (default: from project config)")
deployCmd.Flags().IntVar(&deployTargetPort, "target-port", 0, "Target port for HTTP transport")
deployCmd.Flags().StringVar(&deployCommand, "command", "", "Command to run (overrides project config)")
deployCmd.Flags().StringSliceVar(&deployArgs, "args", []string{}, "Command arguments")
deployCmd.Flags().StringSliceVar(&deployEnv, "env", []string{}, "Environment variables (KEY=VALUE)")
deployCmd.Flags().BoolVar(&deployForce, "force", false, "Force deployment even if validation fails")
deployCmd.Flags().StringVarP(&deployFile, "file", "f", "", "Path to kmcp.yaml file (default: current directory)")
deployCmd.Flags().StringVar(
&deployEnvironment,
"environment",
"staging",
Expand Down Expand Up @@ -167,7 +154,7 @@ func runDeployMCP(_ *cobra.Command, args []string) error {

// Add YAML document separator and standard header
yamlContent := fmt.Sprintf(
"---\n# MCPServer deployment generated by kmcp deploy mcp\n# Project: %s\n# Framework: %s\n%s",
"---\n# MCPServer deployment generated by kmcp deploy\n# Project: %s\n# Framework: %s\n%s",
projectManifest.Name,
projectManifest.Framework,
string(yamlData),
Expand Down Expand Up @@ -376,6 +363,8 @@ func sanitizeLabelValue(value string) string {

func getDefaultCommand(framework string) string {
switch framework {
case manifest.FrameworkMCPGo:
return "./server"
default:
return "python"
}
Expand All @@ -385,6 +374,8 @@ func getDefaultArgs(framework string) []string {
switch framework {
case manifest.FrameworkFastMCPPython:
return []string{"src/main.py"}
case manifest.FrameworkMCPGo:
return []string{}
default:
return []string{"src/main.py"}
}
Expand Down
Loading
Loading