@fentz26/envcp 1.0.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.
Files changed (95) hide show
  1. package/.github/workflows/publish.yml +27 -0
  2. package/LICENSE +21 -0
  3. package/README.md +381 -0
  4. package/dist/adapters/base.d.ts +79 -0
  5. package/dist/adapters/base.d.ts.map +1 -0
  6. package/dist/adapters/base.js +317 -0
  7. package/dist/adapters/base.js.map +1 -0
  8. package/dist/adapters/gemini.d.ts +12 -0
  9. package/dist/adapters/gemini.d.ts.map +1 -0
  10. package/dist/adapters/gemini.js +284 -0
  11. package/dist/adapters/gemini.js.map +1 -0
  12. package/dist/adapters/index.d.ts +5 -0
  13. package/dist/adapters/index.d.ts.map +1 -0
  14. package/dist/adapters/index.js +5 -0
  15. package/dist/adapters/index.js.map +1 -0
  16. package/dist/adapters/openai.d.ts +12 -0
  17. package/dist/adapters/openai.d.ts.map +1 -0
  18. package/dist/adapters/openai.js +294 -0
  19. package/dist/adapters/openai.js.map +1 -0
  20. package/dist/adapters/rest.d.ts +12 -0
  21. package/dist/adapters/rest.d.ts.map +1 -0
  22. package/dist/adapters/rest.js +265 -0
  23. package/dist/adapters/rest.js.map +1 -0
  24. package/dist/cli/index.d.ts +2 -0
  25. package/dist/cli/index.d.ts.map +1 -0
  26. package/dist/cli/index.js +472 -0
  27. package/dist/cli/index.js.map +1 -0
  28. package/dist/cli.d.ts +3 -0
  29. package/dist/cli.d.ts.map +1 -0
  30. package/dist/cli.js +3 -0
  31. package/dist/cli.js.map +1 -0
  32. package/dist/config/manager.d.ts +11 -0
  33. package/dist/config/manager.d.ts.map +1 -0
  34. package/dist/config/manager.js +117 -0
  35. package/dist/config/manager.js.map +1 -0
  36. package/dist/index.d.ts +5 -0
  37. package/dist/index.d.ts.map +1 -0
  38. package/dist/index.js +5 -0
  39. package/dist/index.js.map +1 -0
  40. package/dist/mcp/index.d.ts +2 -0
  41. package/dist/mcp/index.d.ts.map +1 -0
  42. package/dist/mcp/index.js +2 -0
  43. package/dist/mcp/index.js.map +1 -0
  44. package/dist/mcp/server.d.ts +24 -0
  45. package/dist/mcp/server.d.ts.map +1 -0
  46. package/dist/mcp/server.js +539 -0
  47. package/dist/mcp/server.js.map +1 -0
  48. package/dist/server/index.d.ts +2 -0
  49. package/dist/server/index.d.ts.map +1 -0
  50. package/dist/server/index.js +2 -0
  51. package/dist/server/index.js.map +1 -0
  52. package/dist/server/unified.d.ts +21 -0
  53. package/dist/server/unified.d.ts.map +1 -0
  54. package/dist/server/unified.js +397 -0
  55. package/dist/server/unified.js.map +1 -0
  56. package/dist/storage/index.d.ts +23 -0
  57. package/dist/storage/index.d.ts.map +1 -0
  58. package/dist/storage/index.js +92 -0
  59. package/dist/storage/index.js.map +1 -0
  60. package/dist/types.d.ts +404 -0
  61. package/dist/types.d.ts.map +1 -0
  62. package/dist/types.js +92 -0
  63. package/dist/types.js.map +1 -0
  64. package/dist/utils/crypto.d.ts +17 -0
  65. package/dist/utils/crypto.d.ts.map +1 -0
  66. package/dist/utils/crypto.js +73 -0
  67. package/dist/utils/crypto.js.map +1 -0
  68. package/dist/utils/http.d.ts +6 -0
  69. package/dist/utils/http.d.ts.map +1 -0
  70. package/dist/utils/http.js +43 -0
  71. package/dist/utils/http.js.map +1 -0
  72. package/dist/utils/session.d.ts +19 -0
  73. package/dist/utils/session.d.ts.map +1 -0
  74. package/dist/utils/session.js +112 -0
  75. package/dist/utils/session.js.map +1 -0
  76. package/package.json +50 -0
  77. package/src/adapters/base.ts +411 -0
  78. package/src/adapters/gemini.ts +314 -0
  79. package/src/adapters/index.ts +4 -0
  80. package/src/adapters/openai.ts +324 -0
  81. package/src/adapters/rest.ts +294 -0
  82. package/src/cli/index.ts +640 -0
  83. package/src/cli.ts +2 -0
  84. package/src/config/manager.ts +134 -0
  85. package/src/index.ts +4 -0
  86. package/src/mcp/index.ts +1 -0
  87. package/src/mcp/server.ts +623 -0
  88. package/src/server/index.ts +1 -0
  89. package/src/server/unified.ts +460 -0
  90. package/src/storage/index.ts +112 -0
  91. package/src/types.ts +181 -0
  92. package/src/utils/crypto.ts +100 -0
  93. package/src/utils/http.ts +45 -0
  94. package/src/utils/session.ts +141 -0
  95. package/tsconfig.json +20 -0
