@zykeco/sync-server 0.6.0 → 0.7.1

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 CHANGED
@@ -102,6 +102,76 @@ reply: { wiped: { dailyMetrics, weeklyMetrics, sleepSessions: number }, serverNo
102
102
 
103
103
  Deletes every row across the three sync namespaces (blobs untouched). The literal `confirm` string is enforced at the protocol layer.
104
104
 
105
+ ## MCP server
106
+
107
+ The server ships an [MCP](https://modelcontextprotocol.io) endpoint at `POST /mcp` that exposes read-only tools over your synced data. Auth supports two OAuth 2.1 flows:
108
+
109
+ - **`authorization_code` + PKCE** — for interactive clients like the Claude.ai web app and Claude Desktop "Custom Connectors". Dynamic Client Registration (RFC 7591) is supported.
110
+ - **`client_credentials`** — for headless agents / CLIs that hold the `READ_SECRET` directly.
111
+
112
+ ### Required env vars
113
+
114
+ | Var | Default | Purpose |
115
+ | ----------------------- | ------- | -------------------------------------------------------------------------------------------------- |
116
+ | `MCP_ENABLED` | `true` | Set `false` to disable the `/mcp` mount entirely. |
117
+ | `MCP_CLIENT_ID` | _req._ | Bootstrap client ID for the headless `client_credentials` grant. Public — pick anything memorable. |
118
+ | `MCP_TOKEN_SECRET` | _req._ | HMAC key for signing bearer tokens. ≥ 32 random bytes. Treat as a secret. |
119
+ | `MCP_TOKEN_TTL_SECONDS` | `3600` | Bearer token lifetime. |
120
+
121
+ ### Endpoints
122
+
123
+ | Method | Path | Purpose |
124
+ | ------ | ----------------------------------------- | ------------------------------------------------------- |
125
+ | GET | `/.well-known/oauth-authorization-server` | RFC 8414 metadata (root-level, what MCP clients probe). |
126
+ | GET | `/.well-known/oauth-protected-resource` | RFC 9728 resource metadata pointing back to the AS. |
127
+ | POST | `/mcp/register` | Dynamic Client Registration (RFC 7591). |
128
+ | GET | `/mcp/authorize` | Consent screen (PKCE flow). |
129
+ | POST | `/mcp/authorize` | Consent form submit — issues an authorization code. |
130
+ | POST | `/mcp/oauth/token` | Token endpoint (both grants). |
131
+ | POST | `/mcp` | MCP JSON-RPC. Requires `Authorization: Bearer <token>`. |
132
+
133
+ ### Connecting Claude.ai (web or desktop) as a custom connector
134
+
135
+ 1. **Deploy your server with `MCP_*` env vars set** and confirm it answers on a public HTTPS URL. From a browser, `https://YOUR_HOST/.well-known/oauth-authorization-server` should return JSON. If you self-host behind a proxy, make sure that path is forwarded to the sync server.
136
+ 2. **Open Claude.ai → Settings → Connectors → Add custom connector.**
137
+ 3. **Server URL:** enter `https://YOUR_HOST/mcp` (the `/mcp` path matters — that's the JSON-RPC endpoint, not the host root).
138
+ 4. **Click Connect.** Claude.ai will:
139
+ - Fetch the discovery doc at `/.well-known/oauth-authorization-server`.
140
+ - Self-register via `POST /mcp/register`.
141
+ - Open a new tab to `https://YOUR_HOST/mcp/authorize?...`.
142
+ 5. **On the consent screen:** verify the "Redirects to" line shows `https://claude.ai/api/mcp/auth_callback`. Paste your `READ_SECRET` into the form and click **Authorize**.
143
+ 6. **You're done.** Claude.ai redirects back, exchanges the code for a bearer token, and the connector goes online. The eight read tools (`list_daily_metrics`, `list_sleep_sessions`, `get_user_profile`, …) become available in the chat tool picker.
144
+
145
+ Claude Desktop's "Add custom connector" flow is identical — same form, same URL.
146
+
147
+ #### What if Claude.ai shows a 404 at `/authorize`?
148
+
149
+ That means it's hitting the host root instead of the discovered path. Two causes:
150
+
151
+ - The discovery doc isn't reachable. Curl `https://YOUR_HOST/.well-known/oauth-authorization-server` and confirm you get JSON, not your reverse proxy's 404 page. Add the path to your proxy rules.
152
+ - You're on `@zykeco/sync-server < 0.7.0`. Upgrade — the auth-code flow ships from 0.7.0 onward.
153
+
154
+ #### Token / client lifetime
155
+
156
+ - Bearer tokens expire after `MCP_TOKEN_TTL_SECONDS` (default 1 h). Claude.ai re-runs the consent flow when expired.
157
+ - DCR client registrations are **in-memory** — they don't survive a server restart. If you restart the server, remove the connector in Claude.ai and re-add it; takes ten seconds.
158
+
159
+ ### Connecting from headless agents (`client_credentials`)
160
+
161
+ ```bash
162
+ # 1. Get a token using your READ_SECRET as the client secret.
163
+ curl -X POST https://YOUR_HOST/mcp/oauth/token \
164
+ -d grant_type=client_credentials \
165
+ -d client_id=$MCP_CLIENT_ID \
166
+ -d client_secret=$READ_SECRET
167
+
168
+ # 2. Use the access_token to call MCP.
169
+ curl -X POST https://YOUR_HOST/mcp \
170
+ -H "Authorization: Bearer $ACCESS_TOKEN" \
171
+ -H "content-type: application/json" \
172
+ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
173
+ ```
174
+
105
175
  ## Data model
106
176
 
107
177
  - **`daily_metrics`**, **`weekly_metrics`** — `{ id (PK), isoDate / weekStartIsoDate, metricKey, value, createdAt, updatedAt }`. Composite unique on `(date, metricKey)`.
package/dist/app.js CHANGED
@@ -49,7 +49,9 @@ export function buildApp(db, env) {
49
49
  hrZoneHistory: hrZoneHistoryStore,
50
50
  }, auth));
51
51
  if (env.mcp) {
52
- app.route('/mcp', mcpRoutes({ db, env, mcp: env.mcp }));
52
+ const { mcp, discovery } = mcpRoutes({ db, env, mcp: env.mcp });
53
+ app.route('/mcp', mcp);
54
+ app.route('/', discovery);
53
55
  }
54
56
  return app;
55
57
  }
