Skip to content

Commit fccfbf7

Browse files
claude[bot]Dhravya
andcommitted
feat: add Goose client support for MCP server installation
- Add js-yaml dependency for YAML parsing/writing - Extend client-config.ts to support YAML format files - Add Goose configuration path (~/.config/goose/config.yaml) - Update install command to handle Goose''s specific config structure - Goose extensions include: name, cmd, args, enabled, envs, type, timeout Resolves #4 Co-authored-by: Dhravya Shah <[email protected]>
1 parent e127ff7 commit fccfbf7

File tree

3 files changed

+59
-6
lines changed

3 files changed

+59
-6
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
"@jest/globals": "^29.7.0",
5050
"@tsconfig/node20": "^20.1.4",
5151
"@types/jest": "^29.5.12",
52+
"@types/js-yaml": "^4.0.9",
5253
"@types/node": "^20.12.12",
5354
"@types/prompts": "^2.4.9",
5455
"@types/signale": "^1.4.7",
@@ -76,6 +77,7 @@
7677
"consola": "^3.2.3",
7778
"dotenv": "^16.4.5",
7879
"giget": "^1.2.3",
80+
"js-yaml": "^4.1.0",
7981
"picocolors": "^1.0.1",
8082
"yargs": "^17.7.2"
8183
},

src/client-config.ts

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import fs from 'node:fs'
22
import os from 'node:os'
33
import path from 'node:path'
44
import process from 'node:process'
5+
import yaml from 'js-yaml'
56

67
import { verbose } from './logger'
78
// import { execFileSync } from "node:child_process"
@@ -16,6 +17,7 @@ interface ClientFileTarget {
1617
path: string
1718
localPath?: string
1819
configKey: string
20+
format?: 'json' | 'yaml' // Add format property for different file types
1921
}
2022
type ClientInstallTarget = ClientFileTarget
2123

@@ -117,6 +119,12 @@ function getClientPaths(): { [key: string]: ClientInstallTarget } {
117119
localPath: path.join(process.cwd(), '.mcp.json'),
118120
configKey: 'mcpServers',
119121
},
122+
goose: {
123+
type: 'file',
124+
path: path.join(baseDir, 'goose', 'config.yaml'),
125+
configKey: 'extensions',
126+
format: 'yaml',
127+
},
120128
}
121129
}
122130

@@ -132,6 +140,7 @@ export const clientNames = [
132140
'gemini-cli',
133141
'vscode',
134142
'claude-code',
143+
'goose',
135144
]
136145

137146
// Helper function to get nested value from an object using dot notation
@@ -197,7 +206,15 @@ export function readConfig(client: string, local?: boolean): ClientConfig {
197206
}
198207

199208
verbose('Reading config file content')
200-
const rawConfig = JSON.parse(fs.readFileSync(configPath.path, 'utf8'))
209+
const fileContent = fs.readFileSync(configPath.path, 'utf8')
210+
211+
let rawConfig: ClientConfig
212+
if (configPath.format === 'yaml') {
213+
rawConfig = yaml.load(fileContent) as ClientConfig || {}
214+
} else {
215+
rawConfig = JSON.parse(fileContent)
216+
}
217+
201218
verbose(`Config loaded successfully: ${JSON.stringify(rawConfig, null, 2)}`)
202219

203220
// Ensure the nested structure exists
@@ -261,7 +278,14 @@ function writeConfigFile(config: ClientConfig, target: ClientFileTarget): void {
261278
try {
262279
if (fs.existsSync(target.path)) {
263280
verbose('Reading existing config file for merging')
264-
existingConfig = JSON.parse(fs.readFileSync(target.path, 'utf8'))
281+
const fileContent = fs.readFileSync(target.path, 'utf8')
282+
283+
if (target.format === 'yaml') {
284+
existingConfig = yaml.load(fileContent) as ClientConfig || {}
285+
} else {
286+
existingConfig = JSON.parse(fileContent)
287+
}
288+
265289
verbose(`Existing config loaded: ${JSON.stringify(existingConfig, null, 2)}`)
266290
}
267291
} catch (error) {
@@ -274,6 +298,18 @@ function writeConfigFile(config: ClientConfig, target: ClientFileTarget): void {
274298
verbose(`Merged config: ${JSON.stringify(mergedConfig, null, 2)}`)
275299

276300
verbose(`Writing config to file: ${target.path}`)
277-
fs.writeFileSync(target.path, JSON.stringify(mergedConfig, null, 2))
301+
302+
let configContent: string
303+
if (target.format === 'yaml') {
304+
configContent = yaml.dump(mergedConfig, {
305+
indent: 2,
306+
lineWidth: -1,
307+
noRefs: true
308+
})
309+
} else {
310+
configContent = JSON.stringify(mergedConfig, null, 2)
311+
}
312+
313+
fs.writeFileSync(target.path, configContent)
278314
verbose('Config successfully written')
279315
}

src/commands/install.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ function setServerConfig(
1818
configKey: string,
1919
serverName: string,
2020
serverConfig: ClientConfig,
21+
client: string,
2122
): void {
2223
// Get or create the nested config object
2324
let servers = getNestedValue(config, configKey)
@@ -28,7 +29,21 @@ function setServerConfig(
2829

2930
// Set the server config
3031
if (servers) {
31-
servers[serverName] = serverConfig
32+
if (client === 'goose') {
33+
// Goose has a different config structure
34+
servers[serverName] = {
35+
name: serverName,
36+
cmd: serverConfig.command,
37+
args: serverConfig.args,
38+
enabled: true,
39+
envs: {},
40+
type: 'stdio',
41+
timeout: 300,
42+
...serverConfig, // Allow overriding defaults
43+
}
44+
} else {
45+
servers[serverName] = serverConfig
46+
}
3247
}
3348
}
3449

@@ -259,14 +274,14 @@ export async function handler(argv: ArgumentsCamelCase<InstallArgv>) {
259274
setServerConfig(config, configKey, name, {
260275
command: 'npx',
261276
args: args,
262-
})
277+
}, argv.client)
263278
} else {
264279
// Command-based installation (including simple package names)
265280
const cmdParts = command.split(' ')
266281
setServerConfig(config, configKey, name, {
267282
command: cmdParts[0],
268283
args: cmdParts.slice(1),
269-
})
284+
}, argv.client)
270285
}
271286

272287
writeConfig(config, argv.client, argv.local)

0 commit comments

Comments
 (0)