Inherit DSH process proxy for search-mcp HTTP egress.

Use proxyRouteFor when a policy is installed so HTTPS_PROXY/system proxy is not bypassed by the DNS-pinned Agent; keep the pinned direct path otherwise.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-10-10 12:04:38 +08:00
parent 229a36d5f3
commit 26a8c2ecee
5 changed files with 93 additions and 15 deletions

View file

@ -12,7 +12,7 @@ test('package exports resolve and peerDependencies stay open', async () => {
assert.equal(pkg.exports['.'], './lib/index.js')
assert.equal(pkg.exports['./client'], './lib/client.browser.js')
assert.equal(pkg.engines.node, '>=20')
assert.equal(pkg.version, '0.2.39')
assert.equal(pkg.version, '0.2.40')
assert.equal(pkg.dsh.client.immediately, false)
assert.equal(pkg.dependencies['@modelcontextprotocol/client'], '2.0.0')
assert.ok(!Object.hasOwn(pkg.dependencies, '@modelcontextprotocol/sdk'))
@ -20,6 +20,7 @@ test('package exports resolve and peerDependencies stay open', async () => {
for (const name of [
'@deepseek-ai/dsh-api-remotes',
'@deepseek-ai/dsh-credentials',
'@deepseek-ai/dsh-http-proxy',
'@deepseek-ai/dsh-launch-environment',
'@deepseek-ai/dsh-settings',
'@deepseek-ai/dsh-web',
@ -85,13 +86,16 @@ test('known providers are CDKey-only while custom keeps advanced fields', async
assert.match(client, /已知提供商不需要填写端点链接/)
})
test('HTTP transport pins DNS and applies one guarded fetch to every SDK request', async () => {
test('HTTP transport inherits DSH proxy and pins DNS on the direct path', async () => {
const transport = await read('lib/client.js')
assert.match(transport, /from '@modelcontextprotocol\/client'/)
assert.match(transport, /from '@modelcontextprotocol\/client\/stdio'/)
assert.doesNotMatch(transport, /from ['"]@modelcontextprotocol\/sdk/)
assert.match(transport, /@deepseek-ai\/dsh-http-proxy/)
assert.match(transport, /proxyRouteFor/)
assert.match(transport, /parseHttpEndpoint/)
assert.match(transport, /validateHttpEndpoint\(server\.url, \{ signal \}\)/)
assert.match(transport, /dispatcher: agent/)
assert.match(transport, /dispatcher/)
assert.match(transport, /redirect: 'error'/)
assert.match(transport, /versionNegotiation:\s*\{\s*mode:\s*['"]auto['"]/)
})

View file

@ -3,7 +3,9 @@ import assert from 'node:assert/strict';
import {
createPinnedLookup,
isAllowedEndpointAddress,
isNonPublicIpLiteral,
isPublicAddress,
parseHttpEndpoint,
validateHttpEndpoint,
} from '../lib/url-policy.js';
@ -12,6 +14,19 @@ const lookup = (records) => (_hostname, options, callback) => {
queueMicrotask(() => callback(null, records));
};
test('parseHttpEndpoint accepts structure without DNS', () => {
const parsed = parseHttpEndpoint('https://search.example/mcp');
assert.equal(parsed.hostname, 'search.example');
assert.equal(parsed.url.protocol, 'https:');
});
test('isNonPublicIpLiteral gates loopback and private literals', () => {
assert.equal(isNonPublicIpLiteral('127.0.0.1'), true);
assert.equal(isNonPublicIpLiteral('10.0.0.1'), true);
assert.equal(isNonPublicIpLiteral('8.8.8.8'), false);
assert.equal(isNonPublicIpLiteral('search.example'), false);
});
test('URL policy accepts HTTP(S) with public DNS only', async () => {
const result = await validateHttpEndpoint('https://search.example/mcp', {
lookup: lookup([