mcpField
X
X (Twitter)

X (Twitter)

本地服务
github.com社交媒体27 周前更新

关于 X (Twitter)

mcp-x 是面向需要通过 X API v2 读取、搜索和管理 X 账户的客户端的 Go MCP 服务。它提供 42 种工具,并以 OAuth 1.0a 用户上下文执行操作,身份对应所用凭据所属的账户。

主要功能

  1. 01搜索并统计近期帖子
  2. 02读取时间线、提及和书签
  3. 03管理用户和列表
  4. 04发布、点赞和转发帖子
  5. 05上传并附加媒体

适用场景

  1. 01按多个查询搜索近期帖子
  2. 02查看账户时间线和书签
  3. 03以账户身份发布帖子或互动

MCP 相关

向兼容 MCP 的客户端提供 42 种 X API v2 工具,支持 stdio 和流式 HTTP 传输。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

暂未收录安装方式

我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。

代码仓库
安装包
ocighcr.io/role1776/mcp-x:0.1.1
mcpbmcp-x_0.1.1_darwin_amd64.mcpb0.1.1
mcpbmcp-x_0.1.1_darwin_arm64.mcpb0.1.1
mcpbmcp-x_0.1.1_linux_amd64.mcpb0.1.1
mcpbmcp-x_0.1.1_linux_arm64.mcpb0.1.1
mcpbmcp-x_0.1.1_windows_amd64.mcpb0.1.1
mcpbmcp-x_0.1.1_windows_arm64.mcpb0.1.1

工具

还没有获取到工具列表

本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。

详细介绍

mcp-x

一个 MCP 服务,为大模型提供 X(Twitter)API:读取和搜索帖子、管理用户和列表、上传媒体并发布内容——使用其密钥所对应的账号操作。

MIT 许可证 Go MCP 传输方式 X API v2 OAuth 1.0a 付费 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 模式。

获取凭据——以及人人都会弄错的那一步
  1. 前往 developer.x.com → 你的项目 → 你的应用。
  2. 打开 User authentication settings,将 App permissions 设为 Read and Write。先完成这一步。
  3. 然后前往 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_resultsint10每条查询返回的帖子数,范围为 1..100。每条帖子都会计费。
sort_orderstring—recency(最新优先)或 relevancy(匹配度最高优先)。
daysint7向前追溯的天数,范围为 1..7。API 无法查询更早的内容。
include_retweetsboolfalse为 false 时,服务会在每条查询后附加 -is:retweet。
timeout_msint6415000整个调用的超时时间,限制在 [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 相同。
granularitystringhourminute、hour 或 day。
daysint71..7,时间范围相同。
timeout_msint6415000限制在 [2000, 60000]。
x_posts_lookup
参数类型默认值说明
ids[]string—必填。 1..100 个帖子 ID。请批量查询;不要每个 ID 调用一次。
x_posts_by_user
参数类型默认值说明
usernames[]string—必填。 不带 @。上限为 POSTS_MAX_USERNAMES(5),并行获取。
max_resultsint10每位用户的帖子数,1..100。
exclude[]string—replies 和/或 retweets。
daysint71..7。
timeout_msint6415000限制在 [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
参数类型说明
textstring≤ 280 个字符。除非设置了 media_ids,否则必填。
reply_to_idstring回复这条帖子。
quote_idstring引用这条帖子。
media_ids[]string来自 x_media_upload 的 1..4 个 ID。
pollobjectoptions(2..4)+ duration_minutes(5..10080)。不能与 media_ids 同时使用。
reply_settingsstringfollowing、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。

参数类型备注
pathstring本地文件。必须位于 MEDIA_ROOT 内。
base64string使用文件内容而非路径;需要提供 mime。
mimestring例如 image/jpeg、video/mp4。使用 base64 时必填。
categorystring必填。 tweet_image(≤ 5 MB)、tweet_gif(≤ 15 MB)、tweet_video(≤ 512 MB)。
timeout_msint64默认值为 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 category5 MB / 15 MB / 512 MB。
the path is outside the directory this server is allowed to readMEDIA_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 unavailable5xx 或未分类的上游故障。

错误分类在 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_KEYOAuth 1.0a 消费者密钥。
X_API_KEY_SECRETOAuth 1.0a 消费者密钥密文。
X_ACCESS_TOKEN用户访问令牌——请在设置为读写权限之后生成。
X_ACCESS_TOKEN_SECRET用户访问令牌密文。
MEDIA_ROOTx_media_upload 唯一可读取目录的绝对路径。该路径必须存在且为目录;启动时会检查这两项。特意不提供默认值。

缺少其中任何一项都会中止启动,并列出缺少的变量,同时提供 Developer Console 的链接。

MCP 服务
环境变量默认值说明
MCP_TRANSPORTstdiostdio 或 http。
MCP_NAMEmcp-x向客户端公布的服务名称。
MCP_PATH/mcpHTTP 路由(仅限 http 传输方式)。

向客户端公布的版本不可配置:它会在构建时根据 git 标签写入二进制文件。

HTTP 服务(仅限 http 传输方式)
环境变量默认值
SERVER_PORT8080
SERVER_READ_TIMEOUT60s
SERVER_WRITE_TIMEOUT60s
HTTP 客户端
环境变量默认值说明
MAX_IDLE_CONNS_PER_HOST100连接到 X API 的连接池配置。
X_DEBUGfalse记录原始 gotwi 请求。输出较为详细;当 X 错误令人费解时很有用。
日志
环境变量默认值说明
LOG_MODElocallocal → debug 级别的文本处理器;prod → info 级别的 JSON 处理器。日志输出到 stderr(必须如此:stdout 用于传输 MCP 协议)。
限制

每组限制互不影响,因此媒体上传缓慢不会受到搜索超时的限制。

帖子

环境变量默认值说明
POSTS_MAX_QUERIES5每次 x_posts_search / x_posts_count 调用的查询数。
POSTS_MAX_USERNAMES5每次 x_posts_by_user 调用的用户名数。
POSTS_MAX_IDS100每次 x_posts_lookup 调用的 ID 数;受 API 自身上限约束。
POSTS_DEFAULT_RESULTS10
POSTS_MAX_RESULTS100
POSTS_DEFAULT_DAYS7
POSTS_MAX_DAYS7最近搜索无法追溯更早的时间。
POSTS_DEFAULT_TIMEOUT_MS15000
POSTS_MIN_TIMEOUT_MS2000
POSTS_MAX_TIMEOUT_MS60000

用户

环境变量默认值
USERS_MAX_USERNAMES100
USERS_MAX_IDS100
USERS_DEFAULT_RESULTS100
USERS_MAX_RESULTS1000
USERS_DEFAULT_TIMEOUT_MS15000
USERS_MIN_TIMEOUT_MS2000
USERS_MAX_TIMEOUT_MS60000

列表

环境变量默认值
LISTS_DEFAULT_RESULTS25
LISTS_MAX_RESULTS100
LISTS_DEFAULT_TIMEOUT_MS15000
LISTS_MIN_TIMEOUT_MS2000
LISTS_MAX_TIMEOUT_MS60000

媒体

环境变量默认值说明
MEDIA_CHUNK_BYTES41943044 MB。APPEND 端点对每个分段的上限为 5 MB。
MEDIA_POLL_INTERVAL_MS2000X 转码期间轮询 FINALIZE 状态的频率。
MEDIA_DEFAULT_TIMEOUT_MS120000
MEDIA_MIN_TIMEOUT_MS5000
MEDIA_MAX_TIMEOUT_MS60000010 分钟,适用于大型视频。

每个值都会单独检查——必须大于零;对于 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 发布。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。