What's inside Spotify's AI agent app
In May, Spotify released Studio by Spotify Labs, a macOS research preview built around a personal AI agent. If you let it, it reads your listening history, Google Calendar, and Gmail, and uses them to generate custom playlists and private podcasts.
Curious about how Spotify puts an agent like this together, I installed the app on my Mac.
The biggest surprise is that Spotify didn’t write the core agent loop. Studio runs pi—Mario Zechner’s minimal open-source coding agent—shipped unmodified and hooked up to Claude Sonnet 4.6. Spotify’s actual engineering lives in a custom pi extension that exposes 32 tools, wrapped in a local orchestration layer and a macOS sandbox.
I looked at app version 1.2.99.311 (internal build 0.13.6, built 28 August 2026).
What’s running
With Studio open, ps shows two Node services alongside a native binary:
Resources/studio/node Resources/studio/sc-orchestrator.cjs start
Resources/studio/node Resources/studio/sc-gateway.cjs
MacOS/spotify_cli permissions watch --format json
MacOS/spotify_cli chat watch <conversation-id> --format json
I wasn’t sure what the sc- prefix stood for. So I ran spotify_cli version:
cli: 1.2.99.311
super-connect: v0.13.6
save-to-spotify: 0.1.5
“Super Connect” appears to be the internal codename. The workload splits cleanly across the three processes: sc-orchestrator manages agent sessions and schedules, sc-gateway acts as the local backend for the desktop UI and proxies LLM requests, and spotify_cli handles anything that touches your Spotify account. (The UI itself is built on the Chromium Embedded Framework, the same as Spotify’s regular desktop app.)
The harness is pi
Sitting next to a 6.7 MB pi.cjs bundle in Contents/Resources/studio/ is a standard package.json:
"name": "@mariozechner/pi-coding-agent",
"version": "0.62.0",
"license": "MIT",
"repository": { "url": "git+https://github.com/badlogic/pi-mono.git" }
If you haven’t come across pi, it’s a deliberately small coding agent. Out of the box, it only has four tools (read, write, edit, and bash), leaving everything else to extensions. Projects like OpenClaw and JetBrains’ Thinkrail build on top of it, though I hadn’t seen anyone mention that Spotify was using it too.
For each conversation, the orchestrator spawns a pi --mode rpc subprocess and talks to it over stdin/stdout in JSON. A file path left in the bundle (pi-mono/packages/coding-agent/dist/cli.js) suggests Spotify vendors the pi-mono repo directly inside their monorepo. (pi has since moved to the @earendil-works scope on npm, while Studio is still pinned to 0.62.0 under the old namespace.)
Models and credential isolation
Looking at the orchestrator’s session state, the default model is claude-sonnet-4-6, a very cost-efficient choice, routed through an internal provider named aigateway-anthropic. Digging into the code shows a couple of other models in the mix: the agent constructor falls back to claude-opus-4-6 if no model is specified, and a bundled evaluation harness uses claude-haiku-4-5 to grade outputs.
Studio’s persona prompt instructs the agent to only describe itself as AI built into Studio and never reveal the underlying model or provider—a standard product requirement, though always funny to read in a plain-text Markdown file on disk.
Actual secrets, however, are kept well away from the agent. The pi process never sees an API key. When it makes a model call, it hits sc-gateway on localhost (/api/ai/v1/messages), which attaches credentials and forwards the request to Spotify’s servers. The app keeps its tokens in the macOS Keychain, so nothing the agent can read with its file tools contains a credential.
Extending pi into a Spotify app
Instead of forking pi, Spotify wrote four extensions loaded from .pi/extensions/:
| Extension | What it adds |
|---|---|
spotify |
32 tools covering memory, skills, routines, Gmail, Calendar, MCP, permissions, image generation, UI cards, and chat |
background-processes |
Replaces the default bash tool with one that manages background jobs |
show-form |
Lets the agent render interactive forms in the UI |
voice-select |
Picks voices for generated podcasts |
Almost all of the app’s behavior lives in that first spotify extension. Tools like memory_search, find_and_install_skill, create_scheduled_routine, google_gmail_search, call_mcp_tool, and entity_card are how a generic coding agent gets turned into a consumer music app.
Skills, memory, and routines
Because pi works well with local files, most of Studio’s state is just Markdown and SQLite on disk.
Skills work like on-demand prompt injection. Studio ships with 38 skill folders (podcast-creation, recommend, playlist-editing, gmail, garden-companion, etc.), each containing a Markdown instruction file, plus a manifest.json with trigger phrases. When the agent needs to do something specific, it calls find_and_install_skill() to pull that Markdown file into its context window. Interestingly, there’s also a compatibility layer for ClawHub, OpenClaw’s skill registry.
Memory is also stored in Markdown: a long-term memory.md, daily logs under daily/ (today’s and yesterday’s logs are automatically injected into context), and per-session notes. To make this searchable offline, Studio bundles a 23 MB quantized ONNX build of all-MiniLM-L6-v2 and indexes the files in local SQLite.
Routines let the agent run on a schedule. A routine is just a Markdown prompt with YAML frontmatter:
trigger:
type: cron
schedule: 0 8 * * 1-5
timezone: Europe/Berlin
timeout: 3900
notify: true
When a cron job fires—or when a “sensor” like idle or location triggers—the orchestrator spins up an unattended pi session and drops the output into the main chat as a UI card.
How it builds a podcast
pi logs every session as JSONL, and Studio keeps those logs under ~/Library/Caches/com.spotify.studio/.studio/pi-agent/. So after asking Studio to set up a daily morning podcast for me, I went back and read what the agent had actually done. It took about 60 tool calls:
- Load the
podcast-creationskill, look up the shows I follow and what I’ve listened to recently, then show a form so I can pick segments and a delivery time. - Save my answers to memory, create a weekday 8:00 routine, and run it once right away.
- Create a show in my Spotify library with chapter skipping turned on.
- Pull weather and headlines with
spotify_cli websearch, and the latest episodes of my shows withspotify_cli ask. - Write a script and voice it in 17 separate chunks with
spotify_cli tts synthesize, then join them withtts stitch, with 300 ms of silence in between. - Generate a cover image and resize it to 900×900.
- Write a chapters file—segment titles, links to the news sources, and Spotify episode cards timed to pop up while the matching segment plays.
- Upload the episode, wait for Spotify to finish processing it, then attach the chapters.
The upload step is the only part of this pipeline that’s open source (more on that below). Text-to-speech, web search and ask are all closed commands inside spotify_cli.
External tools and sub-agents
Studio also acts as an MCP client (search_mcp_tools, call_mcp_tool), so you can wire up third-party servers. For music-specific queries, though, the local Claude agent doesn’t sift through raw Spotify API responses itself. Instead, it shells out to spotify_cli ask "…", handing the question off in plain English to a separate, server-side Spotify service that handles taste profiles and recommendations. When it recommended music to me, that’s where the tracks came from.
Sandboxing and remote kill-switches
Giving an LLM a bash tool and access to your Gmail is obviously risky. Locally, Studio wraps pi in macOS sandbox profiles (strict-macos.sb and legacy-macos.sb) and filters shell commands through a four-tier policy in profiles/command-policy.yaml:
- allow:
cat,grep,jq,ls, etc. run immediately. - not listed: paused until the user clicks approve.
- high risk: commands like
osascriptthat can escape the sandbox require a two-step confirmation. - deny: blocked outright.
(There’s also spotify_cli pairing, which displays a QR code for pairing with your phone—it looks like you can approve tool calls from the Spotify mobile app.)
What caught my eye is that Spotify doesn’t rely solely on local config files for safety. Studio keeps cached copies of three documents fetched from a Spotify CDN:
- A remote shell policy (denied commands, blocked patterns, and interpreters) that gets merged with the local YAML.
- A skill blocklist.
- A prompt file titled “Soul (immutable)” containing the core safety rules.
On my machine, the policy and the blocklist were still empty. But the plumbing means Spotify can ban a shell command, kill a buggy skill, or tighten the system prompt across all clients without cutting a new release.
Leftovers in the bundle
Poking around a fast-moving research preview always turns up a few fun artifacts:
- The main
AGENTS.mdinstruction file is tailored for a music assistant, but still has a section on how to do pull-request reviews—presumably left over from its coding-agent roots. - The production bundle ships with the team’s internal evaluation framework (complete with concurrency settings, an LLM-as-a-judge review model, and references to an internal backend called “radiant”), plus a mock proxy for local dev.
build-info.jsonproudly reports"sourceState": "dirty".
A note on spotify_cli and save-to-spotify
Back in April, Spotify open-sourced a Go CLI called save-to-spotify (Apache 2.0), designed to let coding agents like Claude Code or OpenClaw upload generated audio into a user’s Spotify library.
Studio’s closed-source spotify_cli binary embeds that exact tool—running spotify_cli sts … invokes save-to-spotify v0.1.5 alongside the closed commands for playback, taste queries, and chat. It looks like save-to-spotify was an early experiment that eventually grew into Studio.
Takeaways
Looking at how Studio is put together, what stands out is how little time Spotify spent reinventing the agent harness. They took an off-the-shelf, MIT-licensed coding loop (pi), left its core alone, and put all of their effort into the edges: the 32 custom tools, the local spotify_cli bridge, the macOS sandbox, and the remote policy controls.
Even the prompts and skills sit in plain Markdown on disk. The value isn’t in hiding the prompt or building a custom ReAct loop—it’s in the plumbing that connects the loop to data only Spotify has.
Doing the same with my own agent
What I actually wanted from Studio was simpler than Studio itself. I already have a personal assistant—an agent running on my own server—and I wanted it to reach into Spotify: see what I’ve been listening to, put together playlists, and make me a podcast in the morning. Studio does all of this, but only inside its own app, and it couldn’t give me segments in more than one language (I’m learning German, so I want the episode to end with a short German practice segment).
Most of the pieces are public. Reading goes through the regular Spotify Web API: you register your own app on Spotify’s developer dashboard, log in once, and get your top tracks and artists, recently played, playlists, liked songs and followed podcasts, plus search and playlist creation. (Since February, apps in development mode need Premium and are limited to five users, which is fine for one person.) Writing an episode goes through save-to-spotify, which can create a show, upload episodes, and attach chapters and links. It ships inside the regular Spotify desktop app, so if you have Spotify on a Mac, you already have it.
So I put them together as rundown. Your agent gets tools to read your Spotify account, search, and make playlists, and two more for the morning podcast: one gathers the weather, a few RSS feeds and what earlier episodes already covered, and one voices the script the agent writes and uploads it with chapters. Each segment can be in its own language and voice, with macOS say, Piper, or any OpenAI-compatible TTS server (Kokoro, Chatterbox, Qwen3-TTS). It’s one package—a pi extension and an MCP server—so the same code runs in pi, in DeepSeek’s DSH, or in Claude Code.
If you use pi, you can try it with one command:
pi install git:github.com/T-64/rundown
then type /rundown for the podcast. Reading your account is optional and needs your own Spotify developer app first; once that’s set up, you can ask something like “make me a playlist from what I’ve had on repeat this month”. Anything that changes your account asks first. The README covers the Spotify setup and the config for DSH and other MCP clients.
If you’d like rundown to do something else, open an issue.
Corrections are welcome via email. If you work on Studio and I got a detail wrong, let me know and I’ll update the post.