package/dist/app.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ export interface RegisteredClient {
2
+ clientId: string;
3
+ redirectUris: string[];
4
+ clientName: string | null;
5
+ createdAt: number;
6
+ }
7
+ export interface AuthCodeRecord {
8
+ clientId: string;
9
+ redirectUri: string;
10
+ codeChallenge: string;
11
+ codeChallengeMethod: 'S256';
12
+ expiresAt: number;
13
+ }
14
+ export interface AuthStore {
15
+ bootstrap(clientId: string): void;
16
+ registerClient(input: {
17
+ redirectUris: string[];
18
+ clientName?: string | null;
19
+ }, nowMs?: number): RegisteredClient;
20
+ getClient(clientId: string): RegisteredClient | undefined;
21
+ issueCode(rec: Omit<AuthCodeRecord, 'expiresAt'>, nowMs?: number): string;
22
+ consumeCode(code: string, nowMs?: number): AuthCodeRecord | null;
23
+ clear(): void;
24
+ }
25
+ export declare function createAuthStore(): AuthStore;
@@ -0,0 +1,51 @@
1
+ import { randomBytes } from 'node:crypto';
2
+ const AUTH_CODE_TTL_MS = 5 * 60 * 1000;
3
+ export function createAuthStore() {
4
+ const clients = new Map();
5
+ const codes = new Map();
6
+ return {
7
+ bootstrap(clientId) {
8
+ if (!clients.has(clientId)) {
9
+ clients.set(clientId, {
10
+ clientId,
11
+ redirectUris: [],
12
+ clientName: 'bootstrap (client_credentials only)',
13
+ createdAt: Date.now(),
14
+ });
15
+ }
16
+ },
17
+ registerClient({ redirectUris, clientName = null }, nowMs = Date.now()) {
18
+ const clientId = `mcp_${randomBytes(18).toString('base64url')}`;
19
+ const client = {
20
+ clientId,
21
+ redirectUris: [...redirectUris],
22
+ clientName,
23
+ createdAt: nowMs,
24
+ };
25
+ clients.set(clientId, client);
26
+ return client;
27
+ },
28
+ getClient(clientId) {
29
+ return clients.get(clientId);
30
+ },
31
+ issueCode(rec, nowMs = Date.now()) {
32
+ const code = randomBytes(32).toString('base64url');
33
+ codes.set(code, { ...rec, expiresAt: nowMs + AUTH_CODE_TTL_MS });
34
+ return code;
35
+ },
36
+ consumeCode(code, nowMs = Date.now()) {
37
+ const rec = codes.get(code);
38
+ if (!rec)
39
+ return null;
40
+ codes.delete(code); // single-use
41
+ if (nowMs >= rec.expiresAt)
42
+ return null;
43
+ return rec;
44
+ },
45
+ clear() {
46
+ clients.clear();
47
+ codes.clear();
48
+ },
49
+ };
50
+ }
51
+ //# sourceMappingURL=auth-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-store.js","sourceRoot":"","sources":["../../src/mcp/auth-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAiB1C,MAAM,gBAAgB,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;AAcvC,MAAM,UAAU,eAAe;IAC7B,MAAM,OAAO,GAAG,IAAI,GAAG,EAA4B,CAAC;IACpD,MAAM,KAAK,GAAG,IAAI,GAAG,EAA0B,CAAC;IAEhD,OAAO;QACL,SAAS,CAAC,QAAQ;YAChB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC3B,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE;oBACpB,QAAQ;oBACR,YAAY,EAAE,EAAE;oBAChB,UAAU,EAAE,qCAAqC;oBACjD,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;iBACtB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QACD,cAAc,CAAC,EAAE,YAAY,EAAE,UAAU,GAAG,IAAI,EAAE,EAAE,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE;YACpE,MAAM,QAAQ,GAAG,OAAO,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;YAChE,MAAM,MAAM,GAAqB;gBAC/B,QAAQ;gBACR,YAAY,EAAE,CAAC,GAAG,YAAY,CAAC;gBAC/B,UAAU;gBACV,SAAS,EAAE,KAAK;aACjB,CAAC;YACF,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;YAC9B,OAAO,MAAM,CAAC;QAChB,CAAC;QACD,SAAS,CAAC,QAAQ;YAChB,OAAO,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC/B,CAAC;QACD,SAAS,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE;YAC/B,MAAM,IAAI,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;YACnD,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,GAAG,GAAG,EAAE,SAAS,EAAE,KAAK,GAAG,gBAAgB,EAAE,CAAC,CAAC;YACjE,OAAO,IAAI,CAAC;QACd,CAAC;QACD,WAAW,CAAC,IAAI,EAAE,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE;YAClC,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC5B,IAAI,CAAC,GAAG;gBAAE,OAAO,IAAI,CAAC;YACtB,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,aAAa;YACjC,IAAI,KAAK,IAAI,GAAG,CAAC,SAAS;gBAAE,OAAO,IAAI,CAAC;YACxC,OAAO,GAAG,CAAC;QACb,CAAC;QACD,KAAK;YACH,OAAO,CAAC,KAAK,EAAE,CAAC;YAChB,KAAK,CAAC,KAAK,EAAE,CAAC;QAChB,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { HtmlEscapedString } from 'hono/utils/html';
2
+ export interface ConsentViewModel {
3
+ clientId: string;
4
+ redirectUri: string;
5
+ state: string;
6
+ codeChallenge: string;
7
+ codeChallengeMethod: 'S256';
8
+ scope: string;
9
+ clientName: string | null;
10
+ error: string | null;
11
+ }
12
+ /**
13
+ * Render the consent screen. All user-controlled values are escaped by the
14
+ * `html` template tag — never inject raw strings into the template.
15
+ */
16
+ export declare function renderConsent(vm: ConsentViewModel): HtmlEscapedString | Promise<HtmlEscapedString>;
@@ -0,0 +1,90 @@
1
+ import { html } from 'hono/html';
2
+ const STYLE = `
3
+ :root { color-scheme: light dark; }
4
+ body {
5
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
6
+ max-width: 28rem;
7
+ margin: 3rem auto;
8
+ padding: 0 1.5rem;
9
+ line-height: 1.5;
10
+ }
11
+ h1 { font-size: 1.25rem; margin-bottom: 0.5rem; }
12
+ .meta { font-size: 0.875rem; color: #666; margin-bottom: 1.5rem; }
13
+ .meta dt { font-weight: 600; }
14
+ .meta dd { margin: 0 0 0.5rem 0; word-break: break-all; font-family: ui-monospace, monospace; font-size: 0.8125rem; }
15
+ .warn {
16
+ border: 1px solid #d97706;
17
+ background: #fef3c7;
18
+ color: #78350f;
19
+ padding: 0.75rem 1rem;
20
+ border-radius: 0.375rem;
21
+ font-size: 0.875rem;
22
+ margin-bottom: 1.5rem;
23
+ }
24
+ @media (prefers-color-scheme: dark) {
25
+ .warn { background: #422006; color: #fde68a; border-color: #b45309; }
26
+ .meta { color: #aaa; }
27
+ }
28
+ label { display: block; font-weight: 600; margin-bottom: 0.25rem; }
29
+ input[type="password"] {
30
+ width: 100%; padding: 0.5rem 0.625rem; font-size: 1rem;
31
+ border: 1px solid #ccc; border-radius: 0.375rem; box-sizing: border-box;
32
+ }
33
+ button {
34
+ margin-top: 1rem; padding: 0.625rem 1rem;
35
+ background: #2563eb; color: white; border: 0;
36
+ border-radius: 0.375rem; font-size: 1rem; cursor: pointer;
37
+ }
38
+ button:hover { background: #1d4ed8; }
39
+ .err { color: #b91c1c; font-size: 0.875rem; margin-top: 0.5rem; }
40
+ `;
41
+ /**
42
+ * Render the consent screen. All user-controlled values are escaped by the
43
+ * `html` template tag — never inject raw strings into the template.
44
+ */
45
+ export function renderConsent(vm) {
46
+ const errBlock = vm.error ? html `<p class="err">${vm.error}</p>` : html ``;
47
+ return html `<!doctype html>
48
+ <html lang="en">
49
+ <head>
50
+ <meta charset="utf-8" />
51
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
52
+ <title>Authorize MCP access</title>
53
+ <style>
54
+ ${STYLE}
55
+ </style>
56
+ </head>
57
+ <body>
58
+ <main>
59
+ <h1>Authorize MCP access</h1>
60
+ <p class="meta">
61
+ An MCP client is requesting access to your Zyke data. Verify the redirect target before
62
+ continuing.
63
+ </p>
64
+ <dl class="meta">
65
+ <dt>Client</dt>
66
+ <dd>${vm.clientName ?? vm.clientId}</dd>
67
+ <dt>Redirects to</dt>
68
+ <dd>${vm.redirectUri}</dd>
69
+ </dl>
70
+ <p class="warn">
71
+ Only continue if the redirect target above is a domain you trust (e.g.
72
+ <code>https://claude.ai/api/mcp/auth_callback</code>).
73
+ </p>
74
+ <form method="POST" action="/mcp/authorize" autocomplete="off">
75
+ <label for="password">Read secret</label>
76
+ <input id="password" type="password" name="password" autofocus required />
77
+ <input type="hidden" name="client_id" value="${vm.clientId}" />
78
+ <input type="hidden" name="redirect_uri" value="${vm.redirectUri}" />
79
+ <input type="hidden" name="state" value="${vm.state}" />
80
+ <input type="hidden" name="code_challenge" value="${vm.codeChallenge}" />
81
+ <input type="hidden" name="code_challenge_method" value="${vm.codeChallengeMethod}" />
82
+ <input type="hidden" name="scope" value="${vm.scope}" />
83
+ <button type="submit">Authorize</button>
84
+ ${errBlock}
85
+ </form>
86
+ </main>
87
+ </body>
88
+ </html>`;
89
+ }
90
+ //# sourceMappingURL=consent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consent.js","sourceRoot":"","sources":["../../src/mcp/consent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAcjC,MAAM,KAAK,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsCb,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,aAAa,CAC3B,EAAoB;IAEpB,MAAM,QAAQ,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAA,kBAAkB,EAAE,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAA,EAAE,CAAC;IAC1E,OAAO,IAAI,CAAA;;;;;;;YAOD,KAAK;;;;;;;;;;;;kBAYC,EAAE,CAAC,UAAU,IAAI,EAAE,CAAC,QAAQ;;kBAE5B,EAAE,CAAC,WAAW;;;;;;;;;2DAS2B,EAAE,CAAC,QAAQ;8DACR,EAAE,CAAC,WAAW;uDACrB,EAAE,CAAC,KAAK;gEACC,EAAE,CAAC,aAAa;uEACT,EAAE,CAAC,mBAAmB;uDACtC,EAAE,CAAC,KAAK;;cAEjD,QAAQ;;;;YAIV,CAAC;AACb,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import type { McpEnv } from '../env.js';
2
+ import type { AuthStore } from './auth-store.js';
2
3
  export interface TokenIssueResult {
3
4
  accessToken: string;
4
5
  expiresIn: number;
@@ -39,3 +40,22 @@ export type GrantResult = {
39
40
  * + the read secret. Returns the access token on success.
40
41
  */
41
42
  export declare function grantClientCredentials(req: ClientCredentialsRequest, env: McpEnv, readSecret: string, nowMs?: number): GrantResult;
43
+ /**
44
+ * Compute the PKCE S256 code challenge for a verifier.
45
+ *
46
+ * RFC 7636 §4.2: `BASE64URL(SHA256(ASCII(verifier)))`.
47
+ */
48
+ export declare function pkceS256Challenge(verifier: string): string;
49
+ export interface AuthCodeRequest {
50
+ grantType: string;
51
+ code: string;
52
+ redirectUri: string;
53
+ clientId: string;
54
+ codeVerifier: string;
55
+ }
56
+ /**
57
+ * Validate an authorization_code grant + PKCE verifier and exchange it for a
58
+ * bearer token. The code is single-use — `consumeCode` deletes it regardless
59
+ * of validation outcome past the lookup.
60
+ */
61
+ export declare function grantAuthorizationCode(req: AuthCodeRequest, env: McpEnv, store: AuthStore, nowMs?: number): GrantResult;
package/dist/mcp/oauth.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createHmac, randomBytes, timingSafeEqual } from 'node:crypto';
1
+ import { createHash, createHmac, randomBytes, timingSafeEqual } from 'node:crypto';
2
2
  const PAYLOAD_RANDOM_BYTES = 16;
3
3
  const PAYLOAD_EXPIRY_BYTES = 8;
4
4
  const PAYLOAD_BYTES = PAYLOAD_RANDOM_BYTES + PAYLOAD_EXPIRY_BYTES;
@@ -87,4 +87,63 @@ export function grantClientCredentials(req, env, readSecret, nowMs = Date.now())
87
87
  }
