tiny-http-mcp-server 0.1.28 → 0.1.29

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.
@@ -18,7 +18,7 @@
18
18
  },
19
19
  {
20
20
  "name": "tiny-http-mcp-server",
21
- "version": "0.1.28",
21
+ "version": "0.1.29",
22
22
  "license": "MIT"
23
23
  },
24
24
  {
@@ -56,6 +56,10 @@ directory or filesystem adapter. Injected encrypted-file adapters need
56
56
  - Configurable salt, directory, and file name
57
57
  - File permissions: `0600`
58
58
  - Random IV per write
59
+ - `fileStore.throwOnInvalidDocument: true` makes malformed or unauthenticated
60
+ existing documents fail with a safe diagnostic; missing files still return
61
+ `null`. The default treats invalid documents as absent. Strict reads preserve
62
+ the existing file until an explicit reset or replacement.
59
63
 
60
64
  ### macOS Keychain
61
65
 
@@ -31,6 +31,8 @@ export interface EncryptedFileStoreInput {
31
31
  getMachineIdentity?: () => MachineIdentity | Promise<MachineIdentity>;
32
32
  getHomeDirectory?: () => string;
33
33
  getRandomBytes?: (size: number) => Buffer;
34
+ /** Fail closed instead of treating malformed or unauthenticated documents as absent. */
35
+ throwOnInvalidDocument?: boolean;
34
36
  }
35
37
  export declare class EncryptedFileStore implements SecretStore {
36
38
  private readonly fs;
@@ -40,6 +42,7 @@ export declare class EncryptedFileStore implements SecretStore {
40
42
  private readonly getMachineIdentity;
41
43
  private readonly getRandomBytes;
42
44
  private keyPromise;
45
+ private readonly throwOnInvalidDocument;
43
46
  constructor(input: EncryptedFileStoreInput);
44
47
  get(): Promise<string | null>;
45
48
  withLock<T>(operation: () => Promise<T>, options?: SecretStoreLockOptions): Promise<T>;
@@ -19,6 +19,7 @@ export class EncryptedFileStore {
19
19
  getMachineIdentity;
20
20
  getRandomBytes;
21
21
  keyPromise = null;
22
+ throwOnInvalidDocument;
22
23
  constructor(input) {
23
24
  this.fs = input.fs ?? fs;
24
25
  this.salt = input.salt;
@@ -37,6 +38,7 @@ export class EncryptedFileStore {
37
38
  }
38
39
  this.getMachineIdentity = input.getMachineIdentity ?? defaultMachineIdentity;
39
40
  this.getRandomBytes = input.getRandomBytes ?? randomBytes;
41
+ this.throwOnInvalidDocument = input.throwOnInvalidDocument ?? false;
40
42
  }
41
43
  async get() {
42
44
  await this.assertCredentialPathHasNoSymbolicLinks(this.filePath);
@@ -52,6 +54,8 @@ export class EncryptedFileStore {
52
54
  }
53
55
  const document = parseEncryptedDocument(rawDocument);
54
56
  if (!document) {
57
+ if (this.throwOnInvalidDocument)
58
+ throw new Error("Invalid encrypted credential document; reset the store explicitly to recover");
55
59
  return null;
56
60
  }
57
61
  const key = await this.getEncryptionKey();
@@ -61,6 +65,8 @@ export class EncryptedFileStore {
61
65
  const ciphertext = Buffer.from(document.ciphertext, "base64");
62
66
  if (iv.byteLength !== ENCRYPTION_IV_BYTES ||
63
67
  authTag.byteLength !== ENCRYPTION_AUTH_TAG_BYTES) {
68
+ if (this.throwOnInvalidDocument)
69
+ throw new Error("Invalid encrypted credential document; reset the store explicitly to recover");
64
70
  return null;
65
71
  }
66
72
  const decipher = createDecipheriv(ENCRYPTION_ALGORITHM, key, iv);
@@ -69,6 +75,8 @@ export class EncryptedFileStore {
69
75
  return plaintext.toString("utf8");
70
76
  }
71
77
  catch {
78
+ if (this.throwOnInvalidDocument)
79
+ throw new Error("Invalid encrypted credential document; reset the store explicitly to recover");
72
80
  return null;
73
81
  }
74
82
  }
@@ -130,6 +130,12 @@ can recover it; headless requests fail with an explicit unknown-outcome error.
130
130
  Only complete OAuth error responses establish a rejected request and allow a
131
131
  transient retry or restoration of the original grant. Gateway error pages do not.
132
132
 
133
+ Native persisted OAuth reads always reject corrupt encrypted documents and
134
+ invalid stored JSON, with diagnostics that omit decrypted contents. They retain
135
+ the existing record for explicit reset rather than interpreting corruption as
136
+ an absent session and reviving an initial grant. Caller file-backend settings
137
+ cannot disable this policy.
138
+
133
139
  ## Environment Variables
134
140
 
135
141
  This package exposes no direct environment variables. When `authStore` is used,
@@ -23,7 +23,13 @@ export function createAuthStoreSessionStore(options = {}) {
23
23
  if (value === null) {
24
24
  return null;
25
25
  }
26
- const parsed = JSON.parse(value);
26
+ let parsed;
27
+ try {
28
+ parsed = JSON.parse(value);
29
+ }
30
+ catch {
31
+ throw new Error("Stored OAuth session must be valid JSON; reset the store explicitly to recover");
32
+ }
27
33
  if (isStoredOAuthSession(parsed)) {
28
34
  return parsed;
29
35
  }
@@ -47,7 +53,13 @@ export function createAuthStoreClientStore(options) {
47
53
  if (value === null) {
48
54
  return null;
49
55
  }
50
- const parsed = JSON.parse(value);
56
+ let parsed;
57
+ try {
58
+ parsed = JSON.parse(value);
59
+ }
60
+ catch {
61
+ throw new Error("Stored OAuth client must be valid JSON; reset the store explicitly to recover");
62
+ }
51
63
  const clientId = isObjectRecord(parsed) ? getOwnString(parsed, "clientId") : undefined;
52
64
  if (clientId !== undefined) {
53
65
  const client = { clientId };
@@ -75,6 +87,7 @@ function createNamedSecretStore(key, options, defaults) {
75
87
  const parsedFilePath = configuredFilePath === undefined ? null : path.parse(configuredFilePath);
76
88
  const fileStore = {
77
89
  ...options.fileStore,
90
+ throwOnInvalidDocument: true,
78
91
  filePath: parsedFilePath === null
79
92
  ? undefined
80
93
  : path.join(parsedFilePath.dir, `${parsedFilePath.name}-${hash}${parsedFilePath.ext || ".enc"}`),
@@ -70,6 +70,8 @@ interface EncryptedFileStoreInput {
70
70
  getMachineIdentity?: () => MachineIdentity | Promise<MachineIdentity>;
71
71
  getHomeDirectory?: () => string;
72
72
  getRandomBytes?: (size: number) => Buffer;
73
+ /** Fail closed instead of treating malformed or unauthenticated documents as absent. */
74
+ throwOnInvalidDocument?: boolean;
73
75
  }
74
76
 
75
77
  interface KeychainCommandResult {
@@ -3575,6 +3575,7 @@ var EncryptedFileStore = class {
3575
3575
  getMachineIdentity;
3576
3576
  getRandomBytes;
3577
3577
  keyPromise = null;
3578
+ throwOnInvalidDocument;
3578
3579
  constructor(input) {
3579
3580
  this.fs = input.fs ?? fs;
3580
3581
  this.salt = input.salt;
@@ -3592,6 +3593,7 @@ var EncryptedFileStore = class {
3592
3593
  }
3593
3594
  this.getMachineIdentity = input.getMachineIdentity ?? defaultMachineIdentity;
3594
3595
  this.getRandomBytes = input.getRandomBytes ?? randomBytes;
3596
+ this.throwOnInvalidDocument = input.throwOnInvalidDocument ?? false;
3595
3597
  }
3596
3598
  async get() {
3597
3599
  await this.assertCredentialPathHasNoSymbolicLinks(this.filePath);
@@ -3606,6 +3608,8 @@ var EncryptedFileStore = class {
3606
3608
  }
3607
3609
  const document = parseEncryptedDocument(rawDocument);
3608
3610
  if (!document) {
3611
+ if (this.throwOnInvalidDocument)
3612
+ throw new Error("Invalid encrypted credential document; reset the store explicitly to recover");
3609
3613
  return null;
3610
3614
  }
3611
3615
  const key2 = await this.getEncryptionKey();
@@ -3614,6 +3618,8 @@ var EncryptedFileStore = class {
3614
3618
  const authTag = Buffer.from(document.authTag, "base64");
3615
3619
  const ciphertext = Buffer.from(document.ciphertext, "base64");
3616
3620
  if (iv.byteLength !== ENCRYPTION_IV_BYTES || authTag.byteLength !== ENCRYPTION_AUTH_TAG_BYTES) {
3621
+ if (this.throwOnInvalidDocument)
3622
+ throw new Error("Invalid encrypted credential document; reset the store explicitly to recover");
3617
3623
  return null;
3618
3624
  }
3619
3625
  const decipher = createDecipheriv(ENCRYPTION_ALGORITHM, key2, iv);
@@ -3621,6 +3627,8 @@ var EncryptedFileStore = class {
3621
3627
  const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
3622
3628
  return plaintext.toString("utf8");
3623
3629
  } catch {
3630
+ if (this.throwOnInvalidDocument)
3631
+ throw new Error("Invalid encrypted credential document; reset the store explicitly to recover");
3624
3632
  return null;
3625
3633
  }
3626
3634
  }
@@ -4083,7 +4091,12 @@ function createAuthStoreSessionStore(options = {}) {
4083
4091
  if (value === null) {
4084
4092
  return null;
4085
4093
  }
4086
- const parsed = JSON.parse(value);
4094
+ let parsed;
4095
+ try {
4096
+ parsed = JSON.parse(value);
4097
+ } catch {
4098
+ throw new Error("Stored OAuth session must be valid JSON; reset the store explicitly to recover");
4099
+ }
4087
4100
  if (isStoredOAuthSession(parsed)) {
4088
4101
  return parsed;
4089
4102
  }
@@ -4107,7 +4120,12 @@ function createAuthStoreClientStore(options) {
4107
4120
  if (value === null) {
4108
4121
  return null;
4109
4122
  }
4110
- const parsed = JSON.parse(value);
4123
+ let parsed;
4124
+ try {
4125
+ parsed = JSON.parse(value);
4126
+ } catch {
4127
+ throw new Error("Stored OAuth client must be valid JSON; reset the store explicitly to recover");
4128
+ }
4111
4129
  const clientId = isObjectRecord(parsed) ? getOwnString(parsed, "clientId") : void 0;
4112
4130
  if (clientId !== void 0) {
4113
4131
  const client = { clientId };
@@ -4134,6 +4152,7 @@ function createNamedSecretStore(key2, options, defaults) {
4134
4152
  const parsedFilePath = configuredFilePath === void 0 ? null : path4.parse(configuredFilePath);
4135
4153
  const fileStore = {
4136
4154
  ...options.fileStore,
4155
+ throwOnInvalidDocument: true,
4137
4156
  filePath: parsedFilePath === null ? void 0 : path4.join(parsedFilePath.dir, `${parsedFilePath.name}-${hash}${parsedFilePath.ext || ".enc"}`),
4138
4157
  salt: options.fileStore?.salt ?? defaults.salt,
4139
4158
  defaultDirectory: options.fileStore?.defaultDirectory || defaults.directory,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tiny-http-mcp-server",
3
- "version": "0.1.28",
3
+ "version": "0.1.29",
4
4
  "description": "Minimal MCP server over HTTP built on tiny-stdio-mcp-server",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",