code-puppy 0.0.135__py3-none-any.whl → 0.0.137__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. code_puppy/agent.py +15 -17
  2. code_puppy/agents/agent_manager.py +320 -9
  3. code_puppy/agents/base_agent.py +58 -2
  4. code_puppy/agents/runtime_manager.py +68 -42
  5. code_puppy/command_line/command_handler.py +82 -33
  6. code_puppy/command_line/mcp/__init__.py +10 -0
  7. code_puppy/command_line/mcp/add_command.py +183 -0
  8. code_puppy/command_line/mcp/base.py +35 -0
  9. code_puppy/command_line/mcp/handler.py +133 -0
  10. code_puppy/command_line/mcp/help_command.py +146 -0
  11. code_puppy/command_line/mcp/install_command.py +176 -0
  12. code_puppy/command_line/mcp/list_command.py +94 -0
  13. code_puppy/command_line/mcp/logs_command.py +126 -0
  14. code_puppy/command_line/mcp/remove_command.py +82 -0
  15. code_puppy/command_line/mcp/restart_command.py +92 -0
  16. code_puppy/command_line/mcp/search_command.py +117 -0
  17. code_puppy/command_line/mcp/start_all_command.py +126 -0
  18. code_puppy/command_line/mcp/start_command.py +98 -0
  19. code_puppy/command_line/mcp/status_command.py +185 -0
  20. code_puppy/command_line/mcp/stop_all_command.py +109 -0
  21. code_puppy/command_line/mcp/stop_command.py +79 -0
  22. code_puppy/command_line/mcp/test_command.py +107 -0
  23. code_puppy/command_line/mcp/utils.py +129 -0
  24. code_puppy/command_line/mcp/wizard_utils.py +259 -0
  25. code_puppy/command_line/model_picker_completion.py +21 -4
  26. code_puppy/command_line/prompt_toolkit_completion.py +9 -0
  27. code_puppy/config.py +5 -5
  28. code_puppy/main.py +23 -17
  29. code_puppy/mcp/__init__.py +42 -16
  30. code_puppy/mcp/async_lifecycle.py +51 -49
  31. code_puppy/mcp/blocking_startup.py +125 -113
  32. code_puppy/mcp/captured_stdio_server.py +63 -70
  33. code_puppy/mcp/circuit_breaker.py +63 -47
  34. code_puppy/mcp/config_wizard.py +169 -136
  35. code_puppy/mcp/dashboard.py +79 -71
  36. code_puppy/mcp/error_isolation.py +147 -100
  37. code_puppy/mcp/examples/retry_example.py +55 -42
  38. code_puppy/mcp/health_monitor.py +152 -141
  39. code_puppy/mcp/managed_server.py +100 -93
  40. code_puppy/mcp/manager.py +168 -156
  41. code_puppy/mcp/registry.py +148 -110
  42. code_puppy/mcp/retry_manager.py +63 -61
  43. code_puppy/mcp/server_registry_catalog.py +271 -225
  44. code_puppy/mcp/status_tracker.py +80 -80
  45. code_puppy/mcp/system_tools.py +47 -52
  46. code_puppy/messaging/message_queue.py +20 -13
  47. code_puppy/messaging/renderers.py +30 -15
  48. code_puppy/state_management.py +103 -0
  49. code_puppy/tui/app.py +64 -7
  50. code_puppy/tui/components/chat_view.py +3 -3
  51. code_puppy/tui/components/human_input_modal.py +12 -8
  52. code_puppy/tui/screens/__init__.py +2 -2
  53. code_puppy/tui/screens/mcp_install_wizard.py +208 -179
  54. code_puppy/tui/tests/test_agent_command.py +3 -3
  55. {code_puppy-0.0.135.dist-info → code_puppy-0.0.137.dist-info}/METADATA +1 -1
  56. {code_puppy-0.0.135.dist-info → code_puppy-0.0.137.dist-info}/RECORD +60 -42
  57. code_puppy/command_line/mcp_commands.py +0 -1789
  58. {code_puppy-0.0.135.data → code_puppy-0.0.137.data}/data/code_puppy/models.json +0 -0
  59. {code_puppy-0.0.135.dist-info → code_puppy-0.0.137.dist-info}/WHEEL +0 -0
  60. {code_puppy-0.0.135.dist-info → code_puppy-0.0.137.dist-info}/entry_points.txt +0 -0
  61. {code_puppy-0.0.135.dist-info → code_puppy-0.0.137.dist-info}/licenses/LICENSE +0 -0
@@ -3,21 +3,35 @@ MCP Server Registry Catalog - Pre-configured MCP servers.
3
3
  A curated collection of MCP servers that can be easily searched and installed.
