audiobookshelf-mcp 0.1.1 → 0.1.3

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
@@ -18,7 +18,15 @@ keep your listening progress, bookmarks, collections and playlists up to date.
18
18
 
19
19
  📖 **Full documentation: <https://audiobookshelf-mcp.ni-c.de>**
20
20
 
21
- <img src="https://audiobookshelf-mcp.ni-c.de/architecture.svg" alt="An MCP client talks to audiobookshelf-mcp over stdio; the server exposes 29 read and 15 write tools, compacts every response, and calls the Audiobookshelf REST API over HTTPS with a bearer API key" width="800">
21
+ <!-- <picture> is resolved against the colour scheme of the page showing it, so GitHub
22
+ picks the variant that matches its own theme toggle. npm strips <picture> and
23
+ <source> when it sanitises the README and keeps the <img>, which is why that
24
+ fallback brings its own dark card instead of relying on a media query. -->
25
+ <picture>
26
+ <source media="(prefers-color-scheme: dark)" srcset="https://audiobookshelf-mcp.ni-c.de/architecture-dark.svg">
27
+ <source media="(prefers-color-scheme: light)" srcset="https://audiobookshelf-mcp.ni-c.de/architecture-light.svg">
28
+ <img src="https://audiobookshelf-mcp.ni-c.de/architecture.svg" alt="An MCP client talks to audiobookshelf-mcp over stdio; the server exposes 29 read and 15 write tools, compacts every response, and calls the Audiobookshelf REST API over HTTPS with a bearer API key" width="800">
29
+ </picture>
22
30
 
23
31
  <img src="https://audiobookshelf-mcp.ni-c.de/demo.gif" alt="Terminal recording: the server reports 44 tools, lists library items as a compact projection, and answers the first delete_collection call with a single-use confirmation token instead of deleting anything" width="800">
24
32
 
package/dist/config.js CHANGED
@@ -27,6 +27,14 @@ export function loadConfig(env = process.env) {
27
27
  const apiKey = env.AUDIOBOOKSHELF_API_KEY;
28
28
  const insecureTls = env.AUDIOBOOKSHELF_INSECURE_TLS === 'true';
29
29
  const readOnly = env.AUDIOBOOKSHELF_READ_ONLY === 'true';
30
+ // Don't keep the key in the environment for the process lifetime — it is
31
+ // visible to child processes and in /proc/<pid>/environ. This happens before
32
+ // any branch on purpose: the paths below either exit or return early, and
33
+ // "the URL is missing or malformed" is exactly the state in which someone
34
+ // runs an inspector or trips a crash reporter, so it is the last moment the
35
+ // key should still be sitting in the environment. Everything after this point
36
+ // reads the locals above, never `env` again.
37
+ delete env.AUDIOBOOKSHELF_API_KEY;
30
38
  const missing = [
31
39
  !url && 'AUDIOBOOKSHELF_URL',
32
40
  !apiKey && 'AUDIOBOOKSHELF_API_KEY',
@@ -42,7 +50,11 @@ export function loadConfig(env = process.env) {
42
50
  parsed = new URL(url);
43
51
  }
44
52
  catch {
45
- console.error(`audiobookshelf-mcp: AUDIOBOOKSHELF_URL is not a valid URL: ${url}`);
53
+ // The value itself is not echoed: this branch fires precisely when the
54
+ // variable does not hold what was expected, and an API key pasted into the
55
+ // wrong environment variable would otherwise be printed verbatim into the
56
+ // MCP host's log.
57
+ console.error('audiobookshelf-mcp: AUDIOBOOKSHELF_URL is not a valid URL (e.g. https://abs.example.com)');
46
58
  process.exit(1);
47
59
  }
48
60
  if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
@@ -58,21 +70,21 @@ export function loadConfig(env = process.env) {
58
70
  console.error('audiobookshelf-mcp: WARNING: AUDIOBOOKSHELF_URL uses plain http to a non-local host — ' +
59
71
  'the API key will be sent unencrypted. Use https:// instead.');
60
72
  }
61
- const config = {
73
+ return {
62
74
  url: url.replace(/\/+$/, ''),
63
75
  apiKey,
64
76
  insecureTls,
65
77
  readOnly,
66
78
  };
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
79
  }
72
80
  function isLoopbackHost(hostname) {
73
- return (hostname === 'localhost' ||
74
- hostname.endsWith('.localhost') ||
75
- hostname.startsWith('127.') ||
76
- hostname === '::1');
81
+ // URL.hostname keeps the brackets around an IPv6 literal, so comparing against
82
+ // a bare '::1' never matches and the plain-http warning fires on a loopback
83
+ // URL written as http://[::1]:13378.
84
+ const host = hostname.replace(/^\[|\]$/g, '');
85
+ return (host === 'localhost' ||
86
+ host.endsWith('.localhost') ||
87
+ host.startsWith('127.') ||
88
+ host === '::1');
77
89
  }
78
90
  //# sourceMappingURL=config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.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.1",
3
+ "version": "0.1.3",
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": [
@@ -40,8 +40,10 @@
40
40
  "test": "vitest run",
41
41
  "test:coverage": "vitest run --coverage",
42
42
  "prepublishOnly": "npm run lint && npm test && npm run build",
43
+ "docs:tools": "node scripts/gen-tools-doc.mjs",
43
44
  "docs:tools:check": "node scripts/gen-tools-doc.mjs --check",
44
- "docs:tools": "node scripts/gen-tools-doc.mjs"
45
+ "assets": "node scripts/gen-assets.mjs",
46
+ "assets:check": "node scripts/gen-assets.mjs --check"
45
47
  },
46
48
  "dependencies": {
47
49
  "@modelcontextprotocol/sdk": "^1.30.0",
@@ -50,6 +52,7 @@
50
52
  },
51
53
  "devDependencies": {
52
54
  "@eslint/js": "^10.0.1",
55
+ "@resvg/resvg-js": "^2.6.2",
53
56
  "@types/node": "^26.2.0",
54
57
  "@vitest/coverage-v8": "4.1.10",
55
58
  "eslint": "^10.8.1",