@alfe.ai/connectwise-screenconnect-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.
- package/README.md +104 -0
- package/dist/bin.cjs +30 -0
- package/dist/bin.d.cts +1 -0
- package/dist/bin.d.ts +1 -0
- package/dist/bin.js +31 -0
- package/dist/screenconnect-client.cjs +5 -0
- package/dist/screenconnect-client.d.cts +92 -0
- package/dist/screenconnect-client.d.ts +92 -0
- package/dist/screenconnect-client.js +2 -0
- package/dist/screenconnect-client2.cjs +357 -0
- package/dist/screenconnect-client2.d.cts +2 -0
- package/dist/screenconnect-client2.d.ts +2 -0
- package/dist/screenconnect-client2.js +280 -0
- package/dist/server.cjs +6 -0
- package/dist/server.d.cts +24 -0
- package/dist/server.d.ts +24 -0
- package/dist/server.js +2 -0
- package/dist/server2.cjs +286 -0
- package/dist/server2.js +263 -0
- package/package.json +50 -0
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { r as ScreenConnectApi } from "./screenconnect-client.js";
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
|
|
4
|
+
//#region src/tools.d.ts
|
|
5
|
+
interface ConnectApi {
|
|
6
|
+
getConnectProviderAccounts(provider: string): Promise<unknown>;
|
|
7
|
+
getConnectionCredentials(connectionId: string): Promise<unknown>;
|
|
8
|
+
}
|
|
9
|
+
interface ToolDependencies {
|
|
10
|
+
createClient?: (credentials: {
|
|
11
|
+
siteUrl: string;
|
|
12
|
+
authenticationSecret: string;
|
|
13
|
+
}) => ScreenConnectApi;
|
|
14
|
+
/** Receives only a static sanitized error and operation name, never credentials or provider bodies. */
|
|
15
|
+
captureError?: (error: Error, operation: string) => void;
|
|
16
|
+
}
|
|
17
|
+
//#endregion
|
|
18
|
+
//#region src/server.d.ts
|
|
19
|
+
declare const SERVER_VERSION: string;
|
|
20
|
+
declare const SERVER_NAME = "connectwise-screenconnect-mcp";
|
|
21
|
+
declare function createServer(api: ConnectApi, dependencies?: ToolDependencies): McpServer;
|
|
22
|
+
declare function startServer(api?: ConnectApi, dependencies?: ToolDependencies): Promise<McpServer>;
|
|
23
|
+
//#endregion
|
|
24
|
+
export { type ConnectApi, SERVER_NAME, SERVER_VERSION, type ToolDependencies, createServer, startServer };
|
package/dist/server.js
ADDED
package/dist/server2.cjs
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
const require_screenconnect_client = require("./screenconnect-client2.cjs");
|
|
2
|
+
let zod = require("zod");
|
|
3
|
+
let node_module = require("node:module");
|
|
4
|
+
let _alfe_ai_agent_api_client = require("@alfe.ai/agent-api-client");
|
|
5
|
+
let _alfe_ai_config = require("@alfe.ai/config");
|
|
6
|
+
let _modelcontextprotocol_sdk_server_mcp_js = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
7
|
+
let _modelcontextprotocol_sdk_server_stdio_js = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
8
|
+
//#region src/tools.ts
|
|
9
|
+
const readAnnotations = {
|
|
10
|
+
readOnlyHint: true,
|
|
11
|
+
destructiveHint: false,
|
|
12
|
+
idempotentHint: true,
|
|
13
|
+
openWorldHint: true
|
|
14
|
+
};
|
|
15
|
+
const addAnnotations = {
|
|
16
|
+
readOnlyHint: false,
|
|
17
|
+
destructiveHint: false,
|
|
18
|
+
idempotentHint: false,
|
|
19
|
+
openWorldHint: true
|
|
20
|
+
};
|
|
21
|
+
const updateAnnotations = {
|
|
22
|
+
readOnlyHint: false,
|
|
23
|
+
destructiveHint: true,
|
|
24
|
+
idempotentHint: true,
|
|
25
|
+
openWorldHint: true
|
|
26
|
+
};
|
|
27
|
+
const actionAnnotations = {
|
|
28
|
+
readOnlyHint: false,
|
|
29
|
+
destructiveHint: true,
|
|
30
|
+
idempotentHint: false,
|
|
31
|
+
openWorldHint: true
|
|
32
|
+
};
|
|
33
|
+
const selected = { connectionId: require_screenconnect_client.connectionIdField };
|
|
34
|
+
const session = {
|
|
35
|
+
...selected,
|
|
36
|
+
sessionId: require_screenconnect_client.sessionIdField
|
|
37
|
+
};
|
|
38
|
+
const limitField = zod.z.coerce.number().int().min(1).max(500).default(100);
|
|
39
|
+
function registerTools(server, api, dependencies = {}) {
|
|
40
|
+
const createClient = dependencies.createClient ?? ((credentials) => new require_screenconnect_client.ScreenConnectClient(credentials));
|
|
41
|
+
const client = async (connectionId) => {
|
|
42
|
+
return createClient(require_screenconnect_client.parseCredentials(await api.getConnectionCredentials(connectionId), connectionId));
|
|
43
|
+
};
|
|
44
|
+
const guarded = async (operation, work) => {
|
|
45
|
+
try {
|
|
46
|
+
return { content: [{
|
|
47
|
+
type: "text",
|
|
48
|
+
text: JSON.stringify(await work())
|
|
49
|
+
}] };
|
|
50
|
+
} catch (error) {
|
|
51
|
+
const message = require_screenconnect_client.safeErrorMessage(error);
|
|
52
|
+
try {
|
|
53
|
+
dependencies.captureError?.(new Error(message), operation);
|
|
54
|
+
} catch {}
|
|
55
|
+
return {
|
|
56
|
+
isError: true,
|
|
57
|
+
content: [{
|
|
58
|
+
type: "text",
|
|
59
|
+
text: message
|
|
60
|
+
}]
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
server.registerTool("screenconnect_list_connections", {
|
|
65
|
+
description: "List authorized ScreenConnect connections and their exact connectionId selectors. Does not expose authentication secrets.",
|
|
66
|
+
inputSchema: {},
|
|
67
|
+
annotations: readAnnotations
|
|
68
|
+
}, () => guarded("list_connections", async () => {
|
|
69
|
+
const raw = await api.getConnectProviderAccounts(require_screenconnect_client.PROVIDER);
|
|
70
|
+
if (!require_screenconnect_client.isRecord(raw) || raw.provider !== "connectwise-screenconnect" || !Array.isArray(raw.accounts) || raw.accounts.length > 500) throw new require_screenconnect_client.ScreenConnectError("Invalid ScreenConnect connection list returned by Alfe.");
|
|
71
|
+
return { connections: raw.accounts.map((account) => {
|
|
72
|
+
const row = zod.z.object({
|
|
73
|
+
connectionId: require_screenconnect_client.connectionIdField,
|
|
74
|
+
accountIdentifier: zod.z.string().max(2048),
|
|
75
|
+
displayName: zod.z.string().max(512).nullable().optional(),
|
|
76
|
+
siteUrl: zod.z.string()
|
|
77
|
+
}).safeParse(account);
|
|
78
|
+
if (!row.success) throw new require_screenconnect_client.ScreenConnectError("Invalid ScreenConnect connection metadata returned by Alfe.");
|
|
79
|
+
return {
|
|
80
|
+
...row.data,
|
|
81
|
+
siteUrl: require_screenconnect_client.normalizeSiteUrl(row.data.siteUrl)
|
|
82
|
+
};
|
|
83
|
+
}) };
|
|
84
|
+
}));
|
|
85
|
+
server.registerTool("screenconnect_check_connection", {
|
|
86
|
+
description: "Verify authentication and the RESTful API Manager extension with an empty session filter. Does not change sessions.",
|
|
87
|
+
inputSchema: selected,
|
|
88
|
+
annotations: readAnnotations
|
|
89
|
+
}, (args) => guarded("check_connection", async () => {
|
|
90
|
+
boundedSessions(await (await client(args.connectionId)).call("GetSessionsByFilter", ["SessionID = '00000000-0000-0000-0000-000000000000'"]), 1);
|
|
91
|
+
return { connected: true };
|
|
92
|
+
}));
|
|
93
|
+
server.registerTool("screenconnect_list_sessions", {
|
|
94
|
+
description: "Find sessions with a required ScreenConnect session filter (for example SessionType = 'Access'). Limit bounds returned items only; the extension has no pagination. Narrow filters for large instances.",
|
|
95
|
+
inputSchema: {
|
|
96
|
+
...selected,
|
|
97
|
+
filter: require_screenconnect_client.textField(2048),
|
|
98
|
+
limit: limitField
|
|
99
|
+
},
|
|
100
|
+
annotations: readAnnotations
|
|
101
|
+
}, (args) => guarded("list_sessions", async () => boundedSessions(await (await client(args.connectionId)).call("GetSessionsByFilter", [args.filter]), args.limit)));
|
|
102
|
+
server.registerTool("screenconnect_find_sessions", {
|
|
103
|
+
description: "Look up sessions by name. Limit bounds returned items; the extension does not expose pagination.",
|
|
104
|
+
inputSchema: {
|
|
105
|
+
...selected,
|
|
106
|
+
name: require_screenconnect_client.textField(256),
|
|
107
|
+
limit: limitField
|
|
108
|
+
},
|
|
109
|
+
annotations: readAnnotations
|
|
110
|
+
}, (args) => guarded("find_sessions", async () => boundedSessions(await (await client(args.connectionId)).call("GetSessionsByName", [args.name]), args.limit)));
|
|
111
|
+
server.registerTool("screenconnect_get_session", {
|
|
112
|
+
description: "Get session data by its ScreenConnect session GUID.",
|
|
113
|
+
inputSchema: session,
|
|
114
|
+
annotations: readAnnotations
|
|
115
|
+
}, (args) => guarded("get_session", async () => (await client(args.connectionId)).call("GetSessionBySessionID", [args.sessionId])));
|
|
116
|
+
server.registerTool("screenconnect_get_session_details", {
|
|
117
|
+
description: "Get session details including events and connection history. Screenshot bytes are omitted.",
|
|
118
|
+
inputSchema: session,
|
|
119
|
+
annotations: readAnnotations
|
|
120
|
+
}, (args) => guarded("get_session_details", async () => {
|
|
121
|
+
const details = await (await client(args.connectionId)).call("GetSessionDetailsBySessionID", [args.sessionId]);
|
|
122
|
+
if (details === null) return null;
|
|
123
|
+
if (!require_screenconnect_client.isRecord(details)) throw new require_screenconnect_client.ScreenConnectError("ScreenConnect returned malformed session details.");
|
|
124
|
+
return Object.fromEntries(Object.entries(details).filter(([key]) => key !== "GuestScreenshotContent"));
|
|
125
|
+
}));
|
|
126
|
+
server.registerTool("screenconnect_create_session", {
|
|
127
|
+
description: "Create a Support or Meeting session. Access agents/installers are a separate ScreenConnect flow. Private sessions require a join code.",
|
|
128
|
+
inputSchema: {
|
|
129
|
+
...selected,
|
|
130
|
+
sessionType: zod.z.enum(["Support", "Meeting"]),
|
|
131
|
+
name: require_screenconnect_client.textField(256),
|
|
132
|
+
isPublic: zod.z.boolean().default(false),
|
|
133
|
+
code: zod.z.string().max(128).default(""),
|
|
134
|
+
customProperties: require_screenconnect_client.customPropertiesField.default([])
|
|
135
|
+
},
|
|
136
|
+
annotations: addAnnotations
|
|
137
|
+
}, (args) => guarded("create_session", async () => {
|
|
138
|
+
if (!args.isPublic && !args.code.trim()) throw new require_screenconnect_client.ScreenConnectError("Private ScreenConnect sessions require a join code.");
|
|
139
|
+
return (await client(args.connectionId)).call("CreateSession", [
|
|
140
|
+
args.sessionType,
|
|
141
|
+
args.name,
|
|
142
|
+
args.isPublic,
|
|
143
|
+
args.code,
|
|
144
|
+
args.customProperties
|
|
145
|
+
]);
|
|
146
|
+
}));
|
|
147
|
+
server.registerTool("screenconnect_rename_session", {
|
|
148
|
+
description: "Replace the display name of one ScreenConnect session.",
|
|
149
|
+
inputSchema: {
|
|
150
|
+
...session,
|
|
151
|
+
name: require_screenconnect_client.textField(256)
|
|
152
|
+
},
|
|
153
|
+
annotations: updateAnnotations
|
|
154
|
+
}, (args) => guarded("rename_session", async () => {
|
|
155
|
+
await (await client(args.connectionId)).call("UpdateSessionName", [args.sessionId, args.name]);
|
|
156
|
+
return { updated: true };
|
|
157
|
+
}));
|
|
158
|
+
server.registerTool("screenconnect_update_custom_properties", {
|
|
159
|
+
description: "Replace all custom property values in positional order (up to eight). Read the session first and retain any values that should remain.",
|
|
160
|
+
inputSchema: {
|
|
161
|
+
...session,
|
|
162
|
+
customProperties: require_screenconnect_client.customPropertiesField
|
|
163
|
+
},
|
|
164
|
+
annotations: updateAnnotations
|
|
165
|
+
}, (args) => guarded("update_custom_properties", async () => {
|
|
166
|
+
await (await client(args.connectionId)).call("UpdateSessionCustomProperties", [args.sessionId, args.customProperties]);
|
|
167
|
+
return { updated: true };
|
|
168
|
+
}));
|
|
169
|
+
server.registerTool("screenconnect_add_note", {
|
|
170
|
+
description: "Append a note to one ScreenConnect session (RESTful API Manager 1.0.6 or newer).",
|
|
171
|
+
inputSchema: {
|
|
172
|
+
...session,
|
|
173
|
+
note: require_screenconnect_client.textField(16384)
|
|
174
|
+
},
|
|
175
|
+
annotations: addAnnotations
|
|
176
|
+
}, (args) => guarded("add_note", async () => {
|
|
177
|
+
await (await client(args.connectionId)).call("AddNoteToSession", [args.sessionId, args.note]);
|
|
178
|
+
return { accepted: true };
|
|
179
|
+
}));
|
|
180
|
+
server.registerTool("screenconnect_send_message", {
|
|
181
|
+
description: "Send a chat message to one session, attributed to the specified host name.",
|
|
182
|
+
inputSchema: {
|
|
183
|
+
...session,
|
|
184
|
+
byHost: require_screenconnect_client.textField(256),
|
|
185
|
+
message: require_screenconnect_client.textField(16384)
|
|
186
|
+
},
|
|
187
|
+
annotations: addAnnotations
|
|
188
|
+
}, (args) => guarded("send_message", async () => {
|
|
189
|
+
await (await client(args.connectionId)).call("SendMessageToSession", [
|
|
190
|
+
args.sessionId,
|
|
191
|
+
args.byHost,
|
|
192
|
+
args.message
|
|
193
|
+
]);
|
|
194
|
+
return { accepted: true };
|
|
195
|
+
}));
|
|
196
|
+
server.registerTool("screenconnect_run_command", {
|
|
197
|
+
description: "Queue a command on the selected remote session. Confirm the exact session and command. Acceptance does not prove execution; inspect session details for results before retrying.",
|
|
198
|
+
inputSchema: {
|
|
199
|
+
...session,
|
|
200
|
+
command: require_screenconnect_client.textField(32768),
|
|
201
|
+
confirmSessionId: require_screenconnect_client.sessionIdField,
|
|
202
|
+
confirmCommand: require_screenconnect_client.textField(32768)
|
|
203
|
+
},
|
|
204
|
+
annotations: actionAnnotations
|
|
205
|
+
}, (args) => guarded("run_command", async () => {
|
|
206
|
+
if (args.confirmSessionId !== args.sessionId || args.confirmCommand !== args.command) throw new require_screenconnect_client.ScreenConnectError("Confirm the exact sessionId and command before running a remote command.");
|
|
207
|
+
await (await client(args.connectionId)).call("SendCommandToSession", [args.sessionId, args.command]);
|
|
208
|
+
return { accepted: true };
|
|
209
|
+
}));
|
|
210
|
+
server.registerTool("screenconnect_run_toolbox_item", {
|
|
211
|
+
description: "Queue an existing toolbox item on one remote session. Confirm the exact session and item. Acceptance does not prove execution.",
|
|
212
|
+
inputSchema: {
|
|
213
|
+
...session,
|
|
214
|
+
toolboxItemName: require_screenconnect_client.textField(512),
|
|
215
|
+
confirmSessionId: require_screenconnect_client.sessionIdField,
|
|
216
|
+
confirmToolboxItemName: require_screenconnect_client.textField(512)
|
|
217
|
+
},
|
|
218
|
+
annotations: actionAnnotations
|
|
219
|
+
}, (args) => guarded("run_toolbox_item", async () => {
|
|
220
|
+
if (args.confirmSessionId !== args.sessionId || args.confirmToolboxItemName !== args.toolboxItemName) throw new require_screenconnect_client.ScreenConnectError("Confirm the exact sessionId and toolboxItemName before running a toolbox item.");
|
|
221
|
+
await (await client(args.connectionId)).call("SendToolboxItemToSession", [args.sessionId, args.toolboxItemName]);
|
|
222
|
+
return { accepted: true };
|
|
223
|
+
}));
|
|
224
|
+
}
|
|
225
|
+
function boundedSessions(value, limit) {
|
|
226
|
+
if (!Array.isArray(value) || !value.every(require_screenconnect_client.isRecord)) throw new require_screenconnect_client.ScreenConnectError("ScreenConnect returned a malformed session list.");
|
|
227
|
+
return {
|
|
228
|
+
sessions: value.slice(0, limit),
|
|
229
|
+
returned: Math.min(value.length, limit),
|
|
230
|
+
total: value.length,
|
|
231
|
+
truncated: value.length > limit
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
const SERVER_VERSION = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href)("../package.json").version;
|
|
235
|
+
const SERVER_NAME = "connectwise-screenconnect-mcp";
|
|
236
|
+
function createServer(api, dependencies = {}) {
|
|
237
|
+
const server = new _modelcontextprotocol_sdk_server_mcp_js.McpServer({
|
|
238
|
+
name: SERVER_NAME,
|
|
239
|
+
version: SERVER_VERSION
|
|
240
|
+
});
|
|
241
|
+
registerTools(server, api, dependencies);
|
|
242
|
+
return server;
|
|
243
|
+
}
|
|
244
|
+
async function startServer(api, dependencies = {}) {
|
|
245
|
+
let client = api;
|
|
246
|
+
if (!client) {
|
|
247
|
+
const { apiKey, apiUrl } = (0, _alfe_ai_config.resolveConfig)();
|
|
248
|
+
client = new _alfe_ai_agent_api_client.AgentApiClient({
|
|
249
|
+
apiKey,
|
|
250
|
+
apiUrl
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
const server = createServer(client, dependencies);
|
|
254
|
+
try {
|
|
255
|
+
await server.connect(new _modelcontextprotocol_sdk_server_stdio_js.StdioServerTransport());
|
|
256
|
+
return server;
|
|
257
|
+
} catch (error) {
|
|
258
|
+
await server.close().catch(() => void 0);
|
|
259
|
+
throw error;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
//#endregion
|
|
263
|
+
Object.defineProperty(exports, "SERVER_NAME", {
|
|
264
|
+
enumerable: true,
|
|
265
|
+
get: function() {
|
|
266
|
+
return SERVER_NAME;
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
Object.defineProperty(exports, "SERVER_VERSION", {
|
|
270
|
+
enumerable: true,
|
|
271
|
+
get: function() {
|
|
272
|
+
return SERVER_VERSION;
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
Object.defineProperty(exports, "createServer", {
|
|
276
|
+
enumerable: true,
|
|
277
|
+
get: function() {
|
|
278
|
+
return createServer;
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
Object.defineProperty(exports, "startServer", {
|
|
282
|
+
enumerable: true,
|
|
283
|
+
get: function() {
|
|
284
|
+
return startServer;
|
|
285
|
+
}
|
|
286
|
+
});
|
package/dist/server2.js
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import { a as ScreenConnectError, c as isRecord, d as safeErrorMessage, f as sessionIdField, i as PROVIDER, l as normalizeSiteUrl, n as ScreenConnectClient, o as connectionIdField, p as textField, s as customPropertiesField, u as parseCredentials } from "./screenconnect-client2.js";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { AgentApiClient } from "@alfe.ai/agent-api-client";
|
|
5
|
+
import { resolveConfig } from "@alfe.ai/config";
|
|
6
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
7
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
8
|
+
//#region src/tools.ts
|
|
9
|
+
const readAnnotations = {
|
|
10
|
+
readOnlyHint: true,
|
|
11
|
+
destructiveHint: false,
|
|
12
|
+
idempotentHint: true,
|
|
13
|
+
openWorldHint: true
|
|
14
|
+
};
|
|
15
|
+
const addAnnotations = {
|
|
16
|
+
readOnlyHint: false,
|
|
17
|
+
destructiveHint: false,
|
|
18
|
+
idempotentHint: false,
|
|
19
|
+
openWorldHint: true
|
|
20
|
+
};
|
|
21
|
+
const updateAnnotations = {
|
|
22
|
+
readOnlyHint: false,
|
|
23
|
+
destructiveHint: true,
|
|
24
|
+
idempotentHint: true,
|
|
25
|
+
openWorldHint: true
|
|
26
|
+
};
|
|
27
|
+
const actionAnnotations = {
|
|
28
|
+
readOnlyHint: false,
|
|
29
|
+
destructiveHint: true,
|
|
30
|
+
idempotentHint: false,
|
|
31
|
+
openWorldHint: true
|
|
32
|
+
};
|
|
33
|
+
const selected = { connectionId: connectionIdField };
|
|
34
|
+
const session = {
|
|
35
|
+
...selected,
|
|
36
|
+
sessionId: sessionIdField
|
|
37
|
+
};
|
|
38
|
+
const limitField = z.coerce.number().int().min(1).max(500).default(100);
|
|
39
|
+
function registerTools(server, api, dependencies = {}) {
|
|
40
|
+
const createClient = dependencies.createClient ?? ((credentials) => new ScreenConnectClient(credentials));
|
|
41
|
+
const client = async (connectionId) => {
|
|
42
|
+
return createClient(parseCredentials(await api.getConnectionCredentials(connectionId), connectionId));
|
|
43
|
+
};
|
|
44
|
+
const guarded = async (operation, work) => {
|
|
45
|
+
try {
|
|
46
|
+
return { content: [{
|
|
47
|
+
type: "text",
|
|
48
|
+
text: JSON.stringify(await work())
|
|
49
|
+
}] };
|
|
50
|
+
} catch (error) {
|
|
51
|
+
const message = safeErrorMessage(error);
|
|
52
|
+
try {
|
|
53
|
+
dependencies.captureError?.(new Error(message), operation);
|
|
54
|
+
} catch {}
|
|
55
|
+
return {
|
|
56
|
+
isError: true,
|
|
57
|
+
content: [{
|
|
58
|
+
type: "text",
|
|
59
|
+
text: message
|
|
60
|
+
}]
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
server.registerTool("screenconnect_list_connections", {
|
|
65
|
+
description: "List authorized ScreenConnect connections and their exact connectionId selectors. Does not expose authentication secrets.",
|
|
66
|
+
inputSchema: {},
|
|
67
|
+
annotations: readAnnotations
|
|
68
|
+
}, () => guarded("list_connections", async () => {
|
|
69
|
+
const raw = await api.getConnectProviderAccounts(PROVIDER);
|
|
70
|
+
if (!isRecord(raw) || raw.provider !== "connectwise-screenconnect" || !Array.isArray(raw.accounts) || raw.accounts.length > 500) throw new ScreenConnectError("Invalid ScreenConnect connection list returned by Alfe.");
|
|
71
|
+
return { connections: raw.accounts.map((account) => {
|
|
72
|
+
const row = z.object({
|
|
73
|
+
connectionId: connectionIdField,
|
|
74
|
+
accountIdentifier: z.string().max(2048),
|
|
75
|
+
displayName: z.string().max(512).nullable().optional(),
|
|
76
|
+
siteUrl: z.string()
|
|
77
|
+
}).safeParse(account);
|
|
78
|
+
if (!row.success) throw new ScreenConnectError("Invalid ScreenConnect connection metadata returned by Alfe.");
|
|
79
|
+
return {
|
|
80
|
+
...row.data,
|
|
81
|
+
siteUrl: normalizeSiteUrl(row.data.siteUrl)
|
|
82
|
+
};
|
|
83
|
+
}) };
|
|
84
|
+
}));
|
|
85
|
+
server.registerTool("screenconnect_check_connection", {
|
|
86
|
+
description: "Verify authentication and the RESTful API Manager extension with an empty session filter. Does not change sessions.",
|
|
87
|
+
inputSchema: selected,
|
|
88
|
+
annotations: readAnnotations
|
|
89
|
+
}, (args) => guarded("check_connection", async () => {
|
|
90
|
+
boundedSessions(await (await client(args.connectionId)).call("GetSessionsByFilter", ["SessionID = '00000000-0000-0000-0000-000000000000'"]), 1);
|
|
91
|
+
return { connected: true };
|
|
92
|
+
}));
|
|
93
|
+
server.registerTool("screenconnect_list_sessions", {
|
|
94
|
+
description: "Find sessions with a required ScreenConnect session filter (for example SessionType = 'Access'). Limit bounds returned items only; the extension has no pagination. Narrow filters for large instances.",
|
|
95
|
+
inputSchema: {
|
|
96
|
+
...selected,
|
|
97
|
+
filter: textField(2048),
|
|
98
|
+
limit: limitField
|
|
99
|
+
},
|
|
100
|
+
annotations: readAnnotations
|
|
101
|
+
}, (args) => guarded("list_sessions", async () => boundedSessions(await (await client(args.connectionId)).call("GetSessionsByFilter", [args.filter]), args.limit)));
|
|
102
|
+
server.registerTool("screenconnect_find_sessions", {
|
|
103
|
+
description: "Look up sessions by name. Limit bounds returned items; the extension does not expose pagination.",
|
|
104
|
+
inputSchema: {
|
|
105
|
+
...selected,
|
|
106
|
+
name: textField(256),
|
|
107
|
+
limit: limitField
|
|
108
|
+
},
|
|
109
|
+
annotations: readAnnotations
|
|
110
|
+
}, (args) => guarded("find_sessions", async () => boundedSessions(await (await client(args.connectionId)).call("GetSessionsByName", [args.name]), args.limit)));
|
|
111
|
+
server.registerTool("screenconnect_get_session", {
|
|
112
|
+
description: "Get session data by its ScreenConnect session GUID.",
|
|
113
|
+
inputSchema: session,
|
|
114
|
+
annotations: readAnnotations
|
|
115
|
+
}, (args) => guarded("get_session", async () => (await client(args.connectionId)).call("GetSessionBySessionID", [args.sessionId])));
|
|
116
|
+
server.registerTool("screenconnect_get_session_details", {
|
|
117
|
+
description: "Get session details including events and connection history. Screenshot bytes are omitted.",
|
|
118
|
+
inputSchema: session,
|
|
119
|
+
annotations: readAnnotations
|
|
120
|
+
}, (args) => guarded("get_session_details", async () => {
|
|
121
|
+
const details = await (await client(args.connectionId)).call("GetSessionDetailsBySessionID", [args.sessionId]);
|
|
122
|
+
if (details === null) return null;
|
|
123
|
+
if (!isRecord(details)) throw new ScreenConnectError("ScreenConnect returned malformed session details.");
|
|
124
|
+
return Object.fromEntries(Object.entries(details).filter(([key]) => key !== "GuestScreenshotContent"));
|
|
125
|
+
}));
|
|
126
|
+
server.registerTool("screenconnect_create_session", {
|
|
127
|
+
description: "Create a Support or Meeting session. Access agents/installers are a separate ScreenConnect flow. Private sessions require a join code.",
|
|
128
|
+
inputSchema: {
|
|
129
|
+
...selected,
|
|
130
|
+
sessionType: z.enum(["Support", "Meeting"]),
|
|
131
|
+
name: textField(256),
|
|
132
|
+
isPublic: z.boolean().default(false),
|
|
133
|
+
code: z.string().max(128).default(""),
|
|
134
|
+
customProperties: customPropertiesField.default([])
|
|
135
|
+
},
|
|
136
|
+
annotations: addAnnotations
|
|
137
|
+
}, (args) => guarded("create_session", async () => {
|
|
138
|
+
if (!args.isPublic && !args.code.trim()) throw new ScreenConnectError("Private ScreenConnect sessions require a join code.");
|
|
139
|
+
return (await client(args.connectionId)).call("CreateSession", [
|
|
140
|
+
args.sessionType,
|
|
141
|
+
args.name,
|
|
142
|
+
args.isPublic,
|
|
143
|
+
args.code,
|
|
144
|
+
args.customProperties
|
|
145
|
+
]);
|
|
146
|
+
}));
|
|
147
|
+
server.registerTool("screenconnect_rename_session", {
|
|
148
|
+
description: "Replace the display name of one ScreenConnect session.",
|
|
149
|
+
inputSchema: {
|
|
150
|
+
...session,
|
|
151
|
+
name: textField(256)
|
|
152
|
+
},
|
|
153
|
+
annotations: updateAnnotations
|
|
154
|
+
}, (args) => guarded("rename_session", async () => {
|
|
155
|
+
await (await client(args.connectionId)).call("UpdateSessionName", [args.sessionId, args.name]);
|
|
156
|
+
return { updated: true };
|
|
157
|
+
}));
|
|
158
|
+
server.registerTool("screenconnect_update_custom_properties", {
|
|
159
|
+
description: "Replace all custom property values in positional order (up to eight). Read the session first and retain any values that should remain.",
|
|
160
|
+
inputSchema: {
|
|
161
|
+
...session,
|
|
162
|
+
customProperties: customPropertiesField
|
|
163
|
+
},
|
|
164
|
+
annotations: updateAnnotations
|
|
165
|
+
}, (args) => guarded("update_custom_properties", async () => {
|
|
166
|
+
await (await client(args.connectionId)).call("UpdateSessionCustomProperties", [args.sessionId, args.customProperties]);
|
|
167
|
+
return { updated: true };
|
|
168
|
+
}));
|
|
169
|
+
server.registerTool("screenconnect_add_note", {
|
|
170
|
+
description: "Append a note to one ScreenConnect session (RESTful API Manager 1.0.6 or newer).",
|
|
171
|
+
inputSchema: {
|
|
172
|
+
...session,
|
|
173
|
+
note: textField(16384)
|
|
174
|
+
},
|
|
175
|
+
annotations: addAnnotations
|
|
176
|
+
}, (args) => guarded("add_note", async () => {
|
|
177
|
+
await (await client(args.connectionId)).call("AddNoteToSession", [args.sessionId, args.note]);
|
|
178
|
+
return { accepted: true };
|
|
179
|
+
}));
|
|
180
|
+
server.registerTool("screenconnect_send_message", {
|
|
181
|
+
description: "Send a chat message to one session, attributed to the specified host name.",
|
|
182
|
+
inputSchema: {
|
|
183
|
+
...session,
|
|
184
|
+
byHost: textField(256),
|
|
185
|
+
message: textField(16384)
|
|
186
|
+
},
|
|
187
|
+
annotations: addAnnotations
|
|
188
|
+
}, (args) => guarded("send_message", async () => {
|
|
189
|
+
await (await client(args.connectionId)).call("SendMessageToSession", [
|
|
190
|
+
args.sessionId,
|
|
191
|
+
args.byHost,
|
|
192
|
+
args.message
|
|
193
|
+
]);
|
|
194
|
+
return { accepted: true };
|
|
195
|
+
}));
|
|
196
|
+
server.registerTool("screenconnect_run_command", {
|
|
197
|
+
description: "Queue a command on the selected remote session. Confirm the exact session and command. Acceptance does not prove execution; inspect session details for results before retrying.",
|
|
198
|
+
inputSchema: {
|
|
199
|
+
...session,
|
|
200
|
+
command: textField(32768),
|
|
201
|
+
confirmSessionId: sessionIdField,
|
|
202
|
+
confirmCommand: textField(32768)
|
|
203
|
+
},
|
|
204
|
+
annotations: actionAnnotations
|
|
205
|
+
}, (args) => guarded("run_command", async () => {
|
|
206
|
+
if (args.confirmSessionId !== args.sessionId || args.confirmCommand !== args.command) throw new ScreenConnectError("Confirm the exact sessionId and command before running a remote command.");
|
|
207
|
+
await (await client(args.connectionId)).call("SendCommandToSession", [args.sessionId, args.command]);
|
|
208
|
+
return { accepted: true };
|
|
209
|
+
}));
|
|
210
|
+
server.registerTool("screenconnect_run_toolbox_item", {
|
|
211
|
+
description: "Queue an existing toolbox item on one remote session. Confirm the exact session and item. Acceptance does not prove execution.",
|
|
212
|
+
inputSchema: {
|
|
213
|
+
...session,
|
|
214
|
+
toolboxItemName: textField(512),
|
|
215
|
+
confirmSessionId: sessionIdField,
|
|
216
|
+
confirmToolboxItemName: textField(512)
|
|
217
|
+
},
|
|
218
|
+
annotations: actionAnnotations
|
|
219
|
+
}, (args) => guarded("run_toolbox_item", async () => {
|
|
220
|
+
if (args.confirmSessionId !== args.sessionId || args.confirmToolboxItemName !== args.toolboxItemName) throw new ScreenConnectError("Confirm the exact sessionId and toolboxItemName before running a toolbox item.");
|
|
221
|
+
await (await client(args.connectionId)).call("SendToolboxItemToSession", [args.sessionId, args.toolboxItemName]);
|
|
222
|
+
return { accepted: true };
|
|
223
|
+
}));
|
|
224
|
+
}
|
|
225
|
+
function boundedSessions(value, limit) {
|
|
226
|
+
if (!Array.isArray(value) || !value.every(isRecord)) throw new ScreenConnectError("ScreenConnect returned a malformed session list.");
|
|
227
|
+
return {
|
|
228
|
+
sessions: value.slice(0, limit),
|
|
229
|
+
returned: Math.min(value.length, limit),
|
|
230
|
+
total: value.length,
|
|
231
|
+
truncated: value.length > limit
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
const SERVER_VERSION = createRequire(import.meta.url)("../package.json").version;
|
|
235
|
+
const SERVER_NAME = "connectwise-screenconnect-mcp";
|
|
236
|
+
function createServer(api, dependencies = {}) {
|
|
237
|
+
const server = new McpServer({
|
|
238
|
+
name: SERVER_NAME,
|
|
239
|
+
version: SERVER_VERSION
|
|
240
|
+
});
|
|
241
|
+
registerTools(server, api, dependencies);
|
|
242
|
+
return server;
|
|
243
|
+
}
|
|
244
|
+
async function startServer(api, dependencies = {}) {
|
|
245
|
+
let client = api;
|
|
246
|
+
if (!client) {
|
|
247
|
+
const { apiKey, apiUrl } = resolveConfig();
|
|
248
|
+
client = new AgentApiClient({
|
|
249
|
+
apiKey,
|
|
250
|
+
apiUrl
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
const server = createServer(client, dependencies);
|
|
254
|
+
try {
|
|
255
|
+
await server.connect(new StdioServerTransport());
|
|
256
|
+
return server;
|
|
257
|
+
} catch (error) {
|
|
258
|
+
await server.close().catch(() => void 0);
|
|
259
|
+
throw error;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
//#endregion
|
|
263
|
+
export { startServer as i, SERVER_VERSION as n, createServer as r, SERVER_NAME as t };
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@alfe.ai/connectwise-screenconnect-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "ScreenConnect session management through the RESTful API Manager extension and Alfe connections",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/server.js",
|
|
7
|
+
"types": "./dist/server.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"connectwise-screenconnect-mcp": "./dist/bin.js"
|
|
10
|
+
},
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/server.d.ts",
|
|
14
|
+
"import": "./dist/server.js",
|
|
15
|
+
"require": "./dist/server.cjs"
|
|
16
|
+
},
|
|
17
|
+
"./client": {
|
|
18
|
+
"types": "./dist/screenconnect-client.d.ts",
|
|
19
|
+
"import": "./dist/screenconnect-client.js",
|
|
20
|
+
"require": "./dist/screenconnect-client.cjs"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"README.md"
|
|
26
|
+
],
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
29
|
+
"zod": "^4.0.5",
|
|
30
|
+
"@alfe.ai/agent-api-client": "0.19.0",
|
|
31
|
+
"@alfe.ai/config": "0.4.1"
|
|
32
|
+
},
|
|
33
|
+
"license": "UNLICENSED",
|
|
34
|
+
"homepage": "https://alfe.ai",
|
|
35
|
+
"author": "Alfe (https://alfe.ai)",
|
|
36
|
+
"keywords": [
|
|
37
|
+
"alfe",
|
|
38
|
+
"mcp",
|
|
39
|
+
"screenconnect",
|
|
40
|
+
"connectwise",
|
|
41
|
+
"remote-support"
|
|
42
|
+
],
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "tsdown",
|
|
45
|
+
"dev": "tsdown --watch",
|
|
46
|
+
"typecheck": "tsc --noEmit",
|
|
47
|
+
"lint": "eslint .",
|
|
48
|
+
"test": "vitest run"
|
|
49
|
+
}
|
|
50
|
+
}
|