@supersoniks/concorde 3.3.2 → 3.3.3

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.
@@ -1,801 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Serveur MCP pour Concorde - Framework de composants Web
4
- Permet à l'IA de Cursor d'accéder facilement à toute la documentation et au code de Concorde
5
- """
6
-
7
- import json
8
- import os
9
- import re
10
- from pathlib import Path
11
- from typing import Any, Dict, List, Optional, Union
12
- import asyncio
13
- from dataclasses import dataclass
14
- from mcp.server import Server
15
- from mcp.server.models import InitializationOptions
16
- from mcp.server.stdio import stdio_server
17
- from mcp.types import (
18
- Resource,
19
- Tool,
20
- TextContent,
21
- ImageContent,
22
- EmbeddedResource,
23
- CallToolRequest,
24
- CallToolResult,
25
- ListResourcesRequest,
26
- ListResourcesResult,
27
- ListToolsRequest,
28
- ListToolsResult,
29
- ReadResourceRequest,
30
- ReadResourceResult
31
- )
32
-
33
- # Configuration du projet Concorde
34
- CONCORDE_ROOT = Path(__file__).parent.parent
35
- SRC_PATH = CONCORDE_ROOT / "src"
36
- DOCS_PATH = CONCORDE_ROOT / "docs"
37
- CORE_PATH = SRC_PATH / "core"
38
- COMPONENTS_PATH = CORE_PATH / "components"
39
-
40
- @dataclass
41
- class ComponentInfo:
42
- """Information sur un composant Concorde"""
43
- name: str
44
- tag_name: str
45
- category: str # 'ui' ou 'functional'
46
- file_path: Path
47
- doc_path: Optional[Path]
48
- description: str
49
- props: List[Dict[str, Any]]
50
- examples: List[str]
51
-
52
- class ConcordeMCPServer:
53
- """Serveur MCP pour Concorde"""
54
-
55
- def __init__(self):
56
- self.server = Server("concorde-mcp")
57
- self.components: Dict[str, ComponentInfo] = {}
58
- self.documentation: Dict[str, str] = {}
59
- self._setup_handlers()
60
- self._load_components()
61
- self._load_documentation()
62
-
63
- def _setup_handlers(self):
64
- """Configure les handlers MCP"""
65
-
66
- @self.server.list_resources()
67
- async def list_resources() -> ListResourcesResult:
68
- """Liste toutes les ressources disponibles"""
69
- resources = []
70
-
71
- # Ressources de composants
72
- for comp_name, comp_info in self.components.items():
73
- resources.append(Resource(
74
- uri=f"concorde://component/{comp_name}",
75
- name=f"Composant {comp_name}",
76
- description=comp_info.description,
77
- mimeType="text/markdown"
78
- ))
79
-
80
- # Ressources de documentation
81
- for doc_name, doc_content in self.documentation.items():
82
- resources.append(Resource(
83
- uri=f"concorde://docs/{doc_name}",
84
- name=f"Documentation {doc_name}",
85
- description=f"Documentation Concorde: {doc_name}",
86
- mimeType="text/markdown"
87
- ))
88
-
89
- # Ressources d'exemples
90
- resources.append(Resource(
91
- uri="concorde://examples/ui-components",
92
- name="Exemples composants UI",
93
- description="Exemples d'utilisation des composants UI",
94
- mimeType="text/markdown"
95
- ))
96
-
97
- resources.append(Resource(
98
- uri="concorde://examples/functional-components",
99
- name="Exemples composants fonctionnels",
100
- description="Exemples d'utilisation des composants fonctionnels",
101
- mimeType="text/markdown"
102
- ))
103
-
104
- return ListResourcesResult(resources=resources)
105
-
106
- @self.server.read_resource()
107
- async def read_resource(uri: str) -> ReadResourceResult:
108
- """Lit une ressource"""
109
- if uri.startswith("concorde://component/"):
110
- comp_name = uri.replace("concorde://component/", "")
111
- if comp_name in self.components:
112
- comp_info = self.components[comp_name]
113
- content = self._generate_component_doc(comp_info)
114
- return ReadResourceResult(contents=[TextContent(type="text", text=content)])
115
-
116
- elif uri.startswith("concorde://docs/"):
117
- doc_name = uri.replace("concorde://docs/", "")
118
- if doc_name in self.documentation:
119
- return ReadResourceResult(contents=[TextContent(type="text", text=self.documentation[doc_name])])
120
-
121
- elif uri == "concorde://examples/ui-components":
122
- content = self._generate_ui_examples()
123
- return ReadResourceResult(contents=[TextContent(type="text", text=content)])
124
-
125
- elif uri == "concorde://examples/functional-components":
126
- content = self._generate_functional_examples()
127
- return ReadResourceResult(contents=[TextContent(type="text", text=content)])
128
-
129
- return ReadResourceResult(contents=[TextContent(type="text", text="Ressource non trouvée")])
130
-
131
- @self.server.list_tools()
132
- async def list_tools() -> ListToolsResult:
133
- """Liste tous les outils disponibles"""
134
- tools = [
135
- Tool(
136
- name="search_component",
137
- description="Recherche un composant Concorde par nom ou fonctionnalité",
138
- inputSchema={
139
- "type": "object",
140
- "properties": {
141
- "query": {
142
- "type": "string",
143
- "description": "Terme de recherche (nom du composant, fonctionnalité, etc.)"
144
- }
145
- },
146
- "required": ["query"]
147
- }
148
- ),
149
- Tool(
150
- name="get_component_info",
151
- description="Obtient des informations détaillées sur un composant spécifique",
152
- inputSchema={
153
- "type": "object",
154
- "properties": {
155
- "component_name": {
156
- "type": "string",
157
- "description": "Nom du composant (ex: button, modal, input)"
158
- }
159
- },
160
- "required": ["component_name"]
161
- }
162
- ),
163
- Tool(
164
- name="generate_component_code",
165
- description="Génère du code HTML pour un composant Concorde avec ses propriétés",
166
- inputSchema={
167
- "type": "object",
168
- "properties": {
169
- "component_name": {
170
- "type": "string",
171
- "description": "Nom du composant"
172
- },
173
- "properties": {
174
- "type": "object",
175
- "description": "Propriétés du composant",
176
- "additionalProperties": True
177
- },
178
- "content": {
179
- "type": "string",
180
- "description": "Contenu du composant (optionnel)"
181
- }
182
- },
183
- "required": ["component_name"]
184
- }
185
- ),
186
- Tool(
187
- name="get_form_example",
188
- description="Génère un exemple de formulaire avec des composants Concorde",
189
- inputSchema={
190
- "type": "object",
191
- "properties": {
192
- "fields": {
193
- "type": "array",
194
- "description": "Liste des champs du formulaire",
195
- "items": {
196
- "type": "object",
197
- "properties": {
198
- "name": {"type": "string"},
199
- "type": {"type": "string"},
200
- "label": {"type": "string"},
201
- "required": {"type": "boolean"}
202
- }
203
- }
204
- }
205
- }
206
- }
207
- ),
208
- Tool(
209
- name="get_theme_info",
210
- description="Obtient des informations sur le système de thèmes Concorde",
211
- inputSchema={
212
- "type": "object",
213
- "properties": {
214
- "theme": {
215
- "type": "string",
216
- "description": "Nom du thème (light, dark, auto)",
217
- "enum": ["light", "dark", "auto"]
218
- }
219
- }
220
- }
221
- ),
222
- Tool(
223
- name="get_icons_list",
224
- description="Liste toutes les icônes disponibles dans Concorde",
225
- inputSchema={
226
- "type": "object",
227
- "properties": {
228
- "library": {
229
- "type": "string",
230
- "description": "Bibliothèque d'icônes (core, iconoir, fontAwesome)",
231
- "enum": ["core", "iconoir", "fontAwesome"]
232
- }
233
- }
234
- }
235
- )
236
- ]
237
- return ListToolsResult(tools=tools)
238
-
239
- @self.server.call_tool()
240
- async def call_tool(name: str, arguments: Dict[str, Any]) -> CallToolResult:
241
- """Exécute un outil"""
242
- if name == "search_component":
243
- return await self._search_component(arguments["query"])
244
- elif name == "get_component_info":
245
- return await self._get_component_info(arguments["component_name"])
246
- elif name == "generate_component_code":
247
- return await self._generate_component_code(
248
- arguments["component_name"],
249
- arguments.get("properties", {}),
250
- arguments.get("content", "")
251
- )
252
- elif name == "get_form_example":
253
- return await self._get_form_example(arguments.get("fields", []))
254
- elif name == "get_theme_info":
255
- return await self._get_theme_info(arguments.get("theme", "light"))
256
- elif name == "get_icons_list":
257
- return await self._get_icons_list(arguments.get("library", "core"))
258
- else:
259
- return CallToolResult(content=[TextContent(type="text", text=f"Outil inconnu: {name}")])
260
-
261
- def _load_components(self):
262
- """Charge tous les composants Concorde"""
263
-
264
- # Composants UI
265
- ui_path = COMPONENTS_PATH / "ui"
266
- for comp_dir in ui_path.iterdir():
267
- if comp_dir.is_dir() and not comp_dir.name.startswith("_"):
268
- self._load_component(comp_dir, "ui")
269
-
270
- # Composants fonctionnels
271
- functional_path = COMPONENTS_PATH / "functional"
272
- for comp_dir in functional_path.iterdir():
273
- if comp_dir.is_dir():
274
- self._load_component(comp_dir, "functional")
275
-
276
- def _load_component(self, comp_dir: Path, category: str):
277
- """Charge un composant spécifique"""
278
- comp_name = comp_dir.name
279
- tag_name = f"sonic-{comp_name}"
280
-
281
- # Trouve le fichier principal
282
- main_file = comp_dir / f"{comp_name}.ts"
283
- if not main_file.exists():
284
- return
285
-
286
- # Trouve le fichier de documentation
287
- doc_file = comp_dir / f"{comp_name}.md"
288
-
289
- # Lit le contenu du composant
290
- try:
291
- with open(main_file, 'r', encoding='utf-8') as f:
292
- content = f.read()
293
- except:
294
- return
295
-
296
- # Extrait les propriétés du composant
297
- props = self._extract_properties(content)
298
-
299
- # Extrait la description
300
- description = self._extract_description(content)
301
-
302
- # Charge la documentation si elle existe
303
- doc_content = ""
304
- if doc_file.exists():
305
- try:
306
- with open(doc_file, 'r', encoding='utf-8') as f:
307
- doc_content = f.read()
308
- except:
309
- pass
310
-
311
- # Extrait les exemples de la documentation
312
- examples = self._extract_examples(doc_content)
313
-
314
- comp_info = ComponentInfo(
315
- name=comp_name,
316
- tag_name=tag_name,
317
- category=category,
318
- file_path=main_file,
319
- doc_path=doc_file if doc_file.exists() else None,
320
- description=description,
321
- props=props,
322
- examples=examples
323
- )
324
-
325
- self.components[comp_name] = comp_info
326
-
327
- def _extract_properties(self, content: str) -> List[Dict[str, Any]]:
328
- """Extrait les propriétés du composant"""
329
- props = []
330
-
331
- # Recherche les décorateurs @property
332
- property_pattern = r'@property\([^)]*\)\s+(\w+)(?::\s*([^=]+))?'
333
- matches = re.findall(property_pattern, content)
334
-
335
- for match in matches:
336
- prop_name = match[0]
337
- prop_type = match[1] if match[1] else "string"
338
-
339
- # Recherche les commentaires JSDoc pour cette propriété
340
- comment_pattern = rf'/\*\*\s*\n\s*\*\s*([^\n]+)'
341
- comment_match = re.search(comment_pattern, content)
342
- description = comment_match.group(1).strip() if comment_match else ""
343
-
344
- props.append({
345
- "name": prop_name,
346
- "type": prop_type,
347
- "description": description
348
- })
349
-
350
- return props
351
-
352
- def _extract_description(self, content: str) -> str:
353
- """Extrait la description du composant"""
354
- # Recherche les commentaires de classe
355
- class_pattern = r'/\*\*\s*\n\s*\*\s*([^\n]+)'
356
- match = re.search(class_pattern, content)
357
- if match:
358
- return match.group(1).strip()
359
-
360
- # Recherche le nom de la classe
361
- class_name_pattern = r'export class (\w+)'
362
- match = re.search(class_name_pattern, content)
363
- if match:
364
- return f"Composant {match.group(1)}"
365
-
366
- return "Composant Concorde"
367
-
368
- def _extract_examples(self, doc_content: str) -> List[str]:
369
- """Extrait les exemples de la documentation"""
370
- examples = []
371
-
372
- # Recherche les blocs sonic-code
373
- code_pattern = r'<sonic-code>(.*?)</sonic-code>'
374
- matches = re.findall(code_pattern, doc_content, re.DOTALL)
375
-
376
- for match in matches:
377
- # Nettoie le contenu
378
- example = match.strip()
379
- if example:
380
- examples.append(example)
381
-
382
- return examples
383
-
384
- def _load_documentation(self):
385
- """Charge la documentation générale"""
386
- # Documentation des concepts de base
387
- self.documentation["architecture"] = """
388
- # Architecture Concorde
389
-
390
- Concorde est un framework de composants Web basé sur Lit Element avec les concepts suivants :
391
-
392
- ## Mixins principaux
393
-
394
- ### Subscriber
395
- - Permet de lier un composant à un publisher
396
- - Gestion automatique des données réactives
397
- - Support du data binding
398
-
399
- ### FormElement
400
- - Gestion des formulaires
401
- - Validation automatique
402
- - Liaison avec les publishers de formulaire
403
-
404
- ### Fetcher
405
- - Gestion des appels API
406
- - Chargement automatique des données
407
- - Support des filtres et pagination
408
-
409
- ## Système de thèmes
410
- - Thèmes light/dark/auto
411
- - Variables CSS personnalisables
412
- - Support des couleurs sémantiques
413
-
414
- ## Composants disponibles
415
- - Composants UI : boutons, inputs, modales, etc.
416
- - Composants fonctionnels : fetch, list, router, etc.
417
- - Système d'icônes intégré
418
- """
419
-
420
- self.documentation["data-binding"] = """
421
- # Data Binding Concorde
422
-
423
- ## Syntaxe
424
- - `data-bind ::attribute="expression"` : Liaison d'attribut
425
- - `data-bind ::inner-html="expression"` : Liaison de contenu HTML
426
- - `data-bind ::text-content="expression"` : Liaison de contenu texte
427
-
428
- ## Expressions
429
- - `$property` : Accès à une propriété du publisher
430
- - `$property.subproperty` : Accès à une sous-propriété
431
- - `function|$property` : Application d'une fonction
432
-
433
- ## Exemples
434
- ```html
435
- <sonic-input data-bind ::value="$email"></sonic-input>
436
- <span data-bind ::inner-html="$name"></span>
437
- <div data-bind ::class="active|$isActive"></div>
438
- ```
439
- """
440
-
441
- async def _search_component(self, query: str) -> CallToolResult:
442
- """Recherche un composant"""
443
- results = []
444
- query_lower = query.lower()
445
-
446
- for comp_name, comp_info in self.components.items():
447
- if (query_lower in comp_name.lower() or
448
- query_lower in comp_info.description.lower() or
449
- query_lower in comp_info.tag_name.lower()):
450
- results.append(f"- **{comp_info.tag_name}** ({comp_info.category}): {comp_info.description}")
451
-
452
- if results:
453
- content = f"Composants trouvés pour '{query}':\n\n" + "\n".join(results)
454
- else:
455
- content = f"Aucun composant trouvé pour '{query}'"
456
-
457
- return CallToolResult(content=[TextContent(type="text", text=content)])
458
-
459
- async def _get_component_info(self, component_name: str) -> CallToolResult:
460
- """Obtient des informations sur un composant"""
461
- if component_name not in self.components:
462
- return CallToolResult(content=[TextContent(type="text", text=f"Composant '{component_name}' non trouvé")])
463
-
464
- comp_info = self.components[component_name]
465
-
466
- # Génère la documentation complète
467
- content = f"""# {comp_info.tag_name}
468
-
469
- **Catégorie:** {comp_info.category}
470
- **Description:** {comp_info.description}
471
-
472
- ## Propriétés
473
-
474
- """
475
-
476
- for prop in comp_info.props:
477
- content += f"- **{prop['name']}** ({prop['type']}): {prop['description']}\n"
478
-
479
- if comp_info.examples:
480
- content += "\n## Exemples\n\n"
481
- for i, example in enumerate(comp_info.examples[:3], 1): # Limite à 3 exemples
482
- content += f"### Exemple {i}\n\n```html\n{example}\n```\n\n"
483
-
484
- return CallToolResult(content=[TextContent(type="text", text=content)])
485
-
486
- async def _generate_component_code(self, component_name: str, properties: Dict[str, Any], content: str = "") -> CallToolResult:
487
- """Génère du code pour un composant"""
488
- if component_name not in self.components:
489
- return CallToolResult(content=[TextContent(type="text", text=f"Composant '{component_name}' non trouvé")])
490
-
491
- comp_info = self.components[component_name]
492
- tag_name = comp_info.tag_name
493
-
494
- # Génère les attributs
495
- attributes = []
496
- for key, value in properties.items():
497
- if isinstance(value, bool):
498
- if value:
499
- attributes.append(key)
500
- else:
501
- attributes.append(f'{key}="{value}"')
502
-
503
- # Génère le code HTML
504
- if content:
505
- html_code = f"<{tag_name} {' '.join(attributes)}>{content}</{tag_name}>"
506
- else:
507
- html_code = f"<{tag_name} {' '.join(attributes)}></{tag_name}>"
508
-
509
- return CallToolResult(content=[TextContent(type="text", text=html_code)])
510
-
511
- async def _get_form_example(self, fields: List[Dict[str, Any]]) -> CallToolResult:
512
- """Génère un exemple de formulaire"""
513
- if not fields:
514
- # Exemple par défaut
515
- fields = [
516
- {"name": "email", "type": "email", "label": "Email", "required": True},
517
- {"name": "password", "type": "password", "label": "Mot de passe", "required": True},
518
- {"name": "remember", "type": "checkbox", "label": "Se souvenir de moi", "required": False}
519
- ]
520
-
521
- form_html = '<div formDataProvider="exampleForm">\n'
522
-
523
- for field in fields:
524
- field_name = field["name"]
525
- field_type = field["type"]
526
- field_label = field["label"]
527
- field_required = field.get("required", False)
528
-
529
- if field_type == "checkbox":
530
- form_html += f' <sonic-checkbox name="{field_name}"'
531
- if field_required:
532
- form_html += " required"
533
- form_html += f'>{field_label}</sonic-checkbox>\n'
534
- else:
535
- form_html += f' <sonic-input name="{field_name}" type="{field_type}" label="{field_label}"'
536
- if field_required:
537
- form_html += " required"
538
- form_html += '></sonic-input>\n'
539
-
540
- form_html += ' <sonic-form-actions>\n'
541
- form_html += ' <sonic-button type="primary">Valider</sonic-button>\n'
542
- form_html += ' <sonic-button variant="outline">Annuler</sonic-button>\n'
543
- form_html += ' </sonic-form-actions>\n'
544
- form_html += '</div>'
545
-
546
- return CallToolResult(content=[TextContent(type="text", text=form_html)])
547
-
548
- async def _get_theme_info(self, theme: str) -> CallToolResult:
549
- """Obtient des informations sur le thème"""
550
- theme_info = {
551
- "light": {
552
- "description": "Thème clair par défaut",
553
- "colors": {
554
- "primary": "#1e293b",
555
- "info": "#2563eb",
556
- "danger": "#f43f5e",
557
- "warning": "#f97316",
558
- "success": "#14b8a6"
559
- }
560
- },
561
- "dark": {
562
- "description": "Thème sombre",
563
- "colors": {
564
- "primary": "#334155",
565
- "info": "#3abff8",
566
- "danger": "#f87272",
567
- "warning": "#fbbd23",
568
- "success": "#36d399"
569
- }
570
- },
571
- "auto": {
572
- "description": "Thème automatique basé sur les préférences système",
573
- "colors": "Utilise les couleurs du thème sombre si l'utilisateur préfère le mode sombre"
574
- }
575
- }
576
-
577
- info = theme_info.get(theme, theme_info["light"])
578
-
579
- content = f"""# Thème {theme}
580
-
581
- {info['description']}
582
-
583
- ## Utilisation
584
-
585
- ```html
586
- <sonic-theme theme="{theme}">
587
- <!-- Vos composants ici -->
588
- </sonic-theme>
589
- ```
590
-
591
- """
592
-
593
- if "colors" in info and isinstance(info["colors"], dict):
594
- content += "## Couleurs\n\n"
595
- for color_name, color_value in info["colors"].items():
596
- content += f"- **{color_name}**: `{color_value}`\n"
597
-
598
- return CallToolResult(content=[TextContent(type="text", text=content)])
599
-
600
- async def _get_icons_list(self, library: str) -> CallToolResult:
601
- """Liste les icônes disponibles"""
602
- icons_info = {
603
- "core": [
604
- "cancel", "check-circled-outline", "check", "emoji-puzzled",
605
- "info-empty", "loader", "minus-small", "more-horiz", "more-vert",
606
- "nav-arrow-down", "warning-circled-outline"
607
- ],
608
- "iconoir": "Bibliothèque d'icônes externe avec de nombreuses icônes",
609
- "fontAwesome": "Bibliothèque FontAwesome avec icônes solid et regular"
610
- }
611
-
612
- if library == "core":
613
- icons = icons_info["core"]
614
- content = f"""# Icônes Core Concorde
615
-
616
- ## Utilisation
617
-
618
- ```html
619
- <sonic-icon library="core" name="nom-de-l-icone"></sonic-icon>
620
- ```
621
-
622
- ## Icônes disponibles
623
-
624
- """
625
- for icon in icons:
626
- content += f"- **{icon}**: `<sonic-icon library="core" name="{icon}"></sonic-icon>`\n"
627
- else:
628
- content = f"""# Bibliothèque d'icônes {library}
629
-
630
- {icons_info[library]}
631
-
632
- ## Utilisation
633
-
634
- ```html
635
- <sonic-icon library="{library}" name="nom-de-l-icone"></sonic-icon>
636
- ```
637
-
638
- Pour les icônes FontAwesome, vous pouvez spécifier le préfixe :
639
- ```html
640
- <sonic-icon library="fontAwesome" prefix="solid" name="home"></sonic-icon>
641
- <sonic-icon library="fontAwesome" prefix="regular" name="user"></sonic-icon>
642
- ```
643
- """
644
-
645
- return CallToolResult(content=[TextContent(type="text", text=content)])
646
-
647
- def _generate_component_doc(self, comp_info: ComponentInfo) -> str:
648
- """Génère la documentation complète d'un composant"""
649
- content = f"""# {comp_info.tag_name}
650
-
651
- **Catégorie:** {comp_info.category}
652
- **Description:** {comp_info.description}
653
-
654
- ## Propriétés
655
-
656
- """
657
-
658
- for prop in comp_info.props:
659
- content += f"- **{prop['name']}** ({prop['type']}): {prop['description']}\n"
660
-
661
- if comp_info.examples:
662
- content += "\n## Exemples\n\n"
663
- for i, example in enumerate(comp_info.examples, 1):
664
- content += f"### Exemple {i}\n\n```html\n{example}\n```\n\n"
665
-
666
- return content
667
-
668
- def _generate_ui_examples(self) -> str:
669
- """Génère des exemples de composants UI"""
670
- content = """# Exemples de composants UI Concorde
671
-
672
- ## Bouton avec icône
673
-
674
- ```html
675
- <sonic-button type="primary">
676
- <sonic-icon library="core" name="check" slot="prefix"></sonic-icon>
677
- Valider
678
- </sonic-button>
679
- ```
680
-
681
- ## Formulaire complet
682
-
683
- ```html
684
- <div formDataProvider="userForm">
685
- <sonic-fieldset label="Informations utilisateur">
686
- <sonic-form-layout>
687
- <sonic-input name="firstName" label="Prénom" required></sonic-input>
688
- <sonic-input name="lastName" label="Nom" required></sonic-input>
689
- <sonic-input name="email" type="email" label="Email" required></sonic-input>
690
- </sonic-form-layout>
691
- </sonic-fieldset>
692
- <sonic-form-actions>
693
- <sonic-button type="primary">Enregistrer</sonic-button>
694
- <sonic-button variant="outline">Annuler</sonic-button>
695
- </sonic-form-actions>
696
- </div>
697
- ```
698
-
699
- ## Modal avec contenu
700
-
701
- ```html
702
- <sonic-modal id="exampleModal">
703
- <sonic-modal-title>Confirmation</sonic-modal-title>
704
- <sonic-modal-content>
705
- Êtes-vous sûr de vouloir continuer ?
706
- </sonic-modal-content>
707
- <sonic-modal-actions>
708
- <sonic-button hideModal>Annuler</sonic-button>
709
- <sonic-button type="primary" hideModal>Confirmer</sonic-button>
710
- </sonic-modal-actions>
711
- </sonic-modal>
712
- ```
713
- """
714
- return content
715
-
716
- def _generate_functional_examples(self) -> str:
717
- """Génère des exemples de composants fonctionnels"""
718
- content = """# Exemples de composants fonctionnels Concorde
719
-
720
- ## Chargement de données avec Fetch
721
-
722
- ```html
723
- <sonic-fetch
724
- serviceURL="https://api.example.com"
725
- endPoint="users"
726
- dataProvider="usersData">
727
- </sonic-fetch>
728
-
729
- <sonic-list dataProvider="usersData" props='$usersData'>
730
- <template>
731
- <div class="user-card">
732
- <h3 data-bind ::inner-html="$name"></h3>
733
- <p data-bind ::inner-html="$email"></p>
734
- </div>
735
- </template>
736
- </sonic-list>
737
- ```
738
-
739
- ## Navigation avec Router
740
-
741
- ```html
742
- <sonic-router>
743
- <template data-route="#home">
744
- <h1>Page d'accueil</h1>
745
- </template>
746
- <template data-route="#about">
747
- <h1>À propos</h1>
748
- </template>
749
- <template data-route="#contact">
750
- <h1>Contact</h1>
751
- </template>
752
- </sonic-router>
753
-
754
- <nav>
755
- <sonic-button href="#home">Accueil</sonic-button>
756
- <sonic-button href="#about">À propos</sonic-button>
757
- <sonic-button href="#contact">Contact</sonic-button>
758
- </nav>
759
- ```
760
-
761
- ## États conditionnels
762
-
763
- ```html
764
- <sonic-states dataProvider="userData" data-path="status">
765
- <template data-value="loading">
766
- <sonic-loader></sonic-loader>
767
- </template>
768
- <template data-value="success">
769
- <div class="success-message">Données chargées avec succès</div>
770
- </template>
771
- <template data-value="error">
772
- <sonic-alert type="danger">Erreur lors du chargement</sonic-alert>
773
- </template>
774
- </sonic-states>
775
- ```
776
- """
777
- return content
778
-
779
- async def run(self):
780
- """Lance le serveur MCP"""
781
- async with stdio_server() as (read_stream, write_stream):
782
- await self.server.run(
783
- read_stream,
784
- write_stream,
785
- InitializationOptions(
786
- server_name="concorde-mcp",
787
- server_version="1.0.0",
788
- capabilities=self.server.get_capabilities(
789
- notification_options=None,
790
- experimental_capabilities={}
791
- )
792
- )
793
- )
794
-
795
- async def main():
796
- """Point d'entrée principal"""
797
- server = ConcordeMCPServer()
798
- await server.run()
799
-
800
- if __name__ == "__main__":
801
- asyncio.run(main())