88
88
  return { ok: true, token: issueToken(env, nowMs) };
89
89
  }
90
+ /**
91
+ * Compute the PKCE S256 code challenge for a verifier.
92
+ *
93
+ * RFC 7636 §4.2: `BASE64URL(SHA256(ASCII(verifier)))`.
94
+ */
95
+ export function pkceS256Challenge(verifier) {
96
+ return b64urlEncode(createHash('sha256').update(verifier).digest());
97
+ }
98
+ function timingSafeEqualStr(a, b) {
99
+ if (a.length !== b.length)
100
+ return false;
101
+ return timingSafeEqual(Buffer.from(a), Buffer.from(b));
102
+ }
103
+ /**
104
+ * Validate an authorization_code grant + PKCE verifier and exchange it for a
105
+ * bearer token. The code is single-use — `consumeCode` deletes it regardless
106
+ * of validation outcome past the lookup.
107
+ */
108
+ export function grantAuthorizationCode(req, env, store, nowMs = Date.now()) {
109
+ if (req.grantType !== 'authorization_code') {
110
+ return {
111
+ ok: false,
112
+ status: 400,
113
+ error: 'unsupported_grant_type',
114
+ };
115
+ }
116
+ if (!req.code || !req.clientId || !req.codeVerifier || !req.redirectUri) {
117
+ return { ok: false, status: 400, error: 'invalid_request', description: 'missing parameter' };
118
+ }
119
+ // RFC 7636: verifier is 43-128 chars of [A-Z a-z 0-9 - . _ ~]
120
+ if (!/^[A-Za-z0-9._~-]{43,128}$/.test(req.codeVerifier)) {
121
+ return {
122
+ ok: false,
123
+ status: 400,
124
+ error: 'invalid_grant',
125
+ description: 'malformed code_verifier',
126
+ };
127
+ }
128
+ const rec = store.consumeCode(req.code, nowMs);
129
+ if (!rec) {
130
+ return {
131
+ ok: false,
132
+ status: 400,
133
+ error: 'invalid_grant',
134
+ description: 'code unknown or expired',
135
+ };
136
+ }
137
+ if (rec.clientId !== req.clientId) {
138
+ return { ok: false, status: 400, error: 'invalid_grant', description: 'client_id mismatch' };
139
+ }
140
+ if (rec.redirectUri !== req.redirectUri) {
141
+ return { ok: false, status: 400, error: 'invalid_grant', description: 'redirect_uri mismatch' };
142
+ }
143
+ const expected = pkceS256Challenge(req.codeVerifier);
144
+ if (!timingSafeEqualStr(expected, rec.codeChallenge)) {
145
+ return { ok: false, status: 400, error: 'invalid_grant', description: 'pkce mismatch' };
146
+ }
147
+ return { ok: true, token: issueToken(env, nowMs) };
148
+ }
90
149
  //# sourceMappingURL=oauth.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"oauth.js","sourceRoot":"","sources":["../../src/mcp/oauth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAavE,MAAM,oBAAoB,GAAG,EAAE,CAAC;AAChC,MAAM,oBAAoB,GAAG,CAAC,CAAC;AAC/B,MAAM,aAAa,GAAG,oBAAoB,GAAG,oBAAoB,CAAC;AAClE,MAAM,WAAW,GAAG,EAAE,CAAC,CAAC,wBAAwB;AAEhD,SAAS,IAAI,CAAC,OAAe,EAAE,MAAc;IAC3C,OAAO,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACpE,CAAC;AAED,SAAS,YAAY,CAAC,GAAW;IAC/B,OAAO,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAC3F,CAAC;AAED,SAAS,YAAY,CAAC,CAAS;IAC7B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;QACrE,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,QAAQ,CAAC,CAAC;IAC9E,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,UAAU,CAAC,GAAW,EAAE,QAAgB,IAAI,CAAC,GAAG,EAAE;IAChE,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,GAAG,CAAC,eAAe,CAAC;IACjE,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IAC5C,WAAW,CAAC,oBAAoB,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACnD,OAAO,CAAC,gBAAgB,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,oBAAoB,CAAC,CAAC;IAClE,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC;IAC3C,OAAO;QACL,WAAW,EAAE,GAAG,YAAY,CAAC,OAAO,CAAC,IAAI,GAAG,EAAE;QAC9C,SAAS,EAAE,GAAG,CAAC,eAAe;KAC/B,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,WAAW,CACzB,KAAa,EACb,GAAW,EACX,QAAgB,IAAI,CAAC,GAAG,EAAE;IAE1B,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC/B,IAAI,GAAG,IAAI,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;IACxD,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACvC,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;IACpC,IAAI,MAAM,CAAC,MAAM,KAAK,WAAW;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;IAE7E,MAAM,OAAO,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC;IACzC,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,KAAK,aAAa;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;IAE5F,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC;IACtD,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACrC,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;IAC7C,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,IAAI,CAAC,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;QACpD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC;IAChD,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,oBAAoB,CAAC,CAAC,CAAC;IACxE,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,IAAI,SAAS,EAAE,CAAC;QAC1C,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAC1C,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;AACjC,CAAC;AAYD;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CACpC,GAA6B,EAC7B,GAAW,EACX,UAAkB,EAClB,QAAgB,IAAI,CAAC,GAAG,EAAE;IAE1B,IAAI,GAAG,CAAC,SAAS,KAAK,oBAAoB,EAAE,CAAC;QAC3C,OAAO;YACL,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,GAAG;YACX,KAAK,EAAE,wBAAwB;YAC/B,WAAW,EAAE,sCAAsC;SACpD,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,QAAQ,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;QACvC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,qBAAqB,EAAE,CAAC;IAClG,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACvC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACvC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IAC3C,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACrC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,IAAI,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACzE,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,IAAI,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACzE,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK,EAAE,CAAC;QACrB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC7D,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,CAAC;AACrD,CAAC"}
