@scorehub/mcp-server 0.2.7 → 0.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +71 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -43,6 +43,53 @@ const TOKEN_STORE_KEY = endpointTokenKey(SCOREHUB_BASE_URL);
|
|
|
43
43
|
const CLIENT_ID = "scorehub-mcp";
|
|
44
44
|
// Proactively refresh token every 4 hours to prevent expiry during long sessions
|
|
45
45
|
const TOKEN_KEEPALIVE_INTERVAL = 4 * 60 * 60 * 1000;
|
|
46
|
+
function isRemoteToolErrorType(value) {
|
|
47
|
+
return value === "oauth_invalid"
|
|
48
|
+
|| value === "shop_auth_invalid"
|
|
49
|
+
|| value === "rate_limited"
|
|
50
|
+
|| value === "quota_exhausted"
|
|
51
|
+
|| value === "invalid_input"
|
|
52
|
+
|| value === "permission_denied"
|
|
53
|
+
|| value === "upstream_error";
|
|
54
|
+
}
|
|
55
|
+
function parseRemoteToolEnvelope(text) {
|
|
56
|
+
try {
|
|
57
|
+
const parsed = JSON.parse(text);
|
|
58
|
+
if (parsed.ok === true && Object.prototype.hasOwnProperty.call(parsed, "data")) {
|
|
59
|
+
return { ok: true, data: parsed.data };
|
|
60
|
+
}
|
|
61
|
+
if (parsed.ok === false
|
|
62
|
+
&& isRemoteToolErrorType(parsed.error_type)
|
|
63
|
+
&& typeof parsed.user_message === "string"
|
|
64
|
+
&& typeof parsed.retryable === "boolean") {
|
|
65
|
+
return {
|
|
66
|
+
ok: false,
|
|
67
|
+
error_type: parsed.error_type,
|
|
68
|
+
user_message: parsed.user_message,
|
|
69
|
+
retryable: parsed.retryable,
|
|
70
|
+
...(Object.prototype.hasOwnProperty.call(parsed, "upstream") ? { upstream: parsed.upstream } : {}),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function extractRemoteTextContent(content) {
|
|
80
|
+
if (!Array.isArray(content)) {
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
return content.flatMap((item) => {
|
|
84
|
+
if (item
|
|
85
|
+
&& typeof item === "object"
|
|
86
|
+
&& item.type === "text"
|
|
87
|
+
&& typeof item.text === "string") {
|
|
88
|
+
return [item.text];
|
|
89
|
+
}
|
|
90
|
+
return [];
|
|
91
|
+
});
|
|
92
|
+
}
|
|
46
93
|
// ── Remote Tool Proxy (MCP Streamable HTTP) ─────────────────
|
|
47
94
|
async function fetchRemoteTools(token) {
|
|
48
95
|
try {
|
|
@@ -74,7 +121,15 @@ async function callRemoteTool(name, args, token) {
|
|
|
74
121
|
try {
|
|
75
122
|
await client.connect(transport);
|
|
76
123
|
const result = await client.callTool({ name, arguments: args });
|
|
77
|
-
|
|
124
|
+
const textBlocks = extractRemoteTextContent(result.content);
|
|
125
|
+
const rawText = textBlocks.length > 0
|
|
126
|
+
? textBlocks.join("\n\n")
|
|
127
|
+
: JSON.stringify(result.content, null, 2);
|
|
128
|
+
const envelope = textBlocks
|
|
129
|
+
.map((text) => parseRemoteToolEnvelope(text))
|
|
130
|
+
.find((candidate) => candidate !== null)
|
|
131
|
+
?? parseRemoteToolEnvelope(rawText);
|
|
132
|
+
return { rawText, envelope };
|
|
78
133
|
}
|
|
79
134
|
finally {
|
|
80
135
|
await client.close();
|
|
@@ -277,8 +332,20 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
277
332
|
};
|
|
278
333
|
}
|
|
279
334
|
try {
|
|
280
|
-
const
|
|
281
|
-
if (
|
|
335
|
+
const remoteResult = await callRemoteTool(name, args, tokenResult.access_token);
|
|
336
|
+
if (remoteResult.envelope?.ok === false && remoteResult.envelope.error_type === "oauth_invalid") {
|
|
337
|
+
(0, auth_js_1.clearTokens)(TOKEN_STORE_KEY);
|
|
338
|
+
const authUrl = generateAuthUrl();
|
|
339
|
+
return {
|
|
340
|
+
content: [
|
|
341
|
+
{
|
|
342
|
+
type: "text",
|
|
343
|
+
text: `⚠️ 授权已失效,已清除本地缓存。请调用 "authorize" 工具重新登录 ScoreHub。\n授权地址: ${authUrl}`,
|
|
344
|
+
},
|
|
345
|
+
],
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
if (remoteResult.rawText.includes("ScoreHub OAuth 授权已过期或无效")) {
|
|
282
349
|
(0, auth_js_1.clearTokens)(TOKEN_STORE_KEY);
|
|
283
350
|
const authUrl = generateAuthUrl();
|
|
284
351
|
return {
|
|
@@ -290,7 +357,7 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
290
357
|
],
|
|
291
358
|
};
|
|
292
359
|
}
|
|
293
|
-
return { content: [{ type: "text", text:
|
|
360
|
+
return { content: [{ type: "text", text: remoteResult.rawText }] };
|
|
294
361
|
}
|
|
295
362
|
catch (e) {
|
|
296
363
|
return {
|