mr-magic-mcp-server 0.1.13 → 0.1.15

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 CHANGED
@@ -12,6 +12,7 @@ automations, and CLI aficionados can all request lyrics from a single toolchain.
12
12
  - [Export and Download Configuration](#export-and-download-configuration)
13
13
  - [Local Deployment](#local-deployment)
14
14
  - [Remote Deployment](#remote-deployment)
15
+ - [HTTP Endpoints](#http-endpoints)
15
16
  - [MCP Tools](#mcp-tools)
16
17
  - [Airtable Integration](#airtable-integration)
17
18
  - [MCP Client Configuration](#mcp-client-configuration)
@@ -233,18 +234,30 @@ The `MR_MAGIC_EXPORT_BACKEND` variable controls where formatted lyrics are store
233
234
  `UPSTASH_REDIS_REST_TOKEN`, and `MR_MAGIC_DOWNLOAD_BASE_URL`.
234
235
 
235
236
  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}`.
237
+ of the server that will serve the download links (not the Upstash URL),
238
+ e.g. `https://lyrics.example.com`. Download links are built as
239
+ `{base_url}/downloads/{id}/{ext}`.
238
240
 
239
- For local testing:
241
+ Both HTTP servers serve `/downloads/:id/:ext` routes:
242
+
243
+ - **`server:mcp:http`** (port `3444`) — the Streamable HTTP MCP server includes its
244
+ own `/downloads` route. If you are already running this server, no additional HTTP
245
+ server is needed for Redis exports on Render or any remote deployment.
246
+ - **`server:http`** (port `3333`) — the JSON HTTP automation server also exposes the
247
+ same route and remains the right choice if you are running `server:mcp` (stdio) only
248
+ or want a standalone HTTP service for download links.
249
+
250
+ For local testing against the MCP HTTP server:
240
251
 
241
252
  ```bash
242
- MR_MAGIC_DOWNLOAD_BASE_URL=http://127.0.0.1:3333
253
+ MR_MAGIC_DOWNLOAD_BASE_URL=http://127.0.0.1:3444
243
254
  ```
244
255
 
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.
256
+ Or against the JSON HTTP server:
257
+
258
+ ```bash
259
+ MR_MAGIC_DOWNLOAD_BASE_URL=http://127.0.0.1:3333
260
+ ```
248
261
 
249
262
  ## Local Deployment
250
263
 
@@ -308,6 +321,93 @@ Recommended Render service settings:
308
321
  | JSON HTTP automation | `npm run server:http` | Container / remote automations |
309
322
  | CLI | `npm run cli` | Ad-hoc / SSH / CI one-shot commands |
310
323
 
324
+ ## HTTP Endpoints
325
+
326
+ Both HTTP servers expose a set of plain HTTP routes in addition to their primary
327
+ transports. These are accessible without any MCP or JSON-RPC framing.
328
+
329
+ | Endpoint | Method | Server | Description |
330
+ |---|---|---|---|
331
+ | `/health` | `GET` | Both | Liveness / readiness probe. Returns `{ "status": "ok", "providers": [...] }`. |
332
+ | `/downloads/:id/:ext` | `GET` | Both | Serve a Redis-backed export by ID and file extension (e.g. `plain`, `lrc`, `srt`). Returns `200 text/plain` on hit, `404` when the key is expired or missing. |
333
+ | `/mcp` | `POST` | `server:mcp:http` only | MCP Streamable HTTP transport endpoint (JSON-RPC 2.0). |
334
+ | `/` | `POST` | `server:http` only | JSON HTTP automation endpoint (action-based API). |
335
+
336
+ ### `/health`
337
+
338
+ Both servers respond to `GET /health` with a JSON object indicating overall status
339
+ and per-provider readiness. Use this as your Render (or container / load-balancer)
340
+ health check path.
341
+
342
+ **Response shape:**
343
+
344
+ ```json
345
+ {
346
+ "status": "ok",
347
+ "providers": [
348
+ { "name": "lrclib", "status": "ok" },
349
+ { "name": "genius", "status": "ok" },
350
+ { "name": "musixmatch", "status": "missing_token" },
351
+ { "name": "melon", "status": "ok" }
352
+ ]
353
+ }
354
+ ```
355
+
356
+ **MCP HTTP server** (default port `3444`):
357
+
358
+ ```bash
359
+ curl -sS http://127.0.0.1:3444/health | jq
360
+ ```
361
+
362
+ **JSON HTTP server** (default port `3333`):
363
+
364
+ ```bash
365
+ curl -sS http://127.0.0.1:3333/health | jq
366
+ ```
367
+
368
+ Provider `status` values:
369
+
370
+ | Value | Meaning |
371
+ |---|---|
372
+ | `ok` | Provider is configured and reachable. |
373
+ | `missing_token` | Required credential env var is not set. |
374
+ | `error` | Provider returned an unexpected error during the status probe. |
375
+
376
+ ### `/downloads/:id/:ext`
377
+
378
+ Serves a Redis-backed export file by its download ID and format extension. Both
379
+ servers expose this route so the same `MR_MAGIC_DOWNLOAD_BASE_URL` works regardless
380
+ of which server you are running.
381
+
382
+ **Parameters:**
383
+
384
+ - `:id` — the opaque download ID returned in the `url` field of an export response
385
+ - `:ext` — the file format: `plain`, `lrc`, `srt`, or `romanized`
386
+
387
+ **Example** (MCP HTTP server):
388
+
389
+ ```bash
390
+ curl -sS http://127.0.0.1:3444/downloads/coldplay-yellow-1741234567890/plain
391
+ ```
392
+
393
+ **Example** (JSON HTTP server):
394
+
395
+ ```bash
396
+ curl -sS http://127.0.0.1:3333/downloads/coldplay-yellow-1741234567890/plain
397
+ ```
398
+
399
+ Responses:
400
+
401
+ - `200 text/plain` — export content served directly
402
+ - `404` — key expired or never written (`MR_MAGIC_EXPORT_TTL_SECONDS` controls TTL,
403
+ default `3600` seconds)
404
+ - `400` — malformed path (missing ID or extension)
405
+ - `500` — Redis lookup error
406
+
407
+ > Requires `MR_MAGIC_EXPORT_BACKEND=redis` and valid `UPSTASH_*` credentials.
408
+ > For local and inline backends, exports are returned directly in the tool response
409
+ > and this route is not used.
410
+
311
411
  ## MCP Tools
312
412
 
313
413
  Both the stdio and Streamable HTTP transports expose the same tool registry:
@@ -844,16 +944,23 @@ curl -sS -X POST http://127.0.0.1:3444/mcp \
844
944
 
845
945
  ### Running both servers side-by-side
846
946
 
847
- For Redis-backed download scenarios, run both servers in separate terminals:
947
+ You may want both servers running at the same time — for example, to serve JSON HTTP
948
+ automations (`server:http`) alongside MCP tool calls (`server:mcp:http`), or to expose
949
+ both a REST API and an MCP endpoint under one deployment.
848
950
 
849
951
  ```bash
850
952
  # Terminal 1
851
- npm run server:http
953
+ npm run server:http # JSON HTTP automation — port 3333
852
954
 
853
955
  # Terminal 2
854
- npm run server:mcp:http
956
+ npm run server:mcp:http # Streamable HTTP MCP — port 3444
855
957
  ```
856
958
 
959
+ > **Note:** Running both is **not** required for Redis exports. The MCP HTTP server
960
+ > (`server:mcp:http`) includes its own `/downloads/:id/:ext` route, so a single
961
+ > `server:mcp:http` instance is self-sufficient for Redis-backed download links.
962
+ > Only run `server:http` alongside it if you also need the JSON HTTP automation API.
963
+
857
964
  ## Provider Notes
858
965
 
859
966
  - **LRCLIB** — Public API with synced lyric coverage. No auth required.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mr-magic-mcp-server",
3
- "version": "0.1.13",
3
+ "version": "0.1.15",
4
4
  "description": "Lyrics MCP server connecting LRCLIB, Genius, Musixmatch, and Melon",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -8,6 +8,7 @@ import { createMcpExpressApp } from '@modelcontextprotocol/sdk/server/express.js
8
8
  import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
9
9
 
10
10
  import { createLogger } from '../utils/logger.js';
11
+ import { getSharedRedisClient } from '../utils/export-storage/shared-redis-client.js';
11
12
 
12
13
  import { mcpToolDefinitions, handleMcpTool } from './mcp-tools.js';
13
14
  import { buildMcpResponse } from './mcp-response.js';
@@ -111,10 +112,35 @@ export async function startMcpHttpServer(options = {}) {
111
112
  : undefined;
112
113
 
113
114
  const app = createMcpExpressApp({ host, ...(allowedHosts ? { allowedHosts } : {}) });
115
+
114
116
  app.get('/health', async (_req, res) => {
115
117
  res.json({ status: 'ok', providers: await getProviderStatus() });
116
118
  });
117
119
 
120
+ app.get('/downloads/:downloadId/*', async (req, res) => {
121
+ const { downloadId } = req.params;
122
+ const extension = req.params[0] || '';
123
+ if (!downloadId || !extension) {
124
+ res.status(400).json({ error: 'Invalid download path' });
125
+ return;
126
+ }
127
+ try {
128
+ const redis = getSharedRedisClient({ context: 'mcp-http-download' });
129
+ const key = `mr-magic:${downloadId}:${extension}`;
130
+ const content = await redis.get(key);
131
+ if (!content) {
132
+ logger.warn('Export download missing', { context: 'mcp-http-download', key, downloadId, extension });
133
+ res.status(404).json({ error: 'Export expired or missing' });
134
+ return;
135
+ }
136
+ res.status(200).type('text/plain').send(content);
137
+ logger.info('Export download served', { context: 'mcp-http-download', key, downloadId, extension, bytes: Buffer.byteLength(content) });
138
+ } catch (error) {
139
+ logger.error('Download lookup failed', { error, url: req.originalUrl });
140
+ res.status(500).json({ error: 'Failed to fetch export' });
141
+ }
142
+ });
143
+
118
144
  app.all('/mcp', async (req, res) => {
119
145
  const normalizedBody = normalizeIncomingRpcBody(req.body);
120
146
  const requestId = randomUUID();