mcp-for-i 0.1.5 → 0.1.7
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/README.md +38 -0
- package/dist/config/store.js +4 -1
- package/dist/controlplane/connectionService.js +1 -1
- package/dist/controlplane/server.js +63 -2
- package/dist/controlplane/ui.js +645 -851
- package/dist/ibmi/Tools.js +28 -7
- package/dist/mcp/context.js +270 -41
- package/dist/mcp/tools.js +46 -46
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -104,6 +104,44 @@ npm test
|
|
|
104
104
|
npm run start:controlplane
|
|
105
105
|
```
|
|
106
106
|
|
|
107
|
+
## Automated Release (No Manual Version Bump)
|
|
108
|
+
|
|
109
|
+
Use the release script instead of raw `npm publish`.
|
|
110
|
+
|
|
111
|
+
Patch release:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
npm run release:patch
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Minor release:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
npm run release:minor
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Major release:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
npm run release:major
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
What it does:
|
|
130
|
+
- Verifies git working tree is clean.
|
|
131
|
+
- Runs `npm test` (unless `--skip-tests` is passed).
|
|
132
|
+
- Checks npm published version.
|
|
133
|
+
- Auto-bumps version only when local version is not ahead.
|
|
134
|
+
- Publishes to npm.
|
|
135
|
+
- Pushes commit/tag to GitHub.
|
|
136
|
+
|
|
137
|
+
Optional flags:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
npm run release:patch -- --skip-tests
|
|
141
|
+
npm run release:patch -- --otp=123456
|
|
142
|
+
npm run release:patch -- --no-push
|
|
143
|
+
```
|
|
144
|
+
|
|
107
145
|
## Security Model
|
|
108
146
|
|
|
109
147
|
- Credential-bearing direct tool arguments are blocked for connection creation/update flows.
|
package/dist/config/store.js
CHANGED
|
@@ -17,7 +17,10 @@ const defaultSettings = {
|
|
|
17
17
|
objectFilters: [],
|
|
18
18
|
ifsShortcuts: [],
|
|
19
19
|
debugPort: 8005,
|
|
20
|
-
debugSepPort: 8008
|
|
20
|
+
debugSepPort: 8008,
|
|
21
|
+
sessionIdleMinutes: 30,
|
|
22
|
+
sessionPingSeconds: 15,
|
|
23
|
+
sessionReconnectAttempts: 2
|
|
21
24
|
};
|
|
22
25
|
export class ConfigStore {
|
|
23
26
|
configDir;
|
|
@@ -35,7 +35,7 @@ export class ConnectionService {
|
|
|
35
35
|
port: input.port ?? existing.port,
|
|
36
36
|
username: input.username ?? existing.username,
|
|
37
37
|
privateKeyPath: input.privateKeyPath ?? existing.privateKeyPath,
|
|
38
|
-
settings: input.settings
|
|
38
|
+
settings: input.settings ? { ...(existing.settings || {}), ...input.settings } : existing.settings,
|
|
39
39
|
policy: input.policy ?? existing.policy
|
|
40
40
|
};
|
|
41
41
|
await this.store.upsertConnection(next);
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import http from "http";
|
|
2
2
|
import fs from "fs";
|
|
3
|
+
import fsp from "fs/promises";
|
|
3
4
|
import path from "path";
|
|
4
5
|
import { URL } from "url";
|
|
5
6
|
import { fileURLToPath } from "url";
|
|
6
|
-
import { ConfigStore } from "../config/store.js";
|
|
7
|
+
import { ConfigStore, getConfigDir } from "../config/store.js";
|
|
7
8
|
import { isKeychainAvailable } from "../security/credentialStore.js";
|
|
8
9
|
import { ConnectionService } from "./connectionService.js";
|
|
9
10
|
import { RuntimeService } from "./runtimeService.js";
|
|
@@ -17,7 +18,7 @@ export async function startControlPlaneServer(opts) {
|
|
|
17
18
|
const runtime = new RuntimeService(rootDir);
|
|
18
19
|
const server = http.createServer(async (req, res) => {
|
|
19
20
|
try {
|
|
20
|
-
await routeRequest(req, res, { connections, runtime });
|
|
21
|
+
await routeRequest(req, res, { store, connections, runtime });
|
|
21
22
|
}
|
|
22
23
|
catch (err) {
|
|
23
24
|
sendJson(res, 500, { error: err?.message || String(err) });
|
|
@@ -48,6 +49,42 @@ async function routeRequest(req, res, services) {
|
|
|
48
49
|
sendJson(res, 200, { ok: true, keychainAvailable: await isKeychainAvailable() });
|
|
49
50
|
return;
|
|
50
51
|
}
|
|
52
|
+
if (method === "GET" && pathname === "/api/settings") {
|
|
53
|
+
const settings = await services.store.getSettings();
|
|
54
|
+
sendJson(res, 200, { settings });
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (method === "PUT" && pathname === "/api/settings") {
|
|
58
|
+
const body = await readJson(req);
|
|
59
|
+
const update = {};
|
|
60
|
+
try {
|
|
61
|
+
if (typeof body.sessionIdleMinutes !== "undefined") {
|
|
62
|
+
update.sessionIdleMinutes = parseNumberSetting(body.sessionIdleMinutes, 1, 24 * 60, "sessionIdleMinutes");
|
|
63
|
+
}
|
|
64
|
+
if (typeof body.sessionPingSeconds !== "undefined") {
|
|
65
|
+
update.sessionPingSeconds = parseNumberSetting(body.sessionPingSeconds, 5, 300, "sessionPingSeconds");
|
|
66
|
+
}
|
|
67
|
+
if (typeof body.sessionReconnectAttempts !== "undefined") {
|
|
68
|
+
update.sessionReconnectAttempts = parseNumberSetting(body.sessionReconnectAttempts, 1, 5, "sessionReconnectAttempts");
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
sendJson(res, 400, { error: err?.message || String(err) });
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (Object.keys(update).length === 0) {
|
|
76
|
+
sendJson(res, 400, { error: "No supported settings provided" });
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
await services.store.updateSettings(update);
|
|
80
|
+
const settings = await services.store.getSettings();
|
|
81
|
+
sendJson(res, 200, { settings });
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (method === "GET" && pathname === "/api/sessions") {
|
|
85
|
+
sendJson(res, 200, { snapshot: await readSessionSnapshot() });
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
51
88
|
if (method === "GET" && pathname === "/api/runtime/status") {
|
|
52
89
|
sendJson(res, 200, { jobs: services.runtime.getStatus() });
|
|
53
90
|
return;
|
|
@@ -197,3 +234,27 @@ function getDefaultRootDir() {
|
|
|
197
234
|
}
|
|
198
235
|
return process.cwd();
|
|
199
236
|
}
|
|
237
|
+
function parseNumberSetting(input, min, max, key) {
|
|
238
|
+
const value = Number(input);
|
|
239
|
+
if (!Number.isFinite(value)) {
|
|
240
|
+
throw new Error(`${key} must be a number`);
|
|
241
|
+
}
|
|
242
|
+
const normalized = Math.floor(value);
|
|
243
|
+
if (normalized < min || normalized > max) {
|
|
244
|
+
throw new Error(`${key} must be between ${min} and ${max}`);
|
|
245
|
+
}
|
|
246
|
+
return normalized;
|
|
247
|
+
}
|
|
248
|
+
async function readSessionSnapshot() {
|
|
249
|
+
const snapshotPath = path.join(getConfigDir(), "session-state.json");
|
|
250
|
+
try {
|
|
251
|
+
const raw = await fsp.readFile(snapshotPath, "utf8");
|
|
252
|
+
const parsed = JSON.parse(raw);
|
|
253
|
+
if (!parsed || typeof parsed !== "object")
|
|
254
|
+
return null;
|
|
255
|
+
return parsed;
|
|
256
|
+
}
|
|
257
|
+
catch {
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
}
|