Skip to content

feat: add send-direct-message and delete-item tools#177

Open
jgalea wants to merge 1 commit intoDoist:mainfrom
jgalea:feat/dm-and-delete-tools
Open

feat: add send-direct-message and delete-item tools#177
jgalea wants to merge 1 commit intoDoist:mainfrom
jgalea:feat/dm-and-delete-tools

Conversation

@jgalea
Copy link
Copy Markdown
Contributor

@jgalea jgalea commented Apr 25, 2026

Summary

Adds two new tools that fill gaps in the current toolset:

  • send-direct-message — send a private DM to one or more workspace users. The current reply tool can post to a conversation if you already have the ID, but there is no tool that exposes a conversation ID for an arbitrary user — fetch-inbox only lists conversations with unread activity. This tool wraps conversations.getOrCreateConversation + conversationMessages.createMessage so an LLM can simply say "DM these users" without round-tripping through inbox lookups.
  • delete-item — permanently delete a thread, comment, or conversation message. Useful for cleaning up posts sent in error. Uses a unified targetType parameter (thread / comment / message), matching the pattern already used by reply and react.

Both tools surfaced when an LLM client (Claude Code via MCP) tried to send a private heads-up to a teammate after a workflow event and discovered there was no route to a 1-on-1 DM, then needed to clean up after posting in the wrong channel.

Implementation

Follows the existing add-a-tool checklist from AGENTS.md:

  • src/utils/tool-names.ts — added SEND_DIRECT_MESSAGE and DELETE_ITEM constants
  • src/utils/output-schemas.ts — added SendDirectMessageOutputSchema, DeleteItemOutputSchema, types, and union entries
  • src/tools/send-direct-message.ts and src/tools/delete-item.ts
  • src/mcp-server.ts — imports, registerTool calls, and tool usage guidelines added to the LLM instructions block
  • src/index.ts — imports, tools object, and named exports
  • scripts/run-tool.ts — imports and tools record entries
  • src/tools/__tests__/tool-annotations.test.ts — annotation expectations for both tools
  • src/tools/__tests__/send-direct-message.test.ts — new (5 tests, 1 snapshot)
  • src/tools/__tests__/delete-item.test.ts — new (6 tests, 1 snapshot)

Annotations

  • send-direct-message: readOnlyHint: false, destructiveHint: false, idempotentHint: false
  • delete-item: readOnlyHint: false, destructiveHint: true, idempotentHint: false

Notes

The send-direct-message tool's createdConversation flag uses a heuristic — messageCount <= 1 after the call means the conversation was just created (only the message we just posted exists). The Twist API doesn't surface a "newly created" indicator on getOrCreateConversation, so this is the cleanest signal available.

Test plan

  • npm test — 172/172 passing (was 161 before)
  • npm run build — clean
  • npm run format:check — clean
  • Smoke-tested both tools live against a real workspace via scripts/run-tool.ts:
    • send-direct-message created a conversation and posted to it
    • delete-item deleted a thread, then a second thread

🤖 Generated with Claude Code

Adds two new tools:

- send-direct-message: sends a private message to one or more workspace
  users by finding (or creating) the appropriate conversation. Closes a
  gap where the existing tools could only post threads in channels and
  had no way to discover conversation IDs to use with the reply tool.

- delete-item: permanently deletes a thread, comment, or conversation
  message via a unified `targetType` parameter, mirroring the shape of
  the existing reply and react tools. Useful for cleaning up posts that
  were sent in error.

Both tools follow the established structure: zod schemas in
output-schemas.ts, registration in mcp-server.ts (including LLM
guidelines), exports in index.ts, run-tool.ts entries, annotation
expectations in the existing test, and per-tool unit tests with snapshot
coverage.

Test suite: 172 passing (was 161).
@scottlovegrove scottlovegrove self-requested a review April 25, 2026 10:09
Copy link
Copy Markdown
Collaborator

@scottlovegrove scottlovegrove left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed — approve with one real fix + nits.

Issue: messageCount <= 1 heuristic in send-direct-message.ts:78-80 is wrong. getOrCreateConversation returns pre-send state, so a brand-new conversation has messageCount === 0, not 1. The comment ("the one we just sent") is also incorrect — the message hasn't been posted yet when the conversation is fetched. As written, an existing conversation with exactly 1 prior message gets flagged createdConversation: true.

Fix: change to === 0, update the comment, and add a messageCount: 1 test to lock the boundary (current tests only cover 0 and 12).

Nits:

  • wasNew=true printing "Conversation existed before: No" reads fine for the LLM but is confusing in code — consider renaming to existedBefore and flipping the ternary.
  • delete-item snapshot only covers the thread case — add comment + message snapshots, they're free and lock per-type formatting.
  • No runtime guard against including the sender in userIds. Documented in the schema description, so OK as a contract — Twist likely dedupes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants