@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.
@@ -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
@@ -0,0 +1,2 @@
1
+ import { i as startServer, n as SERVER_VERSION, r as createServer, t as SERVER_NAME } from "./server2.js";
2
+ export { SERVER_NAME, SERVER_VERSION, createServer, startServer };
@@ -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
+ });
@@ -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
+ }