360 Secure SkillHub
360 安全技能中心

remind-mcp

核心功能为通过fpx浏览器桥接一次性抓取已登录Remind账号的认证头,后续无需启动remind-mcp服务,直接用curl调用Remind的GraphQL接口,可查询课程、聊天、消息、通知设置等数据。适合shell脚本集成、一次性Remind数据拉取场景,输入为GraphQL查询及可选变量,输出JSON结果,附带预置查询示例与jq处理食谱,标注了会话过期、权限限制、写操作风险等注意事项,偏向只读使用。

chrischall
chrischall
热度 61.83
最近更新 2026-10-10 07:30:47
分类:开发依赖:代码执行 / 联网浏览搜索 / 第三方 API / 读写文件应用场景:API 集成 / 自动化脚本 / 数据处理

安装技能

复制下面的话术发给你的 Agent,它会通过 secure-skills 检索并安装该技能。

执行命令安装此 skill: npx secure-skills-cli add remind-mcp
Metadata
name
remind-fpx
description
Read Remind (remind.com) — classes, chats, messages, notification settings — from a shell. Captures the signed-in session once through the fpx browser bridge, then queries Remind's GraphQL API with plain curl. Use for Remind data without running the remind-mcp server, in a script, or one-shot.

Remind from the shell

Remind's web app talks to a single GraphQL endpoint, POST https://www.remind.com/graphql. That endpoint is reachable server-side — no bot wall — so only the credential needs the browser. Capture it once with fpx, then use curl for everything.

Introspection is enabled, so the schema is self-documenting: you can discover any field without guessing (and you should — see the validation-error gotcha below).

Auth model

Two headers, both lifted from a signed-in tab:

Header What it is
cookie The full Cookie request header (includes HttpOnly session cookies)
x-csrf-token Value of the JS-readable csrf_token cookie

Both are required. Only x-csrf-token works — x-xsrf-token and csrf-token are rejected. A named cookie read (fpx cookies csrf_token) returns {} on this site, so header capture is the only route.

One-time setup

Install @fetchproxy/cli (npm i -g @fetchproxy/cli) and the ContextMint Bridge browser extension from https://github.com/nullnet-app/contextmint-bridge/releases (Chrome: load the Chrome zip unpacked; Safari isn't available yet, so use Chrome for now). It's the fetchproxy extension under its new name, same maintainer; source is public there — build it or verify the zip with shasum -a 256 -c contextmint-bridge-chrome-<version>.zip.sha256.

Declare the full scope before the first pairing — widening it later forces a re-pair:

fpx profile add remind --domain remind.com
fpx profile declare remind \
  --capture-header cookie@www.remind.com \
  --capture-header x-csrf-token@www.remind.com

Then capture. The capture waits for the next request the page makes, so load a Remind page while it waits:

fpx session -p remind > ~/.remind-fpx-session.json &   # approve the pair code in ContextMint Bridge
open "https://www.remind.com/"                          # feeds the capture
wait
chmod 600 ~/.remind-fpx-session.json

Use a session file of your own like this one. Do not write into ~/.remind-mcp/ — that is the MCP server's store, with a different schema; overwriting it corrupts its session.

The call pattern

rq() {  # rq '<query>' ['<variables json>']
  local S=~/.remind-fpx-session.json
  local body; body=$(Q="$1" V="${2:-{\}}" node -e '
    process.stdout.write(JSON.stringify({query:process.env.Q,variables:JSON.parse(process.env.V)}))')
  curl -s -X POST https://www.remind.com/graphql \
    -H 'content-type: application/json' \
    -H "cookie: $(node -e 'console.log(require(process.argv[1]).capturedHeaders.cookie)' "$S")" \
    -H "x-csrf-token: $(node -e 'console.log(require(process.argv[1]).capturedHeaders["x-csrf-token"])' "$S")" \
    --data-binary "$body"
}

rq '{ me { uuid first_name last_name email locale } }' | jq .data.me

Start from navigationList — it returns the uuids every other query needs. Ready-to-run documents with jq recipes are in references/graphql-queries.md.

Gotchas

  • An expired session returns HTTP 200, with errors[0].message == "Unauthorized". Check the body, never the status. Re-run the capture above to refresh.
  • "Unauthorized" also means "this account may not do that" — scheduled messages and org-admin queries are owner/teacher-only, and a subscriber (parent) account gets Unauthorized on them with a perfectly valid session.
  • An unknown field reports as Internal service error / GRAPHQL_VALIDATION_FAILED with HTTP 500 metadata, not a readable "no such field". Do not guess field names — introspect:
    rq '{ __type(name:"Class"){ fields { name } } }' | jq -r '.data.__type.fields[].name'
    
  • Check permissions.canSend before trying to send; most fields on a subscriber account are read-only.
  • Writes deliver to real people and cannot be unsent. This skill is read-oriented on purpose; the mutation shapes live in the references file, clearly marked.