feat: Twitter/X integration (read-only free tier)
- New client: src/clients/twitter.ts
- Tools: twitter_search_tweets, twitter_get_user_profile, twitter_get_user_tweets, twitter_create_tweet
- REST endpoints: GET /api/twitter/search, /api/twitter/user, /api/twitter/tweets, POST /api/twitter/tweet
- Multi-account env var: TWITTER_{ACCOUNT}_BEARER_TOKEN
- twitter_create_tweet returns clear error about paid tier requirement
Total tools: 36
This commit is contained in:
113
src/manifest.ts
113
src/manifest.ts
@@ -911,6 +911,115 @@ export function getManifest(serverUrl: string, authEnabled: boolean) {
|
||||
examples: [{ image_url: 'https://example.com/photo.jpg', caption: 'New post!', account: 'default' }],
|
||||
},
|
||||
|
||||
// ── Twitter/X tools ────────────────────────────────────────────────────
|
||||
{
|
||||
name: 'twitter_search_tweets',
|
||||
category: 'twitter',
|
||||
description: 'Search recent tweets on Twitter/X',
|
||||
when_to_use:
|
||||
'User wants to find tweets about a topic, hashtag, keyword, or trend.',
|
||||
input_schema: {
|
||||
type: 'object',
|
||||
required: ['query'],
|
||||
properties: {
|
||||
query: { type: 'string', description: 'Search query (keyword, hashtag #xxx, from:username)' },
|
||||
max_results: { type: 'number', description: 'Max tweets (default: 10, max: 100)' },
|
||||
account: { type: 'string', description: 'Which Twitter account to use (default: "default")' },
|
||||
},
|
||||
},
|
||||
returns: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Tweet ID' },
|
||||
text: { type: 'string' },
|
||||
author_id: { type: 'string' },
|
||||
created_at: { type: 'string', format: 'date-time' },
|
||||
},
|
||||
},
|
||||
},
|
||||
examples: [{ query: '#AI', max_results: 10, account: 'default' }],
|
||||
},
|
||||
{
|
||||
name: 'twitter_get_user_profile',
|
||||
category: 'twitter',
|
||||
description: 'Get a Twitter/X user profile and stats',
|
||||
when_to_use:
|
||||
'User wants follower count, bio, or profile info for a specific Twitter account.',
|
||||
input_schema: {
|
||||
type: 'object',
|
||||
required: ['username'],
|
||||
properties: {
|
||||
username: { type: 'string', description: 'Twitter username without @' },
|
||||
account: { type: 'string', description: 'Which Twitter account to use (default: "default")' },
|
||||
},
|
||||
},
|
||||
returns: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string' },
|
||||
name: { type: 'string' },
|
||||
username: { type: 'string' },
|
||||
description: { type: 'string' },
|
||||
followers_count: { type: 'number' },
|
||||
following_count: { type: 'number' },
|
||||
tweet_count: { type: 'number' },
|
||||
},
|
||||
},
|
||||
examples: [{ username: 'elonmusk', account: 'default' }],
|
||||
},
|
||||
{
|
||||
name: 'twitter_get_user_tweets',
|
||||
category: 'twitter',
|
||||
description: 'Get recent tweets from a specific user',
|
||||
when_to_use:
|
||||
'User wants to read someones recent tweets or timeline.',
|
||||
input_schema: {
|
||||
type: 'object',
|
||||
required: ['username'],
|
||||
properties: {
|
||||
username: { type: 'string', description: 'Twitter username without @' },
|
||||
max_results: { type: 'number', description: 'Max tweets (default: 10, max: 100)' },
|
||||
account: { type: 'string', description: 'Which Twitter account to use (default: "default")' },
|
||||
},
|
||||
},
|
||||
returns: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string' },
|
||||
text: { type: 'string' },
|
||||
created_at: { type: 'string', format: 'date-time' },
|
||||
},
|
||||
},
|
||||
},
|
||||
examples: [{ username: 'elonmusk', max_results: 5, account: 'default' }],
|
||||
},
|
||||
{
|
||||
name: 'twitter_create_tweet',
|
||||
category: 'twitter',
|
||||
description: 'Post a tweet [REQUIRES PAID TIER]',
|
||||
when_to_use:
|
||||
'User wants to post a tweet. NOTE: Free tier is read-only. Paid upgrade required.',
|
||||
input_schema: {
|
||||
type: 'object',
|
||||
required: ['text'],
|
||||
properties: {
|
||||
text: { type: 'string', description: 'Tweet text (max 280 chars)' },
|
||||
account: { type: 'string', description: 'Which Twitter account to use (default: "default")' },
|
||||
},
|
||||
},
|
||||
returns: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
message: { type: 'string', description: 'Error or confirmation' },
|
||||
},
|
||||
},
|
||||
examples: [{ text: 'Hello from Hermes MCP!', account: 'default' }],
|
||||
},
|
||||
|
||||
// ── Obsidian tools ──────────────────────────────────────────────────────
|
||||
{
|
||||
name: 'obsidian_search_notes',
|
||||
@@ -1090,6 +1199,10 @@ export function getManifest(serverUrl: string, authEnabled: boolean) {
|
||||
description: 'Instagram Business/Creator account via Graph API',
|
||||
icon: '📸',
|
||||
},
|
||||
twitter: {
|
||||
description: 'Twitter/X search and profile lookup (read-only on free tier)',
|
||||
icon: '🐦',
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user