{"id":"2aab9143-0781-4189-bcbd-450c07e82f22","entityType":"agent","slug":"clawhub-chrischall-remind-mcp","name":"remind-mcp","canonicalUrl":"https://www.xpersona.co/agent/clawhub-chrischall-remind-mcp","canonicalPath":"/agent/clawhub-chrischall-remind-mcp","generatedAt":"2026-10-11T01:47:25.706Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"editorial-content","verified":true,"confidence":"high","updatedAt":"2026-10-10T23:11:49.620Z","emptyReason":null},"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. Skill: remind-mcp Owner: chrischall Summary: 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. Tags: latest:1.2.6 Version history: v1.2.6 | 2026-10-09T23:28:57.572Z | auto - R","descriptionLabel":"Technical summary","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 1.2K downloads reported by the source. Last updated 10/10/2026.","installCommand":"clawhub skill install s17cjx1a349nz5apaqp02vgz4h85728z:remind-mcp","sourceUrl":"https://clawhub.ai/chrischall/remind-mcp","homepage":"https://clawhub.ai/chrischall/skills/remind-mcp","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/chrischall/remind-mcp","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/chrischall/skills/remind-mcp","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":62,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"Read Remind (remind.com) — classes, chats, messages, notification settings — from a shell. Captures the signed-in session once through the fpx browser bridge, t"},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-10T23:11:49.620Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T23:11:49.620Z","emptyReason":null},"stars":null,"forks":null,"downloads":1233,"packageName":null,"latestVersion":"1.2.6","tractionLabel":"1.2K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T23:11:49.555Z","emptyReason":null},"lastUpdatedAt":"2026-10-10T23:11:49.620Z","lastCrawledAt":"2026-10-10T23:11:49.555Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-11T23:11:49.555Z","lastVerifiedAt":null,"highlights":[{"version":"1.2.6","createdAt":"2026-10-09T23:28:57.572Z","changelog":"- Removed the skill card file (skill-card.md). - No changes to functionality or documentation; internal or metadata-only update.","fileCount":4,"zipByteSize":6177},{"version":"1.2.5","createdAt":"2026-10-07T13:40:45.495Z","changelog":"- Removed the file skill-card.md. - No other changes to skill code or documentation.","fileCount":4,"zipByteSize":6221},{"version":"1.2.4","createdAt":"2026-10-05T02:51:47.382Z","changelog":"- Removed the file skill-card.md. - No changes to code or documentation content. - Housekeeping: this update only deletes an unused or unnecessary file.","fileCount":4,"zipByteSize":6241},{"version":"1.2.3","createdAt":"2026-10-03T01:42:18.480Z","changelog":"- Removed the sample file skill-card.md. - No user-facing features, logic, or documentation were changed.","fileCount":4,"zipByteSize":6212},{"version":"1.2.2","createdAt":"2026-09-28T14:02:12.528Z","changelog":"- Updated setup instructions to use the ContextMint Bridge browser extension (renamed from fetchproxy extension), with details and download link. - Clarified extension installation for Chrome and removed mention of Safari support. - Updated pairing instructions to reference ContextMint Bridge instead of the previous extension. - Removed redundant or outdated details about extensions and renamed terms for consistency. - Removed the obsolete skill-card.md file.","fileCount":4,"zipByteSize":6182},{"version":"1.2.1","createdAt":"2026-09-25T15:56:49.163Z","changelog":"- skill-card.md file removed. - No user-facing or functional changes; documentation and usage unchanged.","fileCount":4,"zipByteSize":6003},{"version":"1.2.0","createdAt":"2026-09-24T15:11:47.032Z","changelog":"- Removed the file skill-card.md. - No other user-facing changes.","fileCount":4,"zipByteSize":6148},{"version":"1.1.3","createdAt":"2026-09-23T21:42:39.893Z","changelog":"- skill-card.md file has been removed. - No functional or user-facing changes; documentation and usage remain the same.","fileCount":4,"zipByteSize":6190}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s17cjx1a349nz5apaqp02vgz4h85728z:remind-mcp","setupComplexity":"low","setupSteps":["Setup complexity is classified as HIGH. You must provision dedicated cloud infrastructure or an isolated VM. Do not run this directly on your local workstation.","Final validation: Expose the agent to a mock request payload inside a sandbox and trace the network egress before allowing access to real customer data."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-remind-mcp/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-remind-mcp/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-remind-mcp/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-remind-mcp/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-remind-mcp/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-remind-mcp/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-11T01:47:25.703Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-remind-mcp/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-remind-mcp/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-remind-mcp/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-remind-mcp/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"high","updatedAt":"2026-10-10T23:11:49.620Z","emptyReason":null},"readme":"Skill: remind-mcp\n\nOwner: chrischall\n\nSummary: 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.\n\nTags: latest:1.2.6\n\nVersion history:\n\nv1.2.6 | 2026-10-09T23:28:57.572Z | auto\n\n- Removed the skill card file (skill-card.md).\n- No changes to functionality or documentation; internal or metadata-only update.\n\nv1.2.5 | 2026-10-07T13:40:45.495Z | auto\n\n- Removed the file skill-card.md.\n- No other changes to skill code or documentation.\n\nv1.2.4 | 2026-10-05T02:51:47.382Z | auto\n\n- Removed the file skill-card.md.\n- No changes to code or documentation content.\n- Housekeeping: this update only deletes an unused or unnecessary file.\n\nv1.2.3 | 2026-10-03T01:42:18.480Z | auto\n\n- Removed the sample file skill-card.md.\n- No user-facing features, logic, or documentation were changed.\n\nv1.2.2 | 2026-09-28T14:02:12.528Z | auto\n\n- Updated setup instructions to use the ContextMint Bridge browser extension (renamed from fetchproxy extension), with details and download link.\n- Clarified extension installation for Chrome and removed mention of Safari support.\n- Updated pairing instructions to reference ContextMint Bridge instead of the previous extension.\n- Removed redundant or outdated details about extensions and renamed terms for consistency.\n- Removed the obsolete skill-card.md file.\n\nv1.2.1 | 2026-09-25T15:56:49.163Z | auto\n\n- skill-card.md file removed.\n- No user-facing or functional changes; documentation and usage unchanged.\n\nv1.2.0 | 2026-09-24T15:11:47.032Z | auto\n\n- Removed the file skill-card.md.\n- No other user-facing changes.\n\nv1.1.3 | 2026-09-23T21:42:39.893Z | auto\n\n- skill-card.md file has been removed.\n- No functional or user-facing changes; documentation and usage remain the same.\n\nv1.1.2 | 2026-09-23T15:44:57.723Z | auto\n\n- Removed the file skill-card.md.\n- No changes to core functionality or user-facing documentation.\n\nv1.1.1 | 2026-09-21T04:12:57.671Z | auto\n\n- Removed the file skill-card.md.\n- No changes were made to code or documentation other than removing this file.\n\nv1.1.0 | 2026-09-20T02:50:26.657Z | auto\n\n- Removed the file skill-card.md.\n- No other functionality or documentation changes in this release.\n\nv1.0.0 | 2026-09-19T11:18:54.089Z | auto\n\n- Removed the sample file skill-card.md.\n- No feature or functionality changes; documentation and usage remain the same.\n\nv0.4.5 | 2026-09-15T19:25:49.775Z | auto\n\n- Removed the file: skill-card.md\n- No user-facing or functionality changes—documentation/file cleanup only\n\nv0.4.4 | 2026-09-14T14:11:03.563Z | auto\n\n- Removed the file skill-card.md.\n- No functional or user-facing changes.\n\nv0.4.3 | 2026-09-11T02:46:15.584Z | auto\n\n- Removed the skill-card.md file.\n- No changes to functionality or documentation in SKILL.md.\n\nv0.4.2 | 2026-09-10T17:54:23.804Z | auto\n\n- Removed the file: skill-card.md\n- No changes to user-facing features or documentation.\n- Version bump to 0.4.2 for maintenance/cleanup.\n\nv0.4.1 | 2026-09-09T21:19:34.037Z | auto\n\n- Removed the file skill-card.md.\n- No functional changes to the skill; this update is limited to file cleanup.\n\nv0.4.0 | 2026-09-04T22:23:50.623Z | auto\n\n- Removed the file skill-card.md.\n- No user-facing changes to functionality or documentation.\n\nv0.3.0 | 2026-08-29T13:54:41.026Z | auto\n\n- Removed the skill card documentation file (skill-card.md).\n- No changes to the skill's code or functionality.\n\nv0.2.1 | 2026-08-28T13:54:56.886Z | auto\n\n- Removed the skill-card.md file.\n- No functional changes to the skill; this is a documentation/packaging update only.\n\nv0.2.0 | 2026-08-28T12:23:16.473Z | auto\n\n- Removed the file skill-card.md.\n- No changes to functionality or usage.\n- Documentation and usage instructions remain unchanged.\n\nv0.1.0 | 2026-08-28T12:00:10.626Z | auto\n\n- Initial release of remind-fpx skill.\n- Enables shell access to Remind (remind.com) classes, chats, messages, and notification settings using GraphQL API via captured browser session.\n- Uses @fetchproxy/cli and Chrome Transporter extension for one-time header capture; works without running the remind-mcp server.\n- Provides setup instructions and sample query patterns for fetching data securely via curl.\n- Read-focused: guides for safe introspection, robust session handling, and field validation included.\n\nArchive index:\n\nArchive v1.2.6: 4 files, 6177 bytes\n\nFiles: references/graphql-queries.md (5822b), skill-card.md (1833b), SKILL.md (4324b), _meta.json (129b)\n\nFile v1.2.6:SKILL.md\n\n---\nname: remind-fpx\ndescription: 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.\n---\n\n# Remind from the shell\n\nRemind's web app talks to a single **GraphQL endpoint**, `POST https://www.remind.com/graphql`.\nThat endpoint is reachable server-side — no bot wall — so only the *credential* needs the\nbrowser. Capture it once with `fpx`, then use `curl` for everything.\n\n**Introspection is enabled**, so the schema is self-documenting: you can discover any field\nwithout guessing (and you should — see the validation-error gotcha below).\n\n## Auth model\n\nTwo headers, both lifted from a signed-in tab:\n\n| Header | What it is |\n|---|---|\n| `cookie` | The full Cookie request header (includes HttpOnly session cookies) |\n| `x-csrf-token` | Value of the JS-readable `csrf_token` cookie |\n\nBoth are required. Only `x-csrf-token` works — `x-xsrf-token` and `csrf-token` are rejected.\nA named cookie read (`fpx cookies csrf_token`) returns `{}` on this site, so **header capture\nis the only route**.\n\n## One-time setup\n\nInstall `@fetchproxy/cli` (`npm i -g @fetchproxy/cli`) and the **ContextMint Bridge**\nbrowser extension from https://github.com/nullnet-app/contextmint-bridge/releases (Chrome: load\nthe Chrome zip unpacked; Safari isn't available yet, so use Chrome for now).\nIt'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`.\n\nDeclare the full scope **before** the first pairing — widening it later forces a re-pair:\n\n```bash\nfpx profile add remind --domain remind.com\nfpx profile declare remind \\\n  --capture-header cookie@www.remind.com \\\n  --capture-header x-csrf-token@www.remind.com\n```\n\nThen capture. The capture waits for the *next* request the page makes, so **load a Remind page\nwhile it waits**:\n\n```bash\nfpx session -p remind > ~/.remind-fpx-session.json &   # approve the pair code in ContextMint Bridge\nopen \"https://www.remind.com/\"                          # feeds the capture\nwait\nchmod 600 ~/.remind-fpx-session.json\n```\n\n> Use a session file of your own like this one. Do **not** write into `~/.remind-mcp/` — that is\n> the MCP server's store, with a different schema; overwriting it corrupts its session.\n\n## The call pattern\n\n```bash\nrq() {  # rq '<query>' ['<variables json>']\n  local S=~/.remind-fpx-session.json\n  local body; body=$(Q=\"$1\" V=\"${2:-{\\}}\" node -e '\n    process.stdout.write(JSON.stringify({query:process.env.Q,variables:JSON.parse(process.env.V)}))')\n  curl -s -X POST https://www.remind.com/graphql \\\n    -H 'content-type: application/json' \\\n    -H \"cookie: $(node -e 'console.log(require(process.argv[1]).capturedHeaders.cookie)' \"$S\")\" \\\n    -H \"x-csrf-token: $(node -e 'console.log(require(process.argv[1]).capturedHeaders[\"x-csrf-token\"])' \"$S\")\" \\\n    --data-binary \"$body\"\n}\n\nrq '{ me { uuid first_name last_name email locale } }' | jq .data.me\n```\n\nStart from `navigationList` — it returns the uuids every other query needs.\nReady-to-run documents with `jq` recipes are in **`references/graphql-queries.md`**.\n\n## Gotchas\n\n- **An expired session returns HTTP 200**, with `errors[0].message == \"Unauthorized\"`. Check the\n  body, never the status. Re-run the capture above to refresh.\n- `\"Unauthorized\"` **also** means \"this account may not do that\" — scheduled messages and org-admin\n  queries are owner/teacher-only, and a subscriber (parent) account gets `Unauthorized` on them\n  with a perfectly valid session.\n- **An unknown field reports as `Internal service error` / `GRAPHQL_VALIDATION_FAILED` with HTTP\n  500 metadata**, not a readable \"no such field\". Do not guess field names — introspect:\n  ```bash\n  rq '{ __type(name:\"Class\"){ fields { name } } }' | jq -r '.data.__type.fields[].name'\n  ```\n- Check `permissions.canSend` before trying to send; most fields on a subscriber account are read-only.\n- **Writes deliver to real people and cannot be unsent.** This skill is read-oriented on purpose;\n  the mutation shapes live in the references file, clearly marked.\n\nFile v1.2.6:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"remind-mcp\",\n  \"version\": \"1.2.6\",\n  \"publishedAt\": 1791588537572\n}\n\nFile v1.2.6:references/graphql-queries.md\n\n# Remind GraphQL — ready-to-run documents\n\nEvery query below was executed against the live API. Field selections come from introspection;\nRemind reports an unknown field as `Internal service error` / `GRAPHQL_VALIDATION_FAILED`, so\nedit them only alongside a re-run.\n\nAll examples assume the `rq` helper from `SKILL.md`.\n\n---\n\n## Start here — what's in the account\n\n`navigationList` is the sidebar, and the source of every uuid you need.\n\n```bash\nrq '{ navigationList { cursor items { __typename\n  ... on TextHeader { text }\n  ... on NavigationListEntityItem { type uuid title subtitle isRostered unreadsCount } } } }'\n```\n\nJust the classes and chats, one per line:\n\n```bash\nrq '{ navigationList { items { __typename\n  ... on NavigationListEntityItem { type uuid title unreadsCount } } } }' \\\n| jq -r '.data.navigationList.items[]\n         | select(.__typename==\"NavigationListEntityItem\")\n         | \"\\(.type)\\t\\(.unreadsCount // 0)\\t\\(.uuid)\\t\\(.title)\"'\n```\n\nPage with `cursor`: pass it back as `lastCursor`.\n\n```bash\nrq 'query($c:String){ navigationList(lastCursor:$c){ cursor items { __typename } } }' '{\"c\":\"<cursor>\"}'\n```\n\n## Account\n\n```bash\nrq '{ me { uuid id first_name last_name display_name email locale is_admin is_child created_at sign_in_count } }' \\\n| jq .data.me\n```\n\n## Classes\n\n```bash\nrq 'query($u:[String!]){ classes(uuids:$u){\n  uuid id code name displayName type\n  messagesCount unreadMessagesCount subscribersCount membershipsCount ownersCount\n  createdAt archivedAt messageHistoryEnabled isMember joinUrl\n  subscriberInitiatedChats canEditInfo canAddOwners } }' \\\n  '{\"u\":[\"<class-uuid>\"]}' | jq '.data.classes[]'\n```\n\nClass members (paginate with `page`/`perPage`):\n\n```bash\nrq 'query($u:[String!]){ classes(uuids:$u){ members(perPage:50){ __typename } } }' '{\"u\":[\"<class-uuid>\"]}'\n```\n\n> Introspect `ClassMembership` before selecting fields:\n> `rq '{ __type(name:\"ClassMembership\"){ fields { name } } }' | jq -r '.data.__type.fields[].name'`\n\n## Chats\n\nList every conversation, with the permission that decides whether you can reply:\n\n```bash\nrq '{ chatStreams { uuid title properTitle streamType type state hidden\n  unreadMessagesCount membershipsCount updatedAt\n  permissions { canSend canAdd canClose canLeave canPhone } } }' \\\n| jq -r '.data.chatStreams[] | \"\\(.updatedAt)\\t\\(.unreadMessagesCount)\\t\\(.uuid)\\t\\(.title)\"'\n```\n\nFilter to one class (numeric `id` from `classes`, not the uuid) or search by name:\n\n```bash\nrq 'query($g:Int,$q:String){ chatStreams(groupId:$g, chatQuery:$q){ uuid title } }' '{\"g\":47615786,\"q\":null}'\n```\n\n## Messages\n\n```bash\nrq 'query($u:[String!],$n:Int){ chatStreams(chatUuids:$u){\n  uuid title unreadMessagesCount\n  sequenceItems(nonGapMessagesLimit:$n){ seq key item { __typename\n    ... on MessageItem { uuid body type createdAt sentAt urgent publicUrl\n                         sender { uuid name } files { __typename } }\n    ... on SystemMessageItem { uuid body createdAt }\n    ... on GapItem { uuid size } } } } }' \\\n  '{\"u\":[\"<chat-uuid>\"],\"n\":25}'\n```\n\nReadable transcript, oldest first:\n\n```bash\n… | jq -r '.data.chatStreams[].sequenceItems[]\n           | select(.item.__typename==\"MessageItem\")\n           | \"\\(.item.createdAt)  \\(.item.sender.name): \\(.item.body)\"'\n```\n\nItem types: **MessageItem** (a real message), **SystemMessageItem** (joins, stream creation),\n**GapItem** (`size` unloaded messages — raise `nonGapMessagesLimit` or page with\n`gapItemNextPageParams`).\n\n## Notification settings\n\n```bash\nrq '{ accountNotificationsScreen {\n  canManageReplies repliesEnabled\n  canManageAnnouncementCopies announcementCopiesEnabled\n  canManageCalls allowsIncomingCalls\n  devices { id type title deliveryDescription isEnabled isPendingConfirmation isPreferredVoiceCallsDevice } } }' \\\n| jq '.data.accountNotificationsScreen'\n```\n\nThe `canManage*` flags say what this account may change. On a subscriber (parent) account they\nare typically all `false` — the *device* toggles below still work.\n\n## Schema discovery\n\n```bash\n# root fields\nrq '{ __type(name:\"Query\"){ fields { name } } }'    | jq -r '.data.__type.fields[].name'\nrq '{ __type(name:\"Mutation\"){ fields { name } } }' | jq -r '.data.__type.fields[].name'\n# one type's fields, with types\nrq '{ __type(name:\"MessageItem\"){ fields { name type { name kind ofType { name } } } } }'\n# union / interface members — needed to write an inline fragment\nrq '{ __type(name:\"StreamItem\"){ possibleTypes { name } } }' | jq -r '.data.__type.possibleTypes[].name'\n```\n\n---\n\n## Mutations — read this first\n\nWrites hit real people and **cannot be unsent**. Nothing here is needed for reading Remind.\n\n**Verified live:** toggling notification devices, which affects only your own account.\n\n```bash\n# disable, then re-enable, a delivery device (id from accountNotificationsScreen)\nrq 'mutation($i:UpdateAccountNotificationsScreenInput!){ updateAccountNotificationsScreen(input:$i){ __typename } }' \\\n   '{\"i\":{\"devicesToDisable\":[<device-id>]}}'\nrq 'mutation($i:UpdateAccountNotificationsScreenInput!){ updateAccountNotificationsScreen(input:$i){ __typename } }' \\\n   '{\"i\":{\"devicesToEnable\":[<device-id>]}}'\n```\n\nA 200 is not proof — re-read `accountNotificationsScreen` and check `devices[].isEnabled` actually moved.\n\n**Shape only, NOT executed here** — sending a message. Check `permissions.canSend` first.\n\n```bash\nrq 'mutation($i:PutMessageInput!){ putMessage(input:$i){ error { __typename } messages { __typename } } }' \\\n   '{\"i\":{\"recipients\":[{\"type\":\"chat\",\"uuid\":\"<chat-uuid>\"}],\"message\":{\"body\":\"…\",\"urgent\":false}}}'\n```\n\n`recipients[].type` is `chat` for a conversation or `group` for a whole class.\n\n**Owner/teacher only:** `scheduleMessage`, `api_scheduledMessages` and `api_deleteScheduledMessage`\nreturn `Unauthorized` on a subscriber account even with a valid session.\n\nFile v1.2.6:skill-card.md\n\n## Description:\n\nGuides agents to read Remind classes, chats, messages, and notification settings using a captured account session and shell commands.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nRemind account holders and developers use this skill to inspect their classes, conversations, messages, and notification settings from a shell without running the Remind MCP server.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: A saved login session can grant continued access to a Remind account.\n\nMitigation: Use only an account you control; protect the session file and delete it when finished.\n\nRisk: Mutation examples can change account settings or send messages to real people.\n\nMitigation: Avoid mutation examples unless you explicitly intend those changes; check permissions and recipients before sending.\n\n## Reference(s):\n\n- [Remind GraphQL query examples](references/graphql-queries.md)\n- [ContextMint Bridge releases](https://github.com/nullnet-app/contextmint-bridge/releases)\n- [ClawHub skill release](https://clawhub.ai/chrischall/skills/remind-mcp)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Guidance]\n\n**Output Format:** [Markdown with shell commands and JSON query examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Queries return account data as JSON; examples include jq filters.]\n\n## Skill Version(s):\n\n1.2.6 (source: ClawHub release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.2.5: 4 files, 6221 bytes\n\nFiles: references/graphql-queries.md (5822b), skill-card.md (1918b), SKILL.md (4324b), _meta.json (129b)\n\nFile v1.2.5:SKILL.md\n\n---\nname: remind-fpx\ndescription: 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.\n---\n\n# Remind from the shell\n\nRemind's web app talks to a single **GraphQL endpoint**, `POST https://www.remind.com/graphql`.\nThat endpoint is reachable server-side — no bot wall — so only the *credential* needs the\nbrowser. Capture it once with `fpx`, then use `curl` for everything.\n\n**Introspection is enabled**, so the schema is self-documenting: you can discover any field\nwithout guessing (and you should — see the validation-error gotcha below).\n\n## Auth model\n\nTwo headers, both lifted from a signed-in tab:\n\n| Header | What it is |\n|---|---|\n| `cookie` | The full Cookie request header (includes HttpOnly session cookies) |\n| `x-csrf-token` | Value of the JS-readable `csrf_token` cookie |\n\nBoth are required. Only `x-csrf-token` works — `x-xsrf-token` and `csrf-token` are rejected.\nA named cookie read (`fpx cookies csrf_token`) returns `{}` on this site, so **header capture\nis the only route**.\n\n## One-time setup\n\nInstall `@fetchproxy/cli` (`npm i -g @fetchproxy/cli`) and the **ContextMint Bridge**\nbrowser extension from https://github.com/nullnet-app/contextmint-bridge/releases (Chrome: load\nthe Chrome zip unpacked; Safari isn't available yet, so use Chrome for now).\nIt'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`.\n\nDeclare the full scope **before** the first pairing — widening it later forces a re-pair:\n\n```bash\nfpx profile add remind --domain remind.com\nfpx profile declare remind \\\n  --capture-header cookie@www.remind.com \\\n  --capture-header x-csrf-token@www.remind.com\n```\n\nThen capture. The capture waits for the *next* request the page makes, so **load a Remind page\nwhile it waits**:\n\n```bash\nfpx session -p remind > ~/.remind-fpx-session.json &   # approve the pair code in ContextMint Bridge\nopen \"https://www.remind.com/\"                          # feeds the capture\nwait\nchmod 600 ~/.remind-fpx-session.json\n```\n\n> Use a session file of your own like this one. Do **not** write into `~/.remind-mcp/` — that is\n> the MCP server's store, with a different schema; overwriting it corrupts its session.\n\n## The call pattern\n\n```bash\nrq() {  # rq '<query>' ['<variables json>']\n  local S=~/.remind-fpx-session.json\n  local body; body=$(Q=\"$1\" V=\"${2:-{\\}}\" node -e '\n    process.stdout.write(JSON.stringify({query:process.env.Q,variables:JSON.parse(process.env.V)}))')\n  curl -s -X POST https://www.remind.com/graphql \\\n    -H 'content-type: application/json' \\\n    -H \"cookie: $(node -e 'console.log(require(process.argv[1]).capturedHeaders.cookie)' \"$S\")\" \\\n    -H \"x-csrf-token: $(node -e 'console.log(require(process.argv[1]).capturedHeaders[\"x-csrf-token\"])' \"$S\")\" \\\n    --data-binary \"$body\"\n}\n\nrq '{ me { uuid first_name last_name email locale } }' | jq .data.me\n```\n\nStart from `navigationList` — it returns the uuids every other query needs.\nReady-to-run documents with `jq` recipes are in **`references/graphql-queries.md`**.\n\n## Gotchas\n\n- **An expired session returns HTTP 200**, with `errors[0].message == \"Unauthorized\"`. Check the\n  body, never the status. Re-run the capture above to refresh.\n- `\"Unauthorized\"` **also** means \"this account may not do that\" — scheduled messages and org-admin\n  queries are owner/teacher-only, and a subscriber (parent) account gets `Unauthorized` on them\n  with a perfectly valid session.\n- **An unknown field reports as `Internal service error` / `GRAPHQL_VALIDATION_FAILED` with HTTP\n  500 metadata**, not a readable \"no such field\". Do not guess field names — introspect:\n  ```bash\n  rq '{ __type(name:\"Class\"){ fields { name } } }' | jq -r '.data.__type.fields[].name'\n  ```\n- Check `permissions.canSend` before trying to send; most fields on a subscriber account are read-only.\n- **Writes deliver to real people and cannot be unsent.** This skill is read-oriented on purpose;\n  the mutation shapes live in the references file, clearly marked.\n\nFile v1.2.5:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"remind-mcp\",\n  \"version\": \"1.2.5\",\n  \"publishedAt\": 1791380445495\n}\n\nFile v1.2.5:references/graphql-queries.md\n\n# Remind GraphQL — ready-to-run documents\n\nEvery query below was executed against the live API. Field selections come from introspection;\nRemind reports an unknown field as `Internal service error` / `GRAPHQL_VALIDATION_FAILED`, so\nedit them only alongside a re-run.\n\nAll examples assume the `rq` helper from `SKILL.md`.\n\n---\n\n## Start here — what's in the account\n\n`navigationList` is the sidebar, and the source of every uuid you need.\n\n```bash\nrq '{ navigationList { cursor items { __typename\n  ... on TextHeader { text }\n  ... on NavigationListEntityItem { type uuid title subtitle isRostered unreadsCount } } } }'\n```\n\nJust the classes and chats, one per line:\n\n```bash\nrq '{ navigationList { items { __typename\n  ... on NavigationListEntityItem { type uuid title unreadsCount } } } }' \\\n| jq -r '.data.navigationList.items[]\n         | select(.__typename==\"NavigationListEntityItem\")\n         | \"\\(.type)\\t\\(.unreadsCount // 0)\\t\\(.uuid)\\t\\(.title)\"'\n```\n\nPage with `cursor`: pass it back as `lastCursor`.\n\n```bash\nrq 'query($c:String){ navigationList(lastCursor:$c){ cursor items { __typename } } }' '{\"c\":\"<cursor>\"}'\n```\n\n## Account\n\n```bash\nrq '{ me { uuid id first_name last_name display_name email locale is_admin is_child created_at sign_in_count } }' \\\n| jq .data.me\n```\n\n## Classes\n\n```bash\nrq 'query($u:[String!]){ classes(uuids:$u){\n  uuid id code name displayName type\n  messagesCount unreadMessagesCount subscribersCount membershipsCount ownersCount\n  createdAt archivedAt messageHistoryEnabled isMember joinUrl\n  subscriberInitiatedChats canEditInfo canAddOwners } }' \\\n  '{\"u\":[\"<class-uuid>\"]}' | jq '.data.classes[]'\n```\n\nClass members (paginate with `page`/`perPage`):\n\n```bash\nrq 'query($u:[String!]){ classes(uuids:$u){ members(perPage:50){ __typename } } }' '{\"u\":[\"<class-uuid>\"]}'\n```\n\n> Introspect `ClassMembership` before selecting fields:\n> `rq '{ __type(name:\"ClassMembership\"){ fields { name } } }' | jq -r '.data.__type.fields[].name'`\n\n## Chats\n\nList every conversation, with the permission that decides whether you can reply:\n\n```bash\nrq '{ chatStreams { uuid title properTitle streamType type state hidden\n  unreadMessagesCount membershipsCount updatedAt\n  permissions { canSend canAdd canClose canLeave canPhone } } }' \\\n| jq -r '.data.chatStreams[] | \"\\(.updatedAt)\\t\\(.unreadMessagesCount)\\t\\(.uuid)\\t\\(.title)\"'\n```\n\nFilter to one class (numeric `id` from `classes`, not the uuid) or search by name:\n\n```bash\nrq 'query($g:Int,$q:String){ chatStreams(groupId:$g, chatQuery:$q){ uuid title } }' '{\"g\":47615786,\"q\":null}'\n```\n\n## Messages\n\n```bash\nrq 'query($u:[String!],$n:Int){ chatStreams(chatUuids:$u){\n  uuid title unreadMessagesCount\n  sequenceItems(nonGapMessagesLimit:$n){ seq key item { __typename\n    ... on MessageItem { uuid body type createdAt sentAt urgent publicUrl\n                         sender { uuid name } files { __typename } }\n    ... on SystemMessageItem { uuid body createdAt }\n    ... on GapItem { uuid size } } } } }' \\\n  '{\"u\":[\"<chat-uuid>\"],\"n\":25}'\n```\n\nReadable transcript, oldest first:\n\n```bash\n… | jq -r '.data.chatStreams[].sequenceItems[]\n           | select(.item.__typename==\"MessageItem\")\n           | \"\\(.item.createdAt)  \\(.item.sender.name): \\(.item.body)\"'\n```\n\nItem types: **MessageItem** (a real message), **SystemMessageItem** (joins, stream creation),\n**GapItem** (`size` unloaded messages — raise `nonGapMessagesLimit` or page with\n`gapItemNextPageParams`).\n\n## Notification settings\n\n```bash\nrq '{ accountNotificationsScreen {\n  canManageReplies repliesEnabled\n  canManageAnnouncementCopies announcementCopiesEnabled\n  canManageCalls allowsIncomingCalls\n  devices { id type title deliveryDescription isEnabled isPendingConfirmation isPreferredVoiceCallsDevice } } }' \\\n| jq '.data.accountNotificationsScreen'\n```\n\nThe `canManage*` flags say what this account may change. On a subscriber (parent) account they\nare typically all `false` — the *device* toggles below still work.\n\n## Schema discovery\n\n```bash\n# root fields\nrq '{ __type(name:\"Query\"){ fields { name } } }'    | jq -r '.data.__type.fields[].name'\nrq '{ __type(name:\"Mutation\"){ fields { name } } }' | jq -r '.data.__type.fields[].name'\n# one type's fields, with types\nrq '{ __type(name:\"MessageItem\"){ fields { name type { name kind ofType { name } } } } }'\n# union / interface members — needed to write an inline fragment\nrq '{ __type(name:\"StreamItem\"){ possibleTypes { name } } }' | jq -r '.data.__type.possibleTypes[].name'\n```\n\n---\n\n## Mutations — read this first\n\nWrites hit real people and **cannot be unsent**. Nothing here is needed for reading Remind.\n\n**Verified live:** toggling notification devices, which affects only your own account.\n\n```bash\n# disable, then re-enable, a delivery device (id from accountNotificationsScreen)\nrq 'mutation($i:UpdateAccountNotificationsScreenInput!){ updateAccountNotificationsScreen(input:$i){ __typename } }' \\\n   '{\"i\":{\"devicesToDisable\":[<device-id>]}}'\nrq 'mutation($i:UpdateAccountNotificationsScreenInput!){ updateAccountNotificationsScreen(input:$i){ __typename } }' \\\n   '{\"i\":{\"devicesToEnable\":[<device-id>]}}'\n```\n\nA 200 is not proof — re-read `accountNotificationsScreen` and check `devices[].isEnabled` actually moved.\n\n**Shape only, NOT executed here** — sending a message. Check `permissions.canSend` first.\n\n```bash\nrq 'mutation($i:PutMessageInput!){ putMessage(input:$i){ error { __typename } messages { __typename } } }' \\\n   '{\"i\":{\"recipients\":[{\"type\":\"chat\",\"uuid\":\"<chat-uuid>\"}],\"message\":{\"body\":\"…\",\"urgent\":false}}}'\n```\n\n`recipients[].type` is `chat` for a conversation or `group` for a whole class.\n\n**Owner/teacher only:** `scheduleMessage`, `api_scheduledMessages` and `api_deleteScheduledMessage`\nreturn `Unauthorized` on a subscriber account even with a valid session.\n\nFile v1.2.5:skill-card.md\n\n## Description:\n\nHelps agents read Remind classes, chats, messages, and notification settings from a shell using a captured signed-in session.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nRemind users and developers use this skill to inspect their account's classes, conversations, messages, and notification settings through shell commands without running the Remind MCP server.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Captured session credentials can grant access to the signed-in Remind account if disclosed.\n\nMitigation: Keep the session file private, out of backups and shared directories, and delete it when finished.\n\nRisk: The included mutation examples can change notification settings or send messages to real people despite the skill's read-oriented purpose.\n\nMitigation: Do not run mutation examples unless you explicitly intend the change; review recipients and permissions before sending messages.\n\n## Reference(s):\n\n- [Remind GraphQL query examples](references/graphql-queries.md)\n- [ContextMint Bridge releases](https://github.com/nullnet-app/contextmint-bridge/releases)\n- [remind-mcp ClawHub release](https://clawhub.ai/chrischall/skills/remind-mcp)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Guidance]\n\n**Output Format:** [Markdown with shell code blocks]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Requires an authenticated Remind session for live queries.]\n\n## Skill Version(s):\n\n1.2.5 (source: ClawHub release evidence)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.2.4: 4 files, 6241 bytes\n\nFiles: references/graphql-queries.md (5822b), skill-card.md (1966b), SKILL.md (4324b), _meta.json (129b)\n\nFile v1.2.4:SKILL.md\n\n---\nname: remind-fpx\ndescription: 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.\n---\n\n# Remind from the shell\n\nRemind's web app talks to a single **GraphQL endpoint**, `POST https://www.remind.com/graphql`.\nThat endpoint is reachable server-side — no bot wall — so only the *credential* needs the\nbrowser. Capture it once with `fpx`, then use `curl` for everything.\n\n**Introspection is enabled**, so the schema is self-documenting: you can discover any field\nwithout guessing (and you should — see the validation-error gotcha below).\n\n## Auth model\n\nTwo headers, both lifted from a signed-in tab:\n\n| Header | What it is |\n|---|---|\n| `cookie` | The full Cookie request header (includes HttpOnly session cookies) |\n| `x-csrf-token` | Value of the JS-readable `csrf_token` cookie |\n\nBoth are required. Only `x-csrf-token` works — `x-xsrf-token` and `csrf-token` are rejected.\nA named cookie read (`fpx cookies csrf_token`) returns `{}` on this site, so **header capture\nis the only route**.\n\n## One-time setup\n\nInstall `@fetchproxy/cli` (`npm i -g @fetchproxy/cli`) and the **ContextMint Bridge**\nbrowser extension from https://github.com/nullnet-app/contextmint-bridge/releases (Chrome: load\nthe Chrome zip unpacked; Safari isn't available yet, so use Chrome for now).\nIt'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`.\n\nDeclare the full scope **before** the first pairing — widening it later forces a re-pair:\n\n```bash\nfpx profile add remind --domain remind.com\nfpx profile declare remind \\\n  --capture-header cookie@www.remind.com \\\n  --capture-header x-csrf-token@www.remind.com\n```\n\nThen capture. The capture waits for the *next* request the page makes, so **load a Remind page\nwhile it waits**:\n\n```bash\nfpx session -p remind > ~/.remind-fpx-session.json &   # approve the pair code in ContextMint Bridge\nopen \"https://www.remind.com/\"                          # feeds the capture\nwait\nchmod 600 ~/.remind-fpx-session.json\n```\n\n> Use a session file of your own like this one. Do **not** write into `~/.remind-mcp/` — that is\n> the MCP server's store, with a different schema; overwriting it corrupts its session.\n\n## The call pattern\n\n```bash\nrq() {  # rq '<query>' ['<variables json>']\n  local S=~/.remind-fpx-session.json\n  local body; body=$(Q=\"$1\" V=\"${2:-{\\}}\" node -e '\n    process.stdout.write(JSON.stringify({query:process.env.Q,variables:JSON.parse(process.env.V)}))')\n  curl -s -X POST https://www.remind.com/graphql \\\n    -H 'content-type: application/json' \\\n    -H \"cookie: $(node -e 'console.log(require(process.argv[1]).capturedHeaders.cookie)' \"$S\")\" \\\n    -H \"x-csrf-token: $(node -e 'console.log(require(process.argv[1]).capturedHeaders[\"x-csrf-token\"])' \"$S\")\" \\\n    --data-binary \"$body\"\n}\n\nrq '{ me { uuid first_name last_name email locale } }' | jq .data.me\n```\n\nStart from `navigationList` — it returns the uuids every other query needs.\nReady-to-run documents with `jq` recipes are in **`references/graphql-queries.md`**.\n\n## Gotchas\n\n- **An expired session returns HTTP 200**, with `errors[0].message == \"Unauthorized\"`. Check the\n  body, never the status. Re-run the capture above to refresh.\n- `\"Unauthorized\"` **also** means \"this account may not do that\" — scheduled messages and org-admin\n  queries are owner/teacher-only, and a subscriber (parent) account gets `Unauthorized` on them\n  with a perfectly valid session.\n- **An unknown field reports as `Internal service error` / `GRAPHQL_VALIDATION_FAILED` with HTTP\n  500 metadata**, not a readable \"no such field\". Do not guess field names — introspect:\n  ```bash\n  rq '{ __type(name:\"Class\"){ fields { name } } }' | jq -r '.data.__type.fields[].name'\n  ```\n- Check `permissions.canSend` before trying to send; most fields on a subscriber account are read-only.\n- **Writes deliver to real people and cannot be unsent.** This skill is read-oriented on purpose;\n  the mutation shapes live in the references file, clearly marked.\n\nFile v1.2.4:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"remind-mcp\",\n  \"version\": \"1.2.4\",\n  \"publishedAt\": 1791168707382\n}\n\nFile v1.2.4:references/graphql-queries.md\n\n# Remind GraphQL — ready-to-run documents\n\nEvery query below was executed against the live API. Field selections come from introspection;\nRemind reports an unknown field as `Internal service error` / `GRAPHQL_VALIDATION_FAILED`, so\nedit them only alongside a re-run.\n\nAll examples assume the `rq` helper from `SKILL.md`.\n\n---\n\n## Start here — what's in the account\n\n`navigationList` is the sidebar, and the source of every uuid you need.\n\n```bash\nrq '{ navigationList { cursor items { __typename\n  ... on TextHeader { text }\n  ... on NavigationListEntityItem { type uuid title subtitle isRostered unreadsCount } } } }'\n```\n\nJust the classes and chats, one per line:\n\n```bash\nrq '{ navigationList { items { __typename\n  ... on NavigationListEntityItem { type uuid title unreadsCount } } } }' \\\n| jq -r '.data.navigationList.items[]\n         | select(.__typename==\"NavigationListEntityItem\")\n         | \"\\(.type)\\t\\(.unreadsCount // 0)\\t\\(.uuid)\\t\\(.title)\"'\n```\n\nPage with `cursor`: pass it back as `lastCursor`.\n\n```bash\nrq 'query($c:String){ navigationList(lastCursor:$c){ cursor items { __typename } } }' '{\"c\":\"<cursor>\"}'\n```\n\n## Account\n\n```bash\nrq '{ me { uuid id first_name last_name display_name email locale is_admin is_child created_at sign_in_count } }' \\\n| jq .data.me\n```\n\n## Classes\n\n```bash\nrq 'query($u:[String!]){ classes(uuids:$u){\n  uuid id code name displayName type\n  messagesCount unreadMessagesCount subscribersCount membershipsCount ownersCount\n  createdAt archivedAt messageHistoryEnabled isMember joinUrl\n  subscriberInitiatedChats canEditInfo canAddOwners } }' \\\n  '{\"u\":[\"<class-uuid>\"]}' | jq '.data.classes[]'\n```\n\nClass members (paginate with `page`/`perPage`):\n\n```bash\nrq 'query($u:[String!]){ classes(uuids:$u){ members(perPage:50){ __typename } } }' '{\"u\":[\"<class-uuid>\"]}'\n```\n\n> Introspect `ClassMembership` before selecting fields:\n> `rq '{ __type(name:\"ClassMembership\"){ fields { name } } }' | jq -r '.data.__type.fields[].name'`\n\n## Chats\n\nList every conversation, with the permission that decides whether you can reply:\n\n```bash\nrq '{ chatStreams { uuid title properTitle streamType type state hidden\n  unreadMessagesCount membershipsCount updatedAt\n  permissions { canSend canAdd canClose canLeave canPhone } } }' \\\n| jq -r '.data.chatStreams[] | \"\\(.updatedAt)\\t\\(.unreadMessagesCount)\\t\\(.uuid)\\t\\(.title)\"'\n```\n\nFilter to one class (numeric `id` from `classes`, not the uuid) or search by name:\n\n```bash\nrq 'query($g:Int,$q:String){ chatStreams(groupId:$g, chatQuery:$q){ uuid title } }' '{\"g\":47615786,\"q\":null}'\n```\n\n## Messages\n\n```bash\nrq 'query($u:[String!],$n:Int){ chatStreams(chatUuids:$u){\n  uuid title unreadMessagesCount\n  sequenceItems(nonGapMessagesLimit:$n){ seq key item { __typename\n    ... on MessageItem { uuid body type createdAt sentAt urgent publicUrl\n                         sender { uuid name } files { __typename } }\n    ... on SystemMessageItem { uuid body createdAt }\n    ... on GapItem { uuid size } } } } }' \\\n  '{\"u\":[\"<chat-uuid>\"],\"n\":25}'\n```\n\nReadable transcript, oldest first:\n\n```bash\n… | jq -r '.data.chatStreams[].sequenceItems[]\n           | select(.item.__typename==\"MessageItem\")\n           | \"\\(.item.createdAt)  \\(.item.sender.name): \\(.item.body)\"'\n```\n\nItem types: **MessageItem** (a real message), **SystemMessageItem** (joins, stream creation),\n**GapItem** (`size` unloaded messages — raise `nonGapMessagesLimit` or page with\n`gapItemNextPageParams`).\n\n## Notification settings\n\n```bash\nrq '{ accountNotificationsScreen {\n  canManageReplies repliesEnabled\n  canManageAnnouncementCopies announcementCopiesEnabled\n  canManageCalls allowsIncomingCalls\n  devices { id type title deliveryDescription isEnabled isPendingConfirmation isPreferredVoiceCallsDevice } } }' \\\n| jq '.data.accountNotificationsScreen'\n```\n\nThe `canManage*` flags say what this account may change. On a subscriber (parent) account they\nare typically all `false` — the *device* toggles below still work.\n\n## Schema discovery\n\n```bash\n# root fields\nrq '{ __type(name:\"Query\"){ fields { name } } }'    | jq -r '.data.__type.fields[].name'\nrq '{ __type(name:\"Mutation\"){ fields { name } } }' | jq -r '.data.__type.fields[].name'\n# one type's fields, with types\nrq '{ __type(name:\"MessageItem\"){ fields { name type { name kind ofType { name } } } } }'\n# union / interface members — needed to write an inline fragment\nrq '{ __type(name:\"StreamItem\"){ possibleTypes { name } } }' | jq -r '.data.__type.possibleTypes[].name'\n```\n\n---\n\n## Mutations — read this first\n\nWrites hit real people and **cannot be unsent**. Nothing here is needed for reading Remind.\n\n**Verified live:** toggling notification devices, which affects only your own account.\n\n```bash\n# disable, then re-enable, a delivery device (id from accountNotificationsScreen)\nrq 'mutation($i:UpdateAccountNotificationsScreenInput!){ updateAccountNotificationsScreen(input:$i){ __typename } }' \\\n   '{\"i\":{\"devicesToDisable\":[<device-id>]}}'\nrq 'mutation($i:UpdateAccountNotificationsScreenInput!){ updateAccountNotificationsScreen(input:$i){ __typename } }' \\\n   '{\"i\":{\"devicesToEnable\":[<device-id>]}}'\n```\n\nA 200 is not proof — re-read `accountNotificationsScreen` and check `devices[].isEnabled` actually moved.\n\n**Shape only, NOT executed here** — sending a message. Check `permissions.canSend` first.\n\n```bash\nrq 'mutation($i:PutMessageInput!){ putMessage(input:$i){ error { __typename } messages { __typename } } }' \\\n   '{\"i\":{\"recipients\":[{\"type\":\"chat\",\"uuid\":\"<chat-uuid>\"}],\"message\":{\"body\":\"…\",\"urgent\":false}}}'\n```\n\n`recipients[].type` is `chat` for a conversation or `group` for a whole class.\n\n**Owner/teacher only:** `scheduleMessage`, `api_scheduledMessages` and `api_deleteScheduledMessage`\nreturn `Unauthorized` on a subscriber account even with a valid session.\n\nFile v1.2.4:skill-card.md\n\n## Description:\n\nGuides agents in reading Remind classes, chats, messages, and notification settings from a signed-in account using shell commands.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nPeople with a Remind account use this skill to retrieve their classes, conversations, messages, and notification settings without running the Remind MCP server. Developers can also adapt the supplied shell queries for scripts or one-off checks.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: A captured, reusable Remind session credential is stored locally.\n\nMitigation: Review the credential capture before installing, protect the saved session file, and delete or rotate the session when it is no longer needed.\n\nRisk: The reference examples include mutations that change notification settings or send messages to real people.\n\nMitigation: Use read-only queries by default; run mutations only when you intentionally want to change settings or send a message.\n\n## Reference(s):\n\n- [Remind-mcp ClawHub release](https://clawhub.ai/chrischall/skills/remind-mcp)\n- [Remind GraphQL query examples](references/graphql-queries.md)\n- [ContextMint Bridge releases](https://github.com/nullnet-app/contextmint-bridge/releases)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Guidance]\n\n**Output Format:** [Markdown with shell and GraphQL examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Query results may include account details and message content.]\n\n## Skill Version(s):\n\n1.2.4 (source: ClawHub release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.2.3: 4 files, 6212 bytes\n\nFiles: references/graphql-queries.md (5822b), skill-card.md (1893b), SKILL.md (4324b), _meta.json (129b)\n\nFile v1.2.3:SKILL.md\n\n---\nname: remind-fpx\ndescription: 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.\n---\n\n# Remind from the shell\n\nRemind's web app talks to a single **GraphQL endpoint**, `POST https://www.remind.com/graphql`.\nThat endpoint is reachable server-side — no bot wall — so only the *credential* needs the\nbrowser. Capture it once with `fpx`, then use `curl` for everything.\n\n**Introspection is enabled**, so the schema is self-documenting: you can discover any field\nwithout guessing (and you should — see the validation-error gotcha below).\n\n## Auth model\n\nTwo headers, both lifted from a signed-in tab:\n\n| Header | What it is |\n|---|---|\n| `cookie` | The full Cookie request header (includes HttpOnly session cookies) |\n| `x-csrf-token` | Value of the JS-readable `csrf_token` cookie |\n\nBoth are required. Only `x-csrf-token` works — `x-xsrf-token` and `csrf-token` are rejected.\nA named cookie read (`fpx cookies csrf_token`) returns `{}` on this site, so **header capture\nis the only route**.\n\n## One-time setup\n\nInstall `@fetchproxy/cli` (`npm i -g @fetchproxy/cli`) and the **ContextMint Bridge**\nbrowser extension from https://github.com/nullnet-app/contextmint-bridge/releases (Chrome: load\nthe Chrome zip unpacked; Safari isn't available yet, so use Chrome for now).\nIt'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`.\n\nDeclare the full scope **before** the first pairing — widening it later forces a re-pair:\n\n```bash\nfpx profile add remind --domain remind.com\nfpx profile declare remind \\\n  --capture-header cookie@www.remind.com \\\n  --capture-header x-csrf-token@www.remind.com\n```\n\nThen capture. The capture waits for the *next* request the page makes, so **load a Remind page\nwhile it waits**:\n\n```bash\nfpx session -p remind > ~/.remind-fpx-session.json &   # approve the pair code in ContextMint Bridge\nopen \"https://www.remind.com/\"                          # feeds the capture\nwait\nchmod 600 ~/.remind-fpx-session.json\n```\n\n> Use a session file of your own like this one. Do **not** write into `~/.remind-mcp/` — that is\n> the MCP server's store, with a different schema; overwriting it corrupts its session.\n\n## The call pattern\n\n```bash\nrq() {  # rq '<query>' ['<variables json>']\n  local S=~/.remind-fpx-session.json\n  local body; body=$(Q=\"$1\" V=\"${2:-{\\}}\" node -e '\n    process.stdout.write(JSON.stringify({query:process.env.Q,variables:JSON.parse(process.env.V)}))')\n  curl -s -X POST https://www.remind.com/graphql \\\n    -H 'content-type: application/json' \\\n    -H \"cookie: $(node -e 'console.log(require(process.argv[1]).capturedHeaders.cookie)' \"$S\")\" \\\n    -H \"x-csrf-token: $(node -e 'console.log(require(process.argv[1]).capturedHeaders[\"x-csrf-token\"])' \"$S\")\" \\\n    --data-binary \"$body\"\n}\n\nrq '{ me { uuid first_name last_name email locale } }' | jq .data.me\n```\n\nStart from `navigationList` — it returns the uuids every other query needs.\nReady-to-run documents with `jq` recipes are in **`references/graphql-queries.md`**.\n\n## Gotchas\n\n- **An expired session returns HTTP 200**, with `errors[0].message == \"Unauthorized\"`. Check the\n  body, never the status. Re-run the capture above to refresh.\n- `\"Unauthorized\"` **also** means \"this account may not do that\" — scheduled messages and org-admin\n  queries are owner/teacher-only, and a subscriber (parent) account gets `Unauthorized` on them\n  with a perfectly valid session.\n- **An unknown field reports as `Internal service error` / `GRAPHQL_VALIDATION_FAILED` with HTTP\n  500 metadata**, not a readable \"no such field\". Do not guess field names — introspect:\n  ```bash\n  rq '{ __type(name:\"Class\"){ fields { name } } }' | jq -r '.data.__type.fields[].name'\n  ```\n- Check `permissions.canSend` before trying to send; most fields on a subscriber account are read-only.\n- **Writes deliver to real people and cannot be unsent.** This skill is read-oriented on purpose;\n  the mutation shapes live in the references file, clearly marked.\n\nFile v1.2.3:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"remind-mcp\",\n  \"version\": \"1.2.3\",\n  \"publishedAt\": 1790991738480\n}\n\nFile v1.2.3:references/graphql-queries.md\n\n# Remind GraphQL — ready-to-run documents\n\nEvery query below was executed against the live API. Field selections come from introspection;\nRemind reports an unknown field as `Internal service error` / `GRAPHQL_VALIDATION_FAILED`, so\nedit them only alongside a re-run.\n\nAll examples assume the `rq` helper from `SKILL.md`.\n\n---\n\n## Start here — what's in the account\n\n`navigationList` is the sidebar, and the source of every uuid you need.\n\n```bash\nrq '{ navigationList { cursor items { __typename\n  ... on TextHeader { text }\n  ... on NavigationListEntityItem { type uuid title subtitle isRostered unreadsCount } } } }'\n```\n\nJust the classes and chats, one per line:\n\n```bash\nrq '{ navigationList { items { __typename\n  ... on NavigationListEntityItem { type uuid title unreadsCount } } } }' \\\n| jq -r '.data.navigationList.items[]\n         | select(.__typename==\"NavigationListEntityItem\")\n         | \"\\(.type)\\t\\(.unreadsCount // 0)\\t\\(.uuid)\\t\\(.title)\"'\n```\n\nPage with `cursor`: pass it back as `lastCursor`.\n\n```bash\nrq 'query($c:String){ navigationList(lastCursor:$c){ cursor items { __typename } } }' '{\"c\":\"<cursor>\"}'\n```\n\n## Account\n\n```bash\nrq '{ me { uuid id first_name last_name display_name email locale is_admin is_child created_at sign_in_count } }' \\\n| jq .data.me\n```\n\n## Classes\n\n```bash\nrq 'query($u:[String!]){ classes(uuids:$u){\n  uuid id code name displayName type\n  messagesCount unreadMessagesCount subscribersCount membershipsCount ownersCount\n  createdAt archivedAt messageHistoryEnabled isMember joinUrl\n  subscriberInitiatedChats canEditInfo canAddOwners } }' \\\n  '{\"u\":[\"<class-uuid>\"]}' | jq '.data.classes[]'\n```\n\nClass members (paginate with `page`/`perPage`):\n\n```bash\nrq 'query($u:[String!]){ classes(uuids:$u){ members(perPage:50){ __typename } } }' '{\"u\":[\"<class-uuid>\"]}'\n```\n\n> Introspect `ClassMembership` before selecting fields:\n> `rq '{ __type(name:\"ClassMembership\"){ fields { name } } }' | jq -r '.data.__type.fields[].name'`\n\n## Chats\n\nList every conversation, with the permission that decides whether you can reply:\n\n```bash\nrq '{ chatStreams { uuid title properTitle streamType type state hidden\n  unreadMessagesCount membershipsCount updatedAt\n  permissions { canSend canAdd canClose canLeave canPhone } } }' \\\n| jq -r '.data.chatStreams[] | \"\\(.updatedAt)\\t\\(.unreadMessagesCount)\\t\\(.uuid)\\t\\(.title)\"'\n```\n\nFilter to one class (numeric `id` from `classes`, not the uuid) or search by name:\n\n```bash\nrq 'query($g:Int,$q:String){ chatStreams(groupId:$g, chatQuery:$q){ uuid title } }' '{\"g\":47615786,\"q\":null}'\n```\n\n## Messages\n\n```bash\nrq 'query($u:[String!],$n:Int){ chatStreams(chatUuids:$u){\n  uuid title unreadMessagesCount\n  sequenceItems(nonGapMessagesLimit:$n){ seq key item { __typename\n    ... on MessageItem { uuid body type createdAt sentAt urgent publicUrl\n                         sender { uuid name } files { __typename } }\n    ... on SystemMessageItem { uuid body createdAt }\n    ... on GapItem { uuid size } } } } }' \\\n  '{\"u\":[\"<chat-uuid>\"],\"n\":25}'\n```\n\nReadable transcript, oldest first:\n\n```bash\n… | jq -r '.data.chatStreams[].sequenceItems[]\n           | select(.item.__typename==\"MessageItem\")\n           | \"\\(.item.createdAt)  \\(.item.sender.name): \\(.item.body)\"'\n```\n\nItem types: **MessageItem** (a real message), **SystemMessageItem** (joins, stream creation),\n**GapItem** (`size` unloaded messages — raise `nonGapMessagesLimit` or page with\n`gapItemNextPageParams`).\n\n## Notification settings\n\n```bash\nrq '{ accountNotificationsScreen {\n  canManageReplies repliesEnabled\n  canManageAnnouncementCopies announcementCopiesEnabled\n  canManageCalls allowsIncomingCalls\n  devices { id type title deliveryDescription isEnabled isPendingConfirmation isPreferredVoiceCallsDevice } } }' \\\n| jq '.data.accountNotificationsScreen'\n```\n\nThe `canManage*` flags say what this account may change. On a subscriber (parent) account they\nare typically all `false` — the *device* toggles below still work.\n\n## Schema discovery\n\n```bash\n# root fields\nrq '{ __type(name:\"Query\"){ fields { name } } }'    | jq -r '.data.__type.fields[].name'\nrq '{ __type(name:\"Mutation\"){ fields { name } } }' | jq -r '.data.__type.fields[].name'\n# one type's fields, with types\nrq '{ __type(name:\"MessageItem\"){ fields { name type { name kind ofType { name } } } } }'\n# union / interface members — needed to write an inline fragment\nrq '{ __type(name:\"StreamItem\"){ possibleTypes { name } } }' | jq -r '.data.__type.possibleTypes[].name'\n```\n\n---\n\n## Mutations — read this first\n\nWrites hit real people and **cannot be unsent**. Nothing here is needed for reading Remind.\n\n**Verified live:** toggling notification devices, which affects only your own account.\n\n```bash\n# disable, then re-enable, a delivery device (id from accountNotificationsScreen)\nrq 'mutation($i:UpdateAccountNotificationsScreenInput!){ updateAccountNotificationsScreen(input:$i){ __typename } }' \\\n   '{\"i\":{\"devicesToDisable\":[<device-id>]}}'\nrq 'mutation($i:UpdateAccountNotificationsScreenInput!){ updateAccountNotificationsScreen(input:$i){ __typename } }' \\\n   '{\"i\":{\"devicesToEnable\":[<device-id>]}}'\n```\n\nA 200 is not proof — re-read `accountNotificationsScreen` and check `devices[].isEnabled` actually moved.\n\n**Shape only, NOT executed here** — sending a message. Check `permissions.canSend` first.\n\n```bash\nrq 'mutation($i:PutMessageInput!){ putMessage(input:$i){ error { __typename } messages { __typename } } }' \\\n   '{\"i\":{\"recipients\":[{\"type\":\"chat\",\"uuid\":\"<chat-uuid>\"}],\"message\":{\"body\":\"…\",\"urgent\":false}}}'\n```\n\n`recipients[].type` is `chat` for a conversation or `group` for a whole class.\n\n**Owner/teacher only:** `scheduleMessage`, `api_scheduledMessages` and `api_deleteScheduledMessage`\nreturn `Unauthorized` on a subscriber account even with a valid session.\n\nFile v1.2.3:skill-card.md\n\n## Description:\n\nGuides agents in reading Remind classes, chats, messages, and notification settings from a shell using a captured browser session.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nRemind users and developers use this skill to inspect their classes, chats, messages, and notification settings through shell-based queries without running the MCP server.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Capturing a full signed-in Remind browser session gives shell access to account data and actions.\n\nMitigation: Keep the session file private, restrict access to it, and delete or rotate it when finished.\n\nRisk: The included mutation examples can change settings or send real messages despite the read-oriented description.\n\nMitigation: Review commands before execution and allow mutations only when you explicitly intend the changes or messages.\n\n## Reference(s):\n\n- [Remind GraphQL query examples](references/graphql-queries.md)\n- [ContextMint Bridge releases](https://github.com/nullnet-app/contextmint-bridge/releases)\n- [ClawHub skill listing](https://clawhub.ai/chrischall/skills/remind-mcp)\n\n## Skill Output:\n\n**Output Type(s):** [Guidance, Shell commands, Configuration instructions]\n\n**Output Format:** [Markdown with shell and GraphQL examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Queries return data available to the signed-in Remind account.]\n\n## Skill Version(s):\n\n1.2.3 (source: ClawHub release evidence)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.2.2: 4 files, 6182 bytes\n\nFiles: references/graphql-queries.md (5822b), skill-card.md (1795b), SKILL.md (4324b), _meta.json (129b)\n\nFile v1.2.2:SKILL.md\n\n---\nname: remind-fpx\ndescription: 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.\n---\n\n# Remind from the shell\n\nRemind's web app talks to a single **GraphQL endpoint**, `POST https://www.remind.com/graphql`.\nThat endpoint is reachable server-side — no bot wall — so only the *credential* needs the\nbrowser. Capture it once with `fpx`, then use `curl` for everything.\n\n**Introspection is enabled**, so the schema is self-documenting: you can discover any field\nwithout guessing (and you should — see the validation-error gotcha below).\n\n## Auth model\n\nTwo headers, both lifted from a signed-in tab:\n\n| Header | What it is |\n|---|---|\n| `cookie` | The full Cookie request header (includes HttpOnly session cookies) |\n| `x-csrf-token` | Value of the JS-readable `csrf_token` cookie |\n\nBoth are required. Only `x-csrf-token` works — `x-xsrf-token` and `csrf-token` are rejected.\nA named cookie read (`fpx cookies csrf_token`) returns `{}` on this site, so **header capture\nis the only route**.\n\n## One-time setup\n\nInstall `@fetchproxy/cli` (`npm i -g @fetchproxy/cli`) and the **ContextMint Bridge**\nbrowser extension from https://github.com/nullnet-app/contextmint-bridge/releases (Chrome: load\nthe Chrome zip unpacked; Safari isn't available yet, so use Chrome for now).\nIt'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`.\n\nDeclare the full scope **before** the first pairing — widening it later forces a re-pair:\n\n```bash\nfpx profile add remind --domain remind.com\nfpx profile declare remind \\\n  --capture-header cookie@www.remind.com \\\n  --capture-header x-csrf-token@www.remind.com\n```\n\nThen capture. The capture waits for the *next* request the page makes, so **load a Remind page\nwhile it waits**:\n\n```bash\nfpx session -p remind > ~/.remind-fpx-session.json &   # approve the pair code in ContextMint Bridge\nopen \"https://www.remind.com/\"                          # feeds the capture\nwait\nchmod 600 ~/.remind-fpx-session.json\n```\n\n> Use a session file of your own like this one. Do **not** write into `~/.remind-mcp/` — that is\n> the MCP server's store, with a different schema; overwriting it corrupts its session.\n\n## The call pattern\n\n```bash\nrq() {  # rq '<query>' ['<variables json>']\n  local S=~/.remind-fpx-session.json\n  local body; body=$(Q=\"$1\" V=\"${2:-{\\}}\" node -e '\n    process.stdout.write(JSON.stringify({query:process.env.Q,variables:JSON.parse(process.env.V)}))')\n  curl -s -X POST https://www.remind.com/graphql \\\n    -H 'content-type: application/json' \\\n    -H \"cookie: $(node -e 'console.log(require(process.argv[1]).capturedHeaders.cookie)' \"$S\")\" \\\n    -H \"x-csrf-token: $(node -e 'console.log(require(process.argv[1]).capturedHeaders[\"x-csrf-token\"])' \"$S\")\" \\\n    --data-binary \"$body\"\n}\n\nrq '{ me { uuid first_name last_name email locale } }' | jq .data.me\n```\n\nStart from `navigationList` — it returns the uuids every other query needs.\nReady-to-run documents with `jq` recipes are in **`references/graphql-queries.md`**.\n\n## Gotchas\n\n- **An expired session returns HTTP 200**, with `errors[0].message == \"Unauthorized\"`. Check the\n  body, never the status. Re-run the capture above to refresh.\n- `\"Unauthorized\"` **also** means \"this account may not do that\" — scheduled messages and org-admin\n  queries are owner/teacher-only, and a subscriber (parent) account gets `Unauthorized` on them\n  with a perfectly valid session.\n- **An unknown field reports as `Internal service error` / `GRAPHQL_VALIDATION_FAILED` with HTTP\n  500 metadata**, not a readable \"no such field\". Do not guess field names — introspect:\n  ```bash\n  rq '{ __type(name:\"Class\"){ fields { name } } }' | jq -r '.data.__type.fields[].name'\n  ```\n- Check `permissions.canSend` before trying to send; most fields on a subscriber account are read-only.\n- **Writes deliver to real people and cannot be unsent.** This skill is read-oriented on purpose;\n  the mutation shapes live in the references file, clearly marked.\n\nFile v1.2.2:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"remind-mcp\",\n  \"version\": \"1.2.2\",\n  \"publishedAt\": 1790604132528\n}\n\nFile v1.2.2:references/graphql-queries.md\n\n# Remind GraphQL — ready-to-run documents\n\nEvery query below was executed against the live API. Field selections come from introspection;\nRemind reports an unknown field as `Internal service error` / `GRAPHQL_VALIDATION_FAILED`, so\nedit them only alongside a re-run.\n\nAll examples assume the `rq` helper from `SKILL.md`.\n\n---\n\n## Start here — what's in the account\n\n`navigationList` is the sidebar, and the source of every uuid you need.\n\n```bash\nrq '{ navigationList { cursor items { __typename\n  ... on TextHeader { text }\n  ... on NavigationListEntityItem { type uuid title subtitle isRostered unreadsCount } } } }'\n```\n\nJust the classes and chats, one per line:\n\n```bash\nrq '{ navigationList { items { __typename\n  ... on NavigationListEntityItem { type uuid title unreadsCount } } } }' \\\n| jq -r '.data.navigationList.items[]\n         | select(.__typename==\"NavigationListEntityItem\")\n         | \"\\(.type)\\t\\(.unreadsCount // 0)\\t\\(.uuid)\\t\\(.title)\"'\n```\n\nPage with `cursor`: pass it back as `lastCursor`.\n\n```bash\nrq 'query($c:String){ navigationList(lastCursor:$c){ cursor items { __typename } } }' '{\"c\":\"<cursor>\"}'\n```\n\n## Account\n\n```bash\nrq '{ me { uuid id first_name last_name display_name email locale is_admin is_child created_at sign_in_count } }' \\\n| jq .data.me\n```\n\n## Classes\n\n```bash\nrq 'query($u:[String!]){ classes(uuids:$u){\n  uuid id code name displayName type\n  messagesCount unreadMessagesCount subscribersCount membershipsCount ownersCount\n  createdAt archivedAt messageHistoryEnabled isMember joinUrl\n  subscriberInitiatedChats canEditInfo canAddOwners } }' \\\n  '{\"u\":[\"<class-uuid>\"]}' | jq '.data.classes[]'\n```\n\nClass members (paginate with `page`/`perPage`):\n\n```bash\nrq 'query($u:[String!]){ classes(uuids:$u){ members(perPage:50){ __typename } } }' '{\"u\":[\"<class-uuid>\"]}'\n```\n\n> Introspect `ClassMembership` before selecting fields:\n> `rq '{ __type(name:\"ClassMembership\"){ fields { name } } }' | jq -r '.data.__type.fields[].name'`\n\n## Chats\n\nList every conversation, with the permission that decides whether you can reply:\n\n```bash\nrq '{ chatStreams { uuid title properTitle streamType type state hidden\n  unreadMessagesCount membershipsCount updatedAt\n  permissions { canSend canAdd canClose canLeave canPhone } } }' \\\n| jq -r '.data.chatStreams[] | \"\\(.updatedAt)\\t\\(.unreadMessagesCount)\\t\\(.uuid)\\t\\(.title)\"'\n```\n\nFilter to one class (numeric `id` from `classes`, not the uuid) or search by name:\n\n```bash\nrq 'query($g:Int,$q:String){ chatStreams(groupId:$g, chatQuery:$q){ uuid title } }' '{\"g\":47615786,\"q\":null}'\n```\n\n## Messages\n\n```bash\nrq 'query($u:[String!],$n:Int){ chatStreams(chatUuids:$u){\n  uuid title unreadMessagesCount\n  sequenceItems(nonGapMessagesLimit:$n){ seq key item { __typename\n    ... on MessageItem { uuid body type createdAt sentAt urgent publicUrl\n                         sender { uuid name } files { __typename } }\n    ... on SystemMessageItem { uuid body createdAt }\n    ... on GapItem { uuid size } } } } }' \\\n  '{\"u\":[\"<chat-uuid>\"],\"n\":25}'\n```\n\nReadable transcript, oldest first:\n\n```bash\n… | jq -r '.data.chatStreams[].sequenceItems[]\n           | select(.item.__typename==\"MessageItem\")\n           | \"\\(.item.createdAt)  \\(.item.sender.name): \\(.item.body)\"'\n```\n\nItem types: **MessageItem** (a real message), **SystemMessageItem** (joins, stream creation),\n**GapItem** (`size` unloaded messages — raise `nonGapMessagesLimit` or page with\n`gapItemNextPageParams`).\n\n## Notification settings\n\n```bash\nrq '{ accountNotificationsScreen {\n  canManageReplies repliesEnabled\n  canManageAnnouncementCopies announcementCopiesEnabled\n  canManageCalls allowsIncomingCalls\n  devices { id type title deliveryDescription isEnabled isPendingConfirmation isPreferredVoiceCallsDevice } } }' \\\n| jq '.data.accountNotificationsScreen'\n```\n\nThe `canManage*` flags say what this account may change. On a subscriber (parent) account they\nare typically all `false` — the *device* toggles below still work.\n\n## Schema discovery\n\n```bash\n# root fields\nrq '{ __type(name:\"Query\"){ fields { name } } }'    | jq -r '.data.__type.fields[].name'\nrq '{ __type(name:\"Mutation\"){ fields { name } } }' | jq -r '.data.__type.fields[].name'\n# one type's fields, with types\nrq '{ __type(name:\"MessageItem\"){ fields { name type { name kind ofType { name } } } } }'\n# union / interface members — needed to write an inline fragment\nrq '{ __type(name:\"StreamItem\"){ possibleTypes { name } } }' | jq -r '.data.__type.possibleTypes[].name'\n```\n\n---\n\n## Mutations — read this first\n\nWrites hit real people and **cannot be unsent**. Nothing here is needed for reading Remind.\n\n**Verified live:** toggling notification devices, which affects only your own account.\n\n```bash\n# disable, then re-enable, a delivery device (id from accountNotificationsScreen)\nrq 'mutation($i:UpdateAccountNotificationsScreenInput!){ updateAccountNotificationsScreen(input:$i){ __typename } }' \\\n   '{\"i\":{\"devicesToDisable\":[<device-id>]}}'\nrq 'mutation($i:UpdateAccountNotificationsScreenInput!){ updateAccountNotificationsScreen(input:$i){ __typename } }' \\\n   '{\"i\":{\"devicesToEnable\":[<device-id>]}}'\n```\n\nA 200 is not proof — re-read `accountNotificationsScreen` and check `devices[].isEnabled` actually moved.\n\n**Shape only, NOT executed here** — sending a message. Check `permissions.canSend` first.\n\n```bash\nrq 'mutation($i:PutMessageInput!){ putMessage(input:$i){ error { __typename } messages { __typename } } }' \\\n   '{\"i\":{\"recipients\":[{\"type\":\"chat\",\"uuid\":\"<chat-uuid>\"}],\"message\":{\"body\":\"…\",\"urgent\":false}}}'\n```\n\n`recipients[].type` is `chat` for a conversation or `group` for a whole class.\n\n**Owner/teacher only:** `scheduleMessage`, `api_scheduledMessages` and `api_deleteScheduledMessage`\nreturn `Unauthorized` on a subscriber account even with a valid session.\n\nFile v1.2.2:skill-card.md\n\n## Description:\n\nHelps agents read Remind classes, chats, messages, and notification settings using a captured signed-in session and shell-based queries.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nRemind users and developers can use this skill to retrieve their account's classes, conversations, messages, and notification settings from a shell without running the remind-mcp server.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: A saved Remind session grants access to a signed-in account if exposed.\n\nMitigation: Keep the session file private, do not sync or share it, and delete it when finished.\n\nRisk: The included mutation examples can change notification settings or send messages from a live account.\n\nMitigation: Avoid mutation examples unless you intentionally want to make those changes or send messages.\n\n## Reference(s):\n\n- [Remind GraphQL query examples](artifact/references/graphql-queries.md)\n- [ContextMint Bridge releases (browser extension)](https://github.com/nullnet-app/contextmint-bridge/releases)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Guidance, Text]\n\n**Output Format:** [Markdown with shell and GraphQL examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Account data is returned by Remind when the suggested commands are run.]\n\n## Skill Version(s):\n\n1.2.2 (source: ClawHub release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.2.1: 4 files, 6003 bytes\n\nFiles: references/graphql-queries.md (5822b), skill-card.md (1837b), SKILL.md (3962b), _meta.json (129b)\n\nFile v1.2.1:SKILL.md\n\n---\nname: remind-fpx\ndescription: 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.\n---\n\n# Remind from the shell\n\nRemind's web app talks to a single **GraphQL endpoint**, `POST https://www.remind.com/graphql`.\nThat endpoint is reachable server-side — no bot wall — so only the *credential* needs the\nbrowser. Capture it once with `fpx`, then use `curl` for everything.\n\n**Introspection is enabled**, so the schema is self-documenting: you can discover any field\nwithout guessing (and you should — see the validation-error gotcha below).\n\n## Auth model\n\nTwo headers, both lifted from a signed-in tab:\n\n| Header | What it is |\n|---|---|\n| `cookie` | The full Cookie request header (includes HttpOnly session cookies) |\n| `x-csrf-token` | Value of the JS-readable `csrf_token` cookie |\n\nBoth are required. Only `x-csrf-token` works — `x-xsrf-token` and `csrf-token` are rejected.\nA named cookie read (`fpx cookies csrf_token`) returns `{}` on this site, so **header capture\nis the only route**.\n\n## One-time setup\n\nInstall `@fetchproxy/cli` (`npm i -g @fetchproxy/cli`) and the **Transporter** Chrome extension.\n\nDeclare the full scope **before** the first pairing — widening it later forces a re-pair:\n\n```bash\nfpx profile add remind --domain remind.com\nfpx profile declare remind \\\n  --capture-header cookie@www.remind.com \\\n  --capture-header x-csrf-token@www.remind.com\n```\n\nThen capture. The capture waits for the *next* request the page makes, so **load a Remind page\nwhile it waits**:\n\n```bash\nfpx session -p remind > ~/.remind-fpx-session.json &   # approve the pair code in Transporter\nopen \"https://www.remind.com/\"                          # feeds the capture\nwait\nchmod 600 ~/.remind-fpx-session.json\n```\n\n> Use a session file of your own like this one. Do **not** write into `~/.remind-mcp/` — that is\n> the MCP server's store, with a different schema; overwriting it corrupts its session.\n\n## The call pattern\n\n```bash\nrq() {  # rq '<query>' ['<variables json>']\n  local S=~/.remind-fpx-session.json\n  local body; body=$(Q=\"$1\" V=\"${2:-{\\}}\" node -e '\n    process.stdout.write(JSON.stringify({query:process.env.Q,variables:JSON.parse(process.env.V)}))')\n  curl -s -X POST https://www.remind.com/graphql \\\n    -H 'content-type: application/json' \\\n    -H \"cookie: $(node -e 'console.log(require(process.argv[1]).capturedHeaders.cookie)' \"$S\")\" \\\n    -H \"x-csrf-token: $(node -e 'console.log(require(process.argv[1]).capturedHeaders[\"x-csrf-token\"])' \"$S\")\" \\\n    --data-binary \"$body\"\n}\n\nrq '{ me { uuid first_name last_name email locale } }' | jq .data.me\n```\n\nStart from `navigationList` — it returns the uuids every other query needs.\nReady-to-run documents with `jq` recipes are in **`references/graphql-queries.md`**.\n\n## Gotchas\n\n- **An expired session returns HTTP 200**, with `errors[0].message == \"Unauthorized\"`. Check the\n  body, never the status. Re-run the capture above to refresh.\n- `\"Unauthorized\"` **also** means \"this account may not do that\" — scheduled messages and org-admin\n  queries are owner/teacher-only, and a subscriber (parent) account gets `Unauthorized` on them\n  with a perfectly valid session.\n- **An unknown field reports as `Internal service error` / `GRAPHQL_VALIDATION_FAILED` with HTTP\n  500 metadata**, not a readable \"no such field\". Do not guess field names — introspect:\n  ```bash\n  rq '{ __type(name:\"Class\"){ fields { name } } }' | jq -r '.data.__type.fields[].name'\n  ```\n- Check `permissions.canSend` before trying to send; most fields on a subscriber account are read-only.\n- **Writes deliver to real people and cannot be unsent.** This skill is read-oriented on purpose;\n  the mutation shapes live in the references file, clearly marked.\n\nFile v1.2.1:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"remind-mcp\",\n  \"version\": \"1.2.1\",\n  \"publishedAt\": 1790351809163\n}\n\nFile v1.2.1:references/graphql-queries.md\n\n# Remind GraphQL — ready-to-run documents\n\nEvery query below was executed against the live API. Field selections come from introspection;\nRemind reports an unknown field as `Internal service error` / `GRAPHQL_VALIDATION_FAILED`, so\nedit them only alongside a re-run.\n\nAll examples assume the `rq` helper from `SKILL.md`.\n\n---\n\n## Start here — what's in the account\n\n`navigationList` is the sidebar, and the source of every uuid you need.\n\n```bash\nrq '{ navigationList { cursor items { __typename\n  ... on TextHeader { text }\n  ... on NavigationListEntityItem { type uuid title subtitle isRostered unreadsCount } } } }'\n```\n\nJust the classes and chats, one per line:\n\n```bash\nrq '{ navigationList { items { __typename\n  ... on NavigationListEntityItem { type uuid title unreadsCount } } } }' \\\n| jq -r '.data.navigationList.items[]\n         | select(.__typename==\"NavigationListEntityItem\")\n         | \"\\(.type)\\t\\(.unreadsCount // 0)\\t\\(.uuid)\\t\\(.title)\"'\n```\n\nPage with `cursor`: pass it back as `lastCursor`.\n\n```bash\nrq 'query($c:String){ navigationList(lastCursor:$c){ cursor items { __typename } } }' '{\"c\":\"<cursor>\"}'\n```\n\n## Account\n\n```bash\nrq '{ me { uuid id first_name last_name display_name email locale is_admin is_child created_at sign_in_count } }' \\\n| jq .data.me\n```\n\n## Classes\n\n```bash\nrq 'query($u:[String!]){ classes(uuids:$u){\n  uuid id code name displayName type\n  messagesCount unreadMessagesCount subscribersCount membershipsCount ownersCount\n  createdAt archivedAt messageHistoryEnabled isMember joinUrl\n  subscriberInitiatedChats canEditInfo canAddOwners } }' \\\n  '{\"u\":[\"<class-uuid>\"]}' | jq '.data.classes[]'\n```\n\nClass members (paginate with `page`/`perPage`):\n\n```bash\nrq 'query($u:[String!]){ classes(uuids:$u){ members(perPage:50){ __typename } } }' '{\"u\":[\"<class-uuid>\"]}'\n```\n\n> Introspect `ClassMembership` before selecting fields:\n> `rq '{ __type(name:\"ClassMembership\"){ fields { name } } }' | jq -r '.data.__type.fields[].name'`\n\n## Chats\n\nList every conversation, with the permission that decides whether you can reply:\n\n```bash\nrq '{ chatStreams { uuid title properTitle streamType type state hidden\n  unreadMessagesCount membershipsCount updatedAt\n  permissions { canSend canAdd canClose canLeave canPhone } } }' \\\n| jq -r '.data.chatStreams[] | \"\\(.updatedAt)\\t\\(.unreadMessagesCount)\\t\\(.uuid)\\t\\(.title)\"'\n```\n\nFilter to one class (numeric `id` from `classes`, not the uuid) or search by name:\n\n```bash\nrq 'query($g:Int,$q:String){ chatStreams(groupId:$g, chatQuery:$q){ uuid title } }' '{\"g\":47615786,\"q\":null}'\n```\n\n## Messages\n\n```bash\nrq 'query($u:[String!],$n:Int){ chatStreams(chatUuids:$u){\n  uuid title unreadMessagesCount\n  sequenceItems(nonGapMessagesLimit:$n){ seq key item { __typename\n    ... on MessageItem { uuid body type createdAt sentAt urgent publicUrl\n                         sender { uuid name } files { __typename } }\n    ... on SystemMessageItem { uuid body createdAt }\n    ... on GapItem { uuid size } } } } }' \\\n  '{\"u\":[\"<chat-uuid>\"],\"n\":25}'\n```\n\nReadable transcript, oldest first:\n\n```bash\n… | jq -r '.data.chatStreams[].sequenceItems[]\n           | select(.item.__typename==\"MessageItem\")\n           | \"\\(.item.createdAt)  \\(.item.sender.name): \\(.item.body)\"'\n```\n\nItem types: **MessageItem** (a real message), **SystemMessageItem** (joins, stream creation),\n**GapItem** (`size` unloaded messages — raise `nonGapMessagesLimit` or page with\n`gapItemNextPageParams`).\n\n## Notification settings\n\n```bash\nrq '{ accountNotificationsScreen {\n  canManageReplies repliesEnabled\n  canManageAnnouncementCopies announcementCopiesEnabled\n  canManageCalls allowsIncomingCalls\n  devices { id type title deliveryDescription isEnabled isPendingConfirmation isPreferredVoiceCallsDevice } } }' \\\n| jq '.data.accountNotificationsScreen'\n```\n\nThe `canManage*` flags say what this account may change. On a subscriber (parent) account they\nare typically all `false` — the *device* toggles below still work.\n\n## Schema discovery\n\n```bash\n# root fields\nrq '{ __type(name:\"Query\"){ fields { name } } }'    | jq -r '.data.__type.fields[].name'\nrq '{ __type(name:\"Mutation\"){ fields { name } } }' | jq -r '.data.__type.fields[].name'\n# one type's fields, with types\nrq '{ __type(name:\"MessageItem\"){ fields { name type { name kind ofType { name } } } } }'\n# union / interface members — needed to write an inline fragment\nrq '{ __type(name:\"StreamItem\"){ possibleTypes { name } } }' | jq -r '.data.__type.possibleTypes[].name'\n```\n\n---\n\n## Mutations — read this first\n\nWrites hit real people and **cannot be unsent**. Nothing here is needed for reading Remind.\n\n**Verified live:** toggling notification devices, which affects only your own account.\n\n```bash\n# disable, then re-enable, a delivery device (id from accountNotificationsScreen)\nrq 'mutation($i:UpdateAccountNotificationsScreenInput!){ updateAccountNotificationsScreen(input:$i){ __typename } }' \\\n   '{\"i\":{\"devicesToDisable\":[<device-id>]}}'\nrq 'mutation($i:UpdateAccountNotificationsScreenInput!){ updateAccountNotificationsScreen(input:$i){ __typename } }' \\\n   '{\"i\":{\"devicesToEnable\":[<device-id>]}}'\n```\n\nA 200 is not proof — re-read `accountNotificationsScreen` and check `devices[].isEnabled` actually moved.\n\n**Shape only, NOT executed here** — sending a message. Check `permissions.canSend` first.\n\n```bash\nrq 'mutation($i:PutMessageInput!){ putMessage(input:$i){ error { __typename } messages { __typename } } }' \\\n   '{\"i\":{\"recipients\":[{\"type\":\"chat\",\"uuid\":\"<chat-uuid>\"}],\"message\":{\"body\":\"…\",\"urgent\":false}}}'\n```\n\n`recipients[].type` is `chat` for a conversation or `group` for a whole class.\n\n**Owner/teacher only:** `scheduleMessage`, `api_scheduledMessages` and `api_deleteScheduledMessage`\nreturn `Unauthorized` on a subscriber account even with a valid session.\n\nFile v1.2.1:skill-card.md\n\n## Description:\n\nGuides agents in reading Remind classes, chats, messages, and notification settings through shell-based GraphQL queries using a captured signed-in session.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and other Remind users can ask an agent to retrieve account, class, conversation, message, and notification information without running the Remind MCP server.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The captured Remind session can be reused to access the account.\n\nMitigation: Treat the session file like a password, restrict its access, and delete it when finished.\n\nRisk: Bundled GraphQL mutations can change settings or send messages to real people.\n\nMitigation: Supervise GraphQL calls and avoid mutation examples unless a specific change or message is explicitly intended.\n\n## Reference(s):\n\n- [Remind GraphQL query examples](references/graphql-queries.md)\n- [ClawHub release page](https://clawhub.ai/chrischall/skills/remind-mcp)\n- [Remind GraphQL endpoint](https://www.remind.com/graphql)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Configuration guidance, Text]\n\n**Output Format:** [Markdown with shell and GraphQL examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Read-oriented queries; the references also include write-capable mutation examples.]\n\n## Skill Version(s):\n\n1.2.1 (source: server-resolved release evidence)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.2.0: 4 files, 6148 bytes\n\nFiles: references/graphql-queries.md (5822b), skill-card.md (2170b), SKILL.md (3962b), _meta.json (129b)\n\nFile v1.2.0:SKILL.md\n\n---\nname: remind-fpx\ndescription: 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.\n---\n\n# Remind from the shell\n\nRemind's web app talks to a single **GraphQL endpoint**, `POST https://www.remind.com/graphql`.\nThat endpoint is reachable server-side — no bot wall — so only the *credential* needs the\nbrowser. Capture it once with `fpx`, then use `curl` for everything.\n\n**Introspection is enabled**, so the schema is self-documenting: you can discover any field\nwithout guessing (and you should — see the validation-error gotcha below).\n\n## Auth model\n\nTwo headers, both lifted from a signed-in tab:\n\n| Header | What it is |\n|---|---|\n| `cookie` | The full Cookie request header (includes HttpOnly session cookies) |\n| `x-csrf-token` | Value of the JS-readable `csrf_token` cookie |\n\nBoth are required. Only `x-csrf-token` works — `x-xsrf-token` and `csrf-token` are rejected.\nA named cookie read (`fpx cookies csrf_token`) returns `{}` on this site, so **header capture\nis the only route**.\n\n## One-time setup\n\nInstall `@fetchproxy/cli` (`npm i -g @fetchproxy/cli`) and the **Transporter** Chrome extension.\n\nDeclare the full scope **before** the first pairing — widening it later forces a re-pair:\n\n```bash\nfpx profile add remind --domain remind.com\nfpx profile declare remind \\\n  --capture-header cookie@www.remind.com \\\n  --capture-header x-csrf-token@www.remind.com\n```\n\nThen capture. The capture waits for the *next* request the page makes, so **load a Remind page\nwhile it waits**:\n\n```bash\nfpx session -p remind > ~/.remind-fpx-session.json &   # approve the pair code in Transporter\nopen \"https://www.remind.com/\"                          # feeds the capture\nwait\nchmod 600 ~/.remind-fpx-session.json\n```\n\n> Use a session file of your own like this one. Do **not** write into `~/.remind-mcp/` — that is\n> the MCP server's store, with a different schema; overwriting it corrupts its session.\n\n## The call pattern\n\n```bash\nrq() {  # rq '<query>' ['<variables json>']\n  local S=~/.remind-fpx-session.json\n  local body; body=$(Q=\"$1\" V=\"${2:-{\\}}\" node -e '\n    process.stdout.write(JSON.stringify({query:process.env.Q,variables:JSON.parse(process.env.V)}))')\n  curl -s -X POST https://www.remind.com/graphql \\\n    -H 'content-type: application/json' \\\n    -H \"cookie: $(node -e 'console.log(require(process.argv[1]).capturedHeaders.cookie)' \"$S\")\" \\\n    -H \"x-csrf-token: $(node -e 'console.log(require(process.argv[1]).capturedHeaders[\"x-csrf-token\"])' \"$S\")\" \\\n    --data-binary \"$body\"\n}\n\nrq '{ me { uuid first_name last_name email locale } }' | jq .data.me\n```\n\nStart from `navigationList` — it returns the uuids every other query needs.\nReady-to-run documents with `jq` recipes are in **`references/graphql-queries.md`**.\n\n## Gotchas\n\n- **An expired session returns HTTP 200**, with `errors[0].message == \"Unauthorized\"`. Check the\n  body, never the status. Re-run the capture above to refresh.\n- `\"Unauthorized\"` **also** means \"this account may not do that\" — scheduled messages and org-admin\n  queries are owner/teacher-only, and a subscriber (parent) account gets `Unauthorized` on them\n  with a perfectly valid session.\n- **An unknown field reports as `Internal service error` / `GRAPHQL_VALIDATION_FAILED` with HTTP\n  500 metadata**, not a readable \"no such field\". Do not guess field names — introspect:\n  ```bash\n  rq '{ __type(name:\"Class\"){ fields { name } } }' | jq -r '.data.__type.fields[].name'\n  ```\n- Check `permissions.canSend` before trying to send; most fields on a subscriber account are read-only.\n- **Writes deliver to real people and cannot be unsent.** This skill is read-oriented on purpose;\n  the mutation shapes live in the references file, clearly marked.\n\nFile v1.2.0:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"remind-mcp\",\n  \"version\": \"1.2.0\",\n  \"publishedAt\": 1790262707032\n}\n\nFile v1.2.0:references/graphql-queries.md\n\n# Remind GraphQL — ready-to-run documents\n\nEvery query below was executed against the live API. Field selections come from introspection;\nRemind reports an unknown field as `Internal service error` / `GRAPHQL_VALIDATION_FAILED`, so\nedit them only alongside a re-run.\n\nAll examples assume the `rq` helper from `SKILL.md`.\n\n---\n\n## Start here — what's in the account\n\n`navigationList` is the sidebar, and the source of every uuid you need.\n\n```bash\nrq '{ navigationList { cursor items { __typename\n  ... on TextHeader { text }\n  ... on NavigationListEntityItem { type uuid title subtitle isRostered unreadsCount } } } }'\n```\n\nJust the classes and chats, one per line:\n\n```bash\nrq '{ navigationList { items { __typename\n  ... on NavigationListEntityItem { type uuid title unreadsCount } } } }' \\\n| jq -r '.data.navigationList.items[]\n         | select(.__typename==\"NavigationListEntityItem\")\n         | \"\\(.type)\\t\\(.unreadsCount // 0)\\t\\(.uuid)\\t\\(.title)\"'\n```\n\nPage with `cursor`: pass it back as `lastCursor`.\n\n```bash\nrq 'query($c:String){ navigationList(lastCursor:$c){ cursor items { __typename } } }' '{\"c\":\"<cursor>\"}'\n```\n\n## Account\n\n```bash\nrq '{ me { uuid id first_name last_name display_name email locale is_admin is_child created_at sign_in_count } }' \\\n| jq .data.me\n```\n\n## Classes\n\n```bash\nrq 'query($u:[String!]){ classes(uuids:$u){\n  uuid id code name displayName type\n  messagesCount unreadMessagesCount subscribersCount membershipsCount ownersCount\n  createdAt archivedAt messageHistoryEnabled isMember joinUrl\n  subscriberInitiatedChats canEditInfo canAddOwners } }' \\\n  '{\"u\":[\"<class-uuid>\"]}' | jq '.data.classes[]'\n```\n\nClass members (paginate with `page`/`perPage`):\n\n```bash\nrq 'query($u:[String!]){ classes(uuids:$u){ members(perPage:50){ __typename } } }' '{\"u\":[\"<class-uuid>\"]}'\n```\n\n> Introspect `ClassMembership` before selecting fields:\n> `rq '{ __type(name:\"ClassMembership\"){ fields { name } } }' | jq -r '.data.__type.fields[].name'`\n\n## Chats\n\nList every conversation, with the permission that decides whether you can reply:\n\n```bash\nrq '{ chatStreams { uuid title properTitle streamType type state hidden\n  unreadMessagesCount membershipsCount updatedAt\n  permissions { canSend canAdd canClose canLeave canPhone } } }' \\\n| jq -r '.data.chatStreams[] | \"\\(.updatedAt)\\t\\(.unreadMessagesCount)\\t\\(.uuid)\\t\\(.title)\"'\n```\n\nFilter to one class (numeric `id` from `classes`, not the uuid) or search by name:\n\n```bash\nrq 'query($g:Int,$q:String){ chatStreams(groupId:$g, chatQuery:$q){ uuid title } }' '{\"g\":47615786,\"q\":null}'\n```\n\n## Messages\n\n```bash\nrq 'query($u:[String!],$n:Int){ chatStreams(chatUuids:$u){\n  uuid title unreadMessagesCount\n  sequenceItems(nonGapMessagesLimit:$n){ seq key item { __typename\n    ... on MessageItem { uuid body type createdAt sentAt urgent publicUrl\n                         sender { uuid name } files { __typename } }\n    ... on SystemMessageItem { uuid body createdAt }\n    ... on GapItem { uuid size } } } } }' \\\n  '{\"u\":[\"<chat-uuid>\"],\"n\":25}'\n```\n\nReadable transcript, oldest first:\n\n```bash\n… | jq -r '.data.chatStreams[].sequenceItems[]\n           | select(.item.__typename==\"MessageItem\")\n           | \"\\(.item.createdAt)  \\(.item.sender.name): \\(.item.body)\"'\n```\n\nItem types: **MessageItem** (a real message), **SystemMessageItem** (joins, stream creation),\n**GapItem** (`size` unloaded messages — raise `nonGapMessagesLimit` or page with\n`gapItemNextPageParams`).\n\n## Notification settings\n\n```bash\nrq '{ accountNotificationsScreen {\n  canManageReplies repliesEnabled\n  canManageAnnouncementCopies announcementCopiesEnabled\n  canManageCalls allowsIncomingCalls\n  devices { id type title deliveryDescription isEnabled isPendingConfirmation isPreferredVoiceCallsDevice } } }' \\\n| jq '.data.accountNotificationsScreen'\n```\n\nThe `canManage*` flags say what this account may change. On a subscriber (parent) account they\nare typically all `false` — the *device* toggles below still work.\n\n## Schema discovery\n\n```bash\n# root fields\nrq '{ __type(name:\"Query\"){ fields { name } } }'    | jq -r '.data.__type.fields[].name'\nrq '{ __type(name:\"Mutation\"){ fields { name } } }' | jq -r '.data.__type.fields[].name'\n# one type's fields, with types\nrq '{ __type(name:\"MessageItem\"){ fields { name type { name kind ofType { name } } } } }'\n# union / interface members — needed to write an inline fragment\nrq '{ __type(name:\"StreamItem\"){ possibleTypes { name } } }' | jq -r '.data.__type.possibleTypes[].name'\n```\n\n---\n\n## Mutations — read this first\n\nWrites hit real people and **cannot be unsent**. Nothing here is needed for reading Remind.\n\n**Verified live:** toggling notification devices, which affects only your own account.\n\n```bash\n# disable, then re-enable, a delivery device (id from accountNotificationsScreen)\nrq 'mutation($i:UpdateAccountNotificationsScreenInput!){ updateAccountNotificationsScreen(input:$i){ __typename } }' \\\n   '{\"i\":{\"devicesToDisable\":[<device-id>]}}'\nrq 'mutation($i:UpdateAccountNotificationsScreenInput!){ updateAccountNotificationsScreen(input:$i){ __typename } }' \\\n   '{\"i\":{\"devicesToEnable\":[<device-id>]}}'\n```\n\nA 200 is not proof — re-read `accountNotificationsScreen` and check `devices[].isEnabled` actually moved.\n\n**Shape only, NOT executed here** — sending a message. Check `permissions.canSend` first.\n\n```bash\nrq 'mutation($i:PutMessageInput!){ putMessage(input:$i){ error { __typename } messages { __typename } } }' \\\n   '{\"i\":{\"recipients\":[{\"type\":\"chat\",\"uuid\":\"<chat-uuid>\"}],\"message\":{\"body\":\"…\",\"urgent\":false}}}'\n```\n\n`recipients[].type` is `chat` for a conversation or `group` for a whole class.\n\n**Owner/teacher only:** `scheduleMessage`, `api_scheduledMessages` and `api_deleteScheduledMessage`\nreturn `Unauthorized` on a subscriber account even with a valid session.\n\nFile v1.2.0:skill-card.md\n\n## Description:\n\nRead Remind classes, chats, messages, and notification settings from a shell by capturing a signed-in browser session with fpx and querying Remind's GraphQL API with curl.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and technically inclined Remind users use this skill to inspect account, class, chat, message, and notification data from shell scripts without running the Remind MCP server. The skill is read-oriented, with mutation examples separated for deliberate account changes only.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: A reusable logged-in Remind session is stored locally and can expose account data if copied, logged, or shared.\n\nMitigation: Keep ~/.remind-fpx-session.json private, restrict file permissions, avoid pasting it into chats or logs, and delete or refresh it when it is no longer needed.\n\nRisk: Mutation examples can change notification settings or send messages; writes can affect real recipients and cannot be unsent.\n\nMitigation: Use read queries by default, run mutation examples only when an account owner explicitly intends the change, check permissions such as canSend first, and re-read state after notification changes.\n\n## Reference(s):\n\n- [Remind GraphQL ready-to-run documents](references/graphql-queries.md)\n- [Remind GraphQL endpoint](https://www.remind.com/graphql)\n\n## Skill Output:\n\n**Output Type(s):** [text, markdown, shell commands, configuration, guidance]\n\n**Output Format:** [Markdown with inline shell commands and GraphQL examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Requires a locally captured Remind browser session and curl-compatible GraphQL requests.]\n\n## Skill Version(s):\n\n1.2.0 (source: server release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.1.3: 4 files, 6190 bytes\n\nFiles: references/graphql-queries.md (5822b), skill-card.md (2314b), SKILL.md (3962b), _meta.json (129b)\n\nFile v1.1.3:SKILL.md\n\n---\nname: remind-fpx\ndescription: 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.\n---\n\n# Remind from the shell\n\nRemind's web app talks to a single **GraphQL endpoint**, `POST https://www.remind.com/graphql`.\nThat endpoint is reachable server-side — no bot wall — so only the *credential* needs the\nbrowser. Capture it once with `fpx`, then use `curl` for everything.\n\n**Introspection is enabled**, so the schema is self-documenting: you can discover any field\nwithout guessing (and you should — see the validation-error gotcha below).\n\n## Auth model\n\nTwo headers, both lifted from a signed-in tab:\n\n| Header | What it is |\n|---|---|\n| `cookie` | The full Cookie request header (includes HttpOnly session cookies) |\n| `x-csrf-token` | Value of the JS-readable `csrf_token` cookie |\n\nBoth are required. Only `x-csrf-token` works — `x-xsrf-token` and `csrf-token` are rejected.\nA named cookie read (`fpx cookies csrf_token`) returns `{}` on this site, so **header capture\nis the only route**.\n\n## One-time setup\n\nInstall `@fetchproxy/cli` (`npm i -g @fetchproxy/cli`) and the **Transporter** Chrome extension.\n\nDeclare the full scope **before** the first pairing — widening it later forces a re-pair:\n\n```bash\nfpx profile add remind --domain remind.com\nfpx profile declare remind \\\n  --capture-header cookie@www.remind.com \\\n  --capture-header x-csrf-token@www.remind.com\n```\n\nThen capture. The capture waits for the *next* request the page makes, so **load a Remind page\nwhile it waits**:\n\n```bash\nfpx session -p remind > ~/.remind-fpx-session.json &   # approve the pair code in Transporter\nopen \"https://www.remind.com/\"                          # feeds the capture\nwait\nchmod 600 ~/.remind-fpx-session.json\n```\n\n> Use a session file of your own like this one. Do **not** write into `~/.remind-mcp/` — that is\n> the MCP server's store, with a different schema; overwriting it corrupts its session.\n\n## The call pattern\n\n```bash\nrq() {  # rq '<query>' ['<variables json>']\n  local S=~/.remind-fpx-session.json\n  local body; body=$(Q=\"$1\" V=\"${2:-{\\}}\" node -e '\n    process.stdout.write(JSON.stringify({query:process.env.Q,variables:JSON.parse(process.env.V)}))')\n  curl -s -X POST https://www.remind.com/graphql \\\n    -H 'content-type: application/json' \\\n    -H \"cookie: $(node -e 'console.log(require(process.argv[1]).capturedHeaders.cookie)' \"$S\")\" \\\n    -H \"x-csrf-token: $(node -e 'console.log(require(process.argv[1]).capturedHeaders[\"x-csrf-token\"])' \"$S\")\" \\\n    --data-binary \"$body\"\n}\n\nrq '{ me { uuid first_name last_name email locale } }' | jq .data.me\n```\n\nStart from `navigationList` — it returns the uuids every other query needs.\nReady-to-run documents with `jq` recipes are in **`references/graphql-queries.md`**.\n\n## Gotchas\n\n- **An expired session returns HTTP 200**, with `errors[0].message == \"Unauthorized\"`. Check the\n  body, never the status. Re-run the capture above to refresh.\n- `\"Unauthorized\"` **also** means \"this account may not do that\" — scheduled messages and org-admin\n  queries are owner/teacher-only, and a subscriber (parent) account gets `Unauthorized` on them\n  with a perfectly valid session.\n- **An unknown field reports as `Internal service error` / `GRAPHQL_VALIDATION_FAILED` with HTTP\n  500 metadata**, not a readable \"no such field\". Do not guess field names — introspect:\n  ```bash\n  rq '{ __type(name:\"Class\"){ fields { name } } }' | jq -r '.data.__type.fields[].name'\n  ```\n- Check `permissions.canSend` before trying to send; most fields on a subscriber account are read-only.\n- **Writes deliver to real people and cannot be unsent.** This skill is read-oriented on purpose;\n  the mutation shapes live in the references file, clearly marked.\n\nFile v1.1.3:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"remind-mcp\",\n  \"version\": \"1.1.3\",\n  \"publishedAt\": 1790199759893\n}\n\nFile v1.1.3:references/graphql-queries.md\n\n# Remind GraphQL — ready-to-run documents\n\nEvery query below was executed against the live API. Field selections come from introspection;\nRemind reports an unknown field as `Internal service error` / `GRAPHQL_VALIDATION_FAILED`, so\nedit them only alongside a re-run.\n\nAll examples assume the `rq` helper from `SKILL.md`.\n\n---\n\n## Start here — what's in the account\n\n`navigationList` is the sidebar, and the source of every uuid you need.\n\n```bash\nrq '{ navigationList { cursor items { __typename\n  ... on TextHeader { text }\n  ... on NavigationListEntityItem { type uuid title subtitle isRostered unreadsCount } } } }'\n```\n\nJust the classes and chats, one per line:\n\n```bash\nrq '{ navigationList { items { __typename\n  ... on NavigationListEntityItem { type uuid title unreadsCount } } } }' \\\n| jq -r '.data.navigationList.items[]\n         | select(.__typename==\"NavigationListEntityItem\")\n         | \"\\(.type)\\t\\(.unreadsCount // 0)\\t\\(.uuid)\\t\\(.title)\"'\n```\n\nPage with `cursor`: pass it back as `lastCursor`.\n\n```bash\nrq 'query($c:String){ navigationList(lastCursor:$c){ cursor items { __typename } } }' '{\"c\":\"<cursor>\"}'\n```\n\n## Account\n\n```bash\nrq '{ me { uuid id first_name last_name display_name email locale is_admin is_child created_at sign_in_count } }' \\\n| jq .data.me\n```\n\n## Classes\n\n```bash\nrq 'query($u:[String!]){ classes(uuids:$u){\n  uuid id code name displayName type\n  messagesCount unreadMessagesCount subscribersCount membershipsCount ownersCount\n  createdAt archivedAt messageHistoryEnabled isMember joinUrl\n  subscriberInitiatedChats canEditInfo canAddOwners } }' \\\n  '{\"u\":[\"<class-uuid>\"]}' | jq '.data.classes[]'\n```\n\nClass members (paginate with `page`/`perPage`):\n\n```bash\nrq 'query($u:[String!]){ classes(uuids:$u){ members(perPage:50){ __typename } } }' '{\"u\":[\"<class-uuid>\"]}'\n```\n\n> Introspect `ClassMembership` before selecting fields:\n> `rq '{ __type(name:\"ClassMembership\"){ fields { name } } }' | jq -r '.data.__type.fields[].name'`\n\n## Chats\n\nList every conversation, with the permission that decides whether you can reply:\n\n```bash\nrq '{ chatStreams { uuid title properTitle streamType type state hidden\n  unreadMessagesCount membershipsCount updatedAt\n  permissions { canSend canAdd canClose canLeave canPhone } } }' \\\n| jq -r '.data.chatStreams[] | \"\\(.updatedAt)\\t\\(.unreadMessagesCount)\\t\\(.uuid)\\t\\(.title)\"'\n```\n\nFilter to one class (numeric `id` from `classes`, not the uuid) or search by name:\n\n```bash\nrq 'query($g:Int,$q:String){ chatStreams(groupId:$g, chatQuery:$q){ uuid title } }' '{\"g\":47615786,\"q\":null}'\n```\n\n## Messages\n\n```bash\nrq 'query($u:[String!],$n:Int){ chatStreams(chatUuids:$u){\n  uuid title unreadMessagesCount\n  sequenceItems(nonGapMessagesLimit:$n){ seq key item { __typename\n    ... on MessageItem { uuid body type createdAt sentAt urgent publicUrl\n                         sender { uuid name } files { __typename } }\n    ... on SystemMessageItem { uuid body createdAt }\n    ... on GapItem { uuid size } } } } }' \\\n  '{\"u\":[\"<chat-uuid>\"],\"n\":25}'\n```\n\nReadable transcript, oldest first:\n\n```bash\n… | jq -r '.data.chatStreams[].sequenceItems[]\n           | select(.item.__typename==\"MessageItem\")\n           | \"\\(.item.createdAt)  \\(.item.sender.name): \\(.item.body)\"'\n```\n\nItem types: **MessageItem** (a real message), **SystemMessageItem** (joins, stream creation),\n**GapItem** (`size` unloaded messages — raise `nonGapMessagesLimit` or page with\n`gapItemNextPageParams`).\n\n## Notification settings\n\n```bash\nrq '{ accountNotificationsScreen {\n  canManageReplies repliesEnabled\n  canManageAnnouncementCopies announcementCopiesEnabled\n  canManageCalls allowsIncomingCalls\n  devices { id type title deliveryDescription isEnabled isPendingConfirmation isPreferredVoiceCallsDevice } } }' \\\n| jq '.data.accountNotificationsScreen'\n```\n\nThe `canManage*` flags say what this account may change. On a subscriber (parent) account they\nare typically all `false` — the *device* toggles below still work.\n\n## Schema discovery\n\n```bash\n# root fields\nrq '{ __type(name:\"Query\"){ fields { name } } }'    | jq -r '.data.__type.fields[].name'\nrq '{ __type(name:\"Mutation\"){ fields { name } } }' | jq -r '.data.__type.fields[].name'\n# one type's fields, with types\nrq '{ __type(name:\"MessageItem\"){ fields { name type { name kind ofType { name } } } } }'\n# union / interface members — needed to write an inline fragment\nrq '{ __type(name:\"StreamItem\"){ possibleTypes { name } } }' | jq -r '.data.__type.possibleTypes[].name'\n```\n\n---\n\n## Mutations — read this first\n\nWrites hit real people and **cannot be unsent**. Nothing here is needed for reading Remind.\n\n**Verified live:** toggling notification devices, which affects only your own account.\n\n```bash\n# disable, then re-enable, a delivery device (id from accountNotificationsScreen)\nrq 'mutation($i:UpdateAccountNotificationsScreenInput!){ updateAccountNotificationsScreen(input:$i){ __typename } }' \\\n   '{\"i\":{\"devicesToDisable\":[<device-id>]}}'\nrq 'mutation($i:UpdateAccountNotificationsScreenInput!){ updateAccountNotificationsScreen(input:$i){ __typename } }' \\\n   '{\"i\":{\"devicesToEnable\":[<device-id>]}}'\n```\n\nA 200 is not proof — re-read `accountNotificationsScreen` and check `devices[].isEnabled` actually moved.\n\n**Shape only, NOT executed here** — sending a message. Check `permissions.canSend` first.\n\n```bash\nrq 'mutation($i:PutMessageInput!){ putMessage(input:$i){ error { __typename } messages { __typename } } }' \\\n   '{\"i\":{\"recipients\":[{\"type\":\"chat\",\"uuid\":\"<chat-uuid>\"}],\"message\":{\"body\":\"…\",\"urgent\":false}}}'\n```\n\n`recipients[].type` is `chat` for a conversation or `group` for a whole class.\n\n**Owner/teacher only:** `scheduleMessage`, `api_scheduledMessages` and `api_deleteScheduledMessage`\nreturn `Unauthorized` on a subscriber account even with a valid session.\n\nFile v1.1.3:skill-card.md\n\n## Description:\n\nremind-mcp helps agents read Remind classes, chats, messages, and notification settings from a shell by capturing a signed-in browser session and querying Remind's GraphQL API with curl.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and agents use this skill to retrieve Remind account, class, chat, message, and notification data through documented shell commands, mainly for one-shot queries or scripts without running the MCP server.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The skill documents live write-capable GraphQL mutations while presenting itself as read-oriented.\n\nMitigation: Review before installing if read-only Remind access is required, and avoid mutation examples unless deliberately changing account settings or sending messages.\n\nRisk: Captured Remind session headers can grant account access if exposed.\n\nMitigation: Treat the saved session file like a password, restrict file permissions, remove it when finished, and avoid storing it in shared locations.\n\nRisk: Message-sending mutations can contact real recipients and cannot be unsent.\n\nMitigation: Check permissions such as canSend first and use write examples only with explicit intent and an account authorized for that action.\n\n## Reference(s):\n\n- [Remind GraphQL ready-to-run documents](references/graphql-queries.md)\n- [Remind GraphQL endpoint](https://www.remind.com/graphql)\n- [Remind web app](https://www.remind.com/)\n\n## Skill Output:\n\n**Output Type(s):** [text, markdown, shell commands, configuration, guidance]\n\n**Output Format:** [Markdown guidance with shell commands and GraphQL/curl examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Includes jq recipes and cautions for session handling, authorization failures, introspection, and write-capable mutations.]\n\n## Skill Version(s):\n\n1.1.3 (source: server-resolved release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.1.2: 4 files, 6104 bytes\n\nFiles: references/graphql-queries.md (5822b), skill-card.md (2140b), SKILL.md (3962b), _meta.json (129b)\n\nFile v1.1.2:SKILL.md\n\n---\nname: remind-fpx\ndescription: 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.\n---\n\n# Remind from the shell\n\nRemind's web app talks to a single **GraphQL endpoint**, `POST https://www.remind.com/graphql`.\nThat endpoint is reachable server-side — no bot wall — so only the *credential* needs the\nbrowser. Capture it once with `fpx`, then use `curl` for everything.\n\n**Introspection is enabled**, so the schema is self-documenting: you can discover any field\nwithout guessing (and you should — see the validation-error gotcha below).\n\n## Auth model\n\nTwo headers, both lifted from a signed-in tab:\n\n| Header | What it is |\n|---|---|\n| `cookie` | The full Cookie request header (includes HttpOnly session cookies) |\n| `x-csrf-token` | Value of the JS-readable `csrf_token` cookie |\n\nBoth are required. Only `x-csrf-token` works — `x-xsrf-token` and `csrf-token` are rejected.\nA named cookie read (`fpx cookies csrf_token`) returns `{}` on this site, so **header capture\nis the only route**.\n\n## One-time setup\n\nInstall `@fetchproxy/cli` (`npm i -g @fetchproxy/cli`) and the **Transporter** Chrome extension.\n\nDeclare the full scope **before** the first pairing — widening it later forces a re-pair:\n\n```bash\nfpx profile add remind --domain remind.com\nfpx profile declare remind \\\n  --capture-header cookie@www.remind.com \\\n  --capture-header x-csrf-token@www.remind.com\n```\n\nThen capture. The capture waits for the *next* request the page makes, so **load a Remind page\nwhile it waits**:\n\n```bash\nfpx session -p remind > ~/.remind-fpx-session.json &   # approve the pair code in Transporter\nopen \"https://www.remind.com/\"                          # feeds the capture\nwait\nchmod 600 ~/.remind-fpx-session.json\n```\n\n> Use a session file of your own like this one. Do **not** write into `~/.remind-mcp/` — that is\n> the MCP server's store, with a different schema; overwriting it corrupts its session.\n\n## The call pattern\n\n```bash\nrq() {  # rq '<query>' ['<variables json>']\n  local S=~/.remind-fpx-session.json\n  local body; body=$(Q=\"$1\" V=\"${2:-{\\}}\" node -e '\n    process.stdout.write(JSON.stringify({query:process.env.Q,variables:JSON.parse(process.env.V)}))')\n  curl -s -X POST https://www.remind.com/graphql \\\n    -H 'content-type: application/json' \\\n    -H \"cookie: $(node -e 'console.log(require(process.argv[1]).capturedHeaders.cookie)' \"$S\")\" \\\n    -H \"x-csrf-token: $(node -e 'console.log(require(process.argv[1]).capturedHeaders[\"x-csrf-token\"])' \"$S\")\" \\\n    --data-binary \"$body\"\n}\n\nrq '{ me { uuid first_name last_name email locale } }' | jq .data.me\n```\n\nStart from `navigationList` — it returns the uuids every other query needs.\nReady-to-run documents with `jq` recipes are in **`references/graphql-queries.md`**.\n\n## Gotchas\n\n- **An expired session returns HTTP 200**, with `errors[0].message == \"Unauthorized\"`. Check the\n  body, never the status. Re-run the capture above to refresh.\n- `\"Unauthorized\"` **also** means \"this account may not do that\" — scheduled messages and org-admin\n  queries are owner/teacher-only, and a subscriber (parent) account gets `Unauthorized` on them\n  with a perfectly valid session.\n- **An unknown field reports as `Internal service error` / `GRAPHQL_VALIDATION_FAILED` with HTTP\n  500 metadata**, not a readable \"no such field\". Do not guess field names — introspect:\n  ```bash\n  rq '{ __type(name:\"Class\"){ fields { name } } }' | jq -r '.data.__type.fields[].name'\n  ```\n- Check `permissions.canSend` before trying to send; most fields on a subscriber account are read-only.\n- **Writes deliver to real people and cannot be unsent.** This skill is read-oriented on purpose;\n  the mutation shapes live in the references file, clearly marked.\n\nFile v1.1.2:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"remind-mcp\",\n  \"version\": \"1.1.2\",\n  \"publishedAt\": 1790178297723\n}\n\nFile v1.1.2:references/graphql-queries.md\n\n# Remind GraphQL — ready-to-run documents\n\nEvery query below was executed against the live API. Field selections come from introspection;\nRemind reports an unknown field as `Internal service error` / `GRAPHQL_VALIDATION_FAILED`, so\nedit them only alongside a re-run.\n\nAll examples assume the `rq` helper from `SKILL.md`.\n\n---\n\n## Start here — what's in the account\n\n`navigationList` is the sidebar, and the source of every uuid you need.\n\n```bash\nrq '{ navigationList { cursor items { __typename\n  ... on TextHeader { text }\n  ... on NavigationListEntityItem { type uuid title subtitle isRostered unreadsCount } } } }'\n```\n\nJust the classes and chats, one per line:\n\n```bash\nrq '{ navigationList { items { __typename\n  ... on NavigationListEntityItem { type uuid title unreadsCount } } } }' \\\n| jq -r '.data.navigationList.items[]\n         | select(.__typename==\"NavigationListEntityItem\")\n         | \"\\(.type)\\t\\(.unreadsCount // 0)\\t\\(.uuid)\\t\\(.title)\"'\n```\n\nPage with `cursor`: pass it back as `lastCursor`.\n\n```bash\nrq 'query($c:String){ navigationList(lastCursor:$c){ cursor items { __typename } } }' '{\"c\":\"<cursor>\"}'\n```\n\n## Account\n\n```bash\nrq '{ me { uuid id first_name last_name display_name email locale is_admin is_child created_at sign_in_count } }' \\\n| jq .data.me\n```\n\n## Classes\n\n```bash\nrq 'query($u:[String!]){ classes(uuids:$u){\n  uuid id code name displayName type\n  messagesCount unreadMessagesCount subscribersCount membershipsCount ownersCount\n  createdAt archivedAt messageHistoryEnabled isMember joinUrl\n  subscriberInitiatedChats canEditInfo canAddOwners } }' \\\n  '{\"u\":[\"<class-uuid>\"]}' | jq '.data.classes[]'\n```\n\nClass members (paginate with `page`/`perPage`):\n\n```bash\nrq 'query($u:[String!]){ classes(uuids:$u){ members(perPage:50){ __typename } } }' '{\"u\":[\"<class-uuid>\"]}'\n```\n\n> Introspect `ClassMembership` before selecting fields:\n> `rq '{ __type(name:\"ClassMembership\"){ fields { name } } }' | jq -r '.data.__type.fields[].name'`\n\n## Chats\n\nList every conversation, with the permission that decides whether you can reply:\n\n```bash\nrq '{ chatStreams { uuid title properTitle streamType type state hidden\n  unreadMessagesCount membershipsCount updatedAt\n  permissions { canSend canAdd canClose canLeave canPhone } } }' \\\n| jq -r '.data.chatStreams[] | \"\\(.updatedAt)\\t\\(.unreadMessagesCount)\\t\\(.uuid)\\t\\(.title)\"'\n```\n\nFilter to one class (numeric `id` from `classes`, not the uuid) or search by name:\n\n```bash\nrq 'query($g:Int,$q:String){ chatStreams(groupId:$g, chatQuery:$q){ uuid title } }' '{\"g\":47615786,\"q\":null}'\n```\n\n## Messages\n\n```bash\nrq 'query($u:[String!],$n:Int){ chatStreams(chatUuids:$u){\n  uuid title unreadMessagesCount\n  sequenceItems(nonGapMessagesLimit:$n){ seq key item { __typename\n    ... on MessageItem { uuid body type createdAt sentAt urgent publicUrl\n                         sender { uuid name } files { __typename } }\n    ... on SystemMessageItem { uuid body createdAt }\n    ... on GapItem { uuid size } } } } }' \\\n  '{\"u\":[\"<chat-uuid>\"],\"n\":25}'\n```\n\nReadable transcript, oldest first:\n\n```bash\n… | jq -r '.data.chatStreams[].sequenceItems[]\n           | select(.item.__typename==\"MessageItem\")\n           | \"\\(.item.createdAt)  \\(.item.sender.name): \\(.item.body)\"'\n```\n\nItem types: **MessageItem** (a real message), **SystemMessageItem** (joins, stream creation),\n**GapItem** (`size` unloaded messages — raise `nonGapMessagesLimit` or page with\n`gapItemNextPageParams`).\n\n## Notification settings\n\n```bash\nrq '{ accountNotificationsScreen {\n  canManageReplies repliesEnabled\n  canManageAnnouncementCopies announcementCopiesEnabled\n  canManageCalls allowsIncomingCalls\n  devices { id type title deliveryDescription isEnabled isPendingConfirmation isPreferredVoiceCallsDevice } } }' \\\n| jq '.data.accountNotificationsScreen'\n```\n\nThe `canManage*` flags say what this account may change. On a subscriber (parent) account they\nare typically all `false` — the *device* toggles below still work.\n\n## Schema discovery\n\n```bash\n# root fields\nrq '{ __type(name:\"Query\"){ fields { name } } }'    | jq -r '.data.__type.fields[].name'\nrq '{ __type(name:\"Mutation\"){ fields { name } } }' | jq -r '.data.__type.fields[].name'\n# one type's fields, with types\nrq '{ __type(name:\"MessageItem\"){ fields { name type { name kind ofType { name } } } } }'\n# union / interface members — needed to write an inline fragment\nrq '{ __type(name:\"StreamItem\"){ possibleTypes { name } } }' | jq -r '.data.__type.possibleTypes[].name'\n```\n\n---\n\n## Mutations — read this first\n\nWrites hit real people and **cannot be unsent**. Nothing here is needed for reading Remind.\n\n**Verified live:** toggling notification devices, which affects only your own account.\n\n```bash\n# disable, then re-enable, a delivery device (id from accountNotificationsScreen)\nrq 'mutation($i:UpdateAccountNotificationsScreenInput!){ updateAccountNotificationsScreen(input:$i){ __typename } }' \\\n   '{\"i\":{\"devicesToDisable\":[<device-id>]}}'\nrq 'mutation($i:UpdateAccountNotificationsScreenInput!){ updateAccountNotificationsScreen(input:$i){ __typename } }' \\\n   '{\"i\":{\"devicesToEnable\":[<device-id>]}}'\n```\n\nA 200 is not proof — re-read `accountNotificationsScreen` and check `devices[].isEnabled` actually moved.\n\n**Shape only, NOT executed here** — sending a message. Check `permissions.canSend` first.\n\n```bash\nrq 'mutation($i:PutMessageInput!){ putMessage(input:$i){ error { __typename } messages { __typename } } }' \\\n   '{\"i\":{\"recipients\":[{\"type\":\"chat\",\"uuid\":\"<chat-uuid>\"}],\"message\":{\"body\":\"…\",\"urgent\":false}}}'\n```\n\n`recipients[].type` is `chat` for a conversation or `group` for a whole class.\n\n**Owner/teacher only:** `scheduleMessage`, `api_scheduledMessages` and `api_deleteScheduledMessage`\nreturn `Unauthorized` on a subscriber account even with a valid session.\n\nFile v1.1.2:skill-card.md\n\n## Description:\n\nRead Remind (remind.com) classes, chats, messages, and notification settings from a shell by capturing a signed-in session once with fpx and querying Remind's GraphQL API with curl.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and technically comfortable users use this skill to capture a Remind web session and query their Remind classes, chats, messages, and notification settings from a shell without running the Remind MCP server.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The skill stores reusable Remind session credentials on disk.\n\nMitigation: Treat ~/.remind-fpx-session.json like a password, restrict file permissions, and delete it when finished.\n\nRisk: Shell commands can access private Remind messages, classes, chats, and account settings.\n\nMitigation: Review commands before execution and only use the skill with Remind accounts and data you are authorized to access.\n\nRisk: The artifact includes write-capable GraphQL mutation examples even though the skill is read-oriented.\n\nMitigation: Avoid mutation examples unless you intentionally want to change account state or send a real message, and verify permissions before running them.\n\n## Reference(s):\n\n- [Remind GraphQL ready-to-run documents](references/graphql-queries.md)\n- [Remind GraphQL endpoint](https://www.remind.com/graphql)\n\n## Skill Output:\n\n**Output Type(s):** [Text, Markdown, Shell commands, Configuration, Guidance]\n\n**Output Format:** [Markdown with inline bash, GraphQL, JSON, and jq examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Includes commands that depend on a captured Remind web session.]\n\n## Skill Version(s):\n\n1.1.2 (source: server release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.1.1: 4 files, 6263 bytes\n\nFiles: references/graphql-queries.md (5822b), skill-card.md (2548b), SKILL.md (3962b), _meta.json (129b)\n\nFile v1.1.1:SKILL.md\n\n---\nname: remind-fpx\ndescription: 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.\n---\n\n# Remind from the shell\n\nRemind's web app talks to a single **GraphQL endpoint**, `POST https://www.remind.com/graphql`.\nThat endpoint is reachable server-side — no bot wall — so only the *credential* needs the\nbrowser. Capture it once with `fpx`, then use `curl` for everything.\n\n**Introspection is enabled**, so the schema is self-documenting: you can discover any field\nwithout guessing (and you should — see the validation-error gotcha below).\n\n## Auth model\n\nTwo headers, both lifted from a signed-in tab:\n\n| Header | What it is |\n|---|---|\n| `cookie` | The full Cookie request header (includes HttpOnly session cookies) |\n| `x-csrf-token` | Value of the JS-readable `csrf_token` cookie |\n\nBoth are required. Only `x-csrf-token` works — `x-xsrf-token` and `csrf-token` are rejected.\nA named cookie read (`fpx cookies csrf_token`) returns `{}` on this site, so **header capture\nis the only route**.\n\n## One-time setup\n\nInstall `@fetchproxy/cli` (`npm i -g @fetchproxy/cli`) and the **Transporter** Chrome extension.\n\nDeclare the full scope **before** the first pairing — widening it later forces a re-pair:\n\n```bash\nfpx profile add remind --domain remind.com\nfpx profile declare remind \\\n  --capture-header cookie@www.remind.com \\\n  --capture-header x-csrf-token@www.remind.com\n```\n\nThen capture. The capture waits for the *next* request the page makes, so **load a Remind page\nwhile it waits**:\n\n```bash\nfpx session -p remind > ~/.remind-fpx-session.json &   # approve the pair code in Transporter\nopen \"https://www.remind.com/\"                          # feeds the capture\nwait\nchmod 600 ~/.remind-fpx-session.json\n```\n\n> Use a session file of your own like this one. Do **not** write into `~/.remind-mcp/` — that is\n> the MCP server's store, with a different schema; overwriting it corrupts its session.\n\n## The call pattern\n\n```bash\nrq() {  # rq '<query>' ['<variables json>']\n  local S=~/.remind-fpx-session.json\n  local body; body=$(Q=\"$1\" V=\"${2:-{\\}}\" node -e '\n    process.stdout.write(JSON.stringify({query:process.env.Q,variables:JSON.parse(process.env.V)}))')\n  curl -s -X POST https://www.remind.com/graphql \\\n    -H 'content-type: application/json' \\\n    -H \"cookie: $(node -e 'console.log(require(process.argv[1]).capturedHeaders.cookie)' \"$S\")\" \\\n    -H \"x-csrf-token: $(node -e 'console.log(require(process.argv[1]).capturedHeaders[\"x-csrf-token\"])' \"$S\")\" \\\n    --data-binary \"$body\"\n}\n\nrq '{ me { uuid first_name last_name email locale } }' | jq .data.me\n```\n\nStart from `navigationList` — it returns the uuids every other query needs.\nReady-to-run documents with `jq` recipes are in **`references/graphql-queries.md`**.\n\n## Gotchas\n\n- **An expired session returns HTTP 200**, with `errors[0].message == \"Unauthorized\"`. Check the\n  body, never the status. Re-run the capture above to refresh.\n- `\"Unauthorized\"` **also** means \"this account may not do that\" — scheduled messages and org-admin\n  queries are owner/teacher-only, and a subscriber (parent) account gets `Unauthorized` on them\n  with a perfectly valid session.\n- **An unknown field reports as `Internal service error` / `GRAPHQL_VALIDATION_FAILED` with HTTP\n  500 metadata**, not a readable \"no such field\". Do not guess field names — introspect:\n  ```bash\n  rq '{ __type(name:\"Class\"){ fields { name } } }' | jq -r '.data.__type.fields[].name'\n  ```\n- Check `permissions.canSend` before trying to send; most fields on a subscriber account are read-only.\n- **Writes deliver to real people and cannot be unsent.** This skill is read-oriented on purpose;\n  the mutation shapes live in the references file, clearly marked.\n\nFile v1.1.1:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"remind-mcp\",\n  \"version\": \"1.1.1\",\n  \"publishedAt\": 1789963977671\n}\n\nFile v1.1.1:references/graphql-queries.md\n\n# Remind GraphQL — ready-to-run documents\n\nEvery query below was executed against the live API. Field selections come from introspection;\nRemind reports an unknown field as `Internal service error` / `GRAPHQL_VALIDATION_FAILED`, so\nedit them only alongside a re-run.\n\nAll examples assume the `rq` helper from `SKILL.md`.\n\n---\n\n## Start here — what's in the account\n\n`navigationList` is the sidebar, and the source of every uuid you need.\n\n```bash\nrq '{ navigationList { cursor items { __typename\n  ... on TextHeader { text }\n  ... on NavigationListEntityItem { type uuid title subtitle isRostered unreadsCount } } } }'\n```\n\nJust the classes and chats, one per line:\n\n```bash\nrq '{ navigationList { items { __typename\n  ... on NavigationListEntityItem { type uuid title unreadsCount } } } }' \\\n| jq -r '.data.navigationList.items[]\n         | select(.__typename==\"NavigationListEntityItem\")\n         | \"\\(.type)\\t\\(.unreadsCount // 0)\\t\\(.uuid)\\t\\(.title)\"'\n```\n\nPage with `cursor`: pass it back as `lastCursor`.\n\n```bash\nrq 'query($c:String){ navigationList(lastCursor:$c){ cursor items { __typename } } }' '{\"c\":\"<cursor>\"}'\n```\n\n## Account\n\n```bash\nrq '{ me { uuid id first_name last_name display_name email locale is_admin is_child created_at sign_in_count } }' \\\n| jq .data.me\n```\n\n## Classes\n\n```bash\nrq 'query($u:[String!]){ classes(uuids:$u){\n  uuid id code name displayName type\n  messagesCount unreadMessagesCount subscribersCount membershipsCount ownersCount\n  createdAt archivedAt messageHistoryEnabled isMember joinUrl\n  subscriberInitiatedChats canEditInfo canAddOwners } }' \\\n  '{\"u\":[\"<class-uuid>\"]}' | jq '.data.classes[]'\n```\n\nClass members (paginate with `page`/`perPage`):\n\n```bash\nrq 'query($u:[String!]){ classes(uuids:$u){ members(perPage:50){ __typename } } }' '{\"u\":[\"<class-uuid>\"]}'\n```\n\n> Introspect `ClassMembership` before selecting fields:\n> `rq '{ __type(name:\"ClassMembership\"){ fields { name } } }' | jq -r '.data.__type.fields[].name'`\n\n## Chats\n\nList every conversation, with the permission that decides whether you can reply:\n\n```bash\nrq '{ chatStreams { uuid title properTitle streamType type state hidden\n  unreadMessagesCount membershipsCount updatedAt\n  permissions { canSend canAdd canClose canLeave canPhone } } }' \\\n| jq -r '.data.chatStreams[] | \"\\(.updatedAt)\\t\\(.unreadMessagesCount)\\t\\(.uuid)\\t\\(.title)\"'\n```\n\nFilter to one class (numeric `id` from `classes`, not the uuid) or search by name:\n\n```bash\nrq 'query($g:Int,$q:String){ chatStreams(groupId:$g, chatQuery:$q){ uuid title } }' '{\"g\":47615786,\"q\":null}'\n```\n\n## Messages\n\n```bash\nrq 'query($u:[String!],$n:Int){ chatStreams(chatUuids:$u){\n  uuid title unreadMessagesCount\n  sequenceItems(nonGapMessagesLimit:$n){ seq key item { __typename\n    ... on MessageItem { uuid body type createdAt sentAt urgent publicUrl\n                         sender { uuid name } files { __typename } }\n    ... on SystemMessageItem { uuid body createdAt }\n    ... on GapItem { uuid size } } } } }' \\\n  '{\"u\":[\"<chat-uuid>\"],\"n\":25}'\n```\n\nReadable transcript, oldest first:\n\n```bash\n… | jq -r '.data.chatStreams[].sequenceItems[]\n           | select(.item.__typename==\"MessageItem\")\n           | \"\\(.item.createdAt)  \\(.item.sender.name): \\(.item.body)\"'\n```\n\nItem types: **MessageItem** (a real message), **SystemMessageItem** (joins, stream creation),\n**GapItem** (`size` unloaded messages — raise `nonGapMessagesLimit` or page with\n`gapItemNextPageParams`).\n\n## Notification settings\n\n```bash\nrq '{ accountNotificationsScreen {\n  canManageReplies repliesEnabled\n  canManageAnnouncementCopies announcementCopiesEnabled\n  canManageCalls allowsIncomingCalls\n  devices { id type title deliveryDescription isEnabled isPendingConfirmation isPreferredVoiceCallsDevice } } }' \\\n| jq '.data.accountNotificationsScreen'\n```\n\nThe `canManage*` flags say what this account may change. On a subscriber (parent) account they\nare typically all `false` — the *device* toggles below still work.\n\n## Schema discovery\n\n```bash\n# root fields\nrq '{ __type(name:\"Query\"){ fields { name } } }'    | jq -r '.data.__type.fields[].name'\nrq '{ __type(name:\"Mutation\"){ fields { name } } }' | jq -r '.data.__type.fields[].name'\n# one type's fields, with types\nrq '{ __type(name:\"MessageItem\"){ fields { name type { name kind ofType { name } } } } }'\n# union / interface members — needed to write an inline fragment\nrq '{ __type(name:\"StreamItem\"){ possibleTypes { name } } }' | jq -r '.data.__type.possibleTypes[].name'\n```\n\n---\n\n## Mutations — read this first\n\nWrites hit real people and **cannot be unsent**. Nothing here is needed for reading Remind.\n\n**Verified live:** toggling notification devices, which affects only your own account.\n\n```bash\n# disable, then re-enable, a delivery device (id from accountNotificationsScreen)\nrq 'mutation($i:UpdateAccountNotificationsScreenInput!){ updateAccountNotificationsScreen(input:$i){ __typename } }' \\\n   '{\"i\":{\"devicesToDisable\":[<device-id>]}}'\nrq 'mutation($i:UpdateAccountNotificationsScreenInput!){ updateAccountNotificationsScreen(input:$i){ __typename } }' \\\n   '{\"i\":{\"devicesToEnable\":[<device-id>]}}'\n```\n\nA 200 is not proof — re-read `accountNotificationsScreen` and check `devices[].isEnabled` actually moved.\n\n**Shape only, NOT executed here** — sending a message. Check `permissions.canSend` first.\n\n```bash\nrq 'mutation($i:PutMessageInput!){ putMessage(input:$i){ error { __typename } messages { __typename } } }' \\\n   '{\"i\":{\"recipients\":[{\"type\":\"chat\",\"uuid\":\"<chat-uuid>\"}],\"message\":{\"body\":\"…\",\"urgent\":false}}}'\n```\n\n`recipients[].type` is `chat` for a conversation or `group` for a whole class.\n\n**Owner/teacher only:** `scheduleMessage`, `api_scheduledMessages` and `api_deleteScheduledMessage`\nreturn `Unauthorized` on a subscriber account even with a valid session.\n\nFile v1.1.1:skill-card.md\n\n## Description:\n\nRead Remind classes, chats, messages, and notification settings from a shell by capturing a signed-in browser session once and querying Remind's GraphQL API with curl.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and technical users use this skill to inspect Remind account data, including classes, chats, messages, and notification settings, through shell commands without running the remind-mcp server.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The skill stores live Remind session credentials in a local file, so anyone or any process that can read that file may be able to act as the signed-in user until the session expires.\n\nMitigation: Keep the session file private, use restrictive file permissions such as 600, refresh or remove the file when access is no longer needed, and review the skill before installing if read-only behavior is required.\n\nRisk: The references include mutation examples that can change notification settings or send messages from the live Remind account.\n\nMitigation: Use the read-oriented queries by default, check account permissions before any write, and run mutation examples only when intentionally changing settings or sending messages.\n\nRisk: Writes to Remind can reach real recipients and cannot be unsent.\n\nMitigation: Review any generated mutation payload and recipient identifiers before execution, and avoid message-sending mutations unless the user explicitly requests that action.\n\n## Reference(s):\n\n- [Remind GraphQL endpoint](https://www.remind.com/graphql)\n- [Remind web app](https://www.remind.com/)\n- [Remind GraphQL ready-to-run documents](references/graphql-queries.md)\n- [ClawHub skill page](https://clawhub.ai/chrischall/skills/remind-mcp)\n\n## Skill Output:\n\n**Output Type(s):** [Guidance, Shell commands, Configuration, Code]\n\n**Output Format:** [Markdown with inline bash and GraphQL examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Produces command patterns and query examples for local execution against a signed-in Remind account.]\n\n## Skill Version(s):\n\n1.1.1 (source: server release evidence)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.","readmeExcerpt":"Skill: remind-mcp Owner: chrischall Summary: 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. Tags: latest:1.2.6 Version history: v1.2.6 | 2026-10-09T23:28:57.572Z | auto - R","codeSnippets":[],"executableExamples":[{"language":"bash","snippet":"fpx profile add remind --domain remind.com\nfpx profile declare remind \\\n  --capture-header cookie@www.remind.com \\\n  --capture-header x-csrf-token@www.remind.com"},{"language":"bash","snippet":"fpx session -p remind > ~/.remind-fpx-session.json &   # approve the pair code in ContextMint Bridge\nopen \"https://www.remind.com/\"                          # feeds the capture\nwait\nchmod 600 ~/.remind-fpx-session.json"},{"language":"bash","snippet":"curl -s -X POST https://www.remind.com/graphql \\\n    -H 'content-type: application/json' \\\n    -H \"cookie: $(node -e 'console.log(require(process.argv[1]).capturedHeaders.cookie)' \"$S\")\" \\\n    -H \"x-csrf-token: $(node -e 'console.log(require(process.argv[1]).capturedHeaders[\"x-csrf-token\"])' \"$S\")\" \\\n    --data-binary \"$body\""},{"language":"bash","snippet":"rq() {  # rq '<query>' ['<variables json>']\n  local S=~/.remind-fpx-session.json\n  local body; body=$(Q=\"$1\" V=\"${2:-{\\}}\" node -e '\n    process.stdout.write(JSON.stringify({query:process.env.Q,variables:JSON.parse(process.env.V)}))')\n  curl -s -X POST https://www.remind.com/graphql \\\n    -H 'content-type: application/json' \\\n    -H \"cookie: $(node -e 'console.log(require(process.argv[1]).capturedHeaders.cookie)' \"$S\")\" \\\n    -H \"x-csrf-token: $(node -e 'console.log(require(process.argv[1]).capturedHeaders[\"x-csrf-token\"])' \"$S\")\" \\\n    --data-binary \"$body\"\n}\n\nrq '{ me { uuid first_name last_name email locale } }' | jq .data.me"},{"language":"bash","snippet":"rq '{ __type(name:\"Class\"){ fields { name } } }' | jq -r '.data.__type.fields[].name'"},{"language":"bash","snippet":"rq '{ navigationList { cursor items { __typename\n  ... on TextHeader { text }\n  ... on NavigationListEntityItem { type uuid title subtitle isRostered unreadsCount } } } }'"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: remind-fpx\ndescription: 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.\n---\n\n# Remind from the shell\n\nRemind's web app talks to a single **GraphQL endpoint**, `POST https://www.remind.com/graphql`.\nThat endpoint is reachable server-side — no bot wall — so only the *credential* needs the\nbrowser. Capture it once with `fpx`, then use `curl` for everything.\n\n**Introspection is enabled**, so the schema is self-documenting: you can discover any field\nwithout guessing (and you should — see the validation-error gotcha below).\n\n## Auth model\n\nTwo headers, both lifted from a signed-in tab:\n\n| Header | What it is |\n|---|---|\n| `cookie` | The full Cookie request header (includes HttpOnly session cookies) |\n| `x-csrf-token` | Value of the JS-readable `csrf_token` cookie |\n\nBoth are required. Only `x-csrf-token` works — `x-xsrf-token` and `csrf-token` are rejected.\nA named cookie read (`fpx cookies csrf_token`) returns `{}` on this site, so **header capture\nis the only route**.\n\n## One-time setup\n\nInstall `@fetchproxy/cli` (`npm i -g @fetchproxy/cli`) and the **ContextMint Bridge**\nbrowser extension from https://github.com/nullnet-app/contextmint-bridge/releases (Chrome: load\nthe Chrome zip unpacked; Safari isn't available yet, so use Chrome for now).\nIt'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`.\n\nDeclare the full scope **before** the first pairing — widening it later forces a re-pair:\n\n```bash\nfpx profile add remind --domain remind.com\nfpx profile declare remind \\\n  --capture-header cookie@www.remind.com \\\n  --capture-header x-csrf-token@www.remind.com\n```\n\nThen capture. The capture waits for the *next* request the page makes, so **load a Remind page\nwhile it waits**:\n\n```bash\nfpx session -p remind > ~/.remind-fpx-session.json &   # approve the pair code in ContextMint Bridge\nopen \"https://www.remind.com/\"                          # feeds the capture\nwait\nchmod 600 ~/.remind-fpx-session.json\n```\n\n> Use a session file of your own like this one. Do **not** write into `~/.remind-mcp/` — that is\n> the MCP server's store, with a different schema; overwriting it corrupts its session.\n\n## The call pattern\n\n```bash\nrq() {  # rq '<query>' ['<variables json>']\n  local S=~/.remind-fpx-session.json\n  local body; body=$(Q=\"$1\" V=\"${2:-{\\}}\" node -e '\n    process.stdout.write(JSON.stringify({query:process.env.Q,variables:JSON.parse(process.env.V)}))')\n  curl -s -X POST https://www.remind.com/graphql \\\n    -H 'content-type: application/json' \\\n    -H \"cookie: $(node -e 'console.log(require(process.argv[1]).capturedHeaders.cookie)' \"$S\")\" \\\n    -H \"x-csrf-token: $(node -e 'consol"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"remind-mcp\",\n  \"version\": \"1.2.6\",\n  \"publishedAt\": 1791588537572\n}"},{"path":"references/graphql-queries.md","content":"# Remind GraphQL — ready-to-run documents\n\nEvery query below was executed against the live API. Field selections come from introspection;\nRemind reports an unknown field as `Internal service error` / `GRAPHQL_VALIDATION_FAILED`, so\nedit them only alongside a re-run.\n\nAll examples assume the `rq` helper from `SKILL.md`.\n\n---\n\n## Start here — what's in the account\n\n`navigationList` is the sidebar, and the source of every uuid you need.\n\n```bash\nrq '{ navigationList { cursor items { __typename\n  ... on TextHeader { text }\n  ... on NavigationListEntityItem { type uuid title subtitle isRostered unreadsCount } } } }'\n```\n\nJust the classes and chats, one per line:\n\n```bash\nrq '{ navigationList { items { __typename\n  ... on NavigationListEntityItem { type uuid title unreadsCount } } } }' \\\n| jq -r '.data.navigationList.items[]\n         | select(.__typename==\"NavigationListEntityItem\")\n         | \"\\(.type)\\t\\(.unreadsCount // 0)\\t\\(.uuid)\\t\\(.title)\"'\n```\n\nPage with `cursor`: pass it back as `lastCursor`.\n\n```bash\nrq 'query($c:String){ navigationList(lastCursor:$c){ cursor items { __typename } } }' '{\"c\":\"<cursor>\"}'\n```\n\n## Account\n\n```bash\nrq '{ me { uuid id first_name last_name display_name email locale is_admin is_child created_at sign_in_count } }' \\\n| jq .data.me\n```\n\n## Classes\n\n```bash\nrq 'query($u:[String!]){ classes(uuids:$u){\n  uuid id code name displayName type\n  messagesCount unreadMessagesCount subscribersCount membershipsCount ownersCount\n  createdAt archivedAt messageHistoryEnabled isMember joinUrl\n  subscriberInitiatedChats canEditInfo canAddOwners } }' \\\n  '{\"u\":[\"<class-uuid>\"]}' | jq '.data.classes[]'\n```\n\nClass members (paginate with `page`/`perPage`):\n\n```bash\nrq 'query($u:[String!]){ classes(uuids:$u){ members(perPage:50){ __typename } } }' '{\"u\":[\"<class-uuid>\"]}'\n```\n\n> Introspect `ClassMembership` before selecting fields:\n> `rq '{ __type(name:\"ClassMembership\"){ fields { name } } }' | jq -r '.data.__type.fields[].name'`\n\n## Chats\n\nList every conversation, with the permission that decides whether you can reply:\n\n```bash\nrq '{ chatStreams { uuid title properTitle streamType type state hidden\n  unreadMessagesCount membershipsCount updatedAt\n  permissions { canSend canAdd canClose canLeave canPhone } } }' \\\n| jq -r '.data.chatStreams[] | \"\\(.updatedAt)\\t\\(.unreadMessagesCount)\\t\\(.uuid)\\t\\(.title)\"'\n```\n\nFilter to one class (numeric `id` from `classes`, not the uuid) or search by name:\n\n```bash\nrq 'query($g:Int,$q:String){ chatStreams(groupId:$g, chatQuery:$q){ uuid title } }' '{\"g\":47615786,\"q\":null}'\n```\n\n## Messages\n\n```bash\nrq 'query($u:[String!],$n:Int){ chatStreams(chatUuids:$u){\n  uuid title unreadMessagesCount\n  sequenceItems(nonGapMessagesLimit:$n){ seq key item { __typename\n    ... on MessageItem { uuid body type createdAt sentAt urgent publicUrl\n                         sender { uuid name } files { __typename } }\n    ... on SystemMessageItem { uuid body createdAt }\n    ... on GapItem { uuid size } } } } }' \\\n  '{\"u\":[\"<cha"},{"path":"skill-card.md","content":"## Description:\n\nGuides agents to read Remind classes, chats, messages, and notification settings using a captured account session and shell commands.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nRemind account holders and developers use this skill to inspect their classes, conversations, messages, and notification settings from a shell without running the Remind MCP server.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: A saved login session can grant continued access to a Remind account.\n\nMitigation: Use only an account you control; protect the session file and delete it when finished.\n\nRisk: Mutation examples can change account settings or send messages to real people.\n\nMitigation: Avoid mutation examples unless you explicitly intend those changes; check permissions and recipients before sending.\n\n## Reference(s):\n\n- [Remind GraphQL query examples](references/graphql-queries.md)\n- [ContextMint Bridge releases](https://github.com/nullnet-app/contextmint-bridge/releases)\n- [ClawHub skill release](https://clawhub.ai/chrischall/skills/remind-mcp)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Guidance]\n\n**Output Format:** [Markdown with shell commands and JSON query examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Queries return account data as JSON; examples include jq filters.]\n\n## Skill Version(s):\n\n1.2.6 (source: ClawHub release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment."}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":"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. Skill: remind-mcp Owner: chrischall Summary: 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. Tags: latest:1.2.6 Version history: v1.2.6 | 2026-10-09T23:28:57.572Z | auto - R","editorialQuality":{"score":100,"threshold":65,"status":"ready","wordCount":1322,"uniquenessScore":49,"reasons":[]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-10T23:11:49.620Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-10T23:11:49.620Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-11T01:47:25.706Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}