π οΈ Development setupΒΆ
PrerequisitesΒΆ
Project setupΒΆ
Clone the repository and create a virtual environment:
git clone https://github.com/confirm/tino.git
cd tino
make venv
source .venv/bin/activate
make develop
make develop installs the Python package in editable mode (with dev dependencies) and the Node.js dependencies.
Running the dev serverΒΆ
Start the development server with auto-reload:
make server
This launches Uvicorn on http://localhost:8000 with hot-reload enabled.
For convenience, make server sets dev-friendly defaults so it runs without any further configuration:
TINO_AUTH_DISABLED=trueTINO_BASE_URL=http://localhost:8000TINO_SECRET_KEY=develop(which keeps you logged in across reloads)
These defaults are overridable from your shell or the command line β for example, to run with authentication enabled (see π Deployment):
make server \
TINO_AUTH_DISABLED=false \
TINO_OIDC_DISCOVERY_URL=https://<sso-host>/realms/<realm>/.well-known/openid-configuration \
TINO_OIDC_CLIENT_ID=tino \
TINO_OIDC_CLIENT_SECRET=<secret>
Developing the MCP serverΒΆ
MCP OAuth requires a public HTTPS URL.
localhost is not enough because the MCP client needs to reach TINOβs well-known endpoints and the OIDC provider needs to redirect back to a real origin.
The easiest way to get one during development is a Cloudflare Tunnel (cloudflared):
# In one terminal β start TINO with auth enabled:
TINO_AUTH_DISABLED=false \
TINO_BASE_URL=https://<tunnel-host> \
TINO_MCP_ENABLED=true \
TINO_OIDC_DISCOVERY_URL=https://<sso-host>/realms/<realm>/.well-known/openid-configuration \
TINO_OIDC_CLIENT_ID=tino \
TINO_OIDC_CLIENT_SECRET=<secret> \
make server
# In another terminal β expose localhost via the tunnel:
cloudflared tunnel --url http://localhost:8000
Note
cloudflared prints the temporary public URL (e.g. https://random-words.trycloudflare.com).
Use that as TINO_BASE_URL.
Important
The tunnel hostname must be registered as a valid redirect URI in your OIDC provider (e.g. as a Valid redirect URIs entry in Keycloak).
For a persistent hostname, create a named tunnel instead of the quick --url shortcut.
Once the tunnel is up, point an MCP client at it:
$ claude mcp add --transport http tino https://<tunnel-host>/mcp
Vendored assetsΒΆ
The CodeMirror editor is bundled into tino/static/js/vendor/codemirror.js.
After modifying tino/static/js/codemirror-entry.js or updating Node dependencies, rebuild with:
make vendor-js
The colours CSS is fetched separately:
make vendor-css
LintingΒΆ
Run all linters at once:
make test
Or run them individually:
make test-pycodestyle # PEP 8
make test-pylint # Pylint
make test-isort # import order
make test-eslint # JavaScript
make test-stylelint # CSS
Building the docsΒΆ
Build the documentation once:
make docs
Or start a live-reloading preview:
make autodocs
This opens the docs in your browser on http://localhost:8888 and watches for changes.
Building the Docker imageΒΆ
make package # creates a wheel in ``build/``
make docker-image # copies wheel from ``build/`` into Docker image.
Project layoutΒΆ
tino/
βββ app.py FastAPI application factory
βββ auth.py Authentication & OIDC
βββ collab.py Real-time collaboration (Yjs/WebSocket)
βββ config.py Environment-based configuration
βββ dependencies.py FastAPI dependency injection
βββ mcp/ MCP server, OAuth resource server & instructions
βββ middleware.py ASGI middleware stack
βββ models.py Pydantic request/response schemas
βββ notifier.py File-change notifications
βββ routers/ API endpoint modules
βββ services/ Business logic (bucket, file, git, compiler, β¦)
βββ static/ SPA frontend
βββ css/
βββ js/ ES modules (no framework, vanilla JS)
βββ index.html