applemusic-mcp 0.10.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- applemusic_mcp-0.10.4/.github/workflows/publish.yml +54 -0
- applemusic_mcp-0.10.4/.github/workflows/release.yml +105 -0
- applemusic_mcp-0.10.4/.github/workflows/test.yml +40 -0
- applemusic_mcp-0.10.4/.gitignore +53 -0
- applemusic_mcp-0.10.4/CHANGELOG.md +898 -0
- applemusic_mcp-0.10.4/LICENSE +21 -0
- applemusic_mcp-0.10.4/PKG-INFO +453 -0
- applemusic_mcp-0.10.4/README.md +415 -0
- applemusic_mcp-0.10.4/SKILL.md +718 -0
- applemusic_mcp-0.10.4/config.example.json +11 -0
- applemusic_mcp-0.10.4/pyproject.toml +75 -0
- applemusic_mcp-0.10.4/scripts/integration_test.py +410 -0
- applemusic_mcp-0.10.4/scripts/integration_test_applescript.py +384 -0
- applemusic_mcp-0.10.4/scripts/validate_endpoints.py +467 -0
- applemusic_mcp-0.10.4/src/applemusic_mcp/__init__.py +6 -0
- applemusic_mcp-0.10.4/src/applemusic_mcp/__main__.py +6 -0
- applemusic_mcp-0.10.4/src/applemusic_mcp/applescript.py +3641 -0
- applemusic_mcp-0.10.4/src/applemusic_mcp/audit_log.py +264 -0
- applemusic_mcp-0.10.4/src/applemusic_mcp/auth.py +371 -0
- applemusic_mcp-0.10.4/src/applemusic_mcp/cli.py +207 -0
- applemusic_mcp-0.10.4/src/applemusic_mcp/server.py +7076 -0
- applemusic_mcp-0.10.4/src/applemusic_mcp/track_cache.py +346 -0
- applemusic_mcp-0.10.4/tests/__init__.py +1 -0
- applemusic_mcp-0.10.4/tests/conftest.py +144 -0
- applemusic_mcp-0.10.4/tests/test_applescript.py +2045 -0
- applemusic_mcp-0.10.4/tests/test_audit_log.py +283 -0
- applemusic_mcp-0.10.4/tests/test_auth.py +262 -0
- applemusic_mcp-0.10.4/tests/test_integration.py +420 -0
- applemusic_mcp-0.10.4/tests/test_server.py +4346 -0
- applemusic_mcp-0.10.4/tests/test_track_cache.py +354 -0
- applemusic_mcp-0.10.4/uv.lock +1208 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
name: publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build:
|
|
11
|
+
name: Build sdist + wheel
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- name: Install uv
|
|
17
|
+
uses: astral-sh/setup-uv@v3
|
|
18
|
+
with:
|
|
19
|
+
enable-cache: true
|
|
20
|
+
|
|
21
|
+
- name: Set up Python
|
|
22
|
+
run: uv python install 3.12
|
|
23
|
+
|
|
24
|
+
- name: Build distributions
|
|
25
|
+
run: uv build
|
|
26
|
+
|
|
27
|
+
- name: Upload build artifacts
|
|
28
|
+
uses: actions/upload-artifact@v4
|
|
29
|
+
with:
|
|
30
|
+
name: dist
|
|
31
|
+
path: dist/
|
|
32
|
+
|
|
33
|
+
publish:
|
|
34
|
+
name: Publish to PyPI
|
|
35
|
+
needs: build
|
|
36
|
+
runs-on: ubuntu-latest
|
|
37
|
+
# Trusted Publishing — no API token needed in repo secrets.
|
|
38
|
+
# Configure once at https://pypi.org/manage/project/applemusic-mcp/settings/publishing/
|
|
39
|
+
# with publisher: GitHub, owner: epheterson, repo: applemusic-mcp,
|
|
40
|
+
# workflow: publish.yml, environment: pypi
|
|
41
|
+
environment:
|
|
42
|
+
name: pypi
|
|
43
|
+
url: https://pypi.org/project/applemusic-mcp/
|
|
44
|
+
permissions:
|
|
45
|
+
id-token: write
|
|
46
|
+
steps:
|
|
47
|
+
- name: Download build artifacts
|
|
48
|
+
uses: actions/download-artifact@v4
|
|
49
|
+
with:
|
|
50
|
+
name: dist
|
|
51
|
+
path: dist/
|
|
52
|
+
|
|
53
|
+
- name: Publish to PyPI
|
|
54
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
release:
|
|
9
|
+
name: Tag + GitHub Release on version bump
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
permissions:
|
|
12
|
+
contents: write # create tags and releases
|
|
13
|
+
actions: write # dispatch publish.yml after creating the release
|
|
14
|
+
# (without this, gh workflow run gets HTTP 403 from
|
|
15
|
+
# the default GITHUB_TOKEN — discovered when v0.10.2
|
|
16
|
+
# release was created but publish.yml never fired)
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
with:
|
|
20
|
+
fetch-depth: 0 # full history so we can list commits since last tag
|
|
21
|
+
|
|
22
|
+
- name: Read version from pyproject.toml
|
|
23
|
+
id: version
|
|
24
|
+
run: |
|
|
25
|
+
# Extract `version = "X.Y.Z"` from the [project] block.
|
|
26
|
+
ver=$(grep -E '^version = ' pyproject.toml | head -1 | sed -E 's/version = "(.*)"/\1/')
|
|
27
|
+
echo "version=$ver" >> "$GITHUB_OUTPUT"
|
|
28
|
+
echo "tag=v$ver" >> "$GITHUB_OUTPUT"
|
|
29
|
+
echo "Detected version: $ver"
|
|
30
|
+
|
|
31
|
+
- name: Check if tag already exists
|
|
32
|
+
id: check
|
|
33
|
+
env:
|
|
34
|
+
TAG: ${{ steps.version.outputs.tag }}
|
|
35
|
+
run: |
|
|
36
|
+
if git rev-parse "$TAG" >/dev/null 2>&1; then
|
|
37
|
+
echo "exists=true" >> "$GITHUB_OUTPUT"
|
|
38
|
+
echo "Tag $TAG already exists - nothing to release."
|
|
39
|
+
else
|
|
40
|
+
echo "exists=false" >> "$GITHUB_OUTPUT"
|
|
41
|
+
echo "Tag $TAG does not exist - will create release."
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
- name: Extract this version's CHANGELOG section
|
|
45
|
+
id: notes
|
|
46
|
+
env:
|
|
47
|
+
VERSION: ${{ steps.version.outputs.version }}
|
|
48
|
+
run: |
|
|
49
|
+
# Pull this release's notes from the section between `## [VERSION]`
|
|
50
|
+
# and the next `## [`. Empty result falls through to --generate-notes.
|
|
51
|
+
notes=$(awk -v ver="$VERSION" '
|
|
52
|
+
/^## \[/ {
|
|
53
|
+
if (capturing) { exit }
|
|
54
|
+
if ($0 ~ "^## \\[" ver "\\]") { capturing=1; next }
|
|
55
|
+
}
|
|
56
|
+
capturing { print }
|
|
57
|
+
' CHANGELOG.md)
|
|
58
|
+
# Trim leading/trailing blank lines.
|
|
59
|
+
notes=$(printf '%s' "$notes" | awk 'NF{found=1} found' | tac | awk 'NF{found=1} found' | tac)
|
|
60
|
+
if [ -z "$notes" ]; then
|
|
61
|
+
echo "No CHANGELOG section for v$VERSION — will fall back to auto-generated notes."
|
|
62
|
+
echo "found=false" >> "$GITHUB_OUTPUT"
|
|
63
|
+
else
|
|
64
|
+
echo "found=true" >> "$GITHUB_OUTPUT"
|
|
65
|
+
{
|
|
66
|
+
echo "body<<NOTES_EOF"
|
|
67
|
+
printf '%s\n' "$notes"
|
|
68
|
+
echo "NOTES_EOF"
|
|
69
|
+
} >> "$GITHUB_OUTPUT"
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
- name: Create tag + GitHub Release
|
|
73
|
+
if: steps.check.outputs.exists == 'false'
|
|
74
|
+
env:
|
|
75
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
76
|
+
TAG: ${{ steps.version.outputs.tag }}
|
|
77
|
+
VERSION: ${{ steps.version.outputs.version }}
|
|
78
|
+
NOTES_FOUND: ${{ steps.notes.outputs.found }}
|
|
79
|
+
NOTES_BODY: ${{ steps.notes.outputs.body }}
|
|
80
|
+
run: |
|
|
81
|
+
if [ "$NOTES_FOUND" = "true" ]; then
|
|
82
|
+
gh release create "$TAG" \
|
|
83
|
+
--title "v$VERSION" \
|
|
84
|
+
--notes "$NOTES_BODY" \
|
|
85
|
+
--target "$GITHUB_SHA"
|
|
86
|
+
else
|
|
87
|
+
gh release create "$TAG" \
|
|
88
|
+
--title "v$VERSION" \
|
|
89
|
+
--generate-notes \
|
|
90
|
+
--target "$GITHUB_SHA"
|
|
91
|
+
fi
|
|
92
|
+
echo "Created release $TAG"
|
|
93
|
+
|
|
94
|
+
- name: Trigger publish workflow
|
|
95
|
+
if: steps.check.outputs.exists == 'false'
|
|
96
|
+
env:
|
|
97
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
98
|
+
run: |
|
|
99
|
+
# Tag pushes made with the default GITHUB_TOKEN do NOT trigger
|
|
100
|
+
# other workflows (loop-prevention safeguard). workflow_dispatch
|
|
101
|
+
# IS exempt from that rule, so explicitly dispatch publish.yml
|
|
102
|
+
# to upload to PyPI. publish.yml checks out the default branch
|
|
103
|
+
# which now has the just-created tag pointing to the same SHA.
|
|
104
|
+
gh workflow run publish.yml --ref main
|
|
105
|
+
echo "Dispatched publish.yml"
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
name: tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
test:
|
|
12
|
+
name: pytest (Python ${{ matrix.python-version }})
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
strategy:
|
|
15
|
+
fail-fast: false
|
|
16
|
+
matrix:
|
|
17
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
18
|
+
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
|
|
22
|
+
- name: Install uv
|
|
23
|
+
uses: astral-sh/setup-uv@v3
|
|
24
|
+
with:
|
|
25
|
+
enable-cache: true
|
|
26
|
+
|
|
27
|
+
- name: Set up Python
|
|
28
|
+
env:
|
|
29
|
+
PYVER: ${{ matrix.python-version }}
|
|
30
|
+
run: uv python install "$PYVER"
|
|
31
|
+
|
|
32
|
+
- name: Install project
|
|
33
|
+
run: uv sync --all-extras --dev
|
|
34
|
+
|
|
35
|
+
- name: Run tests
|
|
36
|
+
# AppleScript-dependent tests (test_applescript.py) auto-skip on
|
|
37
|
+
# non-darwin via pytestmark in the file. Cross-platform mock-based
|
|
38
|
+
# tests in test_server.py / test_auth.py / test_integration.py run
|
|
39
|
+
# on every Python version.
|
|
40
|
+
run: uv run pytest tests/ -q --tb=short
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
build/
|
|
8
|
+
develop-eggs/
|
|
9
|
+
dist/
|
|
10
|
+
downloads/
|
|
11
|
+
eggs/
|
|
12
|
+
.eggs/
|
|
13
|
+
lib/
|
|
14
|
+
lib64/
|
|
15
|
+
parts/
|
|
16
|
+
sdist/
|
|
17
|
+
var/
|
|
18
|
+
wheels/
|
|
19
|
+
*.egg-info/
|
|
20
|
+
.installed.cfg
|
|
21
|
+
*.egg
|
|
22
|
+
|
|
23
|
+
# Virtual environments
|
|
24
|
+
venv/
|
|
25
|
+
ENV/
|
|
26
|
+
env/
|
|
27
|
+
.venv/
|
|
28
|
+
|
|
29
|
+
# IDE
|
|
30
|
+
.idea/
|
|
31
|
+
.vscode/
|
|
32
|
+
*.swp
|
|
33
|
+
*.swo
|
|
34
|
+
*~
|
|
35
|
+
|
|
36
|
+
# OS
|
|
37
|
+
.DS_Store
|
|
38
|
+
Thumbs.db
|
|
39
|
+
|
|
40
|
+
# Secrets - NEVER commit these
|
|
41
|
+
*.p8
|
|
42
|
+
config.json
|
|
43
|
+
developer_token.json
|
|
44
|
+
music_user_token.json
|
|
45
|
+
|
|
46
|
+
# Test
|
|
47
|
+
.pytest_cache/
|
|
48
|
+
.coverage
|
|
49
|
+
htmlcov/
|
|
50
|
+
apple-music-credentials.md
|
|
51
|
+
*_results.txt
|
|
52
|
+
PLAN.md
|
|
53
|
+
.claude/settings.local.json
|