soundiiz-mcp 0.1.0
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/CHANGELOG.md +43 -0
- package/LICENSE +21 -0
- package/README.md +228 -0
- package/assets/logo.svg +42 -0
- package/dist/bin/cli.js +4 -0
- package/dist/bin/cli.js.map +1 -0
- package/dist/package.json +90 -0
- package/dist/src/auth/index.js +42 -0
- package/dist/src/auth/index.js.map +1 -0
- package/dist/src/auth/keyStore.js +104 -0
- package/dist/src/auth/keyStore.js.map +1 -0
- package/dist/src/config/defaults.json +45 -0
- package/dist/src/config/index.js +290 -0
- package/dist/src/config/index.js.map +1 -0
- package/dist/src/core/client.js +175 -0
- package/dist/src/core/client.js.map +1 -0
- package/dist/src/core/spec.js +117 -0
- package/dist/src/core/spec.js.map +1 -0
- package/dist/src/index.js +35 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/infra/logger.js +23 -0
- package/dist/src/infra/logger.js.map +1 -0
- package/dist/src/services/allowlist.js +32 -0
- package/dist/src/services/allowlist.js.map +1 -0
- package/dist/src/services/cache.js +43 -0
- package/dist/src/services/cache.js.map +1 -0
- package/dist/src/services/confirmations.js +45 -0
- package/dist/src/services/confirmations.js.map +1 -0
- package/dist/src/services/rateLimit.js +43 -0
- package/dist/src/services/rateLimit.js.map +1 -0
- package/dist/src/services/smartlinks/index.js +46 -0
- package/dist/src/services/smartlinks/index.js.map +1 -0
- package/dist/src/services/syncs/index.js +56 -0
- package/dist/src/services/syncs/index.js.map +1 -0
- package/dist/src/services/users/index.js +10 -0
- package/dist/src/services/users/index.js.map +1 -0
- package/dist/src/tools/auth.js +29 -0
- package/dist/src/tools/auth.js.map +1 -0
- package/dist/src/tools/cache.js +15 -0
- package/dist/src/tools/cache.js.map +1 -0
- package/dist/src/tools/curated/smartlinks.js +140 -0
- package/dist/src/tools/curated/smartlinks.js.map +1 -0
- package/dist/src/tools/curated/syncs.js +232 -0
- package/dist/src/tools/curated/syncs.js.map +1 -0
- package/dist/src/tools/curated/users.js +45 -0
- package/dist/src/tools/curated/users.js.map +1 -0
- package/dist/src/tools/generated.js +80 -0
- package/dist/src/tools/generated.js.map +1 -0
- package/dist/src/tools/raw.js +41 -0
- package/dist/src/tools/raw.js.map +1 -0
- package/dist/src/tools/registerAllTools.js +26 -0
- package/dist/src/tools/registerAllTools.js.map +1 -0
- package/dist/src/utils/stableStringify.js +19 -0
- package/dist/src/utils/stableStringify.js.map +1 -0
- package/dist/src/utils/toolNames.js +13 -0
- package/dist/src/utils/toolNames.js.map +1 -0
- package/dist/src/utils/toolResponses.js +15 -0
- package/dist/src/utils/toolResponses.js.map +1 -0
- package/docs/architecture.md +100 -0
- package/docs/curated-tools.md +88 -0
- package/docs/design-notes.md +79 -0
- package/docs/evals.md +79 -0
- package/docs/tools-guide.md +56 -0
- package/package.json +90 -0
- package/specs/soundiiz-openapi.json +1351 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
4
|
+
|
|
5
|
+
## [0.1.0] — Unreleased
|
|
6
|
+
|
|
7
|
+
Initial release.
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- MCP server for the [Soundiiz User API (BETA)](https://soundiiz.com/api/doc).
|
|
12
|
+
- Vendored OpenAPI spec at `specs/soundiiz-openapi.json` (refresh with `npm run sync:spec`).
|
|
13
|
+
- Bearer auth via `SOUNDIIZ_API_KEY`, with three key stores: env, on-disk file (mode 0600), or OS keychain (`keytar`).
|
|
14
|
+
- Curated tools:
|
|
15
|
+
- User: `soundiiz_me`, `soundiiz_auth_status`, `soundiiz_auth_set`, `soundiiz_auth_clear`.
|
|
16
|
+
- Syncs (read): `soundiiz_syncs_list`, `soundiiz_sync_get`, `soundiiz_syncs_overview`, `soundiiz_syncs_due`.
|
|
17
|
+
- Syncs (write): `soundiiz_sync_trigger`, `soundiiz_sync_delete`.
|
|
18
|
+
- SmartLinks (read): `soundiiz_smartlinks_list`, `soundiiz_smartlink_get`, `soundiiz_smartlinks_overview`.
|
|
19
|
+
- SmartLinks (write): `soundiiz_smartlink_delete`.
|
|
20
|
+
- Local: `soundiiz_cache_invalidate`.
|
|
21
|
+
- Auto-generated `soundiiz_read_<operationId>` and `soundiiz_write_<operationId>` tools from every operation in the spec.
|
|
22
|
+
- Opt-in raw layer: `soundiiz_call`, `soundiiz_list_operations` (enable with `rawTools.enabled=true`).
|
|
23
|
+
- Confirmation-token flow on `soundiiz_sync_trigger`, `soundiiz_sync_delete`, `soundiiz_smartlink_delete` — the first call returns a `confirmId`; the second call (same args + `confirmId`) executes. Tokens are bound to tool name + arg hash and expire after `writes.confirmTtlMs` (120s default).
|
|
24
|
+
- Per-resource allowlists: `syncs.allowlist`, `smartlinks.allowlist`.
|
|
25
|
+
- Token-bucket rate limiter (60 req/min default, configurable).
|
|
26
|
+
- TTL cache with explicit invalidation per scope (`me`, `syncsList`, `syncDetail`, `smartlinksList`, `smartlinkDetail`).
|
|
27
|
+
- Local harness scripts: `mcp:status`, `mcp:list-tools`, `mcp:call`, `smoke:live`, `sync:spec`, `generate:tools-docs`.
|
|
28
|
+
- 25 Vitest cases covering spec parsing, tool naming, cache, confirmation tokens, allowlist, rate limiter, and a full mock-server-driven service E2E.
|
|
29
|
+
|
|
30
|
+
### Configuration defaults
|
|
31
|
+
|
|
32
|
+
- `writes.allow = true` — writes are enabled. Set to `false` (or `SOUNDIIZ_MCP_ALLOW_WRITES=false`) to lock the server to GET-only.
|
|
33
|
+
- `writes.confirmDestructive = true` — destructive operations require the confirmation flow. Disable for trusted automation.
|
|
34
|
+
- `auth.keyStore = env` — read API key from `SOUNDIIZ_API_KEY`. Switch to `file` or `keychain` for persistence.
|
|
35
|
+
|
|
36
|
+
### Known gaps
|
|
37
|
+
|
|
38
|
+
- The Soundiiz User API is currently in BETA (v1.0). The publisher explicitly warns that the surface may change. Track changes by re-running `npm run sync:spec`.
|
|
39
|
+
- The Soundiiz "exports" endpoints announced in the May 2026 changelog are not in the current spec; the `src/services/exports/` placeholder is reserved.
|
|
40
|
+
- This release has been validated against the mock server (25 passing tests) but **has not been live-validated against a real Soundiiz Creator-plan account by the maintainer**. If you're an early adopter, `npm run smoke:live` is the first thing to run, and please file an issue if any endpoint behaves differently than the spec suggests.
|
|
41
|
+
- The Soundiiz User API requires a [Creator-plan](https://soundiiz.com/pricing) subscription. The webapp's internal API was evaluated but rejected as a backend ([Soundiiz ToS Article 15(f)](https://soundiiz.com/data/termsofservice/Terms_of_Service_SOUNDIIZ20211123EN.pdf) prohibits programmatic use outside the documented API).
|
|
42
|
+
|
|
43
|
+
[0.1.0]: https://github.com/BASIC-BIT/soundiiz-mcp/releases/tag/v0.1.0
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 BASIC-BIT
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="assets/logo.svg" alt="Soundiiz MCP logo" width="112" height="112" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
# Soundiiz MCP
|
|
6
|
+
|
|
7
|
+
An MCP server for [Soundiiz](https://soundiiz.com). Lets your AI assistant inspect your sync jobs and SmartLinks across streaming services, trigger syncs, and clean up stale links.
|
|
8
|
+
|
|
9
|
+
Built for [Claude Desktop](https://claude.ai/download), [OpenCode](https://opencode.ai/), and any other [Model Context Protocol](https://modelcontextprotocol.io/) client.
|
|
10
|
+
|
|
11
|
+
Your API key stays on your machine. Curated tools on top of the Soundiiz User API so agents don't have to paginate through raw endpoints.
|
|
12
|
+
|
|
13
|
+
This project is unofficial and is not affiliated with Soundiiz.
|
|
14
|
+
|
|
15
|
+
## Status
|
|
16
|
+
|
|
17
|
+
`v0.1.0` — early. The Soundiiz User API is itself in BETA, so the surface this server wraps may shift. The 25-test mock suite exercises every curated tool, but the maintainer hasn't run it against a real Creator-plan account in the wild — if you're an early adopter, please try `npm run smoke:live` and file an issue if anything breaks.
|
|
18
|
+
|
|
19
|
+
## What You Can Ask
|
|
20
|
+
|
|
21
|
+
- Show me all my Soundiiz syncs and which ones are due to run next.
|
|
22
|
+
- Summarize sync status: how many succeeded, how many failed, which platforms are involved.
|
|
23
|
+
- Which syncs failed recently, and why?
|
|
24
|
+
- Trigger sync #42 to run now.
|
|
25
|
+
- List all my published SmartLinks and their shortcodes.
|
|
26
|
+
- Show details for SmartLink "abc123": fallback URL, per-platform links, status.
|
|
27
|
+
- Delete this stale draft SmartLink.
|
|
28
|
+
|
|
29
|
+
## Writes and confirmations
|
|
30
|
+
|
|
31
|
+
The Soundiiz API exposes three write operations: delete sync, delete smartlink, and trigger sync. All three are callable, but each goes through a two-step confirm flow:
|
|
32
|
+
|
|
33
|
+
1. The first call returns a `confirmId` and a recap of what would happen.
|
|
34
|
+
2. Re-call the same tool with the same args plus that `confirmId` to execute. The token is single-use and bound to the tool name + arg hash.
|
|
35
|
+
|
|
36
|
+
Knobs:
|
|
37
|
+
|
|
38
|
+
- `writes.confirmDestructive = false` — skip the confirm step (one-call writes).
|
|
39
|
+
- `writes.allow = false` (or `SOUNDIIZ_MCP_ALLOW_WRITES=false`) — disable writes entirely.
|
|
40
|
+
- `syncs.allowlist` / `smartlinks.allowlist` — restrict writes to specific IDs.
|
|
41
|
+
|
|
42
|
+
## Auth and storage
|
|
43
|
+
|
|
44
|
+
Your API key is read from `SOUNDIIZ_API_KEY`, a local file, or the OS keychain (via `keytar`). It never leaves your machine except in `Authorization: Bearer` headers to `api.soundiiz.com`.
|
|
45
|
+
|
|
46
|
+
Logs go to stderr so stdout stays reserved for MCP protocol messages. Live smoke checks and LLM evals are opt-in and read gitignored fixture files.
|
|
47
|
+
|
|
48
|
+
## Quick Start
|
|
49
|
+
|
|
50
|
+
Requirements:
|
|
51
|
+
|
|
52
|
+
- Node.js 22 or newer.
|
|
53
|
+
- A Soundiiz account on the **Creator plan**. The Soundiiz User API is currently in BETA and gated to Creator subscribers.
|
|
54
|
+
- An MCP client such as Claude Desktop, OpenCode, or another MCP-compatible host.
|
|
55
|
+
|
|
56
|
+
Generate your personal API key at [soundiiz.com/webapp/settings/api](https://soundiiz.com/webapp/settings/api).
|
|
57
|
+
|
|
58
|
+
Install from source:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
git clone https://github.com/BASIC-BIT/soundiiz-mcp.git
|
|
62
|
+
cd soundiiz-mcp
|
|
63
|
+
npm install
|
|
64
|
+
npm run build
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## MCP Client Config
|
|
68
|
+
|
|
69
|
+
Use the built server for day-to-day use. Replace the path with your local checkout.
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"mcpServers": {
|
|
74
|
+
"soundiiz": {
|
|
75
|
+
"command": "node",
|
|
76
|
+
"args": ["<ABS_PATH_TO_REPO>/dist/bin/cli.js"],
|
|
77
|
+
"env": {
|
|
78
|
+
"SOUNDIIZ_API_KEY": "<YOUR_KEY>",
|
|
79
|
+
"SOUNDIIZ_MCP_USER_AGENT": "your-name (email@example.com)"
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
For active development, point at the TypeScript entrypoint instead:
|
|
87
|
+
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"mcpServers": {
|
|
91
|
+
"soundiiz-dev": {
|
|
92
|
+
"command": "npx",
|
|
93
|
+
"args": ["tsx", "<ABS_PATH_TO_REPO>/src/index.ts"],
|
|
94
|
+
"env": {
|
|
95
|
+
"SOUNDIIZ_API_KEY": "<YOUR_KEY>",
|
|
96
|
+
"SOUNDIIZ_MCP_USER_AGENT": "your-name (email@example.com)"
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Configuration
|
|
104
|
+
|
|
105
|
+
Defaults live in `src/config/defaults.json`. To override them, create a JSON config file and point to it with `SOUNDIIZ_MCP_CONFIG_FILE`.
|
|
106
|
+
|
|
107
|
+
Example `soundiiz-mcp.config.json`:
|
|
108
|
+
|
|
109
|
+
```json
|
|
110
|
+
{
|
|
111
|
+
"api": {
|
|
112
|
+
"baseUrl": "https://api.soundiiz.com",
|
|
113
|
+
"userAgent": "your-name (email@example.com)"
|
|
114
|
+
},
|
|
115
|
+
"auth": { "keyStore": "env" },
|
|
116
|
+
"writes": { "allow": false, "confirmDestructive": true, "confirmTtlMs": 120000 },
|
|
117
|
+
"syncs": { "allowlist": [] },
|
|
118
|
+
"smartlinks": { "allowlist": [] },
|
|
119
|
+
"rateLimit": { "perMinute": 60 },
|
|
120
|
+
"cache": { "enabled": true }
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Environment variables override the config file when set.
|
|
125
|
+
|
|
126
|
+
Common environment variables:
|
|
127
|
+
|
|
128
|
+
- `SOUNDIIZ_MCP_CONFIG_FILE`: path to a JSON config file.
|
|
129
|
+
- `SOUNDIIZ_API_KEY`: your personal Soundiiz User API key (Bearer token).
|
|
130
|
+
- `SOUNDIIZ_MCP_USER_AGENT`: descriptive user agent. Include contact info when possible.
|
|
131
|
+
- `SOUNDIIZ_MCP_API_BASE`: override the API base URL. Defaults to `https://api.soundiiz.com`.
|
|
132
|
+
- `SOUNDIIZ_MCP_LOG_LEVEL`: `debug`, `info`, `warn`, or `error`.
|
|
133
|
+
- `SOUNDIIZ_MCP_KEY_STORE`: `env`, `file`, or `keychain`.
|
|
134
|
+
- `SOUNDIIZ_MCP_KEY_FILE`: file path when `SOUNDIIZ_MCP_KEY_STORE=file`.
|
|
135
|
+
- `SOUNDIIZ_MCP_ALLOW_WRITES`: enable non-GET operations.
|
|
136
|
+
- `SOUNDIIZ_MCP_CONFIRM_DESTRUCTIVE`: require a confirmation token for DELETE / trigger.
|
|
137
|
+
- `SOUNDIIZ_MCP_SYNC_ALLOWLIST`: comma-separated list of sync IDs permitted for write actions.
|
|
138
|
+
- `SOUNDIIZ_MCP_SMARTLINK_ALLOWLIST`: comma-separated list of smartlink IDs permitted for write actions.
|
|
139
|
+
- `SOUNDIIZ_MCP_ENABLE_RAW_CALL`: enable the raw `soundiiz_call` tool. Disabled by default.
|
|
140
|
+
- `SOUNDIIZ_MCP_DISABLE_GENERATED_READ_TOOLS`: disable auto-generated read tools.
|
|
141
|
+
- `SOUNDIIZ_MCP_DISABLE_GENERATED_WRITE_TOOLS`: disable auto-generated write tools.
|
|
142
|
+
|
|
143
|
+
## Tool Surface
|
|
144
|
+
|
|
145
|
+
Soundiiz MCP exposes three layers (mirroring the vrchat-mcp pattern):
|
|
146
|
+
|
|
147
|
+
- **Curated tools** for common agent workflows: `soundiiz_me`, `soundiiz_syncs_list`, `soundiiz_syncs_overview`, `soundiiz_syncs_due`, `soundiiz_sync_get`, `soundiiz_smartlinks_list`, `soundiiz_smartlinks_overview`, `soundiiz_smartlink_get`, `soundiiz_sync_trigger`, `soundiiz_sync_delete`, `soundiiz_smartlink_delete`.
|
|
148
|
+
- **Auto-generated read tools** named `soundiiz_read_<operationId>` for GET operations from the Soundiiz OpenAPI spec.
|
|
149
|
+
- **Auto-generated write tools** named `soundiiz_write_<operationId>` for non-GET operations.
|
|
150
|
+
|
|
151
|
+
Local-only tools include:
|
|
152
|
+
|
|
153
|
+
- `soundiiz_auth_status` — check whether a key is loaded and valid (calls `/v1/me`).
|
|
154
|
+
- `soundiiz_cache_invalidate` for MCP-local cache control.
|
|
155
|
+
|
|
156
|
+
The generated catalog lives in `docs/tools.md`. The shorter usage guide lives in `docs/tools-guide.md`.
|
|
157
|
+
|
|
158
|
+
## Optional Swagger UI
|
|
159
|
+
|
|
160
|
+
If you want a Swagger UI proxy for the MCP tools, use `mcpo`:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
uvx mcpo --port 8000 --api-key "top-secret" -- node <ABS_PATH_TO_REPO>/dist/bin/cli.js
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Then open `http://localhost:8000/docs`.
|
|
167
|
+
|
|
168
|
+
## Development
|
|
169
|
+
|
|
170
|
+
Useful scripts:
|
|
171
|
+
|
|
172
|
+
- `npm run dev` — run `src/index.ts` through `tsx`.
|
|
173
|
+
- `npm run build` — type-check and emit to `dist/`.
|
|
174
|
+
- `npm run start` — run the built server from `dist/`.
|
|
175
|
+
- `npm run lint`, `npm run typecheck`, `npm test` — quality gates.
|
|
176
|
+
- `npm run check` — lint + typecheck + test.
|
|
177
|
+
- `npm run mcp:status` — check whether the configured key authenticates.
|
|
178
|
+
- `npm run mcp:list-tools`, `npm run mcp:call` — local harness.
|
|
179
|
+
- `npm run smoke:live` — opt-in live smoke matrix against the built server.
|
|
180
|
+
- `npm run sync:spec` — refetch the Soundiiz OpenAPI spec from `https://soundiiz.com/api/doc`.
|
|
181
|
+
- `npm run generate:schemas` — regenerate Zod schemas from `specs/soundiiz-openapi.json`.
|
|
182
|
+
- `npm run generate:tools-docs` — regenerate `docs/tools.md`.
|
|
183
|
+
|
|
184
|
+
Project layout (planned):
|
|
185
|
+
|
|
186
|
+
- `src/index.ts` — server bootstrap.
|
|
187
|
+
- `src/config/` — defaults and config loader.
|
|
188
|
+
- `src/auth/` — API key loading from env / file / keychain.
|
|
189
|
+
- `src/core/` — HTTP client, spec parser, generated tool registries.
|
|
190
|
+
- `src/services/` — domain services for syncs, smartlinks, user, cache.
|
|
191
|
+
- `src/schemas/` — shared Zod schemas for tool inputs and outputs.
|
|
192
|
+
- `src/generated/` — Zod schemas generated from the Soundiiz OpenAPI spec.
|
|
193
|
+
- `src/tools/` — MCP tool registration (curated + auto-generated + raw + auth + cache).
|
|
194
|
+
- `src/infra/` — logging.
|
|
195
|
+
- `src/utils/` — small helpers.
|
|
196
|
+
- `specs/soundiiz-openapi.json` — vendored copy of the Soundiiz User API spec.
|
|
197
|
+
- `docs/` — architecture, tool inventory, evals, design notes, launch plan.
|
|
198
|
+
|
|
199
|
+
## Testing And Evals
|
|
200
|
+
|
|
201
|
+
Local checks:
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
npm run check
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Live smoke checks are opt-in and require a Creator-plan API key:
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
npm run build
|
|
211
|
+
SOUNDIIZ_API_KEY=... npm run smoke:live
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Live E2E and LLM evals use gitignored local fixture files. See `docs/evals.md`.
|
|
215
|
+
|
|
216
|
+
## Documentation
|
|
217
|
+
|
|
218
|
+
- `docs/tools.md` — generated tool catalog with schemas.
|
|
219
|
+
- `docs/tools-guide.md` — short human guide for the tool surface.
|
|
220
|
+
- `docs/architecture.md` — codebase overview and data flow.
|
|
221
|
+
- `docs/curated-tools.md` — curated tool charter and risk tiers.
|
|
222
|
+
- `docs/evals.md` — smoke, LLM, and manual agent eval workflow.
|
|
223
|
+
- `docs/public-launch-plan.md` — release awareness, registry, and launch-channel plan.
|
|
224
|
+
- `docs/design-notes.md` — archived design notes and future-facing ideas.
|
|
225
|
+
|
|
226
|
+
## License
|
|
227
|
+
|
|
228
|
+
MIT.
|
package/assets/logo.svg
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" role="img" aria-labelledby="title desc">
|
|
2
|
+
<title id="title">Soundiiz MCP logo</title>
|
|
3
|
+
<desc id="desc">A central hub node connected by lines to four satellite nodes at compass points, on a dark rounded square.</desc>
|
|
4
|
+
<defs>
|
|
5
|
+
<linearGradient id="hub" x1="0" y1="0" x2="1" y2="1">
|
|
6
|
+
<stop offset="0" stop-color="#f472b6" />
|
|
7
|
+
<stop offset="0.55" stop-color="#a855f7" />
|
|
8
|
+
<stop offset="1" stop-color="#6366f1" />
|
|
9
|
+
</linearGradient>
|
|
10
|
+
<linearGradient id="line" x1="0" y1="0" x2="1" y2="1">
|
|
11
|
+
<stop offset="0" stop-color="#fbcfe8" stop-opacity="0.85" />
|
|
12
|
+
<stop offset="1" stop-color="#c7d2fe" stop-opacity="0.85" />
|
|
13
|
+
</linearGradient>
|
|
14
|
+
<radialGradient id="glow" cx="0.5" cy="0.5" r="0.5">
|
|
15
|
+
<stop offset="0" stop-color="#a855f7" stop-opacity="0.55" />
|
|
16
|
+
<stop offset="1" stop-color="#a855f7" stop-opacity="0" />
|
|
17
|
+
</radialGradient>
|
|
18
|
+
</defs>
|
|
19
|
+
<rect width="256" height="256" rx="56" fill="#0b0721" />
|
|
20
|
+
<circle cx="128" cy="128" r="78" fill="url(#glow)" />
|
|
21
|
+
<g stroke="url(#line)" stroke-width="9" stroke-linecap="round" fill="none">
|
|
22
|
+
<line x1="128" y1="128" x2="128" y2="32" />
|
|
23
|
+
<line x1="128" y1="128" x2="224" y2="128" />
|
|
24
|
+
<line x1="128" y1="128" x2="128" y2="224" />
|
|
25
|
+
<line x1="128" y1="128" x2="32" y2="128" />
|
|
26
|
+
</g>
|
|
27
|
+
<g fill="#f8fafc">
|
|
28
|
+
<circle cx="128" cy="32" r="16" />
|
|
29
|
+
<circle cx="224" cy="128" r="16" />
|
|
30
|
+
<circle cx="128" cy="224" r="16" />
|
|
31
|
+
<circle cx="32" cy="128" r="16" />
|
|
32
|
+
</g>
|
|
33
|
+
<g fill="#a855f7">
|
|
34
|
+
<circle cx="128" cy="32" r="6" />
|
|
35
|
+
<circle cx="224" cy="128" r="6" />
|
|
36
|
+
<circle cx="128" cy="224" r="6" />
|
|
37
|
+
<circle cx="32" cy="128" r="6" />
|
|
38
|
+
</g>
|
|
39
|
+
<circle cx="128" cy="128" r="34" fill="url(#hub)" />
|
|
40
|
+
<circle cx="128" cy="128" r="34" fill="none" stroke="#f8fafc" stroke-width="3" />
|
|
41
|
+
<circle cx="128" cy="128" r="10" fill="#f8fafc" />
|
|
42
|
+
</svg>
|
package/dist/bin/cli.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../bin/cli.ts"],"names":[],"mappings":";AACA,0CAA0C;AAC1C,OAAO,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "soundiiz-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"mcpName": "io.github.basic-bit/soundiiz-mcp",
|
|
5
|
+
"description": "MCP server for Soundiiz syncs and SmartLinks. Curated tools, auto-generated read/write layer from the OpenAPI spec, and confirmation-gated destructive operations.",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "dist/src/index.js",
|
|
8
|
+
"bin": {
|
|
9
|
+
"soundiiz-mcp": "dist/bin/cli.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"specs/soundiiz-openapi.json",
|
|
14
|
+
"assets/logo.svg",
|
|
15
|
+
"docs/architecture.md",
|
|
16
|
+
"docs/curated-tools.md",
|
|
17
|
+
"docs/design-notes.md",
|
|
18
|
+
"docs/evals.md",
|
|
19
|
+
"docs/tools-guide.md",
|
|
20
|
+
"CHANGELOG.md",
|
|
21
|
+
"README.md",
|
|
22
|
+
"LICENSE"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsc -p tsconfig.json",
|
|
26
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
27
|
+
"dev": "tsx src/index.ts",
|
|
28
|
+
"lint": "eslint . --ext .ts,.tsx,.mts --max-warnings=0",
|
|
29
|
+
"lint:fix": "npm run lint -- --fix",
|
|
30
|
+
"format": "prettier --write .",
|
|
31
|
+
"format:check": "prettier --check .",
|
|
32
|
+
"test": "vitest run",
|
|
33
|
+
"test:watch": "vitest",
|
|
34
|
+
"test:coverage": "vitest run --coverage",
|
|
35
|
+
"smoke:live": "tsx scripts/live-smoke.ts",
|
|
36
|
+
"check": "npm run lint && npm run typecheck && npm test",
|
|
37
|
+
"mcp:status": "tsx scripts/mcp-client.ts status",
|
|
38
|
+
"mcp:list-tools": "tsx scripts/mcp-client.ts list-tools",
|
|
39
|
+
"mcp:call": "tsx scripts/mcp-client.ts call",
|
|
40
|
+
"sync:spec": "tsx scripts/sync-spec.ts",
|
|
41
|
+
"generate:tools-docs": "tsx scripts/generate-tools-docs.ts",
|
|
42
|
+
"build:assets": "tsx scripts/build-assets.ts",
|
|
43
|
+
"mcpo": "uvx mcpo --port 8000 --api-key \"top-secret\" -- npx tsx src/index.ts",
|
|
44
|
+
"start": "node dist/bin/cli.js"
|
|
45
|
+
},
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=22"
|
|
48
|
+
},
|
|
49
|
+
"keywords": [
|
|
50
|
+
"soundiiz",
|
|
51
|
+
"mcp",
|
|
52
|
+
"modelcontextprotocol",
|
|
53
|
+
"model-context-protocol",
|
|
54
|
+
"claude",
|
|
55
|
+
"opencode",
|
|
56
|
+
"music",
|
|
57
|
+
"playlist",
|
|
58
|
+
"smartlinks",
|
|
59
|
+
"sync"
|
|
60
|
+
],
|
|
61
|
+
"repository": {
|
|
62
|
+
"type": "git",
|
|
63
|
+
"url": "git+https://github.com/BASIC-BIT/soundiiz-mcp.git"
|
|
64
|
+
},
|
|
65
|
+
"bugs": {
|
|
66
|
+
"url": "https://github.com/BASIC-BIT/soundiiz-mcp/issues"
|
|
67
|
+
},
|
|
68
|
+
"homepage": "https://github.com/BASIC-BIT/soundiiz-mcp#readme",
|
|
69
|
+
"license": "MIT",
|
|
70
|
+
"devDependencies": {
|
|
71
|
+
"@eslint/js": "^9.18.0",
|
|
72
|
+
"@types/node": "^22.10.5",
|
|
73
|
+
"@typescript-eslint/eslint-plugin": "^8.20.0",
|
|
74
|
+
"@typescript-eslint/parser": "^8.20.0",
|
|
75
|
+
"@vitest/coverage-v8": "^3.0.0",
|
|
76
|
+
"eslint": "^9.18.0",
|
|
77
|
+
"eslint-config-prettier": "^10.0.1",
|
|
78
|
+
"globals": "^15.14.0",
|
|
79
|
+
"prettier": "^3.4.2",
|
|
80
|
+
"tsx": "^4.19.2",
|
|
81
|
+
"typescript": "^5.7.3",
|
|
82
|
+
"vitest": "^3.0.0"
|
|
83
|
+
},
|
|
84
|
+
"dependencies": {
|
|
85
|
+
"@modelcontextprotocol/sdk": "^1.0.4",
|
|
86
|
+
"keytar": "^7.9.0",
|
|
87
|
+
"undici": "^7.2.0",
|
|
88
|
+
"zod": "^3.24.1"
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { getKeyStore } from './keyStore.js';
|
|
2
|
+
import { logger } from '../infra/logger.js';
|
|
3
|
+
class AuthManager {
|
|
4
|
+
store;
|
|
5
|
+
memKey = null;
|
|
6
|
+
constructor() {
|
|
7
|
+
this.store = getKeyStore();
|
|
8
|
+
}
|
|
9
|
+
async init() {
|
|
10
|
+
try {
|
|
11
|
+
this.memKey = await this.store.load();
|
|
12
|
+
if (this.memKey) {
|
|
13
|
+
logger.debug('Loaded Soundiiz API key', { length: this.memKey.length });
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
logger.warn('No Soundiiz API key found. Set SOUNDIIZ_API_KEY (env), or use file/keychain key store.');
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
catch (err) {
|
|
20
|
+
logger.warn('Failed to initialize key store', { message: err.message });
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
hasKey() {
|
|
24
|
+
return Boolean(this.memKey);
|
|
25
|
+
}
|
|
26
|
+
getKey() {
|
|
27
|
+
return this.memKey;
|
|
28
|
+
}
|
|
29
|
+
async setKey(key) {
|
|
30
|
+
const trimmed = key.trim();
|
|
31
|
+
if (!trimmed)
|
|
32
|
+
throw new Error('Empty API key');
|
|
33
|
+
this.memKey = trimmed;
|
|
34
|
+
await this.store.save(trimmed);
|
|
35
|
+
}
|
|
36
|
+
async clear() {
|
|
37
|
+
this.memKey = null;
|
|
38
|
+
await this.store.clear();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export const authManager = new AuthManager();
|
|
42
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAiB,MAAM,eAAe,CAAC;AAC3D,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C,MAAM,WAAW;IACP,KAAK,CAAW;IAChB,MAAM,GAAkB,IAAI,CAAC;IAErC;QACE,IAAI,CAAC,KAAK,GAAG,WAAW,EAAE,CAAC;IAC7B,CAAC;IAED,KAAK,CAAC,IAAI;QACR,IAAI,CAAC;YACH,IAAI,CAAC,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;YACtC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBAChB,MAAM,CAAC,KAAK,CAAC,yBAAyB,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;YAC1E,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,IAAI,CACT,wFAAwF,CACzF,CAAC;YACJ,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,IAAI,CAAC,gCAAgC,EAAE,EAAE,OAAO,EAAG,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QACrF,CAAC;IACH,CAAC;IAED,MAAM;QACJ,OAAO,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC9B,CAAC;IAED,MAAM;QACJ,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAW;QACtB,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;QAC3B,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;QAC/C,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC;QACtB,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACjC,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IAC3B,CAAC;CACF;AAED,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { getConfig } from '../config/index.js';
|
|
4
|
+
import { logger } from '../infra/logger.js';
|
|
5
|
+
class EnvStore {
|
|
6
|
+
load() {
|
|
7
|
+
const v = process.env.SOUNDIIZ_API_KEY?.trim();
|
|
8
|
+
return Promise.resolve(v ? v : null);
|
|
9
|
+
}
|
|
10
|
+
save() {
|
|
11
|
+
logger.warn('keyStore=env: save() is a no-op; set SOUNDIIZ_API_KEY in your environment.');
|
|
12
|
+
return Promise.resolve();
|
|
13
|
+
}
|
|
14
|
+
clear() {
|
|
15
|
+
logger.warn('keyStore=env: clear() is a no-op; unset SOUNDIIZ_API_KEY in your environment.');
|
|
16
|
+
return Promise.resolve();
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
class FileStore {
|
|
20
|
+
filePath;
|
|
21
|
+
constructor(filePath) {
|
|
22
|
+
this.filePath = filePath;
|
|
23
|
+
}
|
|
24
|
+
async load() {
|
|
25
|
+
try {
|
|
26
|
+
const content = await fs.readFile(this.filePath, 'utf8');
|
|
27
|
+
const trimmed = content.trim();
|
|
28
|
+
return trimmed || null;
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
if (err.code === 'ENOENT')
|
|
32
|
+
return null;
|
|
33
|
+
logger.warn('Failed to load key file', { message: err.message });
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async save(key) {
|
|
38
|
+
await fs.mkdir(path.dirname(this.filePath), { recursive: true });
|
|
39
|
+
await fs.writeFile(this.filePath, key, { encoding: 'utf8', mode: 0o600 });
|
|
40
|
+
if (process.platform !== 'win32') {
|
|
41
|
+
await fs.chmod(this.filePath, 0o600);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async clear() {
|
|
45
|
+
try {
|
|
46
|
+
await fs.unlink(this.filePath);
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
if (err.code !== 'ENOENT') {
|
|
50
|
+
logger.warn('Failed to delete key file', { message: err.message });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
class KeychainStore {
|
|
56
|
+
service = 'soundiiz-mcp';
|
|
57
|
+
account = 'default';
|
|
58
|
+
keytar = null;
|
|
59
|
+
async ensureKeytar() {
|
|
60
|
+
if (this.keytar)
|
|
61
|
+
return this.keytar;
|
|
62
|
+
try {
|
|
63
|
+
const mod = await import('keytar');
|
|
64
|
+
this.keytar = (mod.default ?? mod);
|
|
65
|
+
return this.keytar;
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
logger.warn('keytar not available; OS keychain disabled', {
|
|
69
|
+
message: err.message,
|
|
70
|
+
});
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async load() {
|
|
75
|
+
const keytar = await this.ensureKeytar();
|
|
76
|
+
if (!keytar)
|
|
77
|
+
return null;
|
|
78
|
+
const v = (await keytar.getPassword(this.service, this.account))?.trim();
|
|
79
|
+
return v ? v : null;
|
|
80
|
+
}
|
|
81
|
+
async save(key) {
|
|
82
|
+
const keytar = await this.ensureKeytar();
|
|
83
|
+
if (!keytar) {
|
|
84
|
+
throw new Error('keytar is not available; cannot save to OS keychain');
|
|
85
|
+
}
|
|
86
|
+
await keytar.setPassword(this.service, this.account, key);
|
|
87
|
+
}
|
|
88
|
+
async clear() {
|
|
89
|
+
const keytar = await this.ensureKeytar();
|
|
90
|
+
if (!keytar)
|
|
91
|
+
return;
|
|
92
|
+
await keytar.deletePassword(this.service, this.account);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
export function getKeyStore() {
|
|
96
|
+
const config = getConfig();
|
|
97
|
+
const mode = config.auth.keyStore;
|
|
98
|
+
if (mode === 'file')
|
|
99
|
+
return new FileStore(path.resolve(config.auth.keyFile));
|
|
100
|
+
if (mode === 'keychain')
|
|
101
|
+
return new KeychainStore();
|
|
102
|
+
return new EnvStore();
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=keyStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyStore.js","sourceRoot":"","sources":["../../../src/auth/keyStore.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"api": {
|
|
3
|
+
"baseUrl": "https://api.soundiiz.com",
|
|
4
|
+
"userAgent": "soundiiz-mcp/{version} (+https://github.com/BASIC-BIT/soundiiz-mcp)",
|
|
5
|
+
"timeoutMs": 15000
|
|
6
|
+
},
|
|
7
|
+
"auth": {
|
|
8
|
+
"keyStore": "env",
|
|
9
|
+
"keyFile": "~/.soundiiz-mcp-key"
|
|
10
|
+
},
|
|
11
|
+
"writes": {
|
|
12
|
+
"allow": true,
|
|
13
|
+
"confirmDestructive": true,
|
|
14
|
+
"confirmTtlMs": 120000
|
|
15
|
+
},
|
|
16
|
+
"syncs": {
|
|
17
|
+
"allowlist": []
|
|
18
|
+
},
|
|
19
|
+
"smartlinks": {
|
|
20
|
+
"allowlist": []
|
|
21
|
+
},
|
|
22
|
+
"rateLimit": {
|
|
23
|
+
"perMinute": 60
|
|
24
|
+
},
|
|
25
|
+
"cache": {
|
|
26
|
+
"enabled": true,
|
|
27
|
+
"ttls": {
|
|
28
|
+
"me": 3600,
|
|
29
|
+
"syncsList": 60,
|
|
30
|
+
"syncDetail": 30,
|
|
31
|
+
"smartlinksList": 300,
|
|
32
|
+
"smartlinkDetail": 300
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"rawTools": {
|
|
36
|
+
"enabled": false
|
|
37
|
+
},
|
|
38
|
+
"generatedTools": {
|
|
39
|
+
"read": true,
|
|
40
|
+
"write": true
|
|
41
|
+
},
|
|
42
|
+
"logging": {
|
|
43
|
+
"level": "info"
|
|
44
|
+
}
|
|
45
|
+
}
|