Run a local MCP server that exposes the X API OpenAPI spec as tools using FastMCP. Streaming and webhook endpoints are excluded.
- Python 3.9+
- An X Developer Platform app (to get tokens)
- Optional: an xAI API key if you want to run the Grok test client
- Create a virtual environment and install dependencies:
python -m venv .venvsource .venv/bin/activatepip install -r requirements.txt
- Create your local
.env:cp env.example .env- Required values (do not skip):
X_OAUTH_CONSUMER_KEYX_OAUTH_CONSUMER_SECRETX_BEARER_TOKEN(required for this setup; keep it set even if using OAuth1)
- OAuth1 callback (defaults are fine):
X_OAUTH_CALLBACK_HOST(default127.0.0.1)X_OAUTH_CALLBACK_PORT(default8976)X_OAUTH_CALLBACK_PATH(default/oauth/callback)X_OAUTH_CALLBACK_TIMEOUT(default300)
- Server settings (optional):
X_API_BASE_URL(defaulthttps://api.x.com)X_API_TIMEOUT(default30)X_OPENAPI_SPEC_URL(defaulthttps://api.x.com/2/openapi.json)X_OPENAPI_SPEC_TIMEOUT(default falls back toX_API_TIMEOUT)X_OPENAPI_FETCH_RETRIES(default3)X_OPENAPI_FETCH_BACKOFF_SECONDS(default2)X_OPENAPI_SPEC_CACHE_PATH(default~/.cache/xmcp/openapi_spec.json)MCP_HOST(default127.0.0.1)MCP_PORT(default8000)X_API_DEBUG(default1)
- Tool filtering (optional, comma-separated):
X_API_TOOL_ALLOWLIST
- Optional Grok test client:
XAI_API_KEYXAI_MODEL(defaultgrok-4-1-fast)MCP_SERVER_URL(defaulthttp://127.0.0.1:8000/mcp)
- Optional OAuth2 token generation:
CLIENT_IDCLIENT_SECRETX_OAUTH_ACCESS_TOKEN-X_OAUTH_ACCESS_TOKEN_SECRET(optional)
- Optional OAuth1 debug output:
X_OAUTH_PRINT_TOKENSX_OAUTH_PRINT_AUTH_HEADER
- Register the callback URL in your X Developer App:
http://<X_OAUTH_CALLBACK_HOST>:<X_OAUTH_CALLBACK_PORT><X_OAUTH_CALLBACK_PATH>
Example (defaults):
http://127.0.0.1:8976/oauth/callback
- Start the server:
python server.py
The MCP endpoint is http://127.0.0.1:8000/mcp by default.
- Connect an MCP client:
- Local client: point it to
http://127.0.0.1:8000/mcp. - Remote client: tunnel your local server (e.g., ngrok) and use the public URL.
Use X_API_TOOL_ALLOWLIST to load a small, explicit set of tools:
X_API_TOOL_ALLOWLIST=getUsersByUsername,createPosts,searchPostsRecent
For bookmark lookup, add the OAuth2-only bookmark tools too:
X_API_TOOL_ALLOWLIST=getUsersMe,getUsersByUsername,getPostsById,searchPostsRecent,getUsersTimeline,getUsersBookmarks,getUsersBookmarkFolders,getUsersBookmarksByFolderId
Whitelisting is applied at startup when the OpenAPI spec is loaded, so restart the server after changes. See the full tool list below before building your allowlist.
At startup XMCP fetches the live X OpenAPI spec and caches the last successful copy locally. If the live fetch later fails due to a transient DNS or network issue, XMCP retries and then falls back to the cached spec instead of exiting.
Default cache path:
$XDG_CACHE_HOME/xmcp/openapi_spec.json
If XDG_CACHE_HOME is unset, XMCP falls back to:
~/.cache/xmcp/openapi_spec.json
Useful knobs:
X_OPENAPI_SPEC_TIMEOUT=30
X_OPENAPI_FETCH_RETRIES=3
X_OPENAPI_FETCH_BACKOFF_SECONDS=2
X_OPENAPI_SPEC_CACHE_PATH=/custom/path/openapi_spec.json
On startup, the server opens a browser for OAuth1 consent and waits for the
callback. Fresh OAuth1 tokens are now persisted back into .env as
X_OAUTH_ACCESS_TOKEN and X_OAUTH_ACCESS_TOKEN_SECRET so restarts do not
require reauth. Set X_OAUTH_PRINT_TOKENS=*** to print tokens, or X_OAUTH_PRINT_AUTH_HEADER=*** to print request headers.
Below is the full list of tool calls you can whitelist via
X_API_TOOL_ALLOWLIST. Copy any of these into your .env allowlist.
addListsMemberaddUserPublicKeyappendMediaUploadblockUsersDmscreateCommunityNotescreateComplianceJobscreateDirectMessagesByConversationIdcreateDirectMessagesByParticipantIdcreateDirectMessagesConversationcreateListscreateMediaMetadatacreateMediaSubtitlescreatePostscreateUsersBookmarkdeleteActivitySubscriptiondeleteAllConnectionsdeleteCommunityNotesdeleteConnectionsByEndpointdeleteConnectionsByUuidsdeleteDirectMessagesEventsdeleteListsdeleteMediaSubtitlesdeletePostsdeleteUsersBookmarkevaluateCommunityNotesfinalizeMediaUploadfollowListfollowUsergetAccountActivitySubscriptionCountgetActivitySubscriptionsgetChatConversationgetChatConversationsgetCommunitiesByIdgetComplianceJobsgetComplianceJobsByIdgetConnectionHistorygetDirectMessagesEventsgetDirectMessagesEventsByConversationIdgetDirectMessagesEventsByIdgetDirectMessagesEventsByParticipantIdgetInsights28HrgetInsightsHistoricalgetListsByIdgetListsFollowersgetListsMembersgetListsPostsgetMarketplaceHandleAvailabilitygetMediaAnalyticsgetMediaByMediaKeygetMediaByMediaKeysgetMediaUploadStatusgetNewsgetOpenApiSpecgetPostsAnalyticsgetPostsByIdgetPostsByIdsgetPostsCountsAllgetPostsCountsRecentgetPostsLikingUsersgetPostsQuotedPostsgetPostsRepostedBygetPostsRepostsgetSpacesBuyersgetSpacesByCreatorIdsgetSpacesByIdgetSpacesByIdsgetSpacesPostsgetTrendsByWoeidgetTrendsPersonalizedTrendsgetUsagegetUserPublicKeysgetUsersAffiliatesgetUsersBlockinggetUsersBookmarkFoldersgetUsersBookmarksgetUsersBookmarksByFolderIdgetUsersByIdgetUsersByIdsgetUsersByUsernamegetUsersByUsernamesgetUsersFollowedListsgetUsersFollowersgetUsersFollowinggetUsersLikedPostsgetUsersListMembershipsgetUsersMegetUsersMentionsgetUsersMutinggetUsersOwnedListsgetUsersPinnedListsgetUsersPostsgetUsersRepostsOfMegetUsersTimelinehidePostsReplyinitializeMediaUploadlikePostmediaUploadmuteUserpinListremoveListsMemberByUserIdrepostPostsearchCommunitiessearchCommunityNotesWrittensearchEligiblePostssearchNewssearchPostsAllsearchPostsRecentsearchSpacessearchUserssendChatMessageunblockUsersDmsunfollowListunfollowUserunlikePostunmuteUserunpinListunrepostPostupdateActivitySubscriptionupdateLists
Use this when your allowlist contains OAuth2-only endpoints such as bookmarks.
- Add
CLIENT_IDandCLIENT_SECRETto your.env. - Make sure your X app allows OAuth2 PKCE and exact-match callback validation.
- Register the callback URL from your
.env(Spark default:http://127.0.0.1:8976/oauth/callback). - Ensure the app has scopes needed for bookmarks:
bookmark.read,tweet.read,users.read,offline.access. - Run:
python generate_authtoken.pyThe helper stores the OAuth2 token set back into .env as:
X_OAUTH2_ACCESS_TOKENX_OAUTH2_REFRESH_TOKENX_OAUTH2_TOKEN_TYPEX_OAUTH2_EXPIRES_AT
The server will automatically refresh the OAuth2 token when offline.access
is present.
- Set
XAI_API_KEYin.env. - Make sure your MCP server is running locally (or set
MCP_SERVER_URL). - If Grok is not running on your machine, use ngrok to expose your local MCP
server and set
MCP_SERVER_URLto the public HTTPS URL that ends with/mcp. Example flow:ngrok http 8000thenMCP_SERVER_URL=https://<id>.ngrok-free.dev/mcp. - Run
python test_grok_mcp.py.
- Endpoints with
/streamor/webhooksin the path are excluded. - Operations tagged
StreamorWebhooks, or marked withx-twitter-streaming: true, are excluded. - The OpenAPI spec is fetched from
https://api.twitter.com/2/openapi.jsonat startup.