@todoist/comms-mcp 0.0.0-stage → 11.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/LICENSE +21 -0
- package/README.md +208 -2
- package/dist/comms-tool.d.ts +14 -0
- package/dist/comms-tool.js +1 -0
- package/dist/index.d.ts +1324 -0
- package/dist/index.js +55 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.js +24 -0
- package/dist/mcp-helpers.d.ts +17 -0
- package/dist/mcp-helpers.js +40 -0
- package/dist/mcp-server.d.ts +10 -0
- package/dist/mcp-server.js +78 -0
- package/dist/tool-registry.d.ts +3 -0
- package/dist/tool-registry.js +49 -0
- package/dist/tools/build-link.d.ts +63 -0
- package/dist/tools/build-link.js +93 -0
- package/dist/tools/channel-output.d.ts +19 -0
- package/dist/tools/channel-output.js +47 -0
- package/dist/tools/create-channel.d.ts +55 -0
- package/dist/tools/create-channel.js +44 -0
- package/dist/tools/create-conversation.d.ts +49 -0
- package/dist/tools/create-conversation.js +75 -0
- package/dist/tools/create-thread.d.ts +67 -0
- package/dist/tools/create-thread.js +126 -0
- package/dist/tools/delete-object.d.ts +44 -0
- package/dist/tools/delete-object.js +84 -0
- package/dist/tools/fetch-inbox.d.ts +91 -0
- package/dist/tools/fetch-inbox.js +227 -0
- package/dist/tools/get-groups.d.ts +62 -0
- package/dist/tools/get-groups.js +116 -0
- package/dist/tools/get-mentions.d.ts +97 -0
- package/dist/tools/get-mentions.js +126 -0
- package/dist/tools/get-users.d.ts +58 -0
- package/dist/tools/get-users.js +107 -0
- package/dist/tools/get-workspaces.d.ts +61 -0
- package/dist/tools/get-workspaces.js +120 -0
- package/dist/tools/list-channels.d.ts +47 -0
- package/dist/tools/list-channels.js +89 -0
- package/dist/tools/list-conversations.d.ts +66 -0
- package/dist/tools/list-conversations.js +212 -0
- package/dist/tools/load-conversation.d.ts +92 -0
- package/dist/tools/load-conversation.js +128 -0
- package/dist/tools/load-thread.d.ts +127 -0
- package/dist/tools/load-thread.js +175 -0
- package/dist/tools/mark-done.d.ts +111 -0
- package/dist/tools/mark-done.js +275 -0
- package/dist/tools/mark-read.d.ts +73 -0
- package/dist/tools/mark-read.js +214 -0
- package/dist/tools/react.d.ts +59 -0
- package/dist/tools/react.js +97 -0
- package/dist/tools/reply.d.ts +69 -0
- package/dist/tools/reply.js +122 -0
- package/dist/tools/search-content.d.ts +103 -0
- package/dist/tools/search-content.js +138 -0
- package/dist/tools/update-channel.d.ts +53 -0
- package/dist/tools/update-channel.js +45 -0
- package/dist/tools/update-object.d.ts +52 -0
- package/dist/tools/update-object.js +161 -0
- package/dist/tools/update-user-info.d.ts +55 -0
- package/dist/tools/update-user-info.js +270 -0
- package/dist/tools/user-info.d.ts +32 -0
- package/dist/tools/user-info.js +16 -0
- package/dist/tools/user-output.d.ts +9 -0
- package/dist/tools/user-output.js +28 -0
- package/dist/usage-tracking.d.ts +23 -0
- package/dist/usage-tracking.js +41 -0
- package/dist/utils/attachments.d.ts +24 -0
- package/dist/utils/attachments.js +57 -0
- package/dist/utils/concurrency.d.ts +20 -0
- package/dist/utils/concurrency.js +24 -0
- package/dist/utils/constants.d.ts +5 -0
- package/dist/utils/constants.js +5 -0
- package/dist/utils/degrade.d.ts +81 -0
- package/dist/utils/degrade.js +149 -0
- package/dist/utils/output-schemas.d.ts +1283 -0
- package/dist/utils/output-schemas.js +657 -0
- package/dist/utils/read-position.d.ts +21 -0
- package/dist/utils/read-position.js +37 -0
- package/dist/utils/search-results.d.ts +36 -0
- package/dist/utils/search-results.js +82 -0
- package/dist/utils/target-types.d.ts +114 -0
- package/dist/utils/target-types.js +58 -0
- package/dist/utils/test-helpers.d.ts +95 -0
- package/dist/utils/test-helpers.js +231 -0
- package/dist/utils/tool-names.d.ts +25 -0
- package/dist/utils/tool-names.js +25 -0
- package/dist/utils/url-helpers.d.ts +3 -0
- package/dist/utils/url-helpers.js +10 -0
- package/dist/utils/user-status.d.ts +8 -0
- package/dist/utils/user-status.js +33 -0
- package/dist/utils/validate-comms-token.d.ts +13 -0
- package/dist/utils/validate-comms-token.js +22 -0
- package/package.json +86 -5
package/dist/index.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { getMcpServer } from './mcp-server.js';
|
|
2
|
+
import { buildLink } from './tools/build-link.js';
|
|
3
|
+
import { createChannel } from './tools/create-channel.js';
|
|
4
|
+
import { createConversation } from './tools/create-conversation.js';
|
|
5
|
+
import { createThread } from './tools/create-thread.js';
|
|
6
|
+
import { deleteObject } from './tools/delete-object.js';
|
|
7
|
+
import { fetchInbox } from './tools/fetch-inbox.js';
|
|
8
|
+
import { getGroups } from './tools/get-groups.js';
|
|
9
|
+
import { getMentions } from './tools/get-mentions.js';
|
|
10
|
+
import { getUsers } from './tools/get-users.js';
|
|
11
|
+
import { getWorkspaces } from './tools/get-workspaces.js';
|
|
12
|
+
import { listChannels } from './tools/list-channels.js';
|
|
13
|
+
import { listConversations } from './tools/list-conversations.js';
|
|
14
|
+
import { loadConversation } from './tools/load-conversation.js';
|
|
15
|
+
import { loadThread } from './tools/load-thread.js';
|
|
16
|
+
import { markDone } from './tools/mark-done.js';
|
|
17
|
+
import { markRead } from './tools/mark-read.js';
|
|
18
|
+
import { react } from './tools/react.js';
|
|
19
|
+
import { reply } from './tools/reply.js';
|
|
20
|
+
import { searchContent } from './tools/search-content.js';
|
|
21
|
+
import { updateChannel } from './tools/update-channel.js';
|
|
22
|
+
import { updateObject } from './tools/update-object.js';
|
|
23
|
+
import { updateUserInfo } from './tools/update-user-info.js';
|
|
24
|
+
import { userInfo } from './tools/user-info.js';
|
|
25
|
+
import { validateCommsToken } from './utils/validate-comms-token.js';
|
|
26
|
+
const tools = {
|
|
27
|
+
userInfo,
|
|
28
|
+
updateUserInfo,
|
|
29
|
+
fetchInbox,
|
|
30
|
+
loadThread,
|
|
31
|
+
loadConversation,
|
|
32
|
+
searchContent,
|
|
33
|
+
getMentions,
|
|
34
|
+
createThread,
|
|
35
|
+
createConversation,
|
|
36
|
+
updateObject,
|
|
37
|
+
deleteObject,
|
|
38
|
+
reply,
|
|
39
|
+
react,
|
|
40
|
+
markDone,
|
|
41
|
+
markRead,
|
|
42
|
+
buildLink,
|
|
43
|
+
listChannels,
|
|
44
|
+
listConversations,
|
|
45
|
+
getGroups,
|
|
46
|
+
getUsers,
|
|
47
|
+
getWorkspaces,
|
|
48
|
+
createChannel,
|
|
49
|
+
updateChannel,
|
|
50
|
+
};
|
|
51
|
+
export { tools, getMcpServer, validateCommsToken };
|
|
52
|
+
// Re-exported so consumers of the exported tools can catch the API's errors without taking a
|
|
53
|
+
// direct @doist/comms-sdk dependency.
|
|
54
|
+
export { CommsRequestError } from '@doist/comms-sdk';
|
|
55
|
+
export { userInfo, updateUserInfo, fetchInbox, loadThread, loadConversation, searchContent, getMentions, createThread, createConversation, updateObject, deleteObject, reply, react, markDone, markRead, buildLink, listChannels, listConversations, getGroups, getUsers, getWorkspaces, createChannel, updateChannel, };
|
package/dist/main.d.ts
ADDED
package/dist/main.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { StdioServerTransport } from '@modelcontextprotocol/server/stdio';
|
|
3
|
+
import dotenv from 'dotenv';
|
|
4
|
+
import { getMcpServer } from './mcp-server.js';
|
|
5
|
+
function main() {
|
|
6
|
+
const commsApiKey = process.env.COMMS_API_KEY;
|
|
7
|
+
if (!commsApiKey) {
|
|
8
|
+
throw new Error('COMMS_API_KEY is not set');
|
|
9
|
+
}
|
|
10
|
+
const server = getMcpServer({ token: commsApiKey });
|
|
11
|
+
const transport = new StdioServerTransport();
|
|
12
|
+
server
|
|
13
|
+
.connect(transport)
|
|
14
|
+
.then(() => {
|
|
15
|
+
// We use console.error because standard I/O is being used for the MCP server communication.
|
|
16
|
+
console.error('Server started');
|
|
17
|
+
})
|
|
18
|
+
.catch((error) => {
|
|
19
|
+
console.error('Error starting the Comms MCP server:', error);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
dotenv.config({ quiet: true });
|
|
24
|
+
main();
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { CommsApi } from '@doist/comms-sdk';
|
|
2
|
+
import type { McpServer } from '@modelcontextprotocol/server';
|
|
3
|
+
import type { AnyCommsTool } from './comms-tool.js';
|
|
4
|
+
/**
|
|
5
|
+
* Registers Comms tools on an MCP server, bound to one Comms API client.
|
|
6
|
+
*
|
|
7
|
+
* Schema caching, annotations, result shaping and turning a thrown error into an error result
|
|
8
|
+
* come from `@todoist/mcp-core`; its default error log names the tool and never its arguments.
|
|
9
|
+
* Each call runs as its tool (`runAsTool`), so the requests it makes carry the tool's name in
|
|
10
|
+
* their usage-tracking headers.
|
|
11
|
+
*/
|
|
12
|
+
declare function registerTools({ tools, server, client, }: {
|
|
13
|
+
tools: readonly AnyCommsTool[];
|
|
14
|
+
server: McpServer;
|
|
15
|
+
client: CommsApi;
|
|
16
|
+
}): void;
|
|
17
|
+
export { registerTools };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { createToolRegistrar } from '@todoist/mcp-core';
|
|
2
|
+
import { runAsTool } from './usage-tracking.js';
|
|
3
|
+
/**
|
|
4
|
+
* Whether to return the structured content directly, vs. in the `content` part of the output.
|
|
5
|
+
*
|
|
6
|
+
* The `structuredContent` part of the output is relatively new in the spec, and it's not yet
|
|
7
|
+
* supported by all clients. This flag controls whether we return the structured content using this
|
|
8
|
+
* new feature of the MCP protocol or not.
|
|
9
|
+
*
|
|
10
|
+
* If `false`, the `structuredContent` is also returned as stringified JSON in one of the `content`
|
|
11
|
+
* parts, ahead of the summary.
|
|
12
|
+
*
|
|
13
|
+
* Eventually we should be able to remove this, and change the code to always work with the
|
|
14
|
+
* structured content returned directly, once most or all MCP clients support it.
|
|
15
|
+
*/
|
|
16
|
+
const USE_STRUCTURED_CONTENT = process.env.USE_STRUCTURED_CONTENT === 'true' || process.env.NODE_ENV === 'test';
|
|
17
|
+
function formatError(error) {
|
|
18
|
+
return error instanceof Error ? error.message : 'An unknown error occurred';
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Registers Comms tools on an MCP server, bound to one Comms API client.
|
|
22
|
+
*
|
|
23
|
+
* Schema caching, annotations, result shaping and turning a thrown error into an error result
|
|
24
|
+
* come from `@todoist/mcp-core`; its default error log names the tool and never its arguments.
|
|
25
|
+
* Each call runs as its tool (`runAsTool`), so the requests it makes carry the tool's name in
|
|
26
|
+
* their usage-tracking headers.
|
|
27
|
+
*/
|
|
28
|
+
function registerTools({ tools, server, client, }) {
|
|
29
|
+
const register = createToolRegistrar({
|
|
30
|
+
titlePrefix: 'Comms',
|
|
31
|
+
createContext: () => client,
|
|
32
|
+
wrapExecute: (tool, run) => runAsTool(tool.name, run),
|
|
33
|
+
formatError,
|
|
34
|
+
structuredContentAsText: !USE_STRUCTURED_CONTENT,
|
|
35
|
+
});
|
|
36
|
+
for (const tool of tools) {
|
|
37
|
+
register(server, tool);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export { registerTools };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/server';
|
|
2
|
+
import type { McpServerOptions } from '@todoist/mcp-core/host';
|
|
3
|
+
/**
|
|
4
|
+
* Create the MCP server.
|
|
5
|
+
* @param token - The user's Comms API token.
|
|
6
|
+
* @param baseUrl - Optional base URL for the Comms API.
|
|
7
|
+
* @returns the MCP server.
|
|
8
|
+
*/
|
|
9
|
+
declare function getMcpServer({ token, baseUrl }: McpServerOptions): McpServer;
|
|
10
|
+
export { getMcpServer };
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/server';
|
|
2
|
+
import { registerTools } from './mcp-helpers.js';
|
|
3
|
+
import { registeredTools } from './tool-registry.js';
|
|
4
|
+
import { COMMS_MCP_VERSION, createCommsClient } from './usage-tracking.js';
|
|
5
|
+
const instructions = `
|
|
6
|
+
## Comms Communication Tools
|
|
7
|
+
|
|
8
|
+
You have access to comprehensive Comms management tools for team communication and collaboration. Use these tools to help users manage threads, messages, channels, and team interactions effectively.
|
|
9
|
+
|
|
10
|
+
### Core Capabilities:
|
|
11
|
+
- Create and manage conversations and threads
|
|
12
|
+
- Send and update messages
|
|
13
|
+
- Organize channels and workspaces
|
|
14
|
+
- Handle team communication workflows
|
|
15
|
+
|
|
16
|
+
### Tool Usage Guidelines:
|
|
17
|
+
|
|
18
|
+
- **fetch-inbox**: Use to fetch inbox threads for a workspace, along with unread conversations and counts. Supports archiveFilter values of active, archived, or all; use all when the user needs both open and done threads. Optionally set onlyUnread to focus on unread items.
|
|
19
|
+
- **list-channels**: Use to discover channels in a workspace. Requires a workspace ID. Optionally set includeArchived to true to also list archived channels. Returns channel names, IDs, descriptions, visibility, archive status, and URLs.
|
|
20
|
+
- **list-conversations**: Use to discover conversations (direct messages and group chats) in a workspace. Requires a workspace ID. Without searchText, returns the first page of conversations. Set searchText to find a group chat by title: it matches whole words in the titles of titled group chats only, so direct messages, untitled group chats, and participant names do not match. Its results are paged with limit and cursor, and a page can have fewer results than limit, so follow hasMore. Optionally set includeArchived to true to also list archived conversations. Returns conversation IDs, titles, a partial list of participant user IDs and names, archive status, last-active timestamps, snippets, and URLs.
|
|
21
|
+
- **get-groups**: Use to discover group IDs in a workspace before notifying groups from tools that support group notifications. Requires a workspace ID. Optionally filter by group IDs or search text. Returns group IDs, names, and member counts (descriptions are never returned). Set includeMembers to true to also get each member's user ID, name, and email — use that to answer "who is in this group?" or to expand a group into individual recipients; leave it off when you only need IDs, since member lists make the response much longer.
|
|
22
|
+
- **create-channel**: Use to create a channel in a workspace. Pass workspaceId, name, and optional description or public; omitting public creates a private channel. Pass numeric userIds to add initial members.
|
|
23
|
+
- **update-channel**: Use to rename a channel or update its description/visibility. Pass channelId and at least one of name, description, or public. Pass description: null to clear the description.
|
|
24
|
+
- **create-thread**: Use to create a new channel thread. Optionally pass recipients for user IDs and groups for group IDs; call get-users or get-groups first when resolving names. To notify everyone in the channel, pass notifyAudience: "channel" (tags the thread with "Everyone in channel") instead of listing every member as recipients; "thread" has no effect at thread creation. Pass displayInInbox: true to unarchive the thread after creation so it appears in the author's own Inbox (defaults to false).
|
|
25
|
+
- **create-conversation**: Use to start a direct or group conversation with one or more users and post an initial message. Pass workspaceId, recipients (user IDs, excluding yourself), and content; call get-users first when resolving names. Reuses an existing conversation when one already exists for the same set of users rather than creating a duplicate.
|
|
26
|
+
- **reply**: Use to reply to a thread or conversation. Thread replies notify everyone who has interacted with the thread by default. Optionally pass recipients for user IDs or groups for group IDs to override that default, and/or notifyAudience ("channel" | "thread") to add a broader audience on top of recipients/groups. Passing groups or notifyAudience to a conversation reply is rejected.
|
|
27
|
+
- **mark-read**: Use to clear unread markers on threads and/or conversations while leaving them in the inbox. Pass workspaceId plus threadIds and/or conversationIds, or all: true to mark everything unread in the workspace. Items that are not currently unread are skipped and reported as alreadyRead. Use mark-done instead when the user wants items archived (done) rather than just read.
|
|
28
|
+
- **get-mentions**: Use to fetch threads, comments, and messages that mention the current user. Prefer this over search-content when no keyword query is needed (search-content requires a non-empty query). Supports filtering by channel, author, and date range, and exposes a cursor for pagination.
|
|
29
|
+
- **update-object**: Use to edit something you previously sent. Pass targetType ("thread", "comment", or "message"), targetId, and the new content. For threads you may also pass title (and may pass title without content). title is only valid for threads.
|
|
30
|
+
- **delete-object**: Use to permanently delete a thread, comment, or conversation message. Pass targetType ("thread", "comment", or "message") and targetId. Deletion is irreversible — confirm with the user before invoking. Deleting a thread also removes all of its comments. Only the object's creator or a workspace admin can delete; the Comms API will reject the call otherwise.
|
|
31
|
+
- **update-user-info**: A status given replaces the whole status, so call user-info first when the user only wants to change part of it. Pass status: null to remove it.
|
|
32
|
+
|
|
33
|
+
### Writing Content (threads, comments, and messages):
|
|
34
|
+
|
|
35
|
+
Content is markdown. Mentions and references are markdown links using \`comms-\` URL schemes — anything else renders as literal text:
|
|
36
|
+
|
|
37
|
+
- User mention: \`[Afzal](comms-mention://29367677)\` — the ID must be numeric
|
|
38
|
+
- Group mention: \`[Design](comms-group-mention://<groupId>)\`
|
|
39
|
+
- Channel reference: \`[#general](comms-channel://<channelId>)\`
|
|
40
|
+
- Thread reference: \`[Q3 planning](comms-thread://<threadId>)\`
|
|
41
|
+
|
|
42
|
+
Rules:
|
|
43
|
+
|
|
44
|
+
1. Never write \`@Name\`, \`[[Name|id]]\`, or any other mention format. Only the link syntax above is parsed; everything else is posted as plain text.
|
|
45
|
+
2. Never put \`@\` inside the label — the client adds it when rendering. \`[Afzal](comms-mention://29367677)\` renders as \`@Afzal\`.
|
|
46
|
+
3. The label is a display fallback only. The client resolves the current name from the ID, so the ID must be correct; a wrong ID mentions the wrong person no matter what the label says.
|
|
47
|
+
4. Resolve IDs with get-users, get-groups, or list-channels before writing content. Never guess an ID.
|
|
48
|
+
5. Mentions control rendering, not delivery, and notification support varies by tool. create-thread and thread replies take \`recipients\`, \`groups\`, and \`notifyAudience\` — pass a mentioned user's numeric ID (or group ID) there to actually notify them. Conversation messages (create-conversation, conversation replies) notify all participants automatically and take no notification parameters — a mentioned person must be a participant to be notified. update-object has no notification parameters; adding a mention while editing does not notify anyone.
|
|
49
|
+
|
|
50
|
+
### Best Practices:
|
|
51
|
+
|
|
52
|
+
1. **Communication**: Write clear, professional messages. Consider context and audience.
|
|
53
|
+
|
|
54
|
+
2. **Organization**: Use appropriate channels and threads for different topics.
|
|
55
|
+
|
|
56
|
+
3. **Collaboration**: Respect team communication patterns and workflows.
|
|
57
|
+
|
|
58
|
+
Always provide clear context and maintain professional communication standards.
|
|
59
|
+
`;
|
|
60
|
+
/**
|
|
61
|
+
* Create the MCP server.
|
|
62
|
+
* @param token - The user's Comms API token.
|
|
63
|
+
* @param baseUrl - Optional base URL for the Comms API.
|
|
64
|
+
* @returns the MCP server.
|
|
65
|
+
*/
|
|
66
|
+
function getMcpServer({ token, baseUrl }) {
|
|
67
|
+
const server = new McpServer({ name: 'comms-mcp-server', version: COMMS_MCP_VERSION }, {
|
|
68
|
+
capabilities: {
|
|
69
|
+
tools: { listChanged: true },
|
|
70
|
+
},
|
|
71
|
+
instructions,
|
|
72
|
+
});
|
|
73
|
+
// Every request it makes carries the usage-tracking headers, named for the tool that made it.
|
|
74
|
+
const comms = createCommsClient({ token, baseUrl });
|
|
75
|
+
registerTools({ tools: registeredTools, server, client: comms });
|
|
76
|
+
return server;
|
|
77
|
+
}
|
|
78
|
+
export { getMcpServer };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { buildLink } from './tools/build-link.js';
|
|
2
|
+
import { createChannel } from './tools/create-channel.js';
|
|
3
|
+
import { createConversation } from './tools/create-conversation.js';
|
|
4
|
+
import { createThread } from './tools/create-thread.js';
|
|
5
|
+
import { deleteObject } from './tools/delete-object.js';
|
|
6
|
+
import { fetchInbox } from './tools/fetch-inbox.js';
|
|
7
|
+
import { getGroups } from './tools/get-groups.js';
|
|
8
|
+
import { getMentions } from './tools/get-mentions.js';
|
|
9
|
+
import { getUsers } from './tools/get-users.js';
|
|
10
|
+
import { getWorkspaces } from './tools/get-workspaces.js';
|
|
11
|
+
import { listChannels } from './tools/list-channels.js';
|
|
12
|
+
import { listConversations } from './tools/list-conversations.js';
|
|
13
|
+
import { loadConversation } from './tools/load-conversation.js';
|
|
14
|
+
import { loadThread } from './tools/load-thread.js';
|
|
15
|
+
import { markDone } from './tools/mark-done.js';
|
|
16
|
+
import { markRead } from './tools/mark-read.js';
|
|
17
|
+
import { react } from './tools/react.js';
|
|
18
|
+
import { reply } from './tools/reply.js';
|
|
19
|
+
import { searchContent } from './tools/search-content.js';
|
|
20
|
+
import { updateChannel } from './tools/update-channel.js';
|
|
21
|
+
import { updateObject } from './tools/update-object.js';
|
|
22
|
+
import { updateUserInfo } from './tools/update-user-info.js';
|
|
23
|
+
import { userInfo } from './tools/user-info.js';
|
|
24
|
+
/** Every tool the MCP server registers, in registration order. */
|
|
25
|
+
export const registeredTools = [
|
|
26
|
+
userInfo,
|
|
27
|
+
updateUserInfo,
|
|
28
|
+
getWorkspaces,
|
|
29
|
+
getUsers,
|
|
30
|
+
getGroups,
|
|
31
|
+
fetchInbox,
|
|
32
|
+
loadThread,
|
|
33
|
+
loadConversation,
|
|
34
|
+
searchContent,
|
|
35
|
+
getMentions,
|
|
36
|
+
buildLink,
|
|
37
|
+
createChannel,
|
|
38
|
+
updateChannel,
|
|
39
|
+
createThread,
|
|
40
|
+
createConversation,
|
|
41
|
+
updateObject,
|
|
42
|
+
deleteObject,
|
|
43
|
+
reply,
|
|
44
|
+
react,
|
|
45
|
+
markDone,
|
|
46
|
+
markRead,
|
|
47
|
+
listChannels,
|
|
48
|
+
listConversations,
|
|
49
|
+
];
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
type BuildLinkStructured = {
|
|
3
|
+
type: 'link_data';
|
|
4
|
+
url: string;
|
|
5
|
+
linkType: 'conversation' | 'message' | 'thread' | 'comment';
|
|
6
|
+
params: {
|
|
7
|
+
workspaceId: number;
|
|
8
|
+
conversationId?: string;
|
|
9
|
+
messageId?: string;
|
|
10
|
+
channelId?: string;
|
|
11
|
+
threadId?: string;
|
|
12
|
+
commentId?: string;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
declare const buildLink: {
|
|
16
|
+
name: "build-link";
|
|
17
|
+
description: string;
|
|
18
|
+
parameters: {
|
|
19
|
+
workspaceId: z.ZodNumber;
|
|
20
|
+
conversationId: z.ZodOptional<z.ZodString>;
|
|
21
|
+
messageId: z.ZodOptional<z.ZodString>;
|
|
22
|
+
channelId: z.ZodOptional<z.ZodString>;
|
|
23
|
+
threadId: z.ZodOptional<z.ZodString>;
|
|
24
|
+
commentId: z.ZodOptional<z.ZodString>;
|
|
25
|
+
fullUrl: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
26
|
+
};
|
|
27
|
+
outputSchema: {
|
|
28
|
+
type: z.ZodLiteral<"link_data">;
|
|
29
|
+
url: z.ZodString;
|
|
30
|
+
linkType: z.ZodEnum<{
|
|
31
|
+
thread: "thread";
|
|
32
|
+
comment: "comment";
|
|
33
|
+
message: "message";
|
|
34
|
+
conversation: "conversation";
|
|
35
|
+
}>;
|
|
36
|
+
params: z.ZodObject<{
|
|
37
|
+
workspaceId: z.ZodNumber;
|
|
38
|
+
conversationId: z.ZodOptional<z.ZodString>;
|
|
39
|
+
messageId: z.ZodOptional<z.ZodString>;
|
|
40
|
+
channelId: z.ZodOptional<z.ZodString>;
|
|
41
|
+
threadId: z.ZodOptional<z.ZodString>;
|
|
42
|
+
commentId: z.ZodOptional<z.ZodString>;
|
|
43
|
+
}, z.core.$strip>;
|
|
44
|
+
};
|
|
45
|
+
annotations: {
|
|
46
|
+
readOnlyHint: true;
|
|
47
|
+
destructiveHint: false;
|
|
48
|
+
idempotentHint: true;
|
|
49
|
+
};
|
|
50
|
+
execute(args: {
|
|
51
|
+
workspaceId: number;
|
|
52
|
+
fullUrl: boolean;
|
|
53
|
+
conversationId?: string | undefined;
|
|
54
|
+
messageId?: string | undefined;
|
|
55
|
+
channelId?: string | undefined;
|
|
56
|
+
threadId?: string | undefined;
|
|
57
|
+
commentId?: string | undefined;
|
|
58
|
+
}, _client: import("@doist/comms-sdk").CommsApi): Promise<{
|
|
59
|
+
textContent: string;
|
|
60
|
+
structuredContent: BuildLinkStructured;
|
|
61
|
+
}>;
|
|
62
|
+
};
|
|
63
|
+
export { buildLink, type BuildLinkStructured };
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { getCommentURL, getCommsURL, getFullCommsURL, getMessageURL } from '@doist/comms-sdk';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { BuildLinkOutputSchema } from '../utils/output-schemas.js';
|
|
4
|
+
import { ToolNames } from '../utils/tool-names.js';
|
|
5
|
+
const ArgsSchema = {
|
|
6
|
+
workspaceId: z.number().describe('The workspace ID.'),
|
|
7
|
+
conversationId: z
|
|
8
|
+
.string()
|
|
9
|
+
.optional()
|
|
10
|
+
.describe('The conversation ID (for direct message links).'),
|
|
11
|
+
messageId: z
|
|
12
|
+
.string()
|
|
13
|
+
.optional()
|
|
14
|
+
.describe('The message ID (for specific message links within a conversation).'),
|
|
15
|
+
channelId: z.string().optional().describe('The channel ID (for thread links in channels).'),
|
|
16
|
+
threadId: z.string().optional().describe('The thread ID (for thread/comment links).'),
|
|
17
|
+
commentId: z
|
|
18
|
+
.string()
|
|
19
|
+
.optional()
|
|
20
|
+
.describe('The comment ID (for specific comment links within a thread).'),
|
|
21
|
+
fullUrl: z
|
|
22
|
+
.boolean()
|
|
23
|
+
.optional()
|
|
24
|
+
.default(true)
|
|
25
|
+
.describe('Whether to return a full URL (with https://comms.todoist.com) or relative path.'),
|
|
26
|
+
};
|
|
27
|
+
const buildLink = {
|
|
28
|
+
name: ToolNames.BUILD_LINK,
|
|
29
|
+
description: 'Build valid Comms URLs for threads, comments, conversations, or messages. Provide workspace_id and either (conversation_id + optional message_id) OR (thread_id + optional channel_id + optional comment_id).',
|
|
30
|
+
parameters: ArgsSchema,
|
|
31
|
+
outputSchema: BuildLinkOutputSchema.shape,
|
|
32
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
33
|
+
async execute(args, _client) {
|
|
34
|
+
const { workspaceId, conversationId, messageId, channelId, threadId, commentId, fullUrl } = args;
|
|
35
|
+
let url;
|
|
36
|
+
let linkType;
|
|
37
|
+
// Determine link type and build URL
|
|
38
|
+
if (conversationId !== undefined) {
|
|
39
|
+
if (messageId !== undefined) {
|
|
40
|
+
// Message link
|
|
41
|
+
linkType = 'message';
|
|
42
|
+
const params = { workspaceId, conversationId, messageId };
|
|
43
|
+
url = fullUrl ? getFullCommsURL(params) : getMessageURL(params);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
// Conversation link
|
|
47
|
+
linkType = 'conversation';
|
|
48
|
+
const params = { workspaceId, conversationId };
|
|
49
|
+
url = fullUrl ? getFullCommsURL(params) : getCommsURL(params);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
else if (threadId !== undefined) {
|
|
53
|
+
if (commentId !== undefined) {
|
|
54
|
+
// Comment link
|
|
55
|
+
linkType = 'comment';
|
|
56
|
+
if (channelId === undefined) {
|
|
57
|
+
throw new Error('channelId is required when building a comment link');
|
|
58
|
+
}
|
|
59
|
+
const params = { workspaceId, channelId, threadId, commentId };
|
|
60
|
+
url = fullUrl ? getFullCommsURL(params) : getCommentURL(params);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
// Thread link
|
|
64
|
+
linkType = 'thread';
|
|
65
|
+
const params = channelId
|
|
66
|
+
? { workspaceId, channelId, threadId }
|
|
67
|
+
: { workspaceId, threadId };
|
|
68
|
+
url = fullUrl ? getFullCommsURL(params) : getCommsURL(params);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
throw new Error('Must provide either conversationId OR threadId to build a link');
|
|
73
|
+
}
|
|
74
|
+
const structuredContent = {
|
|
75
|
+
type: 'link_data',
|
|
76
|
+
url,
|
|
77
|
+
linkType,
|
|
78
|
+
params: {
|
|
79
|
+
workspaceId,
|
|
80
|
+
conversationId,
|
|
81
|
+
messageId,
|
|
82
|
+
channelId,
|
|
83
|
+
threadId,
|
|
84
|
+
commentId,
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
return {
|
|
88
|
+
textContent: url,
|
|
89
|
+
structuredContent,
|
|
90
|
+
};
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
export { buildLink };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Channel } from '@doist/comms-sdk';
|
|
2
|
+
import type { ChannelOutputFields } from '../utils/output-schemas.js';
|
|
3
|
+
type ChannelListData = ChannelOutputFields & {
|
|
4
|
+
id: string;
|
|
5
|
+
creatorId: number;
|
|
6
|
+
creatorName?: string;
|
|
7
|
+
color?: number;
|
|
8
|
+
};
|
|
9
|
+
type ChannelMutationData = ChannelOutputFields & {
|
|
10
|
+
channelId: string;
|
|
11
|
+
workspaceId: number;
|
|
12
|
+
creator: number;
|
|
13
|
+
userIds?: number[];
|
|
14
|
+
};
|
|
15
|
+
declare function getChannelOutputFields(channel: Channel): ChannelOutputFields;
|
|
16
|
+
declare function getChannelMutationData(channel: Channel): ChannelMutationData;
|
|
17
|
+
declare function getChannelMutationText(title: string, channel: Channel): string;
|
|
18
|
+
declare function getChannelListData(channel: Channel, creatorName?: string): ChannelListData;
|
|
19
|
+
export { getChannelListData, getChannelMutationData, getChannelMutationText, getChannelOutputFields, type ChannelListData, };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { getChannelUrl } from '../utils/url-helpers.js';
|
|
2
|
+
function getChannelOutputFields(channel) {
|
|
3
|
+
return {
|
|
4
|
+
name: channel.name,
|
|
5
|
+
...(channel.description != null ? { description: channel.description } : {}),
|
|
6
|
+
public: channel.public,
|
|
7
|
+
archived: channel.archived,
|
|
8
|
+
created: channel.created.toISOString(),
|
|
9
|
+
channelUrl: channel.url ?? getChannelUrl(channel.workspaceId, channel.id),
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
function getChannelMutationData(channel) {
|
|
13
|
+
return {
|
|
14
|
+
channelId: channel.id,
|
|
15
|
+
workspaceId: channel.workspaceId,
|
|
16
|
+
creator: channel.creator,
|
|
17
|
+
...getChannelOutputFields(channel),
|
|
18
|
+
...(channel.userIds != null ? { userIds: channel.userIds } : {}),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function getChannelMutationText(title, channel) {
|
|
22
|
+
const channelData = getChannelMutationData(channel);
|
|
23
|
+
const lines = [
|
|
24
|
+
`# ${title}`,
|
|
25
|
+
'',
|
|
26
|
+
`**Name:** ${channelData.name}`,
|
|
27
|
+
`**Channel ID:** ${channelData.channelId}`,
|
|
28
|
+
`**Workspace ID:** ${channelData.workspaceId}`,
|
|
29
|
+
`**Public:** ${channelData.public ? 'Yes' : 'No'}`,
|
|
30
|
+
`**Created:** ${channelData.created}`,
|
|
31
|
+
`**URL:** ${channelData.channelUrl}`,
|
|
32
|
+
];
|
|
33
|
+
if (channelData.description != null) {
|
|
34
|
+
lines.push(`**Description:** ${channelData.description}`);
|
|
35
|
+
}
|
|
36
|
+
return lines.join('\n');
|
|
37
|
+
}
|
|
38
|
+
function getChannelListData(channel, creatorName) {
|
|
39
|
+
return {
|
|
40
|
+
id: channel.id,
|
|
41
|
+
...getChannelOutputFields(channel),
|
|
42
|
+
creatorId: channel.creator,
|
|
43
|
+
...(creatorName ? { creatorName } : {}),
|
|
44
|
+
...(channel.color != null ? { color: channel.color } : {}),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export { getChannelListData, getChannelMutationData, getChannelMutationText, getChannelOutputFields, };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
declare const createChannel: {
|
|
3
|
+
name: "create-channel";
|
|
4
|
+
description: string;
|
|
5
|
+
parameters: {
|
|
6
|
+
workspaceId: z.ZodNumber;
|
|
7
|
+
name: z.ZodString;
|
|
8
|
+
description: z.ZodOptional<z.ZodString>;
|
|
9
|
+
public: z.ZodOptional<z.ZodBoolean>;
|
|
10
|
+
userIds: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
|
|
11
|
+
};
|
|
12
|
+
outputSchema: {
|
|
13
|
+
name: z.ZodString;
|
|
14
|
+
description: z.ZodOptional<z.ZodString>;
|
|
15
|
+
public: z.ZodBoolean;
|
|
16
|
+
archived: z.ZodBoolean;
|
|
17
|
+
created: z.ZodString;
|
|
18
|
+
channelUrl: z.ZodString;
|
|
19
|
+
type: z.ZodLiteral<"create_channel_result">;
|
|
20
|
+
success: z.ZodBoolean;
|
|
21
|
+
channelId: z.ZodString;
|
|
22
|
+
workspaceId: z.ZodNumber;
|
|
23
|
+
creator: z.ZodNumber;
|
|
24
|
+
userIds: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
|
|
25
|
+
};
|
|
26
|
+
annotations: {
|
|
27
|
+
readOnlyHint: false;
|
|
28
|
+
destructiveHint: false;
|
|
29
|
+
idempotentHint: false;
|
|
30
|
+
};
|
|
31
|
+
execute(args: {
|
|
32
|
+
workspaceId: number;
|
|
33
|
+
name: string;
|
|
34
|
+
description?: string | undefined;
|
|
35
|
+
public?: boolean | undefined;
|
|
36
|
+
userIds?: number[] | undefined;
|
|
37
|
+
}, client: import("@doist/comms-sdk").CommsApi): Promise<{
|
|
38
|
+
textContent: string;
|
|
39
|
+
structuredContent: {
|
|
40
|
+
name: string;
|
|
41
|
+
public: boolean;
|
|
42
|
+
archived: boolean;
|
|
43
|
+
created: string;
|
|
44
|
+
channelUrl: string;
|
|
45
|
+
type: "create_channel_result";
|
|
46
|
+
success: boolean;
|
|
47
|
+
channelId: string;
|
|
48
|
+
workspaceId: number;
|
|
49
|
+
creator: number;
|
|
50
|
+
description?: string | undefined;
|
|
51
|
+
userIds?: number[] | undefined;
|
|
52
|
+
};
|
|
53
|
+
}>;
|
|
54
|
+
};
|
|
55
|
+
export { createChannel };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { CreateChannelOutputSchema } from '../utils/output-schemas.js';
|
|
3
|
+
import { ToolNames } from '../utils/tool-names.js';
|
|
4
|
+
import { getChannelMutationData, getChannelMutationText } from './channel-output.js';
|
|
5
|
+
const ArgsSchema = {
|
|
6
|
+
workspaceId: z.number().describe('The workspace ID where the channel should be created.'),
|
|
7
|
+
name: z.string().min(1).describe('The channel name.'),
|
|
8
|
+
description: z.string().optional().describe('Optional channel description.'),
|
|
9
|
+
public: z
|
|
10
|
+
.boolean()
|
|
11
|
+
.optional()
|
|
12
|
+
.describe('Whether the channel is public. Defaults to false (private) when omitted.'),
|
|
13
|
+
userIds: z
|
|
14
|
+
.array(z.number())
|
|
15
|
+
.optional()
|
|
16
|
+
.describe('Optional numeric user IDs to add to the channel when it is created.'),
|
|
17
|
+
};
|
|
18
|
+
const createChannel = {
|
|
19
|
+
name: ToolNames.CREATE_CHANNEL,
|
|
20
|
+
description: 'Create a channel in a workspace. Requires a workspace ID and channel name. Optionally set description, visibility, and initial user IDs.',
|
|
21
|
+
parameters: ArgsSchema,
|
|
22
|
+
outputSchema: CreateChannelOutputSchema.shape,
|
|
23
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false },
|
|
24
|
+
async execute(args, client) {
|
|
25
|
+
const { workspaceId, name, description, public: isPublic, userIds } = args;
|
|
26
|
+
const channel = await client.channels.createChannel({
|
|
27
|
+
workspaceId,
|
|
28
|
+
name,
|
|
29
|
+
...(description !== undefined ? { description } : {}),
|
|
30
|
+
...(isPublic !== undefined ? { public: isPublic } : {}),
|
|
31
|
+
...(userIds !== undefined ? { userIds } : {}),
|
|
32
|
+
});
|
|
33
|
+
const structuredContent = {
|
|
34
|
+
type: 'create_channel_result',
|
|
35
|
+
success: true,
|
|
36
|
+
...getChannelMutationData(channel),
|
|
37
|
+
};
|
|
38
|
+
return {
|
|
39
|
+
textContent: getChannelMutationText('Channel Created', channel),
|
|
40
|
+
structuredContent,
|
|
41
|
+
};
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
export { createChannel };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
declare const createConversation: {
|
|
3
|
+
name: "create-conversation";
|
|
4
|
+
description: string;
|
|
5
|
+
parameters: {
|
|
6
|
+
workspaceId: z.ZodNumber;
|
|
7
|
+
recipients: z.ZodArray<z.ZodNumber>;
|
|
8
|
+
content: z.ZodString;
|
|
9
|
+
};
|
|
10
|
+
outputSchema: {
|
|
11
|
+
type: z.ZodLiteral<"create_conversation_result">;
|
|
12
|
+
success: z.ZodBoolean;
|
|
13
|
+
conversationId: z.ZodString;
|
|
14
|
+
messageId: z.ZodString;
|
|
15
|
+
workspaceId: z.ZodNumber;
|
|
16
|
+
content: z.ZodString;
|
|
17
|
+
recipients: z.ZodArray<z.ZodNumber>;
|
|
18
|
+
participants: z.ZodArray<z.ZodNumber>;
|
|
19
|
+
created: z.ZodString;
|
|
20
|
+
conversationUrl: z.ZodString;
|
|
21
|
+
messageUrl: z.ZodString;
|
|
22
|
+
};
|
|
23
|
+
annotations: {
|
|
24
|
+
readOnlyHint: false;
|
|
25
|
+
destructiveHint: false;
|
|
26
|
+
idempotentHint: false;
|
|
27
|
+
};
|
|
28
|
+
execute(args: {
|
|
29
|
+
workspaceId: number;
|
|
30
|
+
recipients: number[];
|
|
31
|
+
content: string;
|
|
32
|
+
}, client: import("@doist/comms-sdk").CommsApi): Promise<{
|
|
33
|
+
textContent: string;
|
|
34
|
+
structuredContent: {
|
|
35
|
+
type: "create_conversation_result";
|
|
36
|
+
success: boolean;
|
|
37
|
+
conversationId: string;
|
|
38
|
+
messageId: string;
|
|
39
|
+
workspaceId: number;
|
|
40
|
+
content: string;
|
|
41
|
+
recipients: number[];
|
|
42
|
+
participants: number[];
|
|
43
|
+
created: string;
|
|
44
|
+
conversationUrl: string;
|
|
45
|
+
messageUrl: string;
|
|
46
|
+
};
|
|
47
|
+
}>;
|
|
48
|
+
};
|
|
49
|
+
export { createConversation };
|