myceliumail-mcp 1.0.0

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/server.js ADDED
@@ -0,0 +1,306 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Myceliumail MCP Server
4
+ *
5
+ * Exposes Myceliumail messaging as MCP tools for Claude Desktop
6
+ * and other MCP-compatible clients.
7
+ */
8
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
9
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
10
+ import { z } from 'zod';
11
+ import * as crypto from './lib/crypto.js';
12
+ import * as storage from './lib/storage.js';
13
+ import { getAgentId } from './lib/config.js';
14
+ // Create the MCP server
15
+ const server = new McpServer({
16
+ name: 'myceliumail',
17
+ version: '1.0.0',
18
+ });
19
+ // Tool: check_inbox
20
+ server.tool('check_inbox', 'Check your Myceliumail inbox for messages', {
21
+ unread_only: z.boolean().optional().describe('Only show unread messages'),
22
+ limit: z.number().optional().describe('Maximum number of messages to return'),
23
+ }, async ({ unread_only, limit }) => {
24
+ const agentId = getAgentId();
25
+ const messages = await storage.getInbox(agentId, {
26
+ unreadOnly: unread_only,
27
+ limit: limit || 10,
28
+ });
29
+ if (messages.length === 0) {
30
+ return {
31
+ content: [{ type: 'text', text: 'šŸ“­ No messages in inbox' }],
32
+ };
33
+ }
34
+ const formatted = messages.map(msg => {
35
+ const status = msg.read ? ' ' : 'ā— ';
36
+ const encrypted = msg.encrypted ? 'šŸ” ' : '';
37
+ return `${status}${encrypted}[${msg.id.slice(0, 8)}] From: ${msg.sender} | ${msg.subject || '(no subject)'} | ${msg.createdAt.toLocaleString()}`;
38
+ }).join('\n');
39
+ return {
40
+ content: [{
41
+ type: 'text',
42
+ text: `šŸ“¬ Inbox (${messages.length} messages):\n\n${formatted}`
43
+ }],
44
+ };
45
+ });
46
+ // Tool: read_message
47
+ server.tool('read_message', 'Read a specific message by ID', {
48
+ message_id: z.string().describe('Message ID (can be partial)'),
49
+ }, async ({ message_id }) => {
50
+ const agentId = getAgentId();
51
+ let message = await storage.getMessage(message_id);
52
+ // Try partial ID match
53
+ if (!message) {
54
+ const inbox = await storage.getInbox(agentId, { limit: 100 });
55
+ message = inbox.find(m => m.id.startsWith(message_id)) || null;
56
+ }
57
+ if (!message) {
58
+ return {
59
+ content: [{ type: 'text', text: `āŒ Message not found: ${message_id}` }],
60
+ };
61
+ }
62
+ // Mark as read
63
+ await storage.markAsRead(message.id);
64
+ // Decrypt if needed
65
+ let subject = message.subject;
66
+ let body = message.body;
67
+ if (message.encrypted && message.ciphertext && message.nonce && message.senderPublicKey) {
68
+ const keyPair = crypto.loadKeyPair(agentId);
69
+ if (keyPair) {
70
+ try {
71
+ const decrypted = crypto.decryptMessage({
72
+ ciphertext: message.ciphertext,
73
+ nonce: message.nonce,
74
+ senderPublicKey: message.senderPublicKey,
75
+ }, keyPair);
76
+ if (decrypted) {
77
+ const parsed = JSON.parse(decrypted);
78
+ subject = parsed.subject || subject;
79
+ body = parsed.body || body;
80
+ }
81
+ }
82
+ catch {
83
+ body = '[Failed to decrypt]';
84
+ }
85
+ }
86
+ else {
87
+ body = '[Cannot decrypt - no keypair]';
88
+ }
89
+ }
90
+ const encrypted = message.encrypted ? '\nšŸ” Encrypted: Yes' : '';
91
+ const text = `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
92
+ From: ${message.sender}
93
+ To: ${message.recipient}
94
+ Date: ${message.createdAt.toLocaleString()}
95
+ Subject: ${subject}${encrypted}
96
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
97
+
98
+ ${body}
99
+
100
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
101
+ ID: ${message.id}`;
102
+ return {
103
+ content: [{ type: 'text', text }],
104
+ };
105
+ });
106
+ // Tool: send_message
107
+ server.tool('send_message', 'Send a message to another agent', {
108
+ recipient: z.string().describe('Recipient agent ID'),
109
+ subject: z.string().describe('Message subject'),
110
+ body: z.string().describe('Message body'),
111
+ encrypt: z.boolean().optional().describe('Encrypt the message (requires key exchange)'),
112
+ }, async ({ recipient, subject, body, encrypt }) => {
113
+ const sender = getAgentId();
114
+ if (sender === 'anonymous') {
115
+ return {
116
+ content: [{ type: 'text', text: 'āŒ Agent ID not configured. Set MYCELIUMAIL_AGENT_ID environment variable.' }],
117
+ };
118
+ }
119
+ let messageOptions;
120
+ if (encrypt) {
121
+ const senderKeyPair = crypto.loadKeyPair(sender);
122
+ if (!senderKeyPair) {
123
+ return {
124
+ content: [{ type: 'text', text: 'āŒ No keypair found. Use generate_keys first.' }],
125
+ };
126
+ }
127
+ const recipientPubKeyB64 = crypto.getKnownKey(recipient);
128
+ if (!recipientPubKeyB64) {
129
+ return {
130
+ content: [{ type: 'text', text: `āŒ No public key found for ${recipient}. Use import_key first.` }],
131
+ };
132
+ }
133
+ const recipientPubKey = crypto.decodePublicKey(recipientPubKeyB64);
134
+ const payload = JSON.stringify({ subject, body });
135
+ const encrypted = crypto.encryptMessage(payload, recipientPubKey, senderKeyPair);
136
+ messageOptions = {
137
+ encrypted: true,
138
+ ciphertext: encrypted.ciphertext,
139
+ nonce: encrypted.nonce,
140
+ senderPublicKey: encrypted.senderPublicKey,
141
+ };
142
+ }
143
+ try {
144
+ const message = await storage.sendMessage(sender, recipient, subject, body, messageOptions);
145
+ const encInfo = encrypt ? ' (šŸ” encrypted)' : '';
146
+ return {
147
+ content: [{
148
+ type: 'text',
149
+ text: `āœ… Message sent to ${recipient}${encInfo}\nID: ${message.id}`
150
+ }],
151
+ };
152
+ }
153
+ catch (error) {
154
+ return {
155
+ content: [{ type: 'text', text: `āŒ Failed to send: ${error}` }],
156
+ };
157
+ }
158
+ });
159
+ // Tool: reply_message
160
+ server.tool('reply_message', 'Reply to a message', {
161
+ message_id: z.string().describe('ID of message to reply to'),
162
+ body: z.string().describe('Reply message body'),
163
+ encrypt: z.boolean().optional().describe('Encrypt the reply'),
164
+ }, async ({ message_id, body, encrypt }) => {
165
+ const agentId = getAgentId();
166
+ // Find original message
167
+ let original = await storage.getMessage(message_id);
168
+ if (!original) {
169
+ const inbox = await storage.getInbox(agentId, { limit: 100 });
170
+ original = inbox.find(m => m.id.startsWith(message_id)) || null;
171
+ }
172
+ if (!original) {
173
+ return {
174
+ content: [{ type: 'text', text: `āŒ Message not found: ${message_id}` }],
175
+ };
176
+ }
177
+ // Send reply to original sender
178
+ const subject = original.subject.startsWith('Re: ')
179
+ ? original.subject
180
+ : `Re: ${original.subject}`;
181
+ let messageOptions;
182
+ if (encrypt) {
183
+ const senderKeyPair = crypto.loadKeyPair(agentId);
184
+ const recipientPubKeyB64 = crypto.getKnownKey(original.sender);
185
+ if (senderKeyPair && recipientPubKeyB64) {
186
+ const recipientPubKey = crypto.decodePublicKey(recipientPubKeyB64);
187
+ const payload = JSON.stringify({ subject, body });
188
+ const encrypted = crypto.encryptMessage(payload, recipientPubKey, senderKeyPair);
189
+ messageOptions = {
190
+ encrypted: true,
191
+ ciphertext: encrypted.ciphertext,
192
+ nonce: encrypted.nonce,
193
+ senderPublicKey: encrypted.senderPublicKey,
194
+ };
195
+ }
196
+ }
197
+ const message = await storage.sendMessage(agentId, original.sender, subject, body, messageOptions);
198
+ const encInfo = encrypt && messageOptions ? ' (šŸ” encrypted)' : '';
199
+ return {
200
+ content: [{
201
+ type: 'text',
202
+ text: `āœ… Reply sent to ${original.sender}${encInfo}\nID: ${message.id}`
203
+ }],
204
+ };
205
+ });
206
+ // Tool: generate_keys
207
+ server.tool('generate_keys', 'Generate encryption keypair for this agent', {
208
+ force: z.boolean().optional().describe('Overwrite existing keypair'),
209
+ }, async ({ force }) => {
210
+ const agentId = getAgentId();
211
+ if (crypto.hasKeyPair(agentId) && !force) {
212
+ const existing = crypto.loadKeyPair(agentId);
213
+ if (existing) {
214
+ const pubKey = crypto.getPublicKeyBase64(existing);
215
+ return {
216
+ content: [{
217
+ type: 'text',
218
+ text: `āš ļø Keypair already exists for ${agentId}\n\nšŸ“§ Your public key:\n${pubKey}\n\nUse force=true to regenerate.`
219
+ }],
220
+ };
221
+ }
222
+ }
223
+ const keyPair = crypto.generateKeyPair();
224
+ crypto.saveKeyPair(agentId, keyPair);
225
+ const publicKey = crypto.getPublicKeyBase64(keyPair);
226
+ return {
227
+ content: [{
228
+ type: 'text',
229
+ text: `šŸ” Keypair generated for ${agentId}\n\nšŸ“§ Your public key (share with other agents):\n${publicKey}`
230
+ }],
231
+ };
232
+ });
233
+ // Tool: list_keys
234
+ server.tool('list_keys', 'List all known encryption keys', {}, async () => {
235
+ const agentId = getAgentId();
236
+ const ownKeys = crypto.listOwnKeys();
237
+ const knownKeys = crypto.loadKnownKeys();
238
+ let output = 'šŸ” Encryption Keys\n\n── Your Keys ──\n';
239
+ if (ownKeys.length === 0) {
240
+ output += 'No keypairs. Use generate_keys to create one.\n';
241
+ }
242
+ else {
243
+ for (const id of ownKeys) {
244
+ const kp = crypto.loadKeyPair(id);
245
+ if (kp) {
246
+ const marker = id === agentId ? ' (active)' : '';
247
+ output += `${id}${marker}: ${crypto.getPublicKeyBase64(kp).slice(0, 20)}...\n`;
248
+ }
249
+ }
250
+ }
251
+ output += '\n── Peer Keys ──\n';
252
+ const peers = Object.entries(knownKeys);
253
+ if (peers.length === 0) {
254
+ output += 'No peer keys. Use import_key to add one.\n';
255
+ }
256
+ else {
257
+ for (const [id, key] of peers) {
258
+ output += `${id}: ${key.slice(0, 20)}...\n`;
259
+ }
260
+ }
261
+ return {
262
+ content: [{ type: 'text', text: output }],
263
+ };
264
+ });
265
+ // Tool: import_key
266
+ server.tool('import_key', "Import another agent's public key for encrypted messaging", {
267
+ agent_id: z.string().describe('Agent ID to import key for'),
268
+ public_key: z.string().describe('Base64 encoded public key'),
269
+ }, async ({ agent_id, public_key }) => {
270
+ if (public_key.length < 40) {
271
+ return {
272
+ content: [{ type: 'text', text: 'āŒ Invalid key format. Expected base64 NaCl public key.' }],
273
+ };
274
+ }
275
+ crypto.saveKnownKey(agent_id, public_key);
276
+ return {
277
+ content: [{
278
+ type: 'text',
279
+ text: `āœ… Imported public key for ${agent_id}\n\nšŸ” You can now send encrypted messages to this agent.`
280
+ }],
281
+ };
282
+ });
283
+ // Tool: archive_message
284
+ server.tool('archive_message', 'Archive a message (remove from inbox)', {
285
+ message_id: z.string().describe('Message ID to archive'),
286
+ }, async ({ message_id }) => {
287
+ const success = await storage.archiveMessage(message_id);
288
+ if (success) {
289
+ return {
290
+ content: [{ type: 'text', text: `āœ… Message archived: ${message_id}` }],
291
+ };
292
+ }
293
+ else {
294
+ return {
295
+ content: [{ type: 'text', text: `āŒ Message not found: ${message_id}` }],
296
+ };
297
+ }
298
+ });
299
+ // Start the server
300
+ async function main() {
301
+ const transport = new StdioServerTransport();
302
+ await server.connect(transport);
303
+ console.error('Myceliumail MCP server running');
304
+ }
305
+ main().catch(console.error);
306
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
Binary file
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "myceliumail-mcp",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for Myceliumail - End-to-End Encrypted Messaging for AI Agents",
5
+ "type": "module",
6
+ "main": "dist/server.js",
7
+ "bin": {
8
+ "myceliumail-mcp": "./dist/server.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "dev": "tsc --watch",
13
+ "start": "node dist/server.js",
14
+ "prepublishOnly": "npm run build"
15
+ },
16
+ "keywords": [
17
+ "mcp",
18
+ "model-context-protocol",
19
+ "claude",
20
+ "ai-agents",
21
+ "messaging",
22
+ "encrypted",
23
+ "e2e",
24
+ "myceliumail"
25
+ ],
26
+ "author": "Treebird",
27
+ "license": "MIT",
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "https://github.com/treebird/myceliumail"
31
+ },
32
+ "homepage": "https://github.com/treebird/myceliumail#readme",
33
+ "bugs": {
34
+ "url": "https://github.com/treebird/myceliumail/issues"
35
+ },
36
+ "dependencies": {
37
+ "@modelcontextprotocol/sdk": "^1.0.0",
38
+ "tweetnacl": "^1.0.3",
39
+ "tweetnacl-util": "^0.15.1",
40
+ "zod": "^3.22.0"
41
+ },
42
+ "devDependencies": {
43
+ "@types/node": "^20.10.0",
44
+ "typescript": "^5.3.0"
45
+ },
46
+ "engines": {
47
+ "node": ">=18.0.0"
48
+ }
49
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Myceliumail MCP - Config Module
3
+ */
4
+
5
+ export function getAgentId(): string {
6
+ return process.env.MYCELIUMAIL_AGENT_ID ||
7
+ process.env.MYCELIUMAIL_AGENT ||
8
+ 'anonymous';
9
+ }
10
+
11
+ export function getSupabaseUrl(): string | undefined {
12
+ return process.env.SUPABASE_URL;
13
+ }
14
+
15
+ export function getSupabaseKey(): string | undefined {
16
+ return process.env.SUPABASE_ANON_KEY;
17
+ }
18
+
19
+ export function hasSupabase(): boolean {
20
+ return !!(getSupabaseUrl() && getSupabaseKey());
21
+ }
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Myceliumail MCP - Crypto Module
3
+ *
4
+ * NaCl encryption for agent messaging.
5
+ */
6
+
7
+ import nacl from 'tweetnacl';
8
+ import util from 'tweetnacl-util';
9
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync } from 'fs';
10
+ import { join } from 'path';
11
+ import { homedir } from 'os';
12
+
13
+ const KEYS_DIR = join(homedir(), '.myceliumail', 'keys');
14
+
15
+ export interface KeyPair {
16
+ publicKey: Uint8Array;
17
+ secretKey: Uint8Array;
18
+ }
19
+
20
+ export interface EncryptedMessage {
21
+ ciphertext: string;
22
+ nonce: string;
23
+ senderPublicKey: string;
24
+ }
25
+
26
+ function ensureKeysDir(): void {
27
+ if (!existsSync(KEYS_DIR)) {
28
+ mkdirSync(KEYS_DIR, { recursive: true });
29
+ }
30
+ }
31
+
32
+ export function generateKeyPair(): KeyPair {
33
+ return nacl.box.keyPair();
34
+ }
35
+
36
+ export function saveKeyPair(agentId: string, keyPair: KeyPair): void {
37
+ ensureKeysDir();
38
+ const serialized = {
39
+ publicKey: util.encodeBase64(keyPair.publicKey),
40
+ secretKey: util.encodeBase64(keyPair.secretKey),
41
+ };
42
+ const path = join(KEYS_DIR, `${agentId}.key.json`);
43
+ writeFileSync(path, JSON.stringify(serialized, null, 2), { mode: 0o600 });
44
+ }
45
+
46
+ export function loadKeyPair(agentId: string): KeyPair | null {
47
+ const path = join(KEYS_DIR, `${agentId}.key.json`);
48
+ if (!existsSync(path)) return null;
49
+
50
+ try {
51
+ const data = JSON.parse(readFileSync(path, 'utf-8'));
52
+ return {
53
+ publicKey: util.decodeBase64(data.publicKey),
54
+ secretKey: util.decodeBase64(data.secretKey),
55
+ };
56
+ } catch {
57
+ return null;
58
+ }
59
+ }
60
+
61
+ export function hasKeyPair(agentId: string): boolean {
62
+ return existsSync(join(KEYS_DIR, `${agentId}.key.json`));
63
+ }
64
+
65
+ export function getPublicKeyBase64(keyPair: KeyPair): string {
66
+ return util.encodeBase64(keyPair.publicKey);
67
+ }
68
+
69
+ export function encryptMessage(
70
+ message: string,
71
+ recipientPublicKey: Uint8Array,
72
+ senderKeyPair: KeyPair
73
+ ): EncryptedMessage {
74
+ const messageBytes = util.decodeUTF8(message);
75
+ const nonce = nacl.randomBytes(nacl.box.nonceLength);
76
+
77
+ const ciphertext = nacl.box(
78
+ messageBytes,
79
+ nonce,
80
+ recipientPublicKey,
81
+ senderKeyPair.secretKey
82
+ );
83
+
84
+ return {
85
+ ciphertext: util.encodeBase64(ciphertext),
86
+ nonce: util.encodeBase64(nonce),
87
+ senderPublicKey: util.encodeBase64(senderKeyPair.publicKey),
88
+ };
89
+ }
90
+
91
+ export function decryptMessage(
92
+ encrypted: EncryptedMessage,
93
+ recipientKeyPair: KeyPair
94
+ ): string | null {
95
+ try {
96
+ const ciphertext = util.decodeBase64(encrypted.ciphertext);
97
+ const nonce = util.decodeBase64(encrypted.nonce);
98
+ const senderPublicKey = util.decodeBase64(encrypted.senderPublicKey);
99
+
100
+ const decrypted = nacl.box.open(
101
+ ciphertext,
102
+ nonce,
103
+ senderPublicKey,
104
+ recipientKeyPair.secretKey
105
+ );
106
+
107
+ if (!decrypted) return null;
108
+ return util.encodeUTF8(decrypted);
109
+ } catch {
110
+ return null;
111
+ }
112
+ }
113
+
114
+ export function loadKnownKeys(): Record<string, string> {
115
+ const path = join(KEYS_DIR, 'known_keys.json');
116
+ if (!existsSync(path)) return {};
117
+ try {
118
+ return JSON.parse(readFileSync(path, 'utf-8'));
119
+ } catch {
120
+ return {};
121
+ }
122
+ }
123
+
124
+ export function saveKnownKey(agentId: string, publicKeyBase64: string): void {
125
+ ensureKeysDir();
126
+ const keys = loadKnownKeys();
127
+ keys[agentId] = publicKeyBase64;
128
+ writeFileSync(join(KEYS_DIR, 'known_keys.json'), JSON.stringify(keys, null, 2));
129
+ }
130
+
131
+ export function getKnownKey(agentId: string): string | null {
132
+ const keys = loadKnownKeys();
133
+ return keys[agentId] || null;
134
+ }
135
+
136
+ export function listOwnKeys(): string[] {
137
+ ensureKeysDir();
138
+ try {
139
+ const files = readdirSync(KEYS_DIR);
140
+ return files
141
+ .filter(f => f.endsWith('.key.json'))
142
+ .map(f => f.replace('.key.json', ''));
143
+ } catch {
144
+ return [];
145
+ }
146
+ }
147
+
148
+ export function decodePublicKey(base64: string): Uint8Array {
149
+ return util.decodeBase64(base64);
150
+ }