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 +1 -1
- package/src/index.ts +33 -4
- package/src/test-image.ts +105 -0
package/package.json
CHANGED
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
+
});
|