mirror of
https://github.com/aljazceru/goose.git
synced 2025-12-18 14:44:21 +01:00
feat(extensions): add Java/JDK support for MCP servers (#1816)
This commit is contained in:
@@ -74,6 +74,8 @@ RUN apt-get update && apt-get install -y \
|
|||||||
# Python
|
# Python
|
||||||
python3 \
|
python3 \
|
||||||
python3-pip \
|
python3-pip \
|
||||||
|
# Java
|
||||||
|
openjdk-17-jdk \
|
||||||
# Additional tools
|
# Additional tools
|
||||||
tree \
|
tree \
|
||||||
tmux \
|
tmux \
|
||||||
@@ -82,6 +84,9 @@ RUN apt-get update && apt-get install -y \
|
|||||||
# Install uv using curl
|
# Install uv using curl
|
||||||
RUN curl -LsSf https://astral.sh/uv/install.sh | sh
|
RUN curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||||
|
|
||||||
|
# Install JBang using curl
|
||||||
|
RUN curl -Ls https://sh.jbang.dev | bash -s - app setup
|
||||||
|
|
||||||
# Copy the built binaries
|
# Copy the built binaries
|
||||||
COPY --from=builder /usr/src/goose/target/release/goose /usr/local/bin/
|
COPY --from=builder /usr/src/goose/target/release/goose /usr/local/bin/
|
||||||
|
|
||||||
@@ -109,3 +114,6 @@ RUN git config --global init.defaultBranch main && \
|
|||||||
# Add some helpful aliases
|
# Add some helpful aliases
|
||||||
RUN echo 'alias ll="ls -la"' >> ~/.bashrc && \
|
RUN echo 'alias ll="ls -la"' >> ~/.bashrc && \
|
||||||
echo 'alias fd=fdfind' >> ~/.bashrc
|
echo 'alias fd=fdfind' >> ~/.bashrc
|
||||||
|
|
||||||
|
# Add JBang to PATH
|
||||||
|
RUN echo 'export PATH="$HOME/.jbang/bin:$PATH"' >> ~/.bashrc
|
||||||
@@ -131,6 +131,8 @@ See available servers in the **[MCP Server Directory](https://www.pulsemcp.com/s
|
|||||||
|
|
||||||
#### Example of adding the [Knowledge Graph Memory MCP Server](https://github.com/modelcontextprotocol/servers/tree/main/src/memory):
|
#### Example of adding the [Knowledge Graph Memory MCP Server](https://github.com/modelcontextprotocol/servers/tree/main/src/memory):
|
||||||
|
|
||||||
|
<Tabs groupId="extensions">
|
||||||
|
<TabItem value="node" label="Node">
|
||||||
```
|
```
|
||||||
┌ goose-configure
|
┌ goose-configure
|
||||||
│
|
│
|
||||||
@@ -155,6 +157,74 @@ See available servers in the **[MCP Server Directory](https://www.pulsemcp.com/s
|
|||||||
└ Added Knowledge Graph Memory extension
|
└ Added Knowledge Graph Memory extension
|
||||||
```
|
```
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
<TabItem value="python" label="Python">
|
||||||
|
|
||||||
|
```
|
||||||
|
┌ goose-configure
|
||||||
|
│
|
||||||
|
◇ What would you like to configure?
|
||||||
|
│ Add Extension
|
||||||
|
│
|
||||||
|
◇ What type of extension would you like to add?
|
||||||
|
│ Command-line Extension
|
||||||
|
│
|
||||||
|
◇ What would you like to call this extension?
|
||||||
|
│ Wikipedia Reader
|
||||||
|
│
|
||||||
|
◇ What command should be run?
|
||||||
|
│ uvx mcp-wiki
|
||||||
|
│
|
||||||
|
◇ Please set the timeout for this tool (in secs):
|
||||||
|
│ 300
|
||||||
|
│
|
||||||
|
◆ Would you like to add environment variables?
|
||||||
|
│ No
|
||||||
|
│
|
||||||
|
└ Added Wikipedia Reader extension
|
||||||
|
```
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
<TabItem value="java" label="Java">
|
||||||
|
|
||||||
|
Note: Java and Kotlin extensions are only support on Linux and macOS
|
||||||
|
|
||||||
|
```
|
||||||
|
┌ goose-configure
|
||||||
|
│
|
||||||
|
◇ What would you like to configure?
|
||||||
|
│ Add Extension
|
||||||
|
│
|
||||||
|
◇ What type of extension would you like to add?
|
||||||
|
│ Command-line Extension
|
||||||
|
│
|
||||||
|
◇ What would you like to call this extension?
|
||||||
|
│ Spring Data Explorer
|
||||||
|
│
|
||||||
|
◇ What command should be run?
|
||||||
|
│ jbang -Dspring.profiles.active=dev org.example:spring-data-mcp:1.0.0
|
||||||
|
│
|
||||||
|
◇ Please set the timeout for this tool (in secs):
|
||||||
|
│ 300
|
||||||
|
│
|
||||||
|
◆ Would you like to add environment variables?
|
||||||
|
│ Yes
|
||||||
|
│
|
||||||
|
◇ Environment variable name:
|
||||||
|
│ SPRING_DATASOURCE_URL
|
||||||
|
│
|
||||||
|
◇ Environment variable value:
|
||||||
|
│ jdbc:postgresql://localhost:5432/mydb
|
||||||
|
│
|
||||||
|
◇ Add another environment variable?
|
||||||
|
│ No
|
||||||
|
│
|
||||||
|
└ Added Spring Data Explorer extension
|
||||||
|
```
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem value="ui" label="Goose Desktop">
|
<TabItem value="ui" label="Goose Desktop">
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,10 @@ Note that you'll need [Node.js](https://nodejs.org/) installed on your system to
|
|||||||
Note that you'll need [uv](https://docs.astral.sh/uv/#installation) installed on your system to run this command, as it uses `uvx`.
|
Note that you'll need [uv](https://docs.astral.sh/uv/#installation) installed on your system to run this command, as it uses `uvx`.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
:::info
|
||||||
|
Note that you'll need [JBang](https://www.jbang.dev/download) installed on your system to run this command, as it uses `jbang`.
|
||||||
|
:::
|
||||||
|
|
||||||
<Tabs groupId="interface">
|
<Tabs groupId="interface">
|
||||||
<TabItem value="cli" label="Goose CLI" default>
|
<TabItem value="cli" label="Goose CLI" default>
|
||||||
1. Run the `configure` command:
|
1. Run the `configure` command:
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export function getGooseInstallLink(server: MCPServer): string {
|
|||||||
return `goose://extension?${queryParams}`;
|
return `goose://extension?${queryParams}`;
|
||||||
}
|
}
|
||||||
const parts = server.command.split(" ");
|
const parts = server.command.split(" ");
|
||||||
const baseCmd = parts[0]; // npx or uvx
|
const baseCmd = parts[0]; // jbang, npx or uvx
|
||||||
const args = parts.slice(1); // remaining arguments
|
const args = parts.slice(1); // remaining arguments
|
||||||
const queryParams = [
|
const queryParams = [
|
||||||
`cmd=${encodeURIComponent(baseCmd)}`,
|
`cmd=${encodeURIComponent(baseCmd)}`,
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ export async function initializeAgent(model: string, provider: string) {
|
|||||||
export async function replaceWithShims(cmd: string) {
|
export async function replaceWithShims(cmd: string) {
|
||||||
const binaryPathMap: Record<string, string> = {
|
const binaryPathMap: Record<string, string> = {
|
||||||
goosed: await window.electron.getBinaryPath('goosed'),
|
goosed: await window.electron.getBinaryPath('goosed'),
|
||||||
|
jbang: await window.electron.getBinaryPath('jbang'),
|
||||||
npx: await window.electron.getBinaryPath('npx'),
|
npx: await window.electron.getBinaryPath('npx'),
|
||||||
uvx: await window.electron.getBinaryPath('uvx'),
|
uvx: await window.electron.getBinaryPath('uvx'),
|
||||||
};
|
};
|
||||||
|
|||||||
89
ui/desktop/src/bin/jbang
Executable file
89
ui/desktop/src/bin/jbang
Executable file
@@ -0,0 +1,89 @@
|
|||||||
|
#!/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 jbang 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
|
||||||
|
|
||||||
|
log "setting hermit cache to be local for MCP servers"
|
||||||
|
mkdir -p ~/.config/goose/mcp-hermit/cache
|
||||||
|
export HERMIT_STATE_DIR=~/.config/goose/mcp-hermit/cache
|
||||||
|
|
||||||
|
# Update PATH
|
||||||
|
export PATH=~/.config/goose/mcp-hermit/bin:$PATH
|
||||||
|
log "Updated PATH to include ~/.config/goose/mcp-hermit/bin."
|
||||||
|
|
||||||
|
# Initialize hermit
|
||||||
|
log "Initializing hermit."
|
||||||
|
hermit init >> "$LOG_FILE"
|
||||||
|
|
||||||
|
# Install OpenJDK using hermit
|
||||||
|
log "Installing OpenJDK with hermit."
|
||||||
|
hermit install openjdk@17 >> "$LOG_FILE"
|
||||||
|
|
||||||
|
# Download and install jbang if not present
|
||||||
|
if [ ! -f ~/.config/goose/mcp-hermit/bin/jbang ]; then
|
||||||
|
log "Downloading and installing jbang."
|
||||||
|
curl -Ls https://sh.jbang.dev | bash -s - app setup
|
||||||
|
cp ~/.jbang/bin/jbang ~/.config/goose/mcp-hermit/bin/
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verify installations
|
||||||
|
log "Verifying installation locations:"
|
||||||
|
log "hermit: $(which hermit)"
|
||||||
|
log "java: $(which java)"
|
||||||
|
log "jbang: $(which jbang)"
|
||||||
|
|
||||||
|
# Check for custom registry settings
|
||||||
|
log "Checking for GOOSE_JBANG_REGISTRY environment variable for custom jbang registry setup..."
|
||||||
|
if [ -n "${GOOSE_JBANG_REGISTRY:-}" ] && curl -s --head --fail "$GOOSE_JBANG_REGISTRY" > /dev/null; then
|
||||||
|
log "Checking custom goose registry availability: $GOOSE_JBANG_REGISTRY"
|
||||||
|
log "$GOOSE_JBANG_REGISTRY is accessible. Setting it as JBANG_REPO."
|
||||||
|
export JBANG_REPO="$GOOSE_JBANG_REGISTRY"
|
||||||
|
else
|
||||||
|
log "GOOSE_JBANG_REGISTRY is not set or not accessible. Using default jbang repository."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Trust all jbang scripts that a user might install. Without this, Jbang will attempt to
|
||||||
|
# prompt the user to trust each script. However, Goose does not surfact this modal and without
|
||||||
|
# user input, the addExtension method will timeout and fail.
|
||||||
|
jbang --quiet trust add *
|
||||||
|
|
||||||
|
# Final step: Execute jbang with passed arguments, always including --fresh and --quiet
|
||||||
|
log "Executing 'jbang' command with arguments: $*"
|
||||||
|
jbang --fresh --quiet "$@" || log "Failed to execute 'jbang' with arguments: $*"
|
||||||
|
|
||||||
|
log "jbang setup script completed successfully."
|
||||||
@@ -245,7 +245,7 @@ export async function addExtensionFromDeepLink(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Validate that the command is one of the allowed commands
|
// Validate that the command is one of the allowed commands
|
||||||
const allowedCommands = ['npx', 'uvx', 'goosed'];
|
const allowedCommands = ['jbang', 'npx', 'uvx', 'goosed'];
|
||||||
if (!allowedCommands.includes(cmd)) {
|
if (!allowedCommands.includes(cmd)) {
|
||||||
handleError(
|
handleError(
|
||||||
`Failed to install extension: Invalid command: ${cmd}. Only ${allowedCommands.join(', ')} are allowed.`,
|
`Failed to install extension: Invalid command: ${cmd}. Only ${allowedCommands.join(', ')} are allowed.`,
|
||||||
|
|||||||
@@ -239,6 +239,7 @@ export async function loadAndAddStoredExtensions() {
|
|||||||
export async function replaceWithShims(cmd: string) {
|
export async function replaceWithShims(cmd: string) {
|
||||||
const binaryPathMap: Record<string, string> = {
|
const binaryPathMap: Record<string, string> = {
|
||||||
goosed: await window.electron.getBinaryPath('goosed'),
|
goosed: await window.electron.getBinaryPath('goosed'),
|
||||||
|
jbang: await window.electron.getBinaryPath('jbang'),
|
||||||
npx: await window.electron.getBinaryPath('npx'),
|
npx: await window.electron.getBinaryPath('npx'),
|
||||||
uvx: await window.electron.getBinaryPath('uvx'),
|
uvx: await window.electron.getBinaryPath('uvx'),
|
||||||
};
|
};
|
||||||
@@ -299,7 +300,7 @@ export async function addExtensionFromDeepLink(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Validate that the command is one of the allowed commands
|
// Validate that the command is one of the allowed commands
|
||||||
const allowedCommands = ['npx', 'uvx', 'goosed'];
|
const allowedCommands = ['jbang', 'npx', 'uvx', 'goosed'];
|
||||||
if (!allowedCommands.includes(cmd)) {
|
if (!allowedCommands.includes(cmd)) {
|
||||||
handleError(
|
handleError(
|
||||||
`Failed to install extension: Invalid command: ${cmd}. Only ${allowedCommands.join(', ')} are allowed.`,
|
`Failed to install extension: Invalid command: ${cmd}. Only ${allowedCommands.join(', ')} are allowed.`,
|
||||||
|
|||||||
Reference in New Issue
Block a user