Skip to content

feat(mcp): add create_thread capability for agent-initiated thread creation - #1532

Closed
Alexguitar-zz wants to merge 1 commit into
openabdev:mainfrom
Alexguitar-zz:feat/mcp-create-thread-tool
Closed

Alexguitar-zz wants to merge 1 commit into
openabdev:mainfrom
Alexguitar-zz:feat/mcp-create-thread-tool

Conversation

@Alexguitar-zz

@Alexguitar-zz Alexguitar-zz commented Sep 11, 2026 •

Copy link
Copy Markdown

What problem does this solve?

Agents currently have no supported way to create threads in a target channel. The only path was direct Discord REST API calls from agent exec scripts (e.g. requests.post('/channels/{id}/threads')), which has two safety issues:

  1. Identity mix-ups: agents may use the wrong bot's token (e.g. a shared DISCORD_BOT_TOKEN env var), causing threads to be created under the wrong bot's identity. Thread ownership is determined by the creating token and cannot be changed afterward.
  2. Credential exposure: tokens embedded in exec scripts leak into OpenAB logs and the LLM's context window.

This PR closes that gap by routing thread creation through the adapter's own authenticated HTTP client. The agent calls execute_capability with channel_id, name, and optional content — no token is ever exposed to the agent subprocess.

Closes #

Discord Discussion URL:

Review Contract

Goal

Agents can create threads in a target channel via the MCP facade's execute_capability without handling platform credentials. The thread owner is always the bot itself — no token is exposed to the agent subprocess.

Non-goals

  • Cross-channel thread creation for non-Discord adapters (Slack, Telegram, etc.) — this PR adds the trait method with a default "unsupported" impl; other adapters can implement it later.
  • Thread permission management or access control — the bot's existing channel permissions apply.
  • Thread archival, locking, or moderation operations — only creation is in scope.
  • Forum post creation (create_forum_post) — separate API surface, not included.

Accepted Residual Risks

  • The create_thread tool is available to all agents with access to the MCP facade. An operator who registers the facade with a Discord adapter implicitly grants thread creation to all connected agents. Mitigation: the tool only creates threads in channels the bot already has permission to; Discord's channel permission model is the enforcement boundary.
  • send_message for the starter content is a separate API call after thread creation. If it fails, the thread exists but is empty. The error is returned to the caller; no partial state is hidden.

Acceptance Criteria

  • ChatAdapter::create_thread_in_channel default impl returns "not supported" — existing adapters compile unchanged
  • DiscordAdapter::create_thread_in_channel creates a public thread via ChannelId::create_thread and sends starter content via send_message
  • ThreadSource advertises create_thread in tools() and dispatches via call() — returns (thread_id, parent_id, platform) on success, (error, is_error=true) on failure
  • Title is truncated at 100 chars with char-boundary safety for multi-byte text (中文/Emoji)
  • cargo check passes
  • cargo clippy -- -D warnings passes
  • cargo test — 18/18 binary tests pass (1 pre-existing failure in secrets::tests::resolve_exec_nonzero_exit unrelated to this PR, confirmed failing on main)

Follow-ups

  • Slack adapter implementation of create_thread_in_channel (Slack supports thread creation via conversations.create + posting the starter message)
  • Gateway adapter implementations for Telegram (topics), LINE, etc.
  • Config-driven allowlist restricting which channels agents can create threads in

At a Glance

Agent (MCP client)
  │
  ▼
execute_capability("create_thread", {channel_id, name, content})
  │
  ▼
OAB MCP Facade (loopback HTTP)
  │
  ▼
ThreadSource::call()
  │
  ▼
ChatAdapter::create_thread_in_channel()
  │
  ▼
DiscordAdapter → serenity ChannelId::create_thread() + send_message()
  │
  ▼
