@opentweet/mcp-server 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +99 -0
  2. package/dist/api-client.d.ts +102 -0
  3. package/dist/api-client.d.ts.map +1 -0
  4. package/dist/api-client.js +125 -0
  5. package/dist/api-client.js.map +1 -0
  6. package/dist/index.d.ts +3 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +44 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/prompts/index.d.ts +3 -0
  11. package/dist/prompts/index.d.ts.map +1 -0
  12. package/dist/prompts/index.js +93 -0
  13. package/dist/prompts/index.js.map +1 -0
  14. package/dist/resources/index.d.ts +4 -0
  15. package/dist/resources/index.d.ts.map +1 -0
  16. package/dist/resources/index.js +55 -0
  17. package/dist/resources/index.js.map +1 -0
  18. package/dist/tools/account.d.ts +4 -0
  19. package/dist/tools/account.d.ts.map +1 -0
  20. package/dist/tools/account.js +38 -0
  21. package/dist/tools/account.js.map +1 -0
  22. package/dist/tools/analytics.d.ts +4 -0
  23. package/dist/tools/analytics.d.ts.map +1 -0
  24. package/dist/tools/analytics.js +100 -0
  25. package/dist/tools/analytics.js.map +1 -0
  26. package/dist/tools/media.d.ts +4 -0
  27. package/dist/tools/media.d.ts.map +1 -0
  28. package/dist/tools/media.js +47 -0
  29. package/dist/tools/media.js.map +1 -0
  30. package/dist/tools/posts.d.ts +4 -0
  31. package/dist/tools/posts.d.ts.map +1 -0
  32. package/dist/tools/posts.js +194 -0
  33. package/dist/tools/posts.js.map +1 -0
  34. package/dist/tools/scheduling.d.ts +4 -0
  35. package/dist/tools/scheduling.d.ts.map +1 -0
  36. package/dist/tools/scheduling.js +52 -0
  37. package/dist/tools/scheduling.js.map +1 -0
  38. package/dist/types.d.ts +104 -0
  39. package/dist/types.d.ts.map +1 -0
  40. package/dist/types.js +2 -0
  41. package/dist/types.js.map +1 -0
  42. package/package.json +52 -0
