spotify-mcp-jamiew 0.3.0__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.
- spotify_mcp_jamiew-0.3.0/.dockerignore +57 -0
- spotify_mcp_jamiew-0.3.0/.github/workflows/ci.yml +158 -0
- spotify_mcp_jamiew-0.3.0/.github/workflows/claude-code-review.yml +57 -0
- spotify_mcp_jamiew-0.3.0/.github/workflows/claude.yml +50 -0
- spotify_mcp_jamiew-0.3.0/.github/workflows/deploy-pages.yml +40 -0
- spotify_mcp_jamiew-0.3.0/.github/workflows/publish.yml +52 -0
- spotify_mcp_jamiew-0.3.0/.gitignore +6 -0
- spotify_mcp_jamiew-0.3.0/.python-version +1 -0
- spotify_mcp_jamiew-0.3.0/CHANGELOG.md +58 -0
- spotify_mcp_jamiew-0.3.0/CLAUDE.md +89 -0
- spotify_mcp_jamiew-0.3.0/Dockerfile +39 -0
- spotify_mcp_jamiew-0.3.0/LICENSE +21 -0
- spotify_mcp_jamiew-0.3.0/PKG-INFO +163 -0
- spotify_mcp_jamiew-0.3.0/README.md +147 -0
- spotify_mcp_jamiew-0.3.0/docs/index.html +483 -0
- spotify_mcp_jamiew-0.3.0/media/take5.mp4 +0 -0
- spotify_mcp_jamiew-0.3.0/pyproject.toml +137 -0
- spotify_mcp_jamiew-0.3.0/release.sh +92 -0
- spotify_mcp_jamiew-0.3.0/server.json +43 -0
- spotify_mcp_jamiew-0.3.0/src/spotify_mcp/__init__.py +23 -0
- spotify_mcp_jamiew-0.3.0/src/spotify_mcp/errors.py +188 -0
- spotify_mcp_jamiew-0.3.0/src/spotify_mcp/fastmcp_server.py +1303 -0
- spotify_mcp_jamiew-0.3.0/src/spotify_mcp/logging_utils.py +111 -0
- spotify_mcp_jamiew-0.3.0/src/spotify_mcp/spotify_api.py +124 -0
- spotify_mcp_jamiew-0.3.0/src/spotify_mcp/spotify_types.py +107 -0
- spotify_mcp_jamiew-0.3.0/src/spotify_mcp/utils.py +23 -0
- spotify_mcp_jamiew-0.3.0/tests/conftest.py +176 -0
- spotify_mcp_jamiew-0.3.0/tests/test_errors.py +185 -0
- spotify_mcp_jamiew-0.3.0/tests/test_fastmcp_tools.py +788 -0
- spotify_mcp_jamiew-0.3.0/tests/test_packaging.py +80 -0
- spotify_mcp_jamiew-0.3.0/tests/test_spotify_api.py +97 -0
- spotify_mcp_jamiew-0.3.0/tests/test_utils.py +35 -0
- spotify_mcp_jamiew-0.3.0/uv.lock +1258 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Git and version control
|
|
2
|
+
.git/
|
|
3
|
+
.gitignore
|
|
4
|
+
.gitattributes
|
|
5
|
+
|
|
6
|
+
# System files
|
|
7
|
+
/proc/
|
|
8
|
+
/sys/
|
|
9
|
+
/dev/
|
|
10
|
+
|
|
11
|
+
# Python
|
|
12
|
+
__pycache__/
|
|
13
|
+
*.py[cod]
|
|
14
|
+
*$py.class
|
|
15
|
+
*.so
|
|
16
|
+
.Python
|
|
17
|
+
env/
|
|
18
|
+
venv/
|
|
19
|
+
.venv/
|
|
20
|
+
.env
|
|
21
|
+
.env.*
|
|
22
|
+
|
|
23
|
+
# Build artifacts
|
|
24
|
+
build/
|
|
25
|
+
dist/
|
|
26
|
+
*.egg-info/
|
|
27
|
+
.eggs/
|
|
28
|
+
|
|
29
|
+
# Testing and development
|
|
30
|
+
.pytest_cache/
|
|
31
|
+
.coverage
|
|
32
|
+
.mypy_cache/
|
|
33
|
+
.ruff_cache/
|
|
34
|
+
htmlcov/
|
|
35
|
+
*.log
|
|
36
|
+
|
|
37
|
+
# IDE and editors
|
|
38
|
+
.vscode/
|
|
39
|
+
.idea/
|
|
40
|
+
*.swp
|
|
41
|
+
*.swo
|
|
42
|
+
*~
|
|
43
|
+
.DS_Store
|
|
44
|
+
|
|
45
|
+
# Documentation
|
|
46
|
+
docs/
|
|
47
|
+
*.md
|
|
48
|
+
!README.md
|
|
49
|
+
|
|
50
|
+
# CI/CD
|
|
51
|
+
.github/
|
|
52
|
+
.gitlab-ci.yml
|
|
53
|
+
|
|
54
|
+
# Other
|
|
55
|
+
*.tmp
|
|
56
|
+
*.bak
|
|
57
|
+
node_modules/
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main, develop ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ main, develop ]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
needs: [lint] # Don't test broken code
|
|
13
|
+
strategy:
|
|
14
|
+
matrix:
|
|
15
|
+
python-version: ["3.12", "3.13"]
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- name: Checkout code
|
|
19
|
+
uses: actions/checkout@v4
|
|
20
|
+
|
|
21
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
22
|
+
uses: actions/setup-python@v5
|
|
23
|
+
with:
|
|
24
|
+
python-version: ${{ matrix.python-version }}
|
|
25
|
+
|
|
26
|
+
- name: Install uv
|
|
27
|
+
uses: astral-sh/setup-uv@v4
|
|
28
|
+
with:
|
|
29
|
+
# Install a specific uv version
|
|
30
|
+
version: "latest"
|
|
31
|
+
# Install uv globally
|
|
32
|
+
enable-cache: true
|
|
33
|
+
|
|
34
|
+
- name: Set up project
|
|
35
|
+
run: uv sync --dev
|
|
36
|
+
|
|
37
|
+
- name: Run type checking
|
|
38
|
+
run: uv run mypy src/
|
|
39
|
+
|
|
40
|
+
- name: Run tests
|
|
41
|
+
run: uv run pytest
|
|
42
|
+
|
|
43
|
+
lint:
|
|
44
|
+
runs-on: ubuntu-latest
|
|
45
|
+
steps:
|
|
46
|
+
- name: Checkout code
|
|
47
|
+
uses: actions/checkout@v4
|
|
48
|
+
|
|
49
|
+
- name: Set up Python
|
|
50
|
+
uses: actions/setup-python@v5
|
|
51
|
+
with:
|
|
52
|
+
python-version: "3.12"
|
|
53
|
+
|
|
54
|
+
- name: Install uv
|
|
55
|
+
uses: astral-sh/setup-uv@v4
|
|
56
|
+
with:
|
|
57
|
+
version: "latest"
|
|
58
|
+
enable-cache: true
|
|
59
|
+
|
|
60
|
+
- name: Set up project
|
|
61
|
+
run: uv sync --dev
|
|
62
|
+
|
|
63
|
+
- name: Run ruff linter
|
|
64
|
+
run: uv run ruff check src/ tests/
|
|
65
|
+
|
|
66
|
+
- name: Run ruff formatter check
|
|
67
|
+
run: uv run ruff format --check src/ tests/
|
|
68
|
+
|
|
69
|
+
security:
|
|
70
|
+
runs-on: ubuntu-latest
|
|
71
|
+
needs: [lint] # Don't scan broken code
|
|
72
|
+
steps:
|
|
73
|
+
- name: Checkout code
|
|
74
|
+
uses: actions/checkout@v4
|
|
75
|
+
|
|
76
|
+
- name: Set up Python
|
|
77
|
+
uses: actions/setup-python@v5
|
|
78
|
+
with:
|
|
79
|
+
python-version: "3.12"
|
|
80
|
+
|
|
81
|
+
- name: Install uv
|
|
82
|
+
uses: astral-sh/setup-uv@v4
|
|
83
|
+
with:
|
|
84
|
+
version: "latest"
|
|
85
|
+
enable-cache: true
|
|
86
|
+
|
|
87
|
+
- name: Set up project
|
|
88
|
+
run: uv sync --dev
|
|
89
|
+
|
|
90
|
+
- name: Run bandit security linter
|
|
91
|
+
run: uv run bandit -r src/
|
|
92
|
+
|
|
93
|
+
build:
|
|
94
|
+
runs-on: ubuntu-latest
|
|
95
|
+
needs: [test, lint, security] # Require all quality gates
|
|
96
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
97
|
+
|
|
98
|
+
steps:
|
|
99
|
+
- name: Checkout code
|
|
100
|
+
uses: actions/checkout@v4
|
|
101
|
+
|
|
102
|
+
- name: Set up Python
|
|
103
|
+
uses: actions/setup-python@v5
|
|
104
|
+
with:
|
|
105
|
+
python-version: "3.12"
|
|
106
|
+
|
|
107
|
+
- name: Install uv
|
|
108
|
+
uses: astral-sh/setup-uv@v4
|
|
109
|
+
with:
|
|
110
|
+
version: "latest"
|
|
111
|
+
enable-cache: true
|
|
112
|
+
|
|
113
|
+
- name: Set up project
|
|
114
|
+
run: uv sync --dev
|
|
115
|
+
|
|
116
|
+
- name: Build package
|
|
117
|
+
run: uv build
|
|
118
|
+
|
|
119
|
+
- name: Store build artifacts
|
|
120
|
+
uses: actions/upload-artifact@v4
|
|
121
|
+
with:
|
|
122
|
+
name: dist
|
|
123
|
+
path: dist/
|
|
124
|
+
|
|
125
|
+
docker:
|
|
126
|
+
runs-on: ubuntu-latest
|
|
127
|
+
needs: [test, lint, security]
|
|
128
|
+
steps:
|
|
129
|
+
- name: Checkout code
|
|
130
|
+
uses: actions/checkout@v4
|
|
131
|
+
|
|
132
|
+
- name: Set up Docker Buildx
|
|
133
|
+
uses: docker/setup-buildx-action@v3
|
|
134
|
+
|
|
135
|
+
- name: Build Docker image
|
|
136
|
+
uses: docker/build-push-action@v6
|
|
137
|
+
with:
|
|
138
|
+
context: .
|
|
139
|
+
file: ./Dockerfile
|
|
140
|
+
push: false
|
|
141
|
+
load: true # Load image into Docker daemon for testing
|
|
142
|
+
tags: spotify-mcp:test
|
|
143
|
+
cache-from: type=gha
|
|
144
|
+
cache-to: type=gha,mode=max
|
|
145
|
+
|
|
146
|
+
- name: Test Docker image
|
|
147
|
+
run: |
|
|
148
|
+
# Test that image was built and can run basic commands
|
|
149
|
+
docker run --rm spotify-mcp:test python --version
|
|
150
|
+
|
|
151
|
+
# Test that entry point is installed and accessible
|
|
152
|
+
docker run --rm spotify-mcp:test which spotify-mcp
|
|
153
|
+
|
|
154
|
+
# Test that application starts without errors (with proper env vars)
|
|
155
|
+
docker run --rm -e SPOTIFY_CLIENT_ID=test -e SPOTIFY_CLIENT_SECRET=test -e SPOTIFY_REDIRECT_URI=http://localhost:8888 spotify-mcp:test python -c "print('✅ Docker image works correctly')"
|
|
156
|
+
|
|
157
|
+
# Test security: ensure running as non-root user
|
|
158
|
+
docker run --rm spotify-mcp:test sh -c 'whoami | grep -v root'
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
name: Claude Code Review
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
types: [opened, synchronize]
|
|
6
|
+
# Optional: Only run on specific file changes
|
|
7
|
+
# paths:
|
|
8
|
+
# - "src/**/*.ts"
|
|
9
|
+
# - "src/**/*.tsx"
|
|
10
|
+
# - "src/**/*.js"
|
|
11
|
+
# - "src/**/*.jsx"
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
claude-review:
|
|
15
|
+
# Optional: Filter by PR author
|
|
16
|
+
# if: |
|
|
17
|
+
# github.event.pull_request.user.login == 'external-contributor' ||
|
|
18
|
+
# github.event.pull_request.user.login == 'new-developer' ||
|
|
19
|
+
# github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR'
|
|
20
|
+
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
permissions:
|
|
23
|
+
contents: read
|
|
24
|
+
pull-requests: read
|
|
25
|
+
issues: read
|
|
26
|
+
id-token: write
|
|
27
|
+
|
|
28
|
+
steps:
|
|
29
|
+
- name: Checkout repository
|
|
30
|
+
uses: actions/checkout@v4
|
|
31
|
+
with:
|
|
32
|
+
fetch-depth: 1
|
|
33
|
+
|
|
34
|
+
- name: Run Claude Code Review
|
|
35
|
+
id: claude-review
|
|
36
|
+
uses: anthropics/claude-code-action@v1
|
|
37
|
+
with:
|
|
38
|
+
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
39
|
+
prompt: |
|
|
40
|
+
REPO: ${{ github.repository }}
|
|
41
|
+
PR NUMBER: ${{ github.event.pull_request.number }}
|
|
42
|
+
|
|
43
|
+
Please review this pull request and provide feedback on:
|
|
44
|
+
- Code quality and best practices
|
|
45
|
+
- Potential bugs or issues
|
|
46
|
+
- Performance considerations
|
|
47
|
+
- Security concerns
|
|
48
|
+
- Test coverage
|
|
49
|
+
|
|
50
|
+
Use the repository's CLAUDE.md for guidance on style and conventions. Be constructive and helpful in your feedback.
|
|
51
|
+
|
|
52
|
+
Use `gh pr comment` with your Bash tool to leave your review as a comment on the PR.
|
|
53
|
+
|
|
54
|
+
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
|
|
55
|
+
# or https://docs.claude.com/en/docs/claude-code/cli-reference for available options
|
|
56
|
+
claude_args: '--allowed-tools "Bash(gh issue view:*),Bash(gh search:*),Bash(gh issue list:*),Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr list:*)"'
|
|
57
|
+
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
name: Claude Code
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
issue_comment:
|
|
5
|
+
types: [created]
|
|
6
|
+
pull_request_review_comment:
|
|
7
|
+
types: [created]
|
|
8
|
+
issues:
|
|
9
|
+
types: [opened, assigned]
|
|
10
|
+
pull_request_review:
|
|
11
|
+
types: [submitted]
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
claude:
|
|
15
|
+
if: |
|
|
16
|
+
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
|
|
17
|
+
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
|
|
18
|
+
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
|
|
19
|
+
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
permissions:
|
|
22
|
+
contents: read
|
|
23
|
+
pull-requests: read
|
|
24
|
+
issues: read
|
|
25
|
+
id-token: write
|
|
26
|
+
actions: read # Required for Claude to read CI results on PRs
|
|
27
|
+
steps:
|
|
28
|
+
- name: Checkout repository
|
|
29
|
+
uses: actions/checkout@v4
|
|
30
|
+
with:
|
|
31
|
+
fetch-depth: 1
|
|
32
|
+
|
|
33
|
+
- name: Run Claude Code
|
|
34
|
+
id: claude
|
|
35
|
+
uses: anthropics/claude-code-action@v1
|
|
36
|
+
with:
|
|
37
|
+
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
38
|
+
|
|
39
|
+
# This is an optional setting that allows Claude to read CI results on PRs
|
|
40
|
+
additional_permissions: |
|
|
41
|
+
actions: read
|
|
42
|
+
|
|
43
|
+
# Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it.
|
|
44
|
+
# prompt: 'Update the pull request description to include a summary of changes.'
|
|
45
|
+
|
|
46
|
+
# Optional: Add claude_args to customize behavior and configuration
|
|
47
|
+
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
|
|
48
|
+
# or https://docs.claude.com/en/docs/claude-code/cli-reference for available options
|
|
49
|
+
# claude_args: '--allowed-tools Bash(gh pr:*)'
|
|
50
|
+
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
name: Deploy GitHub Pages
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main ]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
pages: write
|
|
12
|
+
id-token: write
|
|
13
|
+
|
|
14
|
+
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
|
|
15
|
+
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
|
|
16
|
+
concurrency:
|
|
17
|
+
group: "pages"
|
|
18
|
+
cancel-in-progress: false
|
|
19
|
+
|
|
20
|
+
jobs:
|
|
21
|
+
deploy:
|
|
22
|
+
environment:
|
|
23
|
+
name: github-pages
|
|
24
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
steps:
|
|
27
|
+
- name: Checkout
|
|
28
|
+
uses: actions/checkout@v4
|
|
29
|
+
|
|
30
|
+
- name: Setup Pages
|
|
31
|
+
uses: actions/configure-pages@v5
|
|
32
|
+
|
|
33
|
+
- name: Upload artifact
|
|
34
|
+
uses: actions/upload-pages-artifact@v3
|
|
35
|
+
with:
|
|
36
|
+
path: './docs'
|
|
37
|
+
|
|
38
|
+
- name: Deploy to GitHub Pages
|
|
39
|
+
id: deployment
|
|
40
|
+
uses: actions/deploy-pages@v4
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
name: Publish to PyPI & MCP Registry
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
id-token: write
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
publish:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- name: Install uv
|
|
19
|
+
uses: astral-sh/setup-uv@v5
|
|
20
|
+
|
|
21
|
+
- name: Set up Python
|
|
22
|
+
run: uv python install 3.12
|
|
23
|
+
|
|
24
|
+
- name: Install dependencies
|
|
25
|
+
run: uv sync --all-groups
|
|
26
|
+
|
|
27
|
+
- name: Run type checking
|
|
28
|
+
run: uv run mypy src/
|
|
29
|
+
|
|
30
|
+
- name: Run tests
|
|
31
|
+
run: uv run pytest
|
|
32
|
+
|
|
33
|
+
- name: Build package
|
|
34
|
+
run: uv build
|
|
35
|
+
|
|
36
|
+
- name: Publish to PyPI
|
|
37
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
38
|
+
|
|
39
|
+
- name: Install mcp-publisher
|
|
40
|
+
run: |
|
|
41
|
+
curl -L "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_linux_amd64.tar.gz" | tar xz mcp-publisher
|
|
42
|
+
|
|
43
|
+
- name: Authenticate to MCP Registry
|
|
44
|
+
run: ./mcp-publisher login github-oidc
|
|
45
|
+
|
|
46
|
+
- name: Set version in server.json
|
|
47
|
+
run: |
|
|
48
|
+
VERSION=${GITHUB_REF#refs/tags/v}
|
|
49
|
+
jq --arg v "$VERSION" '.version = $v | .packages[0].version = $v' server.json > server.tmp && mv server.tmp server.json
|
|
50
|
+
|
|
51
|
+
- name: Publish to MCP Registry
|
|
52
|
+
run: ./mcp-publisher publish
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 2026-05-29
|
|
4
|
+
|
|
5
|
+
### Packaging & distribution
|
|
6
|
+
- published to PyPI as `spotify-mcp-jamiew` and to the MCP Registry as
|
|
7
|
+
`io.github.jamiew/spotify-mcp` (`spotify-mcp` is taken by upstream)
|
|
8
|
+
- one-command install via `uvx spotify-mcp-jamiew`; README now has copy-paste
|
|
9
|
+
setup for Claude Code, Claude Desktop, and Codex CLI
|
|
10
|
+
- automated release: a GitHub Actions workflow publishes to PyPI + the MCP
|
|
11
|
+
Registry on GitHub release via OIDC trusted publishing (no stored tokens)
|
|
12
|
+
- added packaging tests that keep `server.json` in sync with `pyproject.toml`
|
|
13
|
+
and the env vars the server reads
|
|
14
|
+
|
|
15
|
+
## 2026-05-28
|
|
16
|
+
|
|
17
|
+
### Modern MCP protocol features
|
|
18
|
+
- structured output: every tool returns a typed Pydantic model (real output
|
|
19
|
+
schemas) instead of bare dicts
|
|
20
|
+
- tool annotations (readOnly/destructive/idempotent/openWorld hints) + titles,
|
|
21
|
+
plus a Spotify icon on every tool, resource, and prompt
|
|
22
|
+
- progress + log notifications while paginating large playlists
|
|
23
|
+
- elicitation: `remove_tracks_from_playlist` confirms before deleting on clients
|
|
24
|
+
that support it, and proceeds without prompting on clients that don't
|
|
25
|
+
- new resources: track / playlist / artist / album by id
|
|
26
|
+
|
|
27
|
+
### Fixes
|
|
28
|
+
- playback now reads from `current_playback()`, so device/volume/shuffle/repeat
|
|
29
|
+
are populated instead of always null
|
|
30
|
+
- a destructive removal no longer slips through when an elicitation prompt errors
|
|
31
|
+
on a capable client — only genuinely unsupported clients skip confirmation
|
|
32
|
+
- playlist add/remove now surface the returned `snapshot_id`
|
|
33
|
+
- search tolerates null entries in result items
|
|
34
|
+
- dropped unused error codes left over from the deleted error helpers
|
|
35
|
+
|
|
36
|
+
### Dependencies updated to current majors
|
|
37
|
+
- bumped the runtime + dev stack to latest: mcp 1.27, spotipy 2.26, pytest 9,
|
|
38
|
+
mypy 2.0, ruff 0.15, pytest-cov 7 (pydantic 2.13 pulled in transitively)
|
|
39
|
+
|
|
40
|
+
### Audio-features and recommendations tools removed
|
|
41
|
+
- dropped both tools — Spotify deprecated those endpoints in nov 2024 and they
|
|
42
|
+
return 403 for apps created after that (tool count 13 → 11)
|
|
43
|
+
|
|
44
|
+
### Dead code removed
|
|
45
|
+
- removed the unused Client wrapper, utils parsers, and error/logging helpers
|
|
46
|
+
left over from the FastMCP rewrite (~1400 lines)
|
|
47
|
+
|
|
48
|
+
### Test coverage raised to 95%
|
|
49
|
+
- every tool now has a success and a failure test; the resources and prompts
|
|
50
|
+
are covered too (was 55%)
|
|
51
|
+
|
|
52
|
+
### Typed Spotify response shapes
|
|
53
|
+
- added TypedDicts for the Spotify objects the server consumes, applied at the
|
|
54
|
+
parse and model-building boundaries
|
|
55
|
+
|
|
56
|
+
## 2025-12-08 — 0.2.0
|
|
57
|
+
- batch support for tracks/audio features plus new tools
|
|
58
|
+
- added the release.sh helper script
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides essential guidance for working with the Spotify MCP server codebase.
|
|
4
|
+
|
|
5
|
+
## Essential Commands
|
|
6
|
+
|
|
7
|
+
### Development
|
|
8
|
+
- `uv run spotify-mcp` - Start the MCP server (local alias; the package publishes as `spotify-mcp-jamiew`, so end users run `uvx spotify-mcp-jamiew`)
|
|
9
|
+
- `uv sync` - Sync dependencies
|
|
10
|
+
- `uv run pytest` - Run all tests (must pass before commits)
|
|
11
|
+
- `uv run mypy src/` - Type checking (must pass before commits)
|
|
12
|
+
- Optional speedup on mypy 2.x: `uv run mypy src/ --num-workers 4` (parallel checking)
|
|
13
|
+
|
|
14
|
+
### Environment Setup
|
|
15
|
+
Required environment variables:
|
|
16
|
+
- `SPOTIFY_CLIENT_ID` - Spotify API Client ID
|
|
17
|
+
- `SPOTIFY_CLIENT_SECRET` - Spotify API Client Secret
|
|
18
|
+
|
|
19
|
+
Three-tier configuration (highest priority first):
|
|
20
|
+
1. Environment variables (for production/MCP usage)
|
|
21
|
+
2. `.env` file (for local development)
|
|
22
|
+
3. `pyproject.toml` defaults (fallback - edit `[tool.spotify-mcp.env]` section)
|
|
23
|
+
|
|
24
|
+
### Git Workflow
|
|
25
|
+
**Quality Gates**: Before any commit, ALWAYS run:
|
|
26
|
+
- `uv run mypy src/` - Type checking must pass
|
|
27
|
+
- `uv run pytest` - All tests must pass
|
|
28
|
+
|
|
29
|
+
**Commit Message Format:**
|
|
30
|
+
```
|
|
31
|
+
Brief description of change
|
|
32
|
+
|
|
33
|
+
Detailed explanation of what and why.
|
|
34
|
+
|
|
35
|
+
🤖 Generated with [Claude Code](https://claude.ai/code)
|
|
36
|
+
|
|
37
|
+
Co-Authored-By: Claude <noreply@anthropic.com>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Releasing
|
|
41
|
+
Publishing is fully automated via OIDC trusted publishing — no tokens stored anywhere.
|
|
42
|
+
|
|
43
|
+
1. Bump `version` in `pyproject.toml`, commit, and create a GitHub release for tag `vX.Y.Z`
|
|
44
|
+
(the `/release` skill or `release.sh` does the tag + `gh release create`).
|
|
45
|
+
2. The `release: published` event triggers `.github/workflows/publish.yml`, which tests → builds →
|
|
46
|
+
publishes to **PyPI** (`pypa/gh-action-pypi-publish`, OIDC) → publishes to the **MCP Registry**
|
|
47
|
+
(`mcp-publisher login github-oidc`), injecting the tag version into `server.json` at publish time.
|
|
48
|
+
|
|
49
|
+
The package publishes to PyPI as `spotify-mcp-jamiew` and to the registry as `io.github.jamiew/spotify-mcp`.
|
|
50
|
+
|
|
51
|
+
One-time setup (already required before the first successful run):
|
|
52
|
+
- PyPI: register a trusted publisher for project `spotify-mcp-jamiew` → owner `jamiew`, repo
|
|
53
|
+
`spotify-mcp`, workflow `publish.yml`.
|
|
54
|
+
- MCP Registry: the `io.github.jamiew/*` namespace is authenticated automatically via GitHub OIDC.
|
|
55
|
+
|
|
56
|
+
## Architecture
|
|
57
|
+
|
|
58
|
+
FastMCP-based MCP server for Spotify Web API integration using Python/`uv`.
|
|
59
|
+
|
|
60
|
+
### Core Files
|
|
61
|
+
- **`src/spotify_mcp/fastmcp_server.py`** - Main MCP server: tools, resources, and prompts using `@mcp.tool()`/`@mcp.resource()`/`@mcp.prompt()` decorators, with typed Pydantic output models
|
|
62
|
+
- **`src/spotify_mcp/spotify_api.py`** - OAuth client wrapper (auth/token management only); tools talk to `self.sp` directly
|
|
63
|
+
- **`src/spotify_mcp/spotify_types.py`** - TypedDicts for the Spotify response shapes the server consumes
|
|
64
|
+
- **`src/spotify_mcp/utils.py`** - Redirect-URI normalization
|
|
65
|
+
|
|
66
|
+
### Key Features
|
|
67
|
+
- **MCP Tools**: Playback control, search, queue management, playlist operations, track/artist info
|
|
68
|
+
- **Structured Output**: Every tool returns a typed Pydantic model (real output schema)
|
|
69
|
+
- **Tool Annotations & Icons**: read-only/destructive hints, titles, and a Spotify glyph on tools/resources/prompts
|
|
70
|
+
- **Progress & Elicitation**: progress notifications for large paginations; confirmation prompts before destructive playlist removals (when the client supports it)
|
|
71
|
+
- **Pagination Support**: Handles large datasets (10k+ tracks) with `limit`/`offset` parameters
|
|
72
|
+
- **OAuth Flow**: Automatic token management via spotipy
|
|
73
|
+
- **Type Safety**: Full Pydantic validation and MyPy compliance
|
|
74
|
+
- **Performance Logging**: Comprehensive timing and debug logging for tools and API calls
|
|
75
|
+
|
|
76
|
+
## Development Guidelines
|
|
77
|
+
|
|
78
|
+
### Tool Design Principles
|
|
79
|
+
- **Single Responsibility**: One focused purpose per tool (avoid `action` parameters)
|
|
80
|
+
- **Structured Returns**: Return a typed Pydantic model so the tool has a real output schema
|
|
81
|
+
- **Pagination-First**: Add `limit`/`offset` to tools that can return >20 items
|
|
82
|
+
- **Type Safety**: Use strict type hints and Pydantic validation
|
|
83
|
+
|
|
84
|
+
### Code Quality Standards
|
|
85
|
+
- Run `mypy` and `pytest` before every commit
|
|
86
|
+
- Convert Spotify exceptions to MCP-compliant errors
|
|
87
|
+
- Include Args/Returns in all tool docstrings
|
|
88
|
+
- Mock external API calls in tests
|
|
89
|
+
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Multi-stage build for Spotify MCP server
|
|
2
|
+
FROM python:3.12-alpine AS builder
|
|
3
|
+
|
|
4
|
+
# Install build dependencies
|
|
5
|
+
RUN apk add --no-cache build-base
|
|
6
|
+
|
|
7
|
+
# Install uv
|
|
8
|
+
RUN pip install --no-cache-dir uv
|
|
9
|
+
|
|
10
|
+
# Set working directory
|
|
11
|
+
WORKDIR /app
|
|
12
|
+
|
|
13
|
+
# Copy dependency files first for better caching
|
|
14
|
+
COPY pyproject.toml uv.lock ./
|
|
15
|
+
COPY src/ ./src/
|
|
16
|
+
COPY README.md ./
|
|
17
|
+
|
|
18
|
+
# Sync dependencies and build
|
|
19
|
+
RUN uv sync --frozen --no-dev
|
|
20
|
+
RUN uv build
|
|
21
|
+
|
|
22
|
+
# Production stage
|
|
23
|
+
FROM python:3.12-alpine AS runtime
|
|
24
|
+
|
|
25
|
+
# Install uv for runtime
|
|
26
|
+
RUN pip install --no-cache-dir uv
|
|
27
|
+
|
|
28
|
+
# Copy the built wheel and install it
|
|
29
|
+
COPY --from=builder /app/dist/*.whl /tmp/
|
|
30
|
+
RUN pip install --no-cache-dir /tmp/*.whl && rm /tmp/*.whl
|
|
31
|
+
|
|
32
|
+
# Create non-root user for security
|
|
33
|
+
RUN addgroup -g 1001 -S spotify && \
|
|
34
|
+
adduser -S -D -H -u 1001 -s /sbin/nologin -G spotify spotify
|
|
35
|
+
|
|
36
|
+
USER spotify
|
|
37
|
+
|
|
38
|
+
# Command to run the MCP server
|
|
39
|
+
CMD ["spotify-mcp"]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Varun Neal Srivastava
|
|
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.
|