# How to Create a Custom Music Provider for Music Assistant: A Complete Developer's Guide

> Learn to create a custom music provider for Music Assistant. Follow this developer's guide to implement Python classes and manifest files for seamless integration.

- Repository: [Music Assistant/server](https://github.com/music-assistant/server)
- Tags: how-to-guide
- Published: 2026-06-14

---

**To create a custom music provider for Music Assistant, you must implement a Python class inheriting from `MusicProviderBase` located in [`music_assistant/models/provider.py`](https://github.com/music-assistant/server/blob/main/music_assistant/models/provider.py), define a [`manifest.json`](https://github.com/music-assistant/server/blob/main/manifest.json) configuration file, and place both files in a new subdirectory under `music_assistant/providers/`.**

Music Assistant, the open-source media server from the `music-assistant/server` repository, exposes a modular plugin architecture that enables developers to integrate any external music API or local source capable of providing metadata and streamable URLs. Creating a custom music provider requires adhering to an asynchronous contract defined by the core framework, which automatically discovers and loads providers at runtime by scanning for valid manifest files.

## Understanding the Provider Contract

Music Assistant loads music providers as Python packages from the `music_assistant/providers/` directory. Each provider must contain three essential components that define its interface and metadata.

**[`manifest.json`](https://github.com/music-assistant/server/blob/main/manifest.json)** declares the provider's identity, configuration schema, and dependencies. This file sits at the root of your provider folder and uses the same schema format as Home Assistant configuration flows.

**[`provider.py`](https://github.com/music-assistant/server/blob/main/provider.py)** contains the concrete implementation class that inherits from `MusicProviderBase`. This abstract base class, defined in [`music_assistant/models/provider.py`](https://github.com/music-assistant/server/blob/main/music_assistant/models/provider.py), specifies the asynchronous methods every provider must implement to support search, retrieval, and streaming operations.

**[`__init__.py`](https://github.com/music-assistant/server/blob/main/__init__.py)** makes the directory a valid Python package and typically re-exports the provider class for the framework's import system.

The framework discovers providers by scanning `music_assistant/providers/` for [`manifest.json`](https://github.com/music-assistant/server/blob/main/manifest.json) files. The loading routine in [`music_assistant/providers/__init__.py`](https://github.com/music-assistant/server/blob/main/music_assistant/providers/__init__.py) then imports the module and registers the class automatically.

## Step-by-Step Implementation Guide

### Step 1: Copy the Demo Template

The repository includes a minimal, functional reference implementation at `music_assistant/providers/_demo_player_provider/`. This folder contains working examples of [`manifest.json`](https://github.com/music-assistant/server/blob/main/manifest.json), [`provider.py`](https://github.com/music-assistant/server/blob/main/provider.py), [`__init__.py`](https://github.com/music-assistant/server/blob/main/__init__.py), and an icon asset.

Copy this folder to create your provider structure:

```bash
cp -r music_assistant/providers/_demo_player_provider music_assistant/providers/my_custom_provider

```

Rename the folder to match your provider's identifier, which becomes the unique key used in URLs and configuration.

### Step 2: Configure the Provider Manifest

Edit [`manifest.json`](https://github.com/music-assistant/server/blob/main/manifest.json) to declare your provider's metadata and configuration requirements. The `config_schema` field defines settings that appear in the Music Assistant UI, such as API keys or base URLs.

```json
{
    "type": "music",
    "id": "my_custom_provider",
    "name": "My Custom Music",
    "description": "Integration with custom music API",
    "config_schema": {
        "api_key": {
            "type": "string",
            "title": "API Key",
            "required": true
        },
        "base_url": {
            "type": "string",
            "title": "API Base URL",
            "default": "https://api.example.com"
        }
    },
    "dependencies": ["httpx"]
}

```

The `type` field must be `"music"` for music providers, and `dependencies` lists PyPI packages required for your implementation.

### Step 3: Implement the Provider Logic

Open [`provider.py`](https://github.com/music-assistant/server/blob/main/provider.py) and replace the demo class with your implementation. Your class must inherit from `MusicProviderBase` and implement the core async methods required by the framework.

```python
from music_assistant.models.provider import MusicProviderBase, MediaItem

class MyCustomProvider(MusicProviderBase):
    async def async_initialize(self) -> None:
        """Perform async setup, such as creating HTTP clients."""
        self.client = httpx.AsyncClient(
            base_url=self.config.get("base_url", "https://api.example.com")
        )
        self.api_key = self.config["api_key"]

    async def search(self, query: str, limit: int = 10) -> list[MediaItem]:
        """Return search results matching the query string."""
        resp = await self.client.get(
            "/search",
            params={"q": query, "limit": limit, "key": self.api_key}
        )
        resp.raise_for_status()
        data = resp.json()
        
        return [
            MediaItem(
                item_id=track["id"],
                name=track["title"],
                artist=track["artist"],
                album=track["album"],
                duration=track["duration"],
                thumbnail=track.get("cover_url")
            )
            for track in data["tracks"]
        ]

    async def get_item(self, item_id: str) -> MediaItem:
        """Retrieve a specific track by its provider ID."""
        resp = await self.client.get(
            f"/tracks/{item_id}",
            params={"key": self.api_key}
        )
        resp.raise_for_status()
        track = resp.json()
        
        return MediaItem(
            item_id=track["id"],
            name=track["title"],
            artist=track["artist"],
            album=track["album"],
            duration=track["duration"],
            thumbnail=track.get("cover_url")
        )

    async def stream_url(self, item: MediaItem) -> str:
        """Return a direct URL that Music Assistant can stream to players."""
        return f"{self.config.get('base_url')}/stream/{item.item_id}?token={self.api_key}"

```

The `MusicProviderBase` also defines optional methods such as `async get_playlist(self, playlist_id: str)` and `async get_image(self, item: MediaItem)` that you can implement to support playlist browsing and artwork retrieval.

### Step 4: Add Helper Modules

If your integration requires utility functions for authentication, pagination, or data transformation, place these modules in the same provider directory. Import them from [`provider.py`](https://github.com/music-assistant/server/blob/main/provider.py) to keep your main class focused on the framework contract.

### Step 5: Automatic Registration and Testing

No explicit registration code is required. Once your [`manifest.json`](https://github.com/music-assistant/server/blob/main/manifest.json) is valid and the files are in `music_assistant/providers/<your_provider>/`, the loader in [`music_assistant/providers/__init__.py`](https://github.com/music-assistant/server/blob/main/music_assistant/providers/__init__.py) will discover the provider on the next server start.

Test your implementation by running the server in debug mode:

```bash
python -m music_assistant --log-level debug

```

Your provider will appear under **Music Sources** in the UI. Configure the API key and verify that search, browsing, and streaming function correctly.

## Core Methods Reference

The `MusicProviderBase` abstract class in [`music_assistant/models/provider.py`](https://github.com/music-assistant/server/blob/main/music_assistant/models/provider.py) defines the following critical methods:

- **`async_initialize`** – Called once during provider startup. Use this to initialize HTTP clients, validate credentials, and set up connection pools.
- **`search(query: str, limit: int)`** – Must return a list of `MediaItem` objects matching the search query. This powers the global search functionality.
- **`get_item(item_id: str)`** – Retrieves full metadata for a specific track ID. Essential for resolving items from URLs or library references.
- **`stream_url(item: MediaItem)`** – Returns a string URL that the player can directly access to retrieve audio bytes. This can be a direct file URL or a time-limited signed URL from your API.
- **`get_playlist(playlist_id: str)`** – Optional. Returns a `Playlist` object containing tracks if your source supports playlist entities.
- **`get_image(item: MediaItem)`** – Optional. Returns image bytes for artwork, or `None` if unavailable.

## Summary

- **Provider structure** requires [`manifest.json`](https://github.com/music-assistant/server/blob/main/manifest.json), [`provider.py`](https://github.com/music-assistant/server/blob/main/provider.py), and [`__init__.py`](https://github.com/music-assistant/server/blob/main/__init__.py) in a folder under `music_assistant/providers/`.
- **Base class** `MusicProviderBase` in [`music_assistant/models/provider.py`](https://github.com/music-assistant/server/blob/main/music_assistant/models/provider.py) defines the async contract including `search`, `get_item`, and `stream_url`.
- **Discovery** is automatic via [`music_assistant/providers/__init__.py`](https://github.com/music-assistant/server/blob/main/music_assistant/providers/__init__.py) scanning for [`manifest.json`](https://github.com/music-assistant/server/blob/main/manifest.json) files.
- **Reference implementation** exists at `music_assistant/providers/_demo_player_provider/` for copy-paste development.
- **Configuration** uses the `config_schema` in [`manifest.json`](https://github.com/music-assistant/server/blob/main/manifest.json) to render UI settings using Home Assistant's config flow format.

## Frequently Asked Questions

### What is the minimum code required to implement a custom music provider?

You must create a class inheriting from `MusicProviderBase` that implements at least `async_initialize`, `search`, `get_item`, and `stream_url`. Additionally, you need a valid [`manifest.json`](https://github.com/music-assistant/server/blob/main/manifest.json) file with `type`, `id`, `name`, and `config_schema` fields. Place these in a folder under `music_assistant/providers/` and the framework will load it automatically.

### Can I use external HTTP libraries like `httpx` or `aiohttp` in my provider?

Yes. List any required PyPI packages in the `dependencies` array of your [`manifest.json`](https://github.com/music-assistant/server/blob/main/manifest.json). Music Assistant uses `httpx` internally, but you can import `aiohttp` or any other async-compatible library by declaring it as a dependency and importing it normally in your [`provider.py`](https://github.com/music-assistant/server/blob/main/provider.py).

### How does Music Assistant discover and load custom providers?

The framework scans the `music_assistant/providers/` directory for subdirectories containing [`manifest.json`](https://github.com/music-assistant/server/blob/main/manifest.json) files. The loader routine in [`music_assistant/providers/__init__.py`](https://github.com/music-assistant/server/blob/main/music_assistant/providers/__init__.py) reads each manifest, imports the module specified, and instantiates the provider class during server startup. No manual registration or import statements are required in the core codebase.

### Where should I write unit tests for my custom music provider?

Add your test files under the `tests/` directory at the repository root. Use `pytest-asyncio` for async test support and libraries like `respx` to mock external HTTP APIs. Import your provider class from `music_assistant.providers.your_provider_name.provider` and test each method independently, ensuring proper error handling for API failures.