@@ -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,77 @@ 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+ if ( client ) {
90+ await client . close ( ) ;
91+ } else {
92+ await transport ?. close ( ) ;
93+ }
94+ } catch ( error ) {
95+ logger . error ( 'DeepWiki MCP Client Close Error:' , error ) ;
96+ try {
97+ await transport ?. close ( ) ;
98+ } catch ( transportError ) {
99+ logger . error ( 'DeepWiki MCP Transport Close Error:' , transportError ) ;
100+ }
101+ } finally {
102+ if ( isShared ) {
103+ resetClient ( ) ;
104+ }
105+ }
106+ }
107+
24108/**
25109 * 获取或初始化 MCP 客户端 (单例模式)
26110 * 包含防止并发重复连接的锁机制
@@ -40,42 +124,21 @@ async function getMcpClient(): Promise<Client> {
40124
41125 // 开始初始化连接
42126 _connectingPromise = ( async ( ) => {
127+ let connection : McpConnection | null = null ;
43128 try {
44129 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 ) ;
130+ connection = createMcpConnection ( ) ;
131+ await connectMcpConnection ( { ...connection , isShared : true } ) ;
70132
71133 // 连接成功,赋值给模块级变量
72- _client = client ;
73- _transport = transport ;
134+ _client = connection . client ;
135+ _transport = connection . transport ;
74136 logger . info ( 'DeepWiki MCP: 连接成功' ) ;
75137
76- return client ;
138+ return connection . client ;
77139 } catch ( error ) {
78140 logger . error ( 'DeepWiki MCP: 连接失败' , error ) ;
141+ await closeMcpConnection ( connection ?? { } ) ;
79142 resetClient ( ) ;
80143 throw error ;
81144 } finally {
@@ -106,22 +169,36 @@ function resetClient() {
106169export async function queryDeepWiki ( _params : {
107170 repoName : string ;
108171 question : string ;
172+ connectionMode ?: DeepWikiConnectionMode ;
109173} ) : Promise < string > {
174+ let localConnection : McpConnection | null = null ;
110175 try {
111- const params = {
176+ const params : DeepWikiQueryParams = {
112177 ..._params ,
113178 } ;
114179 if ( ! params . repoName . includes ( '/' ) ) {
115180 params . repoName = getRepoName ( params . repoName as AntVLibrary ) ;
116181 }
117182
118- // 获取单例客户端 (自动处理连接)
119- const client = await getMcpClient ( ) ;
183+ let client : Client ;
184+ if ( params . connectionMode === 'close-after-query' ) {
185+ logger . info ( 'DeepWiki MCP: 初始化一次性连接...' ) ;
186+ localConnection = createMcpConnection ( ) ;
187+ await connectMcpConnection ( localConnection ) ;
188+ client = localConnection . client ;
189+ logger . info ( 'DeepWiki MCP: 一次性连接成功' ) ;
190+ } else {
191+ // 获取单例客户端 (自动处理连接)
192+ client = await getMcpClient ( ) ;
193+ }
120194
121195 // 调用工具
122196 const result : any = await client . callTool ( {
123197 name : 'ask_question' ,
124- arguments : params ,
198+ arguments : {
199+ repoName : params . repoName ,
200+ question : params . question ,
201+ } ,
125202 } ) ;
126203
127204 // ---------------------------------------------------------
@@ -134,6 +211,10 @@ export async function queryDeepWiki(_params: {
134211 . map ( ( item : any ) => item . text )
135212 . join ( '\n' ) ; // 如果有多段文本,用换行符拼接
136213
214+ if ( result . isError ) {
215+ throw new Error ( 'DeepWiki Tool Error, Answer = ' + answer ) ;
216+ }
217+
137218 const regex =
138219 / W i k i p a g e s y o u m i g h t w a n t t o e x p l o r e : | V i e w t h i s s e a r c h o n D e e p W i k i : / i;
139220 const splitIndex = answer . search ( regex ) ;
@@ -159,12 +240,18 @@ export async function queryDeepWiki(_params: {
159240 // resetClient();
160241 }
161242 throw error ;
243+ } finally {
244+ if ( localConnection ) {
245+ logger . info ( 'DeepWiki MCP: 关闭一次性连接...' ) ;
246+ await closeMcpConnection ( localConnection ) ;
247+ }
162248 }
163249}
164250
165251export async function adaptedQueryDeepWiki ( _params : {
166252 repoName : string ;
167253 question : string ;
254+ connectionMode ?: DeepWikiConnectionMode ;
168255} ) {
169256 try {
170257 const answer = await queryDeepWiki ( _params ) ;
@@ -182,16 +269,17 @@ export async function adaptedQueryDeepWiki(_params: {
182269 * 供 EggJS 应用在 app.beforeClose 时调用
183270 */
184271export async function closeDeepWikiConnection ( ) {
185- if ( _transport ) {
272+ if ( _connectingPromise ) {
273+ await _connectingPromise . catch ( ( ) => { } ) ;
274+ }
275+
276+ if ( _client || _transport ) {
186277 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 ( ) ;
278+ await closeMcpConnection ( {
279+ client : _client ,
280+ transport : _transport ,
281+ isShared : true ,
282+ } ) ;
195283 }
196284}
197285
0 commit comments