1
+ {"version":3,"file":"oauth.js","sourceRoot":"","sources":["../../src/mcp/oauth.ts"],"names":[],"mappings":"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"}
@@ -1,9 +1,20 @@
1
1
  import { Hono } from 'hono';
2
2
  import type { Env, McpEnv } from '../env.js';
3
+ import { type AuthStore } from '../mcp/auth-store.js';
3
4
  import type { Db } from '../db/client.js';
4
5
  export interface McpDeps {
5
6
  db: Db;
6
7
  env: Env;
7
8
  mcp: McpEnv;
9
+ authStore?: AuthStore;
8
10
  }
9
- export declare function mcpRoutes(deps: McpDeps): Hono;
11
+ export interface McpAppRoutes {
12
+ mcp: Hono;
13
+ discovery: Hono;
14
+ }
15
+ /**
16
+ * Build the MCP sub-apps. `mcp` is mounted under `/mcp`; `discovery` is
17
+ * mounted at `/` so RFC 8414 / RFC 9728 `.well-known` URLs sit at the root
18
+ * (which is what most MCP clients — including claude.ai — probe).
19
+ */
20
+ export declare function mcpRoutes(deps: McpDeps): McpAppRoutes;
@@ -1,15 +1,168 @@
1
+ import { timingSafeEqual } from 'node:crypto';
1
2
  import { Hono } from 'hono';