Discord API (bot's own token — never exposed to agent)

Prior Art & Industry Research

OpenClaw:
Not applicable — OpenClaw is a game-engine project, not a chat-to-agent harness. No thread-creation abstraction exists.

Hermes Agent:
Not applicable — Hermes Agent has been removed from OpenAB's supported agents (PR #1523). No prior thread-creation capability existed in its integration.

Other references:

  • Discord API: Start Thread without Message — POST /channels/{channel_id}/threads with name, type, and optional content fields.
  • serenity 0.12 ChannelId::create_thread — wraps the above endpoint; CreateThread builder sets name, auto-archive duration, and thread type.
  • The existing ChatAdapter::create_thread (from-message variant) already uses serenity's authenticated HTTP client; this PR adds the standalone variant using the same security model.

Proposed Solution

Add a new create_thread_in_channel method to the ChatAdapter trait with a default "unsupported" implementation, and implement it for DiscordAdapter using serenity's ChannelId::create_thread (start thread without message) followed by send_message for the starter content.

Expose this via a new ThreadSource capability source registered with the MCP facade when a Discord adapter is available. The agent calls execute_capability("create_thread", {channel_id, name, content}) — the facade dispatches to ThreadSource, which calls create_thread_in_channel on the adapter. The adapter uses its own authenticated HTTP client, so the thread owner is the bot itself.

Consolidate the shared Discord adapter construction in main.rs (previously duplicated: once for the cron scheduler, once for the MCP facade block) into a single earlier construction site.

Why this approach?

  • Trait method with default impl: backward-compatible — existing adapters compile unchanged, and other adapters can opt in later.
  • MCP facade capability source: reuses the existing CapabilitySource infrastructure; no new transport, no new config section, no per-session proxy.
  • Adapter-side token handling: the adapter's Http client is constructed at startup from the bot's config; the agent subprocess never sees it. This is the same security model as the existing create_thread (from-message) and send_message methods.
  • Two-step creation (thread + message): serenity's CreateThread builder doesn't support a content field for the "without message" variant. Creating the thread then sending a message via the existing send_message method is simpler than a raw HTTP call and reuses existing message-splitting logic.

Alternatives Considered

  1. Allow agents to call Discord REST API directly with their own token file: rejected — exposes token to the LLM context window and OpenAB logs; no way to technically enforce "only read your own token" since all token files are owned by the same OS user.
  2. Add a new ACP method (e.g. session/create_thread): rejected — ACP is an agent-protocol surface, not a platform-adapter surface; thread creation is platform-specific and belongs in the adapter layer.
  3. Raw HTTP call with content in one request: rejected — serenity's CreateThread builder doesn't expose the content field; a raw HTTP call would bypass serenity's rate limiter and error handling.

Validation

  • cargo check passes
  • cargo clippy -- -D warnings passes
  • cargo test — 18/18 binary tests pass (1 pre-existing failure in secrets::tests::resolve_exec_nonzero_exit unrelated to this PR, confirmed failing on main)
  • Manual testing — agent calls execute_capability with create_thread and verifies thread appears in target channel with correct bot identity

Generated with Devin

…eation

Agents currently have no supported way to create threads in a target
channel. The only path was direct Discord REST API calls from agent exec
scripts, which risks using the wrong bot's token (identity mix-ups) and
leaks credentials into logs and LLM context.

This adds a `create_thread` tool to the OAB MCP Facade, backed by a new
`ThreadSource` capability source. The adapter uses its own authenticated
HTTP client, so the thread owner is always the bot itself — no token is
exposed to the agent subprocess.

Changes:
- `ChatAdapter::create_thread_in_channel`: new trait method (default:
  unsupported) for standalone thread creation without a trigger message
- `DiscordAdapter::create_thread_in_channel`: Discord implementation using
  serenity's `ChannelId::create_thread` + starter message via `send_message`
- `ThreadSource`: new `CapabilitySource` impl exposing `create_thread` via
  the MCP facade's `execute_capability` dispatch
- `main.rs`: register `ThreadSource` when a Discord adapter is available,
  consolidating the shared adapter construction (previously duplicated for
  the cron scheduler)

The tool accepts `channel_id`, `name`, and optional `content`, and returns
the new thread's channel ID. Title is truncated at 100 chars (Discord limit)
with char-boundary safety for multi-byte text (中文/Emoji).

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@openab-app openab-app Bot added the closing-soon PR missing Discord Discussion URL — will auto-close in 24 hours. label Sep 11, 2026
@openab-app

openab-app Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Caution

This PR is missing a Discord Discussion URL in the body.
This PR will be automatically closed in 24 hours if the link is not added.

All PRs must reference a prior Discord discussion to ensure community alignment before implementation.

Please edit the PR description to include a link like:

Discord Discussion URL: https://discord.com/channels/...

@github-actions

Copy link
Copy Markdown

🔒 Auto-closing: this PR has had the closing-soon label for more than 1 days without activity from the author.

If you'd like to continue working on this, please submit a new PR and link to this one if necessary.

@github-actions github-actions Bot closed this Sep 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

closing-soon PR missing Discord Discussion URL — will auto-close in 24 hours.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant