关于 X (Twitter)
mcp-x 是面向需要通过 X API v2 读取、搜索和管理 X 账户的客户端的 Go MCP 服务。它提供 42 种工具,并以 OAuth 1.0a 用户上下文执行操作,身份对应所用凭据所属的账户。
主要功能
- 01搜索并统计近期帖子
- 02读取时间线、提及和书签
- 03管理用户和列表
- 04发布、点赞和转发帖子
- 05上传并附加媒体
适用场景
- 01按多个查询搜索近期帖子
- 02查看账户时间线和书签
- 03以账户身份发布帖子或互动
MCP 相关
向兼容 MCP 的客户端提供 42 种 X API v2 工具,支持 stdio 和流式 HTTP 传输。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
mcp-x
一个 MCP 服务,为大模型提供 X(Twitter)API:读取和搜索帖子、管理用户和列表、上传媒体并发布内容——使用其密钥所对应的账号操作。
网站 · 费用 · 凭据 · 工具 · 快速开始 · 配置 · 架构 · 参与贡献
简介
mcp-x 是一个使用 Go 编写的 Model Context Protocol 服务。它向任何兼容 MCP 的客户端(Claude Desktop、IDE Agent、自定义大模型应用)提供 X API v2,包含 42 个用于操作帖子、用户、列表和媒体的工具。
它使用 OAuth 1.0a 用户上下文进行身份验证,这意味着每次调用都会以一个真实 X 账号的身份执行——该账号对应这四个密钥。x_post_create 会公开发布内容。x_user_follow 会真正关注用户。x_post_delete 无法撤销。这不是沙盒,也不是免费的:将其接入 Agent 前,请先参阅 X API 需要付费。
MCP SDK 支持的两种传输方式均可使用,且提供完全相同的工具集:
- stdio — 客户端启动二进制文件,并通过 stdin/stdout 通信(默认方式,非常适合桌面客户端)。
- http — 长时间运行的流式 HTTP 服务(适用于远程或共享部署)。
X API 需要付费
警告
现在已没有免费套餐。 X 已为新开发者停用 Free/Basic/Pro 订阅套餐,改为按用量付费的额度:你需要在 Developer Console 中预先购买额度,每次请求都会实时扣除余额。旧版 Basic($200/mo)和 Pro($5,000/mo)订阅仅对已经拥有这些订阅的账号保留;Enterprise 起价约为 $42,000/mo。如今新注册的开发者账号只能按用量付费,别无其他选择。
撰写本文时的费率(官方定价——请务必在 Console 中再次确认,2026 年以来价格已多次变动):
| 操作 | 价格 |
|---|---|
| 帖子读取 | 每条返回的帖子 $0.005 |
| 自有数据读取(你自己的帖子、书签、关注者、点赞、列表) | 每项资源 $0.001 |
| 用户读取 | 每位返回的用户 $0.010 |
| 点赞 / 静音 / 屏蔽读取 | 每项资源 $0.001 |
| 关注者 / 正在关注列表读取 | 每项资源 $0.010 |
| 发布帖子 | 每次请求 $0.015 |
| 发布包含 URL 的帖子 | 每次请求 $0.200 |
| 点赞 / 转发及其他互动 | 每次请求 $0.015 |
| 列表和书签写入 | 每次请求 $0.005–$0.010 |
由此可知两点,服务已针对这两点做了相应处理:
- 读取按返回的资源数计费,而非按请求计费。 对同一个查询,
x_posts_search中的max_results: 100成本是max_results: 5的 20 倍。每个读取工具的说明都会要求模型使用能回答问题的最小max_results;批量工具(x_posts_lookup、x_users_lookup)则会要求模型批量查询,而不是循环查询。 x_posts_count不会消耗帖子读取额度。 它会针对相同的查询语法,按分钟 / 小时 / 天汇总返回匹配数量。先用x_posts_count估算主题规模,再为x_posts_search付费。
X 还会去重:在 24 小时 UTC 时段内重复获取同一资源只计费一次。按用量付费每个计费周期最多可读取 300 万条帖子——超过后,只有 Enterprise 可用。
额度用尽时,API 会返回一种独特的错误,服务会将其映射为一条明确告知模型不要重试的消息——参见错误。
凭据
服务需要来自同一个 X 应用的 4 个 OAuth 1.0a 值:
X_API_KEY
X_API_KEY_SECRET
X_ACCESS_TOKEN
X_ACCESS_TOKEN_SECRET
前两项用于标识应用;后两项用于标识通过该应用执行操作的账号。mcp-x 使用 AuthenMethodOAuth1UserContext,而非仅应用的 bearer 认证,因为每个写入端点和每个“我”端点(x_users_me、x_posts_home、x_bookmarks_list、提及)都需要用户上下文。此处没有 bearer token 模式。
获取凭据——以及人人都会弄错的那一步
- 前往 developer.x.com → 你的项目 → 你的应用。
- 打开 User authentication settings,将 App permissions 设为 Read and Write。先完成这一步。
- 然后前往 Keys and tokens,生成 Access Token and Secret。
重要
访问令牌会永久保留生成时应用所拥有的权限。 如果你在应用为只读时创建了令牌,之后再将应用权限切换为 Read and Write,该令牌仍然是只读的。应用设置页面不会以任何方式提示这一点。每次写入都会因 X 的
oauth1-permissions问题类型而失败,直到你返回 Keys and tokens 并重新生成 Access Token and Secret。这是 X API 最常见的设置问题,因此服务会在启动时检查这一点,并拒绝启动,同时显示:
the access token is read-only: set the app permissions to Read and Write in the X Developer Console, then regenerate the Access Token and Secret重新生成 API Key/Secret 并不能解决问题。请重新生成 Access Token and Secret。
启动验证
在注册任何工具之前,服务会调用一次 GET /2/users/me(client.Bootstrap),并设置 15 秒的截止时间。这有三个作用:
- 验证四个密钥是否有效——密钥错误会导致进程失败,而不是等到首次调用工具时才失败;
- 检查上文提到的只读令牌陷阱;
- 缓存密钥所有者的数字用户 ID,因为每个写入端点都是
POST /2/users/:id/...,每次写入都查询一次 ID 会产生额外的计费请求。
此处失败会按设计终止进程。服务启动后却每次调用都失败,不如干脆无法启动。
有一点值得明确说明,这种选择的代价是:**没有已充值的 X 开发者账户,就无法试用此服务。**没有凭据就无法启动,也就没有工具列表——/mcp 和 claude mcp list 会显示连接失败,除此之外什么都没有。若要在不具备这些条件时确认安装情况,请使用 -version 运行二进制文件,并阅读 工具 部分,了解它本会提供哪些功能。
密钥是机密信息
这四个值是具有写入权限的有效账户凭据。请将它们保存在只有你能读取的文件中,通过 -env 传入,并且绝不要提交该文件——.env 已加入 git 忽略规则,.env.example 是模板。在 MCP 客户端中运行时,也可以使用客户端的 env 块;它的优先级高于 .env 文件。
工具
共 42 个工具,分为四组。每个工具都带有 MCP 注释:读取操作带有 readOnlyHint,不可逆操作(x_post_delete、x_list_delete、取消点赞、取消转发、取消关注、移除成员)带有 destructiveHint。每个工具都会返回符合其输出架构的结构化 JSON 数据;对于不读取 structuredContent 的客户端,SDK 也会将相同的 JSON 写入文本内容块。
每个工具都接受可选的 timeout_ms,其值会限制在所属组的 [MIN, MAX] 范围内(见限制)。
帖子 — 读取
| 工具 | 描述 |
|---|---|
x_posts_search | 并行搜索多个查询的近期帖子。近期搜索最多只能追溯 7 天。 |
x_posts_count | 按分钟/小时/天统计每个查询条件的匹配数量。不会消耗帖子读取额度——可先用它估算某个主题的规模,再进行搜索。 |
x_posts_lookup | 一次调用最多按 ID 获取 100 条帖子。 |
x_posts_by_user | 并行获取多个用户名发布的近期帖子。 |
x_posts_mentions | 提及密钥所有者的帖子。 |
x_posts_home | 密钥所有者的主页时间线。 |
x_posts_quotes | 引用指定帖子的帖子。 |
x_posts_liked | 密钥所有者点过赞的帖子。 |
x_bookmarks_list | 密钥所有者的书签。 |
x_post_liked_by | 点赞指定帖子的用户。 |
x_post_reposted_by | 转发指定帖子的用户。 |
x_posts_search
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
queries | []string | — | **必填。**并行运行,最多 POSTS_MAX_QUERIES(5)个。每条查询不超过 512 个字符。 |
max_results | int | 10 | 每条查询返回的帖子数,范围为 1..100。每条帖子都会计费。 |
sort_order | string | — | recency(最新优先)或 relevancy(匹配度最高优先)。 |
days | int | 7 | 向前追溯的天数,范围为 1..7。API 无法查询更早的内容。 |
include_retweets | bool | false | 为 false 时,服务会在每条查询后附加 -is:retweet。 |
timeout_ms | int64 | 15000 | 整个调用的超时时间,限制在 [2000, 60000] 范围内。 |
查询运算符需放在查询字符串中:
| 运算符 | 含义 |
|---|---|
| 空格 | 与 |
OR | 显式或 |
-term | 非 |
( ) | 分组 |
from:user / to:user | 按作者/接收者筛选 |
@user / #tag | 提及/话题标签 |
"exact phrase" | 精确短语 |
lang:en | 语言 |
is:retweet is:reply is:quote is:verified | 帖子类型 |
has:media has:images has:videos has:links | 附件 |
url:example.com | 链接域名 |
conversation_id:123 | 单个对话串 |
x_posts_count
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
queries | []string | — | 必填。 运算符与 x_posts_search 相同。 |
granularity | string | hour | minute、hour 或 day。 |
days | int | 7 | 1..7,时间范围相同。 |
timeout_ms | int64 | 15000 | 限制在 [2000, 60000]。 |
x_posts_lookup
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
ids | []string | — | 必填。 1..100 个帖子 ID。请批量查询;不要每个 ID 调用一次。 |
x_posts_by_user
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
usernames | []string | — | 必填。 不带 @。上限为 POSTS_MAX_USERNAMES(5),并行获取。 |
max_results | int | 10 | 每位用户的帖子数,1..100。 |
exclude | []string | — | replies 和/或 retweets。 |
days | int | 7 | 1..7。 |
timeout_ms | int64 | 15000 | 限制在 [2000, 60000]。 |
时间线工具
x_posts_mentions、x_posts_home、x_posts_liked 和 x_bookmarks_list 共用同一组参数:max_results(1..100)、pagination_token、timeout_ms。x_posts_quotes、x_post_liked_by 和 x_post_reposted_by 还需要必填的 id。
分页需显式手动进行:响应中包含 next_token,传回该值作为 pagination_token 即可获取下一页。服务不会自行遍历页面——每页都会计费,因此由调用方决定是否继续翻页。
帖子 — 发布
| 工具 | 注解 | 说明 |
|---|---|---|
x_post_create | 写入 | 发布帖子。公开且计费($0.015,带链接时为 $0.20)。 |
x_post_delete | 破坏性 | 删除你的一条帖子。不可撤销。 |
x_post_like / x_post_unlike | 写入 / 破坏性 | 点赞是公开的。 |
x_post_repost / x_post_unrepost | 写入 / 破坏性 | 转发是公开的。 |
x_post_create
| 参数 | 类型 | 说明 |
|---|---|---|
text | string | ≤ 280 个字符。除非设置了 media_ids,否则必填。 |
reply_to_id | string | 回复这条帖子。 |
quote_id | string | 引用这条帖子。 |
media_ids | []string | 来自 x_media_upload 的 1..4 个 ID。 |
poll | object | options(2..4)+ duration_minutes(5..10080)。不能与 media_ids 同时使用。 |
reply_settings | string | following、mentionedUsers、subscribers 或 verified。 |
X 会拒绝连续两次发布相同文本,并将其视为重复内容——服务会将其作为单独的消息返回,而非通用失败。
用户
| 工具 | 注解 | 说明 |
|---|---|---|
x_users_lookup | 读取 | 单次调用最多查询 100 个用户名或 100 个 ID——二者选一,不能同时提供。未解析的用户名会出现在 not_found 中。 |
x_users_me | 读取 | 密钥对应的个人资料。 |
x_user_followers / x_user_following | 读取 | 每次获取一页;max_results 最高为 1000。 |
x_user_follow / x_user_unfollow | 写入 / 破坏性 | 操作公开。 |
x_user_mute / x_user_unmute | 写入 | 静音不会通知对方;对方仍然可以看到你。 |
x_users_muted / x_users_blocked | 读取 | 密钥所有者静音/屏蔽的账号。 |
有意不提供屏蔽和取消屏蔽功能,但可以读取屏蔽列表。
列表
| 工具 | 注释 | 描述 |
|---|---|---|
x_list_create | 写入 | 名称 ≤ 25 个字符,描述 ≤ 100 个字符。私有列表仅所有者可见。 |
x_list_update | 写入 | 仅发送要更改的字段。 |
x_list_delete | 破坏性操作 | 不可撤销:列表、成员关系和关注者都会被删除。 |
x_list_get | 读取 | 获取单个列表及其成员数和关注者数。 |
x_lists_owned | 读取 | 获取某用户名拥有的列表;省略用户名时,则获取密钥所有者的列表。 |
x_list_posts | 读取 | 获取列表成员的近期帖子。按帖子计费。 |
x_list_members | 读取 | 分页获取列表中的账号。 |
x_list_member_add / x_list_member_remove | 写入 / 破坏性操作 | 按用户名操作。 |
x_list_follow / x_list_unfollow | 写入 / 破坏性操作 | 仅适用于公开列表。 |
x_list_pin / x_list_unpin / x_lists_pinned | 写入 / 写入 / 读取 | 获取密钥所有者置顶的列表。 |
媒体
x_media_upload
通过分块的 INIT → APPEND → FINALIZE 流程上传图像、GIF 或视频,并返回供 x_post_create 使用的 media_id。
| 参数 | 类型 | 备注 |
|---|---|---|
path | string | 本地文件。必须位于 MEDIA_ROOT 内。 |
base64 | string | 使用文件内容而非路径;需要提供 mime。 |
mime | string | 例如 image/jpeg、video/mp4。使用 base64 时必填。 |
category | string | 必填。 tweet_image(≤ 5 MB)、tweet_gif(≤ 15 MB)、tweet_video(≤ 512 MB)。 |
timeout_ms | int64 | 默认值为 120000,限制在 [5000, 600000] 范围内——涵盖上传和 X 的转码。 |
需要了解两点:
MEDIA_ROOT是严格的边界。 它是必需配置,没有默认值;服务器会拒绝任何解析后位于其外部的路径,包括符号链接。若未设置,使用此工具的大模型就能读取主机上的任意文件并将其发布。请为此指定一个专用目录。- X 会异步转码视频。 服务器会每隔
MEDIA_POLL_INTERVAL_MS轮询 FINALIZE 状态,但编码速度较慢时,仍可能返回status: "pending"。此时media_id有效,但尚不可用于发帖——请稍后重试x_post_create。media_id会在一段时间后过期,且仅供单个帖子使用。
部分结果
扇出工具——x_posts_search、x_posts_count、x_posts_by_user——会并行处理输入,并针对每个查询或用户名返回一个条目,每个条目都有自己的 status,因此某个输入出错不会导致已成功处理的结果丢失。x_posts_lookup 对每个 id 也采用相同方式(not found 会与已解析的帖子并列返回),而 x_users_lookup 会将未解析的名称收集到 not_found 中。
只有当输入在开始处理前就被拒绝,或者其中所有项目都失败时,调用才会彻底失败。
错误
失败时会返回一个工具结果,其中 isError: true,并附带纯文本消息,而不是 JSON-RPC 错误——模型会读取消息并自行修正调用。消息是写给模型看的,而不是给日志阅读者看的,因此,明确表示不得重试的消息都会特别注明。
| 消息 | 含义 |
|---|---|
the access token is read-only: …regenerate the Access Token and Secret | 只读令牌陷阱。 |
the monthly usage cap for this project is reached; …do not retry | 已达到使用上限。重置前无法进行任何操作。 |
the project has no X API credits left; …do not retry | 积分已用尽。积分不会重置——请在 Console 中充值。 |
rate limit exceeded; the window resets in about 15 minutes | 请稍后重试;不要频繁请求。 |
X rejected the credentials; check the four keys | 密钥无效或已被撤销。 |
not authorized for this resource | 该资源受保护、已删除或属于其他人。 |
X rejected this as a duplicate of a recent post | 请更改文本。 |
user not found / post not found / list not found | 顾名思义。 |
X failed to process the uploaded media; re-encode the file | 转码失败。 |
media exceeds the size limit for its category | 5 MB / 15 MB / 512 MB。 |
the path is outside the directory this server is allowed to read | MEDIA_ROOT 违规。 |
post text exceeds 280 characters | — |
a post needs text unless it carries media | — |
a poll needs 2 to 4 options and a duration between 5 and 10080 minutes | — |
a post carries at most 4 media items | — |
too many ids in one call; the limit is 100 | — |
invalid username: 1 to 15 letters, digits or underscores | 在发出请求前进行本地验证。 |
every query failed; the X API may be unreachable | 所有项目均失败。 |
the call timed out; retry with a larger timeout_ms or fewer inputs | — |
the X API is unavailable | 5xx 或未分类的上游故障。 |
错误分类在 adapter/x/apierr 中进行,并且有意采用防御性处理。X 返回错误时使用 Content-Type: application/problem+json,底层 gotwi 客户端无法将其识别为 JSON,因此整个问题响应正文会原样进入消息字段,而不会被解码为类型化字段。因此,映射器会串联响应可能包含的所有来源,并在其中匹配问题类型(oauth1-permissions、usage-capped、credits-depleted、rate-limit-exceeded 等);若未匹配,则回退到 HTTP 状态码。
已知限制
- 仅支持近期搜索。 仅限过去 7 天。历史/归档搜索属于另一种(Enterprise)产品,尚未接入。
- 不支持屏蔽操作。
x_users_blocked可读取屏蔽列表;没有屏蔽/取消屏蔽工具。 - 没有流式端点。 尚未实现筛选流/采样流。
- 不支持私信。
- 每个进程仅支持一个账号。 密钥在整个进程范围内生效;密钥所有者只会在启动时解析一次。要服务多个账号,需要运行多个进程。
- 分页需手动处理。 工具会返回
next_token;不会自动遍历页面,因为每一页都会计费。
快速开始
安装
选择最适合你的方式——它们提供的 MCP 服务完全相同。
容器(无需 Go 工具链):
docker pull ghcr.io/role1776/mcp-x:0.1.1 # or :latest to track the newest release
在任何依赖中都应固定明确的版本。每次稳定版发布时,:latest 都会更新,而新版本可能会添加或更改工具行为;server.json 固定的版本与 MCP Registry 公示的版本相同。
预编译二进制文件——从最新版本下载适用于你平台的压缩包,解压后将 mcp-x 放入 PATH。
MCP Bundle——对于可以安装 .mcpb 文件的客户端,从最新版本下载 mcp-x_<version>_<os>_<arch>.mcpb,然后用客户端打开。该 Bundle 包含已编译的二进制文件,因此无需 Docker 或 Go;安装时,客户端会提示输入五个必填值。选择与你的操作系统和 CPU 架构都匹配的文件:一个 Bundle 只包含一个原生二进制文件。
说明
v0.1.1 之前构建的 Bundle 未声明配置字段,因此客户端不会询问凭据,服务每次都会在启动时退出。请使用 v0.1.1 或更高版本。
从源代码构建(需要 Go 1.26+):
git clone https://github.com/Role1776/mcp-x
cd mcp-x
make build # -> bin/mcp-x
也可以使用 go install,但在执行之前有一点值得注意:
go install github.com/Role1776/mcp-x/app/cmd/mcp-x@latest
Go 模块位于 app/ 中,因此发布标签(v0.1.0)并不对应模块版本——@v0.1.0 会直接失败,而 @latest 会解析为 main 上最新提交的伪版本。通过 go install 构建的版本也会报告为 dev,因为版本由发布流水线写入,而不是由模块写入。若要构建与某个版本完全一致的程序,请使用预编译二进制文件、容器,或在检出的标签上运行 make build。
配置
从克隆的仓库或解压后的发布压缩包中操作(两者都包含模板):
cp .env.example .env
$EDITOR .env # fill in the four X_* keys and MEDIA_ROOT
通过容器或 .mcpb Bundle 安装时,你没有可供复制的检出目录——请从 .env.example 获取模板,或者完全跳过此文件,直接在客户端的 env 块中传入五个值(见下文)。
必须提供五个值;其他所有值都有默认值。MEDIA_ROOT 必须是指向现有目录的绝对路径——服务会在启动时检查这一点,若不符合要求就拒绝启动,而不是等到首次上传时才失败。
运行
./bin/mcp-x -env /absolute/path/to/.env
| 标志 | 含义 |
|---|---|
-version | 打印二进制文件向 MCP 客户端报告的版本,然后退出。无需凭据即可响应,与握手不同。 |
-env | .env 文件的路径。不会隐式查找文件——在 stdio 模式下,工作目录由 MCP 客户端选择,因此相对路径默认值无法预知。如果省略此标志,或文件不存在,服务会退回使用当前环境变量,并在 stderr 中说明;五个必填值仍须在某处设置,否则启动会失败。 |
成功启动后,会记录已验证身份的账户:
INFO authenticated with the X API op=app.Run user_id=1234567890
INFO MCP server has started op=app.Run transport=stdio
连接 MCP 客户端(stdio)
Claude Code——只需一条命令,直接传入凭据(-- 用于分隔服务自身的命令与其前面的标志):
claude mcp add mcp-x \
-e X_API_KEY=... \
-e X_API_KEY_SECRET=... \
-e X_ACCESS_TOKEN=... \
-e X_ACCESS_TOKEN_SECRET=... \
-e MEDIA_ROOT=/absolute/path/to/media \
-- /absolute/path/to/mcp-x
也可以改用 .env 文件:-- /absolute/path/to/mcp-x -env /absolute/path/to/.env。运行 claude mcp list,或在会话中运行 /mcp,检查结果。
Claude Desktop 和其他接受 JSON 配置的客户端——将它们指向已构建的二进制文件:
{
"mcpServers": {
"x": {
"command": "/absolute/path/to/mcp-x",
"args": ["-env", "/absolute/path/to/.env"]
}
}
}
或者跳过该文件,直接在 env 块中传入凭据——环境中已有的变量优先于 .env 文件,因此客户端的 env 块始终生效:
{
"mcpServers": {
"x": {
"command": "/absolute/path/to/mcp-x",
"env": {
"X_API_KEY": "...",
"X_API_KEY_SECRET": "...",
"X_ACCESS_TOKEN": "...",
"X_ACCESS_TOKEN_SECRET": "...",
"MEDIA_ROOT": "/absolute/path/to/media"
}
}
}
}
连接 MCP 客户端(容器)
通过 stdio 运行镜像。Docker 需要在命令行中为每个变量指定 -e,才能将其传递给进程;只有挂载了目录,MEDIA_ROOT 才有意义:
{
"mcpServers": {
"x": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "X_API_KEY",
"-e", "X_API_KEY_SECRET",
"-e", "X_ACCESS_TOKEN",
"-e", "X_ACCESS_TOKEN_SECRET",
"-e", "MEDIA_ROOT",
"-v", "/host/media:/media:ro",
"ghcr.io/role1776/mcp-x:0.1.1"
],
"env": {
"X_API_KEY": "...",
"X_API_KEY_SECRET": "...",
"X_ACCESS_TOKEN": "...",
"X_ACCESS_TOKEN_SECRET": "...",
"MEDIA_ROOT": "/media"
}
}
}
}
必须使用 -i——否则容器不会收到 stdin,客户端会立即看到服务退出。直接从 MCP Registry 安装的客户端会自行构建此调用,并提示输入 server.json 中声明的变量——这是客户端自身的特性,并非所有能与此服务通信的客户端都具备。比如,Claude Code 的 CLI 不会从 Registry 安装,因此请使用上文的 claude mcp add。
客户端提示连接已关闭时
在 stdio 模式下,服务的 stderr 由客户端接管,而大多数客户端会将其丢弃。因此,服务明明能用一行文字清楚说明的配置问题,到你这里却只剩下:
✘ Failed to connect — -32000: Connection closed
使用相同的环境变量手动运行二进制文件,查看真正的原因:
/absolute/path/to/mcp-x -env /absolute/path/to/.env
它会准确指出问题所在——缺少哪些变量以及如何获取,密钥是否被拒绝,或者访问令牌是否为只读——然后退出。几乎所有安装失败都属于这三种情况之一。
通过 HTTP 运行
设置 MCP_TRANSPORT=http 后,服务会在 MCP_PATH 上通过 SERVER_PORT 监听(默认地址为 http://localhost:8080/mcp)。
注意
HTTP 传输方式本身没有身份验证。任何能访问此端点的人都可以使用你的账号和额度发布、删除和关注。请将其绑定到 localhost,或放在带身份验证的反向代理之后——绝不要将其暴露到公共互联网。
配置
所有配置都通过环境变量完成,每个值都会在启动前验证:缺少必需的密钥,或数值不是数字或不大于 0,都会导致启动错误,错误中会指出你实际设置的变量名(X_API_KEY,而不是 APIKey)。启动时不会检查各项限制之间的关系——请参阅限制。环境中已有的变量优先于 .env 文件。
完整变量列表及其默认值见 .env.example,可直接复制到 .env。
必需项
| 环境变量 | 说明 |
|---|---|
X_API_KEY | OAuth 1.0a 消费者密钥。 |
X_API_KEY_SECRET | OAuth 1.0a 消费者密钥密文。 |
X_ACCESS_TOKEN | 用户访问令牌——请在设置为读写权限之后生成。 |
X_ACCESS_TOKEN_SECRET | 用户访问令牌密文。 |
MEDIA_ROOT | x_media_upload 唯一可读取目录的绝对路径。该路径必须存在且为目录;启动时会检查这两项。特意不提供默认值。 |
缺少其中任何一项都会中止启动,并列出缺少的变量,同时提供 Developer Console 的链接。
MCP 服务
| 环境变量 | 默认值 | 说明 |
|---|---|---|
MCP_TRANSPORT | stdio | stdio 或 http。 |
MCP_NAME | mcp-x | 向客户端公布的服务名称。 |
MCP_PATH | /mcp | HTTP 路由(仅限 http 传输方式)。 |
向客户端公布的版本不可配置:它会在构建时根据 git 标签写入二进制文件。
HTTP 服务(仅限 http 传输方式)
| 环境变量 | 默认值 |
|---|---|
SERVER_PORT | 8080 |
SERVER_READ_TIMEOUT | 60s |
SERVER_WRITE_TIMEOUT | 60s |
HTTP 客户端
| 环境变量 | 默认值 | 说明 |
|---|---|---|
MAX_IDLE_CONNS_PER_HOST | 100 | 连接到 X API 的连接池配置。 |
X_DEBUG | false | 记录原始 gotwi 请求。输出较为详细;当 X 错误令人费解时很有用。 |
日志
| 环境变量 | 默认值 | 说明 |
|---|---|---|
LOG_MODE | local | local → debug 级别的文本处理器;prod → info 级别的 JSON 处理器。日志输出到 stderr(必须如此:stdout 用于传输 MCP 协议)。 |
限制
每组限制互不影响,因此媒体上传缓慢不会受到搜索超时的限制。
帖子
| 环境变量 | 默认值 | 说明 |
|---|---|---|
POSTS_MAX_QUERIES | 5 | 每次 x_posts_search / x_posts_count 调用的查询数。 |
POSTS_MAX_USERNAMES | 5 | 每次 x_posts_by_user 调用的用户名数。 |
POSTS_MAX_IDS | 100 | 每次 x_posts_lookup 调用的 ID 数;受 API 自身上限约束。 |
POSTS_DEFAULT_RESULTS | 10 | |
POSTS_MAX_RESULTS | 100 | |
POSTS_DEFAULT_DAYS | 7 | |
POSTS_MAX_DAYS | 7 | 最近搜索无法追溯更早的时间。 |
POSTS_DEFAULT_TIMEOUT_MS | 15000 | |
POSTS_MIN_TIMEOUT_MS | 2000 | |
POSTS_MAX_TIMEOUT_MS | 60000 |
用户
| 环境变量 | 默认值 |
|---|---|
USERS_MAX_USERNAMES | 100 |
USERS_MAX_IDS | 100 |
USERS_DEFAULT_RESULTS | 100 |
USERS_MAX_RESULTS | 1000 |
USERS_DEFAULT_TIMEOUT_MS | 15000 |
USERS_MIN_TIMEOUT_MS | 2000 |
USERS_MAX_TIMEOUT_MS | 60000 |
列表
| 环境变量 | 默认值 |
|---|---|
LISTS_DEFAULT_RESULTS | 25 |
LISTS_MAX_RESULTS | 100 |
LISTS_DEFAULT_TIMEOUT_MS | 15000 |
LISTS_MIN_TIMEOUT_MS | 2000 |
LISTS_MAX_TIMEOUT_MS | 60000 |
媒体
| 环境变量 | 默认值 | 说明 |
|---|---|---|
MEDIA_CHUNK_BYTES | 4194304 | 4 MB。APPEND 端点对每个分段的上限为 5 MB。 |
MEDIA_POLL_INTERVAL_MS | 2000 | X 转码期间轮询 FINALIZE 状态的频率。 |
MEDIA_DEFAULT_TIMEOUT_MS | 120000 | |
MEDIA_MIN_TIMEOUT_MS | 5000 | |
MEDIA_MAX_TIMEOUT_MS | 600000 | 10 分钟,适用于大型视频。 |
每个值都会单独检查——必须大于零;对于 API 本身设有上限的值(*_MAX_IDS、POSTS_MAX_RESULTS、LISTS_MAX_RESULTS、POSTS_MAX_DAYS),还会额外限制在 API 的上限以内,以免输入错误导致 X 拒绝请求。启动时不会交叉检查 DEFAULT_*、MIN_* 和 MAX_* 三元组是否彼此一致。配置不一致不会阻止服务启动,而是在每次请求时进行协调:
- 调用方未提供的值,或提供了零或负值时,会回退到对应的
DEFAULT_*; - 随后会将结果限制在
[MIN_*, MAX_*]范围内,因此,如果DEFAULT_*大于MAX_*,结果就会是MAX_*; - 如果
MIN_*大于MAX_*,则以最大值为准。
因此,有效限制始终不会超过配置的最大值;即使配置错误,服务也会降级为正常运行,而不是启动失败。代价是这种降级不会发出提示:将 POSTS_MAX_RESULTS 设为 1 而不是 10 不会产生警告,只会悄悄返回更少的结果。如果结果看起来不完整,值得再次检查这些值。
架构
项目采用清晰的分层结构。依赖指向领域层,各层通过接口与下一层交互。
app/ the Go module: sources plus its build files
(Dockerfile, .dockerignore, .goreleaser.yaml)
cmd/mcp-x/main.go entry point: parse flags, load config, run app
internal/
app/ wiring + lifecycle (X client, bootstrap, run, graceful shutdown)
config/ config loading (.env → env vars → validate → env-name error messages)
domain/x/ core types and errors
posts/ users/ lists/ media/ value objects: PostID, UserID, Username, Draft, Query, Upload…
format.go shared snowflake-id and username validation
errors.go the sentinel error set the whole app maps onto
dto/x/ request/response shapes for the MCP tools, with jsonschema + validate tags
transport/mcp/ MCP layer
router/ registers every tool group on the MCP server
x/posts|users|lists|media/ tool definitions, handlers and error → tool-result mapping
usecase/x/ business logic: validation, parallelism, timeouts, limit resolution
adapter/x/ X API wiring
client/ gotwi client + startup Bootstrap (key check, key-owner id)
apierr/ X problem responses → domain errors
posts|users|lists|media/ endpoint calls and mappers to DTOs
pkg/ reusable building blocks (mcpserver, server, logger, validator, parallel)
工具调用的请求流程:
MCP client → transport/mcp/x/... (handler) → usecase/x/... → adapter/x/... → gotwi → X API
↑ maps errors ↑ validates, resolves limits,
to isError text fans out, applies timeouts
有两个边界值得特别说明:
- 领域层拒绝构造无效值。
NewPostID、NewUserID、NewUsername、NewDraft等函数会在构造时进行验证,因此格式错误的 ID 或长度为 300 个字符的帖子会在本地被拒绝,不会因此消耗一次请求。ID 会根据雪花 ID 格式进行检查,用户名则根据 X 的 1–15 位[A-Za-z0-9_]规则进行检查,两者都通过同一个共享的format包处理。 - 每一层使用相同的错误词汇。 适配器会将 X 的问题响应转换为
domain/x/errors.go中定义的哨兵错误;传输层是唯一会将哨兵错误转换为人类可读文本的地方。因此,无论 42 个工具中的哪一个产生错误,同一种失败都会以相同方式呈现。
开发
所有 Go 代码都位于 app/ 中,因此可以在仓库根目录使用 makefile,也可以向工具链传入 -C app:
make build # compile the binary -> bin/mcp-x
make test # go test -v ./...
make cover # total coverage percentage
make cover-html # coverage report in the browser
make version # the version that would be stamped into the binary
go -C app build ./...
go -C app test ./...
go -C app vet ./...
Mock 使用 mockgen 根据各个 usecase 接口旁的 //go:generate 指令生成:
go -C app generate ./...
新代码应附带测试。 有关完整的拉取请求指南,请参阅 CONTRIBUTING.md。
许可证
根据 MIT License 发布。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。