@venturekit/mcp 0.0.50 → 0.0.51
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/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/migrations/vk_mcp_0002_token_lookup_hash.sql +18 -0
- package/dist/store.d.ts +18 -8
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +40 -17
- package/dist/store.js.map +1 -1
- package/package.json +3 -3
- package/src/migrations/vk_mcp_0002_token_lookup_hash.sql +18 -0
package/dist/index.d.ts
CHANGED
|
@@ -28,7 +28,7 @@ export { handleMcpRequest } from './server.js';
|
|
|
28
28
|
export { zodToJsonSchema, type JsonSchema } from './json-schema.js';
|
|
29
29
|
export { McpError, type McpErrorOptions } from './errors.js';
|
|
30
30
|
export { generateBearerToken, bearerTokenPrefix, parseScopedToken, parseBearer, DEFAULT_PREFIX_LENGTH, type GenerateBearerTokenOptions, type GeneratedBearerToken, type ParsedScopedToken, } from './tokens.js';
|
|
31
|
-
export { createMcpToken, listMcpTokens, revokeMcpToken, verifyMcpToken, hashTokenSecret, verifyTokenSecret, type Querier, type McpTokenRow, type CreateMcpTokenInput, type CreatedMcpToken, } from './store.js';
|
|
31
|
+
export { createMcpToken, listMcpTokens, revokeMcpToken, verifyMcpToken, hashTokenSecret, tokenLookupHash, verifyTokenSecret, type Querier, type McpTokenRow, type CreateMcpTokenInput, type CreatedMcpToken, } from './store.js';
|
|
32
32
|
export { getMcpMigrationsDir } from './path.js';
|
|
33
33
|
export type { JsonRpcError, JsonRpcRequest, JsonRpcResponse, McpAuthResult, McpHandlerInput, McpHandlerOutput, McpServerConfig, McpServerInfo, McpTool, McpToolCallInfo, McpToolRegistry, } from './types.js';
|
|
34
34
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,QAAQ,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,gBAAgB,EAChB,WAAW,EACX,qBAAqB,EACrB,KAAK,0BAA0B,EAC/B,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,GACvB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,cAAc,EACd,aAAa,EACb,cAAc,EACd,cAAc,EACd,eAAe,EACf,iBAAiB,EACjB,KAAK,OAAO,EACZ,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACxB,KAAK,eAAe,GACrB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAChD,YAAY,EACV,YAAY,EACZ,cAAc,EACd,eAAe,EACf,aAAa,EACb,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,aAAa,EACb,OAAO,EACP,eAAe,EACf,eAAe,GAChB,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,QAAQ,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,gBAAgB,EAChB,WAAW,EACX,qBAAqB,EACrB,KAAK,0BAA0B,EAC/B,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,GACvB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,cAAc,EACd,aAAa,EACb,cAAc,EACd,cAAc,EACd,eAAe,EACf,eAAe,EACf,iBAAiB,EACjB,KAAK,OAAO,EACZ,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACxB,KAAK,eAAe,GACrB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAChD,YAAY,EACV,YAAY,EACZ,cAAc,EACd,eAAe,EACf,aAAa,EACb,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,aAAa,EACb,OAAO,EACP,eAAe,EACf,eAAe,GAChB,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -28,6 +28,6 @@ export { handleMcpRequest } from './server.js';
|
|
|
28
28
|
export { zodToJsonSchema } from './json-schema.js';
|
|
29
29
|
export { McpError } from './errors.js';
|
|
30
30
|
export { generateBearerToken, bearerTokenPrefix, parseScopedToken, parseBearer, DEFAULT_PREFIX_LENGTH, } from './tokens.js';
|
|
31
|
-
export { createMcpToken, listMcpTokens, revokeMcpToken, verifyMcpToken, hashTokenSecret, verifyTokenSecret, } from './store.js';
|
|
31
|
+
export { createMcpToken, listMcpTokens, revokeMcpToken, verifyMcpToken, hashTokenSecret, tokenLookupHash, verifyTokenSecret, } from './store.js';
|
|
32
32
|
export { getMcpMigrationsDir } from './path.js';
|
|
33
33
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAmB,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,QAAQ,EAAwB,MAAM,aAAa,CAAC;AAC7D,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,gBAAgB,EAChB,WAAW,EACX,qBAAqB,GAItB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,cAAc,EACd,aAAa,EACb,cAAc,EACd,cAAc,EACd,eAAe,EACf,iBAAiB,GAKlB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAmB,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,QAAQ,EAAwB,MAAM,aAAa,CAAC;AAC7D,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,gBAAgB,EAChB,WAAW,EACX,qBAAqB,GAItB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,cAAc,EACd,aAAa,EACb,cAAc,EACd,cAAc,EACd,eAAe,EACf,eAAe,EACf,iBAAiB,GAKlB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
-- @venturekit/mcp — direct token lookup.
|
|
2
|
+
--
|
|
3
|
+
-- A token is 256 bits from a CSPRNG, so its SHA-256 is as unguessable as
|
|
4
|
+
-- the token itself and needs no slow hash. Looking a token up by that
|
|
5
|
+
-- digest is one indexed read; the former `(scope, prefix)` narrowing ran
|
|
6
|
+
-- scrypt once per candidate — and every token of a scope shares the
|
|
7
|
+
-- prefix, so an unauthenticated request could make the server run scrypt
|
|
8
|
+
-- once per token the scope holds.
|
|
9
|
+
--
|
|
10
|
+
-- Rows issued before this migration have no digest yet: the verifier
|
|
11
|
+
-- falls back to the scrypt path for them (bounded) and records the digest
|
|
12
|
+
-- on their first successful use.
|
|
13
|
+
|
|
14
|
+
ALTER TABLE vk_mcp_tokens ADD COLUMN IF NOT EXISTS lookup_hash text;
|
|
15
|
+
|
|
16
|
+
CREATE UNIQUE INDEX IF NOT EXISTS vk_mcp_tokens_lookup_hash_idx
|
|
17
|
+
ON vk_mcp_tokens (lookup_hash)
|
|
18
|
+
WHERE lookup_hash IS NOT NULL;
|
package/dist/store.d.ts
CHANGED
|
@@ -9,13 +9,19 @@
|
|
|
9
9
|
* to whatever it owns — a tenant slug, a user id, a project id.
|
|
10
10
|
*
|
|
11
11
|
* # Security
|
|
12
|
-
* - Only a scrypt hash + the
|
|
12
|
+
* - Only digests are persisted (a SHA-256 lookup hash, a scrypt hash) + the `prefix`. The
|
|
13
13
|
* plaintext is returned once from {@link createMcpToken} and never
|
|
14
14
|
* stored.
|
|
15
|
-
* - {@link verifyMcpToken}
|
|
16
|
-
*
|
|
15
|
+
* - {@link verifyMcpToken} looks the token up by its SHA-256 (one indexed read); rows from
|
|
16
|
+
* before the digest existed fall back to a bounded scrypt compare.
|
|
17
17
|
* - Hashing is `node:crypto` scrypt — no third-party dependency.
|
|
18
18
|
*/
|
|
19
|
+
/**
|
|
20
|
+
* The token's lookup key: SHA-256 of the plaintext. A token carries 256
|
|
21
|
+
* bits of CSPRNG entropy, so its digest needs no slow hash to resist
|
|
22
|
+
* guessing, and an equality lookup on it costs one indexed read.
|
|
23
|
+
*/
|
|
24
|
+
export declare function tokenLookupHash(plaintext: string): string;
|
|
19
25
|
/**
|
|
20
26
|
* Minimal querier shape — a function that runs a parameterized SQL
|
|
21
27
|
* statement and returns rows. Matches `@venturekit/data`'s `Querier`
|
|
@@ -36,6 +42,8 @@ export interface McpTokenRow {
|
|
|
36
42
|
prefix: string;
|
|
37
43
|
/** scrypt hash (`scrypt$N$r$p$salt$dk`). */
|
|
38
44
|
tokenHash: string;
|
|
45
|
+
/** SHA-256 of the plaintext ({@link tokenLookupHash}); `null` on rows issued before it existed. */
|
|
46
|
+
lookupHash?: string | null;
|
|
39
47
|
/** Comma-separated tool whitelist; `null` = every tool. */
|
|
40
48
|
allowedTools: string | null;
|
|
41
49
|
lastUsedAt: Date | null;
|
|
@@ -72,11 +80,13 @@ export declare function createMcpToken(q: Querier, input: CreateMcpTokenInput):
|
|
|
72
80
|
export declare function revokeMcpToken(q: Querier, scope: string, id: string): Promise<McpTokenRow | null>;
|
|
73
81
|
/**
|
|
74
82
|
* Resolve a plaintext bearer token to its active row, or `null` if the
|
|
75
|
-
* token is malformed, unknown, or revoked.
|
|
76
|
-
*
|
|
77
|
-
* and stamps `last_used_at` on a match (best-effort; failures
|
|
78
|
-
* block auth).
|
|
79
|
-
*
|
|
83
|
+
* token is malformed, unknown, or revoked. Looks the token up by its
|
|
84
|
+
* SHA-256 ({@link tokenLookupHash}) — one indexed read, no hashing per
|
|
85
|
+
* candidate — and stamps `last_used_at` on a match (best-effort; failures
|
|
86
|
+
* don't block auth). Rows issued before the digest existed are verified
|
|
87
|
+
* with scrypt (at most {@link MAX_LEGACY_CANDIDATES}) and get their digest
|
|
88
|
+
* recorded on first use. The host is responsible for any further checks
|
|
89
|
+
* (e.g. mapping `scope` to a tenant and rejecting suspended ones).
|
|
80
90
|
*/
|
|
81
91
|
export declare function verifyMcpToken(q: Querier, plaintext: string): Promise<McpTokenRow | null>;
|
|
82
92
|
//# sourceMappingURL=store.d.ts.map
|
package/dist/store.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAeH;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEzD;AAaD;;;;;GAKG;AACH,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAClD,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,OAAO,EAAE,KACf,OAAO,CAAC,CAAC,CAAC,CAAC;AAEhB;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,MAAM,EAAE,MAAM,CAAC;IACf,4CAA4C;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,mGAAmG;IACnG,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,2DAA2D;IAC3D,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,IAAI,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,mBAAmB;IAClC,8DAA8D;IAC9D,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,iEAAiE;IACjE,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAC/B,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,WAAW,CAAC;IACjB,4EAA4E;IAC5E,KAAK,EAAE,MAAM,CAAC;CACf;AAID,8EAA8E;AAC9E,wBAAsB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAQxE;AAED,yEAAyE;AACzE,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAY3F;AAID,mEAAmE;AACnE,wBAAsB,aAAa,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAKrF;AAID,kFAAkF;AAClF,wBAAsB,cAAc,CAClC,CAAC,EAAE,OAAO,EACV,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC,eAAe,CAAC,CAe1B;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAClC,CAAC,EAAE,OAAO,EACV,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAS7B;AAED;;;;;;;;;GASG;AACH,wBAAsB,cAAc,CAAC,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAgC/F"}
|
package/dist/store.js
CHANGED
|
@@ -9,17 +9,27 @@
|
|
|
9
9
|
* to whatever it owns — a tenant slug, a user id, a project id.
|
|
10
10
|
*
|
|
11
11
|
* # Security
|
|
12
|
-
* - Only a scrypt hash + the
|
|
12
|
+
* - Only digests are persisted (a SHA-256 lookup hash, a scrypt hash) + the `prefix`. The
|
|
13
13
|
* plaintext is returned once from {@link createMcpToken} and never
|
|
14
14
|
* stored.
|
|
15
|
-
* - {@link verifyMcpToken}
|
|
16
|
-
*
|
|
15
|
+
* - {@link verifyMcpToken} looks the token up by its SHA-256 (one indexed read); rows from
|
|
16
|
+
* before the digest existed fall back to a bounded scrypt compare.
|
|
17
17
|
* - Hashing is `node:crypto` scrypt — no third-party dependency.
|
|
18
18
|
*/
|
|
19
|
-
import { randomBytes, scrypt as scryptCb, timingSafeEqual, } from 'node:crypto';
|
|
19
|
+
import { createHash, randomBytes, scrypt as scryptCb, timingSafeEqual, } from 'node:crypto';
|
|
20
20
|
import { promisify } from 'node:util';
|
|
21
21
|
import { generateBearerToken, bearerTokenPrefix, parseScopedToken } from './tokens.js';
|
|
22
22
|
const scrypt = promisify(scryptCb);
|
|
23
|
+
/**
|
|
24
|
+
* The token's lookup key: SHA-256 of the plaintext. A token carries 256
|
|
25
|
+
* bits of CSPRNG entropy, so its digest needs no slow hash to resist
|
|
26
|
+
* guessing, and an equality lookup on it costs one indexed read.
|
|
27
|
+
*/
|
|
28
|
+
export function tokenLookupHash(plaintext) {
|
|
29
|
+
return createHash('sha256').update(plaintext, 'utf8').digest('hex');
|
|
30
|
+
}
|
|
31
|
+
/** Legacy rows (no lookup hash yet) verified per request, at most — bounds the scrypt work. */
|
|
32
|
+
const MAX_LEGACY_CANDIDATES = 3;
|
|
23
33
|
// scrypt cost parameters. N=16384 keeps a single hash ~50-80ms (on par
|
|
24
34
|
// with bcrypt cost 12) while staying under Node's default 32MB maxmem
|
|
25
35
|
// (128 * N * r = 16MB).
|
|
@@ -66,9 +76,9 @@ export async function createMcpToken(q, input) {
|
|
|
66
76
|
const { token, prefix } = generateBearerToken({ scope: input.scope });
|
|
67
77
|
const tokenHash = await hashTokenSecret(token);
|
|
68
78
|
const allowedTools = input.allowedTools && input.allowedTools.length > 0 ? input.allowedTools.join(',') : null;
|
|
69
|
-
const rows = await q(`INSERT INTO vk_mcp_tokens (scope, label, prefix, token_hash, allowed_tools, created_by)
|
|
70
|
-
VALUES ($1, $2, $3, $4, $5, $6)
|
|
71
|
-
RETURNING *`, [input.scope, input.label, prefix, tokenHash, allowedTools, input.createdBy ?? null]);
|
|
79
|
+
const rows = await q(`INSERT INTO vk_mcp_tokens (scope, label, prefix, token_hash, lookup_hash, allowed_tools, created_by)
|
|
80
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
81
|
+
RETURNING *`, [input.scope, input.label, prefix, tokenHash, tokenLookupHash(token), allowedTools, input.createdBy ?? null]);
|
|
72
82
|
const row = rows[0];
|
|
73
83
|
if (!row)
|
|
74
84
|
throw new Error('[mcp/createMcpToken] INSERT ... RETURNING returned no rows');
|
|
@@ -87,25 +97,38 @@ export async function revokeMcpToken(q, scope, id) {
|
|
|
87
97
|
}
|
|
88
98
|
/**
|
|
89
99
|
* Resolve a plaintext bearer token to its active row, or `null` if the
|
|
90
|
-
* token is malformed, unknown, or revoked.
|
|
91
|
-
*
|
|
92
|
-
* and stamps `last_used_at` on a match (best-effort; failures
|
|
93
|
-
* block auth).
|
|
94
|
-
*
|
|
100
|
+
* token is malformed, unknown, or revoked. Looks the token up by its
|
|
101
|
+
* SHA-256 ({@link tokenLookupHash}) — one indexed read, no hashing per
|
|
102
|
+
* candidate — and stamps `last_used_at` on a match (best-effort; failures
|
|
103
|
+
* don't block auth). Rows issued before the digest existed are verified
|
|
104
|
+
* with scrypt (at most {@link MAX_LEGACY_CANDIDATES}) and get their digest
|
|
105
|
+
* recorded on first use. The host is responsible for any further checks
|
|
106
|
+
* (e.g. mapping `scope` to a tenant and rejecting suspended ones).
|
|
95
107
|
*/
|
|
96
108
|
export async function verifyMcpToken(q, plaintext) {
|
|
97
109
|
const parsed = parseScopedToken(plaintext);
|
|
98
110
|
if (!parsed)
|
|
99
111
|
return null;
|
|
100
|
-
const
|
|
101
|
-
const
|
|
102
|
-
|
|
112
|
+
const lookupHash = tokenLookupHash(plaintext);
|
|
113
|
+
const [direct] = await q(`SELECT * FROM vk_mcp_tokens WHERE lookup_hash = $1 AND scope = $2 AND revoked_at IS NULL`, [lookupHash, parsed.scope]);
|
|
114
|
+
if (direct) {
|
|
115
|
+
touch(q, `UPDATE vk_mcp_tokens SET last_used_at = NOW() WHERE id = $1`, [direct.id]);
|
|
116
|
+
return direct;
|
|
117
|
+
}
|
|
118
|
+
const legacy = await q(`SELECT * FROM vk_mcp_tokens
|
|
119
|
+
WHERE scope = $1 AND prefix = $2 AND lookup_hash IS NULL AND revoked_at IS NULL
|
|
120
|
+
ORDER BY created_at DESC
|
|
121
|
+
LIMIT ${MAX_LEGACY_CANDIDATES}`, [parsed.scope, bearerTokenPrefix(plaintext)]);
|
|
122
|
+
for (const row of legacy) {
|
|
103
123
|
if (await verifyTokenSecret(plaintext, row.tokenHash)) {
|
|
104
|
-
|
|
105
|
-
Promise.resolve(q(`UPDATE vk_mcp_tokens SET last_used_at = NOW() WHERE id = $1`, [row.id])).catch(() => { });
|
|
124
|
+
touch(q, `UPDATE vk_mcp_tokens SET lookup_hash = $2, last_used_at = NOW() WHERE id = $1 AND lookup_hash IS NULL`, [row.id, lookupHash]);
|
|
106
125
|
return row;
|
|
107
126
|
}
|
|
108
127
|
}
|
|
109
128
|
return null;
|
|
110
129
|
}
|
|
130
|
+
/** A bookkeeping write that must never block or fail authentication. */
|
|
131
|
+
function touch(q, sql, params) {
|
|
132
|
+
Promise.resolve(q(sql, params)).catch(() => { });
|
|
133
|
+
}
|
|
111
134
|
//# sourceMappingURL=store.js.map
|
package/dist/store.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EACL,WAAW,EACX,MAAM,IAAI,QAAQ,EAClB,eAAe,GAGhB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEvF,MAAM,MAAM,GAAG,SAAS,CAAwD,QAAQ,CAAC,CAAC;AAE1F,uEAAuE;AACvE,sEAAsE;AACtE,wBAAwB;AACxB,MAAM,QAAQ,GAAG,KAAK,CAAC;AACvB,MAAM,QAAQ,GAAG,CAAC,CAAC;AACnB,MAAM,QAAQ,GAAG,CAAC,CAAC;AACnB,MAAM,aAAa,GAAG,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@venturekit/mcp",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.51",
|
|
4
4
|
"description": "Transport-agnostic Model Context Protocol (MCP) server primitives for VentureKit: JSON-RPC dispatch, a typed tool registry, and a Zod→JSON-Schema serializer.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -41,12 +41,12 @@
|
|
|
41
41
|
"agents"
|
|
42
42
|
],
|
|
43
43
|
"peerDependencies": {
|
|
44
|
-
"zod": "^4.
|
|
44
|
+
"zod": "^4.6.6"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@types/node": "^26.5.1",
|
|
48
48
|
"typescript": "^5.3.0",
|
|
49
|
-
"zod": "^4.
|
|
49
|
+
"zod": "^4.6.6"
|
|
50
50
|
},
|
|
51
51
|
"scripts": {
|
|
52
52
|
"build": "tsc && node -e \"require('fs').cpSync('src/migrations','dist/migrations',{recursive:true})\"",
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
-- @venturekit/mcp — direct token lookup.
|
|
2
|
+
--
|
|
3
|
+
-- A token is 256 bits from a CSPRNG, so its SHA-256 is as unguessable as
|
|
4
|
+
-- the token itself and needs no slow hash. Looking a token up by that
|
|
5
|
+
-- digest is one indexed read; the former `(scope, prefix)` narrowing ran
|
|
6
|
+
-- scrypt once per candidate — and every token of a scope shares the
|
|
7
|
+
-- prefix, so an unauthenticated request could make the server run scrypt
|
|
8
|
+
-- once per token the scope holds.
|
|
9
|
+
--
|
|
10
|
+
-- Rows issued before this migration have no digest yet: the verifier
|
|
11
|
+
-- falls back to the scrypt path for them (bounded) and records the digest
|
|
12
|
+
-- on their first successful use.
|
|
13
|
+
|
|
14
|
+
ALTER TABLE vk_mcp_tokens ADD COLUMN IF NOT EXISTS lookup_hash text;
|
|
15
|
+
|
|
16
|
+
CREATE UNIQUE INDEX IF NOT EXISTS vk_mcp_tokens_lookup_hash_idx
|
|
17
|
+
ON vk_mcp_tokens (lookup_hash)
|
|
18
|
+
WHERE lookup_hash IS NOT NULL;
|