ynab-mcp-deluxe 0.1.9

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/logger.js ADDED
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Pino-based logger for the MCP server.
3
+ *
4
+ * Writes JSON logs to ~/.config/ynab-mcp-deluxe/logs/ with date-based rotation.
5
+ * Use `bun run logs` to tail the log file, optionally piping through pino-pretty.
6
+ */
7
+ import { homedir } from 'node:os';
8
+ import { join } from 'node:path';
9
+ import pino from 'pino';
10
+ // Log directory: ~/.config/ynab-mcp-deluxe/logs/
11
+ const LOG_DIR = join(homedir(), '.config', 'ynab-mcp-deluxe', 'logs');
12
+ // Get log level from environment (default: debug)
13
+ const LOG_LEVEL = (process.env['LOG_LEVEL'] ?? 'debug').toLowerCase();
14
+ // Validate log level
15
+ const VALID_LEVELS = ['trace', 'debug', 'info', 'warn', 'error', 'fatal'];
16
+ const level = VALID_LEVELS.includes(LOG_LEVEL) ? LOG_LEVEL : 'debug';
17
+ /**
18
+ * Format args into a message string.
19
+ * First string arg becomes the message, rest are logged as data.
20
+ */
21
+ function formatMessage(args) {
22
+ const firstString = args.find((arg) => typeof arg === 'string');
23
+ if (typeof firstString === 'string') {
24
+ return firstString;
25
+ }
26
+ return args.length > 0 ? JSON.stringify(args[0]) : '';
27
+ }
28
+ // Create pino transport for rolling file output
29
+ // Files are named: server.YYYY-MM-DD.N.log
30
+ const transport = pino.transport({
31
+ options: {
32
+ file: join(LOG_DIR, 'server'),
33
+ frequency: 'daily',
34
+ limit: { count: 7 }, // Keep logs for 7 days
35
+ mkdir: true,
36
+ },
37
+ target: 'pino-roll',
38
+ });
39
+ // Create pino logger with the transport
40
+ const pinoLogger = pino({
41
+ base: {
42
+ pid: process.pid,
43
+ },
44
+ level,
45
+ timestamp: pino.stdTimeFunctions.isoTime,
46
+ }, transport);
47
+ /**
48
+ * Logger that implements FastMCP's Logger interface.
49
+ * Wraps pino to write structured JSON logs to a rotating file.
50
+ */
51
+ export const logger = {
52
+ debug(...args) {
53
+ pinoLogger.debug({ args }, formatMessage(args));
54
+ },
55
+ error(...args) {
56
+ pinoLogger.error({ args }, formatMessage(args));
57
+ },
58
+ info(...args) {
59
+ pinoLogger.info({ args }, formatMessage(args));
60
+ },
61
+ log(...args) {
62
+ // Map 'log' to 'info' level
63
+ pinoLogger.info({ args }, formatMessage(args));
64
+ },
65
+ warn(...args) {
66
+ pinoLogger.warn({ args }, formatMessage(args));
67
+ },
68
+ };
69
+ /**
70
+ * File-only logger for use when there's no context logger available.
71
+ * Writes directly to the pino log file.
72
+ */
73
+ export const fileLogger = {
74
+ debug(message, data) {
75
+ pinoLogger.debug(data, message);
76
+ },
77
+ error(message, data) {
78
+ pinoLogger.error(data, message);
79
+ },
80
+ info(message, data) {
81
+ pinoLogger.info(data, message);
82
+ },
83
+ warn(message, data) {
84
+ pinoLogger.warn(data, message);
85
+ },
86
+ };
87
+ /**
88
+ * Create a combined logger that writes to both the file logger and a context logger.
89
+ * This ensures logs are visible both in `bun run logs` and in MCP clients that surface context logs.
90
+ *
91
+ * @param contextLog - The FastMCP context logger (from tool execute context)
92
+ * @returns A logger that writes to both destinations
93
+ */
94
+ export function createCombinedLogger(contextLog) {
95
+ // If contextLog is already the fileLogger, just return it to avoid double-logging
96
+ if (contextLog === fileLogger) {
97
+ return fileLogger;
98
+ }
99
+ return {
100
+ debug(message, data) {
101
+ pinoLogger.debug(data, message);
102
+ contextLog.debug(message, data);
103
+ },
104
+ error(message, data) {
105
+ pinoLogger.error(data, message);
106
+ contextLog.error(message, data);
107
+ },
108
+ info(message, data) {
109
+ pinoLogger.info(data, message);
110
+ contextLog.info(message, data);
111
+ },
112
+ warn(message, data) {
113
+ pinoLogger.warn(data, message);
114
+ contextLog.warn(message, data);
115
+ },
116
+ };
117
+ }
118
+ /**
119
+ * Get the glob pattern for log files.
120
+ * pino-roll names files as server.N.log where N is the rotation counter.
121
+ */
122
+ export function getLogFilePattern() {
123
+ return join(LOG_DIR, 'server.*.log');
124
+ }
125
+ /**
126
+ * Get the log directory path.
127
+ */
128
+ export function getLogDir() {
129
+ return LOG_DIR;
130
+ }
131
+ // Log startup
132
+ pinoLogger.info({
133
+ level,
134
+ logDir: LOG_DIR,
135
+ nodeVersion: process.version,
136
+ }, 'Logger initialized');
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * YNAB MCP Server
4
+ *
5
+ * A Model Context Protocol server that provides access to the YNAB API
6
+ * for Claude-assisted transaction categorization.
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";AACA;;;;;GAKG"}