Library / SDK
joreilly/FantasyPremierLeague avatar
joreilly/FantasyPremierLeague

FantasyPremierLeague: a Compose Multiplatform sample that ships Android, iOS, Desktop, a notebook and an MCP server

Fantasy Premier League Kotlin/Compose Multiplatform sample

602 stars62 forksJupyter NotebookApache-2.0

At a glance

What is it?
John O'Reilly's FantasyPremierLeague is a Kotlin Multiplatform reference app for the Fantasy Premier League, with a Koog AI agent, a Jetpack Room and DataStore layer, Navigation 3, and an MCP server that exposes player and fixture data to Claude Desktop. It is a teaching artifact, not a product, and the README says almost nothing about the API it reads from.
Who is it for?
Adopt this if you are evaluating Compose Multiplatform, Jetpack Room in shared code, or the Kotlin MCP SDK and want one repository where all three already coexist across Android, iOS and Desktop. Do not adopt it as a Fantasy Premier League client: the README does not document the upstream data source, there are no releases, and the AI agent needs a Gemini API key set as apiKeyGoogle in FantasyPremierLeagueAgent before it does anything.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 4 days ago.
What is it written in?
Mainly Jupyter Notebook, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

What this repository is actually for

The README describes FantasyPremierLeague as a Compose Multiplatform project running on Android, iOS and Desktop. That sentence is the whole pitch. There is no product framing, no feature list aimed at end users, and no claim that this is a client anyone should install. The repository sits in a set of eleven samples the author links at the bottom of the README, alongside PeopleInSpace, Confetti, BikeShare and others, and it is best read as one entry in that series rather than as a standalone application.

The audience is therefore narrow and specific: Kotlin developers who want to see how a particular stack fits together in a working multiplatform codebase. The interesting part is not football. It is that the same shared module backs four different surfaces (an Android app, an iOS app, a desktop app, and an MCP server process), plus a Kotlin Notebook used for exploration. If you are choosing between Compose Multiplatform and a native-per-platform approach, or deciding whether Jetpack Room belongs in your shared source set, this repository is a concrete data point. If you want to manage a Fantasy Premier League team, it is not aimed at you, and nothing in the README suggests it is maintained with that use in mind.

The module layout and what each surface consumes

The README names the pieces but does not draw a diagram, so the architecture has to be inferred from the run instructions and the library list. There is an Android app configuration run from Android Studio, an Xcode project at ios/FantasyPremierLeague/FantasyPremierLeague.xcodeproj, a Gradle module invoked as :compose-desktop, and a separate :mcp-server module. The MCP server is explicitly stated to use the same KMP shared code, which is the central design claim of the repository: one shared layer, several entry points.

On the shared side, the README lists Jetpack ViewModel, Navigation 3, Room and DataStore. Room in a multiplatform shared source set is the non-obvious choice here, and the author links a dedicated post about using Jetpack Room in Kotlin Multiplatform shared code, which is where the actual explanation lives. DataStore handles persistence of a different kind, and Navigation 3 is used with Compose Multiplatform, again with a linked post. The AI agent is built with Koog and runs in shared KMP code with Gemini and tool calling. The repository topics also mention Koin for dependency injection and SKIE, which the README body does not discuss at all, so treat those as present in the build but undocumented in the primary entry point.

Getting it running on each target

The README gives four commands and they are short. Android: open the project in Android Studio and run the app configuration. iOS: open ios/FantasyPremierLeague/FantasyPremierLeague.xcodeproj in Xcode and run. Desktop: ./gradlew :compose-desktop:run. MCP server: ./gradlew :mcp-server:shadowJar.

The one configuration key called out by name is the Gemini credential. The README states that to use the AI agent you need a Gemini API key and that you set apiKeyGoogle in FantasyPremierLeagueAgent. That is the only config key in the document, and it is placed in a note under the run instructions rather than in a dedicated section. There is no .env example, no mention of a Gradle property, and no indication of what happens if the key is absent. If the agent is the reason you are looking at this repository, plan on reading the source of FantasyPremierLeagueAgent to find the field, because the README does not tell you the file path.

The MCP server has the most complete setup instructions, which is telling. You run the shadowJar task, then in Claude Desktop you open Developer Settings, choose Edit Config, and add a JSON block naming the server. The README's example uses the key fantasy-premier-league, a command of java, and args of -jar followed by the absolute path to mcp-server/build/libs/serverAll.jar and then --stdio. The README notes you must update the path, and its own example path is a personal home directory, so the block is a template rather than something you can paste unchanged. The result is an MCP tools endpoint returning player and fixture info.

Where the documentation stops

