@@ -4,6 +4,19 @@ import EventSource from 'eventsource';
44import { logger } from './logger' ;
55import { AntVLibrary } from '../types' ;
66
7+ export type DeepWikiConnectionMode = 'keep-alive' | 'close-after-query' ;
8+
9+ type DeepWikiQueryParams = {
10+ repoName : string ;
11+ question : string ;
12+ connectionMode ?: DeepWikiConnectionMode ;
13+ } ;
14+
15+ type McpConnection = {
16+ client : Client ;
17+ transport : StreamableHTTPClientTransport ;
18+ } ;
19+
720// ---------------------------------------------------------
821// 1. 全局环境配置
922// ---------------------------------------------------------
@@ -21,6 +34,73 @@ let _client: Client | null = null;
2134let _transport : StreamableHTTPClientTransport | null = null ;
2235let _connectingPromise : Promise < Client > | null = null ;
2336
37+ function createMcpConnection ( ) : McpConnection {
38+ const transport = new StreamableHTTPClientTransport (
39+ new URL ( 'https://mcp.deepwiki.com/mcp' ) ,
40+ ) ;
41+
42+ const client = new Client (
43+ {
44+ name : 'deepwiki-node-client' ,
45+ version : '1.0.0' ,
46+ } ,
47+ { capabilities : { } } ,
48+ ) ;
49+
50+ return { client, transport } ;
51+ }
52+
53+ async function connectMcpConnection ( params : {
54+ client : Client ;
55+ transport : StreamableHTTPClientTransport ;
56+ isShared ?: boolean ;
57+ } ) {
58+ const { client, transport, isShared = false } = params ;
59+
60+ transport . onerror = ( err ) => {
61+ logger . error ( 'DeepWiki MCP Transport Error:' , err ) ;
62+ if ( isShared ) {
63+ resetClient ( ) ;
64+ }
65+ } ;
66+
67+ transport . onclose = ( ) => {
68+ logger . info ( 'DeepWiki MCP Connection Closed' ) ;
69+ if ( isShared ) {
70+ resetClient ( ) ;
71+ }
72+ } ;
73+
74+ await client . connect ( transport ) ;
75+ }
76+
77+ async function closeMcpConnection ( params : {
78+ client ?: Client | null ;
79+ transport ?: StreamableHTTPClientTransport | null ;
80+ isShared ?: boolean ;
81+ } ) {
82+ const { client, transport, isShared = false } = params ;
83+
84+ if ( ! client && ! transport ) {
85+ return ;
86+ }
87+
88+ try {
89+ await client ?. close ( ) ;
90+ } catch ( error ) {
91+ logger . error ( 'DeepWiki MCP Client Close Error:' , error ) ;
92+ try {
93+ await transport ?. close ( ) ;
94+ } catch ( transportError ) {
95+ logger . error ( 'DeepWiki MCP Transport Close Error:' , transportError ) ;
96+ }
97+ } finally {
98+ if ( isShared ) {
99+ resetClient ( ) ;
100+ }
101+ }
102+ }
103+
24104/**
25105 * 获取或初始化 MCP 客户端 (单例模式)
26106 * 包含防止并发重复连接的锁机制
@@ -42,31 +122,8 @@ async function getMcpClient(): Promise<Client> {
42122 _connectingPromise = ( async ( ) => {
43123 try {
44124 logger . info ( 'DeepWiki MCP: 初始化连接...' ) ;
45-
46- const transport = new StreamableHTTPClientTransport (
47- new URL ( 'https://mcp.deepwiki.com/mcp' ) ,
48- ) ;
49-
50- const client = new Client (
51- {
52- name : 'deepwiki-node-client' ,
53- version : '1.0.0' ,
54- } ,
55- { capabilities : { } } ,
56- ) ;
57-
58- // 错误处理:监听传输层关闭或错误,以便下次重连
59- transport . onerror = ( err ) => {
60- logger . error ( 'DeepWiki MCP Transport Error:' , err ) ;
61- resetClient ( ) ; // 出错时重置,下次调用会触发重连
62- } ;
63-
64- transport . onclose = ( ) => {
65- logger . info ( 'DeepWiki MCP Connection Closed' ) ;
66- resetClient ( ) ;
67- } ;
68-
69- await client . connect ( transport ) ;
125+ const { client, transport } = createMcpConnection ( ) ;
126+ await connectMcpConnection ( { client, transport, isShared : true } ) ;
70127
71128 // 连接成功,赋值给模块级变量
72129 _client = client ;
@@ -106,22 +163,36 @@ function resetClient() {
106163export async function queryDeepWiki ( _params : {
107164 repoName : string ;
108165 question : string ;
166+ connectionMode ?: DeepWikiConnectionMode ;
109167} ) : Promise < string > {
168+ let localConnection : McpConnection | null = null ;
110169 try {
111- const params = {
170+ const params : DeepWikiQueryParams = {
112171 ..._params ,
113172 } ;
114173 if ( ! params . repoName . includes ( '/' ) ) {
115174 params . repoName = getRepoName ( params . repoName as AntVLibrary ) ;
116175 }
117176
118- // 获取单例客户端 (自动处理连接)
119- const client = await getMcpClient ( ) ;
177+ let client : Client ;
178+ if ( params . connectionMode === 'close-after-query' ) {
179+ logger . info ( 'DeepWiki MCP: 初始化一次性连接...' ) ;
180+ localConnection = createMcpConnection ( ) ;
181+ await connectMcpConnection ( localConnection ) ;
182+ client = localConnection . client ;
183+ logger . info ( 'DeepWiki MCP: 一次性连接成功' ) ;
184+ } else {
185+ // 获取单例客户端 (自动处理连接)
186+ client = await getMcpClient ( ) ;
187+ }
120188
121189 // 调用工具
122190 const result : any = await client . callTool ( {
123191 name : 'ask_question' ,
124- arguments : params ,
192+ arguments : {
193+ repoName : params . repoName ,
194+ question : params . question ,
195+ } ,
125196 } ) ;
126197
127198 // ---------------------------------------------------------
@@ -159,12 +230,18 @@ export async function queryDeepWiki(_params: {
159230 // resetClient();
160231 }
161232 throw error ;
233+ } finally {
234+ if ( localConnection ) {
235+ logger . info ( 'DeepWiki MCP: 关闭一次性连接...' ) ;
236+ await closeMcpConnection ( localConnection ) ;
237+ }
162238 }
163239}
164240
165241export async function adaptedQueryDeepWiki ( _params : {
166242 repoName : string ;
167243 question : string ;
244+ connectionMode ?: DeepWikiConnectionMode ;
168245} ) {
169246 try {
170247 const answer = await queryDeepWiki ( _params ) ;
@@ -182,16 +259,13 @@ export async function adaptedQueryDeepWiki(_params: {
182259 * 供 EggJS 应用在 app.beforeClose 时调用
183260 */
184261export async function closeDeepWikiConnection ( ) {
185- if ( _transport ) {
262+ if ( _client || _transport ) {
186263 logger . info ( 'DeepWiki MCP: 关闭连接...' ) ;
187- // SDK 目前可能没有直接暴露 close 方法,通常关闭 transport 即可
188- // StreamableHTTPClientTransport 内部并没有显式的 close 方法暴露出来,
189- // 但我们可以将引用置空,让 GC 回收,或者依赖进程退出
190- // 如果 SSEClientTransport 实现了 close,则调用:
191- // await _transport.close();
192-
193- // 目前 SDK 版本可以直接置空
194- resetClient ( ) ;
264+ await closeMcpConnection ( {
265+ client : _client ,
266+ transport : _transport ,
267+ isShared : true ,
268+ } ) ;
195269 }
196270}
197271
0 commit comments