@ironclads/incus-mcp 0.1.2 → 0.1.4

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.
package/package.json CHANGED
@@ -1,9 +1,22 @@
1
1
  {
2
2
  "name": "@ironclads/incus-mcp",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "MCP server for Incus container management",
5
5
  "main": "build/index.js",
6
6
  "type": "module",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./build/index.js",
10
+ "types": "./build/index.d.ts"
11
+ }
12
+ },
13
+ "files": [
14
+ "build/",
15
+ "src/",
16
+ "README.md",
17
+ "CLAUDE.md",
18
+ "mcp-config-example.json"
19
+ ],
7
20
  "scripts": {
8
21
  "build": "tsc",
9
22
  "start": "node build/index.js",
@@ -42,4 +55,4 @@
42
55
  "engines": {
43
56
  "node": ">=18.0.0"
44
57
  }
45
- }
58
+ }
@@ -1,12 +0,0 @@
1
- {
2
- "enabledMcpjsonServers": [
3
- "context7",
4
- "sequential-thinking",
5
- "playwright",
6
- "cloudflare",
7
- "podman",
8
- "incus",
9
- "incus-mcp"
10
- ],
11
- "enableAllProjectMcpServers": true
12
- }
package/.eslintrc.json DELETED
@@ -1,23 +0,0 @@
1
- {
2
- "env": {
3
- "node": true,
4
- "es2022": true
5
- },
6
- "extends": [
7
- "eslint:recommended",
8
- "@typescript-eslint/recommended"
9
- ],
10
- "parser": "@typescript-eslint/parser",
11
- "parserOptions": {
12
- "ecmaVersion": 2022,
13
- "sourceType": "module"
14
- },
15
- "plugins": ["@typescript-eslint"],
16
- "rules": {
17
- "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
18
- "@typescript-eslint/explicit-function-return-type": "off",
19
- "@typescript-eslint/no-explicit-any": "warn",
20
- "prefer-const": "error",
21
- "no-var": "error"
22
- }
23
- }
package/.prettierrc DELETED
@@ -1,8 +0,0 @@
1
- {
2
- "semi": true,
3
- "trailingComma": "es5",
4
- "singleQuote": true,
5
- "printWidth": 100,
6
- "tabWidth": 2,
7
- "useTabs": false
8
- }
package/test/client.ts DELETED
@@ -1,169 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { Client } from '@modelcontextprotocol/sdk/client/index.js';
4
- import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
5
- import { spawn } from 'child_process';
6
-
7
- class TestClient {
8
- private client: Client;
9
- private transport: StdioClientTransport;
10
-
11
- constructor() {
12
- // Spawn the MCP server process
13
- const serverProcess = spawn('node', ['../build/index.js'], {
14
- stdio: ['pipe', 'pipe', 'inherit'],
15
- cwd: __dirname,
16
- });
17
-
18
- this.transport = new StdioClientTransport({
19
- reader: serverProcess.stdout!,
20
- writer: serverProcess.stdin!,
21
- });
22
-
23
- this.client = new Client(
24
- {
25
- name: 'incus-mcp-test-client',
26
- version: '0.1.0',
27
- },
28
- {
29
- capabilities: {},
30
- }
31
- );
32
- }
33
-
34
- async connect() {
35
- await this.client.connect(this.transport);
36
- console.log('āœ… Connected to Incus MCP server');
37
- }
38
-
39
- async testListTools() {
40
- console.log('\nšŸ”§ Testing list tools...');
41
- try {
42
- const result = await this.client.request(
43
- { method: 'tools/list' },
44
- { method: 'tools/list' }
45
- );
46
- console.log(`Found ${result.tools.length} tools:`);
47
- result.tools.forEach((tool: any) => {
48
- console.log(` - ${tool.name}: ${tool.description}`);
49
- });
50
- } catch (error) {
51
- console.error('āŒ Error listing tools:', error);
52
- }
53
- }
54
-
55
- async testListResources() {
56
- console.log('\nšŸ“š Testing list resources...');
57
- try {
58
- const result = await this.client.request(
59
- { method: 'resources/list' },
60
- { method: 'resources/list' }
61
- );
62
- console.log(`Found ${result.resources.length} resources:`);
63
- result.resources.forEach((resource: any) => {
64
- console.log(` - ${resource.uri}: ${resource.name}`);
65
- });
66
- } catch (error) {
67
- console.error('āŒ Error listing resources:', error);
68
- }
69
- }
70
-
71
- async testIncusInfo() {
72
- console.log('\nšŸ’» Testing incus info...');
73
- try {
74
- const result = await this.client.request(
75
- {
76
- method: 'tools/call',
77
- params: {
78
- name: 'incus_info',
79
- arguments: {},
80
- },
81
- },
82
- {
83
- method: 'tools/call',
84
- params: {
85
- name: 'incus_info',
86
- arguments: {},
87
- },
88
- }
89
- );
90
- console.log('Incus info result:', result);
91
- } catch (error) {
92
- console.error('āŒ Error getting incus info:', error);
93
- }
94
- }
95
-
96
- async testListInstances() {
97
- console.log('\nšŸ“¦ Testing list instances...');
98
- try {
99
- const result = await this.client.request(
100
- {
101
- method: 'tools/call',
102
- params: {
103
- name: 'incus_list_instances',
104
- arguments: {},
105
- },
106
- },
107
- {
108
- method: 'tools/call',
109
- params: {
110
- name: 'incus_list_instances',
111
- arguments: {},
112
- },
113
- }
114
- );
115
- console.log('Instances list result:', result);
116
- } catch (error) {
117
- console.error('āŒ Error listing instances:', error);
118
- }
119
- }
120
-
121
- async testListRemotes() {
122
- console.log('\n🌐 Testing list remotes...');
123
- try {
124
- const result = await this.client.request(
125
- {
126
- method: 'tools/call',
127
- params: {
128
- name: 'incus_list_remotes',
129
- arguments: {},
130
- },
131
- },
132
- {
133
- method: 'tools/call',
134
- params: {
135
- name: 'incus_list_remotes',
136
- arguments: {},
137
- },
138
- }
139
- );
140
- console.log('Remotes list result:', result);
141
- } catch (error) {
142
- console.error('āŒ Error listing remotes:', error);
143
- }
144
- }
145
-
146
- async runTests() {
147
- try {
148
- await this.connect();
149
- await this.testListTools();
150
- await this.testListResources();
151
- await this.testIncusInfo();
152
- await this.testListInstances();
153
- await this.testListRemotes();
154
- console.log('\nāœ… All tests completed');
155
- } catch (error) {
156
- console.error('āŒ Test failed:', error);
157
- }
158
- }
159
- }
160
-
161
- // Run tests
162
- async function main() {
163
- const testClient = new TestClient();
164
- await testClient.runTests();
165
- }
166
-
167
- if (import.meta.url === `file://${process.argv[1]}`) {
168
- main().catch(console.error);
169
- }
@@ -1,164 +0,0 @@
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
- });
@@ -1,187 +0,0 @@
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 DELETED
@@ -1,20 +0,0 @@
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
- }