@granitebps/twitter-mcp 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.
- package/LICENSE +7 -0
- package/README.md +326 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +35 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/env.d.ts +22 -0
- package/dist/config/env.js +44 -0
- package/dist/config/env.js.map +1 -0
- package/dist/domain/requests.d.ts +36 -0
- package/dist/domain/requests.js +2 -0
- package/dist/domain/requests.js.map +1 -0
- package/dist/domain/schemas.d.ts +95 -0
- package/dist/domain/schemas.js +53 -0
- package/dist/domain/schemas.js.map +1 -0
- package/dist/errors/twitter-error.d.ts +20 -0
- package/dist/errors/twitter-error.js +35 -0
- package/dist/errors/twitter-error.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/providers/api/mapper.d.ts +34 -0
- package/dist/providers/api/mapper.js +45 -0
- package/dist/providers/api/mapper.js.map +1 -0
- package/dist/providers/api/provider.d.ts +44 -0
- package/dist/providers/api/provider.js +150 -0
- package/dist/providers/api/provider.js.map +1 -0
- package/dist/providers/deadline.d.ts +1 -0
- package/dist/providers/deadline.js +15 -0
- package/dist/providers/deadline.js.map +1 -0
- package/dist/providers/factory.d.ts +7 -0
- package/dist/providers/factory.js +17 -0
- package/dist/providers/factory.js.map +1 -0
- package/dist/providers/provider.d.ts +9 -0
- package/dist/providers/provider.js +2 -0
- package/dist/providers/provider.js.map +1 -0
- package/dist/providers/rettiwt/mapper.d.ts +29 -0
- package/dist/providers/rettiwt/mapper.js +42 -0
- package/dist/providers/rettiwt/mapper.js.map +1 -0
- package/dist/providers/rettiwt/provider.d.ts +30 -0
- package/dist/providers/rettiwt/provider.js +161 -0
- package/dist/providers/rettiwt/provider.js.map +1 -0
- package/dist/server/create-server.d.ts +9 -0
- package/dist/server/create-server.js +10 -0
- package/dist/server/create-server.js.map +1 -0
- package/dist/server/input.d.ts +17 -0
- package/dist/server/input.js +34 -0
- package/dist/server/input.js.map +1 -0
- package/dist/server/output.d.ts +8 -0
- package/dist/server/output.js +34 -0
- package/dist/server/output.js.map +1 -0
- package/dist/server/register-tools.d.ts +9 -0
- package/dist/server/register-tools.js +106 -0
- package/dist/server/register-tools.js.map +1 -0
- package/package.json +83 -0
- package/server.json +30 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { Rettiwt } from "rettiwt-api";
|
|
2
|
+
import { TwitterError, toTwitterError } from "../../errors/twitter-error.js";
|
|
3
|
+
import { withDeadline } from "../deadline.js";
|
|
4
|
+
import { mapRettiwtTweet, mapRettiwtUser, } from "./mapper.js";
|
|
5
|
+
function timeoutError() {
|
|
6
|
+
return new TwitterError({
|
|
7
|
+
code: "TIMEOUT",
|
|
8
|
+
safeMessage: "The Rettiwt request timed out",
|
|
9
|
+
provider: "rettiwt",
|
|
10
|
+
retryable: true,
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
function mapRettiwtFailure(error) {
|
|
14
|
+
if (error instanceof TwitterError)
|
|
15
|
+
return error;
|
|
16
|
+
const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
|
|
17
|
+
if (message.includes("401") || message.includes("auth")) {
|
|
18
|
+
return new TwitterError({
|
|
19
|
+
code: "AUTH_FAILED",
|
|
20
|
+
safeMessage: "Rettiwt authentication failed. Generate a new RETTIWT_API_KEY.",
|
|
21
|
+
provider: "rettiwt",
|
|
22
|
+
retryable: false,
|
|
23
|
+
cause: error,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
if (message.includes("429") || message.includes("rate limit")) {
|
|
27
|
+
return new TwitterError({
|
|
28
|
+
code: "RATE_LIMITED",
|
|
29
|
+
safeMessage: "X rate limit reached",
|
|
30
|
+
provider: "rettiwt",
|
|
31
|
+
retryable: true,
|
|
32
|
+
cause: error,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
if (message.includes("timeout") || message.includes("econnaborted"))
|
|
36
|
+
return timeoutError();
|
|
37
|
+
return toTwitterError(error, "rettiwt");
|
|
38
|
+
}
|
|
39
|
+
function parseSearch(query) {
|
|
40
|
+
const fromUsers = [];
|
|
41
|
+
const hashtags = [];
|
|
42
|
+
const mentions = [];
|
|
43
|
+
const includeWords = [];
|
|
44
|
+
const warnings = [];
|
|
45
|
+
let language;
|
|
46
|
+
for (const token of query.split(/\s+/).filter(Boolean)) {
|
|
47
|
+
if (token.startsWith("from:") && token.length > 5)
|
|
48
|
+
fromUsers.push(token.slice(5));
|
|
49
|
+
else if (token.startsWith("lang:") && token.length > 5)
|
|
50
|
+
language = token.slice(5);
|
|
51
|
+
else if (token.startsWith("#") && token.length > 1)
|
|
52
|
+
hashtags.push(token.slice(1));
|
|
53
|
+
else if (token.startsWith("@") && token.length > 1)
|
|
54
|
+
mentions.push(token.slice(1));
|
|
55
|
+
else if (token.includes(":")) {
|
|
56
|
+
warnings.push({
|
|
57
|
+
code: "UNSUPPORTED_SEARCH_OPERATOR",
|
|
58
|
+
message: `Ignored search operator: ${token}`,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
else
|
|
62
|
+
includeWords.push(token);
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
filter: {
|
|
66
|
+
...(includeWords.length ? { includeWords } : {}),
|
|
67
|
+
...(fromUsers.length ? { fromUsers } : {}),
|
|
68
|
+
...(hashtags.length ? { hashtags } : {}),
|
|
69
|
+
...(mentions.length ? { mentions } : {}),
|
|
70
|
+
...(language ? { language } : {}),
|
|
71
|
+
},
|
|
72
|
+
warnings,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
export class RettiwtProvider {
|
|
76
|
+
client;
|
|
77
|
+
capabilities = {
|
|
78
|
+
provider: "rettiwt",
|
|
79
|
+
authenticated: true,
|
|
80
|
+
operations: {
|
|
81
|
+
getTweet: true,
|
|
82
|
+
getTweetReplies: true,
|
|
83
|
+
getUserProfile: true,
|
|
84
|
+
searchTweets: true,
|
|
85
|
+
},
|
|
86
|
+
pagination: { replies: true, search: true },
|
|
87
|
+
limits: { replies: 100, search: 100 },
|
|
88
|
+
};
|
|
89
|
+
constructor(client) {
|
|
90
|
+
this.client = client;
|
|
91
|
+
}
|
|
92
|
+
static create(config) {
|
|
93
|
+
return new RettiwtProvider(new Rettiwt({
|
|
94
|
+
apiKey: config.apiKey,
|
|
95
|
+
timeout: config.requestTimeoutMs,
|
|
96
|
+
logging: false,
|
|
97
|
+
maxRetries: 0,
|
|
98
|
+
}));
|
|
99
|
+
}
|
|
100
|
+
async getTweet(request, context) {
|
|
101
|
+
try {
|
|
102
|
+
const tweet = await withDeadline(this.client.tweet.details(request.tweetId), context.signal, context.deadline, timeoutError);
|
|
103
|
+
if (!tweet) {
|
|
104
|
+
throw new TwitterError({
|
|
105
|
+
code: "NOT_FOUND",
|
|
106
|
+
safeMessage: `Post ${request.tweetId} was not found`,
|
|
107
|
+
provider: "rettiwt",
|
|
108
|
+
retryable: false,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
return mapRettiwtTweet(tweet);
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
throw mapRettiwtFailure(error);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
async getTweetReplies(request, context) {
|
|
118
|
+
try {
|
|
119
|
+
const result = await withDeadline(this.client.tweet.replies(request.tweetId), context.signal, context.deadline, timeoutError);
|
|
120
|
+
return {
|
|
121
|
+
items: result.list.slice(0, request.maxResults).map(mapRettiwtTweet),
|
|
122
|
+
...(result.next ? { nextCursor: result.next } : {}),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
throw mapRettiwtFailure(error);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async getUserProfile(request, context) {
|
|
130
|
+
try {
|
|
131
|
+
const user = await withDeadline(this.client.user.details(request.username), context.signal, context.deadline, timeoutError);
|
|
132
|
+
if (!user) {
|
|
133
|
+
throw new TwitterError({
|
|
134
|
+
code: "NOT_FOUND",
|
|
135
|
+
safeMessage: `User @${request.username} was not found`,
|
|
136
|
+
provider: "rettiwt",
|
|
137
|
+
retryable: false,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
return mapRettiwtUser(user);
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
throw mapRettiwtFailure(error);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
async searchTweets(request, context) {
|
|
147
|
+
try {
|
|
148
|
+
const { filter, warnings } = parseSearch(request.query);
|
|
149
|
+
const result = await withDeadline(this.client.tweet.search(filter, request.maxResults), context.signal, context.deadline, timeoutError);
|
|
150
|
+
return {
|
|
151
|
+
items: result.list.slice(0, request.maxResults).map(mapRettiwtTweet),
|
|
152
|
+
...(result.next ? { nextCursor: result.next } : {}),
|
|
153
|
+
...(warnings.length ? { warnings } : {}),
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
throw mapRettiwtFailure(error);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../../../src/providers/rettiwt/provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/server";
|
|
2
|
+
import type { RuntimeConfig } from "../config/env.js";
|
|
3
|
+
import type { TwitterProvider } from "../providers/provider.js";
|
|
4
|
+
export interface CreateServerOptions {
|
|
5
|
+
config: RuntimeConfig;
|
|
6
|
+
provider: TwitterProvider;
|
|
7
|
+
version: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function createTwitterServer(options: CreateServerOptions): McpServer;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/server";
|
|
2
|
+
import { registerTwitterTools } from "./register-tools.js";
|
|
3
|
+
export function createTwitterServer(options) {
|
|
4
|
+
const server = new McpServer({ name: "twitter-mcp", version: options.version }, {
|
|
5
|
+
instructions: "Use these tools to read public X data. Results can omit fields that the selected provider does not return. Call get_server_info to check the provider, limits, and pagination support.",
|
|
6
|
+
});
|
|
7
|
+
registerTwitterTools(server, options);
|
|
8
|
+
return server;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=create-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-server.js","sourceRoot":"","sources":["../../src/server/create-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAIzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAQ3D,MAAM,UAAU,mBAAmB,CAAC,OAA4B;IAC9D,MAAM,MAAM,GAAG,IAAI,SAAS,CAC1B,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,EACjD;QACE,YAAY,EACV,wLAAwL;KAC3L,CACF,CAAC;IACF,oBAAoB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare function parseTweetReference(value: string): string;
|
|
3
|
+
export declare function normalizeUsername(value: string): string;
|
|
4
|
+
export declare const getTweetInputSchema: z.ZodObject<{
|
|
5
|
+
tweet_id: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
6
|
+
}, z.core.$strip>;
|
|
7
|
+
export declare const getTweetRepliesInputSchema: z.ZodObject<{
|
|
8
|
+
tweet_id: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
9
|
+
max_results: z.ZodDefault<z.ZodNumber>;
|
|
10
|
+
}, z.core.$strip>;
|
|
11
|
+
export declare const getUserProfileInputSchema: z.ZodObject<{
|
|
12
|
+
username: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
13
|
+
}, z.core.$strip>;
|
|
14
|
+
export declare const searchTweetsInputSchema: z.ZodObject<{
|
|
15
|
+
query: z.ZodString;
|
|
16
|
+
max_results: z.ZodDefault<z.ZodNumber>;
|
|
17
|
+
}, z.core.$strip>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
const TWEET_ID = /^\d{10,20}$/;
|
|
3
|
+
const USERNAME = /^[A-Za-z0-9_]{1,15}$/;
|
|
4
|
+
const STATUS_URL = /^https:\/\/(?:www\.)?(?:x\.com|twitter\.com)\/[^/]+\/status\/(\d{10,20})(?:[/?#].*)?$/i;
|
|
5
|
+
export function parseTweetReference(value) {
|
|
6
|
+
const candidate = value.trim();
|
|
7
|
+
if (TWEET_ID.test(candidate))
|
|
8
|
+
return candidate;
|
|
9
|
+
const match = STATUS_URL.exec(candidate);
|
|
10
|
+
if (match?.[1])
|
|
11
|
+
return match[1];
|
|
12
|
+
throw new Error("tweet_id must be a valid X post ID or URL");
|
|
13
|
+
}
|
|
14
|
+
export function normalizeUsername(value) {
|
|
15
|
+
const candidate = value.trim().replace(/^@/, "");
|
|
16
|
+
if (USERNAME.test(candidate))
|
|
17
|
+
return candidate;
|
|
18
|
+
throw new Error("username must be a valid X username");
|
|
19
|
+
}
|
|
20
|
+
export const getTweetInputSchema = z.object({
|
|
21
|
+
tweet_id: z.string().transform(parseTweetReference),
|
|
22
|
+
});
|
|
23
|
+
export const getTweetRepliesInputSchema = z.object({
|
|
24
|
+
tweet_id: z.string().transform(parseTweetReference),
|
|
25
|
+
max_results: z.number().int().min(1).max(100).default(10),
|
|
26
|
+
});
|
|
27
|
+
export const getUserProfileInputSchema = z.object({
|
|
28
|
+
username: z.string().transform(normalizeUsername),
|
|
29
|
+
});
|
|
30
|
+
export const searchTweetsInputSchema = z.object({
|
|
31
|
+
query: z.string().trim().min(1).max(512),
|
|
32
|
+
max_results: z.number().int().min(1).max(100).default(10),
|
|
33
|
+
});
|
|
34
|
+
//# sourceMappingURL=input.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input.js","sourceRoot":"","sources":["../../src/server/input.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,QAAQ,GAAG,aAAa,CAAC;AAC/B,MAAM,QAAQ,GAAG,sBAAsB,CAAC;AACxC,MAAM,UAAU,GACd,wFAAwF,CAAC;AAE3F,MAAM,UAAU,mBAAmB,CAAC,KAAa;IAC/C,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC/B,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC;QAAE,OAAO,SAAS,CAAC;IAE/C,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACzC,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC;IAEhC,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAAa;IAC7C,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACjD,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC;QAAE,OAAO,SAAS,CAAC;IAC/C,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;AACzD,CAAC;AAED,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1C,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,SAAS,CAAC,mBAAmB,CAAC;CACpD,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,CAAC,MAAM,CAAC;IACjD,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,SAAS,CAAC,mBAAmB,CAAC;IACnD,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;CAC1D,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC,MAAM,CAAC;IAChD,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,SAAS,CAAC,iBAAiB,CAAC;CAClD,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;IACxC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;CAC1D,CAAC,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { CallToolResult } from "@modelcontextprotocol/server";
|
|
2
|
+
import type { Tweet, TweetPage, UserProfile } from "../domain/schemas.js";
|
|
3
|
+
import { type ProviderName } from "../errors/twitter-error.js";
|
|
4
|
+
export declare function tweetResult(tweet: Tweet): CallToolResult;
|
|
5
|
+
export declare function profileResult(profile: UserProfile): CallToolResult;
|
|
6
|
+
export declare function pageResult(page: TweetPage): CallToolResult;
|
|
7
|
+
export declare function objectResult(value: Record<string, unknown>): CallToolResult;
|
|
8
|
+
export declare function errorResult(error: unknown, provider: ProviderName): CallToolResult;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { toTwitterError } from "../errors/twitter-error.js";
|
|
2
|
+
function text(value) {
|
|
3
|
+
return [{ type: "text", text: JSON.stringify(value, null, 2) }];
|
|
4
|
+
}
|
|
5
|
+
export function tweetResult(tweet) {
|
|
6
|
+
return { content: text(tweet), structuredContent: { tweet } };
|
|
7
|
+
}
|
|
8
|
+
export function profileResult(profile) {
|
|
9
|
+
return { content: text(profile), structuredContent: { profile } };
|
|
10
|
+
}
|
|
11
|
+
export function pageResult(page) {
|
|
12
|
+
return { content: text(page.items), structuredContent: page };
|
|
13
|
+
}
|
|
14
|
+
export function objectResult(value) {
|
|
15
|
+
return { content: text(value), structuredContent: value };
|
|
16
|
+
}
|
|
17
|
+
export function errorResult(error, provider) {
|
|
18
|
+
const mapped = toTwitterError(error, provider);
|
|
19
|
+
const details = {
|
|
20
|
+
code: mapped.code,
|
|
21
|
+
message: mapped.safeMessage,
|
|
22
|
+
provider: mapped.provider,
|
|
23
|
+
retryable: mapped.retryable,
|
|
24
|
+
...(mapped.retryAfterSeconds === undefined
|
|
25
|
+
? {}
|
|
26
|
+
: { retryAfterSeconds: mapped.retryAfterSeconds }),
|
|
27
|
+
};
|
|
28
|
+
return {
|
|
29
|
+
isError: true,
|
|
30
|
+
content: [{ type: "text", text: `${mapped.code}: ${mapped.safeMessage}` }],
|
|
31
|
+
structuredContent: { error: details },
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/server/output.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAqB,MAAM,4BAA4B,CAAC;AAE/E,SAAS,IAAI,CAAC,KAAc;IAC1B,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAY;IACtC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,iBAAiB,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC;AAChE,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAAoB;IAChD,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,iBAAiB,EAAE,EAAE,OAAO,EAAE,EAAE,CAAC;AACpE,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAe;IACxC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC;AAChE,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAA8B;IACzD,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,iBAAiB,EAAE,KAAK,EAAE,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAc,EAAE,QAAsB;IAChE,MAAM,MAAM,GAAG,cAAc,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAC/C,MAAM,OAAO,GAAG;QACd,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,OAAO,EAAE,MAAM,CAAC,WAAW;QAC3B,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,GAAG,CAAC,MAAM,CAAC,iBAAiB,KAAK,SAAS;YACxC,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,EAAE,iBAAiB,EAAE,MAAM,CAAC,iBAAiB,EAAE,CAAC;KACrD,CAAC;IAEF,OAAO;QACL,OAAO,EAAE,IAAI;QACb,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC;QAC1E,iBAAiB,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE;KACtC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/server";
|
|
2
|
+
import type { RuntimeConfig } from "../config/env.js";
|
|
3
|
+
import type { TwitterProvider } from "../providers/provider.js";
|
|
4
|
+
export interface RegisterToolsOptions {
|
|
5
|
+
config: RuntimeConfig;
|
|
6
|
+
provider: TwitterProvider;
|
|
7
|
+
version: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function registerTwitterTools(server: McpServer, { config, provider, version }: RegisterToolsOptions): void;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { TweetPageSchema, TweetSchema, UserProfileSchema } from "../domain/schemas.js";
|
|
3
|
+
import { getTweetInputSchema, getTweetRepliesInputSchema, getUserProfileInputSchema, searchTweetsInputSchema, } from "./input.js";
|
|
4
|
+
import { errorResult, objectResult, pageResult, profileResult, tweetResult } from "./output.js";
|
|
5
|
+
const annotations = {
|
|
6
|
+
readOnlyHint: true,
|
|
7
|
+
destructiveHint: false,
|
|
8
|
+
idempotentHint: true,
|
|
9
|
+
openWorldHint: true,
|
|
10
|
+
};
|
|
11
|
+
const tweetOutputSchema = z.object({ tweet: TweetSchema });
|
|
12
|
+
const profileOutputSchema = z.object({ profile: UserProfileSchema });
|
|
13
|
+
const pageOutputSchema = TweetPageSchema;
|
|
14
|
+
const providerCapabilitiesSchema = z.object({
|
|
15
|
+
provider: z.enum(["rettiwt", "api"]),
|
|
16
|
+
authenticated: z.boolean(),
|
|
17
|
+
operations: z.object({
|
|
18
|
+
getTweet: z.boolean(),
|
|
19
|
+
getTweetReplies: z.boolean(),
|
|
20
|
+
getUserProfile: z.boolean(),
|
|
21
|
+
searchTweets: z.boolean(),
|
|
22
|
+
}),
|
|
23
|
+
pagination: z.object({ replies: z.boolean(), search: z.boolean() }),
|
|
24
|
+
limits: z.object({ replies: z.number().int(), search: z.number().int() }),
|
|
25
|
+
});
|
|
26
|
+
const serverInfoOutputSchema = z.object({
|
|
27
|
+
name: z.string(),
|
|
28
|
+
version: z.string(),
|
|
29
|
+
mode: z.enum(["rettiwt", "api"]),
|
|
30
|
+
provider: z.enum(["rettiwt", "api"]),
|
|
31
|
+
authenticated: z.boolean(),
|
|
32
|
+
tools: z.array(z.string()),
|
|
33
|
+
capabilities: providerCapabilitiesSchema,
|
|
34
|
+
});
|
|
35
|
+
const publicTools = [
|
|
36
|
+
"get_tweet",
|
|
37
|
+
"get_tweet_replies",
|
|
38
|
+
"get_user_profile",
|
|
39
|
+
"search_tweets",
|
|
40
|
+
];
|
|
41
|
+
async function execute(context, provider, timeoutMs, operation, format) {
|
|
42
|
+
const controller = new AbortController();
|
|
43
|
+
const deadline = Date.now() + timeoutMs;
|
|
44
|
+
const cancel = () => controller.abort(context.mcpReq.signal.reason);
|
|
45
|
+
const timeout = setTimeout(() => controller.abort(new Error("Request deadline exceeded")), timeoutMs);
|
|
46
|
+
if (context.mcpReq.signal.aborted)
|
|
47
|
+
cancel();
|
|
48
|
+
else
|
|
49
|
+
context.mcpReq.signal.addEventListener("abort", cancel, { once: true });
|
|
50
|
+
try {
|
|
51
|
+
return format(await operation({ signal: controller.signal, deadline }));
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
return errorResult(error, provider.capabilities.provider);
|
|
55
|
+
}
|
|
56
|
+
finally {
|
|
57
|
+
clearTimeout(timeout);
|
|
58
|
+
context.mcpReq.signal.removeEventListener("abort", cancel);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
export function registerTwitterTools(server, { config, provider, version }) {
|
|
62
|
+
server.registerTool("get_tweet", {
|
|
63
|
+
title: "Get tweet",
|
|
64
|
+
description: "Fetch a public X post from its numeric ID or status URL.",
|
|
65
|
+
inputSchema: getTweetInputSchema,
|
|
66
|
+
outputSchema: tweetOutputSchema,
|
|
67
|
+
annotations,
|
|
68
|
+
}, (input, context) => execute(context, provider, config.requestTimeoutMs, (requestContext) => provider.getTweet({ tweetId: input.tweet_id }, requestContext), tweetResult));
|
|
69
|
+
server.registerTool("get_tweet_replies", {
|
|
70
|
+
title: "Get tweet replies",
|
|
71
|
+
description: "Fetch replies to a public X post.",
|
|
72
|
+
inputSchema: getTweetRepliesInputSchema,
|
|
73
|
+
outputSchema: pageOutputSchema,
|
|
74
|
+
annotations,
|
|
75
|
+
}, (input, context) => execute(context, provider, config.requestTimeoutMs, (requestContext) => provider.getTweetReplies({ tweetId: input.tweet_id, maxResults: input.max_results }, requestContext), pageResult));
|
|
76
|
+
server.registerTool("get_user_profile", {
|
|
77
|
+
title: "Get user profile",
|
|
78
|
+
description: "Fetch a public X profile by username.",
|
|
79
|
+
inputSchema: getUserProfileInputSchema,
|
|
80
|
+
outputSchema: profileOutputSchema,
|
|
81
|
+
annotations,
|
|
82
|
+
}, (input, context) => execute(context, provider, config.requestTimeoutMs, (requestContext) => provider.getUserProfile({ username: input.username }, requestContext), profileResult));
|
|
83
|
+
server.registerTool("search_tweets", {
|
|
84
|
+
title: "Search tweets",
|
|
85
|
+
description: "Search public X posts. The selected provider determines which operators work.",
|
|
86
|
+
inputSchema: searchTweetsInputSchema,
|
|
87
|
+
outputSchema: pageOutputSchema,
|
|
88
|
+
annotations,
|
|
89
|
+
}, (input, context) => execute(context, provider, config.requestTimeoutMs, (requestContext) => provider.searchTweets({ query: input.query, maxResults: input.max_results }, requestContext), pageResult));
|
|
90
|
+
server.registerTool("get_server_info", {
|
|
91
|
+
title: "Get server info",
|
|
92
|
+
description: "Show the server version, selected provider, limits, and supported operations.",
|
|
93
|
+
inputSchema: z.object({}),
|
|
94
|
+
outputSchema: serverInfoOutputSchema,
|
|
95
|
+
annotations,
|
|
96
|
+
}, () => objectResult({
|
|
97
|
+
name: "twitter-mcp",
|
|
98
|
+
version,
|
|
99
|
+
mode: config.mode,
|
|
100
|
+
provider: provider.capabilities.provider,
|
|
101
|
+
authenticated: provider.capabilities.authenticated,
|
|
102
|
+
tools: [...publicTools],
|
|
103
|
+
capabilities: provider.capabilities,
|
|
104
|
+
}));
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=register-tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register-tools.js","sourceRoot":"","sources":["../../src/server/register-tools.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@granitebps/twitter-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"bin": {
|
|
14
|
+
"twitter-mcp": "dist/cli.js"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE",
|
|
20
|
+
"server.json"
|
|
21
|
+
],
|
|
22
|
+
"mcpName": "io.github.granitebps/twitter-mcp",
|
|
23
|
+
"scripts": {
|
|
24
|
+
"clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
25
|
+
"build": "npm run clean && tsc -p tsconfig.build.json",
|
|
26
|
+
"dev": "npm run build && npm start",
|
|
27
|
+
"start": "node dist/cli.js",
|
|
28
|
+
"typecheck": "tsc --noEmit",
|
|
29
|
+
"test": "npm run build && vitest run",
|
|
30
|
+
"test:coverage": "npm run build && vitest run --coverage",
|
|
31
|
+
"lint": "eslint .",
|
|
32
|
+
"format": "prettier --write .",
|
|
33
|
+
"format:check": "prettier --check .",
|
|
34
|
+
"check:package": "node scripts/check-package.mjs",
|
|
35
|
+
"check:install": "node scripts/check-install.mjs",
|
|
36
|
+
"test:live": "npm run build && vitest run --config vitest.live.config.ts",
|
|
37
|
+
"check": "npm run format:check && npm run lint && npm run typecheck && npm run test:coverage && npm run check:package && npm run check:install",
|
|
38
|
+
"prepack": "npm run clean && npm run build"
|
|
39
|
+
},
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": "^22.21.0"
|
|
42
|
+
},
|
|
43
|
+
"keywords": [
|
|
44
|
+
"mcp",
|
|
45
|
+
"twitter",
|
|
46
|
+
"x",
|
|
47
|
+
"ai",
|
|
48
|
+
"model-context-protocol",
|
|
49
|
+
"stdio"
|
|
50
|
+
],
|
|
51
|
+
"author": "granitebps",
|
|
52
|
+
"license": "ISC",
|
|
53
|
+
"description": "Read public Twitter/X posts, replies, profiles, and search results through MCP.",
|
|
54
|
+
"repository": {
|
|
55
|
+
"type": "git",
|
|
56
|
+
"url": "git+https://github.com/granitebps/twitter-mcp.git"
|
|
57
|
+
},
|
|
58
|
+
"homepage": "https://github.com/granitebps/twitter-mcp#readme",
|
|
59
|
+
"bugs": {
|
|
60
|
+
"url": "https://github.com/granitebps/twitter-mcp/issues"
|
|
61
|
+
},
|
|
62
|
+
"publishConfig": {
|
|
63
|
+
"access": "public"
|
|
64
|
+
},
|
|
65
|
+
"dependencies": {
|
|
66
|
+
"@modelcontextprotocol/server": "^2.0.0",
|
|
67
|
+
"dotenv": "^17.4.2",
|
|
68
|
+
"rettiwt-api": "^7.1.3",
|
|
69
|
+
"twitter-api-v2": "^1.29.1",
|
|
70
|
+
"zod": "^4.4.3"
|
|
71
|
+
},
|
|
72
|
+
"devDependencies": {
|
|
73
|
+
"@eslint/js": "^9.0.0",
|
|
74
|
+
"@modelcontextprotocol/client": "^2.0.0",
|
|
75
|
+
"@types/node": "^25.5.0",
|
|
76
|
+
"@vitest/coverage-v8": "^3.0.0",
|
|
77
|
+
"eslint": "^9.0.0",
|
|
78
|
+
"prettier": "^3.0.0",
|
|
79
|
+
"typescript": "^6.0.2",
|
|
80
|
+
"typescript-eslint": "^8.0.0",
|
|
81
|
+
"vitest": "^3.0.0"
|
|
82
|
+
}
|
|
83
|
+
}
|
package/server.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
|
+
"name": "io.github.granitebps/twitter-mcp",
|
|
4
|
+
"title": "Twitter/X MCP",
|
|
5
|
+
"description": "Read public Twitter/X posts, replies, profiles, and search results through Rettiwt or the official X API.",
|
|
6
|
+
"repository": {
|
|
7
|
+
"url": "https://github.com/granitebps/twitter-mcp",
|
|
8
|
+
"source": "github"
|
|
9
|
+
},
|
|
10
|
+
"version": "1.0.0",
|
|
11
|
+
"packages": [
|
|
12
|
+
{
|
|
13
|
+
"registryType": "npm",
|
|
14
|
+
"identifier": "@granitebps/twitter-mcp",
|
|
15
|
+
"version": "1.0.0",
|
|
16
|
+
"transport": {
|
|
17
|
+
"type": "stdio"
|
|
18
|
+
},
|
|
19
|
+
"environmentVariables": [
|
|
20
|
+
{
|
|
21
|
+
"name": "RETTIWT_API_KEY",
|
|
22
|
+
"description": "Rettiwt key containing an authenticated X browser session",
|
|
23
|
+
"isRequired": true,
|
|
24
|
+
"isSecret": true,
|
|
25
|
+
"format": "string"
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
}
|