-
Notifications
You must be signed in to change notification settings - Fork 117
Expand file tree
/
Copy pathgoose.py
More file actions
208 lines (163 loc) · 6.71 KB
/
Copy pathgoose.py
File metadata and controls
208 lines (163 loc) · 6.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
"""
Goose MCP server configuration manager
"""
import logging
import os
from typing import Any, Dict, Optional
from pydantic import TypeAdapter
from mcpm.clients.base import YAMLClientManager
from mcpm.core.schema import CustomServerConfig, ServerConfig, STDIOServerConfig
logger = logging.getLogger(__name__)
class GooseClientManager(YAMLClientManager):
"""Manages Goose MCP server configurations
Goose uses YAML files for configuration with a specific structure for extensions.
This manager handles the Goose-specific configuration format and file locations.
"""
# Client information
client_key = "goose-cli"
display_name = "Goose CLI"
download_url = "https://github.com/block/goose/releases/download/stable/download_cli.sh"
def __init__(self, config_path=None):
"""Initialize the Goose CLI client manager
Args:
config_path: Optional path to the config file. If not provided, uses default path.
"""
super().__init__()
# Customize YAML handler
self.yaml_handler.indent(mapping=2, sequence=0, offset=0)
self.yaml_handler.preserve_quotes = True
if config_path:
self.config_path = config_path
else:
# Set config path based on detected platform
if self._system == "Windows":
self.config_path = os.path.join(os.environ.get("USERPROFILE", ""), ".config", "goose", "config.yaml")
else:
# MacOS or Linux
self.config_path = os.path.expanduser("~/.config/goose/config.yaml")
# Also check for workspace config
workspace_config = os.path.join(os.getcwd(), ".goose", "config.yaml")
if os.path.exists(workspace_config):
# Prefer workspace config if it exists
self.config_path = workspace_config
def _get_empty_config(self) -> Dict[str, Any]:
"""Get an empty configuration structure for Goose
Returns:
Dict containing the empty configuration structure
"""
return {"extensions": {}}
def _get_servers_section(self, config: Dict[str, Any]) -> Dict[str, Any]:
"""Get the section of the config that contains server definitions
Args:
config: The loaded configuration
Returns:
The section containing server definitions
"""
return config.get("extensions", {})
def _get_server_config(self, config: Dict[str, Any], server_name: str) -> Optional[Dict[str, Any]]:
"""Get a server configuration from the config by name
Args:
config: The loaded configuration
server_name: Name of the server to find
Returns:
Server configuration if found, None otherwise
"""
return self._get_servers_section(config).get(server_name)
def _get_all_server_names(self, config: Dict[str, Any]) -> list[str]:
"""Get all server names from the configuration
Args:
config: The loaded configuration
Returns:
List of server names
"""
return list(self._get_servers_section(config).keys())
def _add_server_to_config(
self, config: Dict[str, Any], server_name: str, server_config: Dict[str, Any]
) -> Dict[str, Any]:
"""Add or update a server in the config
Args:
config: The loaded configuration
server_name: Name of the server to add or update
server_config: Server configuration to add or update
Returns:
Updated configuration
"""
if "extensions" not in config:
config["extensions"] = {}
config["extensions"][server_name] = server_config
return config
def _remove_server_from_config(self, config: Dict[str, Any], server_name: str) -> Dict[str, Any]:
"""Remove a server from the config
Args:
config: The loaded configuration
server_name: Name of the server to remove
Returns:
Updated configuration
"""
if "extensions" in config and server_name in config["extensions"]:
del config["extensions"][server_name]
return config
def _normalize_server_config(self, server_config: Dict[str, Any]) -> Dict[str, Any]:
"""Normalize server configuration for external use
This method transforms Goose-specific configuration formats to a standard format
expected by external components.
Args:
server_config: Client-specific server configuration
Returns:
Normalized server configuration
"""
# Create a copy
normalized = dict(server_config)
# Map Goose-specific fields to standard fields
if normalized.get("type") == "builtin":
return {"config": normalized}
if "cmd" in normalized:
normalized["command"] = normalized.pop("cmd")
if "envs" in normalized:
normalized["env"] = normalized.pop("envs")
return normalized
def to_client_format(self, server_config: ServerConfig) -> Dict[str, Any]:
"""Convert ServerConfig to Goose-specific format
Args:
server_config: ServerConfig object
Returns:
Dict containing client-specific configuration
"""
# Base result containing only essential execution information
if isinstance(server_config, STDIOServerConfig):
result = {
"cmd": server_config.command,
"args": server_config.args,
"type": "stdio",
}
# Add filtered environment variables if present
non_empty_env = server_config.get_filtered_env_vars(os.environ)
if non_empty_env:
result["envs"] = non_empty_env
elif isinstance(server_config, CustomServerConfig):
result = dict(server_config.config)
result["type"] = "builtin"
else:
result = server_config.to_dict()
result["type"] = "sse"
result.update(
{
"name": server_config.name,
"enabled": True,
"description": server_config.name,
}
)
return result
def from_client_format(self, server_name: str, client_config: Dict[str, Any]) -> ServerConfig:
"""Convert Goose format to ServerConfig
Args:
server_name: Name of the server
client_config: Client-specific configuration
Returns:
ServerConfig object
"""
server_data = {
"name": server_name,
}
server_data.update(client_config)
return TypeAdapter(ServerConfig).validate_python(server_data)