This repository was archived by the owner on May 21, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 33
Expand file tree
/
Copy pathclient.ts
More file actions
155 lines (134 loc) · 4.27 KB
/
Copy pathclient.ts
File metadata and controls
155 lines (134 loc) · 4.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
import fs from 'node:fs'
import path from 'node:path'
import { createConnectorFromConfig, loadConfigFile } from './config.js'
import { logger } from './logging.js'
import { MCPSession } from './session.js'
export class MCPClient {
private config: Record<string, any> = {}
private sessions: Record<string, MCPSession> = {}
public activeSessions: string[] = []
constructor(config?: string | Record<string, any>) {
if (config) {
if (typeof config === 'string') {
this.config = loadConfigFile(config)
}
else {
this.config = config
}
}
}
public static fromDict(cfg: Record<string, any>): MCPClient {
return new MCPClient(cfg)
}
public static fromConfigFile(path: string): MCPClient {
return new MCPClient(loadConfigFile(path))
}
public addServer(name: string, serverConfig: Record<string, any>): void {
this.config.mcpServers = this.config.mcpServers || {}
this.config.mcpServers[name] = serverConfig
}
public removeServer(name: string): void {
if (this.config.mcpServers?.[name]) {
delete this.config.mcpServers[name]
this.activeSessions = this.activeSessions.filter(n => n !== name)
}
}
public getServerNames(): string[] {
return Object.keys(this.config.mcpServers ?? {})
}
public saveConfig(filepath: string): void {
const dir = path.dirname(filepath)
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true })
}
fs.writeFileSync(filepath, JSON.stringify(this.config, null, 2), 'utf-8')
}
public async createSession(
serverName: string,
autoInitialize = true,
): Promise<MCPSession> {
const servers = this.config.mcpServers ?? {}
if (Object.keys(servers).length === 0) {
throw new Error('No MCP servers defined in config')
}
if (!servers[serverName]) {
throw new Error(`Server '${serverName}' not found in config`)
}
const connector = createConnectorFromConfig(servers[serverName])
const session = new MCPSession(connector)
if (autoInitialize) {
await session.initialize()
}
this.sessions[serverName] = session
if (!this.activeSessions.includes(serverName)) {
this.activeSessions.push(serverName)
}
return session
}
public async createAllSessions(
autoInitialize = true,
): Promise<Record<string, MCPSession>> {
const servers = this.config.mcpServers ?? {}
if (Object.keys(servers).length === 0) {
throw new Error('No MCP servers defined in config')
}
for (const name of Object.keys(servers)) {
await this.createSession(name, autoInitialize)
}
return this.sessions
}
public getSession(serverName: string): MCPSession | null {
const session = this.sessions[serverName]
// if (!session) {
// throw new Error(`No session exists for server '${serverName}'`)
// }
if (!session) {
return null
}
return session
}
public getAllActiveSessions(): Record<string, MCPSession> {
return Object.fromEntries(
this.activeSessions.map(n => [n, this.sessions[n]]),
)
}
public async closeSession(serverName: string): Promise<void> {
const session = this.sessions[serverName]
if (!session) {
logger.warn(`No session exists for server ${serverName}, nothing to close`)
return
}
try {
logger.debug(`Closing session for server ${serverName}`)
await session.disconnect()
}
catch (e) {
logger.error(`Error closing session for server '${serverName}': ${e}`)
}
finally {
delete this.sessions[serverName]
this.activeSessions = this.activeSessions.filter(n => n !== serverName)
}
}
public async closeAllSessions(): Promise<void> {
const serverNames = Object.keys(this.sessions)
const errors: string[] = []
for (const serverName of serverNames) {
try {
logger.debug(`Closing session for server ${serverName}`)
await this.closeSession(serverName)
}
catch (e: any) {
const errorMsg = `Failed to close session for server '${serverName}': ${e}`
logger.error(errorMsg)
errors.push(errorMsg)
}
}
if (errors.length) {
logger.error(`Encountered ${errors.length} errors while closing sessions`)
}
else {
logger.debug('All sessions closed successfully')
}
}
}