@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/README.md
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# ConnectWise ScreenConnect MCP
|
|
2
|
+
|
|
3
|
+
Runtime-independent stdio MCP server for ScreenConnect's RESTful API Manager
|
|
4
|
+
extension. Alfe Connections supplies credentials at execution time. Every
|
|
5
|
+
provider operation requires the exact `connectionId` returned by
|
|
6
|
+
`screenconnect_list_connections`.
|
|
7
|
+
|
|
8
|
+
## Setup
|
|
9
|
+
|
|
10
|
+
1. Install the **RESTful API Manager** extension in the ScreenConnect
|
|
11
|
+
administration Extension Marketplace. This integration implements the
|
|
12
|
+
documented 1.0.8 API contract (server 22.3 or newer); notes need 1.0.6+.
|
|
13
|
+
2. Set the extension's `RESTfulAuthenticationSecret` to a strong random secret
|
|
14
|
+
and configure its execution user/permissions for the operations you need.
|
|
15
|
+
3. Leave `RESTfulAllowedOrigin` empty or set it to the ScreenConnect site's
|
|
16
|
+
origin, such as `https://support.example.com`. The runtime sends that origin
|
|
17
|
+
on requests.
|
|
18
|
+
4. Add a **ConnectWise ScreenConnect** connection in Alfe with that site's
|
|
19
|
+
HTTPS origin and authentication secret, at a scope the agent can access.
|
|
20
|
+
5. The connection-driven integration launches the server automatically.
|
|
21
|
+
For manual testing on an already configured agent:
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
npx -y @alfe.ai/connectwise-screenconnect-mcp@0.1.0
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`@alfe.ai/config` supplies the Alfe API key and API URL from the normal agent
|
|
28
|
+
configuration. No ScreenConnect credentials are passed through command-line
|
|
29
|
+
arguments or integration environment variables. The binary is
|
|
30
|
+
`connectwise-screenconnect-mcp`.
|
|
31
|
+
|
|
32
|
+
## Tools
|
|
33
|
+
|
|
34
|
+
| Tool | Purpose |
|
|
35
|
+
| --- | --- |
|
|
36
|
+
| `screenconnect_list_connections` | Discover connections without exposing secrets |
|
|
37
|
+
| `screenconnect_check_connection` | Verify extension access with a read that matches no sessions |
|
|
38
|
+
| `screenconnect_list_sessions` | Query sessions by a required session filter |
|
|
39
|
+
| `screenconnect_find_sessions` | Find sessions by name |
|
|
40
|
+
| `screenconnect_get_session` | Retrieve a session by its GUID |
|
|
41
|
+
| `screenconnect_get_session_details` | Inspect events and connections, omitting screenshot bytes |
|
|
42
|
+
| `screenconnect_create_session` | Create Support or Meeting sessions |
|
|
43
|
+
| `screenconnect_rename_session` | Replace a session's display name |
|
|
44
|
+
| `screenconnect_update_custom_properties` | Replace up to eight positional custom properties |
|
|
45
|
+
| `screenconnect_add_note` | Append a session note |
|
|
46
|
+
| `screenconnect_send_message` | Send a message attributed to a host |
|
|
47
|
+
| `screenconnect_run_command` | Queue a remote command with exact target/content confirmation |
|
|
48
|
+
| `screenconnect_run_toolbox_item` | Queue an existing toolbox item with exact target/item confirmation |
|
|
49
|
+
|
|
50
|
+
The API has no session pagination. The required filter controls the provider
|
|
51
|
+
query; `limit` (1–500, default 100) limits the returned tool result and reports
|
|
52
|
+
truncation. Narrow filters when the provider response exceeds 5 MiB.
|
|
53
|
+
|
|
54
|
+
Create supports `Support` and `Meeting`, and private sessions require a join
|
|
55
|
+
code. Access agent installation is a different API and is not exposed here.
|
|
56
|
+
Custom-property updates replace the entire positional array, so read the
|
|
57
|
+
current values and preserve those you want to keep.
|
|
58
|
+
|
|
59
|
+
Remote command/toolbox responses mean **accepted**, not completed. Inspect
|
|
60
|
+
session details for execution results. Requests are never automatically
|
|
61
|
+
retried; check the resulting session state before retrying a mutation after a
|
|
62
|
+
timeout or transport error.
|
|
63
|
+
|
|
64
|
+
## Request contract
|
|
65
|
+
|
|
66
|
+
All requests target the fixed extension prefix
|
|
67
|
+
`/App_Extensions/2d558935-686a-4bd0-9991-07539f5fe749/Service.ashx/`.
|
|
68
|
+
Read methods use GET and mutations use POST, with `application/json` positional
|
|
69
|
+
array bodies and the `CTRLAuthHeader` authentication header. Node's HTTPS
|
|
70
|
+
transport supports the documented GET request bodies.
|
|
71
|
+
|
|
72
|
+
Before every request, all DNS answers must be public. The connection is pinned
|
|
73
|
+
to those checked addresses with the original TLS hostname/certificate checks.
|
|
74
|
+
Redirects are never followed. DNS, connection, and response streaming share a
|
|
75
|
+
30-second deadline. Request bodies are capped at 128 KiB and responses at
|
|
76
|
+
5 MiB. Errors omit provider bodies, URLs, credential bundles, and original
|
|
77
|
+
network exception causes.
|
|
78
|
+
|
|
79
|
+
## Verification
|
|
80
|
+
|
|
81
|
+
```sh
|
|
82
|
+
pnpm --filter @alfe.ai/connectwise-screenconnect-mcp typecheck
|
|
83
|
+
pnpm --filter @alfe.ai/connectwise-screenconnect-mcp lint
|
|
84
|
+
pnpm --filter @alfe.ai/connectwise-screenconnect-mcp test
|
|
85
|
+
pnpm --filter @alfe.ai/connectwise-screenconnect-mcp build
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Contract tests verify transport/authentication, pinned DNS and SSRF rejection,
|
|
89
|
+
response bounds, no mutation retries, credential rotation/isolation, and real
|
|
90
|
+
MCP client/server exchanges. These tests do not constitute live tenant
|
|
91
|
+
verification. Before rollout, use an approved ScreenConnect test instance with
|
|
92
|
+
the extension configured to verify creation, updates, notes, messaging, and
|
|
93
|
+
command/toolbox execution against a disposable session.
|
|
94
|
+
|
|
95
|
+
Official references verified 2026-09-08:
|
|
96
|
+
|
|
97
|
+
- [RESTful API Manager](https://docs.connectwise.com/ScreenConnect_Documentation/Developers/RESTful_API_Manager)
|
|
98
|
+
- [Session Manager API](https://docs.connectwise.com/ScreenConnect_Documentation/Developers/Session_Manager_API_Reference)
|
|
99
|
+
- [Session and session-details objects](https://docs.connectwise.com/ScreenConnect_Documentation/Developers/Session_Manager_API_Reference/Objects)
|
|
100
|
+
- [Extension release notes](https://screenconnect.product.connectwise.com/communities/26/topics/4301-restful-api-manager)
|
|
101
|
+
|
|
102
|
+
The documentation site may redirect to ConnectWise University authentication.
|
|
103
|
+
The protocol contract was also available through the official page's search
|
|
104
|
+
index at implementation time.
|
package/dist/bin.cjs
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const require_screenconnect_client = require("./screenconnect-client2.cjs");
|
|
3
|
+
const require_server = require("./server2.cjs");
|
|
4
|
+
//#region src/cli.ts
|
|
5
|
+
async function runCli(start = require_server.startServer, runtime = process) {
|
|
6
|
+
let server;
|
|
7
|
+
try {
|
|
8
|
+
server = await start();
|
|
9
|
+
} catch (error) {
|
|
10
|
+
runtime.stderr.write(`[screenconnect-mcp] Failed to start: ${require_screenconnect_client.safeErrorMessage(error)}\n`);
|
|
11
|
+
runtime.exitCode = 1;
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
let closing = false;
|
|
15
|
+
const shutdown = () => {
|
|
16
|
+
if (closing) return;
|
|
17
|
+
closing = true;
|
|
18
|
+
runtime.off("SIGINT", shutdown);
|
|
19
|
+
runtime.off("SIGTERM", shutdown);
|
|
20
|
+
server.close().catch(() => {
|
|
21
|
+
runtime.stderr.write("[screenconnect-mcp] Transport cleanup failed.\n");
|
|
22
|
+
}).finally(() => runtime.exit(0));
|
|
23
|
+
};
|
|
24
|
+
runtime.once("SIGINT", shutdown);
|
|
25
|
+
runtime.once("SIGTERM", shutdown);
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
28
|
+
//#region src/bin.ts
|
|
29
|
+
runCli();
|
|
30
|
+
//#endregion
|
package/dist/bin.d.cts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/bin.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/bin.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { d as safeErrorMessage } from "./screenconnect-client2.js";
|
|
3
|
+
import { i as startServer } from "./server2.js";
|
|
4
|
+
//#region src/cli.ts
|
|
5
|
+
async function runCli(start = startServer, runtime = process) {
|
|
6
|
+
let server;
|
|
7
|
+
try {
|
|
8
|
+
server = await start();
|
|
9
|
+
} catch (error) {
|
|
10
|
+
runtime.stderr.write(`[screenconnect-mcp] Failed to start: ${safeErrorMessage(error)}\n`);
|
|
11
|
+
runtime.exitCode = 1;
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
let closing = false;
|
|
15
|
+
const shutdown = () => {
|
|
16
|
+
if (closing) return;
|
|
17
|
+
closing = true;
|
|
18
|
+
runtime.off("SIGINT", shutdown);
|
|
19
|
+
runtime.off("SIGTERM", shutdown);
|
|
20
|
+
server.close().catch(() => {
|
|
21
|
+
runtime.stderr.write("[screenconnect-mcp] Transport cleanup failed.\n");
|
|
22
|
+
}).finally(() => runtime.exit(0));
|
|
23
|
+
};
|
|
24
|
+
runtime.once("SIGINT", shutdown);
|
|
25
|
+
runtime.once("SIGTERM", shutdown);
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
28
|
+
//#region src/bin.ts
|
|
29
|
+
runCli();
|
|
30
|
+
//#endregion
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_screenconnect_client = require("./screenconnect-client2.cjs");
|
|
3
|
+
exports.EXTENSION_PATH = require_screenconnect_client.EXTENSION_PATH;
|
|
4
|
+
exports.ScreenConnectClient = require_screenconnect_client.ScreenConnectClient;
|
|
5
|
+
exports.pinnedLookup = require_screenconnect_client.pinnedLookup;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { RequestOptions } from "node:https";
|
|
2
|
+
import { ClientRequest, IncomingMessage } from "node:http";
|
|
3
|
+
import { LookupFunction } from "node:net";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
|
|
6
|
+
//#region src/boundary.d.ts
|
|
7
|
+
|
|
8
|
+
interface ResolvedAddress {
|
|
9
|
+
address: string;
|
|
10
|
+
family: number;
|
|
11
|
+
}
|
|
12
|
+
type ResolveHost = (hostname: string) => Promise<ResolvedAddress[]>;
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region src/screenconnect-client.d.ts
|
|
15
|
+
declare const EXTENSION_PATH = "/App_Extensions/2d558935-686a-4bd0-9991-07539f5fe749/Service.ashx/";
|
|
16
|
+
/** Positional contracts from the official RESTful API Manager reference. */
|
|
17
|
+
declare const methods: {
|
|
18
|
+
readonly GetSessionsByFilter: {
|
|
19
|
+
readonly method: "GET";
|
|
20
|
+
readonly parameters: z.ZodTuple<[z.ZodString], null>;
|
|
21
|
+
};
|
|
22
|
+
readonly GetSessionsByName: {
|
|
23
|
+
readonly method: "GET";
|
|
24
|
+
readonly parameters: z.ZodTuple<[z.ZodString], null>;
|
|
25
|
+
};
|
|
26
|
+
readonly GetSessionBySessionID: {
|
|
27
|
+
readonly method: "GET";
|
|
28
|
+
readonly parameters: z.ZodTuple<[z.ZodUUID], null>;
|
|
29
|
+
};
|
|
30
|
+
readonly GetSessionDetailsBySessionID: {
|
|
31
|
+
readonly method: "GET";
|
|
32
|
+
readonly parameters: z.ZodTuple<[z.ZodUUID], null>;
|
|
33
|
+
};
|
|
34
|
+
readonly CreateSession: {
|
|
35
|
+
readonly method: "POST";
|
|
36
|
+
readonly parameters: z.ZodTuple<[z.ZodEnum<{
|
|
37
|
+
Support: "Support";
|
|
38
|
+
Meeting: "Meeting";
|
|
39
|
+
}>, z.ZodString, z.ZodBoolean, z.ZodString, z.ZodArray<z.ZodString>], null>;
|
|
40
|
+
};
|
|
41
|
+
readonly UpdateSessionName: {
|
|
42
|
+
readonly method: "POST";
|
|
43
|
+
readonly parameters: z.ZodTuple<[z.ZodUUID, z.ZodString], null>;
|
|
44
|
+
};
|
|
45
|
+
readonly UpdateSessionCustomProperties: {
|
|
46
|
+
readonly method: "POST";
|
|
47
|
+
readonly parameters: z.ZodTuple<[z.ZodUUID, z.ZodArray<z.ZodString>], null>;
|
|
48
|
+
};
|
|
49
|
+
readonly AddNoteToSession: {
|
|
50
|
+
readonly method: "POST";
|
|
51
|
+
readonly parameters: z.ZodTuple<[z.ZodUUID, z.ZodString], null>;
|
|
52
|
+
};
|
|
53
|
+
readonly SendMessageToSession: {
|
|
54
|
+
readonly method: "POST";
|
|
55
|
+
readonly parameters: z.ZodTuple<[z.ZodUUID, z.ZodString, z.ZodString], null>;
|
|
56
|
+
};
|
|
57
|
+
readonly SendCommandToSession: {
|
|
58
|
+
readonly method: "POST";
|
|
59
|
+
readonly parameters: z.ZodTuple<[z.ZodUUID, z.ZodString], null>;
|
|
60
|
+
};
|
|
61
|
+
readonly SendToolboxItemToSession: {
|
|
62
|
+
readonly method: "POST";
|
|
63
|
+
readonly parameters: z.ZodTuple<[z.ZodUUID, z.ZodString], null>;
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
type ScreenConnectMethod = keyof typeof methods;
|
|
67
|
+
interface ScreenConnectApi {
|
|
68
|
+
call(method: ScreenConnectMethod, parameters: unknown[]): Promise<unknown>;
|
|
69
|
+
}
|
|
70
|
+
type RequestFactory = (url: URL, options: RequestOptions, onResponse: (response: IncomingMessage) => void) => ClientRequest;
|
|
71
|
+
interface ScreenConnectClientOptions {
|
|
72
|
+
siteUrl: string;
|
|
73
|
+
authenticationSecret: string;
|
|
74
|
+
/** Injection boundaries for contract tests; never sourced from tool input. */
|
|
75
|
+
resolveHost?: ResolveHost;
|
|
76
|
+
requestFactory?: RequestFactory;
|
|
77
|
+
requestTimeoutMs?: number;
|
|
78
|
+
}
|
|
79
|
+
declare class ScreenConnectClient implements ScreenConnectApi {
|
|
80
|
+
readonly siteUrl: string;
|
|
81
|
+
private readonly secret;
|
|
82
|
+
private readonly resolveHost?;
|
|
83
|
+
private readonly requestFactory;
|
|
84
|
+
private readonly timeoutMs;
|
|
85
|
+
constructor(options: ScreenConnectClientOptions);
|
|
86
|
+
call(method: ScreenConnectMethod, parameters: unknown[]): Promise<unknown>;
|
|
87
|
+
private send;
|
|
88
|
+
}
|
|
89
|
+
/** No second DNS resolution is allowed between validation and connection. */
|
|
90
|
+
declare function pinnedLookup(addresses: ResolvedAddress[]): LookupFunction;
|
|
91
|
+
//#endregion
|
|
92
|
+
export { ScreenConnectClientOptions as a, ScreenConnectClient as i, RequestFactory as n, ScreenConnectMethod as o, ScreenConnectApi as r, pinnedLookup as s, EXTENSION_PATH as t };
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { LookupFunction } from "node:net";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { RequestOptions } from "node:https";
|
|
4
|
+
import { ClientRequest, IncomingMessage } from "node:http";
|
|
5
|
+
|
|
6
|
+
//#region src/boundary.d.ts
|
|
7
|
+
|
|
8
|
+
interface ResolvedAddress {
|
|
9
|
+
address: string;
|
|
10
|
+
family: number;
|
|
11
|
+
}
|
|
12
|
+
type ResolveHost = (hostname: string) => Promise<ResolvedAddress[]>;
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region src/screenconnect-client.d.ts
|
|
15
|
+
declare const EXTENSION_PATH = "/App_Extensions/2d558935-686a-4bd0-9991-07539f5fe749/Service.ashx/";
|
|
16
|
+
/** Positional contracts from the official RESTful API Manager reference. */
|
|
17
|
+
declare const methods: {
|
|
18
|
+
readonly GetSessionsByFilter: {
|
|
19
|
+
readonly method: "GET";
|
|
20
|
+
readonly parameters: z.ZodTuple<[z.ZodString], null>;
|
|
21
|
+
};
|
|
22
|
+
readonly GetSessionsByName: {
|
|
23
|
+
readonly method: "GET";
|
|
24
|
+
readonly parameters: z.ZodTuple<[z.ZodString], null>;
|
|
25
|
+
};
|
|
26
|
+
readonly GetSessionBySessionID: {
|
|
27
|
+
readonly method: "GET";
|
|
28
|
+
readonly parameters: z.ZodTuple<[z.ZodUUID], null>;
|
|
29
|
+
};
|
|
30
|
+
readonly GetSessionDetailsBySessionID: {
|
|
31
|
+
readonly method: "GET";
|
|
32
|
+
readonly parameters: z.ZodTuple<[z.ZodUUID], null>;
|
|
33
|
+
};
|
|
34
|
+
readonly CreateSession: {
|
|
35
|
+
readonly method: "POST";
|
|
36
|
+
readonly parameters: z.ZodTuple<[z.ZodEnum<{
|
|
37
|
+
Support: "Support";
|
|
38
|
+
Meeting: "Meeting";
|
|
39
|
+
}>, z.ZodString, z.ZodBoolean, z.ZodString, z.ZodArray<z.ZodString>], null>;
|
|
40
|
+
};
|
|
41
|
+
readonly UpdateSessionName: {
|
|
42
|
+
readonly method: "POST";
|
|
43
|
+
readonly parameters: z.ZodTuple<[z.ZodUUID, z.ZodString], null>;
|
|
44
|
+
};
|
|
45
|
+
readonly UpdateSessionCustomProperties: {
|
|
46
|
+
readonly method: "POST";
|
|
47
|
+
readonly parameters: z.ZodTuple<[z.ZodUUID, z.ZodArray<z.ZodString>], null>;
|
|
48
|
+
};
|
|
49
|
+
readonly AddNoteToSession: {
|
|
50
|
+
readonly method: "POST";
|
|
51
|
+
readonly parameters: z.ZodTuple<[z.ZodUUID, z.ZodString], null>;
|
|
52
|
+
};
|
|
53
|
+
readonly SendMessageToSession: {
|
|
54
|
+
readonly method: "POST";
|
|
55
|
+
readonly parameters: z.ZodTuple<[z.ZodUUID, z.ZodString, z.ZodString], null>;
|
|
56
|
+
};
|
|
57
|
+
readonly SendCommandToSession: {
|
|
58
|
+
readonly method: "POST";
|
|
59
|
+
readonly parameters: z.ZodTuple<[z.ZodUUID, z.ZodString], null>;
|
|
60
|
+
};
|
|
61
|
+
readonly SendToolboxItemToSession: {
|
|
62
|
+
readonly method: "POST";
|
|
63
|
+
readonly parameters: z.ZodTuple<[z.ZodUUID, z.ZodString], null>;
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
type ScreenConnectMethod = keyof typeof methods;
|
|
67
|
+
interface ScreenConnectApi {
|
|
68
|
+
call(method: ScreenConnectMethod, parameters: unknown[]): Promise<unknown>;
|
|
69
|
+
}
|
|
70
|
+
type RequestFactory = (url: URL, options: RequestOptions, onResponse: (response: IncomingMessage) => void) => ClientRequest;
|
|
71
|
+
interface ScreenConnectClientOptions {
|
|
72
|
+
siteUrl: string;
|
|
73
|
+
authenticationSecret: string;
|
|
74
|
+
/** Injection boundaries for contract tests; never sourced from tool input. */
|
|
75
|
+
resolveHost?: ResolveHost;
|
|
76
|
+
requestFactory?: RequestFactory;
|
|
77
|
+
requestTimeoutMs?: number;
|
|
78
|
+
}
|
|
79
|
+
declare class ScreenConnectClient implements ScreenConnectApi {
|
|
80
|
+
readonly siteUrl: string;
|
|
81
|
+
private readonly secret;
|
|
82
|
+
private readonly resolveHost?;
|
|
83
|
+
private readonly requestFactory;
|
|
84
|
+
private readonly timeoutMs;
|
|
85
|
+
constructor(options: ScreenConnectClientOptions);
|
|
86
|
+
call(method: ScreenConnectMethod, parameters: unknown[]): Promise<unknown>;
|
|
87
|
+
private send;
|
|
88
|
+
}
|
|
89
|
+
/** No second DNS resolution is allowed between validation and connection. */
|
|
90
|
+
declare function pinnedLookup(addresses: ResolvedAddress[]): LookupFunction;
|
|
91
|
+
//#endregion
|
|
92
|
+
export { ScreenConnectClientOptions as a, ScreenConnectClient as i, RequestFactory as n, ScreenConnectMethod as o, ScreenConnectApi as r, pinnedLookup as s, EXTENSION_PATH as t };
|