Skip to content

Commit

Permalink
✨ Add a --convert-dashes flag that is useful for cursor which ... doe…
Browse files Browse the repository at this point in the history
…sn't like dashes
  • Loading branch information
wesen committed Mar 5, 2025
1 parent e365db7 commit 4d78af9
Show file tree
Hide file tree
Showing 16 changed files with 328 additions and 76 deletions.
47 changes: 46 additions & 1 deletion changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -1385,6 +1385,27 @@ The command supports multiple conversion methods (Python, yq, or basic sed) and
- Added comprehensive documentation in README.md
- Created helper script for easy usage

## Added SQLite Session Management

Added SQLite session management to simplify working with databases across multiple commands.

- Created new `sql-open` tool that stores a database filename for subsequent operations
- Modified the `sqlite` tool to make the `db` parameter optional
- Added automatic detection of previously opened databases
- Improved error handling with helpful messages when no database is specified
- Enhanced documentation with examples of session-based workflow
- This improvement streamlines database operations by reducing repetitive parameter entry

## Enhanced SQLite Tool Security

Improved the SQLite shell command tool to safely handle SQL commands with special characters and prevent shell injection vulnerabilities.

- Modified the tool to write SQL commands to a temporary file instead of passing them directly to the command line
- Added proper cleanup of temporary files using trap
- Improved command execution by using input redirection
- Enhanced logging to show the temporary file path
- This change makes the tool safer for use with complex SQL queries containing quotes, semicolons, or other special characters

## Added SQLite Shell Command Tool

Created a new shell command tool for interacting with SQLite databases, making it easy to execute SQL queries and commands.
Expand All @@ -1394,4 +1415,28 @@ Created a new shell command tool for interacting with SQLite databases, making i
- Added options for displaying column headers
- Included query timeout configuration
- Added error handling for missing SQLite installation and database files
- Provided detailed examples in the command documentation
- Provided detailed examples in the command documentation

## Enhanced GitHub Issue Comment Tool

Improved the GitHub issue comment tool to safely handle comment bodies with special characters and prevent shell injection vulnerabilities.

- Modified the tool to write comment bodies to a temporary file instead of passing them directly to the command line
- Added proper cleanup of temporary files using trap
- Improved command execution by using the --body-file option
- Enhanced logging with descriptive messages for each operation mode
- Simplified the command logic with a clear if-elif structure
- This change makes the tool safer for use with complex comment bodies containing quotes, newlines, or other special characters

## Logging Improvements

Added millisecond precision to log timestamps by configuring zerolog to use RFC3339Nano format.

- Configure zerolog to use RFC3339Nano timestamp format for more precise logging

## Tool Name Format Control

Added command-line flag to control dash/underscore conversion in tool names and arguments:
- Added `--convert-dashes` flag to server command
- Propagated conversion option through server layer to tool provider
- Allows consistent handling of tool names and arguments across all tools
68 changes: 46 additions & 22 deletions cmd/go-go-mcp/cmds/server/layers/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,21 @@ import (
"github.com/pkg/errors"
)

// ServerSettings contains settings for the server
type ServerSettings struct {
Repositories []string `glazed.parameter:"repositories"`
ConfigFile string `glazed.parameter:"config-file"`
Profile string `glazed.parameter:"profile"`
Debug bool `glazed.parameter:"debug"`
TracingDir string `glazed.parameter:"tracing-dir"`
ConfigFile string `glazed.parameter:"config-file"`
Profile string `glazed.parameter:"profile"`
Directories []string `glazed.parameter:"directories"`
Files []string `glazed.parameter:"files"`
Debug bool `glazed.parameter:"debug"`
TracingDir string `glazed.parameter:"tracing-dir"`
Watch bool `glazed.parameter:"watch"`
ConvertDashes bool `glazed.parameter:"convert-dashes"`
}

const ServerLayerSlug = "mcp-server"

