Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/punkpeye/fastmcp/llms.txt

Use this file to discover all available pages before exploring further.

FastMCP provides flexible logging options, allowing you to customize how your server logs messages and integrate with existing logging infrastructure.

Custom Logger

Provide a custom logger implementation to control how the server logs messages:
import { FastMCP, Logger } from "fastmcp";

class CustomLogger implements Logger {
  debug(...args: unknown[]): void {
    console.log("[DEBUG]", new Date().toISOString(), ...args);
  }

  error(...args: unknown[]): void {
    console.error("[ERROR]", new Date().toISOString(), ...args);
  }

  info(...args: unknown[]): void {
    console.info("[INFO]", new Date().toISOString(), ...args);
  }

  log(...args: unknown[]): void {
    console.log("[LOG]", new Date().toISOString(), ...args);
  }

  warn(...args: unknown[]): void {
    console.warn("[WARN]", new Date().toISOString(), ...args);
  }
}

const server = new FastMCP({
  name: "My Server",
  version: "1.0.0",
  logger: new CustomLogger(),
});

Logger Interface

The Logger interface requires five methods:
interface Logger {
  debug(...args: unknown[]): void;
  error(...args: unknown[]): void;
  info(...args: unknown[]): void;
  log(...args: unknown[]): void;
  warn(...args: unknown[]): void;
}

Logging in Tools

Tools can log messages to the client using the log object in the context:
import { FastMCP } from "fastmcp";
import { z } from "zod";

const server = new FastMCP({
  name: "My Server",
  version: "1.0.0",
});

server.addTool({
  name: "download",
  description: "Download a file",
  parameters: z.object({
    url: z.string(),
  }),
  execute: async (args, { log }) => {
    log.info("Downloading file...", {
      url: args.url,
    });

    // Download logic here...

    log.info("Downloaded file");

    return "done";
  },
});

Log Methods

The log object provides four methods:
MethodDescriptionUse Case
debug(message, data?)Debug-level loggingDetailed diagnostic information
error(message, data?)Error-level loggingError conditions
info(message, data?)Info-level loggingGeneral informational messages
warn(message, data?)Warning-level loggingWarning conditions
All methods accept:
  • message: string - The log message
  • data?: SerializableValue - Optional structured data

Integration Examples

import winston from 'winston';
import { FastMCP, Logger } from 'fastmcp';

class WinstonLoggerAdapter implements Logger {
  private winston: winston.Logger;

  constructor() {
    this.winston = winston.createLogger({
      level: 'debug',
      format: winston.format.combine(
        winston.format.timestamp(),
        winston.format.errors({ stack: true }),
        winston.format.json()
      ),
      transports: [
        new winston.transports.Console({
          format: winston.format.combine(
            winston.format.colorize(),
            winston.format.simple()
          )
        }),
        new winston.transports.File({ filename: 'fastmcp.log' })
      ]
    });
  }

  debug(...args: unknown[]): void {
    this.winston.debug(this.formatArgs(args));
  }

  error(...args: unknown[]): void {
    this.winston.error(this.formatArgs(args));
  }

  info(...args: unknown[]): void {
    this.winston.info(this.formatArgs(args));
  }

  log(...args: unknown[]): void {
    this.winston.info(this.formatArgs(args));
  }

  warn(...args: unknown[]): void {
    this.winston.warn(this.formatArgs(args));
  }

  private formatArgs(args: unknown[]): string {
    return args.map(arg =>
      typeof arg === 'object' ? JSON.stringify(arg) : String(arg)
    ).join(' ');
  }
}

const server = new FastMCP({
  name: "My Server",
  version: "1.0.0",
  logger: new WinstonLoggerAdapter(),
});

Real-World Example

Here’s a complete example from the FastMCP repository showing custom logging:
src/examples/custom-logger.ts
import { z } from "zod";
import { FastMCP, Logger } from "../FastMCP.js";

// Simple Custom Logger Implementation
class SimpleCustomLogger implements Logger {
  debug(...args: unknown[]): void {
    console.log("[CUSTOM DEBUG]", new Date().toISOString(), ...args);
  }

  error(...args: unknown[]): void {
    console.error("[CUSTOM ERROR]", new Date().toISOString(), ...args);
  }

  info(...args: unknown[]): void {
    console.info("[CUSTOM INFO]", new Date().toISOString(), ...args);
  }

  log(...args: unknown[]): void {
    console.log("[CUSTOM LOG]", new Date().toISOString(), ...args);
  }

