@vllnt/convex-helpers 0.1.0 → 0.1.1-canary.ba6c009
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 +52 -0
- package/README.md +81 -4
- package/dist/better-auth.d.ts +35 -0
- package/dist/better-auth.d.ts.map +1 -0
- package/dist/better-auth.js +113 -0
- package/dist/better-auth.js.map +1 -0
- package/dist/identity.d.ts +51 -0
- package/dist/identity.d.ts.map +1 -0
- package/dist/identity.js +80 -0
- package/dist/identity.js.map +1 -0
- package/dist/mcp/auth.d.ts.map +1 -1
- package/dist/mcp/auth.js +21 -8
- package/dist/mcp/auth.js.map +1 -1
- package/dist/mcp/callback.d.ts +3 -0
- package/dist/mcp/callback.d.ts.map +1 -0
- package/dist/mcp/callback.js +18 -0
- package/dist/mcp/callback.js.map +1 -0
- package/dist/mcp/resources/register.d.ts.map +1 -1
- package/dist/mcp/resources/register.js +2 -7
- package/dist/mcp/resources/register.js.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +1 -13
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools/register.d.ts +1 -1
- package/dist/mcp/tools/register.d.ts.map +1 -1
- package/dist/mcp/tools/register.js +11 -21
- package/dist/mcp/tools/register.js.map +1 -1
- package/docs/API.md +260 -0
- package/docs/READINESS.md +80 -0
- package/llms-full.txt +1612 -0
- package/llms.txt +37 -0
- package/package.json +27 -6
- package/src/better-auth.ts +153 -0
- package/src/identity.ts +121 -0
- package/src/mcp/auth.ts +28 -13
- package/src/mcp/callback.ts +18 -0
- package/src/mcp/resources/register.ts +2 -7
- package/src/mcp/server.ts +1 -20
- package/src/mcp/tools/register.ts +15 -23
- package/src/index.test.ts +0 -164
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- Fail closed on thrown or stalled MCP authorization hooks; bound auth/lifecycle
|
|
13
|
+
callbacks to 10 seconds and remove raw exception logging.
|
|
14
|
+
- Add optional host-configured `AuthCtx.trustedIssuer` enforcement before identity
|
|
15
|
+
row callbacks, preserving the legacy single-issuer contract when omitted.
|
|
16
|
+
- Reject unsafe preview patterns, localhost paths/normalization and control characters;
|
|
17
|
+
validate cookie origins instead of trusting a prefix.
|
|
18
|
+
- Bound retargeting to 1000 rows with sequential callbacks and failure short-circuiting.
|
|
19
|
+
- Remove release-note shell interpolation; gate stable dispatch to main and explicit
|
|
20
|
+
`RELEASE_ENABLED=true`, and remove workflow-wide write/OIDC privileges.
|
|
21
|
+
- Patch development Handlebars and Vite/esbuild advisory paths; document the remaining
|
|
22
|
+
deepmerge-ts tooling advisory in `docs/READINESS.md`.
|
|
23
|
+
|
|
24
|
+
- Convert MCP `int64` and `bytes` inputs to native Convex values and serialize native Convex
|
|
25
|
+
results with lossless tagged JSON.
|
|
26
|
+
- Enforce resource argument validators before dispatch and reject unsafe mutation/action
|
|
27
|
+
timeouts that could report failure while non-cancellable side effects continue.
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
|
|
31
|
+
- Document actual shipped APIs and host-owned identity/merge/CSRF boundaries; identity
|
|
32
|
+
integration fixtures now receive strict lint checks (legacy MCP fixtures remain excluded).
|
|
33
|
+
|
|
34
|
+
- Refresh all direct dependencies to their latest compatible releases for canary validation.
|
|
35
|
+
- Require `convex@^1.45.0` and update `convex-test` to `^0.0.56`.
|
|
36
|
+
|
|
37
|
+
### Added
|
|
38
|
+
|
|
39
|
+
- `./better-auth` — fail-closed trusted-origin parser + HTTPS cookie settings for `@convex-dev/better-auth` (does not import better-auth).
|
|
40
|
+
- `./identity` — `getOrCreateFromAuth` / `requireFromAuth` / `retargetRows` host-`ctx` glue for anonymous sessions and anonymous→identified upgrades.
|
|
41
|
+
- `./mcp` entry — expose Convex functions as MCP tools (`createMCPServer` + `query`/`mutation`/`action`/`resource`), default-deny auth, lifecycle hooks, validator→zod conversion, and optional HMAC-signed cursor pagination. Absorbed from `@vllnt/convex-mcp` (see ROADMAP `absorb-convex-mcp`). `@modelcontextprotocol/sdk` and `zod` are optional peer deps — the `./mcp` entry is tree-shakeable, so backend-only consumers pull neither. 100% test coverage retained (161 tests).
|
|
42
|
+
|
|
43
|
+
## [0.1.0] - 2026-06-13
|
|
44
|
+
|
|
45
|
+
### Added
|
|
46
|
+
|
|
47
|
+
- Scaffolded to fleet Universal standard (type-B helpers library)
|
|
48
|
+
- Seed root utilities: `asyncMap`, `pruneNull`, `nullThrows`, `NullDocumentError`
|
|
49
|
+
- 100% test coverage gate via vitest thresholds
|
|
50
|
+
- CI workflow, publish workflow, email-guard workflow
|
|
51
|
+
- Full docs set: README, CHANGELOG, CONTRIBUTING, SECURITY, CODE_OF_CONDUCT
|
|
52
|
+
- `llms.txt` and `llms-full.txt` for AI agent discovery
|
package/README.md
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
# @vllnt/convex-helpers
|
|
7
7
|
|
|
8
|
-
Typed host-side
|
|
9
|
-
|
|
8
|
+
Typed Convex host-side utilities, trusted-origin and cookie configuration helpers,
|
|
9
|
+
identity-row glue, and optional MCP tools.
|
|
10
10
|
|
|
11
11
|
```ts
|
|
12
12
|
import { asyncMap, pruneNull, nullThrows } from "@vllnt/convex-helpers";
|
|
@@ -26,10 +26,11 @@ See [ROADMAP.md](./ROADMAP.md) for the planned module surface.
|
|
|
26
26
|
- `asyncMap` — parallel async map that preserves order
|
|
27
27
|
- `pruneNull` — filter null/undefined from arrays
|
|
28
28
|
- `nullThrows` — non-null assertion with typed `NullDocumentError`
|
|
29
|
+
- `./better-auth` — fail-closed trusted origins + HTTPS cookies for better-auth
|
|
30
|
+
- `./identity` — identity-row lookup/bootstrap and bounded row-callback counting
|
|
29
31
|
- `./mcp` — expose Convex functions as MCP tools (`createMCPServer` + `query`/`mutation`/`action`/`resource`), default-deny auth, optional cursor pagination
|
|
30
32
|
- [planned] `./builders` — `customQuery`/`customMutation`/`customAction`/`customCtx`
|
|
31
33
|
- [planned] `./errors` — typed `AppError` + HTTP-status map
|
|
32
|
-
- [planned] `./auth` — provider-agnostic `requireIdentity`/`getCurrentSubject`
|
|
33
34
|
- [planned] `./env` — `defineEnv(zodSchema)` cold-start validation
|
|
34
35
|
- [planned] `./tracing` — span emit + `traceparent` propagation via `@vllnt/logger`
|
|
35
36
|
- [planned] `./testing` — fixture factories + `withIdentity`
|
|
@@ -89,6 +90,78 @@ export const mcp = createMCPServer({
|
|
|
89
90
|
export const { GET, POST } = mcp.handler();
|
|
90
91
|
```
|
|
91
92
|
|
|
93
|
+
MCP auth and lifecycle callbacks have a 10-second deadline per callback. A thrown
|
|
94
|
+
or timed-out `before` hook denies dispatch with a generic message. Auth failures
|
|
95
|
+
also deny access generically. Success/error hooks cannot undo completed dispatch.
|
|
96
|
+
Timeouts stop waiting, not the host callback; callbacks must avoid side effects.
|
|
97
|
+
Raw errors are not logged by the library; hosts may observe hook context and must
|
|
98
|
+
redact errors, arguments and credentials before logging. Resource failures are
|
|
99
|
+
masked without raw logging.
|
|
100
|
+
|
|
101
|
+
### Better-auth origins — `./better-auth`
|
|
102
|
+
|
|
103
|
+
Fail-closed trusted-origin parsing and HTTPS cookie attributes. Does **not** import
|
|
104
|
+
`better-auth` — the host still calls `betterAuth({...})`.
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
import {
|
|
108
|
+
cookieSettingsForSite,
|
|
109
|
+
trustedOriginsFromCsv,
|
|
110
|
+
} from "@vllnt/convex-helpers/better-auth";
|
|
111
|
+
|
|
112
|
+
const trustedOrigins = trustedOriginsFromCsv(
|
|
113
|
+
process.env.CONVEX_SITE_URL ?? "http://localhost:3211",
|
|
114
|
+
process.env.BETTER_AUTH_TRUSTED_ORIGINS ?? "",
|
|
115
|
+
);
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
`parseTrustedOrigin` rejects credentials, paths, query/hash, controls and arbitrary
|
|
119
|
+
wildcards. HTTP is allowed only for exact localhost/127.0.0.1 origins. An explicit
|
|
120
|
+
`previewPattern` permits only HTTPS patterns shaped like
|
|
121
|
+
`https://app-*-org.example.com` (one wildcard inside a prefixed/suffixed first label).
|
|
122
|
+
The host must trust **every** deployment matched by its auth provider's wildcard
|
|
123
|
+
semantics; syntax validation does not establish deployment ownership.
|
|
124
|
+
Invalid extras are discarded; if none survive, the list helpers return `undefined`,
|
|
125
|
+
so the host omits `trustedOrigins` and retains Better Auth defaults. They do not
|
|
126
|
+
validate the host's entire auth configuration. `cookieSettingsForSite(siteUrl)`
|
|
127
|
+
returns secure, HttpOnly, SameSite=None attributes for a valid HTTPS origin only;
|
|
128
|
+
it does not replace CSRF/origin protection and is for hosts requiring cross-site cookies.
|
|
129
|
+
|
|
130
|
+
### Identity — `./identity`
|
|
131
|
+
|
|
132
|
+
Map `ctx.auth.getUserIdentity()` to a host row. The host must configure a trusted
|
|
133
|
+
single issuer (subjects are issuer-local). To enforce it locally, pass
|
|
134
|
+
`{ auth: ctx.auth, trustedIssuer: "https://your-trusted-issuer.example" }` as the
|
|
135
|
+
helper context. A mismatched or missing issuer rejects before lookup/insert. The
|
|
136
|
+
issuer must be host configuration, never a request argument. Omitting it preserves
|
|
137
|
+
compatibility and relies on the host's single-issuer auth configuration.
|
|
138
|
+
Provide callbacks bound to the same
|
|
139
|
+
Convex mutation transaction, including the indexed lookup, for race-safe bootstrap.
|
|
140
|
+
Every competing creator must use the same lookup. Actions or remote callbacks are
|
|
141
|
+
not race-safe. `isAnonymous` reflects only a literal `true` JWT claim; missing/false
|
|
142
|
+
claims do not prove a verified or identified account.
|
|
143
|
+
|
|
144
|
+
`retargetRows(rows, apply)` processes at most 1000 rows sequentially, counts
|
|
145
|
+
`patched`/`deleted`/`skipped`, and propagates failures without starting later rows.
|
|
146
|
+
The host supplies bounded pages, authorization for both identities, conflict rules,
|
|
147
|
+
continuation, transaction/rollback and logging. It is **not** a secure account merge.
|
|
148
|
+
These helpers neither construct `betterAuth` nor install its anonymous plugin.
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
import { getOrCreateFromAuth, retargetRows } from "@vllnt/convex-helpers/identity";
|
|
152
|
+
|
|
153
|
+
const user = await getOrCreateFromAuth(ctx, {
|
|
154
|
+
lookup: (subject) =>
|
|
155
|
+
ctx.db.query("users").withIndex("by_betterauth_id", (q) =>
|
|
156
|
+
q.eq("betterAuthId", subject),
|
|
157
|
+
).unique(),
|
|
158
|
+
insert: async (subject) => {
|
|
159
|
+
const id = await ctx.db.insert("users", { betterAuthId: subject });
|
|
160
|
+
return (await ctx.db.get(id))!;
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
```
|
|
164
|
+
|
|
92
165
|
Migrating from `@vllnt/convex-mcp`? Change the import to `@vllnt/convex-helpers/mcp` — the API is
|
|
93
166
|
identical. See [docs/API.md](./docs/API.md#mcp-tools-mcp) for the full `./mcp` reference.
|
|
94
167
|
|
|
@@ -104,7 +177,11 @@ pnpm test:coverage # must reach 100%
|
|
|
104
177
|
```
|
|
105
178
|
|
|
106
179
|
Tests use [vitest](https://vitest.dev) with the node environment. 100% coverage is enforced via
|
|
107
|
-
`vitest.config.mts` thresholds.
|
|
180
|
+
`vitest.config.mts` thresholds. Identity integration tests use **convex-test mocks**;
|
|
181
|
+
they do not prove real Better Auth sessions or anonymous plugin integration.
|
|
182
|
+
The separate `scripts/local-identity-proof.mjs` passed 16 concurrent real local-backend
|
|
183
|
+
bootstrap mutations with an injected synthetic identity (one row); this tests indexed
|
|
184
|
+
OCC, not login. Consuming apps must verify their actual auth integration independently.
|
|
108
185
|
|
|
109
186
|
## Contributing
|
|
110
187
|
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fail-closed origin and cookie helpers for `@convex-dev/better-auth`.
|
|
3
|
+
* Does not import better-auth — the host still constructs `betterAuth(...)`.
|
|
4
|
+
*/
|
|
5
|
+
export type CookieSettings = {
|
|
6
|
+
advanced: {
|
|
7
|
+
defaultCookieAttributes: {
|
|
8
|
+
httpOnly: true;
|
|
9
|
+
sameSite: "none";
|
|
10
|
+
secure: true;
|
|
11
|
+
};
|
|
12
|
+
useSecureCookies: true;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Parse one origin. HTTPS, no credentials/path/query/hash, no wildcards
|
|
17
|
+
* except an exact `previewPattern` (e.g. `https://app-*-org.vercel.app`).
|
|
18
|
+
*/
|
|
19
|
+
export declare function parseTrustedOrigin(origin: string, options?: {
|
|
20
|
+
previewPattern?: string;
|
|
21
|
+
}): string | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* Merge `siteUrl` with extra origins. Returns `undefined` when nothing extra
|
|
24
|
+
* survived parsing — the host then omits `trustedOrigins`.
|
|
25
|
+
* `siteUrl` uses the same rules (localhost HTTP allowed).
|
|
26
|
+
*/
|
|
27
|
+
export declare function trustedOriginsFromList(siteUrl: string, extra: readonly string[], options?: {
|
|
28
|
+
previewPattern?: string;
|
|
29
|
+
}): string[] | undefined;
|
|
30
|
+
export declare function trustedOriginsFromCsv(siteUrl: string, csv: string, options?: {
|
|
31
|
+
previewPattern?: string;
|
|
32
|
+
}): string[] | undefined;
|
|
33
|
+
/** Secure cross-site cookies when `siteUrl` is HTTPS; otherwise `undefined`. */
|
|
34
|
+
export declare function cookieSettingsForSite(siteUrl: string): CookieSettings | undefined;
|
|
35
|
+
//# sourceMappingURL=better-auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"better-auth.d.ts","sourceRoot":"","sources":["../src/better-auth.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,EAAE;QACR,uBAAuB,EAAE;YACvB,QAAQ,EAAE,IAAI,CAAC;YACf,QAAQ,EAAE,MAAM,CAAC;YACjB,MAAM,EAAE,IAAI,CAAC;SACd,CAAC;QACF,gBAAgB,EAAE,IAAI,CAAC;KACxB,CAAC;CACH,CAAC;AAwDF;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,EACd,OAAO,GAAE;IAAE,cAAc,CAAC,EAAE,MAAM,CAAA;CAAO,GACxC,MAAM,GAAG,SAAS,CAwBpB;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,OAAO,GAAE;IAAE,cAAc,CAAC,EAAE,MAAM,CAAA;CAAO,GACxC,MAAM,EAAE,GAAG,SAAS,CAUtB;AAED,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,OAAO,GAAE;IAAE,cAAc,CAAC,EAAE,MAAM,CAAA;CAAO,GACxC,MAAM,EAAE,GAAG,SAAS,CAEtB;AAED,gFAAgF;AAChF,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,GACd,cAAc,GAAG,SAAS,CAiB5B"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fail-closed origin and cookie helpers for `@convex-dev/better-auth`.
|
|
3
|
+
* Does not import better-auth — the host still constructs `betterAuth(...)`.
|
|
4
|
+
*/
|
|
5
|
+
function hasControlCharacter(value) {
|
|
6
|
+
// Control-character detection is the point of this helper.
|
|
7
|
+
// eslint-disable-next-line no-control-regex -- C0 + DEL
|
|
8
|
+
return /[\u0000-\u001F\u007F]/u.test(value);
|
|
9
|
+
}
|
|
10
|
+
function isLocalHttp(origin) {
|
|
11
|
+
return (origin.startsWith("http://localhost") ||
|
|
12
|
+
origin.startsWith("http://127.0.0.1"));
|
|
13
|
+
}
|
|
14
|
+
function httpsOrigin(trimmed) {
|
|
15
|
+
try {
|
|
16
|
+
const url = new URL(trimmed);
|
|
17
|
+
if (url.protocol !== "https:" ||
|
|
18
|
+
url.username !== "" ||
|
|
19
|
+
url.password !== "" ||
|
|
20
|
+
url.pathname !== "/" ||
|
|
21
|
+
url.search !== "" ||
|
|
22
|
+
url.hash !== "" ||
|
|
23
|
+
(trimmed !== url.origin && trimmed !== `${url.origin}/`)) {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
return url.origin;
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function localHttpOrigin(trimmed) {
|
|
33
|
+
try {
|
|
34
|
+
const url = new URL(trimmed);
|
|
35
|
+
if (url.protocol !== "http:" ||
|
|
36
|
+
url.username !== "" ||
|
|
37
|
+
url.password !== "" ||
|
|
38
|
+
(url.hostname !== "localhost" && url.hostname !== "127.0.0.1") ||
|
|
39
|
+
url.pathname !== "/" ||
|
|
40
|
+
url.search !== "" ||
|
|
41
|
+
url.hash !== "" ||
|
|
42
|
+
(trimmed !== url.origin && trimmed !== `${url.origin}/`)) {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
return url.origin;
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Parse one origin. HTTPS, no credentials/path/query/hash, no wildcards
|
|
53
|
+
* except an exact `previewPattern` (e.g. `https://app-*-org.vercel.app`).
|
|
54
|
+
*/
|
|
55
|
+
export function parseTrustedOrigin(origin, options = {}) {
|
|
56
|
+
const trimmed = origin.trim();
|
|
57
|
+
if (trimmed === "")
|
|
58
|
+
return undefined;
|
|
59
|
+
if (hasControlCharacter(origin))
|
|
60
|
+
return undefined;
|
|
61
|
+
if (options.previewPattern !== undefined &&
|
|
62
|
+
trimmed === options.previewPattern &&
|
|
63
|
+
/^https:\/\/[a-z0-9]+(?:-[a-z0-9]+)*-\*-[a-z0-9]+(?:-[a-z0-9]+)*(?:\.[a-z0-9]+(?:-[a-z0-9]+)*){2,}$/u.test(trimmed)) {
|
|
64
|
+
return trimmed;
|
|
65
|
+
}
|
|
66
|
+
if (isLocalHttp(trimmed)) {
|
|
67
|
+
return localHttpOrigin(trimmed);
|
|
68
|
+
}
|
|
69
|
+
if (!trimmed.startsWith("https://") ||
|
|
70
|
+
trimmed.includes("*") ||
|
|
71
|
+
trimmed.includes("?")) {
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
return httpsOrigin(trimmed);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Merge `siteUrl` with extra origins. Returns `undefined` when nothing extra
|
|
78
|
+
* survived parsing — the host then omits `trustedOrigins`.
|
|
79
|
+
* `siteUrl` uses the same rules (localhost HTTP allowed).
|
|
80
|
+
*/
|
|
81
|
+
export function trustedOriginsFromList(siteUrl, extra, options = {}) {
|
|
82
|
+
const parsed = extra
|
|
83
|
+
.map((origin) => parseTrustedOrigin(origin, options))
|
|
84
|
+
.filter((origin) => origin !== undefined);
|
|
85
|
+
if (parsed.length === 0)
|
|
86
|
+
return undefined;
|
|
87
|
+
const site = parseTrustedOrigin(siteUrl, options);
|
|
88
|
+
if (site === undefined) {
|
|
89
|
+
return parsed;
|
|
90
|
+
}
|
|
91
|
+
return [site, ...parsed];
|
|
92
|
+
}
|
|
93
|
+
export function trustedOriginsFromCsv(siteUrl, csv, options = {}) {
|
|
94
|
+
return trustedOriginsFromList(siteUrl, csv.split(","), options);
|
|
95
|
+
}
|
|
96
|
+
/** Secure cross-site cookies when `siteUrl` is HTTPS; otherwise `undefined`. */
|
|
97
|
+
export function cookieSettingsForSite(siteUrl) {
|
|
98
|
+
if (!siteUrl.startsWith("https://") ||
|
|
99
|
+
parseTrustedOrigin(siteUrl) === undefined) {
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
advanced: {
|
|
104
|
+
defaultCookieAttributes: {
|
|
105
|
+
httpOnly: true,
|
|
106
|
+
sameSite: "none",
|
|
107
|
+
secure: true,
|
|
108
|
+
},
|
|
109
|
+
useSecureCookies: true,
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=better-auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"better-auth.js","sourceRoot":"","sources":["../src/better-auth.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAaH,SAAS,mBAAmB,CAAC,KAAa;IACxC,2DAA2D;IAC3D,wDAAwD;IACxD,OAAO,wBAAwB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAC9C,CAAC;AAED,SAAS,WAAW,CAAC,MAAc;IACjC,OAAO,CACL,MAAM,CAAC,UAAU,CAAC,kBAAkB,CAAC;QACrC,MAAM,CAAC,UAAU,CAAC,kBAAkB,CAAC,CACtC,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,OAAe;IAClC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;QAC7B,IACE,GAAG,CAAC,QAAQ,KAAK,QAAQ;YACzB,GAAG,CAAC,QAAQ,KAAK,EAAE;YACnB,GAAG,CAAC,QAAQ,KAAK,EAAE;YACnB,GAAG,CAAC,QAAQ,KAAK,GAAG;YACpB,GAAG,CAAC,MAAM,KAAK,EAAE;YACjB,GAAG,CAAC,IAAI,KAAK,EAAE;YACf,CAAC,OAAO,KAAK,GAAG,CAAC,MAAM,IAAI,OAAO,KAAK,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,EACxD,CAAC;YACD,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,OAAO,GAAG,CAAC,MAAM,CAAC;IACpB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,SAAS,eAAe,CAAC,OAAe;IACtC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;QAC7B,IACE,GAAG,CAAC,QAAQ,KAAK,OAAO;YACxB,GAAG,CAAC,QAAQ,KAAK,EAAE;YACnB,GAAG,CAAC,QAAQ,KAAK,EAAE;YACnB,CAAC,GAAG,CAAC,QAAQ,KAAK,WAAW,IAAI,GAAG,CAAC,QAAQ,KAAK,WAAW,CAAC;YAC9D,GAAG,CAAC,QAAQ,KAAK,GAAG;YACpB,GAAG,CAAC,MAAM,KAAK,EAAE;YACjB,GAAG,CAAC,IAAI,KAAK,EAAE;YACf,CAAC,OAAO,KAAK,GAAG,CAAC,MAAM,IAAI,OAAO,KAAK,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,EACxD,CAAC;YACD,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,OAAO,GAAG,CAAC,MAAM,CAAC;IACpB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAChC,MAAc,EACd,OAAO,GAAgC,EAAE;IAEzC,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;IAC9B,IAAI,OAAO,KAAK,EAAE;QAAE,OAAO,SAAS,CAAC;IACrC,IAAI,mBAAmB,CAAC,MAAM,CAAC;QAAE,OAAO,SAAS,CAAC;IAClD,IACE,OAAO,CAAC,cAAc,KAAK,SAAS;QACpC,OAAO,KAAK,OAAO,CAAC,cAAc;QAClC,qGAAqG,CAAC,IAAI,CACxG,OAAO,CACR,EACD,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,IAAI,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;QACzB,OAAO,eAAe,CAAC,OAAO,CAAC,CAAC;IAClC,CAAC;IACD,IACE,CAAC,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC;QAC/B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC;QACrB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,EACrB,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,WAAW,CAAC,OAAO,CAAC,CAAC;AAC9B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CACpC,OAAe,EACf,KAAwB,EACxB,OAAO,GAAgC,EAAE;IAEzC,MAAM,MAAM,GAAG,KAAK;SACjB,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,kBAAkB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;SACpD,MAAM,CAAC,CAAC,MAAM,EAAoB,EAAE,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC;IAC9D,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC1C,MAAM,IAAI,GAAG,kBAAkB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAClD,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,OAAO,CAAC,IAAI,EAAE,GAAG,MAAM,CAAC,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,qBAAqB,CACnC,OAAe,EACf,GAAW,EACX,OAAO,GAAgC,EAAE;IAEzC,OAAO,sBAAsB,CAAC,OAAO,EAAE,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC;AAClE,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,qBAAqB,CACnC,OAAe;IAEf,IACE,CAAC,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC;QAC/B,kBAAkB,CAAC,OAAO,CAAC,KAAK,SAAS,EACzC,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO;QACL,QAAQ,EAAE;YACR,uBAAuB,EAAE;gBACvB,QAAQ,EAAE,IAAI;gBACd,QAAQ,EAAE,MAAM;gBAChB,MAAM,EAAE,IAAI;aACb;YACD,gBAAgB,EAAE,IAAI;SACvB;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host-ctx identity glue: map `ctx.auth` to a host row, and retarget rows when
|
|
3
|
+
* an anonymous subject upgrades. Writes the **host's** tables — not a component.
|
|
4
|
+
*/
|
|
5
|
+
export type AuthIdentity = {
|
|
6
|
+
isAnonymous?: unknown;
|
|
7
|
+
issuer?: string;
|
|
8
|
+
subject: string;
|
|
9
|
+
};
|
|
10
|
+
export type AuthCtx = {
|
|
11
|
+
auth: {
|
|
12
|
+
getUserIdentity: () => Promise<AuthIdentity | null>;
|
|
13
|
+
};
|
|
14
|
+
/** Host-configured issuer; never accept this value from request arguments. */
|
|
15
|
+
trustedIssuer?: string;
|
|
16
|
+
};
|
|
17
|
+
export declare function isAnonymousIdentity(identity: AuthIdentity): boolean;
|
|
18
|
+
export declare function requireIdentity(ctx: AuthCtx): Promise<AuthIdentity>;
|
|
19
|
+
/**
|
|
20
|
+
* Query-side lookup. Returns null when unauthenticated or the host row is not
|
|
21
|
+
* there yet (BetterAuth `onCreate` can lag a fresh anonymous session).
|
|
22
|
+
*/
|
|
23
|
+
export declare function getFromAuth<T extends object>(ctx: AuthCtx, lookup: (subject: string) => Promise<null | T>): Promise<null | (T & {
|
|
24
|
+
isAnonymous: boolean;
|
|
25
|
+
})>;
|
|
26
|
+
export declare function requireFromAuth<T extends object>(ctx: AuthCtx, lookup: (subject: string) => Promise<null | T>): Promise<T & {
|
|
27
|
+
isAnonymous: boolean;
|
|
28
|
+
}>;
|
|
29
|
+
/**
|
|
30
|
+
* Mutation-side: look up the host row for the current identity, or insert it.
|
|
31
|
+
* Atomic when both callbacks use the SAME Convex mutation transaction,
|
|
32
|
+
* including an indexed lookup; every competing writer must use that lookup.
|
|
33
|
+
* Actions, remote calls and nontransactional callbacks do not provide uniqueness.
|
|
34
|
+
*/
|
|
35
|
+
export declare function getOrCreateFromAuth<T extends object>(ctx: AuthCtx, handlers: {
|
|
36
|
+
insert: (subject: string) => Promise<T>;
|
|
37
|
+
lookup: (subject: string) => Promise<null | T>;
|
|
38
|
+
}): Promise<T & {
|
|
39
|
+
isAnonymous: boolean;
|
|
40
|
+
}>;
|
|
41
|
+
/**
|
|
42
|
+
* Process at most 1000 rows sequentially (one callback in flight).
|
|
43
|
+
* The host owns authorization, page size, continuation, conflict resolution and
|
|
44
|
+
* transaction boundaries. This is not an account merge or ownership check.
|
|
45
|
+
*/
|
|
46
|
+
export declare function retargetRows<T>(rows: readonly T[], apply: (row: T) => Promise<"deleted" | "patched" | "skipped">): Promise<{
|
|
47
|
+
deleted: number;
|
|
48
|
+
patched: number;
|
|
49
|
+
skipped: number;
|
|
50
|
+
}>;
|
|
51
|
+
//# sourceMappingURL=identity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,MAAM,MAAM,YAAY,GAAG;IACzB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG;IACpB,IAAI,EAAE;QACJ,eAAe,EAAE,MAAM,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;KACrD,CAAC;IACF,8EAA8E;IAC9E,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,YAAY,GAAG,OAAO,CAEnE;AAcD,wBAAsB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,YAAY,CAAC,CAUzE;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,CAAC,SAAS,MAAM,EAChD,GAAG,EAAE,OAAO,EACZ,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,GAAG,CAAC,CAAC,GAC7C,OAAO,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG;IAAE,WAAW,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC,CAOhD;AAED,wBAAsB,eAAe,CAAC,CAAC,SAAS,MAAM,EACpD,GAAG,EAAE,OAAO,EACZ,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,GAAG,CAAC,CAAC,GAC7C,OAAO,CAAC,CAAC,GAAG;IAAE,WAAW,EAAE,OAAO,CAAA;CAAE,CAAC,CASvC;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CAAC,CAAC,SAAS,MAAM,EACxD,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE;IACR,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IACxC,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;CAChD,GACA,OAAO,CAAC,CAAC,GAAG;IAAE,WAAW,EAAE,OAAO,CAAA;CAAE,CAAC,CAKvC;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,CAAC,EAClC,IAAI,EAAE,SAAS,CAAC,EAAE,EAClB,KAAK,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,OAAO,CAAC,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC,GAC5D,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAchE"}
|
package/dist/identity.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host-ctx identity glue: map `ctx.auth` to a host row, and retarget rows when
|
|
3
|
+
* an anonymous subject upgrades. Writes the **host's** tables — not a component.
|
|
4
|
+
*/
|
|
5
|
+
import { ConvexError } from "convex/values";
|
|
6
|
+
export function isAnonymousIdentity(identity) {
|
|
7
|
+
return identity.isAnonymous === true;
|
|
8
|
+
}
|
|
9
|
+
function assertTrustedIssuer(ctx, identity) {
|
|
10
|
+
if (ctx.trustedIssuer !== undefined &&
|
|
11
|
+
identity.issuer !== ctx.trustedIssuer) {
|
|
12
|
+
throw new ConvexError({
|
|
13
|
+
code: "UNAUTHENTICATED",
|
|
14
|
+
message: "Untrusted issuer.",
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export async function requireIdentity(ctx) {
|
|
19
|
+
const identity = await ctx.auth.getUserIdentity();
|
|
20
|
+
if (identity === null) {
|
|
21
|
+
throw new ConvexError({
|
|
22
|
+
code: "UNAUTHENTICATED",
|
|
23
|
+
message: "Sign-in required.",
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
assertTrustedIssuer(ctx, identity);
|
|
27
|
+
return identity;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Query-side lookup. Returns null when unauthenticated or the host row is not
|
|
31
|
+
* there yet (BetterAuth `onCreate` can lag a fresh anonymous session).
|
|
32
|
+
*/
|
|
33
|
+
export async function getFromAuth(ctx, lookup) {
|
|
34
|
+
const identity = await ctx.auth.getUserIdentity();
|
|
35
|
+
if (identity === null)
|
|
36
|
+
return null;
|
|
37
|
+
assertTrustedIssuer(ctx, identity);
|
|
38
|
+
const row = await lookup(identity.subject);
|
|
39
|
+
if (row === null)
|
|
40
|
+
return null;
|
|
41
|
+
return { ...row, isAnonymous: isAnonymousIdentity(identity) };
|
|
42
|
+
}
|
|
43
|
+
export async function requireFromAuth(ctx, lookup) {
|
|
44
|
+
const user = await getFromAuth(ctx, lookup);
|
|
45
|
+
if (user === null) {
|
|
46
|
+
throw new ConvexError({
|
|
47
|
+
code: "USER_NOT_INITIALIZED",
|
|
48
|
+
message: "User not initialized — the auth session may still be settling.",
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
return user;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Mutation-side: look up the host row for the current identity, or insert it.
|
|
55
|
+
* Atomic when both callbacks use the SAME Convex mutation transaction,
|
|
56
|
+
* including an indexed lookup; every competing writer must use that lookup.
|
|
57
|
+
* Actions, remote calls and nontransactional callbacks do not provide uniqueness.
|
|
58
|
+
*/
|
|
59
|
+
export async function getOrCreateFromAuth(ctx, handlers) {
|
|
60
|
+
const identity = await requireIdentity(ctx);
|
|
61
|
+
const existing = await handlers.lookup(identity.subject);
|
|
62
|
+
const row = existing ?? (await handlers.insert(identity.subject));
|
|
63
|
+
return { ...row, isAnonymous: isAnonymousIdentity(identity) };
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Process at most 1000 rows sequentially (one callback in flight).
|
|
67
|
+
* The host owns authorization, page size, continuation, conflict resolution and
|
|
68
|
+
* transaction boundaries. This is not an account merge or ownership check.
|
|
69
|
+
*/
|
|
70
|
+
export async function retargetRows(rows, apply) {
|
|
71
|
+
if (rows.length > 1000) {
|
|
72
|
+
throw new RangeError("retargetRows accepts at most 1000 rows per page");
|
|
73
|
+
}
|
|
74
|
+
return rows.reduce(async (previous, row) => {
|
|
75
|
+
const counts = await previous;
|
|
76
|
+
const outcome = await apply(row);
|
|
77
|
+
return { ...counts, [outcome]: counts[outcome] + 1 };
|
|
78
|
+
}, Promise.resolve({ deleted: 0, patched: 0, skipped: 0 }));
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=identity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity.js","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"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"}
|
package/dist/mcp/auth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/mcp/auth.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/mcp/auth.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CASlE;AAED,wBAAsB,eAAe,CACnC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,UAAU,GACf,OAAO,CACN;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,IAAI,CAAA;CAAE,GACrD;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,KAAK,CAAA;CAAE,CACvC,CA+CA"}
|
package/dist/mcp/auth.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { policyCallback } from "./callback.js";
|
|
1
2
|
export function extractApiKey(request) {
|
|
2
3
|
const authHeader = request.headers.get("authorization");
|
|
3
4
|
if (!authHeader)
|
|
@@ -17,20 +18,32 @@ export async function validateRequest(request, auth) {
|
|
|
17
18
|
valid: false,
|
|
18
19
|
};
|
|
19
20
|
}
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
try {
|
|
22
|
+
const isValid = await policyCallback(() => auth.validate(apiKey));
|
|
23
|
+
if (!isValid) {
|
|
24
|
+
return {
|
|
25
|
+
response: new Response(JSON.stringify({ error: "Invalid API key." }), {
|
|
26
|
+
headers: { "Content-Type": "application/json" },
|
|
27
|
+
status: 401,
|
|
28
|
+
}),
|
|
29
|
+
valid: false,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
let convexToken;
|
|
33
|
+
if (auth.convexToken) {
|
|
34
|
+
const resolveToken = auth.convexToken;
|
|
35
|
+
convexToken = await policyCallback(() => resolveToken(apiKey));
|
|
36
|
+
}
|
|
37
|
+
return { apiKey, convexToken, valid: true };
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
22
40
|
return {
|
|
23
|
-
response: new Response(JSON.stringify({ error: "
|
|
41
|
+
response: new Response(JSON.stringify({ error: "Authentication failed." }), {
|
|
24
42
|
headers: { "Content-Type": "application/json" },
|
|
25
43
|
status: 401,
|
|
26
44
|
}),
|
|
27
45
|
valid: false,
|
|
28
46
|
};
|
|
29
47
|
}
|
|
30
|
-
let convexToken;
|
|
31
|
-
if (auth.convexToken) {
|
|
32
|
-
convexToken = await auth.convexToken(apiKey);
|
|
33
|
-
}
|
|
34
|
-
return { apiKey, convexToken, valid: true };
|
|
35
48
|
}
|
|
36
49
|
//# sourceMappingURL=auth.js.map
|
package/dist/mcp/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/mcp/auth.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/mcp/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAG/C,MAAM,UAAU,aAAa,CAAC,OAAgB;IAC5C,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IACxD,IAAI,CAAC,UAAU;QAAE,OAAO,SAAS,CAAC;IAElC,IAAI,UAAU,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QACrC,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC7B,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,OAAgB,EAChB,IAAgB;IAKhB,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;IAEtC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO;YACL,QAAQ,EAAE,IAAI,QAAQ,CACpB,IAAI,CAAC,SAAS,CAAC;gBACb,KAAK,EACH,gEAAgE;aACnE,CAAC,EACF,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CACjE;YACD,KAAK,EAAE,KAAK;SACb,CAAC;IACJ,CAAC;IAED,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;QAClE,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO;gBACL,QAAQ,EAAE,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAC,EAAE;oBACpE,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;oBAC/C,MAAM,EAAE,GAAG;iBACZ,CAAC;gBACF,KAAK,EAAE,KAAK;aACb,CAAC;QACJ,CAAC;QAED,IAAI,WAA+B,CAAC;QACpC,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,MAAM,YAAY,GAAG,IAAI,CAAC,WAAW,CAAC;YACtC,WAAW,GAAG,MAAM,cAAc,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC;QACjE,CAAC;QAED,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAC9C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;YACL,QAAQ,EAAE,IAAI,QAAQ,CACpB,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,wBAAwB,EAAE,CAAC,EACnD;gBACE,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;gBAC/C,MAAM,EAAE,GAAG;aACZ,CACF;YACD,KAAK,EAAE,KAAK;SACb,CAAC;IACJ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"callback.d.ts","sourceRoot":"","sources":["../../src/mcp/callback.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,wBAAsB,cAAc,CAAC,CAAC,EACpC,QAAQ,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GAC7B,OAAO,CAAC,CAAC,CAAC,CAcZ"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Bound host policy callbacks; timeout does not cancel host-owned work. */
|
|
2
|
+
export async function policyCallback(callback) {
|
|
3
|
+
let handle;
|
|
4
|
+
try {
|
|
5
|
+
return await Promise.race([
|
|
6
|
+
Promise.resolve().then(callback),
|
|
7
|
+
new Promise((_, reject) => {
|
|
8
|
+
handle = setTimeout(() => {
|
|
9
|
+
reject(new Error("Policy callback timed out"));
|
|
10
|
+
}, 10_000);
|
|
11
|
+
}),
|
|
12
|
+
]);
|
|
13
|
+
}
|
|
14
|
+
finally {
|
|
15
|
+
clearTimeout(handle);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=callback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"callback.js","sourceRoot":"","sources":["../../src/mcp/callback.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,QAA8B;IAE9B,IAAI,MAAiD,CAAC;IACtD,IAAI,CAAC;QACH,OAAO,MAAM,OAAO,CAAC,IAAI,CAAC;YACxB,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC;YAChC,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;gBAC/B,MAAM,GAAG,UAAU,CAAC,GAAG,EAAE;oBACvB,MAAM,CAAC,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC,CAAC;gBACjD,CAAC,EAAE,MAAM,CAAC,CAAC;YACb,CAAC,CAAC;SACH,CAAC,CAAC;IACL,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,MAAM,CAAC,CAAC;IACvB,CAAC;AACH,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../../src/mcp/resources/register.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAG3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,KAAK,gBAAgB,GAAG;IACtB,UAAU,EAAE,UAAU,CAAC,OAAO,eAAe,CAAC,GAAG,SAAS,CAAC;IAC3D,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,WAAW,EAAE,WAAW,CAAC;IACzB,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,GACrC,gBAAgB,EAAE,CAUpB;AAED,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,SAAS,EACpB,MAAM,EAAE,YAAY,EACpB,SAAS,EAAE,gBAAgB,EAAE,GAC5B,IAAI,
|
|
1
|
+
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../../src/mcp/resources/register.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAG3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,KAAK,gBAAgB,GAAG;IACtB,UAAU,EAAE,UAAU,CAAC,OAAO,eAAe,CAAC,GAAG,SAAS,CAAC;IAC3D,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,WAAW,EAAE,WAAW,CAAC;IACzB,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,GACrC,gBAAgB,EAAE,CAUpB;AAED,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,SAAS,EACpB,MAAM,EAAE,YAAY,EACpB,SAAS,EAAE,gBAAgB,EAAE,GAC5B,IAAI,CAuCN"}
|
|
@@ -31,13 +31,8 @@ export function registerResources(mcpServer, client, resources) {
|
|
|
31
31
|
],
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
|
-
catch
|
|
35
|
-
|
|
36
|
-
error,
|
|
37
|
-
resource: uriPattern,
|
|
38
|
-
});
|
|
39
|
-
// Mask the raw Convex error from the client (parity with the tool
|
|
40
|
-
// handler); the real error is logged server-side above.
|
|
34
|
+
catch {
|
|
35
|
+
// Do not log raw exceptions: provider errors may contain secrets.
|
|
41
36
|
throw new Error("Resource read failed");
|
|
42
37
|
}
|
|
43
38
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register.js","sourceRoot":"","sources":["../../../src/mcp/resources/register.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAE3E,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAYnD,MAAM,UAAU,gBAAgB,CAC9B,SAAsC;IAEtC,OAAO,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,EAAE,WAAW,CAAC,EAAE,EAAE,CAAC,CAAC;QACnE,UAAU,EAAE,WAAW,CAAC,IAAI;YAC1B,CAAC,CAAC,eAAe,CAAC,WAAW,CAAC,IAAI,CAAC;YACnC,CAAC,CAAC,SAAS;QACb,WAAW,EAAE,WAAW,CAAC,WAAW;QACpC,WAAW;QACX,QAAQ,EAAE,IAAI,gBAAgB,CAAC,UAAU,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;QAC/D,UAAU;KACX,CAAC,CAAC,CAAC;AACN,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,SAAoB,EACpB,MAAoB,EACpB,SAA6B;IAE7B,KAAK,MAAM,EACT,UAAU,EACV,WAAW,EACX,WAAW,EACX,QAAQ,EACR,UAAU,GACX,IAAI,SAAS,EAAE,CAAC;QACf,SAAS,CAAC,QAAQ,CAChB,UAAU,EACV,QAAQ,EACR;YACE,WAAW;YACX,QAAQ,EAAE,kBAAkB;SAC7B,EACD,KAAK,EAAE,GAAG,EAAE,UAAmC,EAAE,EAAE;YACjD,IAAI,CAAC;gBACH,MAAM,mBAAmB,GACvB,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC;gBAC9C,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,KAAK,CAC/B,WAAW,CAAC,GAAG,EACf,mBAAmB,CACpB,CAAC;gBACF,OAAO;oBACL,QAAQ,EAAE;wBACR;4BACE,QAAQ,EAAE,kBAAkB;4BAC5B,IAAI,EAAE,eAAe,CAAC,MAAM,CAAC;4BAC7B,GAAG,EAAE,GAAG,CAAC,IAAI;yBACd;qBACF;iBACF,CAAC;YACJ,CAAC;YAAC,
|
|
1
|
+
{"version":3,"file":"register.js","sourceRoot":"","sources":["../../../src/mcp/resources/register.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAE3E,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAYnD,MAAM,UAAU,gBAAgB,CAC9B,SAAsC;IAEtC,OAAO,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,EAAE,WAAW,CAAC,EAAE,EAAE,CAAC,CAAC;QACnE,UAAU,EAAE,WAAW,CAAC,IAAI;YAC1B,CAAC,CAAC,eAAe,CAAC,WAAW,CAAC,IAAI,CAAC;YACnC,CAAC,CAAC,SAAS;QACb,WAAW,EAAE,WAAW,CAAC,WAAW;QACpC,WAAW;QACX,QAAQ,EAAE,IAAI,gBAAgB,CAAC,UAAU,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;QAC/D,UAAU;KACX,CAAC,CAAC,CAAC;AACN,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,SAAoB,EACpB,MAAoB,EACpB,SAA6B;IAE7B,KAAK,MAAM,EACT,UAAU,EACV,WAAW,EACX,WAAW,EACX,QAAQ,EACR,UAAU,GACX,IAAI,SAAS,EAAE,CAAC;QACf,SAAS,CAAC,QAAQ,CAChB,UAAU,EACV,QAAQ,EACR;YACE,WAAW;YACX,QAAQ,EAAE,kBAAkB;SAC7B,EACD,KAAK,EAAE,GAAG,EAAE,UAAmC,EAAE,EAAE;YACjD,IAAI,CAAC;gBACH,MAAM,mBAAmB,GACvB,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC;gBAC9C,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,KAAK,CAC/B,WAAW,CAAC,GAAG,EACf,mBAAmB,CACpB,CAAC;gBACF,OAAO;oBACL,QAAQ,EAAE;wBACR;4BACE,QAAQ,EAAE,kBAAkB;4BAC5B,IAAI,EAAE,eAAe,CAAC,MAAM,CAAC;4BAC7B,GAAG,EAAE,GAAG,CAAC,IAAI;yBACd;qBACF;iBACF,CAAC;YACJ,CAAC;YAAC,MAAM,CAAC;gBACP,kEAAkE;gBAClE,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;YAC1C,CAAC;QACH,CAAC,CACF,CAAC;IACJ,CAAC;AACH,CAAC"}
|
package/dist/mcp/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAgB,eAAe,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAuB9E,wBAAgB,eAAe,CAAC,MAAM,EAAE,YAAY,GAAG,eAAe,CAyJrE"}
|
package/dist/mcp/server.js
CHANGED
|
@@ -5,7 +5,7 @@ import { validateRequest } from "./auth.js";
|
|
|
5
5
|
import { createPaginationContext } from "./pagination/context.js";
|
|
6
6
|
import { getOriginalToolsList, registerPaginationHandlers, registerTwoPhaseHandlers, } from "./pagination/handlers.js";
|
|
7
7
|
import { prepareResources, registerResources } from "./resources/register.js";
|
|
8
|
-
import {
|
|
8
|
+
import { prepareTools, registerTools } from "./tools/register.js";
|
|
9
9
|
function createDefaultClient(convexUrl, convexToken) {
|
|
10
10
|
const client = new ConvexHttpClient(convexUrl);
|
|
11
11
|
if (convexToken) {
|
|
@@ -49,18 +49,6 @@ export function createMCPServer(config) {
|
|
|
49
49
|
const prepared = prepareTools(config.tools ?? {});
|
|
50
50
|
const preparedRes = prepareResources(config.resources ?? {});
|
|
51
51
|
const paginationCtx = createPaginationContext(config.pagination);
|
|
52
|
-
if (!hooks?.onToolCall) {
|
|
53
|
-
const stripped = findToolsWithReservedArguments(config.tools ?? {});
|
|
54
|
-
if (stripped.size > 0) {
|
|
55
|
-
const summary = [...stripped.entries()]
|
|
56
|
-
.map(([tool, keys]) => `${tool} (${keys.join(", ")})`)
|
|
57
|
-
.join("; ");
|
|
58
|
-
console.warn(`[convex-mcp] tools declare reserved \`_*\` args but no onToolCall hook is configured. ` +
|
|
59
|
-
`These args are stripped from the published schema and will never be injected, ` +
|
|
60
|
-
`so every dispatched call will fail Convex validation. ` +
|
|
61
|
-
`Configure hooks.onToolCall to inject them via extendArgs. Affected tools: ${summary}`);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
52
|
function createServerAndTransport(requestId, convexToken, apiKey) {
|
|
65
53
|
const mcpServer = new McpServer({
|
|
66
54
|
name: serverName,
|