-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathserver.py
More file actions
255 lines (206 loc) · 9.95 KB
/
server.py
File metadata and controls
255 lines (206 loc) · 9.95 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
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
#!/usr/bin/env python3
"""
Tienda Nube API MCP Server
Servidor Model Context Protocol para la API de Tienda Nube
Permite a Cursor codear usando la API de Tienda Nube
"""
import json
import sys
from pathlib import Path
from typing import Any
# Importar la librería de MCP
try:
from mcp.server import Server
from mcp.types import Tool, TextContent, ToolResult
except ImportError:
print("Error: Instala mcp con: pip install mcp", file=sys.stderr)
sys.exit(1)
class TiendaNubeAPIServer:
"""Servidor MCP para la API de Tienda Nube"""
def __init__(self):
self.server = Server("tiendanube-api")
self.api_database = self._load_api_database()
self._register_tools()
def _load_api_database(self) -> dict:
"""Cargar la base de datos de la API"""
db_path = Path(__file__).parent / "api_database.json"
with open(db_path, "r", encoding="utf-8") as f:
return json.load(f)
def _register_tools(self):
"""Registrar todas las herramientas disponibles"""
@self.server.call_tool()
def search_endpoint(resource: str, method: str = None, query: str = None) -> str:
"""
Buscar endpoints en la API de Tienda Nube
Args:
resource: Recurso a buscar ('products' o 'orders')
method: Método HTTP (GET, POST, PUT, PATCH, DELETE) - opcional
query: Búsqueda por nombre o descripción - opcional
Returns:
Lista de endpoints que coinciden con la búsqueda
"""
endpoints = self.api_database.get("endpoints", {}).get(resource, [])
if not endpoints:
return f"No se encontraron endpoints para el recurso '{resource}'"
results = []
for endpoint in endpoints:
# Filtrar por método si se especifica
if method and endpoint.get("method") != method.upper():
continue
# Filtrar por query si se especifica
if query:
query_lower = query.lower()
if not (query_lower in endpoint.get("name", "").lower() or
query_lower in endpoint.get("description", "").lower() or
query_lower in endpoint.get("path", "").lower()):
continue
results.append({
"method": endpoint.get("method"),
"path": endpoint.get("path"),
"name": endpoint.get("name"),
"description": endpoint.get("description")
})
if not results:
return f"No se encontraron endpoints que coincidan con los criterios"
return json.dumps(results, indent=2, ensure_ascii=False)
@self.server.call_tool()
def get_endpoint_details(resource: str, path: str, method: str = "GET") -> str:
"""
Obtener detalles completos de un endpoint
Args:
resource: Recurso ('products' o 'orders')
path: Ruta del endpoint (ej: '/products', '/orders/{id}')
method: Método HTTP (GET, POST, PUT, PATCH, DELETE)
Returns:
Detalles completos del endpoint incluyendo parámetros, esquema y ejemplos
"""
endpoints = self.api_database.get("endpoints", {}).get(resource, [])
for endpoint in endpoints:
if endpoint.get("path") == path and endpoint.get("method") == method.upper():
return json.dumps(endpoint, indent=2, ensure_ascii=False)
return f"Endpoint no encontrado: {method} {path}"
@self.server.call_tool()
def get_schema(resource: str, endpoint_type: str = "response") -> str:
"""
Obtener esquema JSON de solicitud o respuesta
Args:
resource: Recurso ('products' o 'orders')
endpoint_type: 'request' para esquema de solicitud, 'response' para respuesta
Returns:
Esquema JSON en formato legible
"""
endpoints = self.api_database.get("endpoints", {}).get(resource, [])
schemas = []
for endpoint in endpoints:
if endpoint_type == "request" and "request_schema" in endpoint:
schemas.append({
"method": endpoint.get("method"),
"path": endpoint.get("path"),
"schema": endpoint.get("request_schema")
})
elif endpoint_type == "response" and "response_schema" in endpoint:
schemas.append({
"method": endpoint.get("method"),
"path": endpoint.get("path"),
"schema": endpoint.get("response_schema")
})
if not schemas:
return f"No se encontraron esquemas de {endpoint_type} para {resource}"
return json.dumps(schemas, indent=2, ensure_ascii=False)
@self.server.call_tool()
def search_documentation(query: str) -> str:
"""
Buscar en la documentación por palabras clave
Args:
query: Término de búsqueda
Returns:
Resultados relevantes de la documentación
"""
query_lower = query.lower()
results = []
# Buscar en endpoints
for resource_name, endpoints in self.api_database.get("endpoints", {}).items():
for endpoint in endpoints:
if (query_lower in endpoint.get("name", "").lower() or
query_lower in endpoint.get("description", "").lower() or
query_lower in endpoint.get("path", "").lower()):
results.append({
"type": "endpoint",
"resource": resource_name,
"method": endpoint.get("method"),
"path": endpoint.get("path"),
"name": endpoint.get("name"),
"description": endpoint.get("description")
})
# Buscar en notas importantes
for note_key, note_data in self.api_database.get("important_notes", {}).items():
if (query_lower in note_key.lower() or
query_lower in str(note_data).lower()):
results.append({
"type": "note",
"key": note_key,
"data": note_data
})
if not results:
return f"No se encontraron resultados para: {query}"
return json.dumps(results, indent=2, ensure_ascii=False)
@self.server.call_tool()
def get_code_example(resource: str, path: str, method: str = "GET", language: str = "python") -> str:
"""
Obtener ejemplo de código para un endpoint
Args:
resource: Recurso ('products' u 'orders')
path: Ruta del endpoint
method: Método HTTP
language: Lenguaje de programación ('python' o 'javascript')
Returns:
Ejemplo de código en el lenguaje especificado
"""
endpoints = self.api_database.get("endpoints", {}).get(resource, [])
for endpoint in endpoints:
if endpoint.get("path") == path and endpoint.get("method") == method.upper():
examples = endpoint.get("code_examples", {})
if language in examples:
return examples[language]
else:
available = list(examples.keys())
return f"Ejemplo no disponible en {language}. Disponibles: {', '.join(available)}"
return f"Endpoint no encontrado: {method} {path}"
@self.server.call_tool()
def get_authentication_info() -> str:
"""
Obtener información sobre autenticación en la API
Returns:
Detalles de autenticación y scopes disponibles
"""
auth_info = self.api_database.get("important_notes", {}).get("authentication", {})
return json.dumps(auth_info, indent=2, ensure_ascii=False)
@self.server.call_tool()
def get_multi_inventory_info() -> str:
"""
Obtener información sobre la nueva API de Productos con multi-inventario
Returns:
Detalles sobre cambios y migración a multi-inventario
"""
multi_inv = self.api_database.get("important_notes", {}).get("multi_inventory", {})
return json.dumps(multi_inv, indent=2, ensure_ascii=False)
@self.server.call_tool()
def list_resources() -> str:
"""
Listar todos los recursos disponibles en la API
Returns:
Lista de recursos y cantidad de endpoints
"""
resources = {}
for resource_name, endpoints in self.api_database.get("endpoints", {}).items():
resources[resource_name] = len(endpoints)
return json.dumps({
"resources": resources,
"total_endpoints": sum(resources.values())
}, indent=2, ensure_ascii=False)
def run(self):
"""Ejecutar el servidor MCP"""
self.server.run()
if __name__ == "__main__":
server = TiendaNubeAPIServer()
server.run()