@ironclads/incus-mcp 0.1.0

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.
@@ -0,0 +1,164 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawn } from 'child_process';
4
+
5
+ async function testResources() {
6
+ console.log('šŸ” Testing MCP Resources...\n');
7
+
8
+ // Test reading instances list resource
9
+ console.log('1. Testing incus://instances/list resource...');
10
+ const instancesResourceMessage = {
11
+ jsonrpc: '2.0',
12
+ id: 1,
13
+ method: 'resources/read',
14
+ params: {
15
+ uri: 'incus://instances/list'
16
+ }
17
+ };
18
+
19
+ try {
20
+ const result = await sendMCPRequest(instancesResourceMessage);
21
+ console.log(` āœ… Resource read successful`);
22
+ if (result.contents && result.contents[0]) {
23
+ const content = result.contents[0];
24
+ console.log(` URI: ${content.uri}`);
25
+ console.log(` MIME Type: ${content.mimeType}`);
26
+ console.log(` Content length: ${content.text.length} chars`);
27
+
28
+ // Try to parse as JSON
29
+ try {
30
+ const instances = JSON.parse(content.text);
31
+ console.log(` Found ${instances.length} instances in JSON format`);
32
+ } catch (e) {
33
+ console.log(` Content is not JSON: ${e.message}`);
34
+ }
35
+ }
36
+ } catch (error) {
37
+ console.log(` āŒ Error: ${error.message}`);
38
+ }
39
+
40
+ // Test reading remotes list resource
41
+ console.log('\n2. Testing incus://remotes/list resource...');
42
+ const remotesResourceMessage = {
43
+ jsonrpc: '2.0',
44
+ id: 2,
45
+ method: 'resources/read',
46
+ params: {
47
+ uri: 'incus://remotes/list'
48
+ }
49
+ };
50
+
51
+ try {
52
+ const result = await sendMCPRequest(remotesResourceMessage);
53
+ console.log(` āœ… Resource read successful`);
54
+ if (result.contents && result.contents[0]) {
55
+ const content = result.contents[0];
56
+ console.log(` URI: ${content.uri}`);
57
+ console.log(` MIME Type: ${content.mimeType}`);
58
+ console.log(` Content length: ${content.text.length} chars`);
59
+
60
+ // Try to parse as JSON
61
+ try {
62
+ const remotes = JSON.parse(content.text);
63
+ const remoteNames = Object.keys(remotes);
64
+ console.log(` Found ${remoteNames.length} remotes: ${remoteNames.join(', ')}`);
65
+ } catch (e) {
66
+ console.log(` Content is not JSON: ${e.message}`);
67
+ }
68
+ }
69
+ } catch (error) {
70
+ console.log(` āŒ Error: ${error.message}`);
71
+ }
72
+
73
+ // Test reading unknown resource
74
+ console.log('\n3. Testing unknown resource...');
75
+ const unknownResourceMessage = {
76
+ jsonrpc: '2.0',
77
+ id: 3,
78
+ method: 'resources/read',
79
+ params: {
80
+ uri: 'incus://unknown/resource'
81
+ }
82
+ };
83
+
84
+ try {
85
+ const result = await sendMCPRequest(unknownResourceMessage);
86
+ console.log(` āŒ Unexpected success: ${JSON.stringify(result)}`);
87
+ } catch (error) {
88
+ console.log(` āœ… Expected error: ${error.message}`);
89
+ }
90
+ }
91
+
92
+ async function sendMCPRequest(message) {
93
+ return new Promise((resolve, reject) => {
94
+ const serverProcess = spawn('node', ['build/index.js'], {
95
+ stdio: ['pipe', 'pipe', 'pipe']
96
+ });
97
+
98
+ let response = '';
99
+ let error = '';
100
+
101
+ serverProcess.stdout.on('data', (data) => {
102
+ response += data.toString();
103
+ });
104
+
105
+ serverProcess.stderr.on('data', (data) => {
106
+ error += data.toString();
107
+ });
108
+
109
+ serverProcess.on('close', (code) => {
110
+ if (code !== 0) {
111
+ reject(new Error(`Server exited with code ${code}: ${error}`));
112
+ return;
113
+ }
114
+
115
+ try {
116
+ // Parse JSON-RPC response
117
+ const lines = response.trim().split('\n');
118
+ let jsonResponse = null;
119
+
120
+ for (const line of lines) {
121
+ if (line.trim()) {
122
+ try {
123
+ const parsed = JSON.parse(line);
124
+ if (parsed.id === message.id) {
125
+ jsonResponse = parsed;
126
+ break;
127
+ }
128
+ } catch (e) {
129
+ // Skip non-JSON lines
130
+ }
131
+ }
132
+ }
133
+
134
+ if (jsonResponse) {
135
+ if (jsonResponse.error) {
136
+ reject(new Error(jsonResponse.error.message || 'Unknown error'));
137
+ } else {
138
+ resolve(jsonResponse.result);
139
+ }
140
+ } else {
141
+ reject(new Error(`No valid response found in: ${response}`));
142
+ }
143
+ } catch (e) {
144
+ reject(new Error(`Failed to parse response: ${e.message}\nResponse: ${response}`));
145
+ }
146
+ });
147
+
148
+ serverProcess.on('error', (err) => {
149
+ reject(new Error(`Failed to start server: ${err.message}`));
150
+ });
151
+
152
+ // Send the JSON-RPC message
153
+ serverProcess.stdin.write(JSON.stringify(message) + '\n');
154
+ serverProcess.stdin.end();
155
+ });
156
+ }
157
+
158
+ // Run resource tests
159
+ testResources().then(() => {
160
+ console.log('\nāœ… All resource tests completed!');
161
+ }).catch((error) => {
162
+ console.error('\nāŒ Resource test suite failed:', error);
163
+ process.exit(1);
164
+ });
@@ -0,0 +1,187 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawn } from 'child_process';
4
+ import { promises as fs } from 'fs';
5
+
6
+ // Simple MCP client test
7
+ async function testMCPServer() {
8
+ console.log('🧪 Testing Incus MCP Server...\n');
9
+
10
+ // Test 1: List tools
11
+ console.log('1. Testing list tools...');
12
+ const listToolsMessage = {
13
+ jsonrpc: '2.0',
14
+ id: 1,
15
+ method: 'tools/list',
16
+ params: {}
17
+ };
18
+
19
+ try {
20
+ const toolsResult = await sendMCPRequest(listToolsMessage);
21
+ console.log(` āœ… Found ${toolsResult.tools.length} tools`);
22
+ toolsResult.tools.forEach(tool => {
23
+ console.log(` - ${tool.name}`);
24
+ });
25
+ } catch (error) {
26
+ console.log(` āŒ Error: ${error.message}`);
27
+ }
28
+
29
+ // Test 2: List resources
30
+ console.log('\n2. Testing list resources...');
31
+ const listResourcesMessage = {
32
+ jsonrpc: '2.0',
33
+ id: 2,
34
+ method: 'resources/list',
35
+ params: {}
36
+ };
37
+
38
+ try {
39
+ const resourcesResult = await sendMCPRequest(listResourcesMessage);
40
+ console.log(` āœ… Found ${resourcesResult.resources.length} resources`);
41
+ resourcesResult.resources.forEach(resource => {
42
+ console.log(` - ${resource.uri}: ${resource.name}`);
43
+ });
44
+ } catch (error) {
45
+ console.log(` āŒ Error: ${error.message}`);
46
+ }
47
+
48
+ // Test 3: Test incus info tool
49
+ console.log('\n3. Testing incus_info tool...');
50
+ const infoMessage = {
51
+ jsonrpc: '2.0',
52
+ id: 3,
53
+ method: 'tools/call',
54
+ params: {
55
+ name: 'incus_info',
56
+ arguments: {}
57
+ }
58
+ };
59
+
60
+ try {
61
+ const infoResult = await sendMCPRequest(infoMessage);
62
+ console.log(` āœ… Got incus info`);
63
+ if (infoResult.content && infoResult.content[0]) {
64
+ console.log(` First few chars: ${infoResult.content[0].text.substring(0, 100)}...`);
65
+ }
66
+ } catch (error) {
67
+ console.log(` āŒ Error: ${error.message}`);
68
+ }
69
+
70
+ // Test 4: Test list instances
71
+ console.log('\n4. Testing incus_list_instances tool...');
72
+ const listInstancesMessage = {
73
+ jsonrpc: '2.0',
74
+ id: 4,
75
+ method: 'tools/call',
76
+ params: {
77
+ name: 'incus_list_instances',
78
+ arguments: {}
79
+ }
80
+ };
81
+
82
+ try {
83
+ const instancesResult = await sendMCPRequest(listInstancesMessage);
84
+ console.log(` āœ… Got instances list`);
85
+ if (instancesResult.content && instancesResult.content[0]) {
86
+ console.log(` Response: ${instancesResult.content[0].text.substring(0, 200)}...`);
87
+ }
88
+ } catch (error) {
89
+ console.log(` āŒ Error: ${error.message}`);
90
+ }
91
+
92
+ // Test 5: Test list remotes
93
+ console.log('\n5. Testing incus_list_remotes tool...');
94
+ const listRemotesMessage = {
95
+ jsonrpc: '2.0',
96
+ id: 5,
97
+ method: 'tools/call',
98
+ params: {
99
+ name: 'incus_list_remotes',
100
+ arguments: {}
101
+ }
102
+ };
103
+
104
+ try {
105
+ const remotesResult = await sendMCPRequest(listRemotesMessage);
106
+ console.log(` āœ… Got remotes list`);
107
+ if (remotesResult.content && remotesResult.content[0]) {
108
+ console.log(` Response: ${remotesResult.content[0].text.substring(0, 200)}...`);
109
+ }
110
+ } catch (error) {
111
+ console.log(` āŒ Error: ${error.message}`);
112
+ }
113
+ }
114
+
115
+ async function sendMCPRequest(message) {
116
+ return new Promise((resolve, reject) => {
117
+ const serverProcess = spawn('node', ['build/index.js'], {
118
+ stdio: ['pipe', 'pipe', 'pipe']
119
+ });
120
+
121
+ let response = '';
122
+ let error = '';
123
+
124
+ serverProcess.stdout.on('data', (data) => {
125
+ response += data.toString();
126
+ });
127
+
128
+ serverProcess.stderr.on('data', (data) => {
129
+ error += data.toString();
130
+ });
131
+
132
+ serverProcess.on('close', (code) => {
133
+ if (code !== 0) {
134
+ reject(new Error(`Server exited with code ${code}: ${error}`));
135
+ return;
136
+ }
137
+
138
+ try {
139
+ // Parse JSON-RPC response
140
+ const lines = response.trim().split('\n');
141
+ let jsonResponse = null;
142
+
143
+ for (const line of lines) {
144
+ if (line.trim()) {
145
+ try {
146
+ const parsed = JSON.parse(line);
147
+ if (parsed.id === message.id) {
148
+ jsonResponse = parsed;
149
+ break;
150
+ }
151
+ } catch (e) {
152
+ // Skip non-JSON lines
153
+ }
154
+ }
155
+ }
156
+
157
+ if (jsonResponse) {
158
+ if (jsonResponse.error) {
159
+ reject(new Error(jsonResponse.error.message || 'Unknown error'));
160
+ } else {
161
+ resolve(jsonResponse.result);
162
+ }
163
+ } else {
164
+ reject(new Error(`No valid response found in: ${response}`));
165
+ }
166
+ } catch (e) {
167
+ reject(new Error(`Failed to parse response: ${e.message}\nResponse: ${response}`));
168
+ }
169
+ });
170
+
171
+ serverProcess.on('error', (err) => {
172
+ reject(new Error(`Failed to start server: ${err.message}`));
173
+ });
174
+
175
+ // Send the JSON-RPC message
176
+ serverProcess.stdin.write(JSON.stringify(message) + '\n');
177
+ serverProcess.stdin.end();
178
+ });
179
+ }
180
+
181
+ // Run tests
182
+ testMCPServer().then(() => {
183
+ console.log('\nāœ… All tests completed!');
184
+ }).catch((error) => {
185
+ console.error('\nāŒ Test suite failed:', error);
186
+ process.exit(1);
187
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "node",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true,
9
+ "forceConsistentCasingInFileNames": true,
10
+ "outDir": "./build",
11
+ "rootDir": "./src",
12
+ "declaration": true,
13
+ "declarationMap": true,
14
+ "sourceMap": true,
15
+ "resolveJsonModule": true,
16
+ "allowSyntheticDefaultImports": true
17
+ },
18
+ "include": ["src/**/*"],
19
+ "exclude": ["node_modules", "build", "**/*.test.ts"]
20
+ }