@@ -0,0 +1,100 @@
1
+ import { z } from 'zod';
2
+ export function registerAnalyticsTools(server, client) {
3
+ server.tool('opentweet_get_analytics', 'Get your posting analytics including stats, streaks, trends, category breakdown, and recent activity. Use type parameter to get specific analytics.', {
4
+ type: z.enum(['overview', 'tweets', 'best_times', 'growth']).optional()
5
+ .describe('Type of analytics: "overview" (default, posting stats + streaks + trends), "tweets" (per-tweet engagement, requires Advanced plan), "best_times" (optimal posting hours/days), "growth" (follower growth predictions)'),
6
+ period: z.string().optional().describe('Time period for tweet analytics (e.g. "7d", "30d", "90d"). Only for type="tweets".'),
7
+ sort_by: z.string().optional().describe('Sort tweet analytics by metric (e.g. "impressions", "engagement_rate"). Only for type="tweets".'),
8
+ }, async ({ type = 'overview', period, sort_by }) => {
9
+ try {
10
+ if (type === 'overview') {
11
+ const data = await client.getAnalyticsOverview();
12
+ const { stats: s, streaks: st, trends: t, categories } = data;
13
+ let text = `📊 Posting Analytics Overview\n\n`;
14
+ text += `Stats:\n`;
15
+ text += ` Posts created: ${s.total_posts_created}\n`;
16
+ text += ` Posts published: ${s.total_posts_published}\n`;
17
+ text += ` Posts scheduled: ${s.total_posts_scheduled}\n`;
18
+ text += ` Publishing rate: ${s.publishing_rate}%\n`;
19
+ text += ` Active days: ${s.total_active_days}\n`;
20
+ text += ` Avg posts/week: ${s.avg_posts_per_week}\n`;
21
+ text += ` Most active day: ${s.most_active_day}\n`;
22
+ text += ` Most active hour: ${s.most_active_hour}:00\n`;
23
+ text += ` Threads created: ${s.total_threads_created}\n`;
24
+ text += ` Media posts: ${s.total_media_posts}\n\n`;
25
+ text += `Streaks:\n`;
26
+ text += ` Current streak: ${st.current} days\n`;
27
+ text += ` Longest streak: ${st.longest} days\n`;
28
+ text += ` Last posted: ${st.last_posted_date || 'Never'}\n\n`;
29
+ text += `Trends:\n`;
30
+ text += ` This week: ${t.this_week} posts (last week: ${t.last_week})\n`;
31
+ text += ` This month: ${t.this_month} posts (last month: ${t.last_month})\n`;
32
+ if (t.best_month)
33
+ text += ` Best month: ${t.best_month.month} (${t.best_month.count} posts)\n`;
34
+ text += `\nTop categories:\n`;
35
+ categories.slice(0, 5).forEach(c => {
36
+ text += ` ${c.name}: ${c.count} posts (${c.percentage}%)\n`;
37
+ });
38
+ text += `\nLast 7 days activity: [${data.recent_activity.last_7_days.join(', ')}]`;
39
+ return { content: [{ type: 'text', text }] };
40
+ }
41
+ if (type === 'tweets') {
42
+ const data = await client.getTweetAnalytics({ period, sort_by });
43
+ return {
44
+ content: [{
45
+ type: 'text',
46
+ text: `Tweet Engagement Analytics:\n\n${JSON.stringify(data, null, 2)}`,
47
+ }],
48
+ };
49
+ }
50
+ if (type === 'best_times') {
51
+ const data = await client.getBestTimes();
52
+ return {
53
+ content: [{
54
+ type: 'text',
55
+ text: `Best Posting Times:\n\n${JSON.stringify(data, null, 2)}`,
56
+ }],
57
+ };
58
+ }
59
+ if (type === 'growth') {
60
+ const data = await client.getGrowthSummary();
61
+ return {
62
+ content: [{
63
+ type: 'text',
64
+ text: `Growth Summary:\n\n${JSON.stringify(data, null, 2)}`,
65
+ }],
66
+ };
67
+ }
68
+ return {
69
+ content: [{ type: 'text', text: 'Unknown analytics type.' }],
70
+ isError: true,
71
+ };
72
+ }
73
+ catch (error) {
74
+ return {
75
+ content: [{ type: 'text', text: `Error: ${error.message}` }],
76
+ isError: true,
77
+ };
78
+ }
79
+ });
80
+ server.tool('opentweet_get_followers', 'Get follower growth data over time. Shows follower count snapshots and growth trends.', {
81
+ days: z.number().int().min(1).max(365).optional().describe('Number of days of history to retrieve (default 30, max 365)'),
82
+ }, async ({ days }) => {
83
+ try {
84
+ const data = await client.getFollowerGrowth({ days });
85
+ return {
86
+ content: [{
87
+ type: 'text',
88
+ text: `Follower Growth (${days || 30} days):\n\n${JSON.stringify(data, null, 2)}`,
89
+ }],
90
+ };
91
+ }
92
+ catch (error) {
93
+ return {
94
+ content: [{ type: 'text', text: `Error: ${error.message}` }],
95
+ isError: true,
96
+ };
97
+ }
98
+ });
99
+ }
100
+ //# sourceMappingURL=analytics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analytics.js","sourceRoot":"","sources":["../../src/tools/analytics.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { OpenTweetClient } from '../api-client.js';
3
+ export declare function registerMediaTools(server: McpServer, client: OpenTweetClient): void;
4
+ //# sourceMappingURL=media.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media.d.ts","sourceRoot":"","sources":["../../src/tools/media.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAcnD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,QAuC5E"}
@@ -0,0 +1,47 @@
1
+ import { z } from 'zod';
2
+ import { readFileSync } from 'fs';
3
+ import { basename, extname } from 'path';
4
+ const MIME_MAP = {
5
+ '.jpg': 'image/jpeg',
6
+ '.jpeg': 'image/jpeg',
7
+ '.png': 'image/png',
8
+ '.gif': 'image/gif',
9
+ '.webp': 'image/webp',
10
+ '.mp4': 'video/mp4',
11
+ '.mov': 'video/quicktime',
12
+ };
13
+ export function registerMediaTools(server, client) {
14
+ server.tool('opentweet_upload_media', 'Upload an image or video file to use in tweets. Returns a URL that can be passed to create_tweet or update_tweet in the media_urls field. Supported formats: JPG, PNG, GIF, WebP (max 5MB), MP4, MOV (max 20MB).', {
15
+ file_path: z.string().describe('Absolute path to the image or video file on disk'),
16
+ }, async ({ file_path }) => {
17
+ try {
18
+ const ext = extname(file_path).toLowerCase();
19
+ const mimeType = MIME_MAP[ext];
20
+ if (!mimeType) {
21
+ return {
22
+ content: [{
23
+ type: 'text',
24
+ text: `Error: Unsupported file type "${ext}". Supported: ${Object.keys(MIME_MAP).join(', ')}`,
25
+ }],
26
+ isError: true,
27
+ };
28
+ }
29
+ const buffer = readFileSync(file_path);
30
+ const fileName = basename(file_path);
31
+ const result = await client.uploadMedia(buffer, fileName, mimeType);
32
+ return {
33
+ content: [{
34
+ type: 'text',
35
+ text: `Media uploaded successfully.\n\nURL: ${result.url}\n\nUse this URL in the media_urls field when creating or updating a tweet.`,
36
+ }],
37
+ };
38
+ }
39
+ catch (error) {
40
+ return {
41
+ content: [{ type: 'text', text: `Error: ${error.message}` }],
42
+ isError: true,
43
+ };
44
+ }
45
+ });
46
+ }
47
+ //# sourceMappingURL=media.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media.js","sourceRoot":"","sources":["../../src/tools/media.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAEzC,MAAM,QAAQ,GAA2B;IACvC,MAAM,EAAE,YAAY;IACpB,OAAO,EAAE,YAAY;IACrB,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,WAAW;IACnB,OAAO,EAAE,YAAY;IACrB,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,iBAAiB;CAC1B,CAAC;AAEF,MAAM,UAAU,kBAAkB,CAAC,MAAiB,EAAE,MAAuB;IAC3E,MAAM,CAAC,IAAI,CACT,wBAAwB,EACxB,kNAAkN,EAClN;QACE,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,kDAAkD,CAAC;KACnF,EACD,KAAK,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE;QACtB,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE,CAAC;YAC7C,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;YAC/B,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,OAAO;oBACL,OAAO,EAAE,CAAC;4BACR,IAAI,EAAE,MAAe;4BACrB,IAAI,EAAE,iCAAiC,GAAG,iBAAiB,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;yBAC9F,CAAC;oBACF,OAAO,EAAE,IAAI;iBACd,CAAC;YACJ,CAAC;YAED,MAAM,MAAM,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC;YACvC,MAAM,QAAQ,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC;YACrC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAEpE,OAAO;gBACL,OAAO,EAAE,CAAC;wBACR,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,wCAAwC,MAAM,CAAC,GAAG,6EAA6E;qBACtI,CAAC;aACH,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,UAAW,KAAe,CAAC,OAAO,EAAE,EAAE,CAAC;gBAChF,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { OpenTweetClient } from '../api-client.js';
3
+ export declare function registerPostTools(server: McpServer, client: OpenTweetClient): void;
4
+ //# sourceMappingURL=posts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"posts.d.ts","sourceRoot":"","sources":["../../src/tools/posts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,QA8N3E"}
@@ -0,0 +1,194 @@
1
+ import { z } from 'zod';
2
+ export function registerPostTools(server, client) {
3
+ server.tool('opentweet_create_tweet', 'Create a tweet. Can save as draft, schedule for a specific time, or publish immediately to X.', {
4
+ text: z.string().max(280).describe('Tweet text content (max 280 characters)'),
5
+ category: z.string().optional().describe('Category/topic tag (e.g. "Tech", "Marketing"). Defaults to "General"'),
6
+ scheduled_date: z.string().optional().describe('ISO 8601 date-time to schedule the tweet (e.g. "2026-03-15T09:00:00Z"). Omit to save as draft.'),
7
+ publish_now: z.boolean().optional().describe('Set to true to publish immediately to X instead of saving as draft'),
8
+ media_urls: z.array(z.string()).optional().describe('Array of media URLs (upload first with opentweet_upload_media)'),
9
+ }, async ({ text, category, scheduled_date, publish_now, media_urls }) => {
10
+ try {
11
+ const result = await client.createPost({
12
+ text,
13
+ category,
14
+ scheduled_date,
15
+ publish_now,
16
+ media_urls,
17
+ });
18
+ const post = result.posts[0];
19
+ const status = publish_now
20
+ ? `Published to X (x.com/i/status/${post.x_post_id})`
21
+ : scheduled_date
22
+ ? `Scheduled for ${scheduled_date}`
23
+ : 'Saved as draft';
24
+ return {
25
+ content: [{
26
+ type: 'text',
27
+ text: `Tweet created successfully.\n\nStatus: ${status}\nID: ${post.id}\nText: ${post.text}\nCategory: ${post.category}`,
28
+ }],
29
+ };
30
+ }
31
+ catch (error) {
32
+ return {
33
+ content: [{ type: 'text', text: `Error: ${error.message}` }],
34
+ isError: true,
35
+ };
36
+ }
37
+ });
38
+ server.tool('opentweet_create_thread', 'Create a Twitter/X thread (multiple connected tweets posted as a chain).', {
39
+ tweets: z.array(z.string().max(280)).min(2).max(25).describe('Array of tweet texts in order (each max 280 chars, 2-25 tweets)'),
40
+ category: z.string().optional().describe('Category/topic tag'),
41
+ scheduled_date: z.string().optional().describe('ISO 8601 date-time to schedule. Omit to save as draft.'),
42
+ publish_now: z.boolean().optional().describe('Set to true to publish immediately'),
43
+ }, async ({ tweets, category, scheduled_date, publish_now }) => {
44
+ try {
45
+ const result = await client.createPost({
46
+ text: tweets[0],
47
+ is_thread: true,
48
+ thread_tweets: tweets.slice(1),
49
+ category,
50
+ scheduled_date,
51
+ publish_now,
52
+ });
53
+ const post = result.posts[0];
54
+ const status = publish_now
55
+ ? 'Published to X'
56
+ : scheduled_date
57
+ ? `Scheduled for ${scheduled_date}`
58
+ : 'Saved as draft';
59
+ return {
60
+ content: [{
61
+ type: 'text',
62
+ text: `Thread created (${tweets.length} tweets).\n\nStatus: ${status}\nID: ${post.id}\nFirst tweet: ${tweets[0]}`,
63
+ }],
64
+ };
65
+ }
66
+ catch (error) {
67
+ return {
68
+ content: [{ type: 'text', text: `Error: ${error.message}` }],
69
+ isError: true,
70
+ };
71
+ }
72
+ });
73
+ server.tool('opentweet_list_tweets', 'List your tweets with optional filtering by status. Returns paginated results.', {
74
+ status: z.enum(['scheduled', 'posted', 'draft', 'failed']).optional().describe('Filter by status: "scheduled" (upcoming), "posted" (published), "draft" (unscheduled), "failed" (errors)'),
75
+ page: z.number().int().min(1).optional().describe('Page number (default 1)'),
76
+ limit: z.number().int().min(1).max(100).optional().describe('Results per page (default 20, max 100)'),
77
+ }, async ({ status, page, limit }) => {
78
+ try {
79
+ const result = await client.listPosts({ status, page, limit });
80
+ if (result.posts.length === 0) {
81
+ return {
82
+ content: [{
83
+ type: 'text',
84
+ text: status
85
+ ? `No ${status} tweets found.`
86
+ : 'No tweets found.',
87
+ }],
88
+ };
89
+ }
90
+ const lines = result.posts.map((p, i) => {
91
+ const num = ((result.pagination.page - 1) * result.pagination.limit) + i + 1;
92
+ const statusLabel = p.posted
93
+ ? 'POSTED'
94
+ : p.failed
95
+ ? 'FAILED'
96
+ : p.scheduled_date
97
+ ? `SCHEDULED ${p.scheduled_date}`
98
+ : 'DRAFT';
99
+ const preview = p.text.length > 100 ? p.text.slice(0, 100) + '...' : p.text;
100
+ return `${num}. [${statusLabel}] (${p.id})\n ${preview}`;
101
+ });
102
+ const { pagination: pg } = result;
103
+ return {
104
+ content: [{
105
+ type: 'text',
106
+ text: `Tweets (page ${pg.page}/${pg.pages}, ${pg.total} total):\n\n${lines.join('\n\n')}`,
107
+ }],
108
+ };
109
+ }
110
+ catch (error) {
111
+ return {
112
+ content: [{ type: 'text', text: `Error: ${error.message}` }],
113
+ isError: true,
114
+ };
115
+ }
116
+ });
117
+ server.tool('opentweet_get_tweet', 'Get full details of a specific tweet by its ID.', {
118
+ id: z.string().describe('The tweet/post ID'),
119
+ }, async ({ id }) => {
120
+ try {
121
+ const post = await client.getPost(id);
122
+ const statusLabel = post.posted
123
+ ? `Posted on ${post.posted_date}`
124
+ : post.failed
125
+ ? `Failed: ${post.failed_reason}`
126
+ : post.scheduled_date
127
+ ? `Scheduled for ${post.scheduled_date}`
128
+ : 'Draft';
129
+ let text = `Tweet Details:\n\nID: ${post.id}\nStatus: ${statusLabel}\nCategory: ${post.category}\nText: ${post.text}`;
130
+ if (post.is_thread && post.thread_tweets) {
131
+ text += `\n\nThread (${post.thread_tweets.length + 1} tweets):\n1. ${post.text}`;
132
+ post.thread_tweets.forEach((t, i) => {
133
+ text += `\n${i + 2}. ${t}`;
134
+ });
135
+ }
136
+ if (post.x_post_id)
137
+ text += `\nX Post ID: ${post.x_post_id}`;
138
+ if (post.media_urls?.length)
139
+ text += `\nMedia: ${post.media_urls.join(', ')}`;
140
+ return { content: [{ type: 'text', text }] };
141
+ }
142
+ catch (error) {
143
+ return {
144
+ content: [{ type: 'text', text: `Error: ${error.message}` }],
145
+ isError: true,
146
+ };
147
+ }
148
+ });
149
+ server.tool('opentweet_update_tweet', 'Update an existing tweet. Can change text, category, schedule, or media. Cannot edit already-posted tweets.', {
150
+ id: z.string().describe('The tweet/post ID to update'),
151
+ text: z.string().max(280).optional().describe('New tweet text'),
152
+ category: z.string().optional().describe('New category'),
153
+ scheduled_date: z.string().nullable().optional().describe('New schedule date (ISO 8601), or null to unschedule'),
154
+ media_urls: z.array(z.string()).optional().describe('New media URLs (replaces existing)'),
155
+ }, async ({ id, text, category, scheduled_date, media_urls }) => {
156
+ try {
157
+ const updated = await client.updatePost(id, {
158
+ text,
159
+ category,
160
+ scheduled_date,
161
+ media_urls,
162
+ });
163
+ return {
164
+ content: [{
165
+ type: 'text',
166
+ text: `Tweet updated.\n\nID: ${updated.id}\nText: ${updated.text}\nCategory: ${updated.category}\nScheduled: ${updated.scheduled_date || 'Not scheduled (draft)'}`,
167
+ }],
168
+ };
169
+ }
170
+ catch (error) {
171
+ return {
172
+ content: [{ type: 'text', text: `Error: ${error.message}` }],
173
+ isError: true,
174
+ };
175
+ }
176
+ });
177
+ server.tool('opentweet_delete_tweet', 'Permanently delete a tweet. This cannot be undone.', {
178
+ id: z.string().describe('The tweet/post ID to delete'),
179
+ }, async ({ id }) => {
180
+ try {
181
+ await client.deletePost(id);
182
+ return {
183
+ content: [{ type: 'text', text: `Tweet ${id} deleted successfully.` }],
184
+ };
185
+ }
186
+ catch (error) {
187
+ return {
188
+ content: [{ type: 'text', text: `Error: ${error.message}` }],
189
+ isError: true,
190
+ };
191
+ }
192
+ });
193
+ }
194
+ //# sourceMappingURL=posts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"posts.js","sourceRoot":"","sources":["../../src/tools/posts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { OpenTweetClient } from '../api-client.js';
3
+ export declare function registerSchedulingTools(server: McpServer, client: OpenTweetClient): void;
4
+ //# sourceMappingURL=scheduling.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduling.d.ts","sourceRoot":"","sources":["../../src/tools/scheduling.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,QA6DjF"}
@@ -0,0 +1,52 @@
1
+ import { z } from 'zod';
2
+ export function registerSchedulingTools(server, client) {
3
+ server.tool('opentweet_publish_tweet', 'Publish an existing draft or scheduled tweet immediately to X/Twitter. Requires an active subscription and X account connection.', {
4
+ id: z.string().describe('The tweet/post ID to publish now'),
5
+ }, async ({ id }) => {
6
+ try {
7
+ const result = await client.publishPost(id);
8
+ return {
9
+ content: [{
10
+ type: 'text',
11
+ text: `Tweet published to X!\n\nID: ${result.id}\nText: ${result.text}\nX Post ID: ${result.x_post_id}\nURL: https://x.com/i/status/${result.x_post_id}`,
12
+ }],
13
+ };
14
+ }
15
+ catch (error) {
16
+ return {
17
+ content: [{ type: 'text', text: `Error: ${error.message}` }],
18
+ isError: true,
19
+ };
20
+ }
21
+ });
22
+ server.tool('opentweet_batch_schedule', 'Schedule multiple tweets at once. Each tweet must already exist as a draft. Provide an array of post IDs with their scheduled dates.', {
23
+ schedules: z.array(z.object({
24
+ post_id: z.string().describe('The tweet/post ID'),
25
+ scheduled_date: z.string().describe('ISO 8601 date-time (e.g. "2026-03-15T09:00:00Z")'),
26
+ })).min(1).max(50).describe('Array of {post_id, scheduled_date} pairs (max 50)'),
27
+ community_id: z.string().optional().describe('Optional X Community ID to post to'),
28
+ share_with_followers: z.boolean().optional().describe('Share community posts with followers too'),
29
+ }, async ({ schedules, community_id, share_with_followers }) => {
30
+ try {
31
+ const result = await client.batchSchedule(schedules, community_id, share_with_followers);
32
+ let text = `${result.message}\n\nScheduled:`;
33
+ result.scheduled.forEach(p => {
34
+ text += `\n- ${p.id}: "${p.text.slice(0, 60)}${p.text.length > 60 ? '...' : ''}" → ${p.scheduled_date}`;
35
+ });
36
+ if (result.errors?.length) {
37
+ text += `\n\nErrors:`;
38
+ result.errors.forEach(e => {
39
+ text += `\n- ${e.post_id}: ${e.error}`;
40
+ });
41
+ }
42
+ return { content: [{ type: 'text', text }] };
43
+ }
44
+ catch (error) {
45
+ return {
46
+ content: [{ type: 'text', text: `Error: ${error.message}` }],
47
+ isError: true,
48
+ };
49
+ }
50
+ });
51
+ }
52
+ //# sourceMappingURL=scheduling.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduling.js","sourceRoot":"","sources":["../../src/tools/scheduling.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,104 @@
1
+ export interface OpenTweetConfig {
2
+ apiKey: string;
3
+ baseUrl: string;
4
+ }
5
+ export interface PaginatedResponse<T> {
6
+ posts: T[];
7
+ pagination: {
8
+ page: number;
9
+ limit: number;
10
+ total: number;
11
+ pages: number;
12
+ };
13
+ }
14
+ export interface Post {
15
+ id: string;
16
+ category: string;
17
+ text: string;
18
+ scheduled_date: string | null;
19
+ posted: boolean;
20
+ posted_date: string | null;
21
+ failed: boolean;
22
+ failed_reason?: string;
23
+ is_thread: boolean;
24
+ thread_tweets?: string[];
25
+ media_urls?: string[];
26
+ thread_media?: string[][];
27
+ community_id?: string;
28
+ share_with_followers?: boolean;
29
+ x_post_id?: string;
30
+ review_status?: string;
31
+ created_at: string;
32
+ updated_at?: string;
33
+ }
34
+ export interface AccountStatus {
35
+ authenticated: boolean;
36
+ subscription: {
37
+ has_access: boolean;
38
+ status: string;
39
+ is_trialing: boolean;
40
+ trial_ends_at: string | null;
41
+ current_period_ends_at: string | null;
42
+ };
43
+ limits: {
44
+ can_post: boolean;
45
+ posts_published_today: number;
46
+ remaining_posts_today: number;
47
+ daily_limit: number;
48
+ } | null;
49
+ stats: {
50
+ total_posts: number;
51
+ scheduled_posts: number;
52
+ posted_posts: number;
53
+ draft_posts: number;
54
+ };
55
+ }
56
+ export interface AnalyticsOverview {
57
+ stats: {
58
+ total_posts_created: number;
59
+ total_posts_published: number;
60
+ total_posts_scheduled: number;
61
+ publishing_rate: number;
62
+ total_active_days: number;
63
+ avg_posts_per_week: number;
64
+ most_active_day: string;
65
+ most_active_hour: number;
66
+ total_characters_written: number;
67
+ total_threads_created: number;
68
+ total_media_posts: number;
69
+ };
70
+ streaks: {
71
+ current: number;
72
+ longest: number;
73
+ last_posted_date: string | null;
74
+ streak_start_date: string | null;
75
+ };
76
+ trends: {
77
+ this_week: number;
78
+ last_week: number;
79
+ this_month: number;
80
+ last_month: number;
81
+ best_month: {
82
+ month: string;
83
+ count: number;
84
+ } | null;
85
+ };
86
+ categories: {
87
+ name: string;
88
+ count: number;
89
+ percentage: number;
90
+ }[];
91
+ recent_activity: {
92
+ last_7_days: number[];
93
+ last_30_days: number[];
94
+ };
95
+ }
96
+ export interface UploadResponse {
97
+ url: string;
98
+ }
99
+ export interface ApiError {
100
+ error: string;
101
+ details?: string | string[];
102
+ remaining?: number;
103
+ }
104
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@opentweet/mcp-server",
3
+ "version": "1.0.0",
4
+ "description": "OpenTweet MCP Server - Manage your Twitter/X presence from any AI assistant",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "opentweet-mcp": "dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "dev": "tsc --watch",
13
+ "start": "node dist/index.js",
14
+ "prepublishOnly": "npm run build"
15
+ },
16
+ "keywords": [
17
+ "mcp",
18
+ "mcp-server",
19
+ "model-context-protocol",
20
+ "opentweet",
21
+ "twitter",
22
+ "x",
23
+ "tweet",
24
+ "scheduling",
25
+ "ai",
26
+ "claude",
27
+ "automation"
28
+ ],
29
+ "author": "OpenTweet <hello@opentweet.io>",
30
+ "license": "MIT",
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "https://github.com/opentweetio/mcp-server"
34
+ },
35
+ "homepage": "https://opentweet.io/mcp",
36
+ "dependencies": {
37
+ "@modelcontextprotocol/sdk": "^1.12.0",
38
+ "zod": "^3.24.0"
39
+ },
40
+ "devDependencies": {
41
+ "typescript": "^5.7.0",
42
+ "@types/node": "^22.0.0"
43
+ },
44
+ "engines": {
45
+ "node": ">=18.0.0"
46
+ },
47
+ "files": [
48
+ "dist",
49
+ "README.md",
50
+ "LICENSE"
51
+ ]
52
+ }