@dxheroes/local-mcp-backend 0.4.0 → 0.5.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 (40) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/AGENTS.md +4 -0
  3. package/CHANGELOG.md +32 -0
  4. package/dist/app.module.js +5 -0
  5. package/dist/app.module.js.map +1 -1
  6. package/dist/modules/profiles/profiles.service.js +15 -0
  7. package/dist/modules/profiles/profiles.service.js.map +1 -1
  8. package/dist/modules/proxy/proxy.controller.js +258 -9
  9. package/dist/modules/proxy/proxy.controller.js.map +1 -1
  10. package/dist/modules/proxy/proxy.module.js +4 -1
  11. package/dist/modules/proxy/proxy.module.js.map +1 -1
  12. package/dist/modules/settings/settings.constants.js +18 -0
  13. package/dist/modules/settings/settings.constants.js.map +1 -0
  14. package/dist/modules/settings/settings.controller.js +89 -0
  15. package/dist/modules/settings/settings.controller.js.map +1 -0
  16. package/dist/modules/settings/settings.module.js +30 -0
  17. package/dist/modules/settings/settings.module.js.map +1 -0
  18. package/dist/modules/settings/settings.service.js +110 -0
  19. package/dist/modules/settings/settings.service.js.map +1 -0
  20. package/package.json +6 -5
  21. package/src/AGENTS.md +23 -0
  22. package/src/app.module.ts +6 -0
  23. package/src/common/AGENTS.md +19 -0
  24. package/src/config/AGENTS.md +17 -0
  25. package/src/modules/AGENTS.md +31 -0
  26. package/src/modules/database/AGENTS.md +30 -0
  27. package/src/modules/debug/AGENTS.md +30 -0
  28. package/src/modules/health/AGENTS.md +22 -0
  29. package/src/modules/mcp/AGENTS.md +38 -0
  30. package/src/modules/oauth/AGENTS.md +32 -0
  31. package/src/modules/profiles/AGENTS.md +33 -0
  32. package/src/modules/profiles/profiles.service.ts +19 -0
  33. package/src/modules/proxy/AGENTS.md +34 -0
  34. package/src/modules/proxy/proxy.controller.ts +249 -7
  35. package/src/modules/proxy/proxy.module.ts +3 -1
  36. package/src/modules/settings/AGENTS.md +31 -0
  37. package/src/modules/settings/settings.constants.ts +20 -0
  38. package/src/modules/settings/settings.controller.ts +47 -0
  39. package/src/modules/settings/settings.module.ts +16 -0
  40. package/src/modules/settings/settings.service.ts +99 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/modules/proxy/proxy.module.ts"],"sourcesContent":["/**\n * Proxy Module\n *\n * Handles MCP proxy endpoints for profiles.\n */\n\nimport { Module } from '@nestjs/common';\nimport { DebugModule } from '../debug/debug.module.js';\nimport { McpModule } from '../mcp/mcp.module.js';\nimport { ProxyController } from './proxy.controller.js';\nimport { ProxyService } from './proxy.service.js';\n\n@Module({\n imports: [McpModule, DebugModule],\n controllers: [ProxyController],\n providers: [ProxyService],\n exports: [ProxyService],\n})\nexport class ProxyModule {}\n"],"names":["Module","DebugModule","McpModule","ProxyController","ProxyService","ProxyModule","imports","controllers","providers","exports"],"mappings":";;;;;;AAAA;;;;CAIC,GAED,SAASA,MAAM,QAAQ,iBAAiB;AACxC,SAASC,WAAW,QAAQ,2BAA2B;AACvD,SAASC,SAAS,QAAQ,uBAAuB;AACjD,SAASC,eAAe,QAAQ,wBAAwB;AACxD,SAASC,YAAY,QAAQ,qBAAqB;AAQlD,OAAO,MAAMC;AAAa;;;QALxBC,SAAS;YAACJ;YAAWD;SAAY;QACjCM,aAAa;YAACJ;SAAgB;QAC9BK,WAAW;YAACJ;SAAa;QACzBK,SAAS;YAACL;SAAa"}