4
4
  """
5
5
 
6
- from typing import Dict, List, Optional, Union
7
6
  from dataclasses import dataclass, field
7
+ from typing import Dict, List, Optional, Union
8
+
8
9
 
9
10
  @dataclass
10
11
  class MCPServerRequirements:
11
12
  """Comprehensive requirements for an MCP server installation."""
12
- environment_vars: List[str] = field(default_factory=list) # ["GITHUB_TOKEN", "API_KEY"]
13
- command_line_args: List[Dict[str, Union[str, bool]]] = field(default_factory=list) # [{"name": "port", "prompt": "Port number", "default": "3000", "required": False}]
14
- required_tools: List[str] = field(default_factory=list) # ["node", "python", "npm", "npx"]
15
- package_dependencies: List[str] = field(default_factory=list) # ["jupyter", "@modelcontextprotocol/server-discord"]
16
- system_requirements: List[str] = field(default_factory=list) # ["Docker installed", "Git configured"]
13
+
14
+ environment_vars: List[str] = field(
15
+ default_factory=list
16
+ ) # ["GITHUB_TOKEN", "API_KEY"]
17
+ command_line_args: List[Dict[str, Union[str, bool]]] = field(
18
+ default_factory=list
19
+ ) # [{"name": "port", "prompt": "Port number", "default": "3000", "required": False}]
20
+ required_tools: List[str] = field(
21
+ default_factory=list
22
+ ) # ["node", "python", "npm", "npx"]
23
+ package_dependencies: List[str] = field(
24
+ default_factory=list
25
+ ) # ["jupyter", "@modelcontextprotocol/server-discord"]
26
+ system_requirements: List[str] = field(
27
+ default_factory=list
28
+ ) # ["Docker installed", "Git configured"]
29
+
17
30
 
18
31
  @dataclass
19
32
  class MCPServerTemplate:
20
33
  """Template for a pre-configured MCP server."""
34
+
21
35
  id: str
22
36
  name: str
23
37
  display_name: str
@@ -29,66 +43,69 @@ class MCPServerTemplate:
29
43
  author: str = "Community"
30
44
  verified: bool = False
31
45
  popular: bool = False
32
- requires: Union[List[str], MCPServerRequirements] = field(default_factory=list) # Backward compatible
46
+ requires: Union[List[str], MCPServerRequirements] = field(
47
+ default_factory=list
48
+ ) # Backward compatible
33
49
  example_usage: str = ""
34
-
50
+
35
51
  def get_requirements(self) -> MCPServerRequirements:
36
52
  """Get requirements as MCPServerRequirements object."""
37
53
  if isinstance(self.requires, list):
38
54
  # Backward compatibility - treat as required_tools
39
55
  return MCPServerRequirements(required_tools=self.requires)
40
56
  return self.requires
41
-
57
+
42
58
  def get_environment_vars(self) -> List[str]:
43
59
  """Get list of required environment variables."""
44
60
  requirements = self.get_requirements()
45
61
  env_vars = requirements.environment_vars.copy()
46
-
62
+
47
63
  # Also check config for env vars (existing logic)
48
- if 'env' in self.config:
49
- for key, value in self.config['env'].items():
50
- if isinstance(value, str) and value.startswith('$'):
64
+ if "env" in self.config:
65
+ for key, value in self.config["env"].items():
66
+ if isinstance(value, str) and value.startswith("$"):
51
67
  var_name = value[1:]
52
68
  if var_name not in env_vars:
53
69
  env_vars.append(var_name)
54
-
70
+
55
71
  return env_vars
56
-
72
+
57
73
  def get_command_line_args(self) -> List[Dict]:
58
74
  """Get list of configurable command line arguments."""
59
75
  return self.get_requirements().command_line_args
60
-
76
+
61
77
  def get_required_tools(self) -> List[str]:
62
78
  """Get list of required system tools."""
63
79
  return self.get_requirements().required_tools
64
-
80
+
65
81
  def get_package_dependencies(self) -> List[str]:
66
82
  """Get list of package dependencies."""
67
83
  return self.get_requirements().package_dependencies
68
-
84
+
69
85
  def get_system_requirements(self) -> List[str]:
70
86
  """Get list of system requirements."""
71
87
  return self.get_requirements().system_requirements
72
88
 
73
89
  def to_server_config(self, custom_name: Optional[str] = None, **cmd_args) -> Dict:
74
90
  """Convert template to server configuration with optional overrides.
75
-
91
+
76
92
  Replaces placeholders in the config with actual values.
77
93
  Placeholders are in the format ${ARG_NAME} in args array.
