mcp-rubber-duck 1.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.
Files changed (184) hide show
  1. package/.dockerignore +19 -0
  2. package/.env.desktop.example +145 -0
  3. package/.env.example +45 -0
  4. package/.env.pi.example +106 -0
  5. package/.env.template +165 -0
  6. package/.eslintrc.json +40 -0
  7. package/.github/ISSUE_TEMPLATE/bug_report.md +65 -0
  8. package/.github/ISSUE_TEMPLATE/feature_request.md +58 -0
  9. package/.github/ISSUE_TEMPLATE/question.md +67 -0
  10. package/.github/pull_request_template.md +111 -0
  11. package/.github/workflows/docker-build.yml +138 -0
  12. package/.github/workflows/release.yml +182 -0
  13. package/.github/workflows/security.yml +141 -0
  14. package/.github/workflows/semantic-release.yml +89 -0
  15. package/.prettierrc +10 -0
  16. package/.releaserc.json +66 -0
  17. package/CHANGELOG.md +95 -0
  18. package/CONTRIBUTING.md +242 -0
  19. package/Dockerfile +62 -0
  20. package/LICENSE +21 -0
  21. package/README.md +803 -0
  22. package/audit-ci.json +8 -0
  23. package/config/claude_desktop.json +14 -0
  24. package/config/config.example.json +91 -0
  25. package/dist/config/config.d.ts +51 -0
  26. package/dist/config/config.d.ts.map +1 -0
  27. package/dist/config/config.js +301 -0
  28. package/dist/config/config.js.map +1 -0
  29. package/dist/config/types.d.ts +356 -0
  30. package/dist/config/types.d.ts.map +1 -0
  31. package/dist/config/types.js +41 -0
  32. package/dist/config/types.js.map +1 -0
  33. package/dist/index.d.ts +3 -0
  34. package/dist/index.d.ts.map +1 -0
  35. package/dist/index.js +109 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/providers/duck-provider-enhanced.d.ts +29 -0
  38. package/dist/providers/duck-provider-enhanced.d.ts.map +1 -0
  39. package/dist/providers/duck-provider-enhanced.js +230 -0
  40. package/dist/providers/duck-provider-enhanced.js.map +1 -0
  41. package/dist/providers/enhanced-manager.d.ts +54 -0
  42. package/dist/providers/enhanced-manager.d.ts.map +1 -0
  43. package/dist/providers/enhanced-manager.js +217 -0
  44. package/dist/providers/enhanced-manager.js.map +1 -0
  45. package/dist/providers/manager.d.ts +28 -0
  46. package/dist/providers/manager.d.ts.map +1 -0
  47. package/dist/providers/manager.js +204 -0
  48. package/dist/providers/manager.js.map +1 -0
  49. package/dist/providers/provider.d.ts +29 -0
  50. package/dist/providers/provider.d.ts.map +1 -0
  51. package/dist/providers/provider.js +179 -0
  52. package/dist/providers/provider.js.map +1 -0
  53. package/dist/providers/types.d.ts +69 -0
  54. package/dist/providers/types.d.ts.map +1 -0
  55. package/dist/providers/types.js +2 -0
  56. package/dist/providers/types.js.map +1 -0
  57. package/dist/server.d.ts +24 -0
  58. package/dist/server.d.ts.map +1 -0
  59. package/dist/server.js +501 -0
  60. package/dist/server.js.map +1 -0
  61. package/dist/services/approval.d.ts +44 -0
  62. package/dist/services/approval.d.ts.map +1 -0
  63. package/dist/services/approval.js +159 -0
  64. package/dist/services/approval.js.map +1 -0
  65. package/dist/services/cache.d.ts +21 -0
  66. package/dist/services/cache.d.ts.map +1 -0
  67. package/dist/services/cache.js +63 -0
  68. package/dist/services/cache.js.map +1 -0
  69. package/dist/services/conversation.d.ts +24 -0
  70. package/dist/services/conversation.d.ts.map +1 -0
  71. package/dist/services/conversation.js +108 -0
  72. package/dist/services/conversation.js.map +1 -0
  73. package/dist/services/function-bridge.d.ts +41 -0
  74. package/dist/services/function-bridge.d.ts.map +1 -0
  75. package/dist/services/function-bridge.js +259 -0
  76. package/dist/services/function-bridge.js.map +1 -0
  77. package/dist/services/health.d.ts +17 -0
  78. package/dist/services/health.d.ts.map +1 -0
  79. package/dist/services/health.js +77 -0
  80. package/dist/services/health.js.map +1 -0
  81. package/dist/services/mcp-client-manager.d.ts +49 -0
  82. package/dist/services/mcp-client-manager.d.ts.map +1 -0
  83. package/dist/services/mcp-client-manager.js +279 -0
  84. package/dist/services/mcp-client-manager.js.map +1 -0
  85. package/dist/tools/approve-mcp-request.d.ts +9 -0
  86. package/dist/tools/approve-mcp-request.d.ts.map +1 -0
  87. package/dist/tools/approve-mcp-request.js +111 -0
  88. package/dist/tools/approve-mcp-request.js.map +1 -0
  89. package/dist/tools/ask-duck.d.ts +9 -0
  90. package/dist/tools/ask-duck.d.ts.map +1 -0
  91. package/dist/tools/ask-duck.js +43 -0
  92. package/dist/tools/ask-duck.js.map +1 -0
  93. package/dist/tools/chat-duck.d.ts +9 -0
  94. package/dist/tools/chat-duck.d.ts.map +1 -0
  95. package/dist/tools/chat-duck.js +57 -0
  96. package/dist/tools/chat-duck.js.map +1 -0
  97. package/dist/tools/clear-conversations.d.ts +8 -0
  98. package/dist/tools/clear-conversations.d.ts.map +1 -0
  99. package/dist/tools/clear-conversations.js +17 -0
  100. package/dist/tools/clear-conversations.js.map +1 -0
  101. package/dist/tools/compare-ducks.d.ts +8 -0
  102. package/dist/tools/compare-ducks.d.ts.map +1 -0
  103. package/dist/tools/compare-ducks.js +49 -0
  104. package/dist/tools/compare-ducks.js.map +1 -0
  105. package/dist/tools/duck-council.d.ts +8 -0
  106. package/dist/tools/duck-council.d.ts.map +1 -0
  107. package/dist/tools/duck-council.js +69 -0
  108. package/dist/tools/duck-council.js.map +1 -0
  109. package/dist/tools/get-pending-approvals.d.ts +15 -0
  110. package/dist/tools/get-pending-approvals.d.ts.map +1 -0
  111. package/dist/tools/get-pending-approvals.js +74 -0
  112. package/dist/tools/get-pending-approvals.js.map +1 -0
  113. package/dist/tools/list-ducks.d.ts +9 -0
  114. package/dist/tools/list-ducks.d.ts.map +1 -0
  115. package/dist/tools/list-ducks.js +47 -0
  116. package/dist/tools/list-ducks.js.map +1 -0
  117. package/dist/tools/list-models.d.ts +8 -0
  118. package/dist/tools/list-models.d.ts.map +1 -0
  119. package/dist/tools/list-models.js +72 -0
  120. package/dist/tools/list-models.js.map +1 -0
  121. package/dist/tools/mcp-status.d.ts +17 -0
  122. package/dist/tools/mcp-status.d.ts.map +1 -0
  123. package/dist/tools/mcp-status.js +100 -0
  124. package/dist/tools/mcp-status.js.map +1 -0
  125. package/dist/utils/ascii-art.d.ts +19 -0
  126. package/dist/utils/ascii-art.d.ts.map +1 -0
  127. package/dist/utils/ascii-art.js +73 -0
  128. package/dist/utils/ascii-art.js.map +1 -0
  129. package/dist/utils/logger.d.ts +3 -0
  130. package/dist/utils/logger.d.ts.map +1 -0
  131. package/dist/utils/logger.js +86 -0
  132. package/dist/utils/logger.js.map +1 -0
  133. package/dist/utils/safe-logger.d.ts +23 -0
  134. package/dist/utils/safe-logger.d.ts.map +1 -0
  135. package/dist/utils/safe-logger.js +145 -0
  136. package/dist/utils/safe-logger.js.map +1 -0
  137. package/docker-compose.yml +161 -0
  138. package/jest.config.js +26 -0
  139. package/package.json +65 -0
  140. package/scripts/build-multiarch.sh +290 -0
  141. package/scripts/deploy-raspbian.sh +410 -0
  142. package/scripts/deploy.sh +322 -0
  143. package/scripts/gh-deploy.sh +343 -0
  144. package/scripts/setup-docker-raspbian.sh +530 -0
  145. package/server.json +8 -0
  146. package/src/config/config.ts +357 -0
  147. package/src/config/types.ts +89 -0
  148. package/src/index.ts +114 -0
  149. package/src/providers/duck-provider-enhanced.ts +294 -0
  150. package/src/providers/enhanced-manager.ts +290 -0
  151. package/src/providers/manager.ts +257 -0
  152. package/src/providers/provider.ts +207 -0
  153. package/src/providers/types.ts +78 -0
  154. package/src/server.ts +603 -0
  155. package/src/services/approval.ts +225 -0
  156. package/src/services/cache.ts +79 -0
  157. package/src/services/conversation.ts +146 -0
  158. package/src/services/function-bridge.ts +329 -0
  159. package/src/services/health.ts +107 -0
  160. package/src/services/mcp-client-manager.ts +362 -0
  161. package/src/tools/approve-mcp-request.ts +126 -0
  162. package/src/tools/ask-duck.ts +74 -0
  163. package/src/tools/chat-duck.ts +82 -0
  164. package/src/tools/clear-conversations.ts +24 -0
  165. package/src/tools/compare-ducks.ts +67 -0
  166. package/src/tools/duck-council.ts +88 -0
  167. package/src/tools/get-pending-approvals.ts +90 -0
  168. package/src/tools/list-ducks.ts +65 -0
  169. package/src/tools/list-models.ts +101 -0
  170. package/src/tools/mcp-status.ts +117 -0
  171. package/src/utils/ascii-art.ts +85 -0
  172. package/src/utils/logger.ts +116 -0
  173. package/src/utils/safe-logger.ts +165 -0
  174. package/systemd/mcp-rubber-duck-with-ollama.service +55 -0
  175. package/systemd/mcp-rubber-duck.service +58 -0
  176. package/test-functionality.js +147 -0
  177. package/test-mcp-interface.js +221 -0
  178. package/tests/ascii-art.test.ts +36 -0
  179. package/tests/config.test.ts +239 -0
  180. package/tests/conversation.test.ts +308 -0
  181. package/tests/mcp-bridge.test.ts +291 -0
  182. package/tests/providers.test.ts +269 -0
  183. package/tests/tools/clear-conversations.test.ts +163 -0
  184. package/tsconfig.json +26 -0
