mirror of
https://github.com/modelstudioai/cli.git
synced 2026-09-14 19:49:23 +08:00
feat: add MCP WebSearch page URL and enhance error handling in web search command
This commit is contained in:
@@ -20,6 +20,7 @@ runtime/src/urls.ts ← 用户面控制台 URL(cn-only)
|
||||
BAILIAN_CONSOLE BAILIAN_CONSOLE_ROOT/cn-beijing
|
||||
API_KEY_PAGE BAILIAN_CONSOLE/?tab=app#/api-key
|
||||
TOKEN_PLAN_PAGE BAILIAN_CONSOLE_ROOT/cn-beijing?tab=plan#/efm/subscription/overview
|
||||
MCP_WEBSEARCH_PAGE BAILIAN_CONSOLE?tab=mcp#/mcp-market/detail/WebSearch
|
||||
|
||||
core/files/upload.ts ← 文件上传 endpoint(cn-pinned)
|
||||
UPLOAD_API ${REGIONS.cn}/api/v1/uploads
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import { BailianError } from "bailian-cli-core";
|
||||
import { MCP_WEBSEARCH_PAGE } from "bailian-cli-runtime";
|
||||
|
||||
/** recoginze WebSearch MCP not activated / invalid caused 404 (CLI wrapped message from server)。 */
|
||||
export function isWebSearchMcpNotActivated(error: unknown): boolean {
|
||||
if (!(error instanceof BailianError)) return false;
|
||||
const message = error.message;
|
||||
if (!/MCP request failed:\s*404\b/i.test(message)) return false;
|
||||
return /未开通|MCP不存在|MCP_IS_INVALID/i.test(message);
|
||||
}
|
||||
|
||||
/** activate hint; URL from runtime/urls.ts。 */
|
||||
export function webSearchActivateHint(): string {
|
||||
return [
|
||||
"Activate (or re-activate) the WebSearch MCP in the Bailian MCP marketplace, then retry.",
|
||||
"If it was previously on SSE, cancel and activate again to upgrade to Streamable HTTP.",
|
||||
`Open: ${MCP_WEBSEARCH_PAGE}`,
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* keep original message / exitCode for not activated errors, add hint only; other errors throw as is.
|
||||
* do not replace server error message.
|
||||
*/
|
||||
export function rethrowWithWebSearchActivateHint(error: unknown): never {
|
||||
if (isWebSearchMcpNotActivated(error) && error instanceof BailianError && !error.hint) {
|
||||
throw new BailianError(error.message, error.exitCode, webSearchActivateHint(), {
|
||||
cause: error,
|
||||
api: error.api,
|
||||
rawResponse: error.rawResponse,
|
||||
});
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
@@ -5,8 +5,8 @@ import {
|
||||
mcpWebSearchPath,
|
||||
type FlagsDef,
|
||||
} from "bailian-cli-core";
|
||||
import { createSpinner } from "bailian-cli-runtime";
|
||||
import { emitResult } from "bailian-cli-runtime";
|
||||
import { createSpinner, emitResult } from "bailian-cli-runtime";
|
||||
import { rethrowWithWebSearchActivateHint } from "./web-activate-hint.ts";
|
||||
|
||||
const WEB_SEARCH_FLAGS = {
|
||||
query: { type: "string", valueHint: "<text>", description: "Search query text" },
|
||||
@@ -41,11 +41,14 @@ export default defineCommand({
|
||||
return;
|
||||
}
|
||||
|
||||
const client = ctx.client.mcp(mcpWebSearchPath());
|
||||
await client.initialize();
|
||||
const tools = await client.listTools();
|
||||
|
||||
emitResult({ tools }, format);
|
||||
try {
|
||||
const client = ctx.client.mcp(mcpWebSearchPath());
|
||||
await client.initialize();
|
||||
const tools = await client.listTools();
|
||||
emitResult({ tools }, format);
|
||||
} catch (error) {
|
||||
rethrowWithWebSearchActivateHint(error);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -123,7 +126,7 @@ export default defineCommand({
|
||||
}
|
||||
} catch (error) {
|
||||
spinner.stop("Failed.");
|
||||
throw error;
|
||||
rethrowWithWebSearchActivateHint(error);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
import { describe, expect, test } from "vite-plus/test";
|
||||
import { BailianError, ExitCode } from "bailian-cli-core";
|
||||
import { MCP_WEBSEARCH_PAGE } from "bailian-cli-runtime";
|
||||
import {
|
||||
isWebSearchMcpNotActivated,
|
||||
rethrowWithWebSearchActivateHint,
|
||||
webSearchActivateHint,
|
||||
} from "../src/commands/search/web-activate-hint.ts";
|
||||
|
||||
describe("web-activate-hint", () => {
|
||||
test("识别 404 + 未开通 / MCP不存在 / MCP_IS_INVALID", () => {
|
||||
expect(
|
||||
isWebSearchMcpNotActivated(
|
||||
new BailianError("MCP request failed: 404 Not Found - MCP不存在或未开通"),
|
||||
),
|
||||
).toBe(true);
|
||||
expect(
|
||||
isWebSearchMcpNotActivated(new BailianError("MCP request failed: 404 - MCP不存在或未开通")),
|
||||
).toBe(true);
|
||||
expect(
|
||||
isWebSearchMcpNotActivated(
|
||||
new BailianError("MCP request failed: 404 Not Found - MCP_IS_INVALID"),
|
||||
),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
test("裸 404 或非 MCP 错误不加开通判定", () => {
|
||||
expect(isWebSearchMcpNotActivated(new BailianError("MCP request failed: 404 Not Found"))).toBe(
|
||||
false,
|
||||
);
|
||||
expect(
|
||||
isWebSearchMcpNotActivated(new BailianError("MCP request failed: 405 Method Not Allowed")),
|
||||
).toBe(false);
|
||||
expect(isWebSearchMcpNotActivated(new Error("MCP不存在或未开通"))).toBe(false);
|
||||
});
|
||||
|
||||
test("hint 含 MCP 广场 WebSearch 深链", () => {
|
||||
expect(webSearchActivateHint()).toContain(MCP_WEBSEARCH_PAGE);
|
||||
expect(webSearchActivateHint()).toMatch(/Activate|re-activate/i);
|
||||
});
|
||||
|
||||
test("rethrow 保留原 message,补 Hint", () => {
|
||||
const original = new BailianError(
|
||||
"MCP request failed: 404 Not Found - MCP不存在或未开通",
|
||||
ExitCode.GENERAL,
|
||||
);
|
||||
try {
|
||||
rethrowWithWebSearchActivateHint(original);
|
||||
expect.unreachable("should throw");
|
||||
} catch (error) {
|
||||
expect(error).toBeInstanceOf(BailianError);
|
||||
const wrapped = error as BailianError;
|
||||
expect(wrapped.message).toBe(original.message);
|
||||
expect(wrapped.exitCode).toBe(ExitCode.GENERAL);
|
||||
expect(wrapped.hint).toContain(MCP_WEBSEARCH_PAGE);
|
||||
expect(wrapped.cause).toBe(original);
|
||||
}
|
||||
});
|
||||
|
||||
test("已有 hint 或非未开通错误原样抛出", () => {
|
||||
const withHint = new BailianError(
|
||||
"MCP request failed: 404 Not Found - MCP不存在或未开通",
|
||||
ExitCode.GENERAL,
|
||||
"already hinted",
|
||||
);
|
||||
try {
|
||||
rethrowWithWebSearchActivateHint(withHint);
|
||||
expect.unreachable("should throw");
|
||||
} catch (error) {
|
||||
expect(error).toBe(withHint);
|
||||
}
|
||||
|
||||
const other = new BailianError("MCP request failed: 401 Unauthorized");
|
||||
try {
|
||||
rethrowWithWebSearchActivateHint(other);
|
||||
expect.unreachable("should throw");
|
||||
} catch (error) {
|
||||
expect(error).toBe(other);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -34,6 +34,7 @@ export {
|
||||
BAILIAN_CONSOLE,
|
||||
API_KEY_PAGE,
|
||||
TOKEN_PLAN_PAGE,
|
||||
MCP_WEBSEARCH_PAGE,
|
||||
VOICE_TTS_PAGE,
|
||||
} from "./urls.ts";
|
||||
|
||||
|
||||
@@ -18,5 +18,11 @@ export const API_KEY_PAGE = `${BAILIAN_CONSOLE}/?tab=app#/api-key`;
|
||||
/** Direct deep link to the Token Plan subscription overview and API key entry. */
|
||||
export const TOKEN_PLAN_PAGE = `${BAILIAN_CONSOLE_ROOT}/cn-beijing?tab=plan#/efm/subscription/overview`;
|
||||
|
||||
/**
|
||||
* MCP marketplace detail for the built-in WebSearch server.
|
||||
* Users must activate (or re-activate for Streamable HTTP) before `search web` works.
|
||||
*/
|
||||
export const MCP_WEBSEARCH_PAGE = `${BAILIAN_CONSOLE}?tab=mcp#/mcp-market/detail/WebSearch`;
|
||||
|
||||
/** Voice TTS experience center — browse system and custom voices. */
|
||||
export const VOICE_TTS_PAGE = "https://help.aliyun.com/zh/model-studio/cosyvoice-voice-list";
|
||||
|
||||
Reference in New Issue
Block a user