lib/mcp-network.js
Rama main · réplica de lectura del repositorio original.
// Remote MCP must not turn a shared Clay host into a private-network proxy.
var dns = require('dns').promises;
var ipaddr = require('ipaddr.js');
var undici = require('undici');
function publicAddress(address) {
try { return ipaddr.process(address).range() === 'unicast'; } catch (error) { return false; }
}
function remoteUrl(value) {
if (typeof value !== 'string' || value.length > 4096) throw new Error('Enter a valid HTTPS MCP address.');
var url;
try { url = new URL(value.trim()); } catch (error) { throw new Error('Enter a valid HTTPS MCP address.'); }
if (url.protocol !== 'https:' || url.username || url.password || url.hash) throw new Error('Use an HTTPS address without embedded credentials or a fragment.');
var hostname = url.hostname.replace(/^\[|\]$/g, '');
if (hostname === 'localhost' || hostname.endsWith('.localhost') || (ipaddr.isValid(hostname) && !publicAddress(hostname))) {
throw new Error('Remote connections require a public server. Use the extension for tools on your computer.');
}
return url;
}
async function publicLookup(hostname) {
var records = await dns.lookup(hostname, { all: true, verbatim: true });
if (!records.length || records.some(function (item) { return !publicAddress(item.address); })) throw new Error('The server address resolves to a private or reserved network.');
return records;
}
async function assertPublicUrl(value) {
var url = remoteUrl(String(value));
await publicLookup(url.hostname.replace(/^\[|\]$/g, ''));
return url;
}
function createRemoteFetch() {
var agent = new undici.Agent({ connect: { timeout: 15000, lookup: function (host, options, callback) {
publicLookup(host).then(function (records) {
var matching = records.filter(function (item) { return !options.family || item.family === options.family; });
if (!matching.length) return callback(new Error('No public address is available.'));
if (options.all) callback(null, matching); else callback(null, matching[0].address, matching[0].family);
}, callback);
} } });
async function request(input, init) {
var url = await assertPublicUrl(typeof input === 'string' || input instanceof URL ? input : input.url);
// No redirect may forward credentials to a different endpoint.
return undici.fetch(url, Object.assign({}, init, { dispatcher: agent, redirect: 'error',
signal: init && init.signal && typeof AbortSignal.any !== 'function' ? init.signal : (init && init.signal ? AbortSignal.any([init.signal, AbortSignal.timeout(60000)]) : AbortSignal.timeout(60000)) }));
}
return { fetch: request, close: function () { return agent.destroy(); } };
}
module.exports = { remoteUrl: remoteUrl, publicAddress: publicAddress, publicLookup: publicLookup, assertPublicUrl: assertPublicUrl, createRemoteFetch: createRemoteFetch };