@zykeco/sync-server 0.6.0 → 0.7.0

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":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAC1E,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,EAAE,4BAA4B,EAAE,MAAM,iCAAiC,CAAC;AAC/E,OAAO,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAC3E,OAAO,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AACvE,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AAEtE,MAAM,UAAU,QAAQ,CAAC,EAAM,EAAE,GAAQ;IACvC,MAAM,IAAI,GAAe,EAAE,UAAU,EAAE,GAAG,CAAC,UAAU,EAAE,WAAW,EAAE,GAAG,CAAC,WAAW,EAAE,CAAC;IACtF,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IAEvB,GAAG,CAAC,KAAK,CAAC,YAAY,EAAE,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC;IAE1C,MAAM,iBAAiB,GAAG,uBAAuB,CAAC,EAAE,CAAC,CAAC;IACtD,MAAM,kBAAkB,GAAG,wBAAwB,CAAC,EAAE,CAAC,CAAC;IACxD,MAAM,kBAAkB,GAAG,wBAAwB,CAAC,EAAE,CAAC,CAAC;IACxD,MAAM,kBAAkB,GAAG,wBAAwB,CAAC,EAAE,CAAC,CAAC;IACxD,MAAM,gBAAgB,GAAG,sBAAsB,CAAC,EAAE,CAAC,CAAC;IACpD,MAAM,sBAAsB,GAAG,4BAA4B,CAAC,EAAE,CAAC,CAAC;IAChE,MAAM,oBAAoB,GAAG,0BAA0B,CAAC,EAAE,CAAC,CAAC;IAC5D,MAAM,kBAAkB,GAAG,wBAAwB,CAAC,EAAE,CAAC,CAAC;IAExD,GAAG,CAAC,KAAK,CAAC,wBAAwB,EAAE,kBAAkB,CAAC,iBAAiB,EAAE,IAAI,CAAC,CAAC,CAAC;IACjF,GAAG,CAAC,KAAK,CAAC,yBAAyB,EAAE,mBAAmB,CAAC,kBAAkB,EAAE,IAAI,CAAC,CAAC,CAAC;IACpF,GAAG,CAAC,KAAK,CAAC,yBAAyB,EAAE,mBAAmB,CAAC,kBAAkB,EAAE,IAAI,CAAC,CAAC,CAAC;IACpF,GAAG,CAAC,KAAK,CAAC,yBAAyB,EAAE,mBAAmB,CAAC,kBAAkB,EAAE,IAAI,CAAC,CAAC,CAAC;IACpF,GAAG,CAAC,KAAK,CAAC,uBAAuB,EAAE,iBAAiB,CAAC,gBAAgB,EAAE,IAAI,CAAC,CAAC,CAAC;IAC9E,GAAG,CAAC,KAAK,CAAC,8BAA8B,EAAE,uBAAuB,CAAC,sBAAsB,EAAE,IAAI,CAAC,CAAC,CAAC;IACjG,GAAG,CAAC,KAAK,CAAC,4BAA4B,EAAE,qBAAqB,CAAC,oBAAoB,EAAE,IAAI,CAAC,CAAC,CAAC;IAC3F,GAAG,CAAC,KAAK,CAAC,0BAA0B,EAAE,mBAAmB,CAAC,kBAAkB,EAAE,IAAI,CAAC,CAAC,CAAC;IACrF,GAAG,CAAC,KAAK,CACP,eAAe,EACf,UAAU,CACR;QACE,YAAY,EAAE,iBAAiB;QAC/B,aAAa,EAAE,kBAAkB;QACjC,aAAa,EAAE,kBAAkB;QACjC,aAAa,EAAE,kBAAkB;QACjC,WAAW,EAAE,gBAAgB;QAC7B,iBAAiB,EAAE,sBAAsB;QACzC,eAAe,EAAE,oBAAoB;QACrC,aAAa,EAAE,kBAAkB;KAClC,EACD,IAAI,CACL,CACF,CAAC;IAEF,IAAI,GAAG,CAAC,GAAG,EAAE,CAAC;QACZ,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IAC1D,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC"}
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":"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"}
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,14 +1,156 @@
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
+ // Kept under `/mcp/oauth/...` for backward compat with older clients.
45
+ app.get('/oauth/.well-known/oauth-authorization-server', (c) => c.json(authMetadata(issuerFrom(c.req.url))));
46
+ // --- Dynamic Client Registration (RFC 7591) ------------------------------
47
+ app.post('/register', async (c) => {
48
+ let body;
49
+ try {
50
+ body = (await c.req.json());
51
+ }
52
+ catch {
53
+ return c.json({ error: 'invalid_client_metadata', error_description: 'body must be JSON' }, 400);
54
+ }
55
+ const redirectUris = body.redirect_uris;
56
+ if (!Array.isArray(redirectUris) || redirectUris.length === 0) {
57
+ return c.json({ error: 'invalid_redirect_uri', error_description: 'redirect_uris is required' }, 400);
58
+ }
59
+ for (const u of redirectUris) {
60
+ if (typeof u !== 'string' || !isAcceptableRedirectUri(u)) {
61
+ return c.json({ error: 'invalid_redirect_uri' }, 400);
62
+ }
63
+ }
64
+ const clientName = typeof body.client_name === 'string' ? body.client_name : null;
65
+ const client = store.registerClient({
66
+ redirectUris: redirectUris,
67
+ clientName,
68
+ });
69
+ return c.json({
70
+ client_id: client.clientId,
71
+ client_id_issued_at: Math.floor(client.createdAt / 1000),
72
+ redirect_uris: client.redirectUris,
73
+ ...(client.clientName ? { client_name: client.clientName } : {}),
74
+ grant_types: ['authorization_code'],
75
+ response_types: ['code'],
76
+ token_endpoint_auth_method: 'none',
77
+ }, 201);
78
+ });
79
+ // --- Authorization endpoint (auth code + PKCE) ---------------------------
80
+ app.get('/authorize', (c) => {
81
+ const q = c.req.query();
82
+ const v = validateAuthorizeParams(q, store);
83
+ if (v.kind === 'fatal')
84
+ return c.text(`authorization error: ${v.message}`, 400);
85
+ if (v.kind === 'redirect') {
86
+ return c.redirect(appendParams(v.redirectUri, {
87
+ error: v.error,
88
+ error_description: v.message,
89
+ state: q.state ?? '',
90
+ }), 302);
91
+ }
92
+ return c.html(renderConsent({
93
+ clientId: v.client.clientId,
94
+ clientName: v.client.clientName,
95
+ redirectUri: q.redirect_uri,
96
+ state: q.state ?? '',
97
+ codeChallenge: q.code_challenge,
98
+ codeChallengeMethod: 'S256',
99
+ scope: q.scope ?? '',
100
+ error: null,
101
+ }));
102
+ });
103
+ app.post('/authorize', async (c) => {
104
+ const form = await c.req.parseBody();
105
+ const params = { response_type: 'code' };
106
+ for (const k of [
107
+ 'client_id',
108
+ 'redirect_uri',
109
+ 'state',
110
+ 'code_challenge',
111
+ 'code_challenge_method',
112
+ 'scope',
113
+ 'password',
114
+ ]) {
115
+ const val = form[k];
116
+ if (typeof val === 'string')
117
+ params[k] = val;
118
+ }
119
+ const v = validateAuthorizeParams(params, store);
120
+ if (v.kind === 'fatal')
121
+ return c.text(`authorization error: ${v.message}`, 400);
122
+ if (v.kind === 'redirect') {
123
+ return c.redirect(appendParams(v.redirectUri, {
124
+ error: v.error,
125
+ error_description: v.message,
126
+ state: params.state ?? '',
127
+ }), 302);
128
+ }
129
+ const password = params.password ?? '';
130
+ const a = Buffer.from(password);
131
+ const b = Buffer.from(env.readSecret);
132
+ const ok = a.length === b.length && timingSafeEqual(a, b);
133
+ if (!ok) {
134
+ return c.html(renderConsent({
135
+ clientId: v.client.clientId,
136
+ clientName: v.client.clientName,
137
+ redirectUri: params.redirect_uri,
138
+ state: params.state ?? '',
139
+ codeChallenge: params.code_challenge,
140
+ codeChallengeMethod: 'S256',
141
+ scope: params.scope ?? '',
142
+ error: 'Invalid read secret.',
143
+ }), 401);
144
+ }
145
+ const code = store.issueCode({
146
+ clientId: v.client.clientId,
147
+ redirectUri: params.redirect_uri,
148
+ codeChallenge: params.code_challenge,
149
+ codeChallengeMethod: 'S256',
150
+ });
151
+ return c.redirect(appendParams(params.redirect_uri, { code, state: params.state ?? '' }), 302);
152
+ });
153
+ // --- Token endpoint (both grants) ----------------------------------------
12
154
  app.post('/oauth/token', async (c) => {
13
155
  let params;
14
156
  const ct = c.req.header('content-type') ?? '';
@@ -19,9 +161,9 @@ export function mcpRoutes(deps) {
19
161
  else if (ct.includes('application/json')) {
20
162
  const body = (await c.req.json());
21
163
  params = new URLSearchParams();
22
- for (const [k, v] of Object.entries(body)) {
23
- if (typeof v === 'string')
24
- params.set(k, v);
164
+ for (const [k, val] of Object.entries(body)) {
165
+ if (typeof val === 'string')
166
+ params.set(k, val);
25
167
  }
26
168
  }
27
169
  else {
@@ -31,6 +173,27 @@ export function mcpRoutes(deps) {
31
173
  catch {
32
174
  return c.json({ error: 'invalid_request', error_description: 'malformed body' }, 400);
33
175
  }
176
+ const grantType = params.get('grant_type') ?? '';
177
+ if (grantType === 'authorization_code') {
178
+ const result = grantAuthorizationCode({
179
+ grantType,
180
+ code: params.get('code') ?? '',
181
+ redirectUri: params.get('redirect_uri') ?? '',
182
+ clientId: params.get('client_id') ?? '',
183
+ codeVerifier: params.get('code_verifier') ?? '',
184
+ }, mcp, store);
185
+ if (!result.ok) {
186
+ return c.json({
187
+ error: result.error,
188
+ ...(result.description ? { error_description: result.description } : {}),
189
+ }, result.status);
190
+ }
191
+ return c.json({
192
+ access_token: result.token.accessToken,
193
+ token_type: 'Bearer',
194
+ expires_in: result.token.expiresIn,
195
+ });
196
+ }
34
197
  // RFC 6749 §2.3.1: client credentials may also be supplied via Basic auth.
35
198
  let clientId = params.get('client_id') ?? '';
36
199
  let clientSecret = params.get('client_secret') ?? '';
@@ -45,7 +208,7 @@ export function mcpRoutes(deps) {
45
208
  clientSecret = decoded.slice(sep + 1);
46
209
  }
47
210
  }
48
- const result = grantClientCredentials({ clientId, clientSecret, grantType: params.get('grant_type') ?? '' }, mcp, env.readSecret);
211
+ const result = grantClientCredentials({ clientId, clientSecret, grantType }, mcp, env.readSecret);
49
212
  if (!result.ok) {
50
213
  return c.json({
51
214
  error: result.error,
@@ -58,27 +221,18 @@ export function mcpRoutes(deps) {
58
221
  expires_in: result.token.expiresIn,
59
222
  });
60
223
  });
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
- });
72
- });
73
- // MCP endpoint (JSON-RPC over POST). Requires Bearer token.
224
+ // --- MCP endpoint (JSON-RPC over POST). Requires Bearer token. -----------
74
225
  app.post('/', async (c) => {
75
226
  const auth = c.req.header('authorization') ?? '';
227
+ const issuer = issuerFrom(c.req.url);
76
228
  if (!auth.toLowerCase().startsWith('bearer ')) {
229
+ c.header('WWW-Authenticate', `Bearer realm="MCP", resource_metadata="${issuer}/.well-known/oauth-protected-resource"`);
77
230
  return c.json({ error: 'unauthorized' }, 401);
78
231
  }
79
232
  const token = auth.slice(7).trim();
80
233
  const v = verifyToken(token, mcp);
81
234
  if (!v.ok) {
235
+ c.header('WWW-Authenticate', `Bearer realm="MCP", error="invalid_token", resource_metadata="${issuer}/.well-known/oauth-protected-resource"`);
82
236
  return c.json({ error: 'unauthorized', reason: v.reason }, 401);
83
237
  }
84
238
  let body;
@@ -91,6 +245,71 @@ export function mcpRoutes(deps) {
91
245
  const response = await server.handle(body);
92
246
  return c.json(response);
93
247
  });
94
- return app;
248
+ // --- Root-level discovery aliases ----------------------------------------
249
+ const discovery = new Hono();
250
+ discovery.get('/.well-known/oauth-authorization-server', (c) => c.json(authMetadata(issuerFrom(c.req.url))));
251
+ discovery.get('/.well-known/oauth-protected-resource', (c) => c.json(resourceMetadata(issuerFrom(c.req.url))));
252
+ // Per draft RFC 9728: the resource server may publish PRM at a path
253
+ // matching the resource URL, e.g. /.well-known/oauth-protected-resource/mcp.
254
+ discovery.get('/.well-known/oauth-protected-resource/mcp', (c) => c.json(resourceMetadata(issuerFrom(c.req.url))));
255
+ return { mcp: app, discovery };
256
+ }
257
+ function validateAuthorizeParams(q, store) {
258
+ const clientId = q.client_id;
259
+ const redirectUri = q.redirect_uri;
260
+ if (!clientId)
261
+ return { kind: 'fatal', message: 'missing client_id' };
262
+ if (!redirectUri)
263
+ return { kind: 'fatal', message: 'missing redirect_uri' };
264
+ const client = store.getClient(clientId);
265
+ if (!client)
266
+ return { kind: 'fatal', message: 'unknown client_id' };
267
+ if (!client.redirectUris.includes(redirectUri)) {
268
+ return { kind: 'fatal', message: 'redirect_uri not registered for this client_id' };
269
+ }
270
+ if (q.response_type !== 'code') {
271
+ return {
272
+ kind: 'redirect',
273
+ redirectUri,
274
+ error: 'unsupported_response_type',
275
+ message: 'only response_type=code is supported',
276
+ };
277
+ }
278
+ if (!q.code_challenge) {
279
+ return {
280
+ kind: 'redirect',
281
+ redirectUri,
282
+ error: 'invalid_request',
283
+ message: 'code_challenge is required (PKCE)',
284
+ };
285
+ }
286
+ if (q.code_challenge_method !== 'S256') {
287
+ return {
288
+ kind: 'redirect',
289
+ redirectUri,
290
+ error: 'invalid_request',
291
+ message: 'only code_challenge_method=S256 is supported',
292
+ };
293
+ }
294
+ return { kind: 'ok', client };
295
+ }
296
+ function appendParams(base, extra) {
297
+ const u = new URL(base);
298
+ for (const [k, v] of Object.entries(extra)) {
299
+ if (v !== '')
300
+ u.searchParams.set(k, v);
301
+ }
302
+ return u.toString();
303
+ }
304
+ function isAcceptableRedirectUri(uri) {
305
+ try {
306
+ const parsed = new URL(uri);
307
+ const isLoopback = parsed.protocol === 'http:' &&
308
+ (parsed.hostname === '127.0.0.1' || parsed.hostname === 'localhost');
309
+ return parsed.protocol === 'https:' || isLoopback;
310
+ }
311
+ catch {
312
+ return false;
313
+ }
95
314
  }
96
315
  //# 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.0",
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>",