IndusLMS Agent
Read-only agent access to Indus LMS academics: announcements, assignments, shared resources, notifications, attendance. For pi + opencode harnesses via MCP + Skill + CLI.
Quickstart (one command)
git clone https://github.com/StrangeSid/induslms-agent.git
cd induslms-agent
./install.sh
This creates .venv, installs the package, runs lms.py login,
merges MCP configs for pi + opencode, installs the skill, and runs
lms.py doctor. Restart your agent host afterwards.
Manual setup (if you prefer):
Replace
/path/to/induslms-agentwith your checkout path andyou@indusschool.comwith your school email.
cd /path/to/induslms-agent
python3 -m venv .venv && source .venv/bin/activate
pip install -e . # or: pip install -r requirements.txt
cp .env.example .env # then fill INDUSLMS_EMAIL/PASS/TENANT (.env auto-loaded)
# Authenticate once (token saved OUTSIDE the repo)
python3 lms.py login you@indusschool.com
# or: INDUSLMS_EMAIL=... INDUSLMS_PASS=... python3 lms.py login
# Health check
python3 lms.py doctor
# CLI
python3 lms.py courses
python3 lms.py assignments --course <course_id>
python3 lms.py resources --course <course_id>
python3 lms.py children <folder_id>
python3 lms.py download <resource_id> <file_id> --out /tmp/induslms-test
python3 lms.py notifications --unread-only --limit 5
python3 lms.py attendance && python3 lms.py attendance-day
Tokens live at ~/.induslms_token.json (override with INDUSLMS_TOKEN_FILE). Never committed — see .gitignore.
Credentials (.env) — used by programs, never sent to the LLM
cp .env.example .env # then fill in your own values
| Variable | Used by | Never leaves your machine |
|---|---|---|
INDUSLMS_EMAIL / INDUSLMS_PASS |
lms.py login only (one POST, then discarded) |
✅ |
INDUSLMS_TENANT |
Tenant fallback when the token has no roles | ✅ |
INDUSLMS_TOKEN_FILE |
Where the JWT is cached (~/.induslms_token.json) |
✅ |
INDUS_OUTLOOK_CLIENT_ID |
Graph device-code login (outlook.py login) |
✅ |
How it stays private:
lms.py,outlook.py,server.pyload.envviapython-dotenvat startup — values live only in the local process environment.- The MCP server exposes no login/token tools, and no tool ever returns passwords, tokens, or client IDs — tools return school data (assignments, mail, attendance) only.
.gitignoreblocks.env,*token*.json, and downloads, so credentials can't be committed by accident. Share only.env.example(empty values).
PyPI (after release): pipx install induslms-agent or uvx induslms-agent doctor,
then induslms login you@indusschool.com + induslms-server as your MCP command.
MCP server (recommended for agents)
Stdio, read-only tools only (no login, no mark-read, no submissions):
python3 server.py
Tools: get_profile, list_courses, list_resources, get_resource, download_resource, assignments_overview, list_eol, list_assessments, list_notifications, get_attendance, get_attendance_day, list_announcements, list_calendar, schoolmail_search, schoolmail_read, schoolmail_folders, outlook_search, outlook_read, outlook_folders, od_resolve_link, od_browse, od_download.
Run with python3 server.py (or induslms-server after pip install). Copy-paste
configs live in examples/: .mcp.json (Claude Code project scope),
claude_desktop_config.json.example, opencode.jsonc.example,
mcp-uvx.json.example (PyPI/uvx form).
School email (Apple Mail.app — macOS only)
Mail.app already holds the School account, so agents read it via osascript (JXA). No credentials, no app registration. On Linux/Windows schoolmail_* reports unavailable — use outlook_* instead:
python3 mailapp.py folders
python3 mailapp.py search "assignment" --top 5
python3 mailapp.py search --sender teacher@indusschool.com --since 2026-09-01
python3 mailapp.py read Inbox:24203
Refs are Mailbox:id. Override account/mailbox with SCHOOL_MAIL_ACCOUNT / SCHOOL_MAILBOX.
Register in pi
Create ~/.pi/agent/mcp.json (same shape as mcp.json in this repo,
with /path/to/induslms-agent replaced by your checkout path):
{ "mcpServers": { "induslms-academics": {
"command": "/path/to/induslms-agent/.venv/bin/python",
"args": ["/path/to/induslms-agent/server.py"],
"cwd": "/path/to/induslms-agent"
} } }
Use the venv python — system python lacks mcp/msal. Restart pi afterwards.
Register in opencode
In ~/.config/opencode/opencode.jsonc under mcp (replace
/path/to/induslms-agent with your checkout path):
"induslms-academics": {
"type": "local",
"command": ["/path/to/induslms-agent/.venv/bin/python", "/path/to/induslms-agent/server.py"],
"cwd": "/path/to/induslms-agent",
"enabled": true,
"timeout": 15000
}
Restart opencode afterwards. Verify with a prompt like "list my courses using induslms-academics".
Claude Code
claude mcp add induslms-academics -- /path/to/induslms-agent/.venv/bin/python /path/to/induslms-agent/server.py
# or project scope: copy .mcp.json (replace paths), then: claude mcp list
Skill: bash scripts/install-skill.sh also copies to ~/.claude/skills/
(INSTALL_PROJECT_SKILL=1 for .claude/skills/).
Claude Desktop
Copy examples/claude_desktop_config.json.example into
~/Library/Application Support/Claude/claude_desktop_config.json
(macOS; see file for Win/Linux paths), replace paths, relaunch.
OpenAI-compatible agents (code, no config file)
from agents.mcp import MCPServerStdio
async with MCPServerStdio(
params={"command": "/path/to/induslms-agent/.venv/bin/python",
"args": ["/path/to/induslms-agent/server.py"]},
cache_tools_list=True,
) as server:
...
Hosted MCP / GPT Actions need a public HTTPS endpoint (not provided; stdio-only by design).
Skill (workflow guidance)
skills/induslms-academics/SKILL.md teaches the workflow:
announcements → assignments → resources/download → attendance,
plus School inbox. Install (copies to pi, opencode, and Claude skills):
bash scripts/install-skill.sh
End-user prompt: refresh local school materials
examples/update-resources.prompt.md is a copy-paste template that
checks LMS + the School mailbox for new teacher materials, downloads
only what's missing into a School/ folder, and parses everything to
text. Fill in your subjects/teachers, paste it into a fresh agent
session.
Key API notes (reverse-engineered, verified live)
- Base
https://api.induslms.com,Authorization: Bearer <access>fromPOST /api/v1/auth/login/. - Resources:
GET /api/v1/tenants/{tid}/resources/?course_id=for top level; children via?parent_resource_id={rid}(NOT?parent=, which returns unfiltered results). Files:.../resources/{rid}/files/{fid}/content/?disposition=attachment|inline(binary, streamed). - Assignments = EOL tests (
/eol-tests/my/, 23 items) + student assessments (/student/assessments/, 6 items) + resources.test-marks/meis PYP-only (DP getsBAD_REQUEST) — handled gracefully. - DP projects endpoint returns
[]for this student; section fetcher ready for when IDs exist. - Notifications support
?limit=&offset=; unread filter is client-side (is_read) so--limit 5 --unread-onlymay return fewer rows — use a larger limit. - Attendance has two shapes: session summary (
/students/me/attendance/) and day breakdown (/tenants/{tid}/students/me/attendance/day/).
Security
- Read-only by design. The only POSTs in the codebase are
loginand the (CLI-unused) token-refresh helper — the MCP server exposes neither. - Credentials via env/prompt only; tokens outside the repo;
.gitignoreblocks*token*.json,.env, downloads.
Outlook (school inbox via Microsoft Graph)
Read-only (Mail.Read delegated, device-code flow). Token cache at
~/.indus_outlook_token.json — never in repo.
# 1. Entra ID -> App registrations -> New: allow public client flows,
# add delegated Mail.Read. Multitenant OK.
export INDUS_OUTLOOK_CLIENT_ID=<app/client id>
# ..or skip registration: export INDUS_USE_BUILTIN_CLIENT=1 (sign in as yourself)
python3 outlook.py login # approve code in browser
python3 outlook.py search "assignment" --top 5
python3 outlook.py search --sender teacher@indusschool.com --since 2026-09-01
python3 outlook.py read <message_id>
MCP tools: outlook_search, outlook_read, outlook_folders
(same coverage; unconfigured → clear error, not crash).
School tenant blocks user consent → IT admin must grant admin consent first
(not needed with INDUS_USE_BUILTIN_CLIENT=1).
OneDrive / SharePoint (school files via Microsoft Graph)
Read-only (Files.Read + Sites.Read.All, same device-code flow and token
cache as Outlook — re-run login once to consent to the new scopes):
python3 sharepoint.py login
python3 sharepoint.py resolve <sharing-link-from-mail> # shared file/folder metadata
python3 sharepoint.py browse / # your OneDrive root
python3 sharepoint.py download <item-id-or-link> --out /tmp/school
MCP tools: od_resolve_link, od_browse, od_download. No local OneDrive
sync client needed — pure HTTPS, works on any OS.
Metadata
Release files for induslms-agent 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| induslms_agent-0.3.0.tar.gz | 23.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| induslms_agent-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 49.8 kB
Release files / induslms_agent-0.3.0.tar.gz
| Download URL | induslms_agent-0.3.0.tar.gz |
|---|---|
| Size | 23.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ca6e69a6a2909b8ac31b81af5f84fd7339e3c6661edfdac3ae1de9ed07236d81
|
|
BLAKE2b-256 checksum How to use checksums |
0177bfe6db1ec0dc81eb32e0689e722ad2f759ddf77048518f5686b6504a1b92
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 5, 2026.
Transparency logRelease files / induslms_agent-0.3.0-py3-none-any.whl
| Download URL | induslms_agent-0.3.0-py3-none-any.whl |
|---|---|
| Size | 26.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6ed81b2342bc6770a58b7421964ed3155941b2ccc9ab4a80931df6a87bb996b8
|
|
BLAKE2b-256 checksum How to use checksums |
1ec801bb49592fc8c06518f466169238b62df84d2dc1a2c99fa48d6c23ede0c0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 5, 2026.
Transparency log