Skip to main content
Glama

parse_eml_headers

Extract MIME headers from .eml files to analyze email threads, retrieving message IDs, subjects, sender/recipient details, and dates for correlation.

Instructions

Extract MIME headers from an .eml file for email thread correlation. Returns message_id, in_reply_to, references (array), subject, from, to, cc, and date. Handles CRLF/LF line endings, folded headers, and RFC 2047 encoded words in Subject/From/To. Input: { "filePath": "/path/to/email.eml" }

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
filePathYesAbsolute path to the .eml file to parse

Implementation Reference

  • The main function `parseEmlHeaders` that reads the .eml file and extracts headers using helper functions.
    const parseEmlHeaders = async (args: Record<string, unknown>): Promise<EmlHeadersResult> => {
      const filePath = args.filePath as string | undefined;
    
      if (!filePath || typeof filePath !== "string") {
        return { success: false, error: "filePath parameter is required and must be a string" };
      }
    
      if (!existsSync(filePath)) {
        return { success: false, filePath, error: `File not found: ${filePath}` };
      }
    
      let rawContent: string;
      try {
        const HEADER_READ_LIMIT = 65536;
        const fd = await new Promise<Buffer>((resolve, reject) => {
          const stream = createReadStream(filePath, { start: 0, end: HEADER_READ_LIMIT - 1 });
          const chunks: Buffer[] = [];
          stream.on("data", (chunk) => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk as string)));
          stream.on("end", () => resolve(Buffer.concat(chunks)));
          stream.on("error", reject);
        });
        rawContent = fd.toString("utf-8");
      } catch (err) {
        return {
          success: false,
          filePath,
          error: `Failed to read file: ${err instanceof Error ? err.message : String(err)}`,
        };
      }
    
      const crlf = rawContent.indexOf("\r\n\r\n");
      const lf = rawContent.indexOf("\n\n");
      let headerSection: string;
    
      if (crlf !== -1 && (lf === -1 || crlf < lf)) {
        headerSection = rawContent.slice(0, crlf);
      } else if (lf !== -1) {
        headerSection = rawContent.slice(0, lf);
      } else {
        headerSection = rawContent;
      }
    
      const headers = parseHeaders(headerSection);
    
      const get = (name: string): string | null => {
        const val = headers.get(name);
        return val !== undefined ? decodeEncodedWords(val.trim()) : null;
      };
    
      const referencesRaw = get("references");
    
      return {
        success: true,
        filePath,
        messageId: get("message-id"),
        inReplyTo: get("in-reply-to"),
        references: referencesRaw ? parseReferences(referencesRaw) : [],
        subject: get("subject"),
        from: get("from"),
        to: get("to"),
        cc: get("cc"),
        date: get("date"),
      };
    };
  • The `parseEmlHeadersTool` MCP tool definition which links the `parse_eml_headers` name to the `parseEmlHeaders` handler.
    export const parseEmlHeadersTool: McpTool = {
      name: "parse_eml_headers",
      description:
        "Extract MIME headers from an .eml file for email thread correlation. " +
        "Returns message_id, in_reply_to, references (array), subject, from, to, cc, and date. " +
        "Handles CRLF/LF line endings, folded headers, and RFC 2047 encoded words in Subject/From/To. " +
        'Input: { "filePath": "/path/to/email.eml" }',
      inputSchema: {
        type: "object" as const,
        properties: {
          filePath: {
            type: "string",
            description: "Absolute path to the .eml file to parse",
          },
        },
        required: ["filePath"],
        additionalProperties: false,
      },
      run: parseEmlHeaders,
    };
  • The `parseHeaders` helper function used to parse the raw header section of an email file.
    function parseHeaders(headerSection: string): Map<string, string> {
      const headers = new Map<string, string>();
      const normalised = headerSection.replace(/\r\n/g, "\n");
      const lines = normalised.split("\n");
    
      let currentName: string | null = null;
      let currentValue = "";
    
      const flush = (): void => {
        if (currentName !== null) {
          const unfolded = currentValue.replace(/\n[ \t]+/g, " ").trim();
          const key = currentName.toLowerCase();
          if (headers.has(key)) {
            headers.set(key, headers.get(key) + "\n" + unfolded);
          } else {
            headers.set(key, unfolded);
          }
        }
      };
    
      for (const line of lines) {
        if (line === "") break;
        if (line[0] === " " || line[0] === "\t") {
          if (currentName !== null) {
            currentValue += "\n" + line;
          }
        } else {
          flush();
          const colonIdx = line.indexOf(":");
          if (colonIdx > 0) {
            currentName = line.slice(0, colonIdx).trim();
            currentValue = line.slice(colonIdx + 1);
          } else {
            currentName = null;
            currentValue = "";
          }
        }
      }
      flush();
    
      return headers;
    }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/mnott/Devon'

If you have feedback or need assistance with the MCP directory API, please join our Discord server