@oliverames/mcp-server-for-wave 1.0.0 → 1.0.2
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 +12 -6
- package/docs/hosted-oauth-connector.md +13 -0
- package/index.js +31 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -359,10 +359,14 @@ the live schema.
|
|
|
359
359
|
|
|
360
360
|
## Hosted Connector
|
|
361
361
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
362
|
+
A Cloudflare Worker serves the same tools over OAuth instead of a shared token.
|
|
363
|
+
Users authorize against their own Wave account, tokens are encrypted before
|
|
364
|
+
storage, and write access is chosen at authorization time so a read-only
|
|
365
|
+
connection cannot be escalated later.
|
|
366
|
+
|
|
367
|
+
The deployment at **https://wave.amesvt.com/mcp** is private: an owner
|
|
368
|
+
allowlist restricts it to one Wave account, and any other account is refused
|
|
369
|
+
before a token is stored. Deploy your own copy from `worker/` to use it.
|
|
366
370
|
|
|
367
371
|
See [worker/README.md](worker/README.md) for setup and the security model.
|
|
368
372
|
|
|
@@ -375,11 +379,12 @@ index.js Single-file server: client, 74 tools, 7 resources
|
|
|
375
379
|
scripts/
|
|
376
380
|
smoke-validate-graphql.mjs Schema-check every query against live Wave
|
|
377
381
|
smoke-list-tools.mjs Start over stdio and enumerate what is advertised
|
|
382
|
+
smoke-packed-install.mjs Pack, install, and launch the way npx does
|
|
378
383
|
sync-plugin-metadata.mjs Propagate the version to every host manifest
|
|
379
384
|
check-release-consistency Fail the build when anything disagrees
|
|
380
385
|
build-mcpb.mjs Desktop bundle
|
|
381
386
|
worker/ Hosted OAuth connector
|
|
382
|
-
test/unit.test.mjs
|
|
387
|
+
test/unit.test.mjs 58 tests, no network
|
|
383
388
|
```
|
|
384
389
|
|
|
385
390
|
The tool layer lives in one file on purpose. It is imported unchanged by the
|
|
@@ -398,8 +403,9 @@ at all, which catches a field Wave renames before a user does.
|
|
|
398
403
|
|
|
399
404
|
```bash
|
|
400
405
|
npm install
|
|
401
|
-
npm test #
|
|
406
|
+
npm test # 58 unit tests, no network
|
|
402
407
|
npm run smoke:list-tools # start over stdio, enumerate tools
|
|
408
|
+
npm run smoke:packed # pack, install, and launch via the bin symlink
|
|
403
409
|
npm run smoke:schema # validate every query against live Wave
|
|
404
410
|
npm run release:check # version parity across 8 manifests
|
|
405
411
|
npm run build:mcpb # desktop bundle
|
|
@@ -24,6 +24,19 @@ server is simpler and has a smaller surface. The hosted connector earns its
|
|
|
24
24
|
complexity when more than one person needs access, or when your client cannot
|
|
25
25
|
launch a subprocess.
|
|
26
26
|
|
|
27
|
+
## Who may connect
|
|
28
|
+
|
|
29
|
+
A hosted connector is reachable by anyone who learns its URL, so it carries an
|
|
30
|
+
owner allowlist. `ALLOWED_WAVE_USERS` names the Wave accounts, by user id or
|
|
31
|
+
account email, permitted to hold a connection. Anyone else who authorizes at
|
|
32
|
+
Wave is refused at `/callback` before a token is stored, and the check runs
|
|
33
|
+
again on every MCP session, so removing an entry ends a connection that already
|
|
34
|
+
exists rather than only blocking new ones.
|
|
35
|
+
|
|
36
|
+
The deployment at `wave.amesvt.com` is private and allows a single account. A
|
|
37
|
+
copy you self-host should name your own account, or leave the variable empty to
|
|
38
|
+
accept any Wave account.
|
|
39
|
+
|
|
27
40
|
## Access levels
|
|
28
41
|
|
|
29
42
|
Write access is chosen during authorization, not afterwards. A read-only
|
package/index.js
CHANGED
|
@@ -29,7 +29,7 @@ import { z } from "zod";
|
|
|
29
29
|
// import-time config resolution and the stdio autostart are both skipped.
|
|
30
30
|
const IS_CLOUDFLARE_WORKERS = globalThis.navigator?.userAgent === "Cloudflare-Workers";
|
|
31
31
|
|
|
32
|
-
const SERVER_VERSION = "1.0.
|
|
32
|
+
const SERVER_VERSION = "1.0.2";
|
|
33
33
|
const WAVE_ENDPOINT = "https://gql.waveapps.com/graphql/public";
|
|
34
34
|
const WAVE_API_HOST = "gql.waveapps.com";
|
|
35
35
|
const MAX_TOKEN_FILE_BYTES = 4096;
|
|
@@ -312,6 +312,19 @@ function readOnePasswordSecret(reference) {
|
|
|
312
312
|
return token;
|
|
313
313
|
}
|
|
314
314
|
|
|
315
|
+
/**
|
|
316
|
+
* Strip credentials from text headed for stderr.
|
|
317
|
+
*
|
|
318
|
+
* The factory's sanitizeErrorMessage closes over the live token and is not
|
|
319
|
+
* reachable from process-level handlers, so this covers the header forms that
|
|
320
|
+
* show up in a stack trace.
|
|
321
|
+
*/
|
|
322
|
+
function redactTokens(value) {
|
|
323
|
+
return String(value ?? "")
|
|
324
|
+
.replace(/Bearer\s+[A-Za-z0-9._~+/=-]+/gi, "Bearer [REDACTED_TOKEN]")
|
|
325
|
+
.replace(/Authorization:\s*[^\r\n]+/gi, "Authorization: [REDACTED_TOKEN]");
|
|
326
|
+
}
|
|
327
|
+
|
|
315
328
|
function detectAgent() {
|
|
316
329
|
const env = globalThis.process?.env ?? {};
|
|
317
330
|
if (env.CLAUDECODE || env.CLAUDE_CODE_ENTRYPOINT) return "claude-code";
|
|
@@ -5774,6 +5787,7 @@ export const __testables = {
|
|
|
5774
5787
|
extractFromJsonMcpConfig,
|
|
5775
5788
|
pickRuntimeKeys,
|
|
5776
5789
|
isDirectRun,
|
|
5790
|
+
redactTokens,
|
|
5777
5791
|
};
|
|
5778
5792
|
|
|
5779
5793
|
// --- Start ---
|
|
@@ -5808,9 +5822,24 @@ function isDirectRun() {
|
|
|
5808
5822
|
}
|
|
5809
5823
|
}
|
|
5810
5824
|
|
|
5825
|
+
if (!IS_CLOUDFLARE_WORKERS) {
|
|
5826
|
+
// Without these, an unhandled rejection takes the process down with no
|
|
5827
|
+
// usable output, and the MCP client reports only a dead server. Sanitize
|
|
5828
|
+
// first: a stack can carry the access token.
|
|
5829
|
+
globalThis.process.on("uncaughtException", (error) => {
|
|
5830
|
+
console.error(`Uncaught exception: ${redactTokens(error?.stack || error)}`);
|
|
5831
|
+
globalThis.process.exit(1);
|
|
5832
|
+
});
|
|
5833
|
+
|
|
5834
|
+
globalThis.process.on("unhandledRejection", (reason) => {
|
|
5835
|
+
console.error(`Unhandled promise rejection: ${redactTokens(reason?.stack || reason)}`);
|
|
5836
|
+
globalThis.process.exit(1);
|
|
5837
|
+
});
|
|
5838
|
+
}
|
|
5839
|
+
|
|
5811
5840
|
if (isDirectRun()) {
|
|
5812
5841
|
main().catch((error) => {
|
|
5813
|
-
console.error(`Fatal error starting MCP Server for Wave: ${error?.message ?? error}`);
|
|
5842
|
+
console.error(`Fatal error starting MCP Server for Wave: ${redactTokens(error?.message ?? error)}`);
|
|
5814
5843
|
globalThis.process.exit(1);
|
|
5815
5844
|
});
|
|
5816
5845
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oliverames/mcp-server-for-wave",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Local MCP server for Wave Accounting: invoices, estimates, customers, products, taxes, and bookkeeping",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"presmoke:packed": "npm run deps:ensure",
|
|
50
50
|
"smoke:packed": "node scripts/smoke-packed-install.mjs",
|
|
51
51
|
"sync:plugin": "node scripts/sync-plugin-metadata.mjs",
|
|
52
|
-
"version": "npm run sync:plugin && git add .agents/plugins/marketplace.json .claude-plugin/marketplace.json .claude-plugin/plugin.json codex/.codex-plugin/plugin.json codex/.codex-plugin/mcp.json .hermes-plugin/marketplace.json .hermes-plugin/plugin.json .hermes-plugin/mcp.json .antigravity-plugin/marketplace.json .antigravity-plugin/plugin.json .antigravity-plugin/mcp_config.json .mcp.json",
|
|
52
|
+
"version": "npm run sync:plugin && git add index.js .agents/plugins/marketplace.json .claude-plugin/marketplace.json .claude-plugin/plugin.json codex/.codex-plugin/plugin.json codex/.codex-plugin/mcp.json .hermes-plugin/marketplace.json .hermes-plugin/plugin.json .hermes-plugin/mcp.json .antigravity-plugin/marketplace.json .antigravity-plugin/plugin.json .antigravity-plugin/mcp_config.json .mcp.json",
|
|
53
53
|
"release:check": "node scripts/check-release-consistency.mjs",
|
|
54
54
|
"release:check:registry": "node scripts/check-release-consistency.mjs --registry",
|
|
55
55
|
"build:mcpb": "node scripts/build-mcpb.mjs"
|