{"id":"6ec2aded-1dfe-465c-bdd8-cad18f52105c","entityType":"agent","slug":"clawhub-chrischall-schoolpass-curl","name":"schoolpass-curl","canonicalUrl":"https://www.xpersona.co/agent/clawhub-chrischall-schoolpass-curl","canonicalPath":"/agent/clawhub-chrischall-schoolpass-curl","generatedAt":"2026-10-11T14:15:00.455Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T09:44:13.912Z","emptyReason":null},"description":"Read a SchoolPass parent account directly with curl against the regional SchoolPass REST API (a busapi shard on school-pass.net), without running the MCP server. Use for a one-off shell read of your students, arrival/dismissal calendar, pending pickup changes, drivers, dismissal locations, or school info — \"check SchoolPass from the terminal\", \"list my kids in SchoolPass\", \"any dismissal changes today\". Requires SCHOOLPASS_EMAIL / SCHOOLPASS_PASSWORD / SCHOOLPASS_SCHOOL_CODE.","descriptionLabel":"Source description","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 1.1K downloads reported by the source. Last updated 10/11/2026.","installCommand":"clawhub skill install s17cjx1a349nz5apaqp02vgz4h85728z:schoolpass-curl","sourceUrl":"https://clawhub.ai/chrischall/schoolpass-curl","homepage":"https://clawhub.ai/chrischall/skills/schoolpass-curl","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/chrischall/schoolpass-curl","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/chrischall/skills/schoolpass-curl","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":61,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"schoolpass-curl technical dossier on Xpersona with agent coverage, OPENCLEW support, and live trust metadata."},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-11T09:44:13.912Z","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-11T09:44:13.912Z","emptyReason":null},"stars":null,"forks":null,"downloads":1096,"packageName":null,"latestVersion":"1.0.9","tractionLabel":"1.1K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T09:44:13.897Z","emptyReason":null},"lastUpdatedAt":"2026-10-11T09:44:13.912Z","lastCrawledAt":"2026-10-11T09:44:13.897Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-12T09:44:13.897Z","lastVerifiedAt":null,"highlights":[{"version":"1.0.9","createdAt":"2026-10-09T23:27:02.839Z","changelog":"- Removed the file skill-card.md. - No user-facing or functional changes to the skill's logic or documentation.","fileCount":4,"zipByteSize":6145},{"version":"1.0.8","createdAt":"2026-10-07T13:38:48.296Z","changelog":"- Removed the sample file skill-card.md. - No functional or user-facing changes to the skill itself.","fileCount":4,"zipByteSize":6209},{"version":"1.0.7","createdAt":"2026-10-05T02:49:26.900Z","changelog":"- Removed the skill-card.md file. - No functional changes; documentation cleanup only.","fileCount":4,"zipByteSize":6141},{"version":"1.0.6","createdAt":"2026-10-03T01:43:42.161Z","changelog":"- Removed the skill-card.md file. - No changes to code or documentation content; only a sample metadata file was deleted.","fileCount":4,"zipByteSize":6090},{"version":"1.0.5","createdAt":"2026-09-30T16:57:19.896Z","changelog":"- Removed the sample file skill-card.md. - No changes to skill functionality or usage. - Maintenance release; documentation remains unchanged.","fileCount":4,"zipByteSize":6114},{"version":"1.0.4","createdAt":"2026-09-25T15:50:32.103Z","changelog":"- Removed the skill-card.md file. - No changes to functionality or usage; documentation and core instructions remain the same.","fileCount":4,"zipByteSize":6115},{"version":"1.0.3","createdAt":"2026-09-23T21:42:55.720Z","changelog":"- Removed the skill-card.md file. - No functional or documentation changes made to the skill itself.","fileCount":4,"zipByteSize":6161},{"version":"1.0.2","createdAt":"2026-09-23T15:44:45.190Z","changelog":"- Removed the skill card file (skill-card.md) from the project. - No changes to functionality or usage instructions.","fileCount":4,"zipByteSize":6160}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s17cjx1a349nz5apaqp02vgz4h85728z:schoolpass-curl","setupComplexity":"low","setupSteps":["Install using `clawhub skill install s17cjx1a349nz5apaqp02vgz4h85728z:schoolpass-curl` in an isolated environment before connecting it to live workloads.","No published capability contract is available yet, so validate auth and request/response behavior manually.","Review the upstream CLAWHUB listing at https://clawhub.ai/chrischall/schoolpass-curl before using production credentials."],"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-schoolpass-curl/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-schoolpass-curl/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-schoolpass-curl/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-schoolpass-curl/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-schoolpass-curl/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-schoolpass-curl/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-11T14:15:00.453Z"}},"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-schoolpass-curl/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-schoolpass-curl/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-schoolpass-curl/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-schoolpass-curl/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":"medium","updatedAt":"2026-10-11T09:44:13.912Z","emptyReason":null},"readme":"Skill: schoolpass-curl\n\nOwner: chrischall\n\nSummary: Read a SchoolPass parent account directly with curl against the regional SchoolPass REST API (a busapi shard on school-pass.net), without running the MCP server. Use for a one-off shell read of your students, arrival/dismissal calendar, pending pickup changes, drivers, dismissal locations, or school info — \"check SchoolPass from the terminal\", \"list my kids in SchoolPass\", \"any dismissal changes today\". Requires SCHOOLPASS_EMAIL / SCHOOLPASS_PASSWORD / SCHOOLPASS_SCHOOL_CODE.\n\nTags: latest:1.0.9\n\nVersion history:\n\nv1.0.9 | 2026-10-09T23:27:02.839Z | auto\n\n- Removed the file skill-card.md.\n- No user-facing or functional changes to the skill's logic or documentation.\n\nv1.0.8 | 2026-10-07T13:38:48.296Z | auto\n\n- Removed the sample file skill-card.md.\n- No functional or user-facing changes to the skill itself.\n\nv1.0.7 | 2026-10-05T02:49:26.900Z | auto\n\n- Removed the skill-card.md file.\n- No functional changes; documentation cleanup only.\n\nv1.0.6 | 2026-10-03T01:43:42.161Z | auto\n\n- Removed the skill-card.md file.\n- No changes to code or documentation content; only a sample metadata file was deleted.\n\nv1.0.5 | 2026-09-30T16:57:19.896Z | auto\n\n- Removed the sample file skill-card.md.\n- No changes to skill functionality or usage.\n- Maintenance release; documentation remains unchanged.\n\nv1.0.4 | 2026-09-25T15:50:32.103Z | auto\n\n- Removed the skill-card.md file.\n- No changes to functionality or usage; documentation and core instructions remain the same.\n\nv1.0.3 | 2026-09-23T21:42:55.720Z | auto\n\n- Removed the skill-card.md file.\n- No functional or documentation changes made to the skill itself.\n\nv1.0.2 | 2026-09-23T15:44:45.190Z | auto\n\n- Removed the skill card file (skill-card.md) from the project.\n- No changes to functionality or usage instructions.\n\nv1.0.1 | 2026-09-21T04:14:13.646Z | auto\n\n- Removed the skill-card.md file from the project.\n- No changes to core functionality or documentation in SKILL.md.\n\nv1.0.0 | 2026-09-20T02:52:27.154Z | auto\n\n- Removed the file skill-card.md.\n- No user-facing or functional changes to the schoolpass-curl skill itself.\n\nv0.4.2 | 2026-09-10T17:51:46.779Z | auto\n\n- Removed the skill card file (skill-card.md).\n- No changes to code or documentation content.\n- Maintains all previous functionality and usage instructions.\n\nv0.4.1 | 2026-09-05T00:51:34.445Z | auto\n\n- Removed the skill-card.md file.\n- No changes to skill functionality or documentation content.\n\nv0.4.0 | 2026-09-04T22:32:32.164Z | auto\n\n- Removed the file: skill-card.md\n- No other functional or documentation changes in this release.\n\nv0.3.1 | 2026-08-28T21:07:37.610Z | auto\n\n- Removed the file skill-card.md.\n- No functional or documentation changes to the skill itself.\n\nv0.3.0 | 2026-08-28T11:34:37.730Z | auto\n\n- Removed the file: skill-card.md.\n- No user-facing feature or functionality changes.\n\nv0.2.0 | 2026-08-25T14:00:30.040Z | auto\n\n- Removed the skill-card.md file from the project.\n- Updated references/requests.md (details not shown here).\n- No changes to usage, configuration, or behavior for end users.\n- Documentation remains up to date with ready-to-use examples.\n\nv0.1.0 | 2026-08-24T22:31:44.415Z | auto\n\n- Initial public release of schoolpass-curl.\n- Enables direct shell access to SchoolPass parent accounts using curl and the REST API; no server required.\n- Supports one-off reads for students, calendars, pickup changes, drivers, dismissal locations, and school info.\n- Requires environment variables: SCHOOLPASS_EMAIL, SCHOOLPASS_PASSWORD, SCHOOLPASS_SCHOOL_CODE, and SCHOOLPASS_API_HOST.\n- Provides detailed setup instructions, authentication flow, and endpoint references for common parent operations.\n- Emphasizes security: never retry failed logins repeatedly or share credentials/tokens.\n\nArchive index:\n\nArchive v1.0.9: 4 files, 6145 bytes\n\nFiles: references/requests.md (6390b), skill-card.md (1918b), SKILL.md (3519b), _meta.json (134b)\n\nFile v1.0.9:SKILL.md\n\n---\nname: schoolpass-curl\ndescription: Read a SchoolPass parent account directly with curl against the regional SchoolPass REST API (a busapi shard on school-pass.net), without running the MCP server. Use for a one-off shell read of your students, arrival/dismissal calendar, pending pickup changes, drivers, dismissal locations, or school info — \"check SchoolPass from the terminal\", \"list my kids in SchoolPass\", \"any dismissal changes today\". Requires SCHOOLPASS_EMAIL / SCHOOLPASS_PASSWORD / SCHOOLPASS_SCHOOL_CODE.\n---\n\n# SchoolPass API via curl\n\nThe SchoolPass JSON API is reachable server-side — no browser, no bridge, no\nextension. This skill talks to it directly with `curl`.\n\nPrefer the `schoolpass-mcp` server for anything conversational or repeated; use\nthis for one-off shell work, scripts, or when the server isn't running.\n\n**Ready-to-run request bodies and `jq` recipes: `references/requests.md`.**\nFull shape reference: `../../docs/SCHOOLPASS-API.md`.\n\n## Setup\n\n```bash\nexport SCHOOLPASS_EMAIL='you@example.com'\nexport SCHOOLPASS_PASSWORD='…'\nexport SCHOOLPASS_SCHOOL_CODE=1183          # your school id (the AppCode)\nexport SCHOOLPASS_API_HOST=busapi-east16-ss.school-pass.net   # your region's shard\n```\n\nFind your school id and region host by signing into your school's\n`<school>.school-pass.net` portal, opening the new SchoolPass app, and reading\n`appCode` (the id) and `apiUrl` (the host) from its `localStorage`.\n\n## Two rules\n\n1. **Every request needs an `AppCode: <schoolCode>` header.** It selects your\n   school. Omit it (or send the wrong one) and you get `401`. It is not a\n   secret. `references/requests.md`'s `sp_curl` helper adds it for you.\n2. **HTTP status codes are real.** `401` = the token or AppCode was rejected,\n   `403` = a parent account cannot reach that route (it is admin-only), `2xx` =\n   success. Branch on the HTTP code.\n\n## Auth: email/password → bearer\n\n1. `POST Auth/users` with `{schoolCode,email,password,authType:\"Credentials\"}`\n   → the identities your email owns. Take the one whose `userType` is `3`\n   (Parent).\n2. `POST Auth/token` with `{schoolCode,userId,userType,password,authType:\"Credentials\"}`\n   → `{ access_token, refresh_token, … }`. Send `access_token` as\n   `Authorization: Bearer …` on every subsequent call.\n\n`references/requests.md`'s `sp_login` does both and caches the token.\n\n> **Never retry a rejected login.** SchoolPass fronts its login with reCAPTCHA;\n> repeated wrong passwords can get the account challenged and break shell login.\n> Fix the credential and try once.\n\n## Reads\n\nAll are `GET` with the `AppCode` header + `Authorization: Bearer`. Most parent\nendpoints take an optional `memberId` (your own parent id, from `Auth/users`).\n\n| Want | Endpoint |\n| --- | --- |\n| your students | `GET parent/getstudents` |\n| parent profile | `GET parent/profile` |\n| authorized drivers | `GET parent/parentdrivers?includeCarpool=true` |\n| student calendar | `GET Student/StudentCalendar?schoolCode=&studentId=&startDate=&endDate=` |\n| pickup changes | `GET PickupChange/GetChanges?studentId=&date=` |\n| dismissal locations | `GET dismissal/getDismissalLocations` |\n| school info | `GET SchoolInfo/GetBasicSchoolInfo?schoolCode=` |\n| reachability (no auth) | `GET version?schoolCode=` |\n\n## Scope\n\nParent tokens are parent-scoped: the admin surface (visitor management, carline\noperations, reports, bus routing) returns `403`. This is expected, not a bug.\n\nNever echo the password or the bearer token into a shared transcript.\n\nFile v1.0.9:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"schoolpass-curl\",\n  \"version\": \"1.0.9\",\n  \"publishedAt\": 1791588422839\n}\n\nFile v1.0.9:references/requests.md\n\n# Ready-to-run requests\n\nShapes come from the SchoolPass Swagger spec and `docs/SCHOOLPASS-API.md`. Run\nthe live check (`node --env-file=.env scripts/live-check.mjs`) once to confirm\nthe auth-response fields on your account before trusting the `jq` recipes below.\n\n## Helper — source this first\n\n```bash\n: \"${SCHOOLPASS_SCHOOL_CODE:?export SCHOOLPASS_SCHOOL_CODE first}\"\n: \"${SCHOOLPASS_EMAIL:?}\"; : \"${SCHOOLPASS_PASSWORD:?}\"\nSP_HOST=\"${SCHOOLPASS_API_HOST:-busapi-east16-ss.school-pass.net}\"\nSP_BASE=\"https://${SP_HOST}/api\"\n# Token cache for this skill (NOT the MCP server's store — the server keeps none\n# on disk, but keep this skill's state self-contained regardless).\nSP_SESSION=\"${SCHOOLPASS_CURL_SESSION:-$HOME/.schoolpass-mcp/curl-token.json}\"\n\n# sp_curl <method> <path> [body-json] [-- extra-curl-args...]\n# Adds the AppCode header, Authorization (if $SP_TOKEN set), and JSON accept.\nsp_curl() {\n  local method=\"$1\" path=\"$2\" body=\"${3:-}\"\n  shift 2; [ $# -gt 0 ] && shift\n  [ \"${1:-}\" = \"--\" ] && shift\n  curl -sS -X \"$method\" \"${SP_BASE}/${path}\" \\\n    -H \"AppCode: ${SCHOOLPASS_SCHOOL_CODE}\" \\\n    -H \"accept: application/json\" \\\n    -H \"content-type: application/json\" \\\n    ${SP_TOKEN:+-H \"Authorization: Bearer ${SP_TOKEN}\"} \\\n    ${body:+--data \"$body\"} \"$@\"\n}\n\n# sp_login: Auth/users -> pick Parent identity -> Auth/token -> export SP_TOKEN.\n# Verify the response field names against your account once (live-check) — the\n# access-token field is `access_token`; adjust the jq path if yours differs.\nsp_login() {\n  local users uid utype token\n  users=$(sp_curl POST Auth/users \"$(jq -nc \\\n    --argjson sc \"$SCHOOLPASS_SCHOOL_CODE\" --arg em \"$SCHOOLPASS_EMAIL\" --arg pw \"$SCHOOLPASS_PASSWORD\" \\\n    '{schoolCode:$sc,email:$em,password:$pw,ssoToken:null,authType:\"Credentials\"}')\") || return 1\n  # Pick the Parent identity (userType 3), else the sole identity. Field names\n  # vary (userId|id, userType|type) — normalize.\n  uid=$(echo \"$users\" | jq -r '(if type==\"array\" then . else (.users // .data // [.]) end)\n      | map({id:(.userId // .id), t:(.userType // .type)})\n      | (map(select(.t==3))[0] // .[0]) | .id')\n  utype=$(echo \"$users\" | jq -r '(if type==\"array\" then . else (.users // .data // [.]) end)\n      | map({id:(.userId // .id), t:(.userType // .type)})\n      | (map(select(.t==3))[0] // .[0]) | .t')\n  token=$(sp_curl POST Auth/token \"$(jq -nc \\\n    --argjson sc \"$SCHOOLPASS_SCHOOL_CODE\" --argjson uid \"$uid\" --argjson ut \"$utype\" --arg pw \"$SCHOOLPASS_PASSWORD\" \\\n    '{schoolCode:$sc,userId:$uid,userType:$ut,password:$pw,ssoToken:null,authType:\"Credentials\"}')\" \\\n    | jq -r '.access_token // .payload.access_token // .accessToken')\n  [ -n \"$token\" ] && [ \"$token\" != \"null\" ] || { echo \"login failed\" >&2; return 1; }\n  export SP_TOKEN=\"$token\" SP_MEMBER_ID=\"$uid\"\n  mkdir -p \"$(dirname \"$SP_SESSION\")\" && chmod 700 \"$(dirname \"$SP_SESSION\")\"\n  printf '{\"memberId\":%s}\\n' \"$uid\" > \"$SP_SESSION\"   # never write the token to disk\n}\n```\n\n## Reachability (no auth)\n\n```bash\nsp_curl GET \"version?schoolCode=${SCHOOLPASS_SCHOOL_CODE}\"\n# -> \"Host:…,Version:5.0.4.7602,…,School:SprAPiServer 1183 Scholars Academy\"\n```\n\n## Reads (after `sp_login`)\n\n```bash\n# Students — id, name, grade, home dismissal location.\nsp_curl GET \"parent/getstudents?memberId=${SP_MEMBER_ID}\" \\\n  | jq '.[] | {id, name:\"\\(.firstName) \\(.lastName)\", gradeId, dismissalLocationId, aftercare}'\n\n# Parent profile.\nsp_curl GET \"parent/profile?memberId=${SP_MEMBER_ID}\"\n\n# Authorized drivers, with carpools.\nsp_curl GET \"parent/parentdrivers?memberId=${SP_MEMBER_ID}&includeCarpool=true\"\n\n# Dismissal locations — the vocabulary a change refers to.\nsp_curl GET \"dismissal/getDismissalLocations\" \\\n  | jq '.[] | {id, name, locationTypeId, dismissalSessionTimeId}'\n\n# A student's arrival/dismissal calendar for a date range.\nSTU=12345; TODAY=$(date +%F); IN2W=$(date -v+14d +%F 2>/dev/null || date -d '+14 days' +%F)\nsp_curl GET \"Student/StudentCalendar?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&studentId=${STU}&startDate=${TODAY}&endDate=${IN2W}\"\n\n# Pickup / dismissal changes for a student today.\nsp_curl GET \"PickupChange/GetChanges?studentId=${STU}&date=${TODAY}\"\n\n# School info + config.\nsp_curl GET \"SchoolInfo/GetBasicSchoolInfo?schoolCode=${SCHOOLPASS_SCHOOL_CODE}\"\nsp_curl GET \"Config/configsettings?schoolCode=${SCHOOLPASS_SCHOOL_CODE}\"\n```\n\n## Notes\n\n- A `403` on any read means your parent token cannot reach that route (it is\n  admin-only). Expected — not a login problem.\n- The `jq` field paths above follow the Swagger shapes; if the live check shows a\n  different envelope on your account, adjust the paths and update this file.\n\n## Writes (verified live)\n\nSubmit a dismissal/arrival change — `POST studentchange`. The body must match the\napp exactly: `dateSet.dates` EMPTY, `daysOfWeek` as NUMERIC ids (Monday=1…Sunday=7),\n`modifiedBy` = your parent member id (= `parentMemberId`), `changeType` from the E2\nenum (Absent=1, LateArrival=2, EarlyDismissal=3, Carpool=4, Activity=5, Bus=6).\n\n```bash\nSTU=11278; DATE=2026-09-14; DOW=1   # DOW: Mon=1..Sun=7 for $DATE\nsp_curl POST \"studentchange?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&parentMemberId=${SP_MEMBER_ID}\" \"$(jq -nc \\\n  --argjson sid \"$STU\" --arg date \"$DATE\" --argjson dow \"$DOW\" --argjson mid \"$SP_MEMBER_ID\" '{\n    studentId:$sid, moveToId:null, busStopId:null,\n    dateSet:{dates:[], daysOfWeek:[$dow], startDate:$date, endDate:$date, recurringWeeks:0},\n    notes:\"\", pickupDropoffPerson:null, willReturn:false, timeOfDay:null,\n    changeSeriesId:0, changeType:1, adType:3, userType:3, modifiedBy:$mid }')\"\n# Verify: re-read the calendar; a non-default entry (isDefault:false, changeSeriesId set) appears.\nsp_curl GET \"Student/StudentCalendar?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&studentId=${STU}&startDate=${DATE}&endDate=${DATE}\" | jq '.dailyList'\n```\n\nCancel a change — `DELETE studentchange/DeleteMobileChange`, keyed on the\n`changeSeriesId` from the calendar:\n\n```bash\nCSID=27074   # from the calendar entry's changeSeriesId\nsp_curl DELETE \"studentchange/DeleteMobileChange?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&ChangeSeriesId=${CSID}&ChangeType=1&ADType=4&dt=${DATE}\"\n```\n\n- `moveToId` for a Carpool move (changeType 4) is a **carpool id**; moving to the\n  carpool the student is already in returns 500.\n- A 2xx is not proof — re-read the calendar to confirm the change (or its removal).\n\nFile v1.0.9:skill-card.md\n\n## Description:\n\nProvides curl and jq guidance for reading a SchoolPass parent account, with separate examples for changing arrival or dismissal records.\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\nParents and authorized helpers use this skill for one-off terminal checks of students, calendars, pickup changes, drivers, dismissal locations, and school information in their SchoolPass account.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The reference guide includes live commands that can create or delete student arrival or dismissal changes despite the skill's read-focused description.\n\nMitigation: Avoid the POST and DELETE examples unless you explicitly intend to change live records; re-read the calendar to confirm any intended change.\n\nRisk: SchoolPass passwords, bearer tokens, and student information can be exposed when commands or responses are shared.\n\nMitigation: Keep credentials and tokens out of shared transcripts, and copy only appCode and apiUrl when checking browser localStorage.\n\n## Reference(s):\n\n- [SchoolPass Curl release](https://clawhub.ai/chrischall/skills/schoolpass-curl)\n- [Ready-to-run requests](references/requests.md)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Guidance]\n\n**Output Format:** [Markdown with bash and jq examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Requires a SchoolPass parent account, school code, and regional API host.]\n\n## Skill Version(s):\n\n1.0.9 (source: server-resolved release)\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.0.8: 4 files, 6209 bytes\n\nFiles: references/requests.md (6390b), skill-card.md (2095b), SKILL.md (3519b), _meta.json (134b)\n\nFile v1.0.8:SKILL.md\n\n---\nname: schoolpass-curl\ndescription: Read a SchoolPass parent account directly with curl against the regional SchoolPass REST API (a busapi shard on school-pass.net), without running the MCP server. Use for a one-off shell read of your students, arrival/dismissal calendar, pending pickup changes, drivers, dismissal locations, or school info — \"check SchoolPass from the terminal\", \"list my kids in SchoolPass\", \"any dismissal changes today\". Requires SCHOOLPASS_EMAIL / SCHOOLPASS_PASSWORD / SCHOOLPASS_SCHOOL_CODE.\n---\n\n# SchoolPass API via curl\n\nThe SchoolPass JSON API is reachable server-side — no browser, no bridge, no\nextension. This skill talks to it directly with `curl`.\n\nPrefer the `schoolpass-mcp` server for anything conversational or repeated; use\nthis for one-off shell work, scripts, or when the server isn't running.\n\n**Ready-to-run request bodies and `jq` recipes: `references/requests.md`.**\nFull shape reference: `../../docs/SCHOOLPASS-API.md`.\n\n## Setup\n\n```bash\nexport SCHOOLPASS_EMAIL='you@example.com'\nexport SCHOOLPASS_PASSWORD='…'\nexport SCHOOLPASS_SCHOOL_CODE=1183          # your school id (the AppCode)\nexport SCHOOLPASS_API_HOST=busapi-east16-ss.school-pass.net   # your region's shard\n```\n\nFind your school id and region host by signing into your school's\n`<school>.school-pass.net` portal, opening the new SchoolPass app, and reading\n`appCode` (the id) and `apiUrl` (the host) from its `localStorage`.\n\n## Two rules\n\n1. **Every request needs an `AppCode: <schoolCode>` header.** It selects your\n   school. Omit it (or send the wrong one) and you get `401`. It is not a\n   secret. `references/requests.md`'s `sp_curl` helper adds it for you.\n2. **HTTP status codes are real.** `401` = the token or AppCode was rejected,\n   `403` = a parent account cannot reach that route (it is admin-only), `2xx` =\n   success. Branch on the HTTP code.\n\n## Auth: email/password → bearer\n\n1. `POST Auth/users` with `{schoolCode,email,password,authType:\"Credentials\"}`\n   → the identities your email owns. Take the one whose `userType` is `3`\n   (Parent).\n2. `POST Auth/token` with `{schoolCode,userId,userType,password,authType:\"Credentials\"}`\n   → `{ access_token, refresh_token, … }`. Send `access_token` as\n   `Authorization: Bearer …` on every subsequent call.\n\n`references/requests.md`'s `sp_login` does both and caches the token.\n\n> **Never retry a rejected login.** SchoolPass fronts its login with reCAPTCHA;\n> repeated wrong passwords can get the account challenged and break shell login.\n> Fix the credential and try once.\n\n## Reads\n\nAll are `GET` with the `AppCode` header + `Authorization: Bearer`. Most parent\nendpoints take an optional `memberId` (your own parent id, from `Auth/users`).\n\n| Want | Endpoint |\n| --- | --- |\n| your students | `GET parent/getstudents` |\n| parent profile | `GET parent/profile` |\n| authorized drivers | `GET parent/parentdrivers?includeCarpool=true` |\n| student calendar | `GET Student/StudentCalendar?schoolCode=&studentId=&startDate=&endDate=` |\n| pickup changes | `GET PickupChange/GetChanges?studentId=&date=` |\n| dismissal locations | `GET dismissal/getDismissalLocations` |\n| school info | `GET SchoolInfo/GetBasicSchoolInfo?schoolCode=` |\n| reachability (no auth) | `GET version?schoolCode=` |\n\n## Scope\n\nParent tokens are parent-scoped: the admin surface (visitor management, carline\noperations, reports, bus routing) returns `403`. This is expected, not a bug.\n\nNever echo the password or the bearer token into a shared transcript.\n\nFile v1.0.8:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"schoolpass-curl\",\n  \"version\": \"1.0.8\",\n  \"publishedAt\": 1791380328296\n}\n\nFile v1.0.8:references/requests.md\n\n# Ready-to-run requests\n\nShapes come from the SchoolPass Swagger spec and `docs/SCHOOLPASS-API.md`. Run\nthe live check (`node --env-file=.env scripts/live-check.mjs`) once to confirm\nthe auth-response fields on your account before trusting the `jq` recipes below.\n\n## Helper — source this first\n\n```bash\n: \"${SCHOOLPASS_SCHOOL_CODE:?export SCHOOLPASS_SCHOOL_CODE first}\"\n: \"${SCHOOLPASS_EMAIL:?}\"; : \"${SCHOOLPASS_PASSWORD:?}\"\nSP_HOST=\"${SCHOOLPASS_API_HOST:-busapi-east16-ss.school-pass.net}\"\nSP_BASE=\"https://${SP_HOST}/api\"\n# Token cache for this skill (NOT the MCP server's store — the server keeps none\n# on disk, but keep this skill's state self-contained regardless).\nSP_SESSION=\"${SCHOOLPASS_CURL_SESSION:-$HOME/.schoolpass-mcp/curl-token.json}\"\n\n# sp_curl <method> <path> [body-json] [-- extra-curl-args...]\n# Adds the AppCode header, Authorization (if $SP_TOKEN set), and JSON accept.\nsp_curl() {\n  local method=\"$1\" path=\"$2\" body=\"${3:-}\"\n  shift 2; [ $# -gt 0 ] && shift\n  [ \"${1:-}\" = \"--\" ] && shift\n  curl -sS -X \"$method\" \"${SP_BASE}/${path}\" \\\n    -H \"AppCode: ${SCHOOLPASS_SCHOOL_CODE}\" \\\n    -H \"accept: application/json\" \\\n    -H \"content-type: application/json\" \\\n    ${SP_TOKEN:+-H \"Authorization: Bearer ${SP_TOKEN}\"} \\\n    ${body:+--data \"$body\"} \"$@\"\n}\n\n# sp_login: Auth/users -> pick Parent identity -> Auth/token -> export SP_TOKEN.\n# Verify the response field names against your account once (live-check) — the\n# access-token field is `access_token`; adjust the jq path if yours differs.\nsp_login() {\n  local users uid utype token\n  users=$(sp_curl POST Auth/users \"$(jq -nc \\\n    --argjson sc \"$SCHOOLPASS_SCHOOL_CODE\" --arg em \"$SCHOOLPASS_EMAIL\" --arg pw \"$SCHOOLPASS_PASSWORD\" \\\n    '{schoolCode:$sc,email:$em,password:$pw,ssoToken:null,authType:\"Credentials\"}')\") || return 1\n  # Pick the Parent identity (userType 3), else the sole identity. Field names\n  # vary (userId|id, userType|type) — normalize.\n  uid=$(echo \"$users\" | jq -r '(if type==\"array\" then . else (.users // .data // [.]) end)\n      | map({id:(.userId // .id), t:(.userType // .type)})\n      | (map(select(.t==3))[0] // .[0]) | .id')\n  utype=$(echo \"$users\" | jq -r '(if type==\"array\" then . else (.users // .data // [.]) end)\n      | map({id:(.userId // .id), t:(.userType // .type)})\n      | (map(select(.t==3))[0] // .[0]) | .t')\n  token=$(sp_curl POST Auth/token \"$(jq -nc \\\n    --argjson sc \"$SCHOOLPASS_SCHOOL_CODE\" --argjson uid \"$uid\" --argjson ut \"$utype\" --arg pw \"$SCHOOLPASS_PASSWORD\" \\\n    '{schoolCode:$sc,userId:$uid,userType:$ut,password:$pw,ssoToken:null,authType:\"Credentials\"}')\" \\\n    | jq -r '.access_token // .payload.access_token // .accessToken')\n  [ -n \"$token\" ] && [ \"$token\" != \"null\" ] || { echo \"login failed\" >&2; return 1; }\n  export SP_TOKEN=\"$token\" SP_MEMBER_ID=\"$uid\"\n  mkdir -p \"$(dirname \"$SP_SESSION\")\" && chmod 700 \"$(dirname \"$SP_SESSION\")\"\n  printf '{\"memberId\":%s}\\n' \"$uid\" > \"$SP_SESSION\"   # never write the token to disk\n}\n```\n\n## Reachability (no auth)\n\n```bash\nsp_curl GET \"version?schoolCode=${SCHOOLPASS_SCHOOL_CODE}\"\n# -> \"Host:…,Version:5.0.4.7602,…,School:SprAPiServer 1183 Scholars Academy\"\n```\n\n## Reads (after `sp_login`)\n\n```bash\n# Students — id, name, grade, home dismissal location.\nsp_curl GET \"parent/getstudents?memberId=${SP_MEMBER_ID}\" \\\n  | jq '.[] | {id, name:\"\\(.firstName) \\(.lastName)\", gradeId, dismissalLocationId, aftercare}'\n\n# Parent profile.\nsp_curl GET \"parent/profile?memberId=${SP_MEMBER_ID}\"\n\n# Authorized drivers, with carpools.\nsp_curl GET \"parent/parentdrivers?memberId=${SP_MEMBER_ID}&includeCarpool=true\"\n\n# Dismissal locations — the vocabulary a change refers to.\nsp_curl GET \"dismissal/getDismissalLocations\" \\\n  | jq '.[] | {id, name, locationTypeId, dismissalSessionTimeId}'\n\n# A student's arrival/dismissal calendar for a date range.\nSTU=12345; TODAY=$(date +%F); IN2W=$(date -v+14d +%F 2>/dev/null || date -d '+14 days' +%F)\nsp_curl GET \"Student/StudentCalendar?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&studentId=${STU}&startDate=${TODAY}&endDate=${IN2W}\"\n\n# Pickup / dismissal changes for a student today.\nsp_curl GET \"PickupChange/GetChanges?studentId=${STU}&date=${TODAY}\"\n\n# School info + config.\nsp_curl GET \"SchoolInfo/GetBasicSchoolInfo?schoolCode=${SCHOOLPASS_SCHOOL_CODE}\"\nsp_curl GET \"Config/configsettings?schoolCode=${SCHOOLPASS_SCHOOL_CODE}\"\n```\n\n## Notes\n\n- A `403` on any read means your parent token cannot reach that route (it is\n  admin-only). Expected — not a login problem.\n- The `jq` field paths above follow the Swagger shapes; if the live check shows a\n  different envelope on your account, adjust the paths and update this file.\n\n## Writes (verified live)\n\nSubmit a dismissal/arrival change — `POST studentchange`. The body must match the\napp exactly: `dateSet.dates` EMPTY, `daysOfWeek` as NUMERIC ids (Monday=1…Sunday=7),\n`modifiedBy` = your parent member id (= `parentMemberId`), `changeType` from the E2\nenum (Absent=1, LateArrival=2, EarlyDismissal=3, Carpool=4, Activity=5, Bus=6).\n\n```bash\nSTU=11278; DATE=2026-09-14; DOW=1   # DOW: Mon=1..Sun=7 for $DATE\nsp_curl POST \"studentchange?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&parentMemberId=${SP_MEMBER_ID}\" \"$(jq -nc \\\n  --argjson sid \"$STU\" --arg date \"$DATE\" --argjson dow \"$DOW\" --argjson mid \"$SP_MEMBER_ID\" '{\n    studentId:$sid, moveToId:null, busStopId:null,\n    dateSet:{dates:[], daysOfWeek:[$dow], startDate:$date, endDate:$date, recurringWeeks:0},\n    notes:\"\", pickupDropoffPerson:null, willReturn:false, timeOfDay:null,\n    changeSeriesId:0, changeType:1, adType:3, userType:3, modifiedBy:$mid }')\"\n# Verify: re-read the calendar; a non-default entry (isDefault:false, changeSeriesId set) appears.\nsp_curl GET \"Student/StudentCalendar?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&studentId=${STU}&startDate=${DATE}&endDate=${DATE}\" | jq '.dailyList'\n```\n\nCancel a change — `DELETE studentchange/DeleteMobileChange`, keyed on the\n`changeSeriesId` from the calendar:\n\n```bash\nCSID=27074   # from the calendar entry's changeSeriesId\nsp_curl DELETE \"studentchange/DeleteMobileChange?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&ChangeSeriesId=${CSID}&ChangeType=1&ADType=4&dt=${DATE}\"\n```\n\n- `moveToId` for a Carpool move (changeType 4) is a **carpool id**; moving to the\n  carpool the student is already in returns 500.\n- A 2xx is not proof — re-read the calendar to confirm the change (or its removal).\n\nFile v1.0.8:skill-card.md\n\n## Description:\n\nProvides curl and jq guidance for accessing a SchoolPass parent account, including student records, dismissal information, and pickup-change requests.\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\nParents and their authorized assistants use shell commands to check SchoolPass students, calendars, drivers, and dismissal details, or manage pickup changes when explicitly intended.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The examples include live POST and DELETE requests that can create or cancel arrival and dismissal changes, despite the read-only description.\n\nMitigation: Review commands before execution and require explicit confirmation for every non-authentication POST or DELETE request.\n\nRisk: Parent credentials and bearer tokens may be exposed through shell environments, command history, or shared transcripts.\n\nMitigation: Keep credentials and tokens out of shared transcripts and command history; restrict access to the shell session.\n\nRisk: Responses may contain sensitive student and family information.\n\nMitigation: Limit access and sharing to authorized people, and avoid printing unnecessary personal data.\n\n## Reference(s):\n\n- [Schoolpass Curl on ClawHub](https://clawhub.ai/chrischall/skills/schoolpass-curl)\n- [Ready-to-run requests](references/requests.md)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Guidance]\n\n**Output Format:** [Markdown with bash commands and JSON examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Requires SchoolPass parent credentials, a school code, and the appropriate regional API host.]\n\n## Skill Version(s):\n\n1.0.8 (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.0.7: 4 files, 6141 bytes\n\nFiles: references/requests.md (6390b), skill-card.md (1984b), SKILL.md (3519b), _meta.json (134b)\n\nFile v1.0.7:SKILL.md\n\n---\nname: schoolpass-curl\ndescription: Read a SchoolPass parent account directly with curl against the regional SchoolPass REST API (a busapi shard on school-pass.net), without running the MCP server. Use for a one-off shell read of your students, arrival/dismissal calendar, pending pickup changes, drivers, dismissal locations, or school info — \"check SchoolPass from the terminal\", \"list my kids in SchoolPass\", \"any dismissal changes today\". Requires SCHOOLPASS_EMAIL / SCHOOLPASS_PASSWORD / SCHOOLPASS_SCHOOL_CODE.\n---\n\n# SchoolPass API via curl\n\nThe SchoolPass JSON API is reachable server-side — no browser, no bridge, no\nextension. This skill talks to it directly with `curl`.\n\nPrefer the `schoolpass-mcp` server for anything conversational or repeated; use\nthis for one-off shell work, scripts, or when the server isn't running.\n\n**Ready-to-run request bodies and `jq` recipes: `references/requests.md`.**\nFull shape reference: `../../docs/SCHOOLPASS-API.md`.\n\n## Setup\n\n```bash\nexport SCHOOLPASS_EMAIL='you@example.com'\nexport SCHOOLPASS_PASSWORD='…'\nexport SCHOOLPASS_SCHOOL_CODE=1183          # your school id (the AppCode)\nexport SCHOOLPASS_API_HOST=busapi-east16-ss.school-pass.net   # your region's shard\n```\n\nFind your school id and region host by signing into your school's\n`<school>.school-pass.net` portal, opening the new SchoolPass app, and reading\n`appCode` (the id) and `apiUrl` (the host) from its `localStorage`.\n\n## Two rules\n\n1. **Every request needs an `AppCode: <schoolCode>` header.** It selects your\n   school. Omit it (or send the wrong one) and you get `401`. It is not a\n   secret. `references/requests.md`'s `sp_curl` helper adds it for you.\n2. **HTTP status codes are real.** `401` = the token or AppCode was rejected,\n   `403` = a parent account cannot reach that route (it is admin-only), `2xx` =\n   success. Branch on the HTTP code.\n\n## Auth: email/password → bearer\n\n1. `POST Auth/users` with `{schoolCode,email,password,authType:\"Credentials\"}`\n   → the identities your email owns. Take the one whose `userType` is `3`\n   (Parent).\n2. `POST Auth/token` with `{schoolCode,userId,userType,password,authType:\"Credentials\"}`\n   → `{ access_token, refresh_token, … }`. Send `access_token` as\n   `Authorization: Bearer …` on every subsequent call.\n\n`references/requests.md`'s `sp_login` does both and caches the token.\n\n> **Never retry a rejected login.** SchoolPass fronts its login with reCAPTCHA;\n> repeated wrong passwords can get the account challenged and break shell login.\n> Fix the credential and try once.\n\n## Reads\n\nAll are `GET` with the `AppCode` header + `Authorization: Bearer`. Most parent\nendpoints take an optional `memberId` (your own parent id, from `Auth/users`).\n\n| Want | Endpoint |\n| --- | --- |\n| your students | `GET parent/getstudents` |\n| parent profile | `GET parent/profile` |\n| authorized drivers | `GET parent/parentdrivers?includeCarpool=true` |\n| student calendar | `GET Student/StudentCalendar?schoolCode=&studentId=&startDate=&endDate=` |\n| pickup changes | `GET PickupChange/GetChanges?studentId=&date=` |\n| dismissal locations | `GET dismissal/getDismissalLocations` |\n| school info | `GET SchoolInfo/GetBasicSchoolInfo?schoolCode=` |\n| reachability (no auth) | `GET version?schoolCode=` |\n\n## Scope\n\nParent tokens are parent-scoped: the admin surface (visitor management, carline\noperations, reports, bus routing) returns `403`. This is expected, not a bug.\n\nNever echo the password or the bearer token into a shared transcript.\n\nFile v1.0.7:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"schoolpass-curl\",\n  \"version\": \"1.0.7\",\n  \"publishedAt\": 1791168566900\n}\n\nFile v1.0.7:references/requests.md\n\n# Ready-to-run requests\n\nShapes come from the SchoolPass Swagger spec and `docs/SCHOOLPASS-API.md`. Run\nthe live check (`node --env-file=.env scripts/live-check.mjs`) once to confirm\nthe auth-response fields on your account before trusting the `jq` recipes below.\n\n## Helper — source this first\n\n```bash\n: \"${SCHOOLPASS_SCHOOL_CODE:?export SCHOOLPASS_SCHOOL_CODE first}\"\n: \"${SCHOOLPASS_EMAIL:?}\"; : \"${SCHOOLPASS_PASSWORD:?}\"\nSP_HOST=\"${SCHOOLPASS_API_HOST:-busapi-east16-ss.school-pass.net}\"\nSP_BASE=\"https://${SP_HOST}/api\"\n# Token cache for this skill (NOT the MCP server's store — the server keeps none\n# on disk, but keep this skill's state self-contained regardless).\nSP_SESSION=\"${SCHOOLPASS_CURL_SESSION:-$HOME/.schoolpass-mcp/curl-token.json}\"\n\n# sp_curl <method> <path> [body-json] [-- extra-curl-args...]\n# Adds the AppCode header, Authorization (if $SP_TOKEN set), and JSON accept.\nsp_curl() {\n  local method=\"$1\" path=\"$2\" body=\"${3:-}\"\n  shift 2; [ $# -gt 0 ] && shift\n  [ \"${1:-}\" = \"--\" ] && shift\n  curl -sS -X \"$method\" \"${SP_BASE}/${path}\" \\\n    -H \"AppCode: ${SCHOOLPASS_SCHOOL_CODE}\" \\\n    -H \"accept: application/json\" \\\n    -H \"content-type: application/json\" \\\n    ${SP_TOKEN:+-H \"Authorization: Bearer ${SP_TOKEN}\"} \\\n    ${body:+--data \"$body\"} \"$@\"\n}\n\n# sp_login: Auth/users -> pick Parent identity -> Auth/token -> export SP_TOKEN.\n# Verify the response field names against your account once (live-check) — the\n# access-token field is `access_token`; adjust the jq path if yours differs.\nsp_login() {\n  local users uid utype token\n  users=$(sp_curl POST Auth/users \"$(jq -nc \\\n    --argjson sc \"$SCHOOLPASS_SCHOOL_CODE\" --arg em \"$SCHOOLPASS_EMAIL\" --arg pw \"$SCHOOLPASS_PASSWORD\" \\\n    '{schoolCode:$sc,email:$em,password:$pw,ssoToken:null,authType:\"Credentials\"}')\") || return 1\n  # Pick the Parent identity (userType 3), else the sole identity. Field names\n  # vary (userId|id, userType|type) — normalize.\n  uid=$(echo \"$users\" | jq -r '(if type==\"array\" then . else (.users // .data // [.]) end)\n      | map({id:(.userId // .id), t:(.userType // .type)})\n      | (map(select(.t==3))[0] // .[0]) | .id')\n  utype=$(echo \"$users\" | jq -r '(if type==\"array\" then . else (.users // .data // [.]) end)\n      | map({id:(.userId // .id), t:(.userType // .type)})\n      | (map(select(.t==3))[0] // .[0]) | .t')\n  token=$(sp_curl POST Auth/token \"$(jq -nc \\\n    --argjson sc \"$SCHOOLPASS_SCHOOL_CODE\" --argjson uid \"$uid\" --argjson ut \"$utype\" --arg pw \"$SCHOOLPASS_PASSWORD\" \\\n    '{schoolCode:$sc,userId:$uid,userType:$ut,password:$pw,ssoToken:null,authType:\"Credentials\"}')\" \\\n    | jq -r '.access_token // .payload.access_token // .accessToken')\n  [ -n \"$token\" ] && [ \"$token\" != \"null\" ] || { echo \"login failed\" >&2; return 1; }\n  export SP_TOKEN=\"$token\" SP_MEMBER_ID=\"$uid\"\n  mkdir -p \"$(dirname \"$SP_SESSION\")\" && chmod 700 \"$(dirname \"$SP_SESSION\")\"\n  printf '{\"memberId\":%s}\\n' \"$uid\" > \"$SP_SESSION\"   # never write the token to disk\n}\n```\n\n## Reachability (no auth)\n\n```bash\nsp_curl GET \"version?schoolCode=${SCHOOLPASS_SCHOOL_CODE}\"\n# -> \"Host:…,Version:5.0.4.7602,…,School:SprAPiServer 1183 Scholars Academy\"\n```\n\n## Reads (after `sp_login`)\n\n```bash\n# Students — id, name, grade, home dismissal location.\nsp_curl GET \"parent/getstudents?memberId=${SP_MEMBER_ID}\" \\\n  | jq '.[] | {id, name:\"\\(.firstName) \\(.lastName)\", gradeId, dismissalLocationId, aftercare}'\n\n# Parent profile.\nsp_curl GET \"parent/profile?memberId=${SP_MEMBER_ID}\"\n\n# Authorized drivers, with carpools.\nsp_curl GET \"parent/parentdrivers?memberId=${SP_MEMBER_ID}&includeCarpool=true\"\n\n# Dismissal locations — the vocabulary a change refers to.\nsp_curl GET \"dismissal/getDismissalLocations\" \\\n  | jq '.[] | {id, name, locationTypeId, dismissalSessionTimeId}'\n\n# A student's arrival/dismissal calendar for a date range.\nSTU=12345; TODAY=$(date +%F); IN2W=$(date -v+14d +%F 2>/dev/null || date -d '+14 days' +%F)\nsp_curl GET \"Student/StudentCalendar?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&studentId=${STU}&startDate=${TODAY}&endDate=${IN2W}\"\n\n# Pickup / dismissal changes for a student today.\nsp_curl GET \"PickupChange/GetChanges?studentId=${STU}&date=${TODAY}\"\n\n# School info + config.\nsp_curl GET \"SchoolInfo/GetBasicSchoolInfo?schoolCode=${SCHOOLPASS_SCHOOL_CODE}\"\nsp_curl GET \"Config/configsettings?schoolCode=${SCHOOLPASS_SCHOOL_CODE}\"\n```\n\n## Notes\n\n- A `403` on any read means your parent token cannot reach that route (it is\n  admin-only). Expected — not a login problem.\n- The `jq` field paths above follow the Swagger shapes; if the live check shows a\n  different envelope on your account, adjust the paths and update this file.\n\n## Writes (verified live)\n\nSubmit a dismissal/arrival change — `POST studentchange`. The body must match the\napp exactly: `dateSet.dates` EMPTY, `daysOfWeek` as NUMERIC ids (Monday=1…Sunday=7),\n`modifiedBy` = your parent member id (= `parentMemberId`), `changeType` from the E2\nenum (Absent=1, LateArrival=2, EarlyDismissal=3, Carpool=4, Activity=5, Bus=6).\n\n```bash\nSTU=11278; DATE=2026-09-14; DOW=1   # DOW: Mon=1..Sun=7 for $DATE\nsp_curl POST \"studentchange?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&parentMemberId=${SP_MEMBER_ID}\" \"$(jq -nc \\\n  --argjson sid \"$STU\" --arg date \"$DATE\" --argjson dow \"$DOW\" --argjson mid \"$SP_MEMBER_ID\" '{\n    studentId:$sid, moveToId:null, busStopId:null,\n    dateSet:{dates:[], daysOfWeek:[$dow], startDate:$date, endDate:$date, recurringWeeks:0},\n    notes:\"\", pickupDropoffPerson:null, willReturn:false, timeOfDay:null,\n    changeSeriesId:0, changeType:1, adType:3, userType:3, modifiedBy:$mid }')\"\n# Verify: re-read the calendar; a non-default entry (isDefault:false, changeSeriesId set) appears.\nsp_curl GET \"Student/StudentCalendar?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&studentId=${STU}&startDate=${DATE}&endDate=${DATE}\" | jq '.dailyList'\n```\n\nCancel a change — `DELETE studentchange/DeleteMobileChange`, keyed on the\n`changeSeriesId` from the calendar:\n\n```bash\nCSID=27074   # from the calendar entry's changeSeriesId\nsp_curl DELETE \"studentchange/DeleteMobileChange?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&ChangeSeriesId=${CSID}&ChangeType=1&ADType=4&dt=${DATE}\"\n```\n\n- `moveToId` for a Carpool move (changeType 4) is a **carpool id**; moving to the\n  carpool the student is already in returns 500.\n- A 2xx is not proof — re-read the calendar to confirm the change (or its removal).\n\nFile v1.0.7:skill-card.md\n\n## Description:\n\nProvides curl and jq recipes for reading a SchoolPass parent account, with additional examples that can submit or cancel student attendance and dismissal changes.\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\nParents and authorized account users can check students, calendars, pickup changes, drivers, and school information from the terminal. The included write examples can also change or cancel student records and require explicit approval before use.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Write and delete examples can change or cancel live student attendance or dismissal records despite the read-oriented description.\n\nMitigation: Use read-only requests for routine checks; require explicit confirmation before any write or delete and verify the resulting record.\n\nRisk: Running account requests exposes credentials and bearer tokens if commands or output are shared.\n\nMitigation: Keep credentials and tokens out of shared transcripts and limit access to local session data.\n\n## Reference(s):\n\n- [Ready-to-run SchoolPass requests](artifact/references/requests.md)\n- [SchoolPass Curl on ClawHub](https://clawhub.ai/chrischall/skills/schoolpass-curl)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Guidance]\n\n**Output Format:** [Markdown with bash commands and JSON examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Requires SchoolPass account credentials and a school code; commands may return account and student data.]\n\n## Skill Version(s):\n\n1.0.7 (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.0.6: 4 files, 6090 bytes\n\nFiles: references/requests.md (6390b), skill-card.md (1934b), SKILL.md (3519b), _meta.json (134b)\n\nFile v1.0.6:SKILL.md\n\n---\nname: schoolpass-curl\ndescription: Read a SchoolPass parent account directly with curl against the regional SchoolPass REST API (a busapi shard on school-pass.net), without running the MCP server. Use for a one-off shell read of your students, arrival/dismissal calendar, pending pickup changes, drivers, dismissal locations, or school info — \"check SchoolPass from the terminal\", \"list my kids in SchoolPass\", \"any dismissal changes today\". Requires SCHOOLPASS_EMAIL / SCHOOLPASS_PASSWORD / SCHOOLPASS_SCHOOL_CODE.\n---\n\n# SchoolPass API via curl\n\nThe SchoolPass JSON API is reachable server-side — no browser, no bridge, no\nextension. This skill talks to it directly with `curl`.\n\nPrefer the `schoolpass-mcp` server for anything conversational or repeated; use\nthis for one-off shell work, scripts, or when the server isn't running.\n\n**Ready-to-run request bodies and `jq` recipes: `references/requests.md`.**\nFull shape reference: `../../docs/SCHOOLPASS-API.md`.\n\n## Setup\n\n```bash\nexport SCHOOLPASS_EMAIL='you@example.com'\nexport SCHOOLPASS_PASSWORD='…'\nexport SCHOOLPASS_SCHOOL_CODE=1183          # your school id (the AppCode)\nexport SCHOOLPASS_API_HOST=busapi-east16-ss.school-pass.net   # your region's shard\n```\n\nFind your school id and region host by signing into your school's\n`<school>.school-pass.net` portal, opening the new SchoolPass app, and reading\n`appCode` (the id) and `apiUrl` (the host) from its `localStorage`.\n\n## Two rules\n\n1. **Every request needs an `AppCode: <schoolCode>` header.** It selects your\n   school. Omit it (or send the wrong one) and you get `401`. It is not a\n   secret. `references/requests.md`'s `sp_curl` helper adds it for you.\n2. **HTTP status codes are real.** `401` = the token or AppCode was rejected,\n   `403` = a parent account cannot reach that route (it is admin-only), `2xx` =\n   success. Branch on the HTTP code.\n\n## Auth: email/password → bearer\n\n1. `POST Auth/users` with `{schoolCode,email,password,authType:\"Credentials\"}`\n   → the identities your email owns. Take the one whose `userType` is `3`\n   (Parent).\n2. `POST Auth/token` with `{schoolCode,userId,userType,password,authType:\"Credentials\"}`\n   → `{ access_token, refresh_token, … }`. Send `access_token` as\n   `Authorization: Bearer …` on every subsequent call.\n\n`references/requests.md`'s `sp_login` does both and caches the token.\n\n> **Never retry a rejected login.** SchoolPass fronts its login with reCAPTCHA;\n> repeated wrong passwords can get the account challenged and break shell login.\n> Fix the credential and try once.\n\n## Reads\n\nAll are `GET` with the `AppCode` header + `Authorization: Bearer`. Most parent\nendpoints take an optional `memberId` (your own parent id, from `Auth/users`).\n\n| Want | Endpoint |\n| --- | --- |\n| your students | `GET parent/getstudents` |\n| parent profile | `GET parent/profile` |\n| authorized drivers | `GET parent/parentdrivers?includeCarpool=true` |\n| student calendar | `GET Student/StudentCalendar?schoolCode=&studentId=&startDate=&endDate=` |\n| pickup changes | `GET PickupChange/GetChanges?studentId=&date=` |\n| dismissal locations | `GET dismissal/getDismissalLocations` |\n| school info | `GET SchoolInfo/GetBasicSchoolInfo?schoolCode=` |\n| reachability (no auth) | `GET version?schoolCode=` |\n\n## Scope\n\nParent tokens are parent-scoped: the admin surface (visitor management, carline\noperations, reports, bus routing) returns `403`. This is expected, not a bug.\n\nNever echo the password or the bearer token into a shared transcript.\n\nFile v1.0.6:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"schoolpass-curl\",\n  \"version\": \"1.0.6\",\n  \"publishedAt\": 1790991822161\n}\n\nFile v1.0.6:references/requests.md\n\n# Ready-to-run requests\n\nShapes come from the SchoolPass Swagger spec and `docs/SCHOOLPASS-API.md`. Run\nthe live check (`node --env-file=.env scripts/live-check.mjs`) once to confirm\nthe auth-response fields on your account before trusting the `jq` recipes below.\n\n## Helper — source this first\n\n```bash\n: \"${SCHOOLPASS_SCHOOL_CODE:?export SCHOOLPASS_SCHOOL_CODE first}\"\n: \"${SCHOOLPASS_EMAIL:?}\"; : \"${SCHOOLPASS_PASSWORD:?}\"\nSP_HOST=\"${SCHOOLPASS_API_HOST:-busapi-east16-ss.school-pass.net}\"\nSP_BASE=\"https://${SP_HOST}/api\"\n# Token cache for this skill (NOT the MCP server's store — the server keeps none\n# on disk, but keep this skill's state self-contained regardless).\nSP_SESSION=\"${SCHOOLPASS_CURL_SESSION:-$HOME/.schoolpass-mcp/curl-token.json}\"\n\n# sp_curl <method> <path> [body-json] [-- extra-curl-args...]\n# Adds the AppCode header, Authorization (if $SP_TOKEN set), and JSON accept.\nsp_curl() {\n  local method=\"$1\" path=\"$2\" body=\"${3:-}\"\n  shift 2; [ $# -gt 0 ] && shift\n  [ \"${1:-}\" = \"--\" ] && shift\n  curl -sS -X \"$method\" \"${SP_BASE}/${path}\" \\\n    -H \"AppCode: ${SCHOOLPASS_SCHOOL_CODE}\" \\\n    -H \"accept: application/json\" \\\n    -H \"content-type: application/json\" \\\n    ${SP_TOKEN:+-H \"Authorization: Bearer ${SP_TOKEN}\"} \\\n    ${body:+--data \"$body\"} \"$@\"\n}\n\n# sp_login: Auth/users -> pick Parent identity -> Auth/token -> export SP_TOKEN.\n# Verify the response field names against your account once (live-check) — the\n# access-token field is `access_token`; adjust the jq path if yours differs.\nsp_login() {\n  local users uid utype token\n  users=$(sp_curl POST Auth/users \"$(jq -nc \\\n    --argjson sc \"$SCHOOLPASS_SCHOOL_CODE\" --arg em \"$SCHOOLPASS_EMAIL\" --arg pw \"$SCHOOLPASS_PASSWORD\" \\\n    '{schoolCode:$sc,email:$em,password:$pw,ssoToken:null,authType:\"Credentials\"}')\") || return 1\n  # Pick the Parent identity (userType 3), else the sole identity. Field names\n  # vary (userId|id, userType|type) — normalize.\n  uid=$(echo \"$users\" | jq -r '(if type==\"array\" then . else (.users // .data // [.]) end)\n      | map({id:(.userId // .id), t:(.userType // .type)})\n      | (map(select(.t==3))[0] // .[0]) | .id')\n  utype=$(echo \"$users\" | jq -r '(if type==\"array\" then . else (.users // .data // [.]) end)\n      | map({id:(.userId // .id), t:(.userType // .type)})\n      | (map(select(.t==3))[0] // .[0]) | .t')\n  token=$(sp_curl POST Auth/token \"$(jq -nc \\\n    --argjson sc \"$SCHOOLPASS_SCHOOL_CODE\" --argjson uid \"$uid\" --argjson ut \"$utype\" --arg pw \"$SCHOOLPASS_PASSWORD\" \\\n    '{schoolCode:$sc,userId:$uid,userType:$ut,password:$pw,ssoToken:null,authType:\"Credentials\"}')\" \\\n    | jq -r '.access_token // .payload.access_token // .accessToken')\n  [ -n \"$token\" ] && [ \"$token\" != \"null\" ] || { echo \"login failed\" >&2; return 1; }\n  export SP_TOKEN=\"$token\" SP_MEMBER_ID=\"$uid\"\n  mkdir -p \"$(dirname \"$SP_SESSION\")\" && chmod 700 \"$(dirname \"$SP_SESSION\")\"\n  printf '{\"memberId\":%s}\\n' \"$uid\" > \"$SP_SESSION\"   # never write the token to disk\n}\n```\n\n## Reachability (no auth)\n\n```bash\nsp_curl GET \"version?schoolCode=${SCHOOLPASS_SCHOOL_CODE}\"\n# -> \"Host:…,Version:5.0.4.7602,…,School:SprAPiServer 1183 Scholars Academy\"\n```\n\n## Reads (after `sp_login`)\n\n```bash\n# Students — id, name, grade, home dismissal location.\nsp_curl GET \"parent/getstudents?memberId=${SP_MEMBER_ID}\" \\\n  | jq '.[] | {id, name:\"\\(.firstName) \\(.lastName)\", gradeId, dismissalLocationId, aftercare}'\n\n# Parent profile.\nsp_curl GET \"parent/profile?memberId=${SP_MEMBER_ID}\"\n\n# Authorized drivers, with carpools.\nsp_curl GET \"parent/parentdrivers?memberId=${SP_MEMBER_ID}&includeCarpool=true\"\n\n# Dismissal locations — the vocabulary a change refers to.\nsp_curl GET \"dismissal/getDismissalLocations\" \\\n  | jq '.[] | {id, name, locationTypeId, dismissalSessionTimeId}'\n\n# A student's arrival/dismissal calendar for a date range.\nSTU=12345; TODAY=$(date +%F); IN2W=$(date -v+14d +%F 2>/dev/null || date -d '+14 days' +%F)\nsp_curl GET \"Student/StudentCalendar?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&studentId=${STU}&startDate=${TODAY}&endDate=${IN2W}\"\n\n# Pickup / dismissal changes for a student today.\nsp_curl GET \"PickupChange/GetChanges?studentId=${STU}&date=${TODAY}\"\n\n# School info + config.\nsp_curl GET \"SchoolInfo/GetBasicSchoolInfo?schoolCode=${SCHOOLPASS_SCHOOL_CODE}\"\nsp_curl GET \"Config/configsettings?schoolCode=${SCHOOLPASS_SCHOOL_CODE}\"\n```\n\n## Notes\n\n- A `403` on any read means your parent token cannot reach that route (it is\n  admin-only). Expected — not a login problem.\n- The `jq` field paths above follow the Swagger shapes; if the live check shows a\n  different envelope on your account, adjust the paths and update this file.\n\n## Writes (verified live)\n\nSubmit a dismissal/arrival change — `POST studentchange`. The body must match the\napp exactly: `dateSet.dates` EMPTY, `daysOfWeek` as NUMERIC ids (Monday=1…Sunday=7),\n`modifiedBy` = your parent member id (= `parentMemberId`), `changeType` from the E2\nenum (Absent=1, LateArrival=2, EarlyDismissal=3, Carpool=4, Activity=5, Bus=6).\n\n```bash\nSTU=11278; DATE=2026-09-14; DOW=1   # DOW: Mon=1..Sun=7 for $DATE\nsp_curl POST \"studentchange?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&parentMemberId=${SP_MEMBER_ID}\" \"$(jq -nc \\\n  --argjson sid \"$STU\" --arg date \"$DATE\" --argjson dow \"$DOW\" --argjson mid \"$SP_MEMBER_ID\" '{\n    studentId:$sid, moveToId:null, busStopId:null,\n    dateSet:{dates:[], daysOfWeek:[$dow], startDate:$date, endDate:$date, recurringWeeks:0},\n    notes:\"\", pickupDropoffPerson:null, willReturn:false, timeOfDay:null,\n    changeSeriesId:0, changeType:1, adType:3, userType:3, modifiedBy:$mid }')\"\n# Verify: re-read the calendar; a non-default entry (isDefault:false, changeSeriesId set) appears.\nsp_curl GET \"Student/StudentCalendar?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&studentId=${STU}&startDate=${DATE}&endDate=${DATE}\" | jq '.dailyList'\n```\n\nCancel a change — `DELETE studentchange/DeleteMobileChange`, keyed on the\n`changeSeriesId` from the calendar:\n\n```bash\nCSID=27074   # from the calendar entry's changeSeriesId\nsp_curl DELETE \"studentchange/DeleteMobileChange?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&ChangeSeriesId=${CSID}&ChangeType=1&ADType=4&dt=${DATE}\"\n```\n\n- `moveToId` for a Carpool move (changeType 4) is a **carpool id**; moving to the\n  carpool the student is already in returns 500.\n- A 2xx is not proof — re-read the calendar to confirm the change (or its removal).\n\nFile v1.0.6:skill-card.md\n\n## Description:\n\nProvides curl examples to read SchoolPass parent-account information and submit or cancel student arrival and dismissal changes.\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\nSchoolPass parent-account users and developers can use one-off shell commands to check student schedules, pickup changes, drivers, and school information. The reference also includes commands to submit or cancel arrival and dismissal changes, which require explicit confirmation.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Although described as read-only, the reference includes live commands to submit or cancel student arrival and dismissal changes.\n\nMitigation: Require explicit user confirmation before any studentchange POST or DELETE command; verify the intended student, date, and action.\n\nRisk: SchoolPass credentials and responses may expose sensitive parent and student information.\n\nMitigation: Limit access to trusted agents and avoid displaying passwords, bearer tokens, or student details in shared transcripts.\n\n## Reference(s):\n\n- [SchoolPass curl skill on ClawHub](https://clawhub.ai/chrischall/skills/schoolpass-curl)\n- [Ready-to-run requests](references/requests.md)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Guidance]\n\n**Output Format:** [Markdown with curl and jq examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Commands require SchoolPass parent credentials and a school code.]\n\n## Skill Version(s):\n\n1.0.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.0.5: 4 files, 6114 bytes\n\nFiles: references/requests.md (6390b), skill-card.md (1903b), SKILL.md (3519b), _meta.json (134b)\n\nFile v1.0.5:SKILL.md\n\n---\nname: schoolpass-curl\ndescription: Read a SchoolPass parent account directly with curl against the regional SchoolPass REST API (a busapi shard on school-pass.net), without running the MCP server. Use for a one-off shell read of your students, arrival/dismissal calendar, pending pickup changes, drivers, dismissal locations, or school info — \"check SchoolPass from the terminal\", \"list my kids in SchoolPass\", \"any dismissal changes today\". Requires SCHOOLPASS_EMAIL / SCHOOLPASS_PASSWORD / SCHOOLPASS_SCHOOL_CODE.\n---\n\n# SchoolPass API via curl\n\nThe SchoolPass JSON API is reachable server-side — no browser, no bridge, no\nextension. This skill talks to it directly with `curl`.\n\nPrefer the `schoolpass-mcp` server for anything conversational or repeated; use\nthis for one-off shell work, scripts, or when the server isn't running.\n\n**Ready-to-run request bodies and `jq` recipes: `references/requests.md`.**\nFull shape reference: `../../docs/SCHOOLPASS-API.md`.\n\n## Setup\n\n```bash\nexport SCHOOLPASS_EMAIL='you@example.com'\nexport SCHOOLPASS_PASSWORD='…'\nexport SCHOOLPASS_SCHOOL_CODE=1183          # your school id (the AppCode)\nexport SCHOOLPASS_API_HOST=busapi-east16-ss.school-pass.net   # your region's shard\n```\n\nFind your school id and region host by signing into your school's\n`<school>.school-pass.net` portal, opening the new SchoolPass app, and reading\n`appCode` (the id) and `apiUrl` (the host) from its `localStorage`.\n\n## Two rules\n\n1. **Every request needs an `AppCode: <schoolCode>` header.** It selects your\n   school. Omit it (or send the wrong one) and you get `401`. It is not a\n   secret. `references/requests.md`'s `sp_curl` helper adds it for you.\n2. **HTTP status codes are real.** `401` = the token or AppCode was rejected,\n   `403` = a parent account cannot reach that route (it is admin-only), `2xx` =\n   success. Branch on the HTTP code.\n\n## Auth: email/password → bearer\n\n1. `POST Auth/users` with `{schoolCode,email,password,authType:\"Credentials\"}`\n   → the identities your email owns. Take the one whose `userType` is `3`\n   (Parent).\n2. `POST Auth/token` with `{schoolCode,userId,userType,password,authType:\"Credentials\"}`\n   → `{ access_token, refresh_token, … }`. Send `access_token` as\n   `Authorization: Bearer …` on every subsequent call.\n\n`references/requests.md`'s `sp_login` does both and caches the token.\n\n> **Never retry a rejected login.** SchoolPass fronts its login with reCAPTCHA;\n> repeated wrong passwords can get the account challenged and break shell login.\n> Fix the credential and try once.\n\n## Reads\n\nAll are `GET` with the `AppCode` header + `Authorization: Bearer`. Most parent\nendpoints take an optional `memberId` (your own parent id, from `Auth/users`).\n\n| Want | Endpoint |\n| --- | --- |\n| your students | `GET parent/getstudents` |\n| parent profile | `GET parent/profile` |\n| authorized drivers | `GET parent/parentdrivers?includeCarpool=true` |\n| student calendar | `GET Student/StudentCalendar?schoolCode=&studentId=&startDate=&endDate=` |\n| pickup changes | `GET PickupChange/GetChanges?studentId=&date=` |\n| dismissal locations | `GET dismissal/getDismissalLocations` |\n| school info | `GET SchoolInfo/GetBasicSchoolInfo?schoolCode=` |\n| reachability (no auth) | `GET version?schoolCode=` |\n\n## Scope\n\nParent tokens are parent-scoped: the admin surface (visitor management, carline\noperations, reports, bus routing) returns `403`. This is expected, not a bug.\n\nNever echo the password or the bearer token into a shared transcript.\n\nFile v1.0.5:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"schoolpass-curl\",\n  \"version\": \"1.0.5\",\n  \"publishedAt\": 1790787439896\n}\n\nFile v1.0.5:references/requests.md\n\n# Ready-to-run requests\n\nShapes come from the SchoolPass Swagger spec and `docs/SCHOOLPASS-API.md`. Run\nthe live check (`node --env-file=.env scripts/live-check.mjs`) once to confirm\nthe auth-response fields on your account before trusting the `jq` recipes below.\n\n## Helper — source this first\n\n```bash\n: \"${SCHOOLPASS_SCHOOL_CODE:?export SCHOOLPASS_SCHOOL_CODE first}\"\n: \"${SCHOOLPASS_EMAIL:?}\"; : \"${SCHOOLPASS_PASSWORD:?}\"\nSP_HOST=\"${SCHOOLPASS_API_HOST:-busapi-east16-ss.school-pass.net}\"\nSP_BASE=\"https://${SP_HOST}/api\"\n# Token cache for this skill (NOT the MCP server's store — the server keeps none\n# on disk, but keep this skill's state self-contained regardless).\nSP_SESSION=\"${SCHOOLPASS_CURL_SESSION:-$HOME/.schoolpass-mcp/curl-token.json}\"\n\n# sp_curl <method> <path> [body-json] [-- extra-curl-args...]\n# Adds the AppCode header, Authorization (if $SP_TOKEN set), and JSON accept.\nsp_curl() {\n  local method=\"$1\" path=\"$2\" body=\"${3:-}\"\n  shift 2; [ $# -gt 0 ] && shift\n  [ \"${1:-}\" = \"--\" ] && shift\n  curl -sS -X \"$method\" \"${SP_BASE}/${path}\" \\\n    -H \"AppCode: ${SCHOOLPASS_SCHOOL_CODE}\" \\\n    -H \"accept: application/json\" \\\n    -H \"content-type: application/json\" \\\n    ${SP_TOKEN:+-H \"Authorization: Bearer ${SP_TOKEN}\"} \\\n    ${body:+--data \"$body\"} \"$@\"\n}\n\n# sp_login: Auth/users -> pick Parent identity -> Auth/token -> export SP_TOKEN.\n# Verify the response field names against your account once (live-check) — the\n# access-token field is `access_token`; adjust the jq path if yours differs.\nsp_login() {\n  local users uid utype token\n  users=$(sp_curl POST Auth/users \"$(jq -nc \\\n    --argjson sc \"$SCHOOLPASS_SCHOOL_CODE\" --arg em \"$SCHOOLPASS_EMAIL\" --arg pw \"$SCHOOLPASS_PASSWORD\" \\\n    '{schoolCode:$sc,email:$em,password:$pw,ssoToken:null,authType:\"Credentials\"}')\") || return 1\n  # Pick the Parent identity (userType 3), else the sole identity. Field names\n  # vary (userId|id, userType|type) — normalize.\n  uid=$(echo \"$users\" | jq -r '(if type==\"array\" then . else (.users // .data // [.]) end)\n      | map({id:(.userId // .id), t:(.userType // .type)})\n      | (map(select(.t==3))[0] // .[0]) | .id')\n  utype=$(echo \"$users\" | jq -r '(if type==\"array\" then . else (.users // .data // [.]) end)\n      | map({id:(.userId // .id), t:(.userType // .type)})\n      | (map(select(.t==3))[0] // .[0]) | .t')\n  token=$(sp_curl POST Auth/token \"$(jq -nc \\\n    --argjson sc \"$SCHOOLPASS_SCHOOL_CODE\" --argjson uid \"$uid\" --argjson ut \"$utype\" --arg pw \"$SCHOOLPASS_PASSWORD\" \\\n    '{schoolCode:$sc,userId:$uid,userType:$ut,password:$pw,ssoToken:null,authType:\"Credentials\"}')\" \\\n    | jq -r '.access_token // .payload.access_token // .accessToken')\n  [ -n \"$token\" ] && [ \"$token\" != \"null\" ] || { echo \"login failed\" >&2; return 1; }\n  export SP_TOKEN=\"$token\" SP_MEMBER_ID=\"$uid\"\n  mkdir -p \"$(dirname \"$SP_SESSION\")\" && chmod 700 \"$(dirname \"$SP_SESSION\")\"\n  printf '{\"memberId\":%s}\\n' \"$uid\" > \"$SP_SESSION\"   # never write the token to disk\n}\n```\n\n## Reachability (no auth)\n\n```bash\nsp_curl GET \"version?schoolCode=${SCHOOLPASS_SCHOOL_CODE}\"\n# -> \"Host:…,Version:5.0.4.7602,…,School:SprAPiServer 1183 Scholars Academy\"\n```\n\n## Reads (after `sp_login`)\n\n```bash\n# Students — id, name, grade, home dismissal location.\nsp_curl GET \"parent/getstudents?memberId=${SP_MEMBER_ID}\" \\\n  | jq '.[] | {id, name:\"\\(.firstName) \\(.lastName)\", gradeId, dismissalLocationId, aftercare}'\n\n# Parent profile.\nsp_curl GET \"parent/profile?memberId=${SP_MEMBER_ID}\"\n\n# Authorized drivers, with carpools.\nsp_curl GET \"parent/parentdrivers?memberId=${SP_MEMBER_ID}&includeCarpool=true\"\n\n# Dismissal locations — the vocabulary a change refers to.\nsp_curl GET \"dismissal/getDismissalLocations\" \\\n  | jq '.[] | {id, name, locationTypeId, dismissalSessionTimeId}'\n\n# A student's arrival/dismissal calendar for a date range.\nSTU=12345; TODAY=$(date +%F); IN2W=$(date -v+14d +%F 2>/dev/null || date -d '+14 days' +%F)\nsp_curl GET \"Student/StudentCalendar?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&studentId=${STU}&startDate=${TODAY}&endDate=${IN2W}\"\n\n# Pickup / dismissal changes for a student today.\nsp_curl GET \"PickupChange/GetChanges?studentId=${STU}&date=${TODAY}\"\n\n# School info + config.\nsp_curl GET \"SchoolInfo/GetBasicSchoolInfo?schoolCode=${SCHOOLPASS_SCHOOL_CODE}\"\nsp_curl GET \"Config/configsettings?schoolCode=${SCHOOLPASS_SCHOOL_CODE}\"\n```\n\n## Notes\n\n- A `403` on any read means your parent token cannot reach that route (it is\n  admin-only). Expected — not a login problem.\n- The `jq` field paths above follow the Swagger shapes; if the live check shows a\n  different envelope on your account, adjust the paths and update this file.\n\n## Writes (verified live)\n\nSubmit a dismissal/arrival change — `POST studentchange`. The body must match the\napp exactly: `dateSet.dates` EMPTY, `daysOfWeek` as NUMERIC ids (Monday=1…Sunday=7),\n`modifiedBy` = your parent member id (= `parentMemberId`), `changeType` from the E2\nenum (Absent=1, LateArrival=2, EarlyDismissal=3, Carpool=4, Activity=5, Bus=6).\n\n```bash\nSTU=11278; DATE=2026-09-14; DOW=1   # DOW: Mon=1..Sun=7 for $DATE\nsp_curl POST \"studentchange?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&parentMemberId=${SP_MEMBER_ID}\" \"$(jq -nc \\\n  --argjson sid \"$STU\" --arg date \"$DATE\" --argjson dow \"$DOW\" --argjson mid \"$SP_MEMBER_ID\" '{\n    studentId:$sid, moveToId:null, busStopId:null,\n    dateSet:{dates:[], daysOfWeek:[$dow], startDate:$date, endDate:$date, recurringWeeks:0},\n    notes:\"\", pickupDropoffPerson:null, willReturn:false, timeOfDay:null,\n    changeSeriesId:0, changeType:1, adType:3, userType:3, modifiedBy:$mid }')\"\n# Verify: re-read the calendar; a non-default entry (isDefault:false, changeSeriesId set) appears.\nsp_curl GET \"Student/StudentCalendar?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&studentId=${STU}&startDate=${DATE}&endDate=${DATE}\" | jq '.dailyList'\n```\n\nCancel a change — `DELETE studentchange/DeleteMobileChange`, keyed on the\n`changeSeriesId` from the calendar:\n\n```bash\nCSID=27074   # from the calendar entry's changeSeriesId\nsp_curl DELETE \"studentchange/DeleteMobileChange?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&ChangeSeriesId=${CSID}&ChangeType=1&ADType=4&dt=${DATE}\"\n```\n\n- `moveToId` for a Carpool move (changeType 4) is a **carpool id**; moving to the\n  carpool the student is already in returns 500.\n- A 2xx is not proof — re-read the calendar to confirm the change (or its removal).\n\nFile v1.0.5:skill-card.md\n\n## Description:\n\nGuides one-off SchoolPass parent-account requests through curl for student, calendar, pickup, driver, dismissal location, and school information.\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\nParents or authorized developers use shell examples to check SchoolPass account information without running the MCP server. The included reference also contains commands that can submit or cancel real student arrival and dismissal changes.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The reference includes live commands that submit or cancel student arrival and dismissal changes, despite the skill's read-only framing.\n\nMitigation: Review commands before running them; execute write or delete examples only when intentionally changing a real student's records, and confirm the result by rereading the calendar.\n\nRisk: Live SchoolPass access exposes credentials, bearer tokens, and sensitive student information.\n\nMitigation: Use only with authorized accounts and keep passwords, tokens, and returned student details out of shared terminals, logs, and transcripts.\n\n## Reference(s):\n\n- [Ready-to-run SchoolPass requests](references/requests.md)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Guidance]\n\n**Output Format:** [Markdown with bash and jq examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Commands can return JSON containing parent and student information.]\n\n## Skill Version(s):\n\n1.0.5 (source: ClawHub release)\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.0.4: 4 files, 6115 bytes\n\nFiles: references/requests.md (6390b), skill-card.md (1923b), SKILL.md (3519b), _meta.json (134b)\n\nFile v1.0.4:SKILL.md\n\n---\nname: schoolpass-curl\ndescription: Read a SchoolPass parent account directly with curl against the regional SchoolPass REST API (a busapi shard on school-pass.net), without running the MCP server. Use for a one-off shell read of your students, arrival/dismissal calendar, pending pickup changes, drivers, dismissal locations, or school info — \"check SchoolPass from the terminal\", \"list my kids in SchoolPass\", \"any dismissal changes today\". Requires SCHOOLPASS_EMAIL / SCHOOLPASS_PASSWORD / SCHOOLPASS_SCHOOL_CODE.\n---\n\n# SchoolPass API via curl\n\nThe SchoolPass JSON API is reachable server-side — no browser, no bridge, no\nextension. This skill talks to it directly with `curl`.\n\nPrefer the `schoolpass-mcp` server for anything conversational or repeated; use\nthis for one-off shell work, scripts, or when the server isn't running.\n\n**Ready-to-run request bodies and `jq` recipes: `references/requests.md`.**\nFull shape reference: `../../docs/SCHOOLPASS-API.md`.\n\n## Setup\n\n```bash\nexport SCHOOLPASS_EMAIL='you@example.com'\nexport SCHOOLPASS_PASSWORD='…'\nexport SCHOOLPASS_SCHOOL_CODE=1183          # your school id (the AppCode)\nexport SCHOOLPASS_API_HOST=busapi-east16-ss.school-pass.net   # your region's shard\n```\n\nFind your school id and region host by signing into your school's\n`<school>.school-pass.net` portal, opening the new SchoolPass app, and reading\n`appCode` (the id) and `apiUrl` (the host) from its `localStorage`.\n\n## Two rules\n\n1. **Every request needs an `AppCode: <schoolCode>` header.** It selects your\n   school. Omit it (or send the wrong one) and you get `401`. It is not a\n   secret. `references/requests.md`'s `sp_curl` helper adds it for you.\n2. **HTTP status codes are real.** `401` = the token or AppCode was rejected,\n   `403` = a parent account cannot reach that route (it is admin-only), `2xx` =\n   success. Branch on the HTTP code.\n\n## Auth: email/password → bearer\n\n1. `POST Auth/users` with `{schoolCode,email,password,authType:\"Credentials\"}`\n   → the identities your email owns. Take the one whose `userType` is `3`\n   (Parent).\n2. `POST Auth/token` with `{schoolCode,userId,userType,password,authType:\"Credentials\"}`\n   → `{ access_token, refresh_token, … }`. Send `access_token` as\n   `Authorization: Bearer …` on every subsequent call.\n\n`references/requests.md`'s `sp_login` does both and caches the token.\n\n> **Never retry a rejected login.** SchoolPass fronts its login with reCAPTCHA;\n> repeated wrong passwords can get the account challenged and break shell login.\n> Fix the credential and try once.\n\n## Reads\n\nAll are `GET` with the `AppCode` header + `Authorization: Bearer`. Most parent\nendpoints take an optional `memberId` (your own parent id, from `Auth/users`).\n\n| Want | Endpoint |\n| --- | --- |\n| your students | `GET parent/getstudents` |\n| parent profile | `GET parent/profile` |\n| authorized drivers | `GET parent/parentdrivers?includeCarpool=true` |\n| student calendar | `GET Student/StudentCalendar?schoolCode=&studentId=&startDate=&endDate=` |\n| pickup changes | `GET PickupChange/GetChanges?studentId=&date=` |\n| dismissal locations | `GET dismissal/getDismissalLocations` |\n| school info | `GET SchoolInfo/GetBasicSchoolInfo?schoolCode=` |\n| reachability (no auth) | `GET version?schoolCode=` |\n\n## Scope\n\nParent tokens are parent-scoped: the admin surface (visitor management, carline\noperations, reports, bus routing) returns `403`. This is expected, not a bug.\n\nNever echo the password or the bearer token into a shared transcript.\n\nFile v1.0.4:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"schoolpass-curl\",\n  \"version\": \"1.0.4\",\n  \"publishedAt\": 1790351432103\n}\n\nFile v1.0.4:references/requests.md\n\n# Ready-to-run requests\n\nShapes come from the SchoolPass Swagger spec and `docs/SCHOOLPASS-API.md`. Run\nthe live check (`node --env-file=.env scripts/live-check.mjs`) once to confirm\nthe auth-response fields on your account before trusting the `jq` recipes below.\n\n## Helper — source this first\n\n```bash\n: \"${SCHOOLPASS_SCHOOL_CODE:?export SCHOOLPASS_SCHOOL_CODE first}\"\n: \"${SCHOOLPASS_EMAIL:?}\"; : \"${SCHOOLPASS_PASSWORD:?}\"\nSP_HOST=\"${SCHOOLPASS_API_HOST:-busapi-east16-ss.school-pass.net}\"\nSP_BASE=\"https://${SP_HOST}/api\"\n# Token cache for this skill (NOT the MCP server's store — the server keeps none\n# on disk, but keep this skill's state self-contained regardless).\nSP_SESSION=\"${SCHOOLPASS_CURL_SESSION:-$HOME/.schoolpass-mcp/curl-token.json}\"\n\n# sp_curl <method> <path> [body-json] [-- extra-curl-args...]\n# Adds the AppCode header, Authorization (if $SP_TOKEN set), and JSON accept.\nsp_curl() {\n  local method=\"$1\" path=\"$2\" body=\"${3:-}\"\n  shift 2; [ $# -gt 0 ] && shift\n  [ \"${1:-}\" = \"--\" ] && shift\n  curl -sS -X \"$method\" \"${SP_BASE}/${path}\" \\\n    -H \"AppCode: ${SCHOOLPASS_SCHOOL_CODE}\" \\\n    -H \"accept: application/json\" \\\n    -H \"content-type: application/json\" \\\n    ${SP_TOKEN:+-H \"Authorization: Bearer ${SP_TOKEN}\"} \\\n    ${body:+--data \"$body\"} \"$@\"\n}\n\n# sp_login: Auth/users -> pick Parent identity -> Auth/token -> export SP_TOKEN.\n# Verify the response field names against your account once (live-check) — the\n# access-token field is `access_token`; adjust the jq path if yours differs.\nsp_login() {\n  local users uid utype token\n  users=$(sp_curl POST Auth/users \"$(jq -nc \\\n    --argjson sc \"$SCHOOLPASS_SCHOOL_CODE\" --arg em \"$SCHOOLPASS_EMAIL\" --arg pw \"$SCHOOLPASS_PASSWORD\" \\\n    '{schoolCode:$sc,email:$em,password:$pw,ssoToken:null,authType:\"Credentials\"}')\") || return 1\n  # Pick the Parent identity (userType 3), else the sole identity. Field names\n  # vary (userId|id, userType|type) — normalize.\n  uid=$(echo \"$users\" | jq -r '(if type==\"array\" then . else (.users // .data // [.]) end)\n      | map({id:(.userId // .id), t:(.userType // .type)})\n      | (map(select(.t==3))[0] // .[0]) | .id')\n  utype=$(echo \"$users\" | jq -r '(if type==\"array\" then . else (.users // .data // [.]) end)\n      | map({id:(.userId // .id), t:(.userType // .type)})\n      | (map(select(.t==3))[0] // .[0]) | .t')\n  token=$(sp_curl POST Auth/token \"$(jq -nc \\\n    --argjson sc \"$SCHOOLPASS_SCHOOL_CODE\" --argjson uid \"$uid\" --argjson ut \"$utype\" --arg pw \"$SCHOOLPASS_PASSWORD\" \\\n    '{schoolCode:$sc,userId:$uid,userType:$ut,password:$pw,ssoToken:null,authType:\"Credentials\"}')\" \\\n    | jq -r '.access_token // .payload.access_token // .accessToken')\n  [ -n \"$token\" ] && [ \"$token\" != \"null\" ] || { echo \"login failed\" >&2; return 1; }\n  export SP_TOKEN=\"$token\" SP_MEMBER_ID=\"$uid\"\n  mkdir -p \"$(dirname \"$SP_SESSION\")\" && chmod 700 \"$(dirname \"$SP_SESSION\")\"\n  printf '{\"memberId\":%s}\\n' \"$uid\" > \"$SP_SESSION\"   # never write the token to disk\n}\n```\n\n## Reachability (no auth)\n\n```bash\nsp_curl GET \"version?schoolCode=${SCHOOLPASS_SCHOOL_CODE}\"\n# -> \"Host:…,Version:5.0.4.7602,…,School:SprAPiServer 1183 Scholars Academy\"\n```\n\n## Reads (after `sp_login`)\n\n```bash\n# Students — id, name, grade, home dismissal location.\nsp_curl GET \"parent/getstudents?memberId=${SP_MEMBER_ID}\" \\\n  | jq '.[] | {id, name:\"\\(.firstName) \\(.lastName)\", gradeId, dismissalLocationId, aftercare}'\n\n# Parent profile.\nsp_curl GET \"parent/profile?memberId=${SP_MEMBER_ID}\"\n\n# Authorized drivers, with carpools.\nsp_curl GET \"parent/parentdrivers?memberId=${SP_MEMBER_ID}&includeCarpool=true\"\n\n# Dismissal locations — the vocabulary a change refers to.\nsp_curl GET \"dismissal/getDismissalLocations\" \\\n  | jq '.[] | {id, name, locationTypeId, dismissalSessionTimeId}'\n\n# A student's arrival/dismissal calendar for a date range.\nSTU=12345; TODAY=$(date +%F); IN2W=$(date -v+14d +%F 2>/dev/null || date -d '+14 days' +%F)\nsp_curl GET \"Student/StudentCalendar?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&studentId=${STU}&startDate=${TODAY}&endDate=${IN2W}\"\n\n# Pickup / dismissal changes for a student today.\nsp_curl GET \"PickupChange/GetChanges?studentId=${STU}&date=${TODAY}\"\n\n# School info + config.\nsp_curl GET \"SchoolInfo/GetBasicSchoolInfo?schoolCode=${SCHOOLPASS_SCHOOL_CODE}\"\nsp_curl GET \"Config/configsettings?schoolCode=${SCHOOLPASS_SCHOOL_CODE}\"\n```\n\n## Notes\n\n- A `403` on any read means your parent token cannot reach that route (it is\n  admin-only). Expected — not a login problem.\n- The `jq` field paths above follow the Swagger shapes; if the live check shows a\n  different envelope on your account, adjust the paths and update this file.\n\n## Writes (verified live)\n\nSubmit a dismissal/arrival change — `POST studentchange`. The body must match the\napp exactly: `dateSet.dates` EMPTY, `daysOfWeek` as NUMERIC ids (Monday=1…Sunday=7),\n`modifiedBy` = your parent member id (= `parentMemberId`), `changeType` from the E2\nenum (Absent=1, LateArrival=2, EarlyDismissal=3, Carpool=4, Activity=5, Bus=6).\n\n```bash\nSTU=11278; DATE=2026-09-14; DOW=1   # DOW: Mon=1..Sun=7 for $DATE\nsp_curl POST \"studentchange?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&parentMemberId=${SP_MEMBER_ID}\" \"$(jq -nc \\\n  --argjson sid \"$STU\" --arg date \"$DATE\" --argjson dow \"$DOW\" --argjson mid \"$SP_MEMBER_ID\" '{\n    studentId:$sid, moveToId:null, busStopId:null,\n    dateSet:{dates:[], daysOfWeek:[$dow], startDate:$date, endDate:$date, recurringWeeks:0},\n    notes:\"\", pickupDropoffPerson:null, willReturn:false, timeOfDay:null,\n    changeSeriesId:0, changeType:1, adType:3, userType:3, modifiedBy:$mid }')\"\n# Verify: re-read the calendar; a non-default entry (isDefault:false, changeSeriesId set) appears.\nsp_curl GET \"Student/StudentCalendar?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&studentId=${STU}&startDate=${DATE}&endDate=${DATE}\" | jq '.dailyList'\n```\n\nCancel a change — `DELETE studentchange/DeleteMobileChange`, keyed on the\n`changeSeriesId` from the calendar:\n\n```bash\nCSID=27074   # from the calendar entry's changeSeriesId\nsp_curl DELETE \"studentchange/DeleteMobileChange?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&ChangeSeriesId=${CSID}&ChangeType=1&ADType=4&dt=${DATE}\"\n```\n\n- `moveToId` for a Carpool move (changeType 4) is a **carpool id**; moving to the\n  carpool the student is already in returns 500.\n- A 2xx is not proof — re-read the calendar to confirm the change (or its removal).\n\nFile v1.0.4:skill-card.md\n\n## Description:\n\nProvides curl and jq recipes for accessing SchoolPass parent-account information and submitting or canceling student dismissal changes through the regional API.\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\nParents and authorized assistants use these shell recipes to check student calendars, pickup changes, drivers, and school details. The included write commands can also submit or cancel dismissal changes when deliberately authorized.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Ready-to-run write and delete commands can change or cancel real student dismissal records.\n\nMitigation: Run change or cancellation commands only with deliberate authorization, then re-read the calendar to confirm the result.\n\nRisk: Parent credentials, bearer tokens, and student information may be exposed in shell history, logs, shared terminals, or transcripts.\n\nMitigation: Protect credentials, tokens, and API output; do not copy sensitive values into shared histories, logs, or transcripts.\n\n## Reference(s):\n\n- [SchoolPass Curl release on ClawHub](https://clawhub.ai/chrischall/skills/schoolpass-curl)\n- [Ready-to-run requests](references/requests.md)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Guidance]\n\n**Output Format:** [Markdown with bash and jq examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Requires a SchoolPass parent account, school code, and regional API host.]\n\n## Skill Version(s):\n\n1.0.4 (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.0.3: 4 files, 6161 bytes\n\nFiles: references/requests.md (6390b), skill-card.md (1964b), SKILL.md (3519b), _meta.json (134b)\n\nFile v1.0.3:SKILL.md\n\n---\nname: schoolpass-curl\ndescription: Read a SchoolPass parent account directly with curl against the regional SchoolPass REST API (a busapi shard on school-pass.net), without running the MCP server. Use for a one-off shell read of your students, arrival/dismissal calendar, pending pickup changes, drivers, dismissal locations, or school info — \"check SchoolPass from the terminal\", \"list my kids in SchoolPass\", \"any dismissal changes today\". Requires SCHOOLPASS_EMAIL / SCHOOLPASS_PASSWORD / SCHOOLPASS_SCHOOL_CODE.\n---\n\n# SchoolPass API via curl\n\nThe SchoolPass JSON API is reachable server-side — no browser, no bridge, no\nextension. This skill talks to it directly with `curl`.\n\nPrefer the `schoolpass-mcp` server for anything conversational or repeated; use\nthis for one-off shell work, scripts, or when the server isn't running.\n\n**Ready-to-run request bodies and `jq` recipes: `references/requests.md`.**\nFull shape reference: `../../docs/SCHOOLPASS-API.md`.\n\n## Setup\n\n```bash\nexport SCHOOLPASS_EMAIL='you@example.com'\nexport SCHOOLPASS_PASSWORD='…'\nexport SCHOOLPASS_SCHOOL_CODE=1183          # your school id (the AppCode)\nexport SCHOOLPASS_API_HOST=busapi-east16-ss.school-pass.net   # your region's shard\n```\n\nFind your school id and region host by signing into your school's\n`<school>.school-pass.net` portal, opening the new SchoolPass app, and reading\n`appCode` (the id) and `apiUrl` (the host) from its `localStorage`.\n\n## Two rules\n\n1. **Every request needs an `AppCode: <schoolCode>` header.** It selects your\n   school. Omit it (or send the wrong one) and you get `401`. It is not a\n   secret. `references/requests.md`'s `sp_curl` helper adds it for you.\n2. **HTTP status codes are real.** `401` = the token or AppCode was rejected,\n   `403` = a parent account cannot reach that route (it is admin-only), `2xx` =\n   success. Branch on the HTTP code.\n\n## Auth: email/password → bearer\n\n1. `POST Auth/users` with `{schoolCode,email,password,authType:\"Credentials\"}`\n   → the identities your email owns. Take the one whose `userType` is `3`\n   (Parent).\n2. `POST Auth/token` with `{schoolCode,userId,userType,password,authType:\"Credentials\"}`\n   → `{ access_token, refresh_token, … }`. Send `access_token` as\n   `Authorization: Bearer …` on every subsequent call.\n\n`references/requests.md`'s `sp_login` does both and caches the token.\n\n> **Never retry a rejected login.** SchoolPass fronts its login with reCAPTCHA;\n> repeated wrong passwords can get the account challenged and break shell login.\n> Fix the credential and try once.\n\n## Reads\n\nAll are `GET` with the `AppCode` header + `Authorization: Bearer`. Most parent\nendpoints take an optional `memberId` (your own parent id, from `Auth/users`).\n\n| Want | Endpoint |\n| --- | --- |\n| your students | `GET parent/getstudents` |\n| parent profile | `GET parent/profile` |\n| authorized drivers | `GET parent/parentdrivers?includeCarpool=true` |\n| student calendar | `GET Student/StudentCalendar?schoolCode=&studentId=&startDate=&endDate=` |\n| pickup changes | `GET PickupChange/GetChanges?studentId=&date=` |\n| dismissal locations | `GET dismissal/getDismissalLocations` |\n| school info | `GET SchoolInfo/GetBasicSchoolInfo?schoolCode=` |\n| reachability (no auth) | `GET version?schoolCode=` |\n\n## Scope\n\nParent tokens are parent-scoped: the admin surface (visitor management, carline\noperations, reports, bus routing) returns `403`. This is expected, not a bug.\n\nNever echo the password or the bearer token into a shared transcript.\n\nFile v1.0.3:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"schoolpass-curl\",\n  \"version\": \"1.0.3\",\n  \"publishedAt\": 1790199775720\n}\n\nFile v1.0.3:references/requests.md\n\n# Ready-to-run requests\n\nShapes come from the SchoolPass Swagger spec and `docs/SCHOOLPASS-API.md`. Run\nthe live check (`node --env-file=.env scripts/live-check.mjs`) once to confirm\nthe auth-response fields on your account before trusting the `jq` recipes below.\n\n## Helper — source this first\n\n```bash\n: \"${SCHOOLPASS_SCHOOL_CODE:?export SCHOOLPASS_SCHOOL_CODE first}\"\n: \"${SCHOOLPASS_EMAIL:?}\"; : \"${SCHOOLPASS_PASSWORD:?}\"\nSP_HOST=\"${SCHOOLPASS_API_HOST:-busapi-east16-ss.school-pass.net}\"\nSP_BASE=\"https://${SP_HOST}/api\"\n# Token cache for this skill (NOT the MCP server's store — the server keeps none\n# on disk, but keep this skill's state self-contained regardless).\nSP_SESSION=\"${SCHOOLPASS_CURL_SESSION:-$HOME/.schoolpass-mcp/curl-token.json}\"\n\n# sp_curl <method> <path> [body-json] [-- extra-curl-args...]\n# Adds the AppCode header, Authorization (if $SP_TOKEN set), and JSON accept.\nsp_curl() {\n  local method=\"$1\" path=\"$2\" body=\"${3:-}\"\n  shift 2; [ $# -gt 0 ] && shift\n  [ \"${1:-}\" = \"--\" ] && shift\n  curl -sS -X \"$method\" \"${SP_BASE}/${path}\" \\\n    -H \"AppCode: ${SCHOOLPASS_SCHOOL_CODE}\" \\\n    -H \"accept: application/json\" \\\n    -H \"content-type: application/json\" \\\n    ${SP_TOKEN:+-H \"Authorization: Bearer ${SP_TOKEN}\"} \\\n    ${body:+--data \"$body\"} \"$@\"\n}\n\n# sp_login: Auth/users -> pick Parent identity -> Auth/token -> export SP_TOKEN.\n# Verify the response field names against your account once (live-check) — the\n# access-token field is `access_token`; adjust the jq path if yours differs.\nsp_login() {\n  local users uid utype token\n  users=$(sp_curl POST Auth/users \"$(jq -nc \\\n    --argjson sc \"$SCHOOLPASS_SCHOOL_CODE\" --arg em \"$SCHOOLPASS_EMAIL\" --arg pw \"$SCHOOLPASS_PASSWORD\" \\\n    '{schoolCode:$sc,email:$em,password:$pw,ssoToken:null,authType:\"Credentials\"}')\") || return 1\n  # Pick the Parent identity (userType 3), else the sole identity. Field names\n  # vary (userId|id, userType|type) — normalize.\n  uid=$(echo \"$users\" | jq -r '(if type==\"array\" then . else (.users // .data // [.]) end)\n      | map({id:(.userId // .id), t:(.userType // .type)})\n      | (map(select(.t==3))[0] // .[0]) | .id')\n  utype=$(echo \"$users\" | jq -r '(if type==\"array\" then . else (.users // .data // [.]) end)\n      | map({id:(.userId // .id), t:(.userType // .type)})\n      | (map(select(.t==3))[0] // .[0]) | .t')\n  token=$(sp_curl POST Auth/token \"$(jq -nc \\\n    --argjson sc \"$SCHOOLPASS_SCHOOL_CODE\" --argjson uid \"$uid\" --argjson ut \"$utype\" --arg pw \"$SCHOOLPASS_PASSWORD\" \\\n    '{schoolCode:$sc,userId:$uid,userType:$ut,password:$pw,ssoToken:null,authType:\"Credentials\"}')\" \\\n    | jq -r '.access_token // .payload.access_token // .accessToken')\n  [ -n \"$token\" ] && [ \"$token\" != \"null\" ] || { echo \"login failed\" >&2; return 1; }\n  export SP_TOKEN=\"$token\" SP_MEMBER_ID=\"$uid\"\n  mkdir -p \"$(dirname \"$SP_SESSION\")\" && chmod 700 \"$(dirname \"$SP_SESSION\")\"\n  printf '{\"memberId\":%s}\\n' \"$uid\" > \"$SP_SESSION\"   # never write the token to disk\n}\n```\n\n## Reachability (no auth)\n\n```bash\nsp_curl GET \"version?schoolCode=${SCHOOLPASS_SCHOOL_CODE}\"\n# -> \"Host:…,Version:5.0.4.7602,…,School:SprAPiServer 1183 Scholars Academy\"\n```\n\n## Reads (after `sp_login`)\n\n```bash\n# Students — id, name, grade, home dismissal location.\nsp_curl GET \"parent/getstudents?memberId=${SP_MEMBER_ID}\" \\\n  | jq '.[] | {id, name:\"\\(.firstName) \\(.lastName)\", gradeId, dismissalLocationId, aftercare}'\n\n# Parent profile.\nsp_curl GET \"parent/profile?memberId=${SP_MEMBER_ID}\"\n\n# Authorized drivers, with carpools.\nsp_curl GET \"parent/parentdrivers?memberId=${SP_MEMBER_ID}&includeCarpool=true\"\n\n# Dismissal locations — the vocabulary a change refers to.\nsp_curl GET \"dismissal/getDismissalLocations\" \\\n  | jq '.[] | {id, name, locationTypeId, dismissalSessionTimeId}'\n\n# A student's arrival/dismissal calendar for a date range.\nSTU=12345; TODAY=$(date +%F); IN2W=$(date -v+14d +%F 2>/dev/null || date -d '+14 days' +%F)\nsp_curl GET \"Student/StudentCalendar?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&studentId=${STU}&startDate=${TODAY}&endDate=${IN2W}\"\n\n# Pickup / dismissal changes for a student today.\nsp_curl GET \"PickupChange/GetChanges?studentId=${STU}&date=${TODAY}\"\n\n# School info + config.\nsp_curl GET \"SchoolInfo/GetBasicSchoolInfo?schoolCode=${SCHOOLPASS_SCHOOL_CODE}\"\nsp_curl GET \"Config/configsettings?schoolCode=${SCHOOLPASS_SCHOOL_CODE}\"\n```\n\n## Notes\n\n- A `403` on any read means your parent token cannot reach that route (it is\n  admin-only). Expected — not a login problem.\n- The `jq` field paths above follow the Swagger shapes; if the live check shows a\n  different envelope on your account, adjust the paths and update this file.\n\n## Writes (verified live)\n\nSubmit a dismissal/arrival change — `POST studentchange`. The body must match the\napp exactly: `dateSet.dates` EMPTY, `daysOfWeek` as NUMERIC ids (Monday=1…Sunday=7),\n`modifiedBy` = your parent member id (= `parentMemberId`), `changeType` from the E2\nenum (Absent=1, LateArrival=2, EarlyDismissal=3, Carpool=4, Activity=5, Bus=6).\n\n```bash\nSTU=11278; DATE=2026-09-14; DOW=1   # DOW: Mon=1..Sun=7 for $DATE\nsp_curl POST \"studentchange?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&parentMemberId=${SP_MEMBER_ID}\" \"$(jq -nc \\\n  --argjson sid \"$STU\" --arg date \"$DATE\" --argjson dow \"$DOW\" --argjson mid \"$SP_MEMBER_ID\" '{\n    studentId:$sid, moveToId:null, busStopId:null,\n    dateSet:{dates:[], daysOfWeek:[$dow], startDate:$date, endDate:$date, recurringWeeks:0},\n    notes:\"\", pickupDropoffPerson:null, willReturn:false, timeOfDay:null,\n    changeSeriesId:0, changeType:1, adType:3, userType:3, modifiedBy:$mid }')\"\n# Verify: re-read the calendar; a non-default entry (isDefault:false, changeSeriesId set) appears.\nsp_curl GET \"Student/StudentCalendar?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&studentId=${STU}&startDate=${DATE}&endDate=${DATE}\" | jq '.dailyList'\n```\n\nCancel a change — `DELETE studentchange/DeleteMobileChange`, keyed on the\n`changeSeriesId` from the calendar:\n\n```bash\nCSID=27074   # from the calendar entry's changeSeriesId\nsp_curl DELETE \"studentchange/DeleteMobileChange?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&ChangeSeriesId=${CSID}&ChangeType=1&ADType=4&dt=${DATE}\"\n```\n\n- `moveToId` for a Carpool move (changeType 4) is a **carpool id**; moving to the\n  carpool the student is already in returns 500.\n- A 2xx is not proof — re-read the calendar to confirm the change (or its removal).\n\nFile v1.0.3:skill-card.md\n\n## Description:\n\nRead a SchoolPass parent account directly with curl against the regional SchoolPass REST API (a busapi shard on school-pass.net), without running the MCP server.\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 authorized SchoolPass parent account users use this skill for one-off terminal reads of student, calendar, pickup-change, driver, dismissal-location, and school information through curl and jq examples.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The reference material includes write and delete commands that can change live student dismissal or arrival records.\n\nMitigation: Review commands before execution and use the write/delete examples only when the user intends to modify live SchoolPass data.\n\nRisk: SchoolPass credentials and bearer tokens could be exposed in shared transcripts or logs.\n\nMitigation: Keep credentials local, avoid echoing passwords or bearer tokens, and use the skill only with accounts the user is authorized to access.\n\n## Reference(s):\n\n- [Ready-to-run requests](references/requests.md)\n- [ClawHub skill page](https://clawhub.ai/chrischall/skills/schoolpass-curl)\n\n## Skill Output:\n\n**Output Type(s):** [text, markdown, shell commands, configuration, guidance]\n\n**Output Format:** [Markdown with curl, jq, and shell command examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Requires SchoolPass email, password, school code, and regional API host supplied by the user.]\n\n## Skill Version(s):\n\n1.0.3 (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.\n\nArchive v1.0.2: 4 files, 6160 bytes\n\nFiles: references/requests.md (6390b), skill-card.md (1986b), SKILL.md (3519b), _meta.json (134b)\n\nFile v1.0.2:SKILL.md\n\n---\nname: schoolpass-curl\ndescription: Read a SchoolPass parent account directly with curl against the regional SchoolPass REST API (a busapi shard on school-pass.net), without running the MCP server. Use for a one-off shell read of your students, arrival/dismissal calendar, pending pickup changes, drivers, dismissal locations, or school info — \"check SchoolPass from the terminal\", \"list my kids in SchoolPass\", \"any dismissal changes today\". Requires SCHOOLPASS_EMAIL / SCHOOLPASS_PASSWORD / SCHOOLPASS_SCHOOL_CODE.\n---\n\n# SchoolPass API via curl\n\nThe SchoolPass JSON API is reachable server-side — no browser, no bridge, no\nextension. This skill talks to it directly with `curl`.\n\nPrefer the `schoolpass-mcp` server for anything conversational or repeated; use\nthis for one-off shell work, scripts, or when the server isn't running.\n\n**Ready-to-run request bodies and `jq` recipes: `references/requests.md`.**\nFull shape reference: `../../docs/SCHOOLPASS-API.md`.\n\n## Setup\n\n```bash\nexport SCHOOLPASS_EMAIL='you@example.com'\nexport SCHOOLPASS_PASSWORD='…'\nexport SCHOOLPASS_SCHOOL_CODE=1183          # your school id (the AppCode)\nexport SCHOOLPASS_API_HOST=busapi-east16-ss.school-pass.net   # your region's shard\n```\n\nFind your school id and region host by signing into your school's\n`<school>.school-pass.net` portal, opening the new SchoolPass app, and reading\n`appCode` (the id) and `apiUrl` (the host) from its `localStorage`.\n\n## Two rules\n\n1. **Every request needs an `AppCode: <schoolCode>` header.** It selects your\n   school. Omit it (or send the wrong one) and you get `401`. It is not a\n   secret. `references/requests.md`'s `sp_curl` helper adds it for you.\n2. **HTTP status codes are real.** `401` = the token or AppCode was rejected,\n   `403` = a parent account cannot reach that route (it is admin-only), `2xx` =\n   success. Branch on the HTTP code.\n\n## Auth: email/password → bearer\n\n1. `POST Auth/users` with `{schoolCode,email,password,authType:\"Credentials\"}`\n   → the identities your email owns. Take the one whose `userType` is `3`\n   (Parent).\n2. `POST Auth/token` with `{schoolCode,userId,userType,password,authType:\"Credentials\"}`\n   → `{ access_token, refresh_token, … }`. Send `access_token` as\n   `Authorization: Bearer …` on every subsequent call.\n\n`references/requests.md`'s `sp_login` does both and caches the token.\n\n> **Never retry a rejected login.** SchoolPass fronts its login with reCAPTCHA;\n> repeated wrong passwords can get the account challenged and break shell login.\n> Fix the credential and try once.\n\n## Reads\n\nAll are `GET` with the `AppCode` header + `Authorization: Bearer`. Most parent\nendpoints take an optional `memberId` (your own parent id, from `Auth/users`).\n\n| Want | Endpoint |\n| --- | --- |\n| your students | `GET parent/getstudents` |\n| parent profile | `GET parent/profile` |\n| authorized drivers | `GET parent/parentdrivers?includeCarpool=true` |\n| student calendar | `GET Student/StudentCalendar?schoolCode=&studentId=&startDate=&endDate=` |\n| pickup changes | `GET PickupChange/GetChanges?studentId=&date=` |\n| dismissal locations | `GET dismissal/getDismissalLocations` |\n| school info | `GET SchoolInfo/GetBasicSchoolInfo?schoolCode=` |\n| reachability (no auth) | `GET version?schoolCode=` |\n\n## Scope\n\nParent tokens are parent-scoped: the admin surface (visitor management, carline\noperations, reports, bus routing) returns `403`. This is expected, not a bug.\n\nNever echo the password or the bearer token into a shared transcript.\n\nFile v1.0.2:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"schoolpass-curl\",\n  \"version\": \"1.0.2\",\n  \"publishedAt\": 1790178285190\n}\n\nFile v1.0.2:references/requests.md\n\n# Ready-to-run requests\n\nShapes come from the SchoolPass Swagger spec and `docs/SCHOOLPASS-API.md`. Run\nthe live check (`node --env-file=.env scripts/live-check.mjs`) once to confirm\nthe auth-response fields on your account before trusting the `jq` recipes below.\n\n## Helper — source this first\n\n```bash\n: \"${SCHOOLPASS_SCHOOL_CODE:?export SCHOOLPASS_SCHOOL_CODE first}\"\n: \"${SCHOOLPASS_EMAIL:?}\"; : \"${SCHOOLPASS_PASSWORD:?}\"\nSP_HOST=\"${SCHOOLPASS_API_HOST:-busapi-east16-ss.school-pass.net}\"\nSP_BASE=\"https://${SP_HOST}/api\"\n# Token cache for this skill (NOT the MCP server's store — the server keeps none\n# on disk, but keep this skill's state self-contained regardless).\nSP_SESSION=\"${SCHOOLPASS_CURL_SESSION:-$HOME/.schoolpass-mcp/curl-token.json}\"\n\n# sp_curl <method> <path> [body-json] [-- extra-curl-args...]\n# Adds the AppCode header, Authorization (if $SP_TOKEN set), and JSON accept.\nsp_curl() {\n  local method=\"$1\" path=\"$2\" body=\"${3:-}\"\n  shift 2; [ $# -gt 0 ] && shift\n  [ \"${1:-}\" = \"--\" ] && shift\n  curl -sS -X \"$method\" \"${SP_BASE}/${path}\" \\\n    -H \"AppCode: ${SCHOOLPASS_SCHOOL_CODE}\" \\\n    -H \"accept: application/json\" \\\n    -H \"content-type: application/json\" \\\n    ${SP_TOKEN:+-H \"Authorization: Bearer ${SP_TOKEN}\"} \\\n    ${body:+--data \"$body\"} \"$@\"\n}\n\n# sp_login: Auth/users -> pick Parent identity -> Auth/token -> export SP_TOKEN.\n# Verify the response field names against your account once (live-check) — the\n# access-token field is `access_token`; adjust the jq path if yours differs.\nsp_login() {\n  local users uid utype token\n  users=$(sp_curl POST Auth/users \"$(jq -nc \\\n    --argjson sc \"$SCHOOLPASS_SCHOOL_CODE\" --arg em \"$SCHOOLPASS_EMAIL\" --arg pw \"$SCHOOLPASS_PASSWORD\" \\\n    '{schoolCode:$sc,email:$em,password:$pw,ssoToken:null,authType:\"Credentials\"}')\") || return 1\n  # Pick the Parent identity (userType 3), else the sole identity. Field names\n  # vary (userId|id, userType|type) — normalize.\n  uid=$(echo \"$users\" | jq -r '(if type==\"array\" then . else (.users // .data // [.]) end)\n      | map({id:(.userId // .id), t:(.userType // .type)})\n      | (map(select(.t==3))[0] // .[0]) | .id')\n  utype=$(echo \"$users\" | jq -r '(if type==\"array\" then . else (.users // .data // [.]) end)\n      | map({id:(.userId // .id), t:(.userType // .type)})\n      | (map(select(.t==3))[0] // .[0]) | .t')\n  token=$(sp_curl POST Auth/token \"$(jq -nc \\\n    --argjson sc \"$SCHOOLPASS_SCHOOL_CODE\" --argjson uid \"$uid\" --argjson ut \"$utype\" --arg pw \"$SCHOOLPASS_PASSWORD\" \\\n    '{schoolCode:$sc,userId:$uid,userType:$ut,password:$pw,ssoToken:null,authType:\"Credentials\"}')\" \\\n    | jq -r '.access_token // .payload.access_token // .accessToken')\n  [ -n \"$token\" ] && [ \"$token\" != \"null\" ] || { echo \"login failed\" >&2; return 1; }\n  export SP_TOKEN=\"$token\" SP_MEMBER_ID=\"$uid\"\n  mkdir -p \"$(dirname \"$SP_SESSION\")\" && chmod 700 \"$(dirname \"$SP_SESSION\")\"\n  printf '{\"memberId\":%s}\\n' \"$uid\" > \"$SP_SESSION\"   # never write the token to disk\n}\n```\n\n## Reachability (no auth)\n\n```bash\nsp_curl GET \"version?schoolCode=${SCHOOLPASS_SCHOOL_CODE}\"\n# -> \"Host:…,Version:5.0.4.7602,…,School:SprAPiServer 1183 Scholars Academy\"\n```\n\n## Reads (after `sp_login`)\n\n```bash\n# Students — id, name, grade, home dismissal location.\nsp_curl GET \"parent/getstudents?memberId=${SP_MEMBER_ID}\" \\\n  | jq '.[] | {id, name:\"\\(.firstName) \\(.lastName)\", gradeId, dismissalLocationId, aftercare}'\n\n# Parent profile.\nsp_curl GET \"parent/profile?memberId=${SP_MEMBER_ID}\"\n\n# Authorized drivers, with carpools.\nsp_curl GET \"parent/parentdrivers?memberId=${SP_MEMBER_ID}&includeCarpool=true\"\n\n# Dismissal locations — the vocabulary a change refers to.\nsp_curl GET \"dismissal/getDismissalLocations\" \\\n  | jq '.[] | {id, name, locationTypeId, dismissalSessionTimeId}'\n\n# A student's arrival/dismissal calendar for a date range.\nSTU=12345; TODAY=$(date +%F); IN2W=$(date -v+14d +%F 2>/dev/null || date -d '+14 days' +%F)\nsp_curl GET \"Student/StudentCalendar?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&studentId=${STU}&startDate=${TODAY}&endDate=${IN2W}\"\n\n# Pickup / dismissal changes for a student today.\nsp_curl GET \"PickupChange/GetChanges?studentId=${STU}&date=${TODAY}\"\n\n# School info + config.\nsp_curl GET \"SchoolInfo/GetBasicSchoolInfo?schoolCode=${SCHOOLPASS_SCHOOL_CODE}\"\nsp_curl GET \"Config/configsettings?schoolCode=${SCHOOLPASS_SCHOOL_CODE}\"\n```\n\n## Notes\n\n- A `403` on any read means your parent token cannot reach that route (it is\n  admin-only). Expected — not a login problem.\n- The `jq` field paths above follow the Swagger shapes; if the live check shows a\n  different envelope on your account, adjust the paths and update this file.\n\n## Writes (verified live)\n\nSubmit a dismissal/arrival change — `POST studentchange`. The body must match the\napp exactly: `dateSet.dates` EMPTY, `daysOfWeek` as NUMERIC ids (Monday=1…Sunday=7),\n`modifiedBy` = your parent member id (= `parentMemberId`), `changeType` from the E2\nenum (Absent=1, LateArrival=2, EarlyDismissal=3, Carpool=4, Activity=5, Bus=6).\n\n```bash\nSTU=11278; DATE=2026-09-14; DOW=1   # DOW: Mon=1..Sun=7 for $DATE\nsp_curl POST \"studentchange?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&parentMemberId=${SP_MEMBER_ID}\" \"$(jq -nc \\\n  --argjson sid \"$STU\" --arg date \"$DATE\" --argjson dow \"$DOW\" --argjson mid \"$SP_MEMBER_ID\" '{\n    studentId:$sid, moveToId:null, busStopId:null,\n    dateSet:{dates:[], daysOfWeek:[$dow], startDate:$date, endDate:$date, recurringWeeks:0},\n    notes:\"\", pickupDropoffPerson:null, willReturn:false, timeOfDay:null,\n    changeSeriesId:0, changeType:1, adType:3, userType:3, modifiedBy:$mid }')\"\n# Verify: re-read the calendar; a non-default entry (isDefault:false, changeSeriesId set) appears.\nsp_curl GET \"Student/StudentCalendar?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&studentId=${STU}&startDate=${DATE}&endDate=${DATE}\" | jq '.dailyList'\n```\n\nCancel a change — `DELETE studentchange/DeleteMobileChange`, keyed on the\n`changeSeriesId` from the calendar:\n\n```bash\nCSID=27074   # from the calendar entry's changeSeriesId\nsp_curl DELETE \"studentchange/DeleteMobileChange?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&ChangeSeriesId=${CSID}&ChangeType=1&ADType=4&dt=${DATE}\"\n```\n\n- `moveToId` for a Carpool move (changeType 4) is a **carpool id**; moving to the\n  carpool the student is already in returns 500.\n- A 2xx is not proof — re-read the calendar to confirm the change (or its removal).\n\nFile v1.0.2:skill-card.md\n\n## Description:\n\nRead a SchoolPass parent account directly with curl against the regional SchoolPass REST API, without running the MCP server.\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, engineers, and authorized SchoolPass parent-account users use this skill to generate one-off curl and jq commands for checking students, calendars, pickup changes, drivers, dismissal locations, and school information from a terminal.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The skill needs SchoolPass account credentials and can read sensitive parent, student, driver, calendar, and pickup-change data.\n\nMitigation: Use only an authorized SchoolPass parent account, keep credentials and bearer tokens out of shared transcripts or logs, and review command output before sharing it.\n\nRisk: The reference file documents commands that can submit or cancel live dismissal or arrival changes.\n\nMitigation: Run write or delete requests only when live SchoolPass changes are intended, and re-read the calendar afterward to confirm the result.\n\n## Reference(s):\n\n- [Ready-to-run requests](references/requests.md)\n- [ClawHub skill page](https://clawhub.ai/chrischall/skills/schoolpass-curl)\n\n## Skill Output:\n\n**Output Type(s):** [shell commands, configuration, guidance]\n\n**Output Format:** [Markdown with inline bash and jq code blocks]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Requires SchoolPass email, password, school code, and regional API host configuration.]\n\n## Skill Version(s):\n\n1.0.2 (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.\n\nArchive v1.0.1: 4 files, 6236 bytes\n\nFiles: references/requests.md (6390b), skill-card.md (2140b), SKILL.md (3519b), _meta.json (134b)\n\nFile v1.0.1:SKILL.md\n\n---\nname: schoolpass-curl\ndescription: Read a SchoolPass parent account directly with curl against the regional SchoolPass REST API (a busapi shard on school-pass.net), without running the MCP server. Use for a one-off shell read of your students, arrival/dismissal calendar, pending pickup changes, drivers, dismissal locations, or school info — \"check SchoolPass from the terminal\", \"list my kids in SchoolPass\", \"any dismissal changes today\". Requires SCHOOLPASS_EMAIL / SCHOOLPASS_PASSWORD / SCHOOLPASS_SCHOOL_CODE.\n---\n\n# SchoolPass API via curl\n\nThe SchoolPass JSON API is reachable server-side — no browser, no bridge, no\nextension. This skill talks to it directly with `curl`.\n\nPrefer the `schoolpass-mcp` server for anything conversational or repeated; use\nthis for one-off shell work, scripts, or when the server isn't running.\n\n**Ready-to-run request bodies and `jq` recipes: `references/requests.md`.**\nFull shape reference: `../../docs/SCHOOLPASS-API.md`.\n\n## Setup\n\n```bash\nexport SCHOOLPASS_EMAIL='you@example.com'\nexport SCHOOLPASS_PASSWORD='…'\nexport SCHOOLPASS_SCHOOL_CODE=1183          # your school id (the AppCode)\nexport SCHOOLPASS_API_HOST=busapi-east16-ss.school-pass.net   # your region's shard\n```\n\nFind your school id and region host by signing into your school's\n`<school>.school-pass.net` portal, opening the new SchoolPass app, and reading\n`appCode` (the id) and `apiUrl` (the host) from its `localStorage`.\n\n## Two rules\n\n1. **Every request needs an `AppCode: <schoolCode>` header.** It selects your\n   school. Omit it (or send the wrong one) and you get `401`. It is not a\n   secret. `references/requests.md`'s `sp_curl` helper adds it for you.\n2. **HTTP status codes are real.** `401` = the token or AppCode was rejected,\n   `403` = a parent account cannot reach that route (it is admin-only), `2xx` =\n   success. Branch on the HTTP code.\n\n## Auth: email/password → bearer\n\n1. `POST Auth/users` with `{schoolCode,email,password,authType:\"Credentials\"}`\n   → the identities your email owns. Take the one whose `userType` is `3`\n   (Parent).\n2. `POST Auth/token` with `{schoolCode,userId,userType,password,authType:\"Credentials\"}`\n   → `{ access_token, refresh_token, … }`. Send `access_token` as\n   `Authorization: Bearer …` on every subsequent call.\n\n`references/requests.md`'s `sp_login` does both and caches the token.\n\n> **Never retry a rejected login.** SchoolPass fronts its login with reCAPTCHA;\n> repeated wrong passwords can get the account challenged and break shell login.\n> Fix the credential and try once.\n\n## Reads\n\nAll are `GET` with the `AppCode` header + `Authorization: Bearer`. Most parent\nendpoints take an optional `memberId` (your own parent id, from `Auth/users`).\n\n| Want | Endpoint |\n| --- | --- |\n| your students | `GET parent/getstudents` |\n| parent profile | `GET parent/profile` |\n| authorized drivers | `GET parent/parentdrivers?includeCarpool=true` |\n| student calendar | `GET Student/StudentCalendar?schoolCode=&studentId=&startDate=&endDate=` |\n| pickup changes | `GET PickupChange/GetChanges?studentId=&date=` |\n| dismissal locations | `GET dismissal/getDismissalLocations` |\n| school info | `GET SchoolInfo/GetBasicSchoolInfo?schoolCode=` |\n| reachability (no auth) | `GET version?schoolCode=` |\n\n## Scope\n\nParent tokens are parent-scoped: the admin surface (visitor management, carline\noperations, reports, bus routing) returns `403`. This is expected, not a bug.\n\nNever echo the password or the bearer token into a shared transcript.\n\nFile v1.0.1:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"schoolpass-curl\",\n  \"version\": \"1.0.1\",\n  \"publishedAt\": 1789964053646\n}\n\nFile v1.0.1:references/requests.md\n\n# Ready-to-run requests\n\nShapes come from the SchoolPass Swagger spec and `docs/SCHOOLPASS-API.md`. Run\nthe live check (`node --env-file=.env scripts/live-check.mjs`) once to confirm\nthe auth-response fields on your account before trusting the `jq` recipes below.\n\n## Helper — source this first\n\n```bash\n: \"${SCHOOLPASS_SCHOOL_CODE:?export SCHOOLPASS_SCHOOL_CODE first}\"\n: \"${SCHOOLPASS_EMAIL:?}\"; : \"${SCHOOLPASS_PASSWORD:?}\"\nSP_HOST=\"${SCHOOLPASS_API_HOST:-busapi-east16-ss.school-pass.net}\"\nSP_BASE=\"https://${SP_HOST}/api\"\n# Token cache for this skill (NOT the MCP server's store — the server keeps none\n# on disk, but keep this skill's state self-contained regardless).\nSP_SESSION=\"${SCHOOLPASS_CURL_SESSION:-$HOME/.schoolpass-mcp/curl-token.json}\"\n\n# sp_curl <method> <path> [body-json] [-- extra-curl-args...]\n# Adds the AppCode header, Authorization (if $SP_TOKEN set), and JSON accept.\nsp_curl() {\n  local method=\"$1\" path=\"$2\" body=\"${3:-}\"\n  shift 2; [ $# -gt 0 ] && shift\n  [ \"${1:-}\" = \"--\" ] && shift\n  curl -sS -X \"$method\" \"${SP_BASE}/${path}\" \\\n    -H \"AppCode: ${SCHOOLPASS_SCHOOL_CODE}\" \\\n    -H \"accept: application/json\" \\\n    -H \"content-type: application/json\" \\\n    ${SP_TOKEN:+-H \"Authorization: Bearer ${SP_TOKEN}\"} \\\n    ${body:+--data \"$body\"} \"$@\"\n}\n\n# sp_login: Auth/users -> pick Parent identity -> Auth/token -> export SP_TOKEN.\n# Verify the response field names against your account once (live-check) — the\n# access-token field is `access_token`; adjust the jq path if yours differs.\nsp_login() {\n  local users uid utype token\n  users=$(sp_curl POST Auth/users \"$(jq -nc \\\n    --argjson sc \"$SCHOOLPASS_SCHOOL_CODE\" --arg em \"$SCHOOLPASS_EMAIL\" --arg pw \"$SCHOOLPASS_PASSWORD\" \\\n    '{schoolCode:$sc,email:$em,password:$pw,ssoToken:null,authType:\"Credentials\"}')\") || return 1\n  # Pick the Parent identity (userType 3), else the sole identity. Field names\n  # vary (userId|id, userType|type) — normalize.\n  uid=$(echo \"$users\" | jq -r '(if type==\"array\" then . else (.users // .data // [.]) end)\n      | map({id:(.userId // .id), t:(.userType // .type)})\n      | (map(select(.t==3))[0] // .[0]) | .id')\n  utype=$(echo \"$users\" | jq -r '(if type==\"array\" then . else (.users // .data // [.]) end)\n      | map({id:(.userId // .id), t:(.userType // .type)})\n      | (map(select(.t==3))[0] // .[0]) | .t')\n  token=$(sp_curl POST Auth/token \"$(jq -nc \\\n    --argjson sc \"$SCHOOLPASS_SCHOOL_CODE\" --argjson uid \"$uid\" --argjson ut \"$utype\" --arg pw \"$SCHOOLPASS_PASSWORD\" \\\n    '{schoolCode:$sc,userId:$uid,userType:$ut,password:$pw,ssoToken:null,authType:\"Credentials\"}')\" \\\n    | jq -r '.access_token // .payload.access_token // .accessToken')\n  [ -n \"$token\" ] && [ \"$token\" != \"null\" ] || { echo \"login failed\" >&2; return 1; }\n  export SP_TOKEN=\"$token\" SP_MEMBER_ID=\"$uid\"\n  mkdir -p \"$(dirname \"$SP_SESSION\")\" && chmod 700 \"$(dirname \"$SP_SESSION\")\"\n  printf '{\"memberId\":%s}\\n' \"$uid\" > \"$SP_SESSION\"   # never write the token to disk\n}\n```\n\n## Reachability (no auth)\n\n```bash\nsp_curl GET \"version?schoolCode=${SCHOOLPASS_SCHOOL_CODE}\"\n# -> \"Host:…,Version:5.0.4.7602,…,School:SprAPiServer 1183 Scholars Academy\"\n```\n\n## Reads (after `sp_login`)\n\n```bash\n# Students — id, name, grade, home dismissal location.\nsp_curl GET \"parent/getstudents?memberId=${SP_MEMBER_ID}\" \\\n  | jq '.[] | {id, name:\"\\(.firstName) \\(.lastName)\", gradeId, dismissalLocationId, aftercare}'\n\n# Parent profile.\nsp_curl GET \"parent/profile?memberId=${SP_MEMBER_ID}\"\n\n# Authorized drivers, with carpools.\nsp_curl GET \"parent/parentdrivers?memberId=${SP_MEMBER_ID}&includeCarpool=true\"\n\n# Dismissal locations — the vocabulary a change refers to.\nsp_curl GET \"dismissal/getDismissalLocations\" \\\n  | jq '.[] | {id, name, locationTypeId, dismissalSessionTimeId}'\n\n# A student's arrival/dismissal calendar for a date range.\nSTU=12345; TODAY=$(date +%F); IN2W=$(date -v+14d +%F 2>/dev/null || date -d '+14 days' +%F)\nsp_curl GET \"Student/StudentCalendar?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&studentId=${STU}&startDate=${TODAY}&endDate=${IN2W}\"\n\n# Pickup / dismissal changes for a student today.\nsp_curl GET \"PickupChange/GetChanges?studentId=${STU}&date=${TODAY}\"\n\n# School info + config.\nsp_curl GET \"SchoolInfo/GetBasicSchoolInfo?schoolCode=${SCHOOLPASS_SCHOOL_CODE}\"\nsp_curl GET \"Config/configsettings?schoolCode=${SCHOOLPASS_SCHOOL_CODE}\"\n```\n\n## Notes\n\n- A `403` on any read means your parent token cannot reach that route (it is\n  admin-only). Expected — not a login problem.\n- The `jq` field paths above follow the Swagger shapes; if the live check shows a\n  different envelope on your account, adjust the paths and update this file.\n\n## Writes (verified live)\n\nSubmit a dismissal/arrival change — `POST studentchange`. The body must match the\napp exactly: `dateSet.dates` EMPTY, `daysOfWeek` as NUMERIC ids (Monday=1…Sunday=7),\n`modifiedBy` = your parent member id (= `parentMemberId`), `changeType` from the E2\nenum (Absent=1, LateArrival=2, EarlyDismissal=3, Carpool=4, Activity=5, Bus=6).\n\n```bash\nSTU=11278; DATE=2026-09-14; DOW=1   # DOW: Mon=1..Sun=7 for $DATE\nsp_curl POST \"studentchange?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&parentMemberId=${SP_MEMBER_ID}\" \"$(jq -nc \\\n  --argjson sid \"$STU\" --arg date \"$DATE\" --argjson dow \"$DOW\" --argjson mid \"$SP_MEMBER_ID\" '{\n    studentId:$sid, moveToId:null, busStopId:null,\n    dateSet:{dates:[], daysOfWeek:[$dow], startDate:$date, endDate:$date, recurringWeeks:0},\n    notes:\"\", pickupDropoffPerson:null, willReturn:false, timeOfDay:null,\n    changeSeriesId:0, changeType:1, adType:3, userType:3, modifiedBy:$mid }')\"\n# Verify: re-read the calendar; a non-default entry (isDefault:false, changeSeriesId set) appears.\nsp_curl GET \"Student/StudentCalendar?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&studentId=${STU}&startDate=${DATE}&endDate=${DATE}\" | jq '.dailyList'\n```\n\nCancel a change — `DELETE studentchange/DeleteMobileChange`, keyed on the\n`changeSeriesId` from the calendar:\n\n```bash\nCSID=27074   # from the calendar entry's changeSeriesId\nsp_curl DELETE \"studentchange/DeleteMobileChange?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&ChangeSeriesId=${CSID}&ChangeType=1&ADType=4&dt=${DATE}\"\n```\n\n- `moveToId` for a Carpool move (changeType 4) is a **carpool id**; moving to the\n  carpool the student is already in returns 500.\n- A 2xx is not proof — re-read the calendar to confirm the change (or its removal).\n\nFile v1.0.1:skill-card.md\n\n## Description:\n\nRead a SchoolPass parent account directly with curl against the regional SchoolPass REST API for one-off shell access to students, arrival and dismissal calendars, pickup changes, drivers, dismissal locations, and school information.\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, operators, and authorized SchoolPass parent-account users use this skill to generate curl and jq commands for inspecting their own SchoolPass account data from a shell. It is best suited for one-off checks and scripted reads, not repeated conversational workflows.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The reference guide includes POST and DELETE examples that can create or cancel live student arrival or dismissal changes.\n\nMitigation: Run write or delete examples only after confirming the student, school, date, change type, identifier, and intended outcome; prefer read-only commands for routine checks.\n\nRisk: Shell commands require SchoolPass parent-account credentials and bearer tokens.\n\nMitigation: Keep credentials in local environment variables, avoid echoing passwords or bearer tokens into shared transcripts, and retry rejected logins only after correcting credentials.\n\n## Reference(s):\n\n- [Ready-to-run requests](references/requests.md)\n- [ClawHub skill page](https://clawhub.ai/chrischall/skills/schoolpass-curl)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Configuration, Guidance]\n\n**Output Format:** [Markdown with inline bash and jq examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Requires user-provided SchoolPass credentials, school code, and regional API host.]\n\n## Skill Version(s):\n\n1.0.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.\n\nArchive v1.0.0: 4 files, 6392 bytes\n\nFiles: references/requests.md (6390b), skill-card.md (2591b), SKILL.md (3519b), _meta.json (134b)\n\nFile v1.0.0:SKILL.md\n\n---\nname: schoolpass-curl\ndescription: Read a SchoolPass parent account directly with curl against the regional SchoolPass REST API (a busapi shard on school-pass.net), without running the MCP server. Use for a one-off shell read of your students, arrival/dismissal calendar, pending pickup changes, drivers, dismissal locations, or school info — \"check SchoolPass from the terminal\", \"list my kids in SchoolPass\", \"any dismissal changes today\". Requires SCHOOLPASS_EMAIL / SCHOOLPASS_PASSWORD / SCHOOLPASS_SCHOOL_CODE.\n---\n\n# SchoolPass API via curl\n\nThe SchoolPass JSON API is reachable server-side — no browser, no bridge, no\nextension. This skill talks to it directly with `curl`.\n\nPrefer the `schoolpass-mcp` server for anything conversational or repeated; use\nthis for one-off shell work, scripts, or when the server isn't running.\n\n**Ready-to-run request bodies and `jq` recipes: `references/requests.md`.**\nFull shape reference: `../../docs/SCHOOLPASS-API.md`.\n\n## Setup\n\n```bash\nexport SCHOOLPASS_EMAIL='you@example.com'\nexport SCHOOLPASS_PASSWORD='…'\nexport SCHOOLPASS_SCHOOL_CODE=1183          # your school id (the AppCode)\nexport SCHOOLPASS_API_HOST=busapi-east16-ss.school-pass.net   # your region's shard\n```\n\nFind your school id and region host by signing into your school's\n`<school>.school-pass.net` portal, opening the new SchoolPass app, and reading\n`appCode` (the id) and `apiUrl` (the host) from its `localStorage`.\n\n## Two rules\n\n1. **Every request needs an `AppCode: <schoolCode>` header.** It selects your\n   school. Omit it (or send the wrong one) and you get `401`. It is not a\n   secret. `references/requests.md`'s `sp_curl` helper adds it for you.\n2. **HTTP status codes are real.** `401` = the token or AppCode was rejected,\n   `403` = a parent account cannot reach that route (it is admin-only), `2xx` =\n   success. Branch on the HTTP code.\n\n## Auth: email/password → bearer\n\n1. `POST Auth/users` with `{schoolCode,email,password,authType:\"Credentials\"}`\n   → the identities your email owns. Take the one whose `userType` is `3`\n   (Parent).\n2. `POST Auth/token` with `{schoolCode,userId,userType,password,authType:\"Credentials\"}`\n   → `{ access_token, refresh_token, … }`. Send `access_token` as\n   `Authorization: Bearer …` on every subsequent call.\n\n`references/requests.md`'s `sp_login` does both and caches the token.\n\n> **Never retry a rejected login.** SchoolPass fronts its login with reCAPTCHA;\n> repeated wrong passwords can get the account challenged and break shell login.\n> Fix the credential and try once.\n\n## Reads\n\nAll are `GET` with the `AppCode` header + `Authorization: Bearer`. Most parent\nendpoints take an optional `memberId` (your own parent id, from `Auth/users`).\n\n| Want | Endpoint |\n| --- | --- |\n| your students | `GET parent/getstudents` |\n| parent profile | `GET parent/profile` |\n| authorized drivers | `GET parent/parentdrivers?includeCarpool=true` |\n| student calendar | `GET Student/StudentCalendar?schoolCode=&studentId=&startDate=&endDate=` |\n| pickup changes | `GET PickupChange/GetChanges?studentId=&date=` |\n| dismissal locations | `GET dismissal/getDismissalLocations` |\n| school info | `GET SchoolInfo/GetBasicSchoolInfo?schoolCode=` |\n| reachability (no auth) | `GET version?schoolCode=` |\n\n## Scope\n\nParent tokens are parent-scoped: the admin surface (visitor management, carline\noperations, reports, bus routing) returns `403`. This is expected, not a bug.\n\nNever echo the password or the bearer token into a shared transcript.\n\nFile v1.0.0:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"schoolpass-curl\",\n  \"version\": \"1.0.0\",\n  \"publishedAt\": 1789872747154\n}\n\nFile v1.0.0:references/requests.md\n\n# Ready-to-run requests\n\nShapes come from the SchoolPass Swagger spec and `docs/SCHOOLPASS-API.md`. Run\nthe live check (`node --env-file=.env scripts/live-check.mjs`) once to confirm\nthe auth-response fields on your account before trusting the `jq` recipes below.\n\n## Helper — source this first\n\n```bash\n: \"${SCHOOLPASS_SCHOOL_CODE:?export SCHOOLPASS_SCHOOL_CODE first}\"\n: \"${SCHOOLPASS_EMAIL:?}\"; : \"${SCHOOLPASS_PASSWORD:?}\"\nSP_HOST=\"${SCHOOLPASS_API_HOST:-busapi-east16-ss.school-pass.net}\"\nSP_BASE=\"https://${SP_HOST}/api\"\n# Token cache for this skill (NOT the MCP server's store — the server keeps none\n# on disk, but keep this skill's state self-contained regardless).\nSP_SESSION=\"${SCHOOLPASS_CURL_SESSION:-$HOME/.schoolpass-mcp/curl-token.json}\"\n\n# sp_curl <method> <path> [body-json] [-- extra-curl-args...]\n# Adds the AppCode header, Authorization (if $SP_TOKEN set), and JSON accept.\nsp_curl() {\n  local method=\"$1\" path=\"$2\" body=\"${3:-}\"\n  shift 2; [ $# -gt 0 ] && shift\n  [ \"${1:-}\" = \"--\" ] && shift\n  curl -sS -X \"$method\" \"${SP_BASE}/${path}\" \\\n    -H \"AppCode: ${SCHOOLPASS_SCHOOL_CODE}\" \\\n    -H \"accept: application/json\" \\\n    -H \"content-type: application/json\" \\\n    ${SP_TOKEN:+-H \"Authorization: Bearer ${SP_TOKEN}\"} \\\n    ${body:+--data \"$body\"} \"$@\"\n}\n\n# sp_login: Auth/users -> pick Parent identity -> Auth/token -> export SP_TOKEN.\n# Verify the response field names against your account once (live-check) — the\n# access-token field is `access_token`; adjust the jq path if yours differs.\nsp_login() {\n  local users uid utype token\n  users=$(sp_curl POST Auth/users \"$(jq -nc \\\n    --argjson sc \"$SCHOOLPASS_SCHOOL_CODE\" --arg em \"$SCHOOLPASS_EMAIL\" --arg pw \"$SCHOOLPASS_PASSWORD\" \\\n    '{schoolCode:$sc,email:$em,password:$pw,ssoToken:null,authType:\"Credentials\"}')\") || return 1\n  # Pick the Parent identity (userType 3), else the sole identity. Field names\n  # vary (userId|id, userType|type) — normalize.\n  uid=$(echo \"$users\" | jq -r '(if type==\"array\" then . else (.users // .data // [.]) end)\n      | map({id:(.userId // .id), t:(.userType // .type)})\n      | (map(select(.t==3))[0] // .[0]) | .id')\n  utype=$(echo \"$users\" | jq -r '(if type==\"array\" then . else (.users // .data // [.]) end)\n      | map({id:(.userId // .id), t:(.userType // .type)})\n      | (map(select(.t==3))[0] // .[0]) | .t')\n  token=$(sp_curl POST Auth/token \"$(jq -nc \\\n    --argjson sc \"$SCHOOLPASS_SCHOOL_CODE\" --argjson uid \"$uid\" --argjson ut \"$utype\" --arg pw \"$SCHOOLPASS_PASSWORD\" \\\n    '{schoolCode:$sc,userId:$uid,userType:$ut,password:$pw,ssoToken:null,authType:\"Credentials\"}')\" \\\n    | jq -r '.access_token // .payload.access_token // .accessToken')\n  [ -n \"$token\" ] && [ \"$token\" != \"null\" ] || { echo \"login failed\" >&2; return 1; }\n  export SP_TOKEN=\"$token\" SP_MEMBER_ID=\"$uid\"\n  mkdir -p \"$(dirname \"$SP_SESSION\")\" && chmod 700 \"$(dirname \"$SP_SESSION\")\"\n  printf '{\"memberId\":%s}\\n' \"$uid\" > \"$SP_SESSION\"   # never write the token to disk\n}\n```\n\n## Reachability (no auth)\n\n```bash\nsp_curl GET \"version?schoolCode=${SCHOOLPASS_SCHOOL_CODE}\"\n# -> \"Host:…,Version:5.0.4.7602,…,School:SprAPiServer 1183 Scholars Academy\"\n```\n\n## Reads (after `sp_login`)\n\n```bash\n# Students — id, name, grade, home dismissal location.\nsp_curl GET \"parent/getstudents?memberId=${SP_MEMBER_ID}\" \\\n  | jq '.[] | {id, name:\"\\(.firstName) \\(.lastName)\", gradeId, dismissalLocationId, aftercare}'\n\n# Parent profile.\nsp_curl GET \"parent/profile?memberId=${SP_MEMBER_ID}\"\n\n# Authorized drivers, with carpools.\nsp_curl GET \"parent/parentdrivers?memberId=${SP_MEMBER_ID}&includeCarpool=true\"\n\n# Dismissal locations — the vocabulary a change refers to.\nsp_curl GET \"dismissal/getDismissalLocations\" \\\n  | jq '.[] | {id, name, locationTypeId, dismissalSessionTimeId}'\n\n# A student's arrival/dismissal calendar for a date range.\nSTU=12345; TODAY=$(date +%F); IN2W=$(date -v+14d +%F 2>/dev/null || date -d '+14 days' +%F)\nsp_curl GET \"Student/StudentCalendar?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&studentId=${STU}&startDate=${TODAY}&endDate=${IN2W}\"\n\n# Pickup / dismissal changes for a student today.\nsp_curl GET \"PickupChange/GetChanges?studentId=${STU}&date=${TODAY}\"\n\n# School info + config.\nsp_curl GET \"SchoolInfo/GetBasicSchoolInfo?schoolCode=${SCHOOLPASS_SCHOOL_CODE}\"\nsp_curl GET \"Config/configsettings?schoolCode=${SCHOOLPASS_SCHOOL_CODE}\"\n```\n\n## Notes\n\n- A `403` on any read means your parent token cannot reach that route (it is\n  admin-only). Expected — not a login problem.\n- The `jq` field paths above follow the Swagger shapes; if the live check shows a\n  different envelope on your account, adjust the paths and update this file.\n\n## Writes (verified live)\n\nSubmit a dismissal/arrival change — `POST studentchange`. The body must match the\napp exactly: `dateSet.dates` EMPTY, `daysOfWeek` as NUMERIC ids (Monday=1…Sunday=7),\n`modifiedBy` = your parent member id (= `parentMemberId`), `changeType` from the E2\nenum (Absent=1, LateArrival=2, EarlyDismissal=3, Carpool=4, Activity=5, Bus=6).\n\n```bash\nSTU=11278; DATE=2026-09-14; DOW=1   # DOW: Mon=1..Sun=7 for $DATE\nsp_curl POST \"studentchange?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&parentMemberId=${SP_MEMBER_ID}\" \"$(jq -nc \\\n  --argjson sid \"$STU\" --arg date \"$DATE\" --argjson dow \"$DOW\" --argjson mid \"$SP_MEMBER_ID\" '{\n    studentId:$sid, moveToId:null, busStopId:null,\n    dateSet:{dates:[], daysOfWeek:[$dow], startDate:$date, endDate:$date, recurringWeeks:0},\n    notes:\"\", pickupDropoffPerson:null, willReturn:false, timeOfDay:null,\n    changeSeriesId:0, changeType:1, adType:3, userType:3, modifiedBy:$mid }')\"\n# Verify: re-read the calendar; a non-default entry (isDefault:false, changeSeriesId set) appears.\nsp_curl GET \"Student/StudentCalendar?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&studentId=${STU}&startDate=${DATE}&endDate=${DATE}\" | jq '.dailyList'\n```\n\nCancel a change — `DELETE studentchange/DeleteMobileChange`, keyed on the\n`changeSeriesId` from the calendar:\n\n```bash\nCSID=27074   # from the calendar entry's changeSeriesId\nsp_curl DELETE \"studentchange/DeleteMobileChange?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&ChangeSeriesId=${CSID}&ChangeType=1&ADType=4&dt=${DATE}\"\n```\n\n- `moveToId` for a Carpool move (changeType 4) is a **carpool id**; moving to the\n  carpool the student is already in returns 500.\n- A 2xx is not proof — re-read the calendar to confirm the change (or its removal).\n\nFile v1.0.0:skill-card.md\n\n## Description:\n\nRead a SchoolPass parent account directly with curl against the regional SchoolPass REST API for one-off checks of students, arrival and dismissal calendars, pickup changes, drivers, dismissal locations, and school information.\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, operators, and SchoolPass parent-account users can use this skill to ask an agent for curl and jq-based SchoolPass API commands for one-off account reads. It is intended for live SchoolPass credentials and should be used carefully around student, parent, token, and password data.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The skill can guide an agent to use SchoolPass credentials against live SchoolPass APIs, exposing sensitive student, parent, password, and token data in transcripts or environment variables.\n\nMitigation: Use only in trusted workspaces, keep credentials out of shared transcripts, avoid printing bearer tokens or passwords, and clear shell history or logs that may contain sensitive values.\n\nRisk: Referenced examples include commands that can create or delete student dismissal or arrival changes.\n\nMitigation: Treat write and delete examples as high-impact operations; require explicit user confirmation and re-read the calendar or change record after execution to verify the intended result.\n\nRisk: Repeated rejected logins may trigger SchoolPass reCAPTCHA or account challenges.\n\nMitigation: Do not retry failed logins automatically; correct the credential or school code before trying again.\n\n## Reference(s):\n\n- [Ready-to-run requests](references/requests.md)\n- [ClawHub skill page](https://clawhub.ai/chrischall/skills/schoolpass-curl)\n- [Publisher profile](https://clawhub.ai/user/chrischall)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Configuration instructions, API Calls, Code, Guidance]\n\n**Output Format:** [Markdown with inline bash, curl, jq, and JSON examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [May include commands that read or modify live SchoolPass data; users should review commands before execution.]\n\n## Skill Version(s):\n\n1.0.0 (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: schoolpass-curl Owner: chrischall Summary: Read a SchoolPass parent account directly with curl against the regional SchoolPass REST API (a busapi shard on school-pass.net), without running the MCP server. Use for a one-off shell read of your students, arrival/dismissal calendar, pending pickup changes, drivers, dismissal locations, or school info — \"check SchoolPass from the terminal\", \"list my kids in SchoolP","codeSnippets":[],"executableExamples":[{"language":"bash","snippet":"export SCHOOLPASS_EMAIL='you@example.com'\nexport SCHOOLPASS_PASSWORD='…'\nexport SCHOOLPASS_SCHOOL_CODE=1183          # your school id (the AppCode)\nexport SCHOOLPASS_API_HOST=busapi-east16-ss.school-pass.net   # your region's shard"},{"language":"bash","snippet":"curl -sS -X \"$method\" \"${SP_BASE}/${path}\" \\\n    -H \"AppCode: ${SCHOOLPASS_SCHOOL_CODE}\" \\\n    -H \"accept: application/json\" \\\n    -H \"content-type: application/json\" \\"},{"language":"bash","snippet":": \"${SCHOOLPASS_SCHOOL_CODE:?export SCHOOLPASS_SCHOOL_CODE first}\"\n: \"${SCHOOLPASS_EMAIL:?}\"; : \"${SCHOOLPASS_PASSWORD:?}\"\nSP_HOST=\"${SCHOOLPASS_API_HOST:-busapi-east16-ss.school-pass.net}\"\nSP_BASE=\"https://${SP_HOST}/api\"\n# Token cache for this skill (NOT the MCP server's store — the server keeps none\n# on disk, but keep this skill's state self-contained regardless).\nSP_SESSION=\"${SCHOOLPASS_CURL_SESSION:-$HOME/.schoolpass-mcp/curl-token.json}\"\n\n# sp_curl <method> <path> [body-json] [-- extra-curl-args...]\n# Adds the AppCode header, Authorization (if $SP_TOKEN set), and JSON accept.\nsp_curl() {\n  local method=\"$1\" path=\"$2\" body=\"${3:-}\"\n  shift 2; [ $# -gt 0 ] && shift\n  [ \"${1:-}\" = \"--\" ] && shift\n  curl -sS -X \"$method\" \"${SP_BASE}/${path}\" \\\n    -H \"AppCode: ${SCHOOLPASS_SCHOOL_CODE}\" \\\n    -H \"accept: application/json\" \\\n    -H \"content-type: application/json\" \\\n    ${SP_TOKEN:+-H \"Authorization: Bearer ${SP_TOKEN}\"} \\\n    ${body:+--data \"$body\"} \"$@\"\n}\n\n# sp_login: Auth/users -> pick Parent identity -> Auth/token -> export SP_TOKEN.\n# Verify the response field names against your account once (live-check) — the\n# access-token field is `access_token`; adjust the jq path if yours differs.\nsp_login() {\n  local users uid utype token\n  users=$(sp_curl POST Auth/users \"$(jq -nc \\\n    --argjson sc \"$SCHOOLPASS_SCHOOL_CODE\" --arg em \"$SCHOOLPASS_EMAIL\" --arg pw \"$SCHOOLPASS_PASSWORD\" \\\n    '{schoolCode:$sc,email:$em,password:$pw,ssoToken:null,authType:\"Credentials\"}')\") || return 1\n  # Pick the Parent identity (userType 3), else the sole identity. Field names\n  # vary (userId|id, userType|type) — normalize.\n  uid=$(echo \"$users\" | jq -r '(if type==\"array\" then . else (.users // .data // [.]) end)\n      | map({id:(.userId // .id), t:(.userType // .type)})\n      | (map(select(.t==3))[0] // .[0]) | .id')\n  utype=$(echo \"$users\" | jq -r '(if type==\"array\" then . else (.users // .data // [.]) end)\n      | map({id:(.userId // .id), t:(.userType // .type)})\n      | (map(sel"},{"language":"bash","snippet":"sp_curl GET \"version?schoolCode=${SCHOOLPASS_SCHOOL_CODE}\"\n# -> \"Host:…,Version:5.0.4.7602,…,School:SprAPiServer 1183 Scholars Academy\""},{"language":"bash","snippet":"# Students — id, name, grade, home dismissal location.\nsp_curl GET \"parent/getstudents?memberId=${SP_MEMBER_ID}\" \\\n  | jq '.[] | {id, name:\"\\(.firstName) \\(.lastName)\", gradeId, dismissalLocationId, aftercare}'\n\n# Parent profile.\nsp_curl GET \"parent/profile?memberId=${SP_MEMBER_ID}\"\n\n# Authorized drivers, with carpools.\nsp_curl GET \"parent/parentdrivers?memberId=${SP_MEMBER_ID}&includeCarpool=true\"\n\n# Dismissal locations — the vocabulary a change refers to.\nsp_curl GET \"dismissal/getDismissalLocations\" \\\n  | jq '.[] | {id, name, locationTypeId, dismissalSessionTimeId}'\n\n# A student's arrival/dismissal calendar for a date range.\nSTU=12345; TODAY=$(date +%F); IN2W=$(date -v+14d +%F 2>/dev/null || date -d '+14 days' +%F)\nsp_curl GET \"Student/StudentCalendar?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&studentId=${STU}&startDate=${TODAY}&endDate=${IN2W}\"\n\n# Pickup / dismissal changes for a student today.\nsp_curl GET \"PickupChange/GetChanges?studentId=${STU}&date=${TODAY}\"\n\n# School info + config.\nsp_curl GET \"SchoolInfo/GetBasicSchoolInfo?schoolCode=${SCHOOLPASS_SCHOOL_CODE}\"\nsp_curl GET \"Config/configsettings?schoolCode=${SCHOOLPASS_SCHOOL_CODE}\""},{"language":"bash","snippet":"STU=11278; DATE=2026-09-14; DOW=1   # DOW: Mon=1..Sun=7 for $DATE\nsp_curl POST \"studentchange?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&parentMemberId=${SP_MEMBER_ID}\" \"$(jq -nc \\\n  --argjson sid \"$STU\" --arg date \"$DATE\" --argjson dow \"$DOW\" --argjson mid \"$SP_MEMBER_ID\" '{\n    studentId:$sid, moveToId:null, busStopId:null,\n    dateSet:{dates:[], daysOfWeek:[$dow], startDate:$date, endDate:$date, recurringWeeks:0},\n    notes:\"\", pickupDropoffPerson:null, willReturn:false, timeOfDay:null,\n    changeSeriesId:0, changeType:1, adType:3, userType:3, modifiedBy:$mid }')\"\n# Verify: re-read the calendar; a non-default entry (isDefault:false, changeSeriesId set) appears.\nsp_curl GET \"Student/StudentCalendar?schoolCode=${SCHOOLPASS_SCHOOL_CODE}&studentId=${STU}&startDate=${DATE}&endDate=${DATE}\" | jq '.dailyList'"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: schoolpass-curl\ndescription: Read a SchoolPass parent account directly with curl against the regional SchoolPass REST API (a busapi shard on school-pass.net), without running the MCP server. Use for a one-off shell read of your students, arrival/dismissal calendar, pending pickup changes, drivers, dismissal locations, or school info — \"check SchoolPass from the terminal\", \"list my kids in SchoolPass\", \"any dismissal changes today\". Requires SCHOOLPASS_EMAIL / SCHOOLPASS_PASSWORD / SCHOOLPASS_SCHOOL_CODE.\n---\n\n# SchoolPass API via curl\n\nThe SchoolPass JSON API is reachable server-side — no browser, no bridge, no\nextension. This skill talks to it directly with `curl`.\n\nPrefer the `schoolpass-mcp` server for anything conversational or repeated; use\nthis for one-off shell work, scripts, or when the server isn't running.\n\n**Ready-to-run request bodies and `jq` recipes: `references/requests.md`.**\nFull shape reference: `../../docs/SCHOOLPASS-API.md`.\n\n## Setup\n\n```bash\nexport SCHOOLPASS_EMAIL='you@example.com'\nexport SCHOOLPASS_PASSWORD='…'\nexport SCHOOLPASS_SCHOOL_CODE=1183          # your school id (the AppCode)\nexport SCHOOLPASS_API_HOST=busapi-east16-ss.school-pass.net   # your region's shard\n```\n\nFind your school id and region host by signing into your school's\n`<school>.school-pass.net` portal, opening the new SchoolPass app, and reading\n`appCode` (the id) and `apiUrl` (the host) from its `localStorage`.\n\n## Two rules\n\n1. **Every request needs an `AppCode: <schoolCode>` header.** It selects your\n   school. Omit it (or send the wrong one) and you get `401`. It is not a\n   secret. `references/requests.md`'s `sp_curl` helper adds it for you.\n2. **HTTP status codes are real.** `401` = the token or AppCode was rejected,\n   `403` = a parent account cannot reach that route (it is admin-only), `2xx` =\n   success. Branch on the HTTP code.\n\n## Auth: email/password → bearer\n\n1. `POST Auth/users` with `{schoolCode,email,password,authType:\"Credentials\"}`\n   → the identities your email owns. Take the one whose `userType` is `3`\n   (Parent).\n2. `POST Auth/token` with `{schoolCode,userId,userType,password,authType:\"Credentials\"}`\n   → `{ access_token, refresh_token, … }`. Send `access_token` as\n   `Authorization: Bearer …` on every subsequent call.\n\n`references/requests.md`'s `sp_login` does both and caches the token.\n\n> **Never retry a rejected login.** SchoolPass fronts its login with reCAPTCHA;\n> repeated wrong passwords can get the account challenged and break shell login.\n> Fix the credential and try once.\n\n## Reads\n\nAll are `GET` with the `AppCode` header + `Authorization: Bearer`. Most parent\nendpoints take an optional `memberId` (your own parent id, from `Auth/users`).\n\n| Want | Endpoint |\n| --- | --- |\n| your students | `GET parent/getstudents` |\n| parent profile | `GET parent/profile` |\n| authorized drivers | `GET parent/parentdrivers?includeCarpool=true` |\n| student calendar | `GET Student/StudentCalendar?schoolCode=&studentId=&startDate=&en"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"schoolpass-curl\",\n  \"version\": \"1.0.9\",\n  \"publishedAt\": 1791588422839\n}"},{"path":"references/requests.md","content":"# Ready-to-run requests\n\nShapes come from the SchoolPass Swagger spec and `docs/SCHOOLPASS-API.md`. Run\nthe live check (`node --env-file=.env scripts/live-check.mjs`) once to confirm\nthe auth-response fields on your account before trusting the `jq` recipes below.\n\n## Helper — source this first\n\n```bash\n: \"${SCHOOLPASS_SCHOOL_CODE:?export SCHOOLPASS_SCHOOL_CODE first}\"\n: \"${SCHOOLPASS_EMAIL:?}\"; : \"${SCHOOLPASS_PASSWORD:?}\"\nSP_HOST=\"${SCHOOLPASS_API_HOST:-busapi-east16-ss.school-pass.net}\"\nSP_BASE=\"https://${SP_HOST}/api\"\n# Token cache for this skill (NOT the MCP server's store — the server keeps none\n# on disk, but keep this skill's state self-contained regardless).\nSP_SESSION=\"${SCHOOLPASS_CURL_SESSION:-$HOME/.schoolpass-mcp/curl-token.json}\"\n\n# sp_curl <method> <path> [body-json] [-- extra-curl-args...]\n# Adds the AppCode header, Authorization (if $SP_TOKEN set), and JSON accept.\nsp_curl() {\n  local method=\"$1\" path=\"$2\" body=\"${3:-}\"\n  shift 2; [ $# -gt 0 ] && shift\n  [ \"${1:-}\" = \"--\" ] && shift\n  curl -sS -X \"$method\" \"${SP_BASE}/${path}\" \\\n    -H \"AppCode: ${SCHOOLPASS_SCHOOL_CODE}\" \\\n    -H \"accept: application/json\" \\\n    -H \"content-type: application/json\" \\\n    ${SP_TOKEN:+-H \"Authorization: Bearer ${SP_TOKEN}\"} \\\n    ${body:+--data \"$body\"} \"$@\"\n}\n\n# sp_login: Auth/users -> pick Parent identity -> Auth/token -> export SP_TOKEN.\n# Verify the response field names against your account once (live-check) — the\n# access-token field is `access_token`; adjust the jq path if yours differs.\nsp_login() {\n  local users uid utype token\n  users=$(sp_curl POST Auth/users \"$(jq -nc \\\n    --argjson sc \"$SCHOOLPASS_SCHOOL_CODE\" --arg em \"$SCHOOLPASS_EMAIL\" --arg pw \"$SCHOOLPASS_PASSWORD\" \\\n    '{schoolCode:$sc,email:$em,password:$pw,ssoToken:null,authType:\"Credentials\"}')\") || return 1\n  # Pick the Parent identity (userType 3), else the sole identity. Field names\n  # vary (userId|id, userType|type) — normalize.\n  uid=$(echo \"$users\" | jq -r '(if type==\"array\" then . else (.users // .data // [.]) end)\n      | map({id:(.userId // .id), t:(.userType // .type)})\n      | (map(select(.t==3))[0] // .[0]) | .id')\n  utype=$(echo \"$users\" | jq -r '(if type==\"array\" then . else (.users // .data // [.]) end)\n      | map({id:(.userId // .id), t:(.userType // .type)})\n      | (map(select(.t==3))[0] // .[0]) | .t')\n  token=$(sp_curl POST Auth/token \"$(jq -nc \\\n    --argjson sc \"$SCHOOLPASS_SCHOOL_CODE\" --argjson uid \"$uid\" --argjson ut \"$utype\" --arg pw \"$SCHOOLPASS_PASSWORD\" \\\n    '{schoolCode:$sc,userId:$uid,userType:$ut,password:$pw,ssoToken:null,authType:\"Credentials\"}')\" \\\n    | jq -r '.access_token // .payload.access_token // .accessToken')\n  [ -n \"$token\" ] && [ \"$token\" != \"null\" ] || { echo \"login failed\" >&2; return 1; }\n  export SP_TOKEN=\"$token\" SP_MEMBER_ID=\"$uid\"\n  mkdir -p \"$(dirname \"$SP_SESSION\")\" && chmod 700 \"$(dirname \"$SP_SESSION\")\"\n  printf '{\"memberId\":%s}\\n' \"$uid\" > \"$SP_SESSION\"   # never write the token to disk\n}\n```\n\n## Reachability (no aut"},{"path":"skill-card.md","content":"## Description:\n\nProvides curl and jq guidance for reading a SchoolPass parent account, with separate examples for changing arrival or dismissal records.\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\nParents and authorized helpers use this skill for one-off terminal checks of students, calendars, pickup changes, drivers, dismissal locations, and school information in their SchoolPass account.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The reference guide includes live commands that can create or delete student arrival or dismissal changes despite the skill's read-focused description.\n\nMitigation: Avoid the POST and DELETE examples unless you explicitly intend to change live records; re-read the calendar to confirm any intended change.\n\nRisk: SchoolPass passwords, bearer tokens, and student information can be exposed when commands or responses are shared.\n\nMitigation: Keep credentials and tokens out of shared transcripts, and copy only appCode and apiUrl when checking browser localStorage.\n\n## Reference(s):\n\n- [SchoolPass Curl release](https://clawhub.ai/chrischall/skills/schoolpass-curl)\n- [Ready-to-run requests](references/requests.md)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Guidance]\n\n**Output Format:** [Markdown with bash and jq examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Requires a SchoolPass parent account, school code, and regional API host.]\n\n## Skill Version(s):\n\n1.0.9 (source: server-resolved release)\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":null,"editorialQuality":{"score":100,"threshold":65,"status":"thin","wordCount":1257,"uniquenessScore":44,"reasons":["uniqueness-below-45"]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-11T09:44:13.912Z","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-11T09:44:13.912Z","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-11T14:15:00.455Z","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"}]}}}