From 82bd77c9bcb177a8a295c72ef375a748fbfa4380 Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Wed, 17 Sep 2025 20:05:26 +0200 Subject: [PATCH] Replace console logging with a proper logging system (Pino) --- mcp-server/package-lock.json | 255 ++++++++++++++++++++++++++++++ mcp-server/package.json | 4 +- mcp-server/src/PenpotMcpServer.ts | 10 +- mcp-server/src/PluginBridge.ts | 30 ++-- mcp-server/src/PluginTask.ts | 2 +- mcp-server/src/index.ts | 20 +-- mcp-server/src/logger.ts | 36 +++++ 7 files changed, 329 insertions(+), 28 deletions(-) create mode 100644 mcp-server/src/logger.ts diff --git a/mcp-server/package-lock.json b/mcp-server/package-lock.json index 52e63dd..dea5a97 100644 --- a/mcp-server/package-lock.json +++ b/mcp-server/package-lock.json @@ -14,6 +14,8 @@ "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "express": "^4.18.0", + "pino": "^9.10.0", + "pino-pretty": "^13.1.1", "reflect-metadata": "^0.1.13", "ws": "^8.18.0" }, @@ -975,6 +977,15 @@ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "license": "MIT" }, + "node_modules/atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/body-parser": { "version": "1.20.3", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", @@ -1080,6 +1091,12 @@ "validator": "^13.9.0" } }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "license": "MIT" + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -1148,6 +1165,15 @@ "node": ">= 8" } }, + "node_modules/dateformat": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", + "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==", + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -1213,6 +1239,15 @@ "node": ">= 0.8" } }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -1379,6 +1414,12 @@ "express": ">= 4.11" } }, + "node_modules/fast-copy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.2.tgz", + "integrity": "sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==", + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -1391,6 +1432,21 @@ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "license": "MIT" }, + "node_modules/fast-redact": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz", + "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "license": "MIT" + }, "node_modules/finalhandler": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", @@ -1509,6 +1565,12 @@ "node": ">= 0.4" } }, + "node_modules/help-me": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", + "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==", + "license": "MIT" + }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", @@ -1565,6 +1627,15 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "license": "ISC" }, + "node_modules/joycon": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -1652,6 +1723,15 @@ "node": ">= 0.6" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -1688,6 +1768,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/on-exit-leak-free": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -1733,6 +1822,67 @@ "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", "license": "MIT" }, + "node_modules/pino": { + "version": "9.10.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-9.10.0.tgz", + "integrity": "sha512-VOFxoNnxICtxaN8S3E73pR66c5MTFC+rwRcNRyHV/bV/c90dXvJqMfjkeRFsGBDXmlUN3LccJQPqGIufnaJePA==", + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0", + "fast-redact": "^3.1.1", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^2.0.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^5.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^4.0.1", + "thread-stream": "^3.0.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/pino-abstract-transport": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz", + "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==", + "license": "MIT", + "dependencies": { + "split2": "^4.0.0" + } + }, + "node_modules/pino-pretty": { + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-13.1.1.tgz", + "integrity": "sha512-TNNEOg0eA0u+/WuqH0MH0Xui7uqVk9D74ESOpjtebSQYbNWJk/dIxCXIxFsNfeN53JmtWqYHP2OrIZjT/CBEnA==", + "license": "MIT", + "dependencies": { + "colorette": "^2.0.7", + "dateformat": "^4.6.3", + "fast-copy": "^3.0.2", + "fast-safe-stringify": "^2.1.1", + "help-me": "^5.0.0", + "joycon": "^3.1.1", + "minimist": "^1.2.6", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^2.0.0", + "pump": "^3.0.0", + "secure-json-parse": "^4.0.0", + "sonic-boom": "^4.0.1", + "strip-json-comments": "^5.0.2" + }, + "bin": { + "pino-pretty": "bin.js" + } + }, + "node_modules/pino-std-serializers": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz", + "integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==", + "license": "MIT" + }, "node_modules/pkce-challenge": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz", @@ -1758,6 +1908,22 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/process-warning": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz", + "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -1771,6 +1937,16 @@ "node": ">= 0.10" } }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -1795,6 +1971,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", + "license": "MIT" + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -1818,6 +2000,15 @@ "node": ">= 0.10" } }, + "node_modules/real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", + "license": "MIT", + "engines": { + "node": ">= 12.13.0" + } + }, "node_modules/reflect-metadata": { "version": "0.1.14", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz", @@ -1893,11 +2084,36 @@ ], "license": "MIT" }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "node_modules/secure-json-parse": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-4.0.0.tgz", + "integrity": "sha512-dxtLJO6sc35jWidmLxo7ij+Eg48PM/kleBsxpC8QJE0qJICe+KawkDQmvCMZUr9u7WKVHgMW6vy3fQ7zMiFZMA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/send": { "version": "0.19.0", "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", @@ -2050,6 +2266,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sonic-boom": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz", + "integrity": "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==", + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -2058,6 +2292,27 @@ "node": ">= 0.8" } }, + "node_modules/strip-json-comments": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.3.tgz", + "integrity": "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==", + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/thread-stream": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz", + "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==", + "license": "MIT", + "dependencies": { + "real-require": "^0.2.0" + } + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", diff --git a/mcp-server/package.json b/mcp-server/package.json index 08fb6ad..5ae8b82 100644 --- a/mcp-server/package.json +++ b/mcp-server/package.json @@ -5,7 +5,7 @@ "type": "module", "main": "dist/index.js", "scripts": { - "build": "esbuild src/index.ts --bundle --platform=node --target=node18 --format=esm --outfile=dist/index.js --external:@modelcontextprotocol/* --external:ws --external:express --external:class-transformer --external:class-validator --external:reflect-metadata", + "build": "esbuild src/index.ts --bundle --platform=node --target=node18 --format=esm --outfile=dist/index.js --external:@modelcontextprotocol/* --external:ws --external:express --external:class-transformer --external:class-validator --external:reflect-metadata --external:pino --external:pino-pretty", "build:types": "tsc --emitDeclarationOnly --outDir dist", "build:full": "npm run build && npm run build:types", "start": "node dist/index.js", @@ -26,6 +26,8 @@ "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "express": "^4.18.0", + "pino": "^9.10.0", + "pino-pretty": "^13.1.1", "reflect-metadata": "^0.1.13", "ws": "^8.18.0" }, diff --git a/mcp-server/src/PenpotMcpServer.ts b/mcp-server/src/PenpotMcpServer.ts index 6c10629..d9ef53e 100644 --- a/mcp-server/src/PenpotMcpServer.ts +++ b/mcp-server/src/PenpotMcpServer.ts @@ -5,11 +5,13 @@ import { ToolInterface } from "./Tool"; import { HelloWorldTool } from "./tools/HelloWorldTool"; import { PrintTextTool } from "./tools/PrintTextTool"; import { PluginBridge } from "./PluginBridge"; +import { createLogger } from "./logger"; /** * Penpot MCP server implementation with HTTP and SSE Transport Support */ export class PenpotMcpServer { + private readonly logger = createLogger("PenpotMcpServer"); private readonly server: Server; private readonly tools: Map; private app: any; // Express app @@ -227,10 +229,10 @@ export class PenpotMcpServer { return new Promise((resolve) => { this.app.listen(this.port, () => { - console.error(`Penpot MCP Server started successfully on port ${this.port}`); - console.error(`Modern Streamable HTTP endpoint: http://localhost:${this.port}/mcp`); - console.error(`Legacy SSE endpoint: http://localhost:${this.port}/sse`); - console.error("WebSocket server is listening on ws://localhost:8080"); + this.logger.info(`Penpot MCP Server started successfully on port ${this.port}`); + this.logger.info(`Modern Streamable HTTP endpoint: http://localhost:${this.port}/mcp`); + this.logger.info(`Legacy SSE endpoint: http://localhost:${this.port}/sse`); + this.logger.info("WebSocket server is listening on ws://localhost:8080"); resolve(); }); }); diff --git a/mcp-server/src/PluginBridge.ts b/mcp-server/src/PluginBridge.ts index 172dbe0..ccd697e 100644 --- a/mcp-server/src/PluginBridge.ts +++ b/mcp-server/src/PluginBridge.ts @@ -1,11 +1,13 @@ -import {WebSocket, WebSocketServer} from "ws"; -import {PluginTask} from "./PluginTask"; -import {PluginTaskResponse, PluginTaskResult} from "@penpot-mcp/common"; +import { WebSocket, WebSocketServer } from "ws"; +import { PluginTask } from "./PluginTask"; +import { PluginTaskResponse, PluginTaskResult } from "@penpot-mcp/common"; +import { createLogger } from "./logger"; /** * Provides the connection to the Penpot MCP Plugin via WebSocket */ export class PluginBridge { + private readonly logger = createLogger("PluginBridge"); private readonly wsServer: WebSocketServer; private readonly connectedClients: Set = new Set(); private readonly pendingTasks: Map> = new Map(); @@ -27,31 +29,31 @@ export class PluginBridge { */ private setupWebSocketHandlers(): void { this.wsServer.on("connection", (ws: WebSocket) => { - console.error("New WebSocket connection established"); + this.logger.info("New WebSocket connection established"); this.connectedClients.add(ws); ws.on("message", (data: Buffer) => { - console.error("Received WebSocket message:", data.toString()); + this.logger.info("Received WebSocket message: %s", data.toString()); try { const response: PluginTaskResponse = JSON.parse(data.toString()); this.handlePluginTaskResponse(response); } catch (error) { - console.error("Failed to parse WebSocket message:", error); + this.logger.error(error, "Failed to parse WebSocket message"); } }); ws.on("close", () => { - console.error("WebSocket connection closed"); + this.logger.info("WebSocket connection closed"); this.connectedClients.delete(ws); }); ws.on("error", (error) => { - console.error("WebSocket connection error:", error); + this.logger.error(error, "WebSocket connection error"); this.connectedClients.delete(ws); }); }); - console.error("WebSocket server started on port 8080"); + this.logger.info("WebSocket server started on port 8080"); } /** @@ -65,7 +67,7 @@ export class PluginBridge { private handlePluginTaskResponse(response: PluginTaskResponse): void { const task = this.pendingTasks.get(response.id); if (!task) { - console.error(`Received response for unknown task ID: ${response.id}`); + this.logger.info(`Received response for unknown task ID: ${response.id}`); return; } @@ -85,7 +87,7 @@ export class PluginBridge { task.rejectWithError(error); } - console.error(`Task ${response.id} completed: success=${response.success}`); + this.logger.info(`Task ${response.id} completed: success=${response.success}`); } /** @@ -97,7 +99,9 @@ export class PluginBridge { * @param task - The plugin task to execute * @throws Error if no plugin instances are connected or available */ - public async executePluginTask>(task: PluginTask): Promise { + public async executePluginTask>( + task: PluginTask + ): Promise { // Check if there are connected clients if (this.connectedClients.size === 0) { throw new Error( @@ -143,7 +147,7 @@ export class PluginBridge { }, this.taskTimeoutSecs * 1000); this.taskTimeouts.set(task.id, timeoutHandle); - console.error(`Sent task ${task.id} to ${sentCount} connected clients`); + this.logger.info(`Sent task ${task.id} to ${sentCount} connected clients`); return await task.getResultPromise(); } diff --git a/mcp-server/src/PluginTask.ts b/mcp-server/src/PluginTask.ts index 3d047cc..d2d8d21 100644 --- a/mcp-server/src/PluginTask.ts +++ b/mcp-server/src/PluginTask.ts @@ -6,7 +6,7 @@ * * @template TParams - The strongly-typed parameters for this task */ -import { PluginTaskRequest, PluginTaskResult } from '@penpot-mcp/common'; +import { PluginTaskRequest, PluginTaskResult } from "@penpot-mcp/common"; import { randomUUID } from "crypto"; /** diff --git a/mcp-server/src/index.ts b/mcp-server/src/index.ts index cd41e46..8438eb1 100644 --- a/mcp-server/src/index.ts +++ b/mcp-server/src/index.ts @@ -1,6 +1,7 @@ #!/usr/bin/env node import { PenpotMcpServer } from "./PenpotMcpServer"; +import { createLogger } from "./logger"; /** * Entry point for Penpot MCP Server @@ -15,6 +16,7 @@ import { PenpotMcpServer } from "./PenpotMcpServer"; */ async function main(): Promise { + const logger = createLogger("main"); try { // Parse command line arguments for port configuration const args = process.argv.slice(2); @@ -27,14 +29,14 @@ async function main(): Promise { if (!isNaN(portArg) && portArg > 0 && portArg <= 65535) { port = portArg; } else { - console.error("Invalid port number. Using default port 4401."); + logger.info("Invalid port number. Using default port 4401."); } } } else if (args[i] === "--help" || args[i] === "-h") { - console.log("Usage: node dist/index.js [options]"); - console.log("Options:"); - console.log(" --port, -p Port number for the HTTP/SSE server (default: 4401)"); - console.log(" --help, -h Show this help message"); + logger.info("Usage: node dist/index.js [options]"); + logger.info("Options:"); + logger.info(" --port, -p Port number for the HTTP/SSE server (default: 4401)"); + logger.info(" --help, -h Show this help message"); process.exit(0); } } @@ -44,16 +46,16 @@ async function main(): Promise { // Keep the process alive process.on("SIGINT", () => { - console.error("Received SIGINT, shutting down gracefully..."); + logger.info("Received SIGINT, shutting down gracefully..."); process.exit(0); }); process.on("SIGTERM", () => { - console.error("Received SIGTERM, shutting down gracefully..."); + logger.info("Received SIGTERM, shutting down gracefully..."); process.exit(0); }); } catch (error) { - console.error("Failed to start MCP server:", error); + logger.error(error, "Failed to start MCP server"); process.exit(1); } } @@ -61,7 +63,7 @@ async function main(): Promise { // Start the server if this file is run directly if (import.meta.url.endsWith(process.argv[1]) || process.argv[1].endsWith("index.js")) { main().catch((error) => { - console.error("Unhandled error in main:", error); + createLogger("main").error(error, "Unhandled error in main"); process.exit(1); }); } diff --git a/mcp-server/src/logger.ts b/mcp-server/src/logger.ts new file mode 100644 index 0000000..bf6f730 --- /dev/null +++ b/mcp-server/src/logger.ts @@ -0,0 +1,36 @@ +import pino from "pino"; + +/** + * Logger instance configured for console output with metadata. + * + * Configured to output to console only with level, full timestamp, origin, and message. + */ +export const logger = pino({ + level: "info", + timestamp: pino.stdTimeFunctions.isoTime, + formatters: { + level: (label) => { + return { level: label }; + }, + }, + transport: { + target: "pino-pretty", + options: { + colorize: true, + translateTime: "SYS:yyyy-mm-dd HH:MM:ss.l", + ignore: "pid,hostname", + messageFormat: "{msg}", + levelFirst: true, + }, + }, +}); + +/** + * Creates a child logger with the specified name/origin. + * + * @param name - The name/origin identifier for the logger + * @returns Child logger instance with the specified name + */ +export function createLogger(name: string) { + return logger.child({ name }); +}