夜雨聆风学习资料网

ARTICLE · 1325092

一条 URL,把「附近」接进 AI 助手:我做 PowPow MCP 的 34 个工具和几个真实教训

一条 URL,把「附近」接进 AI 助手:我做 PowPow MCP 的 34 个工具和几个真实教训

在 ChatGPT 里问一句「故宫周围有什么有趣的嘛?」,返回的不是一段泛泛的景点介绍,而是一张带地图的卡片:7 个公开数字人、20 条带位置的帖子,最近的一个距定位点 205 米。

而且工具会主动提醒一句:它把「故宫」理解成了「故宫博物院」,如果跟你说的不是同一个地方,请先确认。

下面这段是线上服务的真实返回,不是我编的示例:

PowPow 附近发现:故宫 半径 1.0 公里注意:按地名「故宫」定位到「故宫博物院」。若与用户想问的地点不符,请先与用户确认。共 7 个公开数字人、0 位用户、20 条公开帖子。数字人(7):- 紫微北极大帝 · 位于 北京故宫,取”紫微垣”象 · 距你 205 米  众星拱之的北极星主,掌方向与位置。居紫微垣以统御众星、掌天经地纬与四时运行…- 故宫讲解员 · 位于 39.919628, 116.39891… · 距你 256 米  一个精通历史的故宫讲解员- 老舍 · 位于 老舍故居 · 距你 440 米  我是老舍(1899-1966)。我现在在我故居,丰富胡同19号是我在北京的住所…

这篇文章不讲「MCP 是什么」——这类科普已经很多了。我想聊的是:把一个基于位置的社交 App 做成远程 MCP 服务时,我实际做了哪些取舍,以及哪些坑是文档里不会写、但真会把人卡住的。

它其实只有一条 URL

PowPow(泡泡)是一款面向中国大陆的位置社交应用:用户在地图上发带位置的帖子、创建能对话的数字人(AI 角色)、参与线下打卡活动。我把它包成了一个远程 MCP 服务端,源码已开源,也上了官方 MCP Registry。

项
值
端点
https://global.powpow.online/api/mcp
传输
Streamable HTTP
工具数
34 个(20 个只读 + 14 个写)
鉴权
OAuth 2.1 + PKCE,支持动态客户端注册
源码
https://github.com/durenzidu/powpow-mcp

任何支持 MCP 的客户端,填这一条地址就行:

https://global.powpow.online/api/mcp

不需要申请 API key,不需要手工配 client_id,不需要注册开发者账号。

为什么「填一条 URL」就够了

因为复杂度被我全放到了服务端:元数据发现、动态客户端注册(DCR)、PKCE,都由客户端自动完成。链路大致是这样:

  1. 客户端匿名请求端点,拿到 401,响应头 WWW-Authenticate 里带着 resource_metadata 的地址;
  2. 顺着资源元数据找到 authorization_servers,再读到授权服务器元数据;
  3. 客户端自己完成动态注册,拉起浏览器,打开 PowPow 自己托管的同意页;
  4. 你登录并确认,客户端用授权码 + PKCE 换到 access token,然后才开始 initialize。

支持的 scope 只有 openid、profile、email 三个。令牌每请求校验,吊销 session 即刻失效。

这里有个特别容易被误判成「配置错了」的现象:这个服务不开放匿名访问,连initialize都不例外,所以第一次连的时候看到 401 是预期行为。它不是报错,是 OAuth 2.1 流程的入口。

我的判断是:远程 MCP 想要真正「零配置」,就得把 401 当成握手信号,而不是错误去绕开。不少接入问题,根子都在客户端把这一步拦下来自己处理了。

四个客户端怎么填,照抄即可

Claude 网页版 / 桌面版走界面:设置 → 连接器 → 添加自定义连接器 → 名称填 PowPow,URL 填上面那条 → 点「连接」,浏览器会跳到 PowPow 授权页。

Claude Code 一行命令:

claude mcp add --transport http powpow https://global.powpow.online/api/mcp

Cursor 写进项目级 .cursor/mcp.json 或全局 ~/.cursor/mcp.json:

