powerschool-mcp
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
powerschool-mcp
A read-only MCP server that lets Claude read a student's PowerSchool guardian portal: current grades, assignments and missing work, attendance, schedule, grade history, teacher comments, plus a few computed extras (what-if grades and an estimated GPA).
It cannot change anything in PowerSchool. Every request goes through a strict allowlist (see Read-only allowlist).
How it works
PowerSchool sign-in is district SSO, which a server can't do. So login happens on your PC:
ps-loginopens a real browser, you sign in, and it extracts only the two session cookies (JSESSIONID,psaid).It either saves them to
.auth/session.json(local stdio mode) or POSTs them to your deployed server.The server uses plain HTTPS requests with those cookies and keeps the session alive with a page request every 10 minutes (idle timeout is about 40 minutes).
When the session dies, tools return session_expired and tell you to run ps-login again.
Related MCP server: io.github.thehesiod/psquare
Tools
Tool | What it does |
| Logged in or not, seconds of idle time left, active student. Does not extend the session. |
| Students on the account and the index to pass as |
| Current letter grade and percent per class (home page), absences, tardies. |
| Assignments for one class: score, points, percent, due date, category, flags (missing, late, exempt, collected). |
| All missing or late assignments across classes. |
| Absences and tardies from the attendance plugin, with an HTML fallback. Optional |
| Weekly bell schedule plus the period matrix (teacher, room, term). |
| Stored final grades for every term on file. |
| Teacher comments per class for a term. |
| Estimated GPA on a 4.0 scale from grade history; |
| Recomputes a class percent from the real assignment data after hypothetical scores. Points-based; states its assumptions. |
Every tool takes an optional student (name or index). It defaults to PS_DEFAULT_STUDENT, then to the student PowerSchool is showing.
Multiple students: grade pages only ever show the student PowerSchool has active, and the portal's student-switch mechanism could not be found on the account used for recon (see docs/auth-notes.md). Asking for a different student returns a clear error instead of guessing.
Setup (local, stdio, Claude Desktop)
Needs Node 20+.
npm install
npx playwright install chromium # one-time, for ps-login only
copy .env.example .env # set PS_BASE_URL, optionally PS_DEFAULT_STUDENT
npm run build
npm run login # opens a browser, sign in via SSO; saves .auth/session.jsonClaude Desktop config (Settings -> Developer -> Edit Config), then restart Claude Desktop fully:
{
"mcpServers": {
"powerschool": {
"command": "node",
"args": ["C:\\path\\to\\powerschoolMCP\\dist\\main.js"],
"env": {
"PS_BASE_URL": "https://ps.yourdistrict.org",
"PS_DEFAULT_STUDENT": ""
}
}
}
}The stdio server reads .auth/session.json relative to its working directory. Claude Desktop starts servers from its own directory, so pass an absolute path with "--session-file":
"args": ["C:\\path\\to\\powerschoolMCP\\dist\\main.js", "--session-file", "C:\\path\\to\\powerschoolMCP\\.auth\\session.json"]It re-reads the file whenever ps-login --local rewrites it, so you don't need to restart anything after logging in again.
Deploy (Coolify / Docker)
openssl rand -hex 24 # run twice: one for MCP_SECRET, one for SESSION_PUSH_SECRETCoolify: New Resource -> Dockerfile from this repo (or any Docker host). No Playwright in the image.
Environment variables:
Variable
Value
PS_BASE_URLhttps://ps.yourdistrict.orgMCP_SECRETrandom, at least 24 chars. Becomes the MCP URL path.
SESSION_PUSH_SECRETrandom, at least 24 chars, different from
MCP_SECRETPS_DEFAULT_STUDENToptional
PORT3417(default)The container listens on port 3417. Health check:
GET /healthalways returns200, even with no PowerSchool session, so a logged-out server never fails its deploy.Expose it on a subdomain. Don't put it behind Cloudflare Access or Basic Auth (claude.ai can't pass those); the secret path is the protection.
Log in from your PC:
npm run login:server -- --server https://ps-mcp.yourdomain.com --secret <SESSION_PUSH_SECRET>(or setPUBLIC_URLandSESSION_PUSH_SECRETin.envand runnpm run login:server).In claude.ai: Settings -> Connectors -> add custom connector ->
https://ps-mcp.yourdomain.com/mcp/<MCP_SECRET>.
The session lives in server memory only. If the container restarts, run ps-login again.
ps-login
npx tsx src/cli/ps-login.ts [--local] [--server https://...] [--secret ...]Opens headed Chromium with the persistent
.auth/profileprofile and waits up to 5 minutes for/guardian/home.html.Extracts only
JSESSIONIDandpsaid; cookie values are never printed.--localwrites.auth/session.json.--serverPOSTs{jsessionid, psaid}to<server>/sessionwith theX-Session-Secretheader; the server checks them against PowerSchool (seconds-remaining) before accepting and answers ok or fail.During login the browser only reaches PowerSchool's sign-in pages; afterwards the same read-only allowlist applies.
npm run login is --local; npm run login:server pushes to a server.
Configuration
Env var | Flag | Default |
|
| (required) |
|
| the active student |
|
| required for |
|
| required for |
|
| 3417 / 127.0.0.1 (the Docker image sets |
|
| 10 (0 disables) |
|
| 300 |
| used by |
Security notes
Secrets only in
.envand.auth/, both gitignored. Nothing in this repo contains cookies, tokens, grades, names or student identifiers; test fixtures are synthetic.Remote auth: the MCP endpoint is
/mcp/<MCP_SECRET>(same approach as canvas-mcp).POST /sessionneeds theX-Session-Secretheader. Both compare in constant time, both are rate-limited after repeated failures, and the two secrets must differ./healthis the only unauthenticated route and reveals nothing.Treat the MCP URL like a password. Anyone with it can read the student's grades. If it leaks, change
MCP_SECRETand redeploy.Cookies are held in memory on the server and never logged or returned by any tool.
list_studentshides student identifiers.Run it over HTTPS only.
ps-loginrefuses a non-HTTPS--serverunless it is localhost.The session cookies are as powerful as your portal login: keep
.auth/private and don't share it.Use it for the student's own family account. Respect your district's acceptable-use policy.
Read-only allowlist
Every PowerSchool request, from the server, from ps-login and from the tests, goes through assertAllowed() in src/ps/allowlist.ts. It is an allowlist, not a blocklist: anything not listed throws before any network call.
Allowed:
GETof/guardian/home,scores,termgrades,teachercomments,myschedule*,attendance*pages (home also serves as the keep-alive request)POST /ws/xte/assignment/lookup(the read-only lookup the portal's own score pages send)GET /ws/xte/section/id/<n>/setting_enabled/GET /ws/session/seconds-remaining(reports idle time, does not extend it)POST /guardian/mba_attendance_monitor/json/*.json(attendance plugin reads)
Always rejected, even if a path would otherwise match: any URL (path, query or fragment, also URL-encoded) containing logoff, logout, signout, signoff, singlelogout or password, and any ac= query parameter. Requests to other hosts and credentials in URLs are rejected too. This exists because the Phase 1 crawl once followed home.html?ac=logoff and ended its own session. PUT /ws/session/last-hit is deliberately not on the list.
Development
npm run dev # stdio, from source
npm run dev:http # HTTP, from source
npm run typecheck
npm test # vitest: allowlist, parsers, session expiry, MCP over HTTPTests use synthetic fixtures in test/fixtures/. Extra parser tests run against recon-output/raw when that folder exists and are skipped otherwise.
Limitations
HTML pages are scraped, so a PowerSchool template change can break a parser.
Category weights are not exposed by PowerSchool;
what_if_gradeis points-based unless you pass weights.Stored-only terms (E1, E2, closed S2) usually have no assignment data.
Only the active student's grade pages can be read (see above).
The attendance plugin is third-party and district-installed; the HTML grid is used if it fails.
MIT licensed.
This server cannot be deployed
Maintenance
Related MCP Connectors
Read-only access to your own crypto portfolio across 11+ exchanges and 20+ chains: holdings, full history, FIFO cost basis and P&L, fees and income. Ask Claude or ChatGPT about your portfolio; the connector can never trade, move funds or act as a login.
Your Telegram, WhatsApp, Instagram, mail and calendar in Claude or ChatGPT. Never sends messages.
Give Claude only the Google Drive files you choose. Every action logged.
Read-only access to your own bank, card and brokerage accounts for Claude, ChatGPT and other AI agents: balances, transactions, spending, subscriptions, cashflow and investment holdings. It can never move money.
Related MCP Servers
- FlicenseAqualityDmaintenanceEnables natural language queries about grades, GPA, attendance, and schedules by connecting Claude to StudentVue school dashboards. Includes analytical tools for grade simulation, what-if scenarios, and academic performance tracking based on live school data.19-
- AlicenseAqualityAmaintenanceEnables Claude to access ParentSquare school-parent communication platform, including feeds, calendar, conversations, and media files.4878 PyPI14MIT
- AlicenseAqualityFmaintenanceEnables Claude to access the ParentSquare school-parent communication platform via its web interface and internal APIs, covering parent features like feeds, calendars, messages, forms, and payments, plus school admin roster management such as students, guardians, classes, and staff.48MIT
- AlicenseAqualityCmaintenanceEnables Claude to access school information system data such as timetables, grades, homework, messages, students, teachers, and meal information, including meal ratings.26GPL 3.0