The README never says where the Fantasy Premier League data comes from. There is no mention of an upstream API, an authentication scheme, a rate limit, or whether the data is fetched live or cached. Given that DataStore and Room both appear in the library list, persistence is clearly part of the design, but the README does not explain what is stored, how long it lives, or how a refresh is triggered. For a sample whose entire subject is one league's data, that is a real gap, and it means you cannot judge from the README alone whether the data layer is a pattern worth copying.

The repository has no releases, so there is no changelog and no version to pin. The last push timestamp is the only signal of activity, and the README's own screenshots carry dates in their filenames (November 2025 for the Android, iOS and desktop captures, July 2025 for the Claude Desktop integration, and April 2024 for the Kotlin Notebook images), which suggests the notebook surface has not been touched as recently as the rest. The primary language reported for the repository is Jupyter Notebook, which is a quirk of how the notebook files are counted and not a description of the application code, but it does mean that tooling which classifies repositories by language will mislabel this one.

The AI agent and MCP server are the parts worth studying

Two features here are less common in KMP samples than the usual list of screens and view models. The first is a Koog-based agent using Gemini with tool calling, running in shared Kotlin rather than in platform-specific code. The second is the MCP server built on the Kotlin MCP SDK, which turns the shared data layer into a tools endpoint that a desktop assistant can call. The README links a post specifically about adding the Koog assistant with structured output, which implies the output is schema-constrained rather than free text, though the README itself does not describe the schema.

The practical consequence is that the shared module has to be usable from a headless JVM process, not just from a UI. That is a stricter constraint than a typical Compose sample imposes, and it is the reason the MCP server is worth reading even if you have no interest in football. If your shared code has grown assumptions about a running UI or a platform lifecycle, a module like :mcp-server will expose them. The cost is that the agent path depends on an external paid API and a key you must supply, so the sample is not fully self-contained: the UI targets run without any credential, the agent does not.

A genuine alternative and how it differs

PeopleInSpace, by the same author and linked from the README's sample list, covers overlapping ground: Kotlin Multiplatform with Compose on Android and desktop and SwiftUI on iOS. The difference in approach is the point. PeopleInSpace is built around a public, well-known data source and is widely used as a first KMP sample precisely because the data layer is not the interesting part. FantasyPremierLeague pushes in the other direction: it adds Jetpack Room in shared code, DataStore, Navigation 3, a Koog agent and an MCP server, and it links out to blog posts for each of those decisions instead of explaining them inline.

So the choice is between a sample that teaches the multiplatform skeleton and a sample that teaches the persistence and tooling layers on top of it. If you have never set up a KMP project, PeopleInSpace is the shorter path. If you already have a KMP project and are deciding whether Room belongs in commonMain, whether Navigation 3 is ready for Compose Multiplatform, or how to expose shared code over MCP, FantasyPremierLeague is the more relevant repository. Neither is a library you add as a dependency, and neither should be treated as one.

Licence and the cost of tracking upstream

The repository is Apache-2.0, which permits commercial use, modification and redistribution provided you keep the licence and notice files and state significant changes. That is a permissive licence, and it means copying a module into your own project is legally straightforward. It does not mean the code is maintained for you. There are no releases, so there is nothing to pin to and no upgrade path to follow; you track the main branch or you fork. Apache-2.0 also includes an explicit patent grant, which matters if you are lifting code into a product, but this is a description of the licence text and not legal advice. If the licence terms affect a commercial decision, have counsel read them.

The real maintenance cost is not the licence, it is the dependency surface. The README's badge shows Kotlin 2.4.10, and the stack spans Compose Multiplatform, Jetpack Room, DataStore, Navigation 3, Koin, SKIE, Koog and the Kotlin MCP SDK. Navigation 3 and Koog are both young enough that their APIs move, and a sample that glues eight moving parts together will break when any one of them changes. The Gradle commands above are the fastest way to find out whether it currently builds on your machine, and the mcp-server shadowJar task is the cheapest single check because it exercises the shared code without needing a simulator or a Gemini key.

Editorial conclusion

Adopt this if you are evaluating Compose Multiplatform, Jetpack Room in shared code, or the Kotlin MCP SDK and want one repository where all three already coexist across Android, iOS and Desktop. Do not adopt it as a Fantasy Premier League client: the README does not document the upstream data source, there are no releases, and the AI agent needs a Gemini API key set as apiKeyGoogle in FantasyPremierLeagueAgent before it does anything. Before copying anything, build the mcp-server shadowJar and run the desktop target with ./gradlew :compose-desktop:run, then read the Navigation 3 and Room posts linked from the README, because those, not the README itself, are where the design decisions are actually explained.

Official sources

  1. Issues
  2. joreilly/FantasyPremierLeague on GitHub
  3. License: Apache-2.0
  4. README
Community notes

Community notes