Extension Development: Four-Week Online Course
A four-week online course on building tools for LLMs as Smarter Extensions: Python packages that a
platform administrator installs at run time, and that every LLMClient on the platform can then
use. Students learn from the official weather Extension, which implements the get_weather
tool of OpenAI’s function calling guide for real, from the open source framework that runs it,
apps/llmtool/extension/, and
from the signed extensions-registry that
publishes it. There are twelve 30-minute recorded lectures and a 90-minute live session each
week. Week 1 covers tools and the framework, week 2 writing tools, week 3 settings, the lifecycle
and testing, and week 4 loading, publishing and trust. See Course Formats and Grading for the
format, the grading scale and the course policies, and Extension Development for an
overview of the subject.
At a Glance
Students |
Python developers who want to give LLMs new abilities. |
Deployment |
Each student runs Smarter from source ( |
Models |
Any OpenAI-compatible model that supports tool calling, through the instructor’s provider key. |
Workload |
3 hours of instruction and 4 to 6 hours of assignments each week. |
Cost |
Under $10 per student in model charges, capped with a Budget. The weather Extension needs a Google Maps Geocoding API key, whose free monthly allowance covers the course. A GitHub account’s free Actions minutes cover the release workflow. |
Learning Objectives
By the end of the course, students can:
Explain how Smarter offers a tool to an LLM, runs its call and returns the result, and choose between a Plugin, an Extension and an MCP server for a given need.
Write an Extension’s
pyproject.tomland tools whose Pydantic arguments models are their definitions.Design tools’ results and error messages for an LLM to read, and treat its arguments as untrusted input.
Use settings, secrets, waffle switches, SAM resources and lifecycle hooks to manage what an Extension needs on a platform.
Test tools without the platform, the database or an LLM, and run the tests in CI.
Package, version, release and publish an Extension, and evaluate the risks of installing someone else’s.
Technical Prerequisites
Required: Python: classes and inheritance, type hints, exceptions, packages and relative imports.
Required: Git, GitHub and pull requests.
Required by the end of week 1: Docker, and Smarter installed from source with a superuser login. See the Smarter Instructor Guide.
Helpful: Pydantic models, Python’s
unittestandunittest.mock, GitHub Actions, and having called an LLM API.Not needed: Django or JavaScript. The framework hides both.
Schedule
The files that the lectures refer to are listed in full under Smarter in the Source Code.
Week |
Item |
Content |
|---|---|---|
1 |
Lecture 1 |
How LLMs call tools. Tool definitions, tool calls and results, and the loop that runs
them in |
1 |
Lecture 2 |
Plugins, Extensions and MCP. Configuration or code, account or platform, selected by search terms or offered on every prompt: Extensibility. Why Extensions replaced three functions hardcoded in the chat provider, read from the body of commit fda234a37. |
1 |
Lecture 3 |
A tour of the weather Extension. Its |
1 |
Live session 1 |
Setup triage. Enabling the weather Extension in the web console, and tracing a tool call live. Start Assignment 1. |
2 |
Lecture 4 |
The pyproject.toml. |
2 |
Lecture 5 |
The tool protocol. |
2 |
Lecture 6 |
Results and errors for an LLM. Results that need no guessing, |
2 |
Live session 2 |
Review of Assignment 1. Designing an arguments model together, and watching the LLM use it. Start Assignment 2. |
3 |
Lecture 7 |
Settings and secrets. Typed settings and |
3 |
Lecture 8 |
The lifecycle. Waffle switches and SAM manifests that an Extension creates and its
uninstall removes ( |
3 |
Lecture 9 |
Testing. |
3 |
Live session 3 |
Review of Assignment 2. A test-writing workshop on the planted bugs of an instructor’s Extension. Capstone proposals discussed. Start Assignment 3. |
4 |
Lecture 10 |
Loading code at run time. |
4 |
Lecture 11 |
Packaging and publishing. The archive’s limits and reproducible builds
( |
4 |
Lecture 12 |
Trust and operations. Extensions run with the platform’s own access, unsandboxed. The registry’s signature and sha256 checks, and what they do and don’t protect. Updates, rollbacks and errors on the web console’s Extensions pages (Managing Extensions), and the signals that log every tool call. |
4 |
Live session 4 |
Capstone presentations (about 6 minutes each, with 2 minutes of questions), and a course retrospective. |
Assignments 1 to 3 are due before the next live session. Assignment 4 is due in the middle of week 4. The capstone proposal is due before Live session 3, and the capstone at the start of Live session 4.
Smarter in the Source Code
Every lecture, lab and assignment in this course rests on the files and commits below. Every
reference is to the main branch of github.com/smarter-sh/smarter, unless another repository is named, so that students
can open each one during class. Python paths are under smarter/smarter/.
Tools and Extensibility
Topic |
What Smarter does |
Where to look |
|---|---|---|
The tool call loop |
Smarter offers the LLM the tools that its LLMClient lists, the LLM asks to call one,
Smarter runs it and returns the result, and the LLM answers from it. Extension tools are
loaded by |
|
Plugins and Extensions |
Two ways to add a tool, for two kinds of people. A Plugin is a SAM manifest that configures one of six built-in tool patterns; an Extension is a Python package that a platform superuser installs at run time. Extensibility compares them line by line. |
|
The framework’s map |
The package’s module docstring lists each module and what it is for: the manifest, the archive, the protocol, settings, the loader, lifecycle, the installer, the toolkit and the registry. |
|
The models |
|
|
Listing an Extension |
An LLMClient lists Extensions by slug in |
The Official Extensions
Topic |
What it shows |
Where to look |
|---|---|---|
The weather Extension |
The developer guide’s worked example: the |
smarter-extension-weather: pyproject.toml, weather/tools.py, weather/models.py, weather/services.py |
The documentation |
The package’s module docstring, in reStructuredText, is the Extension’s documentation, written for the administrator who decides whether to install it. The platform renders it with raw HTML and file inclusion disabled. |
|
Untrusted input |
The calculator never calls |
|
The smallest Extension |
One tool and no settings, for date arithmetic: a template to start from. |
|
The test fixtures |
The framework’s own test Extensions: |
Writing an Extension
Topic |
What Smarter does |
Where to look |
|---|---|---|
The pyproject.toml |
A standard Python project file, not a SAM manifest. |
|
The tool protocol |
A tool is a class with a |
|
Running a tool |
|
|
Settings and secrets |
Settings are declared in |
|
Lifecycle hooks |
Eight hooks, from |
|
Switches and resources |
Waffle switches declared in |
|
Testing helpers |
|
extension/testing.py, tests/test_tools.py (weather) |
Management commands |
|
Installing, Loading and Publishing
Topic |
What Smarter does |
Where to look |
|---|---|---|
The archive |
A zip file, validated without importing it: at most 10 MB, 50 MB expanded and 2,000
files, with no links or paths that leave its directory. |
|
The installer |
The one place where an Extension’s state changes: install, update, roll back, configure, enable, disable and uninstall. The web console, the management commands and the registry client all go through it. |
|
Loading code at run time |
Each process extracts a version’s archive once, atomically by rename, and imports it under a name unique to its version and sha256, so an update loads beside the old version instead of reloading modules. This is why an Extension’s modules use relative imports. |
|
The registry |
A public GitHub repository whose CI validates each submission, builds an index, and signs it with an Ed25519 key. Platforms verify the signature on every read, through the hardened HTTP client, and check every archive against the signed sha256. Publishing an Extension. |
extension/registry.py, extensions-registry: scripts/validate.py, scripts/build_index.py, publishers.yaml |
Releasing a version |
semantic-release on each push to |
release.yml, test.yml, scripts/package.py (weather) |
The web console |
Superusers browse the registry, install, upload, configure, enable, update, roll back and uninstall on two React pages, whose api is typed by Pydantic models exported as a JSON Schema. Managing Extensions. |
extension/views.py, extension/contract.py, smarter-extension-console/ |
Observability |
Eight signals, from |
Extensions in the History
Extensions arrived in one large commit, with two follow-ups, and the official Extensions have histories of their own.
Commit |
Why it is worth studying |
Where to look |
|---|---|---|
Extensions arrive |
Adding a Python tool used to mean changing Smarter itself: three functions hardcoded in the
chat provider, a choices enum, and a release. The commit body explains every module of the
new framework, the migration from |
|
Drift after a big change |
Built-in system prompts still named tools that the LLM was no longer offered. The fix
renames them to |
|
From admin forms to the console |
Extension management moves from the Django admin to two React pages, with a typed contract, and the admin becomes read-only. |
|
A result the LLM can read |
|
commit 51ec052a5 (weather) |
Automating releases |
Hand-made tags replaced by semantic-release, so that a commit’s type decides the version. |
commit 45f3690c2 (weather) |
A registry release |
The commit that publishes two Extensions’ 0.2.0: one YAML file per version. |
commit a42a93ae9 (extensions-registry) |
Graded Assignments
Assignment 1: A Tool Call, Traced
Students follow two weather questions, one that succeeds and one that fails, from the prompt to
the answer, naming the code that handles each step, and compare the tool definition that
validate_extension prints with the source that produced it.
Turn in: a two-page Markdown report with the conversation, the tool calls, their arguments and results, and the code that handled each step.
Criterion |
Pts |
Exemplary |
Developing |
|---|---|---|---|
Evidence |
6 |
Real tool calls, arguments and results, from the chat and the logs. |
Paraphrased, or from the docs. |
Code located |
6 |
Each step tied to the file and method that handles it. |
Steps named, not located. |
Definition explained |
5 |
Each part of the definition traced to the docstring, model or slug. |
Pasted without comment. |
Failure path |
3 |
Explains how the error reached the LLM, and what it did with it. |
Only the success. |
Assignment 2: Two Tools
Students write an Extension with two related tools that do something an LLM can’t do reliably on its own, with described Pydantic arguments and result models, and show an LLMClient choosing between them correctly.
Turn in: a Git repository with the Extension, the output of validate_extension, and a
conversation that uses both tools.
Criterion |
Pts |
Exemplary |
Developing |
|---|---|---|---|
Models |
6 |
Arguments and results typed, constrained and described. |
Bare fields and hand-built dicts. |
Tool choice |
5 |
Descriptions distinct enough that the LLM picks the right tool. |
The LLM confuses the two. |
Untrusted input |
5 |
Every argument checked before use; nothing evaluated or executed. |
Trusts the LLM. |
Valid Extension |
4 |
|
Validates only after help. |
Assignment 3: Settings, a Hook and Tests
Students add a secret setting and a waffle switch to their Extension, and a lifecycle hook that
checks something real, such as on_enable refusing to enable without a working API key. They
test the tools and the hook with every service mocked.
Turn in: the updated repository, its test_extension output, and screenshots of the
settings form and of the hook refusing to enable.
Criterion |
Pts |
Exemplary |
Developing |
|---|---|---|---|
Settings |
5 |
Typed, described, and the secret never logged or returned. |
A secret in a log or a result. |
Hook |
5 |
A real check, in the right hook, with a clear message. |
A hook that does nothing useful. |
Tests |
6 |
Success, invalid arguments, tool errors and the hook, with no network. |
Only the success. |
Verified |
4 |
The hook’s refusal shown on the platform, not only in a test. |
Took the test’s word for it. |
Assignment 4: A Release Pipeline
Students add CI to their Extension’s repository, modeled on the weather Extension’s test.yml
and release.yml: tests on every pull request, and a release on each push to main that
builds the archive and attaches it with its sha256. They write the registry entry that a release
would need, without submitting it.
Turn in: the repository with two passing workflow runs and a GitHub release, and the draft
extensions/<slug>/<version>.yaml.
Criterion |
Pts |
Exemplary |
Developing |
|---|---|---|---|
Tests in CI |
5 |
Run against a checkout of Smarter, on every pull request. |
Run only locally. |
Release |
6 |
Versioned from commit types, with the archive and sha256 attached. |
A hand-made tag. |
Reproducible |
5 |
Two builds of the same commit have the same sha256, shown. |
Not checked. |
Registry entry |
4 |
Slug, version, URL and sha256 all match the release. |
A sha256 copied from elsewhere. |
Capstone Projects
Students choose one, propose it before Live session 3, and present it in Live session 4.
Capstone A: A Published Extension
Learning objective. Build, test, release and publish an Extension that gives an LLM an ability it lacks, to the standard of the official Extensions.
Students build an Extension around a real service or computation, with at least two tools,
settings including a secret, a SAM resource or waffle switch, and a lifecycle hook. It is tested
in CI, released by its workflow, and documented for administrators. They install it from its
GitHub release on a clean platform, and show LLMClients using it. A capstone that submits its
Extension to the Smarter registry agrees this with the maintainers in an issue on
extensions-registry first, and follows Publishing an Extension.
What students turn in:
The Extension’s repository, with its workflows, releases and
CHANGELOG.md.A conversation in which an LLMClient uses each tool, including one failure it recovers from.
A two-page design note on the tools, settings, lifecycle and the trust an administrator must place in it.
A six-minute presentation, with questions on the code.
How it is graded (100 points):
Criterion |
Pts |
Exemplary |
Developing |
|---|---|---|---|
Tool design |
25 |
Arguments and results modeled and described; the LLM calls each tool when it should. |
The LLM ignores a tool, or guesses at its result. |
Robustness |
20 |
Input validated as untrusted; every failure returns a useful error; nothing runs at import. |
Exceptions escape, or the Extension fails to load. |
Settings and lifecycle |
15 |
Each used for a real need; uninstall leaves nothing behind. |
Declared but unused, or left behind on uninstall. |
Tests and CI |
20 |
Mocked, thorough, and passing in CI. |
Tests need an API key, or CI is red. |
Release and documentation |
10 |
A reproducible release, installed from GitHub; documentation an administrator can act on. |
Installed only from a local build. |
Presentation |
10 |
Clear, and answers questions on any line. |
Can’t explain key parts. |
Capstone B: A Security Review of the Extension Framework
Learning objective. Evaluate the trust model of a plugin system that runs other people’s code, and propose improvements that keep it usable.
Students threat-model the path from a developer’s repository to a tool call: the archive, the registry’s CI, the signed index, the download, the installer, the loader, the toolkit and the settings. For each threat they cite the code that addresses it, or show that nothing does, with a demonstration Extension installed only on their own platform. They propose two improvements as Architecture Decision Records, such as sandboxing tool calls or signing archives by publisher.
What students turn in:
A threat model, as a table of assets, threats, existing controls (with file references) and gaps.
The demonstration Extension’s repository, and evidence of each demonstration.
Two draft ADRs in the format of ADR.
A six-minute presentation.
How it is graded (100 points):
Criterion |
Pts |
Exemplary |
Developing |
|---|---|---|---|
Coverage |
20 |
Every step from repository to tool call. |
The archive only. |
Evidence |
25 |
Each control cited in the code; each gap demonstrated. |
Claims without references. |
Demonstrations |
15 |
Safe, on the student’s own platform, and convincing. |
Hypothetical. |
Proposals |
25 |
Two ADRs with alternatives, costs and effect on developers. |
“Add sandboxing”, with no design. |
Presentation |
15 |
Clear and proportionate. |
Alarmist or vague. |
Recommended Texts
Required: Extensibility and Building an Extension, in week 1.
Required: OpenAI’s function calling guide, in week 1.
Required: The Extension Lifecycle and Testing an Extension, in week 3.
Required: Publishing an Extension and Managing Extensions, in week 4.
Reference: Luciano Ramalho, Fluent Python (2nd edition, O’Reilly, 2022), on classes, type hints and packages.
Reference: Chip Huyen, AI Engineering (O’Reilly, 2025), on how tools and agents fit into AI applications.
Reference: Harry Percival and Bob Gregory, Architecture Patterns with Python (O’Reilly, 2020), on keeping services apart from the code that calls them, as the weather Extension’s
services.pydoes.In the repository: protocol.py, toolkit.py, loader.py, and the weather Extension.
Further Study
Official documentation: OpenAI’s function calling guide, whose
get_weatherexample the weather Extension implements; the Pydantic documentation, on models, validation and JSON Schema; and the Python Packaging User Guide, including thepyproject.tomlspecification.Standards: PEP 440 (version identifiers), PEP 508 (dependency specifiers), Semantic Versioning, Conventional Commits, and JSON Schema.
Other plugin ecosystems: the WordPress Plugin Handbook, the model for Smarter’s run-time install, and the VS Code Extension API, for how another platform describes, packages and publishes extensions.
Software supply chain: The Update Framework, the design behind signed package indexes, Sigstore and SLSA, to compare with the registry’s Ed25519 signature and sha256 checks.
Other tool protocols: the Model Context Protocol specification, the third way to give a Smarter LLMClient tools.
YouTube: the PyCon US channel, for talks on packaging, Pydantic and testing, and the AI Engineer channel, for talks on tool use and agents.
Podcasts: Talk Python To Me and Python Bytes, which cover packaging, Pydantic and the Python ecosystem regularly, and Latent Space, on building with LLMs.
Writing: blog.smarter.sh, on how Smarter was built.