Twitter / X Voice

@tuttiai/twitter — read tweets, post tweets and threads, with HITL gates on writes

The Twitter voice gives agents the ability to post, read, search, and manage tweets on X (Twitter).

Write tools (post_tweet, post_thread, delete_tweet) are marked destructive: true, so HITL-enabled runtimes can gate them behind human approval before anything publishes.

Installation

npx tutti-ai add twitter

Required permissions

permissions: ["network"]

Required environment variables

Get credentials at developer.x.com.

VariableNeeded forNotes
TWITTER_BEARER_TOKENRead-only toolsApp-only bearer token
TWITTER_API_KEYWrite toolsOAuth 1.0a consumer key
TWITTER_API_SECRETWrite toolsOAuth 1.0a consumer secret
TWITTER_ACCESS_TOKENWrite toolsOAuth 1.0a user token
TWITTER_ACCESS_TOKEN_SECRETWrite toolsOAuth 1.0a user token secret

If OAuth 1.0a credentials are set, the voice uses them for everything (read + write). If only the bearer token is set, write tools return a clear is_error message instead of running.

TWITTER_BEARER_TOKEN=...
TWITTER_API_KEY=...
TWITTER_API_SECRET=...
TWITTER_ACCESS_TOKEN=...
TWITTER_ACCESS_TOKEN_SECRET=...

Configuration

// Default: reads credentials from env
new TwitterVoice()

// Explicit credentials
new TwitterVoice({
  bearer_token: "...",
  api_key: "...",
  api_secret: "...",
  access_token: "...",
  access_token_secret: "...",
})

Tool reference

ToolDestructiveDescription
post_tweetyesPublish a tweet. Optional reply_to, quote_url.
post_threadyesPublish a thread (≥ 2 tweets, each ≤ 280 chars).
delete_tweetyesDelete one of your tweets by id.
search_tweetsnoSearch recent tweets by query. filter: 'recent' | 'popular'.
get_tweetnoFetch one tweet — text, author, likes, retweets, replies.
list_mentionsnoList tweets mentioning the authenticated user (requires OAuth 1.0a).
list_repliesnoList replies to a tweet via conversation_id: search.
get_usernoFetch a user’s bio, follower count, following count, tweet count.
get_timelinenoRecent tweets by a user, or by @me if username omitted.

Example

import { defineScore, AnthropicProvider } from "@tuttiai/core";
import { TwitterVoice } from "@tuttiai/twitter";

export default defineScore({
  provider: new AnthropicProvider(),
  agents: {
    marketing: {
      name: "marketing",
      model: "claude-sonnet-4-20250514",
      system_prompt:
        "You are a marketing agent. Draft tweets that match our brand voice. Never publish without explicit human approval.",
      voices: [new TwitterVoice()],
      permissions: ["network"],
    },
  },
});

Run it:

tutti-ai run marketing "Draft a launch announcement for the new Studio UI"

Edit this page on GitHub →