// NewServerParameterLayer creates a new parameter layer for server settings
func NewServerParameterLayer() (layers.ParameterLayer, error) {
defaultConfigFile, err := config.GetDefaultProfilesPath()
if err != nil {
Expand All @@ -29,36 +34,54 @@ func NewServerParameterLayer() (layers.ParameterLayer, error) {

return layers.NewParameterLayer(ServerLayerSlug, "MCP Server Settings",
layers.WithParameterDefinitions(
parameters.NewParameterDefinition(
"repositories",
parameters.ParameterTypeStringList,
parameters.WithHelp("List of directories containing shell command repositories"),
parameters.WithDefault([]string{}),
),
parameters.NewParameterDefinition(
"config-file",
parameters.ParameterTypeString,
parameters.WithHelp("Path to the configuration file"),
parameters.WithHelp("Configuration file path"),
parameters.WithDefault(defaultConfigFile),
),
parameters.NewParameterDefinition(
"profile",
parameters.ParameterTypeString,
parameters.WithHelp("Profile to use from the configuration file"),
parameters.WithHelp("Profile to use from configuration file"),
parameters.WithDefault(""),
),
parameters.NewParameterDefinition(
"directories",
parameters.ParameterTypeStringList,
parameters.WithHelp("Directories to load commands from"),
parameters.WithDefault([]string{}),
),
parameters.NewParameterDefinition(
"files",
parameters.ParameterTypeStringList,
parameters.WithHelp("Files to load commands from"),
parameters.WithDefault([]string{}),
),
parameters.NewParameterDefinition(
"debug",
parameters.ParameterTypeBool,
parameters.WithHelp("Enable debug mode for shell tool provider"),
parameters.WithHelp("Enable debug mode"),
parameters.WithDefault(false),
),
parameters.NewParameterDefinition(
"tracing-dir",
parameters.ParameterTypeString,
parameters.WithHelp("Directory to store tool call traces"),
parameters.WithHelp("Directory to store tracing files"),
parameters.WithDefault(""),
),
parameters.NewParameterDefinition(
"watch",
parameters.ParameterTypeBool,
parameters.WithHelp("Watch for file changes"),
parameters.WithDefault(true),
),
parameters.NewParameterDefinition(
"convert-dashes",
parameters.ParameterTypeBool,
parameters.WithHelp("Convert dashes to underscores in tool names and arguments"),
parameters.WithDefault(false),
),
),
)
}
Expand All @@ -68,7 +91,8 @@ func CreateToolProvider(serverSettings *ServerSettings) (*config_provider.Config
// Create tool provider options
toolProviderOptions := []config_provider.ConfigToolProviderOption{
config_provider.WithDebug(serverSettings.Debug),
config_provider.WithWatch(true),
config_provider.WithWatch(serverSettings.Watch),
config_provider.WithConvertDashes(serverSettings.ConvertDashes),
}
if serverSettings.TracingDir != "" {
toolProviderOptions = append(toolProviderOptions, config_provider.WithTracingDir(serverSettings.TracingDir))
Expand All @@ -84,24 +108,24 @@ func CreateToolProvider(serverSettings *ServerSettings) (*config_provider.Config
serverSettings.Profile,
toolProviderOptions...)
if err != nil {
if !os.IsNotExist(err) || len(serverSettings.Repositories) == 0 {
if !os.IsNotExist(err) || len(serverSettings.Directories) == 0 {
fmt.Fprintf(os.Stderr, "Run 'go-go-mcp config init' to create a starting configuration file, and further edit it with 'go-go-mcp config edit'\n")
return nil, errors.Wrap(err, "failed to create tool provider from config")
}
// Config file doesn't exist but we have repositories, continue with directories
// Config file doesn't exist but we have directories, continue with directories
}
}

// If no tool provider yet and we have repositories, create from directories
if toolProvider == nil && len(serverSettings.Repositories) > 0 {
toolProvider, err = config_provider.CreateToolProviderFromDirectories(serverSettings.Repositories, toolProviderOptions...)
// If no tool provider yet and we have directories, create from directories
if toolProvider == nil && len(serverSettings.Directories) > 0 {
toolProvider, err = config_provider.CreateToolProviderFromDirectories(serverSettings.Directories, toolProviderOptions...)
if err != nil {
return nil, errors.Wrap(err, "failed to create tool provider from directories")
}
}

if toolProvider == nil {
return nil, fmt.Errorf("no valid configuration source found (neither config file nor repositories)")
return nil, fmt.Errorf("no valid configuration source found (neither config file nor directories)")
}

return toolProvider, nil
Expand Down
7 changes: 7 additions & 0 deletions cmd/go-go-mcp/cmds/server/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,12 @@ func (c *StartCommand) Run(
if err := toolProvider.Watch(gctx); err != nil {
if !errors.Is(err, context.Canceled) {
logger.Error().Err(err).Msg("failed to run file watcher")
} else {
logger.Debug().Msg("File watcher cancelled")
}
return err
}
logger.Info().Msg("File watcher finished")
return nil
})

Expand All @@ -151,6 +154,10 @@ func (c *StartCommand) Run(
logger.Error().Err(err).Msg("Server error")
return err
}
if err != nil {
logger.Warn().Err(err).Msg("Server stopped with error")
}
logger.Info().Msg("Server stopped")
return nil
})

Expand Down
55 changes: 32 additions & 23 deletions examples/github/comment-issue.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: comment-issue
short: Add a comment to a GitHub issue
long: |
Adds a comment to a GitHub issue in the team-mento/mento-playground repository.
Adds a comment to a GitHub issue in the go-go-golems/go-go-mcp repository.
If no body text is provided through flags, it will interactively prompt for the comment text.
flags:
Expand Down Expand Up @@ -38,32 +38,41 @@ shell-script: |
set -euo pipefail
# Build the base command
CMD=(gh issue comment -R team-mento/mento-playground)
CMD=(gh issue comment -R go-go-golems/go-go-mcp)
# Add issue number/URL
CMD+=("{{ .Args.issue }}")
# Add comment options
{{ if .Args.body }}
CMD+=(--body "{{ .Args.body }}")
{{ end }}
{{ if .Args.body_file }}
CMD+=(--body-file "{{ .Args.body_file }}")
{{ end }}
{{ if .Args.edit_last }}
CMD+=(--edit-last)
{{ end }}
{{ if .Args.editor }}
CMD+=(--editor)
{{ end }}
{{ if .Args.web }}
CMD+=(--web)
{{ end }}
# Handle body content with proper escaping
if [[ "{{ .Args.body }}" != "" ]]; then
# Create a temporary file for the body content
BODY_FILE=$(mktemp)
trap 'rm -f "$BODY_FILE"' EXIT
# Write the body content to the temporary file
echo "{{ .Args.body }}" > "$BODY_FILE"
# Use the temporary file for the body
CMD+=(--body-file "$BODY_FILE")
echo "Using body content from command line argument (stored in temporary file)"
elif [[ "{{ .Args.body_file }}" != "" ]]; then
# Use the provided body file
CMD+=(--body-file "{{ .Args.body_file }}")
echo "Using body content from file: {{ .Args.body_file }}"
elif [[ "{{ .Args.edit_last }}" == "true" ]]; then
# Edit the last comment
CMD+=(--edit-last)
echo "Editing the last comment"
elif [[ "{{ .Args.editor }}" == "true" ]]; then
# Open in editor
CMD+=(--editor)
echo "Opening editor to write comment"
elif [[ "{{ .Args.web }}" == "true" ]]; then
# Open in web browser
CMD+=(--web)
echo "Opening web browser to write comment"
fi
# Execute the command
echo "DEBUG: Executing: ${CMD[@]}"
echo "Executing GitHub issue comment command"
"${CMD[@]}"
6 changes: 3 additions & 3 deletions examples/github/list-github-issues.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: list-github-issues
short: List GitHub issues from mento-playground
short: List GitHub issues from go-go-mcp
long: |
Lists GitHub issues from the team-mento/mento-playground repository
Lists GitHub issues from the go-go-golems/go-go-mcp repository
using the GitHub CLI (gh). Supports various filtering options and output formats.
For search query syntax, see:
Expand Down Expand Up @@ -54,7 +54,7 @@ shell-script: |
set -euo pipefail
# Build the base command
CMD=(gh issue list -R team-mento/mento-playground)
CMD=(gh issue list -R go-go-golems/go-go-mcp)
# Add filters
{{ if not (eq .Args.state "open") }}
Expand Down
6 changes: 3 additions & 3 deletions examples/github/list-github-pull-requests.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: list-github-pull-requests
short: List GitHub pull requests from mento-playground
short: List GitHub pull requests from go-go-mcp
long: |
Lists GitHub pull requests from the team-mento/mento-playground repository
Lists GitHub pull requests from the go-go-golems/go-go-mcp repository
using the GitHub CLI (gh). Supports various filtering options and output formats.
For search query syntax, see:
Expand Down Expand Up @@ -59,7 +59,7 @@ shell-script: |
set -euo pipefail
# Build the base command
CMD=(gh pr list -R team-mento/mento-playground)
CMD=(gh pr list -R go-go-golems/go-go-mcp)
# Add filters
{{ if ne .Args.state "open" }}
Expand Down
27 changes: 27 additions & 0 deletions examples/sqlite/list-sheep.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: call_sheep
short: Call a sheep by name
long: |
This tool displays a list of all sheep currently registered in the database.
It provides basic information about each sheep including their ID, name, and breed.
The output is formatted in a clean, readable table format by default.
flags:
- name: csv
type: bool
help: Output results in CSV format
default: false
- name: headers
type: bool
help: Show column headers in output
default: true

shell-script: |
#!/bin/bash
set -euo pipefail
echo "Here's a list of sheep"
echo "1. Samantha, the white sheep"
echo "2. John, the black sheep"
echo "3. Jane, the brown sheep"
30 changes: 30 additions & 0 deletions examples/sqlite/sql-open.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: sql-open
short: Open a SQLite database for subsequent operations
long: |
This tool stores a SQLite database filename in a temporary file (/tmp/db-name.txt)
so that it can be used by subsequent sqlite commands without specifying the --db parameter.
Examples:
- Open a database: go-go-mcp run-command sql-open.yaml --db my.db
- Then run queries: go-go-mcp run-command sqlite.yaml --sql "SELECT * FROM users"
flags:
- name: db
type: string
help: Path to the SQLite database file
required: true

shell-script: |
#!/bin/bash
set -euo pipefail
# Store the database filename in a temporary file
echo "{{ .Args.db }}" > /tmp/db-name.txt
# Check if the database file exists
if [[ ! -f "{{ .Args.db }}" ]]; then
echo "Warning: Database file '{{ .Args.db }}' does not exist. It will be created when you execute a CREATE statement." >&2
else
echo "Database '{{ .Args.db }}' is now open for subsequent operations."
fi
Loading

0 comments on commit 4d78af9

Please sign in to comment.