1
+ {"version":3,"sources":["../../../src/modules/proxy/proxy.module.ts"],"sourcesContent":["/**\n * Proxy Module\n *\n * Handles MCP proxy endpoints for profiles.\n * Supports SSE notifications for MCP Streamable HTTP transport.\n */\n\nimport { Module } from '@nestjs/common';\nimport { DebugModule } from '../debug/debug.module.js';\nimport { McpModule } from '../mcp/mcp.module.js';\nimport { SettingsModule } from '../settings/settings.module.js';\nimport { ProxyController } from './proxy.controller.js';\nimport { ProxyService } from './proxy.service.js';\n\n@Module({\n imports: [McpModule, DebugModule, SettingsModule],\n controllers: [ProxyController],\n providers: [ProxyService],\n exports: [ProxyService],\n})\nexport class ProxyModule {}\n"],"names":["Module","DebugModule","McpModule","SettingsModule","ProxyController","ProxyService","ProxyModule","imports","controllers","providers","exports"],"mappings":";;;;;;AAAA;;;;;CAKC,GAED,SAASA,MAAM,QAAQ,iBAAiB;AACxC,SAASC,WAAW,QAAQ,2BAA2B;AACvD,SAASC,SAAS,QAAQ,uBAAuB;AACjD,SAASC,cAAc,QAAQ,iCAAiC;AAChE,SAASC,eAAe,QAAQ,wBAAwB;AACxD,SAASC,YAAY,QAAQ,qBAAqB;AAQlD,OAAO,MAAMC;AAAa;;;QALxBC,SAAS;YAACL;YAAWD;YAAaE;SAAe;QACjDK,aAAa;YAACJ;SAAgB;QAC9BK,WAAW;YAACJ;SAAa;QACzBK,SAAS;YAACL;SAAa"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Settings Constants
3
+ *
4
+ * Defines setting keys, defaults, and reserved profile names.
5
+ */ // Setting keys
6
+ export const SETTING_KEYS = {
7
+ DEFAULT_GATEWAY_PROFILE: 'default_gateway_profile'
8
+ };
9
+ // Default values for settings
10
+ export const SETTING_DEFAULTS = {
11
+ [SETTING_KEYS.DEFAULT_GATEWAY_PROFILE]: 'default'
12
+ };
13
+ // Reserved profile names that cannot be created by users
14
+ export const RESERVED_PROFILE_NAMES = [
15
+ 'gateway'
16
+ ];
17
+
18
+ //# sourceMappingURL=settings.constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/modules/settings/settings.constants.ts"],"sourcesContent":["/**\n * Settings Constants\n *\n * Defines setting keys, defaults, and reserved profile names.\n */\n\n// Setting keys\nexport const SETTING_KEYS = {\n DEFAULT_GATEWAY_PROFILE: 'default_gateway_profile',\n} as const;\n\n// Default values for settings\nexport const SETTING_DEFAULTS: Record<string, string> = {\n [SETTING_KEYS.DEFAULT_GATEWAY_PROFILE]: 'default',\n};\n\n// Reserved profile names that cannot be created by users\nexport const RESERVED_PROFILE_NAMES = ['gateway'] as const;\n\nexport type SettingKey = (typeof SETTING_KEYS)[keyof typeof SETTING_KEYS];\n"],"names":["SETTING_KEYS","DEFAULT_GATEWAY_PROFILE","SETTING_DEFAULTS","RESERVED_PROFILE_NAMES"],"mappings":"AAAA;;;;CAIC,GAED,eAAe;AACf,OAAO,MAAMA,eAAe;IAC1BC,yBAAyB;AAC3B,EAAW;AAEX,8BAA8B;AAC9B,OAAO,MAAMC,mBAA2C;IACtD,CAACF,aAAaC,uBAAuB,CAAC,EAAE;AAC1C,EAAE;AAEF,yDAAyD;AACzD,OAAO,MAAME,yBAAyB;IAAC;CAAU,CAAU"}
@@ -0,0 +1,89 @@
1
+ function _ts_decorate(decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ }
7
+ function _ts_metadata(k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ }
10
+ function _ts_param(paramIndex, decorator) {
11
+ return function(target, key) {
12
+ decorator(target, key, paramIndex);
13
+ };
14
+ }
15
+ /**
16
+ * Settings Controller
17
+ *
18
+ * REST API endpoints for gateway settings management.
19
+ */ import { Body, Controller, Get, Put } from "@nestjs/common";
20
+ import { IsNotEmpty, IsString, MaxLength } from "class-validator";
21
+ import { SettingsService } from "./settings.service.js";
22
+ export class UpdateDefaultGatewayProfileDto {
23
+ }
24
+ _ts_decorate([
25
+ IsString(),
26
+ IsNotEmpty({
27
+ message: 'Profile name cannot be empty'
28
+ }),
29
+ MaxLength(100, {
30
+ message: 'Profile name must be at most 100 characters'
31
+ }),
32
+ _ts_metadata("design:type", String)
33
+ ], UpdateDefaultGatewayProfileDto.prototype, "profileName", void 0);
34
+ export class SettingsController {
35
+ constructor(settingsService){
36
+ this.settingsService = settingsService;
37
+ }
38
+ /**
39
+ * Get all gateway settings
40
+ */ async getAllSettings() {
41
+ return this.settingsService.getAllSettings();
42
+ }
43
+ /**
44
+ * Get the default gateway profile setting
45
+ */ async getDefaultGatewayProfile() {
46
+ const profileName = await this.settingsService.getDefaultGatewayProfile();
47
+ return {
48
+ profileName
49
+ };
50
+ }
51
+ /**
52
+ * Set the default gateway profile
53
+ */ async setDefaultGatewayProfile(dto) {
54
+ await this.settingsService.setDefaultGatewayProfile(dto.profileName);
55
+ return {
56
+ profileName: dto.profileName
57
+ };
58
+ }
59
+ }
60
+ _ts_decorate([
61
+ Get(),
62
+ _ts_metadata("design:type", Function),
63
+ _ts_metadata("design:paramtypes", []),
64
+ _ts_metadata("design:returntype", Promise)
65
+ ], SettingsController.prototype, "getAllSettings", null);
66
+ _ts_decorate([
67
+ Get('default-gateway-profile'),
68
+ _ts_metadata("design:type", Function),
69
+ _ts_metadata("design:paramtypes", []),
70
+ _ts_metadata("design:returntype", Promise)
71
+ ], SettingsController.prototype, "getDefaultGatewayProfile", null);
72
+ _ts_decorate([
73
+ Put('default-gateway-profile'),
74
+ _ts_param(0, Body()),
75
+ _ts_metadata("design:type", Function),
76
+ _ts_metadata("design:paramtypes", [
77
+ typeof UpdateDefaultGatewayProfileDto === "undefined" ? Object : UpdateDefaultGatewayProfileDto
78
+ ]),
79
+ _ts_metadata("design:returntype", Promise)
80
+ ], SettingsController.prototype, "setDefaultGatewayProfile", null);
81
+ SettingsController = _ts_decorate([
82
+ Controller('settings'),
83
+ _ts_metadata("design:type", Function),
84
+ _ts_metadata("design:paramtypes", [
85
+ typeof SettingsService === "undefined" ? Object : SettingsService
86
+ ])
87
+ ], SettingsController);
88
+
89
+ //# sourceMappingURL=settings.controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/modules/settings/settings.controller.ts"],"sourcesContent":["/**\n * Settings Controller\n *\n * REST API endpoints for gateway settings management.\n */\n\nimport { Body, Controller, Get, Put } from '@nestjs/common';\nimport { IsNotEmpty, IsString, MaxLength } from 'class-validator';\nimport { SettingsService } from './settings.service.js';\n\nexport class UpdateDefaultGatewayProfileDto {\n @IsString()\n @IsNotEmpty({ message: 'Profile name cannot be empty' })\n @MaxLength(100, { message: 'Profile name must be at most 100 characters' })\n profileName: string;\n}\n\n@Controller('settings')\nexport class SettingsController {\n constructor(private readonly settingsService: SettingsService) {}\n\n /**\n * Get all gateway settings\n */\n @Get()\n async getAllSettings() {\n return this.settingsService.getAllSettings();\n }\n\n /**\n * Get the default gateway profile setting\n */\n @Get('default-gateway-profile')\n async getDefaultGatewayProfile() {\n const profileName = await this.settingsService.getDefaultGatewayProfile();\n return { profileName };\n }\n\n /**\n * Set the default gateway profile\n */\n @Put('default-gateway-profile')\n async setDefaultGatewayProfile(@Body() dto: UpdateDefaultGatewayProfileDto) {\n await this.settingsService.setDefaultGatewayProfile(dto.profileName);\n return { profileName: dto.profileName };\n }\n}\n"],"names":["Body","Controller","Get","Put","IsNotEmpty","IsString","MaxLength","SettingsService","UpdateDefaultGatewayProfileDto","message","SettingsController","settingsService","getAllSettings","getDefaultGatewayProfile","profileName","setDefaultGatewayProfile","dto"],"mappings":";;;;;;;;;;;;;;AAAA;;;;CAIC,GAED,SAASA,IAAI,EAAEC,UAAU,EAAEC,GAAG,EAAEC,GAAG,QAAQ,iBAAiB;AAC5D,SAASC,UAAU,EAAEC,QAAQ,EAAEC,SAAS,QAAQ,kBAAkB;AAClE,SAASC,eAAe,QAAQ,wBAAwB;AAExD,OAAO,MAAMC;AAKb;;;;QAHgBC,SAAS;;;QACLA,SAAS;;;;AAK7B,OAAO,MAAMC;IACX,YAAY,AAAiBC,eAAgC,CAAE;aAAlCA,kBAAAA;IAAmC;IAEhE;;GAEC,GACD,MACMC,iBAAiB;QACrB,OAAO,IAAI,CAACD,eAAe,CAACC,cAAc;IAC5C;IAEA;;GAEC,GACD,MACMC,2BAA2B;QAC/B,MAAMC,cAAc,MAAM,IAAI,CAACH,eAAe,CAACE,wBAAwB;QACvE,OAAO;YAAEC;QAAY;IACvB;IAEA;;GAEC,GACD,MACMC,yBAAyB,AAAQC,GAAmC,EAAE;QAC1E,MAAM,IAAI,CAACL,eAAe,CAACI,wBAAwB,CAACC,IAAIF,WAAW;QACnE,OAAO;YAAEA,aAAaE,IAAIF,WAAW;QAAC;IACxC;AACF"}
@@ -0,0 +1,30 @@
1
+ function _ts_decorate(decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ }
7
+ /**
8
+ * Settings Module
9
+ *
10
+ * Handles gateway configuration settings.
11
+ */ import { Module } from "@nestjs/common";
12
+ import { SettingsController } from "./settings.controller.js";
13
+ import { SettingsService } from "./settings.service.js";
14
+ export class SettingsModule {
15
+ }
16
+ SettingsModule = _ts_decorate([
17
+ Module({
18
+ controllers: [
19
+ SettingsController
20
+ ],
21
+ providers: [
22
+ SettingsService
23
+ ],
24
+ exports: [
25
+ SettingsService
26
+ ]
27
+ })
28
+ ], SettingsModule);
29
+
30
+ //# sourceMappingURL=settings.module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/modules/settings/settings.module.ts"],"sourcesContent":["/**\n * Settings Module\n *\n * Handles gateway configuration settings.\n */\n\nimport { Module } from '@nestjs/common';\nimport { SettingsController } from './settings.controller.js';\nimport { SettingsService } from './settings.service.js';\n\n@Module({\n controllers: [SettingsController],\n providers: [SettingsService],\n exports: [SettingsService],\n})\nexport class SettingsModule {}\n"],"names":["Module","SettingsController","SettingsService","SettingsModule","controllers","providers","exports"],"mappings":";;;;;;AAAA;;;;CAIC,GAED,SAASA,MAAM,QAAQ,iBAAiB;AACxC,SAASC,kBAAkB,QAAQ,2BAA2B;AAC9D,SAASC,eAAe,QAAQ,wBAAwB;AAOxD,OAAO,MAAMC;AAAgB;;;QAJ3BC,aAAa;YAACH;SAAmB;QACjCI,WAAW;YAACH;SAAgB;QAC5BI,SAAS;YAACJ;SAAgB"}
@@ -0,0 +1,110 @@
1
+ function _ts_decorate(decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ }
7
+ function _ts_metadata(k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ }
10
+ /**
11
+ * Settings Service
12
+ *
13
+ * Business logic for gateway settings management.
14
+ * Emits events when gateway settings change for SSE notifications.
15
+ */ import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
16
+ import { EventEmitter2 } from "@nestjs/event-emitter";
17
+ import { PrismaService } from "../database/prisma.service.js";
18
+ import { SETTING_DEFAULTS, SETTING_KEYS } from "./settings.constants.js";
19
+ /** Event name for gateway profile changes */ export const GATEWAY_PROFILE_CHANGED = 'gateway.profile.changed';
20
+ export class SettingsService {
21
+ constructor(prisma, eventEmitter){
22
+ this.prisma = prisma;
23
+ this.eventEmitter = eventEmitter;
24
+ }
25
+ /**
26
+ * Get a setting value by key, returns default if not set
27
+ */ async getSetting(key) {
28
+ const setting = await this.prisma.gatewaySetting.findUnique({
29
+ where: {
30
+ key
31
+ }
32
+ });
33
+ return setting?.value ?? SETTING_DEFAULTS[key];
34
+ }
35
+ /**
36
+ * Set a setting value
37
+ */ async setSetting(key, value) {
38
+ const setting = await this.prisma.gatewaySetting.upsert({
39
+ where: {
40
+ key
41
+ },
42
+ update: {
43
+ value
44
+ },
45
+ create: {
46
+ key,
47
+ value
48
+ }
49
+ });
50
+ return {
51
+ key: setting.key,
52
+ value: setting.value
53
+ };
54
+ }
55
+ /**
56
+ * Get the default gateway profile name
57
+ */ async getDefaultGatewayProfile() {
58
+ return this.getSetting(SETTING_KEYS.DEFAULT_GATEWAY_PROFILE);
59
+ }
60
+ /**
61
+ * Set the default gateway profile
62
+ * Validates that the profile exists before setting.
63
+ * Emits GATEWAY_PROFILE_CHANGED event for SSE subscribers.
64
+ */ async setDefaultGatewayProfile(profileName) {
65
+ // Input sanitization
66
+ const trimmedName = profileName?.trim();
67
+ if (!trimmedName) {
68
+ throw new BadRequestException('Profile name cannot be empty');
69
+ }
70
+ // Validate that profile exists
71
+ const profile = await this.prisma.profile.findUnique({
72
+ where: {
73
+ name: trimmedName
74
+ }
75
+ });
76
+ if (!profile) {
77
+ throw new NotFoundException(`Profile "${trimmedName}" not found`);
78
+ }
79
+ const result = await this.setSetting(SETTING_KEYS.DEFAULT_GATEWAY_PROFILE, trimmedName);
80
+ // Emit event for SSE subscribers (MCP Streamable HTTP notifications)
81
+ this.eventEmitter.emit(GATEWAY_PROFILE_CHANGED, {
82
+ profileName: trimmedName
83
+ });
84
+ return result;
85
+ }
86
+ /**
87
+ * Get all settings with their current values
88
+ */ async getAllSettings() {
89
+ const settings = await this.prisma.gatewaySetting.findMany();
90
+ // Start with defaults
91
+ const result = {
92
+ ...SETTING_DEFAULTS
93
+ };
94
+ // Override with stored values
95
+ for (const setting of settings){
96
+ result[setting.key] = setting.value;
97
+ }
98
+ return result;
99
+ }
100
+ }
101
+ SettingsService = _ts_decorate([
102
+ Injectable(),
103
+ _ts_metadata("design:type", Function),
104
+ _ts_metadata("design:paramtypes", [
105
+ typeof PrismaService === "undefined" ? Object : PrismaService,
106
+ typeof EventEmitter2 === "undefined" ? Object : EventEmitter2
107
+ ])
108
+ ], SettingsService);
109
+
110
+ //# sourceMappingURL=settings.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/modules/settings/settings.service.ts"],"sourcesContent":["/**\n * Settings Service\n *\n * Business logic for gateway settings management.\n * Emits events when gateway settings change for SSE notifications.\n */\n\nimport { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';\nimport { EventEmitter2 } from '@nestjs/event-emitter';\nimport { PrismaService } from '../database/prisma.service.js';\nimport { SETTING_DEFAULTS, SETTING_KEYS, type SettingKey } from './settings.constants.js';\n\n/** Event name for gateway profile changes */\nexport const GATEWAY_PROFILE_CHANGED = 'gateway.profile.changed';\n\n@Injectable()\nexport class SettingsService {\n constructor(\n private readonly prisma: PrismaService,\n private readonly eventEmitter: EventEmitter2\n ) {}\n\n /**\n * Get a setting value by key, returns default if not set\n */\n async getSetting(key: SettingKey): Promise<string> {\n const setting = await this.prisma.gatewaySetting.findUnique({\n where: { key },\n });\n\n return setting?.value ?? SETTING_DEFAULTS[key];\n }\n\n /**\n * Set a setting value\n */\n async setSetting(key: SettingKey, value: string): Promise<{ key: string; value: string }> {\n const setting = await this.prisma.gatewaySetting.upsert({\n where: { key },\n update: { value },\n create: { key, value },\n });\n\n return { key: setting.key, value: setting.value };\n }\n\n /**\n * Get the default gateway profile name\n */\n async getDefaultGatewayProfile(): Promise<string> {\n return this.getSetting(SETTING_KEYS.DEFAULT_GATEWAY_PROFILE);\n }\n\n /**\n * Set the default gateway profile\n * Validates that the profile exists before setting.\n * Emits GATEWAY_PROFILE_CHANGED event for SSE subscribers.\n */\n async setDefaultGatewayProfile(profileName: string): Promise<{ key: string; value: string }> {\n // Input sanitization\n const trimmedName = profileName?.trim();\n if (!trimmedName) {\n throw new BadRequestException('Profile name cannot be empty');\n }\n\n // Validate that profile exists\n const profile = await this.prisma.profile.findUnique({\n where: { name: trimmedName },\n });\n\n if (!profile) {\n throw new NotFoundException(`Profile \"${trimmedName}\" not found`);\n }\n\n const result = await this.setSetting(SETTING_KEYS.DEFAULT_GATEWAY_PROFILE, trimmedName);\n\n // Emit event for SSE subscribers (MCP Streamable HTTP notifications)\n this.eventEmitter.emit(GATEWAY_PROFILE_CHANGED, { profileName: trimmedName });\n\n return result;\n }\n\n /**\n * Get all settings with their current values\n */\n async getAllSettings(): Promise<Record<string, string>> {\n const settings = await this.prisma.gatewaySetting.findMany();\n\n // Start with defaults\n const result: Record<string, string> = { ...SETTING_DEFAULTS };\n\n // Override with stored values\n for (const setting of settings) {\n result[setting.key] = setting.value;\n }\n\n return result;\n }\n}\n"],"names":["BadRequestException","Injectable","NotFoundException","EventEmitter2","PrismaService","SETTING_DEFAULTS","SETTING_KEYS","GATEWAY_PROFILE_CHANGED","SettingsService","prisma","eventEmitter","getSetting","key","setting","gatewaySetting","findUnique","where","value","setSetting","upsert","update","create","getDefaultGatewayProfile","DEFAULT_GATEWAY_PROFILE","setDefaultGatewayProfile","profileName","trimmedName","trim","profile","name","result","emit","getAllSettings","settings","findMany"],"mappings":";;;;;;;;;AAAA;;;;;CAKC,GAED,SAASA,mBAAmB,EAAEC,UAAU,EAAEC,iBAAiB,QAAQ,iBAAiB;AACpF,SAASC,aAAa,QAAQ,wBAAwB;AACtD,SAASC,aAAa,QAAQ,gCAAgC;AAC9D,SAASC,gBAAgB,EAAEC,YAAY,QAAyB,0BAA0B;AAE1F,2CAA2C,GAC3C,OAAO,MAAMC,0BAA0B,0BAA0B;AAGjE,OAAO,MAAMC;IACX,YACE,AAAiBC,MAAqB,EACtC,AAAiBC,YAA2B,CAC5C;aAFiBD,SAAAA;aACAC,eAAAA;IAChB;IAEH;;GAEC,GACD,MAAMC,WAAWC,GAAe,EAAmB;QACjD,MAAMC,UAAU,MAAM,IAAI,CAACJ,MAAM,CAACK,cAAc,CAACC,UAAU,CAAC;YAC1DC,OAAO;gBAAEJ;YAAI;QACf;QAEA,OAAOC,SAASI,SAASZ,gBAAgB,CAACO,IAAI;IAChD;IAEA;;GAEC,GACD,MAAMM,WAAWN,GAAe,EAAEK,KAAa,EAA2C;QACxF,MAAMJ,UAAU,MAAM,IAAI,CAACJ,MAAM,CAACK,cAAc,CAACK,MAAM,CAAC;YACtDH,OAAO;gBAAEJ;YAAI;YACbQ,QAAQ;gBAAEH;YAAM;YAChBI,QAAQ;gBAAET;gBAAKK;YAAM;QACvB;QAEA,OAAO;YAAEL,KAAKC,QAAQD,GAAG;YAAEK,OAAOJ,QAAQI,KAAK;QAAC;IAClD;IAEA;;GAEC,GACD,MAAMK,2BAA4C;QAChD,OAAO,IAAI,CAACX,UAAU,CAACL,aAAaiB,uBAAuB;IAC7D;IAEA;;;;GAIC,GACD,MAAMC,yBAAyBC,WAAmB,EAA2C;QAC3F,qBAAqB;QACrB,MAAMC,cAAcD,aAAaE;QACjC,IAAI,CAACD,aAAa;YAChB,MAAM,IAAI1B,oBAAoB;QAChC;QAEA,+BAA+B;QAC/B,MAAM4B,UAAU,MAAM,IAAI,CAACnB,MAAM,CAACmB,OAAO,CAACb,UAAU,CAAC;YACnDC,OAAO;gBAAEa,MAAMH;YAAY;QAC7B;QAEA,IAAI,CAACE,SAAS;YACZ,MAAM,IAAI1B,kBAAkB,CAAC,SAAS,EAAEwB,YAAY,WAAW,CAAC;QAClE;QAEA,MAAMI,SAAS,MAAM,IAAI,CAACZ,UAAU,CAACZ,aAAaiB,uBAAuB,EAAEG;QAE3E,qEAAqE;QACrE,IAAI,CAAChB,YAAY,CAACqB,IAAI,CAACxB,yBAAyB;YAAEkB,aAAaC;QAAY;QAE3E,OAAOI;IACT;IAEA;;GAEC,GACD,MAAME,iBAAkD;QACtD,MAAMC,WAAW,MAAM,IAAI,CAACxB,MAAM,CAACK,cAAc,CAACoB,QAAQ;QAE1D,sBAAsB;QACtB,MAAMJ,SAAiC;YAAE,GAAGzB,gBAAgB;QAAC;QAE7D,8BAA8B;QAC9B,KAAK,MAAMQ,WAAWoB,SAAU;YAC9BH,MAAM,CAACjB,QAAQD,GAAG,CAAC,GAAGC,QAAQI,KAAK;QACrC;QAEA,OAAOa;IACT;AACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxheroes/local-mcp-backend",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "NestJS API server providing MCP proxy, server aggregation, OAuth 2.1, and profile management",
5
5
  "license": "Elastic-2.0",
6
6
  "type": "module",
@@ -10,6 +10,7 @@
10
10
  "@nestjs/common": "^11.1.11",
11
11
  "@nestjs/config": "^4.0.2",
12
12
  "@nestjs/core": "^11.1.11",
13
+ "@nestjs/event-emitter": "^3.0.1",
13
14
  "@nestjs/platform-express": "^11.1.11",
14
15
  "@nestjs/throttler": "^6.5.0",
15
16
  "class-transformer": "^0.5.1",
@@ -19,9 +20,9 @@
19
20
  "reflect-metadata": "^0.2.2",
20
21
  "rxjs": "^7.8.2",
21
22
  "zod": "^4.3.5",
22
- "@dxheroes/local-mcp-core": "0.3.3",
23
- "@dxheroes/mcp-gemini-deep-research": "0.3.3",
24
- "@dxheroes/local-mcp-database": "0.3.3"
23
+ "@dxheroes/local-mcp-core": "0.4.0",
24
+ "@dxheroes/mcp-gemini-deep-research": "0.4.0",
25
+ "@dxheroes/local-mcp-database": "0.4.0"
25
26
  },