78
94
  """
79
95
  import copy
96
+
80
97
  config = {
81
98
  "name": custom_name or self.name,
82
99
  "type": self.type,
83
- **copy.deepcopy(self.config)
100
+ **copy.deepcopy(self.config),
84
101
  }
85
-
102
+
86
103
  # Apply command line argument substitutions
87
- if cmd_args and 'args' in config:
104
+ if cmd_args and "args" in config:
88
105
  new_args = []
89
- for arg in config['args']:
106
+ for arg in config["args"]:
90
107
  # Check if this arg contains a placeholder like ${db_path}
91
- if isinstance(arg, str) and '${' in arg:
108
+ if isinstance(arg, str) and "${" in arg:
92
109
  # Replace all placeholders in this arg
93
110
  new_arg = arg
94
111
  for key, value in cmd_args.items():
@@ -98,18 +115,20 @@ class MCPServerTemplate:
98
115
  new_args.append(new_arg)
99
116
  else:
100
117
  new_args.append(arg)
101
- config['args'] = new_args
102
-
118
+ config["args"] = new_args
119
+
103
120
  # Also handle environment variable placeholders
104
- if 'env' in config:
105
- for env_key, env_value in config['env'].items():
106
- if isinstance(env_value, str) and '${' in env_value:
121
+ if "env" in config:
122
+ for env_key, env_value in config["env"].items():
123
+ if isinstance(env_value, str) and "${" in env_value:
107
124
  # Replace placeholders in env values
108
125
  for key, value in cmd_args.items():
109
126
  placeholder = f"${{{key}}}"
110
127
  if placeholder in env_value:
111
- config['env'][env_key] = env_value.replace(placeholder, str(value))
112
-
128
+ config["env"][env_key] = env_value.replace(
129
+ placeholder, str(value)
130
+ )
131
+
113
132
  return config
114
133
 
115
134
 
@@ -125,12 +144,17 @@ MCP_SERVER_REGISTRY: List[MCPServerTemplate] = [
125
144
  type="stdio",
126
145
  config={
127
146
  "command": "uvx",
128
- "args": ["--from", "git+https://github.com/oraios/serena", "serena", "start-mcp-server"]
147
+ "args": [
148
+ "--from",
149
+ "git+https://github.com/oraios/serena",
150
+ "serena",
151
+ "start-mcp-server",
152
+ ],
129
153
  },
130
154
  verified=True,
131
155
  popular=True,
132
156
  example_usage="Agentic AI for writing programs",
133
- requires=["uvx"]
157
+ requires=["uvx"],
134
158
  ),
135
159
  # ========== File System & Storage ==========
136
160
  MCPServerTemplate(
@@ -144,14 +168,13 @@ MCP_SERVER_REGISTRY: List[MCPServerTemplate] = [
144
168
  config={
145
169
  "command": "npx",
146
170
  "args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"],
147
- "timeout": 30
171
+ "timeout": 30,
148
172
  },
149
173
  verified=True,
150
174
  popular=True,
151
175
  requires=["node", "npm"],
152
- example_usage="Access and modify files in /tmp directory"
176
+ example_usage="Access and modify files in /tmp directory",
153
177
  ),
154
-
155
178
  MCPServerTemplate(
156
179
  id="filesystem-home",
157
180
  name="filesystem-home",
@@ -163,12 +186,11 @@ MCP_SERVER_REGISTRY: List[MCPServerTemplate] = [
163
186
  config={
164
187
  "command": "npx",
165
188
  "args": ["-y", "@modelcontextprotocol/server-filesystem", "~"],
166
- "timeout": 30
189
+ "timeout": 30,
167
190
  },
168
191
  verified=True,
169
- requires=["node", "npm"]
192
+ requires=["node", "npm"],
170
193
  ),
171
-
172
194
  # Enhanced server with comprehensive requirements
173
195
  MCPServerTemplate(
174
196
  id="gdrive",
@@ -183,60 +205,63 @@ MCP_SERVER_REGISTRY: List[MCPServerTemplate] = [
183
205
  "args": ["-y", "@modelcontextprotocol/server-gdrive"],
184
206
  "env": {
185
207
  "GOOGLE_CLIENT_ID": "$GOOGLE_CLIENT_ID",
186
- "GOOGLE_CLIENT_SECRET": "$GOOGLE_CLIENT_SECRET"
187
- }
208
+ "GOOGLE_CLIENT_SECRET": "$GOOGLE_CLIENT_SECRET",
209
+ },
188
210
  },
189
211
  requires=MCPServerRequirements(
190
212
  environment_vars=["GOOGLE_CLIENT_ID", "GOOGLE_CLIENT_SECRET"],
191
213
  command_line_args=[
192
214
  {
193
- "name": "port",
194
- "prompt": "OAuth redirect port",
195
- "default": "3000",
196
- "required": False
215
+ "name": "port",
216
+ "prompt": "OAuth redirect port",
217
+ "default": "3000",
218
+ "required": False,
197
219
  },
198
220
  {
199
221
  "name": "scope",
200
222
  "prompt": "Google Drive API scope",
201
223
  "default": "https://www.googleapis.com/auth/drive.readonly",
202
- "required": False
203
- }
224
+ "required": False,
225
+ },
204
226
  ],
205
227
  required_tools=["node", "npx", "npm"],
206
228
  package_dependencies=["@modelcontextprotocol/server-gdrive"],
207
- system_requirements=["Internet connection for OAuth"]
229
+ system_requirements=["Internet connection for OAuth"],
208
230
  ),
209
231
  verified=True,
210
232
  popular=True,
211
- example_usage="List files: 'Show me my Google Drive files'"
233
+ example_usage="List files: 'Show me my Google Drive files'",
212
234
  ),
213
-
214
235
  # Regular server (backward compatible)
215
236
  MCPServerTemplate(
216
237
  id="filesystem-simple",
217
238
  name="filesystem-simple",
218
239
  display_name="Simple Filesystem",
219
240
  description="Basic filesystem access",
220
- category="Storage",
241
+ category="Storage",
221
242
  tags=["files", "basic"],
222
243
  type="stdio",
223
244
  config={
224
245
  "command": "npx",
225
246
  "args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"],
226
- "timeout": 30
247
+ "timeout": 30,
227
248
  },
228
249
  verified=True,
229
250
  popular=True,
230
251
  requires=MCPServerRequirements(
231
252
  environment_vars=["GOOGLE_CLIENT_ID", "GOOGLE_CLIENT_SECRET"],
232
253
  command_line_args=[
233
- {"name": "port", "prompt": "OAuth redirect port", "default": "3000", "required": False}
254
+ {
255
+ "name": "port",
256
+ "prompt": "OAuth redirect port",
257
+ "default": "3000",
258
+ "required": False,
259
+ }
234
260
  ],
235
261
  required_tools=["node", "npm", "npx"],
236
- package_dependencies=["@modelcontextprotocol/server-gdrive"]
237
- )
262
+ package_dependencies=["@modelcontextprotocol/server-gdrive"],
263
+ ),
238
264
  ),
239
-
240
265
  # ========== Databases ==========
241
266
  MCPServerTemplate(
242
267
  id="postgres",
@@ -248,23 +273,31 @@ MCP_SERVER_REGISTRY: List[MCPServerTemplate] = [
248
273
  type="stdio",
249
274
  config={
250
275
  "command": "npx",
251
- "args": ["-y", "@modelcontextprotocol/server-postgres", "${connection_string}"],
252
- "timeout": 30
276
+ "args": [
277
+ "-y",
278
+ "@modelcontextprotocol/server-postgres",
279
+ "${connection_string}",
280
+ ],
281
+ "timeout": 30,
253
282
  },
254
283
  verified=True,
255
284
  popular=True,
256
285
  requires=MCPServerRequirements(
257
286
  environment_vars=["DATABASE_URL"],
258
287
  command_line_args=[
259
- {"name": "connection_string", "prompt": "PostgreSQL connection string", "default": "postgresql://localhost/mydb", "required": True}
288
+ {
289
+ "name": "connection_string",
290
+ "prompt": "PostgreSQL connection string",
291
+ "default": "postgresql://localhost/mydb",
292
+ "required": True,
293
+ }
260
294
  ],
261
295
  required_tools=["node", "npm", "npx"],
262
296
  package_dependencies=["@modelcontextprotocol/server-postgres"],
263
- system_requirements=["PostgreSQL server running"]
297
+ system_requirements=["PostgreSQL server running"],
264
298
  ),
265
- example_usage="postgresql://user:password@localhost:5432/dbname"
299
+ example_usage="postgresql://user:password@localhost:5432/dbname",
266
300
  ),
267
-
268
301
  MCPServerTemplate(
269
302
  id="sqlite",
270
303
  name="sqlite",
@@ -276,19 +309,23 @@ MCP_SERVER_REGISTRY: List[MCPServerTemplate] = [
276
309
  config={
277
310
  "command": "npx",
278
311
  "args": ["-y", "mcp-sqlite", "${db_path}"],
279
- "timeout": 30
312
+ "timeout": 30,
280
313
  },
281
314
  verified=True,
282
315
  popular=True,
283
316
  requires=MCPServerRequirements(
284
317
  command_line_args=[
285
- {"name": "db_path", "prompt": "Path to SQLite database file", "default": "./database.db", "required": True}
318
+ {
319
+ "name": "db_path",
320
+ "prompt": "Path to SQLite database file",
321
+ "default": "./database.db",
322
+ "required": True,
323
+ }
286
324
  ],
287
325
  required_tools=["node", "npm", "npx"],
288
- package_dependencies=["@modelcontextprotocol/server-sqlite"]
289
- )
326
+ package_dependencies=["@modelcontextprotocol/server-sqlite"],
327
+ ),
290
328
  ),
291
-
292
329
  MCPServerTemplate(
293
330
  id="mysql",
294
331
  name="mysql",
@@ -299,21 +336,29 @@ MCP_SERVER_REGISTRY: List[MCPServerTemplate] = [
299
336
  type="stdio",
300
337
  config={
301
338
  "command": "npx",
302
- "args": ["-y", "@modelcontextprotocol/server-mysql", "${connection_string}"],
303
- "timeout": 30
339
+ "args": [
340
+ "-y",
341
+ "@modelcontextprotocol/server-mysql",
342
+ "${connection_string}",
343
+ ],
344
+ "timeout": 30,
304
345
  },
305
346
  verified=True,
306
347
  requires=MCPServerRequirements(
307
348
  environment_vars=["MYSQL_URL"],
308
349
  command_line_args=[
309
- {"name": "connection_string", "prompt": "MySQL connection string", "default": "mysql://localhost/mydb", "required": True}
350
+ {
351
+ "name": "connection_string",
352
+ "prompt": "MySQL connection string",
353
+ "default": "mysql://localhost/mydb",
354
+ "required": True,
355
+ }
310
356
  ],
311
357
  required_tools=["node", "npm", "npx"],
312
358
  package_dependencies=["@modelcontextprotocol/server-mysql"],
313
- system_requirements=["MySQL server running"]
314
- )
359
+ system_requirements=["MySQL server running"],
360
+ ),
315
361
  ),
316
-
317
362
  MCPServerTemplate(
318
363
  id="mongodb",
319
364
  name="mongodb",
@@ -324,21 +369,29 @@ MCP_SERVER_REGISTRY: List[MCPServerTemplate] = [
324
369
  type="stdio",
325
370
  config={
326
371
  "command": "npx",
327
- "args": ["-y", "@modelcontextprotocol/server-mongodb", "${connection_string}"],
328
- "timeout": 30
372
+ "args": [
373
+ "-y",
374
+ "@modelcontextprotocol/server-mongodb",
375
+ "${connection_string}",
376
+ ],
377
+ "timeout": 30,
329
378
  },
330
379
  verified=True,
331
380
  requires=MCPServerRequirements(
332
381
  environment_vars=["MONGODB_URI"],
333
382
  command_line_args=[
334
- {"name": "connection_string", "prompt": "MongoDB connection string", "default": "mongodb://localhost:27017/mydb", "required": True}
383
+ {
384
+ "name": "connection_string",
385
+ "prompt": "MongoDB connection string",
386
+ "default": "mongodb://localhost:27017/mydb",
387
+ "required": True,
388
+ }
335
389
  ],
336
390
  required_tools=["node", "npm", "npx"],
337
391
  package_dependencies=["@modelcontextprotocol/server-mongodb"],
338
- system_requirements=["MongoDB server running"]
339
- )
392
+ system_requirements=["MongoDB server running"],
393
+ ),
340
394
  ),
341
-
342
395
  # ========== Development Tools ==========
343
396
  MCPServerTemplate(
344
397
  id="git",
@@ -351,17 +404,16 @@ MCP_SERVER_REGISTRY: List[MCPServerTemplate] = [
351
404
  config={
352
405
  "command": "npx",
353
406
  "args": ["-y", "@modelcontextprotocol/server-git"],
354
- "timeout": 30
407
+ "timeout": 30,
355
408
  },
356
409
  verified=True,
357
410
  popular=True,
358
411
  requires=MCPServerRequirements(
359
412
  required_tools=["node", "npm", "npx", "git"],
360
413
  package_dependencies=["@modelcontextprotocol/server-git"],
361
- system_requirements=["Git repository initialized"]
362
- )
414
+ system_requirements=["Git repository initialized"],
415
+ ),
363
416
  ),
364
-
365
417
  MCPServerTemplate(
366
418
  id="github",
367
419
  name="github",
@@ -374,7 +426,7 @@ MCP_SERVER_REGISTRY: List[MCPServerTemplate] = [
374
426
  "command": "npx",
375
427
  "args": ["-y", "@modelcontextprotocol/server-github"],
376
428
  "env": {"GITHUB_TOKEN": "$GITHUB_TOKEN"},
377
- "timeout": 30
429
+ "timeout": 30,
378
430
  },
379
431
  verified=True,
380
432
  popular=True,
@@ -382,10 +434,9 @@ MCP_SERVER_REGISTRY: List[MCPServerTemplate] = [
382
434
  environment_vars=["GITHUB_TOKEN"],
383
435
  required_tools=["node", "npm", "npx"],
384
436
  package_dependencies=["@modelcontextprotocol/server-github"],
385
- system_requirements=["GitHub account with personal access token"]
386
- )
437
+ system_requirements=["GitHub account with personal access token"],
438
+ ),
387
439
  ),
388
-
389
440
  MCPServerTemplate(
390
441
  id="gitlab",
391
442
  name="gitlab",
@@ -398,17 +449,16 @@ MCP_SERVER_REGISTRY: List[MCPServerTemplate] = [
398
449
  "command": "npx",
399
450
  "args": ["-y", "@modelcontextprotocol/server-gitlab"],
400
451
  "env": {"GITLAB_TOKEN": "$GITLAB_TOKEN"},
401
- "timeout": 30
452
+ "timeout": 30,
402
453
  },
403
454
  verified=True,
404
455
  requires=MCPServerRequirements(
405
456
  environment_vars=["GITLAB_TOKEN"],
406
457
  required_tools=["node", "npm", "npx"],
407
458
  package_dependencies=["@modelcontextprotocol/server-gitlab"],
408
- system_requirements=["GitLab account with personal access token"]
409
- )
459
+ system_requirements=["GitLab account with personal access token"],
460
+ ),
410
461
  ),
411
-
412
462
  # ========== Web & Browser ==========
413
463
  MCPServerTemplate(
414
464
  id="puppeteer",
@@ -421,20 +471,24 @@ MCP_SERVER_REGISTRY: List[MCPServerTemplate] = [
421
471
  config={
422
472
  "command": "npx",
423
473
  "args": ["-y", "@modelcontextprotocol/server-puppeteer"],
424
- "timeout": 60
474
+ "timeout": 60,
425
475
  },
426
476
  verified=True,
427
477
  popular=True,
428
478
  requires=MCPServerRequirements(
429
479
  command_line_args=[
430
- {"name": "headless", "prompt": "Run in headless mode", "default": "true", "required": False}
480
+ {
481
+ "name": "headless",
482
+ "prompt": "Run in headless mode",
483
+ "default": "true",
484
+ "required": False,
485
+ }
431
486
  ],
432
487
  required_tools=["node", "npm", "npx"],
433
488
  package_dependencies=["@modelcontextprotocol/server-puppeteer"],
434
- system_requirements=["Chrome/Chromium browser"]
435
- )
489
+ system_requirements=["Chrome/Chromium browser"],
490
+ ),
436
491
  ),
437
-
438
492
  MCPServerTemplate(
439
493
  id="playwright",
440
494
  name="playwright",
@@ -446,19 +500,23 @@ MCP_SERVER_REGISTRY: List[MCPServerTemplate] = [
446
500
  config={
447
501
  "command": "npx",
448
502
  "args": ["-y", "@modelcontextprotocol/server-playwright"],
449
- "timeout": 60
503
+ "timeout": 60,
450
504
  },
451
505
  verified=True,
452
506
  requires=MCPServerRequirements(
453
507
  command_line_args=[
454
- {"name": "browser", "prompt": "Browser to use", "default": "chromium", "required": False}
508
+ {
509
+ "name": "browser",
510
+ "prompt": "Browser to use",
511
+ "default": "chromium",
512
+ "required": False,
513
+ }
455
514
  ],
456
515
  required_tools=["node", "npm", "npx"],
457
516
  package_dependencies=["@modelcontextprotocol/server-playwright"],
458
- system_requirements=["Playwright browsers (will be installed)"]
459
- )
517
+ system_requirements=["Playwright browsers (will be installed)"],
518
+ ),
460
519
  ),
461
-
462
520
  MCPServerTemplate(
463
521
  id="fetch",
464
522
  name="fetch",
@@ -470,15 +528,14 @@ MCP_SERVER_REGISTRY: List[MCPServerTemplate] = [
470
528
  config={
471
529
  "command": "npx",
472
530
  "args": ["-y", "@modelcontextprotocol/server-fetch"],
473
- "timeout": 30
531
+ "timeout": 30,
474
532
  },
475
533
  verified=True,
476
534
  requires=MCPServerRequirements(
477
535
  required_tools=["node", "npm", "npx"],
478
- package_dependencies=["@modelcontextprotocol/server-fetch"]
479
- )
536
+ package_dependencies=["@modelcontextprotocol/server-fetch"],
537
+ ),
480
538
  ),
481
-
482
539
  # ========== Communication ==========
483
540
  MCPServerTemplate(
484
541
  id="slack",
@@ -492,7 +549,7 @@ MCP_SERVER_REGISTRY: List[MCPServerTemplate] = [
492
549
  "command": "npx",
493
550
  "args": ["-y", "@modelcontextprotocol/server-slack"],
494
551
  "env": {"SLACK_TOKEN": "$SLACK_TOKEN"},
495
- "timeout": 30
552
+ "timeout": 30,
496
553
  },
497
554
  verified=True,
498
555
  popular=True,
@@ -500,10 +557,9 @@ MCP_SERVER_REGISTRY: List[MCPServerTemplate] = [
500
557
  environment_vars=["SLACK_TOKEN"],
501
558
  required_tools=["node", "npm", "npx"],
502
559
  package_dependencies=["@modelcontextprotocol/server-slack"],
503
- system_requirements=["Slack app with bot token"]
504
- )
560
+ system_requirements=["Slack app with bot token"],
561
+ ),
505
562
  ),
506
-
507
563
  MCPServerTemplate(
508
564
  id="discord",
509
565
  name="discord",
@@ -516,17 +572,16 @@ MCP_SERVER_REGISTRY: List[MCPServerTemplate] = [
516
572
  "command": "npx",
517
573
  "args": ["-y", "@modelcontextprotocol/server-discord"],
518
574
  "env": {"DISCORD_TOKEN": "$DISCORD_TOKEN"},
519
- "timeout": 30
575
+ "timeout": 30,
520
576
  },
521
577
  verified=True,
522
578
  requires=MCPServerRequirements(
523
579
  environment_vars=["DISCORD_TOKEN"],
524
580
  required_tools=["node", "npm", "npx"],
525
581
  package_dependencies=["@modelcontextprotocol/server-discord"],
526
- system_requirements=["Discord bot token"]
527
- )
582
+ system_requirements=["Discord bot token"],
583
+ ),
528
584
  ),
529
-
530
585
  MCPServerTemplate(
531
586
  id="email",
532
587
  name="email",
@@ -538,16 +593,15 @@ MCP_SERVER_REGISTRY: List[MCPServerTemplate] = [
538
593
  config={
539
594
  "command": "npx",
540
595
  "args": ["-y", "@modelcontextprotocol/server-email"],
541
- "timeout": 30
596
+ "timeout": 30,
542
597
  },
543
598
  verified=True,
544
599
  requires=MCPServerRequirements(
545
600
  environment_vars=["EMAIL_HOST", "EMAIL_PORT", "EMAIL_USER", "EMAIL_PASS"],
546
601
  required_tools=["node", "npm", "npx"],
547
- package_dependencies=["@modelcontextprotocol/server-email"]
548
- )
602
+ package_dependencies=["@modelcontextprotocol/server-email"],
603
+ ),
549
604
  ),
550
-
551
605
  # ========== AI & Machine Learning ==========
552
606
  MCPServerTemplate(
553
607
  id="openai",
@@ -561,17 +615,16 @@ MCP_SERVER_REGISTRY: List[MCPServerTemplate] = [
561
615
  "command": "npx",
562
616
  "args": ["-y", "@modelcontextprotocol/server-openai"],
563
617
  "env": {"OPENAI_API_KEY": "$OPENAI_API_KEY"},
564
- "timeout": 60
618
+ "timeout": 60,
565
619
  },
566
620
  verified=True,
567
621
  popular=True,
568
622
  requires=MCPServerRequirements(
569
623
  environment_vars=["OPENAI_API_KEY"],
570
624
  required_tools=["node", "npm", "npx"],
571
- package_dependencies=["@modelcontextprotocol/server-openai"]
572
- )
625
+ package_dependencies=["@modelcontextprotocol/server-openai"],
626
+ ),
573
627
  ),
574
-
575
628
  MCPServerTemplate(
576
629
  id="anthropic",
577
630
  name="anthropic",
@@ -584,16 +637,15 @@ MCP_SERVER_REGISTRY: List[MCPServerTemplate] = [
584
637
  "command": "npx",
585
638
  "args": ["-y", "@modelcontextprotocol/server-anthropic"],
586
639
  "env": {"ANTHROPIC_API_KEY": "$ANTHROPIC_API_KEY"},
587
- "timeout": 60
640
+ "timeout": 60,
588
641
  },
589
642
  verified=True,
590
643
  requires=MCPServerRequirements(
591
644
  environment_vars=["ANTHROPIC_API_KEY"],
592
645
  required_tools=["node", "npm", "npx"],
593
- package_dependencies=["@modelcontextprotocol/server-anthropic"]
594
- )
646
+ package_dependencies=["@modelcontextprotocol/server-anthropic"],
647
+ ),
595
648
  ),
596
-
597
649
  # ========== Data Processing ==========
598
650
  MCPServerTemplate(
599
651
  id="pandas",
@@ -606,16 +658,15 @@ MCP_SERVER_REGISTRY: List[MCPServerTemplate] = [
606
658
  config={
607
659
  "command": "python",
608
660
  "args": ["-m", "mcp_server_pandas"],
609
- "timeout": 30
661
+ "timeout": 30,
610
662
  },
611
663
  verified=True,
612
664
  popular=True,
613
665
  requires=MCPServerRequirements(
614
666
  required_tools=["python", "pip"],
615
- package_dependencies=["pandas", "mcp-server-pandas"]
616
- )
667
+ package_dependencies=["pandas", "mcp-server-pandas"],
668
+ ),
617
669
  ),
618
-
619
670
  MCPServerTemplate(
620
671
  id="jupyter",
621
672
  name="jupyter",
@@ -627,15 +678,14 @@ MCP_SERVER_REGISTRY: List[MCPServerTemplate] = [
627
678
  config={
628
679
  "command": "python",
629
680
  "args": ["-m", "mcp_server_jupyter"],
630
- "timeout": 60
681
+ "timeout": 60,
631
682
  },
632
683
  verified=True,
633
684
  requires=MCPServerRequirements(
634
685
  required_tools=["python", "pip", "jupyter"],
635
- package_dependencies=["jupyter", "mcp-server-jupyter"]
636
- )
686
+ package_dependencies=["jupyter", "mcp-server-jupyter"],
687
+ ),
637
688
  ),
638
-
639
689
  # ========== Cloud Services ==========
640
690
  MCPServerTemplate(
641
691
  id="aws-s3",
@@ -650,23 +700,27 @@ MCP_SERVER_REGISTRY: List[MCPServerTemplate] = [
650
700
  "args": ["-y", "@modelcontextprotocol/server-aws-s3"],
651
701
  "env": {
652
702
  "AWS_ACCESS_KEY_ID": "$AWS_ACCESS_KEY_ID",
653
- "AWS_SECRET_ACCESS_KEY": "$AWS_SECRET_ACCESS_KEY"
703
+ "AWS_SECRET_ACCESS_KEY": "$AWS_SECRET_ACCESS_KEY",
654
704
  },
655
- "timeout": 30
705
+ "timeout": 30,
656
706
  },
657
707
  verified=True,
658
708
  popular=True,
659
709
  requires=MCPServerRequirements(
660
710
  environment_vars=["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"],
661
711
  command_line_args=[
662
- {"name": "region", "prompt": "AWS region", "default": "us-east-1", "required": False}
712
+ {
713
+ "name": "region",
714
+ "prompt": "AWS region",
715
+ "default": "us-east-1",
716
+ "required": False,
717
+ }
663
718
  ],
664
719
  required_tools=["node", "npm", "npx"],
665
720
  package_dependencies=["@modelcontextprotocol/server-aws-s3"],
666
- system_requirements=["AWS account with S3 access"]
667
- )
721
+ system_requirements=["AWS account with S3 access"],
722
+ ),
668
723
  ),
669
-
670
724
  MCPServerTemplate(
671
725
  id="azure-storage",
672
726
  name="azure-storage",
@@ -678,18 +732,19 @@ MCP_SERVER_REGISTRY: List[MCPServerTemplate] = [
678
732
  config={
679
733
  "command": "npx",
680
734
  "args": ["-y", "@modelcontextprotocol/server-azure-storage"],
681
- "env": {"AZURE_STORAGE_CONNECTION_STRING": "$AZURE_STORAGE_CONNECTION_STRING"},
682
- "timeout": 30
735
+ "env": {
736
+ "AZURE_STORAGE_CONNECTION_STRING": "$AZURE_STORAGE_CONNECTION_STRING"
737
+ },
738
+ "timeout": 30,
683
739
  },
684
740
  verified=True,
685
741
  requires=MCPServerRequirements(
686
742
  environment_vars=["AZURE_STORAGE_CONNECTION_STRING"],
687
743
  required_tools=["node", "npm", "npx"],
688
744
  package_dependencies=["@modelcontextprotocol/server-azure-storage"],
689
- system_requirements=["Azure storage account"]
690
- )
745
+ system_requirements=["Azure storage account"],
746
+ ),
691
747
  ),
692
-
693
748
  # ========== Security & Authentication ==========
694
749
  MCPServerTemplate(
695
750
  id="1password",
@@ -699,18 +754,13 @@ MCP_SERVER_REGISTRY: List[MCPServerTemplate] = [
699
754
  category="Security",
700
755
  tags=["security", "password", "vault", "1password", "secrets"],
701
756
  type="stdio",
702
- config={
703
- "command": "op",
704
- "args": ["mcp-server"],
705
- "timeout": 30
706
- },
757
+ config={"command": "op", "args": ["mcp-server"], "timeout": 30},
707
758
  verified=True,
708
759
  requires=MCPServerRequirements(
709
760
  required_tools=["op"],
710
- system_requirements=["1Password CLI installed and authenticated"]
711
- )
761
+ system_requirements=["1Password CLI installed and authenticated"],
762
+ ),
712
763
  ),
713
-
714
764
  MCPServerTemplate(
715
765
  id="vault",
716
766
  name="vault",
@@ -723,17 +773,16 @@ MCP_SERVER_REGISTRY: List[MCPServerTemplate] = [
723
773
  "command": "npx",
724
774
  "args": ["-y", "@modelcontextprotocol/server-vault"],
725
775
  "env": {"VAULT_TOKEN": "$VAULT_TOKEN"},
726
- "timeout": 30
776
+ "timeout": 30,
727
777
  },
728
778
  verified=True,
729
779
  requires=MCPServerRequirements(
730
780
  environment_vars=["VAULT_TOKEN", "VAULT_ADDR"],
731
781
  required_tools=["node", "npm", "npx"],
732
782
  package_dependencies=["@modelcontextprotocol/server-vault"],
733
- system_requirements=["HashiCorp Vault server accessible"]
734
- )
783
+ system_requirements=["HashiCorp Vault server accessible"],
784
+ ),
735
785
  ),
736
-
737
786
  # ========== Documentation & Knowledge ==========
738
787
  MCPServerTemplate(
739
788
  id="context7",
@@ -746,18 +795,17 @@ MCP_SERVER_REGISTRY: List[MCPServerTemplate] = [
746
795
  config={
747
796
  "timeout": 30,
748
797
  "command": "npx",
749
- "args": ["-y", "@upstash/context7-mcp","--api-key", "$CONTEXT7_API_KEY"]
798
+ "args": ["-y", "@upstash/context7-mcp", "--api-key", "$CONTEXT7_API_KEY"],
750
799
  },
751
800
  verified=True,
752
801
  popular=True,
753
802
  requires=MCPServerRequirements(
754
803
  environment_vars=["CONTEXT7_API_KEY"],
755
804
  required_tools=["node", "npx"],
756
- package_dependencies=["@upstash/context7-mcp"]
805
+ package_dependencies=["@upstash/context7-mcp"],
757
806
  ),
758
- example_usage="Cloud-based service - no local setup required"
807
+ example_usage="Cloud-based service - no local setup required",
759
808
  ),
760
-
761
809
  MCPServerTemplate(
762
810
  id="confluence",
763
811
  name="confluence",
@@ -770,17 +818,16 @@ MCP_SERVER_REGISTRY: List[MCPServerTemplate] = [
770
818
  "command": "npx",
771
819
  "args": ["-y", "@modelcontextprotocol/server-confluence"],
772
820
  "env": {"CONFLUENCE_TOKEN": "$CONFLUENCE_TOKEN"},
773
- "timeout": 30
821
+ "timeout": 30,
774
822
  },
775
823
  verified=True,
776
824
  requires=MCPServerRequirements(
777
825
  environment_vars=["CONFLUENCE_TOKEN", "CONFLUENCE_BASE_URL"],
778
826
  required_tools=["node", "npm", "npx"],
779
827
  package_dependencies=["@modelcontextprotocol/server-confluence"],
780
- system_requirements=["Confluence API access"]
781
- )
828
+ system_requirements=["Confluence API access"],
829
+ ),
782
830
  ),
783
-
784
831
  MCPServerTemplate(
785
832
  id="notion",
786
833
  name="notion",
@@ -793,7 +840,7 @@ MCP_SERVER_REGISTRY: List[MCPServerTemplate] = [
793
840
  "command": "npx",
794
841
  "args": ["-y", "@modelcontextprotocol/server-notion"],
795
842
  "env": {"NOTION_TOKEN": "$NOTION_TOKEN"},
796
- "timeout": 30
843
+ "timeout": 30,
797
844
  },
798
845
  verified=True,
799
846
  popular=True,
@@ -801,10 +848,9 @@ MCP_SERVER_REGISTRY: List[MCPServerTemplate] = [
801
848
  environment_vars=["NOTION_TOKEN"],
802
849
  required_tools=["node", "npm", "npx"],
803
850
  package_dependencies=["@modelcontextprotocol/server-notion"],
804
- system_requirements=["Notion integration API key"]
805
- )
851
+ system_requirements=["Notion integration API key"],
852
+ ),
806
853
  ),
807
-
808
854
  # ========== DevOps & Infrastructure ==========
809
855
  MCPServerTemplate(
810
856
  id="docker",
@@ -817,17 +863,16 @@ MCP_SERVER_REGISTRY: List[MCPServerTemplate] = [
817
863
  config={
818
864
  "command": "npx",
819
865
  "args": ["-y", "@modelcontextprotocol/server-docker"],
820
- "timeout": 30
866
+ "timeout": 30,
821
867
  },
822
868
  verified=True,
823
869
  popular=True,
824
870
  requires=MCPServerRequirements(
825
871
  required_tools=["node", "npm", "npx", "docker"],
826
872
  package_dependencies=["@modelcontextprotocol/server-docker"],
827
- system_requirements=["Docker daemon running"]
828
- )
873
+ system_requirements=["Docker daemon running"],
874
+ ),
829
875
  ),
830
-
831
876
  MCPServerTemplate(
832
877
  id="kubernetes",
833
878
  name="kubernetes",
@@ -839,16 +884,15 @@ MCP_SERVER_REGISTRY: List[MCPServerTemplate] = [
839
884
  config={
840
885
  "command": "npx",
841
886
  "args": ["-y", "@modelcontextprotocol/server-kubernetes"],
842
- "timeout": 30
887
+ "timeout": 30,
843
888
  },
844
889
  verified=True,
845
890
  requires=MCPServerRequirements(
846
891
  required_tools=["node", "npm", "npx", "kubectl"],
847
892
  package_dependencies=["@modelcontextprotocol/server-kubernetes"],
848
- system_requirements=["Kubernetes cluster access (kubeconfig)"]
849
- )
893
+ system_requirements=["Kubernetes cluster access (kubeconfig)"],
894
+ ),
850
895
  ),
851
-
852
896
  MCPServerTemplate(
853
897
  id="terraform",
854
898
  name="terraform",
@@ -860,16 +904,15 @@ MCP_SERVER_REGISTRY: List[MCPServerTemplate] = [
860
904
  config={
861
905
  "command": "npx",
862
906
  "args": ["-y", "@modelcontextprotocol/server-terraform"],
863
- "timeout": 60
907
+ "timeout": 60,
864
908
  },
865
909
  verified=True,
866
910
  requires=MCPServerRequirements(
867
911
  required_tools=["node", "npm", "npx", "terraform"],
868
912
  package_dependencies=["@modelcontextprotocol/server-terraform"],
869
- system_requirements=["Terraform configuration files"]
870
- )
913
+ system_requirements=["Terraform configuration files"],
914
+ ),
871
915
  ),
872
-
873
916
  # ========== Monitoring & Observability ==========
874
917
  MCPServerTemplate(
875
918
  id="prometheus",
@@ -881,20 +924,28 @@ MCP_SERVER_REGISTRY: List[MCPServerTemplate] = [
881
924
  type="stdio",
882
925
  config={
883
926
  "command": "npx",
884
- "args": ["-y", "@modelcontextprotocol/server-prometheus", "${prometheus_url}"],
885
- "timeout": 30
927
+ "args": [
928
+ "-y",
929
+ "@modelcontextprotocol/server-prometheus",
930
+ "${prometheus_url}",
931
+ ],
932
+ "timeout": 30,
886
933
  },
887
934
  verified=True,
888
935
  requires=MCPServerRequirements(
889
936
  command_line_args=[
890
- {"name": "prometheus_url", "prompt": "Prometheus server URL", "default": "http://localhost:9090", "required": True}
937
+ {
938
+ "name": "prometheus_url",
939
+ "prompt": "Prometheus server URL",
940
+ "default": "http://localhost:9090",
941
+ "required": True,
942
+ }
891
943
  ],
892
944
  required_tools=["node", "npm", "npx"],
893
945
  package_dependencies=["@modelcontextprotocol/server-prometheus"],
894
- system_requirements=["Prometheus server accessible"]
895
- )
946
+ system_requirements=["Prometheus server accessible"],
947
+ ),
896
948
  ),
897
-
898
949
  MCPServerTemplate(
899
950
  id="grafana",
900
951
  name="grafana",
@@ -907,17 +958,16 @@ MCP_SERVER_REGISTRY: List[MCPServerTemplate] = [
907
958
  "command": "npx",
908
959
  "args": ["-y", "@modelcontextprotocol/server-grafana"],
909
960
  "env": {"GRAFANA_TOKEN": "$GRAFANA_TOKEN"},
910
- "timeout": 30
961
+ "timeout": 30,
911
962
  },
912
963
  verified=True,
913
964
  requires=MCPServerRequirements(
914
965
  environment_vars=["GRAFANA_TOKEN", "GRAFANA_URL"],
915
966
  required_tools=["node", "npm", "npx"],
916
967
  package_dependencies=["@modelcontextprotocol/server-grafana"],
917
- system_requirements=["Grafana server with API access"]
918
- )
968
+ system_requirements=["Grafana server with API access"],
969
+ ),
919
970
  ),
920
-
921
971
  # ========== Package Management ==========
922
972
  MCPServerTemplate(
923
973
  id="npm",
@@ -930,15 +980,14 @@ MCP_SERVER_REGISTRY: List[MCPServerTemplate] = [
930
980
  config={
931
981
  "command": "npx",
932
982
  "args": ["-y", "@modelcontextprotocol/server-npm"],
933
- "timeout": 30
983
+ "timeout": 30,
934
984
  },
935
985
  verified=True,
936
986
  requires=MCPServerRequirements(
937
987
  required_tools=["node", "npm", "npx"],
938
- package_dependencies=["@modelcontextprotocol/server-npm"]
939
- )
988
+ package_dependencies=["@modelcontextprotocol/server-npm"],
989
+ ),
940
990
  ),
941
-
942
991
  MCPServerTemplate(
943
992
  id="pypi",
944
993
  name="pypi",
@@ -947,27 +996,22 @@ MCP_SERVER_REGISTRY: List[MCPServerTemplate] = [
947
996
  category="Package Management",
948
997
  tags=["python", "pip", "pypi", "package"],
949
998
  type="stdio",
950
- config={
951
- "command": "python",
952
- "args": ["-m", "mcp_server_pypi"],
953
- "timeout": 30
954
- },
999
+ config={"command": "python", "args": ["-m", "mcp_server_pypi"], "timeout": 30},
955
1000
  verified=True,
956
1001
  requires=MCPServerRequirements(
957
- required_tools=["python", "pip"],
958
- package_dependencies=["mcp-server-pypi"]
959
- )
1002
+ required_tools=["python", "pip"], package_dependencies=["mcp-server-pypi"]
1003
+ ),
960
1004
  ),
961
1005
  ]
962
1006
 
963
1007
 
964
1008
  class MCPServerCatalog:
965
1009
  """Catalog for searching and managing pre-configured MCP servers."""
966
-
1010
+
967
1011
  def __init__(self):
968
1012
  self.servers = MCP_SERVER_REGISTRY
969
1013
  self._build_index()
970
-
1014
+
971
1015
  def _build_index(self):
972
1016
  """Build search index for fast lookups."""
973
1017
  self.by_id = {s.id: s for s in self.servers}
@@ -976,76 +1020,78 @@ class MCPServerCatalog:
976
1020
  if server.category not in self.by_category:
977
1021
  self.by_category[server.category] = []
978
1022
  self.by_category[server.category].append(server)
979
-
1023
+
980
1024
  def search(self, query: str) -> List[MCPServerTemplate]:
981
1025
  """
