@wukongcrm/mcp-server 0.1.3 → 0.1.4
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 +940 -97
- package/dist/http-entry.d.ts +1 -0
- package/dist/http-entry.js +30 -0
- package/dist/http.d.ts +41 -0
- package/dist/http.js +253 -0
- package/dist/modules.js +4 -4
- package/dist/oauth.d.ts +71 -0
- package/dist/oauth.js +400 -0
- package/dist/server.d.ts +12 -1
- package/dist/server.js +218 -17
- package/dist/tools.js +3897 -540
- package/package.json +11 -15
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { createWukongHttpApp, loadRemoteServerEnvironment } from "./http.js";
|
|
2
|
+
const environment = loadRemoteServerEnvironment();
|
|
3
|
+
const { app, resourceUrl } = createWukongHttpApp({
|
|
4
|
+
publicUrl: environment.publicUrl,
|
|
5
|
+
oauthSecret: environment.oauthSecret,
|
|
6
|
+
dataDirectory: environment.dataDirectory,
|
|
7
|
+
crmBaseUrl: environment.crmBaseUrl,
|
|
8
|
+
crmBindingAuthorizeUrl: environment.crmBindingAuthorizeUrl,
|
|
9
|
+
crmBindingExchangeUrl: environment.crmBindingExchangeUrl,
|
|
10
|
+
crmBindingCallbackUrl: environment.crmBindingCallbackUrl,
|
|
11
|
+
crmBindingClientId: environment.crmBindingClientId,
|
|
12
|
+
crmBindingClientSecret: environment.crmBindingClientSecret,
|
|
13
|
+
host: environment.host,
|
|
14
|
+
allowedHosts: environment.allowedHosts,
|
|
15
|
+
trustProxy: environment.trustProxy
|
|
16
|
+
});
|
|
17
|
+
const httpServer = app.listen(environment.port, environment.host, () => {
|
|
18
|
+
console.log(`WukongCRM remote MCP listening on ${environment.host}:${environment.port}`);
|
|
19
|
+
console.log(`Public MCP URL: ${resourceUrl.href}`);
|
|
20
|
+
console.log(`Express trust proxy: ${String(environment.trustProxy)}`);
|
|
21
|
+
});
|
|
22
|
+
httpServer.on("error", (error) => {
|
|
23
|
+
console.error("Failed to start remote MCP server:", error.message);
|
|
24
|
+
process.exitCode = 1;
|
|
25
|
+
});
|
|
26
|
+
for (const signal of ["SIGINT", "SIGTERM"]) {
|
|
27
|
+
process.on(signal, () => {
|
|
28
|
+
httpServer.close(() => process.exit(0));
|
|
29
|
+
});
|
|
30
|
+
}
|
package/dist/http.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Express } from "express";
|
|
2
|
+
import { WukongOAuthProvider } from "./oauth.js";
|
|
3
|
+
export type TrustProxySetting = boolean | number | string;
|
|
4
|
+
export interface WukongHttpAppOptions {
|
|
5
|
+
publicUrl: string | URL;
|
|
6
|
+
oauthSecret: string;
|
|
7
|
+
dataDirectory: string;
|
|
8
|
+
crmBaseUrl?: string;
|
|
9
|
+
crmBindingAuthorizeUrl: string | URL;
|
|
10
|
+
crmBindingExchangeUrl: string | URL;
|
|
11
|
+
crmBindingCallbackUrl?: string | URL;
|
|
12
|
+
crmBindingClientId: string;
|
|
13
|
+
crmBindingClientSecret: string;
|
|
14
|
+
fetchImpl?: typeof fetch;
|
|
15
|
+
host?: string;
|
|
16
|
+
allowedHosts?: string[];
|
|
17
|
+
trustProxy?: TrustProxySetting;
|
|
18
|
+
}
|
|
19
|
+
export interface WukongHttpApp {
|
|
20
|
+
app: Express;
|
|
21
|
+
provider: WukongOAuthProvider;
|
|
22
|
+
resourceUrl: URL;
|
|
23
|
+
resourceMetadataUrl: string;
|
|
24
|
+
}
|
|
25
|
+
export declare function createWukongHttpApp(options: WukongHttpAppOptions): WukongHttpApp;
|
|
26
|
+
export interface RemoteServerEnvironment {
|
|
27
|
+
publicUrl: string;
|
|
28
|
+
oauthSecret: string;
|
|
29
|
+
dataDirectory: string;
|
|
30
|
+
crmBaseUrl?: string;
|
|
31
|
+
crmBindingAuthorizeUrl: string;
|
|
32
|
+
crmBindingExchangeUrl: string;
|
|
33
|
+
crmBindingCallbackUrl: string;
|
|
34
|
+
crmBindingClientId: string;
|
|
35
|
+
crmBindingClientSecret: string;
|
|
36
|
+
host: string;
|
|
37
|
+
allowedHosts: string[];
|
|
38
|
+
trustProxy: TrustProxySetting;
|
|
39
|
+
port: number;
|
|
40
|
+
}
|
|
41
|
+
export declare function loadRemoteServerEnvironment(env?: NodeJS.ProcessEnv): RemoteServerEnvironment;
|
package/dist/http.js
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { authorizationHandler } from "@modelcontextprotocol/sdk/server/auth/handlers/authorize.js";
|
|
3
|
+
import { metadataHandler } from "@modelcontextprotocol/sdk/server/auth/handlers/metadata.js";
|
|
4
|
+
import { clientRegistrationHandler } from "@modelcontextprotocol/sdk/server/auth/handlers/register.js";
|
|
5
|
+
import { revocationHandler } from "@modelcontextprotocol/sdk/server/auth/handlers/revoke.js";
|
|
6
|
+
import { tokenHandler } from "@modelcontextprotocol/sdk/server/auth/handlers/token.js";
|
|
7
|
+
import { requireBearerAuth } from "@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js";
|
|
8
|
+
import { getOAuthProtectedResourceMetadataUrl } from "@modelcontextprotocol/sdk/server/auth/router.js";
|
|
9
|
+
import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
|
|
10
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
11
|
+
import express from "express";
|
|
12
|
+
import { WukongOAuthProvider } from "./oauth.js";
|
|
13
|
+
import { MCP_RESOURCE_SCOPES, OAUTH_SCOPES, createWukongMcpServer } from "./server.js";
|
|
14
|
+
export function createWukongHttpApp(options) {
|
|
15
|
+
const resourceUrl = validatePublicUrl(options.publicUrl);
|
|
16
|
+
const issuerUrl = new URL("/", resourceUrl);
|
|
17
|
+
const resourcePath = resourceUrl.pathname;
|
|
18
|
+
const oauthBasePath = `${resourcePath}/oauth`;
|
|
19
|
+
const bindingCallbackUrl = options.crmBindingCallbackUrl
|
|
20
|
+
? new URL(options.crmBindingCallbackUrl.toString())
|
|
21
|
+
: new URL(`${oauthBasePath}/72crm/callback`, resourceUrl);
|
|
22
|
+
if (bindingCallbackUrl.origin !== resourceUrl.origin) {
|
|
23
|
+
throw new Error("CRM_BINDING_CALLBACK_URL 必须与 MCP_PUBLIC_URL 使用相同域名。");
|
|
24
|
+
}
|
|
25
|
+
const resourceMetadataUrl = getOAuthProtectedResourceMetadataUrl(resourceUrl);
|
|
26
|
+
const resourceMetadataPath = new URL(resourceMetadataUrl).pathname;
|
|
27
|
+
const serviceDocumentationUrl = new URL("/", resourceUrl);
|
|
28
|
+
const host = options.host ?? "127.0.0.1";
|
|
29
|
+
const allowedHosts = options.allowedHosts ?? uniqueStrings([
|
|
30
|
+
resourceUrl.hostname,
|
|
31
|
+
"localhost",
|
|
32
|
+
"127.0.0.1",
|
|
33
|
+
"[::1]"
|
|
34
|
+
]);
|
|
35
|
+
const provider = new WukongOAuthProvider({
|
|
36
|
+
resourceUrl,
|
|
37
|
+
crmBaseUrl: options.crmBaseUrl,
|
|
38
|
+
crmBindingAuthorizeUrl: new URL(options.crmBindingAuthorizeUrl.toString()),
|
|
39
|
+
crmBindingExchangeUrl: new URL(options.crmBindingExchangeUrl.toString()),
|
|
40
|
+
crmBindingCallbackUrl: bindingCallbackUrl,
|
|
41
|
+
crmBindingClientId: options.crmBindingClientId,
|
|
42
|
+
crmBindingClientSecret: options.crmBindingClientSecret,
|
|
43
|
+
secret: options.oauthSecret,
|
|
44
|
+
dataDirectory: options.dataDirectory,
|
|
45
|
+
fetchImpl: options.fetchImpl
|
|
46
|
+
});
|
|
47
|
+
const app = createMcpExpressApp({ host, allowedHosts });
|
|
48
|
+
app.set("trust proxy", options.trustProxy ?? false);
|
|
49
|
+
const protectedResourceMetadata = {
|
|
50
|
+
resource: resourceUrl.href,
|
|
51
|
+
authorization_servers: [issuerUrl.href],
|
|
52
|
+
scopes_supported: [...MCP_RESOURCE_SCOPES],
|
|
53
|
+
resource_name: "WukongCRM MCP",
|
|
54
|
+
resource_documentation: serviceDocumentationUrl.href
|
|
55
|
+
};
|
|
56
|
+
const oauthMetadata = {
|
|
57
|
+
issuer: issuerUrl.href,
|
|
58
|
+
service_documentation: serviceDocumentationUrl.href,
|
|
59
|
+
authorization_endpoint: new URL(`${oauthBasePath}/authorize`, resourceUrl).href,
|
|
60
|
+
token_endpoint: new URL(`${oauthBasePath}/token`, resourceUrl).href,
|
|
61
|
+
registration_endpoint: new URL(`${oauthBasePath}/register`, resourceUrl).href,
|
|
62
|
+
revocation_endpoint: new URL(`${oauthBasePath}/revoke`, resourceUrl).href,
|
|
63
|
+
response_types_supported: ["code"],
|
|
64
|
+
code_challenge_methods_supported: ["S256"],
|
|
65
|
+
token_endpoint_auth_methods_supported: ["none"],
|
|
66
|
+
grant_types_supported: ["authorization_code", "refresh_token"],
|
|
67
|
+
scopes_supported: [...OAUTH_SCOPES],
|
|
68
|
+
revocation_endpoint_auth_methods_supported: ["none"]
|
|
69
|
+
};
|
|
70
|
+
app.use(express.urlencoded({ extended: false, limit: "16kb" }));
|
|
71
|
+
app.get(`${resourcePath}/healthz`, (_req, res) => {
|
|
72
|
+
res.status(200).json({ ok: true, service: "wukong-mcp" });
|
|
73
|
+
});
|
|
74
|
+
app.get(bindingCallbackUrl.pathname, async (req, res) => {
|
|
75
|
+
setSensitiveResponseHeaders(res);
|
|
76
|
+
try {
|
|
77
|
+
const requestId = typeof req.query.state === "string" ? req.query.state : "";
|
|
78
|
+
const bindingCode = typeof req.query.code === "string" ? req.query.code : "";
|
|
79
|
+
const redirectUrl = await provider.completeBindingAuthorization(requestId, bindingCode);
|
|
80
|
+
res.redirect(302, redirectUrl);
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
console.warn("72CRM binding callback failed:", error instanceof Error ? error.message : "unknown error");
|
|
84
|
+
res.status(400).type("text").send("授权失败,请返回 ChatGPT 重新发起连接。");
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
app.use("/.well-known/oauth-protected-resource", metadataHandler(protectedResourceMetadata));
|
|
88
|
+
app.use(resourceMetadataPath, metadataHandler(protectedResourceMetadata));
|
|
89
|
+
app.use("/.well-known/oauth-authorization-server", metadataHandler(oauthMetadata));
|
|
90
|
+
app.use("/.well-known/openid-configuration", metadataHandler(oauthMetadata));
|
|
91
|
+
app.use(`${oauthBasePath}/authorize`, authorizationHandler({ provider }));
|
|
92
|
+
app.use(`${oauthBasePath}/token`, tokenHandler({ provider }));
|
|
93
|
+
app.use(`${oauthBasePath}/register`, clientRegistrationHandler({ clientsStore: provider.clientsStore }));
|
|
94
|
+
app.use(`${oauthBasePath}/revoke`, revocationHandler({ provider }));
|
|
95
|
+
const bearerAuth = requireBearerAuth({
|
|
96
|
+
verifier: provider,
|
|
97
|
+
requiredScopes: [...MCP_RESOURCE_SCOPES],
|
|
98
|
+
resourceMetadataUrl
|
|
99
|
+
});
|
|
100
|
+
app.post(resourceUrl.pathname, bearerAuth, async (req, res) => {
|
|
101
|
+
await handleMcpPost(req, res, options, resourceMetadataUrl);
|
|
102
|
+
});
|
|
103
|
+
app.get(resourceUrl.pathname, bearerAuth, (_req, res) => {
|
|
104
|
+
sendMethodNotAllowed(res);
|
|
105
|
+
});
|
|
106
|
+
app.delete(resourceUrl.pathname, bearerAuth, (_req, res) => {
|
|
107
|
+
sendMethodNotAllowed(res);
|
|
108
|
+
});
|
|
109
|
+
return { app, provider, resourceUrl, resourceMetadataUrl };
|
|
110
|
+
}
|
|
111
|
+
async function handleMcpPost(req, res, options, resourceMetadataUrl) {
|
|
112
|
+
const apiKey = req.auth?.extra?.crmApiKey;
|
|
113
|
+
if (typeof apiKey !== "string" || !apiKey) {
|
|
114
|
+
res.setHeader("WWW-Authenticate", `Bearer resource_metadata="${resourceMetadataUrl}"`);
|
|
115
|
+
res.status(401).json({
|
|
116
|
+
jsonrpc: "2.0",
|
|
117
|
+
error: { code: -32001, message: "Unauthorized" },
|
|
118
|
+
id: null
|
|
119
|
+
});
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const server = createWukongMcpServer({
|
|
123
|
+
baseUrl: options.crmBaseUrl,
|
|
124
|
+
apiKey,
|
|
125
|
+
fetchImpl: options.fetchImpl,
|
|
126
|
+
oauth: {
|
|
127
|
+
scopes: req.auth?.scopes ?? [],
|
|
128
|
+
resourceMetadataUrl
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
|
|
132
|
+
try {
|
|
133
|
+
await server.connect(transport);
|
|
134
|
+
await transport.handleRequest(req, res, req.body);
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
console.error("MCP request failed:", error instanceof Error ? error.message : "unknown error");
|
|
138
|
+
if (!res.headersSent) {
|
|
139
|
+
res.status(500).json({
|
|
140
|
+
jsonrpc: "2.0",
|
|
141
|
+
error: { code: -32603, message: "Internal server error" },
|
|
142
|
+
id: null
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
finally {
|
|
147
|
+
await transport.close().catch(() => undefined);
|
|
148
|
+
await server.close().catch(() => undefined);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
function validatePublicUrl(value) {
|
|
152
|
+
const url = new URL(value.toString());
|
|
153
|
+
if (url.search || url.hash) {
|
|
154
|
+
throw new Error("MCP_PUBLIC_URL 不能包含查询参数或片段。");
|
|
155
|
+
}
|
|
156
|
+
const isLoopback = url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "[::1]";
|
|
157
|
+
if (url.protocol !== "https:" && !(url.protocol === "http:" && isLoopback)) {
|
|
158
|
+
throw new Error("MCP_PUBLIC_URL 必须使用 HTTPS;只有本地开发地址可以使用 HTTP。");
|
|
159
|
+
}
|
|
160
|
+
const normalizedPath = url.pathname.replace(/\/+$/, "");
|
|
161
|
+
if (!normalizedPath) {
|
|
162
|
+
throw new Error("MCP_PUBLIC_URL 必须包含 MCP 路径,例如 https://mcp.example.com/mcp。");
|
|
163
|
+
}
|
|
164
|
+
url.pathname = normalizedPath;
|
|
165
|
+
url.hash = "";
|
|
166
|
+
return url;
|
|
167
|
+
}
|
|
168
|
+
function setSensitiveResponseHeaders(res) {
|
|
169
|
+
res.setHeader("Cache-Control", "no-store");
|
|
170
|
+
res.setHeader("Pragma", "no-cache");
|
|
171
|
+
res.setHeader("X-Frame-Options", "DENY");
|
|
172
|
+
res.setHeader("Referrer-Policy", "no-referrer");
|
|
173
|
+
res.setHeader("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'; form-action 'self'; base-uri 'none'; frame-ancestors 'none'");
|
|
174
|
+
}
|
|
175
|
+
function sendMethodNotAllowed(res) {
|
|
176
|
+
res.status(405).json({
|
|
177
|
+
jsonrpc: "2.0",
|
|
178
|
+
error: { code: -32000, message: "Method not allowed" },
|
|
179
|
+
id: null
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
function uniqueStrings(values) {
|
|
183
|
+
return [...new Set(values.filter(Boolean))];
|
|
184
|
+
}
|
|
185
|
+
export function loadRemoteServerEnvironment(env = process.env) {
|
|
186
|
+
const publicUrl = requiredEnvironment(env, "MCP_PUBLIC_URL");
|
|
187
|
+
const oauthSecret = requiredEnvironment(env, "MCP_OAUTH_SECRET");
|
|
188
|
+
const parsedPublicUrl = validatePublicUrl(publicUrl);
|
|
189
|
+
const crmBindingAuthorizeUrl = requiredEnvironment(env, "CRM_BINDING_AUTHORIZE_URL");
|
|
190
|
+
const crmBindingExchangeUrl = requiredEnvironment(env, "CRM_BINDING_EXCHANGE_URL");
|
|
191
|
+
const crmBindingClientId = requiredEnvironment(env, "CRM_BINDING_CLIENT_ID");
|
|
192
|
+
const crmBindingClientSecret = requiredEnvironment(env, "CRM_BINDING_CLIENT_SECRET");
|
|
193
|
+
const resourcePath = parsedPublicUrl.pathname;
|
|
194
|
+
const crmBindingCallbackUrl = env.CRM_BINDING_CALLBACK_URL?.trim() ||
|
|
195
|
+
new URL(`${resourcePath}/oauth/72crm/callback`, parsedPublicUrl).href;
|
|
196
|
+
const portText = env.MCP_PORT ?? env.PORT ?? "3000";
|
|
197
|
+
const port = Number.parseInt(portText, 10);
|
|
198
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
199
|
+
throw new Error("MCP_PORT/PORT 必须是 1 到 65535 之间的整数。");
|
|
200
|
+
}
|
|
201
|
+
const configuredHosts = (env.MCP_ALLOWED_HOSTS ?? "")
|
|
202
|
+
.split(",")
|
|
203
|
+
.map((value) => value.trim())
|
|
204
|
+
.filter(Boolean);
|
|
205
|
+
return {
|
|
206
|
+
publicUrl,
|
|
207
|
+
oauthSecret,
|
|
208
|
+
dataDirectory: path.resolve(env.MCP_DATA_DIR ?? ".wukong-mcp-data"),
|
|
209
|
+
crmBaseUrl: env.CRM_BASE_URL,
|
|
210
|
+
crmBindingAuthorizeUrl,
|
|
211
|
+
crmBindingExchangeUrl,
|
|
212
|
+
crmBindingCallbackUrl,
|
|
213
|
+
crmBindingClientId,
|
|
214
|
+
crmBindingClientSecret,
|
|
215
|
+
host: env.MCP_HOST ?? "127.0.0.1",
|
|
216
|
+
allowedHosts: uniqueStrings([
|
|
217
|
+
...configuredHosts,
|
|
218
|
+
parsedPublicUrl.hostname,
|
|
219
|
+
"localhost",
|
|
220
|
+
"127.0.0.1",
|
|
221
|
+
"[::1]"
|
|
222
|
+
]),
|
|
223
|
+
trustProxy: parseTrustProxy(env.MCP_TRUST_PROXY ?? "1"),
|
|
224
|
+
port
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
function parseTrustProxy(value) {
|
|
228
|
+
const normalized = value.trim();
|
|
229
|
+
if (!normalized) {
|
|
230
|
+
throw new Error("MCP_TRUST_PROXY 不能为空。");
|
|
231
|
+
}
|
|
232
|
+
if (normalized === "true") {
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
if (normalized === "false") {
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
if (/^-?\d+$/.test(normalized)) {
|
|
239
|
+
const hops = Number.parseInt(normalized, 10);
|
|
240
|
+
if (!Number.isSafeInteger(hops) || hops < 0) {
|
|
241
|
+
throw new Error("MCP_TRUST_PROXY 的代理层数必须是非负整数。");
|
|
242
|
+
}
|
|
243
|
+
return hops;
|
|
244
|
+
}
|
|
245
|
+
return normalized;
|
|
246
|
+
}
|
|
247
|
+
function requiredEnvironment(env, name) {
|
|
248
|
+
const value = env[name]?.trim();
|
|
249
|
+
if (!value) {
|
|
250
|
+
throw new Error(`远程模式必须配置 ${name}。`);
|
|
251
|
+
}
|
|
252
|
+
return value;
|
|
253
|
+
}
|
package/dist/modules.js
CHANGED
|
@@ -41,9 +41,9 @@ export const CRM_MODULES = [
|
|
|
41
41
|
basePath: "/crmProduct",
|
|
42
42
|
primaryKey: "productId",
|
|
43
43
|
aliases: ["产品", "crmProduct", "4"],
|
|
44
|
-
supportedOperations: [...COMMON_READ, "简要实体", "销售产品列表"],
|
|
45
|
-
unsupportedOperations: ["
|
|
46
|
-
writable:
|
|
44
|
+
supportedOperations: [...COMMON_READ, "简要实体", "销售产品列表", "新增", "更新", "字段更新", "上下架", "负责人转移", "删除"],
|
|
45
|
+
unsupportedOperations: ["导入导出", "文件流下载", "multipart 文件上传代理"],
|
|
46
|
+
writable: true
|
|
47
47
|
},
|
|
48
48
|
{
|
|
49
49
|
key: "business",
|
|
@@ -167,7 +167,7 @@ export const CRM_MODULES = [
|
|
|
167
167
|
writable: false
|
|
168
168
|
}
|
|
169
169
|
];
|
|
170
|
-
export const WRITE_MODULES = new Set(["leads", "customer", "contacts", "business"]);
|
|
170
|
+
export const WRITE_MODULES = new Set(["leads", "customer", "contacts", "product", "business"]);
|
|
171
171
|
const MODULE_LOOKUP = new Map();
|
|
172
172
|
for (const moduleDefinition of CRM_MODULES) {
|
|
173
173
|
MODULE_LOOKUP.set(moduleDefinition.key.toLowerCase(), moduleDefinition);
|
package/dist/oauth.d.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { OAuthRegisteredClientsStore } from "@modelcontextprotocol/sdk/server/auth/clients.js";
|
|
2
|
+
import type { OAuthServerProvider, AuthorizationParams } from "@modelcontextprotocol/sdk/server/auth/provider.js";
|
|
3
|
+
import type { AuthInfo } from "@modelcontextprotocol/sdk/server/auth/types.js";
|
|
4
|
+
import type { OAuthClientInformationFull, OAuthTokenRevocationRequest, OAuthTokens } from "@modelcontextprotocol/sdk/shared/auth.js";
|
|
5
|
+
import type { Response } from "express";
|
|
6
|
+
type RegisterableClient = Omit<OAuthClientInformationFull, "client_id" | "client_id_issued_at"> & {
|
|
7
|
+
client_id?: string;
|
|
8
|
+
client_id_issued_at?: number;
|
|
9
|
+
};
|
|
10
|
+
export declare class FileOAuthState implements OAuthRegisteredClientsStore {
|
|
11
|
+
private readonly filePath;
|
|
12
|
+
private readonly clients;
|
|
13
|
+
private readonly revoked;
|
|
14
|
+
private readonly ready;
|
|
15
|
+
private persistQueue;
|
|
16
|
+
constructor(dataDirectory: string);
|
|
17
|
+
getClient(clientId: string): Promise<OAuthClientInformationFull | undefined>;
|
|
18
|
+
registerClient(client: RegisterableClient): Promise<OAuthClientInformationFull>;
|
|
19
|
+
revoke(jti: string, expiresAt: number): Promise<void>;
|
|
20
|
+
isRevoked(jti: string): Promise<boolean>;
|
|
21
|
+
private load;
|
|
22
|
+
private removeExpiredRevocations;
|
|
23
|
+
private persist;
|
|
24
|
+
}
|
|
25
|
+
export interface WukongOAuthProviderOptions {
|
|
26
|
+
resourceUrl: URL;
|
|
27
|
+
crmBaseUrl?: string;
|
|
28
|
+
crmBindingAuthorizeUrl: URL;
|
|
29
|
+
crmBindingExchangeUrl: URL;
|
|
30
|
+
crmBindingCallbackUrl: URL;
|
|
31
|
+
crmBindingClientId: string;
|
|
32
|
+
crmBindingClientSecret: string;
|
|
33
|
+
secret: string;
|
|
34
|
+
dataDirectory: string;
|
|
35
|
+
fetchImpl?: typeof fetch;
|
|
36
|
+
accessTokenTtlSeconds?: number;
|
|
37
|
+
refreshTokenTtlSeconds?: number;
|
|
38
|
+
}
|
|
39
|
+
export declare class WukongOAuthProvider implements OAuthServerProvider {
|
|
40
|
+
readonly clientsStore: FileOAuthState;
|
|
41
|
+
private readonly resourceUrl;
|
|
42
|
+
private readonly crmBaseUrl?;
|
|
43
|
+
private readonly fetchImpl?;
|
|
44
|
+
private readonly crmBindingAuthorizeUrl;
|
|
45
|
+
private readonly crmBindingExchangeUrl;
|
|
46
|
+
private readonly crmBindingCallbackUrl;
|
|
47
|
+
private readonly crmBindingClientId;
|
|
48
|
+
private readonly crmBindingClientSecret;
|
|
49
|
+
private readonly encryptionKey;
|
|
50
|
+
private readonly accessTokenTtlSeconds;
|
|
51
|
+
private readonly refreshTokenTtlSeconds;
|
|
52
|
+
private readonly pendingAuthorizations;
|
|
53
|
+
private readonly authorizationCodes;
|
|
54
|
+
constructor(options: WukongOAuthProviderOptions);
|
|
55
|
+
authorize(client: OAuthClientInformationFull, params: AuthorizationParams, res: Response): Promise<void>;
|
|
56
|
+
completeBindingAuthorization(requestId: string, bindingCode: string): Promise<string>;
|
|
57
|
+
private exchangeBindingCode;
|
|
58
|
+
challengeForAuthorizationCode(client: OAuthClientInformationFull, authorizationCode: string): Promise<string>;
|
|
59
|
+
exchangeAuthorizationCode(client: OAuthClientInformationFull, authorizationCode: string, _codeVerifier?: string, redirectUri?: string, resource?: URL): Promise<OAuthTokens>;
|
|
60
|
+
exchangeRefreshToken(client: OAuthClientInformationFull, refreshToken: string, scopes?: string[], resource?: URL): Promise<OAuthTokens>;
|
|
61
|
+
verifyAccessToken(token: string): Promise<AuthInfo>;
|
|
62
|
+
revokeToken(client: OAuthClientInformationFull, request: OAuthTokenRevocationRequest): Promise<void>;
|
|
63
|
+
private getAuthorizationCode;
|
|
64
|
+
private issueTokens;
|
|
65
|
+
private decodeAndVerifyToken;
|
|
66
|
+
private encryptToken;
|
|
67
|
+
private decryptToken;
|
|
68
|
+
private assertResource;
|
|
69
|
+
private cleanupTransientState;
|
|
70
|
+
}
|
|
71
|
+
export {};
|