@scorehub/mcp-server 0.3.3 → 0.4.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.en.md CHANGED
@@ -64,7 +64,17 @@ Common configuration locations are:
64
64
 
65
65
  On the first launch, `npx` downloads `@scorehub/mcp-server` and connects it to the remote endpoint. When you first call a remote tool, invoke `authorize` when prompted by your client. A browser opens the ScoreHub login page; after authorization it returns to a random `127.0.0.1` port and stores the token locally. Later calls reuse the cached token and do not require another login.
66
66
 
67
- > After you install Tiky through the WorkBuddy share link, Tiky can run `npx -y @scorehub/creator-outreach@latest` when needed to complete the local bootstrap and write or repair this MCP configuration automatically. Manual editing is not required.
67
+ > After you install Tiky through the WorkBuddy share link, Tiky's first-turn bootstrap gate writes or repairs this MCP configuration after the user's one-time confirmation. Manual editing is not required.
68
+
69
+ ### Startup self-check
70
+
71
+ Before writing the WorkBuddy configuration, creator-outreach prefetches npm latest and runs:
72
+
73
+ ```bash
74
+ npx -y @scorehub/mcp-server@latest --self-check --json
75
+ ```
76
+
77
+ The command reports the package version, Node.js version, and executable entrypoint status, then exits immediately. It does not start stdio, contact the remote MCP endpoint, enter OAuth, or write tokens.
68
78
 
69
79
  ## Windows
70
80
 
@@ -203,6 +213,8 @@ With `npx -y ...@latest`, every launch resolves npm's current `latest` tag. For
203
213
  npm update -g @scorehub/mcp-server
204
214
  ```
205
215
 
216
+ Because the public WorkBuddy configuration always follows `@latest`, releases under this package name must preserve backward compatibility for tool names, parameters, OAuth, and the `status` contract. An incompatible protocol must use a new package name.
217
+
206
218
  ## Related Packages
207
219
 
208
220
  | Package | Description |
package/README.md CHANGED
@@ -64,7 +64,17 @@ npx --version
64
64
 
65
65
  首次启动时,`npx` 会下载 `@scorehub/mcp-server` 并连接远程端点。首次调用远程工具时,按客户端提示调用 `authorize`:浏览器会打开 ScoreHub 登录页,授权完成后回调到 `127.0.0.1` 的随机端口并写入本地 Token。之后会复用缓存,不需要每次登录。
66
66
 
67
- > 通过 WorkBuddy 分享链接安装 Tiky 后,Tiky 会在需要时执行 `npx -y @scorehub/creator-outreach@latest` 完成本地引导,并自动写入或修复这份 MCP 配置;无需手动编辑。
67
+ > 通过 WorkBuddy 分享链接安装 Tiky 后,Tiky 会在首轮 bootstrap 门禁中经用户首次确认后自动写入或修复这份 MCP 配置;无需手动编辑。
68
+
69
+ ### 启动前自检
70
+
71
+ 在写入 WorkBuddy 配置前,creator-outreach 会预拉取 npm latest 并执行:
72
+
73
+ ```bash
74
+ npx -y @scorehub/mcp-server@latest --self-check --json
75
+ ```
76
+
77
+ 该命令返回包版本、Node.js 版本和可执行入口状态,然后立即退出。它不启动 stdio 服务、不访问远端 MCP、不进入 OAuth,也不写入 Token。
68
78
 
69
79
  ## Windows 使用说明
70
80
 
@@ -203,6 +213,8 @@ Token 持久化在本机文件中;调用默认 ScoreHub 生产端点时,mcp-
203
213
  npm update -g @scorehub/mcp-server
204
214
  ```
205
215
 
216
+ 由于 WorkBuddy 公开配置始终跟随 `@latest`,同一包名下的 latest 发布必须保持工具名、参数、OAuth 和 `status` 契约向后兼容。无法兼容的新协议必须使用新包名。
217
+
206
218
  ## 相关包
207
219
 
208
220
  | 包 | 说明 |
package/dist/index.js CHANGED
@@ -18,11 +18,12 @@ const index_js_2 = require("@modelcontextprotocol/sdk/client/index.js");
18
18
  const streamableHttp_js_1 = require("@modelcontextprotocol/sdk/client/streamableHttp.js");