982
1026
  Search for servers by name, description, or tags.
983
-
1027
+
984
1028
  Args:
985
1029
  query: Search query string
986
-
1030
+
987
1031
  Returns:
988
1032
  List of matching server templates
989
1033
  """
990
1034
  query_lower = query.lower()
991
1035
  results = []
992
-
1036
+
993
1037
  for server in self.servers:
994
1038
  # Check name
995
1039
  if query_lower in server.name.lower():
996
1040
  results.append(server)
997
1041
  continue
998
-
1042
+
999
1043
  # Check display name
1000
1044
  if query_lower in server.display_name.lower():
1001
1045
  results.append(server)
1002
1046
  continue
1003
-
1047
+
1004
1048
  # Check description
1005
1049
  if query_lower in server.description.lower():
1006
1050
  results.append(server)
1007
1051
  continue
1008
-
1052
+
1009
1053
  # Check tags
1010
1054
  for tag in server.tags:
1011
1055
  if query_lower in tag.lower():
1012
1056
  results.append(server)
1013
1057
  break
1014
-
1058
+
1015
1059
  # Check category
1016
1060
  if query_lower in server.category.lower() and server not in results:
1017
1061
  results.append(server)
1018
-
1062
+
1019
1063
  # Sort by relevance (name matches first, then popular)
1020
- results.sort(key=lambda s: (
1021
- not s.name.lower().startswith(query_lower),
1022
- not s.popular,
1023
- s.name
1024
- ))
1025
-
1064
+ results.sort(
1065
+ key=lambda s: (
1066
+ not s.name.lower().startswith(query_lower),
1067
+ not s.popular,
1068
+ s.name,
1069
+ )
1070
+ )
1071
+
1026
1072
  return results
1027
-
1073
+
1028
1074
  def get_by_id(self, server_id: str) -> Optional[MCPServerTemplate]:
1029
1075
  """Get server template by ID."""
1030
1076
  return self.by_id.get(server_id)
1031
-
1077
+
1032
1078
  def get_by_category(self, category: str) -> List[MCPServerTemplate]:
1033
1079
  """Get all servers in a category."""
1034
1080
  return self.by_category.get(category, [])
1035
-
1081
+
1036
1082
  def list_categories(self) -> List[str]:
1037
1083
  """List all available categories."""
1038
1084
  return sorted(self.by_category.keys())
1039
-
1085
+
1040
1086
  def get_popular(self, limit: int = 10) -> List[MCPServerTemplate]:
1041
1087
  """Get popular servers."""
1042
1088
  popular = [s for s in self.servers if s.popular]
1043
1089
  return popular[:limit]
1044
-
1090
+
1045
1091
  def get_verified(self) -> List[MCPServerTemplate]:
1046
1092
  """Get all verified servers."""
1047
1093
  return [s for s in self.servers if s.verified]
1048
1094
 
1049
1095
 
1050
1096
  # Global catalog instance
1051
- catalog = MCPServerCatalog()
1097
+ catalog = MCPServerCatalog()