aiexecode 1.0.90 → 1.0.92
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of aiexecode might be problematic. Click here for more details.
- package/README.md +1 -0
- package/index.js +13 -11
- package/mcp-agent-lib/init.sh +3 -0
- package/mcp-agent-lib/package-lock.json +14 -1
- package/mcp-agent-lib/package.json +4 -6
- package/mcp-agent-lib/sampleFastMCPClient/client.py +25 -0
- package/mcp-agent-lib/sampleFastMCPClient/run.sh +3 -0
- package/mcp-agent-lib/sampleFastMCPServer/run.sh +3 -0
- package/mcp-agent-lib/sampleFastMCPServer/server.py +12 -0
- package/mcp-agent-lib/sampleFastMCPServerElicitationRequest/run.sh +3 -0
- package/mcp-agent-lib/sampleFastMCPServerElicitationRequest/server.py +43 -0
- package/mcp-agent-lib/sampleFastMCPServerRootsRequest/server.py +63 -0
- package/mcp-agent-lib/sampleMCPHost/index.js +182 -63
- package/mcp-agent-lib/sampleMCPHost/mcp_config.json +7 -1
- package/mcp-agent-lib/sampleMCPHostFeatures/elicitation.js +151 -0
- package/mcp-agent-lib/sampleMCPHostFeatures/index.js +166 -0
- package/mcp-agent-lib/sampleMCPHostFeatures/roots.js +197 -0
- package/mcp-agent-lib/src/mcp_client.js +129 -67
- package/mcp-agent-lib/src/mcp_message_logger.js +516 -0
- package/package.json +3 -1
- package/payload_viewer/out/404/index.html +1 -1
- package/payload_viewer/out/404.html +1 -1
- package/payload_viewer/out/index.html +1 -1
- package/payload_viewer/out/index.txt +1 -1
- package/src/LLMClient/client.js +992 -0
- package/src/LLMClient/converters/input-normalizer.js +238 -0
- package/src/LLMClient/converters/responses-to-claude.js +454 -0
- package/src/LLMClient/converters/responses-to-gemini.js +648 -0
- package/src/LLMClient/converters/responses-to-ollama.js +348 -0
- package/src/LLMClient/errors.js +372 -0
- package/src/LLMClient/index.js +31 -0
- package/src/commands/apikey.js +10 -22
- package/src/commands/model.js +28 -28
- package/src/commands/reasoning_effort.js +9 -23
- package/src/config/ai_models.js +212 -0
- package/src/config/feature_flags.js +1 -1
- package/src/frontend/App.js +5 -10
- package/src/frontend/components/CurrentModelView.js +0 -33
- package/src/frontend/components/Footer.js +3 -3
- package/src/frontend/components/ModelListView.js +30 -87
- package/src/frontend/components/ModelUpdatedView.js +7 -142
- package/src/frontend/components/SetupWizard.js +37 -32
- package/src/system/ai_request.js +57 -42
- package/src/util/config.js +26 -4
- package/src/util/setup_wizard.js +1 -6
- package/mcp-agent-lib/.claude/settings.local.json +0 -9
- package/src/config/openai_models.js +0 -152
- /package/payload_viewer/out/_next/static/{w4dMVYalgk7djrLxRxWiE → d0-fu2rgYnshgGFPxr1CR}/_buildManifest.js +0 -0
- /package/payload_viewer/out/_next/static/{w4dMVYalgk7djrLxRxWiE → d0-fu2rgYnshgGFPxr1CR}/_clientMiddlewareManifest.json +0 -0
- /package/payload_viewer/out/_next/static/{w4dMVYalgk7djrLxRxWiE → d0-fu2rgYnshgGFPxr1CR}/_ssgManifest.js +0 -0
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"type": "http",
|
|
5
5
|
"url": "https://mcp.context7.com/mcp",
|
|
6
6
|
"headers": {
|
|
7
|
-
"CONTEXT7_API_KEY": "ctx7sk-
|
|
7
|
+
"CONTEXT7_API_KEY": "ctx7sk-11-44-4857-22-33"
|
|
8
8
|
}
|
|
9
9
|
},
|
|
10
10
|
"chrome-devtools": {
|
|
@@ -13,6 +13,12 @@
|
|
|
13
13
|
"-y",
|
|
14
14
|
"chrome-devtools-mcp@latest"
|
|
15
15
|
]
|
|
16
|
+
},
|
|
17
|
+
"simple-calculator": {
|
|
18
|
+
"command": "../sampleFastMCPServer/venv/bin/python",
|
|
19
|
+
"args": [
|
|
20
|
+
"../sampleFastMCPServer/server.py"
|
|
21
|
+
]
|
|
16
22
|
}
|
|
17
23
|
}
|
|
18
24
|
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
2
|
+
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
3
|
+
import { ElicitRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
4
|
+
import { MCPMessageLogger, attachLoggerToTransport } from '../src/mcp_message_logger.js';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
import readline from 'readline';
|
|
9
|
+
|
|
10
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
|
|
12
|
+
function getUserInput(prompt) {
|
|
13
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
14
|
+
return new Promise((resolve) => {
|
|
15
|
+
rl.question(prompt, (answer) => {
|
|
16
|
+
rl.close();
|
|
17
|
+
resolve(answer);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
class ElicitationHost {
|
|
23
|
+
constructor() {
|
|
24
|
+
this.client = null;
|
|
25
|
+
this.logDir = path.join(__dirname, 'log');
|
|
26
|
+
|
|
27
|
+
// 로그 폴더 초기화
|
|
28
|
+
if (fs.existsSync(this.logDir)) fs.rmSync(this.logDir, { recursive: true, force: true });
|
|
29
|
+
fs.mkdirSync(this.logDir, { recursive: true });
|
|
30
|
+
console.log(`✓ Log directory initialized: ${this.logDir}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async connect(serverConfig) {
|
|
34
|
+
console.log('Connecting to MCP server...');
|
|
35
|
+
|
|
36
|
+
const transport = new StdioClientTransport({
|
|
37
|
+
command: serverConfig.command,
|
|
38
|
+
args: serverConfig.args || [],
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const logger = new MCPMessageLogger({
|
|
42
|
+
enabled: true,
|
|
43
|
+
output: 'both',
|
|
44
|
+
logDir: this.logDir,
|
|
45
|
+
prettyPrint: true
|
|
46
|
+
});
|
|
47
|
+
attachLoggerToTransport(transport, 'elicitation-server', logger);
|
|
48
|
+
|
|
49
|
+
this.client = new Client({
|
|
50
|
+
name: 'elicitation-host',
|
|
51
|
+
version: '1.0.0',
|
|
52
|
+
}, {
|
|
53
|
+
capabilities: { elicitation: {} }
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
this._setupElicitationHandler();
|
|
57
|
+
await this.client.connect(transport);
|
|
58
|
+
|
|
59
|
+
console.log('✓ Successfully connected to server');
|
|
60
|
+
return this.client;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
_setupElicitationHandler() {
|
|
64
|
+
this.client.setRequestHandler(ElicitRequestSchema, async (request) => {
|
|
65
|
+
console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
66
|
+
console.log('📥 Elicitation:', request.params.message);
|
|
67
|
+
console.log('스키마:', JSON.stringify(request.params.requestedSchema, null, 2));
|
|
68
|
+
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
|
69
|
+
|
|
70
|
+
const { properties = {}, required = [] } = request.params.requestedSchema || {};
|
|
71
|
+
const data = {};
|
|
72
|
+
|
|
73
|
+
// 사용자 입력 받기
|
|
74
|
+
for (const [key, prop] of Object.entries(properties)) {
|
|
75
|
+
const input = await getUserInput(`${prop.description || key}${required.includes(key) ? ' (필수)' : ' (선택)'}: `);
|
|
76
|
+
|
|
77
|
+
if (input || required.includes(key)) {
|
|
78
|
+
if (prop.type === 'number' || prop.type === 'integer') {
|
|
79
|
+
data[key] = parseFloat(input);
|
|
80
|
+
} else if (prop.type === 'boolean') {
|
|
81
|
+
data[key] = ['true', 'yes', '1', 'y'].includes(input.toLowerCase());
|
|
82
|
+
} else {
|
|
83
|
+
data[key] = input;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// 확인
|
|
89
|
+
const confirm = await getUserInput('\n실행하시겠습니까? (yes/no/cancel): ');
|
|
90
|
+
const action = ['yes', 'y'].includes(confirm.toLowerCase()) ? 'accept'
|
|
91
|
+
: ['no', 'n'].includes(confirm.toLowerCase()) ? 'decline'
|
|
92
|
+
: 'cancel';
|
|
93
|
+
|
|
94
|
+
console.log(`\n📤 응답: action=${action}, content=${JSON.stringify(data)}\n`);
|
|
95
|
+
|
|
96
|
+
return { action, content: action === 'accept' ? data : undefined };
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async disconnect() {
|
|
101
|
+
if (this.client) {
|
|
102
|
+
console.log('Disconnecting...');
|
|
103
|
+
await this.client.close();
|
|
104
|
+
console.log('✓ Disconnected');
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async listTools() {
|
|
109
|
+
return (await this.client.listTools()).tools;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async callTool(toolName, args) {
|
|
113
|
+
console.log(`\n🔧 ${toolName}(${JSON.stringify(args)})`);
|
|
114
|
+
const result = await this.client.callTool({ name: toolName, arguments: args });
|
|
115
|
+
console.log(`✅ Result:`, result);
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function main() {
|
|
121
|
+
const host = new ElicitationHost();
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
await host.connect({
|
|
125
|
+
command: '../sampleFastMCPServerElicitationRequest/venv/bin/python',
|
|
126
|
+
args: ['../sampleFastMCPServerElicitationRequest/server.py']
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
console.log('\n📚 Available tools:');
|
|
130
|
+
(await host.listTools()).forEach(t => console.log(` - ${t.name}: ${t.description}`));
|
|
131
|
+
|
|
132
|
+
console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
133
|
+
console.log('Example 1: add_with_confirmation');
|
|
134
|
+
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
|
135
|
+
await host.callTool('add_with_confirmation', { a: 10, b: 20 });
|
|
136
|
+
|
|
137
|
+
console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
138
|
+
console.log('Example 2: greet_user');
|
|
139
|
+
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
|
140
|
+
await host.callTool('greet_user', {});
|
|
141
|
+
|
|
142
|
+
} catch (error) {
|
|
143
|
+
console.error('Error:', error);
|
|
144
|
+
} finally {
|
|
145
|
+
await host.disconnect();
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (import.meta.url === `file://${process.argv[1]}`) main().catch(console.error);
|
|
150
|
+
|
|
151
|
+
export { ElicitationHost };
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
2
|
+
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
3
|
+
import { MCPMessageLogger, attachLoggerToTransport } from '../src/mcp_message_logger.js';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
|
|
8
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
|
|
10
|
+
class MCPHostExample {
|
|
11
|
+
constructor() {
|
|
12
|
+
this.clients = {};
|
|
13
|
+
this.transports = {};
|
|
14
|
+
this.loggers = {};
|
|
15
|
+
this.logDir = path.join(__dirname, 'log');
|
|
16
|
+
|
|
17
|
+
// 로그 폴더 초기화
|
|
18
|
+
if (fs.existsSync(this.logDir)) fs.rmSync(this.logDir, { recursive: true, force: true });
|
|
19
|
+
fs.mkdirSync(this.logDir, { recursive: true });
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async connectToServer(serverName, serverConfig) {
|
|
23
|
+
if (this.clients[serverName]) {
|
|
24
|
+
return this.clients[serverName];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (!serverConfig?.command) {
|
|
28
|
+
throw new Error(`Invalid server config for '${serverName}'`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const transport = new StdioClientTransport({
|
|
32
|
+
command: serverConfig.command,
|
|
33
|
+
args: serverConfig.args || [],
|
|
34
|
+
});
|
|
35
|
+
this.transports[serverName] = transport;
|
|
36
|
+
|
|
37
|
+
const logger = new MCPMessageLogger({
|
|
38
|
+
enabled: true,
|
|
39
|
+
output: 'file',
|
|
40
|
+
logDir: this.logDir,
|
|
41
|
+
prettyPrint: true
|
|
42
|
+
});
|
|
43
|
+
this.loggers[serverName] = logger;
|
|
44
|
+
attachLoggerToTransport(transport, serverName, logger);
|
|
45
|
+
|
|
46
|
+
const client = new Client({
|
|
47
|
+
name: 'mcp-host-example',
|
|
48
|
+
version: '1.0.0',
|
|
49
|
+
}, { capabilities: {} });
|
|
50
|
+
this.clients[serverName] = client;
|
|
51
|
+
|
|
52
|
+
await client.connect(transport);
|
|
53
|
+
return client;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async disconnect(serverName = null) {
|
|
57
|
+
if (serverName) {
|
|
58
|
+
if (this.clients[serverName]) {
|
|
59
|
+
await this.clients[serverName].close();
|
|
60
|
+
if (this.loggers[serverName]) this.loggers[serverName].close();
|
|
61
|
+
|
|
62
|
+
delete this.clients[serverName];
|
|
63
|
+
delete this.transports[serverName];
|
|
64
|
+
delete this.loggers[serverName];
|
|
65
|
+
}
|
|
66
|
+
} else {
|
|
67
|
+
for (const name of Object.keys(this.clients)) {
|
|
68
|
+
await this.disconnect(name);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
getClient(serverName) {
|
|
74
|
+
return this.clients[serverName] || null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
getConnectedServers() {
|
|
78
|
+
return Object.keys(this.clients);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async listTools(serverName) {
|
|
82
|
+
const client = this.getClient(serverName);
|
|
83
|
+
if (!client) throw new Error(`Server '${serverName}' not connected`);
|
|
84
|
+
return (await client.listTools()).tools;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async callTool(serverName, toolName, args = {}) {
|
|
88
|
+
const client = this.getClient(serverName);
|
|
89
|
+
if (!client) throw new Error(`Server '${serverName}' not connected`);
|
|
90
|
+
return await client.callTool({ name: toolName, arguments: args });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function main() {
|
|
95
|
+
const host = new MCPHostExample();
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
// simple-calculator 서버 접속
|
|
99
|
+
console.log('Connecting to simple-calculator...');
|
|
100
|
+
await host.connectToServer('simple-calculator', {
|
|
101
|
+
command: '../sampleFastMCPServer/venv/bin/python',
|
|
102
|
+
args: ['../sampleFastMCPServer/server.py']
|
|
103
|
+
});
|
|
104
|
+
console.log('✓ Connected to simple-calculator');
|
|
105
|
+
|
|
106
|
+
// chrome-devtools 서버 접속
|
|
107
|
+
console.log('\nConnecting to chrome-devtools...');
|
|
108
|
+
await host.connectToServer('chrome-devtools', {
|
|
109
|
+
command: 'npx',
|
|
110
|
+
args: ['-y', 'chrome-devtools-mcp@latest']
|
|
111
|
+
});
|
|
112
|
+
console.log('✓ Connected to chrome-devtools');
|
|
113
|
+
|
|
114
|
+
console.log('\nConnected servers:', host.getConnectedServers());
|
|
115
|
+
|
|
116
|
+
// simple-calculator 도구 사용
|
|
117
|
+
console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
118
|
+
console.log('[simple-calculator 도구 사용]');
|
|
119
|
+
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
120
|
+
|
|
121
|
+
console.log('\n📚 Available tools:');
|
|
122
|
+
const calcTools = await host.listTools('simple-calculator');
|
|
123
|
+
calcTools.forEach(t => console.log(` - ${t.name}: ${t.description}`));
|
|
124
|
+
|
|
125
|
+
console.log('\n🔧 Calling add(10, 20)...');
|
|
126
|
+
const addResult = await host.callTool('simple-calculator', 'add', { a: 10, b: 20 });
|
|
127
|
+
console.log('✅ Result:', addResult);
|
|
128
|
+
|
|
129
|
+
// chrome-devtools 도구 사용
|
|
130
|
+
console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
131
|
+
console.log('[chrome-devtools 도구 사용]');
|
|
132
|
+
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
133
|
+
|
|
134
|
+
console.log('\n📚 Available tools:');
|
|
135
|
+
const chromeTools = await host.listTools('chrome-devtools');
|
|
136
|
+
chromeTools.forEach(t => console.log(` - ${t.name}: ${t.description}`));
|
|
137
|
+
|
|
138
|
+
console.log('\n🔧 Opening new page...');
|
|
139
|
+
const pageResult = await host.callTool('chrome-devtools', 'new_page', { url: 'about:blank' });
|
|
140
|
+
console.log('✅ Page opened:', pageResult);
|
|
141
|
+
|
|
142
|
+
console.log('\n🔧 Running countdown script...');
|
|
143
|
+
for (let count = 10; count >= 0; count--) {
|
|
144
|
+
console.log(` Displaying: ${count}`);
|
|
145
|
+
await host.callTool('chrome-devtools', 'evaluate_script', {
|
|
146
|
+
function: `() => {
|
|
147
|
+
document.body.innerHTML = '<div style="color:blue;display:flex;justify-content:center;align-items:center;height:100vh;font-size:200px;font-family:Arial">${count}</div>';
|
|
148
|
+
return ${count};
|
|
149
|
+
}`
|
|
150
|
+
});
|
|
151
|
+
if (count > 0) await new Promise(resolve => setTimeout(resolve, 100));
|
|
152
|
+
}
|
|
153
|
+
console.log('✅ Countdown complete');
|
|
154
|
+
|
|
155
|
+
} catch (error) {
|
|
156
|
+
console.error('Error:', error);
|
|
157
|
+
} finally {
|
|
158
|
+
console.log('\nDisconnecting...');
|
|
159
|
+
await host.disconnect();
|
|
160
|
+
console.log('✓ Disconnected');
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (import.meta.url === `file://${process.argv[1]}`) main().catch(console.error);
|
|
165
|
+
|
|
166
|
+
export { MCPHostExample };
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
2
|
+
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
3
|
+
import { ListRootsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
4
|
+
import { MCPMessageLogger, attachLoggerToTransport } from '../src/mcp_message_logger.js';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
import os from 'os';
|
|
9
|
+
|
|
10
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
|
|
12
|
+
class RootsHost {
|
|
13
|
+
constructor() {
|
|
14
|
+
this.client = null;
|
|
15
|
+
this.logDir = path.join(__dirname, 'log');
|
|
16
|
+
|
|
17
|
+
// 로그 폴더 초기화
|
|
18
|
+
if (fs.existsSync(this.logDir)) fs.rmSync(this.logDir, { recursive: true, force: true });
|
|
19
|
+
fs.mkdirSync(this.logDir, { recursive: true });
|
|
20
|
+
console.log(`✓ Log directory initialized: ${this.logDir}`);
|
|
21
|
+
|
|
22
|
+
// 동적으로 변경 가능한 roots 목록
|
|
23
|
+
this.currentRoots = [
|
|
24
|
+
{
|
|
25
|
+
uri: `file://${path.join(__dirname, '..')}`,
|
|
26
|
+
name: 'Project Root'
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
uri: `file://${path.join(os.homedir(), '.aiexe')}`,
|
|
30
|
+
name: 'Home Directory'
|
|
31
|
+
}
|
|
32
|
+
];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async connect(serverConfig) {
|
|
36
|
+
console.log('Connecting to MCP server...');
|
|
37
|
+
|
|
38
|
+
const transport = new StdioClientTransport({
|
|
39
|
+
command: serverConfig.command,
|
|
40
|
+
args: serverConfig.args || [],
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const logger = new MCPMessageLogger({
|
|
44
|
+
enabled: true,
|
|
45
|
+
output: 'both',
|
|
46
|
+
logDir: this.logDir,
|
|
47
|
+
prettyPrint: true
|
|
48
|
+
});
|
|
49
|
+
attachLoggerToTransport(transport, 'roots-server', logger);
|
|
50
|
+
|
|
51
|
+
this.client = new Client({
|
|
52
|
+
name: 'roots-host',
|
|
53
|
+
version: '1.0.0',
|
|
54
|
+
}, {
|
|
55
|
+
capabilities: { roots: { listChanged: true } }
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
this._setupRootsHandler();
|
|
59
|
+
await this.client.connect(transport);
|
|
60
|
+
|
|
61
|
+
console.log('✓ Successfully connected to server');
|
|
62
|
+
return this.client;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
_setupRootsHandler() {
|
|
66
|
+
// Roots 요청 핸들러 설정
|
|
67
|
+
this.client.setRequestHandler(ListRootsRequestSchema, async (request) => {
|
|
68
|
+
console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
69
|
+
console.log('📥 Roots 요청 받음 (서버가 클라이언트에게 roots 목록을 요청)');
|
|
70
|
+
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
|
71
|
+
|
|
72
|
+
console.log('📤 응답할 Roots:');
|
|
73
|
+
this.currentRoots.forEach((root, idx) => {
|
|
74
|
+
console.log(` ${idx + 1}. ${root.name}: ${root.uri}`);
|
|
75
|
+
});
|
|
76
|
+
console.log();
|
|
77
|
+
|
|
78
|
+
return { roots: this.currentRoots };
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
updateRoots(newRoots) {
|
|
83
|
+
console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
84
|
+
console.log('🔄 Roots 목록 변경');
|
|
85
|
+
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
|
86
|
+
|
|
87
|
+
console.log('이전 Roots:');
|
|
88
|
+
this.currentRoots.forEach((root, idx) => {
|
|
89
|
+
console.log(` ${idx + 1}. ${root.name}: ${root.uri}`);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
this.currentRoots = newRoots;
|
|
93
|
+
|
|
94
|
+
console.log('\n새 Roots:');
|
|
95
|
+
this.currentRoots.forEach((root, idx) => {
|
|
96
|
+
console.log(` ${idx + 1}. ${root.name}: ${root.uri}`);
|
|
97
|
+
});
|
|
98
|
+
console.log();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async disconnect() {
|
|
102
|
+
if (this.client) {
|
|
103
|
+
console.log('Disconnecting...');
|
|
104
|
+
await this.client.close();
|
|
105
|
+
console.log('✓ Disconnected');
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async listTools() {
|
|
110
|
+
return (await this.client.listTools()).tools;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async callTool(toolName, args) {
|
|
114
|
+
console.log(`\n🔧 ${toolName}(${JSON.stringify(args)})`);
|
|
115
|
+
const result = await this.client.callTool({ name: toolName, arguments: args });
|
|
116
|
+
console.log(`✅ Result:`, result);
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async sendRootsListChanged() {
|
|
121
|
+
console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
122
|
+
console.log('📢 클라이언트 → 서버: roots/list_changed notification 전송');
|
|
123
|
+
console.log(' (서버에게 roots 목록이 변경되었음을 알림)');
|
|
124
|
+
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
|
125
|
+
await this.client.sendRootsListChanged();
|
|
126
|
+
console.log('✓ Notification 전송 완료\n');
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async function main() {
|
|
131
|
+
const host = new RootsHost();
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
await host.connect({
|
|
135
|
+
command: '../sampleFastMCPServerRootsRequest/venv/bin/python',
|
|
136
|
+
args: ['../sampleFastMCPServerRootsRequest/server.py']
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
console.log('\n📚 Available tools:');
|
|
140
|
+
(await host.listTools()).forEach(t => console.log(` - ${t.name}: ${t.description}`));
|
|
141
|
+
|
|
142
|
+
console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
143
|
+
console.log('Example 1: 초기 roots 목록 조회');
|
|
144
|
+
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
|
145
|
+
await host.callTool('list_accessible_roots', {});
|
|
146
|
+
|
|
147
|
+
console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
148
|
+
console.log('Example 2: count_files_in_root (index=1)');
|
|
149
|
+
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
|
150
|
+
await host.callTool('count_files_in_root', { root_index: 1 });
|
|
151
|
+
|
|
152
|
+
// Roots 목록 변경 시나리오
|
|
153
|
+
console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
154
|
+
console.log('Example 3: Roots 목록 변경 및 알림 전송');
|
|
155
|
+
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
|
156
|
+
|
|
157
|
+
// Roots 목록 변경
|
|
158
|
+
const newRoots = [
|
|
159
|
+
{
|
|
160
|
+
uri: `file://${path.join(__dirname, '..')}`,
|
|
161
|
+
name: 'Project Root'
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
uri: `file://${path.join(__dirname)}`,
|
|
165
|
+
name: 'Sample Features Directory'
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
uri: `file://${os.tmpdir()}`,
|
|
169
|
+
name: 'Temp Directory'
|
|
170
|
+
}
|
|
171
|
+
];
|
|
172
|
+
host.updateRoots(newRoots);
|
|
173
|
+
|
|
174
|
+
// 서버에 roots 변경 알림
|
|
175
|
+
await host.sendRootsListChanged();
|
|
176
|
+
|
|
177
|
+
// 약간의 지연 후 서버가 다시 roots를 조회하도록 도구 호출
|
|
178
|
+
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
179
|
+
console.log('Example 4: 변경된 roots 목록 재조회');
|
|
180
|
+
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
|
181
|
+
await host.callTool('list_accessible_roots', {});
|
|
182
|
+
|
|
183
|
+
console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
184
|
+
console.log('Example 5: 새 root 파일 개수 조회 (index=2)');
|
|
185
|
+
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
|
186
|
+
await host.callTool('count_files_in_root', { root_index: 2 });
|
|
187
|
+
|
|
188
|
+
} catch (error) {
|
|
189
|
+
console.error('Error:', error);
|
|
190
|
+
} finally {
|
|
191
|
+
await host.disconnect();
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (import.meta.url === `file://${process.argv[1]}`) main().catch(console.error);
|
|
196
|
+
|
|
197
|
+
export { RootsHost };
|