audiobookshelf-mcp 0.1.2 → 0.1.4
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 +1 -0
- package/dist/config.js +19 -10
- package/dist/config.js.map +1 -1
- package/dist/hosts.d.ts +22 -0
- package/dist/hosts.js +144 -0
- package/dist/hosts.js.map +1 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
[](https://github.com/ni-c/audiobookshelf-mcp/pkgs/container/audiobookshelf-mcp)
|
|
8
8
|
[](LICENSE)
|
|
9
9
|
[](https://audiobookshelf-mcp.ni-c.de)
|
|
10
|
+
[](https://github.com/sponsors/ni-c)
|
|
10
11
|
|
|
11
12
|
An [MCP](https://modelcontextprotocol.io) server for
|
|
12
13
|
[Audiobookshelf](https://www.audiobookshelf.org/), the self-hosted audiobook and
|
package/dist/config.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { internalHostKind } from './hosts.js';
|
|
1
2
|
/** Shown when the configuration is incomplete — at startup and on every API call. */
|
|
2
3
|
export function missingConfigMessage(missing) {
|
|
3
4
|
return (`missing required environment variable(s): ${missing.join(', ')}\n` +
|
|
@@ -27,6 +28,14 @@ export function loadConfig(env = process.env) {
|
|
|
27
28
|
const apiKey = env.AUDIOBOOKSHELF_API_KEY;
|
|
28
29
|
const insecureTls = env.AUDIOBOOKSHELF_INSECURE_TLS === 'true';
|
|
29
30
|
const readOnly = env.AUDIOBOOKSHELF_READ_ONLY === 'true';
|
|
31
|
+
// Don't keep the key in the environment for the process lifetime — it is
|
|
32
|
+
// visible to child processes and in /proc/<pid>/environ. This happens before
|
|
33
|
+
// any branch on purpose: the paths below either exit or return early, and
|
|
34
|
+
// "the URL is missing or malformed" is exactly the state in which someone
|
|
35
|
+
// runs an inspector or trips a crash reporter, so it is the last moment the
|
|
36
|
+
// key should still be sitting in the environment. Everything after this point
|
|
37
|
+
// reads the locals above, never `env` again.
|
|
38
|
+
delete env.AUDIOBOOKSHELF_API_KEY;
|
|
30
39
|
const missing = [
|
|
31
40
|
!url && 'AUDIOBOOKSHELF_URL',
|
|
32
41
|
!apiKey && 'AUDIOBOOKSHELF_API_KEY',
|
|
@@ -42,7 +51,11 @@ export function loadConfig(env = process.env) {
|
|
|
42
51
|
parsed = new URL(url);
|
|
43
52
|
}
|
|
44
53
|
catch {
|
|
45
|
-
|
|
54
|
+
// The value itself is not echoed: this branch fires precisely when the
|
|
55
|
+
// variable does not hold what was expected, and an API key pasted into the
|
|
56
|
+
// wrong environment variable would otherwise be printed verbatim into the
|
|
57
|
+
// MCP host's log.
|
|
58
|
+
console.error('audiobookshelf-mcp: AUDIOBOOKSHELF_URL is not a valid URL (e.g. https://abs.example.com)');
|
|
46
59
|
process.exit(1);
|
|
47
60
|
}
|
|
48
61
|
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
|
|
@@ -58,21 +71,17 @@ export function loadConfig(env = process.env) {
|
|
|
58
71
|
console.error('audiobookshelf-mcp: WARNING: AUDIOBOOKSHELF_URL uses plain http to a non-local host — ' +
|
|
59
72
|
'the API key will be sent unencrypted. Use https:// instead.');
|
|
60
73
|
}
|
|
61
|
-
|
|
74
|
+
return {
|
|
62
75
|
url: url.replace(/\/+$/, ''),
|
|
63
76
|
apiKey,
|
|
64
77
|
insecureTls,
|
|
65
78
|
readOnly,
|
|
66
79
|
};
|
|
67
|
-
// Don't keep the key in the environment for the process lifetime — it is
|
|
68
|
-
// visible to child processes and in /proc/<pid>/environ.
|
|
69
|
-
delete env.AUDIOBOOKSHELF_API_KEY;
|
|
70
|
-
return config;
|
|
71
80
|
}
|
|
72
81
|
function isLoopbackHost(hostname) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
82
|
+
// The shared classifier, so every spelling of a loopback address is
|
|
83
|
+
// recognised — including http://[::ffff:127.0.0.1] and 'localhost.' with its
|
|
84
|
+
// root label, which the string comparison this replaced did not see.
|
|
85
|
+
return internalHostKind(hostname) === 'loopback';
|
|
77
86
|
}
|
|
78
87
|
//# sourceMappingURL=config.js.map
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/hosts.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Why a host is only reachable from the machine it is named on: `loopback`
|
|
3
|
+
* addresses that machine itself, `link-local` covers 169.254/16, fe80::/10 and
|
|
4
|
+
* the handful of addresses cloud providers put their metadata service on.
|
|
5
|
+
*/
|
|
6
|
+
export type InternalHostKind = 'loopback' | 'link-local';
|
|
7
|
+
/**
|
|
8
|
+
* Classifies a hostname that addresses the machine itself, its link-local
|
|
9
|
+
* range, or a cloud metadata service — or returns null for anything routable.
|
|
10
|
+
*
|
|
11
|
+
* It does the job numerically rather than by comparing strings, because the
|
|
12
|
+
* spellings differ more than they look. `URL` canonicalises an IPv4-mapped IPv6
|
|
13
|
+
* literal into hex — `http://[::ffff:127.0.0.1]/` arrives here as
|
|
14
|
+
* `[::ffff:7f00:1]` — while every dual-stack client dials it as plain
|
|
15
|
+
* 127.0.0.1, and `localhost.` with its root label is the same name as
|
|
16
|
+
* `localhost`. String comparison misses both.
|
|
17
|
+
*
|
|
18
|
+
* Takes a hostname from anywhere, not only from `URL`: a resolver hands back
|
|
19
|
+
* `::ffff:127.0.0.1` in dotted form and may attach a `%zone` suffix, so both
|
|
20
|
+
* are handled here rather than assumed away.
|
|
21
|
+
*/
|
|
22
|
+
export declare function internalHostKind(hostname: string): InternalHostKind | null;
|
package/dist/hosts.js
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { isIP } from 'node:net';
|
|
2
|
+
/**
|
|
3
|
+
* Names for the cloud metadata service, which resolve on an instance and
|
|
4
|
+
* nowhere else.
|
|
5
|
+
*/
|
|
6
|
+
const METADATA_NAMES = new Set([
|
|
7
|
+
'metadata',
|
|
8
|
+
'metadata.google.internal',
|
|
9
|
+
'metadata.goog',
|
|
10
|
+
'instance-data',
|
|
11
|
+
'instance-data.ec2.internal',
|
|
12
|
+
]);
|
|
13
|
+
/**
|
|
14
|
+
* Metadata endpoints that sit outside 169.254/16.
|
|
15
|
+
*
|
|
16
|
+
* Alibaba Cloud answers on 100.100.100.200, which is carrier-grade NAT space,
|
|
17
|
+
* and Oracle's legacy endpoint is 192.0.0.192, which is IETF protocol
|
|
18
|
+
* assignments. Neither is link-local by address, but both are the same thing by
|
|
19
|
+
* purpose, and neither is somewhere a legitimate target lives.
|
|
20
|
+
*/
|
|
21
|
+
const METADATA_ADDRESSES = new Set(['100.100.100.200', '192.0.0.192']);
|
|
22
|
+
/**
|
|
23
|
+
* Classifies a hostname that addresses the machine itself, its link-local
|
|
24
|
+
* range, or a cloud metadata service — or returns null for anything routable.
|
|
25
|
+
*
|
|
26
|
+
* It does the job numerically rather than by comparing strings, because the
|
|
27
|
+
* spellings differ more than they look. `URL` canonicalises an IPv4-mapped IPv6
|
|
28
|
+
* literal into hex — `http://[::ffff:127.0.0.1]/` arrives here as
|
|
29
|
+
* `[::ffff:7f00:1]` — while every dual-stack client dials it as plain
|
|
30
|
+
* 127.0.0.1, and `localhost.` with its root label is the same name as
|
|
31
|
+
* `localhost`. String comparison misses both.
|
|
32
|
+
*
|
|
33
|
+
* Takes a hostname from anywhere, not only from `URL`: a resolver hands back
|
|
34
|
+
* `::ffff:127.0.0.1` in dotted form and may attach a `%zone` suffix, so both
|
|
35
|
+
* are handled here rather than assumed away.
|
|
36
|
+
*/
|
|
37
|
+
export function internalHostKind(hostname) {
|
|
38
|
+
const host = bareHost(hostname);
|
|
39
|
+
if (host === 'localhost' || host.endsWith('.localhost'))
|
|
40
|
+
return 'loopback';
|
|
41
|
+
if (METADATA_NAMES.has(host))
|
|
42
|
+
return 'link-local';
|
|
43
|
+
if (METADATA_ADDRESSES.has(host))
|
|
44
|
+
return 'link-local';
|
|
45
|
+
const version = isIP(host);
|
|
46
|
+
if (version === 4)
|
|
47
|
+
return ipv4Kind(host.split('.').map(Number));
|
|
48
|
+
if (version !== 6)
|
|
49
|
+
return null;
|
|
50
|
+
const groups = expandIpv6(host);
|
|
51
|
+
if (groups === null)
|
|
52
|
+
return null;
|
|
53
|
+
const explicit = ipv6Kind(groups);
|
|
54
|
+
if (explicit !== null)
|
|
55
|
+
return explicit;
|
|
56
|
+
const embedded = embeddedIpv4(groups);
|
|
57
|
+
return embedded === null ? null : ipv4Kind(embedded);
|
|
58
|
+
}
|
|
59
|
+
function bareHost(hostname) {
|
|
60
|
+
return (hostname
|
|
61
|
+
.toLowerCase()
|
|
62
|
+
// URL.hostname keeps the brackets around an IPv6 literal, so a bare '::1'
|
|
63
|
+
// would never match.
|
|
64
|
+
.replace(/^\[|]$/g, '')
|
|
65
|
+
// A scope id belongs to the interface, not to the address. `isIP` accepts
|
|
66
|
+
// it, so leaving it on would desynchronise every check below from what
|
|
67
|
+
// `isIP` just agreed was an address.
|
|
68
|
+
.replace(/%.*$/, '')
|
|
69
|
+
// 'localhost.' is the same name as 'localhost' — the root label is what
|
|
70
|
+
// makes it fully qualified, not a different host.
|
|
71
|
+
.replace(/\.+$/, ''));
|
|
72
|
+
}
|
|
73
|
+
/** Expands an IPv6 literal into its eight 16-bit groups. */
|
|
74
|
+
function expandIpv6(address) {
|
|
75
|
+
let text = address;
|
|
76
|
+
// A literal may end in dotted-quad notation (::ffff:127.0.0.1). Fold that tail
|
|
77
|
+
// into the two hex groups it stands for so the rest of this is uniform.
|
|
78
|
+
const dotted = /:(\d{1,3}(?:\.\d{1,3}){3})$/.exec(text);
|
|
79
|
+
if (dotted !== null) {
|
|
80
|
+
const [a = 0, b = 0, c = 0, d = 0] = (dotted[1] ?? '')
|
|
81
|
+
.split('.')
|
|
82
|
+
.map(Number);
|
|
83
|
+
const high = ((a << 8) | b).toString(16);
|
|
84
|
+
const low = ((c << 8) | d).toString(16);
|
|
85
|
+
text = `${text.slice(0, dotted.index)}:${high}:${low}`;
|
|
86
|
+
}
|
|
87
|
+
const [head = '', tail] = text.split('::');
|
|
88
|
+
const left = head === '' ? [] : head.split(':');
|
|
89
|
+
const right = tail === undefined ? null : tail === '' ? [] : tail.split(':');
|
|
90
|
+
if (right === null)
|
|
91
|
+
return toGroups(left);
|
|
92
|
+
const missing = 8 - left.length - right.length;
|
|
93
|
+
if (missing < 0)
|
|
94
|
+
return null;
|
|
95
|
+
return toGroups([...left, ...Array(missing).fill('0'), ...right]);
|
|
96
|
+
}
|
|
97
|
+
function toGroups(groups) {
|
|
98
|
+
if (groups.length !== 8)
|
|
99
|
+
return null;
|
|
100
|
+
// Tested rather than left to `parseInt`, which stops at the first character it
|
|
101
|
+
// does not like and returns a number for '7f00xyz' just as happily.
|
|
102
|
+
if (!groups.every((group) => /^[0-9a-f]{1,4}$/.test(group)))
|
|
103
|
+
return null;
|
|
104
|
+
return groups.map((group) => parseInt(group, 16));
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Extracts the IPv4 address an IPv6 literal carries, for the prefixes whose
|
|
108
|
+
* whole purpose is to stand in for one.
|
|
109
|
+
*/
|
|
110
|
+
function embeddedIpv4(groups) {
|
|
111
|
+
const [a = 0, b = 0, c = 0, d = 0, e = 0, f = 0, g = 0, h = 0] = groups;
|
|
112
|
+
const carriesIpv4 =
|
|
113
|
+
// ::a.b.c.d (IPv4-compatible) and ::ffff:a.b.c.d (IPv4-mapped, RFC 4291).
|
|
114
|
+
((a | b | c | d | e) === 0 && (f === 0 || f === 0xffff)) ||
|
|
115
|
+
// ::ffff:0:a.b.c.d — IPv4-translated (RFC 2765).
|
|
116
|
+
((a | b | c | d) === 0 && e === 0xffff && f === 0) ||
|
|
117
|
+
// 64:ff9b::a.b.c.d — the well-known NAT64 prefix (RFC 6052).
|
|
118
|
+
(a === 0x64 && b === 0xff9b && (c | d | e | f) === 0);
|
|
119
|
+
return carriesIpv4 ? [g >> 8, g & 0xff, h >> 8, h & 0xff] : null;
|
|
120
|
+
}
|
|
121
|
+
function ipv4Kind(octets) {
|
|
122
|
+
const [a = 0, b = 0] = octets;
|
|
123
|
+
// 0.0.0.0/8 ('this host', RFC 1122) and 127/8 both reach the machine itself.
|
|
124
|
+
if (a === 0 || a === 127)
|
|
125
|
+
return 'loopback';
|
|
126
|
+
// 169.254/16, which holds 169.254.169.254 — the AWS/GCP/Azure metadata service.
|
|
127
|
+
if (a === 169 && b === 254)
|
|
128
|
+
return 'link-local';
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
function ipv6Kind(groups) {
|
|
132
|
+
// `::` and `::1` are also caught by the IPv4-compatible unwrapping below, but
|
|
133
|
+
// they are named here so that removing that unwrapping cannot silently take
|
|
134
|
+
// the two most obvious loopback literals with it.
|
|
135
|
+
if (groups.every((group) => group === 0))
|
|
136
|
+
return 'loopback'; // ::
|
|
137
|
+
if (groups.slice(0, 7).every((group) => group === 0) && groups[7] === 1) {
|
|
138
|
+
return 'loopback'; // ::1
|
|
139
|
+
}
|
|
140
|
+
if (((groups[0] ?? 0) & 0xffc0) === 0xfe80)
|
|
141
|
+
return 'link-local'; // fe80::/10
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=hosts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hosts.js","sourceRoot":"","sources":["../src/hosts.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "audiobookshelf-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"mcpName": "io.github.ni-c/audiobookshelf-mcp",
|
|
5
5
|
"description": "MCP server for Audiobookshelf, the self-hosted audiobook and podcast server",
|
|
6
6
|
"keywords": [
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"url": "https://github.com/ni-c/audiobookshelf-mcp/issues"
|
|
22
22
|
},
|
|
23
23
|
"homepage": "https://github.com/ni-c/audiobookshelf-mcp#readme",
|
|
24
|
+
"funding": "https://github.com/sponsors/ni-c",
|
|
24
25
|
"type": "module",
|
|
25
26
|
"bin": {
|
|
26
27
|
"audiobookshelf-mcp": "dist/index.js"
|
|
@@ -54,7 +55,7 @@
|
|
|
54
55
|
"@eslint/js": "^10.0.1",
|
|
55
56
|
"@resvg/resvg-js": "^2.6.2",
|
|
56
57
|
"@types/node": "^26.2.0",
|
|
57
|
-
"@vitest/coverage-v8": "4.1.
|
|
58
|
+
"@vitest/coverage-v8": "4.1.11",
|
|
58
59
|
"eslint": "^10.8.1",
|
|
59
60
|
"prettier": "^3.9.6",
|
|
60
61
|
"typescript": "^6.0.3",
|