Skip to content

Commit

Permalink
use an npx and uvx shim with hermit for MCPs (#655)
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelneale authored and acekyd committed Jan 21, 2025
1 parent 2dc5e33 commit 550b456
Show file tree
Hide file tree
Showing 3 changed files with 217 additions and 1 deletion.
98 changes: 98 additions & 0 deletions ui/desktop/src/bin/npx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#!/bin/bash

# Enable strict mode to exit on errors and unset variables
set -euo pipefail

# Set log file
LOG_FILE="/tmp/mcp.log"

# Clear the log file at the start
> "$LOG_FILE"

# Function for logging
log() {
local MESSAGE="$1"
echo "$(date +'%Y-%m-%d %H:%M:%S') - $MESSAGE" | tee -a "$LOG_FILE"
}

# Trap errors and log them before exiting
trap 'log "An error occurred. Exiting with status $?."' ERR

log "Starting npx setup script."

# Ensure ~/.config/goose/mcp-hermit/bin exists
log "Creating directory ~/.config/goose/mcp-hermit/bin if it does not exist."
mkdir -p ~/.config/goose/mcp-hermit/bin

# Change to the ~/.config/goose/mcp-hermit directory
log "Changing to directory ~/.config/goose/mcp-hermit."
cd ~/.config/goose/mcp-hermit

# Check if hermit binary exists and download if not
if [ ! -f ~/.config/goose/mcp-hermit/bin/hermit ]; then
log "Hermit binary not found. Downloading hermit binary."
curl -fsSL "https://github.com/cashapp/hermit/releases/download/stable/hermit-$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m | sed 's/x86_64/amd64/' | sed 's/aarch64/arm64/').gz" \
| gzip -dc > ~/.config/goose/mcp-hermit/bin/hermit && chmod +x ~/.config/goose/mcp-hermit/bin/hermit
log "Hermit binary downloaded and made executable."
else
log "Hermit binary already exists. Skipping download."
fi

# Update PATH
export PATH=~/.config/goose/mcp-hermit/bin:$PATH
log "Updated PATH to include ~/.config/goose/mcp-hermit/bin."


# Verify hermit installation
log "Checking for hermit in PATH."
which hermit >> "$LOG_FILE"

# Initialize hermit
log "Initializing hermit."
hermit init >> "$LOG_FILE"

# Install Node.js using hermit
log "Installing Node.js with hermit."
hermit install node >> "$LOG_FILE"

# Verify installations
log "Verifying installation locations:"
log "hermit: $(which hermit)"
log "node: $(which node)"
log "npx: $(which npx)"


log "Checking for GOOSE_NPM_REGISTRY and GOOSE_NPM_CERT environment variables for custom npm registry setup..."
# Check if GOOSE_NPM_REGISTRY is set and accessible
if [ -n "${GOOSE_NPM_REGISTRY:-}" ] && curl -s --head --fail "$GOOSE_NPM_REGISTRY" > /dev/null; then
log "Checking custom goose registry availability: $GOOSE_NPM_REGISTRY"
log "$GOOSE_NPM_REGISTRY is accessible. Using it for npm registry."
export NPM_CONFIG_REGISTRY="$GOOSE_NPM_REGISTRY"

# Check if GOOSE_NPM_CERT is set and accessible
if [ -n "${GOOSE_NPM_CERT:-}" ] && curl -s --head --fail "$GOOSE_NPM_CERT" > /dev/null; then
log "Downloading certificate from: $GOOSE_NPM_CERT"
curl -sSL -o ~/.config/goose/mcp-hermit/cert.pem "$GOOSE_NPM_CERT"
if [ $? -eq 0 ]; then
log "Certificate downloaded successfully."
export NODE_EXTRA_CA_CERTS=~/.config/goose/mcp-hermit/cert.pem
else
log "Unable to download the certificate. Skipping certificate setup."
fi
else
log "GOOSE_NPM_CERT is either not set or not accessible. Skipping certificate setup."
fi

else
log "GOOSE_NPM_REGISTRY is either not set or not accessible. Falling back to default npm registry."
export NPM_CONFIG_REGISTRY="https://registry.npmjs.org/"
fi




# Final step: Execute npx with passed arguments
log "Executing 'npx' command with arguments: $*"
npx "$@" || log "Failed to execute 'npx' with arguments: $*"

log "npx setup script completed successfully."
99 changes: 99 additions & 0 deletions ui/desktop/src/bin/uvx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#!/bin/bash

# Enable strict mode to exit on errors and unset variables
set -euo pipefail

# Set log file
LOG_FILE="/tmp/mcp.log"

# Clear the log file at the start
> "$LOG_FILE"

# Function for logging
log() {
local MESSAGE="$1"
echo "$(date +'%Y-%m-%d %H:%M:%S') - $MESSAGE" | tee -a "$LOG_FILE"
}

# Trap errors and log them before exiting
trap 'log "An error occurred. Exiting with status $?."' ERR

log "Starting uvx setup script."

# Ensure ~/.config/goose/mcp-hermit/bin exists
log "Creating directory ~/.config/goose/mcp-hermit/bin if it does not exist."
mkdir -p ~/.config/goose/mcp-hermit/bin

# Change to the ~/.config/goose/mcp-hermit directory
log "Changing to directory ~/.config/goose/mcp-hermit."
cd ~/.config/goose/mcp-hermit

# Check if hermit binary exists and download if not
if [ ! -f ~/.config/goose/mcp-hermit/bin/hermit ]; then
log "Hermit binary not found. Downloading hermit binary."
curl -fsSL "https://github.com/cashapp/hermit/releases/download/stable/hermit-$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m | sed 's/x86_64/amd64/' | sed 's/aarch64/arm64/').gz" \
| gzip -dc > ~/.config/goose/mcp-hermit/bin/hermit && chmod +x ~/.config/goose/mcp-hermit/bin/hermit
log "Hermit binary downloaded and made executable."
else
log "Hermit binary already exists. Skipping download."
fi

# Update PATH
export PATH=~/.config/goose/mcp-hermit/bin:$PATH
log "Updated PATH to include ~/.config/goose/mcp-hermit/bin."


# Verify hermit installation
log "Checking for hermit in PATH."
which hermit >> "$LOG_FILE"

# Initialize hermit
log "Initializing hermit."
hermit init >> "$LOG_FILE"

# Install UV for python using hermit
log "Installing UV with hermit."
hermit install uv >> "$LOG_FILE"

# Verify installations
log "Verifying installation locations:"
log "hermit: $(which hermit)"
log "uv: $(which uv)"
log "uvx: $(which uvx)"


log "Checking for GOOSE_UV_REGISTRY and GOOSE_UV_CERT environment variables for custom python/pip/UV registry setup..."
# Check if GOOSE_UV_REGISTRY is set and accessible
if [ -n "${GOOSE_UV_REGISTRY:-}" ] && curl -s --head --fail "$GOOSE_UV_REGISTRY" > /dev/null; then
log "Checking custom goose registry availability: $GOOSE_UV_REGISTRY"
log "$GOOSE_UV_REGISTRY is accessible. Using it for UV registry."
export UV_INDEX_URL="$GOOSE_UV_REGISTRY"

if [ -n "${GOOSE_UV_CERT:-}" ] && curl -s --head --fail "$GOOSE_UV_CERT" > /dev/null; then
log "Downloading certificate from: $GOOSE_UV_CERT"
curl -sSL -o ~/.config/goose/mcp-hermit/cert.pem "$GOOSE_UV_CERT"
if [ $? -eq 0 ]; then
log "Certificate downloaded successfully."
export SSL_CLIENT_CERT=~/.config/goose/mcp-hermit/cert.pem
else
log "Unable to download the certificate. Skipping certificate setup."
fi
else
log "GOOSE_UV_CERT is either not set or not accessible. Skipping certificate setup."
fi

else
log "GOOSE_UV_REGISTRY is either not set or not accessible. Falling back to default pip registry."
export UV_INDEX_URL="https://pypi.org/simple"
fi






# Final step: Execute uvx with passed arguments
log "Executing 'uvx' command with arguments: $*"
uvx "$@" || log "Failed to execute 'uvx' with arguments: $*"

log "uvx setup script completed successfully."
21 changes: 20 additions & 1 deletion ui/desktop/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,33 @@ export const extendGoosed = async (config: SystemConfig) => {
console.error(`System ${config.cmd} is not supported right now`);
return;
}

// if its goosed - we will update the path to the binary
if (config.cmd === 'goosed') {
config = {
...config,
cmd: await window.electron.getBinaryPath('goosed')
};
}

if (config.cmd === 'npx') {
// use our special npx shim which uses hermit.
config = {
...config,
cmd: await window.electron.getBinaryPath('npx')
};
}

if (config.cmd === 'uvx') {
// use our special uvx shim which uses hermit.
config = {
...config,
cmd: await window.electron.getBinaryPath('uvx')
};
}



}

try {
Expand Down

0 comments on commit 550b456

Please sign in to comment.