Files
SnowLuma/.pnpm-store/v11/files/fd/1661af02eceafdeff82fdc90d7b13682be45b3f6aa344d3144042d757fcde27edcee6c3ee941c87a703e2cd3e357dfde4eea3b9e5d90edc022a90d34c176e8
T
TanTan 2a69b23b47
Dev Build / cleanup (push) Waiting to run
Dev Build / typecheck (push) Waiting to run
docs: 仓库链接指向本镜像仓库
2026-11-13 09:27:07 +08:00

1 line
14 KiB
Plaintext

{"version":3,"file":"stdio.cjs","names":["process","env: Record<string, string>","ReadBuffer","PassThrough","SdkError","SdkErrorCode","serializeMessage"],"sources":["../src/client/stdio.ts"],"sourcesContent":["import type { ChildProcess, IOType } from 'node:child_process';\nimport process from 'node:process';\nimport type { Stream } from 'node:stream';\nimport { PassThrough } from 'node:stream';\n\nimport type { JSONRPCMessage, Transport } from '@modelcontextprotocol/core-internal';\nimport { ReadBuffer, SdkError, SdkErrorCode, serializeMessage } from '@modelcontextprotocol/core-internal';\nimport spawn from 'cross-spawn';\n\nexport type StdioServerParameters = {\n /**\n * The executable to run to start the server.\n */\n command: string;\n\n /**\n * Command line arguments to pass to the executable.\n */\n args?: string[];\n\n /**\n * The environment to use when spawning the process.\n *\n * If not specified, the result of {@linkcode getDefaultEnvironment} will be used.\n */\n env?: Record<string, string>;\n\n /**\n * How to handle stderr of the child process. This matches the semantics of Node's `child_process.spawn`.\n *\n * The default is `\"inherit\"`, meaning messages to stderr will be printed to the parent process's stderr.\n */\n stderr?: IOType | Stream | number;\n\n /**\n * The working directory to use when spawning the process.\n *\n * If not specified, the current working directory will be inherited.\n */\n cwd?: string;\n\n /**\n * Maximum size of the read buffer in bytes. If a single message exceeds\n * this size the transport will emit an error and close.\n *\n * Defaults to 10 MB.\n */\n maxBufferSize?: number;\n};\n\n/**\n * Environment variables to inherit by default, if an environment is not explicitly given.\n */\nexport const DEFAULT_INHERITED_ENV_VARS =\n process.platform === 'win32'\n ? [\n 'APPDATA',\n 'COMSPEC',\n 'HOMEDRIVE',\n 'HOMEPATH',\n 'LOCALAPPDATA',\n 'PATH',\n 'PATHEXT',\n 'PROCESSOR_ARCHITECTURE',\n 'PROGRAMDATA',\n 'PROGRAMFILES',\n 'PROGRAMFILES(X86)',\n 'PROGRAMW6432',\n 'SYSTEMDRIVE',\n 'SYSTEMROOT',\n 'TEMP',\n 'USERNAME',\n 'USERPROFILE',\n 'WINDIR'\n ]\n : /* list inspired by the default env inheritance of sudo */\n ['HOME', 'LOGNAME', 'PATH', 'SHELL', 'TERM', 'USER'];\n\n/**\n * Returns a default environment object including only environment variables deemed safe to inherit.\n */\nexport function getDefaultEnvironment(): Record<string, string> {\n const env: Record<string, string> = {};\n\n for (const key of DEFAULT_INHERITED_ENV_VARS) {\n const value = process.env[key];\n if (value === undefined) {\n continue;\n }\n\n if (value.startsWith('()')) {\n // Skip functions, which are a security risk.\n continue;\n }\n\n env[key] = value;\n }\n\n return env;\n}\n\n/**\n * Client transport for stdio: this will connect to a server by spawning a process and communicating with it over stdin/stdout.\n *\n * This transport is only available in Node.js environments.\n */\nexport class StdioClientTransport implements Transport {\n private _process?: ChildProcess;\n private _readBuffer: ReadBuffer;\n private _serverParams: StdioServerParameters;\n private _stderrStream: PassThrough | null = null;\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n\n constructor(server: StdioServerParameters) {\n this._serverParams = server;\n this._readBuffer = new ReadBuffer({ maxBufferSize: server.maxBufferSize });\n if (server.stderr === 'pipe' || server.stderr === 'overlapped') {\n this._stderrStream = new PassThrough();\n }\n }\n\n /**\n * Starts the server process and prepares to communicate with it.\n */\n async start(): Promise<void> {\n if (this._process) {\n throw new Error(\n 'StdioClientTransport already started! If using Client class, note that connect() calls start() automatically.'\n );\n }\n\n return new Promise((resolve, reject) => {\n this._process = spawn(this._serverParams.command, this._serverParams.args ?? [], {\n // merge default env with server env because mcp server needs some env vars\n env: {\n ...getDefaultEnvironment(),\n ...this._serverParams.env\n },\n stdio: ['pipe', 'pipe', this._serverParams.stderr ?? 'inherit'],\n shell: false,\n windowsHide: process.platform === 'win32',\n cwd: this._serverParams.cwd\n });\n\n this._process.on('error', error => {\n reject(error);\n this.onerror?.(error);\n });\n\n this._process.on('spawn', () => {\n resolve();\n });\n\n this._process.on('close', _code => {\n this._process = undefined;\n this.onclose?.();\n });\n\n this._process.stdin?.on('error', error => {\n this.onerror?.(error);\n });\n\n this._process.stdout?.on('data', chunk => {\n try {\n this._readBuffer.append(chunk);\n this.processReadBuffer();\n } catch (error) {\n this.onerror?.(error as Error);\n this.close().catch(() => {});\n }\n });\n\n this._process.stdout?.on('error', error => {\n this.onerror?.(error);\n });\n\n if (this._stderrStream && this._process.stderr) {\n this._process.stderr.pipe(this._stderrStream);\n }\n });\n }\n\n /**\n * The `stderr` stream of the child process, if {@linkcode StdioServerParameters.stderr} was set to `\"pipe\"` or `\"overlapped\"`.\n *\n * If `stderr` piping was requested, a `PassThrough` stream is returned _immediately_, allowing callers to\n * attach listeners before the `start` method is invoked. This prevents loss of any early\n * error output emitted by the child process.\n */\n get stderr(): Stream | null {\n if (this._stderrStream) {\n return this._stderrStream;\n }\n\n return this._process?.stderr ?? null;\n }\n\n /**\n * The child process pid spawned by this transport.\n *\n * This is only available after the transport has been started.\n */\n get pid(): number | null {\n return this._process?.pid ?? null;\n }\n\n private processReadBuffer() {\n while (true) {\n try {\n const message = this._readBuffer.readMessage();\n if (message === null) {\n break;\n }\n\n this.onmessage?.(message);\n } catch (error) {\n this.onerror?.(error as Error);\n }\n }\n }\n\n /**\n * Reap a disposable probe sibling (see the version-negotiation sibling\n * flow): signal-first teardown awaiting process `exit` — never the `close`\n * event, so a helper process holding the child's stdio pipes can never\n * block disposal. Not part of the public transport lifecycle.\n *\n * @internal\n */\n private async _dispose(): Promise<void> {\n const proc = this._process;\n this._process = undefined;\n if (proc && proc.exitCode === null && proc.signalCode === null) {\n const exited = new Promise<void>(resolve => proc.once('exit', () => resolve()));\n try {\n proc.stdin?.end();\n } catch {\n // ignore\n }\n try {\n proc.kill('SIGTERM');\n } catch {\n // ignore\n }\n await Promise.race([exited, new Promise(resolve => setTimeout(resolve, 1000).unref())]);\n if (proc.exitCode === null && proc.signalCode === null) {\n try {\n proc.kill('SIGKILL');\n } catch {\n // ignore\n }\n }\n await exited;\n }\n // The child is gone — release the PARENT-side pipe handles too. A helper\n // process holding the inherited write ends would otherwise keep them (and\n // with them the host's event loop: stdout carries a flowing 'data'\n // listener from start()) alive until the helper exits.\n try {\n proc?.stdout?.destroy();\n } catch {\n // ignore\n }\n try {\n proc?.stdin?.destroy();\n } catch {\n // ignore\n }\n try {\n proc?.stderr?.destroy();\n } catch {\n // ignore\n }\n this._readBuffer.clear();\n }\n\n async close(): Promise<void> {\n if (this._process) {\n const processToClose = this._process;\n this._process = undefined;\n\n const closePromise = new Promise<void>(resolve => {\n processToClose.once('close', () => {\n resolve();\n });\n });\n\n try {\n processToClose.stdin?.end();\n } catch {\n // ignore\n }\n\n await Promise.race([closePromise, new Promise(resolve => setTimeout(resolve, 2000).unref())]);\n\n if (processToClose.exitCode === null) {\n try {\n processToClose.kill('SIGTERM');\n } catch {\n // ignore\n }\n\n await Promise.race([closePromise, new Promise(resolve => setTimeout(resolve, 2000).unref())]);\n }\n\n if (processToClose.exitCode === null) {\n try {\n processToClose.kill('SIGKILL');\n } catch {\n // ignore\n }\n }\n }\n\n this._readBuffer.clear();\n }\n\n send(message: JSONRPCMessage): Promise<void> {\n return new Promise(resolve => {\n if (!this._process?.stdin) {\n throw new SdkError(SdkErrorCode.NotConnected, 'Not connected');\n }\n\n const json = serializeMessage(message);\n if (this._process.stdin.write(json)) {\n resolve();\n } else {\n this._process.stdin.once('drain', resolve);\n }\n });\n }\n}\n"],"mappings":";;;;;;;;;;;;AAqDA,MAAa,6BACTA,qBAAQ,aAAa,UACf;CACI;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACH,GAED;CAAC;CAAQ;CAAW;CAAQ;CAAS;CAAQ;CAAO;;;;AAK9D,SAAgB,wBAAgD;CAC5D,MAAMC,MAA8B,EAAE;AAEtC,MAAK,MAAM,OAAO,4BAA4B;EAC1C,MAAM,QAAQD,qBAAQ,IAAI;AAC1B,MAAI,UAAU,OACV;AAGJ,MAAI,MAAM,WAAW,KAAK,CAEtB;AAGJ,MAAI,OAAO;;AAGf,QAAO;;;;;;;AAQX,IAAa,uBAAb,MAAuD;CACnD,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ,gBAAoC;CAE5C;CACA;CACA;CAEA,YAAY,QAA+B;AACvC,OAAK,gBAAgB;AACrB,OAAK,cAAc,IAAIE,uBAAW,EAAE,eAAe,OAAO,eAAe,CAAC;AAC1E,MAAI,OAAO,WAAW,UAAU,OAAO,WAAW,aAC9C,MAAK,gBAAgB,IAAIC,yBAAa;;;;;CAO9C,MAAM,QAAuB;AACzB,MAAI,KAAK,SACL,OAAM,IAAI,MACN,gHACH;AAGL,SAAO,IAAI,SAAS,SAAS,WAAW;AACpC,QAAK,oCAAiB,KAAK,cAAc,SAAS,KAAK,cAAc,QAAQ,EAAE,EAAE;IAE7E,KAAK;KACD,GAAG,uBAAuB;KAC1B,GAAG,KAAK,cAAc;KACzB;IACD,OAAO;KAAC;KAAQ;KAAQ,KAAK,cAAc,UAAU;KAAU;IAC/D,OAAO;IACP,aAAaH,qBAAQ,aAAa;IAClC,KAAK,KAAK,cAAc;IAC3B,CAAC;AAEF,QAAK,SAAS,GAAG,UAAS,UAAS;AAC/B,WAAO,MAAM;AACb,SAAK,UAAU,MAAM;KACvB;AAEF,QAAK,SAAS,GAAG,eAAe;AAC5B,aAAS;KACX;AAEF,QAAK,SAAS,GAAG,UAAS,UAAS;AAC/B,SAAK,WAAW;AAChB,SAAK,WAAW;KAClB;AAEF,QAAK,SAAS,OAAO,GAAG,UAAS,UAAS;AACtC,SAAK,UAAU,MAAM;KACvB;AAEF,QAAK,SAAS,QAAQ,GAAG,SAAQ,UAAS;AACtC,QAAI;AACA,UAAK,YAAY,OAAO,MAAM;AAC9B,UAAK,mBAAmB;aACnB,OAAO;AACZ,UAAK,UAAU,MAAe;AAC9B,UAAK,OAAO,CAAC,YAAY,GAAG;;KAElC;AAEF,QAAK,SAAS,QAAQ,GAAG,UAAS,UAAS;AACvC,SAAK,UAAU,MAAM;KACvB;AAEF,OAAI,KAAK,iBAAiB,KAAK,SAAS,OACpC,MAAK,SAAS,OAAO,KAAK,KAAK,cAAc;IAEnD;;;;;;;;;CAUN,IAAI,SAAwB;AACxB,MAAI,KAAK,cACL,QAAO,KAAK;AAGhB,SAAO,KAAK,UAAU,UAAU;;;;;;;CAQpC,IAAI,MAAqB;AACrB,SAAO,KAAK,UAAU,OAAO;;CAGjC,AAAQ,oBAAoB;AACxB,SAAO,KACH,KAAI;GACA,MAAM,UAAU,KAAK,YAAY,aAAa;AAC9C,OAAI,YAAY,KACZ;AAGJ,QAAK,YAAY,QAAQ;WACpB,OAAO;AACZ,QAAK,UAAU,MAAe;;;;;;;;;;;CAa1C,MAAc,WAA0B;EACpC,MAAM,OAAO,KAAK;AAClB,OAAK,WAAW;AAChB,MAAI,QAAQ,KAAK,aAAa,QAAQ,KAAK,eAAe,MAAM;GAC5D,MAAM,SAAS,IAAI,SAAc,YAAW,KAAK,KAAK,cAAc,SAAS,CAAC,CAAC;AAC/E,OAAI;AACA,SAAK,OAAO,KAAK;WACb;AAGR,OAAI;AACA,SAAK,KAAK,UAAU;WAChB;AAGR,SAAM,QAAQ,KAAK,CAAC,QAAQ,IAAI,SAAQ,YAAW,WAAW,SAAS,IAAK,CAAC,OAAO,CAAC,CAAC,CAAC;AACvF,OAAI,KAAK,aAAa,QAAQ,KAAK,eAAe,KAC9C,KAAI;AACA,SAAK,KAAK,UAAU;WAChB;AAIZ,SAAM;;AAMV,MAAI;AACA,SAAM,QAAQ,SAAS;UACnB;AAGR,MAAI;AACA,SAAM,OAAO,SAAS;UAClB;AAGR,MAAI;AACA,SAAM,QAAQ,SAAS;UACnB;AAGR,OAAK,YAAY,OAAO;;CAG5B,MAAM,QAAuB;AACzB,MAAI,KAAK,UAAU;GACf,MAAM,iBAAiB,KAAK;AAC5B,QAAK,WAAW;GAEhB,MAAM,eAAe,IAAI,SAAc,YAAW;AAC9C,mBAAe,KAAK,eAAe;AAC/B,cAAS;MACX;KACJ;AAEF,OAAI;AACA,mBAAe,OAAO,KAAK;WACvB;AAIR,SAAM,QAAQ,KAAK,CAAC,cAAc,IAAI,SAAQ,YAAW,WAAW,SAAS,IAAK,CAAC,OAAO,CAAC,CAAC,CAAC;AAE7F,OAAI,eAAe,aAAa,MAAM;AAClC,QAAI;AACA,oBAAe,KAAK,UAAU;YAC1B;AAIR,UAAM,QAAQ,KAAK,CAAC,cAAc,IAAI,SAAQ,YAAW,WAAW,SAAS,IAAK,CAAC,OAAO,CAAC,CAAC,CAAC;;AAGjG,OAAI,eAAe,aAAa,KAC5B,KAAI;AACA,mBAAe,KAAK,UAAU;WAC1B;;AAMhB,OAAK,YAAY,OAAO;;CAG5B,KAAK,SAAwC;AACzC,SAAO,IAAI,SAAQ,YAAW;AAC1B,OAAI,CAAC,KAAK,UAAU,MAChB,OAAM,IAAII,qBAASC,yBAAa,cAAc,gBAAgB;GAGlE,MAAM,OAAOC,6BAAiB,QAAQ;AACtC,OAAI,KAAK,SAAS,MAAM,MAAM,KAAK,CAC/B,UAAS;OAET,MAAK,SAAS,MAAM,KAAK,SAAS,QAAQ;IAEhD"}