├── .gitignore ├── .python-version ├── LICENSE ├── README.md ├── mcp_applemusic.py ├── pyproject.toml └── uv.lock /.gitignore: -------------------------------------------------------------------------------- 1 | mcp_applemusic.egg-info 2 | .vscode 3 | 4 | *.pyc 5 | -------------------------------------------------------------------------------- /.python-version: -------------------------------------------------------------------------------- 1 | 3.13 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2025 Kenneth Reitz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![MseeP.ai Security Assessment Badge](https://mseep.net/pr/kennethreitz-mcp-applemusic-badge.png)](https://mseep.ai/app/kennethreitz-mcp-applemusic) 2 | 3 | # MCP-AppleMusic 4 | 5 | A FastMCP server implementation for controlling Apple Music (formerly iTunes) on macOS through AppleScript commands. 6 | 7 | ## Requirements 8 | 9 | - Python 3.13+ 10 | - macOS with Apple Music app installed 11 | - MCP library ≥1.2.1 12 | 13 | ## Installation 14 | 15 | First, ensure you have uv installed: 16 | ```bash 17 | $ brew install uv 18 | ``` 19 | 20 | Then, with **Claude Desktop**, add the following to `claude_desktop_config.json`: 21 | 22 | ```json 23 | { 24 | "mcpServers": { 25 | "iTunesControlServer": { 26 | "command": "uvx", 27 | "args": ["-n", "mcp-applemusic"] 28 | } 29 | } 30 | } 31 | ``` 32 | 33 | ## Available Commands 34 | 35 | The following commands are available through the MCP server: 36 | 37 | ```python 38 | itunes_play() # Start playback 39 | itunes_pause() # Pause playback 40 | itunes_next() # Skip to next track 41 | itunes_previous() # Go to previous track 42 | itunes_search(query) # Search library for tracks 43 | itunes_play_song(song) # Play specific song 44 | itunes_create_playlist(name, songs) # Create new playlist 45 | itunes_library() # Get library statistics 46 | ``` 47 | 48 | ## Usage 49 | 50 | Start the server: 51 | 52 | ```bash 53 | python server.py 54 | ``` 55 | 56 | Example interactions: 57 | 58 | ```python 59 | # Search for a song 60 | results = itunes_search("Hey Jude") 61 | 62 | # Create a new playlist 63 | itunes_create_playlist("Beatles Favorites", ["Yesterday", "Hey Jude", "Let It Be"]) 64 | 65 | # Play a specific song 66 | itunes_play_song("Hey Jude") 67 | ``` 68 | 69 | ## Development 70 | 71 | 1. Clone the repository: 72 | ```bash 73 | git clone https://github.com/yourusername/mcp-applemusic.git 74 | cd mcp-applemusic 75 | ``` 76 | 77 | 2. Install development dependencies: 78 | ```bash 79 | pip install -e ".[dev]" 80 | ``` 81 | 82 | ## Contributing 83 | 84 | 1. Fork the repository 85 | 2. Create your feature branch (`git checkout -b feature/amazing-feature`) 86 | 3. Commit your changes (`git commit -m 'Add amazing feature'`) 87 | 4. Push to the branch (`git push origin feature/amazing-feature`) 88 | 5. Open a Pull Request 89 | 90 | ## License 91 | 92 | This project is licensed under the MIT License - see the LICENSE file for details. 93 | 94 | ## Notes 95 | 96 | - This tool only works on macOS systems due to its AppleScript dependency 97 | - Requires Apple Music (formerly iTunes) to be installed 98 | -------------------------------------------------------------------------------- /mcp_applemusic.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | from mcp.server.fastmcp import FastMCP 3 | 4 | 5 | def run_applescript(script: str) -> str: 6 | """Execute an AppleScript command via osascript and return its output.""" 7 | result = subprocess.run(["osascript", "-e", script], capture_output=True, text=True) 8 | if result.returncode != 0: 9 | return f"Error: {result.stderr.strip()}" 10 | return result.stdout.strip() 11 | 12 | 13 | # Instantiate the MCP server. 14 | mcp = FastMCP("iTunesControlServer") 15 | 16 | 17 | @mcp.tool() 18 | def itunes_play() -> str: 19 | """Start playback in Music (iTunes).""" 20 | script = 'tell application "Music" to play' 21 | return run_applescript(script) 22 | 23 | 24 | @mcp.tool() 25 | def itunes_pause() -> str: 26 | """Pause playback in Music (iTunes).""" 27 | script = 'tell application "Music" to pause' 28 | return run_applescript(script) 29 | 30 | 31 | @mcp.tool() 32 | def itunes_next() -> str: 33 | """Skip to the next track.""" 34 | script = 'tell application "Music" to next track' 35 | return run_applescript(script) 36 | 37 | 38 | @mcp.tool() 39 | def itunes_previous() -> str: 40 | """Return to the previous track.""" 41 | script = 'tell application "Music" to previous track' 42 | return run_applescript(script) 43 | 44 | 45 | @mcp.tool() 46 | def itunes_search(query: str) -> str: 47 | """ 48 | Search the Music library for tracks whose names contain the given query. 49 | Returns a list of tracks formatted as "Track Name - Artist". 50 | """ 51 | script = f""" 52 | tell application "Music" 53 | set trackList to every track of playlist "Library" whose name contains "{query}" 54 | set output to "" 55 | repeat with t in trackList 56 | set output to output & (name of t) & " - " & (artist of t) & linefeed 57 | end repeat 58 | return output 59 | end tell 60 | """ 61 | return run_applescript(script) 62 | 63 | 64 | @mcp.tool() 65 | def itunes_play_song(song: str) -> str: 66 | """ 67 | Play the first track whose name exactly matches the given song name. 68 | Returns a confirmation message. 69 | """ 70 | script = f""" 71 | tell application "Music" 72 | set theTrack to first track of playlist "Library" whose name is "{song}" 73 | play theTrack 74 | return "Now playing: " & (name of theTrack) & " by " & (artist of theTrack) 75 | end tell 76 | """ 77 | return run_applescript(script) 78 | 79 | 80 | @mcp.tool() 81 | def itunes_create_playlist(name: str, songs: str) -> str: 82 | """ 83 | Create a new playlist with the given name and add tracks to it. 84 | 'songs' should be a comma-separated list of exact track names. 85 | Returns a confirmation message including the number of tracks added. 86 | """ 87 | # Split the songs string into a list. 88 | song_list = [s.strip() for s in songs.split(",") if s.strip()] 89 | if not song_list: 90 | return "No songs provided." 91 | # Build a condition string that matches any one of the song names. 92 | # Example: 'name is "Song1" or name is "Song2"' 93 | conditions = " or ".join([f'name is "{s}"' for s in song_list]) 94 | script = f""" 95 | tell application "Music" 96 | set newPlaylist to make new user playlist with properties {{name:"{name}"}} 97 | set matchingTracks to every track of playlist "Library" whose ({conditions}) 98 | repeat with t in matchingTracks 99 | duplicate t to newPlaylist 100 | end repeat 101 | return "Playlist \\"{name}\\" created with " & (count of tracks of newPlaylist) & " tracks." 102 | end tell 103 | """ 104 | return run_applescript(script) 105 | 106 | 107 | @mcp.tool() 108 | def itunes_library() -> str: 109 | """ 110 | Return a summary of the Music library, including total tracks and user playlists. 111 | """ 112 | script = """ 113 | tell application "Music" 114 | set totalTracks to count of every track of playlist "Library" 115 | set totalPlaylists to count of user playlists 116 | return "Total tracks: " & totalTracks & linefeed & "Total playlists: " & totalPlaylists 117 | end tell 118 | """ 119 | return run_applescript(script) 120 | 121 | 122 | @mcp.tool() 123 | def itunes_current_song() -> str: 124 | """ 125 | Get information about the currently playing track. 126 | Returns the track name, artist, and album. 127 | """ 128 | script = """ 129 | tell application "Music" 130 | if player state is playing then 131 | set currentTrack to current track 132 | return "Now playing: " & (name of currentTrack) & " by " & (artist of currentTrack) & " from " & (album of currentTrack) 133 | else 134 | return "No track is currently playing" 135 | end if 136 | end tell 137 | """ 138 | return run_applescript(script) 139 | 140 | 141 | @mcp.tool() 142 | def itunes_all_songs() -> str: 143 | """ 144 | Get a list of all songs in the Music library. 145 | Returns a formatted list of all tracks with their names and artists. 146 | """ 147 | script = """ 148 | tell application "Music" 149 | set trackList to every track of playlist "Library" 150 | set output to "" 151 | repeat with t in trackList 152 | set output to output & (name of t) & " - " & (artist of t) & linefeed 153 | end repeat 154 | return output 155 | end tell 156 | """ 157 | return run_applescript(script) 158 | 159 | 160 | def main(): 161 | mcp.run() 162 | 163 | 164 | if __name__ == "__main__": 165 | main() 166 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "mcp-applemusic" 3 | version = "0.1.5" 4 | description = "A simple Apple Music API client for MCP" 5 | readme = "README.md" 6 | requires-python = ">=3.13" 7 | dependencies = ["mcp>=1.2.1"] 8 | 9 | [project.scripts] 10 | mcp-applemusic = "mcp_applemusic:main" 11 | -------------------------------------------------------------------------------- /uv.lock: -------------------------------------------------------------------------------- 1 | version = 1 2 | requires-python = ">=3.13" 3 | 4 | [[package]] 5 | name = "annotated-types" 6 | version = "0.7.0" 7 | source = { registry = "https://pypi.org/simple" } 8 | sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } 9 | wheels = [ 10 | { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, 11 | ] 12 | 13 | [[package]] 14 | name = "anyio" 15 | version = "4.8.0" 16 | source = { registry = "https://pypi.org/simple" } 17 | dependencies = [ 18 | { name = "idna" }, 19 | { name = "sniffio" }, 20 | ] 21 | sdist = { url = "https://files.pythonhosted.org/packages/a3/73/199a98fc2dae33535d6b8e8e6ec01f8c1d76c9adb096c6b7d64823038cde/anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a", size = 181126 } 22 | wheels = [ 23 | { url = "https://files.pythonhosted.org/packages/46/eb/e7f063ad1fec6b3178a3cd82d1a3c4de82cccf283fc42746168188e1cdd5/anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a", size = 96041 }, 24 | ] 25 | 26 | [[package]] 27 | name = "certifi" 28 | version = "2025.1.31" 29 | source = { registry = "https://pypi.org/simple" } 30 | sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577 } 31 | wheels = [ 32 | { url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393 }, 33 | ] 34 | 35 | [[package]] 36 | name = "click" 37 | version = "8.1.8" 38 | source = { registry = "https://pypi.org/simple" } 39 | dependencies = [ 40 | { name = "colorama", marker = "sys_platform == 'win32'" }, 41 | ] 42 | sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 } 43 | wheels = [ 44 | { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 }, 45 | ] 46 | 47 | [[package]] 48 | name = "colorama" 49 | version = "0.4.6" 50 | source = { registry = "https://pypi.org/simple" } 51 | sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } 52 | wheels = [ 53 | { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, 54 | ] 55 | 56 | [[package]] 57 | name = "h11" 58 | version = "0.14.0" 59 | source = { registry = "https://pypi.org/simple" } 60 | sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 } 61 | wheels = [ 62 | { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 }, 63 | ] 64 | 65 | [[package]] 66 | name = "httpcore" 67 | version = "1.0.7" 68 | source = { registry = "https://pypi.org/simple" } 69 | dependencies = [ 70 | { name = "certifi" }, 71 | { name = "h11" }, 72 | ] 73 | sdist = { url = "https://files.pythonhosted.org/packages/6a/41/d7d0a89eb493922c37d343b607bc1b5da7f5be7e383740b4753ad8943e90/httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c", size = 85196 } 74 | wheels = [ 75 | { url = "https://files.pythonhosted.org/packages/87/f5/72347bc88306acb359581ac4d52f23c0ef445b57157adedb9aee0cd689d2/httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd", size = 78551 }, 76 | ] 77 | 78 | [[package]] 79 | name = "httpx" 80 | version = "0.28.1" 81 | source = { registry = "https://pypi.org/simple" } 82 | dependencies = [ 83 | { name = "anyio" }, 84 | { name = "certifi" }, 85 | { name = "httpcore" }, 86 | { name = "idna" }, 87 | ] 88 | sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } 89 | wheels = [ 90 | { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, 91 | ] 92 | 93 | [[package]] 94 | name = "httpx-sse" 95 | version = "0.4.0" 96 | source = { registry = "https://pypi.org/simple" } 97 | sdist = { url = "https://files.pythonhosted.org/packages/4c/60/8f4281fa9bbf3c8034fd54c0e7412e66edbab6bc74c4996bd616f8d0406e/httpx-sse-0.4.0.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721", size = 12624 } 98 | wheels = [ 99 | { url = "https://files.pythonhosted.org/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.4.0-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f", size = 7819 }, 100 | ] 101 | 102 | [[package]] 103 | name = "idna" 104 | version = "3.10" 105 | source = { registry = "https://pypi.org/simple" } 106 | sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } 107 | wheels = [ 108 | { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, 109 | ] 110 | 111 | [[package]] 112 | name = "mcp" 113 | version = "1.2.1" 114 | source = { registry = "https://pypi.org/simple" } 115 | dependencies = [ 116 | { name = "anyio" }, 117 | { name = "httpx" }, 118 | { name = "httpx-sse" }, 119 | { name = "pydantic" }, 120 | { name = "pydantic-settings" }, 121 | { name = "sse-starlette" }, 122 | { name = "starlette" }, 123 | { name = "uvicorn" }, 124 | ] 125 | sdist = { url = "https://files.pythonhosted.org/packages/fc/30/51e4555826126e3954fa2ab1e934bf74163c5fe05e98f38ca4d0f8abbf63/mcp-1.2.1.tar.gz", hash = "sha256:c9d43dbfe943aa1530e2be8f54b73af3ebfb071243827b4483d421684806cb45", size = 103968 } 126 | wheels = [ 127 | { url = "https://files.pythonhosted.org/packages/4c/0d/6770742a84c8aa1d36c0d628896a380584c5759612e66af7446af07d8775/mcp-1.2.1-py3-none-any.whl", hash = "sha256:579bf9c9157850ebb1344f3ca6f7a3021b0123c44c9f089ef577a7062522f0fd", size = 66453 }, 128 | ] 129 | 130 | [[package]] 131 | name = "mcp-applemusic" 132 | version = "0.1.0" 133 | source = { virtual = "." } 134 | dependencies = [ 135 | { name = "mcp" }, 136 | ] 137 | 138 | [package.metadata] 139 | requires-dist = [{ name = "mcp", specifier = ">=1.2.1" }] 140 | 141 | [[package]] 142 | name = "pydantic" 143 | version = "2.10.6" 144 | source = { registry = "https://pypi.org/simple" } 145 | dependencies = [ 146 | { name = "annotated-types" }, 147 | { name = "pydantic-core" }, 148 | { name = "typing-extensions" }, 149 | ] 150 | sdist = { url = "https://files.pythonhosted.org/packages/b7/ae/d5220c5c52b158b1de7ca89fc5edb72f304a70a4c540c84c8844bf4008de/pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236", size = 761681 } 151 | wheels = [ 152 | { url = "https://files.pythonhosted.org/packages/f4/3c/8cc1cc84deffa6e25d2d0c688ebb80635dfdbf1dbea3e30c541c8cf4d860/pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584", size = 431696 }, 153 | ] 154 | 155 | [[package]] 156 | name = "pydantic-core" 157 | version = "2.27.2" 158 | source = { registry = "https://pypi.org/simple" } 159 | dependencies = [ 160 | { name = "typing-extensions" }, 161 | ] 162 | sdist = { url = "https://files.pythonhosted.org/packages/fc/01/f3e5ac5e7c25833db5eb555f7b7ab24cd6f8c322d3a3ad2d67a952dc0abc/pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39", size = 413443 } 163 | wheels = [ 164 | { url = "https://files.pythonhosted.org/packages/41/b1/9bc383f48f8002f99104e3acff6cba1231b29ef76cfa45d1506a5cad1f84/pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b", size = 1892709 }, 165 | { url = "https://files.pythonhosted.org/packages/10/6c/e62b8657b834f3eb2961b49ec8e301eb99946245e70bf42c8817350cbefc/pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154", size = 1811273 }, 166 | { url = "https://files.pythonhosted.org/packages/ba/15/52cfe49c8c986e081b863b102d6b859d9defc63446b642ccbbb3742bf371/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9", size = 1823027 }, 167 | { url = "https://files.pythonhosted.org/packages/b1/1c/b6f402cfc18ec0024120602bdbcebc7bdd5b856528c013bd4d13865ca473/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9", size = 1868888 }, 168 | { url = "https://files.pythonhosted.org/packages/bd/7b/8cb75b66ac37bc2975a3b7de99f3c6f355fcc4d89820b61dffa8f1e81677/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1", size = 2037738 }, 169 | { url = "https://files.pythonhosted.org/packages/c8/f1/786d8fe78970a06f61df22cba58e365ce304bf9b9f46cc71c8c424e0c334/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a", size = 2685138 }, 170 | { url = "https://files.pythonhosted.org/packages/a6/74/d12b2cd841d8724dc8ffb13fc5cef86566a53ed358103150209ecd5d1999/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e", size = 1997025 }, 171 | { url = "https://files.pythonhosted.org/packages/a0/6e/940bcd631bc4d9a06c9539b51f070b66e8f370ed0933f392db6ff350d873/pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4", size = 2004633 }, 172 | { url = "https://files.pythonhosted.org/packages/50/cc/a46b34f1708d82498c227d5d80ce615b2dd502ddcfd8376fc14a36655af1/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27", size = 1999404 }, 173 | { url = "https://files.pythonhosted.org/packages/ca/2d/c365cfa930ed23bc58c41463bae347d1005537dc8db79e998af8ba28d35e/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee", size = 2130130 }, 174 | { url = "https://files.pythonhosted.org/packages/f4/d7/eb64d015c350b7cdb371145b54d96c919d4db516817f31cd1c650cae3b21/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1", size = 2157946 }, 175 | { url = "https://files.pythonhosted.org/packages/a4/99/bddde3ddde76c03b65dfd5a66ab436c4e58ffc42927d4ff1198ffbf96f5f/pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130", size = 1834387 }, 176 | { url = "https://files.pythonhosted.org/packages/71/47/82b5e846e01b26ac6f1893d3c5f9f3a2eb6ba79be26eef0b759b4fe72946/pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee", size = 1990453 }, 177 | { url = "https://files.pythonhosted.org/packages/51/b2/b2b50d5ecf21acf870190ae5d093602d95f66c9c31f9d5de6062eb329ad1/pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b", size = 1885186 }, 178 | ] 179 | 180 | [[package]] 181 | name = "pydantic-settings" 182 | version = "2.7.1" 183 | source = { registry = "https://pypi.org/simple" } 184 | dependencies = [ 185 | { name = "pydantic" }, 186 | { name = "python-dotenv" }, 187 | ] 188 | sdist = { url = "https://files.pythonhosted.org/packages/73/7b/c58a586cd7d9ac66d2ee4ba60ca2d241fa837c02bca9bea80a9a8c3d22a9/pydantic_settings-2.7.1.tar.gz", hash = "sha256:10c9caad35e64bfb3c2fbf70a078c0e25cc92499782e5200747f942a065dec93", size = 79920 } 189 | wheels = [ 190 | { url = "https://files.pythonhosted.org/packages/b4/46/93416fdae86d40879714f72956ac14df9c7b76f7d41a4d68aa9f71a0028b/pydantic_settings-2.7.1-py3-none-any.whl", hash = "sha256:590be9e6e24d06db33a4262829edef682500ef008565a969c73d39d5f8bfb3fd", size = 29718 }, 191 | ] 192 | 193 | [[package]] 194 | name = "python-dotenv" 195 | version = "1.0.1" 196 | source = { registry = "https://pypi.org/simple" } 197 | sdist = { url = "https://files.pythonhosted.org/packages/bc/57/e84d88dfe0aec03b7a2d4327012c1627ab5f03652216c63d49846d7a6c58/python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", size = 39115 } 198 | wheels = [ 199 | { url = "https://files.pythonhosted.org/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a", size = 19863 }, 200 | ] 201 | 202 | [[package]] 203 | name = "sniffio" 204 | version = "1.3.1" 205 | source = { registry = "https://pypi.org/simple" } 206 | sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 } 207 | wheels = [ 208 | { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, 209 | ] 210 | 211 | [[package]] 212 | name = "sse-starlette" 213 | version = "2.2.1" 214 | source = { registry = "https://pypi.org/simple" } 215 | dependencies = [ 216 | { name = "anyio" }, 217 | { name = "starlette" }, 218 | ] 219 | sdist = { url = "https://files.pythonhosted.org/packages/71/a4/80d2a11af59fe75b48230846989e93979c892d3a20016b42bb44edb9e398/sse_starlette-2.2.1.tar.gz", hash = "sha256:54470d5f19274aeed6b2d473430b08b4b379ea851d953b11d7f1c4a2c118b419", size = 17376 } 220 | wheels = [ 221 | { url = "https://files.pythonhosted.org/packages/d9/e0/5b8bd393f27f4a62461c5cf2479c75a2cc2ffa330976f9f00f5f6e4f50eb/sse_starlette-2.2.1-py3-none-any.whl", hash = "sha256:6410a3d3ba0c89e7675d4c273a301d64649c03a5ef1ca101f10b47f895fd0e99", size = 10120 }, 222 | ] 223 | 224 | [[package]] 225 | name = "starlette" 226 | version = "0.45.3" 227 | source = { registry = "https://pypi.org/simple" } 228 | dependencies = [ 229 | { name = "anyio" }, 230 | ] 231 | sdist = { url = "https://files.pythonhosted.org/packages/ff/fb/2984a686808b89a6781526129a4b51266f678b2d2b97ab2d325e56116df8/starlette-0.45.3.tar.gz", hash = "sha256:2cbcba2a75806f8a41c722141486f37c28e30a0921c5f6fe4346cb0dcee1302f", size = 2574076 } 232 | wheels = [ 233 | { url = "https://files.pythonhosted.org/packages/d9/61/f2b52e107b1fc8944b33ef56bf6ac4ebbe16d91b94d2b87ce013bf63fb84/starlette-0.45.3-py3-none-any.whl", hash = "sha256:dfb6d332576f136ec740296c7e8bb8c8a7125044e7c6da30744718880cdd059d", size = 71507 }, 234 | ] 235 | 236 | [[package]] 237 | name = "typing-extensions" 238 | version = "4.12.2" 239 | source = { registry = "https://pypi.org/simple" } 240 | sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 } 241 | wheels = [ 242 | { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 }, 243 | ] 244 | 245 | [[package]] 246 | name = "uvicorn" 247 | version = "0.34.0" 248 | source = { registry = "https://pypi.org/simple" } 249 | dependencies = [ 250 | { name = "click" }, 251 | { name = "h11" }, 252 | ] 253 | sdist = { url = "https://files.pythonhosted.org/packages/4b/4d/938bd85e5bf2edeec766267a5015ad969730bb91e31b44021dfe8b22df6c/uvicorn-0.34.0.tar.gz", hash = "sha256:404051050cd7e905de2c9a7e61790943440b3416f49cb409f965d9dcd0fa73e9", size = 76568 } 254 | wheels = [ 255 | { url = "https://files.pythonhosted.org/packages/61/14/33a3a1352cfa71812a3a21e8c9bfb83f60b0011f5e36f2b1399d51928209/uvicorn-0.34.0-py3-none-any.whl", hash = "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4", size = 62315 }, 256 | ] 257 | --------------------------------------------------------------------------------