@aernoud/contactsmcp 0.1.1 → 0.2.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/index.js +48 -0
- package/dist/tools/createGroup.d.ts +4 -0
- package/dist/tools/createGroup.js +12 -0
- package/dist/tools/deleteContact.d.ts +4 -0
- package/dist/tools/deleteContact.js +21 -0
- package/dist/tools/deleteGroup.d.ts +4 -0
- package/dist/tools/deleteGroup.js +21 -0
- package/dist/tools/listGroupMembers.d.ts +1 -0
- package/dist/tools/listGroupMembers.js +37 -0
- package/dist/tools/removeFromGroup.d.ts +3 -0
- package/dist/tools/removeFromGroup.js +30 -0
- package/dist/tools/renameGroup.d.ts +5 -0
- package/dist/tools/renameGroup.js +22 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -8,6 +8,12 @@ import { createContact } from "./tools/createContact.js";
|
|
|
8
8
|
import { updateContact } from "./tools/updateContact.js";
|
|
9
9
|
import { listGroups } from "./tools/listGroups.js";
|
|
10
10
|
import { addToGroup } from "./tools/addToGroup.js";
|
|
11
|
+
import { removeFromGroup } from "./tools/removeFromGroup.js";
|
|
12
|
+
import { deleteContact } from "./tools/deleteContact.js";
|
|
13
|
+
import { createGroup } from "./tools/createGroup.js";
|
|
14
|
+
import { deleteGroup } from "./tools/deleteGroup.js";
|
|
15
|
+
import { renameGroup } from "./tools/renameGroup.js";
|
|
16
|
+
import { listGroupMembers } from "./tools/listGroupMembers.js";
|
|
11
17
|
const server = new McpServer({
|
|
12
18
|
name: "contactsmcp",
|
|
13
19
|
version: "0.1.0",
|
|
@@ -66,5 +72,47 @@ server.tool("add-to-group", "Add a contact to a group in macOS Contacts.app", {
|
|
|
66
72
|
}
|
|
67
73
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
68
74
|
});
|
|
75
|
+
server.tool("remove-from-group", "Remove a contact from a group in macOS Contacts.app", {
|
|
76
|
+
contactId: z.string().describe("Contact ID from Contacts.app"),
|
|
77
|
+
groupName: z.string().describe("Name of the group to remove the contact from"),
|
|
78
|
+
}, async ({ contactId, groupName }) => {
|
|
79
|
+
const result = await removeFromGroup(contactId, groupName);
|
|
80
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
81
|
+
});
|
|
82
|
+
server.tool("delete-contact", "Delete a contact from macOS Contacts.app", {
|
|
83
|
+
contactId: z.string().describe("Contact ID from Contacts.app"),
|
|
84
|
+
}, async ({ contactId }) => {
|
|
85
|
+
const result = await deleteContact(contactId);
|
|
86
|
+
if (!result) {
|
|
87
|
+
return { content: [{ type: "text", text: "Contact not found." }] };
|
|
88
|
+
}
|
|
89
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
90
|
+
});
|
|
91
|
+
server.tool("create-group", "Create a new contact group in macOS Contacts.app", {
|
|
92
|
+
name: z.string().describe("Name for the new group"),
|
|
93
|
+
}, async ({ name }) => {
|
|
94
|
+
const result = await createGroup(name);
|
|
95
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
96
|
+
});
|
|
97
|
+
server.tool("delete-group", "Delete a contact group from macOS Contacts.app", {
|
|
98
|
+
groupName: z.string().describe("Name of the group to delete"),
|
|
99
|
+
}, async ({ groupName }) => {
|
|
100
|
+
const result = await deleteGroup(groupName);
|
|
101
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
102
|
+
});
|
|
103
|
+
server.tool("rename-group", "Rename a contact group in macOS Contacts.app", {
|
|
104
|
+
groupName: z.string().describe("Current name of the group"),
|
|
105
|
+
newName: z.string().describe("New name for the group"),
|
|
106
|
+
}, async ({ groupName, newName }) => {
|
|
107
|
+
const result = await renameGroup(groupName, newName);
|
|
108
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
109
|
+
});
|
|
110
|
+
server.tool("list-group-members", "List all contacts in a group in macOS Contacts.app", {
|
|
111
|
+
groupName: z.string().describe("Name of the group"),
|
|
112
|
+
limit: z.number().optional().default(100).describe("Max members to return"),
|
|
113
|
+
}, async ({ groupName, limit }) => {
|
|
114
|
+
const members = await listGroupMembers(groupName, limit);
|
|
115
|
+
return { content: [{ type: "text", text: JSON.stringify(members, null, 2) }] };
|
|
116
|
+
});
|
|
69
117
|
const transport = new StdioServerTransport();
|
|
70
118
|
await server.connect(transport);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { runAppleScript, escapeForAppleScript } from "@mailappmcp/shared";
|
|
2
|
+
export async function createGroup(name) {
|
|
3
|
+
const gName = escapeForAppleScript(name);
|
|
4
|
+
const script = `
|
|
5
|
+
tell application "Contacts"
|
|
6
|
+
set newGroup to make new group with properties {name:"${gName}"}
|
|
7
|
+
save
|
|
8
|
+
return id of newGroup
|
|
9
|
+
end tell`;
|
|
10
|
+
const id = (await runAppleScript(script)).trim();
|
|
11
|
+
return { id, name };
|
|
12
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { runAppleScript, escapeForAppleScript } from "@mailappmcp/shared";
|
|
2
|
+
export async function deleteContact(contactId) {
|
|
3
|
+
const cId = escapeForAppleScript(contactId);
|
|
4
|
+
const script = `
|
|
5
|
+
tell application "Contacts"
|
|
6
|
+
set personResults to (every person whose id is "${cId}")
|
|
7
|
+
if (count of personResults) = 0 then
|
|
8
|
+
return "CONTACT_NOT_FOUND"
|
|
9
|
+
end if
|
|
10
|
+
set p to item 1 of personResults
|
|
11
|
+
delete p
|
|
12
|
+
save
|
|
13
|
+
return "deleted"
|
|
14
|
+
end tell`;
|
|
15
|
+
const result = await runAppleScript(script);
|
|
16
|
+
const trimmed = result.trim();
|
|
17
|
+
if (trimmed === "CONTACT_NOT_FOUND") {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
return { success: true, contactId };
|
|
21
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { runAppleScript, escapeForAppleScript } from "@mailappmcp/shared";
|
|
2
|
+
export async function deleteGroup(groupName) {
|
|
3
|
+
const gName = escapeForAppleScript(groupName);
|
|
4
|
+
const script = `
|
|
5
|
+
tell application "Contacts"
|
|
6
|
+
set groupResults to (every group whose name is "${gName}")
|
|
7
|
+
if (count of groupResults) = 0 then
|
|
8
|
+
return "GROUP_NOT_FOUND"
|
|
9
|
+
end if
|
|
10
|
+
set g to item 1 of groupResults
|
|
11
|
+
delete g
|
|
12
|
+
save
|
|
13
|
+
return "deleted"
|
|
14
|
+
end tell`;
|
|
15
|
+
const result = await runAppleScript(script);
|
|
16
|
+
const trimmed = result.trim();
|
|
17
|
+
if (trimmed === "GROUP_NOT_FOUND") {
|
|
18
|
+
throw new Error(`Group not found: ${groupName}`);
|
|
19
|
+
}
|
|
20
|
+
return { success: true, groupName };
|
|
21
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function listGroupMembers(groupName: string, limit?: number): Promise<Record<string, string>[]>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { runAppleScript, escapeForAppleScript, parseRecords, FIELD_SEP, RECORD_SEP } from "@mailappmcp/shared";
|
|
2
|
+
export async function listGroupMembers(groupName, limit = 100) {
|
|
3
|
+
const gName = escapeForAppleScript(groupName);
|
|
4
|
+
const maxCount = Math.floor(limit);
|
|
5
|
+
const script = `
|
|
6
|
+
tell application "Contacts"
|
|
7
|
+
set groupResults to (every group whose name is "${gName}")
|
|
8
|
+
if (count of groupResults) = 0 then
|
|
9
|
+
return "GROUP_NOT_FOUND"
|
|
10
|
+
end if
|
|
11
|
+
set g to item 1 of groupResults
|
|
12
|
+
set output to ""
|
|
13
|
+
set i to 0
|
|
14
|
+
repeat with p in people of g
|
|
15
|
+
if i >= ${maxCount} then exit repeat
|
|
16
|
+
set pId to id of p
|
|
17
|
+
set pName to name of p
|
|
18
|
+
set pEmail to ""
|
|
19
|
+
try
|
|
20
|
+
set pEmail to value of first email of p
|
|
21
|
+
end try
|
|
22
|
+
set pPhone to ""
|
|
23
|
+
try
|
|
24
|
+
set pPhone to value of first phone of p
|
|
25
|
+
end try
|
|
26
|
+
set output to output & pId & "${FIELD_SEP}" & pName & "${FIELD_SEP}" & pEmail & "${FIELD_SEP}" & pPhone & "${RECORD_SEP}"
|
|
27
|
+
set i to i + 1
|
|
28
|
+
end repeat
|
|
29
|
+
return output
|
|
30
|
+
end tell`;
|
|
31
|
+
const result = await runAppleScript(script);
|
|
32
|
+
const trimmed = result.trim();
|
|
33
|
+
if (trimmed === "GROUP_NOT_FOUND") {
|
|
34
|
+
throw new Error(`Group not found: ${groupName}`);
|
|
35
|
+
}
|
|
36
|
+
return parseRecords(trimmed, ["id", "name", "email", "phone"]);
|
|
37
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { runAppleScript, escapeForAppleScript } from "@mailappmcp/shared";
|
|
2
|
+
export async function removeFromGroup(contactId, groupName) {
|
|
3
|
+
const cId = escapeForAppleScript(contactId);
|
|
4
|
+
const gName = escapeForAppleScript(groupName);
|
|
5
|
+
const script = `
|
|
6
|
+
tell application "Contacts"
|
|
7
|
+
set personResults to (every person whose id is "${cId}")
|
|
8
|
+
if (count of personResults) = 0 then
|
|
9
|
+
return "CONTACT_NOT_FOUND"
|
|
10
|
+
end if
|
|
11
|
+
set groupResults to (every group whose name is "${gName}")
|
|
12
|
+
if (count of groupResults) = 0 then
|
|
13
|
+
return "GROUP_NOT_FOUND"
|
|
14
|
+
end if
|
|
15
|
+
set p to item 1 of personResults
|
|
16
|
+
set g to item 1 of groupResults
|
|
17
|
+
remove p from g
|
|
18
|
+
save
|
|
19
|
+
return "done"
|
|
20
|
+
end tell`;
|
|
21
|
+
const result = await runAppleScript(script);
|
|
22
|
+
const trimmed = result.trim();
|
|
23
|
+
if (trimmed === "CONTACT_NOT_FOUND") {
|
|
24
|
+
throw new Error(`Contact not found: ${contactId}`);
|
|
25
|
+
}
|
|
26
|
+
if (trimmed === "GROUP_NOT_FOUND") {
|
|
27
|
+
throw new Error(`Group not found: ${groupName}`);
|
|
28
|
+
}
|
|
29
|
+
return { success: true };
|
|
30
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { runAppleScript, escapeForAppleScript } from "@mailappmcp/shared";
|
|
2
|
+
export async function renameGroup(groupName, newName) {
|
|
3
|
+
const gName = escapeForAppleScript(groupName);
|
|
4
|
+
const gNewName = escapeForAppleScript(newName);
|
|
5
|
+
const script = `
|
|
6
|
+
tell application "Contacts"
|
|
7
|
+
set groupResults to (every group whose name is "${gName}")
|
|
8
|
+
if (count of groupResults) = 0 then
|
|
9
|
+
return "GROUP_NOT_FOUND"
|
|
10
|
+
end if
|
|
11
|
+
set g to item 1 of groupResults
|
|
12
|
+
set name of g to "${gNewName}"
|
|
13
|
+
save
|
|
14
|
+
return "renamed"
|
|
15
|
+
end tell`;
|
|
16
|
+
const result = await runAppleScript(script);
|
|
17
|
+
const trimmed = result.trim();
|
|
18
|
+
if (trimmed === "GROUP_NOT_FOUND") {
|
|
19
|
+
throw new Error(`Group not found: ${groupName}`);
|
|
20
|
+
}
|
|
21
|
+
return { success: true, oldName: groupName, newName };
|
|
22
|
+
}
|
package/package.json
CHANGED