@opendatalabs/personal-server-ts-server 1.34.2 → 1.35.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/dist/api-auth.d.ts +1 -0
- package/dist/api-auth.d.ts.map +1 -1
- package/dist/api-auth.js.map +1 -1
- package/dist/api.d.ts +3 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +3 -0
- package/dist/api.js.map +1 -1
- package/dist/app.d.ts +14 -2
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +64 -8
- package/dist/app.js.map +1 -1
- package/dist/bootstrap.d.ts +19 -0
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +87 -4
- package/dist/bootstrap.js.map +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +7 -0
- package/dist/client.js.map +1 -1
- package/dist/enclave-main.d.ts.map +1 -1
- package/dist/enclave-main.js +0 -2
- package/dist/enclave-main.js.map +1 -1
- package/dist/mcp/durable-state.d.ts +2 -1
- package/dist/mcp/durable-state.d.ts.map +1 -1
- package/dist/mcp/durable-state.js +35 -1
- package/dist/mcp/durable-state.js.map +1 -1
- package/dist/pdpp/bootstrap.d.ts.map +1 -1
- package/dist/pdpp/bootstrap.js +1 -0
- package/dist/pdpp/bootstrap.js.map +1 -1
- package/dist/pdpp/records-bootstrap.d.ts +47 -1
- package/dist/pdpp/records-bootstrap.d.ts.map +1 -1
- package/dist/pdpp/records-bootstrap.js +78 -2
- package/dist/pdpp/records-bootstrap.js.map +1 -1
- package/dist/pdpp/replica-authorization.d.ts +35 -0
- package/dist/pdpp/replica-authorization.d.ts.map +1 -0
- package/dist/pdpp/replica-authorization.js +234 -0
- package/dist/pdpp/replica-authorization.js.map +1 -0
- package/dist/pending-blob-deletions.d.ts +1 -0
- package/dist/pending-blob-deletions.d.ts.map +1 -1
- package/dist/pending-blob-deletions.js +8 -4
- package/dist/pending-blob-deletions.js.map +1 -1
- package/dist/routes/mcp.d.ts +18 -2
- package/dist/routes/mcp.d.ts.map +1 -1
- package/dist/routes/mcp.js +111 -11
- package/dist/routes/mcp.js.map +1 -1
- package/dist/routes/oura.d.ts +6 -0
- package/dist/routes/oura.d.ts.map +1 -0
- package/dist/routes/oura.js +272 -0
- package/dist/routes/oura.js.map +1 -0
- package/dist/routes/pdpp-auth-query-keys.d.ts +7 -0
- package/dist/routes/pdpp-auth-query-keys.d.ts.map +1 -0
- package/dist/routes/pdpp-auth-query-keys.js +25 -0
- package/dist/routes/pdpp-auth-query-keys.js.map +1 -0
- package/dist/routes/pdpp-auth.d.ts +3 -1
- package/dist/routes/pdpp-auth.d.ts.map +1 -1
- package/dist/routes/pdpp-auth.js +126 -45
- package/dist/routes/pdpp-auth.js.map +1 -1
- package/dist/routes/pdpp-blobs.d.ts +13 -1
- package/dist/routes/pdpp-blobs.d.ts.map +1 -1
- package/dist/routes/pdpp-blobs.js +100 -65
- package/dist/routes/pdpp-blobs.js.map +1 -1
- package/dist/routes/pdpp-connections.d.ts.map +1 -1
- package/dist/routes/pdpp-connections.js +24 -1
- package/dist/routes/pdpp-connections.js.map +1 -1
- package/dist/routes/pdpp-instance-bindings.d.ts +3 -0
- package/dist/routes/pdpp-instance-bindings.d.ts.map +1 -1
- package/dist/routes/pdpp-instance-bindings.js +63 -0
- package/dist/routes/pdpp-instance-bindings.js.map +1 -1
- package/dist/routes/pdpp-record-query.d.ts +19 -0
- package/dist/routes/pdpp-record-query.d.ts.map +1 -0
- package/dist/routes/pdpp-record-query.js +51 -0
- package/dist/routes/pdpp-record-query.js.map +1 -0
- package/dist/routes/pdpp-records.d.ts +12 -1
- package/dist/routes/pdpp-records.d.ts.map +1 -1
- package/dist/routes/pdpp-records.js +120 -233
- package/dist/routes/pdpp-records.js.map +1 -1
- package/dist/routes/pdpp-replica-quarantine-fixture.d.ts +65 -0
- package/dist/routes/pdpp-replica-quarantine-fixture.d.ts.map +1 -0
- package/dist/routes/pdpp-replica-quarantine-fixture.js +389 -0
- package/dist/routes/pdpp-replica-quarantine-fixture.js.map +1 -0
- package/dist/storage/claude-method-transition.d.ts +12 -0
- package/dist/storage/claude-method-transition.d.ts.map +1 -0
- package/dist/storage/claude-method-transition.js +139 -0
- package/dist/storage/claude-method-transition.js.map +1 -0
- package/dist/storage/import-operations.d.ts +16 -0
- package/dist/storage/import-operations.d.ts.map +1 -1
- package/dist/storage/import-operations.js +54 -1
- package/dist/storage/import-operations.js.map +1 -1
- package/dist/storage/index-schema.d.ts +1 -1
- package/dist/storage/index-schema.d.ts.map +1 -1
- package/dist/storage/index-schema.js +22 -5
- package/dist/storage/index-schema.js.map +1 -1
- package/dist/storage/legacy-scope-authority.d.ts.map +1 -1
- package/dist/storage/legacy-scope-authority.js +83 -11
- package/dist/storage/legacy-scope-authority.js.map +1 -1
- package/dist/storage/node-data-storage.d.ts.map +1 -1
- package/dist/storage/node-data-storage.js +58 -2
- package/dist/storage/node-data-storage.js.map +1 -1
- package/dist/storage/pdpp-binary-chunks.d.ts +120 -0
- package/dist/storage/pdpp-binary-chunks.d.ts.map +1 -0
- package/dist/storage/pdpp-binary-chunks.js +480 -0
- package/dist/storage/pdpp-binary-chunks.js.map +1 -0
- package/dist/storage/pdpp-records-sqlite-store.d.ts +96 -15
- package/dist/storage/pdpp-records-sqlite-store.d.ts.map +1 -1
- package/dist/storage/pdpp-records-sqlite-store.js +616 -211
- package/dist/storage/pdpp-records-sqlite-store.js.map +1 -1
- package/dist/storage/pdpp-replica-canonical-port.d.ts +202 -0
- package/dist/storage/pdpp-replica-canonical-port.d.ts.map +1 -0
- package/dist/storage/pdpp-replica-canonical-port.js +451 -0
- package/dist/storage/pdpp-replica-canonical-port.js.map +1 -0
- package/dist/storage/pdpp-replica-coordinator-state.d.ts +16 -0
- package/dist/storage/pdpp-replica-coordinator-state.d.ts.map +1 -0
- package/dist/storage/pdpp-replica-coordinator-state.js +25 -0
- package/dist/storage/pdpp-replica-coordinator-state.js.map +1 -0
- package/dist/storage/pdpp-replica-copies.d.ts +81 -0
- package/dist/storage/pdpp-replica-copies.d.ts.map +1 -0
- package/dist/storage/pdpp-replica-copies.js +59 -0
- package/dist/storage/pdpp-replica-copies.js.map +1 -0
- package/dist/storage/pdpp-replica-reconciler.d.ts +96 -0
- package/dist/storage/pdpp-replica-reconciler.d.ts.map +1 -0
- package/dist/storage/pdpp-replica-reconciler.js +710 -0
- package/dist/storage/pdpp-replica-reconciler.js.map +1 -0
- package/dist/storage/pdpp-replica-sql.d.ts +13 -0
- package/dist/storage/pdpp-replica-sql.d.ts.map +1 -0
- package/dist/storage/pdpp-replica-sql.js +43 -0
- package/dist/storage/pdpp-replica-sql.js.map +1 -0
- package/dist/storage/pdpp-replica-staging.d.ts +36 -0
- package/dist/storage/pdpp-replica-staging.d.ts.map +1 -0
- package/dist/storage/pdpp-replica-staging.js +202 -0
- package/dist/storage/pdpp-replica-staging.js.map +1 -0
- package/dist/storage/pdpp-replica-visibility-view.d.ts +64 -0
- package/dist/storage/pdpp-replica-visibility-view.d.ts.map +1 -0
- package/dist/storage/pdpp-replica-visibility-view.js +1397 -0
- package/dist/storage/pdpp-replica-visibility-view.js.map +1 -0
- package/dist/storage/pdpp-replica-writer-identity.d.ts +64 -0
- package/dist/storage/pdpp-replica-writer-identity.d.ts.map +1 -0
- package/dist/storage/pdpp-replica-writer-identity.js +81 -0
- package/dist/storage/pdpp-replica-writer-identity.js.map +1 -0
- package/dist/storage/pdpp-replication-door.d.ts +60 -0
- package/dist/storage/pdpp-replication-door.d.ts.map +1 -0
- package/dist/storage/pdpp-replication-door.js +203 -0
- package/dist/storage/pdpp-replication-door.js.map +1 -0
- package/dist/storage/pdpp-replication-host.d.ts +67 -0
- package/dist/storage/pdpp-replication-host.d.ts.map +1 -0
- package/dist/storage/pdpp-replication-host.js +159 -0
- package/dist/storage/pdpp-replication-host.js.map +1 -0
- package/dist/storage/pdpp-replication-memory.fixture.d.ts +2 -0
- package/dist/storage/pdpp-replication-memory.fixture.d.ts.map +1 -0
- package/dist/storage/pdpp-replication-memory.fixture.js +267 -0
- package/dist/storage/pdpp-replication-memory.fixture.js.map +1 -0
- package/dist/storage/pdpp-replication-preparation.d.ts +3 -0
- package/dist/storage/pdpp-replication-preparation.d.ts.map +1 -0
- package/dist/storage/pdpp-replication-preparation.js +3 -0
- package/dist/storage/pdpp-replication-preparation.js.map +1 -0
- package/dist/storage/pdpp-replication-promotion-memory.fixture.d.ts +2 -0
- package/dist/storage/pdpp-replication-promotion-memory.fixture.d.ts.map +1 -0
- package/dist/storage/pdpp-replication-promotion-memory.fixture.js +65 -0
- package/dist/storage/pdpp-replication-promotion-memory.fixture.js.map +1 -0
- package/dist/storage/pdpp-replication-receiver.d.ts +230 -0
- package/dist/storage/pdpp-replication-receiver.d.ts.map +1 -0
- package/dist/storage/pdpp-replication-receiver.js +950 -0
- package/dist/storage/pdpp-replication-receiver.js.map +1 -0
- package/dist/storage/pdpp-replication-sqlite.d.ts +95 -0
- package/dist/storage/pdpp-replication-sqlite.d.ts.map +1 -0
- package/dist/storage/pdpp-replication-sqlite.js +615 -0
- package/dist/storage/pdpp-replication-sqlite.js.map +1 -0
- package/dist/storage/pdpp-replication-transport.d.ts +39 -0
- package/dist/storage/pdpp-replication-transport.d.ts.map +1 -0
- package/dist/storage/pdpp-replication-transport.js +196 -0
- package/dist/storage/pdpp-replication-transport.js.map +1 -0
- package/dist/storage/schema-versions.d.ts +5 -0
- package/dist/storage/schema-versions.d.ts.map +1 -0
- package/dist/storage/schema-versions.js +26 -0
- package/dist/storage/schema-versions.js.map +1 -0
- package/dist/storage/strava-method-transition.d.ts +18 -0
- package/dist/storage/strava-method-transition.d.ts.map +1 -0
- package/dist/storage/strava-method-transition.js +209 -0
- package/dist/storage/strava-method-transition.js.map +1 -0
- package/dist/ui/ps-lite-debug.js +154938 -124896
- package/package.json +3 -3
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { PdppAuthorizationUnavailableError } from "@opendatalabs/personal-server-ts-core/ports/pdpp-auth";
|
|
2
|
+
import { resolveLocalInstance } from "@opendatalabs/personal-server-ts-core/replication/identity";
|
|
3
|
+
import { PDPP_DATA_ACCESS_TYPE_V02 } from "@opendatalabs/personal-server-ts-core/pdpp";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
const nonempty = z.string().min(1);
|
|
6
|
+
const date = nonempty.refine((value) => Number.isFinite(Date.parse(value)));
|
|
7
|
+
const uniqueNames = z
|
|
8
|
+
.array(nonempty)
|
|
9
|
+
.min(1)
|
|
10
|
+
.refine((values) => new Set(values).size === values.length);
|
|
11
|
+
const stream = z
|
|
12
|
+
.object({
|
|
13
|
+
name: nonempty,
|
|
14
|
+
instance_ids: uniqueNames,
|
|
15
|
+
fields: uniqueNames,
|
|
16
|
+
resources: z.array(nonempty).optional(),
|
|
17
|
+
time_constraint: z
|
|
18
|
+
.object({
|
|
19
|
+
field: nonempty,
|
|
20
|
+
since: date.optional(),
|
|
21
|
+
until: date.optional(),
|
|
22
|
+
})
|
|
23
|
+
.strict()
|
|
24
|
+
.optional(),
|
|
25
|
+
})
|
|
26
|
+
.strict();
|
|
27
|
+
// This adapter accepts the complete frozen v0.2 grant. The v0.1 wire
|
|
28
|
+
// projection omits retained facts; inventing them here would create authority.
|
|
29
|
+
const introspection = z.object({
|
|
30
|
+
active: z.literal(true),
|
|
31
|
+
pdpp_token_kind: z.literal("client"),
|
|
32
|
+
subject_id: nonempty,
|
|
33
|
+
grant_id: nonempty,
|
|
34
|
+
client_id: nonempty,
|
|
35
|
+
exp: z.number().int().positive(),
|
|
36
|
+
authorization_details: z.tuple([
|
|
37
|
+
z.object({
|
|
38
|
+
type: z.literal(PDPP_DATA_ACCESS_TYPE_V02),
|
|
39
|
+
grant: z
|
|
40
|
+
.object({
|
|
41
|
+
version: z.literal("0.2.0"),
|
|
42
|
+
grant_id: nonempty,
|
|
43
|
+
issued_at: date,
|
|
44
|
+
subject: z.object({ id: nonempty }),
|
|
45
|
+
client: z.object({ client_id: nonempty }).passthrough(),
|
|
46
|
+
source: z.object({
|
|
47
|
+
kind: z.enum(["connector", "provider_native"]),
|
|
48
|
+
id: nonempty,
|
|
49
|
+
}),
|
|
50
|
+
source_declaration: z.object({ version: nonempty }),
|
|
51
|
+
purpose_code: nonempty,
|
|
52
|
+
access_mode: z.enum(["single_use", "continuous"]),
|
|
53
|
+
streams: z
|
|
54
|
+
.array(stream)
|
|
55
|
+
.min(1)
|
|
56
|
+
.refine((streams) => new Set(streams.map((entry) => entry.name)).size ===
|
|
57
|
+
streams.length),
|
|
58
|
+
expires_at: date.optional(),
|
|
59
|
+
})
|
|
60
|
+
.passthrough(),
|
|
61
|
+
}),
|
|
62
|
+
]),
|
|
63
|
+
});
|
|
64
|
+
async function readContext(response) {
|
|
65
|
+
if (!response.ok || !response.body) {
|
|
66
|
+
await response.body?.cancel();
|
|
67
|
+
throw new PdppAuthorizationUnavailableError();
|
|
68
|
+
}
|
|
69
|
+
const reader = response.body.getReader();
|
|
70
|
+
const chunks = [];
|
|
71
|
+
let size = 0;
|
|
72
|
+
try {
|
|
73
|
+
while (true) {
|
|
74
|
+
const { done, value } = await reader.read();
|
|
75
|
+
if (done)
|
|
76
|
+
break;
|
|
77
|
+
size += value.byteLength;
|
|
78
|
+
if (size > 1024 * 1024)
|
|
79
|
+
throw new PdppAuthorizationUnavailableError();
|
|
80
|
+
chunks.push(value);
|
|
81
|
+
}
|
|
82
|
+
const bytes = new Uint8Array(size);
|
|
83
|
+
let offset = 0;
|
|
84
|
+
for (const chunk of chunks) {
|
|
85
|
+
bytes.set(chunk, offset);
|
|
86
|
+
offset += chunk.length;
|
|
87
|
+
}
|
|
88
|
+
return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
89
|
+
}
|
|
90
|
+
finally {
|
|
91
|
+
await reader.cancel().catch(() => undefined);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Replica read authority stays at one configured issuer, with no positive cache.
|
|
96
|
+
* The host provisions a source-scoped issuer owner token for the existing
|
|
97
|
+
* authenticated introspection endpoint; it is never accepted as an app token.
|
|
98
|
+
* This harness-only credential has owner read authority. See the issuer's
|
|
99
|
+
* acknowledged separated-RS credential gap in pdpp-auth.ts (RFC 7662 route).
|
|
100
|
+
* No credential, grant, token, or revocation state is imported with records.
|
|
101
|
+
* This is explicit experimental host wiring, not ordinary bootstrap wiring.
|
|
102
|
+
* Inactive RFC 7662 results conceal revocation reasons and yield 401; the
|
|
103
|
+
* co-located issuer can distinguish grant_revoked and returns 403 instead.
|
|
104
|
+
* The returned grant is a translated local enforcement view. Its grant_id
|
|
105
|
+
* identifies the immutable issuer grant; never hash this view as consent.
|
|
106
|
+
*/
|
|
107
|
+
export function createReplicaAuthorizationService(options) {
|
|
108
|
+
if (!options.owner)
|
|
109
|
+
throw new Error("Replica owner is required");
|
|
110
|
+
const issuer = new URL(options.issuerOrigin);
|
|
111
|
+
if (issuer.username ||
|
|
112
|
+
issuer.password ||
|
|
113
|
+
issuer.search ||
|
|
114
|
+
issuer.hash ||
|
|
115
|
+
issuer.pathname !== "/" ||
|
|
116
|
+
(issuer.protocol !== "https:" &&
|
|
117
|
+
!(issuer.protocol === "http:" &&
|
|
118
|
+
["127.0.0.1", "[::1]"].includes(issuer.hostname)))) {
|
|
119
|
+
throw new Error("Replica issuer must be an HTTPS origin or loopback HTTP origin");
|
|
120
|
+
}
|
|
121
|
+
const fetcher = options.fetch ?? globalThis.fetch;
|
|
122
|
+
const now = options.now ?? Date.now;
|
|
123
|
+
const denied = (reason) => {
|
|
124
|
+
options.onDecision?.({ outcome: "denied", reason });
|
|
125
|
+
return { active: false };
|
|
126
|
+
};
|
|
127
|
+
const unavailable = (reason) => {
|
|
128
|
+
options.onDecision?.({ outcome: "unavailable", reason });
|
|
129
|
+
throw new PdppAuthorizationUnavailableError();
|
|
130
|
+
};
|
|
131
|
+
return {
|
|
132
|
+
async resolveToken(accessToken) {
|
|
133
|
+
if (!accessToken)
|
|
134
|
+
return denied("issuer_inactive");
|
|
135
|
+
let credential;
|
|
136
|
+
try {
|
|
137
|
+
credential = await options.credential();
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
return unavailable("credential_unavailable");
|
|
141
|
+
}
|
|
142
|
+
if (!credential)
|
|
143
|
+
return unavailable("credential_unavailable");
|
|
144
|
+
let wire;
|
|
145
|
+
try {
|
|
146
|
+
const response = await fetcher(`${issuer.origin}/pdpp/v1/introspect`, {
|
|
147
|
+
method: "POST",
|
|
148
|
+
headers: {
|
|
149
|
+
authorization: `Bearer ${credential}`,
|
|
150
|
+
"content-type": "application/x-www-form-urlencoded",
|
|
151
|
+
},
|
|
152
|
+
body: new URLSearchParams({ token: accessToken }),
|
|
153
|
+
redirect: "error",
|
|
154
|
+
cache: "no-store",
|
|
155
|
+
signal: AbortSignal.timeout(10_000),
|
|
156
|
+
});
|
|
157
|
+
wire = await readContext(response);
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
return unavailable("issuer_unavailable");
|
|
161
|
+
}
|
|
162
|
+
if (z
|
|
163
|
+
.object({ active: z.literal(false) })
|
|
164
|
+
.strict()
|
|
165
|
+
.safeParse(wire).success)
|
|
166
|
+
return denied("issuer_inactive");
|
|
167
|
+
if (z
|
|
168
|
+
.object({
|
|
169
|
+
active: z.literal(true),
|
|
170
|
+
pdpp_token_kind: z.literal("owner"),
|
|
171
|
+
})
|
|
172
|
+
.safeParse(wire).success)
|
|
173
|
+
return denied("token_kind");
|
|
174
|
+
const parsed = introspection.safeParse(wire);
|
|
175
|
+
if (!parsed.success)
|
|
176
|
+
return unavailable("invalid_response");
|
|
177
|
+
const context = parsed.data;
|
|
178
|
+
const grant = context.authorization_details[0].grant;
|
|
179
|
+
if (context.subject_id !== options.owner ||
|
|
180
|
+
grant.subject.id !== options.owner)
|
|
181
|
+
return denied("owner_mismatch");
|
|
182
|
+
if (grant.grant_id !== context.grant_id ||
|
|
183
|
+
grant.client.client_id !== context.client_id)
|
|
184
|
+
return denied("grant_mismatch");
|
|
185
|
+
const currentTime = now();
|
|
186
|
+
if (context.exp * 1000 <= currentTime ||
|
|
187
|
+
(grant.expires_at && Date.parse(grant.expires_at) <= currentTime))
|
|
188
|
+
return denied("expired");
|
|
189
|
+
const mappings = options.connections();
|
|
190
|
+
let mappingFailure;
|
|
191
|
+
const streams = grant.streams.map((stream) => ({
|
|
192
|
+
...stream,
|
|
193
|
+
instance_ids: stream.instance_ids.map((handle) => {
|
|
194
|
+
const matches = mappings.filter((entry) => entry.issuerInstanceId === handle &&
|
|
195
|
+
entry.owner === options.owner &&
|
|
196
|
+
entry.sourceId === grant.source.id);
|
|
197
|
+
const identities = new Set(matches.map((entry) => entry.connectionId));
|
|
198
|
+
if (identities.size !== 1) {
|
|
199
|
+
mappingFailure =
|
|
200
|
+
identities.size === 0 ? "mapping_missing" : "mapping_ambiguous";
|
|
201
|
+
return "";
|
|
202
|
+
}
|
|
203
|
+
let local;
|
|
204
|
+
try {
|
|
205
|
+
local = resolveLocalInstance({
|
|
206
|
+
owner: options.owner,
|
|
207
|
+
sourceId: grant.source.id,
|
|
208
|
+
connectionId: matches[0].connectionId,
|
|
209
|
+
}, mappings);
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
mappingFailure = "mapping_invalid";
|
|
213
|
+
}
|
|
214
|
+
if (!local)
|
|
215
|
+
mappingFailure ??= "mapping_missing";
|
|
216
|
+
return local ?? "";
|
|
217
|
+
}),
|
|
218
|
+
}));
|
|
219
|
+
if (mappingFailure)
|
|
220
|
+
return denied(mappingFailure);
|
|
221
|
+
if (streams.some((stream) => new Set(stream.instance_ids).size !== stream.instance_ids.length))
|
|
222
|
+
return denied("mapping_collapsed");
|
|
223
|
+
return {
|
|
224
|
+
active: true,
|
|
225
|
+
tokenKind: "client",
|
|
226
|
+
subjectId: context.subject_id,
|
|
227
|
+
clientId: context.client_id,
|
|
228
|
+
expiresAt: new Date(context.exp * 1000).toISOString(),
|
|
229
|
+
grant: { ...grant, streams },
|
|
230
|
+
};
|
|
231
|
+
},
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
//# sourceMappingURL=replica-authorization.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replica-authorization.js","sourceRoot":"","sources":["../../src/pdpp/replica-authorization.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,6 +5,7 @@ import type { PendingBlobDeletionStore } from "@opendatalabs/personal-server-ts-
|
|
|
5
5
|
* each. Lives next to `sync-cursor.json` under the server root; written
|
|
6
6
|
* atomically (tmp + rename) like the cursor. A version-1 file (whole scopes)
|
|
7
7
|
* is read as version-less markers the retry expands into exact keys.
|
|
8
|
+
* `planned` keeps each scope's planned-through watermark in the same write.
|
|
8
9
|
*/
|
|
9
10
|
export declare function createFilePendingBlobDeletionStore(filePath: string): PendingBlobDeletionStore;
|
|
10
11
|
//# sourceMappingURL=pending-blob-deletions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pending-blob-deletions.d.ts","sourceRoot":"","sources":["../src/pending-blob-deletions.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"pending-blob-deletions.d.ts","sourceRoot":"","sources":["../src/pending-blob-deletions.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAEV,wBAAwB,EACzB,MAAM,6CAA6C,CAAC;AAarD;;;;;;;GAOG;AACH,wBAAgB,kCAAkC,CAChD,QAAQ,EAAE,MAAM,GACf,wBAAwB,CAmC1B"}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
2
2
|
import { dirname } from "node:path";
|
|
3
|
-
import { createPendingBlobDeletionStore, normalizePendingBlobDeletions, } from "@opendatalabs/personal-server-ts-core/sync";
|
|
3
|
+
import { createPendingBlobDeletionStore, normalizePendingBlobDeletionPlanned, normalizePendingBlobDeletions, } from "@opendatalabs/personal-server-ts-core/sync";
|
|
4
4
|
/**
|
|
5
5
|
* File-backed retry markers for blob deletions whose gateway tombstone landed
|
|
6
6
|
* but whose storage DELETE did not finish: one exact (scope, version) key
|
|
7
7
|
* each. Lives next to `sync-cursor.json` under the server root; written
|
|
8
8
|
* atomically (tmp + rename) like the cursor. A version-1 file (whole scopes)
|
|
9
9
|
* is read as version-less markers the retry expands into exact keys.
|
|
10
|
+
* `planned` keeps each scope's planned-through watermark in the same write.
|
|
10
11
|
*/
|
|
11
12
|
export function createFilePendingBlobDeletionStore(filePath) {
|
|
12
13
|
const kv = {
|
|
@@ -14,7 +15,10 @@ export function createFilePendingBlobDeletionStore(filePath) {
|
|
|
14
15
|
try {
|
|
15
16
|
const raw = await readFile(filePath, "utf8");
|
|
16
17
|
const parsed = JSON.parse(raw);
|
|
17
|
-
return
|
|
18
|
+
return {
|
|
19
|
+
keys: normalizePendingBlobDeletions(parsed.keys ?? parsed.scopes),
|
|
20
|
+
planned: normalizePendingBlobDeletionPlanned(parsed.planned),
|
|
21
|
+
};
|
|
18
22
|
}
|
|
19
23
|
catch (err) {
|
|
20
24
|
if (err instanceof Error &&
|
|
@@ -25,10 +29,10 @@ export function createFilePendingBlobDeletionStore(filePath) {
|
|
|
25
29
|
throw err;
|
|
26
30
|
}
|
|
27
31
|
},
|
|
28
|
-
async write(keys) {
|
|
32
|
+
async write(keys, planned) {
|
|
29
33
|
await mkdir(dirname(filePath), { recursive: true });
|
|
30
34
|
const tmpPath = `${filePath}.tmp`;
|
|
31
|
-
const state = { version: 2, keys };
|
|
35
|
+
const state = { version: 2, keys, planned };
|
|
32
36
|
await writeFile(tmpPath, `${JSON.stringify(state, null, 2)}\n`);
|
|
33
37
|
await rename(tmpPath, filePath);
|
|
34
38
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pending-blob-deletions.js","sourceRoot":"","sources":["../src/pending-blob-deletions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EACL,8BAA8B,EAC9B,6BAA6B,
|
|
1
|
+
{"version":3,"file":"pending-blob-deletions.js","sourceRoot":"","sources":["../src/pending-blob-deletions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EACL,8BAA8B,EAC9B,mCAAmC,EACnC,6BAA6B,GAG9B,MAAM,4CAA4C,CAAC;AAiBpD;;;;;;;GAOG;AACH,MAAM,UAAU,kCAAkC,CAChD,QAAgB;IAEhB,MAAM,EAAE,GAA0B;QAChC,KAAK,CAAC,IAAI;YACR,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;gBAC7C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAK5B,CAAC;gBACF,OAAO;oBACL,IAAI,EAAE,6BAA6B,CAAC,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC;oBACjE,OAAO,EAAE,mCAAmC,CAAC,MAAM,CAAC,OAAO,CAAC;iBAC7D,CAAC;YACJ,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IACE,GAAG,YAAY,KAAK;oBACpB,MAAM,IAAI,GAAG;oBACZ,GAA6B,CAAC,IAAI,KAAK,QAAQ,EAChD,CAAC;oBACD,OAAO,IAAI,CAAC;gBACd,CAAC;gBACD,MAAM,GAAG,CAAC;YACZ,CAAC;QACH,CAAC;QACD,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO;YACvB,MAAM,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACpD,MAAM,OAAO,GAAG,GAAG,QAAQ,MAAM,CAAC;YAClC,MAAM,KAAK,GAA6B,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;YACtE,MAAM,SAAS,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;YAChE,MAAM,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAClC,CAAC;KACF,CAAC;IACF,OAAO,8BAA8B,CAAC,EAAE,CAAC,CAAC;AAC5C,CAAC"}
|
package/dist/routes/mcp.d.ts
CHANGED
|
@@ -19,9 +19,10 @@
|
|
|
19
19
|
import { Hono } from "hono";
|
|
20
20
|
import type { Logger } from "pino";
|
|
21
21
|
import type { DataPortabilityGatewayConfig, GatewayClient } from "@opendatalabs/vana-sdk/node";
|
|
22
|
-
import { buildMcpUrl, buildStableMcpUrl, type McpProofReplayStore, type McpConnectionRecord, type McpConnectionStore, type McpSessionStore, type McpOAuthAuthorizationStore, type McpActivityRecorder, type McpScopeRequestApprovalUrlHook } from "@opendatalabs/personal-server-ts-core/mcp";
|
|
22
|
+
import { buildMcpUrl, buildStableMcpUrl, type McpProofReplayStore, type McpConnectionRecord, type McpConnectionStore, type McpSessionStore, type McpOAuthAuthorizationStore, type McpOAuthClientStore, type McpActivityRecorder, type McpScopeRequestApprovalUrlHook } from "@opendatalabs/personal-server-ts-core/mcp";
|
|
23
23
|
import type { PersonalServerReadFulfillmentReporter } from "@opendatalabs/personal-server-ts-core/api";
|
|
24
24
|
import type { AccessLogWriter } from "@opendatalabs/personal-server-ts-core/logging/access-log";
|
|
25
|
+
import type { WriteSessionStore } from "@opendatalabs/personal-server-ts-core/write";
|
|
25
26
|
import type { ServerSigner } from "@opendatalabs/personal-server-ts-core/signing";
|
|
26
27
|
import type { HierarchyManagerOptions } from "@opendatalabs/personal-server-ts-core/storage/hierarchy";
|
|
27
28
|
import type { IndexManager } from "@opendatalabs/personal-server-ts-core/storage/index";
|
|
@@ -82,6 +83,8 @@ export interface McpRouteDeps {
|
|
|
82
83
|
* `/mcp/oauth/token`, and the owner approval endpoint.
|
|
83
84
|
*/
|
|
84
85
|
oauthAuthorizationStore?: McpOAuthAuthorizationStore;
|
|
86
|
+
/** DCR client metadata, read back at authorize to name the connection. */
|
|
87
|
+
oauthClientStore?: McpOAuthClientStore;
|
|
85
88
|
/** Activity recorder for live MCP call visibility. */
|
|
86
89
|
activityRecorder?: McpActivityRecorder;
|
|
87
90
|
/**
|
|
@@ -95,6 +98,19 @@ export interface McpRouteDeps {
|
|
|
95
98
|
* user. Absent: the agent is told to wait for approval in Vana.
|
|
96
99
|
*/
|
|
97
100
|
scopeRequestApprovalUrl?: McpScopeRequestApprovalUrlHook;
|
|
101
|
+
/**
|
|
102
|
+
* MCP writes (`remember`): the store the data route redeems write sessions
|
|
103
|
+
* from, and a dispatcher into the mounted data route. Absent: MCP stays
|
|
104
|
+
* read-only and `remember` answers write_unavailable.
|
|
105
|
+
*/
|
|
106
|
+
mcpWrites?: {
|
|
107
|
+
sessionStore: WriteSessionStore;
|
|
108
|
+
dispatch(request: Request): Promise<Response>;
|
|
109
|
+
};
|
|
110
|
+
/** Loopback auth listener port; requests on it are local MCP clients. */
|
|
111
|
+
localApprovalPort?: number;
|
|
112
|
+
/** Main (tunneled) port; a collision with `localApprovalPort` disables local MCP. */
|
|
113
|
+
serverPort?: number;
|
|
98
114
|
}
|
|
99
115
|
/** Shared engine for ordinary MCP connections and authenticated TEE dispatch. */
|
|
100
116
|
export declare function executeMcpConnectionRequest(request: Request, connection: McpConnectionRecord, deps: McpRouteDeps): Promise<Response>;
|
|
@@ -109,7 +125,7 @@ export declare function mcpConnectionsRoutes(deps: McpRouteDeps): Hono;
|
|
|
109
125
|
* Mount at `/` so the well-known documents are served from the PS origin and
|
|
110
126
|
* Claude can discover auth for the stable `/mcp` resource.
|
|
111
127
|
*/
|
|
112
|
-
export type McpOAuthRouteDeps = Pick<McpRouteDeps, "connectionStore" | "oauthAuthorizationStore" | "oauthApprovalUrl" | "serverOrigin" | "serverOwner" | "gateway" | "devToken" | "accessToken" | "tokenStore" | "gatewayConfig" | "serverSigner" | "mcpGrantTtlSeconds">;
|
|
128
|
+
export type McpOAuthRouteDeps = Pick<McpRouteDeps, "connectionStore" | "oauthAuthorizationStore" | "oauthClientStore" | "oauthApprovalUrl" | "serverOrigin" | "serverOwner" | "gateway" | "devToken" | "accessToken" | "tokenStore" | "gatewayConfig" | "serverSigner" | "mcpGrantTtlSeconds" | "localApprovalPort" | "serverPort">;
|
|
113
129
|
export declare function mcpOAuthRoutes(deps: McpOAuthRouteDeps): Hono;
|
|
114
130
|
/**
|
|
115
131
|
* Hono sub-app for the Claude-facing Streamable HTTP endpoint.
|
package/dist/routes/mcp.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../../src/routes/mcp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AACnC,OAAO,KAAK,EACV,4BAA4B,EAC5B,aAAa,EACd,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAML,WAAW,EACX,iBAAiB,
|
|
1
|
+
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../../src/routes/mcp.ts"],"names":[],"mappings":"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"}
|
package/dist/routes/mcp.js
CHANGED
|
@@ -18,16 +18,40 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import { randomBytes } from "node:crypto";
|
|
20
20
|
import { Hono } from "hono";
|
|
21
|
-
import { approveMcpOAuthAuthorization, approveMcpOAuthAuthorizationWithScopes, approveMcpConnection, approveMcpScopeAccessRequest, buildMcpProtectedResourceMetadataUrl, buildMcpUrl, buildStableMcpUrl, createInMemoryMcpOAuthAuthorizationStore, createMcpConnection, createMcpOAuthAuthorization, createInMemoryMcpConnectionStore, createInMemoryMcpSessionStore, createInMemoryMcpProofReplayStore, createMcpSession, createMcpSessionAuthPort, buildMcpSessionConnection, createMcpDataReadClient, denyMcpScopeAccessRequest, getMcpConnectionView, handleMcpStreamableHttpRequest, hashConnectionToken, listMcpConnectionViews, loadMcpGranteeAccount, MCP_CONNECTION_NOT_FOUND, McpConnectionNotFoundError, McpConnectionStateError, McpOAuthAuthorizationError, isAllowedOAuthRedirectUri, McpScopeRequestError, MCP_SCOPE_REQUEST_NOT_FOUND, MCP_SCOPE_REQUEST_TOKEN_HEADER, readMcpScopeRequestWithToken, redeemMcpOAuthAuthorizationCode, refreshMcpOAuthToken, requestMcpScopeAccess, toMcpOAuthAuthorizationView, revokeMcpConnection, toMcpConnectionView, } from "@opendatalabs/personal-server-ts-core/mcp";
|
|
21
|
+
import { approveMcpOAuthAuthorization, approveMcpOAuthAuthorizationWithScopes, approveMcpConnection, approveMcpScopeAccessRequest, buildMcpProtectedResourceMetadataUrl, buildMcpUrl, buildStableMcpUrl, createInMemoryMcpOAuthAuthorizationStore, createInMemoryMcpOAuthClientStore, createMcpConnection, createMcpOAuthAuthorization, createInMemoryMcpConnectionStore, createInMemoryMcpSessionStore, createInMemoryMcpProofReplayStore, createMcpSession, createMcpSessionAuthPort, buildMcpSessionConnection, createMcpDataReadClient, createMcpDataWriteClient, denyMcpScopeAccessRequest, getMcpConnectionView, handleMcpStreamableHttpRequest, hashConnectionToken, listMcpConnectionViews, loadMcpGranteeAccount, MCP_CONNECTION_NOT_FOUND, McpConnectionNotFoundError, McpConnectionStateError, McpIngress, McpOAuthAuthorizationError, isAllowedOAuthRedirectUri, McpScopeRequestError, MCP_SCOPE_REQUEST_NOT_FOUND, MCP_SCOPE_REQUEST_TOKEN_HEADER, readMcpScopeRequestWithToken, redeemMcpOAuthAuthorizationCode, refreshMcpOAuthToken, requestMcpScopeAccess, toMcpOAuthAuthorizationView, revokeMcpConnection, toMcpConnectionView, } from "@opendatalabs/personal-server-ts-core/mcp";
|
|
22
22
|
import { ProtocolError } from "@opendatalabs/personal-server-ts-core/errors";
|
|
23
23
|
import { createServerApiAuth } from "../api-auth.js";
|
|
24
24
|
import { createBodyLimit, DEFAULT_MAX_SIZE } from "../middleware/body-limit.js";
|
|
25
|
+
import { arrivedOnLoopbackListener } from "../middleware/local-listener.js";
|
|
25
26
|
import { authenticateRequest, isWeb3SignedLifetimeError, web3SignedProofId, boundedProofExpiry, } from "@opendatalabs/personal-server-ts-core/auth";
|
|
27
|
+
import { verifyDataWritePolicy } from "@opendatalabs/personal-server-ts-core/policy";
|
|
26
28
|
import { withLegacyProjection } from "@opendatalabs/personal-server-ts-core/storage/legacy-projection";
|
|
27
29
|
import { createNodeDataStorage } from "../storage/node-data-storage.js";
|
|
28
30
|
function resolveOrigin(origin) {
|
|
29
31
|
return typeof origin === "function" ? origin() : origin;
|
|
30
32
|
}
|
|
33
|
+
/** Which listener accepted the request: the loopback one or the tunneled main one. */
|
|
34
|
+
function requestIngress(c, deps) {
|
|
35
|
+
const listeners = {
|
|
36
|
+
localApprovalPort: deps.localApprovalPort,
|
|
37
|
+
serverPort: deps.serverPort,
|
|
38
|
+
};
|
|
39
|
+
return arrivedOnLoopbackListener(c, listeners)
|
|
40
|
+
? McpIngress.Loopback
|
|
41
|
+
: McpIngress.Public;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The origin every OAuth/discovery URL in this response must name. A local
|
|
45
|
+
* client on the loopback listener gets the exact origin it called (RFC 9728:
|
|
46
|
+
* the advertised resource must match the URL the client used, so `localhost`
|
|
47
|
+
* and `127.0.0.1` differ); everyone else gets the public origin.
|
|
48
|
+
*/
|
|
49
|
+
function requestOrigin(c, deps) {
|
|
50
|
+
if (requestIngress(c, deps) === McpIngress.Public) {
|
|
51
|
+
return resolveOrigin(deps.serverOrigin);
|
|
52
|
+
}
|
|
53
|
+
return new URL(c.req.url).origin;
|
|
54
|
+
}
|
|
31
55
|
function jsonError(status, errorCode, message) {
|
|
32
56
|
return {
|
|
33
57
|
error: { code: status, errorCode, message },
|
|
@@ -59,6 +83,33 @@ const SUPPORTED_GRANT_TYPES = [
|
|
|
59
83
|
function isSupportedGrantType(value) {
|
|
60
84
|
return SUPPORTED_GRANT_TYPES.includes(value);
|
|
61
85
|
}
|
|
86
|
+
/** Longest DCR client_name kept, in code points; it labels the connection. */
|
|
87
|
+
const MAX_CLIENT_NAME_CHARS = 64;
|
|
88
|
+
const MAX_CLIENT_URI_CHARS = 2048;
|
|
89
|
+
/** Control and format chars: NUL, newlines, bidi overrides, zero-width. */
|
|
90
|
+
const INVISIBLE_CHARS = /[\p{Cc}\p{Cf}]/gu;
|
|
91
|
+
const WEB_PROTOCOLS = new Set(["https:", "http:"]);
|
|
92
|
+
/** A printable label from an untrusted string, e.g. " Maple\u202e " → "Maple". */
|
|
93
|
+
function cleanClientName(value) {
|
|
94
|
+
if (typeof value !== "string")
|
|
95
|
+
return undefined;
|
|
96
|
+
const visible = value.replace(INVISIBLE_CHARS, "").trim();
|
|
97
|
+
const capped = Array.from(visible).slice(0, MAX_CLIENT_NAME_CHARS).join("");
|
|
98
|
+
return capped.trim() || undefined;
|
|
99
|
+
}
|
|
100
|
+
/** An http(s) URL or nothing: the owner may be shown it as a link. */
|
|
101
|
+
function cleanClientUri(value) {
|
|
102
|
+
if (typeof value !== "string" || value.length > MAX_CLIENT_URI_CHARS) {
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
const url = new URL(value.trim());
|
|
107
|
+
return WEB_PROTOCOLS.has(url.protocol) ? url.href : undefined;
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
62
113
|
function authorizationServerMetadata(origin) {
|
|
63
114
|
return {
|
|
64
115
|
issuer: origin,
|
|
@@ -140,6 +191,31 @@ function buildDataApiDeps(deps) {
|
|
|
140
191
|
logger: deps.logger,
|
|
141
192
|
};
|
|
142
193
|
}
|
|
194
|
+
/** Writes as the connection grantee, when the host wired MCP writes. */
|
|
195
|
+
function buildWriteClient(deps, granteeAccount, storage) {
|
|
196
|
+
const serverOwner = deps.serverOwner;
|
|
197
|
+
if (!deps.mcpWrites || !serverOwner)
|
|
198
|
+
return undefined;
|
|
199
|
+
return createMcpDataWriteClient({
|
|
200
|
+
serverOrigin: resolveOrigin(deps.serverOrigin),
|
|
201
|
+
granteeAccount,
|
|
202
|
+
storage,
|
|
203
|
+
sessionStore: deps.mcpWrites.sessionStore,
|
|
204
|
+
dispatch: deps.mcpWrites.dispatch,
|
|
205
|
+
authorize: async ({ scope, grantId }) => {
|
|
206
|
+
await verifyDataWritePolicy({
|
|
207
|
+
signer: granteeAccount.address,
|
|
208
|
+
grantId,
|
|
209
|
+
requestedScope: scope,
|
|
210
|
+
serverOwner,
|
|
211
|
+
}, {
|
|
212
|
+
authSessionVerifier: deps.gateway,
|
|
213
|
+
grantVerifier: deps.gateway,
|
|
214
|
+
runtimeAvailability: deps.runtimeAvailability,
|
|
215
|
+
});
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
}
|
|
143
219
|
/** Shared engine for ordinary MCP connections and authenticated TEE dispatch. */
|
|
144
220
|
export function executeMcpConnectionRequest(request, connection, deps) {
|
|
145
221
|
const granteeAccount = loadMcpGranteeAccount({
|
|
@@ -147,13 +223,15 @@ export function executeMcpConnectionRequest(request, connection, deps) {
|
|
|
147
223
|
publicKey: connection.granteePublicKey,
|
|
148
224
|
encryptedPrivateKey: connection.encryptedGranteePrivateKey,
|
|
149
225
|
});
|
|
226
|
+
const dataApiDeps = buildDataApiDeps(deps);
|
|
150
227
|
return handleMcpStreamableHttpRequest(request, {
|
|
151
228
|
connection,
|
|
152
229
|
readClient: createMcpDataReadClient({
|
|
153
230
|
serverOrigin: resolveOrigin(deps.serverOrigin),
|
|
154
231
|
granteeAccount,
|
|
155
|
-
dataApiDeps
|
|
232
|
+
dataApiDeps,
|
|
156
233
|
}),
|
|
234
|
+
writeClient: buildWriteClient(deps, granteeAccount, dataApiDeps.storage),
|
|
157
235
|
activityRecorder: deps.activityRecorder,
|
|
158
236
|
// Denied tool calls are recorded by the wrapper, not the read path.
|
|
159
237
|
reporterDeps: {
|
|
@@ -401,6 +479,7 @@ export function mcpOAuthRoutes(deps) {
|
|
|
401
479
|
app.use("*", createBodyLimit(DEFAULT_MAX_SIZE));
|
|
402
480
|
const connectionStore = deps.connectionStore ?? createInMemoryMcpConnectionStore();
|
|
403
481
|
const authorizationStore = deps.oauthAuthorizationStore ?? createInMemoryMcpOAuthAuthorizationStore();
|
|
482
|
+
const clientStore = deps.oauthClientStore ?? createInMemoryMcpOAuthClientStore();
|
|
404
483
|
const ownerAuth = createServerApiAuth({
|
|
405
484
|
serverOrigin: deps.serverOrigin,
|
|
406
485
|
serverOwner: deps.serverOwner,
|
|
@@ -422,10 +501,10 @@ export function mcpOAuthRoutes(deps) {
|
|
|
422
501
|
}
|
|
423
502
|
}
|
|
424
503
|
app.get("/.well-known/oauth-protected-resource/mcp", (c) => resolveApprovalUrl(deps)
|
|
425
|
-
? c.json(protectedResourceMetadata(
|
|
504
|
+
? c.json(protectedResourceMetadata(requestOrigin(c, deps)))
|
|
426
505
|
: c.json(jsonError(404, "MCP_OAUTH_NOT_CONFIGURED", "MCP OAuth is not configured"), 404));
|
|
427
506
|
app.get("/.well-known/oauth-authorization-server", (c) => resolveApprovalUrl(deps)
|
|
428
|
-
? c.json(authorizationServerMetadata(
|
|
507
|
+
? c.json(authorizationServerMetadata(requestOrigin(c, deps)))
|
|
429
508
|
: c.json(jsonError(404, "MCP_OAUTH_NOT_CONFIGURED", "MCP OAuth is not configured"), 404));
|
|
430
509
|
app.post("/mcp/oauth/register", async (c) => {
|
|
431
510
|
if (!resolveApprovalUrl(deps)) {
|
|
@@ -445,9 +524,20 @@ export function mcpOAuthRoutes(deps) {
|
|
|
445
524
|
error_description: "One or more redirect_uris are not allowed",
|
|
446
525
|
}, 400);
|
|
447
526
|
}
|
|
527
|
+
// Kept so /authorize can name the connection after the client.
|
|
528
|
+
const clientName = cleanClientName(body.client_name);
|
|
529
|
+
const clientUri = cleanClientUri(body.client_uri);
|
|
530
|
+
const client = {
|
|
531
|
+
clientId: `mcp-client-${crypto.randomUUID()}`,
|
|
532
|
+
...(clientName ? { clientName } : {}),
|
|
533
|
+
...(clientUri ? { clientUri } : {}),
|
|
534
|
+
createdAt: new Date().toISOString(),
|
|
535
|
+
};
|
|
536
|
+
await clientStore.create(client);
|
|
448
537
|
return c.json({
|
|
449
|
-
client_id:
|
|
450
|
-
client_name:
|
|
538
|
+
client_id: client.clientId,
|
|
539
|
+
...(clientName ? { client_name: clientName } : {}),
|
|
540
|
+
...(clientUri ? { client_uri: clientUri } : {}),
|
|
451
541
|
redirect_uris: Array.isArray(body.redirect_uris)
|
|
452
542
|
? body.redirect_uris
|
|
453
543
|
: [],
|
|
@@ -460,7 +550,7 @@ export function mcpOAuthRoutes(deps) {
|
|
|
460
550
|
if (!resolveApprovalUrl(deps)) {
|
|
461
551
|
return c.json(jsonError(404, "MCP_OAUTH_NOT_CONFIGURED", "MCP OAuth is not configured"), 404);
|
|
462
552
|
}
|
|
463
|
-
const origin =
|
|
553
|
+
const origin = requestOrigin(c, deps);
|
|
464
554
|
const url = new URL(c.req.url);
|
|
465
555
|
const responseType = url.searchParams.get("response_type");
|
|
466
556
|
const clientId = url.searchParams.get("client_id") ?? "";
|
|
@@ -473,13 +563,18 @@ export function mcpOAuthRoutes(deps) {
|
|
|
473
563
|
return redirectWithOAuthError(redirectUri, "unsupported_response_type", "Only response_type=code is supported", state);
|
|
474
564
|
}
|
|
475
565
|
try {
|
|
566
|
+
// Unknown ids (registered elsewhere or before) get the default name.
|
|
567
|
+
const client = clientId ? await clientStore.getById(clientId) : null;
|
|
476
568
|
const created = await createMcpOAuthAuthorization({
|
|
477
569
|
clientId,
|
|
570
|
+
clientName: client?.clientName,
|
|
571
|
+
clientUri: client?.clientUri,
|
|
478
572
|
redirectUri,
|
|
479
573
|
codeChallenge,
|
|
480
574
|
codeChallengeMethod,
|
|
481
575
|
scope,
|
|
482
576
|
...(state ? { state } : {}),
|
|
577
|
+
ingress: requestIngress(c, deps),
|
|
483
578
|
}, {
|
|
484
579
|
connectionStore,
|
|
485
580
|
authorizationStore,
|
|
@@ -491,7 +586,8 @@ export function mcpOAuthRoutes(deps) {
|
|
|
491
586
|
}
|
|
492
587
|
const approve = new URL(approvalUrl);
|
|
493
588
|
approve.searchParams.set("mcp_authorization", created.authorizationId);
|
|
494
|
-
|
|
589
|
+
// The approver signs for the registered public origin, never loopback.
|
|
590
|
+
approve.searchParams.set("ps_origin", resolveOrigin(deps.serverOrigin));
|
|
495
591
|
return c.redirect(approve.toString(), 302);
|
|
496
592
|
}
|
|
497
593
|
catch (err) {
|
|
@@ -652,6 +748,7 @@ export function mcpStreamableHttpRoutes(deps) {
|
|
|
652
748
|
const response = await handleMcpStreamableHttpRequest(c.req.raw, {
|
|
653
749
|
connection: record,
|
|
654
750
|
readClient,
|
|
751
|
+
writeClient: buildWriteClient(deps, granteeAccount, dataApiDeps.storage),
|
|
655
752
|
activityRecorder: deps.activityRecorder,
|
|
656
753
|
requestScopeAccess: async (input) => {
|
|
657
754
|
return requestMcpScopeAccess({ connectionId: record.id, ...input }, { store });
|
|
@@ -744,19 +841,22 @@ export function mcpStreamableHttpRoutes(deps) {
|
|
|
744
841
|
if (!options.oauthChallenge) {
|
|
745
842
|
return c.json(jsonError(401, "INVALID_TOKEN", "Missing MCP connection token"), 401);
|
|
746
843
|
}
|
|
747
|
-
return mcpUnauthorized(
|
|
844
|
+
return mcpUnauthorized(requestOrigin(c, deps));
|
|
748
845
|
}
|
|
749
846
|
const tokenHash = await hashConnectionToken(rawToken);
|
|
750
847
|
const record = await store.getByTokenHash(tokenHash);
|
|
751
|
-
|
|
848
|
+
// A token presented on the other listener reads as unknown.
|
|
849
|
+
const ingress = record?.ingress ?? McpIngress.Public;
|
|
850
|
+
if (record && ingress === requestIngress(c, deps)) {
|
|
752
851
|
return handleConnectionRead(c, record);
|
|
852
|
+
}
|
|
753
853
|
const session = await sessionStore.getByTokenHash(tokenHash);
|
|
754
854
|
if (session)
|
|
755
855
|
return handleSessionRead(c, session);
|
|
756
856
|
if (!options.oauthChallenge) {
|
|
757
857
|
return c.json(jsonError(401, "INVALID_TOKEN", "Unknown or revoked MCP connection"), 401);
|
|
758
858
|
}
|
|
759
|
-
return mcpUnauthorized(
|
|
859
|
+
return mcpUnauthorized(requestOrigin(c, deps), "Unknown or revoked MCP connection");
|
|
760
860
|
}
|
|
761
861
|
// Self-signing handshake (Option A): the app proves control of its key + DCR
|
|
762
862
|
// grant with a Web3Signed proof, and gets a short-lived bearer session token.
|