@@ -25,20 +25,35 @@ const (
2525 unansweredWindow = 24 * time .Hour
2626)
2727
28+ var debugLogging bool
29+
2830func init () {
2931 // Disable log output by default so it doesn't break MCP stdio (stdout is used for JSON-RPC).
3032 // Set TWITTER_DEBUG=1 or MCP_TWITTER_DEBUG=1 to enable logging for debugging.
31- if os .Getenv ("TWITTER_DEBUG" ) == "" && os .Getenv ("MCP_TWITTER_DEBUG" ) == "" {
33+ debugLogging = os .Getenv ("TWITTER_DEBUG" ) != "" || os .Getenv ("MCP_TWITTER_DEBUG" ) != ""
34+ if ! debugLogging {
3235 log .SetOutput (io .Discard )
3336 }
3437}
3538
39+ func debugLog (v ... interface {}) {
40+ if debugLogging {
41+ log .Println (v ... )
42+ }
43+ }
44+
45+ func debugLogf (format string , v ... interface {}) {
46+ if debugLogging {
47+ log .Printf (format , v ... )
48+ }
49+ }
50+
3651var (
37- client * twitter.Client
38- authUserID string
39- maxTweets int
40- hasUserCtx bool
41- v1Client * http.Client
52+ client * twitter.Client
53+ authUserID string
54+ maxTweets int
55+ hasUserCtx bool
56+ v1Client * http.Client
4257)
4358
4459// bearerAuthorizer adds Bearer token to requests
@@ -111,9 +126,9 @@ type GetTrendsInput struct {
111126}
112127
113128type GetUserRelationshipsInput struct {
114- UserID string `json:"user_id" jsonschema:"user ID"`
115- Type string `json:"type" jsonschema:"followers or following"`
116- MaxResults int `json:"max_results,omitempty" jsonschema:"max users (default 50, cap 100)"`
129+ UserID string `json:"user_id" jsonschema:"user ID"`
130+ Type string `json:"type" jsonschema:"followers or following"`
131+ MaxResults int `json:"max_results,omitempty" jsonschema:"max users (default 50, cap 100)"`
117132}
118133
119134type FollowUserInput struct {
@@ -128,12 +143,12 @@ type UploadMediaInput struct {
128143
129144// Simplified output types (JSON-friendly)
130145type TweetOut struct {
131- ID string `json:"id"`
132- Text string `json:"text"`
133- AuthorID string `json:"author_id,omitempty"`
134- CreatedAt string `json:"created_at,omitempty"`
135- Metrics map [string ]int `json:"public_metrics,omitempty"`
136- MediaURLs []string `json:"media_urls,omitempty"`
146+ ID string `json:"id"`
147+ Text string `json:"text"`
148+ AuthorID string `json:"author_id,omitempty"`
149+ CreatedAt string `json:"created_at,omitempty"`
150+ Metrics map [string ]int `json:"public_metrics,omitempty"`
151+ MediaURLs []string `json:"media_urls,omitempty"`
137152}
138153
139154type UserOut struct {
@@ -170,9 +185,9 @@ type GetListTweetsOutput struct {
170185}
171186
172187type TrendOut struct {
173- Name string `json:"name"`
174- Query string `json:"query"`
175- TweetVolume int `json:"tweet_volume,omitempty"`
188+ Name string `json:"name"`
189+ Query string `json:"query"`
190+ TweetVolume int `json:"tweet_volume,omitempty"`
176191}
177192
178193type GetTrendsOutput struct {
@@ -223,10 +238,10 @@ func tweetFromObj(t *twitter.TweetObj, includes *twitter.TweetRawIncludes) Tweet
223238 }
224239 if t .PublicMetrics != nil {
225240 out .Metrics = map [string ]int {
226- "like_count" : t .PublicMetrics .Likes ,
227- "reply_count" : t .PublicMetrics .Replies ,
228- "retweet_count" : t .PublicMetrics .Retweets ,
229- "quote_count" : t .PublicMetrics .Quotes ,
241+ "like_count" : t .PublicMetrics .Likes ,
242+ "reply_count" : t .PublicMetrics .Replies ,
243+ "retweet_count" : t .PublicMetrics .Retweets ,
244+ "quote_count" : t .PublicMetrics .Quotes ,
230245 "impression_count" : t .PublicMetrics .Impressions ,
231246 }
232247 }
@@ -245,11 +260,11 @@ func userFromObj(u *twitter.UserObj) UserOut {
245260 return UserOut {}
246261 }
247262 out := UserOut {
248- ID : u .ID ,
249- Name : u .Name ,
250- Username : u .UserName ,
251- Description : u .Description ,
252- ProfileImageURL : u .ProfileImageURL ,
263+ ID : u .ID ,
264+ Name : u .Name ,
265+ Username : u .UserName ,
266+ Description : u .Description ,
267+ ProfileImageURL : u .ProfileImageURL ,
253268 }
254269 if u .PublicMetrics != nil {
255270 out .PublicMetrics = map [string ]int {
@@ -832,11 +847,11 @@ func InitClientFromEnv() bool {
832847 ctx := context .Background ()
833848 resp , err := client .AuthUserLookup (ctx , twitter.UserLookupOpts {})
834849 if err != nil {
835- log . Printf ("Warning: could not resolve auth user: %v" , err )
850+ debugLogf ("Warning: could not resolve auth user: %v" , err )
836851 } else if resp .Raw != nil && len (resp .Raw .Users ) > 0 {
837852 authUserID = resp .Raw .Users [0 ].ID
838853 }
839- log . Println ("Twitter MCP: using OAuth 1.0a user context" )
854+ debugLog ("Twitter MCP: using OAuth 1.0a user context" )
840855 return true
841856 }
842857 if bearer != "" {
@@ -845,7 +860,7 @@ func InitClientFromEnv() bool {
845860 Client : http .DefaultClient ,
846861 Host : "https://api.twitter.com" ,
847862 }
848- log . Println ("Twitter MCP: using Bearer (app-only); write and home/mentions tools will fail without user context" )
863+ debugLog ("Twitter MCP: using Bearer (app-only); write and home/mentions tools will fail without user context" )
849864 return true
850865 }
851866 return false
@@ -856,6 +871,7 @@ func main() {
856871 fmt .Fprintln (os .Stderr , "twitter MCP: Set TWITTER_BEARER_TOKEN or all of TWITTER_API_KEY, TWITTER_API_SECRET, TWITTER_ACCESS_TOKEN, TWITTER_ACCESS_SECRET" )
857872 os .Exit (1 )
858873 }
874+
859875 server := mcp .NewServer (& mcp.Implementation {Name : "twitter" , Version : "v1.0.0" }, nil )
860876 mcp .AddTool (server , & mcp.Tool {Name : "get_tweets" , Description : "Fetch recent tweets from a user (with media support)" }, GetTweets )
861877 mcp .AddTool (server , & mcp.Tool {Name : "get_profile" , Description : "Get a user's profile information" }, GetProfile )
0 commit comments