@solvapay/mcp 0.1.0 → 0.2.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 +35 -9
- package/dist/chunk-3VDAADZU.js +211 -0
- package/dist/express/index.cjs +504 -0
- package/dist/express/index.d.cts +65 -0
- package/dist/express/index.d.ts +65 -0
- package/dist/express/index.js +481 -0
- package/dist/fetch/index.cjs +787 -0
- package/dist/fetch/index.d.cts +238 -0
- package/dist/fetch/index.d.ts +238 -0
- package/dist/fetch/index.js +565 -0
- package/dist/index.cjs +116 -75
- package/dist/index.d.cts +46 -30
- package/dist/index.d.ts +46 -30
- package/dist/index.js +14 -171
- package/package.json +16 -7
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/express/index.ts
|
|
21
|
+
var express_exports = {};
|
|
22
|
+
__export(express_exports, {
|
|
23
|
+
McpBearerAuthError: () => import_mcp_core2.McpBearerAuthError,
|
|
24
|
+
buildAuthInfoFromBearer: () => import_mcp_core2.buildAuthInfoFromBearer,
|
|
25
|
+
createMcpOAuthBridge: () => createMcpOAuthBridge,
|
|
26
|
+
createOAuthAuthorizeHandler: () => createOAuthAuthorizeHandler,
|
|
27
|
+
createOAuthRegisterHandler: () => createOAuthRegisterHandler,
|
|
28
|
+
createOAuthRevokeHandler: () => createOAuthRevokeHandler,
|
|
29
|
+
createOAuthTokenHandler: () => createOAuthTokenHandler,
|
|
30
|
+
decodeJwtPayload: () => import_mcp_core2.decodeJwtPayload,
|
|
31
|
+
extractBearerToken: () => import_mcp_core2.extractBearerToken,
|
|
32
|
+
getCustomerRefFromBearerAuthHeader: () => import_mcp_core2.getCustomerRefFromBearerAuthHeader,
|
|
33
|
+
getCustomerRefFromJwtPayload: () => import_mcp_core2.getCustomerRefFromJwtPayload,
|
|
34
|
+
getOAuthAuthorizationServerResponse: () => import_mcp_core2.getOAuthAuthorizationServerResponse,
|
|
35
|
+
getOAuthProtectedResourceResponse: () => import_mcp_core2.getOAuthProtectedResourceResponse
|
|
36
|
+
});
|
|
37
|
+
module.exports = __toCommonJS(express_exports);
|
|
38
|
+
|
|
39
|
+
// src/express/oauth-bridge.ts
|
|
40
|
+
var import_mcp_core = require("@solvapay/mcp-core");
|
|
41
|
+
var NATIVE_CLIENT_ORIGIN_SCHEMES = ["cursor:", "vscode:", "vscode-webview:", "claude:"];
|
|
42
|
+
function getRequestAuthHeader(req) {
|
|
43
|
+
const header = req.headers?.authorization;
|
|
44
|
+
if (typeof header === "string") return header;
|
|
45
|
+
if (Array.isArray(header)) return header[0] || null;
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
function getHeader(req, name) {
|
|
49
|
+
const header = req.headers?.[name.toLowerCase()];
|
|
50
|
+
if (typeof header === "string") return header;
|
|
51
|
+
if (Array.isArray(header)) return header[0] || null;
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
function getRequestJsonRpcId(body) {
|
|
55
|
+
if (body && typeof body === "object" && "id" in body) {
|
|
56
|
+
const id = body.id;
|
|
57
|
+
return id ?? null;
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
function makeUnauthorizedJsonRpc(id) {
|
|
62
|
+
return {
|
|
63
|
+
jsonrpc: "2.0",
|
|
64
|
+
id,
|
|
65
|
+
error: {
|
|
66
|
+
code: -32001,
|
|
67
|
+
message: "Unauthorized"
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function setMcpChallengeHeader(res, publicBaseUrl, protectedResourcePath) {
|
|
72
|
+
res.setHeader(
|
|
73
|
+
"WWW-Authenticate",
|
|
74
|
+
`Bearer resource_metadata="${(0, import_mcp_core.withoutTrailingSlash)(publicBaseUrl)}${protectedResourcePath}"`
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
function getRequestQuery(req) {
|
|
78
|
+
const raw = req.url ?? req.path ?? "";
|
|
79
|
+
const qIndex = raw.indexOf("?");
|
|
80
|
+
return qIndex === -1 ? "" : raw.slice(qIndex);
|
|
81
|
+
}
|
|
82
|
+
function isNativeClientOrigin(origin) {
|
|
83
|
+
try {
|
|
84
|
+
const url = new URL(origin);
|
|
85
|
+
return NATIVE_CLIENT_ORIGIN_SCHEMES.includes(
|
|
86
|
+
url.protocol
|
|
87
|
+
);
|
|
88
|
+
} catch {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function applyCorsHeaders(req, res) {
|
|
93
|
+
const origin = getHeader(req, "origin");
|
|
94
|
+
if (!origin) return;
|
|
95
|
+
if (isNativeClientOrigin(origin)) {
|
|
96
|
+
res.setHeader("Access-Control-Allow-Origin", origin);
|
|
97
|
+
res.setHeader("Vary", "Origin");
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function handlePreflight(req, res) {
|
|
101
|
+
applyCorsHeaders(req, res);
|
|
102
|
+
const requestedMethod = getHeader(req, "access-control-request-method") ?? "POST";
|
|
103
|
+
const requestedHeaders = getHeader(req, "access-control-request-headers") ?? "authorization, content-type";
|
|
104
|
+
res.setHeader("Access-Control-Allow-Methods", `${requestedMethod}, OPTIONS`);
|
|
105
|
+
res.setHeader("Access-Control-Allow-Headers", requestedHeaders);
|
|
106
|
+
res.setHeader("Access-Control-Max-Age", "600");
|
|
107
|
+
res.status(204);
|
|
108
|
+
if (typeof res.end === "function") {
|
|
109
|
+
res.end();
|
|
110
|
+
} else {
|
|
111
|
+
res.json({});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
async function readJsonFromResponse(upstream) {
|
|
115
|
+
const text = await upstream.text();
|
|
116
|
+
if (!text) return { body: {}, text: "" };
|
|
117
|
+
try {
|
|
118
|
+
return { body: JSON.parse(text), text };
|
|
119
|
+
} catch {
|
|
120
|
+
return { body: text, text };
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
async function relayJsonResponse(upstream, res) {
|
|
124
|
+
const { body, text } = await readJsonFromResponse(upstream);
|
|
125
|
+
res.status(upstream.status);
|
|
126
|
+
const contentType = upstream.headers.get("content-type");
|
|
127
|
+
if (contentType) res.setHeader("Content-Type", contentType);
|
|
128
|
+
if (text === "" && upstream.status === 204) {
|
|
129
|
+
if (typeof res.end === "function") {
|
|
130
|
+
res.end();
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
res.json(body);
|
|
135
|
+
}
|
|
136
|
+
function hasOAuthErrorShape(body) {
|
|
137
|
+
return body !== null && typeof body === "object" && typeof body.error === "string";
|
|
138
|
+
}
|
|
139
|
+
function extractZodErrors(body) {
|
|
140
|
+
const errs = body.errors;
|
|
141
|
+
if (!Array.isArray(errs)) return [];
|
|
142
|
+
return errs.filter((e) => !!e && typeof e === "object");
|
|
143
|
+
}
|
|
144
|
+
function deriveOAuthErrorCode(status, nestBody) {
|
|
145
|
+
if (status === 401 || status === 403) return "invalid_client";
|
|
146
|
+
if (status >= 500) return "server_error";
|
|
147
|
+
const zodErrors = extractZodErrors(nestBody);
|
|
148
|
+
const touches = (field) => zodErrors.some((e) => {
|
|
149
|
+
const path = e.path;
|
|
150
|
+
return Array.isArray(path) && path.includes(field);
|
|
151
|
+
});
|
|
152
|
+
if (touches("grant_type")) {
|
|
153
|
+
const grantTypeErr = zodErrors.find((e) => {
|
|
154
|
+
const path = e.path;
|
|
155
|
+
return Array.isArray(path) && path.includes("grant_type");
|
|
156
|
+
});
|
|
157
|
+
const received = grantTypeErr && grantTypeErr.received;
|
|
158
|
+
if (received !== "undefined" && received !== void 0 && received !== "") {
|
|
159
|
+
return "unsupported_grant_type";
|
|
160
|
+
}
|
|
161
|
+
return "invalid_request";
|
|
162
|
+
}
|
|
163
|
+
if (touches("code") || touches("refresh_token")) return "invalid_grant";
|
|
164
|
+
if (touches("scope")) return "invalid_scope";
|
|
165
|
+
if (touches("client_id") || touches("client_secret")) return "invalid_client";
|
|
166
|
+
return "invalid_request";
|
|
167
|
+
}
|
|
168
|
+
function buildErrorDescription(nestBody) {
|
|
169
|
+
const zodErrors = extractZodErrors(nestBody);
|
|
170
|
+
if (zodErrors.length > 0) {
|
|
171
|
+
const parts = zodErrors.map((e) => {
|
|
172
|
+
const path = e.path;
|
|
173
|
+
const message2 = e.message;
|
|
174
|
+
const pathStr = Array.isArray(path) ? path.filter((p) => typeof p === "string").join(".") : "";
|
|
175
|
+
const msgStr = typeof message2 === "string" ? message2 : "";
|
|
176
|
+
if (pathStr && msgStr) return `${pathStr}: ${msgStr}`;
|
|
177
|
+
return pathStr || msgStr;
|
|
178
|
+
}).filter(Boolean);
|
|
179
|
+
if (parts.length > 0) return parts.join("; ");
|
|
180
|
+
}
|
|
181
|
+
const message = nestBody.message;
|
|
182
|
+
if (typeof message === "string") return message;
|
|
183
|
+
if (Array.isArray(message)) {
|
|
184
|
+
const strings = message.filter((m) => typeof m === "string");
|
|
185
|
+
if (strings.length > 0) return strings.join("; ");
|
|
186
|
+
}
|
|
187
|
+
return void 0;
|
|
188
|
+
}
|
|
189
|
+
function toOAuthErrorBody(body, text, status) {
|
|
190
|
+
if (hasOAuthErrorShape(body)) return body;
|
|
191
|
+
if (body && typeof body === "object") {
|
|
192
|
+
const nestBody = body;
|
|
193
|
+
const error = deriveOAuthErrorCode(status, nestBody);
|
|
194
|
+
const error_description = buildErrorDescription(nestBody);
|
|
195
|
+
return error_description ? { error, error_description } : { error };
|
|
196
|
+
}
|
|
197
|
+
const fallbackError = status >= 500 ? "server_error" : "invalid_request";
|
|
198
|
+
const description = typeof text === "string" && text.length > 0 && text.length < 500 ? text : void 0;
|
|
199
|
+
return description ? { error: fallbackError, error_description: description } : { error: fallbackError };
|
|
200
|
+
}
|
|
201
|
+
async function relayOAuthJsonResponse(upstream, res) {
|
|
202
|
+
if (upstream.ok || upstream.status === 204) {
|
|
203
|
+
await relayJsonResponse(upstream, res);
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
const { body, text } = await readJsonFromResponse(upstream);
|
|
207
|
+
const normalized = toOAuthErrorBody(body, text, upstream.status);
|
|
208
|
+
res.status(upstream.status);
|
|
209
|
+
res.setHeader("Content-Type", "application/json");
|
|
210
|
+
res.json(normalized);
|
|
211
|
+
}
|
|
212
|
+
function sendUpstreamError(res, _error) {
|
|
213
|
+
res.status(502);
|
|
214
|
+
res.setHeader("Content-Type", "application/json");
|
|
215
|
+
res.json({ error: "upstream_unreachable" });
|
|
216
|
+
}
|
|
217
|
+
function serializeRegisterBody(body) {
|
|
218
|
+
if (typeof body === "string") return body;
|
|
219
|
+
if (body instanceof Uint8Array) return Buffer.from(body).toString("utf8");
|
|
220
|
+
return JSON.stringify(body ?? {});
|
|
221
|
+
}
|
|
222
|
+
function serializeFormBody(body) {
|
|
223
|
+
if (typeof body === "string") return body;
|
|
224
|
+
if (body instanceof Uint8Array) return Buffer.from(body).toString("utf8");
|
|
225
|
+
if (body && typeof body === "object") {
|
|
226
|
+
const params = new URLSearchParams();
|
|
227
|
+
for (const [key, value] of Object.entries(body)) {
|
|
228
|
+
if (value === void 0 || value === null) continue;
|
|
229
|
+
if (Array.isArray(value)) {
|
|
230
|
+
for (const entry of value) params.append(key, String(entry));
|
|
231
|
+
} else {
|
|
232
|
+
params.append(key, String(value));
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return params.toString();
|
|
236
|
+
}
|
|
237
|
+
return "";
|
|
238
|
+
}
|
|
239
|
+
function serializeRequestBody(contentType, body) {
|
|
240
|
+
if (contentType && contentType.includes("application/x-www-form-urlencoded")) {
|
|
241
|
+
return serializeFormBody(body);
|
|
242
|
+
}
|
|
243
|
+
if (typeof body === "string") return body;
|
|
244
|
+
if (body instanceof Uint8Array) return Buffer.from(body).toString("utf8");
|
|
245
|
+
return JSON.stringify(body ?? {});
|
|
246
|
+
}
|
|
247
|
+
function createOAuthRegisterHandler(options) {
|
|
248
|
+
const path = options.path ?? "/oauth/register";
|
|
249
|
+
const api = (0, import_mcp_core.withoutTrailingSlash)(options.apiBaseUrl);
|
|
250
|
+
const upstream = `${api}/v1/customer/auth/register?product_ref=${encodeURIComponent(options.productRef)}`;
|
|
251
|
+
return async (req, res, next) => {
|
|
252
|
+
if (req.path !== path) {
|
|
253
|
+
next();
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
if (req.method === "OPTIONS") {
|
|
257
|
+
handlePreflight(req, res);
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
if (req.method !== "POST") {
|
|
261
|
+
next();
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
try {
|
|
265
|
+
const response = await fetch(upstream, {
|
|
266
|
+
method: "POST",
|
|
267
|
+
headers: { "content-type": "application/json" },
|
|
268
|
+
body: serializeRegisterBody(req.body)
|
|
269
|
+
});
|
|
270
|
+
applyCorsHeaders(req, res);
|
|
271
|
+
await relayJsonResponse(response, res);
|
|
272
|
+
} catch (error) {
|
|
273
|
+
applyCorsHeaders(req, res);
|
|
274
|
+
sendUpstreamError(res, error);
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
function createOAuthAuthorizeHandler(options) {
|
|
279
|
+
const path = options.path ?? "/oauth/authorize";
|
|
280
|
+
const api = (0, import_mcp_core.withoutTrailingSlash)(options.apiBaseUrl);
|
|
281
|
+
return (req, res, next) => {
|
|
282
|
+
if (req.path !== path) {
|
|
283
|
+
next();
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
if (req.method === "OPTIONS") {
|
|
287
|
+
handlePreflight(req, res);
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
if (req.method !== "GET") {
|
|
291
|
+
next();
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
const query = getRequestQuery(req);
|
|
295
|
+
const location = `${api}/v1/customer/auth/authorize${query}`;
|
|
296
|
+
res.setHeader("Location", location);
|
|
297
|
+
res.status(302);
|
|
298
|
+
if (typeof res.end === "function") {
|
|
299
|
+
res.end();
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
res.json({});
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
function createOAuthTokenHandler(options) {
|
|
306
|
+
const path = options.path ?? "/oauth/token";
|
|
307
|
+
const api = (0, import_mcp_core.withoutTrailingSlash)(options.apiBaseUrl);
|
|
308
|
+
const upstream = `${api}/v1/customer/auth/token`;
|
|
309
|
+
return async (req, res, next) => {
|
|
310
|
+
if (req.path !== path) {
|
|
311
|
+
next();
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
if (req.method === "OPTIONS") {
|
|
315
|
+
handlePreflight(req, res);
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
if (req.method !== "POST") {
|
|
319
|
+
next();
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
const contentType = getHeader(req, "content-type") ?? "application/x-www-form-urlencoded";
|
|
323
|
+
const authorization = getHeader(req, "authorization");
|
|
324
|
+
const headers = { "content-type": contentType };
|
|
325
|
+
if (authorization) headers["authorization"] = authorization;
|
|
326
|
+
try {
|
|
327
|
+
const response = await fetch(upstream, {
|
|
328
|
+
method: "POST",
|
|
329
|
+
headers,
|
|
330
|
+
body: serializeRequestBody(contentType, req.body)
|
|
331
|
+
});
|
|
332
|
+
applyCorsHeaders(req, res);
|
|
333
|
+
await relayOAuthJsonResponse(response, res);
|
|
334
|
+
} catch (error) {
|
|
335
|
+
applyCorsHeaders(req, res);
|
|
336
|
+
sendUpstreamError(res, error);
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
function createOAuthRevokeHandler(options) {
|
|
341
|
+
const path = options.path ?? "/oauth/revoke";
|
|
342
|
+
const api = (0, import_mcp_core.withoutTrailingSlash)(options.apiBaseUrl);
|
|
343
|
+
const upstream = `${api}/v1/customer/auth/revoke`;
|
|
344
|
+
return async (req, res, next) => {
|
|
345
|
+
if (req.path !== path) {
|
|
346
|
+
next();
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
if (req.method === "OPTIONS") {
|
|
350
|
+
handlePreflight(req, res);
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
if (req.method !== "POST") {
|
|
354
|
+
next();
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
const contentType = getHeader(req, "content-type") ?? "application/x-www-form-urlencoded";
|
|
358
|
+
const authorization = getHeader(req, "authorization");
|
|
359
|
+
const headers = { "content-type": contentType };
|
|
360
|
+
if (authorization) headers["authorization"] = authorization;
|
|
361
|
+
try {
|
|
362
|
+
const response = await fetch(upstream, {
|
|
363
|
+
method: "POST",
|
|
364
|
+
headers,
|
|
365
|
+
body: serializeRequestBody(contentType, req.body)
|
|
366
|
+
});
|
|
367
|
+
applyCorsHeaders(req, res);
|
|
368
|
+
await relayOAuthJsonResponse(response, res);
|
|
369
|
+
} catch (error) {
|
|
370
|
+
applyCorsHeaders(req, res);
|
|
371
|
+
sendUpstreamError(res, error);
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
function createMcpOAuthBridge(options) {
|
|
376
|
+
const {
|
|
377
|
+
publicBaseUrl,
|
|
378
|
+
apiBaseUrl,
|
|
379
|
+
productRef,
|
|
380
|
+
mcpPath = "/mcp",
|
|
381
|
+
requireAuth = true,
|
|
382
|
+
authInfo,
|
|
383
|
+
protectedResourcePath = "/.well-known/oauth-protected-resource",
|
|
384
|
+
authorizationServerPath = "/.well-known/oauth-authorization-server",
|
|
385
|
+
oauthPaths
|
|
386
|
+
} = options;
|
|
387
|
+
const paths = (0, import_mcp_core.resolveOAuthPaths)(oauthPaths);
|
|
388
|
+
const openidDiscoveryMiddleware = (req, res, next) => {
|
|
389
|
+
if (req.method !== "GET" || req.path !== "/.well-known/openid-configuration") {
|
|
390
|
+
next();
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
applyCorsHeaders(req, res);
|
|
394
|
+
res.status(404);
|
|
395
|
+
if (typeof res.end === "function") {
|
|
396
|
+
res.end();
|
|
397
|
+
} else {
|
|
398
|
+
res.json({ error: "not_found" });
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
const protectedResourceMiddleware = (req, res, next) => {
|
|
402
|
+
if (req.method !== "GET" || req.path !== protectedResourcePath) {
|
|
403
|
+
next();
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
res.json((0, import_mcp_core.getOAuthProtectedResourceResponse)(publicBaseUrl));
|
|
407
|
+
};
|
|
408
|
+
const authorizationServerMiddleware = (req, res, next) => {
|
|
409
|
+
if (req.method !== "GET" || req.path !== authorizationServerPath) {
|
|
410
|
+
next();
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
if (!productRef) {
|
|
414
|
+
res.status(500).json({ error: "SOLVAPAY_PRODUCT_REF missing" });
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
res.json(
|
|
418
|
+
(0, import_mcp_core.getOAuthAuthorizationServerResponse)({
|
|
419
|
+
publicBaseUrl,
|
|
420
|
+
paths
|
|
421
|
+
})
|
|
422
|
+
);
|
|
423
|
+
};
|
|
424
|
+
const registerMiddleware = createOAuthRegisterHandler({
|
|
425
|
+
apiBaseUrl,
|
|
426
|
+
productRef,
|
|
427
|
+
path: paths.register
|
|
428
|
+
});
|
|
429
|
+
const authorizeMiddleware = createOAuthAuthorizeHandler({
|
|
430
|
+
apiBaseUrl,
|
|
431
|
+
path: paths.authorize
|
|
432
|
+
});
|
|
433
|
+
const tokenMiddleware = createOAuthTokenHandler({ apiBaseUrl, path: paths.token });
|
|
434
|
+
const revokeMiddleware = createOAuthRevokeHandler({ apiBaseUrl, path: paths.revoke });
|
|
435
|
+
const mcpAuthMiddleware = (req, res, next) => {
|
|
436
|
+
if (req.path !== mcpPath) {
|
|
437
|
+
next();
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
if (req.method && req.method !== "POST" && req.method !== "OPTIONS") {
|
|
441
|
+
applyCorsHeaders(req, res);
|
|
442
|
+
res.setHeader("Allow", "POST, OPTIONS");
|
|
443
|
+
res.status(405);
|
|
444
|
+
if (typeof res.end === "function") {
|
|
445
|
+
res.end();
|
|
446
|
+
} else {
|
|
447
|
+
res.json({ error: "method_not_allowed" });
|
|
448
|
+
}
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
const authHeader = getRequestAuthHeader(req);
|
|
452
|
+
const id = getRequestJsonRpcId(req.body);
|
|
453
|
+
if (!authHeader && !requireAuth) {
|
|
454
|
+
next();
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
try {
|
|
458
|
+
const auth = (0, import_mcp_core.buildAuthInfoFromBearer)(authHeader, authInfo);
|
|
459
|
+
if (!auth) {
|
|
460
|
+
throw new import_mcp_core.McpBearerAuthError("Missing bearer token");
|
|
461
|
+
}
|
|
462
|
+
req.auth = auth;
|
|
463
|
+
next();
|
|
464
|
+
} catch {
|
|
465
|
+
applyCorsHeaders(req, res);
|
|
466
|
+
res.setHeader("Access-Control-Expose-Headers", "WWW-Authenticate");
|
|
467
|
+
setMcpChallengeHeader(res, publicBaseUrl, protectedResourcePath);
|
|
468
|
+
if (req.method === "POST") {
|
|
469
|
+
res.status(401).json(makeUnauthorizedJsonRpc(id));
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
res.status(401).json({ error: "Unauthorized" });
|
|
473
|
+
}
|
|
474
|
+
};
|
|
475
|
+
return [
|
|
476
|
+
openidDiscoveryMiddleware,
|
|
477
|
+
protectedResourceMiddleware,
|
|
478
|
+
authorizationServerMiddleware,
|
|
479
|
+
registerMiddleware,
|
|
480
|
+
authorizeMiddleware,
|
|
481
|
+
tokenMiddleware,
|
|
482
|
+
revokeMiddleware,
|
|
483
|
+
mcpAuthMiddleware
|
|
484
|
+
];
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// src/express/index.ts
|
|
488
|
+
var import_mcp_core2 = require("@solvapay/mcp-core");
|
|
489
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
490
|
+
0 && (module.exports = {
|
|
491
|
+
McpBearerAuthError,
|
|
492
|
+
buildAuthInfoFromBearer,
|
|
493
|
+
createMcpOAuthBridge,
|
|
494
|
+
createOAuthAuthorizeHandler,
|
|
495
|
+
createOAuthRegisterHandler,
|
|
496
|
+
createOAuthRevokeHandler,
|
|
497
|
+
createOAuthTokenHandler,
|
|
498
|
+
decodeJwtPayload,
|
|
499
|
+
extractBearerToken,
|
|
500
|
+
getCustomerRefFromBearerAuthHeader,
|
|
501
|
+
getCustomerRefFromJwtPayload,
|
|
502
|
+
getOAuthAuthorizationServerResponse,
|
|
503
|
+
getOAuthProtectedResourceResponse
|
|
504
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { BuildAuthInfoFromBearerOptions, OAuthBridgePaths } from '@solvapay/mcp-core';
|
|
2
|
+
export { BuildAuthInfoFromBearerOptions, McpBearerAuthError, McpBearerCustomerRefOptions, OAuthAuthorizationServerOptions, OAuthBridgePaths, buildAuthInfoFromBearer, decodeJwtPayload, extractBearerToken, getCustomerRefFromBearerAuthHeader, getCustomerRefFromJwtPayload, getOAuthAuthorizationServerResponse, getOAuthProtectedResourceResponse } from '@solvapay/mcp-core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Node `(req, res, next)` OAuth bridge middleware for the SolvaPay MCP
|
|
6
|
+
* server.
|
|
7
|
+
*
|
|
8
|
+
* Lifted verbatim from `@solvapay/mcp-express` when it was folded into
|
|
9
|
+
* this package as a subpath export. Pure JSON discovery builders come
|
|
10
|
+
* from `@solvapay/mcp-core`; everything request/response-shaped lives
|
|
11
|
+
* here so `@solvapay/mcp-core` stays runtime-agnostic.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
type RequestLike = {
|
|
15
|
+
method?: string;
|
|
16
|
+
path?: string;
|
|
17
|
+
url?: string;
|
|
18
|
+
headers?: Record<string, string | string[] | undefined>;
|
|
19
|
+
body?: unknown;
|
|
20
|
+
auth?: unknown;
|
|
21
|
+
};
|
|
22
|
+
type ResponseLike = {
|
|
23
|
+
status: (code: number) => ResponseLike;
|
|
24
|
+
json: (payload: unknown) => void;
|
|
25
|
+
setHeader: (name: string, value: string) => void;
|
|
26
|
+
end?: (body?: string) => void;
|
|
27
|
+
send?: (body?: string | Buffer) => void;
|
|
28
|
+
};
|
|
29
|
+
type NextLike = () => void;
|
|
30
|
+
type Middleware = (req: RequestLike, res: ResponseLike, next: NextLike) => void | Promise<void>;
|
|
31
|
+
interface OAuthRegisterHandlerOptions {
|
|
32
|
+
apiBaseUrl: string;
|
|
33
|
+
productRef: string;
|
|
34
|
+
path?: string;
|
|
35
|
+
}
|
|
36
|
+
interface OAuthAuthorizeHandlerOptions {
|
|
37
|
+
apiBaseUrl: string;
|
|
38
|
+
path?: string;
|
|
39
|
+
}
|
|
40
|
+
interface OAuthTokenHandlerOptions {
|
|
41
|
+
apiBaseUrl: string;
|
|
42
|
+
path?: string;
|
|
43
|
+
}
|
|
44
|
+
interface OAuthRevokeHandlerOptions {
|
|
45
|
+
apiBaseUrl: string;
|
|
46
|
+
path?: string;
|
|
47
|
+
}
|
|
48
|
+
interface McpOAuthBridgeOptions {
|
|
49
|
+
publicBaseUrl: string;
|
|
50
|
+
apiBaseUrl: string;
|
|
51
|
+
productRef: string;
|
|
52
|
+
mcpPath?: string;
|
|
53
|
+
requireAuth?: boolean;
|
|
54
|
+
authInfo?: BuildAuthInfoFromBearerOptions;
|
|
55
|
+
protectedResourcePath?: string;
|
|
56
|
+
authorizationServerPath?: string;
|
|
57
|
+
oauthPaths?: OAuthBridgePaths;
|
|
58
|
+
}
|
|
59
|
+
declare function createOAuthRegisterHandler(options: OAuthRegisterHandlerOptions): Middleware;
|
|
60
|
+
declare function createOAuthAuthorizeHandler(options: OAuthAuthorizeHandlerOptions): Middleware;
|
|
61
|
+
declare function createOAuthTokenHandler(options: OAuthTokenHandlerOptions): Middleware;
|
|
62
|
+
declare function createOAuthRevokeHandler(options: OAuthRevokeHandlerOptions): Middleware;
|
|
63
|
+
declare function createMcpOAuthBridge(options: McpOAuthBridgeOptions): Middleware[];
|
|
64
|
+
|
|
65
|
+
export { type McpOAuthBridgeOptions, type OAuthAuthorizeHandlerOptions, type OAuthRegisterHandlerOptions, type OAuthRevokeHandlerOptions, type OAuthTokenHandlerOptions, createMcpOAuthBridge, createOAuthAuthorizeHandler, createOAuthRegisterHandler, createOAuthRevokeHandler, createOAuthTokenHandler };
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { BuildAuthInfoFromBearerOptions, OAuthBridgePaths } from '@solvapay/mcp-core';
|
|
2
|
+
export { BuildAuthInfoFromBearerOptions, McpBearerAuthError, McpBearerCustomerRefOptions, OAuthAuthorizationServerOptions, OAuthBridgePaths, buildAuthInfoFromBearer, decodeJwtPayload, extractBearerToken, getCustomerRefFromBearerAuthHeader, getCustomerRefFromJwtPayload, getOAuthAuthorizationServerResponse, getOAuthProtectedResourceResponse } from '@solvapay/mcp-core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Node `(req, res, next)` OAuth bridge middleware for the SolvaPay MCP
|
|
6
|
+
* server.
|
|
7
|
+
*
|
|
8
|
+
* Lifted verbatim from `@solvapay/mcp-express` when it was folded into
|
|
9
|
+
* this package as a subpath export. Pure JSON discovery builders come
|
|
10
|
+
* from `@solvapay/mcp-core`; everything request/response-shaped lives
|
|
11
|
+
* here so `@solvapay/mcp-core` stays runtime-agnostic.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
type RequestLike = {
|
|
15
|
+
method?: string;
|
|
16
|
+
path?: string;
|
|
17
|
+
url?: string;
|
|
18
|
+
headers?: Record<string, string | string[] | undefined>;
|
|
19
|
+
body?: unknown;
|
|
20
|
+
auth?: unknown;
|
|
21
|
+
};
|
|
22
|
+
type ResponseLike = {
|
|
23
|
+
status: (code: number) => ResponseLike;
|
|
24
|
+
json: (payload: unknown) => void;
|
|
25
|
+
setHeader: (name: string, value: string) => void;
|
|
26
|
+
end?: (body?: string) => void;
|
|
27
|
+
send?: (body?: string | Buffer) => void;
|
|
28
|
+
};
|
|
29
|
+
type NextLike = () => void;
|
|
30
|
+
type Middleware = (req: RequestLike, res: ResponseLike, next: NextLike) => void | Promise<void>;
|
|
31
|
+
interface OAuthRegisterHandlerOptions {
|
|
32
|
+
apiBaseUrl: string;
|
|
33
|
+
productRef: string;
|
|
34
|
+
path?: string;
|
|
35
|
+
}
|
|
36
|
+
interface OAuthAuthorizeHandlerOptions {
|
|
37
|
+
apiBaseUrl: string;
|
|
38
|
+
path?: string;
|
|
39
|
+
}
|
|
40
|
+
interface OAuthTokenHandlerOptions {
|
|
41
|
+
apiBaseUrl: string;
|
|
42
|
+
path?: string;
|
|
43
|
+
}
|
|
44
|
+
interface OAuthRevokeHandlerOptions {
|
|
45
|
+
apiBaseUrl: string;
|
|
46
|
+
path?: string;
|
|
47
|
+
}
|
|
48
|
+
interface McpOAuthBridgeOptions {
|
|
49
|
+
publicBaseUrl: string;
|
|
50
|
+
apiBaseUrl: string;
|
|
51
|
+
productRef: string;
|
|
52
|
+
mcpPath?: string;
|
|
53
|
+
requireAuth?: boolean;
|
|
54
|
+
authInfo?: BuildAuthInfoFromBearerOptions;
|
|
55
|
+
protectedResourcePath?: string;
|
|
56
|
+
authorizationServerPath?: string;
|
|
57
|
+
oauthPaths?: OAuthBridgePaths;
|
|
58
|
+
}
|
|
59
|
+
declare function createOAuthRegisterHandler(options: OAuthRegisterHandlerOptions): Middleware;
|
|
60
|
+
declare function createOAuthAuthorizeHandler(options: OAuthAuthorizeHandlerOptions): Middleware;
|
|
61
|
+
declare function createOAuthTokenHandler(options: OAuthTokenHandlerOptions): Middleware;
|
|
62
|
+
declare function createOAuthRevokeHandler(options: OAuthRevokeHandlerOptions): Middleware;
|
|
63
|
+
declare function createMcpOAuthBridge(options: McpOAuthBridgeOptions): Middleware[];
|
|
64
|
+
|
|
65
|
+
export { type McpOAuthBridgeOptions, type OAuthAuthorizeHandlerOptions, type OAuthRegisterHandlerOptions, type OAuthRevokeHandlerOptions, type OAuthTokenHandlerOptions, createMcpOAuthBridge, createOAuthAuthorizeHandler, createOAuthRegisterHandler, createOAuthRevokeHandler, createOAuthTokenHandler };
|