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.
- package/.dockerignore +19 -0
- package/.env.desktop.example +145 -0
- package/.env.example +45 -0
- package/.env.pi.example +106 -0
- package/.env.template +165 -0
- package/.eslintrc.json +40 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +65 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +58 -0
- package/.github/ISSUE_TEMPLATE/question.md +67 -0
- package/.github/pull_request_template.md +111 -0
- package/.github/workflows/docker-build.yml +138 -0
- package/.github/workflows/release.yml +182 -0
- package/.github/workflows/security.yml +141 -0
- package/.github/workflows/semantic-release.yml +89 -0
- package/.prettierrc +10 -0
- package/.releaserc.json +66 -0
- package/CHANGELOG.md +95 -0
- package/CONTRIBUTING.md +242 -0
- package/Dockerfile +62 -0
- package/LICENSE +21 -0
- package/README.md +803 -0
- package/audit-ci.json +8 -0
- package/config/claude_desktop.json +14 -0
- package/config/config.example.json +91 -0
- package/dist/config/config.d.ts +51 -0
- package/dist/config/config.d.ts.map +1 -0
- package/dist/config/config.js +301 -0
- package/dist/config/config.js.map +1 -0
- package/dist/config/types.d.ts +356 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +41 -0
- package/dist/config/types.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +109 -0
- package/dist/index.js.map +1 -0
- package/dist/providers/duck-provider-enhanced.d.ts +29 -0
- package/dist/providers/duck-provider-enhanced.d.ts.map +1 -0
- package/dist/providers/duck-provider-enhanced.js +230 -0
- package/dist/providers/duck-provider-enhanced.js.map +1 -0
- package/dist/providers/enhanced-manager.d.ts +54 -0
- package/dist/providers/enhanced-manager.d.ts.map +1 -0
- package/dist/providers/enhanced-manager.js +217 -0
- package/dist/providers/enhanced-manager.js.map +1 -0
- package/dist/providers/manager.d.ts +28 -0
- package/dist/providers/manager.d.ts.map +1 -0
- package/dist/providers/manager.js +204 -0
- package/dist/providers/manager.js.map +1 -0
- package/dist/providers/provider.d.ts +29 -0
- package/dist/providers/provider.d.ts.map +1 -0
- package/dist/providers/provider.js +179 -0
- package/dist/providers/provider.js.map +1 -0
- package/dist/providers/types.d.ts +69 -0
- package/dist/providers/types.d.ts.map +1 -0
- package/dist/providers/types.js +2 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/server.d.ts +24 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +501 -0
- package/dist/server.js.map +1 -0
- package/dist/services/approval.d.ts +44 -0
- package/dist/services/approval.d.ts.map +1 -0
- package/dist/services/approval.js +159 -0
- package/dist/services/approval.js.map +1 -0
- package/dist/services/cache.d.ts +21 -0
- package/dist/services/cache.d.ts.map +1 -0
- package/dist/services/cache.js +63 -0
- package/dist/services/cache.js.map +1 -0
- package/dist/services/conversation.d.ts +24 -0
- package/dist/services/conversation.d.ts.map +1 -0
- package/dist/services/conversation.js +108 -0
- package/dist/services/conversation.js.map +1 -0
- package/dist/services/function-bridge.d.ts +41 -0
- package/dist/services/function-bridge.d.ts.map +1 -0
- package/dist/services/function-bridge.js +259 -0
- package/dist/services/function-bridge.js.map +1 -0
- package/dist/services/health.d.ts +17 -0
- package/dist/services/health.d.ts.map +1 -0
- package/dist/services/health.js +77 -0
- package/dist/services/health.js.map +1 -0
- package/dist/services/mcp-client-manager.d.ts +49 -0
- package/dist/services/mcp-client-manager.d.ts.map +1 -0
- package/dist/services/mcp-client-manager.js +279 -0
- package/dist/services/mcp-client-manager.js.map +1 -0
- package/dist/tools/approve-mcp-request.d.ts +9 -0
- package/dist/tools/approve-mcp-request.d.ts.map +1 -0
- package/dist/tools/approve-mcp-request.js +111 -0
- package/dist/tools/approve-mcp-request.js.map +1 -0
- package/dist/tools/ask-duck.d.ts +9 -0
- package/dist/tools/ask-duck.d.ts.map +1 -0
- package/dist/tools/ask-duck.js +43 -0
- package/dist/tools/ask-duck.js.map +1 -0
- package/dist/tools/chat-duck.d.ts +9 -0
- package/dist/tools/chat-duck.d.ts.map +1 -0
- package/dist/tools/chat-duck.js +57 -0
- package/dist/tools/chat-duck.js.map +1 -0
- package/dist/tools/clear-conversations.d.ts +8 -0
- package/dist/tools/clear-conversations.d.ts.map +1 -0
- package/dist/tools/clear-conversations.js +17 -0
- package/dist/tools/clear-conversations.js.map +1 -0
- package/dist/tools/compare-ducks.d.ts +8 -0
- package/dist/tools/compare-ducks.d.ts.map +1 -0
- package/dist/tools/compare-ducks.js +49 -0
- package/dist/tools/compare-ducks.js.map +1 -0
- package/dist/tools/duck-council.d.ts +8 -0
- package/dist/tools/duck-council.d.ts.map +1 -0
- package/dist/tools/duck-council.js +69 -0
- package/dist/tools/duck-council.js.map +1 -0
- package/dist/tools/get-pending-approvals.d.ts +15 -0
- package/dist/tools/get-pending-approvals.d.ts.map +1 -0
- package/dist/tools/get-pending-approvals.js +74 -0
- package/dist/tools/get-pending-approvals.js.map +1 -0
- package/dist/tools/list-ducks.d.ts +9 -0
- package/dist/tools/list-ducks.d.ts.map +1 -0
- package/dist/tools/list-ducks.js +47 -0
- package/dist/tools/list-ducks.js.map +1 -0
- package/dist/tools/list-models.d.ts +8 -0
- package/dist/tools/list-models.d.ts.map +1 -0
- package/dist/tools/list-models.js +72 -0
- package/dist/tools/list-models.js.map +1 -0
- package/dist/tools/mcp-status.d.ts +17 -0
- package/dist/tools/mcp-status.d.ts.map +1 -0
- package/dist/tools/mcp-status.js +100 -0
- package/dist/tools/mcp-status.js.map +1 -0
- package/dist/utils/ascii-art.d.ts +19 -0
- package/dist/utils/ascii-art.d.ts.map +1 -0
- package/dist/utils/ascii-art.js +73 -0
- package/dist/utils/ascii-art.js.map +1 -0
- package/dist/utils/logger.d.ts +3 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +86 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/safe-logger.d.ts +23 -0
- package/dist/utils/safe-logger.d.ts.map +1 -0
- package/dist/utils/safe-logger.js +145 -0
- package/dist/utils/safe-logger.js.map +1 -0
- package/docker-compose.yml +161 -0
- package/jest.config.js +26 -0
- package/package.json +65 -0
- package/scripts/build-multiarch.sh +290 -0
- package/scripts/deploy-raspbian.sh +410 -0
- package/scripts/deploy.sh +322 -0
- package/scripts/gh-deploy.sh +343 -0
- package/scripts/setup-docker-raspbian.sh +530 -0
- package/server.json +8 -0
- package/src/config/config.ts +357 -0
- package/src/config/types.ts +89 -0
- package/src/index.ts +114 -0
- package/src/providers/duck-provider-enhanced.ts +294 -0
- package/src/providers/enhanced-manager.ts +290 -0
- package/src/providers/manager.ts +257 -0
- package/src/providers/provider.ts +207 -0
- package/src/providers/types.ts +78 -0
- package/src/server.ts +603 -0
- package/src/services/approval.ts +225 -0
- package/src/services/cache.ts +79 -0
- package/src/services/conversation.ts +146 -0
- package/src/services/function-bridge.ts +329 -0
- package/src/services/health.ts +107 -0
- package/src/services/mcp-client-manager.ts +362 -0
- package/src/tools/approve-mcp-request.ts +126 -0
- package/src/tools/ask-duck.ts +74 -0
- package/src/tools/chat-duck.ts +82 -0
- package/src/tools/clear-conversations.ts +24 -0
- package/src/tools/compare-ducks.ts +67 -0
- package/src/tools/duck-council.ts +88 -0
- package/src/tools/get-pending-approvals.ts +90 -0
- package/src/tools/list-ducks.ts +65 -0
- package/src/tools/list-models.ts +101 -0
- package/src/tools/mcp-status.ts +117 -0
- package/src/utils/ascii-art.ts +85 -0
- package/src/utils/logger.ts +116 -0
- package/src/utils/safe-logger.ts +165 -0
- package/systemd/mcp-rubber-duck-with-ollama.service +55 -0
- package/systemd/mcp-rubber-duck.service +58 -0
- package/test-functionality.js +147 -0
- package/test-mcp-interface.js +221 -0
- package/tests/ascii-art.test.ts +36 -0
- package/tests/config.test.ts +239 -0
- package/tests/conversation.test.ts +308 -0
- package/tests/mcp-bridge.test.ts +291 -0
- package/tests/providers.test.ts +269 -0
- package/tests/tools/clear-conversations.test.ts +163 -0
- package/tsconfig.json +26 -0
|
@@ -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
|
+
}
|