fastmail-mcp-server 0.4.2 → 0.5.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fastmail-mcp-server",
3
- "version": "0.4.2",
3
+ "version": "0.5.1",
4
4
  "description": "MCP server for Fastmail - read, search, and send emails via Claude",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
package/src/index.ts CHANGED
@@ -812,10 +812,39 @@ server.tool(
812
812
  }
813
813
 
814
814
  // Images - return as image content (resize if >1MB for model limits)
815
- if (result.type.startsWith("image/")) {
815
+ // Check by extension since JMAP blob download often returns application/octet-stream
816
+ const imageExts = [
817
+ ".jpg",
818
+ ".jpeg",
819
+ ".png",
820
+ ".gif",
821
+ ".webp",
822
+ ".bmp",
823
+ ".tiff",
824
+ ".tif",
825
+ ];
826
+ const ext = result.name
827
+ ? `.${result.name.split(".").pop()?.toLowerCase()}`
828
+ : "";
829
+ const isImage = result.type.startsWith("image/") || imageExts.includes(ext);
830
+
831
+ if (isImage) {
816
832
  const MAX_SIZE = 1024 * 1024; // 1MB
817
- let imageData = result.data;
818
- let mimeType = result.type;
833
+ let imageData: Buffer | Uint8Array = result.data;
834
+ // Infer mimeType from extension if we got octet-stream
835
+ const extToMime: Record<string, string> = {
836
+ ".jpg": "image/jpeg",
837
+ ".jpeg": "image/jpeg",
838
+ ".png": "image/png",
839
+ ".gif": "image/gif",
840
+ ".webp": "image/webp",
841
+ ".bmp": "image/bmp",
842
+ ".tiff": "image/tiff",
843
+ ".tif": "image/tiff",
844
+ };
845
+ let mimeType = result.type.startsWith("image/")
846
+ ? result.type
847
+ : extToMime[ext] || "image/jpeg";
819
848
  let resized = false;
820
849
 
821
850
  if (result.data.byteLength > MAX_SIZE) {
@@ -869,7 +898,7 @@ server.tool(
869
898
  {
870
899
  type: "image" as const,
871
900
  data: base64,
872
- mimeType: mimeType,
901
+ mimeType,
873
902
  },
874
903
  ],
875
904
  };
@@ -0,0 +1,105 @@
1
+ #!/usr/bin/env bun
2
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
3
+ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
4
+ import { mkdirSync, writeFileSync } from "fs";
5
+
6
+ async function main() {
7
+ const transport = new StdioClientTransport({
8
+ command: "bun",
9
+ args: ["run", "src/index.ts"],
10
+ env: {
11
+ ...process.env,
12
+ FASTMAIL_API_TOKEN: process.env.FASTMAIL_API_TOKEN || "",
13
+ },
14
+ });
15
+
16
+ const client = new Client({ name: "fastmail-test", version: "1.0.0" });
17
+ await client.connect(transport);
18
+
19
+ console.log("Connected to Fastmail MCP server\n");
20
+
21
+ // Search for the test email
22
+ console.log("=== Searching for 'CLAUDE TEST IMAGE 3' ===");
23
+ const searchResult = await client.callTool({
24
+ name: "search_emails",
25
+ arguments: { subject: "CLAUDE TEST IMAGE 3", limit: 1 },
26
+ });
27
+ const searchText =
28
+ (searchResult.content as Array<{ text: string }>)[0]?.text ?? "";
29
+ console.log(searchText);
30
+
31
+ // Extract email ID from search results
32
+ const idMatch = searchText.match(/ID: ([^\s\n]+)/);
33
+ if (!idMatch) {
34
+ console.error("Could not find email ID");
35
+ await client.close();
36
+ return;
37
+ }
38
+ const emailId = idMatch[1];
39
+ console.log(`\nFound email ID: ${emailId}\n`);
40
+
41
+ // List attachments to get blob ID
42
+ console.log("=== Listing attachments ===");
43
+ const attachListResult = await client.callTool({
44
+ name: "list_attachments",
45
+ arguments: { email_id: emailId },
46
+ });
47
+ const attachListText =
48
+ (attachListResult.content as Array<{ text: string }>)[0]?.text ?? "";
49
+ console.log(attachListText);
50
+
51
+ // Extract blob ID from attachments list
52
+ const blobMatch = attachListText.match(/Blob ID: ([^\s\n)]+)/);
53
+ if (!blobMatch) {
54
+ console.error("Could not find blob ID");
55
+ await client.close();
56
+ return;
57
+ }
58
+ const blobId = blobMatch[1];
59
+ console.log(`\nFound blob ID: ${blobId}\n`);
60
+
61
+ // Get the attachment
62
+ console.log("=== Getting attachment ===");
63
+ const attachResult = await client.callTool({
64
+ name: "get_attachment",
65
+ arguments: { email_id: emailId, blob_id: blobId },
66
+ });
67
+
68
+ // Write results to tmp dir
69
+ mkdirSync("/tmp/mcp-test", { recursive: true });
70
+
71
+ const content = attachResult.content as Array<{
72
+ type: string;
73
+ text?: string;
74
+ data?: string;
75
+ mimeType?: string;
76
+ }>;
77
+ console.log(`\nAttachment response has ${content.length} content blocks:`);
78
+
79
+ for (let i = 0; i < content.length; i++) {
80
+ const block = content[i];
81
+ console.log(` [${i}] type: ${block.type}`);
82
+ if (block.type === "text") {
83
+ console.log(` text: ${block.text}`);
84
+ writeFileSync(`/tmp/mcp-test/block-${i}.txt`, block.text || "");
85
+ } else if (block.type === "image") {
86
+ console.log(` mimeType: ${block.mimeType}`);
87
+ console.log(` data length: ${block.data?.length} chars`);
88
+ const buffer = Buffer.from(block.data || "", "base64");
89
+ console.log(
90
+ ` decoded size: ${buffer.length} bytes (${Math.round(buffer.length / 1024)}KB)`,
91
+ );
92
+ const ext = block.mimeType?.split("/")[1] || "bin";
93
+ writeFileSync(`/tmp/mcp-test/block-${i}.${ext}`, buffer);
94
+ console.log(` wrote to /tmp/mcp-test/block-${i}.${ext}`);
95
+ }
96
+ }
97
+
98
+ await client.close();
99
+ console.log("\n=== Done ===");
100
+ }
101
+
102
+ main().catch((e) => {
103
+ console.error("Test failed:", e);
104
+ process.exit(1);
105
+ });