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:
50
src/index.ts
50
src/index.ts
@@ -769,6 +769,56 @@ app.post('/api/instagram/post', requireAuth, async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// ── Twitter/X REST endpoints ────────────────────────────────────
|
||||
app.get('/api/twitter/search', requireAuth, async (req, res) => {
|
||||
const query = req.query.query as string | undefined;
|
||||
const max_results = req.query.max_results ? parseInt(req.query.max_results as string, 10) : undefined;
|
||||
const account = req.query.account as string | undefined;
|
||||
if (!query) { res.status(400).json({ error: 'query is required' }); return; }
|
||||
try {
|
||||
const result = await handleToolCall('twitter_search_tweets', { query, max_results, account });
|
||||
res.json(parseToolResult(result));
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: (err as Error).message });
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/api/twitter/user', requireAuth, async (req, res) => {
|
||||
const username = req.query.username as string | undefined;
|
||||
const account = req.query.account as string | undefined;
|
||||
if (!username) { res.status(400).json({ error: 'username is required' }); return; }
|
||||
try {
|
||||
const result = await handleToolCall('twitter_get_user_profile', { username, account });
|
||||
res.json(parseToolResult(result));
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: (err as Error).message });
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/api/twitter/tweets', requireAuth, async (req, res) => {
|
||||
const username = req.query.username as string | undefined;
|
||||
const max_results = req.query.max_results ? parseInt(req.query.max_results as string, 10) : undefined;
|
||||
const account = req.query.account as string | undefined;
|
||||
if (!username) { res.status(400).json({ error: 'username is required' }); return; }
|
||||
try {
|
||||
const result = await handleToolCall('twitter_get_user_tweets', { username, max_results, account });
|
||||
res.json(parseToolResult(result));
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: (err as Error).message });
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/api/twitter/tweet', requireAuth, async (req, res) => {
|
||||
const { text, account } = req.body as Record<string, unknown>;
|
||||
if (!text) { res.status(400).json({ error: 'text is required' }); return; }
|
||||
try {
|
||||
const result = await handleToolCall('twitter_create_tweet', { text, account });
|
||||
res.json(parseToolResult(result));
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: (err as Error).message });
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/api/pilot-request', async (req, res) => {
|
||||
const origin = req.get('origin');
|
||||
if (origin && !SQUAREMCP_ALLOWED_ORIGINS.has(origin)) {
|
||||
|
||||
Reference in New Issue
Block a user