@@ -0,0 +1,3 @@
1
+ import winston from 'winston';
2
+ export declare const logger: winston.Logger;
3
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAkE9B,eAAO,MAAM,MAAM,gBAcjB,CAAC"}
@@ -0,0 +1,86 @@
1
+ import winston from 'winston';
2
+ import { existsSync, mkdirSync } from 'fs';
3
+ import { join, dirname } from 'path';
4
+ import { fileURLToPath } from 'url';
5
+ import { homedir } from 'os';
6
+ const logLevel = process.env.LOG_LEVEL || 'info';
7
+ const isMCP = process.env.MCP_SERVER === 'true' || process.argv.includes('--mcp');
8
+ // Determine logs directory based on environment and execution context
9
+ function getLogsDirectory() {
10
+ // Allow custom logs directory via environment variable
11
+ if (process.env.LOGS_DIR) {
12
+ return process.env.LOGS_DIR;
13
+ }
14
+ try {
15
+ // Try to use project directory when possible (development/direct execution)
16
+ const __filename = fileURLToPath(import.meta.url);
17
+ const __dirname = dirname(__filename);
18
+ const projectRoot = join(__dirname, '..', '..');
19
+ const projectLogsDir = join(projectRoot, 'logs');
20
+ // Check if we can write to project directory
21
+ if (existsSync(projectRoot)) {
22
+ return projectLogsDir;
23
+ }
24
+ }
25
+ catch (error) {
26
+ // Fall through to user directory if project root detection fails
27
+ }
28
+ // Fallback to user directory for MCP server or when project root isn't writable
29
+ return join(homedir(), '.mcp-rubber-duck', 'logs');
30
+ }
31
+ // Ensure logs directory exists
32
+ const logsDir = getLogsDirectory();
33
+ if (!existsSync(logsDir)) {
34
+ mkdirSync(logsDir, { recursive: true });
35
+ }
36
+ // Use simpler format for MCP to avoid interfering with JSON communication
37
+ const consoleFormat = isMCP
38
+ ? winston.format.simple()
39
+ : winston.format.combine(winston.format.colorize(), winston.format.simple());
40
+ // File format with more details for debugging crashes
41
+ const fileFormat = winston.format.combine(winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), winston.format.errors({ stack: true }), winston.format.splat(), winston.format.printf(({ timestamp, level, message, stack, ...meta }) => {
42
+ let log = `${String(timestamp)} [${String(level).toUpperCase()}]: ${String(message)}`;
43
+ if (stack && typeof stack === 'string') {
44
+ log += `\nStack: ${stack}`;
45
+ }
46
+ if (Object.keys(meta).length > 0) {
47
+ log += `\nMeta: ${JSON.stringify(meta, null, 2)}`;
48
+ }
49
+ return log;
50
+ }));
51
+ export const logger = winston.createLogger({
52
+ level: logLevel,
53
+ format: winston.format.combine(winston.format.timestamp(), winston.format.errors({ stack: true }), winston.format.splat(), winston.format.json()),
54
+ transports: [
55
+ new winston.transports.Console({
56
+ format: consoleFormat,
57
+ silent: isMCP, // Always silence console logs in MCP mode to avoid interfering with JSON-RPC
58
+ }),
59
+ ],
60
+ });
61
+ // Always add file logging for better crash diagnosis
62
+ const filePrefix = isMCP ? 'mcp' : 'server';
63
+ // Error log
64
+ logger.add(new winston.transports.File({
65
+ filename: join(logsDir, `${filePrefix}-error.log`),
66
+ level: 'error',
67
+ format: fileFormat,
68
+ maxsize: 10 * 1024 * 1024, // 10MB
69
+ maxFiles: 5,
70
+ }));
71
+ // Combined log
72
+ logger.add(new winston.transports.File({
73
+ filename: join(logsDir, `${filePrefix}-combined.log`),
74
+ format: fileFormat,
75
+ maxsize: 50 * 1024 * 1024, // 50MB
76
+ maxFiles: 3,
77
+ }));
78
+ // Crash log for fatal errors
79
+ logger.add(new winston.transports.File({
80
+ filename: join(logsDir, `${filePrefix}-crash.log`),
81
+ level: 'error',
82
+ format: fileFormat,
83
+ maxsize: 10 * 1024 * 1024, // 10MB
84
+ maxFiles: 10,
85
+ }));
86
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Sanitizes an object by redacting sensitive fields
3
+ */
4
+ declare function sanitizeObject(obj: unknown, maxDepth?: number, currentDepth?: number): unknown;
5
+ /**
6
+ * Safe logger that sanitizes sensitive data before logging
7
+ */
8
+ export declare class SafeLogger {
9
+ static debug(message: string, data?: unknown): void;
10
+ static info(message: string, data?: unknown): void;
11
+ static warn(message: string, data?: unknown): void;
12
+ static error(message: string, data?: unknown): void;
13
+ /**
14
+ * Sanitize arguments object specifically for MCP tool calls
15
+ */
16
+ static sanitizeToolArgs(args: unknown): unknown;
17
+ /**
18
+ * Create a safe message for approval requests
19
+ */
20
+ static createApprovalMessage(duckName: string, server: string, tool: string, args: unknown): string;
21
+ }
22
+ export { sanitizeObject };
23
+ //# sourceMappingURL=safe-logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"safe-logger.d.ts","sourceRoot":"","sources":["../../src/utils/safe-logger.ts"],"names":[],"mappings":"AA8BA;;GAEG;AACH,iBAAS,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,SAAI,EAAE,YAAY,SAAI,GAAG,OAAO,CAkD7E;AAED;;GAEG;AACH,qBAAa,UAAU;IACrB,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI;IASnD,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI;IASlD,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI;IASlD,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI;IASnD;;OAEG;IACH,MAAM,CAAC,gBAAgB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO;IAwB/C;;OAEG;IACH,MAAM,CAAC,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM;CAMpG;AAGD,OAAO,EAAE,cAAc,EAAE,CAAC"}
@@ -0,0 +1,145 @@
1
+ import { logger } from './logger.js';
2
+ // List of fields that should be sanitized in logs
3
+ const SENSITIVE_FIELDS = [
4
+ 'password',
5
+ 'apiKey',
6
+ 'api_key',
7
+ 'token',
8
+ 'secret',
9
+ 'auth',
10
+ 'authorization',
11
+ 'cookie',
12
+ 'session',
13
+ 'private_key',
14
+ 'privateKey',
15
+ 'client_secret',
16
+ 'clientSecret',
17
+ ];
18
+ // List of field patterns to look for
19
+ const SENSITIVE_PATTERNS = [
20
+ /password/i,
21
+ /secret/i,
22
+ /token/i,
23
+ /key$/i,
24
+ /auth/i,
25
+ /cookie/i,
26
+ /session/i,
27
+ ];
28
+ /**
29
+ * Sanitizes an object by redacting sensitive fields
30
+ */
31
+ function sanitizeObject(obj, maxDepth = 5, currentDepth = 0) {
32
+ if (currentDepth >= maxDepth) {
33
+ return '[Max depth exceeded]';
34
+ }
35
+ if (obj === null || obj === undefined) {
36
+ return obj;
37
+ }
38
+ if (typeof obj === 'string') {
39
+ // Check if the string looks like a sensitive value (e.g., long random strings)
40
+ if (obj.length > 20 && /^[a-zA-Z0-9+/=]{20,}$/.test(obj)) {
41
+ return `[REDACTED:${obj.length}chars]`;
42
+ }
43
+ return obj;
44
+ }
45
+ if (typeof obj === 'number' || typeof obj === 'boolean') {
46
+ return obj;
47
+ }
48
+ if (Array.isArray(obj)) {
49
+ return obj.map((item) => sanitizeObject(item, maxDepth, currentDepth + 1));
50
+ }
51
+ if (typeof obj === 'object') {
52
+ const sanitized = {};
53
+ for (const [key, value] of Object.entries(obj)) {
54
+ const keyLower = key.toLowerCase();
55
+ // Check if the field name is sensitive
56
+ const isSensitive = SENSITIVE_FIELDS.includes(keyLower) ||
57
+ SENSITIVE_PATTERNS.some(pattern => pattern.test(key));
58
+ if (isSensitive) {
59
+ if (typeof value === 'string' && value.length > 0) {
60
+ sanitized[key] = `[REDACTED:${value.length}chars]`;
61
+ }
62
+ else {
63
+ sanitized[key] = '[REDACTED]';
64
+ }
65
+ }
66
+ else {
67
+ sanitized[key] = sanitizeObject(value, maxDepth, currentDepth + 1);
68
+ }
69
+ }
70
+ return sanitized;
71
+ }
72
+ return obj;
73
+ }
74
+ /**
75
+ * Safe logger that sanitizes sensitive data before logging
76
+ */
77
+ export class SafeLogger {
78
+ static debug(message, data) {
79
+ if (data) {
80
+ const sanitizedData = sanitizeObject(data);
81
+ logger.debug(message, sanitizedData);
82
+ }
83
+ else {
84
+ logger.debug(message);
85
+ }
86
+ }
87
+ static info(message, data) {
88
+ if (data) {
89
+ const sanitizedData = sanitizeObject(data);
90
+ logger.info(message, sanitizedData);
91
+ }
92
+ else {
93
+ logger.info(message);
94
+ }
95
+ }
96
+ static warn(message, data) {
97
+ if (data) {
98
+ const sanitizedData = sanitizeObject(data);
99
+ logger.warn(message, sanitizedData);
100
+ }
101
+ else {
102
+ logger.warn(message);
103
+ }
104
+ }
105
+ static error(message, data) {
106
+ if (data) {
107
+ const sanitizedData = sanitizeObject(data);
108
+ logger.error(message, sanitizedData);
109
+ }
110
+ else {
111
+ logger.error(message);
112
+ }
113
+ }
114
+ /**
115
+ * Sanitize arguments object specifically for MCP tool calls
116
+ */
117
+ static sanitizeToolArgs(args) {
118
+ const sanitized = sanitizeObject(args);
119
+ // Additional sanitization for common patterns in tool arguments
120
+ if (typeof sanitized === 'object' && sanitized !== null) {
121
+ const objSanitized = sanitized;
122
+ // Sanitize file paths that might contain usernames
123
+ if (objSanitized.path && typeof objSanitized.path === 'string') {
124
+ objSanitized.path = objSanitized.path.replace(/\/Users\/[^/]+/, '/Users/[USER]');
125
+ objSanitized.path = objSanitized.path.replace(/\\Users\\[^\\]+/, '\\Users\\[USER]');
126
+ }
127
+ // Sanitize URLs that might contain credentials
128
+ if (objSanitized.url && typeof objSanitized.url === 'string') {
129
+ objSanitized.url = objSanitized.url.replace(/(https?:\/\/)([^:]+):([^@]+)@/, '$1[USER]:[REDACTED]@');
130
+ }
131
+ }
132
+ return sanitized;
133
+ }
134
+ /**
135
+ * Create a safe message for approval requests
136
+ */
137
+ static createApprovalMessage(duckName, server, tool, args) {
138
+ const sanitizedArgs = this.sanitizeToolArgs(args);
139
+ const argsStr = JSON.stringify(sanitizedArgs, null, 2);
140
+ return `Duck "${duckName}" wants to call ${server}:${tool} with arguments:\n${argsStr}`;
141
+ }
142
+ }
143
+ // Export the sanitize function for direct use
144
+ export { sanitizeObject };
145
+ //# sourceMappingURL=safe-logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"safe-logger.js","sourceRoot":"","sources":["../../src/utils/safe-logger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,161 @@
1
+ version: '3.8'
2
+
3
+ services:
4
+ mcp-rubber-duck:
5
+ # Multi-platform Docker image (AMD64, ARM64)
6
+ # Works on: macOS, Linux, Windows, Raspberry Pi 3+
7
+ image: ${DOCKER_IMAGE:-ghcr.io/nesquikm/mcp-rubber-duck:latest}
8
+ container_name: mcp-rubber-duck
9
+ init: true
10
+ restart: unless-stopped
11
+ profiles:
12
+ - default
13
+ - lightweight
14
+ - desktop
15
+
16
+ # Resource limits - adjust based on your platform
17
+ # Raspberry Pi: Use 'lightweight' profile (default settings below)
18
+ # Desktop/Server: Use 'desktop' profile with higher limits
19
+ deploy:
20
+ resources:
21
+ limits:
22
+ cpus: ${DOCKER_CPU_LIMIT:-1.5}
23
+ memory: ${DOCKER_MEMORY_LIMIT:-512M}
24
+ reservations:
25
+ memory: ${DOCKER_MEMORY_RESERVATION:-256M}
26
+
27
+ # Fallback for older docker-compose versions
28
+ mem_limit: ${DOCKER_MEMORY_LIMIT:-512m}
29
+ cpus: ${DOCKER_CPU_LIMIT:-1.5}
30
+
31
+ # Environment variables
32
+ environment:
33
+ # Node.js optimizations - adjust based on available memory
34
+ # Pi/lightweight: --max-old-space-size=256
35
+ # Desktop/server: --max-old-space-size=1024
36
+ - NODE_OPTIONS=${NODE_OPTIONS:---max-old-space-size=256}
37
+ - NODE_ENV=production
38
+
39
+ # MCP Server configuration
40
+ - MCP_SERVER=true
41
+
42
+ # Logging
43
+ - LOG_LEVEL=${LOG_LEVEL:-info}
44
+
45
+ # Provider configuration
46
+ - DEFAULT_PROVIDER=${DEFAULT_PROVIDER:-openai}
47
+ - DEFAULT_TEMPERATURE=${DEFAULT_TEMPERATURE:-0.7}
48
+
49
+ # OpenAI Provider
50
+ - OPENAI_API_KEY=${OPENAI_API_KEY}
51
+ - OPENAI_DEFAULT_MODEL=${OPENAI_DEFAULT_MODEL:-gpt-4o-mini}
52
+
53
+ # Google Gemini Provider
54
+ - GEMINI_API_KEY=${GEMINI_API_KEY}
55
+ - GEMINI_DEFAULT_MODEL=${GEMINI_DEFAULT_MODEL:-gemini-2.5-flash}
56
+
57
+ # Groq Provider
58
+ - GROQ_API_KEY=${GROQ_API_KEY}
59
+ - GROQ_DEFAULT_MODEL=${GROQ_DEFAULT_MODEL:-llama-3.3-70b-versatile}
60
+
61
+ # Together AI Provider
62
+ - TOGETHER_API_KEY=${TOGETHER_API_KEY}
63
+
64
+ # Perplexity Provider
65
+ - PERPLEXITY_API_KEY=${PERPLEXITY_API_KEY}
66
+
67
+ # Ollama (Local AI - requires 'with-ollama' profile)
68
+ - OLLAMA_BASE_URL=${OLLAMA_BASE_URL:-http://ollama:11434/v1}
69
+ - OLLAMA_DEFAULT_MODEL=${OLLAMA_DEFAULT_MODEL:-llama3.2}
70
+
71
+ # Custom providers (you can add multiple)
72
+ - CUSTOM_API_KEY=${CUSTOM_API_KEY}
73
+ - CUSTOM_BASE_URL=${CUSTOM_BASE_URL}
74
+ - CUSTOM_DEFAULT_MODEL=${CUSTOM_DEFAULT_MODEL}
75
+
76
+ # MCP Bridge Settings (Optional)
77
+ - MCP_BRIDGE_ENABLED=${MCP_BRIDGE_ENABLED:-false}
78
+ - MCP_APPROVAL_MODE=${MCP_APPROVAL_MODE:-trusted}
79
+ - MCP_APPROVAL_TIMEOUT=${MCP_APPROVAL_TIMEOUT:-300}
80
+
81
+ # Volume mounts for persistent data and configuration
82
+ volumes:
83
+ # Mount config directory (read-only)
84
+ - ./config:/app/config:ro
85
+ # Mount data directory for logs and cache
86
+ - ./data:/app/data
87
+ # Optional: Mount custom .env file
88
+ - ./.env:/app/.env:ro
89
+
90
+ # For MCP stdio communication
91
+ stdin_open: true
92
+ tty: true
93
+
94
+ # Logging configuration to prevent disk space issues
95
+ logging:
96
+ driver: "json-file"
97
+ options:
98
+ max-size: "10m"
99
+ max-file: "3"
100
+
101
+ # Health check configuration
102
+ healthcheck:
103
+ test: ["CMD", "node", "-e", "process.exit(0)"]
104
+ interval: 30s
105
+ timeout: 10s
106
+ retries: 3
107
+ start_period: 30s
108
+
109
+ # Network configuration (optional)
110
+ # ports:
111
+ # - "3000:3000" # Only uncomment if you need HTTP access
112
+
113
+ # Optional: Local Ollama service (works on all platforms)
114
+ # Enable with: docker-compose --profile with-ollama up -d
115
+ ollama:
116
+ image: ollama/ollama:latest
117
+ container_name: ollama
118
+ restart: unless-stopped
119
+ profiles:
120
+ - with-ollama
121
+
122
+ # Resource limits - adjust based on your platform
123
+ # Pi: Use default settings below (1GB limit)
124
+ # Desktop: Increase memory for better performance
125
+ deploy:
126
+ resources:
127
+ limits:
128
+ cpus: ${OLLAMA_CPU_LIMIT:-2.0}
129
+ memory: ${OLLAMA_MEMORY_LIMIT:-1G}
130
+ reservations:
131
+ memory: ${OLLAMA_MEMORY_RESERVATION:-512M}
132
+
133
+ # Fallback for older docker-compose versions
134
+ mem_limit: ${OLLAMA_MEMORY_LIMIT:-1g}
135
+ cpus: ${OLLAMA_CPU_LIMIT:-2.0}
136
+
137
+ ports:
138
+ - "11434:11434"
139
+
140
+ volumes:
141
+ - ollama_data:/root/.ollama
142
+
143
+ # Environment for ARM optimization
144
+ environment:
145
+ - OLLAMA_HOST=0.0.0.0
146
+ - OLLAMA_ORIGINS=*
147
+
148
+ logging:
149
+ driver: "json-file"
150
+ options:
151
+ max-size: "20m"
152
+ max-file: "3"
153
+
154
+ volumes:
155
+ ollama_data:
156
+ driver: local
157
+
158
+ # Networks (optional)
159
+ networks:
160
+ default:
161
+ name: mcp-rubber-duck-network
package/jest.config.js ADDED
@@ -0,0 +1,26 @@
1
+ /** @type {import('jest').Config} */
2
+ export default {
3
+ preset: 'ts-jest/presets/default-esm',
4
+ testEnvironment: 'node',
5
+ extensionsToTreatAsEsm: ['.ts'],
6
+ moduleNameMapper: {
7
+ '^(\\.{1,2}/.*)\\.js$': '$1',
8
+ },
9
+ transform: {
10
+ '^.+\\.tsx?$': [
11
+ 'ts-jest',
12
+ {
13
+ useESM: true,
14
+ },
15
+ ],
16
+ },
17
+ testMatch: ['**/tests/**/*.test.ts'],
18
+ collectCoverage: true,
19
+ coverageDirectory: 'coverage',
20
+ coverageReporters: ['text', 'lcov', 'html'],
21
+ collectCoverageFrom: [
22
+ 'src/**/*.ts',
23
+ '!src/**/*.d.ts',
24
+ '!src/index.ts',
25
+ ],
26
+ };
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "mcp-rubber-duck",
3
+ "version": "1.1.0",
4
+ "description": "An MCP server that bridges to multiple OpenAI-compatible LLMs - your AI rubber duck debugging panel",
5
+ "main": "dist/index.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "mcp-rubber-duck": "./dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "dev": "tsx watch src/index.ts",
13
+ "start": "node dist/index.js",
14
+ "test": "NODE_OPTIONS='--experimental-vm-modules' jest",
15
+ "test:watch": "NODE_OPTIONS='--experimental-vm-modules' jest --watch",
16
+ "lint": "eslint src --ext .ts",
17
+ "format": "prettier --write \"src/**/*.ts\"",
18
+ "typecheck": "tsc --noEmit",
19
+ "release": "semantic-release"
20
+ },
21
+ "keywords": [
22
+ "mcp",
23
+ "model-context-protocol",
24
+ "llm",
25
+ "openai",
26
+ "ai",
27
+ "rubber-duck"
28
+ ],
29
+ "mcpName": "io.github.nesquikm.rubber-duck",
30
+ "author": "nesquikm",
31
+ "license": "MIT",
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "git+https://github.com/nesquikm/mcp-rubber-duck.git"
35
+ },
36
+ "homepage": "https://github.com/nesquikm/mcp-rubber-duck#readme",
37
+ "bugs": {
38
+ "url": "https://github.com/nesquikm/mcp-rubber-duck/issues"
39
+ },
40
+ "dependencies": {
41
+ "@modelcontextprotocol/sdk": "^1.0.0",
42
+ "ajv": "^8.17.1",
43
+ "dotenv": "^16.4.0",
44
+ "node-cache": "^5.1.2",
45
+ "openai": "^4.0.0",
46
+ "winston": "^3.11.0",
47
+ "zod": "^3.23.0"
48
+ },
49
+ "devDependencies": {
50
+ "@semantic-release/changelog": "^6.0.3",
51
+ "@semantic-release/git": "^10.0.1",
52
+ "@semantic-release/github": "^11.0.6",
53
+ "@types/jest": "^29.0.0",
54
+ "@types/node": "^20.0.0",
55
+ "@typescript-eslint/eslint-plugin": "^6.0.0",
56
+ "@typescript-eslint/parser": "^6.0.0",
57
+ "eslint": "^8.0.0",
58
+ "jest": "^29.0.0",
59
+ "prettier": "^3.0.0",
60
+ "semantic-release": "^24.2.8",
61
+ "ts-jest": "^29.0.0",
62
+ "tsx": "^4.0.0",
63
+ "typescript": "^5.0.0"
64
+ }
65
+ }