integrate-sdk 0.5.7 → 0.5.9
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 +84 -44
- package/dist/index.js +170 -89
- package/dist/server.js +199 -89
- package/dist/src/adapters/nextjs.d.ts +62 -0
- package/dist/src/adapters/nextjs.d.ts.map +1 -1
- package/dist/src/server.d.ts +80 -0
- package/dist/src/server.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -26,44 +26,60 @@ npm install integrate-sdk
|
|
|
26
26
|
bun add integrate-sdk
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
## Quick Start
|
|
29
|
+
## Quick Start (2 Files Only!)
|
|
30
30
|
|
|
31
|
-
### Server
|
|
31
|
+
### 1. Create Server Config
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
Define your OAuth providers once:
|
|
34
34
|
|
|
35
35
|
```typescript
|
|
36
36
|
// lib/integrate-server.ts (server-side only!)
|
|
37
|
-
import {
|
|
37
|
+
import {
|
|
38
|
+
createMCPServer,
|
|
39
|
+
githubPlugin,
|
|
40
|
+
gmailPlugin,
|
|
41
|
+
} from "integrate-sdk/server";
|
|
38
42
|
|
|
39
|
-
export const { client: serverClient
|
|
43
|
+
export const { client: serverClient } = createMCPServer({
|
|
40
44
|
plugins: [
|
|
41
45
|
githubPlugin({
|
|
42
46
|
clientId: process.env.GITHUB_CLIENT_ID,
|
|
43
47
|
clientSecret: process.env.GITHUB_CLIENT_SECRET,
|
|
44
|
-
scopes: [
|
|
48
|
+
scopes: ["repo", "user"],
|
|
45
49
|
}),
|
|
46
50
|
gmailPlugin({
|
|
47
51
|
clientId: process.env.GMAIL_CLIENT_ID,
|
|
48
52
|
clientSecret: process.env.GMAIL_CLIENT_SECRET,
|
|
49
|
-
scopes: [
|
|
53
|
+
scopes: ["gmail.readonly"],
|
|
50
54
|
}),
|
|
51
55
|
],
|
|
52
56
|
});
|
|
53
57
|
```
|
|
54
58
|
|
|
55
|
-
|
|
59
|
+
### 2. Create Single Catch-All Route
|
|
60
|
+
|
|
61
|
+
That's it! Just import and export:
|
|
56
62
|
|
|
57
63
|
```typescript
|
|
58
|
-
// app/api/integrate/
|
|
59
|
-
|
|
64
|
+
// app/api/integrate/[...all]/route.ts
|
|
65
|
+
import { serverClient } from "@/lib/integrate-server";
|
|
66
|
+
import { toNextJsHandler } from "integrate-sdk/server";
|
|
67
|
+
|
|
68
|
+
export const { POST, GET } = toNextJsHandler({
|
|
69
|
+
client: serverClient, // Pass the client
|
|
70
|
+
redirectUrl: "/dashboard",
|
|
71
|
+
});
|
|
60
72
|
```
|
|
61
73
|
|
|
74
|
+
This imports your config from step 1 and handles ALL OAuth operations (authorize, callback, status, disconnect) in one file!
|
|
75
|
+
|
|
76
|
+
### 3. Use in Your App
|
|
77
|
+
|
|
62
78
|
Use the server client in API routes or server components:
|
|
63
79
|
|
|
64
80
|
```typescript
|
|
65
81
|
// app/api/repos/route.ts
|
|
66
|
-
import { serverClient } from
|
|
82
|
+
import { serverClient } from "@/lib/integrate-server";
|
|
67
83
|
|
|
68
84
|
export async function GET() {
|
|
69
85
|
// Automatically connects on first call - no manual setup needed!
|
|
@@ -77,34 +93,35 @@ export async function GET() {
|
|
|
77
93
|
Use in your client components (no secrets needed):
|
|
78
94
|
|
|
79
95
|
```typescript
|
|
80
|
-
|
|
81
|
-
import { createMCPClient, githubPlugin } from
|
|
96
|
+
"use client";
|
|
97
|
+
import { createMCPClient, githubPlugin } from "integrate-sdk";
|
|
82
98
|
|
|
83
99
|
const client = createMCPClient({
|
|
84
100
|
plugins: [
|
|
85
101
|
githubPlugin({
|
|
86
|
-
scopes: [
|
|
102
|
+
scopes: ["repo", "user"],
|
|
87
103
|
// No clientId or clientSecret needed!
|
|
88
104
|
}),
|
|
89
105
|
],
|
|
90
|
-
oauthFlow: { mode:
|
|
106
|
+
oauthFlow: { mode: "popup" },
|
|
91
107
|
});
|
|
92
108
|
|
|
93
109
|
// Authorize user (opens popup)
|
|
94
|
-
await client.authorize(
|
|
110
|
+
await client.authorize("github");
|
|
95
111
|
|
|
96
112
|
// Use the client - automatically connects!
|
|
97
113
|
const result = await client.github.createIssue({
|
|
98
|
-
owner:
|
|
99
|
-
repo:
|
|
100
|
-
title:
|
|
101
|
-
body:
|
|
114
|
+
owner: "owner",
|
|
115
|
+
repo: "repo",
|
|
116
|
+
title: "Bug report",
|
|
117
|
+
body: "Description of the bug",
|
|
102
118
|
});
|
|
103
119
|
|
|
104
|
-
console.log(
|
|
120
|
+
console.log("Issue created:", result);
|
|
105
121
|
```
|
|
106
122
|
|
|
107
123
|
**That's it!** The SDK automatically:
|
|
124
|
+
|
|
108
125
|
- ✅ Connects on first method call (no manual `connect()` needed)
|
|
109
126
|
- ✅ Cleans up on exit (no manual `disconnect()` needed)
|
|
110
127
|
- ✅ Manages OAuth tokens securely through your API routes
|
|
@@ -115,6 +132,7 @@ console.log('Issue created:', result);
|
|
|
115
132
|
The SDK automatically manages connections for you - no manual `connect()` or `disconnect()` calls needed!
|
|
116
133
|
|
|
117
134
|
**Features:**
|
|
135
|
+
|
|
118
136
|
- **Lazy Connection**: Automatically connects on first method call
|
|
119
137
|
- **Auto-Cleanup**: Cleans up on process exit
|
|
120
138
|
- **Singleton Pattern**: Reuses connections efficiently (configurable)
|
|
@@ -124,25 +142,25 @@ The SDK automatically manages connections for you - no manual `connect()` or `di
|
|
|
124
142
|
const client = createMCPClient({
|
|
125
143
|
plugins: [
|
|
126
144
|
githubPlugin({
|
|
127
|
-
scopes: [
|
|
145
|
+
scopes: ["repo", "user"],
|
|
128
146
|
}),
|
|
129
147
|
],
|
|
130
148
|
});
|
|
131
149
|
|
|
132
150
|
// Use immediately - no connect() needed!
|
|
133
|
-
await client.authorize(
|
|
134
|
-
await client.github.listRepos({ username:
|
|
151
|
+
await client.authorize("github");
|
|
152
|
+
await client.github.listRepos({ username: "octocat" });
|
|
135
153
|
|
|
136
154
|
// ✅ Want manual control? Use manual mode
|
|
137
155
|
const manualClient = createMCPClient({
|
|
138
|
-
plugins: [githubPlugin({ scopes: [
|
|
139
|
-
connectionMode:
|
|
156
|
+
plugins: [githubPlugin({ scopes: ["repo"] })],
|
|
157
|
+
connectionMode: "manual",
|
|
140
158
|
singleton: false,
|
|
141
159
|
});
|
|
142
160
|
|
|
143
161
|
await manualClient.connect();
|
|
144
|
-
await manualClient.authorize(
|
|
145
|
-
await manualClient.github.listRepos({ username:
|
|
162
|
+
await manualClient.authorize("github");
|
|
163
|
+
await manualClient.github.listRepos({ username: "octocat" });
|
|
146
164
|
await manualClient.disconnect();
|
|
147
165
|
```
|
|
148
166
|
|
|
@@ -165,7 +183,11 @@ Instead of generic tool calls, use typed methods with full autocomplete:
|
|
|
165
183
|
|
|
166
184
|
```typescript
|
|
167
185
|
// ✅ New: Typed methods with autocomplete
|
|
168
|
-
await client.github.createIssue({
|
|
186
|
+
await client.github.createIssue({
|
|
187
|
+
owner: "user",
|
|
188
|
+
repo: "project",
|
|
189
|
+
title: "Bug",
|
|
190
|
+
});
|
|
169
191
|
await client.gmail.sendEmail({ to: "user@example.com", subject: "Hello" });
|
|
170
192
|
```
|
|
171
193
|
|
|
@@ -180,14 +202,21 @@ await client.gmail.sendEmail({ to: "user@example.com", subject: "Hello" });
|
|
|
180
202
|
|
|
181
203
|
```typescript
|
|
182
204
|
// 1. Typed plugin methods (recommended for built-in plugins like GitHub/Gmail)
|
|
183
|
-
await client.github.createIssue({
|
|
205
|
+
await client.github.createIssue({
|
|
206
|
+
owner: "user",
|
|
207
|
+
repo: "project",
|
|
208
|
+
title: "Bug",
|
|
209
|
+
});
|
|
184
210
|
await client.gmail.sendEmail({ to: "user@example.com", subject: "Hello" });
|
|
185
211
|
|
|
186
212
|
// 2. Typed server methods (for server-level tools)
|
|
187
213
|
await client.server.listToolsByIntegration({ integration: "github" });
|
|
188
214
|
|
|
189
215
|
// 3. Direct tool calls (for other server-supported integrations)
|
|
190
|
-
await client._callToolByName("slack_send_message", {
|
|
216
|
+
await client._callToolByName("slack_send_message", {
|
|
217
|
+
channel: "#general",
|
|
218
|
+
text: "Hello",
|
|
219
|
+
});
|
|
191
220
|
```
|
|
192
221
|
|
|
193
222
|
## OAuth Authorization
|
|
@@ -195,16 +224,18 @@ await client._callToolByName("slack_send_message", { channel: "#general", text:
|
|
|
195
224
|
The SDK implements OAuth 2.0 Authorization Code Flow with PKCE for secure authorization.
|
|
196
225
|
|
|
197
226
|
**Key Features:**
|
|
227
|
+
|
|
198
228
|
- ✅ Popup or redirect flow modes
|
|
199
229
|
- ✅ Session token management
|
|
200
230
|
- ✅ Multiple provider support
|
|
201
231
|
- ✅ PKCE security
|
|
202
232
|
|
|
203
233
|
**Basic Usage:**
|
|
234
|
+
|
|
204
235
|
```typescript
|
|
205
236
|
// Check authorization
|
|
206
|
-
if (!await client.isAuthorized(
|
|
207
|
-
await client.authorize(
|
|
237
|
+
if (!(await client.isAuthorized("github"))) {
|
|
238
|
+
await client.authorize("github"); // Opens popup or redirects
|
|
208
239
|
}
|
|
209
240
|
|
|
210
241
|
// Use authorized client
|
|
@@ -212,6 +243,7 @@ const repos = await client.github.listOwnRepos({});
|
|
|
212
243
|
```
|
|
213
244
|
|
|
214
245
|
For complete OAuth setup including:
|
|
246
|
+
|
|
215
247
|
- Popup vs redirect flows
|
|
216
248
|
- Session token management
|
|
217
249
|
- Multiple providers
|
|
@@ -227,9 +259,13 @@ Access GitHub repositories, issues, pull requests, and more with type-safe metho
|
|
|
227
259
|
|
|
228
260
|
```typescript
|
|
229
261
|
// Available methods
|
|
230
|
-
await client.github.getRepo({ owner:
|
|
231
|
-
await client.github.createIssue({ owner:
|
|
232
|
-
await client.github.listPullRequests({
|
|
262
|
+
await client.github.getRepo({ owner: "facebook", repo: "react" });
|
|
263
|
+
await client.github.createIssue({ owner: "user", repo: "repo", title: "Bug" });
|
|
264
|
+
await client.github.listPullRequests({
|
|
265
|
+
owner: "user",
|
|
266
|
+
repo: "repo",
|
|
267
|
+
state: "open",
|
|
268
|
+
});
|
|
233
269
|
await client.github.listOwnRepos({});
|
|
234
270
|
```
|
|
235
271
|
|
|
@@ -241,9 +277,13 @@ Send emails, manage labels, and search messages with type-safe methods.
|
|
|
241
277
|
|
|
242
278
|
```typescript
|
|
243
279
|
// Available methods
|
|
244
|
-
await client.gmail.sendEmail({
|
|
245
|
-
|
|
246
|
-
|
|
280
|
+
await client.gmail.sendEmail({
|
|
281
|
+
to: "user@example.com",
|
|
282
|
+
subject: "Hello",
|
|
283
|
+
body: "Hi!",
|
|
284
|
+
});
|
|
285
|
+
await client.gmail.listEmails({ maxResults: 10, q: "is:unread" });
|
|
286
|
+
await client.gmail.searchEmails({ query: "from:notifications@github.com" });
|
|
247
287
|
```
|
|
248
288
|
|
|
249
289
|
[→ Gmail plugin documentation](https://integrate.dev/docs/plugins/gmail)
|
|
@@ -253,15 +293,15 @@ await client.gmail.searchEmails({ query: 'from:notifications@github.com' });
|
|
|
253
293
|
Use `genericOAuthPlugin` to configure any server-supported integration:
|
|
254
294
|
|
|
255
295
|
```typescript
|
|
256
|
-
import { genericOAuthPlugin } from
|
|
296
|
+
import { genericOAuthPlugin } from "integrate-sdk/server";
|
|
257
297
|
|
|
258
298
|
const slackPlugin = genericOAuthPlugin({
|
|
259
|
-
id:
|
|
260
|
-
provider:
|
|
299
|
+
id: "slack",
|
|
300
|
+
provider: "slack",
|
|
261
301
|
clientId: process.env.SLACK_CLIENT_ID,
|
|
262
302
|
clientSecret: process.env.SLACK_CLIENT_SECRET,
|
|
263
|
-
scopes: [
|
|
264
|
-
tools: [
|
|
303
|
+
scopes: ["chat:write", "channels:read"],
|
|
304
|
+
tools: ["slack_send_message", "slack_list_channels"],
|
|
265
305
|
});
|
|
266
306
|
```
|
|
267
307
|
|
package/dist/index.js
CHANGED
|
@@ -148,6 +148,102 @@ var init_errors = __esm(() => {
|
|
|
148
148
|
};
|
|
149
149
|
});
|
|
150
150
|
|
|
151
|
+
// src/oauth/pkce.ts
|
|
152
|
+
var exports_pkce = {};
|
|
153
|
+
__export(exports_pkce, {
|
|
154
|
+
parseState: () => parseState,
|
|
155
|
+
generateStateWithReturnUrl: () => generateStateWithReturnUrl,
|
|
156
|
+
generateState: () => generateState,
|
|
157
|
+
generateCodeVerifier: () => generateCodeVerifier,
|
|
158
|
+
generateCodeChallenge: () => generateCodeChallenge
|
|
159
|
+
});
|
|
160
|
+
function generateCodeVerifier() {
|
|
161
|
+
const array = new Uint8Array(32);
|
|
162
|
+
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
|
|
163
|
+
crypto.getRandomValues(array);
|
|
164
|
+
} else if (typeof globalThis !== "undefined" && globalThis.crypto) {
|
|
165
|
+
globalThis.crypto.getRandomValues(array);
|
|
166
|
+
} else {
|
|
167
|
+
throw new Error("crypto.getRandomValues is not available. Please use Node.js 19+ or a modern browser.");
|
|
168
|
+
}
|
|
169
|
+
return base64UrlEncode(array);
|
|
170
|
+
}
|
|
171
|
+
async function generateCodeChallenge(verifier) {
|
|
172
|
+
const encoder = new TextEncoder;
|
|
173
|
+
const data = encoder.encode(verifier);
|
|
174
|
+
let hashBuffer;
|
|
175
|
+
if (typeof crypto !== "undefined" && crypto.subtle) {
|
|
176
|
+
hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
177
|
+
} else if (typeof globalThis !== "undefined" && globalThis.crypto?.subtle) {
|
|
178
|
+
hashBuffer = await globalThis.crypto.subtle.digest("SHA-256", data);
|
|
179
|
+
} else {
|
|
180
|
+
throw new Error("crypto.subtle.digest is not available. Please use Node.js 19+ or a modern browser.");
|
|
181
|
+
}
|
|
182
|
+
return base64UrlEncode(new Uint8Array(hashBuffer));
|
|
183
|
+
}
|
|
184
|
+
function generateState() {
|
|
185
|
+
const array = new Uint8Array(16);
|
|
186
|
+
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
|
|
187
|
+
crypto.getRandomValues(array);
|
|
188
|
+
} else if (typeof globalThis !== "undefined" && globalThis.crypto) {
|
|
189
|
+
globalThis.crypto.getRandomValues(array);
|
|
190
|
+
} else {
|
|
191
|
+
throw new Error("crypto.getRandomValues is not available. Please use Node.js 19+ or a modern browser.");
|
|
192
|
+
}
|
|
193
|
+
return base64UrlEncode(array);
|
|
194
|
+
}
|
|
195
|
+
function generateStateWithReturnUrl(returnUrl) {
|
|
196
|
+
const csrf = generateState();
|
|
197
|
+
const stateData = returnUrl ? { csrf, returnUrl } : { csrf };
|
|
198
|
+
const encoder = new TextEncoder;
|
|
199
|
+
const jsonBytes = encoder.encode(JSON.stringify(stateData));
|
|
200
|
+
return base64UrlEncode(jsonBytes);
|
|
201
|
+
}
|
|
202
|
+
function parseState(state) {
|
|
203
|
+
try {
|
|
204
|
+
const decoded = base64UrlDecode(state);
|
|
205
|
+
const parsed = JSON.parse(decoded);
|
|
206
|
+
if (typeof parsed === "string") {
|
|
207
|
+
return { csrf: parsed };
|
|
208
|
+
} else if (parsed && typeof parsed === "object") {
|
|
209
|
+
return {
|
|
210
|
+
csrf: parsed.csrf || state,
|
|
211
|
+
returnUrl: parsed.returnUrl
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
return { csrf: state };
|
|
215
|
+
} catch {
|
|
216
|
+
return { csrf: state };
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
function base64UrlEncode(array) {
|
|
220
|
+
let base64 = "";
|
|
221
|
+
if (typeof Buffer !== "undefined") {
|
|
222
|
+
base64 = Buffer.from(array).toString("base64");
|
|
223
|
+
} else {
|
|
224
|
+
const binary = String.fromCharCode(...array);
|
|
225
|
+
base64 = btoa(binary);
|
|
226
|
+
}
|
|
227
|
+
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
228
|
+
}
|
|
229
|
+
function base64UrlDecode(str) {
|
|
230
|
+
let base64 = str.replace(/-/g, "+").replace(/_/g, "/");
|
|
231
|
+
while (base64.length % 4 !== 0) {
|
|
232
|
+
base64 += "=";
|
|
233
|
+
}
|
|
234
|
+
if (typeof Buffer !== "undefined") {
|
|
235
|
+
return Buffer.from(base64, "base64").toString("utf-8");
|
|
236
|
+
} else {
|
|
237
|
+
const binary = atob(base64);
|
|
238
|
+
const bytes = new Uint8Array(binary.length);
|
|
239
|
+
for (let i = 0;i < binary.length; i++) {
|
|
240
|
+
bytes[i] = binary.charCodeAt(i);
|
|
241
|
+
}
|
|
242
|
+
const decoder = new TextDecoder;
|
|
243
|
+
return decoder.decode(bytes);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
151
247
|
// src/protocol/jsonrpc.ts
|
|
152
248
|
function parseMessage(message) {
|
|
153
249
|
try {
|
|
@@ -369,95 +465,6 @@ function methodToToolName(methodName, pluginId) {
|
|
|
369
465
|
const snakeCaseMethod = camelToSnake(methodName);
|
|
370
466
|
return `${pluginId}_${snakeCaseMethod}`;
|
|
371
467
|
}
|
|
372
|
-
|
|
373
|
-
// src/oauth/pkce.ts
|
|
374
|
-
function generateCodeVerifier() {
|
|
375
|
-
const array = new Uint8Array(32);
|
|
376
|
-
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
|
|
377
|
-
crypto.getRandomValues(array);
|
|
378
|
-
} else if (typeof globalThis !== "undefined" && globalThis.crypto) {
|
|
379
|
-
globalThis.crypto.getRandomValues(array);
|
|
380
|
-
} else {
|
|
381
|
-
throw new Error("crypto.getRandomValues is not available. Please use Node.js 19+ or a modern browser.");
|
|
382
|
-
}
|
|
383
|
-
return base64UrlEncode(array);
|
|
384
|
-
}
|
|
385
|
-
async function generateCodeChallenge(verifier) {
|
|
386
|
-
const encoder = new TextEncoder;
|
|
387
|
-
const data = encoder.encode(verifier);
|
|
388
|
-
let hashBuffer;
|
|
389
|
-
if (typeof crypto !== "undefined" && crypto.subtle) {
|
|
390
|
-
hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
391
|
-
} else if (typeof globalThis !== "undefined" && globalThis.crypto?.subtle) {
|
|
392
|
-
hashBuffer = await globalThis.crypto.subtle.digest("SHA-256", data);
|
|
393
|
-
} else {
|
|
394
|
-
throw new Error("crypto.subtle.digest is not available. Please use Node.js 19+ or a modern browser.");
|
|
395
|
-
}
|
|
396
|
-
return base64UrlEncode(new Uint8Array(hashBuffer));
|
|
397
|
-
}
|
|
398
|
-
function generateState() {
|
|
399
|
-
const array = new Uint8Array(16);
|
|
400
|
-
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
|
|
401
|
-
crypto.getRandomValues(array);
|
|
402
|
-
} else if (typeof globalThis !== "undefined" && globalThis.crypto) {
|
|
403
|
-
globalThis.crypto.getRandomValues(array);
|
|
404
|
-
} else {
|
|
405
|
-
throw new Error("crypto.getRandomValues is not available. Please use Node.js 19+ or a modern browser.");
|
|
406
|
-
}
|
|
407
|
-
return base64UrlEncode(array);
|
|
408
|
-
}
|
|
409
|
-
function generateStateWithReturnUrl(returnUrl) {
|
|
410
|
-
const csrf = generateState();
|
|
411
|
-
const stateData = returnUrl ? { csrf, returnUrl } : { csrf };
|
|
412
|
-
const encoder = new TextEncoder;
|
|
413
|
-
const jsonBytes = encoder.encode(JSON.stringify(stateData));
|
|
414
|
-
return base64UrlEncode(jsonBytes);
|
|
415
|
-
}
|
|
416
|
-
function parseState(state) {
|
|
417
|
-
try {
|
|
418
|
-
const decoded = base64UrlDecode(state);
|
|
419
|
-
const parsed = JSON.parse(decoded);
|
|
420
|
-
if (typeof parsed === "string") {
|
|
421
|
-
return { csrf: parsed };
|
|
422
|
-
} else if (parsed && typeof parsed === "object") {
|
|
423
|
-
return {
|
|
424
|
-
csrf: parsed.csrf || state,
|
|
425
|
-
returnUrl: parsed.returnUrl
|
|
426
|
-
};
|
|
427
|
-
}
|
|
428
|
-
return { csrf: state };
|
|
429
|
-
} catch {
|
|
430
|
-
return { csrf: state };
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
function base64UrlEncode(array) {
|
|
434
|
-
let base64 = "";
|
|
435
|
-
if (typeof Buffer !== "undefined") {
|
|
436
|
-
base64 = Buffer.from(array).toString("base64");
|
|
437
|
-
} else {
|
|
438
|
-
const binary = String.fromCharCode(...array);
|
|
439
|
-
base64 = btoa(binary);
|
|
440
|
-
}
|
|
441
|
-
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
442
|
-
}
|
|
443
|
-
function base64UrlDecode(str) {
|
|
444
|
-
let base64 = str.replace(/-/g, "+").replace(/_/g, "/");
|
|
445
|
-
while (base64.length % 4 !== 0) {
|
|
446
|
-
base64 += "=";
|
|
447
|
-
}
|
|
448
|
-
if (typeof Buffer !== "undefined") {
|
|
449
|
-
return Buffer.from(base64, "base64").toString("utf-8");
|
|
450
|
-
} else {
|
|
451
|
-
const binary = atob(base64);
|
|
452
|
-
const bytes = new Uint8Array(binary.length);
|
|
453
|
-
for (let i = 0;i < binary.length; i++) {
|
|
454
|
-
bytes[i] = binary.charCodeAt(i);
|
|
455
|
-
}
|
|
456
|
-
const decoder = new TextDecoder;
|
|
457
|
-
return decoder.decode(bytes);
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
|
|
461
468
|
// src/oauth/window-manager.ts
|
|
462
469
|
function isBrowser() {
|
|
463
470
|
return typeof window !== "undefined" && typeof window.document !== "undefined";
|
|
@@ -1747,6 +1754,80 @@ function createNextOAuthHandler(config) {
|
|
|
1747
1754
|
return Response.json({ error: `Unknown action: ${action}` }, { status: 404 });
|
|
1748
1755
|
}
|
|
1749
1756
|
};
|
|
1757
|
+
},
|
|
1758
|
+
toNextJsHandler(redirectConfig) {
|
|
1759
|
+
const defaultRedirectUrl = redirectConfig?.redirectUrl || "/";
|
|
1760
|
+
const errorRedirectUrl = redirectConfig?.errorRedirectUrl || "/auth-error";
|
|
1761
|
+
return {
|
|
1762
|
+
async POST(req, context) {
|
|
1763
|
+
const params = context.params instanceof Promise ? await context.params : context.params;
|
|
1764
|
+
const segments = params.all || [];
|
|
1765
|
+
if (segments.length === 2 && segments[0] === "oauth") {
|
|
1766
|
+
const action = segments[1];
|
|
1767
|
+
if (action === "authorize") {
|
|
1768
|
+
return handlers.authorize(req);
|
|
1769
|
+
}
|
|
1770
|
+
if (action === "callback") {
|
|
1771
|
+
return handlers.callback(req);
|
|
1772
|
+
}
|
|
1773
|
+
if (action === "disconnect") {
|
|
1774
|
+
return handlers.disconnect(req);
|
|
1775
|
+
}
|
|
1776
|
+
return Response.json({ error: `Unknown action: ${action}` }, { status: 404 });
|
|
1777
|
+
}
|
|
1778
|
+
return Response.json({ error: `Invalid route: /${segments.join("/")}` }, { status: 404 });
|
|
1779
|
+
},
|
|
1780
|
+
async GET(req, context) {
|
|
1781
|
+
const params = context.params instanceof Promise ? await context.params : context.params;
|
|
1782
|
+
const segments = params.all || [];
|
|
1783
|
+
if (segments.length === 2 && segments[0] === "oauth") {
|
|
1784
|
+
const action = segments[1];
|
|
1785
|
+
if (action === "status") {
|
|
1786
|
+
return handlers.status(req);
|
|
1787
|
+
}
|
|
1788
|
+
if (action === "callback") {
|
|
1789
|
+
const { searchParams } = new URL(req.url);
|
|
1790
|
+
const code = searchParams.get("code");
|
|
1791
|
+
const state = searchParams.get("state");
|
|
1792
|
+
const error = searchParams.get("error");
|
|
1793
|
+
const errorDescription = searchParams.get("error_description");
|
|
1794
|
+
if (error) {
|
|
1795
|
+
const errorMsg = errorDescription || error;
|
|
1796
|
+
console.error("[OAuth Redirect] Error:", errorMsg);
|
|
1797
|
+
return Response.redirect(new URL(`${errorRedirectUrl}?error=${encodeURIComponent(errorMsg)}`, req.url));
|
|
1798
|
+
}
|
|
1799
|
+
if (!code || !state) {
|
|
1800
|
+
console.error("[OAuth Redirect] Missing code or state parameter");
|
|
1801
|
+
return Response.redirect(new URL(`${errorRedirectUrl}?error=${encodeURIComponent("Invalid OAuth callback")}`, req.url));
|
|
1802
|
+
}
|
|
1803
|
+
let returnUrl = defaultRedirectUrl;
|
|
1804
|
+
try {
|
|
1805
|
+
const { parseState: parseState2 } = await Promise.resolve().then(() => exports_pkce);
|
|
1806
|
+
const stateData = parseState2(state);
|
|
1807
|
+
if (stateData.returnUrl) {
|
|
1808
|
+
returnUrl = stateData.returnUrl;
|
|
1809
|
+
}
|
|
1810
|
+
} catch (e) {
|
|
1811
|
+
try {
|
|
1812
|
+
const referrer = req.headers?.get?.("referer") || req.headers?.get?.("referrer");
|
|
1813
|
+
if (referrer) {
|
|
1814
|
+
const referrerUrl = new URL(referrer);
|
|
1815
|
+
const currentUrl = new URL(req.url);
|
|
1816
|
+
if (referrerUrl.origin === currentUrl.origin) {
|
|
1817
|
+
returnUrl = referrerUrl.pathname + referrerUrl.search;
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
} catch {}
|
|
1821
|
+
}
|
|
1822
|
+
const targetUrl = new URL(returnUrl, req.url);
|
|
1823
|
+
targetUrl.hash = `oauth_callback=${encodeURIComponent(JSON.stringify({ code, state }))}`;
|
|
1824
|
+
return Response.redirect(targetUrl);
|
|
1825
|
+
}
|
|
1826
|
+
return Response.json({ error: `Unknown action: ${action}` }, { status: 404 });
|
|
1827
|
+
}
|
|
1828
|
+
return Response.json({ error: `Invalid route: /${segments.join("/")}` }, { status: 404 });
|
|
1829
|
+
}
|
|
1830
|
+
};
|
|
1750
1831
|
}
|
|
1751
1832
|
};
|
|
1752
1833
|
return handlers;
|
package/dist/server.js
CHANGED
|
@@ -148,6 +148,102 @@ var init_errors = __esm(() => {
|
|
|
148
148
|
};
|
|
149
149
|
});
|
|
150
150
|
|
|
151
|
+
// src/oauth/pkce.ts
|
|
152
|
+
var exports_pkce = {};
|
|
153
|
+
__export(exports_pkce, {
|
|
154
|
+
parseState: () => parseState,
|
|
155
|
+
generateStateWithReturnUrl: () => generateStateWithReturnUrl,
|
|
156
|
+
generateState: () => generateState,
|
|
157
|
+
generateCodeVerifier: () => generateCodeVerifier,
|
|
158
|
+
generateCodeChallenge: () => generateCodeChallenge
|
|
159
|
+
});
|
|
160
|
+
function generateCodeVerifier() {
|
|
161
|
+
const array = new Uint8Array(32);
|
|
162
|
+
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
|
|
163
|
+
crypto.getRandomValues(array);
|
|
164
|
+
} else if (typeof globalThis !== "undefined" && globalThis.crypto) {
|
|
165
|
+
globalThis.crypto.getRandomValues(array);
|
|
166
|
+
} else {
|
|
167
|
+
throw new Error("crypto.getRandomValues is not available. Please use Node.js 19+ or a modern browser.");
|
|
168
|
+
}
|
|
169
|
+
return base64UrlEncode(array);
|
|
170
|
+
}
|
|
171
|
+
async function generateCodeChallenge(verifier) {
|
|
172
|
+
const encoder = new TextEncoder;
|
|
173
|
+
const data = encoder.encode(verifier);
|
|
174
|
+
let hashBuffer;
|
|
175
|
+
if (typeof crypto !== "undefined" && crypto.subtle) {
|
|
176
|
+
hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
177
|
+
} else if (typeof globalThis !== "undefined" && globalThis.crypto?.subtle) {
|
|
178
|
+
hashBuffer = await globalThis.crypto.subtle.digest("SHA-256", data);
|
|
179
|
+
} else {
|
|
180
|
+
throw new Error("crypto.subtle.digest is not available. Please use Node.js 19+ or a modern browser.");
|
|
181
|
+
}
|
|
182
|
+
return base64UrlEncode(new Uint8Array(hashBuffer));
|
|
183
|
+
}
|
|
184
|
+
function generateState() {
|
|
185
|
+
const array = new Uint8Array(16);
|
|
186
|
+
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
|
|
187
|
+
crypto.getRandomValues(array);
|
|
188
|
+
} else if (typeof globalThis !== "undefined" && globalThis.crypto) {
|
|
189
|
+
globalThis.crypto.getRandomValues(array);
|
|
190
|
+
} else {
|
|
191
|
+
throw new Error("crypto.getRandomValues is not available. Please use Node.js 19+ or a modern browser.");
|
|
192
|
+
}
|
|
193
|
+
return base64UrlEncode(array);
|
|
194
|
+
}
|
|
195
|
+
function generateStateWithReturnUrl(returnUrl) {
|
|
196
|
+
const csrf = generateState();
|
|
197
|
+
const stateData = returnUrl ? { csrf, returnUrl } : { csrf };
|
|
198
|
+
const encoder = new TextEncoder;
|
|
199
|
+
const jsonBytes = encoder.encode(JSON.stringify(stateData));
|
|
200
|
+
return base64UrlEncode(jsonBytes);
|
|
201
|
+
}
|
|
202
|
+
function parseState(state) {
|
|
203
|
+
try {
|
|
204
|
+
const decoded = base64UrlDecode(state);
|
|
205
|
+
const parsed = JSON.parse(decoded);
|
|
206
|
+
if (typeof parsed === "string") {
|
|
207
|
+
return { csrf: parsed };
|
|
208
|
+
} else if (parsed && typeof parsed === "object") {
|
|
209
|
+
return {
|
|
210
|
+
csrf: parsed.csrf || state,
|
|
211
|
+
returnUrl: parsed.returnUrl
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
return { csrf: state };
|
|
215
|
+
} catch {
|
|
216
|
+
return { csrf: state };
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
function base64UrlEncode(array) {
|
|
220
|
+
let base64 = "";
|
|
221
|
+
if (typeof Buffer !== "undefined") {
|
|
222
|
+
base64 = Buffer.from(array).toString("base64");
|
|
223
|
+
} else {
|
|
224
|
+
const binary = String.fromCharCode(...array);
|
|
225
|
+
base64 = btoa(binary);
|
|
226
|
+
}
|
|
227
|
+
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
228
|
+
}
|
|
229
|
+
function base64UrlDecode(str) {
|
|
230
|
+
let base64 = str.replace(/-/g, "+").replace(/_/g, "/");
|
|
231
|
+
while (base64.length % 4 !== 0) {
|
|
232
|
+
base64 += "=";
|
|
233
|
+
}
|
|
234
|
+
if (typeof Buffer !== "undefined") {
|
|
235
|
+
return Buffer.from(base64, "base64").toString("utf-8");
|
|
236
|
+
} else {
|
|
237
|
+
const binary = atob(base64);
|
|
238
|
+
const bytes = new Uint8Array(binary.length);
|
|
239
|
+
for (let i = 0;i < binary.length; i++) {
|
|
240
|
+
bytes[i] = binary.charCodeAt(i);
|
|
241
|
+
}
|
|
242
|
+
const decoder = new TextDecoder;
|
|
243
|
+
return decoder.decode(bytes);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
151
247
|
// src/protocol/jsonrpc.ts
|
|
152
248
|
function parseMessage(message) {
|
|
153
249
|
try {
|
|
@@ -369,95 +465,6 @@ function methodToToolName(methodName, pluginId) {
|
|
|
369
465
|
const snakeCaseMethod = camelToSnake(methodName);
|
|
370
466
|
return `${pluginId}_${snakeCaseMethod}`;
|
|
371
467
|
}
|
|
372
|
-
|
|
373
|
-
// src/oauth/pkce.ts
|
|
374
|
-
function generateCodeVerifier() {
|
|
375
|
-
const array = new Uint8Array(32);
|
|
376
|
-
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
|
|
377
|
-
crypto.getRandomValues(array);
|
|
378
|
-
} else if (typeof globalThis !== "undefined" && globalThis.crypto) {
|
|
379
|
-
globalThis.crypto.getRandomValues(array);
|
|
380
|
-
} else {
|
|
381
|
-
throw new Error("crypto.getRandomValues is not available. Please use Node.js 19+ or a modern browser.");
|
|
382
|
-
}
|
|
383
|
-
return base64UrlEncode(array);
|
|
384
|
-
}
|
|
385
|
-
async function generateCodeChallenge(verifier) {
|
|
386
|
-
const encoder = new TextEncoder;
|
|
387
|
-
const data = encoder.encode(verifier);
|
|
388
|
-
let hashBuffer;
|
|
389
|
-
if (typeof crypto !== "undefined" && crypto.subtle) {
|
|
390
|
-
hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
391
|
-
} else if (typeof globalThis !== "undefined" && globalThis.crypto?.subtle) {
|
|
392
|
-
hashBuffer = await globalThis.crypto.subtle.digest("SHA-256", data);
|
|
393
|
-
} else {
|
|
394
|
-
throw new Error("crypto.subtle.digest is not available. Please use Node.js 19+ or a modern browser.");
|
|
395
|
-
}
|
|
396
|
-
return base64UrlEncode(new Uint8Array(hashBuffer));
|
|
397
|
-
}
|
|
398
|
-
function generateState() {
|
|
399
|
-
const array = new Uint8Array(16);
|
|
400
|
-
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
|
|
401
|
-
crypto.getRandomValues(array);
|
|
402
|
-
} else if (typeof globalThis !== "undefined" && globalThis.crypto) {
|
|
403
|
-
globalThis.crypto.getRandomValues(array);
|
|
404
|
-
} else {
|
|
405
|
-
throw new Error("crypto.getRandomValues is not available. Please use Node.js 19+ or a modern browser.");
|
|
406
|
-
}
|
|
407
|
-
return base64UrlEncode(array);
|
|
408
|
-
}
|
|
409
|
-
function generateStateWithReturnUrl(returnUrl) {
|
|
410
|
-
const csrf = generateState();
|
|
411
|
-
const stateData = returnUrl ? { csrf, returnUrl } : { csrf };
|
|
412
|
-
const encoder = new TextEncoder;
|
|
413
|
-
const jsonBytes = encoder.encode(JSON.stringify(stateData));
|
|
414
|
-
return base64UrlEncode(jsonBytes);
|
|
415
|
-
}
|
|
416
|
-
function parseState(state) {
|
|
417
|
-
try {
|
|
418
|
-
const decoded = base64UrlDecode(state);
|
|
419
|
-
const parsed = JSON.parse(decoded);
|
|
420
|
-
if (typeof parsed === "string") {
|
|
421
|
-
return { csrf: parsed };
|
|
422
|
-
} else if (parsed && typeof parsed === "object") {
|
|
423
|
-
return {
|
|
424
|
-
csrf: parsed.csrf || state,
|
|
425
|
-
returnUrl: parsed.returnUrl
|
|
426
|
-
};
|
|
427
|
-
}
|
|
428
|
-
return { csrf: state };
|
|
429
|
-
} catch {
|
|
430
|
-
return { csrf: state };
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
function base64UrlEncode(array) {
|
|
434
|
-
let base64 = "";
|
|
435
|
-
if (typeof Buffer !== "undefined") {
|
|
436
|
-
base64 = Buffer.from(array).toString("base64");
|
|
437
|
-
} else {
|
|
438
|
-
const binary = String.fromCharCode(...array);
|
|
439
|
-
base64 = btoa(binary);
|
|
440
|
-
}
|
|
441
|
-
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
442
|
-
}
|
|
443
|
-
function base64UrlDecode(str) {
|
|
444
|
-
let base64 = str.replace(/-/g, "+").replace(/_/g, "/");
|
|
445
|
-
while (base64.length % 4 !== 0) {
|
|
446
|
-
base64 += "=";
|
|
447
|
-
}
|
|
448
|
-
if (typeof Buffer !== "undefined") {
|
|
449
|
-
return Buffer.from(base64, "base64").toString("utf-8");
|
|
450
|
-
} else {
|
|
451
|
-
const binary = atob(base64);
|
|
452
|
-
const bytes = new Uint8Array(binary.length);
|
|
453
|
-
for (let i = 0;i < binary.length; i++) {
|
|
454
|
-
bytes[i] = binary.charCodeAt(i);
|
|
455
|
-
}
|
|
456
|
-
const decoder = new TextDecoder;
|
|
457
|
-
return decoder.decode(bytes);
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
|
|
461
468
|
// src/oauth/window-manager.ts
|
|
462
469
|
function isBrowser() {
|
|
463
470
|
return typeof window !== "undefined" && typeof window.document !== "undefined";
|
|
@@ -1749,6 +1756,80 @@ function createNextOAuthHandler(config) {
|
|
|
1749
1756
|
return Response.json({ error: `Unknown action: ${action}` }, { status: 404 });
|
|
1750
1757
|
}
|
|
1751
1758
|
};
|
|
1759
|
+
},
|
|
1760
|
+
toNextJsHandler(redirectConfig) {
|
|
1761
|
+
const defaultRedirectUrl = redirectConfig?.redirectUrl || "/";
|
|
1762
|
+
const errorRedirectUrl = redirectConfig?.errorRedirectUrl || "/auth-error";
|
|
1763
|
+
return {
|
|
1764
|
+
async POST(req, context) {
|
|
1765
|
+
const params = context.params instanceof Promise ? await context.params : context.params;
|
|
1766
|
+
const segments = params.all || [];
|
|
1767
|
+
if (segments.length === 2 && segments[0] === "oauth") {
|
|
1768
|
+
const action = segments[1];
|
|
1769
|
+
if (action === "authorize") {
|
|
1770
|
+
return handlers.authorize(req);
|
|
1771
|
+
}
|
|
1772
|
+
if (action === "callback") {
|
|
1773
|
+
return handlers.callback(req);
|
|
1774
|
+
}
|
|
1775
|
+
if (action === "disconnect") {
|
|
1776
|
+
return handlers.disconnect(req);
|
|
1777
|
+
}
|
|
1778
|
+
return Response.json({ error: `Unknown action: ${action}` }, { status: 404 });
|
|
1779
|
+
}
|
|
1780
|
+
return Response.json({ error: `Invalid route: /${segments.join("/")}` }, { status: 404 });
|
|
1781
|
+
},
|
|
1782
|
+
async GET(req, context) {
|
|
1783
|
+
const params = context.params instanceof Promise ? await context.params : context.params;
|
|
1784
|
+
const segments = params.all || [];
|
|
1785
|
+
if (segments.length === 2 && segments[0] === "oauth") {
|
|
1786
|
+
const action = segments[1];
|
|
1787
|
+
if (action === "status") {
|
|
1788
|
+
return handlers.status(req);
|
|
1789
|
+
}
|
|
1790
|
+
if (action === "callback") {
|
|
1791
|
+
const { searchParams } = new URL(req.url);
|
|
1792
|
+
const code = searchParams.get("code");
|
|
1793
|
+
const state = searchParams.get("state");
|
|
1794
|
+
const error = searchParams.get("error");
|
|
1795
|
+
const errorDescription = searchParams.get("error_description");
|
|
1796
|
+
if (error) {
|
|
1797
|
+
const errorMsg = errorDescription || error;
|
|
1798
|
+
console.error("[OAuth Redirect] Error:", errorMsg);
|
|
1799
|
+
return Response.redirect(new URL(`${errorRedirectUrl}?error=${encodeURIComponent(errorMsg)}`, req.url));
|
|
1800
|
+
}
|
|
1801
|
+
if (!code || !state) {
|
|
1802
|
+
console.error("[OAuth Redirect] Missing code or state parameter");
|
|
1803
|
+
return Response.redirect(new URL(`${errorRedirectUrl}?error=${encodeURIComponent("Invalid OAuth callback")}`, req.url));
|
|
1804
|
+
}
|
|
1805
|
+
let returnUrl = defaultRedirectUrl;
|
|
1806
|
+
try {
|
|
1807
|
+
const { parseState: parseState2 } = await Promise.resolve().then(() => exports_pkce);
|
|
1808
|
+
const stateData = parseState2(state);
|
|
1809
|
+
if (stateData.returnUrl) {
|
|
1810
|
+
returnUrl = stateData.returnUrl;
|
|
1811
|
+
}
|
|
1812
|
+
} catch (e) {
|
|
1813
|
+
try {
|
|
1814
|
+
const referrer = req.headers?.get?.("referer") || req.headers?.get?.("referrer");
|
|
1815
|
+
if (referrer) {
|
|
1816
|
+
const referrerUrl = new URL(referrer);
|
|
1817
|
+
const currentUrl = new URL(req.url);
|
|
1818
|
+
if (referrerUrl.origin === currentUrl.origin) {
|
|
1819
|
+
returnUrl = referrerUrl.pathname + referrerUrl.search;
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
} catch {}
|
|
1823
|
+
}
|
|
1824
|
+
const targetUrl = new URL(returnUrl, req.url);
|
|
1825
|
+
targetUrl.hash = `oauth_callback=${encodeURIComponent(JSON.stringify({ code, state }))}`;
|
|
1826
|
+
return Response.redirect(targetUrl);
|
|
1827
|
+
}
|
|
1828
|
+
return Response.json({ error: `Unknown action: ${action}` }, { status: 404 });
|
|
1829
|
+
}
|
|
1830
|
+
return Response.json({ error: `Invalid route: /${segments.join("/")}` }, { status: 404 });
|
|
1831
|
+
}
|
|
1832
|
+
};
|
|
1752
1833
|
}
|
|
1753
1834
|
};
|
|
1754
1835
|
return handlers;
|
|
@@ -1911,6 +1992,7 @@ function createMCPServer(config) {
|
|
|
1911
1992
|
singleton: config.singleton ?? true
|
|
1912
1993
|
};
|
|
1913
1994
|
const client = new MCPClient(clientConfig);
|
|
1995
|
+
client.__oauthConfig = { providers };
|
|
1914
1996
|
const { POST, GET } = createOAuthRouteHandlers({ providers });
|
|
1915
1997
|
return {
|
|
1916
1998
|
client,
|
|
@@ -1938,7 +2020,35 @@ var GET = async (req, context) => {
|
|
|
1938
2020
|
const routes = handler.createRoutes();
|
|
1939
2021
|
return routes.GET(req, context);
|
|
1940
2022
|
};
|
|
2023
|
+
function toNextJsHandler(options) {
|
|
2024
|
+
const POST2 = async (req, context) => {
|
|
2025
|
+
const config = options.config || options.client?.__oauthConfig;
|
|
2026
|
+
if (!config) {
|
|
2027
|
+
return Response.json({ error: 'OAuth not configured. You must pass either "client" (from createMCPServer) or "config" to toNextJsHandler().' }, { status: 500 });
|
|
2028
|
+
}
|
|
2029
|
+
const handler = createNextOAuthHandler(config);
|
|
2030
|
+
const routes = handler.toNextJsHandler({
|
|
2031
|
+
redirectUrl: options.redirectUrl,
|
|
2032
|
+
errorRedirectUrl: options.errorRedirectUrl
|
|
2033
|
+
});
|
|
2034
|
+
return routes.POST(req, context);
|
|
2035
|
+
};
|
|
2036
|
+
const GET2 = async (req, context) => {
|
|
2037
|
+
const config = options.config || options.client?.__oauthConfig;
|
|
2038
|
+
if (!config) {
|
|
2039
|
+
return Response.json({ error: 'OAuth not configured. You must pass either "client" (from createMCPServer) or "config" to toNextJsHandler().' }, { status: 500 });
|
|
2040
|
+
}
|
|
2041
|
+
const handler = createNextOAuthHandler(config);
|
|
2042
|
+
const routes = handler.toNextJsHandler({
|
|
2043
|
+
redirectUrl: options.redirectUrl,
|
|
2044
|
+
errorRedirectUrl: options.errorRedirectUrl
|
|
2045
|
+
});
|
|
2046
|
+
return routes.GET(req, context);
|
|
2047
|
+
};
|
|
2048
|
+
return { POST: POST2, GET: GET2 };
|
|
2049
|
+
}
|
|
1941
2050
|
export {
|
|
2051
|
+
toNextJsHandler,
|
|
1942
2052
|
gmailPlugin,
|
|
1943
2053
|
githubPlugin,
|
|
1944
2054
|
genericOAuthPlugin,
|
|
@@ -265,6 +265,68 @@ export declare function createNextOAuthHandler(config: OAuthHandlerConfig): {
|
|
|
265
265
|
}>;
|
|
266
266
|
}): Promise<NextResponse>;
|
|
267
267
|
};
|
|
268
|
+
/**
|
|
269
|
+
* Create unified catch-all route handler
|
|
270
|
+
*
|
|
271
|
+
* This is the simplest way to set up OAuth routes - create a single catch-all
|
|
272
|
+
* route file that handles ALL OAuth actions including provider redirects.
|
|
273
|
+
*
|
|
274
|
+
* @param redirectConfig - Configuration for OAuth redirect behavior
|
|
275
|
+
* @returns Object with POST and GET handlers for Next.js catch-all routes
|
|
276
|
+
*
|
|
277
|
+
* @example
|
|
278
|
+
* ```typescript
|
|
279
|
+
* // app/api/integrate/[...all]/route.ts
|
|
280
|
+
* import { createNextOAuthHandler } from 'integrate-sdk';
|
|
281
|
+
*
|
|
282
|
+
* const handler = createNextOAuthHandler({
|
|
283
|
+
* providers: {
|
|
284
|
+
* github: {
|
|
285
|
+
* clientId: process.env.GITHUB_CLIENT_ID!,
|
|
286
|
+
* clientSecret: process.env.GITHUB_CLIENT_SECRET!,
|
|
287
|
+
* },
|
|
288
|
+
* },
|
|
289
|
+
* });
|
|
290
|
+
*
|
|
291
|
+
* export const { POST, GET } = handler.toNextJsHandler({
|
|
292
|
+
* redirectUrl: '/',
|
|
293
|
+
* });
|
|
294
|
+
* ```
|
|
295
|
+
*
|
|
296
|
+
* This single route file handles:
|
|
297
|
+
* - POST /api/integrate/oauth/authorize - Get authorization URL
|
|
298
|
+
* - POST /api/integrate/oauth/callback - Exchange code for token
|
|
299
|
+
* - GET /api/integrate/oauth/callback - Provider OAuth redirect
|
|
300
|
+
* - GET /api/integrate/oauth/status - Check authorization status
|
|
301
|
+
* - POST /api/integrate/oauth/disconnect - Disconnect provider
|
|
302
|
+
*/
|
|
303
|
+
toNextJsHandler(redirectConfig?: {
|
|
304
|
+
/** URL to redirect to after OAuth callback (default: '/') */
|
|
305
|
+
redirectUrl?: string;
|
|
306
|
+
/** URL to redirect to on OAuth error (default: '/auth-error') */
|
|
307
|
+
errorRedirectUrl?: string;
|
|
308
|
+
}): {
|
|
309
|
+
/**
|
|
310
|
+
* POST handler for authorize, callback, and disconnect actions
|
|
311
|
+
*/
|
|
312
|
+
POST(req: NextRequest, context: {
|
|
313
|
+
params: {
|
|
314
|
+
all: string[];
|
|
315
|
+
} | Promise<{
|
|
316
|
+
all: string[];
|
|
317
|
+
}>;
|
|
318
|
+
}): Promise<NextResponse>;
|
|
319
|
+
/**
|
|
320
|
+
* GET handler for status action and OAuth provider redirects
|
|
321
|
+
*/
|
|
322
|
+
GET(req: NextRequest, context: {
|
|
323
|
+
params: {
|
|
324
|
+
all: string[];
|
|
325
|
+
} | Promise<{
|
|
326
|
+
all: string[];
|
|
327
|
+
}>;
|
|
328
|
+
}): Promise<NextResponse>;
|
|
329
|
+
};
|
|
268
330
|
};
|
|
269
331
|
export {};
|
|
270
332
|
//# sourceMappingURL=nextjs.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nextjs.d.ts","sourceRoot":"","sources":["../../../src/adapters/nextjs.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAgB,KAAK,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAG1E,KAAK,WAAW,GAAG,GAAG,CAAC;AACvB,KAAK,YAAY,GAAG,GAAG,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,kBAAkB;IAI7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAwCG;mBACkB,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IAcxD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAyCG;kBACiB,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IAcvD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAoCG;gBACe,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IA+BrD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAuCG;oBACmB,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IAiCzD;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;;QAGC;;WAEG;kBAEI,WAAW,WACP;YAAE,MAAM,EAAE;gBAAE,MAAM,EAAE,MAAM,CAAA;aAAE,GAAG,OAAO,CAAC;gBAAE,MAAM,EAAE,MAAM,CAAA;aAAE,CAAC,CAAA;SAAE,GACpE,OAAO,CAAC,YAAY,CAAC;QAuBxB;;WAEG;iBAEI,WAAW,WACP;YAAE,MAAM,EAAE;gBAAE,MAAM,EAAE,MAAM,CAAA;aAAE,GAAG,OAAO,CAAC;gBAAE,MAAM,EAAE,MAAM,CAAA;aAAE,CAAC,CAAA;SAAE,GACpE,OAAO,CAAC,YAAY,CAAC;;
|
|
1
|
+
{"version":3,"file":"nextjs.d.ts","sourceRoot":"","sources":["../../../src/adapters/nextjs.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAgB,KAAK,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAG1E,KAAK,WAAW,GAAG,GAAG,CAAC;AACvB,KAAK,YAAY,GAAG,GAAG,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,kBAAkB;IAI7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAwCG;mBACkB,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IAcxD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAyCG;kBACiB,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IAcvD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAoCG;gBACe,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IA+BrD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAuCG;oBACmB,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IAiCzD;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;;QAGC;;WAEG;kBAEI,WAAW,WACP;YAAE,MAAM,EAAE;gBAAE,MAAM,EAAE,MAAM,CAAA;aAAE,GAAG,OAAO,CAAC;gBAAE,MAAM,EAAE,MAAM,CAAA;aAAE,CAAC,CAAA;SAAE,GACpE,OAAO,CAAC,YAAY,CAAC;QAuBxB;;WAEG;iBAEI,WAAW,WACP;YAAE,MAAM,EAAE;gBAAE,MAAM,EAAE,MAAM,CAAA;aAAE,GAAG,OAAO,CAAC;gBAAE,MAAM,EAAE,MAAM,CAAA;aAAE,CAAC,CAAA;SAAE,GACpE,OAAO,CAAC,YAAY,CAAC;;IAiB5B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAkCG;qCAC8B;QAC/B,6DAA6D;QAC7D,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,iEAAiE;QACjE,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC3B;QAKG;;WAEG;kBAEI,WAAW,WACP;YAAE,MAAM,EAAE;gBAAE,GAAG,EAAE,MAAM,EAAE,CAAA;aAAE,GAAG,OAAO,CAAC;gBAAE,GAAG,EAAE,MAAM,EAAE,CAAA;aAAE,CAAC,CAAA;SAAE,GAClE,OAAO,CAAC,YAAY,CAAC;QAiCxB;;WAEG;iBAEI,WAAW,WACP;YAAE,MAAM,EAAE;gBAAE,GAAG,EAAE,MAAM,EAAE,CAAA;aAAE,GAAG,OAAO,CAAC;gBAAE,GAAG,EAAE,MAAM,EAAE,CAAA;aAAE,CAAC,CAAA;SAAE,GAClE,OAAO,CAAC,YAAY,CAAC;;EA+F/B"}
|
package/dist/src/server.d.ts
CHANGED
|
@@ -121,4 +121,84 @@ export declare const GET: (req: any, context: {
|
|
|
121
121
|
action: string;
|
|
122
122
|
}>;
|
|
123
123
|
}) => Promise<any>;
|
|
124
|
+
/**
|
|
125
|
+
* Create catch-all route handlers from the global server configuration
|
|
126
|
+
*
|
|
127
|
+
* This is a helper function to create POST and GET handlers for catch-all routes
|
|
128
|
+
* that use the configuration from createMCPServer().
|
|
129
|
+
*
|
|
130
|
+
* @param redirectConfig - Optional configuration for OAuth redirect behavior
|
|
131
|
+
* @returns Object with POST and GET handlers for Next.js catch-all routes
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* ```typescript
|
|
135
|
+
* // lib/integrate-server.ts
|
|
136
|
+
* import { createMCPServer, githubPlugin } from 'integrate-sdk/server';
|
|
137
|
+
*
|
|
138
|
+
* export const { client: serverClient } = createMCPServer({
|
|
139
|
+
* plugins: [
|
|
140
|
+
* githubPlugin({
|
|
141
|
+
* clientId: process.env.GITHUB_CLIENT_ID!,
|
|
142
|
+
* clientSecret: process.env.GITHUB_CLIENT_SECRET!,
|
|
143
|
+
* scopes: ['repo', 'user'],
|
|
144
|
+
* }),
|
|
145
|
+
* ],
|
|
146
|
+
* });
|
|
147
|
+
*
|
|
148
|
+
* // app/api/integrate/[...all]/route.ts
|
|
149
|
+
*
|
|
150
|
+
* // RECOMMENDED: Import serverClient from your server setup file
|
|
151
|
+
* import { serverClient } from '@/lib/integrate-server';
|
|
152
|
+
* import { toNextJsHandler } from 'integrate-sdk/server';
|
|
153
|
+
*
|
|
154
|
+
* export const { POST, GET } = toNextJsHandler({
|
|
155
|
+
* client: serverClient, // Pass the client from createMCPServer
|
|
156
|
+
* redirectUrl: '/dashboard',
|
|
157
|
+
* });
|
|
158
|
+
*
|
|
159
|
+
* // Alternative: Provide config inline
|
|
160
|
+
* export const { POST, GET } = toNextJsHandler({
|
|
161
|
+
* config: {
|
|
162
|
+
* providers: {
|
|
163
|
+
* github: {
|
|
164
|
+
* clientId: process.env.GITHUB_CLIENT_ID!,
|
|
165
|
+
* clientSecret: process.env.GITHUB_CLIENT_SECRET!,
|
|
166
|
+
* },
|
|
167
|
+
* },
|
|
168
|
+
* },
|
|
169
|
+
* redirectUrl: '/dashboard',
|
|
170
|
+
* });
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
173
|
+
export declare function toNextJsHandler(options: {
|
|
174
|
+
/** Server client instance from createMCPServer (extracts config automatically) */
|
|
175
|
+
client?: any;
|
|
176
|
+
/** Custom OAuth handler config (provide inline) */
|
|
177
|
+
config?: {
|
|
178
|
+
providers: Record<string, {
|
|
179
|
+
clientId: string;
|
|
180
|
+
clientSecret: string;
|
|
181
|
+
redirectUri?: string;
|
|
182
|
+
}>;
|
|
183
|
+
};
|
|
184
|
+
/** URL to redirect to after successful OAuth */
|
|
185
|
+
redirectUrl?: string;
|
|
186
|
+
/** URL to redirect to on OAuth error */
|
|
187
|
+
errorRedirectUrl?: string;
|
|
188
|
+
}): {
|
|
189
|
+
POST: (req: any, context: {
|
|
190
|
+
params: {
|
|
191
|
+
all: string[];
|
|
192
|
+
} | Promise<{
|
|
193
|
+
all: string[];
|
|
194
|
+
}>;
|
|
195
|
+
}) => Promise<any>;
|
|
196
|
+
GET: (req: any, context: {
|
|
197
|
+
params: {
|
|
198
|
+
all: string[];
|
|
199
|
+
} | Promise<{
|
|
200
|
+
all: string[];
|
|
201
|
+
}>;
|
|
202
|
+
}) => Promise<any>;
|
|
203
|
+
};
|
|
124
204
|
//# sourceMappingURL=server.d.ts.map
|
package/dist/src/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/server.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAyCpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AACH,wBAAgB,eAAe,CAAC,QAAQ,SAAS,SAAS,SAAS,EAAE,EACnE,MAAM,EAAE,eAAe,CAAC,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/server.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAyCpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AACH,wBAAgB,eAAe,CAAC,QAAQ,SAAS,SAAS,SAAS,EAAE,EACnE,MAAM,EAAE,eAAe,CAAC,QAAQ,CAAC;IAsE/B,2DAA2D;;IAG3D,4DAA4D;;;;;;;;IAG5D,2DAA2D;;;;;;;;EAG9D;AAYD,YAAY,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACpD,YAAY,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGzD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE9E;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,IAAI,GACf,KAAK,GAAG,EACR,SAAS;IAAE,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,iBAYtE,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,GAAG,GACd,KAAK,GAAG,EACR,SAAS;IAAE,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,iBAYtE,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE;IACvC,kFAAkF;IAClF,MAAM,CAAC,EAAE,GAAG,CAAC;IACb,mDAAmD;IACnD,MAAM,CAAC,EAAE;QACP,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE;YACxB,QAAQ,EAAE,MAAM,CAAC;YACjB,YAAY,EAAE,MAAM,CAAC;YACrB,WAAW,CAAC,EAAE,MAAM,CAAC;SACtB,CAAC,CAAC;KACJ,CAAC;IACF,gDAAgD;IAChD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wCAAwC;IACxC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;gBAMQ,GAAG,WACC;QAAE,MAAM,EAAE;YAAE,GAAG,EAAE,MAAM,EAAE,CAAA;SAAE,GAAG,OAAO,CAAC;YAAE,GAAG,EAAE,MAAM,EAAE,CAAA;SAAE,CAAC,CAAA;KAAE;eAwB9D,GAAG,WACC;QAAE,MAAM,EAAE;YAAE,GAAG,EAAE,MAAM,EAAE,CAAA;SAAE,GAAG,OAAO,CAAC;YAAE,GAAG,EAAE,MAAM,EAAE,CAAA;SAAE,CAAC,CAAA;KAAE;EAoBtE"}
|