Thanks for considering a contribution — genuinely. Whether it's a one-line typo fix, a new MCP tool, or just an issue saying "this didn't work on my machine, here's what I saw" — all of it helps.
This file just has a couple of notes so you know what to expect and we can keep the back-and-forth short.
Basically anything that makes the app better for someone self-hosting it. A non-exhaustive list of things I'd love help with:
- Bug fixes — obvious one. If you hit a bug, please file an issue even if you can't fix it yourself.
- Self-host polish — clearer docs, better error messages when an env var is missing, packaging niceties (a Helm chart, a one-click deploy button for popular PaaS, a
compose.override.ymltemplate for common variations). - Accessibility + keyboard shortcuts — the UI is dark, dense, and keyboard-navigable-ish, but could be better.
- i18n / localization — the Slot
kindlabels are German (Vorlesung,Übung) by default. Making those user-configurable or translatable would help non-EU users a lot. - New MCP tools — if you find yourself wishing Claude could do
Xand there's a natural way to expose it, just add it. Pattern is inapp/mcp_tools.py. - Performance / bundle size — the frontend chunk is bigger than it needs to be; someone who knows their way around Vite code-splitting could shave a lot.
- Tests — there's no suite yet. Adding pytest + Vitest scaffolding is its own welcome PR.
Not "no" — just "let's talk first so you don't waste a weekend":
- Major framework swaps (React → Svelte, FastAPI → Django).
- Replacing PostgREST with a hand-rolled SQLAlchemy / asyncpg layer (the postgrest-py shim is small but it's load-bearing — every service file goes through it).
- New top-level entities beyond the current data model (Course / Schedule slot / Lecture / Study topic / Deliverable / Task / Klausur).
- Multi-user / team / sharing features — the single-user-per-deploy assumption is load-bearing in a bunch of places, and shifting it is a big project.
A one-liner issue like "would you take a PR that X?" is all it takes.
See INSTALL.md for the full walkthrough. TL;DR:
cp .env.example .env # fill APP_PASSWORD_HASH, SESSION_SECRET
cat > .env.docker <<EOF # Postgres credentials
POSTGRES_USER=openstudy
POSTGRES_PASSWORD=$(openssl rand -hex 24)
POSTGRES_DB=openstudy
EOF
./deploy.sh # builds + brings up postgres + postgrest + openstudy
cd web && pnpm install && pnpm dev- Python:
ruffis configured inpyproject.toml(line-length 100, py312 target). Runuv run ruff check .anduv run ruff format .before pushing. - TypeScript/React:
eslintis configured inweb/. Runpnpm lintfromweb/. Components are function components + hooks; state via React Query for server state anduseState/useReducerfor local. - Commit messages: short imperative subject line (≤70 chars), optional body explaining the why. See
git logfor the house style. - Scope per PR: one logical change. A bug fix + a refactor + a new feature should be three PRs.
app/services/fall_behind.py (Python, runs on the server / in MCP) and web/src/lib/fall-behind.ts (TypeScript, runs in the browser) are intentional mirrors. Any change to the rules — severity thresholds, grace periods, which topics count — must be applied to both. PRs that only touch one side will get a request for the other.
There's no automated test suite yet (honest). Until there is:
- For backend changes: rebuild + restart the openstudy container (
./deploy.sh), then exercise the changed endpoint(s) manually viacurlor the/api/docsSwagger UI. - For frontend changes: run
pnpm buildto make sure TypeScript is still happy, then manually click through the affected views inpnpm dev. - For MCP tool changes: rebuild via
./deploy.sh, then either hit the tool via aPOST /mcpJSON-RPC request with your bearer token, or register the local endpoint with Claude Code (claude mcp add --transport http openstudy-local http://localhost:8000/mcp) and call the tool in chat.
If you're up for adding test infrastructure (pytest + a Postgres testcontainer, Vitest for the frontend), that itself is a welcome PR.
- Fork, branch, commit.
- Run the relevant linter/build (
ruff,pnpm lint,pnpm build). - Push and open a PR against
main. - In the PR description, include: what the change does, how you tested it, anything reviewers should pay attention to.
- Be patient — this is a side project. Responses may take a few days.
If you're running a public deploy of this code, the frontend reads VITE_SITE_URL and VITE_SITE_NAME from the environment so you can set your own domain + display name without code edits — scripts/build-seo.mjs regenerates robots.txt, sitemap.xml, and manifest.webmanifest from those vars at build time. The brand assets under web/public/brand/ are also a single drop-in path if you want to swap the wordmark for your own.
PRs that improve the VITE_SITE_* plumbing or make rebranding easier are welcome.
By submitting a PR you agree that your contribution is licensed under the same MIT license as the rest of the project (see LICENSE).