26
27
  "devDependencies": {
27
28
  "@nestjs/cli": "^11.0.14",
@@ -34,7 +35,7 @@
34
35
  "@types/node": "^25.0.6",
35
36
  "typescript": "^5.9.3",
36
37
  "vitest": "^4.0.17",
37
- "@dxheroes/local-mcp-config": "0.3.3"
38
+ "@dxheroes/local-mcp-config": "0.4.0"
38
39
  },
39
40
  "scripts": {
40
41
  "build": "nest build",
package/src/AGENTS.md ADDED
@@ -0,0 +1,23 @@
1
+ # Backend Source Directory
2
+
3
+ ## Description
4
+
5
+ Root source directory for the NestJS backend application. Contains the main entry point, app module, configuration, common utilities, and all feature modules.
6
+
7
+ ## Contents
8
+
9
+ - `main.ts` - NestJS bootstrap and server initialization
10
+ - `app.module.ts` - Root module that imports all feature modules
11
+
12
+ ## Subdirectories
13
+
14
+ - **[config/](config/AGENTS.md)** - Application and database configuration
15
+ - **[common/](common/AGENTS.md)** - Shared utilities (filters, interceptors, decorators, pipes)
16
+ - **[modules/](modules/AGENTS.md)** - Feature modules (mcp, profiles, proxy, oauth, etc.)
17
+
18
+ ## Key Concepts
19
+
20
+ - NestJS follows modular architecture with dependency injection
21
+ - Each feature has its own module with controller, service, and DTOs
22
+ - Global modules (database) are imported in app.module.ts
23
+ - Configuration is loaded via @nestjs/config
package/src/app.module.ts CHANGED
@@ -7,6 +7,7 @@
7
7
 
8
8
  import { Module } from '@nestjs/common';
9
9
  import { ConfigModule } from '@nestjs/config';
10
+ import { EventEmitterModule } from '@nestjs/event-emitter';
10
11
  import { ThrottlerModule } from '@nestjs/throttler';
11
12
  import appConfig from './config/app.config.js';
12
13
  import databaseConfig from './config/database.config.js';
@@ -17,6 +18,7 @@ import { McpModule } from './modules/mcp/mcp.module.js';
17
18
  import { OAuthModule } from './modules/oauth/oauth.module.js';
18
19
  import { ProfilesModule } from './modules/profiles/profiles.module.js';
19
20
  import { ProxyModule } from './modules/proxy/proxy.module.js';
21
+ import { SettingsModule } from './modules/settings/settings.module.js';
20
22
 
21
23
  @Module({
22
24
  imports: [
@@ -27,6 +29,9 @@ import { ProxyModule } from './modules/proxy/proxy.module.js';
27
29
  envFilePath: ['../../.env', '.env.local', '.env'],
28
30
  }),
29
31
 
32
+ // Event emitter for SSE notifications
33
+ EventEmitterModule.forRoot(),
34
+
30
35
  // Rate limiting
31
36
  ThrottlerModule.forRoot([
32
37
  {
@@ -51,6 +56,7 @@ import { ProxyModule } from './modules/proxy/proxy.module.js';
51
56
  McpModule,
52
57
  ProfilesModule,
53
58
  OAuthModule, // For OAuth MCP servers, not user authentication
59
+ SettingsModule, // Gateway settings
54
60
  ProxyModule,
55
61
  HealthModule,
56
62
  DebugModule,
@@ -0,0 +1,19 @@
1
+ # Common Directory
2
+
3
+ ## Description
4
+
5
+ Shared utilities and cross-cutting concerns for the NestJS backend. Contains reusable decorators, filters, interceptors, and pipes.
6
+
7
+ ## Subdirectories
8
+
9
+ - `decorators/` - Custom decorators for route handlers
10
+ - `filters/` - Exception filters for error handling
11
+ - `interceptors/` - Request/response interceptors (logging, timeout)
12
+ - `pipes/` - Validation pipes for input transformation
13
+
14
+ ## Key Concepts
15
+
16
+ - Common utilities are used across all modules
17
+ - Filters handle uncaught exceptions globally
18
+ - Interceptors can modify request/response flow
19
+ - Pipes validate and transform incoming data
@@ -0,0 +1,17 @@
1
+ # Config Directory
2
+
3
+ ## Description
4
+
5
+ Application configuration files using @nestjs/config. Provides type-safe configuration for the NestJS backend.
6
+
7
+ ## Contents
8
+
9
+ - `app.config.ts` - General application configuration (port, environment)
10
+ - `database.config.ts` - Database/Prisma configuration (SQLite path)
11
+
12
+ ## Key Concepts
13
+
14
+ - Configuration is loaded at application startup
15
+ - Uses environment variables with defaults
16
+ - Database path defaults to `~/.local-mcp-gateway-data/local-mcp-gateway.db`
17
+ - Configuration is injected via ConfigService
@@ -0,0 +1,31 @@
1
+ # Modules Directory
2
+
3
+ ## Description
4
+
5
+ NestJS feature modules organized by domain. Each module encapsulates related functionality with its own controller, service, and DTOs.
6
+
7
+ ## Subdirectories
8
+
9
+ - **[database/](database/AGENTS.md)** - Prisma database service (global module)
10
+ - **[debug/](debug/AGENTS.md)** - Debug logging for MCP traffic
11
+ - **[health/](health/AGENTS.md)** - Health check endpoints
12
+ - **[mcp/](mcp/AGENTS.md)** - MCP server management, discovery, and registry
13
+ - **[oauth/](oauth/AGENTS.md)** - OAuth 2.1 authentication for MCP servers
14
+ - **[profiles/](profiles/AGENTS.md)** - Profile CRUD operations
15
+ - **[proxy/](proxy/AGENTS.md)** - MCP proxy endpoints (HTTP/SSE)
16
+ - **[settings/](settings/AGENTS.md)** - Application settings management
17
+
18
+ ## Module Structure Pattern
19
+
20
+ Each module typically contains:
21
+ - `*.module.ts` - Module definition with imports/providers/controllers
22
+ - `*.controller.ts` - REST API endpoints
23
+ - `*.service.ts` - Business logic and data access
24
+ - `dto/` - Data transfer objects (optional)
25
+
26
+ ## Key Concepts
27
+
28
+ - Modules are self-contained units with clear boundaries
29
+ - Services use Prisma for database access
30
+ - Controllers handle HTTP requests and validation
31
+ - Dependency injection connects all components
@@ -0,0 +1,30 @@
1
+ # Database Module
2
+
3
+ ## Description
4
+
5
+ Global database module providing Prisma ORM access to all other modules. Handles SQLite database connection and lifecycle.
6
+
7
+ ## Contents
8
+
9
+ - `database.module.ts` - Global module definition
10
+ - `prisma.service.ts` - Prisma client wrapper with lifecycle hooks
11
+
12
+ ## Key Concepts
13
+
14
+ - **Global Module**: Imported once in AppModule, available everywhere
15
+ - **PrismaService**: Extends PrismaClient with NestJS lifecycle integration
16
+ - **Connection Management**: Handles connect/disconnect on app start/stop
17
+ - **SQLite**: Default database stored in `~/.local-mcp-gateway-data/`
18
+
19
+ ## Usage
20
+
21
+ ```typescript
22
+ @Injectable()
23
+ export class MyService {
24
+ constructor(private readonly prisma: PrismaService) {}
25
+
26
+ async findAll() {
27
+ return this.prisma.myModel.findMany();
28
+ }
29
+ }
30
+ ```
@@ -0,0 +1,30 @@
1
+ # Debug Module
2
+
3
+ ## Description
4
+
5
+ Debug logging module for capturing and querying MCP traffic. Records all requests/responses between AI clients and MCP servers.
6
+
7
+ ## Contents
8
+
9
+ - `debug.module.ts` - Module definition
10
+ - `debug.controller.ts` - REST API for debug log queries
11
+ - `debug.service.ts` - Debug log storage and retrieval
12
+
13
+ ## Key Endpoints
14
+
15
+ - `GET /api/debug/logs` - Query debug logs with filters
16
+ - `DELETE /api/debug/logs` - Clear debug logs
17
+
18
+ ## Key Concepts
19
+
20
+ - **Log Capture**: Records MCP requests and responses
21
+ - **Filtering**: Query by profile, server, type, status
22
+ - **Auto-Cleanup**: Configurable log retention
23
+ - **Real-time**: Supports polling for live monitoring
24
+
25
+ ## Debug Log Fields
26
+
27
+ - Profile and MCP server identification
28
+ - Request/response payloads
29
+ - Duration and status
30
+ - Timestamps
@@ -0,0 +1,22 @@
1
+ # Health Module
2
+
3
+ ## Description
4
+
5
+ Health check endpoints for monitoring application status. Used by container orchestrators and load balancers.
6
+
7
+ ## Contents
8
+
9
+ - `health.module.ts` - Module definition
10
+ - `health.controller.ts` - Health check endpoints
11
+
12
+ ## Key Endpoints
13
+
14
+ - `GET /health` - Basic health check
15
+ - `GET /health/ready` - Readiness probe (database connection)
16
+
17
+ ## Key Concepts
18
+
19
+ - **Liveness**: Application is running
20
+ - **Readiness**: Application can serve traffic
21
+ - **Docker**: Used by Docker health checks
22
+ - **Kubernetes**: Compatible with K8s probes
@@ -0,0 +1,38 @@
1
+ # MCP Module
2
+
3
+ ## Description
4
+
5
+ Core MCP server management module. Handles MCP server CRUD, auto-discovery of MCP packages, and the in-memory registry.
6
+
7
+ ## Contents
8
+
9
+ - `mcp.module.ts` - Module definition with all MCP services
10
+ - `mcp.controller.ts` - REST API for MCP server management
11
+ - `mcp.service.ts` - MCP server CRUD and connection management
12
+ - `mcp-discovery.service.ts` - Auto-discovery of MCP packages from `mcp-servers/`
13
+ - `mcp-registry.ts` - In-memory registry of discovered MCP packages
14
+ - `mcp-seed.service.ts` - Database seeding for discovered packages
15
+ - `dto/` - Data transfer objects for API validation
16
+
17
+ ## Key Endpoints
18
+
19
+ - `GET /api/mcp-servers` - List all MCP servers
20
+ - `POST /api/mcp-servers` - Create new MCP server
21
+ - `GET /api/mcp-servers/:id` - Get MCP server details
22
+ - `PUT /api/mcp-servers/:id` - Update MCP server
23
+ - `DELETE /api/mcp-servers/:id` - Delete MCP server
24
+
25
+ ## Key Concepts
26
+
27
+ - **Server Types**: builtin, custom, remote_http, remote_sse, external
28
+ - **Auto-Discovery**: Scans `mcp-servers/` for packages with `mcpPackage: true`
29
+ - **Registry**: In-memory store of discovered package metadata
30
+ - **Seeding**: Creates database records for discovered packages on startup
31
+
32
+ ## MCP Package Discovery Flow
33
+
34
+ 1. `McpDiscoveryService.onModuleInit()` - Scan dependencies
35
+ 2. Filter packages with `mcpPackage: true` in package.json
36
+ 3. Import and validate McpPackage interface
37
+ 4. Register in `McpRegistry`
38
+ 5. `McpSeedService` creates database records
@@ -0,0 +1,32 @@
1
+ # OAuth Module
2
+
3
+ ## Description
4
+
5
+ OAuth 2.1 authentication module for MCP servers. Handles authorization flows for MCP servers that require OAuth credentials.
6
+
7
+ ## Contents
8
+
9
+ - `oauth.module.ts` - Module definition
10
+ - `oauth.controller.ts` - OAuth callback and token endpoints
11
+ - `oauth.service.ts` - OAuth flow management and token storage
12
+
13
+ ## Key Endpoints
14
+
15
+ - `GET /api/oauth/authorize/:serverId` - Initiate OAuth flow
16
+ - `GET /api/oauth/callback` - OAuth callback handler
17
+ - `POST /api/oauth/refresh/:serverId` - Refresh access token
18
+
19
+ ## Key Concepts
20
+
21
+ - **OAuth 2.1**: Modern OAuth with PKCE support
22
+ - **DCR**: Dynamic Client Registration for MCP servers
23
+ - **Token Storage**: Encrypted token storage in database
24
+ - **Auto-Refresh**: Automatic token refresh before expiry
25
+
26
+ ## OAuth Flow
27
+
28
+ 1. User initiates authorization for MCP server
29
+ 2. Redirect to provider's authorization endpoint
30
+ 3. Callback receives authorization code
31
+ 4. Exchange code for access/refresh tokens
32
+ 5. Store tokens for future MCP calls
@@ -0,0 +1,33 @@
1
+ # Profiles Module
2
+
3
+ ## Description
4
+
5
+ Profile management module. Profiles are logical groupings of MCP servers that can be accessed via a single endpoint.
6
+
7
+ ## Contents
8
+
9
+ - `profiles.module.ts` - Module definition
10
+ - `profiles.controller.ts` - REST API for profile CRUD
11
+ - `profiles.service.ts` - Profile business logic and tool aggregation
12
+
13
+ ## Key Endpoints
14
+
15
+ - `GET /api/profiles` - List all profiles
16
+ - `POST /api/profiles` - Create new profile
17
+ - `GET /api/profiles/:id` - Get profile details with MCP servers
18
+ - `PUT /api/profiles/:id` - Update profile
19
+ - `DELETE /api/profiles/:id` - Delete profile
20
+
21
+ ## Key Concepts
22
+
23
+ - **Profile**: Named collection of MCP servers
24
+ - **Endpoint**: Each profile gets `/api/mcp/{slug}` endpoint
25
+ - **Tool Aggregation**: Combines tools from all servers in profile
26
+ - **AI Prompt**: Generates TOON/Markdown prompts for AI integration
27
+ - **Default Profile**: System creates "default" profile on first run
28
+
29
+ ## Profile-MCP Relationship
30
+
31
+ - Profile has many MCP servers (many-to-many via ProfileMcpServer)
32
+ - Each assignment has order, enabled status, and tool filters
33
+ - Tools can be individually enabled/disabled per profile