Fix Desktop search-mcp takeover so web_search uses Bailian.

Drop nested Config.volatile that left the fiber inactive, unwrap the cordis
web proxy to pin/wrap search at runtime, and restore deepseek on disable.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-10-10 09:05:00 +08:00
parent d729b03f70
commit 23685cd545
8 changed files with 343 additions and 72 deletions

View file

@ -0,0 +1,73 @@
/**
* Reproduce Desktop Config validation for search-mcp.
* Usage: node scripts/_validate-config.mjs
*/
import { createRequire } from 'node:module'
import { pathToFileURL } from 'node:url'
import { join, dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
const pkgRoot = join(dirname(fileURLToPath(import.meta.url)), '..')
const nm = join(process.env.USERPROFILE || '', '.dsh', 'profiles', 'node_modules')
const req = createRequire(join(nm, 'package.json'))
const cordisUrl = pathToFileURL(req.resolve('@deepseek-ai/cordis')).href
const { Context, Service } = await import(cordisUrl)
const mod = await import(pathToFileURL(join(pkgRoot, 'lib', 'index.js')).href)
class WebRuntime extends Service {
searchProviders = new Map()
searchProviderId = 'deepseek-official'
constructor(ctx) {
super(ctx, 'web')
}
registerSearchProvider(p) {
this.searchProviders.set(p.id, p)
return () => this.searchProviders.delete(p.id)
}
async search() {
return { used: this.searchProviderId }
}
}
function plugin(ctx, config) {
mod.apply(ctx, config)
}
plugin.inject = ['web']
plugin.Config = mod.Config
const root = new Context()
try {
await root.plugin(WebRuntime)
await root.plugin(plugin, {
defaultServer: 'bailian',
maxResults: 8,
searchTimeoutMs: 30000,
servers: [{ id: 'bailian', kind: 'bailian', apiKeyEnv: 'DASHSCOPE_API_KEY' }],
})
const raw = root.web[Symbol.for('cordis.original')]
console.log(
JSON.stringify(
{
ok: true,
faceId: root.web.searchProviderId,
rawId: raw?.searchProviderId,
wrapped: !!raw?.search?.__searchMcpWrapped,
},
null,
2,
),
)
} catch (e) {
console.log(
JSON.stringify(
{
ok: false,
message: String(e?.message || e),
stack: String(e?.stack || '').slice(0, 1200),
},
null,
2,
),
)
process.exit(1)
}

View file

@ -0,0 +1,78 @@
/**
* Prove unwrap + direct web.search wrap routes off deepseek-official.
* Run: node scripts/_verify-takeover.mjs
*/
import { createRequire } from 'node:module'
import { dirname, join } from 'node:path'
import { fileURLToPath, pathToFileURL } from 'node:url'
const pkgRoot = join(dirname(fileURLToPath(import.meta.url)), '..')
const profilesNm = join(process.env.USERPROFILE || '', '.dsh', 'profiles', 'node_modules')
const require = createRequire(join(profilesNm, 'package.json'))
const { Context, Service } = require('@deepseek-ai/cordis')
const { apply } = await import(pathToFileURL(join(pkgRoot, 'lib', 'index.js')).href)
const ORIGINAL = Symbol.for('cordis.original')
class WebRuntime extends Service {
searchProviders = new Map()
searchProviderId
constructor(ctx, config = {}) {
super(ctx, 'web')
this.searchProviderId = config.searchProvider ?? 'deepseek-official'
}
registerSearchProvider(p) {
this.searchProviders.set(p.id, p)
return () => this.searchProviders.delete(p.id)
}
async search() {
if (this.searchProviderId === 'deepseek-official') {
return { used: 'deepseek-official', error: 'DeepSeek API error (HTTP 401)' }
}
const p = this.searchProviders.get(this.searchProviderId)
return { used: this.searchProviderId, via: p?.id }
}
}
const smcp = Object.assign(function (ctx) {
apply(ctx, {
defaultServer: 'bailian',
maxResults: 8,
searchTimeoutMs: 30000,
servers: [{ id: 'bailian', kind: 'bailian', apiKeyEnv: 'DASHSCOPE_API_KEY' }],
})
}, { inject: ['web'] })
const app = new Context()
await app.plugin(WebRuntime, { searchProvider: 'deepseek-official' })
const fiber = await app.plugin(smcp)
const raw = app.web[ORIGINAL]
let thrown
try {
await app.web.search({ query: 'venezuela', maxResults: 5 })
} catch (error) {
thrown = String(error?.message || error)
}
const enabledOk =
raw.searchProviderId === 'search-mcp'
&& raw.search.__searchMcpWrapped === true
&& /search-mcp/.test(thrown || '')
await fiber.dispose()
const afterDisable = await app.web.search({ query: 'venezuela', maxResults: 5 })
const disabledOk =
raw.searchProviderId === 'deepseek-official'
&& !raw.search.__searchMcpWrapped
&& afterDisable?.used === 'deepseek-official'
const ok = enabledOk && disabledOk
console.log(JSON.stringify({
ok,
enabledOk,
disabledOk,
thrown: thrown?.slice(0, 160),
afterDisable,
rawId: raw.searchProviderId,
}, null, 2))
process.exit(ok ? 0 : 1)