{  ”mcpServers”: {    ”powpow”: {      ”url”: ”https://global.powpow.online/api/mcp”    }  }}

VS Code(Copilot Chat)写进 .vscode/mcp.json:

{  ”servers”: {    ”powpow”: {      ”type”: ”http”,      ”url”: ”https://global.powpow.online/api/mcp”    }  }}

注意这两份 JSON 的字段名不一样:Cursor 用 mcpServers,VS Code 用 servers 并且要多一个 "type": "http"。直接互相复制是最常见的低级错误。

Claude Code 还可以装现成插件,两步到位(额外带一个引导注册账号的 skill):

claude plugin marketplace add durenzidu/powpow-claude-pluginclaude plugin install powpow@powpow

ChatGPT 则是纯图形操作:加号 → 添加自定义 MCP 服务 → 填名称和描述 → 连接选公开端点、URL 填上面那条 → 认证选 OAuth → Create。新开对话用 @ 选中它就能调。

34 个工具,我按「读 / 写」切了一刀

20 个只读、14 个写。按用途分三组:

附近发现(6 个):find_nearby(某地点附近的数字人、用户、帖子)、geocode_place(地名转坐标)、nearby_attractions(景点与商圈)、list_campaigns(线下打卡活动)、search_posts、search_users。

账号与社交(只读):whoami、get_my_account(会员等级与到期)、my_posts、my_digital_humans、my_following / my_followers、list_friends、list_favorites、get_badge_history、get_user_profile、get_post_detail、get_post_replies / get_my_post_replies、get_digital_human。

写操作(14 个):prepare_post、publish_post、delete_post、like_post、unlike_post、reply_to_post、follow_user、unfollow_user、add_favorite、update_favorite、remove_favorite、update_profile、create_digital_human、delete_digital_human。

写工具占了四成还多,这个比例决定了下面那套安全设计不是「锦上添花」,而是必须做的东西。

三个我认为不能省的安全设计

第一,发帖永远是两步。AI 先调 prepare_post 出草稿,你确认之后才调 publish_post 真正发布。关键点是:这个「确认」由服务端强制,不是靠提示词里写一句「请先问我」。提示词约束是靠不住的,模型完全可能忘;只有把发布动作拆成两个工具、把草稿状态放服务端,才拦得住。

第二,给了后悔的余地。通过 AI 创建的帖子或数字人,24 小时内可以由 AI 撤销;更早的内容请在 App 内管理。另外 PowPow「设置 → AI 连接」里可以整个关掉 AI 写权限。工具注解也分开:只读工具标 readOnlyHint: true,删除类写操作标 destructiveHint: true,让客户端只对真正破坏性的操作打断用户。

第三,只限流该限流的。find_nearby / geocode_place / nearby_attractions 限12 次 / 60 秒 / 访问令牌,因为它们会消耗第三方地理编码配额——不限流的话,一个客户端就能把配额刷光。剩下 31 个工具不限流。这个数是实测过的:第 13 次调用会直接返回「请求太频繁」。

一把梭给所有工具套同一个限流当然省事,但会让绝大多数只读查询平白变卡。这种地方我倾向于麻烦一点。

几个不看 schema 一定会踩的坑

参数名不能猜。find_nearby 的半径叫 radius_meters,不是 radius。传 radius 不会报错,会被静默丢掉,然后按默认值 1000 米返回结果——你以为自己查了 500 米,其实查的是 1 公里。我实测过:传 {"radius": 500} 得到 7 个数字人,换成 {"radius_meters": 500} 才是 3 个。相比之下 types 反而是数组,传字符串会明确报输入校验错误,这个至少还会告诉你。

别自己传 GPS 坐标。地理编码走高德,坐标是GCJ-02,不是 WGS-84,服务端不做转换。外部传进来的 WGS-84 坐标会有约300–600 米偏差。想查「某地附近」,用 geocode_place 让服务端解析地名,比自己塞经纬度靠谱得多。

返回值是纯中文。站点本身有 8 种语言(zh-CN / en / ja / ko / fr / ms / ru / th),但 MCP 层不读 locale cookie,原始 payload 永远是中文。模型会帮你翻译,但如果你在做二次解析,别指望能拿到英文。

响应是 SSE。即使你发的是普通 JSON-RPC,响应也以 text/event-stream 返回,要按行读 data:  后面的内容。只认 application/json 的客户端会解析失败,这是我见过最多的接入报错。

想自己部署:源码里有什么

服务跑在Supabase Edge Functions(Deno)上,公开仓库的结构是这样:

powpow-mcp/├── server.json# MCP Registry 元数据(发布到 Registry 的就是这份文件)├── src/│   ├── index.ts# 工具注册与实现│   ├── auth.ts# OAuth 2.1 资源服务器侧│   └── widget-html.ts# 可交互地图 widget(HTML)└── LICENSE

环境变量按需配置:SUPABASE_URL、SUPABASE_ANON_KEY、SUPABASE_SERVICE_ROLE_KEY、MCP_OAUTH 是必需的;AMAP_WEB_SERVICE_KEY 只在需要地理编码时给;MCP_HIDE_WIDGET 和 MCP_ENABLE_PROBE(诊断工具,默认关闭)是可选开关。

部署命令:

supabase functions deploy mcp-server --project-ref  --no-verify-jwt

仓库不包含任何凭据,部署时所有密钥都从平台环境变量读。网络目的地也只有两个:global.powpow.online(MCP 端点、OAuth 同意页、账号注册)和 xhahsqbaohraqrdhtvay.supabase.co(Supabase Auth,登录与令牌签发)。没有埋点、没有第三方分析、不读取本地文件。

仓库 README 里还放了两张手机截图和三段录屏(完整链路 45 秒、OAuth 授权 18 秒、读取公开帖子 20 秒),这里就不贴图了。

边界,我如实说

  • 覆盖范围集中在中国大陆,坐标体系也是大陆这套;
  • 帖子仅支持纯文字,不能传照片;
  • 头像、私信、通知、订阅、支付都不在 MCP 能力范围内;
  • 不开放匿名访问,必须有一次 OAuth 授权。

服务已在官方 MCP Registry 上架:

io.github.durenzidu/powpow-mcp

用到的链接:

  • 源码(MIT):
  • 34 个工具的逐项说明:
  • 官方网站:
  • 隐私政策 / 服务条款 / 支持: ·  ·

如果你也在做 MCP 服务,上面那几个坑(SSE 响应、静默失效的参数名、GCJ-02 坐标)大概能替你省掉半天。也欢迎在评论区说说你想让 AI 帮你做哪类「附近」的事——如果某个场景现有 34 个工具做不到,我更想听。

回首页,搜索你想要的文档

在首页搜索框输入关键词,快速定位资料

文库首页搜索指引 立即搜索

相关学习资料