Musicians MCP
README.md
# Musicians MCP Server
Python MCP server for a classical musicians database stored in Supabase/PostgreSQL.
This project now supports both:
- **trusted database read/review tools**, and
- **deterministic external-source enrichment/staging tools**
for MusicBrainz and Wikidata.
The MCP client is the AI agent. This server does **not** call any LLM APIs internally.
## Features
### Core database tools
- `health_check()`
- `search_musicians(query: str, limit: int = 10)`
- `get_musician_profile(musician_id: str)`
- `create_import_job(names: list[str], job_name: str | None = None)`
- `get_review_queue(limit: int = 20)`
- `approve_staged_musician(staged_profile_id: str)`
### External-source enrichment and staging tools
- `resolve_musician_identity(name: str, limit: int = 5)`
- `fetch_wikidata_profile(qid: str)`
- `fetch_musicbrainz_artist(mbid: str)`
- `stage_musician_from_sources(...)`
- `detect_staged_profile_conflicts(staged_profile_id: str)`
- `process_import_job_next(import_job_id: str)`
- `process_import_job_batch(import_job_id: str, max_names: int = 5)`
## Project Structure
```text
.
├── .env.example
├── pyproject.toml
├── README.md
└── src
└── musicians_mcp
├── __init__.py
├── db.py
├── external_sources.py
└── server.py
```
## Requirements
- Python 3.11+
- Supabase project URL
- Supabase service role key
- a valid MusicBrainz User-Agent string
- a valid Wikimedia User-Agent string
## Setup
```bash
python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install -e .
cp .env.example .env
```
Update `.env` with:
```env
SUPABASE_URL=https://your-project-ref.supabase.co
SUPABASE_SERVICE_ROLE_KEY=your-supabase-service-role-key
MUSICBRAINZ_USER_AGENT=ClassicalMusiciansMCP/0.1.0 (your-email-or-project-url)
WIKIMEDIA_USER_AGENT=ClassicalMusiciansMCP/0.1.0 (your-email-or-project-url)
```
User-Agent notes:
- MusicBrainz expects a descriptive User-Agent.
- Wikimedia also expects a descriptive User-Agent for automated access.
- A good format is:
`ClassicalMusiciansMCP/0.1.0 (your-email-or-project-url)`
## Run the Server
```bash
source .venv/bin/activate
python -m musicians_mcp.server
```
Or with the installed script:
```bash
source .venv/bin/activate
musicians-mcp
```
## Example MCP Client Config
```json
{
"mcpServers": {
"musicians": {
"command": "/absolute/path/to/Musicians MCP/.venv/bin/python",
"args": ["-m", "musicians_mcp.server"],
"cwd": "/absolute/path/to/Musicians MCP",
"env": {
"SUPABASE_URL": "https://your-project-ref.supabase.co",
"SUPABASE_SERVICE_ROLE_KEY": "your-supabase-service-role-key",
"MUSICBRAINZ_USER_AGENT": "ClassicalMusiciansMCP/0.1.0 (your-email-or-project-url)",
"WIKIMEDIA_USER_AGENT": "ClassicalMusiciansMCP/0.1.0 (your-email-or-project-url)"
},
"disabled": false,
"autoApprove": []
}
}
}
```
If your MCP client loads environment variables from the working directory, you can keep the values in `.env` instead.
## Safety Rules
This server is intentionally conservative.
- It does **not** write directly to `musicians`.
- It does **not** auto-approve staged musicians.
- It does **not** call `approve_staged_musician` during staging or batch processing.
- It only stages facts that came from source-backed deterministic fetches.
- It writes review signals and ambiguity details into staged rows/audit logs.
- If identity resolution is ambiguous, it keeps the profile in review-oriented status instead of pretending the identity is verified.
Trusted promotion must still happen manually through:
- `approve_staged_musician(p_staged_profile_id uuid)`
## External Source Behavior
### MusicBrainz
The server can:
- search artists by name
- fetch artist details by MBID
- normalize aliases, artist type, country/area, life-span, disambiguation, and source URL
### Wikidata
The server can:
- search entities by name
- fetch item details by QID
- normalize labels, description, aliases, dates, citizenship, occupations, instruments, notable works, websites, and selected external identifiers
### Request discipline
Both source clients:
- always send configured User-Agent headers
- apply conservative pacing
- use retry logic for transient HTTP failures
- log every external request
## Tool Summary
### `health_check()`
Verifies that the server can connect to Supabase.
### `search_musicians(query, limit=10)`
Searches `musicians` by:
- `full_name`
- `display_name`
- `sort_name`
- `nationality`
- `primary_role`
Returns:
- `id`
- `full_name`
- `display_name`
- `nationality`
- `primary_role`
- `birth_date`
- `death_date`
- `confidence`
- `is_verified`
### `get_musician_profile(musician_id)`
Returns a single trusted musician plus related:
- `fact_claims`
- `external_identifiers`
### `create_import_job(names, job_name=None)`
Creates an import job and returns:
- `job_id`
- `total_names`
- `status`
### `get_review_queue(limit=20)`
Reads staged profiles from `staged_musician_review_queue`.
### `approve_staged_musician(staged_profile_id)`
Manually approves a staged profile through the Postgres RPC.
### `resolve_musician_identity(name, limit=5)`
Searches both Wikidata and MusicBrainz and returns ranked normalized candidates.
Each candidate includes:
- `source`
- `external_id`
- `name`
- `description` / `disambiguation`
- `birth_date`
- `death_date`
- `country`
- `roles`
- `match_score`
- `source_url`
This tool does **not** write to the database.
### `fetch_wikidata_profile(qid)`
Fetches and normalizes one Wikidata item.
Returns:
- structured profile data
- raw key fields useful for debugging
This tool does **not** write to the database.
### `fetch_musicbrainz_artist(mbid)`
Fetches and normalizes one MusicBrainz artist.
Returns:
- structured profile data
- raw key fields useful for debugging
This tool does **not** write to the database.
### `stage_musician_from_sources(...)`
Resolves and stages a musician from deterministic external sources.
Behavior:
- if explicit external IDs are provided, it fetches those exact profiles
- otherwise it resolves likely candidates first
- only strong matches may be selected automatically
- ambiguous cases remain review-oriented
- inserts one row into `staged_musician_profiles`
- inserts source-backed rows into `staged_fact_claims`
- writes audit entries into `import_audit_logs`
- does **not** write to `musicians`
- does **not** call approval automatically
Returns:
- `staged_profile_id`
- `status`
- `confidence`
- `selected_sources`
- `warnings`
### `detect_staged_profile_conflicts(staged_profile_id)`
Checks staged facts for conflicts such as:
- different birth dates
- different death dates
- different nationality/citizenship values
- inconsistent roles/occupations
Returns property-level conflict details.
### `process_import_job_next(import_job_id)`
Processes exactly one pending name from an import job.
It:
- stages a single profile
- updates processed names safely
- writes audit logs
- never approves automatically
### `process_import_job_batch(import_job_id, max_names=5)`
Processes up to `max_names` pending names.
Returns per-name:
- staging results
- errors
- job status progression
## Recommended Manual Test Flow
1. Run `health_check`
2. Run `resolve_musician_identity("Claude Debussy")`
3. Run `fetch_wikidata_profile` on the selected QID
4. Run `fetch_musicbrainz_artist` on the selected MusicBrainz artist ID
5. Run `create_import_job(["Claude Debussy", "Maurice Ravel"])`
6. Run `process_import_job_next(job_id)`
7. Run `get_review_queue()`
8. Run `detect_staged_profile_conflicts(staged_profile_id)`
9. Only then run `approve_staged_musician` manually if the staged record is acceptable
## Local Verification Commands
After setup, these are the main commands to run locally:
```bash
source .venv/bin/activate
python -m pip install -e .
python -m compileall src
python -c "import sys; sys.path.insert(0, 'src'); import musicians_mcp.server; print('server import ok')"
python -m musicians_mcp.server
```
## Notes on Schema Flexibility
This implementation is intentionally defensive around the import/staging tables because exact column layouts were not fully specified here.
In particular, `db.py` uses fallback payload shapes for:
- `musician_import_jobs`
- `staged_musician_profiles`
- `staged_fact_claims`
- `import_audit_logs`
That makes the first version more resilient, but if you share the exact table schemas next, I can tighten the insert/update logic to match them precisely and reduce fallback branching.
This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues