# How to Add Music Providers to Music Assistant: A Developer's Guide

> Learn how to add music providers to Music Assistant by creating a Python module. This guide details subclassing MusicProvider and implementing essential async methods and manifest configuration for seamless integration.

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

---

**Adding a music provider to Music Assistant requires creating a Python module that subclasses `MusicProvider` from [`music_assistant/models/music_provider.py`](https://github.com/music-assistant/server/blob/main/music_assistant/models/music_provider.py) and implementing async methods for search, library synchronization, and streaming, alongside a [`manifest.json`](https://github.com/music-assistant/server/blob/main/manifest.json) configuration file.**

Music Assistant is an open-source media server that aggregates music from multiple sources into a unified library. To add music providers to Music Assistant, developers implement a plugin architecture that bridges external catalogs with the core system using standardized async interfaces. This guide walks through the exact file structure, required methods, and manifest configuration needed to integrate a new streaming service or local source.

## Understand the MusicProvider Base Class

All music providers inherit from the `MusicProvider` base class defined in [`music_assistant/models/music_provider.py`](https://github.com/music-assistant/server/blob/main/music_assistant/models/music_provider.py). This abstract class establishes the contract between external music sources and Music Assistant's central library, handling authentication, capability declaration, and data transformation.

The base class provides default implementations for browsing and library management, but requires you to override specific methods depending on your provider's capabilities. You interact with the core system through `self.mass`, which provides access to the database, cache, and task runner without requiring direct database manipulation.

### Required Methods and Properties

Implement these key methods to enable full functionality:

- **`setup()`** – Called once during provider instantiation. Handle authentication, token refresh, and initial connection here.
- **`supported_features`** – Property returning a list of `ProviderFeature` enums (e.g., `ProviderFeature.SEARCH`, `ProviderFeature.LIBRARY_TRACKS`, `ProviderFeature.STREAM`).
- **`search(search_query, media_types, limit)`** – Return a `SearchResults` object containing matching artists, albums, tracks, or playlists.
- **`get_library_artists()`**, **`get_library_albums()`**, **`get_library_tracks()`**, **`get_library_playlists()`** – Async generators yielding respective media items from the user's library.
- **`get_artist()`**, **`get_album()`**, **`get_track()`**, **`get_playlist()`** – Fetch full metadata for a specific item by provider ID.
- **`get_stream_details(item_id, media_type)`** – Return `StreamDetails` including protocol, URL, and audio format.
- **`get_audio_stream(streamdetails, seek_position)`** – Async generator yielding audio bytes for playback.

Optional write-back methods include `library_add()`, `library_remove()`, and `set_favorite()` if your provider supports modifying the external catalog.

## Create the Provider Manifest

Every provider requires a [`manifest.json`](https://github.com/music-assistant/server/blob/main/manifest.json) file located at `music_assistant/providers/<your_provider>/manifest.json`. This file declares metadata, configuration requirements, and Python dependencies.

Key fields include:

- **`type`** – Must be `"music"` for music providers.
- **`domain`** – Unique identifier (e.g., `"spotify"`).
- **`name`** – Human-readable display name.
- **`config_entries`** – Array of configuration items defining `key`, `type` (`string`, `secure_string`, `boolean`), `label`, and defaults. Access values via `self.config.get_value(key)`.
- **`requirements`** – Pip-style dependencies (e.g., `["spotipy==2.23.0"]`) installed automatically by the setup script.
- **`multi_instances`** – Boolean indicating whether users can configure multiple accounts.
- **`documentation`** – URL to usage instructions.

## Implement the Provider Step-by-Step

### 1. Set Up the Provider Directory

Create the provider folder structure:

```bash
mkdir -p music_assistant/providers/my_provider
touch music_assistant/providers/my_provider/__init__.py
touch music_assistant/providers/my_provider/manifest.json

```

### 2. Implement the Core Class

Subclass `MusicProvider` and declare supported features:

```python

# music_assistant/providers/my_provider/__init__.py

from __future__ import annotations

from music_assistant.models.music_provider import MusicProvider
from music_assistant.models.provider import ProviderFeature
from music_assistant.helpers.util import async_log_exception

class MyProvider(MusicProvider):
    """Custom music provider implementation."""

    @property
    def supported_features(self) -> list[ProviderFeature]:
        """Declare capabilities."""
        return [
            ProviderFeature.SEARCH,
            ProviderFeature.LIBRARY_ARTISTS,
            ProviderFeature.LIBRARY_ALBUMS,
            ProviderFeature.LIBRARY_TRACKS,
            ProviderFeature.STREAM,
        ]

    async def setup(self) -> None:
        """Initialize connection and authenticate."""
        self._api_key = self.config.get_value("api_key")
        # Validate credentials here

```

### 3. Handle Search and Library Sync

Implement search and library enumeration methods:

```python
    @async_log_exception
    async def search(self, search_query: str, media_types: list[MediaType], limit: int = 5):
        """Search the external catalog."""
        # Call external API

        results = await self._api.search(query=search_query, limit=limit)
        # Transform to SearchResults model

        return SearchResults(...)

    @async_log_exception
    async def get_library_artists(self):
        """Yield artists from user's library."""
        async for raw in self._api.library_artists():
            yield Artist(
                item_id=raw["id"],
                provider=self.instance_id,
                name=raw["name"],
                # Populate additional metadata

            )

```

Implement `get_library_albums()`, `get_library_tracks()`, and `get_library_playlists()` similarly using async generators.

### 4. Enable Audio Streaming

For playback support, implement the streaming interface:

```python
    async def get_stream_details(self, item_id: str, media_type: MediaType) -> StreamDetails:
        """Return stream metadata."""
        return StreamDetails(
            protocol=StreamType.CUSTOM,
            url=self._api.get_stream_url(item_id),
            audio_format=AudioFormat(
                content_type=ContentType.MP3,
                sample_rate=44100,
                bit_depth=16,
                channels=2,
            ),
        )

    async def get_audio_stream(self, streamdetails: StreamDetails, seek_position: int = 0):
        """Stream audio bytes."""
        import aiohttp
        async with aiohttp.ClientSession() as session:
            headers = {"Range": f"bytes={seek_position}-"} if seek_position else {}
            async with session.get(streamdetails.url, headers=headers) as resp:
                async for chunk in resp.content.iter_any():
                    yield chunk

```

## Code Examples

### Minimal manifest.json

```json
{
  "type": "music",
  "domain": "myprovider",
  "name": "MyProvider",
  "description": "Integration with MyProvider music catalog.",
  "codeowners": ["@yourusername"],
  "config_entries": [
    {
      "key": "api_key",
      "type": "secure_string",
      "label": "API Key",
      "default": ""
    }
  ],
  "requirements": ["aiohttp==3.9.0"],
  "documentation": "https://github.com/music-assistant/server/discussions/123",
  "multi_instances": false
}

```

### Provider Implementation Skeleton

```python
from music_assistant.models.music_provider import MusicProvider
from music_assistant.models.provider import ProviderFeature
from music_assistant.models.media_items import Artist, Album, Track, SearchResults
from music_assistant.helpers.util import async_log_exception
import aiohttp

class MyProvider(MusicProvider):
    @property
    def supported_features(self):
        return [
            ProviderFeature.SEARCH,
            ProviderFeature.LIBRARY_ARTISTS,
            ProviderFeature.LIBRARY_ALBUMS,
            ProviderFeature.LIBRARY_TRACKS,
            ProviderFeature.STREAM,
        ]

    async def setup(self):
        self._session = aiohttp.ClientSession()
        self._api_key = self.config.get_value("api_key")

    @async_log_exception
    async def search(self, search_query, media_types, limit=5):
        params = {"q": search_query, "limit": limit, "key": self._api_key}
        async with self._session.get("https://api.example.com/search", params=params) as resp:
            data = await resp.json()
        return self._parse_search_results(data)

    @async_log_exception
    async def get_library_artists(self):
        async with self._session.get(f"https://api.example.com/artists?key={self._api_key}") as resp:
            data = await resp.json()
            for item in data["artists"]:
                yield Artist(
                    item_id=item["id"],
                    provider=self.instance_id,
                    name=item["name"],
                )

```

## Register and Test the Provider

Music Assistant automatically discovers providers by scanning the `music_assistant/providers/` directory at startup. No manual registration code is required.

Install dependencies and register the provider:

```bash
scripts/setup.sh

```

Start the server locally and enable your provider through the web interface. Verify functionality by testing search queries, library synchronization, and audio playback. Add unit tests under [`tests/providers/my_provider/test_provider.py`](https://github.com/music-assistant/server/blob/main/tests/providers/my_provider/test_provider.py) following existing patterns in the repository.

## Summary

- **Inherit from `MusicProvider`** in [`music_assistant/models/music_provider.py`](https://github.com/music-assistant/server/blob/main/music_assistant/models/music_provider.py) to create a new provider class.
- **Declare capabilities** via the `supported_features` property using `ProviderFeature` enums.
- **Implement async methods** for `setup()`, `search()`, library enumeration (`get_library_*`), item lookup (`get_*`), and streaming (`get_stream_details()`, `get_audio_stream()`).
- **Define metadata** in [`manifest.json`](https://github.com/music-assistant/server/blob/main/manifest.json) including `domain`, `config_entries`, and `requirements`.
- **Place files** in `music_assistant/providers/<your_provider>/` with [`__init__.py`](https://github.com/music-assistant/server/blob/main/__init__.py) and [`manifest.json`](https://github.com/music-assistant/server/blob/main/manifest.json).
- **Run [`scripts/setup.sh`](https://github.com/music-assistant/server/blob/main/scripts/setup.sh)** to install dependencies and auto-register the provider.

## Frequently Asked Questions

### What is the MusicProvider base class in Music Assistant?

The `MusicProvider` base class is an abstract Python class defined in [`music_assistant/models/music_provider.py`](https://github.com/music-assistant/server/blob/main/music_assistant/models/music_provider.py) that standardizes how external music catalogs integrate with the server. It defines async methods for authentication, searching, library synchronization, metadata retrieval, and audio streaming that subclasses must implement to enable full functionality within the Music Assistant ecosystem.

### How do I configure authentication for my music provider?

Implement the `setup()` method to handle authentication during provider initialization. Store API keys or tokens by retrieving them from `self.config.get_value(key)` where the key matches entries defined in your [`manifest.json`](https://github.com/music-assistant/server/blob/main/manifest.json) `config_entries` array. Use `secure_string` type for sensitive credentials to ensure they are masked in the UI and logs.

### Can a music provider support multiple instances?

Yes, set `"multi_instances": true` in your [`manifest.json`](https://github.com/music-assistant/server/blob/main/manifest.json) to allow users to configure multiple accounts or endpoints for the same provider type. When enabled, Music Assistant instantiates separate provider objects for each configuration, each with unique `instance_id` values while sharing the same implementation code.

### Where should I place unit tests for my new provider?

Create test files under `tests/providers/<your_provider>/test_provider.py` following the existing test patterns in the repository. Tests should mock external API calls and verify that your provider correctly implements `MusicProvider` methods, handles authentication errors gracefully, and returns properly formatted media items that match the expected Music Assistant models.