19
19
  const node_fs_1 = require("node:fs");
20
20
  const node_path_1 = require("node:path");
21
- function readPackageVersion() {
22
- const packageJson = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(__dirname, "../package.json"), "utf-8"));
23
- return packageJson.version;
21
+ const self_check_js_1 = require("./self-check.js");
22
+ function readPackageMetadata() {
23
+ return JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(__dirname, "../package.json"), "utf-8"));
24
24
  }
25
- const SERVER_VERSION = readPackageVersion();
25
+ const PACKAGE_METADATA = readPackageMetadata();
26
+ const SERVER_VERSION = PACKAGE_METADATA.version;
26
27
  const DEFAULT_SCOREHUB_BASE_URL = "https://app.scorehub.cn/mcp";
27
28
  const SCOREHUB_BASE_URL = normalizeBaseUrl(process.env.SCOREHUB_REMOTE_MCP_URL || DEFAULT_SCOREHUB_BASE_URL);
28
29
  // OAuth endpoints are at the root path
@@ -378,6 +379,23 @@ function startTokenKeepAlive() {
378
379
  }
379
380
  // ── Start ───────────────────────────────────────────────────
380
381
  async function main() {
382
+ if (process.argv.includes("--self-check")) {
383
+ const payload = (0, self_check_js_1.buildSelfCheckPayload)({
384
+ packageName: PACKAGE_METADATA.name,
385
+ serverVersion: SERVER_VERSION,
386
+ });
387
+ if (process.argv.includes("--json")) {
388
+ process.stdout.write(`${JSON.stringify(payload)}\n`);
389
+ }
390
+ else {
391
+ process.stdout.write(payload.ok
392
+ ? `ScoreHub MCP Server v${payload.server_version} is ready (Node.js ${payload.node_version}).\n`
393
+ : `Node.js ${payload.minimum_node_major}+ is required. Detected ${payload.node_version}.\n`);
394
+ }
395
+ if (!payload.ok)
396
+ process.exitCode = 1;
397
+ return;
398
+ }
381
399
  // Set process title for easy identification in ps/task manager
382
400
  process.title = 'scorehub-mcp';
383
401
  // Gracefully exit when parent AI agent closes stdin (normal shutdown or crash).
@@ -0,0 +1,18 @@
1
+ export declare const MIN_NODE_MAJOR = 18;
2
+ export interface SelfCheckPayload {
3
+ schema_version: 1;
4
+ ok: boolean;
5
+ package_name: string;
6
+ server_version: string;
7
+ node_version: string;
8
+ node_supported: boolean;
9
+ minimum_node_major: number;
10
+ entrypoint: "scorehub-mcp";
11
+ }
12
+ export interface SelfCheckInput {
13
+ packageName: string;
14
+ serverVersion: string;
15
+ nodeVersion?: string;
16
+ }
17
+ export declare function getNodeMajorVersion(nodeVersion: string): number;
18
+ export declare function buildSelfCheckPayload(input: SelfCheckInput): SelfCheckPayload;
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MIN_NODE_MAJOR = void 0;
4
+ exports.getNodeMajorVersion = getNodeMajorVersion;
5
+ exports.buildSelfCheckPayload = buildSelfCheckPayload;
6
+ exports.MIN_NODE_MAJOR = 18;
7
+ function getNodeMajorVersion(nodeVersion) {
8
+ return Number.parseInt(nodeVersion.split(".")[0], 10);
9
+ }
10
+ function buildSelfCheckPayload(input) {
11
+ const nodeVersion = input.nodeVersion ?? process.versions.node;
12
+ const nodeSupported = getNodeMajorVersion(nodeVersion) >= exports.MIN_NODE_MAJOR;
13
+ return {
14
+ schema_version: 1,
15
+ ok: nodeSupported,
16
+ package_name: input.packageName,
17
+ server_version: input.serverVersion,
18
+ node_version: nodeVersion,
19
+ node_supported: nodeSupported,
20
+ minimum_node_major: exports.MIN_NODE_MAJOR,
21
+ entrypoint: "scorehub-mcp",
22
+ };
23
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scorehub/mcp-server",
3
- "version": "0.3.3",
3
+ "version": "0.4.0",
4
4
  "description": "ScoreHub local MCP proxy — OAuth login, token caching, and tool forwarding to remote MCP endpoint",
5
5
  "engines": {
6
6
  "node": ">=18"
@@ -18,7 +18,7 @@
18
18
  "access": "public"
19
19
  },
20
20
  "scripts": {
21
- "build": "tsc && node -e \"const fs=require('fs');const f='dist/index.js';fs.writeFileSync(f,'#!/usr/bin/env node\\n'+fs.readFileSync(f,'utf8'));fs.chmodSync(f,0o755)\"",
21
+ "build": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && tsc && node -e \"const fs=require('fs');const f='dist/index.js';fs.writeFileSync(f,'#!/usr/bin/env node\\n'+fs.readFileSync(f,'utf8'));fs.chmodSync(f,0o755)\"",
22
22
  "dev": "tsx src/index.ts",
23
23
  "start": "node dist/index.js",
24
24
  "test": "tsx --test test/*.test.ts"
@@ -1,39 +0,0 @@
1
- export declare const VERSION_CACHE_TTL_MS: number;
2
- export declare const VERSION_REQUEST_TIMEOUT_MS = 3000;
3
- export type StartupCheckStatus = "ready" | "restart_required" | "migration_required" | "verification_unavailable";
4
- export interface LatestVersions {
5
- creatorOutreach?: string;
6
- mcpServer?: string;
7
- checkedAt: string;
8
- error?: string;
9
- }
10
- export interface StartupCheckResult {
11
- status: StartupCheckStatus;
12
- creator_outreach: {
13
- agent_version: string;
14
- bootstrap_version: string | null;
15
- latest_version: string | null;
16
- };
17
- mcp_server: {
18
- running_version: string;
19
- latest_version: string | null;
20
- };
21
- checked_at: string;
22
- message: string;
23
- }
24
- interface GetLatestVersionsOptions {
25
- registryUrl?: string;
26
- fetchImpl?: typeof fetch;
27
- now?: () => number;
28
- timeoutMs?: number;
29
- }
30
- interface EvaluateStartupCheckOptions {
31
- agentVersion: string;
32
- bootstrapVersion?: string;
33
- mcpServerVersion: string;
34
- latestVersions: LatestVersions;
35
- }
36
- export declare function getLatestVersions({ registryUrl, fetchImpl, now, timeoutMs, }?: GetLatestVersionsOptions): Promise<LatestVersions>;
37
- export declare function evaluateStartupCheck({ agentVersion, bootstrapVersion, mcpServerVersion, latestVersions, }: EvaluateStartupCheckOptions): StartupCheckResult;
38
- export declare function resetVersionCacheForTests(): void;
39
- export {};
@@ -1,120 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.VERSION_REQUEST_TIMEOUT_MS = exports.VERSION_CACHE_TTL_MS = void 0;
4
- exports.getLatestVersions = getLatestVersions;
5
- exports.evaluateStartupCheck = evaluateStartupCheck;
6
- exports.resetVersionCacheForTests = resetVersionCacheForTests;
7
- const DEFAULT_NPM_REGISTRY = "https://registry.npmjs.org";
8
- exports.VERSION_CACHE_TTL_MS = 15 * 60 * 1000;
9
- exports.VERSION_REQUEST_TIMEOUT_MS = 3000;
10
- const CREATOR_OUTREACH_PACKAGE = "@scorehub/creator-outreach";
11
- const MCP_SERVER_PACKAGE = "@scorehub/mcp-server";
12
- let versionCache = null;
13
- function normalizeRegistryUrl(value) {
14
- const registry = (value || DEFAULT_NPM_REGISTRY).trim();
15
- return registry.endsWith("/") ? registry : `${registry}/`;
16
- }
17
- async function fetchPackageVersion(packageName, registryUrl, fetchImpl, timeoutMs) {
18
- const controller = new AbortController();
19
- const timeout = setTimeout(() => controller.abort(), timeoutMs);
20
- timeout.unref?.();
21
- try {
22
- const url = new URL(`${encodeURIComponent(packageName)}/latest`, registryUrl);
23
- const response = await fetchImpl(url, {
24
- headers: { accept: "application/json" },
25
- signal: controller.signal,
26
- });
27
- if (!response.ok) {
28
- throw new Error(`${packageName} returned HTTP ${response.status}`);
29
- }
30
- const payload = await response.json();
31
- if (typeof payload.version !== "string" || payload.version.trim() === "") {
32
- throw new Error(`${packageName} latest metadata did not include a version`);
33
- }
34
- return payload.version.trim();
35
- }
36
- finally {
37
- clearTimeout(timeout);
38
- }
39
- }
40
- async function getLatestVersions({ registryUrl = process.env.npm_config_registry, fetchImpl = fetch, now = Date.now, timeoutMs = exports.VERSION_REQUEST_TIMEOUT_MS, } = {}) {
41
- const nowMs = now();
42
- if (versionCache && versionCache.expiresAt > nowMs) {
43
- return versionCache.value;
44
- }
45
- const checkedAt = new Date(nowMs).toISOString();
46
- const normalizedRegistry = normalizeRegistryUrl(registryUrl);
47
- const [creatorResult, mcpResult] = await Promise.allSettled([
48
- fetchPackageVersion(CREATOR_OUTREACH_PACKAGE, normalizedRegistry, fetchImpl, timeoutMs),
49
- fetchPackageVersion(MCP_SERVER_PACKAGE, normalizedRegistry, fetchImpl, timeoutMs),
50
- ]);
51
- const errors = [];
52
- const value = { checkedAt };
53
- if (creatorResult.status === "fulfilled") {
54
- value.creatorOutreach = creatorResult.value;
55
- }
56
- else {
57
- errors.push(`creator-outreach: ${String(creatorResult.reason)}`);
58
- }
59
- if (mcpResult.status === "fulfilled") {
60
- value.mcpServer = mcpResult.value;
61
- }
62
- else {
63
- errors.push(`mcp-server: ${String(mcpResult.reason)}`);
64
- }
65
- if (errors.length > 0) {
66
- value.error = errors.join("; ");
67
- }
68
- versionCache = {
69
- expiresAt: nowMs + exports.VERSION_CACHE_TTL_MS,
70
- value,
71
- };
72
- return value;
73
- }
74
- function evaluateStartupCheck({ agentVersion, bootstrapVersion, mcpServerVersion, latestVersions, }) {
75
- const result = {
76
- creator_outreach: {
77
- agent_version: agentVersion,
78
- bootstrap_version: bootstrapVersion || null,
79
- latest_version: latestVersions.creatorOutreach || null,
80
- },
81
- mcp_server: {
82
- running_version: mcpServerVersion,
83
- latest_version: latestVersions.mcpServer || null,
84
- },
85
- checked_at: latestVersions.checkedAt,
86
- };
87
- if (!bootstrapVersion) {
88
- return {
89
- status: "migration_required",
90
- ...result,
91
- message: "当前仍在使用旧版 MCP 启动配置。请执行一次 npx -y @scorehub/creator-outreach@latest,然后完全退出并重新打开客户端。",
92
- };
93
- }
94
- if (agentVersion !== bootstrapVersion
95
- || (latestVersions.creatorOutreach && bootstrapVersion !== latestVersions.creatorOutreach)
96
- || (latestVersions.mcpServer && mcpServerVersion !== latestVersions.mcpServer)) {
97
- return {
98
- status: "restart_required",
99
- ...result,
100
- message: "检测到 Tiky 配置或 ScoreHub MCP 版本已变化。请完全退出并重新打开客户端,以加载已刷新的版本。",
101
- };
102
- }
103
- if (latestVersions.error
104
- || !latestVersions.creatorOutreach
105
- || !latestVersions.mcpServer) {
106
- return {
107
- status: "verification_unavailable",
108
- ...result,
109
- message: "暂时无法连接 npm registry 确认是否为最新版;当前本地版本一致,可以继续使用。",
110
- };
111
- }
112
- return {
113
- status: "ready",
114
- ...result,
115
- message: "Tiky 配置与 ScoreHub MCP 均已就绪。",
116
- };
117
- }
118
- function resetVersionCacheForTests() {
119
- versionCache = null;
120
- }