  warn(...args: unknown[]): void {
    console.warn("[CUSTOM WARN]", new Date().toISOString(), ...args);
  }
}

const logger = new SimpleCustomLogger();

const server = new FastMCP({
  logger: logger,
  name: "custom-logger-example",
  version: "1.0.0",
});

server.addTool({
  description: "A test tool that demonstrates custom logging",
  execute: async (args) => {
    return `Received: ${args.message}`;
  },
  name: "test_tool",
  parameters: z.object({
    message: z.string().describe("A message to log"),
  }),
});

server.start({ transportType: "stdio" });

Structured Logging

Log structured data alongside messages:
server.addTool({
  name: "processOrder",
  description: "Process an order",
  parameters: z.object({
    orderId: z.string(),
    items: z.array(z.string()),
  }),
  execute: async ({ orderId, items }, { log }) => {
    log.info("Processing order", {
      orderId,
      itemCount: items.length,
      timestamp: new Date().toISOString(),
    });

    try {
      // Process order...
      log.info("Order processed successfully", { orderId });
    } catch (error) {
      log.error("Order processing failed", {
        orderId,
        error: error.message,
      });
      throw error;
    }

    return "Order processed";
  },
});

Best Practices

1

Use Appropriate Log Levels

  • debug: Detailed diagnostic information
  • info: General informational messages
  • warn: Warning conditions
  • error: Error conditions
2

Include Context in Logs

Always log relevant context data:
log.info("Processing file", {
  filename: file.name,
  size: file.size,
  userId: session.userId,
});
3

Log at Key Points

Log at important stages:
  • Start of operations
  • Completion of operations
  • Error conditions
  • State changes
4

Avoid Logging Sensitive Data

Never log:
  • Passwords
  • API keys
  • Personal information (unless necessary and compliant)
  • Credit card numbers
5

Use Structured Logging

Prefer structured data over string concatenation:
// Good
log.info("User login", { userId, timestamp });

// Avoid
log.info(`User ${userId} logged in at ${timestamp}`);

Production Considerations

Log Rotation

For file-based logging, implement log rotation:
import * as fs from 'fs';
import * as path from 'path';

class RotatingFileLogger implements Logger {
  private logFile: string;
  private maxSize: number = 10 * 1024 * 1024; // 10MB
  private maxFiles: number = 5;

  constructor(logFile: string) {
    this.logFile = logFile;
  }

  private rotateIfNeeded(): void {
    if (!fs.existsSync(this.logFile)) return;

    const stats = fs.statSync(this.logFile);
    if (stats.size < this.maxSize) return;

    // Rotate logs
    for (let i = this.maxFiles - 1; i >= 0; i--) {
      const oldFile = `${this.logFile}.${i}`;
      const newFile = `${this.logFile}.${i + 1}`;
      if (fs.existsSync(oldFile)) {
        fs.renameSync(oldFile, newFile);
      }
    }

    fs.renameSync(this.logFile, `${this.logFile}.0`);
  }

  private logToFile(level: string, ...args: unknown[]): void {
    this.rotateIfNeeded();

    const timestamp = new Date().toISOString();
    const message = `[${timestamp}] [${level}] ${args.map(arg =>
      typeof arg === 'object' ? JSON.stringify(arg) : String(arg)
    ).join(' ')}\n`;

    fs.appendFileSync(this.logFile, message);
  }

  debug(...args: unknown[]): void {
    this.logToFile('DEBUG', ...args);
  }

  error(...args: unknown[]): void {
    this.logToFile('ERROR', ...args);
  }

  info(...args: unknown[]): void {
    this.logToFile('INFO', ...args);
  }

  log(...args: unknown[]): void {
    this.logToFile('LOG', ...args);
  }

  warn(...args: unknown[]): void {
    this.logToFile('WARN', ...args);
  }
}

Performance Monitoring

Log performance metrics:
server.addTool({
  name: "expensiveOperation",
  execute: async (args, { log }) => {
    const startTime = Date.now();

    log.info("Starting expensive operation", { args });

    try {
      // Do work...
      const result = await doWork();

      const duration = Date.now() - startTime;
      log.info("Operation completed", {
        duration,
        success: true,
      });

      return result;
    } catch (error) {
      const duration = Date.now() - startTime;
      log.error("Operation failed", {
        duration,
        error: error.message,
      });
      throw error;
    }
  },
});

Next Steps

Streaming

Learn how to stream content and report progress

Authentication

Secure your server with authentication

Build docs developers (and LLMs) love