remnote-mcp-server 0.1.3 → 0.3.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/dist/cli.js ADDED
@@ -0,0 +1,51 @@
1
+ import { Command } from 'commander';
2
+ import { createRequire } from 'module';
3
+ const require = createRequire(import.meta.url);
4
+ const packageJson = require('../package.json');
5
+ const validLogLevels = ['debug', 'info', 'warn', 'error'];
6
+ /**
7
+ * Parse CLI arguments and return typed options
8
+ */
9
+ export function parseCliArgs() {
10
+ const program = new Command();
11
+ program
12
+ .name('remnote-mcp-server')
13
+ .description('MCP server bridge for RemNote knowledge base')
14
+ .version(packageJson.version)
15
+ .option('--ws-port <number>', 'WebSocket port (default: 3002, env: REMNOTE_WS_PORT)', parsePort)
16
+ .option('--http-port <number>', 'HTTP MCP port (default: 3001, env: REMNOTE_HTTP_PORT)', parsePort)
17
+ .option('--log-level <level>', `Console log level: ${validLogLevels.join(', ')} (default: info)`, validateLogLevel)
18
+ .option('--log-level-file <level>', `File log level (default: same as --log-level)`, validateLogLevel)
19
+ .option('--verbose', 'Shorthand for --log-level debug')
20
+ .option('--log-file <path>', 'Log to file (default: console only)')
21
+ .option('--request-log <path>', 'Log all WebSocket requests to file (JSON Lines)')
22
+ .option('--response-log <path>', 'Log all WebSocket responses to file (JSON Lines)');
23
+ program.parse();
24
+ const options = program.opts();
25
+ // Validate port conflicts
26
+ if (options.wsPort && options.httpPort && options.wsPort === options.httpPort) {
27
+ console.error('Error: WebSocket port and HTTP port cannot be the same');
28
+ process.exit(1);
29
+ }
30
+ return options;
31
+ }
32
+ /**
33
+ * Parse and validate port number
34
+ */
35
+ function parsePort(value) {
36
+ const port = parseInt(value, 10);
37
+ if (isNaN(port) || port < 1 || port > 65535) {
38
+ throw new Error(`Invalid port number: ${value}. Must be between 1 and 65535.`);
39
+ }
40
+ return port;
41
+ }
42
+ /**
43
+ * Validate log level string
44
+ */
45
+ function validateLogLevel(value) {
46
+ if (!validLogLevels.includes(value.toLowerCase())) {
47
+ throw new Error(`Invalid log level: ${value}. Valid levels: ${validLogLevels.join(', ')}`);
48
+ }
49
+ return value.toLowerCase();
50
+ }
51
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAEvC,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,MAAM,WAAW,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAa/C,MAAM,cAAc,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;AAE1D;;GAEG;AACH,MAAM,UAAU,YAAY;IAC1B,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;IAE9B,OAAO;SACJ,IAAI,CAAC,oBAAoB,CAAC;SAC1B,WAAW,CAAC,8CAA8C,CAAC;SAC3D,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC;SAC5B,MAAM,CAAC,oBAAoB,EAAE,sDAAsD,EAAE,SAAS,CAAC;SAC/F,MAAM,CACL,sBAAsB,EACtB,uDAAuD,EACvD,SAAS,CACV;SACA,MAAM,CACL,qBAAqB,EACrB,sBAAsB,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,kBAAkB,EACjE,gBAAgB,CACjB;SACA,MAAM,CACL,0BAA0B,EAC1B,+CAA+C,EAC/C,gBAAgB,CACjB;SACA,MAAM,CAAC,WAAW,EAAE,iCAAiC,CAAC;SACtD,MAAM,CAAC,mBAAmB,EAAE,qCAAqC,CAAC;SAClE,MAAM,CAAC,sBAAsB,EAAE,iDAAiD,CAAC;SACjF,MAAM,CAAC,uBAAuB,EAAE,kDAAkD,CAAC,CAAC;IAEvF,OAAO,CAAC,KAAK,EAAE,CAAC;IAEhB,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,EAAc,CAAC;IAE3C,0BAA0B;IAC1B,IAAI,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,MAAM,KAAK,OAAO,CAAC,QAAQ,EAAE,CAAC;QAC9E,OAAO,CAAC,KAAK,CAAC,wDAAwD,CAAC,CAAC;QACxE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,SAAS,SAAS,CAAC,KAAa;IAC9B,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACjC,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,KAAK,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CAAC,wBAAwB,KAAK,gCAAgC,CAAC,CAAC;IACjF,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,SAAS,gBAAgB,CAAC,KAAa;IACrC,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;QAClD,MAAM,IAAI,KAAK,CAAC,sBAAsB,KAAK,mBAAmB,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC7F,CAAC;IACD,OAAO,KAAK,CAAC,WAAW,EAAE,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { CliOptions } from './cli.js';
2
+ export interface ServerConfig {
3
+ wsPort: number;
4
+ httpPort: number;
5
+ logLevel: string;
6
+ logLevelFile?: string;
7
+ logFile?: string;
8
+ requestLog?: string;
9
+ responseLog?: string;
10
+ prettyLogs: boolean;
11
+ }
12
+ /**
13
+ * Merge CLI options with environment variables and apply defaults
14
+ * Precedence: CLI > Environment Variables > Defaults
15
+ */
16
+ export declare function getConfig(cliOptions: CliOptions): ServerConfig;
package/dist/config.js ADDED
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Merge CLI options with environment variables and apply defaults
3
+ * Precedence: CLI > Environment Variables > Defaults
4
+ */
5
+ export function getConfig(cliOptions) {
6
+ // Apply verbose flag override
7
+ let logLevel = cliOptions.logLevel || 'info';
8
+ if (cliOptions.verbose) {
9
+ logLevel = 'debug';
10
+ }
11
+ // Validate CLI port ranges before merging
12
+ if (cliOptions.wsPort !== undefined && (cliOptions.wsPort < 1 || cliOptions.wsPort > 65535)) {
13
+ throw new Error(`Invalid WebSocket port: ${cliOptions.wsPort}. Must be between 1 and 65535.`);
14
+ }
15
+ if (cliOptions.httpPort !== undefined &&
16
+ (cliOptions.httpPort < 1 || cliOptions.httpPort > 65535)) {
17
+ throw new Error(`Invalid HTTP port: ${cliOptions.httpPort}. Must be between 1 and 65535.`);
18
+ }
19
+ // Get ports with CLI > env > default precedence
20
+ const wsPort = cliOptions.wsPort || parseInt(process.env.REMNOTE_WS_PORT || '3002', 10);
21
+ const httpPort = cliOptions.httpPort || parseInt(process.env.REMNOTE_HTTP_PORT || '3001', 10);
22
+ // Validate port conflicts
23
+ if (wsPort === httpPort) {
24
+ throw new Error(`WebSocket port and HTTP port cannot be the same (both set to ${wsPort})`);
25
+ }
26
+ // File log level defaults to console log level if not specified
27
+ const logLevelFile = cliOptions.logLevelFile || (cliOptions.logFile ? logLevel : undefined);
28
+ // Pretty logs in development (when using pino-pretty)
29
+ const prettyLogs = process.stdout.isTTY === true;
30
+ return {
31
+ wsPort,
32
+ httpPort,
33
+ logLevel,
34
+ logLevelFile,
35
+ logFile: cliOptions.logFile,
36
+ requestLog: cliOptions.requestLog,
37
+ responseLog: cliOptions.responseLog,
38
+ prettyLogs,
39
+ };
40
+ }
41
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAaA;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,UAAsB;IAC9C,8BAA8B;IAC9B,IAAI,QAAQ,GAAG,UAAU,CAAC,QAAQ,IAAI,MAAM,CAAC;IAC7C,IAAI,UAAU,CAAC,OAAO,EAAE,CAAC;QACvB,QAAQ,GAAG,OAAO,CAAC;IACrB,CAAC;IAED,0CAA0C;IAC1C,IAAI,UAAU,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,IAAI,UAAU,CAAC,MAAM,GAAG,KAAK,CAAC,EAAE,CAAC;QAC5F,MAAM,IAAI,KAAK,CAAC,2BAA2B,UAAU,CAAC,MAAM,gCAAgC,CAAC,CAAC;IAChG,CAAC;IACD,IACE,UAAU,CAAC,QAAQ,KAAK,SAAS;QACjC,CAAC,UAAU,CAAC,QAAQ,GAAG,CAAC,IAAI,UAAU,CAAC,QAAQ,GAAG,KAAK,CAAC,EACxD,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,sBAAsB,UAAU,CAAC,QAAQ,gCAAgC,CAAC,CAAC;IAC7F,CAAC;IAED,gDAAgD;IAChD,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,IAAI,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,MAAM,EAAE,EAAE,CAAC,CAAC;IACxF,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,IAAI,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,MAAM,EAAE,EAAE,CAAC,CAAC;IAE9F,0BAA0B;IAC1B,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,gEAAgE,MAAM,GAAG,CAAC,CAAC;IAC7F,CAAC;IAED,gEAAgE;IAChE,MAAM,YAAY,GAAG,UAAU,CAAC,YAAY,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAE5F,sDAAsD;IACtD,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,KAAK,IAAI,CAAC;IAEjD,OAAO;QACL,MAAM;QACN,QAAQ;QACR,QAAQ;QACR,YAAY;QACZ,OAAO,EAAE,UAAU,CAAC,OAAO;QAC3B,UAAU,EAAE,UAAU,CAAC,UAAU;QACjC,WAAW,EAAE,UAAU,CAAC,WAAW;QACnC,UAAU;KACX,CAAC;AACJ,CAAC"}
@@ -0,0 +1,23 @@
1
+ import { WebSocketServer } from './websocket-server.js';
2
+ import type { Logger } from './logger.js';
3
+ interface ServerInfo {
4
+ name: string;
5
+ version: string;
6
+ }
7
+ export declare class HttpMcpServer {
8
+ private app;
9
+ private server;
10
+ private port;
11
+ private wsServer;
12
+ private serverInfo;
13
+ private logger;
14
+ private transports;
15
+ constructor(port: number, wsServer: WebSocketServer, serverInfo: ServerInfo, logger: Logger);
16
+ private setupRoutes;
17
+ private initializeNewSession;
18
+ private handleSessionRequest;
19
+ start(): Promise<void>;
20
+ stop(): Promise<void>;
21
+ getActiveSessionCount(): number;
22
+ }
23
+ export {};
@@ -0,0 +1,197 @@
1
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
+ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
3
+ import express from 'express';
4
+ import { randomUUID } from 'crypto';
5
+ import { registerAllTools } from './tools/index.js';
6
+ export class HttpMcpServer {
7
+ app;
8
+ server = null;
9
+ port;
10
+ wsServer;
11
+ serverInfo;
12
+ logger;
13
+ transports = new Map();
14
+ constructor(port, wsServer, serverInfo, logger) {
15
+ this.port = port;
16
+ this.wsServer = wsServer;
17
+ this.serverInfo = serverInfo;
18
+ this.logger = logger.child({ context: 'http-server' });
19
+ // Create Express app with JSON parsing
20
+ this.app = express();
21
+ this.app.use(express.json());
22
+ // Route handlers
23
+ this.setupRoutes();
24
+ }
25
+ setupRoutes() {
26
+ // POST: Handle session initialization and requests
27
+ this.app.post('/mcp', async (req, res) => {
28
+ try {
29
+ const sessionId = req.headers['mcp-session-id'];
30
+ const body = req.body;
31
+ // Check if this is an initialize request
32
+ const isInitializeRequest = body && body.method === 'initialize' && body.jsonrpc === '2.0';
33
+ this.logger.debug({
34
+ sessionId: sessionId || 'none',
35
+ method: body?.method || 'unknown',
36
+ }, 'POST request received');
37
+ if (!sessionId && isInitializeRequest) {
38
+ // New session initialization
39
+ await this.initializeNewSession(req, res, body);
40
+ }
41
+ else if (sessionId) {
42
+ // Existing session request
43
+ await this.handleSessionRequest(sessionId, req, res, body);
44
+ }
45
+ else {
46
+ // Missing session ID for non-initialize request
47
+ res.status(400).json({
48
+ jsonrpc: '2.0',
49
+ error: {
50
+ code: -32600,
51
+ message: 'Missing mcp-session-id header for non-initialize request',
52
+ },
53
+ id: body?.id ?? null,
54
+ });
55
+ }
56
+ }
57
+ catch (error) {
58
+ this.logger.error({
59
+ error,
60
+ sessionId: req.headers['mcp-session-id'],
61
+ }, 'Error handling POST request');
62
+ res.status(500).json({
63
+ jsonrpc: '2.0',
64
+ error: {
65
+ code: -32603,
66
+ message: error instanceof Error ? error.message : String(error),
67
+ },
68
+ id: null,
69
+ });
70
+ }
71
+ });
72
+ // GET: SSE stream for session notifications
73
+ this.app.get('/mcp', async (req, res) => {
74
+ const sessionId = req.headers['mcp-session-id'];
75
+ this.logger.debug({ sessionId: sessionId || 'none' }, 'SSE stream opened');
76
+ if (!sessionId) {
77
+ res.status(400).json({ error: 'Missing mcp-session-id header' });
78
+ return;
79
+ }
80
+ const transport = this.transports.get(sessionId);
81
+ if (!transport) {
82
+ res.status(404).json({ error: `Session not found: ${sessionId}` });
83
+ return;
84
+ }
85
+ // Let transport handle SSE stream setup
86
+ await transport.handleRequest(req, res);
87
+ });
88
+ // DELETE: Terminate session
89
+ this.app.delete('/mcp', async (req, res) => {
90
+ const sessionId = req.headers['mcp-session-id'];
91
+ this.logger.debug({ sessionId: sessionId || 'none' }, 'Session termination requested');
92
+ if (!sessionId) {
93
+ res.status(400).json({ error: 'Missing mcp-session-id header' });
94
+ return;
95
+ }
96
+ const transport = this.transports.get(sessionId);
97
+ if (!transport) {
98
+ res.status(404).json({ error: `Session not found: ${sessionId}` });
99
+ return;
100
+ }
101
+ // Let transport handle session termination
102
+ await transport.handleRequest(req, res);
103
+ });
104
+ }
105
+ async initializeNewSession(req, res, body) {
106
+ // Create new transport with session ID generator
107
+ const transport = new StreamableHTTPServerTransport({
108
+ sessionIdGenerator: () => randomUUID(),
109
+ onsessioninitialized: (sessionId) => {
110
+ this.transports.set(sessionId, transport);
111
+ this.logger.info({ sessionId }, 'New MCP session initialized');
112
+ },
113
+ });
114
+ // Set up onclose handler to clean up transport when closed
115
+ transport.onclose = () => {
116
+ const sessionId = transport.sessionId;
117
+ if (sessionId && this.transports.has(sessionId)) {
118
+ this.logger.info({ sessionId }, 'MCP session closed');
119
+ this.transports.delete(sessionId);
120
+ }
121
+ };
122
+ // Create new MCP server instance for this session
123
+ const server = new Server(this.serverInfo, {
124
+ capabilities: {
125
+ tools: {},
126
+ },
127
+ });
128
+ // Register all tools with the shared WebSocket server
129
+ registerAllTools(server, this.wsServer, this.logger);
130
+ // Connect server to transport
131
+ await server.connect(transport);
132
+ // Handle the initialize request
133
+ await transport.handleRequest(req, res, body);
134
+ }
135
+ async handleSessionRequest(sessionId, req, res, body) {
136
+ const transport = this.transports.get(sessionId);
137
+ if (!transport) {
138
+ res.status(400).json({
139
+ jsonrpc: '2.0',
140
+ error: {
141
+ code: -32600,
142
+ message: `Invalid session ID: ${sessionId}`,
143
+ },
144
+ id: body?.id ?? null,
145
+ });
146
+ return;
147
+ }
148
+ await transport.handleRequest(req, res, body);
149
+ }
150
+ async start() {
151
+ return new Promise((resolve, reject) => {
152
+ try {
153
+ this.server = this.app.listen(this.port, () => {
154
+ this.logger.info({ port: this.port }, 'HTTP server started');
155
+ resolve();
156
+ });
157
+ this.server.on('error', (error) => {
158
+ this.logger.error({ error }, 'HTTP server error');
159
+ reject(error);
160
+ });
161
+ }
162
+ catch (error) {
163
+ reject(error);
164
+ }
165
+ });
166
+ }
167
+ async stop() {
168
+ // Close all active transports
169
+ for (const [sessionId, transport] of this.transports.entries()) {
170
+ try {
171
+ this.logger.debug({ sessionId }, 'Closing MCP session');
172
+ await transport.close();
173
+ this.transports.delete(sessionId);
174
+ }
175
+ catch (error) {
176
+ this.logger.error({ sessionId, error }, 'Error closing session');
177
+ }
178
+ }
179
+ // Close HTTP server
180
+ return new Promise((resolve) => {
181
+ if (this.server) {
182
+ this.server.close(() => {
183
+ this.logger.info('HTTP server stopped');
184
+ this.server = null;
185
+ resolve();
186
+ });
187
+ }
188
+ else {
189
+ resolve();
190
+ }
191
+ });
192
+ }
193
+ getActiveSessionCount() {
194
+ return this.transports.size;
195
+ }
196
+ }
197
+ //# sourceMappingURL=http-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-server.js","sourceRoot":"","sources":["../src/http-server.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,51 +1,74 @@
1
1
  #!/usr/bin/env node
