@cocaxcode/api-testing-mcp 0.1.0 → 0.3.0
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.
- package/README.md +48 -4
- package/dist/index.js +505 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -62,10 +62,19 @@ claude mcp add api-testing -- npx -y @cocaxcode/api-testing-mcp
|
|
|
62
62
|
|
|
63
63
|
Execute an HTTP request with optional authentication and variable interpolation.
|
|
64
64
|
|
|
65
|
+
Relative URLs (starting with `/`) automatically use `BASE_URL` from the active environment — no need to write `{{BASE_URL}}` every time.
|
|
66
|
+
|
|
65
67
|
```
|
|
68
|
+
// Relative URL — auto-prepends BASE_URL from active environment
|
|
69
|
+
request({ method: "GET", url: "/api/users" })
|
|
70
|
+
|
|
71
|
+
// Equivalent to:
|
|
72
|
+
request({ method: "GET", url: "{{BASE_URL}}/api/users" })
|
|
73
|
+
|
|
74
|
+
// Full example with all options
|
|
66
75
|
request({
|
|
67
76
|
method: "GET",
|
|
68
|
-
url: "
|
|
77
|
+
url: "/api/users",
|
|
69
78
|
headers: { "Authorization": "Bearer {{TOKEN}}" },
|
|
70
79
|
query: { "page": "1" },
|
|
71
80
|
timeout: 5000
|
|
@@ -174,6 +183,39 @@ Switch the active environment. Active environment variables are used for `{{inte
|
|
|
174
183
|
env_switch({ name: "prod" })
|
|
175
184
|
```
|
|
176
185
|
|
|
186
|
+
### `api_import`
|
|
187
|
+
|
|
188
|
+
Import an OpenAPI/Swagger spec from a URL or local file. Endpoints and schemas are stored locally for browsing.
|
|
189
|
+
|
|
190
|
+
```
|
|
191
|
+
api_import({
|
|
192
|
+
name: "my-backend",
|
|
193
|
+
source: "http://localhost:3001/api-docs-json"
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
// Or from a local file:
|
|
197
|
+
api_import({ name: "my-backend", source: "./openapi.json" })
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### `api_endpoints`
|
|
201
|
+
|
|
202
|
+
List endpoints from an imported API. Filter by tag, method, or path.
|
|
203
|
+
|
|
204
|
+
```
|
|
205
|
+
api_endpoints({ name: "my-backend" })
|
|
206
|
+
api_endpoints({ name: "my-backend", tag: "users" })
|
|
207
|
+
api_endpoints({ name: "my-backend", method: "POST" })
|
|
208
|
+
api_endpoints({ name: "my-backend", path: "/blog" })
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### `api_endpoint_detail`
|
|
212
|
+
|
|
213
|
+
Get full details of an endpoint: parameters, request body schema, and responses. Useful to know what data to send.
|
|
214
|
+
|
|
215
|
+
```
|
|
216
|
+
api_endpoint_detail({ name: "my-backend", method: "POST", path: "/blog" })
|
|
217
|
+
```
|
|
218
|
+
|
|
177
219
|
## Storage
|
|
178
220
|
|
|
179
221
|
All data is stored locally as JSON files in `.api-testing/` (in your current working directory by default):
|
|
@@ -184,9 +226,11 @@ All data is stored locally as JSON files in `.api-testing/` (in your current wor
|
|
|
184
226
|
├── collections/
|
|
185
227
|
│ ├── get-users.json
|
|
186
228
|
│ └── create-post.json
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
229
|
+
├── environments/
|
|
230
|
+
│ ├── dev.json
|
|
231
|
+
│ └── prod.json
|
|
232
|
+
└── specs/
|
|
233
|
+
└── my-backend.json # Imported OpenAPI specs
|
|
190
234
|
```
|
|
191
235
|
|
|
192
236
|
You can version these files in git if you want to share collections and environments with your team.
|
package/dist/index.js
CHANGED
|
@@ -14,11 +14,13 @@ var Storage = class {
|
|
|
14
14
|
baseDir;
|
|
15
15
|
collectionsDir;
|
|
16
16
|
environmentsDir;
|
|
17
|
+
specsDir;
|
|
17
18
|
activeEnvFile;
|
|
18
19
|
constructor(baseDir) {
|
|
19
20
|
this.baseDir = baseDir ?? process.env.API_TESTING_DIR ?? join(process.cwd(), ".api-testing");
|
|
20
21
|
this.collectionsDir = join(this.baseDir, "collections");
|
|
21
22
|
this.environmentsDir = join(this.baseDir, "environments");
|
|
23
|
+
this.specsDir = join(this.baseDir, "specs");
|
|
22
24
|
this.activeEnvFile = join(this.baseDir, "active-env");
|
|
23
25
|
}
|
|
24
26
|
// ── Collections ──
|
|
@@ -119,6 +121,41 @@ var Storage = class {
|
|
|
119
121
|
const env = await this.getEnvironment(activeName);
|
|
120
122
|
return env?.variables ?? {};
|
|
121
123
|
}
|
|
124
|
+
// ── API Specs ──
|
|
125
|
+
async saveSpec(spec) {
|
|
126
|
+
await this.ensureDir("specs");
|
|
127
|
+
const filePath = join(this.specsDir, `${this.sanitizeName(spec.name)}.json`);
|
|
128
|
+
await this.writeJson(filePath, spec);
|
|
129
|
+
}
|
|
130
|
+
async getSpec(name) {
|
|
131
|
+
const filePath = join(this.specsDir, `${this.sanitizeName(name)}.json`);
|
|
132
|
+
return this.readJson(filePath);
|
|
133
|
+
}
|
|
134
|
+
async listSpecs() {
|
|
135
|
+
await this.ensureDir("specs");
|
|
136
|
+
const files = await this.listJsonFiles(this.specsDir);
|
|
137
|
+
const items = [];
|
|
138
|
+
for (const file of files) {
|
|
139
|
+
const spec = await this.readJson(join(this.specsDir, file));
|
|
140
|
+
if (!spec) continue;
|
|
141
|
+
items.push({
|
|
142
|
+
name: spec.name,
|
|
143
|
+
source: spec.source,
|
|
144
|
+
endpointCount: spec.endpoints.length,
|
|
145
|
+
version: spec.version
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
return items;
|
|
149
|
+
}
|
|
150
|
+
async deleteSpec(name) {
|
|
151
|
+
const filePath = join(this.specsDir, `${this.sanitizeName(name)}.json`);
|
|
152
|
+
try {
|
|
153
|
+
await unlink(filePath);
|
|
154
|
+
return true;
|
|
155
|
+
} catch {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
122
159
|
// ── Internal ──
|
|
123
160
|
async ensureDir(subdir) {
|
|
124
161
|
const dir = subdir ? join(this.baseDir, subdir) : this.baseDir;
|
|
@@ -302,10 +339,12 @@ var AuthSchema = {
|
|
|
302
339
|
function registerRequestTool(server, storage) {
|
|
303
340
|
server.tool(
|
|
304
341
|
"request",
|
|
305
|
-
"Ejecuta un HTTP request.
|
|
342
|
+
"Ejecuta un HTTP request. URLs relativas (/path) usan BASE_URL del entorno activo. Soporta {{variables}}.",
|
|
306
343
|
{
|
|
307
344
|
method: z.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]).describe("HTTP method"),
|
|
308
|
-
url: z.string().describe(
|
|
345
|
+
url: z.string().describe(
|
|
346
|
+
"URL del endpoint. Si empieza con / se antepone BASE_URL del entorno activo. Soporta {{variables}}."
|
|
347
|
+
),
|
|
309
348
|
headers: z.record(z.string()).optional().describe("Headers HTTP como key-value pairs"),
|
|
310
349
|
body: z.any().optional().describe("Body del request (JSON). Soporta {{variables}}"),
|
|
311
350
|
query: z.record(z.string()).optional().describe("Query parameters como key-value pairs"),
|
|
@@ -315,9 +354,14 @@ function registerRequestTool(server, storage) {
|
|
|
315
354
|
async (params) => {
|
|
316
355
|
try {
|
|
317
356
|
const variables = await storage.getActiveVariables();
|
|
357
|
+
let resolvedUrl = params.url;
|
|
358
|
+
if (resolvedUrl.startsWith("/") && variables.BASE_URL) {
|
|
359
|
+
const baseUrl = variables.BASE_URL.replace(/\/+$/, "");
|
|
360
|
+
resolvedUrl = `${baseUrl}${resolvedUrl}`;
|
|
361
|
+
}
|
|
318
362
|
const config = {
|
|
319
363
|
method: params.method,
|
|
320
|
-
url:
|
|
364
|
+
url: resolvedUrl,
|
|
321
365
|
headers: params.headers,
|
|
322
366
|
body: params.body,
|
|
323
367
|
query: params.query,
|
|
@@ -714,8 +758,464 @@ function registerEnvironmentTools(server, storage) {
|
|
|
714
758
|
);
|
|
715
759
|
}
|
|
716
760
|
|
|
761
|
+
// src/tools/api-spec.ts
|
|
762
|
+
import { z as z4 } from "zod";
|
|
763
|
+
|
|
764
|
+
// src/lib/openapi-parser.ts
|
|
765
|
+
var VALID_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"];
|
|
766
|
+
function resolveRef(ref, root) {
|
|
767
|
+
const parts = ref.replace(/^#\//, "").split("/");
|
|
768
|
+
let current = root;
|
|
769
|
+
for (const part of parts) {
|
|
770
|
+
if (current && typeof current === "object" && part in current) {
|
|
771
|
+
current = current[part];
|
|
772
|
+
} else {
|
|
773
|
+
return void 0;
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
return current;
|
|
777
|
+
}
|
|
778
|
+
function resolveSchema(schema, root, depth = 0) {
|
|
779
|
+
if (!schema || depth > 10) return schema;
|
|
780
|
+
if (schema.$ref) {
|
|
781
|
+
const resolved = resolveRef(schema.$ref, root);
|
|
782
|
+
if (resolved) {
|
|
783
|
+
return resolveSchema(resolved, root, depth + 1);
|
|
784
|
+
}
|
|
785
|
+
return { type: "object", description: `Unresolved: ${schema.$ref}` };
|
|
786
|
+
}
|
|
787
|
+
const result = { ...schema };
|
|
788
|
+
if (result.properties) {
|
|
789
|
+
const resolvedProps = {};
|
|
790
|
+
for (const [key, prop] of Object.entries(result.properties)) {
|
|
791
|
+
resolvedProps[key] = resolveSchema(prop, root, depth + 1) ?? prop;
|
|
792
|
+
}
|
|
793
|
+
result.properties = resolvedProps;
|
|
794
|
+
}
|
|
795
|
+
if (result.items) {
|
|
796
|
+
result.items = resolveSchema(result.items, root, depth + 1) ?? result.items;
|
|
797
|
+
}
|
|
798
|
+
return result;
|
|
799
|
+
}
|
|
800
|
+
function parseOpenApiSpec(doc, name, source) {
|
|
801
|
+
const info = doc.info;
|
|
802
|
+
const paths = doc.paths;
|
|
803
|
+
const components = doc.components;
|
|
804
|
+
const rawSchemas = components?.schemas ?? {};
|
|
805
|
+
const schemas = {};
|
|
806
|
+
for (const [schemaName, schema] of Object.entries(rawSchemas)) {
|
|
807
|
+
schemas[schemaName] = resolveSchema(schema, doc, 0) ?? schema;
|
|
808
|
+
}
|
|
809
|
+
const endpoints = [];
|
|
810
|
+
if (paths) {
|
|
811
|
+
for (const [path, pathItem] of Object.entries(paths)) {
|
|
812
|
+
for (const [method, operation] of Object.entries(pathItem)) {
|
|
813
|
+
const upperMethod = method.toUpperCase();
|
|
814
|
+
if (!VALID_METHODS.includes(upperMethod)) continue;
|
|
815
|
+
const op = operation;
|
|
816
|
+
const rawParams = op.parameters ?? [];
|
|
817
|
+
const parameters = rawParams.map((p) => ({
|
|
818
|
+
name: p.name,
|
|
819
|
+
in: p.in,
|
|
820
|
+
required: p.required,
|
|
821
|
+
description: p.description,
|
|
822
|
+
schema: resolveSchema(p.schema, doc)
|
|
823
|
+
}));
|
|
824
|
+
const rawBody = op.requestBody;
|
|
825
|
+
let requestBody = void 0;
|
|
826
|
+
if (rawBody) {
|
|
827
|
+
const bodyContent = rawBody.content;
|
|
828
|
+
const resolvedContent = {};
|
|
829
|
+
if (bodyContent) {
|
|
830
|
+
for (const [contentType, mediaType] of Object.entries(bodyContent)) {
|
|
831
|
+
resolvedContent[contentType] = {
|
|
832
|
+
schema: resolveSchema(mediaType.schema, doc)
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
requestBody = {
|
|
837
|
+
required: rawBody.required,
|
|
838
|
+
description: rawBody.description,
|
|
839
|
+
content: resolvedContent
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
const rawResponses = op.responses ?? {};
|
|
843
|
+
const responses = {};
|
|
844
|
+
for (const [statusCode, resp] of Object.entries(rawResponses)) {
|
|
845
|
+
const respContent = resp.content;
|
|
846
|
+
const resolvedRespContent = {};
|
|
847
|
+
if (respContent) {
|
|
848
|
+
for (const [contentType, mediaType] of Object.entries(respContent)) {
|
|
849
|
+
resolvedRespContent[contentType] = {
|
|
850
|
+
schema: resolveSchema(mediaType.schema, doc)
|
|
851
|
+
};
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
responses[statusCode] = {
|
|
855
|
+
description: resp.description,
|
|
856
|
+
content: respContent ? resolvedRespContent : void 0
|
|
857
|
+
};
|
|
858
|
+
}
|
|
859
|
+
endpoints.push({
|
|
860
|
+
method: upperMethod,
|
|
861
|
+
path,
|
|
862
|
+
summary: op.summary,
|
|
863
|
+
description: op.description,
|
|
864
|
+
tags: op.tags,
|
|
865
|
+
parameters,
|
|
866
|
+
requestBody,
|
|
867
|
+
responses
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
873
|
+
return {
|
|
874
|
+
name,
|
|
875
|
+
source,
|
|
876
|
+
version: info?.version,
|
|
877
|
+
endpoints,
|
|
878
|
+
schemas,
|
|
879
|
+
importedAt: now,
|
|
880
|
+
updatedAt: now
|
|
881
|
+
};
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
// src/tools/api-spec.ts
|
|
885
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
886
|
+
function registerApiSpecTools(server, storage) {
|
|
887
|
+
server.tool(
|
|
888
|
+
"api_import",
|
|
889
|
+
"Importa un spec OpenAPI/Swagger desde una URL o archivo local. Guarda los endpoints y schemas para consulta.",
|
|
890
|
+
{
|
|
891
|
+
name: z4.string().describe('Nombre para identificar este API (ej: "mi-backend", "cocaxcode-api")'),
|
|
892
|
+
source: z4.string().describe(
|
|
893
|
+
"URL del spec OpenAPI JSON (ej: http://localhost:3001/api-docs-json) o ruta a archivo local"
|
|
894
|
+
)
|
|
895
|
+
},
|
|
896
|
+
async (params) => {
|
|
897
|
+
try {
|
|
898
|
+
let rawDoc;
|
|
899
|
+
if (params.source.startsWith("http://") || params.source.startsWith("https://")) {
|
|
900
|
+
const controller = new AbortController();
|
|
901
|
+
const timeout = setTimeout(() => controller.abort(), 3e4);
|
|
902
|
+
try {
|
|
903
|
+
const response = await fetch(params.source, { signal: controller.signal });
|
|
904
|
+
if (!response.ok) {
|
|
905
|
+
return {
|
|
906
|
+
content: [
|
|
907
|
+
{
|
|
908
|
+
type: "text",
|
|
909
|
+
text: `Error: No se pudo descargar el spec. Status: ${response.status} ${response.statusText}`
|
|
910
|
+
}
|
|
911
|
+
],
|
|
912
|
+
isError: true
|
|
913
|
+
};
|
|
914
|
+
}
|
|
915
|
+
rawDoc = await response.json();
|
|
916
|
+
} finally {
|
|
917
|
+
clearTimeout(timeout);
|
|
918
|
+
}
|
|
919
|
+
} else {
|
|
920
|
+
try {
|
|
921
|
+
const content = await readFile2(params.source, "utf-8");
|
|
922
|
+
rawDoc = JSON.parse(content);
|
|
923
|
+
} catch {
|
|
924
|
+
return {
|
|
925
|
+
content: [
|
|
926
|
+
{
|
|
927
|
+
type: "text",
|
|
928
|
+
text: `Error: No se pudo leer el archivo '${params.source}'. Verifica que existe y es JSON v\xE1lido.`
|
|
929
|
+
}
|
|
930
|
+
],
|
|
931
|
+
isError: true
|
|
932
|
+
};
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
if (!rawDoc.openapi && !rawDoc.swagger) {
|
|
936
|
+
return {
|
|
937
|
+
content: [
|
|
938
|
+
{
|
|
939
|
+
type: "text",
|
|
940
|
+
text: 'Error: El documento no parece ser un spec OpenAPI/Swagger v\xE1lido. Falta la propiedad "openapi" o "swagger".'
|
|
941
|
+
}
|
|
942
|
+
],
|
|
943
|
+
isError: true
|
|
944
|
+
};
|
|
945
|
+
}
|
|
946
|
+
const spec = parseOpenApiSpec(rawDoc, params.name, params.source);
|
|
947
|
+
await storage.saveSpec(spec);
|
|
948
|
+
const tagCounts = {};
|
|
949
|
+
for (const ep of spec.endpoints) {
|
|
950
|
+
for (const tag of ep.tags ?? ["sin-tag"]) {
|
|
951
|
+
tagCounts[tag] = (tagCounts[tag] ?? 0) + 1;
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
const tagSummary = Object.entries(tagCounts).map(([tag, count]) => ` - ${tag}: ${count} endpoints`).join("\n");
|
|
955
|
+
const schemaCount = Object.keys(spec.schemas).length;
|
|
956
|
+
return {
|
|
957
|
+
content: [
|
|
958
|
+
{
|
|
959
|
+
type: "text",
|
|
960
|
+
text: [
|
|
961
|
+
`API '${params.name}' importada correctamente.`,
|
|
962
|
+
"",
|
|
963
|
+
`Versi\xF3n: ${spec.version ?? "no especificada"}`,
|
|
964
|
+
`Endpoints: ${spec.endpoints.length}`,
|
|
965
|
+
`Schemas: ${schemaCount}`,
|
|
966
|
+
"",
|
|
967
|
+
"Endpoints por tag:",
|
|
968
|
+
tagSummary,
|
|
969
|
+
"",
|
|
970
|
+
"Usa api_endpoints para ver los endpoints disponibles.",
|
|
971
|
+
"Usa api_endpoint_detail para ver el detalle de un endpoint espec\xEDfico."
|
|
972
|
+
].join("\n")
|
|
973
|
+
}
|
|
974
|
+
]
|
|
975
|
+
};
|
|
976
|
+
} catch (error) {
|
|
977
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
978
|
+
return {
|
|
979
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
980
|
+
isError: true
|
|
981
|
+
};
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
);
|
|
985
|
+
server.tool(
|
|
986
|
+
"api_endpoints",
|
|
987
|
+
"Lista los endpoints de un API importada. Filtra por tag, m\xE9todo o path.",
|
|
988
|
+
{
|
|
989
|
+
name: z4.string().describe("Nombre del API importada"),
|
|
990
|
+
tag: z4.string().optional().describe('Filtrar por tag (ej: "blog", "auth", "users")'),
|
|
991
|
+
method: z4.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]).optional().describe("Filtrar por m\xE9todo HTTP"),
|
|
992
|
+
path: z4.string().optional().describe('Filtrar por path (b\xFAsqueda parcial, ej: "/blog" muestra todos los que contienen /blog)')
|
|
993
|
+
},
|
|
994
|
+
async (params) => {
|
|
995
|
+
try {
|
|
996
|
+
const spec = await storage.getSpec(params.name);
|
|
997
|
+
if (!spec) {
|
|
998
|
+
return {
|
|
999
|
+
content: [
|
|
1000
|
+
{
|
|
1001
|
+
type: "text",
|
|
1002
|
+
text: `Error: API '${params.name}' no encontrada. Usa api_import para importarla primero.`
|
|
1003
|
+
}
|
|
1004
|
+
],
|
|
1005
|
+
isError: true
|
|
1006
|
+
};
|
|
1007
|
+
}
|
|
1008
|
+
let endpoints = spec.endpoints;
|
|
1009
|
+
if (params.tag) {
|
|
1010
|
+
endpoints = endpoints.filter(
|
|
1011
|
+
(ep) => (ep.tags ?? []).some((t) => t.toLowerCase() === params.tag.toLowerCase())
|
|
1012
|
+
);
|
|
1013
|
+
}
|
|
1014
|
+
if (params.method) {
|
|
1015
|
+
endpoints = endpoints.filter((ep) => ep.method === params.method);
|
|
1016
|
+
}
|
|
1017
|
+
if (params.path) {
|
|
1018
|
+
const search = params.path.toLowerCase();
|
|
1019
|
+
endpoints = endpoints.filter((ep) => ep.path.toLowerCase().includes(search));
|
|
1020
|
+
}
|
|
1021
|
+
if (endpoints.length === 0) {
|
|
1022
|
+
return {
|
|
1023
|
+
content: [
|
|
1024
|
+
{
|
|
1025
|
+
type: "text",
|
|
1026
|
+
text: "No se encontraron endpoints con los filtros aplicados."
|
|
1027
|
+
}
|
|
1028
|
+
]
|
|
1029
|
+
};
|
|
1030
|
+
}
|
|
1031
|
+
const lines = endpoints.map((ep) => {
|
|
1032
|
+
const tags = ep.tags?.length ? ` [${ep.tags.join(", ")}]` : "";
|
|
1033
|
+
const summary = ep.summary ? ` \u2014 ${ep.summary}` : "";
|
|
1034
|
+
return `${ep.method.padEnd(7)} ${ep.path}${summary}${tags}`;
|
|
1035
|
+
});
|
|
1036
|
+
return {
|
|
1037
|
+
content: [
|
|
1038
|
+
{
|
|
1039
|
+
type: "text",
|
|
1040
|
+
text: [
|
|
1041
|
+
`API: ${spec.name} (${endpoints.length} endpoints)`,
|
|
1042
|
+
"",
|
|
1043
|
+
...lines,
|
|
1044
|
+
"",
|
|
1045
|
+
"Usa api_endpoint_detail para ver par\xE1metros, body y respuestas de un endpoint."
|
|
1046
|
+
].join("\n")
|
|
1047
|
+
}
|
|
1048
|
+
]
|
|
1049
|
+
};
|
|
1050
|
+
} catch (error) {
|
|
1051
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1052
|
+
return {
|
|
1053
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
1054
|
+
isError: true
|
|
1055
|
+
};
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
);
|
|
1059
|
+
server.tool(
|
|
1060
|
+
"api_endpoint_detail",
|
|
1061
|
+
"Muestra el detalle completo de un endpoint: par\xE1metros, body schema, y respuestas. \xDAtil para saber qu\xE9 datos enviar.",
|
|
1062
|
+
{
|
|
1063
|
+
name: z4.string().describe("Nombre del API importada"),
|
|
1064
|
+
method: z4.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]).describe("M\xE9todo HTTP del endpoint"),
|
|
1065
|
+
path: z4.string().describe('Path exacto del endpoint (ej: "/blog", "/auth/login")')
|
|
1066
|
+
},
|
|
1067
|
+
async (params) => {
|
|
1068
|
+
try {
|
|
1069
|
+
const spec = await storage.getSpec(params.name);
|
|
1070
|
+
if (!spec) {
|
|
1071
|
+
return {
|
|
1072
|
+
content: [
|
|
1073
|
+
{
|
|
1074
|
+
type: "text",
|
|
1075
|
+
text: `Error: API '${params.name}' no encontrada. Usa api_import para importarla primero.`
|
|
1076
|
+
}
|
|
1077
|
+
],
|
|
1078
|
+
isError: true
|
|
1079
|
+
};
|
|
1080
|
+
}
|
|
1081
|
+
const endpoint = spec.endpoints.find(
|
|
1082
|
+
(ep) => ep.method === params.method && ep.path === params.path
|
|
1083
|
+
);
|
|
1084
|
+
if (!endpoint) {
|
|
1085
|
+
const similar = spec.endpoints.filter(
|
|
1086
|
+
(ep) => ep.path.includes(params.path) || params.path.includes(ep.path)
|
|
1087
|
+
);
|
|
1088
|
+
const suggestion = similar.length > 0 ? `
|
|
1089
|
+
|
|
1090
|
+
Endpoints similares:
|
|
1091
|
+
${similar.map((ep) => ` ${ep.method} ${ep.path}`).join("\n")}` : "";
|
|
1092
|
+
return {
|
|
1093
|
+
content: [
|
|
1094
|
+
{
|
|
1095
|
+
type: "text",
|
|
1096
|
+
text: `Error: Endpoint ${params.method} ${params.path} no encontrado.${suggestion}`
|
|
1097
|
+
}
|
|
1098
|
+
],
|
|
1099
|
+
isError: true
|
|
1100
|
+
};
|
|
1101
|
+
}
|
|
1102
|
+
const sections = [];
|
|
1103
|
+
sections.push(`## ${endpoint.method} ${endpoint.path}`);
|
|
1104
|
+
if (endpoint.summary) sections.push(`**${endpoint.summary}**`);
|
|
1105
|
+
if (endpoint.description) sections.push(endpoint.description);
|
|
1106
|
+
if (endpoint.tags?.length) sections.push(`Tags: ${endpoint.tags.join(", ")}`);
|
|
1107
|
+
if (endpoint.parameters?.length) {
|
|
1108
|
+
sections.push("");
|
|
1109
|
+
sections.push("### Par\xE1metros");
|
|
1110
|
+
for (const param of endpoint.parameters) {
|
|
1111
|
+
const required = param.required ? " (requerido)" : " (opcional)";
|
|
1112
|
+
const type = param.schema?.type ?? "string";
|
|
1113
|
+
const desc = param.description ? ` \u2014 ${param.description}` : "";
|
|
1114
|
+
sections.push(`- **${param.name}** [${param.in}] ${type}${required}${desc}`);
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
if (endpoint.requestBody) {
|
|
1118
|
+
sections.push("");
|
|
1119
|
+
sections.push("### Body");
|
|
1120
|
+
const required = endpoint.requestBody.required ? " (requerido)" : " (opcional)";
|
|
1121
|
+
sections.push(`Body${required}`);
|
|
1122
|
+
if (endpoint.requestBody.content) {
|
|
1123
|
+
for (const [contentType, media] of Object.entries(endpoint.requestBody.content)) {
|
|
1124
|
+
sections.push(`
|
|
1125
|
+
Content-Type: ${contentType}`);
|
|
1126
|
+
if (media.schema) {
|
|
1127
|
+
sections.push("```json");
|
|
1128
|
+
sections.push(formatSchema(media.schema));
|
|
1129
|
+
sections.push("```");
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
if (endpoint.responses) {
|
|
1135
|
+
sections.push("");
|
|
1136
|
+
sections.push("### Respuestas");
|
|
1137
|
+
for (const [status, resp] of Object.entries(endpoint.responses)) {
|
|
1138
|
+
const desc = resp.description ? ` \u2014 ${resp.description}` : "";
|
|
1139
|
+
sections.push(`
|
|
1140
|
+
**${status}**${desc}`);
|
|
1141
|
+
if (resp.content) {
|
|
1142
|
+
for (const [, media] of Object.entries(resp.content)) {
|
|
1143
|
+
if (media.schema) {
|
|
1144
|
+
sections.push("```json");
|
|
1145
|
+
sections.push(formatSchema(media.schema));
|
|
1146
|
+
sections.push("```");
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
return {
|
|
1153
|
+
content: [
|
|
1154
|
+
{
|
|
1155
|
+
type: "text",
|
|
1156
|
+
text: sections.join("\n")
|
|
1157
|
+
}
|
|
1158
|
+
]
|
|
1159
|
+
};
|
|
1160
|
+
} catch (error) {
|
|
1161
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1162
|
+
return {
|
|
1163
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
1164
|
+
isError: true
|
|
1165
|
+
};
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
);
|
|
1169
|
+
}
|
|
1170
|
+
function formatSchema(schema, depth = 0) {
|
|
1171
|
+
if (depth > 5) return '"..."';
|
|
1172
|
+
const indent = " ".repeat(depth);
|
|
1173
|
+
const innerIndent = " ".repeat(depth + 1);
|
|
1174
|
+
if (schema.example !== void 0) {
|
|
1175
|
+
return JSON.stringify(schema.example, null, 2).split("\n").map((line, i) => i === 0 ? line : indent + line).join("\n");
|
|
1176
|
+
}
|
|
1177
|
+
if (schema.enum) {
|
|
1178
|
+
return JSON.stringify(schema.enum[0]);
|
|
1179
|
+
}
|
|
1180
|
+
if (schema.type === "object" && schema.properties) {
|
|
1181
|
+
const props = Object.entries(schema.properties);
|
|
1182
|
+
if (props.length === 0) return "{}";
|
|
1183
|
+
const requiredFields = new Set(schema.required ?? []);
|
|
1184
|
+
const lines = ["{"];
|
|
1185
|
+
for (const [key, prop] of props) {
|
|
1186
|
+
const isRequired = requiredFields.has(key);
|
|
1187
|
+
const comment = [];
|
|
1188
|
+
if (prop.description) comment.push(prop.description);
|
|
1189
|
+
if (!isRequired) comment.push("opcional");
|
|
1190
|
+
const commentStr = comment.length > 0 ? ` // ${comment.join(" \u2014 ")}` : "";
|
|
1191
|
+
const value = formatSchema(prop, depth + 1);
|
|
1192
|
+
lines.push(`${innerIndent}"${key}": ${value},${commentStr}`);
|
|
1193
|
+
}
|
|
1194
|
+
lines.push(`${indent}}`);
|
|
1195
|
+
return lines.join("\n");
|
|
1196
|
+
}
|
|
1197
|
+
if (schema.type === "array" && schema.items) {
|
|
1198
|
+
const itemValue = formatSchema(schema.items, depth + 1);
|
|
1199
|
+
return `[${itemValue}]`;
|
|
1200
|
+
}
|
|
1201
|
+
switch (schema.type) {
|
|
1202
|
+
case "string":
|
|
1203
|
+
if (schema.format === "date-time") return '"2024-01-01T00:00:00Z"';
|
|
1204
|
+
if (schema.format === "email") return '"user@example.com"';
|
|
1205
|
+
if (schema.format === "uri" || schema.format === "url") return '"https://example.com"';
|
|
1206
|
+
return '"string"';
|
|
1207
|
+
case "number":
|
|
1208
|
+
case "integer":
|
|
1209
|
+
return "0";
|
|
1210
|
+
case "boolean":
|
|
1211
|
+
return "true";
|
|
1212
|
+
default:
|
|
1213
|
+
return "null";
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
|
|
717
1217
|
// src/server.ts
|
|
718
|
-
var VERSION = "0.
|
|
1218
|
+
var VERSION = "0.3.0";
|
|
719
1219
|
function createServer(storageDir) {
|
|
720
1220
|
const server = new McpServer({
|
|
721
1221
|
name: "api-testing-mcp",
|
|
@@ -725,6 +1225,7 @@ function createServer(storageDir) {
|
|
|
725
1225
|
registerRequestTool(server, storage);
|
|
726
1226
|
registerCollectionTools(server, storage);
|
|
727
1227
|
registerEnvironmentTools(server, storage);
|
|
1228
|
+
registerApiSpecTools(server, storage);
|
|
728
1229
|
return server;
|
|
729
1230
|
}
|
|
730
1231
|
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/server.ts","../src/lib/storage.ts","../src/tools/request.ts","../src/lib/http-client.ts","../src/lib/interpolation.ts","../src/tools/collection.ts","../src/tools/environment.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { createServer } from './server.js'\n\nasync function main() {\n const server = createServer()\n const transport = new StdioServerTransport()\n await server.connect(transport)\n console.error('api-testing-mcp server running on stdio')\n}\n\nmain().catch((error) => {\n console.error('Fatal:', error)\n process.exit(1)\n})\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { Storage } from './lib/storage.js'\nimport { registerRequestTool } from './tools/request.js'\nimport { registerCollectionTools } from './tools/collection.js'\nimport { registerEnvironmentTools } from './tools/environment.js'\n\n// Leer version del package.json en build time no es posible con ESM fácilmente,\n// así que la definimos como constante sincronizada manualmente.\nconst VERSION = '0.1.0'\n\n/**\n * Crea y configura el MCP server con todos los tools registrados.\n * Exportada como factory para testabilidad con InMemoryTransport.\n */\nexport function createServer(storageDir?: string): McpServer {\n const server = new McpServer({\n name: 'api-testing-mcp',\n version: VERSION,\n })\n\n const storage = new Storage(storageDir)\n\n // Registrar tools\n registerRequestTool(server, storage)\n registerCollectionTools(server, storage)\n registerEnvironmentTools(server, storage)\n\n return server\n}\n","import { mkdir, readFile, writeFile, readdir, unlink } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport type {\n SavedRequest,\n CollectionListItem,\n Environment,\n EnvironmentListItem,\n} from './types.js'\n\nexport class Storage {\n private readonly baseDir: string\n private readonly collectionsDir: string\n private readonly environmentsDir: string\n private readonly activeEnvFile: string\n\n constructor(baseDir?: string) {\n this.baseDir = baseDir ?? process.env.API_TESTING_DIR ?? join(process.cwd(), '.api-testing')\n this.collectionsDir = join(this.baseDir, 'collections')\n this.environmentsDir = join(this.baseDir, 'environments')\n this.activeEnvFile = join(this.baseDir, 'active-env')\n }\n\n // ── Collections ──\n\n async saveCollection(saved: SavedRequest): Promise<void> {\n await this.ensureDir('collections')\n const filePath = join(this.collectionsDir, `${this.sanitizeName(saved.name)}.json`)\n await this.writeJson(filePath, saved)\n }\n\n async getCollection(name: string): Promise<SavedRequest | null> {\n const filePath = join(this.collectionsDir, `${this.sanitizeName(name)}.json`)\n return this.readJson<SavedRequest>(filePath)\n }\n\n async listCollections(tag?: string): Promise<CollectionListItem[]> {\n await this.ensureDir('collections')\n const files = await this.listJsonFiles(this.collectionsDir)\n const items: CollectionListItem[] = []\n\n for (const file of files) {\n const saved = await this.readJson<SavedRequest>(join(this.collectionsDir, file))\n if (!saved) continue\n\n if (tag && !(saved.tags ?? []).includes(tag)) continue\n\n items.push({\n name: saved.name,\n method: saved.request.method,\n url: saved.request.url,\n tags: saved.tags ?? [],\n })\n }\n\n return items\n }\n\n async deleteCollection(name: string): Promise<boolean> {\n const filePath = join(this.collectionsDir, `${this.sanitizeName(name)}.json`)\n try {\n await unlink(filePath)\n return true\n } catch {\n return false\n }\n }\n\n // ── Environments ──\n\n async createEnvironment(env: Environment): Promise<void> {\n await this.ensureDir('environments')\n const filePath = join(this.environmentsDir, `${this.sanitizeName(env.name)}.json`)\n await this.writeJson(filePath, env)\n }\n\n async getEnvironment(name: string): Promise<Environment | null> {\n const filePath = join(this.environmentsDir, `${this.sanitizeName(name)}.json`)\n return this.readJson<Environment>(filePath)\n }\n\n async listEnvironments(): Promise<EnvironmentListItem[]> {\n await this.ensureDir('environments')\n const files = await this.listJsonFiles(this.environmentsDir)\n const activeEnv = await this.getActiveEnvironment()\n const items: EnvironmentListItem[] = []\n\n for (const file of files) {\n const env = await this.readJson<Environment>(join(this.environmentsDir, file))\n if (!env) continue\n\n items.push({\n name: env.name,\n active: env.name === activeEnv,\n variableCount: Object.keys(env.variables).length,\n })\n }\n\n return items\n }\n\n async updateEnvironment(name: string, variables: Record<string, string>): Promise<void> {\n const env = await this.getEnvironment(name)\n if (!env) {\n throw new Error(`Entorno '${name}' no encontrado`)\n }\n\n env.variables = { ...env.variables, ...variables }\n env.updatedAt = new Date().toISOString()\n\n const filePath = join(this.environmentsDir, `${this.sanitizeName(name)}.json`)\n await this.writeJson(filePath, env)\n }\n\n async getActiveEnvironment(): Promise<string | null> {\n try {\n const content = await readFile(this.activeEnvFile, 'utf-8')\n return content.trim() || null\n } catch {\n return null\n }\n }\n\n async setActiveEnvironment(name: string): Promise<void> {\n // Verificar que el entorno existe\n const env = await this.getEnvironment(name)\n if (!env) {\n throw new Error(`Entorno '${name}' no encontrado`)\n }\n\n await this.ensureDir('')\n await writeFile(this.activeEnvFile, name, 'utf-8')\n }\n\n /**\n * Carga las variables del entorno activo.\n * Retorna objeto vacío si no hay entorno activo.\n */\n async getActiveVariables(): Promise<Record<string, string>> {\n const activeName = await this.getActiveEnvironment()\n if (!activeName) return {}\n\n const env = await this.getEnvironment(activeName)\n return env?.variables ?? {}\n }\n\n // ── Internal ──\n\n private async ensureDir(subdir: string): Promise<void> {\n const dir = subdir ? join(this.baseDir, subdir) : this.baseDir\n await mkdir(dir, { recursive: true })\n }\n\n private async readJson<T>(filePath: string): Promise<T | null> {\n try {\n const content = await readFile(filePath, 'utf-8')\n return JSON.parse(content) as T\n } catch {\n return null\n }\n }\n\n private async writeJson(filePath: string, data: unknown): Promise<void> {\n await writeFile(filePath, JSON.stringify(data, null, 2), 'utf-8')\n }\n\n private async listJsonFiles(dir: string): Promise<string[]> {\n try {\n const entries = await readdir(dir)\n return entries.filter((f) => f.endsWith('.json')).sort()\n } catch {\n return []\n }\n }\n\n /**\n * Sanitiza un nombre para usarlo como nombre de archivo.\n * Reemplaza caracteres no alfanuméricos por guiones.\n */\n private sanitizeName(name: string): string {\n return name\n .toLowerCase()\n .replace(/[^a-z0-9_-]/g, '-')\n .replace(/-+/g, '-')\n .replace(/^-|-$/g, '')\n }\n}\n","import { z } from 'zod'\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport type { Storage } from '../lib/storage.js'\nimport { executeRequest } from '../lib/http-client.js'\nimport { interpolateRequest } from '../lib/interpolation.js'\nimport type { RequestConfig } from '../lib/types.js'\n\nconst AuthSchema = {\n type: z.enum(['bearer', 'api-key', 'basic']).describe('Tipo de autenticación'),\n token: z.string().optional().describe('Token para Bearer auth'),\n key: z.string().optional().describe('API key value'),\n header: z.string().optional().describe('Header name para API key (default: X-API-Key)'),\n username: z.string().optional().describe('Username para Basic auth'),\n password: z.string().optional().describe('Password para Basic auth'),\n}\n\nexport function registerRequestTool(server: McpServer, storage: Storage): void {\n server.tool(\n 'request',\n 'Ejecuta un HTTP request. Soporta {{variables}} del entorno activo para URL, headers y body.',\n {\n method: z\n .enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'])\n .describe('HTTP method'),\n url: z.string().describe('URL del endpoint. Soporta {{variables}} de entorno'),\n headers: z\n .record(z.string())\n .optional()\n .describe('Headers HTTP como key-value pairs'),\n body: z.any().optional().describe('Body del request (JSON). Soporta {{variables}}'),\n query: z\n .record(z.string())\n .optional()\n .describe('Query parameters como key-value pairs'),\n timeout: z.number().optional().describe('Timeout en milisegundos (default: 30000)'),\n auth: z\n .object(AuthSchema)\n .optional()\n .describe('Configuración de autenticación'),\n },\n async (params) => {\n try {\n // Cargar variables del entorno activo\n const variables = await storage.getActiveVariables()\n\n // Construir RequestConfig\n const config: RequestConfig = {\n method: params.method,\n url: params.url,\n headers: params.headers,\n body: params.body,\n query: params.query,\n timeout: params.timeout,\n auth: params.auth,\n }\n\n // Interpolar variables\n const interpolated = interpolateRequest(config, variables)\n\n // Ejecutar request\n const response = await executeRequest(interpolated)\n\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify(response, null, 2),\n },\n ],\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n }\n }\n },\n )\n}\n","import type { RequestConfig, RequestResponse, AuthConfig } from './types.js'\n\nconst DEFAULT_TIMEOUT = 30_000\n\n/**\n * Aplica la configuración de auth a los headers del request.\n */\nfunction applyAuth(\n headers: Record<string, string>,\n auth: AuthConfig,\n): Record<string, string> {\n const result = { ...headers }\n\n switch (auth.type) {\n case 'bearer':\n if (auth.token) {\n result['Authorization'] = `Bearer ${auth.token}`\n }\n break\n\n case 'api-key':\n if (auth.key) {\n const headerName = auth.header ?? 'X-API-Key'\n result[headerName] = auth.key\n }\n break\n\n case 'basic':\n if (auth.username && auth.password) {\n const credentials = Buffer.from(`${auth.username}:${auth.password}`).toString('base64')\n result['Authorization'] = `Basic ${credentials}`\n }\n break\n }\n\n return result\n}\n\n/**\n * Construye la URL final con query parameters.\n */\nfunction buildUrl(baseUrl: string, query?: Record<string, string>): string {\n const url = new URL(baseUrl)\n\n if (query) {\n for (const [key, value] of Object.entries(query)) {\n url.searchParams.set(key, value)\n }\n }\n\n return url.toString()\n}\n\n/**\n * Ejecuta un HTTP request y retorna la respuesta con métricas de timing.\n */\nexport async function executeRequest(config: RequestConfig): Promise<RequestResponse> {\n const timeout = config.timeout ?? DEFAULT_TIMEOUT\n\n // Construir URL con query params\n const url = buildUrl(config.url, config.query)\n\n // Preparar headers\n let headers: Record<string, string> = { ...config.headers }\n\n // Aplicar auth\n if (config.auth) {\n headers = applyAuth(headers, config.auth)\n }\n\n // Preparar body\n let body: string | undefined\n if (config.body !== undefined && config.body !== null) {\n if (typeof config.body === 'string') {\n body = config.body\n } else {\n body = JSON.stringify(config.body)\n // Solo añadir Content-Type si no está definido\n if (!headers['Content-Type'] && !headers['content-type']) {\n headers['Content-Type'] = 'application/json'\n }\n }\n }\n\n // AbortController para timeout\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), timeout)\n\n // Medir timing\n const startTime = performance.now()\n\n try {\n const response = await fetch(url, {\n method: config.method,\n headers,\n body,\n signal: controller.signal,\n })\n\n const endTime = performance.now()\n const totalMs = Math.round((endTime - startTime) * 100) / 100\n\n // Parsear response body\n const responseText = await response.text()\n let responseBody: unknown\n try {\n responseBody = JSON.parse(responseText)\n } catch {\n responseBody = responseText\n }\n\n // Convertir headers a Record\n const responseHeaders: Record<string, string> = {}\n response.headers.forEach((value, key) => {\n responseHeaders[key] = value\n })\n\n // Calcular tamaño\n const sizeBytes =\n Number(response.headers.get('content-length')) ||\n Buffer.byteLength(responseText, 'utf-8')\n\n return {\n status: response.status,\n statusText: response.statusText,\n headers: responseHeaders,\n body: responseBody,\n timing: { total_ms: totalMs },\n size_bytes: sizeBytes,\n }\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(`Request timeout: superado el límite de ${timeout}ms`)\n }\n throw error\n } finally {\n clearTimeout(timeoutId)\n }\n}\n","import type { RequestConfig } from './types.js'\n\nconst VARIABLE_PATTERN = /\\{\\{(\\w+)\\}\\}/g\n\n/**\n * Resuelve todas las ocurrencias de {{variable}} en un string.\n * Las variables no encontradas se dejan intactas.\n */\nexport function interpolateString(\n template: string,\n variables: Record<string, string>,\n): string {\n return template.replace(VARIABLE_PATTERN, (match, varName: string) => {\n return varName in variables ? variables[varName] : match\n })\n}\n\n/**\n * Interpola {{variables}} recursivamente en un valor.\n * - string → interpolateString\n * - object → interpola cada valor (recursivo)\n * - array → interpola cada elemento\n * - otros tipos → retorna sin cambios\n */\nfunction interpolateValue(value: unknown, variables: Record<string, string>): unknown {\n if (typeof value === 'string') {\n return interpolateString(value, variables)\n }\n\n if (Array.isArray(value)) {\n return value.map((item) => interpolateValue(item, variables))\n }\n\n if (value !== null && typeof value === 'object') {\n const result: Record<string, unknown> = {}\n for (const [key, val] of Object.entries(value)) {\n result[key] = interpolateValue(val, variables)\n }\n return result\n }\n\n return value\n}\n\n/**\n * Interpola un Record<string, string> (headers, query params).\n * Solo interpola los valores, no las keys.\n */\nfunction interpolateRecord(\n record: Record<string, string> | undefined,\n variables: Record<string, string>,\n): Record<string, string> | undefined {\n if (!record) return undefined\n\n const result: Record<string, string> = {}\n for (const [key, value] of Object.entries(record)) {\n result[key] = interpolateString(value, variables)\n }\n return result\n}\n\n/**\n * Resuelve {{variables}} en todos los campos de un RequestConfig:\n * url, headers (valores), body (recursivo), query params (valores).\n */\nexport function interpolateRequest(\n config: RequestConfig,\n variables: Record<string, string>,\n): RequestConfig {\n return {\n ...config,\n url: interpolateString(config.url, variables),\n headers: interpolateRecord(config.headers, variables),\n query: interpolateRecord(config.query, variables),\n body: config.body !== undefined ? interpolateValue(config.body, variables) : undefined,\n }\n}\n","import { z } from 'zod'\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport type { Storage } from '../lib/storage.js'\nimport type { SavedRequest } from '../lib/types.js'\n\nconst AuthSchema = {\n type: z.enum(['bearer', 'api-key', 'basic']).describe('Tipo de autenticación'),\n token: z.string().optional().describe('Token para Bearer auth'),\n key: z.string().optional().describe('API key value'),\n header: z.string().optional().describe('Header name para API key (default: X-API-Key)'),\n username: z.string().optional().describe('Username para Basic auth'),\n password: z.string().optional().describe('Password para Basic auth'),\n}\n\nexport function registerCollectionTools(server: McpServer, storage: Storage): void {\n // ── collection_save ──\n server.tool(\n 'collection_save',\n 'Guarda un request en la colección local. Si ya existe un request con el mismo nombre, lo sobreescribe.',\n {\n name: z.string().describe('Nombre único del request guardado'),\n request: z\n .object({\n method: z.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS']),\n url: z.string(),\n headers: z.record(z.string()).optional(),\n body: z.any().optional(),\n query: z.record(z.string()).optional(),\n auth: z.object(AuthSchema).optional(),\n })\n .describe('Configuración del request a guardar'),\n tags: z\n .array(z.string())\n .optional()\n .describe('Tags para organizar (ej: [\"auth\", \"users\"])'),\n },\n async (params) => {\n try {\n const now = new Date().toISOString()\n const existing = await storage.getCollection(params.name)\n\n const saved: SavedRequest = {\n name: params.name,\n request: params.request,\n tags: params.tags,\n createdAt: existing?.createdAt ?? now,\n updatedAt: now,\n }\n\n await storage.saveCollection(saved)\n\n return {\n content: [\n {\n type: 'text' as const,\n text: `Request '${params.name}' guardado (${params.request.method} ${params.request.url})`,\n },\n ],\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n }\n }\n },\n )\n\n // ── collection_list ──\n server.tool(\n 'collection_list',\n 'Lista todos los requests guardados en la colección. Opcionalmente filtra por tag.',\n {\n tag: z.string().optional().describe('Filtrar por tag'),\n },\n async (params) => {\n try {\n const items = await storage.listCollections(params.tag)\n\n if (items.length === 0) {\n const msg = params.tag\n ? `No hay requests con tag '${params.tag}'`\n : 'La colección está vacía'\n return { content: [{ type: 'text' as const, text: msg }] }\n }\n\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify(items, null, 2),\n },\n ],\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n }\n }\n },\n )\n\n // ── collection_get ──\n server.tool(\n 'collection_get',\n 'Obtiene los detalles completos de un request guardado por su nombre.',\n {\n name: z.string().describe('Nombre del request guardado'),\n },\n async (params) => {\n try {\n const saved = await storage.getCollection(params.name)\n\n if (!saved) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Request '${params.name}' no encontrado`,\n },\n ],\n isError: true,\n }\n }\n\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify(saved, null, 2),\n },\n ],\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n }\n }\n },\n )\n\n // ── collection_delete ──\n server.tool(\n 'collection_delete',\n 'Elimina un request guardado de la colección.',\n {\n name: z.string().describe('Nombre del request a eliminar'),\n },\n async (params) => {\n try {\n const deleted = await storage.deleteCollection(params.name)\n\n if (!deleted) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Request '${params.name}' no encontrado`,\n },\n ],\n isError: true,\n }\n }\n\n return {\n content: [\n {\n type: 'text' as const,\n text: `Request '${params.name}' eliminado`,\n },\n ],\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n }\n }\n },\n )\n}\n","import { z } from 'zod'\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport type { Storage } from '../lib/storage.js'\nimport type { Environment } from '../lib/types.js'\n\nexport function registerEnvironmentTools(server: McpServer, storage: Storage): void {\n // ── env_create ──\n server.tool(\n 'env_create',\n 'Crea un nuevo entorno (ej: dev, staging, prod) con variables opcionales.',\n {\n name: z.string().describe('Nombre del entorno (ej: dev, staging, prod)'),\n variables: z\n .record(z.string())\n .optional()\n .describe('Variables iniciales como key-value'),\n },\n async (params) => {\n try {\n const now = new Date().toISOString()\n const env: Environment = {\n name: params.name,\n variables: params.variables ?? {},\n createdAt: now,\n updatedAt: now,\n }\n\n await storage.createEnvironment(env)\n\n const varCount = Object.keys(env.variables).length\n return {\n content: [\n {\n type: 'text' as const,\n text: `Entorno '${params.name}' creado con ${varCount} variable(s)`,\n },\n ],\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n }\n }\n },\n )\n\n // ── env_list ──\n server.tool(\n 'env_list',\n 'Lista todos los entornos disponibles e indica cuál está activo.',\n {},\n async () => {\n try {\n const items = await storage.listEnvironments()\n\n if (items.length === 0) {\n return {\n content: [{ type: 'text' as const, text: 'No hay entornos configurados' }],\n }\n }\n\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify(items, null, 2),\n },\n ],\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n }\n }\n },\n )\n\n // ── env_set ──\n server.tool(\n 'env_set',\n 'Establece una variable en un entorno. Si no se especifica entorno, usa el activo.',\n {\n key: z.string().describe('Nombre de la variable'),\n value: z.string().describe('Valor de la variable'),\n environment: z\n .string()\n .optional()\n .describe('Entorno destino (default: entorno activo)'),\n },\n async (params) => {\n try {\n // Determinar entorno destino\n const envName = params.environment ?? (await storage.getActiveEnvironment())\n\n if (!envName) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'No hay entorno activo. Usa env_create para crear uno y env_switch para activarlo.',\n },\n ],\n isError: true,\n }\n }\n\n await storage.updateEnvironment(envName, { [params.key]: params.value })\n\n return {\n content: [\n {\n type: 'text' as const,\n text: `Variable '${params.key}' establecida en entorno '${envName}'`,\n },\n ],\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n }\n }\n },\n )\n\n // ── env_get ──\n server.tool(\n 'env_get',\n 'Obtiene una variable específica o todas las variables de un entorno.',\n {\n key: z\n .string()\n .optional()\n .describe('Variable específica. Si se omite, retorna todas'),\n environment: z\n .string()\n .optional()\n .describe('Entorno a consultar (default: entorno activo)'),\n },\n async (params) => {\n try {\n const envName = params.environment ?? (await storage.getActiveEnvironment())\n\n if (!envName) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'No hay entorno activo. Usa env_switch para activar uno.',\n },\n ],\n isError: true,\n }\n }\n\n const env = await storage.getEnvironment(envName)\n if (!env) {\n return {\n content: [\n { type: 'text' as const, text: `Entorno '${envName}' no encontrado` },\n ],\n isError: true,\n }\n }\n\n if (params.key) {\n const value = env.variables[params.key]\n if (value === undefined) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Variable '${params.key}' no encontrada en entorno '${envName}'`,\n },\n ],\n isError: true,\n }\n }\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify({ key: params.key, value, environment: envName }, null, 2),\n },\n ],\n }\n }\n\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify(\n { environment: envName, variables: env.variables },\n null,\n 2,\n ),\n },\n ],\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n }\n }\n },\n )\n\n // ── env_switch ──\n server.tool(\n 'env_switch',\n 'Cambia el entorno activo. Las variables del entorno activo se usan en {{interpolación}}.',\n {\n name: z.string().describe('Nombre del entorno a activar'),\n },\n async (params) => {\n try {\n await storage.setActiveEnvironment(params.name)\n\n return {\n content: [\n {\n type: 'text' as const,\n text: `Entorno activo cambiado a '${params.name}'`,\n },\n ],\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n }\n }\n },\n )\n}\n"],"mappings":"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","names":["z","AuthSchema","z"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/server.ts","../src/lib/storage.ts","../src/tools/request.ts","../src/lib/http-client.ts","../src/lib/interpolation.ts","../src/tools/collection.ts","../src/tools/environment.ts","../src/tools/api-spec.ts","../src/lib/openapi-parser.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { createServer } from './server.js'\n\nasync function main() {\n const server = createServer()\n const transport = new StdioServerTransport()\n await server.connect(transport)\n console.error('api-testing-mcp server running on stdio')\n}\n\nmain().catch((error) => {\n console.error('Fatal:', error)\n process.exit(1)\n})\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { Storage } from './lib/storage.js'\nimport { registerRequestTool } from './tools/request.js'\nimport { registerCollectionTools } from './tools/collection.js'\nimport { registerEnvironmentTools } from './tools/environment.js'\nimport { registerApiSpecTools } from './tools/api-spec.js'\n\n// Leer version del package.json en build time no es posible con ESM fácilmente,\n// así que la definimos como constante sincronizada manualmente.\nconst VERSION = '0.3.0'\n\n/**\n * Crea y configura el MCP server con todos los tools registrados.\n * Exportada como factory para testabilidad con InMemoryTransport.\n */\nexport function createServer(storageDir?: string): McpServer {\n const server = new McpServer({\n name: 'api-testing-mcp',\n version: VERSION,\n })\n\n const storage = new Storage(storageDir)\n\n // Registrar tools\n registerRequestTool(server, storage)\n registerCollectionTools(server, storage)\n registerEnvironmentTools(server, storage)\n registerApiSpecTools(server, storage)\n\n return server\n}\n","import { mkdir, readFile, writeFile, readdir, unlink } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport type {\n SavedRequest,\n CollectionListItem,\n Environment,\n EnvironmentListItem,\n ApiSpec,\n ApiSpecListItem,\n} from './types.js'\n\nexport class Storage {\n private readonly baseDir: string\n private readonly collectionsDir: string\n private readonly environmentsDir: string\n private readonly specsDir: string\n private readonly activeEnvFile: string\n\n constructor(baseDir?: string) {\n this.baseDir = baseDir ?? process.env.API_TESTING_DIR ?? join(process.cwd(), '.api-testing')\n this.collectionsDir = join(this.baseDir, 'collections')\n this.environmentsDir = join(this.baseDir, 'environments')\n this.specsDir = join(this.baseDir, 'specs')\n this.activeEnvFile = join(this.baseDir, 'active-env')\n }\n\n // ── Collections ──\n\n async saveCollection(saved: SavedRequest): Promise<void> {\n await this.ensureDir('collections')\n const filePath = join(this.collectionsDir, `${this.sanitizeName(saved.name)}.json`)\n await this.writeJson(filePath, saved)\n }\n\n async getCollection(name: string): Promise<SavedRequest | null> {\n const filePath = join(this.collectionsDir, `${this.sanitizeName(name)}.json`)\n return this.readJson<SavedRequest>(filePath)\n }\n\n async listCollections(tag?: string): Promise<CollectionListItem[]> {\n await this.ensureDir('collections')\n const files = await this.listJsonFiles(this.collectionsDir)\n const items: CollectionListItem[] = []\n\n for (const file of files) {\n const saved = await this.readJson<SavedRequest>(join(this.collectionsDir, file))\n if (!saved) continue\n\n if (tag && !(saved.tags ?? []).includes(tag)) continue\n\n items.push({\n name: saved.name,\n method: saved.request.method,\n url: saved.request.url,\n tags: saved.tags ?? [],\n })\n }\n\n return items\n }\n\n async deleteCollection(name: string): Promise<boolean> {\n const filePath = join(this.collectionsDir, `${this.sanitizeName(name)}.json`)\n try {\n await unlink(filePath)\n return true\n } catch {\n return false\n }\n }\n\n // ── Environments ──\n\n async createEnvironment(env: Environment): Promise<void> {\n await this.ensureDir('environments')\n const filePath = join(this.environmentsDir, `${this.sanitizeName(env.name)}.json`)\n await this.writeJson(filePath, env)\n }\n\n async getEnvironment(name: string): Promise<Environment | null> {\n const filePath = join(this.environmentsDir, `${this.sanitizeName(name)}.json`)\n return this.readJson<Environment>(filePath)\n }\n\n async listEnvironments(): Promise<EnvironmentListItem[]> {\n await this.ensureDir('environments')\n const files = await this.listJsonFiles(this.environmentsDir)\n const activeEnv = await this.getActiveEnvironment()\n const items: EnvironmentListItem[] = []\n\n for (const file of files) {\n const env = await this.readJson<Environment>(join(this.environmentsDir, file))\n if (!env) continue\n\n items.push({\n name: env.name,\n active: env.name === activeEnv,\n variableCount: Object.keys(env.variables).length,\n })\n }\n\n return items\n }\n\n async updateEnvironment(name: string, variables: Record<string, string>): Promise<void> {\n const env = await this.getEnvironment(name)\n if (!env) {\n throw new Error(`Entorno '${name}' no encontrado`)\n }\n\n env.variables = { ...env.variables, ...variables }\n env.updatedAt = new Date().toISOString()\n\n const filePath = join(this.environmentsDir, `${this.sanitizeName(name)}.json`)\n await this.writeJson(filePath, env)\n }\n\n async getActiveEnvironment(): Promise<string | null> {\n try {\n const content = await readFile(this.activeEnvFile, 'utf-8')\n return content.trim() || null\n } catch {\n return null\n }\n }\n\n async setActiveEnvironment(name: string): Promise<void> {\n // Verificar que el entorno existe\n const env = await this.getEnvironment(name)\n if (!env) {\n throw new Error(`Entorno '${name}' no encontrado`)\n }\n\n await this.ensureDir('')\n await writeFile(this.activeEnvFile, name, 'utf-8')\n }\n\n /**\n * Carga las variables del entorno activo.\n * Retorna objeto vacío si no hay entorno activo.\n */\n async getActiveVariables(): Promise<Record<string, string>> {\n const activeName = await this.getActiveEnvironment()\n if (!activeName) return {}\n\n const env = await this.getEnvironment(activeName)\n return env?.variables ?? {}\n }\n\n // ── API Specs ──\n\n async saveSpec(spec: ApiSpec): Promise<void> {\n await this.ensureDir('specs')\n const filePath = join(this.specsDir, `${this.sanitizeName(spec.name)}.json`)\n await this.writeJson(filePath, spec)\n }\n\n async getSpec(name: string): Promise<ApiSpec | null> {\n const filePath = join(this.specsDir, `${this.sanitizeName(name)}.json`)\n return this.readJson<ApiSpec>(filePath)\n }\n\n async listSpecs(): Promise<ApiSpecListItem[]> {\n await this.ensureDir('specs')\n const files = await this.listJsonFiles(this.specsDir)\n const items: ApiSpecListItem[] = []\n\n for (const file of files) {\n const spec = await this.readJson<ApiSpec>(join(this.specsDir, file))\n if (!spec) continue\n\n items.push({\n name: spec.name,\n source: spec.source,\n endpointCount: spec.endpoints.length,\n version: spec.version,\n })\n }\n\n return items\n }\n\n async deleteSpec(name: string): Promise<boolean> {\n const filePath = join(this.specsDir, `${this.sanitizeName(name)}.json`)\n try {\n await unlink(filePath)\n return true\n } catch {\n return false\n }\n }\n\n // ── Internal ──\n\n private async ensureDir(subdir: string): Promise<void> {\n const dir = subdir ? join(this.baseDir, subdir) : this.baseDir\n await mkdir(dir, { recursive: true })\n }\n\n private async readJson<T>(filePath: string): Promise<T | null> {\n try {\n const content = await readFile(filePath, 'utf-8')\n return JSON.parse(content) as T\n } catch {\n return null\n }\n }\n\n private async writeJson(filePath: string, data: unknown): Promise<void> {\n await writeFile(filePath, JSON.stringify(data, null, 2), 'utf-8')\n }\n\n private async listJsonFiles(dir: string): Promise<string[]> {\n try {\n const entries = await readdir(dir)\n return entries.filter((f) => f.endsWith('.json')).sort()\n } catch {\n return []\n }\n }\n\n /**\n * Sanitiza un nombre para usarlo como nombre de archivo.\n * Reemplaza caracteres no alfanuméricos por guiones.\n */\n private sanitizeName(name: string): string {\n return name\n .toLowerCase()\n .replace(/[^a-z0-9_-]/g, '-')\n .replace(/-+/g, '-')\n .replace(/^-|-$/g, '')\n }\n}\n","import { z } from 'zod'\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport type { Storage } from '../lib/storage.js'\nimport { executeRequest } from '../lib/http-client.js'\nimport { interpolateRequest } from '../lib/interpolation.js'\nimport type { RequestConfig } from '../lib/types.js'\n\nconst AuthSchema = {\n type: z.enum(['bearer', 'api-key', 'basic']).describe('Tipo de autenticación'),\n token: z.string().optional().describe('Token para Bearer auth'),\n key: z.string().optional().describe('API key value'),\n header: z.string().optional().describe('Header name para API key (default: X-API-Key)'),\n username: z.string().optional().describe('Username para Basic auth'),\n password: z.string().optional().describe('Password para Basic auth'),\n}\n\nexport function registerRequestTool(server: McpServer, storage: Storage): void {\n server.tool(\n 'request',\n 'Ejecuta un HTTP request. URLs relativas (/path) usan BASE_URL del entorno activo. Soporta {{variables}}.',\n {\n method: z\n .enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'])\n .describe('HTTP method'),\n url: z\n .string()\n .describe(\n 'URL del endpoint. Si empieza con / se antepone BASE_URL del entorno activo. Soporta {{variables}}.',\n ),\n headers: z\n .record(z.string())\n .optional()\n .describe('Headers HTTP como key-value pairs'),\n body: z.any().optional().describe('Body del request (JSON). Soporta {{variables}}'),\n query: z\n .record(z.string())\n .optional()\n .describe('Query parameters como key-value pairs'),\n timeout: z.number().optional().describe('Timeout en milisegundos (default: 30000)'),\n auth: z\n .object(AuthSchema)\n .optional()\n .describe('Configuración de autenticación'),\n },\n async (params) => {\n try {\n // Cargar variables del entorno activo\n const variables = await storage.getActiveVariables()\n\n // Auto-prepend BASE_URL para URLs relativas (empiezan con /)\n let resolvedUrl = params.url\n if (resolvedUrl.startsWith('/') && variables.BASE_URL) {\n // Quitar trailing slash de BASE_URL para evitar doble slash\n const baseUrl = variables.BASE_URL.replace(/\\/+$/, '')\n resolvedUrl = `${baseUrl}${resolvedUrl}`\n }\n\n // Construir RequestConfig\n const config: RequestConfig = {\n method: params.method,\n url: resolvedUrl,\n headers: params.headers,\n body: params.body,\n query: params.query,\n timeout: params.timeout,\n auth: params.auth,\n }\n\n // Interpolar variables\n const interpolated = interpolateRequest(config, variables)\n\n // Ejecutar request\n const response = await executeRequest(interpolated)\n\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify(response, null, 2),\n },\n ],\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n }\n }\n },\n )\n}\n","import type { RequestConfig, RequestResponse, AuthConfig } from './types.js'\n\nconst DEFAULT_TIMEOUT = 30_000\n\n/**\n * Aplica la configuración de auth a los headers del request.\n */\nfunction applyAuth(\n headers: Record<string, string>,\n auth: AuthConfig,\n): Record<string, string> {\n const result = { ...headers }\n\n switch (auth.type) {\n case 'bearer':\n if (auth.token) {\n result['Authorization'] = `Bearer ${auth.token}`\n }\n break\n\n case 'api-key':\n if (auth.key) {\n const headerName = auth.header ?? 'X-API-Key'\n result[headerName] = auth.key\n }\n break\n\n case 'basic':\n if (auth.username && auth.password) {\n const credentials = Buffer.from(`${auth.username}:${auth.password}`).toString('base64')\n result['Authorization'] = `Basic ${credentials}`\n }\n break\n }\n\n return result\n}\n\n/**\n * Construye la URL final con query parameters.\n */\nfunction buildUrl(baseUrl: string, query?: Record<string, string>): string {\n const url = new URL(baseUrl)\n\n if (query) {\n for (const [key, value] of Object.entries(query)) {\n url.searchParams.set(key, value)\n }\n }\n\n return url.toString()\n}\n\n/**\n * Ejecuta un HTTP request y retorna la respuesta con métricas de timing.\n */\nexport async function executeRequest(config: RequestConfig): Promise<RequestResponse> {\n const timeout = config.timeout ?? DEFAULT_TIMEOUT\n\n // Construir URL con query params\n const url = buildUrl(config.url, config.query)\n\n // Preparar headers\n let headers: Record<string, string> = { ...config.headers }\n\n // Aplicar auth\n if (config.auth) {\n headers = applyAuth(headers, config.auth)\n }\n\n // Preparar body\n let body: string | undefined\n if (config.body !== undefined && config.body !== null) {\n if (typeof config.body === 'string') {\n body = config.body\n } else {\n body = JSON.stringify(config.body)\n // Solo añadir Content-Type si no está definido\n if (!headers['Content-Type'] && !headers['content-type']) {\n headers['Content-Type'] = 'application/json'\n }\n }\n }\n\n // AbortController para timeout\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), timeout)\n\n // Medir timing\n const startTime = performance.now()\n\n try {\n const response = await fetch(url, {\n method: config.method,\n headers,\n body,\n signal: controller.signal,\n })\n\n const endTime = performance.now()\n const totalMs = Math.round((endTime - startTime) * 100) / 100\n\n // Parsear response body\n const responseText = await response.text()\n let responseBody: unknown\n try {\n responseBody = JSON.parse(responseText)\n } catch {\n responseBody = responseText\n }\n\n // Convertir headers a Record\n const responseHeaders: Record<string, string> = {}\n response.headers.forEach((value, key) => {\n responseHeaders[key] = value\n })\n\n // Calcular tamaño\n const sizeBytes =\n Number(response.headers.get('content-length')) ||\n Buffer.byteLength(responseText, 'utf-8')\n\n return {\n status: response.status,\n statusText: response.statusText,\n headers: responseHeaders,\n body: responseBody,\n timing: { total_ms: totalMs },\n size_bytes: sizeBytes,\n }\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(`Request timeout: superado el límite de ${timeout}ms`)\n }\n throw error\n } finally {\n clearTimeout(timeoutId)\n }\n}\n","import type { RequestConfig } from './types.js'\n\nconst VARIABLE_PATTERN = /\\{\\{(\\w+)\\}\\}/g\n\n/**\n * Resuelve todas las ocurrencias de {{variable}} en un string.\n * Las variables no encontradas se dejan intactas.\n */\nexport function interpolateString(\n template: string,\n variables: Record<string, string>,\n): string {\n return template.replace(VARIABLE_PATTERN, (match, varName: string) => {\n return varName in variables ? variables[varName] : match\n })\n}\n\n/**\n * Interpola {{variables}} recursivamente en un valor.\n * - string → interpolateString\n * - object → interpola cada valor (recursivo)\n * - array → interpola cada elemento\n * - otros tipos → retorna sin cambios\n */\nfunction interpolateValue(value: unknown, variables: Record<string, string>): unknown {\n if (typeof value === 'string') {\n return interpolateString(value, variables)\n }\n\n if (Array.isArray(value)) {\n return value.map((item) => interpolateValue(item, variables))\n }\n\n if (value !== null && typeof value === 'object') {\n const result: Record<string, unknown> = {}\n for (const [key, val] of Object.entries(value)) {\n result[key] = interpolateValue(val, variables)\n }\n return result\n }\n\n return value\n}\n\n/**\n * Interpola un Record<string, string> (headers, query params).\n * Solo interpola los valores, no las keys.\n */\nfunction interpolateRecord(\n record: Record<string, string> | undefined,\n variables: Record<string, string>,\n): Record<string, string> | undefined {\n if (!record) return undefined\n\n const result: Record<string, string> = {}\n for (const [key, value] of Object.entries(record)) {\n result[key] = interpolateString(value, variables)\n }\n return result\n}\n\n/**\n * Resuelve {{variables}} en todos los campos de un RequestConfig:\n * url, headers (valores), body (recursivo), query params (valores).\n */\nexport function interpolateRequest(\n config: RequestConfig,\n variables: Record<string, string>,\n): RequestConfig {\n return {\n ...config,\n url: interpolateString(config.url, variables),\n headers: interpolateRecord(config.headers, variables),\n query: interpolateRecord(config.query, variables),\n body: config.body !== undefined ? interpolateValue(config.body, variables) : undefined,\n }\n}\n","import { z } from 'zod'\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport type { Storage } from '../lib/storage.js'\nimport type { SavedRequest } from '../lib/types.js'\n\nconst AuthSchema = {\n type: z.enum(['bearer', 'api-key', 'basic']).describe('Tipo de autenticación'),\n token: z.string().optional().describe('Token para Bearer auth'),\n key: z.string().optional().describe('API key value'),\n header: z.string().optional().describe('Header name para API key (default: X-API-Key)'),\n username: z.string().optional().describe('Username para Basic auth'),\n password: z.string().optional().describe('Password para Basic auth'),\n}\n\nexport function registerCollectionTools(server: McpServer, storage: Storage): void {\n // ── collection_save ──\n server.tool(\n 'collection_save',\n 'Guarda un request en la colección local. Si ya existe un request con el mismo nombre, lo sobreescribe.',\n {\n name: z.string().describe('Nombre único del request guardado'),\n request: z\n .object({\n method: z.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS']),\n url: z.string(),\n headers: z.record(z.string()).optional(),\n body: z.any().optional(),\n query: z.record(z.string()).optional(),\n auth: z.object(AuthSchema).optional(),\n })\n .describe('Configuración del request a guardar'),\n tags: z\n .array(z.string())\n .optional()\n .describe('Tags para organizar (ej: [\"auth\", \"users\"])'),\n },\n async (params) => {\n try {\n const now = new Date().toISOString()\n const existing = await storage.getCollection(params.name)\n\n const saved: SavedRequest = {\n name: params.name,\n request: params.request,\n tags: params.tags,\n createdAt: existing?.createdAt ?? now,\n updatedAt: now,\n }\n\n await storage.saveCollection(saved)\n\n return {\n content: [\n {\n type: 'text' as const,\n text: `Request '${params.name}' guardado (${params.request.method} ${params.request.url})`,\n },\n ],\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n }\n }\n },\n )\n\n // ── collection_list ──\n server.tool(\n 'collection_list',\n 'Lista todos los requests guardados en la colección. Opcionalmente filtra por tag.',\n {\n tag: z.string().optional().describe('Filtrar por tag'),\n },\n async (params) => {\n try {\n const items = await storage.listCollections(params.tag)\n\n if (items.length === 0) {\n const msg = params.tag\n ? `No hay requests con tag '${params.tag}'`\n : 'La colección está vacía'\n return { content: [{ type: 'text' as const, text: msg }] }\n }\n\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify(items, null, 2),\n },\n ],\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n }\n }\n },\n )\n\n // ── collection_get ──\n server.tool(\n 'collection_get',\n 'Obtiene los detalles completos de un request guardado por su nombre.',\n {\n name: z.string().describe('Nombre del request guardado'),\n },\n async (params) => {\n try {\n const saved = await storage.getCollection(params.name)\n\n if (!saved) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Request '${params.name}' no encontrado`,\n },\n ],\n isError: true,\n }\n }\n\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify(saved, null, 2),\n },\n ],\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n }\n }\n },\n )\n\n // ── collection_delete ──\n server.tool(\n 'collection_delete',\n 'Elimina un request guardado de la colección.',\n {\n name: z.string().describe('Nombre del request a eliminar'),\n },\n async (params) => {\n try {\n const deleted = await storage.deleteCollection(params.name)\n\n if (!deleted) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Request '${params.name}' no encontrado`,\n },\n ],\n isError: true,\n }\n }\n\n return {\n content: [\n {\n type: 'text' as const,\n text: `Request '${params.name}' eliminado`,\n },\n ],\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n }\n }\n },\n )\n}\n","import { z } from 'zod'\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport type { Storage } from '../lib/storage.js'\nimport type { Environment } from '../lib/types.js'\n\nexport function registerEnvironmentTools(server: McpServer, storage: Storage): void {\n // ── env_create ──\n server.tool(\n 'env_create',\n 'Crea un nuevo entorno (ej: dev, staging, prod) con variables opcionales.',\n {\n name: z.string().describe('Nombre del entorno (ej: dev, staging, prod)'),\n variables: z\n .record(z.string())\n .optional()\n .describe('Variables iniciales como key-value'),\n },\n async (params) => {\n try {\n const now = new Date().toISOString()\n const env: Environment = {\n name: params.name,\n variables: params.variables ?? {},\n createdAt: now,\n updatedAt: now,\n }\n\n await storage.createEnvironment(env)\n\n const varCount = Object.keys(env.variables).length\n return {\n content: [\n {\n type: 'text' as const,\n text: `Entorno '${params.name}' creado con ${varCount} variable(s)`,\n },\n ],\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n }\n }\n },\n )\n\n // ── env_list ──\n server.tool(\n 'env_list',\n 'Lista todos los entornos disponibles e indica cuál está activo.',\n {},\n async () => {\n try {\n const items = await storage.listEnvironments()\n\n if (items.length === 0) {\n return {\n content: [{ type: 'text' as const, text: 'No hay entornos configurados' }],\n }\n }\n\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify(items, null, 2),\n },\n ],\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n }\n }\n },\n )\n\n // ── env_set ──\n server.tool(\n 'env_set',\n 'Establece una variable en un entorno. Si no se especifica entorno, usa el activo.',\n {\n key: z.string().describe('Nombre de la variable'),\n value: z.string().describe('Valor de la variable'),\n environment: z\n .string()\n .optional()\n .describe('Entorno destino (default: entorno activo)'),\n },\n async (params) => {\n try {\n // Determinar entorno destino\n const envName = params.environment ?? (await storage.getActiveEnvironment())\n\n if (!envName) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'No hay entorno activo. Usa env_create para crear uno y env_switch para activarlo.',\n },\n ],\n isError: true,\n }\n }\n\n await storage.updateEnvironment(envName, { [params.key]: params.value })\n\n return {\n content: [\n {\n type: 'text' as const,\n text: `Variable '${params.key}' establecida en entorno '${envName}'`,\n },\n ],\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n }\n }\n },\n )\n\n // ── env_get ──\n server.tool(\n 'env_get',\n 'Obtiene una variable específica o todas las variables de un entorno.',\n {\n key: z\n .string()\n .optional()\n .describe('Variable específica. Si se omite, retorna todas'),\n environment: z\n .string()\n .optional()\n .describe('Entorno a consultar (default: entorno activo)'),\n },\n async (params) => {\n try {\n const envName = params.environment ?? (await storage.getActiveEnvironment())\n\n if (!envName) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'No hay entorno activo. Usa env_switch para activar uno.',\n },\n ],\n isError: true,\n }\n }\n\n const env = await storage.getEnvironment(envName)\n if (!env) {\n return {\n content: [\n { type: 'text' as const, text: `Entorno '${envName}' no encontrado` },\n ],\n isError: true,\n }\n }\n\n if (params.key) {\n const value = env.variables[params.key]\n if (value === undefined) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Variable '${params.key}' no encontrada en entorno '${envName}'`,\n },\n ],\n isError: true,\n }\n }\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify({ key: params.key, value, environment: envName }, null, 2),\n },\n ],\n }\n }\n\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify(\n { environment: envName, variables: env.variables },\n null,\n 2,\n ),\n },\n ],\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n }\n }\n },\n )\n\n // ── env_switch ──\n server.tool(\n 'env_switch',\n 'Cambia el entorno activo. Las variables del entorno activo se usan en {{interpolación}}.',\n {\n name: z.string().describe('Nombre del entorno a activar'),\n },\n async (params) => {\n try {\n await storage.setActiveEnvironment(params.name)\n\n return {\n content: [\n {\n type: 'text' as const,\n text: `Entorno activo cambiado a '${params.name}'`,\n },\n ],\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n }\n }\n },\n )\n}\n","import { z } from 'zod'\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport type { Storage } from '../lib/storage.js'\nimport { parseOpenApiSpec } from '../lib/openapi-parser.js'\nimport { readFile } from 'node:fs/promises'\n\nexport function registerApiSpecTools(server: McpServer, storage: Storage): void {\n // ── api_import ──\n\n server.tool(\n 'api_import',\n 'Importa un spec OpenAPI/Swagger desde una URL o archivo local. Guarda los endpoints y schemas para consulta.',\n {\n name: z\n .string()\n .describe('Nombre para identificar este API (ej: \"mi-backend\", \"cocaxcode-api\")'),\n source: z\n .string()\n .describe(\n 'URL del spec OpenAPI JSON (ej: http://localhost:3001/api-docs-json) o ruta a archivo local',\n ),\n },\n async (params) => {\n try {\n let rawDoc: Record<string, unknown>\n\n if (params.source.startsWith('http://') || params.source.startsWith('https://')) {\n // Fetch from URL\n const controller = new AbortController()\n const timeout = setTimeout(() => controller.abort(), 30000)\n\n try {\n const response = await fetch(params.source, { signal: controller.signal })\n if (!response.ok) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Error: No se pudo descargar el spec. Status: ${response.status} ${response.statusText}`,\n },\n ],\n isError: true,\n }\n }\n rawDoc = (await response.json()) as Record<string, unknown>\n } finally {\n clearTimeout(timeout)\n }\n } else {\n // Read from local file\n try {\n const content = await readFile(params.source, 'utf-8')\n rawDoc = JSON.parse(content) as Record<string, unknown>\n } catch {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Error: No se pudo leer el archivo '${params.source}'. Verifica que existe y es JSON válido.`,\n },\n ],\n isError: true,\n }\n }\n }\n\n // Validate it looks like OpenAPI\n if (!rawDoc.openapi && !rawDoc.swagger) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'Error: El documento no parece ser un spec OpenAPI/Swagger válido. Falta la propiedad \"openapi\" o \"swagger\".',\n },\n ],\n isError: true,\n }\n }\n\n // Parse and save\n const spec = parseOpenApiSpec(rawDoc, params.name, params.source)\n await storage.saveSpec(spec)\n\n // Build summary\n const tagCounts: Record<string, number> = {}\n for (const ep of spec.endpoints) {\n for (const tag of ep.tags ?? ['sin-tag']) {\n tagCounts[tag] = (tagCounts[tag] ?? 0) + 1\n }\n }\n\n const tagSummary = Object.entries(tagCounts)\n .map(([tag, count]) => ` - ${tag}: ${count} endpoints`)\n .join('\\n')\n\n const schemaCount = Object.keys(spec.schemas).length\n\n return {\n content: [\n {\n type: 'text' as const,\n text: [\n `API '${params.name}' importada correctamente.`,\n '',\n `Versión: ${spec.version ?? 'no especificada'}`,\n `Endpoints: ${spec.endpoints.length}`,\n `Schemas: ${schemaCount}`,\n '',\n 'Endpoints por tag:',\n tagSummary,\n '',\n 'Usa api_endpoints para ver los endpoints disponibles.',\n 'Usa api_endpoint_detail para ver el detalle de un endpoint específico.',\n ].join('\\n'),\n },\n ],\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n }\n }\n },\n )\n\n // ── api_endpoints ──\n\n server.tool(\n 'api_endpoints',\n 'Lista los endpoints de un API importada. Filtra por tag, método o path.',\n {\n name: z\n .string()\n .describe('Nombre del API importada'),\n tag: z\n .string()\n .optional()\n .describe('Filtrar por tag (ej: \"blog\", \"auth\", \"users\")'),\n method: z\n .enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'])\n .optional()\n .describe('Filtrar por método HTTP'),\n path: z\n .string()\n .optional()\n .describe('Filtrar por path (búsqueda parcial, ej: \"/blog\" muestra todos los que contienen /blog)'),\n },\n async (params) => {\n try {\n const spec = await storage.getSpec(params.name)\n if (!spec) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Error: API '${params.name}' no encontrada. Usa api_import para importarla primero.`,\n },\n ],\n isError: true,\n }\n }\n\n let endpoints = spec.endpoints\n\n // Apply filters\n if (params.tag) {\n endpoints = endpoints.filter((ep) =>\n (ep.tags ?? []).some((t) => t.toLowerCase() === params.tag!.toLowerCase()),\n )\n }\n if (params.method) {\n endpoints = endpoints.filter((ep) => ep.method === params.method)\n }\n if (params.path) {\n const search = params.path.toLowerCase()\n endpoints = endpoints.filter((ep) => ep.path.toLowerCase().includes(search))\n }\n\n if (endpoints.length === 0) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'No se encontraron endpoints con los filtros aplicados.',\n },\n ],\n }\n }\n\n // Format output\n const lines = endpoints.map((ep) => {\n const tags = ep.tags?.length ? ` [${ep.tags.join(', ')}]` : ''\n const summary = ep.summary ? ` — ${ep.summary}` : ''\n return `${ep.method.padEnd(7)} ${ep.path}${summary}${tags}`\n })\n\n return {\n content: [\n {\n type: 'text' as const,\n text: [\n `API: ${spec.name} (${endpoints.length} endpoints)`,\n '',\n ...lines,\n '',\n 'Usa api_endpoint_detail para ver parámetros, body y respuestas de un endpoint.',\n ].join('\\n'),\n },\n ],\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n }\n }\n },\n )\n\n // ── api_endpoint_detail ──\n\n server.tool(\n 'api_endpoint_detail',\n 'Muestra el detalle completo de un endpoint: parámetros, body schema, y respuestas. Útil para saber qué datos enviar.',\n {\n name: z\n .string()\n .describe('Nombre del API importada'),\n method: z\n .enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'])\n .describe('Método HTTP del endpoint'),\n path: z\n .string()\n .describe('Path exacto del endpoint (ej: \"/blog\", \"/auth/login\")'),\n },\n async (params) => {\n try {\n const spec = await storage.getSpec(params.name)\n if (!spec) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Error: API '${params.name}' no encontrada. Usa api_import para importarla primero.`,\n },\n ],\n isError: true,\n }\n }\n\n const endpoint = spec.endpoints.find(\n (ep) => ep.method === params.method && ep.path === params.path,\n )\n\n if (!endpoint) {\n // Try partial match\n const similar = spec.endpoints.filter((ep) =>\n ep.path.includes(params.path) || params.path.includes(ep.path),\n )\n\n const suggestion = similar.length > 0\n ? `\\n\\nEndpoints similares:\\n${similar.map((ep) => ` ${ep.method} ${ep.path}`).join('\\n')}`\n : ''\n\n return {\n content: [\n {\n type: 'text' as const,\n text: `Error: Endpoint ${params.method} ${params.path} no encontrado.${suggestion}`,\n },\n ],\n isError: true,\n }\n }\n\n // Build detailed output\n const sections: string[] = []\n\n // Header\n sections.push(`## ${endpoint.method} ${endpoint.path}`)\n if (endpoint.summary) sections.push(`**${endpoint.summary}**`)\n if (endpoint.description) sections.push(endpoint.description)\n if (endpoint.tags?.length) sections.push(`Tags: ${endpoint.tags.join(', ')}`)\n\n // Parameters\n if (endpoint.parameters?.length) {\n sections.push('')\n sections.push('### Parámetros')\n for (const param of endpoint.parameters) {\n const required = param.required ? ' (requerido)' : ' (opcional)'\n const type = param.schema?.type ?? 'string'\n const desc = param.description ? ` — ${param.description}` : ''\n sections.push(`- **${param.name}** [${param.in}] ${type}${required}${desc}`)\n }\n }\n\n // Request body\n if (endpoint.requestBody) {\n sections.push('')\n sections.push('### Body')\n const required = endpoint.requestBody.required ? ' (requerido)' : ' (opcional)'\n sections.push(`Body${required}`)\n\n if (endpoint.requestBody.content) {\n for (const [contentType, media] of Object.entries(endpoint.requestBody.content)) {\n sections.push(`\\nContent-Type: ${contentType}`)\n if (media.schema) {\n sections.push('```json')\n sections.push(formatSchema(media.schema))\n sections.push('```')\n }\n }\n }\n }\n\n // Responses\n if (endpoint.responses) {\n sections.push('')\n sections.push('### Respuestas')\n for (const [status, resp] of Object.entries(endpoint.responses)) {\n const desc = resp.description ? ` — ${resp.description}` : ''\n sections.push(`\\n**${status}**${desc}`)\n\n if (resp.content) {\n for (const [, media] of Object.entries(resp.content)) {\n if (media.schema) {\n sections.push('```json')\n sections.push(formatSchema(media.schema))\n sections.push('```')\n }\n }\n }\n }\n }\n\n return {\n content: [\n {\n type: 'text' as const,\n text: sections.join('\\n'),\n },\n ],\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n }\n }\n },\n )\n}\n\n/**\n * Formatea un schema como ejemplo JSON legible.\n * Genera un ejemplo basado en los tipos y propiedades del schema.\n */\nfunction formatSchema(schema: { type?: string; properties?: Record<string, unknown>; items?: unknown; required?: string[]; enum?: unknown[]; example?: unknown; format?: string; description?: string }, depth = 0): string {\n if (depth > 5) return '\"...\"'\n\n const indent = ' '.repeat(depth)\n const innerIndent = ' '.repeat(depth + 1)\n\n if (schema.example !== undefined) {\n return JSON.stringify(schema.example, null, 2)\n .split('\\n')\n .map((line, i) => (i === 0 ? line : indent + line))\n .join('\\n')\n }\n\n if (schema.enum) {\n return JSON.stringify(schema.enum[0])\n }\n\n if (schema.type === 'object' && schema.properties) {\n const props = Object.entries(schema.properties as Record<string, Record<string, unknown>>)\n if (props.length === 0) return '{}'\n\n const requiredFields = new Set(schema.required ?? [])\n const lines: string[] = ['{']\n\n for (const [key, prop] of props) {\n const isRequired = requiredFields.has(key)\n const comment = []\n if (prop.description) comment.push(prop.description as string)\n if (!isRequired) comment.push('opcional')\n const commentStr = comment.length > 0 ? ` // ${comment.join(' — ')}` : ''\n\n const value = formatSchema(prop as typeof schema, depth + 1)\n lines.push(`${innerIndent}\"${key}\": ${value},${commentStr}`)\n }\n\n lines.push(`${indent}}`)\n return lines.join('\\n')\n }\n\n if (schema.type === 'array' && schema.items) {\n const itemValue = formatSchema(schema.items as typeof schema, depth + 1)\n return `[${itemValue}]`\n }\n\n // Primitive types\n switch (schema.type) {\n case 'string':\n if (schema.format === 'date-time') return '\"2024-01-01T00:00:00Z\"'\n if (schema.format === 'email') return '\"user@example.com\"'\n if (schema.format === 'uri' || schema.format === 'url') return '\"https://example.com\"'\n return '\"string\"'\n case 'number':\n case 'integer':\n return '0'\n case 'boolean':\n return 'true'\n default:\n return 'null'\n }\n}\n","import type {\n HttpMethod,\n ApiSpecEndpoint,\n ApiSpecSchema,\n ApiSpec,\n} from './types.js'\n\nconst VALID_METHODS: HttpMethod[] = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS']\n\n/**\n * Resuelve $ref references en un schema OpenAPI.\n * Soporta refs tipo \"#/components/schemas/MyModel\".\n */\nfunction resolveRef(\n ref: string,\n root: Record<string, unknown>,\n): ApiSpecSchema | undefined {\n const parts = ref.replace(/^#\\//, '').split('/')\n let current: unknown = root\n\n for (const part of parts) {\n if (current && typeof current === 'object' && part in current) {\n current = (current as Record<string, unknown>)[part]\n } else {\n return undefined\n }\n }\n\n return current as ApiSpecSchema\n}\n\n/**\n * Resuelve recursivamente todos los $ref en un schema.\n */\nfunction resolveSchema(\n schema: ApiSpecSchema | undefined,\n root: Record<string, unknown>,\n depth = 0,\n): ApiSpecSchema | undefined {\n if (!schema || depth > 10) return schema\n\n if (schema.$ref) {\n const resolved = resolveRef(schema.$ref, root)\n if (resolved) {\n return resolveSchema(resolved, root, depth + 1)\n }\n return { type: 'object', description: `Unresolved: ${schema.$ref}` }\n }\n\n const result: ApiSpecSchema = { ...schema }\n\n // Resolve properties recursively\n if (result.properties) {\n const resolvedProps: Record<string, ApiSpecSchema> = {}\n for (const [key, prop] of Object.entries(result.properties)) {\n resolvedProps[key] = resolveSchema(prop, root, depth + 1) ?? prop\n }\n result.properties = resolvedProps\n }\n\n // Resolve array items\n if (result.items) {\n result.items = resolveSchema(result.items, root, depth + 1) ?? result.items\n }\n\n return result\n}\n\n/**\n * Parsea un documento OpenAPI 3.x y extrae endpoints y schemas.\n */\nexport function parseOpenApiSpec(\n doc: Record<string, unknown>,\n name: string,\n source: string,\n): ApiSpec {\n const info = doc.info as Record<string, unknown> | undefined\n const paths = doc.paths as Record<string, Record<string, unknown>> | undefined\n const components = doc.components as Record<string, unknown> | undefined\n const rawSchemas = (components?.schemas ?? {}) as Record<string, ApiSpecSchema>\n\n // Resolve all schemas\n const schemas: Record<string, ApiSpecSchema> = {}\n for (const [schemaName, schema] of Object.entries(rawSchemas)) {\n schemas[schemaName] = resolveSchema(schema, doc, 0) ?? schema\n }\n\n const endpoints: ApiSpecEndpoint[] = []\n\n if (paths) {\n for (const [path, pathItem] of Object.entries(paths)) {\n for (const [method, operation] of Object.entries(pathItem)) {\n const upperMethod = method.toUpperCase() as HttpMethod\n if (!VALID_METHODS.includes(upperMethod)) continue\n\n const op = operation as Record<string, unknown>\n\n // Parse parameters\n const rawParams = (op.parameters ?? []) as Array<Record<string, unknown>>\n const parameters = rawParams.map((p) => ({\n name: p.name as string,\n in: p.in as 'path' | 'query' | 'header' | 'cookie',\n required: p.required as boolean | undefined,\n description: p.description as string | undefined,\n schema: resolveSchema(p.schema as ApiSpecSchema | undefined, doc),\n }))\n\n // Parse request body\n const rawBody = op.requestBody as Record<string, unknown> | undefined\n let requestBody = undefined\n if (rawBody) {\n const bodyContent = rawBody.content as Record<string, Record<string, unknown>> | undefined\n const resolvedContent: Record<string, { schema?: ApiSpecSchema }> = {}\n\n if (bodyContent) {\n for (const [contentType, mediaType] of Object.entries(bodyContent)) {\n resolvedContent[contentType] = {\n schema: resolveSchema(mediaType.schema as ApiSpecSchema | undefined, doc),\n }\n }\n }\n\n requestBody = {\n required: rawBody.required as boolean | undefined,\n description: rawBody.description as string | undefined,\n content: resolvedContent,\n }\n }\n\n // Parse responses\n const rawResponses = (op.responses ?? {}) as Record<string, Record<string, unknown>>\n const responses: Record<string, { description?: string; content?: Record<string, { schema?: ApiSpecSchema }> }> = {}\n\n for (const [statusCode, resp] of Object.entries(rawResponses)) {\n const respContent = resp.content as Record<string, Record<string, unknown>> | undefined\n const resolvedRespContent: Record<string, { schema?: ApiSpecSchema }> = {}\n\n if (respContent) {\n for (const [contentType, mediaType] of Object.entries(respContent)) {\n resolvedRespContent[contentType] = {\n schema: resolveSchema(mediaType.schema as ApiSpecSchema | undefined, doc),\n }\n }\n }\n\n responses[statusCode] = {\n description: resp.description as string | undefined,\n content: respContent ? resolvedRespContent : undefined,\n }\n }\n\n endpoints.push({\n method: upperMethod,\n path,\n summary: op.summary as string | undefined,\n description: op.description as string | undefined,\n tags: op.tags as string[] | undefined,\n parameters,\n requestBody,\n responses,\n })\n }\n }\n }\n\n const now = new Date().toISOString()\n\n return {\n name,\n source,\n version: info?.version as string | undefined,\n endpoints,\n schemas,\n importedAt: now,\n updatedAt: now,\n }\n}\n"],"mappings":"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","names":["z","AuthSchema","z","z","readFile","z"]}
|