@@ -0,0 +1,623 @@
1
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import {
4
+ CallToolRequestSchema,
5
+ ListToolsRequestSchema,
6
+ ErrorCode,
7
+ McpError,
8
+ } from '@modelcontextprotocol/sdk/types.js';
9
+ import { StorageManager, LogManager } from '../storage/index.js';
10
+ import { EnvCPConfig, Variable } from '../types.js';
11
+ import { maskValue } from '../utils/crypto.js';
12
+ import { canAccess, isBlacklisted, canAIActiveCheck, requiresUserReference } from '../config/manager.js';
13
+ import { SessionManager } from '../utils/session.js';
14
+ import * as fs from 'fs-extra';
15
+ import * as path from 'path';
16
+ import * as dotenv from 'dotenv';
17
+
18
+ export class EnvCPServer {
19
+ private server: Server;
20
+ private storage: StorageManager;
21
+ private logs: LogManager;
22
+ private sessionManager: SessionManager;
23
+ private config: EnvCPConfig;
24
+ private projectPath: string;
25
+
26
+ constructor(config: EnvCPConfig, projectPath: string, password?: string) {
27
+ this.config = config;
28
+ this.projectPath = projectPath;
29
+ this.storage = new StorageManager(
30
+ path.join(projectPath, config.storage.path),
31
+ config.storage.encrypted
32
+ );
33
+
34
+ this.sessionManager = new SessionManager(
35
+ path.join(projectPath, config.session?.path || '.envcp/.session'),
36
+ config.session?.timeout_minutes || 30,
37
+ config.session?.max_extensions || 5
38
+ );
39
+
40
+ if (password) {
41
+ this.storage.setPassword(password);
42
+ }
43
+
44
+ this.logs = new LogManager(path.join(projectPath, '.envcp', 'logs'));
45
+
46
+ this.server = new Server(
47
+ { name: 'envcp', version: '1.0.0' },
48
+ { capabilities: { tools: {} } }
49
+ );
50
+
51
+ this.setupHandlers();
52
+ }
53
+
54
+ private setupHandlers(): void {
55
+ this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
56
+ tools: [
57
+ {
58
+ name: 'envcp_list',
59
+ description: 'List all available environment variable names. Values are never shown to AI. Only available if allow_ai_active_check is enabled.',
60
+ inputSchema: {
61
+ type: 'object',
62
+ properties: {
63
+ tags: {
64
+ type: 'array',
65
+ items: { type: 'string' },
66
+ description: 'Filter by tags',
67
+ },
68
+ },
69
+ },
70
+ },
71
+ {
72
+ name: 'envcp_get',
73
+ description: 'Get an environment variable. Returns masked value by default. Use show_value=true to see the actual value (requires user confirmation).',
74
+ inputSchema: {
75
+ type: 'object',
76
+ properties: {
77
+ name: {
78
+ type: 'string',
79
+ description: 'Variable name',
80
+ },
81
+ show_value: {
82
+ type: 'boolean',
83
+ description: 'Show actual value (default: false, returns masked value)',
84
+ },
85
+ },
86
+ required: ['name'],
87
+ },
88
+ },
89
+ {
90
+ name: 'envcp_set',
91
+ description: 'Create or update an environment variable. Only available if allow_ai_write is enabled.',
92
+ inputSchema: {
93
+ type: 'object',
94
+ properties: {
95
+ name: {
96
+ type: 'string',
97
+ description: 'Variable name',
98
+ },
99
+ value: {
100
+ type: 'string',
101
+ description: 'Variable value',
102
+ },
103
+ tags: {
104
+ type: 'array',
105
+ items: { type: 'string' },
106
+ description: 'Tags for organization',
107
+ },
108
+ description: {
109
+ type: 'string',
110
+ description: 'Variable description',
111
+ },
112
+ },
113
+ required: ['name', 'value'],
114
+ },
115
+ },
116
+ {
117
+ name: 'envcp_delete',
118
+ description: 'Delete an environment variable. Only available if allow_ai_delete is enabled.',
119
+ inputSchema: {
120
+ type: 'object',
121
+ properties: {
122
+ name: {
123
+ type: 'string',
124
+ description: 'Variable name',
125
+ },
126
+ },
127
+ required: ['name'],
128
+ },
129
+ },
130
+ {
131
+ name: 'envcp_sync',
132
+ description: 'Sync variables to .env file. Only available if sync is enabled.',
133
+ inputSchema: {
134
+ type: 'object',
135
+ properties: {},
136
+ },
137
+ },
138
+ {
139
+ name: 'envcp_run',
140
+ description: 'Execute a command with environment variables injected. Variables are loaded but not shown.',
141
+ inputSchema: {
142
+ type: 'object',
143
+ properties: {
144
+ command: {
145
+ type: 'string',
146
+ description: 'Command to execute',
147
+ },
148
+ variables: {
149
+ type: 'array',
150
+ items: { type: 'string' },
151
+ description: 'Variable names to inject',
152
+ },
153
+ },
154
+ required: ['command', 'variables'],
155
+ },
156
+ },
157
+ {
158
+ name: 'envcp_add_to_env',
159
+ description: 'Write a stored variable to a .env file. The value is written to disk but not returned in the response.',
160
+ inputSchema: {
161
+ type: 'object',
162
+ properties: {
163
+ name: {
164
+ type: 'string',
165
+ description: 'Variable name to add',
166
+ },
167
+ env_file: {
168
+ type: 'string',
169
+ description: 'Path to .env file (default: .env)',
170
+ },
171
+ },
172
+ required: ['name'],
173
+ },
174
+ },
175
+ {
176
+ name: 'envcp_check_access',
177
+ description: 'Check if a variable exists and can be accessed. Returns yes/no, not the value.',
178
+ inputSchema: {
179
+ type: 'object',
180
+ properties: {
181
+ name: {
182
+ type: 'string',
183
+ description: 'Variable name to check',
184
+ },
185
+ },
186
+ required: ['name'],
187
+ },
188
+ },
189
+ ],
190
+ }));
191
+
192
+ this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
193
+ const { name, arguments: args } = request.params;
194
+
195
+ try {
196
+ await this.ensurePassword();
197
+
198
+ switch (name) {
199
+ case 'envcp_list':
200
+ return await this.handleList(args as any);
201
+ case 'envcp_get':
202
+ return await this.handleGet(args as any);
203
+ case 'envcp_set':
204
+ return await this.handleSet(args as any);
205
+ case 'envcp_delete':
206
+ return await this.handleDelete(args as any);
207
+ case 'envcp_sync':
208
+ return await this.handleSync();
209
+ case 'envcp_run':
210
+ return await this.handleRun(args as any);
211
+ case 'envcp_add_to_env':
212
+ return await this.handleAddToEnv(args as any);
213
+ case 'envcp_check_access':
214
+ return await this.handleCheckAccess(args as any);
215
+ default:
216
+ throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
217
+ }
218
+ } catch (error: any) {
219
+ throw new McpError(ErrorCode.InternalError, error.message);
220
+ }
221
+ });
222
+ }
223
+
224
+ private async ensurePassword(): Promise<void> {
225
+ const pwd = this.sessionManager.getPassword();
226
+ if (pwd && await this.sessionManager.isValid()) {
227
+ this.storage.setPassword(pwd);
228
+ return;
229
+ }
230
+
231
+ throw new Error('Session locked. Please unlock first using: envcp unlock');
232
+ }
233
+
234
+ private async handleList(args: { tags?: string[] }): Promise<any> {
235
+ if (!this.config.access.allow_ai_read) {
236
+ throw new Error('AI read access is disabled');
237
+ }
238
+
239
+ if (!canAIActiveCheck(this.config)) {
240
+ throw new Error('AI active check is disabled. User must explicitly mention variable names.');
241
+ }
242
+
243
+ const names = await this.storage.list();
244
+ let filtered = names.filter(n => canAccess(n, this.config) && !isBlacklisted(n, this.config));
245
+
246
+ if (args.tags && args.tags.length > 0) {
247
+ const variables = await this.storage.load();
248
+ filtered = filtered.filter(name => {
249
+ const v = variables[name];
250
+ return v.tags && args.tags!.some(t => v.tags!.includes(t));
251
+ });
252
+ }
253
+
254
+ await this.logs.log({
255
+ timestamp: new Date().toISOString(),
256
+ operation: 'list',
257
+ source: 'mcp',
258
+ success: true,
259
+ message: `Listed ${filtered.length} variables`,
260
+ });
261
+
262
+ return {
263
+ content: [
264
+ {
265
+ type: 'text',
266
+ text: JSON.stringify({ variables: filtered, count: filtered.length }, null, 2),
267
+ },
268
+ ],
269
+ };
270
+ }
271
+
272
+ private async handleGet(args: { name: string; show_value?: boolean }): Promise<any> {
273
+ if (!this.config.access.allow_ai_read) {
274
+ throw new Error('AI read access is disabled');
275
+ }
276
+
277
+ if (requiresUserReference(this.config)) {
278
+ // In MCP context, the AI is calling this tool - require_user_reference means
279
+ // the user must have explicitly mentioned the variable name in their message.
280
+ // We can't verify this server-side, so we log a warning for audit purposes.
281
+ }
282
+
283
+ const variable = await this.storage.get(args.name);
284
+
285
+ if (!variable) {
286
+ throw new Error(`Variable '${args.name}' not found`);
287
+ }
288
+
289
+ if (isBlacklisted(args.name, this.config)) {
290
+ throw new Error(`Variable '${args.name}' is blacklisted and cannot be accessed`);
291
+ }
292
+
293
+ if (!canAccess(args.name, this.config)) {
294
+ throw new Error(`Access denied to variable '${args.name}'`);
295
+ }
296
+
297
+ variable.accessed = new Date().toISOString();
298
+ await this.storage.set(args.name, variable);
299
+
300
+ const canReveal = args.show_value && !this.config.access.mask_values && !this.config.access.require_confirmation;
301
+ const value = canReveal ? variable.value : maskValue(variable.value);
302
+
303
+ await this.logs.log({
304
+ timestamp: new Date().toISOString(),
305
+ operation: 'get',
306
+ variable: args.name,
307
+ source: 'mcp',
308
+ success: true,
309
+ message: canReveal ? 'Value revealed' : 'Value masked',
310
+ });
311
+
312
+ return {
313
+ content: [
314
+ {
315
+ type: 'text',
316
+ text: JSON.stringify({
317
+ name: variable.name,
318
+ value: value,
319
+ tags: variable.tags,
320
+ description: variable.description,
321
+ encrypted: variable.encrypted,
322
+ }, null, 2),
323
+ },
324
+ ],
325
+ };
326
+ }
327
+
328
+ private async handleSet(args: { name: string; value: string; tags?: string[]; description?: string }): Promise<any> {
329
+ if (!this.config.access.allow_ai_write) {
330
+ throw new Error('AI write access is disabled');
331
+ }
332
+
333
+ if (isBlacklisted(args.name, this.config)) {
334
+ throw new Error(`Variable '${args.name}' is blacklisted`);
335
+ }
336
+
337
+ const existing = await this.storage.get(args.name);
338
+ const now = new Date().toISOString();
339
+
340
+ const variable: Variable = {
341
+ name: args.name,
342
+ value: args.value,
343
+ encrypted: this.config.storage.encrypted,
344
+ tags: args.tags,
345
+ description: args.description,
346
+ created: existing?.created || now,
347
+ updated: now,
348
+ sync_to_env: true,
349
+ };
350
+
351
+ await this.storage.set(args.name, variable);
352
+
353
+ await this.logs.log({
354
+ timestamp: new Date().toISOString(),
355
+ operation: existing ? 'update' : 'add',
356
+ variable: args.name,
357
+ source: 'mcp',
358
+ success: true,
359
+ message: `Variable ${existing ? 'updated' : 'created'}`,
360
+ });
361
+
362
+ return {
363
+ content: [
364
+ {
365
+ type: 'text',
366
+ text: JSON.stringify({ success: true, message: `Variable '${args.name}' ${existing ? 'updated' : 'created'}` }),
367
+ },
368
+ ],
369
+ };
370
+ }
371
+
372
+ private async handleDelete(args: { name: string }): Promise<any> {
373
+ if (!this.config.access.allow_ai_delete) {
374
+ throw new Error('AI delete access is disabled');
375
+ }
376
+
377
+ const deleted = await this.storage.delete(args.name);
378
+
379
+ await this.logs.log({
380
+ timestamp: new Date().toISOString(),
381
+ operation: 'delete',
382
+ variable: args.name,
383
+ source: 'mcp',
384
+ success: deleted,
385
+ message: deleted ? 'Variable deleted' : 'Variable not found',
386
+ });
387
+
388
+ return {
389
+ content: [
390
+ {
391
+ type: 'text',
392
+ text: JSON.stringify({ success: deleted, message: deleted ? `Variable '${args.name}' deleted` : `Variable '${args.name}' not found` }),
393
+ },
394
+ ],
395
+ };
396
+ }
397
+
398
+ private async handleSync(): Promise<any> {
399
+ if (!this.config.access.allow_ai_export) {
400
+ throw new Error('AI export access is disabled');
401
+ }
402
+
403
+ if (!this.config.sync.enabled) {
404
+ throw new Error('Sync is disabled in configuration');
405
+ }
406
+
407
+ const variables = await this.storage.load();
408
+ const lines: string[] = [];
409
+
410
+ if (this.config.sync.header) {
411
+ lines.push(this.config.sync.header);
412
+ }
413
+
414
+ for (const [name, variable] of Object.entries(variables)) {
415
+ if (isBlacklisted(name, this.config)) {
416
+ continue;
417
+ }
418
+
419
+ const excluded = this.config.sync.exclude?.some(pattern => {
420
+ const regex = new RegExp('^' + pattern.replace(/\*/g, '.*') + '$');
421
+ return regex.test(name);
422
+ });
423
+
424
+ if (excluded || !variable.sync_to_env) {
425
+ continue;
426
+ }
427
+
428
+ lines.push(`${name}=${variable.value}`);
429
+ }
430
+
431
+ const envPath = path.join(this.projectPath, this.config.sync.target);
432
+ await fs.writeFile(envPath, lines.join('\n'), 'utf8');
433
+
434
+ await this.logs.log({
435
+ timestamp: new Date().toISOString(),
436
+ operation: 'sync',
437
+ source: 'mcp',
438
+ success: true,
439
+ message: `Synced ${lines.length} variables to ${this.config.sync.target}`,
440
+ });
441
+
442
+ return {
443
+ content: [
444
+ {
445
+ type: 'text',
446
+ text: JSON.stringify({ success: true, message: `Synced ${lines.length} variables to ${this.config.sync.target}` }),
447
+ },
448
+ ],
449
+ };
450
+ }
451
+
452
+ private parseCommand(command: string): { program: string; args: string[] } {
453
+ const tokens: string[] = [];
454
+ let current = '';
455
+ let inSingle = false;
456
+ let inDouble = false;
457
+
458
+ for (let i = 0; i < command.length; i++) {
459
+ const ch = command[i];
460
+ if (ch === "'" && !inDouble) {
461
+ inSingle = !inSingle;
462
+ } else if (ch === '"' && !inSingle) {
463
+ inDouble = !inDouble;
464
+ } else if (ch === ' ' && !inSingle && !inDouble) {
465
+ if (current.length > 0) {
466
+ tokens.push(current);
467
+ current = '';
468
+ }
469
+ } else {
470
+ current += ch;
471
+ }
472
+ }
473
+ if (current.length > 0) tokens.push(current);
474
+
475
+ if (tokens.length === 0) throw new Error('Empty command');
476
+ return { program: tokens[0], args: tokens.slice(1) };
477
+ }
478
+
479
+ private validateCommand(command: string): void {
480
+ const shellMetachars = /[;&|`$(){}!><\n\\]/;
481
+ if (shellMetachars.test(command)) {
482
+ throw new Error('Command contains disallowed shell metacharacters: ; & | ` $ ( ) { } ! > < \\');
483
+ }
484
+ }
485
+
486
+ private async handleRun(args: { command: string; variables: string[] }): Promise<any> {
487
+ this.validateCommand(args.command);
488
+
489
+ const { spawn } = await import('child_process');
490
+ const { program, args: cmdArgs } = this.parseCommand(args.command);
491
+ const env: Record<string, string> = { ...process.env } as Record<string, string>;
492
+
493
+ for (const name of args.variables) {
494
+ if (isBlacklisted(name, this.config)) {
495
+ continue;
496
+ }
497
+ const variable = await this.storage.get(name);
498
+ if (variable) {
499
+ env[name] = variable.value;
500
+ }
501
+ }
502
+
503
+ return new Promise((resolve) => {
504
+ const proc = spawn(program, cmdArgs, {
505
+ env,
506
+ cwd: this.projectPath,
507
+ });
508
+
509
+ let stdout = '';
510
+ let stderr = '';
511
+
512
+ proc.stdout.on('data', (data) => { stdout += data; });
513
+ proc.stderr.on('data', (data) => { stderr += data; });
514
+
515
+ proc.on('close', (code) => {
516
+ resolve({
517
+ content: [
518
+ {
519
+ type: 'text',
520
+ text: JSON.stringify({
521
+ exitCode: code,
522
+ stdout,
523
+ stderr,
524
+ }, null, 2),
525
+ },
526
+ ],
527
+ });
528
+ });
529
+ });
530
+ }
531
+
532
+ private async handleAddToEnv(args: { name: string; env_file?: string }): Promise<any> {
533
+ const variable = await this.storage.get(args.name);
534
+
535
+ if (!variable) {
536
+ throw new Error(`Variable '${args.name}' not found`);
537
+ }
538
+
539
+ if (isBlacklisted(args.name, this.config)) {
540
+ throw new Error(`Variable '${args.name}' is blacklisted`);
541
+ }
542
+
543
+ const envPath = path.join(this.projectPath, args.env_file || '.env');
544
+ let content = '';
545
+
546
+ if (await fs.pathExists(envPath)) {
547
+ content = await fs.readFile(envPath, 'utf8');
548
+ }
549
+
550
+ const envVars = dotenv.parse(content);
551
+
552
+ if (envVars[args.name]) {
553
+ const lines = content.split('\n');
554
+ const newLines = lines.map(line => {
555
+ if (line.startsWith(`${args.name}=`)) {
556
+ return `${args.name}=${variable.value}`;
557
+ }
558
+ return line;
559
+ });
560
+ content = newLines.join('\n');
561
+ } else {
562
+ content += `\n${args.name}=${variable.value}`;
563
+ }
564
+
565
+ await fs.writeFile(envPath, content, 'utf8');
566
+
567
+ await this.logs.log({
568
+ timestamp: new Date().toISOString(),
569
+ operation: 'add',
570
+ variable: args.name,
571
+ source: 'mcp',
572
+ success: true,
573
+ message: `Added to ${args.env_file || '.env'}`,
574
+ });
575
+
576
+ return {
577
+ content: [
578
+ {
579
+ type: 'text',
580
+ text: JSON.stringify({ success: true, message: `Variable '${args.name}' added to ${args.env_file || '.env'}` }),
581
+ },
582
+ ],
583
+ };
584
+ }
585
+
586
+ private async handleCheckAccess(args: { name: string }): Promise<any> {
587
+ const variable = await this.storage.get(args.name);
588
+ const exists = !!variable;
589
+ const blacklisted = isBlacklisted(args.name, this.config);
590
+ const accessible = exists && !blacklisted && canAccess(args.name, this.config);
591
+
592
+ await this.logs.log({
593
+ timestamp: new Date().toISOString(),
594
+ operation: 'check_access',
595
+ variable: args.name,
596
+ source: 'mcp',
597
+ success: true,
598
+ message: `Access check: ${accessible ? 'granted' : 'denied'}`,
599
+ });
600
+
601
+ return {
602
+ content: [
603
+ {
604
+ type: 'text',
605
+ text: JSON.stringify({
606
+ name: args.name,
607
+ exists,
608
+ accessible,
609
+ blacklisted,
610
+ message: accessible ? 'Variable exists and can be accessed' : 'Variable cannot be accessed or does not exist',
611
+ }, null, 2),
612
+ },
613
+ ],
614
+ };
615
+ }
616
+
617
+ async start(): Promise<void> {
618
+ await this.logs.init();
619
+ await this.sessionManager.init();
620
+ const transport = new StdioServerTransport();
621
+ await this.server.connect(transport);
622
+ }
623
+ }
@@ -0,0 +1 @@
1
+ export { UnifiedServer } from './unified.js';