2
- import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
- import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
2
  import { createRequire } from 'module';
5
3
  import { WebSocketServer } from './websocket-server.js';
6
- import { registerAllTools } from './tools/index.js';
4
+ import { HttpMcpServer } from './http-server.js';
5
+ import { parseCliArgs } from './cli.js';
6
+ import { getConfig } from './config.js';
7
+ import { createLogger, ensureLogDirectory, createRequestResponseLogger } from './logger.js';
7
8
  const require = createRequire(import.meta.url);
8
9
  const packageJson = require('../package.json');
9
- const WS_PORT = parseInt(process.env.REMNOTE_WS_PORT || '3002', 10);
10
10
  async function main() {
11
- // Initialize MCP server
12
- const mcpServer = new Server({
13
- name: 'remnote-mcp-server',
14
- version: packageJson.version,
15
- }, {
16
- capabilities: {
17
- tools: {},
18
- },
11
+ // Parse CLI arguments and merge with environment variables
12
+ const cliOptions = parseCliArgs();
13
+ const config = getConfig(cliOptions);
14
+ // Ensure log directories exist
15
+ if (config.logFile) {
16
+ await ensureLogDirectory(config.logFile);
17
+ }
18
+ if (config.requestLog) {
19
+ await ensureLogDirectory(config.requestLog);
20
+ }
21
+ if (config.responseLog) {
22
+ await ensureLogDirectory(config.responseLog);
23
+ }
24
+ // Create logger
25
+ const logger = createLogger({
26
+ consoleLevel: config.logLevel,
27
+ fileLevel: config.logLevelFile,
28
+ filePath: config.logFile,
29
+ pretty: config.prettyLogs,
19
30
  });
31
+ // Create request/response loggers if configured
32
+ const requestLogger = config.requestLog
33
+ ? createRequestResponseLogger(config.requestLog)
34
+ : undefined;
35
+ const responseLogger = config.responseLog
36
+ ? createRequestResponseLogger(config.responseLog)
37
+ : undefined;
20
38
  // Initialize WebSocket server for RemNote plugin
21
- const wsServer = new WebSocketServer(WS_PORT);
22
- // Log connection status to stderr (stdio reserved for MCP)
39
+ const wsServer = new WebSocketServer(config.wsPort, logger, requestLogger, responseLogger);
40
+ // Log connection status
23
41
  wsServer.onClientConnect(() => {
24
- console.error('[RemNote Bridge] RemNote plugin connected');
42
+ logger.info('RemNote plugin connected');
25
43
  });
26
44
  wsServer.onClientDisconnect(() => {
27
- console.error('[RemNote Bridge] RemNote plugin disconnected');
45
+ logger.info('RemNote plugin disconnected');
28
46
  });
29
47
  // Start WebSocket server
30
48
  await wsServer.start();
31
- console.error(`[WebSocket Server] Listening on port ${WS_PORT}`);
32
- // Register all RemNote MCP tools
33
- registerAllTools(mcpServer, wsServer);
34
- // Set up stdio transport for MCP
35
- const transport = new StdioServerTransport();
36
- await mcpServer.connect(transport);
37
- console.error('[MCP Server] Server started on stdio');
49
+ // Initialize HTTP MCP server
50
+ const httpServer = new HttpMcpServer(config.httpPort, wsServer, {
51
+ name: 'remnote-mcp-server',
52
+ version: packageJson.version,
53
+ }, logger);
54
+ await httpServer.start();
55
+ // Log startup message
56
+ logger.info({
57
+ wsPort: config.wsPort,
58
+ httpPort: config.httpPort,
59
+ }, `RemNote MCP Server v${packageJson.version} listening`);
38
60
  // Graceful shutdown
39
61
  const shutdown = async () => {
40
- console.error('[MCP Server] Shutting down...');
62
+ logger.info('Shutting down');
63
+ await httpServer.stop();
41
64
  await wsServer.stop();
42
- await mcpServer.close();
43
65
  process.exit(0);
44
66
  };
45
67
  process.on('SIGINT', shutdown);
46
68
  process.on('SIGTERM', shutdown);
47
69
  }
48
70
  main().catch((error) => {
71
+ // Pre-logger error handling
49
72
  console.error('[MCP Server] Fatal error:', error);
50
73
  process.exit(1);
51
74
  });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEpD,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,MAAM,WAAW,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAE/C,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,MAAM,EAAE,EAAE,CAAC,CAAC;AAEpE,KAAK,UAAU,IAAI;IACjB,wBAAwB;IACxB,MAAM,SAAS,GAAG,IAAI,MAAM,CAC1B;QACE,IAAI,EAAE,oBAAoB;QAC1B,OAAO,EAAE,WAAW,CAAC,OAAO;KAC7B,EACD;QACE,YAAY,EAAE;YACZ,KAAK,EAAE,EAAE;SACV;KACF,CACF,CAAC;IAEF,iDAAiD;IACjD,MAAM,QAAQ,GAAG,IAAI,eAAe,CAAC,OAAO,CAAC,CAAC;IAE9C,2DAA2D;IAC3D,QAAQ,CAAC,eAAe,CAAC,GAAG,EAAE;QAC5B,OAAO,CAAC,KAAK,CAAC,2CAA2C,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,kBAAkB,CAAC,GAAG,EAAE;QAC/B,OAAO,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;IAEH,yBAAyB;IACzB,MAAM,QAAQ,CAAC,KAAK,EAAE,CAAC;IACvB,OAAO,CAAC,KAAK,CAAC,wCAAwC,OAAO,EAAE,CAAC,CAAC;IAEjE,iCAAiC;IACjC,gBAAgB,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAEtC,iCAAiC;IACjC,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAEnC,OAAO,CAAC,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAEtD,oBAAoB;IACpB,MAAM,QAAQ,GAAG,KAAK,IAAI,EAAE;QAC1B,OAAO,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC;QAC/C,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACtB,MAAM,SAAS,CAAC,KAAK,EAAE,CAAC;QACxB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC;IAEF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AAClC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,2BAA2B,EAAE,KAAK,CAAC,CAAC;IAClD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import pino from 'pino';
2
+ export type { Logger } from 'pino';
3
+ export interface LoggerConfig {
4
+ consoleLevel: string;
5
+ fileLevel?: string;
6
+ filePath?: string;
7
+ pretty?: boolean;
8
+ }
9
+ /**
10
+ * Create a Pino logger with the specified configuration
11
+ */
12
+ export declare function createLogger(config: LoggerConfig): pino.Logger;
13
+ /**
14
+ * Create a logger for request/response logging (JSON Lines format)
15
+ */
16
+ export declare function createRequestResponseLogger(filePath: string): pino.Logger;
17
+ /**
18
+ * Ensure directory exists for log file
19
+ */
20
+ export declare function ensureLogDirectory(filePath: string): Promise<void>;
package/dist/logger.js ADDED
@@ -0,0 +1,85 @@
1
+ import pino from 'pino';
2
+ import { mkdir } from 'fs/promises';
3
+ import { dirname } from 'path';
4
+ /**
5
+ * Create a Pino logger with the specified configuration
6
+ */
7
+ export function createLogger(config) {
8
+ const targets = [];
9
+ // Console transport
10
+ if (config.pretty) {
11
+ targets.push({
12
+ level: config.consoleLevel,
13
+ target: 'pino-pretty',
14
+ options: {
15
+ colorize: true,
16
+ translateTime: 'HH:MM:ss.l',
17
+ ignore: 'pid,hostname',
18
+ },
19
+ });
20
+ }
21
+ else {
22
+ targets.push({
23
+ level: config.consoleLevel,
24
+ target: 'pino/file',
25
+ options: { destination: 2 }, // stderr
26
+ });
27
+ }
28
+ // File transport (if configured)
29
+ if (config.filePath && config.fileLevel) {
30
+ targets.push({
31
+ level: config.fileLevel,
32
+ target: 'pino/file',
33
+ options: { destination: config.filePath },
34
+ });
35
+ }
36
+ return pino({
37
+ level: getMinLevel(config.consoleLevel, config.fileLevel),
38
+ transport: {
39
+ targets,
40
+ },
41
+ });
42
+ }
43
+ /**
44
+ * Create a logger for request/response logging (JSON Lines format)
45
+ */
46
+ export function createRequestResponseLogger(filePath) {
47
+ return pino({
48
+ level: 'info',
49
+ timestamp: pino.stdTimeFunctions.isoTime,
50
+ }, pino.destination(filePath));
51
+ }
52
+ /**
53
+ * Ensure directory exists for log file
54
+ */
55
+ export async function ensureLogDirectory(filePath) {
56
+ try {
57
+ const dir = dirname(filePath);
58
+ await mkdir(dir, { recursive: true });
59
+ }
60
+ catch (error) {
61
+ throw new Error(`Failed to create log directory for ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
62
+ }
63
+ }
64
+ /**
65
+ * Get minimum log level between console and file
66
+ */
67
+ function getMinLevel(consoleLevel, fileLevel) {
68
+ const levels = {
69
+ debug: 20,
70
+ info: 30,
71
+ warn: 40,
72
+ error: 50,
73
+ };
74
+ const consoleLevelNum = levels[consoleLevel] || 30;
75
+ const fileLevelNum = fileLevel ? levels[fileLevel] || 30 : Infinity;
76
+ const minLevelNum = Math.min(consoleLevelNum, fileLevelNum);
77
+ // Return level name for minimum level number
78
+ for (const [name, num] of Object.entries(levels)) {
79
+ if (num === minLevelNum) {
80
+ return name;
81
+ }
82
+ }
83
+ return 'info';
84
+ }
85
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
2
  import { WebSocketServer } from '../websocket-server.js';
3
+ import type { Logger } from '../logger.js';
3
4
  export declare const CREATE_NOTE_TOOL: {
4
5
  name: string;
5
6
  description: string;
@@ -131,4 +132,4 @@ export declare const STATUS_TOOL: {
131
132
  properties: {};
132
133
  };
133
134
  };
134
- export declare function registerAllTools(server: Server, wsServer: WebSocketServer): void;
135
+ export declare function registerAllTools(server: Server, wsServer: WebSocketServer, logger: Logger): void;