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 +117 -10
- package/package.json +1 -1
- package/src/transport/mcp-http-server.js +26 -0
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
|
|
237
|
-
Download links are built as
|
|
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
|
-
|
|
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:
|
|
253
|
+
MR_MAGIC_DOWNLOAD_BASE_URL=http://127.0.0.1:3444
|
|
243
254
|
```
|
|
244
255
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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
|
-
|
|
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
|
@@ -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();
|