mr-magic-mcp-server 0.1.12 → 0.1.13
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.
- package/README.md +487 -817
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,22 +1,39 @@
|
|
|
1
1
|
# Mr. Magic MCP Server
|
|
2
2
|
|
|
3
|
-
Mr. Magic bridges LRCLIB, Genius, Musixmatch, and Melon so MCP clients,
|
|
4
|
-
|
|
3
|
+
Mr. Magic bridges LRCLIB, Genius, Musixmatch, and Melon so MCP clients, JSON HTTP
|
|
4
|
+
automations, and CLI aficionados can all request lyrics from a single toolchain.
|
|
5
|
+
|
|
6
|
+
## Table of Contents
|
|
7
|
+
|
|
8
|
+
- [Prerequisites](#prerequisites)
|
|
9
|
+
- [Installation](#installation)
|
|
10
|
+
- [Environment Variables](#environment-variables)
|
|
11
|
+
- [Provider Credentials](#provider-credentials)
|
|
12
|
+
- [Export and Download Configuration](#export-and-download-configuration)
|
|
13
|
+
- [Local Deployment](#local-deployment)
|
|
14
|
+
- [Remote Deployment](#remote-deployment)
|
|
15
|
+
- [MCP Tools](#mcp-tools)
|
|
16
|
+
- [Airtable Integration](#airtable-integration)
|
|
17
|
+
- [MCP Client Configuration](#mcp-client-configuration)
|
|
18
|
+
- [CLI](#cli)
|
|
19
|
+
- [Manual Testing](#manual-testing)
|
|
20
|
+
- [Provider Notes](#provider-notes)
|
|
21
|
+
- [Changelog](#changelog)
|
|
22
|
+
- [License](#license)
|
|
5
23
|
|
|
6
24
|
## Prerequisites
|
|
7
25
|
|
|
8
26
|
- Node.js 18.17 or newer
|
|
9
27
|
- npm 9+
|
|
10
|
-
- macOS/Linux/WSL
|
|
11
|
-
- Provider credentials (see
|
|
28
|
+
- macOS / Linux / WSL
|
|
29
|
+
- Provider credentials (see [Provider Credentials](#provider-credentials))
|
|
12
30
|
|
|
13
31
|
## Installation
|
|
14
32
|
|
|
15
|
-
### Quick start — no clone required
|
|
33
|
+
### Quick start — npx (no clone required)
|
|
16
34
|
|
|
17
|
-
The easiest way to use Mr. Magic in an MCP client is via `npx`. No clone or
|
|
18
|
-
|
|
19
|
-
locally:
|
|
35
|
+
The easiest way to use Mr. Magic in an MCP client is via `npx`. No clone or local
|
|
36
|
+
install needed — the package is fetched from npm on first run and cached locally:
|
|
20
37
|
|
|
21
38
|
```bash
|
|
22
39
|
npx -y mr-magic-mcp-server
|
|
@@ -31,10 +48,10 @@ npm install -g mr-magic-mcp-server
|
|
|
31
48
|
When installed globally, start any server directly:
|
|
32
49
|
|
|
33
50
|
```bash
|
|
34
|
-
mcp-server
|
|
35
|
-
mcp-http-server
|
|
36
|
-
http-server
|
|
37
|
-
mrmagic-cli --help
|
|
51
|
+
mcp-server # MCP stdio server (recommended for local MCP clients)
|
|
52
|
+
mcp-http-server # Streamable HTTP MCP server
|
|
53
|
+
http-server # JSON HTTP automation server
|
|
54
|
+
mrmagic-cli --help # CLI
|
|
38
55
|
```
|
|
39
56
|
|
|
40
57
|
### Local repo (development / contribution)
|
|
@@ -46,376 +63,290 @@ mrmagic-cli --help # CLI
|
|
|
46
63
|
cd mr-magic-mcp-server
|
|
47
64
|
```
|
|
48
65
|
|
|
49
|
-
> Alternatively, download a ZIP from GitHub, extract it, and `cd` into the
|
|
50
|
-
> extracted directory.
|
|
51
|
-
|
|
52
66
|
2. Install dependencies:
|
|
53
67
|
|
|
54
68
|
```bash
|
|
55
69
|
npm install
|
|
56
70
|
```
|
|
57
71
|
|
|
58
|
-
> `npm install`
|
|
59
|
-
> `
|
|
60
|
-
>
|
|
61
|
-
> For local repo usage, run the CLI via `npm run cli -- ...` (or
|
|
62
|
-
> `node src/bin/cli.js ...`). If you want direct commands like
|
|
63
|
-
> `mrmagic-cli --help`, run `npm link` (dev symlink) or install globally.
|
|
72
|
+
> `npm install` does **not** add `mrmagic-cli` to your shell `PATH`.
|
|
73
|
+
> For local repo usage, run the CLI via `npm run cli -- ...` or `node src/bin/cli.js ...`.
|
|
74
|
+
> Run `npm link` (dev symlink) or install globally to get `mrmagic-cli` on `PATH`.
|
|
64
75
|
|
|
65
|
-
3. Configure `.env` (see Environment variables
|
|
66
|
-
your shell before running any commands.
|
|
76
|
+
3. Configure `.env` (see [Environment Variables](#environment-variables)) or export
|
|
77
|
+
env vars in your shell before running any commands.
|
|
67
78
|
|
|
68
79
|
4. Run the desired entrypoint:
|
|
69
|
-
|
|
80
|
+
|
|
81
|
+
- MCP stdio server: `npm run server:mcp`
|
|
70
82
|
- MCP Streamable HTTP server: `npm run server:mcp:http`
|
|
71
|
-
-
|
|
83
|
+
- JSON HTTP automation server: `npm run server:http`
|
|
72
84
|
- CLI: `npm run cli -- --help`
|
|
73
85
|
|
|
74
|
-
## Environment
|
|
75
|
-
|
|
76
|
-
Copy `.env.example` to `.env` (or export values in your shell) and fill in the
|
|
77
|
-
credentials plus any storage configuration:
|
|
78
|
-
|
|
79
|
-
```env
|
|
80
|
-
PORT= # Override all server ports, or leave blank to default to 3444 for MCP, 3333 the JSON HTTP automation server.
|
|
81
|
-
LOG_LEVEL= # Optional. error|warn|info|debug. Defaults to info.
|
|
82
|
-
MR_MAGIC_ROOT= # Optional. Force the project root used for resolving .env/.cache paths.
|
|
83
|
-
MR_MAGIC_ENV_PATH= # Optional. Custom path to an env file when the default isn't desired.
|
|
84
|
-
GENIUS_CLIENT_ID= # Get from https://genius.com/api-clients, required for Genius client-credentials auth.
|
|
85
|
-
GENIUS_CLIENT_SECRET= # Get from https://genius.com/api-clients, required for Genius client-credentials auth.
|
|
86
|
-
GENIUS_ACCESS_TOKEN= # Get from https://genius.com/api-clients, required for Genius lyrics support when client credentials are not supplied.
|
|
87
|
-
MUSIXMATCH_FALLBACK_TOKEN= # Fallback token (1st priority). Set as env var for production/ephemeral hosts where the filesystem is not persistent.
|
|
88
|
-
MUSIXMATCH_ALT_FALLBACK_TOKEN= # Fallback token (2nd priority). Alternative env var; same token value, second-choice source.
|
|
89
|
-
MUSIXMATCH_AUTO_FETCH=0 # Optional. When 1, provider will attempt to re-run the fetch script automatically (headless) if no token is available.
|
|
90
|
-
MUSIXMATCH_TOKEN_CACHE=.cache/musixmatch-token.json
|
|
91
|
-
MELON_COOKIE= # Optional. Pin a session cookie for consistent Melon results; anonymous access generally works without it.
|
|
92
|
-
MR_MAGIC_EXPORT_BACKEND= # local|inline|redis
|
|
93
|
-
MR_MAGIC_EXPORT_DIR=/absolute/path/to/exports # Required if MR_MAGIC_EXPORT_BACKEND=local
|
|
94
|
-
MR_MAGIC_EXPORT_TTL_SECONDS=3600 # Optional, default 3600 (1 hour). Only applies to local and redis backends, ignored for inline.
|
|
95
|
-
MR_MAGIC_DOWNLOAD_BASE_URL=https://yourserver.com|http://localhost:GIVEN_PORT # Used for generating download links for exported files. See README for details.
|
|
96
|
-
MR_MAGIC_INLINE_PAYLOAD_MAX_CHARS=1500 # Optional, default 1500. build_catalog_payload auto-promotes payload transport to reference when omitInlineLyrics is true and lyrics exceed this threshold.
|
|
97
|
-
MR_MAGIC_QUIET_STDIO=0 # Optional, default 0. If set to 1, suppresses all non-error logs to stdout. Recommended when running under MCP clients that read stdio (forces LOG_LEVEL=error).
|
|
98
|
-
MR_MAGIC_HTTP_TIMEOUT_MS=10000 # Optional, default 10000. Global outbound HTTP timeout (ms) to prevent hanging provider/storage requests.
|
|
99
|
-
MR_MAGIC_LOG_TOOL_ARGS_CHUNKS=0 # Optional, default 0. Set to 1/true to emit chunk-by-chunk MCP tool argument previews for truncation debugging.
|
|
100
|
-
MR_MAGIC_TOOL_ARG_CHUNK_SIZE=400 # Optional, default 400. Chunk size used when MR_MAGIC_LOG_TOOL_ARGS_CHUNKS is enabled.
|
|
101
|
-
MR_MAGIC_MCP_HTTP_DIAGNOSTICS=0 # Optional, default 0. Set to 1 to log enriched Streamable HTTP MCP request diagnostics at transport ingress.
|
|
102
|
-
MR_MAGIC_ALLOWED_HOSTS= # Optional. Comma-separated extra hostnames for DNS rebinding protection when binding to 0.0.0.0 (e.g. custom domains). Render hostname is auto-included via RENDER_EXTERNAL_HOSTNAME.
|
|
103
|
-
MR_MAGIC_SDK_REPRO_HTTP_DEBUG=0 # Optional, default 0. Set to 1 for verbose HTTP request/response previews in the SDK repro harness script.
|
|
104
|
-
UPSTASH_REDIS_REST_URL= # Get from https://console.upstash.com/redis/rest, required if MR_MAGIC_EXPORT_BACKEND=redis
|
|
105
|
-
UPSTASH_REDIS_REST_TOKEN= # Get from https://console.upstash.com/redis/rest, required if MR_MAGIC_EXPORT_BACKEND=redis
|
|
106
|
-
AIRTABLE_PERSONAL_ACCESS_TOKEN= # Required for push_catalog_to_airtable tool. Get from https://airtable.com/create/tokens
|
|
107
|
-
```
|
|
86
|
+
## Environment Variables
|
|
108
87
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
- **Auto-refresh** (`GENIUS_CLIENT_ID` + `GENIUS_CLIENT_SECRET`) — the server calls the
|
|
112
|
-
Genius OAuth `client_credentials` endpoint at runtime and keeps the token refreshed
|
|
113
|
-
in memory automatically. **Recommended for all deployments**, including Render/ephemeral
|
|
114
|
-
hosts. No disk, no scripts, no manual token copying.
|
|
115
|
-
- **Fallback token** (`GENIUS_ACCESS_TOKEN` env var) — a static bearer token. Works
|
|
116
|
-
everywhere but does not auto-refresh. Use only when client_credentials are unavailable;
|
|
117
|
-
redeploy with a new token when it expires.
|
|
118
|
-
- **Cache token** (on-disk `.cache/genius-token.json`) — written by
|
|
119
|
-
`npm run fetch:genius-token`. Loaded on startup when a persistent writable filesystem
|
|
120
|
-
is available. Not suitable for ephemeral hosts.
|
|
121
|
-
- **Musixmatch token sources** — the server resolves the Musixmatch token using
|
|
122
|
-
two named sources, tried in order:
|
|
123
|
-
- **Fallback token** (`MUSIXMATCH_FALLBACK_TOKEN`, then `MUSIXMATCH_ALT_FALLBACK_TOKEN`) — the
|
|
124
|
-
token value is set directly as an environment variable. This is the
|
|
125
|
-
recommended approach for production and ephemeral hosts (e.g. Render free
|
|
126
|
-
tier, containers) where the filesystem cannot be relied upon between
|
|
127
|
-
restarts. Set `MUSIXMATCH_FALLBACK_TOKEN` first; `MUSIXMATCH_ALT_FALLBACK_TOKEN` is the
|
|
128
|
-
legacy/alternative env var for the same value.
|
|
129
|
-
- **Cache token** (on-disk `.cache/musixmatch-token.json`) — written by the
|
|
130
|
-
`fetch:musixmatch-token` script after a browser sign-in. Used for local
|
|
131
|
-
development when a persistent writable filesystem is available. Not suitable
|
|
132
|
-
for ephemeral hosts.
|
|
133
|
-
- **MUSIXMATCH_TOKEN_CACHE** controls where the on-disk cache token file is
|
|
134
|
-
read/written (default `<project root>/.cache/musixmatch-token.json`).
|
|
135
|
-
- **MELON_COOKIE** is optional—anonymous access generally works, but pinning a
|
|
136
|
-
cookie can improve consistency.
|
|
137
|
-
- **MR_MAGIC_EXPORT_BACKEND** controls where formatted lyrics land:
|
|
138
|
-
- `local` (default) writes to `MR_MAGIC_EXPORT_DIR` (or `exports/` when
|
|
139
|
-
omitted).
|
|
140
|
-
- `inline` skips disk writes and returns the formatted strings directly in
|
|
141
|
-
the response body.
|
|
142
|
-
- `redis` stores each export in Upstash; you must also set the `UPSTASH_*`
|
|
143
|
-
vars plus `MR_MAGIC_DOWNLOAD_BASE_URL` so clients know which HTTP server
|
|
144
|
-
serves `/downloads/:id/:ext`.
|
|
145
|
-
- **MR_MAGIC_EXPORT_DIR** can be any absolute path (e.g., `/tmp/mr-magic`).
|
|
146
|
-
Quote it only when the path contains spaces or special characters
|
|
147
|
-
(`MR_MAGIC_EXPORT_DIR="/Users/you/My Exports"`).
|
|
148
|
-
- **PORT** overrides both HTTP entrypoints when your platform injects one
|
|
149
|
-
(Render, Fly, etc.). If unset, the MCP HTTP transport binds to `3444` and
|
|
150
|
-
the JSON HTTP automation server binds to `3333`. CLI flags such as
|
|
151
|
-
`mrmagic-cli server --port 4000` always take precedence. On Render,
|
|
152
|
-
`PORT` is set automatically by the platform (default `10000`) — no manual
|
|
153
|
-
override is needed.
|
|
154
|
-
- **MR_MAGIC_DOWNLOAD_BASE_URL** should match the public URL that exposes the
|
|
155
|
-
`/downloads` routes. Include `:port` only when the HTTP server isn’t using
|
|
156
|
-
the default for its protocol.
|
|
157
|
-
- **LOG_LEVEL** (error|warn|info|debug, default `info`) controls global logging verbosity.
|
|
158
|
-
Set `LOG_LEVEL=debug` when you need verbose diagnostics.
|
|
159
|
-
- **MR_MAGIC_QUIET_STDIO** set to `1` silences stdio transports (helpful when a
|
|
160
|
-
host MCP client expects clean JSON over stdout). When enabled, it forces
|
|
161
|
-
`LOG_LEVEL=error` so stdout stays quiet.
|
|
162
|
-
- **MR_MAGIC_HTTP_TIMEOUT_MS** (default `10000`) applies a global timeout to
|
|
163
|
-
outbound provider/export-storage network calls so slow upstream endpoints
|
|
164
|
-
fail fast instead of hanging MCP tool calls.
|
|
165
|
-
- **MR_MAGIC_LOG_TOOL_ARGS_CHUNKS** (default `0`) enables diagnostic chunk
|
|
166
|
-
logging for incoming MCP `arguments` payloads. Set to `1`/`true` when
|
|
167
|
-
debugging malformed/truncated tool calls from external clients.
|
|
168
|
-
- **MR_MAGIC_TOOL_ARG_CHUNK_SIZE** (default `400`) controls the size of each
|
|
169
|
-
chunk preview emitted when chunk logging is enabled.
|
|
170
|
-
- **MR_MAGIC_INLINE_PAYLOAD_MAX_CHARS** (default `1500`) controls when
|
|
171
|
-
`build_catalog_payload` auto-promotes payload transport to `reference`
|
|
172
|
-
in compact Airtable-safe flows to reduce large inline lyric blobs.
|
|
173
|
-
- **MR_MAGIC_MCP_HTTP_DIAGNOSTICS** (default `0`) enables detailed request
|
|
174
|
-
metadata logging at the Streamable HTTP transport boundary (method, content
|
|
175
|
-
type, body shape/length, session header, and safe body preview).
|
|
176
|
-
- **MR_MAGIC_ALLOWED_HOSTS** — comma-separated list of extra hostnames to allow
|
|
177
|
-
when the MCP HTTP server binds to `0.0.0.0` (required by the MCP SDK for DNS
|
|
178
|
-
rebinding protection). On Render, `RENDER_EXTERNAL_HOSTNAME` is automatically
|
|
179
|
-
included; set `MR_MAGIC_ALLOWED_HOSTS` only when you have a custom domain or
|
|
180
|
-
need to add additional hosts beyond `localhost`/`127.0.0.1`.
|
|
181
|
-
- **MR_MAGIC_SDK_REPRO_HTTP_DEBUG** (default `0`) enables HTTP-level debugging
|
|
182
|
-
output in `scripts/mcp-arg-boundary-sdk-repro.mjs` when validating argument
|
|
183
|
-
boundary behavior from the SDK client path.
|
|
184
|
-
- **MR_MAGIC_ROOT** overrides the project root used for loading `.env` and `.cache`.
|
|
185
|
-
Useful when an MCP host launches the server from another directory.
|
|
186
|
-
- **MR_MAGIC_ENV_PATH** lets you point to a specific `.env` file instead of the
|
|
187
|
-
default `<project root>/.env`.
|
|
188
|
-
- **AIRTABLE_PERSONAL_ACCESS_TOKEN** is required only when using the
|
|
189
|
-
`push_catalog_to_airtable` tool. Generate a personal access token at
|
|
190
|
-
https://airtable.com/create/tokens and grant it the `data.records:write` scope
|
|
191
|
-
for the bases you want to write to.
|
|
192
|
-
- For hosted deployments, inject the variables via your platform dashboard so
|
|
193
|
-
no `.env` file is required at runtime.
|
|
194
|
-
|
|
195
|
-
### Getting the Musixmatch token
|
|
196
|
-
|
|
197
|
-
All Musixmatch support in this project uses a captured browser session token.
|
|
198
|
-
There is no OAuth callback — the fetch script captures and persists the token
|
|
199
|
-
in one of two ways depending on your deployment:
|
|
200
|
-
|
|
201
|
-
- **Cache token** (local dev): the fetch script writes the token to
|
|
202
|
-
`.cache/musixmatch-token.json`. The server loads it on startup whenever a
|
|
203
|
-
persistent, writable filesystem is available.
|
|
204
|
-
- **Fallback token** (production/ephemeral): copy the captured token value and
|
|
205
|
-
set it as `MUSIXMATCH_FALLBACK_TOKEN` (recommended) or `MUSIXMATCH_ALT_FALLBACK_TOKEN` in your
|
|
206
|
-
platform's environment. This is the only reliable option on ephemeral hosts
|
|
207
|
-
(Render free tier, containers without a mounted volume) where the filesystem
|
|
208
|
-
is wiped between restarts.
|
|
209
|
-
|
|
210
|
-
#### Workflow
|
|
211
|
-
|
|
212
|
-
1. From any machine that can open a browser, run:
|
|
88
|
+
Copy `.env.example` to `.env` (or inject via your platform dashboard). Variables are
|
|
89
|
+
grouped below by purpose.
|
|
213
90
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
91
|
+
### Server and runtime
|
|
92
|
+
|
|
93
|
+
| Variable | Default | Description |
|
|
94
|
+
|---|---|---|
|
|
95
|
+
| `PORT` | `3444` / `3333` | Override server port. On Render this is set automatically (default `10000`). |
|
|
96
|
+
| `LOG_LEVEL` | `info` | Verbosity: `error` \| `warn` \| `info` \| `debug`. |
|
|
97
|
+
| `MR_MAGIC_QUIET_STDIO` | `0` | Set to `1` to suppress non-error stdout logs (forces `LOG_LEVEL=error`). Recommended under stdio MCP clients. |
|
|
98
|
+
| `MR_MAGIC_HTTP_TIMEOUT_MS` | `10000` | Global outbound HTTP timeout in milliseconds. |
|
|
99
|
+
| `MR_MAGIC_ROOT` | _(project root)_ | Override the project root used for `.env` and `.cache` path resolution. |
|
|
100
|
+
| `MR_MAGIC_ENV_PATH` | _(auto)_ | Point to a specific `.env` file instead of `<project root>/.env`. |
|
|
101
|
+
| `MR_MAGIC_ALLOWED_HOSTS` | _(empty)_ | Comma-separated extra hostnames allowed for DNS rebinding protection when binding to `0.0.0.0`. `RENDER_EXTERNAL_HOSTNAME` is included automatically on Render. Only needed for custom domains. |
|
|
102
|
+
|
|
103
|
+
### Genius credentials
|
|
104
|
+
|
|
105
|
+
| Variable | Description |
|
|
106
|
+
|---|---|
|
|
107
|
+
| `GENIUS_CLIENT_ID` | OAuth client ID for auto-refresh (recommended). Get from [genius.com/api-clients](https://genius.com/api-clients). |
|
|
108
|
+
| `GENIUS_CLIENT_SECRET` | OAuth client secret for auto-refresh (recommended). |
|
|
109
|
+
| `GENIUS_ACCESS_TOKEN` | Static fallback bearer token. Used when client credentials are unavailable. |
|
|
110
|
+
|
|
111
|
+
Token resolution order (first match wins):
|
|
112
|
+
|
|
113
|
+
1. In-memory runtime cache
|
|
114
|
+
2. Auto-refresh via `GENIUS_CLIENT_ID` + `GENIUS_CLIENT_SECRET` ← **recommended**
|
|
115
|
+
3. `GENIUS_ACCESS_TOKEN` env var (static, no auto-refresh)
|
|
116
|
+
4. On-disk `.cache/genius-token.json` (local dev only)
|
|
117
|
+
|
|
118
|
+
### Musixmatch credentials
|
|
119
|
+
|
|
120
|
+
| Variable | Description |
|
|
121
|
+
|---|---|
|
|
122
|
+
| `MUSIXMATCH_FALLBACK_TOKEN` | Token env var (1st priority). Use for production / ephemeral hosts. |
|
|
123
|
+
| `MUSIXMATCH_ALT_FALLBACK_TOKEN` | Token env var (2nd priority). Alternative name for the same token. |
|
|
124
|
+
| `MUSIXMATCH_TOKEN_CACHE` | Path to the on-disk cache file. Default: `.cache/musixmatch-token.json`. |
|
|
125
|
+
| `MUSIXMATCH_AUTO_FETCH` | Set to `1` to attempt headless token re-fetch when no token is found. |
|
|
126
|
+
|
|
127
|
+
### Export and storage
|
|
128
|
+
|
|
129
|
+
| Variable | Default | Description |
|
|
130
|
+
|---|---|---|
|
|
131
|
+
| `MR_MAGIC_EXPORT_BACKEND` | `local` | Storage backend: `local` \| `inline` \| `redis`. |
|
|
132
|
+
| `MR_MAGIC_EXPORT_DIR` | `exports/` | Absolute path for local exports. Required when backend is `local`. |
|
|
133
|
+
| `MR_MAGIC_EXPORT_TTL_SECONDS` | `3600` | TTL for `local` and `redis` backends (ignored for `inline`). |
|
|
134
|
+
| `MR_MAGIC_DOWNLOAD_BASE_URL` | _(none)_ | Public base URL for download links, e.g. `https://lyrics.example.com`. |
|
|
135
|
+
| `MR_MAGIC_INLINE_PAYLOAD_MAX_CHARS` | `1500` | Character threshold at which `build_catalog_payload` auto-promotes to `reference` transport when `omitInlineLyrics` is `true`. |
|
|
136
|
+
| `UPSTASH_REDIS_REST_URL` | _(none)_ | Required when `MR_MAGIC_EXPORT_BACKEND=redis`. |
|
|
137
|
+
| `UPSTASH_REDIS_REST_TOKEN` | _(none)_ | Required when `MR_MAGIC_EXPORT_BACKEND=redis`. |
|
|
138
|
+
|
|
139
|
+
### Airtable
|
|
140
|
+
|
|
141
|
+
| Variable | Description |
|
|
142
|
+
|---|---|
|
|
143
|
+
| `AIRTABLE_PERSONAL_ACCESS_TOKEN` | Required for `push_catalog_to_airtable`. Generate at [airtable.com/create/tokens](https://airtable.com/create/tokens) with `data.records:write` scope. |
|
|
144
|
+
|
|
145
|
+
### Melon
|
|
146
|
+
|
|
147
|
+
| Variable | Description |
|
|
148
|
+
|---|---|
|
|
149
|
+
| `MELON_COOKIE` | Optional. Pin a session cookie for consistent results. Anonymous access generally works without it. |
|
|
150
|
+
|
|
151
|
+
### Diagnostics and debugging
|
|
152
|
+
|
|
153
|
+
| Variable | Default | Description |
|
|
154
|
+
|---|---|---|
|
|
155
|
+
| `MR_MAGIC_MCP_HTTP_DIAGNOSTICS` | `0` | Set to `1` to log enriched request metadata at the Streamable HTTP transport boundary. |
|
|
156
|
+
| `MR_MAGIC_LOG_TOOL_ARGS_CHUNKS` | `0` | Set to `1` to emit chunk-by-chunk MCP tool argument previews for truncation debugging. |
|
|
157
|
+
| `MR_MAGIC_TOOL_ARG_CHUNK_SIZE` | `400` | Chunk size (chars) used when chunk logging is enabled. |
|
|
158
|
+
| `MR_MAGIC_SDK_REPRO_HTTP_DEBUG` | `0` | Set to `1` for verbose HTTP traces in the SDK repro harness script. |
|
|
159
|
+
|
|
160
|
+
## Provider Credentials
|
|
161
|
+
|
|
162
|
+
### Genius
|
|
163
|
+
|
|
164
|
+
Genius credentials are resolved in this order — the first available source wins:
|
|
165
|
+
|
|
166
|
+
1. **Auto-refresh** (`GENIUS_CLIENT_ID` + `GENIUS_CLIENT_SECRET`) — the server calls
|
|
167
|
+
the Genius OAuth `client_credentials` endpoint at runtime and keeps the token
|
|
168
|
+
refreshed in memory. **Recommended for all deployments**, including Render and
|
|
169
|
+
ephemeral hosts. No disk, no scripts, no manual token copying.
|
|
217
170
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
VNC, RDP, etc.).
|
|
171
|
+
2. **Fallback token** (`GENIUS_ACCESS_TOKEN`) — a static bearer token. Works
|
|
172
|
+
everywhere but does not auto-refresh. Update by redeploying with a new value.
|
|
221
173
|
|
|
174
|
+
3. **Cache token** (`.cache/genius-token.json`) — written by `npm run fetch:genius-token`.
|
|
175
|
+
Only suitable for local dev with a persistent filesystem.
|
|
176
|
+
|
|
177
|
+
### Musixmatch
|
|
178
|
+
|
|
179
|
+
Musixmatch uses a captured browser session token. There is no OAuth callback.
|
|
180
|
+
|
|
181
|
+
**For production / ephemeral hosts (Render, containers):**
|
|
182
|
+
|
|
183
|
+
Set `MUSIXMATCH_FALLBACK_TOKEN` (first priority) or `MUSIXMATCH_ALT_FALLBACK_TOKEN`
|
|
184
|
+
(second priority) directly in your environment. These are the only reliable options
|
|
185
|
+
when the filesystem may be wiped between restarts.
|
|
186
|
+
|
|
187
|
+
**For local development:**
|
|
188
|
+
|
|
189
|
+
Run the fetch script once — it opens a Playwright-controlled Chromium window, signs
|
|
190
|
+
you in, and writes the token to `.cache/musixmatch-token.json`:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
npm run fetch:musixmatch-token
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
The workflow:
|
|
197
|
+
|
|
198
|
+
1. Run the script on any machine that can open a browser.
|
|
222
199
|
2. Sign in with Musixmatch (Google sign-in works) when prompted.
|
|
200
|
+
3. After the redirect to `https://www.musixmatch.com/discover`, the script prints
|
|
201
|
+
the captured token and writes the cache file.
|
|
202
|
+
4. **For remote deployments:** copy the `token` value from the printed JSON and set
|
|
203
|
+
it as `MUSIXMATCH_FALLBACK_TOKEN` in your platform environment. Do **not** rely on
|
|
204
|
+
the cache file surviving restarts on ephemeral hosts.
|
|
205
|
+
|
|
206
|
+
> **Developer accounts:** Get API access from [developer.musixmatch.com](https://developer.musixmatch.com)
|
|
207
|
+
> and set the resulting token as `MUSIXMATCH_FALLBACK_TOKEN`.
|
|
208
|
+
>
|
|
209
|
+
> **Public accounts:** Visit [auth.musixmatch.com](https://auth.musixmatch.com), sign in,
|
|
210
|
+
> and capture the token using the script above.
|
|
211
|
+
>
|
|
212
|
+
> ⚠️ **WARNING:** Calling the API from an unauthorized account may result in a ban.
|
|
213
|
+
|
|
214
|
+
### Melon
|
|
215
|
+
|
|
216
|
+
Fetching Melon endpoints works anonymously. If `MELON_COOKIE` is blank, the server
|
|
217
|
+
requests session cookies automatically. Set `MELON_COOKIE` to a complete cookie header
|
|
218
|
+
string only when you need pinned, reproducible sessions.
|
|
219
|
+
|
|
220
|
+
## Export and Download Configuration
|
|
221
|
+
|
|
222
|
+
The `MR_MAGIC_EXPORT_BACKEND` variable controls where formatted lyrics are stored:
|
|
223
|
+
|
|
224
|
+
- **`local`** (default) — writes files to `MR_MAGIC_EXPORT_DIR` (or `exports/` when
|
|
225
|
+
unset). Make sure the target directory is writable. The `export_lyrics` tool also
|
|
226
|
+
returns the raw `content` field so clients can inline results when file writes fail.
|
|
227
|
+
|
|
228
|
+
- **`inline`** — skips disk writes entirely. Each export is returned in the tool
|
|
229
|
+
response with `content` populated and `skipped: true` to signal that persistence
|
|
230
|
+
was intentionally bypassed.
|
|
231
|
+
|
|
232
|
+
- **`redis`** — stores exports in Upstash. Requires `UPSTASH_REDIS_REST_URL`,
|
|
233
|
+
`UPSTASH_REDIS_REST_TOKEN`, and `MR_MAGIC_DOWNLOAD_BASE_URL`.
|
|
234
|
+
|
|
235
|
+
For Redis exports, `MR_MAGIC_DOWNLOAD_BASE_URL` must be the publicly reachable base URL
|
|
236
|
+
of your HTTP automation server (not the Upstash URL), e.g. `https://lyrics.example.com`.
|
|
237
|
+
Download links are built as `{base_url}/downloads/{id}/{ext}`.
|
|
238
|
+
|
|
239
|
+
For local testing:
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
MR_MAGIC_DOWNLOAD_BASE_URL=http://127.0.0.1:3333
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
> Even when using only the stdio MCP server, you still need the HTTP automation
|
|
246
|
+
> server running to serve `/downloads/:id/:ext` routes when the `redis` backend
|
|
247
|
+
> is enabled.
|
|
248
|
+
|
|
249
|
+
## Local Deployment
|
|
250
|
+
|
|
251
|
+
Run whichever entrypoint you need via npm scripts:
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
npm run server:http # JSON HTTP automation — 127.0.0.1:3333 by default
|
|
255
|
+
npm run server:mcp # MCP stdio transport — ideal for local MCP clients
|
|
256
|
+
npm run server:mcp:http # Streamable HTTP MCP — 127.0.0.1:3444 by default
|
|
257
|
+
npm run cli -- --help # CLI entrypoint
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Set provider tokens via `.env` before running. `dotenv` is for local convenience only —
|
|
261
|
+
production environments should inject vars directly.
|
|
262
|
+
|
|
263
|
+
## Remote Deployment
|
|
223
264
|
|
|
224
|
-
|
|
225
|
-
the captured token and writes the cache token file (default
|
|
226
|
-
`<project>/.cache/musixmatch-token.json`). The file contains both the token
|
|
227
|
-
value and the `web-desktop-app-v1.0` desktop cookie so the server can replay
|
|
228
|
-
the session.
|
|
229
|
-
|
|
230
|
-
4. **For remote/ephemeral deployments:** copy the `token` value from the
|
|
231
|
-
printed JSON and set it as `MUSIXMATCH_FALLBACK_TOKEN` in your platform
|
|
232
|
-
environment (the fallback token). Do **not** rely on the cache file
|
|
233
|
-
surviving a restart on ephemeral hosts.
|
|
234
|
-
If `MUSIXMATCH_AUTO_FETCH=1`, the provider can attempt to re-run the fetch
|
|
235
|
-
script headlessly when no token is found, but the initial sign-in still
|
|
236
|
-
requires a browser.
|
|
237
|
-
|
|
238
|
-
#### Developer Accounts
|
|
239
|
-
|
|
240
|
-
1. Get API access from `https://developer.musixmatch.com`
|
|
241
|
-
2. Run the script above and set the resulting token as `MUSIXMATCH_FALLBACK_TOKEN`
|
|
242
|
-
(fallback token) in your environment, or keep the on-disk cache token in sync
|
|
243
|
-
for local development.
|
|
244
|
-
|
|
245
|
-
#### Public Account (WARNING: MAY RESULT IN BAN)
|
|
246
|
-
|
|
247
|
-
1. Visit `https://auth.musixmatch.com/`
|
|
248
|
-
2. Sign in with a Musixmatch account and allow the app. When redirected, the
|
|
249
|
-
helper script above will capture the session and write the cache token.
|
|
250
|
-
3. Copy the `token` value and set it as `MUSIXMATCH_FALLBACK_TOKEN` for any remote
|
|
251
|
-
environment that needs it.
|
|
252
|
-
|
|
253
|
-
**WARNING: CALLING THE API FROM AN UNAUTHORIZED ACCOUNT MAY RESULT IN A BAN.**
|
|
254
|
-
|
|
255
|
-
### Optional Melon cookie
|
|
256
|
-
|
|
257
|
-
Fetching Melon search/lyric endpoints still works with the MCP’s built-in cookie
|
|
258
|
-
collection. If `MELON_COOKIE` is blank, the app will quietly request whatever
|
|
259
|
-
session cookies the site provides, so you rarely need to copy a manual string.
|
|
260
|
-
If you prefer to pin a cookie for repeatable results, set `MELON_COOKIE` to the
|
|
261
|
-
complete cookie header you already trust.
|
|
262
|
-
|
|
263
|
-
### Export + download configuration
|
|
264
|
-
|
|
265
|
-
- **Local files:** The default `local` backend writes into `exports/` (repo
|
|
266
|
-
root). Override with `MR_MAGIC_EXPORT_DIR=/absolute/path` when the working
|
|
267
|
-
directory isn’t writable. The `export_lyrics` tool also includes the raw
|
|
268
|
-
`content` field so clients can still inline results if file writes fail.
|
|
269
|
-
- **Redis downloads:** Set `MR_MAGIC_EXPORT_BACKEND=redis` plus
|
|
270
|
-
`UPSTASH_REDIS_REST_URL`, `UPSTASH_REDIS_REST_TOKEN`, and
|
|
271
|
-
`MR_MAGIC_DOWNLOAD_BASE_URL`. Each export is cached in Upstash for
|
|
272
|
-
`MR_MAGIC_EXPORT_TTL_SECONDS` seconds, but the download link should point at
|
|
273
|
-
_your own_ HTTP server’s `/downloads/:id/:ext` route (not the Upstash REST
|
|
274
|
-
URL). In other words, `MR_MAGIC_DOWNLOAD_BASE_URL` must be the publicly
|
|
275
|
-
reachable base URL for the HTTP automation server (e.g.,
|
|
276
|
-
`https://lyrics.example.com`), and request paths are appended to it
|
|
277
|
-
(`https://lyrics.example.com/downloads/...`). MCP clients can take the
|
|
278
|
-
returned URLs and download the files from the same HTTP server or proxy where
|
|
279
|
-
`/downloads` is routed.
|
|
280
|
-
- Even if you’re only using the stdio MCP server locally, you still need the
|
|
281
|
-
HTTP automation server running to serve those `/downloads/:id/:ext` routes
|
|
282
|
-
whenever `redis` storage is enabled.
|
|
283
|
-
- For local testing, set `MR_MAGIC_DOWNLOAD_BASE_URL=http://127.0.0.1:3333` (or
|
|
284
|
-
`http://localhost:3333`) so the generated links look like
|
|
285
|
-
`http://127.0.0.1:3333/downloads/<id>/<ext>`. In remote deployments, point it
|
|
286
|
-
at your public host (e.g., `https://lyrics.example.com`). Only include a
|
|
287
|
-
`:port` suffix when the HTTP server listens on a nonstandard port (e.g.,
|
|
288
|
-
`https://lyrics.example.com:8443`). If you override the local port via `PORT`
|
|
289
|
-
or CLI flags, update the base URL accordingly.
|
|
290
|
-
- **Inline:** `MR_MAGIC_EXPORT_BACKEND=inline` is handy for sandboxes that
|
|
291
|
-
prohibit writes. Instead of touching the file system or Redis, each export is
|
|
292
|
-
returned inline in the tool/server response with `content` populated and
|
|
293
|
-
`skipped: true` to signal that persistence was intentionally bypassed (not
|
|
294
|
-
that the export failed).
|
|
295
|
-
|
|
296
|
-
## Local deployment
|
|
297
|
-
|
|
298
|
-
Run whichever entrypoint you need via npm scripts so the repo’s `NODE_PATH`
|
|
299
|
-
settings and dotenv loading are consistent:
|
|
300
|
-
|
|
301
|
-
- `npm run server:http` — JSON HTTP automation endpoint (`127.0.0.1:3333` by
|
|
302
|
-
default; honors `PORT`/CLI overrides).
|
|
303
|
-
- `npm run server:mcp` — MCP stdio transport (ideal for local MCP clients that
|
|
304
|
-
speak stdio).
|
|
305
|
-
- `npm run server:mcp:http` — Streamable HTTP MCP transport
|
|
306
|
-
(`127.0.0.1:3444` unless overridden).
|
|
307
|
-
- `npm run cli` — interactive CLI entrypoint (`src/tools/cli.js`); combine with
|
|
308
|
-
`server`, `search`, `find`, or `select` subcommands.
|
|
309
|
-
|
|
310
|
-
Set provider tokens/env vars via `.env` before running any command.
|
|
311
|
-
`dotenv` is only for local convenience—production runners should inject env vars
|
|
312
|
-
directly.
|
|
313
|
-
|
|
314
|
-
## Remote deployment
|
|
315
|
-
|
|
316
|
-
Ensure the deployment environment injects the same environment variables, then
|
|
317
|
-
choose the transport you need. Typical remote workflows look like:
|
|
265
|
+
Install dependencies and start the desired transport:
|
|
318
266
|
|
|
319
267
|
```bash
|
|
320
268
|
npm ci
|
|
321
|
-
npm run server:http
|
|
269
|
+
npm run server:mcp:http # or server:http / server:mcp
|
|
322
270
|
```
|
|
323
271
|
|
|
324
|
-
Use a process manager (systemd, PM2, Docker CMD
|
|
325
|
-
servers running.
|
|
272
|
+
Use a process manager (systemd, PM2, Docker `CMD`, etc.) to keep servers running.
|
|
326
273
|
|
|
327
274
|
### Deploying on Render
|
|
328
275
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
variables that the server reads at startup:
|
|
276
|
+
Both HTTP servers (`server:mcp:http` and `server:http`) are ready for Render with
|
|
277
|
+
no extra network configuration. Render automatically sets:
|
|
332
278
|
|
|
333
|
-
| Variable |
|
|
334
|
-
|
|
335
|
-
| `RENDER` |
|
|
336
|
-
| `PORT` |
|
|
279
|
+
| Variable | Value |
|
|
280
|
+
|---|---|
|
|
281
|
+
| `RENDER` | `"true"` |
|
|
282
|
+
| `PORT` | `10000` (default; overridable in the Render Dashboard) |
|
|
283
|
+
| `RENDER_EXTERNAL_HOSTNAME` | Your service hostname, e.g. `myapp.onrender.com` |
|
|
337
284
|
|
|
338
|
-
When `RENDER=true` is detected, the server
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
285
|
+
When `RENDER=true` is detected, the server binds to `0.0.0.0` automatically and reads
|
|
286
|
+
the platform-assigned `PORT`. No manual `HOST` or `PORT` configuration is needed.
|
|
287
|
+
|
|
288
|
+
The `RENDER_EXTERNAL_HOSTNAME` is automatically added to the DNS rebinding `allowedHosts`
|
|
289
|
+
list so the MCP SDK does not emit host-validation warnings.
|
|
342
290
|
|
|
343
291
|
Recommended Render service settings:
|
|
344
292
|
|
|
345
293
|
- **Start Command:** `npm run server:mcp:http`
|
|
346
|
-
- **Environment:**
|
|
347
|
-
`
|
|
348
|
-
Dashboard → Environment tab
|
|
294
|
+
- **Environment:** set provider credentials (`GENIUS_CLIENT_ID`, `GENIUS_CLIENT_SECRET`,
|
|
295
|
+
`MUSIXMATCH_FALLBACK_TOKEN`, etc.) in the Render Dashboard → Environment tab
|
|
349
296
|
- **Health Check Path:** `/health` (returns `{ "status": "ok", "providers": [...] }`)
|
|
350
297
|
|
|
351
|
-
>
|
|
352
|
-
>
|
|
353
|
-
>
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
| `format_lyrics` | Format lyrics in memory (optional romanization) for display. |
|
|
390
|
-
| `select_match` | Pick a prior result by provider/index/synced flag. |
|
|
391
|
-
| `runtime_status` | Snapshot provider readiness plus present env vars. |
|
|
392
|
-
| `push_catalog_to_airtable` | Write catalog records directly to Airtable with server-side lyric resolution — lyrics never pass through LLM arguments. Requires `AIRTABLE_PERSONAL_ACCESS_TOKEN`. |
|
|
393
|
-
|
|
394
|
-
### Airtable integration (server-side lyrics)
|
|
395
|
-
|
|
396
|
-
Mr. Magic ships a dedicated Airtable workflow that routes lyrics entirely
|
|
397
|
-
server-side so long lyric text never passes through LLM tool-call arguments.
|
|
398
|
-
This eliminates the JSON truncation and malformed-request errors that occur
|
|
399
|
-
when multiline Korean / CJK lyrics are interpolated into automation payloads.
|
|
400
|
-
|
|
401
|
-
#### How it works
|
|
298
|
+
> For custom domains, add them to `MR_MAGIC_ALLOWED_HOSTS` (comma-separated) in
|
|
299
|
+
> your Render environment so the DNS rebinding protection accepts requests with
|
|
300
|
+
> those `Host` headers.
|
|
301
|
+
|
|
302
|
+
### Transport selection
|
|
303
|
+
|
|
304
|
+
| Transport | Command | Use case |
|
|
305
|
+
|---|---|---|
|
|
306
|
+
| MCP stdio | `npm run server:mcp` | Local MCP clients that speak stdio |
|
|
307
|
+
| MCP Streamable HTTP | `npm run server:mcp:http` | Remote MCP clients |
|
|
308
|
+
| JSON HTTP automation | `npm run server:http` | Container / remote automations |
|
|
309
|
+
| CLI | `npm run cli` | Ad-hoc / SSH / CI one-shot commands |
|
|
310
|
+
|
|
311
|
+
## MCP Tools
|
|
312
|
+
|
|
313
|
+
Both the stdio and Streamable HTTP transports expose the same tool registry:
|
|
314
|
+
|
|
315
|
+
| Tool | Purpose |
|
|
316
|
+
|---|---|
|
|
317
|
+
| `find_lyrics` | Fetch best lyrics (prefers synced) plus metadata and payload. |
|
|
318
|
+
| `find_synced_lyrics` | Like `find_lyrics` but rejects plain-only results. |
|
|
319
|
+
| `search_lyrics` | List candidate matches across all providers without downloading lyrics. |
|
|
320
|
+
| `search_provider` | Query a single named provider. |
|
|
321
|
+
| `get_provider_status` | Report readiness and notes for each provider. |
|
|
322
|
+
| `format_lyrics` | Format lyrics in memory (optional romanization) for display. |
|
|
323
|
+
| `export_lyrics` | Write plain / LRC / SRT / romanized files to the export backend. |
|
|
324
|
+
| `select_match` | Pick a prior result by provider, index, or synced flag. |
|
|
325
|
+
| `build_catalog_payload` | Return a compact record (title / link / lyrics) for Airtable-style inserts. |
|
|
326
|
+
| `push_catalog_to_airtable` | Write catalog records to Airtable server-side — lyrics never pass through LLM arguments. Requires `AIRTABLE_PERSONAL_ACCESS_TOKEN`. |
|
|
327
|
+
| `runtime_status` | Snapshot provider readiness plus relevant env vars. |
|
|
328
|
+
|
|
329
|
+
## Airtable Integration
|
|
330
|
+
|
|
331
|
+
Mr. Magic routes lyrics entirely server-side so long lyric text never passes through
|
|
332
|
+
LLM tool-call arguments. This eliminates the JSON truncation and malformed-request
|
|
333
|
+
errors that occur when multiline Korean / CJK lyrics are interpolated into payloads.
|
|
334
|
+
|
|
335
|
+
### How it works
|
|
402
336
|
|
|
403
337
|
1. **Call `build_catalog_payload`** for each song. The response contains a
|
|
404
|
-
`lyricsCacheKey`
|
|
405
|
-
|
|
406
|
-
across the MCP session).
|
|
338
|
+
`lyricsCacheKey` (e.g. `kda-ill-show-you`) that identifies the resolved lyrics
|
|
339
|
+
in the server's in-memory LRU cache (20 entries, shared across the MCP session).
|
|
407
340
|
|
|
408
|
-
2. **
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
created record.
|
|
341
|
+
2. **Create Airtable records** (Song title, Spotify link, etc.) using your Airtable
|
|
342
|
+
MCP's bulk create tools (up to 10 records per call). Capture the `recordId`
|
|
343
|
+
returned for each created record.
|
|
412
344
|
|
|
413
|
-
3. **Call `push_catalog_to_airtable`** with
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
as an MCP argument.
|
|
345
|
+
3. **Call `push_catalog_to_airtable`** with `recordId`, `lyricsFieldId`, and
|
|
346
|
+
`lyricsCacheKey`. The server looks up the cached lyrics and calls the Airtable
|
|
347
|
+
REST API directly. Lyric text **never leaves the server process** as an MCP argument.
|
|
417
348
|
|
|
418
|
-
|
|
349
|
+
### `push_catalog_to_airtable` call shape
|
|
419
350
|
|
|
420
351
|
```json
|
|
421
352
|
{
|
|
@@ -429,146 +360,90 @@ when multiline Korean / CJK lyrics are interpolated into automation payloads.
|
|
|
429
360
|
}
|
|
430
361
|
```
|
|
431
362
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
the full payload in one request.
|
|
363
|
+
Set `"splitLyricsUpdate": true` when the combined create + lyrics payload is too large —
|
|
364
|
+
this forces a two-step create → PATCH so the full payload is never sent in one request.
|
|
435
365
|
|
|
436
|
-
|
|
366
|
+
### Bundled prompt template
|
|
437
367
|
|
|
438
|
-
`prompts/airtable-song-importer.md` (shipped in the npm package
|
|
439
|
-
|
|
440
|
-
songs into Airtable in bulk. It covers:
|
|
368
|
+
`prompts/airtable-song-importer.md` (shipped in the npm package) is a ready-to-use
|
|
369
|
+
system prompt for MCP assistants that bulk-import songs into Airtable. It covers:
|
|
441
370
|
|
|
442
|
-
- Phased execution: resolve
|
|
443
|
-
write lyrics → SRT export
|
|
371
|
+
- Phased execution: resolve → bulk create → write lyrics → SRT export
|
|
444
372
|
- Bulk record creation up to 10 records per Airtable MCP call
|
|
445
373
|
- Spotify link resolution via the Spotify MCP
|
|
446
374
|
- Romanized lyric priority for K-pop / CJK content
|
|
447
375
|
- `splitLyricsUpdate` fallback for oversized payloads
|
|
448
|
-
- SRT export delivery requirements
|
|
449
376
|
|
|
450
|
-
Copy the contents
|
|
451
|
-
client's system prompt to deploy this workflow immediately.
|
|
377
|
+
Copy the file contents into your MCP client's system prompt to deploy immediately.
|
|
452
378
|
|
|
453
|
-
|
|
379
|
+
### Safe lyric payload handoff
|
|
454
380
|
|
|
455
|
-
|
|
456
|
-
"inline JSON lyric" problem reported in workflows that pipe lyrics straight
|
|
457
|
-
into Airtable tool calls. Large multiline text that contains quotes, emoji, or
|
|
458
|
-
Unicode can corrupt downstream JSON when it is interpolated directly into a
|
|
459
|
-
payload string. To avoid this, request a structured lyric payload instead of
|
|
460
|
-
embedding the raw text:
|
|
381
|
+
To avoid embedding raw lyric text in tool-call arguments, request a structured payload:
|
|
461
382
|
|
|
462
|
-
```
|
|
383
|
+
```json
|
|
463
384
|
{
|
|
464
385
|
"track": { "artist": "K/DA", "title": "I'll Show You" },
|
|
465
386
|
"options": {
|
|
466
387
|
"omitInlineLyrics": true,
|
|
467
|
-
"lyricsPayloadMode": "payload"
|
|
388
|
+
"lyricsPayloadMode": "payload"
|
|
468
389
|
}
|
|
469
390
|
}
|
|
470
391
|
```
|
|
471
392
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
- `
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
literal `\"`, `\\`, `\n`) and prefers compact/reference-style payload handoff
|
|
492
|
-
when paired with `omitInlineLyrics: true`.
|
|
493
|
-
- Optional `lyricsPayloadOutput` lets you override the output directory when
|
|
494
|
-
using the default local backend.
|
|
495
|
-
|
|
496
|
-
Downstream automations (Airtable, Zapier, Make, etc.) should map either
|
|
497
|
-
`lyricsPayload.content` (inline mode) or fetch from
|
|
498
|
-
`lyricsPayload.reference.url/filePath` (reference mode) and place that string
|
|
499
|
-
into Airtable using the platform’s native variable substitution. This avoids
|
|
500
|
-
hand-written JSON concatenation and eliminates the malformed request errors
|
|
501
|
-
seen with long lyric fields.
|
|
502
|
-
|
|
503
|
-
#### SDK vs fetch calling guidance for Airtable-safe payloads
|
|
504
|
-
|
|
505
|
-
- **Preferred:** MCP SDK client calls (`client.callTool`) with `arguments` as a
|
|
506
|
-
native object.
|
|
507
|
-
- **Also valid:** raw `fetch`/HTTP requests, as long as you build one outer
|
|
508
|
-
request object and call `JSON.stringify()` once at send time.
|
|
509
|
-
- **Avoid:** manual JSON string templates that interpolate multiline lyrics.
|
|
510
|
-
|
|
511
|
-
SDK example (recommended):
|
|
393
|
+
Option reference:
|
|
394
|
+
|
|
395
|
+
- `omitInlineLyrics: true` — removes `lyrics`, `plainLyrics`, and `romanizedPlainLyrics`
|
|
396
|
+
from the response, keeping it compact.
|
|
397
|
+
- `lyricsPayloadMode: "payload"` — adds a `lyricsPayload` object with the full text
|
|
398
|
+
inline (`transport: "inline"`). May auto-promote to `"reference"` for long lyrics when
|
|
399
|
+
`omitInlineLyrics` is also `true`.
|
|
400
|
+
- `lyricsPayloadMode: "reference"` — stores lyrics via the export backend and returns
|
|
401
|
+
a `lyricsPayload.reference` object with `filePath` or `url` instead of raw text.
|
|
402
|
+
- `airtableSafePayload: true` — adds `lyricsPayload.airtableEscapedContent` (quotes /
|
|
403
|
+
backslashes / newlines pre-escaped) and prefers compact / reference-style handoff.
|
|
404
|
+
|
|
405
|
+
**Important:** `tools/call.params.arguments` must be a plain JSON object. Do not
|
|
406
|
+
pre-serialize arguments into a string — `build_catalog_payload` and `select_match`
|
|
407
|
+
reject stringified payloads.
|
|
408
|
+
|
|
409
|
+
### Calling patterns
|
|
410
|
+
|
|
411
|
+
Preferred (MCP SDK):
|
|
512
412
|
|
|
513
413
|
```js
|
|
514
414
|
await client.callTool({
|
|
515
415
|
name: 'build_catalog_payload',
|
|
516
416
|
arguments: {
|
|
517
417
|
track: { artist: 'K/DA', title: "I'll Show You" },
|
|
518
|
-
options: {
|
|
519
|
-
omitInlineLyrics: true,
|
|
520
|
-
lyricsPayloadMode: 'payload',
|
|
521
|
-
airtableSafePayload: true
|
|
522
|
-
}
|
|
418
|
+
options: { omitInlineLyrics: true, lyricsPayloadMode: 'payload', airtableSafePayload: true }
|
|
523
419
|
}
|
|
524
420
|
});
|
|
525
421
|
```
|
|
526
422
|
|
|
527
|
-
|
|
423
|
+
Also valid (raw `fetch`, object-based):
|
|
528
424
|
|
|
529
425
|
```js
|
|
530
|
-
const body = {
|
|
531
|
-
jsonrpc: '2.0',
|
|
532
|
-
id: 1,
|
|
533
|
-
method: 'tools/call',
|
|
534
|
-
params: {
|
|
535
|
-
name: 'build_catalog_payload',
|
|
536
|
-
arguments: {
|
|
537
|
-
track: { artist: 'K/DA', title: "I'll Show You" },
|
|
538
|
-
options: {
|
|
539
|
-
omitInlineLyrics: true,
|
|
540
|
-
lyricsPayloadMode: 'payload',
|
|
541
|
-
airtableSafePayload: true
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
};
|
|
546
|
-
|
|
547
426
|
await fetch('http://127.0.0.1:3444/mcp', {
|
|
548
427
|
method: 'POST',
|
|
549
|
-
headers: {
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
428
|
+
headers: { 'Content-Type': 'application/json', Accept: 'application/json, text/event-stream' },
|
|
429
|
+
body: JSON.stringify({
|
|
430
|
+
jsonrpc: '2.0', id: 1, method: 'tools/call',
|
|
431
|
+
params: {
|
|
432
|
+
name: 'build_catalog_payload',
|
|
433
|
+
arguments: {
|
|
434
|
+
track: { artist: 'K/DA', title: "I'll Show You" },
|
|
435
|
+
options: { omitInlineLyrics: true, lyricsPayloadMode: 'payload', airtableSafePayload: true }
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
})
|
|
554
439
|
});
|
|
555
440
|
```
|
|
556
441
|
|
|
557
|
-
|
|
442
|
+
**Avoid:** manual JSON string templates that interpolate multiline lyrics.
|
|
558
443
|
|
|
559
|
-
|
|
560
|
-
string that was truncated mid-quote), the server now validates and normalizes
|
|
561
|
-
incoming arguments at the transport boundary before tool execution:
|
|
444
|
+
### Debugging truncated arguments
|
|
562
445
|
|
|
563
|
-
|
|
564
|
-
- For compatibility tools, string args are parsed once; parse failures return a
|
|
565
|
-
consistent `Invalid JSON format for params: ...` error.
|
|
566
|
-
- For Airtable-heavy tools (`build_catalog_payload`, `select_match`), string
|
|
567
|
-
args are rejected so callers keep `params.arguments` as object/record.
|
|
568
|
-
- Logs include argument length plus head/tail previews to pinpoint where data
|
|
569
|
-
was cut off.
|
|
570
|
-
|
|
571
|
-
For deeper diagnostics, enable chunk logging:
|
|
446
|
+
Enable chunk logging to diagnose malformed / truncated MCP tool arguments:
|
|
572
447
|
|
|
573
448
|
```bash
|
|
574
449
|
MR_MAGIC_LOG_TOOL_ARGS_CHUNKS=1
|
|
@@ -576,63 +451,24 @@ MR_MAGIC_TOOL_ARG_CHUNK_SIZE=400
|
|
|
576
451
|
LOG_LEVEL=debug
|
|
577
452
|
```
|
|
578
453
|
|
|
579
|
-
|
|
580
|
-
occurred before or at MCP transport ingress.
|
|
581
|
-
|
|
582
|
-
Recommended debugging presets:
|
|
583
|
-
|
|
584
|
-
- **Normal operation (default):**
|
|
585
|
-
|
|
586
|
-
```env
|
|
587
|
-
LOG_LEVEL=info
|
|
588
|
-
MR_MAGIC_LOG_TOOL_ARGS_CHUNKS=0
|
|
589
|
-
```
|
|
590
|
-
|
|
591
|
-
- **General verbose debugging (without chunk spam):**
|
|
592
|
-
|
|
593
|
-
```env
|
|
594
|
-
LOG_LEVEL=debug
|
|
595
|
-
MR_MAGIC_LOG_TOOL_ARGS_CHUNKS=0
|
|
596
|
-
```
|
|
454
|
+
Recommended presets:
|
|
597
455
|
|
|
598
|
-
|
|
456
|
+
| Scenario | `LOG_LEVEL` | `MR_MAGIC_LOG_TOOL_ARGS_CHUNKS` |
|
|
457
|
+
|---|---|---|
|
|
458
|
+
| Normal operation | `info` | `0` |
|
|
459
|
+
| General verbose | `debug` | `0` |
|
|
460
|
+
| Truncation diagnostics | `debug` | `1` |
|
|
599
461
|
|
|
600
|
-
|
|
601
|
-
LOG_LEVEL=debug
|
|
602
|
-
MR_MAGIC_LOG_TOOL_ARGS_CHUNKS=1
|
|
603
|
-
MR_MAGIC_TOOL_ARG_CHUNK_SIZE=400
|
|
604
|
-
```
|
|
462
|
+
## MCP Client Configuration
|
|
605
463
|
|
|
606
|
-
|
|
607
|
-
|
|
464
|
+
> ⚠️ **Stdio MCP clients:** Always invoke the server binary directly — never via
|
|
465
|
+
> `npm run server:mcp`. The npm script preamble (`> mr-magic-mcp-server@x.x.x …`) is
|
|
466
|
+
> written to stdout before Node starts, and stdio MCP clients try to parse every stdout
|
|
467
|
+
> line as JSON-RPC, causing "Unexpected token '>'" errors on every connection.
|
|
608
468
|
|
|
609
|
-
###
|
|
469
|
+
### npx (recommended — no clone required)
|
|
610
470
|
|
|
611
|
-
|
|
612
|
-
> binary directly rather than `npm run server:mcp`. When `npm` runs a script it
|
|
613
|
-
> echoes a preamble like `> mr-magic-mcp-server@x.x.x server:mcp` to stdout
|
|
614
|
-
> before the Node process starts. Cline and other stdio MCP clients try to parse
|
|
615
|
-
> every stdout line as JSON-RPC, so those `>` lines cause "Unexpected token '>'"
|
|
616
|
-
> parse errors on every connection attempt. Direct invocation produces no such
|
|
617
|
-
> preamble.
|
|
618
|
-
|
|
619
|
-
#### npx (recommended — no clone needed)
|
|
620
|
-
|
|
621
|
-
Works with any MCP client that supports `command`/`args`. The package is fetched
|
|
622
|
-
from npm on first run and cached locally:
|
|
623
|
-
|
|
624
|
-
```json
|
|
625
|
-
{
|
|
626
|
-
"mcpServers": {
|
|
627
|
-
"Mr. Magic": {
|
|
628
|
-
"command": "npx",
|
|
629
|
-
"args": ["-y", "mr-magic-mcp-server"]
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
```
|
|
634
|
-
|
|
635
|
-
Add env vars inline if your client supports the `env` field:
|
|
471
|
+
Works with any MCP client that supports `command` / `args`:
|
|
636
472
|
|
|
637
473
|
```json
|
|
638
474
|
{
|
|
@@ -650,10 +486,9 @@ Add env vars inline if your client supports the `env` field:
|
|
|
650
486
|
}
|
|
651
487
|
```
|
|
652
488
|
|
|
653
|
-
|
|
489
|
+
### Global install
|
|
654
490
|
|
|
655
|
-
After `npm install -g mr-magic-mcp-server`,
|
|
656
|
-
directly (it's on `PATH`):
|
|
491
|
+
After `npm install -g mr-magic-mcp-server`, the `mcp-server` binary is on `PATH`:
|
|
657
492
|
|
|
658
493
|
```json
|
|
659
494
|
{
|
|
@@ -665,10 +500,9 @@ directly (it's on `PATH`):
|
|
|
665
500
|
}
|
|
666
501
|
```
|
|
667
502
|
|
|
668
|
-
|
|
503
|
+
### Local repo — Cline
|
|
669
504
|
|
|
670
|
-
Cline supports `cwd`, so you can
|
|
671
|
-
here**, as npm's script echo will corrupt the stdio stream:
|
|
505
|
+
Cline supports `cwd`, so you can invoke `node` directly:
|
|
672
506
|
|
|
673
507
|
```json
|
|
674
508
|
{
|
|
@@ -685,10 +519,9 @@ here**, as npm's script echo will corrupt the stdio stream:
|
|
|
685
519
|
}
|
|
686
520
|
```
|
|
687
521
|
|
|
688
|
-
|
|
522
|
+
### Local repo — clients without `cwd` support
|
|
689
523
|
|
|
690
|
-
For clients like TypingMind that don't support
|
|
691
|
-
wrapper with the absolute path:
|
|
524
|
+
For clients like TypingMind that don't support `cwd`, use a shell wrapper:
|
|
692
525
|
|
|
693
526
|
```json
|
|
694
527
|
{
|
|
@@ -701,113 +534,125 @@ wrapper with the absolute path:
|
|
|
701
534
|
}
|
|
702
535
|
```
|
|
703
536
|
|
|
704
|
-
|
|
537
|
+
## CLI
|
|
538
|
+
|
|
539
|
+
A single CLI entrypoint (`mrmagic-cli`) is published with the package. Inside the
|
|
540
|
+
local repo use `npm run cli -- <subcommand>` unless you have run `npm link` or
|
|
541
|
+
installed globally.
|
|
542
|
+
|
|
543
|
+
### Commands
|
|
705
544
|
|
|
706
|
-
|
|
545
|
+
| Command | Purpose | Notable flags |
|
|
546
|
+
|---|---|---|
|
|
547
|
+
| `mrmagic-cli search` | List candidates across providers without downloading. | `--artist`, `--title`, `--provider`, `--duration`, `--show-all`, `--pick` |
|
|
548
|
+
| `mrmagic-cli find` | Resolve best lyric (prefers synced) and print / export. | `--providers`, `--synced-only`, `--export`, `--format`, `--output`, `--no-romanize`, `--choose`, `--index` |
|
|
549
|
+
| `mrmagic-cli select` | Pick first match from a prioritized provider list. | `--providers`, `--artist`, `--title`, `--require-synced` |
|
|
550
|
+
| `mrmagic-cli server` | Start the JSON automation API. | `--host`, `--port`, `--remote` |
|
|
551
|
+
| `mrmagic-cli server:mcp` | Start the MCP stdio server. | — |
|
|
552
|
+
| `mrmagic-cli server:mcp:http` | Start the Streamable HTTP MCP server. | `--host`, `--port`, `--remote`, `--sessionless` |
|
|
553
|
+
| `mrmagic-cli search-provider` | Query a single provider only. | `--provider`, `--artist`, `--title` |
|
|
554
|
+
| `mrmagic-cli status` | Print provider readiness. | — |
|
|
555
|
+
|
|
556
|
+
### Examples
|
|
557
|
+
|
|
558
|
+
```bash
|
|
559
|
+
# Search all providers
|
|
560
|
+
npm run cli -- search --artist "BLACKPINK" --title "Kill This Love"
|
|
707
561
|
|
|
708
|
-
|
|
709
|
-
|
|
562
|
+
# Find best lyric (prefers synced LRC)
|
|
563
|
+
npm run cli -- find --artist "Nayeon" --title "POP!"
|
|
710
564
|
|
|
711
|
-
|
|
565
|
+
# Pick first synced match from a prioritized provider list
|
|
566
|
+
npm run cli -- select --providers lrclib,genius --artist "Nayeon" --title "POP!" --require-synced
|
|
712
567
|
|
|
713
|
-
|
|
714
|
-
-
|
|
715
|
-
|
|
716
|
-
object-vs-string argument boundary checks.
|
|
717
|
-
- `npm run repro:mcp:arg-boundary:sdk` – SDK client transport repro harness
|
|
718
|
-
(supports `MR_MAGIC_SDK_REPRO_HTTP_DEBUG=1` for verbose HTTP traces).
|
|
719
|
-
- `npm run lint` – ESLint
|
|
720
|
-
- `npm run format:check` – Prettier check mode
|
|
568
|
+
# Start JSON automation API on a custom port
|
|
569
|
+
mrmagic-cli server --port 4000
|
|
570
|
+
```
|
|
721
571
|
|
|
722
|
-
|
|
572
|
+
### npm argument forwarding
|
|
723
573
|
|
|
724
|
-
|
|
574
|
+
Both of these forms work:
|
|
725
575
|
|
|
726
576
|
```bash
|
|
727
|
-
npm run
|
|
577
|
+
npm run cli search --artist "K/DA" --title "I'll Show You"
|
|
578
|
+
npm run cli -- search --artist "K/DA" --title "I'll Show You"
|
|
728
579
|
```
|
|
729
580
|
|
|
730
|
-
|
|
581
|
+
For direct binary usage: `mrmagic-cli search --artist "K/DA" --title "I'll Show You"`.
|
|
731
582
|
|
|
732
|
-
|
|
733
|
-
- `POST /` with body shape:
|
|
583
|
+
## Manual Testing
|
|
734
584
|
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
585
|
+
Automated checks:
|
|
586
|
+
|
|
587
|
+
```bash
|
|
588
|
+
npm run test # full bundled test runner
|
|
589
|
+
node tests/mcp-tools.test.js # raw MCP integration harness
|
|
590
|
+
npm run repro:mcp:arg-boundary # JSON-RPC argument boundary repro
|
|
591
|
+
npm run repro:mcp:arg-boundary:sdk # SDK client transport repro
|
|
592
|
+
npm run lint
|
|
593
|
+
npm run format:check
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
### JSON HTTP server (`server:http`)
|
|
597
|
+
|
|
598
|
+
Start the server:
|
|
599
|
+
|
|
600
|
+
```bash
|
|
601
|
+
npm run server:http
|
|
741
602
|
```
|
|
742
603
|
|
|
743
|
-
|
|
604
|
+
Default base URL: `http://127.0.0.1:3333`
|
|
605
|
+
|
|
606
|
+
Accepted requests:
|
|
607
|
+
|
|
608
|
+
- `GET /health`
|
|
609
|
+
- `POST /` with body `{ "action": "find|findSynced|search", "track": {...}, "options": {...} }`
|
|
610
|
+
|
|
611
|
+
#### Health check
|
|
744
612
|
|
|
745
613
|
```bash
|
|
746
614
|
curl -sS http://127.0.0.1:3333/health | jq
|
|
747
615
|
```
|
|
748
616
|
|
|
749
|
-
|
|
617
|
+
#### Basic lyric lookup (`action=find`)
|
|
750
618
|
|
|
751
619
|
```bash
|
|
752
620
|
curl -sS -X POST http://127.0.0.1:3333 \
|
|
753
621
|
-H 'Content-Type: application/json' \
|
|
754
|
-
-d '{
|
|
755
|
-
"action":"find",
|
|
756
|
-
"track":{"artist":"Coldplay","title":"Yellow"},
|
|
757
|
-
"options":{}
|
|
758
|
-
}' | jq
|
|
622
|
+
-d '{"action":"find","track":{"artist":"Coldplay","title":"Yellow"},"options":{}}' | jq
|
|
759
623
|
```
|
|
760
624
|
|
|
761
|
-
|
|
625
|
+
#### Synced-only lookup (`action=findSynced`)
|
|
762
626
|
|
|
763
627
|
```bash
|
|
764
628
|
curl -sS -X POST http://127.0.0.1:3333 \
|
|
765
629
|
-H 'Content-Type: application/json' \
|
|
766
|
-
-d '{
|
|
767
|
-
"action":"findSynced",
|
|
768
|
-
"track":{"artist":"Coldplay","title":"Yellow"},
|
|
769
|
-
"options":{}
|
|
770
|
-
}' | jq
|
|
630
|
+
-d '{"action":"findSynced","track":{"artist":"Coldplay","title":"Yellow"},"options":{}}' | jq
|
|
771
631
|
```
|
|
772
632
|
|
|
773
|
-
|
|
633
|
+
#### Search candidates (`action=search`)
|
|
774
634
|
|
|
775
635
|
```bash
|
|
776
636
|
curl -sS -X POST http://127.0.0.1:3333 \
|
|
777
637
|
-H 'Content-Type: application/json' \
|
|
778
|
-
-d '{
|
|
779
|
-
"action":"search",
|
|
780
|
-
"track":{"artist":"Coldplay","title":"Yellow"}
|
|
781
|
-
}' | jq
|
|
638
|
+
-d '{"action":"search","track":{"artist":"Coldplay","title":"Yellow"}}' | jq
|
|
782
639
|
```
|
|
783
640
|
|
|
784
|
-
|
|
641
|
+
#### Export flow
|
|
785
642
|
|
|
786
643
|
```bash
|
|
787
644
|
curl -sS -X POST http://127.0.0.1:3333 \
|
|
788
645
|
-H 'Content-Type: application/json' \
|
|
789
|
-
-d '{
|
|
790
|
-
"action":"find",
|
|
791
|
-
"track":{"artist":"Coldplay","title":"Yellow"},
|
|
792
|
-
"options":{"export":true,"formats":["plain"]}
|
|
793
|
-
}' | jq
|
|
646
|
+
-d '{"action":"find","track":{"artist":"Coldplay","title":"Yellow"},"options":{"export":true,"formats":["plain"]}}' | jq
|
|
794
647
|
```
|
|
795
648
|
|
|
796
649
|
Look for `exports.plain` in the response:
|
|
797
650
|
|
|
798
|
-
- **redis backend:** `url`
|
|
799
|
-
- **local backend:** `filePath`
|
|
800
|
-
- **inline backend:** `content`
|
|
801
|
-
|
|
802
|
-
##### F. Verify the exported download URL
|
|
803
|
-
|
|
804
|
-
If using Redis + HTTP downloads, fetch the URL returned from `exports.*.url`:
|
|
805
|
-
|
|
806
|
-
```bash
|
|
807
|
-
curl -sS 'http://127.0.0.1:3333/downloads/<export-id>/txt' | head -n 10
|
|
808
|
-
```
|
|
651
|
+
- **redis backend:** `url` field present, `skipped: false`
|
|
652
|
+
- **local backend:** `filePath` field present
|
|
653
|
+
- **inline backend:** `content` field present, `skipped: true`
|
|
809
654
|
|
|
810
|
-
|
|
655
|
+
To fetch a Redis export download:
|
|
811
656
|
|
|
812
657
|
```bash
|
|
813
658
|
EXPORT_URL=$(curl -sS -X POST http://127.0.0.1:3333 \
|
|
@@ -818,42 +663,33 @@ EXPORT_URL=$(curl -sS -X POST http://127.0.0.1:3333 \
|
|
|
818
663
|
curl -sS "$EXPORT_URL" | head -n 10
|
|
819
664
|
```
|
|
820
665
|
|
|
821
|
-
|
|
822
|
-
> and `MR_MAGIC_DOWNLOAD_BASE_URL` matches the HTTP server base URL.
|
|
823
|
-
|
|
824
|
-
#### 2) Manual MCP Streamable HTTP testing (`server:mcp:http`)
|
|
666
|
+
### MCP Streamable HTTP server (`server:mcp:http`)
|
|
825
667
|
|
|
826
|
-
Start
|
|
668
|
+
Start the server:
|
|
827
669
|
|
|
828
670
|
```bash
|
|
829
671
|
npm run server:mcp:http
|
|
830
672
|
```
|
|
831
673
|
|
|
832
|
-
Default
|
|
833
|
-
- `http://127.0.0.1:3444/mcp`
|
|
834
|
-
|
|
835
|
-
All manual calls are JSON-RPC 2.0 requests.
|
|
674
|
+
Default endpoint: `http://127.0.0.1:3444/mcp`
|
|
836
675
|
|
|
837
|
-
|
|
676
|
+
All requests are JSON-RPC 2.0. Required headers:
|
|
838
677
|
|
|
839
|
-
|
|
840
|
-
-
|
|
841
|
-
|
|
842
|
-
```json
|
|
843
|
-
{
|
|
844
|
-
"action": "find | findSynced | search",
|
|
845
|
-
"track": { "artist": "...", "title": "...", "album": "..." },
|
|
846
|
-
"options": { "...": "..." }
|
|
847
|
-
}
|
|
678
|
+
```
|
|
679
|
+
Content-Type: application/json
|
|
680
|
+
Accept: application/json, text/event-stream
|
|
848
681
|
```
|
|
849
682
|
|
|
850
|
-
|
|
683
|
+
> Tip: use `--sessionless` for easier stateless manual testing:
|
|
684
|
+
> `npm run cli -- server:mcp:http --sessionless`
|
|
685
|
+
|
|
686
|
+
#### Health check
|
|
851
687
|
|
|
852
688
|
```bash
|
|
853
|
-
curl -sS http://127.0.0.1:
|
|
689
|
+
curl -sS http://127.0.0.1:3444/health | jq
|
|
854
690
|
```
|
|
855
691
|
|
|
856
|
-
|
|
692
|
+
#### List tools
|
|
857
693
|
|
|
858
694
|
```bash
|
|
859
695
|
curl -sS -X POST http://127.0.0.1:3444/mcp \
|
|
@@ -862,269 +698,153 @@ curl -sS -X POST http://127.0.0.1:3444/mcp \
|
|
|
862
698
|
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | jq
|
|
863
699
|
```
|
|
864
700
|
|
|
865
|
-
|
|
701
|
+
#### `find_lyrics`
|
|
866
702
|
|
|
867
703
|
```bash
|
|
868
704
|
curl -sS -X POST http://127.0.0.1:3444/mcp \
|
|
869
705
|
-H 'Content-Type: application/json' \
|
|
870
706
|
-H 'Accept: application/json, text/event-stream' \
|
|
871
|
-
-d '{
|
|
872
|
-
"jsonrpc":"2.0",
|
|
873
|
-
"id":2,
|
|
874
|
-
"method":"tools/call",
|
|
875
|
-
"params":{
|
|
876
|
-
"name":"find_lyrics",
|
|
877
|
-
"arguments":{"track":{"artist":"Coldplay","title":"Yellow"}}
|
|
878
|
-
}
|
|
879
|
-
}' | jq
|
|
707
|
+
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"find_lyrics","arguments":{"track":{"artist":"Coldplay","title":"Yellow"}}}}' | jq
|
|
880
708
|
```
|
|
881
709
|
|
|
882
|
-
|
|
710
|
+
#### `find_synced_lyrics`
|
|
883
711
|
|
|
884
712
|
```bash
|
|
885
713
|
curl -sS -X POST http://127.0.0.1:3444/mcp \
|
|
886
714
|
-H 'Content-Type: application/json' \
|
|
887
715
|
-H 'Accept: application/json, text/event-stream' \
|
|
888
|
-
-d '{
|
|
889
|
-
"jsonrpc":"2.0",
|
|
890
|
-
"id":3,
|
|
891
|
-
"method":"tools/call",
|
|
892
|
-
"params":{
|
|
893
|
-
"name":"find_synced_lyrics",
|
|
894
|
-
"arguments":{"track":{"artist":"Coldplay","title":"Yellow"}}
|
|
895
|
-
}
|
|
896
|
-
}' | jq
|
|
716
|
+
-d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"find_synced_lyrics","arguments":{"track":{"artist":"Coldplay","title":"Yellow"}}}}' | jq
|
|
897
717
|
```
|
|
898
718
|
|
|
899
|
-
|
|
719
|
+
#### `build_catalog_payload` — inline lyrics
|
|
900
720
|
|
|
901
721
|
```bash
|
|
902
722
|
curl -sS -X POST http://127.0.0.1:3444/mcp \
|
|
903
723
|
-H 'Content-Type: application/json' \
|
|
904
724
|
-H 'Accept: application/json, text/event-stream' \
|
|
905
725
|
-d '{
|
|
906
|
-
"jsonrpc":"2.0",
|
|
907
|
-
"
|
|
908
|
-
"method":"tools/call",
|
|
909
|
-
"params":{
|
|
910
|
-
"name":"build_catalog_payload",
|
|
911
|
-
"arguments":{
|
|
912
|
-
"track":{"artist":"K/DA","title":"I'\''LL SHOW YOU"},
|
|
913
|
-
"options":{"preferRomanized":false}
|
|
914
|
-
}
|
|
915
|
-
}
|
|
726
|
+
"jsonrpc":"2.0","id":4,"method":"tools/call",
|
|
727
|
+
"params":{"name":"build_catalog_payload","arguments":{"track":{"artist":"K/DA","title":"I'\''LL SHOW YOU"},"options":{"preferRomanized":false}}}
|
|
916
728
|
}' | jq
|
|
917
729
|
```
|
|
918
730
|
|
|
919
|
-
|
|
731
|
+
#### `build_catalog_payload` — Airtable-safe mode
|
|
920
732
|
|
|
921
733
|
```bash
|
|
922
734
|
curl -sS -X POST http://127.0.0.1:3444/mcp \
|
|
923
735
|
-H 'Content-Type: application/json' \
|
|
924
736
|
-H 'Accept: application/json, text/event-stream' \
|
|
925
737
|
-d '{
|
|
926
|
-
"jsonrpc":"2.0",
|
|
927
|
-
"
|
|
928
|
-
"method":"tools/call",
|
|
929
|
-
"params":{
|
|
930
|
-
"name":"build_catalog_payload",
|
|
931
|
-
"arguments":{
|
|
932
|
-
"track":{"artist":"K/DA","title":"I'\''LL SHOW YOU"},
|
|
933
|
-
"options":{
|
|
934
|
-
"omitInlineLyrics":true,
|
|
935
|
-
"lyricsPayloadMode":"payload",
|
|
936
|
-
"airtableSafePayload":true
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
}
|
|
738
|
+
"jsonrpc":"2.0","id":5,"method":"tools/call",
|
|
739
|
+
"params":{"name":"build_catalog_payload","arguments":{"track":{"artist":"K/DA","title":"I'\''LL SHOW YOU"},"options":{"omitInlineLyrics":true,"lyricsPayloadMode":"payload","airtableSafePayload":true}}}
|
|
940
740
|
}' | jq
|
|
941
741
|
```
|
|
942
742
|
|
|
943
|
-
|
|
743
|
+
#### `search_lyrics`
|
|
944
744
|
|
|
945
745
|
```bash
|
|
946
746
|
curl -sS -X POST http://127.0.0.1:3444/mcp \
|
|
947
747
|
-H 'Content-Type: application/json' \
|
|
948
748
|
-H 'Accept: application/json, text/event-stream' \
|
|
949
|
-
-d '{
|
|
950
|
-
"jsonrpc":"2.0",
|
|
951
|
-
"id":6,
|
|
952
|
-
"method":"tools/call",
|
|
953
|
-
"params":{
|
|
954
|
-
"name":"search_lyrics",
|
|
955
|
-
"arguments":{"track":{"artist":"Coldplay","title":"Yellow"}}
|
|
956
|
-
}
|
|
957
|
-
}' | jq
|
|
749
|
+
-d '{"jsonrpc":"2.0","id":6,"method":"tools/call","params":{"name":"search_lyrics","arguments":{"track":{"artist":"Coldplay","title":"Yellow"}}}}' | jq
|
|
958
750
|
```
|
|
959
751
|
|
|
960
|
-
|
|
752
|
+
#### `search_provider`
|
|
961
753
|
|
|
962
754
|
```bash
|
|
963
755
|
curl -sS -X POST http://127.0.0.1:3444/mcp \
|
|
964
756
|
-H 'Content-Type: application/json' \
|
|
965
757
|
-H 'Accept: application/json, text/event-stream' \
|
|
966
|
-
-d '{
|
|
967
|
-
"jsonrpc":"2.0",
|
|
968
|
-
"id":7,
|
|
969
|
-
"method":"tools/call",
|
|
970
|
-
"params":{
|
|
971
|
-
"name":"search_provider",
|
|
972
|
-
"arguments":{
|
|
973
|
-
"provider":"lrclib",
|
|
974
|
-
"track":{"artist":"Coldplay","title":"Yellow"}
|
|
975
|
-
}
|
|
976
|
-
}
|
|
977
|
-
}' | jq
|
|
758
|
+
-d '{"jsonrpc":"2.0","id":7,"method":"tools/call","params":{"name":"search_provider","arguments":{"provider":"lrclib","track":{"artist":"Coldplay","title":"Yellow"}}}}' | jq
|
|
978
759
|
```
|
|
979
760
|
|
|
980
|
-
|
|
761
|
+
#### `format_lyrics`
|
|
981
762
|
|
|
982
763
|
```bash
|
|
983
764
|
curl -sS -X POST http://127.0.0.1:3444/mcp \
|
|
984
765
|
-H 'Content-Type: application/json' \
|
|
985
766
|
-H 'Accept: application/json, text/event-stream' \
|
|
986
|
-
-d '{
|
|
987
|
-
"jsonrpc":"2.0",
|
|
988
|
-
"id":8,
|
|
989
|
-
"method":"tools/call",
|
|
990
|
-
"params":{
|
|
991
|
-
"name":"format_lyrics",
|
|
992
|
-
"arguments":{
|
|
993
|
-
"track":{"artist":"aespa","title":"Supernova"},
|
|
994
|
-
"options":{"includeSynced":true}
|
|
995
|
-
}
|
|
996
|
-
}
|
|
997
|
-
}' | jq
|
|
767
|
+
-d '{"jsonrpc":"2.0","id":8,"method":"tools/call","params":{"name":"format_lyrics","arguments":{"track":{"artist":"aespa","title":"Supernova"},"options":{"includeSynced":true}}}}' | jq
|
|
998
768
|
```
|
|
999
769
|
|
|
1000
|
-
|
|
770
|
+
#### `export_lyrics`
|
|
1001
771
|
|
|
1002
772
|
```bash
|
|
1003
773
|
curl -sS -X POST http://127.0.0.1:3444/mcp \
|
|
1004
774
|
-H 'Content-Type: application/json' \
|
|
1005
775
|
-H 'Accept: application/json, text/event-stream' \
|
|
1006
|
-
-d '{
|
|
1007
|
-
"jsonrpc":"2.0",
|
|
1008
|
-
"id":9,
|
|
1009
|
-
"method":"tools/call",
|
|
1010
|
-
"params":{
|
|
1011
|
-
"name":"export_lyrics",
|
|
1012
|
-
"arguments":{
|
|
1013
|
-
"track":{"artist":"Coldplay","title":"Yellow"},
|
|
1014
|
-
"options":{"formats":["plain","lrc","srt"]}
|
|
1015
|
-
}
|
|
1016
|
-
}
|
|
1017
|
-
}' | jq
|
|
776
|
+
-d '{"jsonrpc":"2.0","id":9,"method":"tools/call","params":{"name":"export_lyrics","arguments":{"track":{"artist":"Coldplay","title":"Yellow"},"options":{"formats":["plain","lrc","srt"]}}}}' | jq
|
|
1018
777
|
```
|
|
1019
778
|
|
|
1020
|
-
|
|
779
|
+
#### `get_provider_status`
|
|
1021
780
|
|
|
1022
781
|
```bash
|
|
1023
782
|
curl -sS -X POST http://127.0.0.1:3444/mcp \
|
|
1024
783
|
-H 'Content-Type: application/json' \
|
|
1025
784
|
-H 'Accept: application/json, text/event-stream' \
|
|
1026
|
-
-d '{
|
|
1027
|
-
"jsonrpc":"2.0",
|
|
1028
|
-
"id":10,
|
|
1029
|
-
"method":"tools/call",
|
|
1030
|
-
"params":{"name":"get_provider_status","arguments":{}}
|
|
1031
|
-
}' | jq
|
|
785
|
+
-d '{"jsonrpc":"2.0","id":10,"method":"tools/call","params":{"name":"get_provider_status","arguments":{}}}' | jq
|
|
1032
786
|
```
|
|
1033
787
|
|
|
1034
|
-
|
|
788
|
+
#### `runtime_status`
|
|
1035
789
|
|
|
1036
790
|
```bash
|
|
1037
791
|
curl -sS -X POST http://127.0.0.1:3444/mcp \
|
|
1038
792
|
-H 'Content-Type: application/json' \
|
|
1039
793
|
-H 'Accept: application/json, text/event-stream' \
|
|
1040
|
-
-d '{
|
|
1041
|
-
"jsonrpc":"2.0",
|
|
1042
|
-
"id":11,
|
|
1043
|
-
"method":"tools/call",
|
|
1044
|
-
"params":{"name":"runtime_status","arguments":{}}
|
|
1045
|
-
}' | jq
|
|
794
|
+
-d '{"jsonrpc":"2.0","id":11,"method":"tools/call","params":{"name":"runtime_status","arguments":{}}}' | jq
|
|
1046
795
|
```
|
|
1047
796
|
|
|
1048
|
-
|
|
797
|
+
#### `push_catalog_to_airtable`
|
|
1049
798
|
|
|
1050
|
-
First call `build_catalog_payload`
|
|
1051
|
-
|
|
1052
|
-
to write lyrics to Airtable entirely server-side — no lyric text passes through
|
|
1053
|
-
your tool-call arguments.
|
|
799
|
+
First call `build_catalog_payload` to populate the lyric cache and capture `lyricsCacheKey`,
|
|
800
|
+
then pass it here — no lyric text goes through tool-call arguments:
|
|
1054
801
|
|
|
1055
802
|
```bash
|
|
1056
803
|
curl -sS -X POST http://127.0.0.1:3444/mcp \
|
|
1057
804
|
-H 'Content-Type: application/json' \
|
|
1058
805
|
-H 'Accept: application/json, text/event-stream' \
|
|
1059
806
|
-d '{
|
|
1060
|
-
"jsonrpc":"2.0",
|
|
1061
|
-
"
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
"
|
|
1065
|
-
"
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
"lyricsFieldId":"fldHV1qmPYmsvglff",
|
|
1071
|
-
"lyricsCacheKey":"kda-ill-show-you",
|
|
1072
|
-
"preferRomanized":true
|
|
1073
|
-
}
|
|
1074
|
-
}
|
|
807
|
+
"jsonrpc":"2.0","id":12,"method":"tools/call",
|
|
808
|
+
"params":{"name":"push_catalog_to_airtable","arguments":{
|
|
809
|
+
"baseId":"appeBUkVEp3N4RT0C",
|
|
810
|
+
"tableId":"tbl0y5XHFXpjUJXHu",
|
|
811
|
+
"recordId":"rec1234567890abcd",
|
|
812
|
+
"fields":{},
|
|
813
|
+
"lyricsFieldId":"fldHV1qmPYmsvglff",
|
|
814
|
+
"lyricsCacheKey":"kda-ill-show-you",
|
|
815
|
+
"preferRomanized":true
|
|
816
|
+
}}
|
|
1075
817
|
}' | jq
|
|
1076
818
|
```
|
|
1077
819
|
|
|
1078
|
-
> Replace `baseId`, `tableId`, `recordId`, `lyricsFieldId`, and `lyricsCacheKey`
|
|
1079
|
-
>
|
|
1080
|
-
> response. Requires `AIRTABLE_PERSONAL_ACCESS_TOKEN` to be set in `.env`.
|
|
820
|
+
> Replace `baseId`, `tableId`, `recordId`, `lyricsFieldId`, and `lyricsCacheKey` with
|
|
821
|
+
> real values. Requires `AIRTABLE_PERSONAL_ACCESS_TOKEN`.
|
|
1081
822
|
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
First run `search_lyrics` (section F above) and capture the matches, then
|
|
1085
|
-
pick the first synced result:
|
|
823
|
+
#### `select_match`
|
|
1086
824
|
|
|
1087
825
|
```bash
|
|
1088
826
|
curl -sS -X POST http://127.0.0.1:3444/mcp \
|
|
1089
827
|
-H 'Content-Type: application/json' \
|
|
1090
828
|
-H 'Accept: application/json, text/event-stream' \
|
|
1091
829
|
-d '{
|
|
1092
|
-
"jsonrpc":"2.0",
|
|
1093
|
-
"
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
"arguments":{
|
|
1098
|
-
"matches": [
|
|
1099
|
-
{
|
|
1100
|
-
"provider":"lrclib",
|
|
1101
|
-
"result":{
|
|
1102
|
-
"title":"Yellow","artist":"Coldplay",
|
|
1103
|
-
"synced":true,"plainOnly":false
|
|
1104
|
-
}
|
|
1105
|
-
}
|
|
1106
|
-
],
|
|
1107
|
-
"criteria":{"requireSynced":true,"index":0}
|
|
1108
|
-
}
|
|
1109
|
-
}
|
|
830
|
+
"jsonrpc":"2.0","id":13,"method":"tools/call",
|
|
831
|
+
"params":{"name":"select_match","arguments":{
|
|
832
|
+
"matches":[{"provider":"lrclib","result":{"title":"Yellow","artist":"Coldplay","synced":true,"plainOnly":false}}],
|
|
833
|
+
"criteria":{"requireSynced":true,"index":0}
|
|
834
|
+
}}
|
|
1110
835
|
}' | jq
|
|
1111
836
|
```
|
|
1112
837
|
|
|
1113
838
|
> **MCP tool response shape:**
|
|
1114
|
-
>
|
|
1115
|
-
> - `result.structuredContent` — machine-friendly object (all fields present, full values)
|
|
839
|
+
> - `result.structuredContent` — machine-friendly object (all fields, full values)
|
|
1116
840
|
> - `result.content[0].text` — complete pretty-printed JSON (identical to `structuredContent`)
|
|
1117
841
|
>
|
|
1118
|
-
> Both channels carry the same complete payload.
|
|
1119
|
-
>
|
|
1120
|
-
> reading `content[0].text` get the full JSON string.
|
|
1121
|
-
|
|
1122
|
-
> Tip: if your manual client has trouble with MCP sessions, start with
|
|
1123
|
-
> `npm run cli -- server:mcp:http --sessionless` for easier stateless testing.
|
|
842
|
+
> Both channels carry the same complete payload. Programmatic consumers should prefer
|
|
843
|
+
> `structuredContent`; LLM agents reading `content[0].text` get the full JSON string.
|
|
1124
844
|
|
|
1125
|
-
|
|
845
|
+
### Running both servers side-by-side
|
|
1126
846
|
|
|
1127
|
-
For
|
|
847
|
+
For Redis-backed download scenarios, run both servers in separate terminals:
|
|
1128
848
|
|
|
1129
849
|
```bash
|
|
1130
850
|
# Terminal 1
|
|
@@ -1134,78 +854,28 @@ npm run server:http
|
|
|
1134
854
|
npm run server:mcp:http
|
|
1135
855
|
```
|
|
1136
856
|
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
## CLI overview
|
|
1140
|
-
|
|
1141
|
-
A single CLI entrypoint (`mrmagic-cli`) is published with the package.
|
|
1142
|
-
Inside this repository, use `npm run cli -- --help` unless you've run
|
|
1143
|
-
`npm link` (or installed globally) so `mrmagic-cli` is available on `PATH`.
|
|
1144
|
-
Running `mrmagic-cli --help` (or `npm run cli -- --help` inside the repo),
|
|
1145
|
-
prints a top-level summary, while subcommand-specific help—e.g.,
|
|
1146
|
-
`mrmagic-cli search --help`—lists all flags
|
|
1147
|
-
with descriptions, defaults, and examples.
|
|
1148
|
-
|
|
1149
|
-
### Command summary
|
|
1150
|
-
|
|
1151
|
-
| Command | Purpose | Notable flags |
|
|
1152
|
-
| ----------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1153
|
-
| `mrmagic-cli search` | List candidate matches across providers without downloading lyrics. | `--artist`/`--title` (required track metadata), `--provider` (limit providers), `--duration` (match duration in ms), `--show-all` (print table), `--pick` (auto-select provider result). |
|
|
1154
|
-
| `mrmagic-cli find` | Resolve the best lyric (prefers synced) and print/export it. | `--providers` (CSV priority list), `--synced-only` (reject plain results), `--export` (write files), `--format` (repeatable; e.g., lrc,srt), `--output` (custom export dir), `--no-romanize`, `--choose`/`--index` (select specific match). |
|
|
1155
|
-
| `mrmagic-cli select` | Pick the first match from a prioritized provider list. | `--providers` (CSV order), `--artist`, `--title`, `--require-synced` (only accept synced lyrics). |
|
|
1156
|
-
| `mrmagic-cli server` | Run the JSON automation API (same as `npm run server:http`). | `--host` (interface to bind; default 127.0.0.1), `--port` (listening port; overrides env/`PORT`), `--remote` (shorthand for `--host 0.0.0.0`). |
|
|
1157
|
-
| `mrmagic-cli server:mcp` | Start the MCP stdio server (stdio transport). | _(none)_ |
|
|
1158
|
-
| `mrmagic-cli server:mcp:http` | Start the Streamable HTTP MCP server. | `--host`, `--port`, `--remote`, `--sessionless` (disable per-session connection IDs; useful for stateless/manual debugging). |
|
|
1159
|
-
| `mrmagic-cli search-provider` | Query a single provider only. | `--provider` (required provider name), `--artist`, `--title`. |
|
|
1160
|
-
| `mrmagic-cli status` | Print provider readiness information. | _(none)_ |
|
|
1161
|
-
|
|
1162
|
-
### Command Examples
|
|
1163
|
-
|
|
1164
|
-
- Local repo usage: `npm run cli -- search --artist "BLACKPINK" --title "Kill This Love"`
|
|
1165
|
-
– list candidates across all providers.
|
|
1166
|
-
- Local repo usage: `npm run cli -- find --artist "Nayeon" --title "POP!"`
|
|
1167
|
-
– download the best lyric (prefers synced LRC when possible).
|
|
1168
|
-
- Local repo usage: `npm run cli -- select --providers lrclib,genius --artist "Nayeon" --title "POP!" --require-synced`
|
|
1169
|
-
– pick the first synced match from the prioritized provider list.
|
|
1170
|
-
- Linked/global usage: `mrmagic-cli server --port 4000`
|
|
1171
|
-
– run the JSON automation API locally once `mrmagic-cli` is on `PATH`.
|
|
1172
|
-
|
|
1173
|
-
### CLI troubleshooting (npm argument forwarding)
|
|
1174
|
-
|
|
1175
|
-
The `cli` npm script supports both invocation styles:
|
|
1176
|
-
|
|
1177
|
-
- ✅ `npm run cli search --artist "K/DA" --title "I'll Show You"`
|
|
1178
|
-
- ✅ `npm run cli -- search --artist "K/DA" --title "I'll Show You"`
|
|
1179
|
-
|
|
1180
|
-
The first form is preferred for readability, but both are supported.
|
|
1181
|
-
|
|
1182
|
-
For direct binary usage, use `mrmagic-cli search --artist ... --title ...`.
|
|
1183
|
-
|
|
1184
|
-
## Provider notes
|
|
857
|
+
## Provider Notes
|
|
1185
858
|
|
|
1186
|
-
- **LRCLIB
|
|
1187
|
-
- **Genius
|
|
1188
|
-
|
|
1189
|
-
- **Musixmatch
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
- **Melon**: Works anonymously but benefits from `MELON_COOKIE` for reliability
|
|
1194
|
-
if needed.
|
|
859
|
+
- **LRCLIB** — Public API with synced lyric coverage. No auth required.
|
|
860
|
+
- **Genius** — Requires `GENIUS_CLIENT_ID` + `GENIUS_CLIENT_SECRET` (auto-refresh,
|
|
861
|
+
recommended) or `GENIUS_ACCESS_TOKEN` (static fallback token).
|
|
862
|
+
- **Musixmatch** — Requires a token. Use `MUSIXMATCH_FALLBACK_TOKEN` for production /
|
|
863
|
+
ephemeral hosts; use the on-disk cache token (`npm run fetch:musixmatch-token`) for
|
|
864
|
+
local dev. See [Musixmatch](#musixmatch) for the full workflow.
|
|
865
|
+
- **Melon** — Works anonymously. Set `MELON_COOKIE` for pinned / reproducible sessions.
|
|
1195
866
|
|
|
1196
|
-
Providers are queried concurrently
|
|
1197
|
-
|
|
867
|
+
Providers are queried concurrently and results are normalized into a shared schema
|
|
868
|
+
exposed via the CLI, HTTP API, and MCP tools.
|
|
1198
869
|
|
|
1199
870
|
## Changelog
|
|
1200
871
|
|
|
1201
|
-
See `CHANGELOG.md` for a
|
|
1202
|
-
changes and test improvements.
|
|
872
|
+
See [`CHANGELOG.md`](CHANGELOG.md) for a full history of changes.
|
|
1203
873
|
|
|
1204
|
-
##
|
|
874
|
+
## License
|
|
1205
875
|
|
|
1206
|
-
[MIT
|
|
876
|
+
[MIT](LICENSE)
|
|
1207
877
|
|
|
1208
|
-
I am not and cannot be held liable for any
|
|
878
|
+
I am not and cannot be held liable for any infringement or ban from services
|
|
1209
879
|
that could occur as a result of using this software. Your usage is solely
|
|
1210
880
|
your responsibility. Godspeed.
|
|
1211
881
|
|