2
- import { grantClientCredentials, verifyToken } from '../mcp/oauth.js';
3
+ import { createAuthStore } from '../mcp/auth-store.js';
4
+ import { renderConsent } from '../mcp/consent.js';
5
+ import { grantAuthorizationCode, grantClientCredentials, verifyToken } from '../mcp/oauth.js';
3
6
  import { createMcpServer } from '../mcp/server.js';
4
7
  import { buildTools } from '../mcp/tools.js';
5
8
  import { createTimezoneResolver } from '../mcp/timezones.js';
9
+ function issuerFrom(reqUrl) {
10
+ const u = new URL(reqUrl);
11
+ return `${u.protocol}//${u.host}`;
12
+ }
13
+ function authMetadata(issuer) {
14
+ return {
15
+ issuer,
16
+ authorization_endpoint: `${issuer}/mcp/authorize`,
17
+ token_endpoint: `${issuer}/mcp/oauth/token`,
18
+ registration_endpoint: `${issuer}/mcp/register`,
19
+ grant_types_supported: ['authorization_code', 'client_credentials'],
20
+ response_types_supported: ['code'],
21
+ code_challenge_methods_supported: ['S256'],
22
+ token_endpoint_auth_methods_supported: ['none', 'client_secret_post', 'client_secret_basic'],
23
+ };
24
+ }
25
+ function resourceMetadata(issuer) {
26
+ return {
27
+ resource: `${issuer}/mcp`,
28
+ authorization_servers: [issuer],
29
+ bearer_methods_supported: ['header'],
30
+ };
31
+ }
32
+ /**
33
+ * Build the MCP sub-apps. `mcp` is mounted under `/mcp`; `discovery` is
34
+ * mounted at `/` so RFC 8414 / RFC 9728 `.well-known` URLs sit at the root
35
+ * (which is what most MCP clients — including claude.ai — probe).
36
+ */
6
37
  export function mcpRoutes(deps) {
7
38
  const { db, env, mcp } = deps;
8
39
  const tz = createTimezoneResolver(db);
9
40
  const server = createMcpServer(buildTools(db, tz));
41
+ const store = deps.authStore ?? createAuthStore();
42
+ store.bootstrap(mcp.clientId);
10
43
  const app = new Hono();
11
- // OAuth: client_credentials grant.
44
+ // Permissive CORS preflight on every endpoint in the sub-app — browser MCP
45
+ // clients send OPTIONS before POST.
46
+ app.options('*', (c) => {
47
+ addCorsHeaders(c);
48
+ return c.body(null, 204);
49
+ });
50
+ // Kept under `/mcp/oauth/...` for backward compat with older clients.
51
+ app.get('/oauth/.well-known/oauth-authorization-server', (c) => {
52
+ addCorsHeaders(c);
53
+ return c.json(authMetadata(issuerFrom(c.req.url)));
54
+ });
55
+ // --- Dynamic Client Registration (RFC 7591) ------------------------------
56
+ app.post('/register', async (c) => {
57
+ addCorsHeaders(c);
58
+ let body;
59
+ try {
60
+ body = (await c.req.json());
61
+ }
62
+ catch {
63
+ return c.json({ error: 'invalid_client_metadata', error_description: 'body must be JSON' }, 400);
64
+ }
65
+ const redirectUris = body.redirect_uris;
66
+ if (!Array.isArray(redirectUris) || redirectUris.length === 0) {
67
+ return c.json({ error: 'invalid_redirect_uri', error_description: 'redirect_uris is required' }, 400);
68
+ }
69
+ for (const u of redirectUris) {
70
+ if (typeof u !== 'string' || !isAcceptableRedirectUri(u)) {
71
+ return c.json({ error: 'invalid_redirect_uri' }, 400);
72
+ }
73
+ }
74
+ const clientName = typeof body.client_name === 'string' ? body.client_name : null;
75
+ const client = store.registerClient({
76
+ redirectUris: redirectUris,
77
+ clientName,
78
+ });
79
+ return c.json({
80
+ client_id: client.clientId,
81
+ client_id_issued_at: Math.floor(client.createdAt / 1000),
82
+ redirect_uris: client.redirectUris,
83
+ ...(client.clientName ? { client_name: client.clientName } : {}),
84
+ grant_types: ['authorization_code'],
85
+ response_types: ['code'],
86
+ token_endpoint_auth_method: 'none',
87
+ }, 201);
88
+ });
89
+ // --- Authorization endpoint (auth code + PKCE) ---------------------------
90
+ app.get('/authorize', (c) => {
91
+ const q = c.req.query();
92
+ const v = validateAuthorizeParams(q, store);
93
+ if (v.kind === 'fatal')
94
+ return c.text(`authorization error: ${v.message}`, 400);
95
+ if (v.kind === 'redirect') {
96
+ return c.redirect(appendParams(v.redirectUri, {
97
+ error: v.error,
98
+ error_description: v.message,
99
+ state: q.state ?? '',
100
+ }), 302);
101
+ }
102
+ return c.html(renderConsent({
103
+ clientId: v.client.clientId,
104
+ clientName: v.client.clientName,
105
+ redirectUri: q.redirect_uri,
106
+ state: q.state ?? '',
107
+ codeChallenge: q.code_challenge,
108
+ codeChallengeMethod: 'S256',
109
+ scope: q.scope ?? '',
110
+ error: null,
111
+ }));
112
+ });
113
+ app.post('/authorize', async (c) => {
114
+ const form = await c.req.parseBody();
115
+ const params = { response_type: 'code' };
116
+ for (const k of [
117
+ 'client_id',
118
+ 'redirect_uri',
119
+ 'state',
120
+ 'code_challenge',
121
+ 'code_challenge_method',
122
+ 'scope',
123
+ 'password',
124
+ ]) {
125
+ const val = form[k];
126
+ if (typeof val === 'string')
127
+ params[k] = val;
128
+ }
129
+ const v = validateAuthorizeParams(params, store);
130
+ if (v.kind === 'fatal')
131
+ return c.text(`authorization error: ${v.message}`, 400);
132
+ if (v.kind === 'redirect') {
133
+ return c.redirect(appendParams(v.redirectUri, {
134
+ error: v.error,
135
+ error_description: v.message,
136
+ state: params.state ?? '',
137
+ }), 302);
138
+ }
139
+ const password = params.password ?? '';
140
+ const a = Buffer.from(password);
141
+ const b = Buffer.from(env.readSecret);
142
+ const ok = a.length === b.length && timingSafeEqual(a, b);
143
+ if (!ok) {
144
+ return c.html(renderConsent({
145
+ clientId: v.client.clientId,
146
+ clientName: v.client.clientName,
147
+ redirectUri: params.redirect_uri,
148
+ state: params.state ?? '',
149
+ codeChallenge: params.code_challenge,
150
+ codeChallengeMethod: 'S256',
151
+ scope: params.scope ?? '',
152
+ error: 'Invalid read secret.',
153
+ }), 401);
154
+ }
155
+ const code = store.issueCode({
156
+ clientId: v.client.clientId,
157
+ redirectUri: params.redirect_uri,
158
+ codeChallenge: params.code_challenge,
159
+ codeChallengeMethod: 'S256',
160
+ });
161
+ return c.redirect(appendParams(params.redirect_uri, { code, state: params.state ?? '' }), 302);
162
+ });
163
+ // --- Token endpoint (both grants) ----------------------------------------
12
164
  app.post('/oauth/token', async (c) => {
165
+ addCorsHeaders(c);
13
166
  let params;
14
167
  const ct = c.req.header('content-type') ?? '';
15
168
  try {
@@ -19,9 +172,9 @@ export function mcpRoutes(deps) {
19
172
  else if (ct.includes('application/json')) {
20
173
  const body = (await c.req.json());
21
174
  params = new URLSearchParams();
22
- for (const [k, v] of Object.entries(body)) {
23
- if (typeof v === 'string')
24
- params.set(k, v);
175
+ for (const [k, val] of Object.entries(body)) {
176
+ if (typeof val === 'string')
177
+ params.set(k, val);
25
178
  }
26
179
  }
27
180
  else {
@@ -31,6 +184,27 @@ export function mcpRoutes(deps) {
31
184
  catch {
32
185
  return c.json({ error: 'invalid_request', error_description: 'malformed body' }, 400);
33
186
  }
187
+ const grantType = params.get('grant_type') ?? '';
188
+ if (grantType === 'authorization_code') {
189
+ const result = grantAuthorizationCode({
190
+ grantType,
191
+ code: params.get('code') ?? '',
192
+ redirectUri: params.get('redirect_uri') ?? '',
193
+ clientId: params.get('client_id') ?? '',
194
+ codeVerifier: params.get('code_verifier') ?? '',
195
+ }, mcp, store);
196
+ if (!result.ok) {
197
+ return c.json({
198
+ error: result.error,
199
+ ...(result.description ? { error_description: result.description } : {}),
200
+ }, result.status);
201
+ }
202
+ return c.json({
203
+ access_token: result.token.accessToken,
204
+ token_type: 'Bearer',
205
+ expires_in: result.token.expiresIn,
206
+ });
207
+ }
34
208
  // RFC 6749 §2.3.1: client credentials may also be supplied via Basic auth.
35
209
  let clientId = params.get('client_id') ?? '';
36
210
  let clientSecret = params.get('client_secret') ?? '';
@@ -45,7 +219,7 @@ export function mcpRoutes(deps) {
45
219
  clientSecret = decoded.slice(sep + 1);
46
220
  }
47
221
  }
48
- const result = grantClientCredentials({ clientId, clientSecret, grantType: params.get('grant_type') ?? '' }, mcp, env.readSecret);
222
+ const result = grantClientCredentials({ clientId, clientSecret, grantType }, mcp, env.readSecret);
49
223
  if (!result.ok) {
50
224
  return c.json({
51
225
  error: result.error,
@@ -58,27 +232,55 @@ export function mcpRoutes(deps) {
58
232
  expires_in: result.token.expiresIn,
59
233
  });
60
234
  });
61
- // OAuth metadata discovery (RFC 8414).
62
- app.get('/oauth/.well-known/oauth-authorization-server', (c) => {
63
- const base = new URL(c.req.url);
64
- const issuer = `${base.protocol}//${base.host}`;
65
- return c.json({
66
- issuer,
67
- token_endpoint: `${issuer}/mcp/oauth/token`,
68
- grant_types_supported: ['client_credentials'],
69
- token_endpoint_auth_methods_supported: ['client_secret_post', 'client_secret_basic'],
70
- response_types_supported: [],
71
- });
235
+ // --- MCP endpoint (JSON-RPC over POST). Requires Bearer token. -----------
236
+ //
237
+ // We also answer GET / DELETE / OPTIONS so that MCP clients probing the
238
+ // Streamable HTTP transport (claude.ai, Claude Desktop) get a proper
239
+ // 401 / 405 / 204 response instead of a generic 404. A 404 on the resource
240
+ // URL is interpreted as "server unreachable" by claude.ai.
241
+ function buildWwwAuthenticate(reqUrl, reason) {
242
+ const issuer = issuerFrom(reqUrl);
243
+ const errPart = reason ? ', error="invalid_token"' : '';
244
+ return `Bearer realm="MCP"${errPart}, resource_metadata="${issuer}/.well-known/oauth-protected-resource"`;
245
+ }
246
+ app.options('/', (c) => {
247
+ addCorsHeaders(c);
248
+ return c.body(null, 204);
249
+ });
250
+ app.get('/', (c) => {
251
+ addCorsHeaders(c);
252
+ const auth = c.req.header('authorization') ?? '';
253
+ if (!auth.toLowerCase().startsWith('bearer ')) {
254
+ c.header('WWW-Authenticate', buildWwwAuthenticate(c.req.url));
255
+ return c.json({ error: 'unauthorized' }, 401);
256
+ }
257
+ const v = verifyToken(auth.slice(7).trim(), mcp);
258
+ if (!v.ok) {
259
+ c.header('WWW-Authenticate', buildWwwAuthenticate(c.req.url, v.reason));
260
+ return c.json({ error: 'unauthorized', reason: v.reason }, 401);
261
+ }
262
+ // Authenticated but we don't expose a server-to-client SSE stream — the
263
+ // MCP spec allows the server to refuse GET with 405.
264
+ c.header('Allow', 'POST, OPTIONS');
265
+ return c.body(null, 405);
266
+ });
267
+ app.delete('/', (c) => {
268
+ // No session state to terminate. Acknowledge so clients that send DELETE
269
+ // on disconnect don't surface an error.
270
+ addCorsHeaders(c);
271
+ return c.body(null, 204);
72
272
  });
73
- // MCP endpoint (JSON-RPC over POST). Requires Bearer token.
74
273
  app.post('/', async (c) => {
274
+ addCorsHeaders(c);
75
275
  const auth = c.req.header('authorization') ?? '';
76
276
  if (!auth.toLowerCase().startsWith('bearer ')) {
277
+ c.header('WWW-Authenticate', buildWwwAuthenticate(c.req.url));
77
278
  return c.json({ error: 'unauthorized' }, 401);
78
279
  }
79
280
  const token = auth.slice(7).trim();
80
281
  const v = verifyToken(token, mcp);
81
282
  if (!v.ok) {
283
+ c.header('WWW-Authenticate', buildWwwAuthenticate(c.req.url, v.reason));
82
284
  return c.json({ error: 'unauthorized', reason: v.reason }, 401);
83
285
  }
84
286
  let body;
@@ -91,6 +293,93 @@ export function mcpRoutes(deps) {
91
293
  const response = await server.handle(body);
92
294
  return c.json(response);
93
295
  });
94
- return app;
296
+ // --- Root-level discovery aliases ----------------------------------------
297
+ const discovery = new Hono();
298
+ discovery.options('*', (c) => {
299
+ addCorsHeaders(c);
300
+ return c.body(null, 204);
301
+ });
302
+ discovery.get('/.well-known/oauth-authorization-server', (c) => {
303
+ addCorsHeaders(c);
304
+ return c.json(authMetadata(issuerFrom(c.req.url)));
305
+ });
306
+ discovery.get('/.well-known/oauth-protected-resource', (c) => {
307
+ addCorsHeaders(c);
308
+ return c.json(resourceMetadata(issuerFrom(c.req.url)));
309
+ });
310
+ // Per draft RFC 9728: the resource server may publish PRM at a path
311
+ // matching the resource URL, e.g. /.well-known/oauth-protected-resource/mcp.
312
+ discovery.get('/.well-known/oauth-protected-resource/mcp', (c) => {
313
+ addCorsHeaders(c);
314
+ return c.json(resourceMetadata(issuerFrom(c.req.url)));
315
+ });
316
+ return { mcp: app, discovery };
317
+ }
318
+ function addCorsHeaders(c) {
319
+ const origin = c.req.header('origin') ?? '*';
320
+ c.header('Access-Control-Allow-Origin', origin);
321
+ c.header('Access-Control-Allow-Credentials', 'true');
322
+ c.header('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS');
323
+ c.header('Access-Control-Allow-Headers', 'authorization, content-type, mcp-session-id, mcp-protocol-version');
324
+ c.header('Access-Control-Expose-Headers', 'www-authenticate, mcp-session-id, mcp-protocol-version');
325
+ c.header('Access-Control-Max-Age', '86400');
326
+ }
327
+ function validateAuthorizeParams(q, store) {
328
+ const clientId = q.client_id;
329
+ const redirectUri = q.redirect_uri;
330
+ if (!clientId)
331
+ return { kind: 'fatal', message: 'missing client_id' };
332
+ if (!redirectUri)
333
+ return { kind: 'fatal', message: 'missing redirect_uri' };
334
+ const client = store.getClient(clientId);
335
+ if (!client)
336
+ return { kind: 'fatal', message: 'unknown client_id' };
337
+ if (!client.redirectUris.includes(redirectUri)) {
338
+ return { kind: 'fatal', message: 'redirect_uri not registered for this client_id' };
339
+ }
340
+ if (q.response_type !== 'code') {
341
+ return {
342
+ kind: 'redirect',
343
+ redirectUri,
344
+ error: 'unsupported_response_type',
345
+ message: 'only response_type=code is supported',
346
+ };
347
+ }
348
+ if (!q.code_challenge) {
349
+ return {
350
+ kind: 'redirect',
351
+ redirectUri,
352
+ error: 'invalid_request',
353
+ message: 'code_challenge is required (PKCE)',
354
+ };
355
+ }
356
+ if (q.code_challenge_method !== 'S256') {
357
+ return {
358
+ kind: 'redirect',
359
+ redirectUri,
360
+ error: 'invalid_request',
361
+ message: 'only code_challenge_method=S256 is supported',
362
+ };
363
+ }
364
+ return { kind: 'ok', client };
365
+ }
366
+ function appendParams(base, extra) {
367
+ const u = new URL(base);
368
+ for (const [k, v] of Object.entries(extra)) {
369
+ if (v !== '')
370
+ u.searchParams.set(k, v);
371
+ }
372
+ return u.toString();
373
+ }
374
+ function isAcceptableRedirectUri(uri) {
375
+ try {
376
+ const parsed = new URL(uri);
377
+ const isLoopback = parsed.protocol === 'http:' &&
378
+ (parsed.hostname === '127.0.0.1' || parsed.hostname === 'localhost');
379
+ return parsed.protocol === 'https:' || isLoopback;
380
+ }
381
+ catch {
382
+ return false;
383
+ }
95
384
  }
96
385
  //# sourceMappingURL=mcp.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"mcp.js","sourceRoot":"","sources":["../../src/routes/mcp.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mcp.js","sourceRoot":"","sources":["../../src/routes/mcp.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zykeco/sync-server",
3
- "version": "0.6.0",
3
+ "version": "0.7.1",
4
4
  "description": "Self-hosted Zyke sync server (Hono + Drizzle + libsql). Imports to auto-start; ships a migrate CLI.",
5
5
  "license": "AGPL-3.0-or-later",
6
6
  "author": "P2LS9K GmbH <office@p2ls9k.com>",