mcp-insomnia 0.1.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/dist/tools.js ADDED
@@ -0,0 +1,698 @@
1
+ import axios from 'axios';
2
+ import * as fs from 'fs';
3
+ import { v4 as uuidv4 } from 'uuid';
4
+ import { storage } from './storage.js';
5
+ function normalizeHeaders(headers) {
6
+ const normalized = {};
7
+ if (headers) {
8
+ for (const [key, value] of Object.entries(headers)) {
9
+ normalized[key] = String(value);
10
+ }
11
+ }
12
+ return normalized;
13
+ }
14
+ export function createInsomniaTools() {
15
+ return [
16
+ {
17
+ name: 'create_collection',
18
+ description: 'Create a new collection/workspace in Insomnia',
19
+ inputSchema: {
20
+ type: 'object',
21
+ properties: {
22
+ name: { type: 'string', description: 'Collection name' },
23
+ description: { type: 'string', description: 'Collection description' },
24
+ scope: { type: 'string', enum: ['collection', 'design'], default: 'collection' },
25
+ },
26
+ required: ['name'],
27
+ },
28
+ handler: async (request) => {
29
+ const params = request.params.arguments;
30
+ const workspaceId = `wrk_${uuidv4().replace(/-/g, '')}`;
31
+ const workspace = {
32
+ _id: workspaceId,
33
+ _type: 'workspace',
34
+ name: params.name,
35
+ description: params.description || '',
36
+ scope: params.scope || 'collection',
37
+ modified: Date.now(),
38
+ created: Date.now(),
39
+ };
40
+ const collectionStructure = {
41
+ workspace,
42
+ folders: [],
43
+ requests: [],
44
+ environments: [],
45
+ };
46
+ storage.saveCollection(workspaceId, collectionStructure);
47
+ return {
48
+ content: [
49
+ {
50
+ type: 'text',
51
+ text: `Collection "${params.name}" created successfully with ID: ${workspaceId}`,
52
+ },
53
+ ],
54
+ };
55
+ },
56
+ },
57
+ {
58
+ name: 'create_folder',
59
+ description: 'Create a folder to group requests within a collection',
60
+ inputSchema: {
61
+ type: 'object',
62
+ properties: {
63
+ collectionId: { type: 'string', description: 'ID collection' },
64
+ parentId: { type: 'string', description: 'ID parent folder (optional)' },
65
+ name: { type: 'string', description: 'Folder name' },
66
+ description: { type: 'string', description: 'Folder description' },
67
+ },
68
+ required: ['collectionId', 'name'],
69
+ },
70
+ handler: async (request) => {
71
+ const params = request.params.arguments;
72
+ const collection = storage.getCollection(params.collectionId);
73
+ if (!collection) {
74
+ throw new Error(`Collection with ID ${params.collectionId} not found`);
75
+ }
76
+ const folderId = `fld_${uuidv4().replace(/-/g, '')}`;
77
+ const folder = {
78
+ _id: folderId,
79
+ _type: 'request_group',
80
+ parentId: params.parentId || params.collectionId,
81
+ name: params.name,
82
+ description: params.description || '',
83
+ modified: Date.now(),
84
+ created: Date.now(),
85
+ };
86
+ collection.folders.push(folder);
87
+ storage.saveCollection(params.collectionId, collection);
88
+ return {
89
+ content: [
90
+ {
91
+ type: 'text',
92
+ text: `Folder "${params.name}" created successfully with ID: ${folderId}`,
93
+ },
94
+ ],
95
+ };
96
+ },
97
+ },
98
+ {
99
+ name: 'list_collections',
100
+ description: 'List all collections and their structure',
101
+ inputSchema: {
102
+ type: 'object',
103
+ properties: {},
104
+ },
105
+ handler: async () => {
106
+ const collections = storage.getAllCollections();
107
+ const result = Array.from(collections.entries()).map(([id, structure]) => ({
108
+ id,
109
+ name: structure.workspace.name,
110
+ description: structure.workspace.description,
111
+ scope: structure.workspace.scope,
112
+ folders: structure.folders.map(f => ({
113
+ id: f._id,
114
+ name: f.name,
115
+ description: f.description,
116
+ })),
117
+ requestCount: structure.requests.length,
118
+ environmentCount: structure.environments.length,
119
+ }));
120
+ return {
121
+ content: [
122
+ {
123
+ type: 'text',
124
+ text: JSON.stringify(result, null, 2),
125
+ },
126
+ ],
127
+ };
128
+ },
129
+ },
130
+ {
131
+ name: 'create_request_in_collection',
132
+ description: 'Create a new request within a specific collection/folder',
133
+ inputSchema: {
134
+ type: 'object',
135
+ properties: {
136
+ collectionId: { type: 'string', description: 'ID collection' },
137
+ folderId: { type: 'string', description: 'ID folder (optional)' },
138
+ name: { type: 'string', description: 'Request name' },
139
+ method: { type: 'string', enum: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'] },
140
+ url: { type: 'string', description: 'Endpoint URL' },
141
+ headers: {
142
+ type: 'array',
143
+ description: 'Request headers',
144
+ items: {
145
+ type: 'object',
146
+ properties: {
147
+ id: { type: 'string' },
148
+ name: { type: 'string' },
149
+ value: { type: 'string' },
150
+ description: { type: 'string' },
151
+ disabled: { type: 'boolean' }
152
+ },
153
+ required: ['name', 'value']
154
+ }
155
+ },
156
+ body: { type: 'object', description: 'Request body' },
157
+ parameters: {
158
+ type: 'array',
159
+ description: 'Query parameters',
160
+ items: {
161
+ type: 'object',
162
+ properties: {
163
+ id: { type: 'string' },
164
+ name: { type: 'string' },
165
+ value: { type: 'string' },
166
+ description: { type: 'string' },
167
+ disabled: { type: 'boolean' }
168
+ },
169
+ required: ['name', 'value']
170
+ }
171
+ },
172
+ authentication: { type: 'object', description: 'Authentication config' },
173
+ description: { type: 'string', description: 'Request description' },
174
+ },
175
+ required: ['collectionId', 'name', 'method', 'url'],
176
+ },
177
+ handler: async (request) => {
178
+ const params = request.params.arguments;
179
+ const collection = storage.getCollection(params.collectionId);
180
+ if (!collection) {
181
+ throw new Error(`Collection with ID ${params.collectionId} not found`);
182
+ }
183
+ const requestId = `req_${uuidv4().replace(/-/g, '')}`;
184
+ const newRequest = {
185
+ _id: requestId,
186
+ _type: 'request',
187
+ parentId: params.folderId || params.collectionId,
188
+ name: params.name,
189
+ description: params.description || '',
190
+ url: params.url,
191
+ method: params.method,
192
+ headers: params.headers || [],
193
+ parameters: params.parameters || [],
194
+ body: params.body,
195
+ authentication: params.authentication,
196
+ modified: Date.now(),
197
+ created: Date.now(),
198
+ };
199
+ collection.requests.push(newRequest);
200
+ storage.saveCollection(params.collectionId, collection);
201
+ return {
202
+ content: [
203
+ {
204
+ type: 'text',
205
+ text: `Request "${params.name}" created successfully with ID: ${requestId}`,
206
+ },
207
+ ],
208
+ };
209
+ },
210
+ },
211
+ {
212
+ name: 'execute_request',
213
+ description: 'Execute a request and get a response',
214
+ inputSchema: {
215
+ type: 'object',
216
+ properties: {
217
+ requestId: { type: 'string', description: 'ID of the request to execute' },
218
+ environmentVariables: { type: 'object', description: 'Environment variables for the request' },
219
+ },
220
+ required: ['requestId'],
221
+ },
222
+ handler: async (request) => {
223
+ const { requestId, environmentVariables = {} } = request.params.arguments;
224
+ let targetRequest = null;
225
+ let collectionId = null;
226
+ const collections = storage.getAllCollections();
227
+ for (const [cId, collection] of collections.entries()) {
228
+ const foundRequest = collection.requests.find(r => r._id === requestId);
229
+ if (foundRequest) {
230
+ targetRequest = foundRequest;
231
+ collectionId = cId;
232
+ break;
233
+ }
234
+ }
235
+ if (!targetRequest || !collectionId) {
236
+ throw new Error(`Request with ID ${requestId} not found`);
237
+ }
238
+ const startTime = Date.now();
239
+ try {
240
+ let processedUrl = targetRequest.url;
241
+ Object.entries(environmentVariables).forEach(([key, value]) => {
242
+ processedUrl = processedUrl.replace(new RegExp(`{{${key}}}`, 'g'), String(value));
243
+ });
244
+ const processedHeaders = {};
245
+ targetRequest.headers?.forEach(header => {
246
+ if (!header.disabled) {
247
+ let processedValue = header.value;
248
+ Object.entries(environmentVariables).forEach(([key, value]) => {
249
+ processedValue = processedValue.replace(new RegExp(`{{${key}}}`, 'g'), String(value));
250
+ });
251
+ processedHeaders[header.name] = processedValue;
252
+ }
253
+ });
254
+ if (targetRequest.authentication) {
255
+ const auth = targetRequest.authentication;
256
+ switch (auth.type) {
257
+ case 'bearer':
258
+ processedHeaders['Authorization'] = `Bearer ${auth.token}`;
259
+ break;
260
+ case 'basic':
261
+ const credentials = Buffer.from(`${auth.username}:${auth.password}`).toString('base64');
262
+ processedHeaders['Authorization'] = `Basic ${credentials}`;
263
+ break;
264
+ }
265
+ }
266
+ let processedBody = undefined;
267
+ if (targetRequest.body) {
268
+ if (targetRequest.body.text) {
269
+ processedBody = targetRequest.body.text;
270
+ Object.entries(environmentVariables).forEach(([key, value]) => {
271
+ processedBody = processedBody.replace(new RegExp(`{{${key}}}`, 'g'), String(value));
272
+ });
273
+ if (targetRequest.body.mimeType === 'application/json') {
274
+ try {
275
+ processedBody = JSON.parse(processedBody);
276
+ }
277
+ catch (e) {
278
+ }
279
+ }
280
+ }
281
+ }
282
+ const response = await axios({
283
+ method: targetRequest.method.toLowerCase(),
284
+ url: processedUrl,
285
+ headers: processedHeaders,
286
+ data: processedBody,
287
+ timeout: 30000,
288
+ });
289
+ const duration = Date.now() - startTime;
290
+ const result = {
291
+ status: response.status,
292
+ statusText: response.statusText,
293
+ headers: normalizeHeaders(response.headers),
294
+ data: response.data,
295
+ duration,
296
+ size: JSON.stringify(response.data).length,
297
+ timestamp: new Date().toISOString(),
298
+ };
299
+ const execution = {
300
+ _id: `ex_${uuidv4().replace(/-/g, '')}`,
301
+ parentId: requestId,
302
+ timestamp: Date.now(),
303
+ response: {
304
+ statusCode: response.status,
305
+ statusMessage: response.statusText,
306
+ headers: normalizeHeaders(response.headers),
307
+ body: JSON.stringify(response.data),
308
+ duration,
309
+ size: result.size,
310
+ },
311
+ };
312
+ storage.addExecution(collectionId, requestId, execution);
313
+ return {
314
+ content: [
315
+ {
316
+ type: 'text',
317
+ text: JSON.stringify(result, null, 2),
318
+ },
319
+ ],
320
+ };
321
+ }
322
+ catch (error) {
323
+ const duration = Date.now() - startTime;
324
+ const errorResult = {
325
+ error: true,
326
+ message: error.message,
327
+ status: error.response?.status,
328
+ statusText: error.response?.statusText,
329
+ headers: normalizeHeaders(error.response?.headers),
330
+ data: error.response?.data,
331
+ duration,
332
+ timestamp: new Date().toISOString(),
333
+ };
334
+ const execution = {
335
+ _id: `ex_${uuidv4().replace(/-/g, '')}`,
336
+ parentId: requestId,
337
+ timestamp: Date.now(),
338
+ response: {
339
+ statusCode: error.response?.status ?? 0,
340
+ statusMessage: error.response?.statusText ?? error.message,
341
+ headers: normalizeHeaders(error.response?.headers),
342
+ body: JSON.stringify(error.response?.data),
343
+ duration,
344
+ size: JSON.stringify(error.response?.data).length,
345
+ },
346
+ error: {
347
+ message: error.message,
348
+ stack: error.stack,
349
+ },
350
+ };
351
+ storage.addExecution(collectionId, requestId, execution);
352
+ return {
353
+ content: [
354
+ {
355
+ type: 'text',
356
+ text: JSON.stringify(errorResult, null, 2),
357
+ },
358
+ ],
359
+ };
360
+ }
361
+ },
362
+ },
363
+ {
364
+ name: 'update_request',
365
+ description: 'Update an existing request',
366
+ inputSchema: {
367
+ type: 'object',
368
+ properties: {
369
+ requestId: { type: 'string', description: 'ID of the request to update' },
370
+ name: { type: 'string', description: 'New request name' },
371
+ method: { type: 'string', enum: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'] },
372
+ url: { type: 'string', description: 'New endpoint URL' },
373
+ headers: {
374
+ type: 'array',
375
+ description: 'New request headers',
376
+ items: {
377
+ type: 'object',
378
+ properties: {
379
+ id: { type: 'string' },
380
+ name: { type: 'string' },
381
+ value: { type: 'string' },
382
+ description: { type: 'string' },
383
+ disabled: { type: 'boolean' }
384
+ },
385
+ required: ['name', 'value']
386
+ }
387
+ },
388
+ body: { type: 'object', description: 'New request body' },
389
+ parameters: {
390
+ type: 'array',
391
+ description: 'New query parameters',
392
+ items: {
393
+ type: 'object',
394
+ properties: {
395
+ id: { type: 'string' },
396
+ name: { type: 'string' },
397
+ value: { type: 'string' },
398
+ description: { type: 'string' },
399
+ disabled: { type: 'boolean' }
400
+ },
401
+ required: ['name', 'value']
402
+ }
403
+ },
404
+ authentication: { type: 'object', description: 'New authentication config' },
405
+ description: { type: 'string', description: 'New request description' },
406
+ },
407
+ required: ['requestId'],
408
+ },
409
+ handler: async (request) => {
410
+ const params = request.params.arguments;
411
+ let targetRequest = null;
412
+ let targetCollectionId = null;
413
+ const collections = storage.getAllCollections();
414
+ for (const [collectionId, collection] of collections.entries()) {
415
+ const foundRequest = collection.requests.find(r => r._id === params.requestId);
416
+ if (foundRequest) {
417
+ targetRequest = foundRequest;
418
+ targetCollectionId = collectionId;
419
+ break;
420
+ }
421
+ }
422
+ if (!targetRequest || !targetCollectionId) {
423
+ throw new Error(`Request with ID ${params.requestId} not found`);
424
+ }
425
+ if (params.name !== undefined)
426
+ targetRequest.name = params.name;
427
+ if (params.method !== undefined)
428
+ targetRequest.method = params.method;
429
+ if (params.url !== undefined)
430
+ targetRequest.url = params.url;
431
+ if (params.headers !== undefined)
432
+ targetRequest.headers = params.headers;
433
+ if (params.body !== undefined)
434
+ targetRequest.body = params.body;
435
+ if (params.parameters !== undefined)
436
+ targetRequest.parameters = params.parameters;
437
+ if (params.authentication !== undefined)
438
+ targetRequest.authentication = params.authentication;
439
+ if (params.description !== undefined)
440
+ targetRequest.description = params.description;
441
+ targetRequest.modified = Date.now();
442
+ const updatedCollection = collections.get(targetCollectionId);
443
+ storage.saveCollection(targetCollectionId, updatedCollection);
444
+ return {
445
+ content: [
446
+ {
447
+ type: 'text',
448
+ text: `Request with ID ${params.requestId} updated successfully`,
449
+ },
450
+ ],
451
+ };
452
+ },
453
+ },
454
+ {
455
+ name: 'delete_request',
456
+ description: 'Delete a request from a collection',
457
+ inputSchema: {
458
+ type: 'object',
459
+ properties: {
460
+ requestId: { type: 'string', description: 'ID of the request to delete' },
461
+ },
462
+ required: ['requestId'],
463
+ },
464
+ handler: async (request) => {
465
+ const { requestId } = request.params.arguments;
466
+ const collections = storage.getAllCollections();
467
+ for (const [collectionId, collection] of collections.entries()) {
468
+ const requestIndex = collection.requests.findIndex(r => r._id === requestId);
469
+ if (requestIndex !== -1) {
470
+ collection.requests.splice(requestIndex, 1);
471
+ storage.saveCollection(collectionId, collection);
472
+ return {
473
+ content: [
474
+ {
475
+ type: 'text',
476
+ text: `Request with ID ${requestId} deleted successfully`,
477
+ },
478
+ ],
479
+ };
480
+ }
481
+ }
482
+ throw new Error(`Request with ID ${requestId} not found`);
483
+ },
484
+ },
485
+ {
486
+ name: 'set_environment_variable',
487
+ description: 'Set environment variables for a collection',
488
+ inputSchema: {
489
+ type: 'object',
490
+ properties: {
491
+ collectionId: { type: 'string', description: 'ID collection' },
492
+ environmentId: { type: 'string', description: 'Environment ID (optional, will be created if not exists)' },
493
+ key: { type: 'string', description: 'Variable name' },
494
+ value: { type: 'string', description: 'Variable value' },
495
+ description: { type: 'string', description: 'Variable description' },
496
+ },
497
+ required: ['collectionId', 'key', 'value'],
498
+ },
499
+ handler: async (request) => {
500
+ const params = request.params.arguments;
501
+ const collection = storage.getCollection(params.collectionId);
502
+ if (!collection) {
503
+ throw new Error(`Collection with ID ${params.collectionId} not found`);
504
+ }
505
+ let environment = collection.environments.find(env => env._id === params.environmentId);
506
+ if (!environment) {
507
+ const envId = `env_${uuidv4().replace(/-/g, '')}`;
508
+ environment = {
509
+ _id: envId,
510
+ _type: 'environment',
511
+ parentId: params.collectionId,
512
+ name: 'Base Environment',
513
+ description: 'Base environment variables',
514
+ data: {},
515
+ modified: Date.now(),
516
+ created: Date.now(),
517
+ };
518
+ collection.environments.push(environment);
519
+ }
520
+ environment.data[params.key] = params.value;
521
+ environment.modified = Date.now();
522
+ storage.saveCollection(params.collectionId, collection);
523
+ return {
524
+ content: [
525
+ {
526
+ type: 'text',
527
+ text: `Environment variable "${params.key}" set successfully with value: ${params.value}`,
528
+ },
529
+ ],
530
+ };
531
+ },
532
+ },
533
+ {
534
+ name: 'get_environment_variables',
535
+ description: 'Get environment variables from a collection',
536
+ inputSchema: {
537
+ type: 'object',
538
+ properties: {
539
+ collectionId: { type: 'string', description: 'ID collection' },
540
+ environmentId: { type: 'string', description: 'Specific environment ID (optional)' },
541
+ },
542
+ required: ['collectionId'],
543
+ },
544
+ handler: async (request) => {
545
+ const { collectionId, environmentId } = request.params.arguments;
546
+ const collection = storage.getCollection(collectionId);
547
+ if (!collection) {
548
+ throw new Error(`Collection with ID ${collectionId} not found`);
549
+ }
550
+ let environments = collection.environments;
551
+ if (environmentId) {
552
+ environments = environments.filter(env => env._id === environmentId);
553
+ }
554
+ const result = environments.map(env => ({
555
+ id: env._id,
556
+ name: env.name,
557
+ description: env.description,
558
+ variables: env.data,
559
+ }));
560
+ return {
561
+ content: [
562
+ {
563
+ type: 'text',
564
+ text: JSON.stringify(result, null, 2),
565
+ },
566
+ ],
567
+ };
568
+ },
569
+ },
570
+ {
571
+ name: 'export_collection',
572
+ description: 'Export collection to JSON (Insomnia V4 format)',
573
+ inputSchema: {
574
+ type: 'object',
575
+ properties: {
576
+ collectionId: { type: 'string', description: 'ID of the collection to export' },
577
+ filePath: { type: 'string', description: 'Output file path (optional)' },
578
+ },
579
+ required: ['collectionId'],
580
+ },
581
+ handler: async (request) => {
582
+ const { collectionId, filePath } = request.params.arguments;
583
+ const collection = storage.getCollection(collectionId);
584
+ if (!collection) {
585
+ throw new Error(`Collection with ID ${collectionId} not found`);
586
+ }
587
+ const exportData = {
588
+ _type: 'export',
589
+ __export_format: 4,
590
+ __export_date: new Date().toISOString(),
591
+ __export_source: 'mcp-insomnia',
592
+ resources: [
593
+ collection.workspace,
594
+ ...collection.folders,
595
+ ...collection.requests,
596
+ ...collection.environments,
597
+ ],
598
+ };
599
+ const jsonData = JSON.stringify(exportData, null, 2);
600
+ if (filePath) {
601
+ fs.writeFileSync(filePath, jsonData);
602
+ return {
603
+ content: [
604
+ {
605
+ type: 'text',
606
+ text: `Collection exported successfully to: ${filePath}`,
607
+ },
608
+ ],
609
+ };
610
+ }
611
+ else {
612
+ return {
613
+ content: [
614
+ {
615
+ type: 'text',
616
+ text: jsonData,
617
+ },
618
+ ],
619
+ };
620
+ }
621
+ },
622
+ },
623
+ {
624
+ name: 'import_from_insomnia_export',
625
+ description: 'Import collections from a standard Insomnia V4 export file',
626
+ inputSchema: {
627
+ type: 'object',
628
+ properties: {
629
+ filePath: { type: 'string', description: 'The absolute path to the Insomnia export file' },
630
+ },
631
+ required: ['filePath'],
632
+ },
633
+ handler: async (request) => {
634
+ const { filePath } = request.params.arguments;
635
+ if (!fs.existsSync(filePath)) {
636
+ throw new Error(`File not found at path: ${filePath}`);
637
+ }
638
+ const fileContent = fs.readFileSync(filePath, 'utf-8');
639
+ const data = JSON.parse(fileContent);
640
+ if (data._type !== 'export' || data.__export_format !== 4) {
641
+ throw new Error('Invalid Insomnia V4 export file format.');
642
+ }
643
+ const allResources = data.resources || [];
644
+ const workspaces = allResources.filter(r => r._type === 'workspace');
645
+ if (workspaces.length === 0) {
646
+ throw new Error('No workspaces found in the export file.');
647
+ }
648
+ let importedCount = 0;
649
+ for (const workspace of workspaces) {
650
+ const structure = {
651
+ workspace,
652
+ folders: [],
653
+ requests: [],
654
+ environments: [],
655
+ };
656
+ const allChildrenIds = new Set([workspace._id]);
657
+ const resourcesInWorkspace = allResources.filter(r => r.parentId === workspace._id);
658
+ const folderIds = new Set();
659
+ resourcesInWorkspace.forEach(r => {
660
+ if (r._type === 'request_group') {
661
+ folderIds.add(r._id);
662
+ allChildrenIds.add(r._id);
663
+ }
664
+ });
665
+ allResources.forEach(r => {
666
+ if (!r.parentId || !allChildrenIds.has(r.parentId))
667
+ return;
668
+ switch (r._type) {
669
+ case 'request_group':
670
+ structure.folders.push(r);
671
+ break;
672
+ case 'request':
673
+ structure.requests.push(r);
674
+ break;
675
+ case 'environment':
676
+ // Environments can be at the root of the workspace
677
+ if (r.parentId === workspace._id) {
678
+ structure.environments.push(r);
679
+ }
680
+ break;
681
+ }
682
+ });
683
+ storage.saveCollection(workspace._id, structure);
684
+ importedCount++;
685
+ }
686
+ return {
687
+ content: [
688
+ {
689
+ type: 'text',
690
+ text: `Successfully imported ${importedCount} collection(s) from ${filePath}`,
691
+ },
692
+ ],
693
+ };
694
+ },
695
+ },
696
+ ];
697
+ }
698
+ //# sourceMappingURL=tools.js.map