@visualsentinel/mcp-server 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/CHANGELOG.md +15 -0
- package/LICENSE +21 -0
- package/README.md +174 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +463 -0
- package/dist/index.js.map +1 -0
- package/package.json +63 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@visualsentinel/mcp-server`. Format follows [Keep a Changelog](https://keepachangelog.com/) and the package uses [Semantic Versioning](https://semver.org/).
|
|
4
|
+
|
|
5
|
+
## [0.1.0] - 2026-04-29
|
|
6
|
+
|
|
7
|
+
Initial public release.
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
* 16 tools wrapping the public Visual Sentinel REST API (5 public, 11 authenticated).
|
|
12
|
+
* stdio transport (MCP protocol version `2025-06-18`).
|
|
13
|
+
* `VS_API_KEY` (or `VISUALSENTINEL_API_KEY`) for authenticated tools; `VS_API_BASE` override for local development.
|
|
14
|
+
* TypeScript build via tsup, ESM-only, Node 18+.
|
|
15
|
+
* README with installation snippets for Claude Desktop, Claude Code, Cline, Continue.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Visual Sentinel
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# Visual Sentinel MCP Server
|
|
2
|
+
|
|
3
|
+
Model Context Protocol server for [Visual Sentinel](https://visualsentinel.com), the six-layer website monitoring platform: uptime, performance, SSL, DNS, visual regression, and content change detection.
|
|
4
|
+
|
|
5
|
+
Connect Claude Desktop, Claude Code, Cline, Continue, or any MCP-compatible client to your Visual Sentinel account so an AI assistant can list monitors, create them, fetch incident history, run free DNS / SSL / speed checks, and acknowledge alerts on your behalf.
|
|
6
|
+
|
|
7
|
+
* **npm:** `@visualsentinel/mcp-server`
|
|
8
|
+
* **OpenAPI spec:** https://github.com/VisualSentinel/openapi
|
|
9
|
+
* **Get an API key:** https://visualsentinel.com/settings/api-keys
|
|
10
|
+
* **Hosted API reference:** https://visualsentinel.com/docs
|
|
11
|
+
|
|
12
|
+
## What you get
|
|
13
|
+
|
|
14
|
+
16 tools spanning the public Visual Sentinel API:
|
|
15
|
+
|
|
16
|
+
**Public tools (no API key required)**
|
|
17
|
+
|
|
18
|
+
| Tool | Endpoint | Purpose |
|
|
19
|
+
|---|---|---|
|
|
20
|
+
| `vs_health` | `GET /api/health` | Service health probe |
|
|
21
|
+
| `vs_dns_check` | `GET /api/tools/dns-check` | Resolve A / AAAA / MX / NS / TXT / CNAME / SOA records |
|
|
22
|
+
| `vs_ssl_check` | `GET /api/tools/ssl-check` | Inspect TLS certificate (issuer, validity, SAN list, chain) |
|
|
23
|
+
| `vs_speed_test` | `GET /api/tools/speed-test` | One-shot performance check (TTFB, total load, transfer size) |
|
|
24
|
+
| `vs_website_check` | `GET /api/tools/website-check` | Quick HTTP health check |
|
|
25
|
+
|
|
26
|
+
**Authenticated tools (require `VS_API_KEY`)**
|
|
27
|
+
|
|
28
|
+
| Tool | Endpoint | Purpose |
|
|
29
|
+
|---|---|---|
|
|
30
|
+
| `vs_monitors_list` | `GET /api/monitors` | List monitors with optional status / type filters |
|
|
31
|
+
| `vs_monitors_get` | `GET /api/monitors/{id}` | Fetch a single monitor with current status |
|
|
32
|
+
| `vs_monitors_create` | `POST /api/monitors` | Create a new monitor |
|
|
33
|
+
| `vs_monitors_check_now` | `POST /api/monitors/{id}/check` | Trigger an immediate check |
|
|
34
|
+
| `vs_monitors_uptime` | `GET /api/monitors/{id}/uptime` | Uptime percentage over 24h / 7d / 30d / 90d / 365d |
|
|
35
|
+
| `vs_incidents_list` | `GET /api/incidents` | List incidents, filterable by status or monitor |
|
|
36
|
+
| `vs_incidents_get` | `GET /api/incidents/{id}` | Fetch an incident with full check history |
|
|
37
|
+
| `vs_alerts_list` | `GET /api/alerts` | List alerts by status |
|
|
38
|
+
| `vs_alerts_acknowledge` | `POST /api/alerts/{id}/acknowledge` | Acknowledge an alert |
|
|
39
|
+
| `vs_status_pages_list` | `GET /api/status-pages` | List your public status pages |
|
|
40
|
+
| `vs_servers_list` | `GET /api/servers` | List registered origin servers |
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
### Claude Desktop
|
|
45
|
+
|
|
46
|
+
Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"mcpServers": {
|
|
51
|
+
"visualsentinel": {
|
|
52
|
+
"command": "npx",
|
|
53
|
+
"args": ["-y", "@visualsentinel/mcp-server"],
|
|
54
|
+
"env": {
|
|
55
|
+
"VS_API_KEY": "vs_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Restart Claude Desktop. The 16 tools appear under the tools menu.
|
|
63
|
+
|
|
64
|
+
### Claude Code (CLI)
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
claude mcp add visualsentinel \
|
|
68
|
+
-- npx -y @visualsentinel/mcp-server
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Then export `VS_API_KEY` in the shell where Claude Code runs.
|
|
72
|
+
|
|
73
|
+
### Cline (VS Code extension)
|
|
74
|
+
|
|
75
|
+
In Cline's MCP settings, add:
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
{
|
|
79
|
+
"visualsentinel": {
|
|
80
|
+
"command": "npx",
|
|
81
|
+
"args": ["-y", "@visualsentinel/mcp-server"],
|
|
82
|
+
"env": { "VS_API_KEY": "vs_live_..." }
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Continue
|
|
88
|
+
|
|
89
|
+
In `~/.continue/config.json`:
|
|
90
|
+
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"mcpServers": [
|
|
94
|
+
{
|
|
95
|
+
"name": "visualsentinel",
|
|
96
|
+
"command": "npx",
|
|
97
|
+
"args": ["-y", "@visualsentinel/mcp-server"],
|
|
98
|
+
"env": { "VS_API_KEY": "vs_live_..." }
|
|
99
|
+
}
|
|
100
|
+
]
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Generic MCP client (stdio)
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
VS_API_KEY=vs_live_xxx npx -y @visualsentinel/mcp-server
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
The server speaks JSON-RPC over stdio per the MCP specification (`2025-06-18`).
|
|
111
|
+
|
|
112
|
+
## Configuration
|
|
113
|
+
|
|
114
|
+
Two environment variables, both optional except in the noted cases:
|
|
115
|
+
|
|
116
|
+
| Variable | Default | Purpose |
|
|
117
|
+
|---|---|---|
|
|
118
|
+
| `VS_API_KEY` | (none) | Required for the 11 authenticated tools. Public tools work without it. Generate at https://visualsentinel.com/settings/api-keys. |
|
|
119
|
+
| `VS_API_BASE` | `https://visualsentinel.com` | Override only for testing against a local development instance. |
|
|
120
|
+
|
|
121
|
+
If a tool that needs auth is called without `VS_API_KEY`, the server returns a clear error rather than silently failing.
|
|
122
|
+
|
|
123
|
+
`VISUALSENTINEL_API_KEY` is accepted as an alias for `VS_API_KEY` for clients that prefer the longer name.
|
|
124
|
+
|
|
125
|
+
## Usage examples
|
|
126
|
+
|
|
127
|
+
After installation, ask the AI assistant things like:
|
|
128
|
+
|
|
129
|
+
* "What's the SSL certificate situation on visualsentinel.com? When does it expire?"
|
|
130
|
+
* "List my Visual Sentinel monitors that are currently down."
|
|
131
|
+
* "Add a new HTTPS monitor for https://shop.example.com checking every minute."
|
|
132
|
+
* "Show me incidents from the last 24 hours and acknowledge the ones for monitor-id `clm123`."
|
|
133
|
+
* "What's the uptime percentage for my landing page over the last 30 days?"
|
|
134
|
+
* "Run a speed test on https://example.com/checkout."
|
|
135
|
+
|
|
136
|
+
The assistant chooses the appropriate tool, fills in arguments from the conversation, and shows the JSON response.
|
|
137
|
+
|
|
138
|
+
## Security notes
|
|
139
|
+
|
|
140
|
+
* Your API key never leaves the machine running this MCP server. Requests go directly to `https://visualsentinel.com` over HTTPS.
|
|
141
|
+
* Public tools (`vs_health`, `vs_dns_check`, `vs_ssl_check`, `vs_speed_test`, `vs_website_check`) work without an API key, but their results are visible to anyone who can read your conversation logs. Treat them like any other shell-out.
|
|
142
|
+
* Revoke a leaked API key immediately at https://visualsentinel.com/settings/api-keys. Revocation takes effect within seconds.
|
|
143
|
+
* Tools that mutate state (`vs_monitors_create`, `vs_monitors_check_now`, `vs_alerts_acknowledge`) inherit your API key's role-based permissions. A read-only key cannot create monitors.
|
|
144
|
+
|
|
145
|
+
## Development
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
git clone https://github.com/VisualSentinel/mcp-server.git
|
|
149
|
+
cd mcp-server
|
|
150
|
+
npm install
|
|
151
|
+
npm run build # tsup, output to dist/
|
|
152
|
+
npm run lint # tsc --noEmit
|
|
153
|
+
npm run dev # tsup --watch
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
To smoke-test locally against the production API:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
VS_API_KEY=vs_live_... node dist/index.js
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
The server speaks JSON-RPC on stdin / stdout. Pipe in a `tools/list` request to see the registered tools.
|
|
163
|
+
|
|
164
|
+
## Versioning
|
|
165
|
+
|
|
166
|
+
Semantic versioning. Breaking changes to tool names or required arguments get a major bump. Adding new tools or new optional arguments is a minor or patch.
|
|
167
|
+
|
|
168
|
+
The server's tool surface tracks the public Visual Sentinel API spec at https://github.com/VisualSentinel/openapi. When the upstream API gains a useful endpoint, this repository typically catches up within a release.
|
|
169
|
+
|
|
170
|
+
## License
|
|
171
|
+
|
|
172
|
+
MIT. See [`LICENSE`](./LICENSE).
|
|
173
|
+
|
|
174
|
+
The Visual Sentinel API service itself is governed by https://visualsentinel.com/terms.
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
import {
|
|
7
|
+
CallToolRequestSchema,
|
|
8
|
+
ListToolsRequestSchema
|
|
9
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
10
|
+
|
|
11
|
+
// src/api-client.ts
|
|
12
|
+
var DEFAULT_BASE = "https://visualsentinel.com";
|
|
13
|
+
var VsApiError = class extends Error {
|
|
14
|
+
constructor(message, status, body) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.status = status;
|
|
17
|
+
this.body = body;
|
|
18
|
+
this.name = "VsApiError";
|
|
19
|
+
}
|
|
20
|
+
status;
|
|
21
|
+
body;
|
|
22
|
+
};
|
|
23
|
+
var ApiClient = class {
|
|
24
|
+
apiKey;
|
|
25
|
+
baseUrl;
|
|
26
|
+
userAgent;
|
|
27
|
+
constructor(opts = {}) {
|
|
28
|
+
this.apiKey = opts.apiKey ?? process.env.VS_API_KEY ?? process.env.VISUALSENTINEL_API_KEY ?? void 0;
|
|
29
|
+
this.baseUrl = (opts.baseUrl ?? process.env.VS_API_BASE ?? DEFAULT_BASE).replace(
|
|
30
|
+
/\/+$/,
|
|
31
|
+
""
|
|
32
|
+
);
|
|
33
|
+
this.userAgent = opts.userAgent ?? "@visualsentinel/mcp-server";
|
|
34
|
+
}
|
|
35
|
+
hasApiKey() {
|
|
36
|
+
return typeof this.apiKey === "string" && this.apiKey.length > 0;
|
|
37
|
+
}
|
|
38
|
+
async request(method, path, options = {}) {
|
|
39
|
+
const auth = options.auth ?? true;
|
|
40
|
+
if (auth && !this.hasApiKey()) {
|
|
41
|
+
throw new VsApiError(
|
|
42
|
+
"Visual Sentinel API key required. Set VS_API_KEY (https://visualsentinel.com/settings/api-keys).",
|
|
43
|
+
401,
|
|
44
|
+
null
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
const url = new URL(path.startsWith("/") ? path : `/${path}`, this.baseUrl);
|
|
48
|
+
if (options.query) {
|
|
49
|
+
for (const [k, v] of Object.entries(options.query)) {
|
|
50
|
+
if (v !== void 0 && v !== null) url.searchParams.set(k, String(v));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const headers = {
|
|
54
|
+
Accept: "application/json",
|
|
55
|
+
"User-Agent": this.userAgent
|
|
56
|
+
};
|
|
57
|
+
if (auth && this.apiKey) headers["X-API-Key"] = this.apiKey;
|
|
58
|
+
if (options.body !== void 0) headers["Content-Type"] = "application/json";
|
|
59
|
+
const res = await fetch(url, {
|
|
60
|
+
method,
|
|
61
|
+
headers,
|
|
62
|
+
body: options.body !== void 0 ? JSON.stringify(options.body) : void 0
|
|
63
|
+
});
|
|
64
|
+
const text = await res.text();
|
|
65
|
+
let parsed = null;
|
|
66
|
+
if (text.length > 0) {
|
|
67
|
+
try {
|
|
68
|
+
parsed = JSON.parse(text);
|
|
69
|
+
} catch {
|
|
70
|
+
parsed = text;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (!res.ok) {
|
|
74
|
+
throw new VsApiError(
|
|
75
|
+
`Visual Sentinel API ${method} ${path} returned ${res.status}`,
|
|
76
|
+
res.status,
|
|
77
|
+
parsed
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
return parsed;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// src/tools.ts
|
|
85
|
+
var STR = { type: "string" };
|
|
86
|
+
var INT = { type: "integer" };
|
|
87
|
+
function pickString(args, key) {
|
|
88
|
+
const v = args[key];
|
|
89
|
+
return typeof v === "string" && v.length > 0 ? v : void 0;
|
|
90
|
+
}
|
|
91
|
+
function pickNumber(args, key) {
|
|
92
|
+
const v = args[key];
|
|
93
|
+
return typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
94
|
+
}
|
|
95
|
+
function requireString(args, key) {
|
|
96
|
+
const v = pickString(args, key);
|
|
97
|
+
if (!v) throw new Error(`Argument "${key}" (string) is required.`);
|
|
98
|
+
return v;
|
|
99
|
+
}
|
|
100
|
+
var TOOLS = [
|
|
101
|
+
// -------------------------------------------------------------------------
|
|
102
|
+
// Public tools (no auth)
|
|
103
|
+
// -------------------------------------------------------------------------
|
|
104
|
+
{
|
|
105
|
+
name: "vs_health",
|
|
106
|
+
description: "Check whether Visual Sentinel itself is up. Returns the service health status. No authentication required.",
|
|
107
|
+
inputSchema: {
|
|
108
|
+
type: "object",
|
|
109
|
+
properties: {},
|
|
110
|
+
additionalProperties: false
|
|
111
|
+
},
|
|
112
|
+
requiresAuth: false,
|
|
113
|
+
handler: async (_args, client) => client.request("GET", "/api/health", { auth: false })
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
name: "vs_dns_check",
|
|
117
|
+
description: "Resolve DNS records (A, AAAA, MX, NS, TXT, CNAME) for a domain using Visual Sentinel's public DNS lookup tool. No authentication required.",
|
|
118
|
+
inputSchema: {
|
|
119
|
+
type: "object",
|
|
120
|
+
properties: {
|
|
121
|
+
domain: { ...STR, description: "Domain to resolve, e.g. example.com (without protocol)." },
|
|
122
|
+
recordType: {
|
|
123
|
+
...STR,
|
|
124
|
+
description: "Optional: limit to one record type. One of: A, AAAA, MX, NS, TXT, CNAME, SOA. Default: all."
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
required: ["domain"],
|
|
128
|
+
additionalProperties: false
|
|
129
|
+
},
|
|
130
|
+
requiresAuth: false,
|
|
131
|
+
handler: async (args, client) => client.request("GET", "/api/tools/dns-check", {
|
|
132
|
+
auth: false,
|
|
133
|
+
query: {
|
|
134
|
+
domain: requireString(args, "domain"),
|
|
135
|
+
recordType: pickString(args, "recordType")
|
|
136
|
+
}
|
|
137
|
+
})
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
name: "vs_ssl_check",
|
|
141
|
+
description: "Inspect a TLS/SSL certificate for a hostname: issuer, subject, validity dates, SAN list, key algorithm, and certificate chain. No authentication required.",
|
|
142
|
+
inputSchema: {
|
|
143
|
+
type: "object",
|
|
144
|
+
properties: {
|
|
145
|
+
host: { ...STR, description: "Hostname to inspect, e.g. example.com (without protocol)." },
|
|
146
|
+
port: { ...INT, description: "TCP port. Default 443." }
|
|
147
|
+
},
|
|
148
|
+
required: ["host"],
|
|
149
|
+
additionalProperties: false
|
|
150
|
+
},
|
|
151
|
+
requiresAuth: false,
|
|
152
|
+
handler: async (args, client) => client.request("GET", "/api/tools/ssl-check", {
|
|
153
|
+
auth: false,
|
|
154
|
+
query: { host: requireString(args, "host"), port: pickNumber(args, "port") }
|
|
155
|
+
})
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
name: "vs_speed_test",
|
|
159
|
+
description: "Run a one-shot performance check against a URL: TTFB, total load time, transfer size, status code, and redirect chain. No authentication required.",
|
|
160
|
+
inputSchema: {
|
|
161
|
+
type: "object",
|
|
162
|
+
properties: {
|
|
163
|
+
url: { ...STR, description: "Full URL to test, including protocol (https://...)." }
|
|
164
|
+
},
|
|
165
|
+
required: ["url"],
|
|
166
|
+
additionalProperties: false
|
|
167
|
+
},
|
|
168
|
+
requiresAuth: false,
|
|
169
|
+
handler: async (args, client) => client.request("GET", "/api/tools/speed-test", {
|
|
170
|
+
auth: false,
|
|
171
|
+
query: { url: requireString(args, "url") }
|
|
172
|
+
})
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
name: "vs_website_check",
|
|
176
|
+
description: "Quick health check for a URL: HTTP status, response time, server header, content snippet, and basic SSL state. No authentication required.",
|
|
177
|
+
inputSchema: {
|
|
178
|
+
type: "object",
|
|
179
|
+
properties: {
|
|
180
|
+
url: { ...STR, description: "Full URL to test, including protocol (https://...)." }
|
|
181
|
+
},
|
|
182
|
+
required: ["url"],
|
|
183
|
+
additionalProperties: false
|
|
184
|
+
},
|
|
185
|
+
requiresAuth: false,
|
|
186
|
+
handler: async (args, client) => client.request("GET", "/api/tools/website-check", {
|
|
187
|
+
auth: false,
|
|
188
|
+
query: { url: requireString(args, "url") }
|
|
189
|
+
})
|
|
190
|
+
},
|
|
191
|
+
// -------------------------------------------------------------------------
|
|
192
|
+
// Authenticated tools (require VS_API_KEY)
|
|
193
|
+
// -------------------------------------------------------------------------
|
|
194
|
+
{
|
|
195
|
+
name: "vs_monitors_list",
|
|
196
|
+
description: "List monitors in the authenticated organization. Optional filters narrow by status, type, or paginate.",
|
|
197
|
+
inputSchema: {
|
|
198
|
+
type: "object",
|
|
199
|
+
properties: {
|
|
200
|
+
status: { ...STR, description: "Filter: UP, DOWN, PAUSED, MAINTENANCE." },
|
|
201
|
+
type: {
|
|
202
|
+
...STR,
|
|
203
|
+
description: "Filter by monitor type: HTTP, HTTPS, KEYWORD, PING, PORT, DNS, SSL, VISUAL, CONTENT, API, GRAPHQL, WEBSOCKET."
|
|
204
|
+
},
|
|
205
|
+
page: { ...INT, description: "Page number (default 1)." },
|
|
206
|
+
limit: { ...INT, description: "Items per page (default 50, max 200)." }
|
|
207
|
+
},
|
|
208
|
+
additionalProperties: false
|
|
209
|
+
},
|
|
210
|
+
requiresAuth: true,
|
|
211
|
+
handler: async (args, client) => client.request("GET", "/api/monitors", {
|
|
212
|
+
query: {
|
|
213
|
+
status: pickString(args, "status"),
|
|
214
|
+
type: pickString(args, "type"),
|
|
215
|
+
page: pickNumber(args, "page"),
|
|
216
|
+
limit: pickNumber(args, "limit")
|
|
217
|
+
}
|
|
218
|
+
})
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
name: "vs_monitors_get",
|
|
222
|
+
description: "Fetch a single monitor by id, including its current status, last check, and configuration.",
|
|
223
|
+
inputSchema: {
|
|
224
|
+
type: "object",
|
|
225
|
+
properties: {
|
|
226
|
+
id: { ...STR, description: "Monitor id (cuid)." }
|
|
227
|
+
},
|
|
228
|
+
required: ["id"],
|
|
229
|
+
additionalProperties: false
|
|
230
|
+
},
|
|
231
|
+
requiresAuth: true,
|
|
232
|
+
handler: async (args, client) => client.request("GET", `/api/monitors/${encodeURIComponent(requireString(args, "id"))}`)
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
name: "vs_monitors_create",
|
|
236
|
+
description: "Create a new monitor. Provide at least name, url, and type. Returns the created monitor including its assigned id.",
|
|
237
|
+
inputSchema: {
|
|
238
|
+
type: "object",
|
|
239
|
+
properties: {
|
|
240
|
+
name: { ...STR, description: "Display name for the monitor." },
|
|
241
|
+
url: { ...STR, description: "Full URL to monitor (or hostname for PING/PORT/DNS/SSL)." },
|
|
242
|
+
type: {
|
|
243
|
+
...STR,
|
|
244
|
+
description: "Monitor type: HTTP, HTTPS, KEYWORD, PING, PORT, DNS, SSL, VISUAL, CONTENT, API, GRAPHQL, WEBSOCKET."
|
|
245
|
+
},
|
|
246
|
+
interval: { ...INT, description: "Check interval in seconds (60, 300, 600, 1800, 3600)." },
|
|
247
|
+
timeout: { ...INT, description: "Per-check timeout in seconds (default 30)." },
|
|
248
|
+
method: { ...STR, description: "HTTP method for HTTP/HTTPS monitors (default GET)." },
|
|
249
|
+
keyword: { ...STR, description: "Keyword to assert for KEYWORD monitors." },
|
|
250
|
+
port: { ...INT, description: "TCP port for PORT monitors." }
|
|
251
|
+
},
|
|
252
|
+
required: ["name", "url", "type"],
|
|
253
|
+
additionalProperties: false
|
|
254
|
+
},
|
|
255
|
+
requiresAuth: true,
|
|
256
|
+
handler: async (args, client) => client.request("POST", "/api/monitors", {
|
|
257
|
+
body: {
|
|
258
|
+
name: requireString(args, "name"),
|
|
259
|
+
url: requireString(args, "url"),
|
|
260
|
+
type: requireString(args, "type"),
|
|
261
|
+
interval: pickNumber(args, "interval"),
|
|
262
|
+
timeout: pickNumber(args, "timeout"),
|
|
263
|
+
method: pickString(args, "method"),
|
|
264
|
+
keyword: pickString(args, "keyword"),
|
|
265
|
+
port: pickNumber(args, "port")
|
|
266
|
+
}
|
|
267
|
+
})
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
name: "vs_monitors_check_now",
|
|
271
|
+
description: "Trigger an immediate check for a monitor (in addition to its scheduled cadence). Returns the freshly-collected check result.",
|
|
272
|
+
inputSchema: {
|
|
273
|
+
type: "object",
|
|
274
|
+
properties: {
|
|
275
|
+
id: { ...STR, description: "Monitor id." }
|
|
276
|
+
},
|
|
277
|
+
required: ["id"],
|
|
278
|
+
additionalProperties: false
|
|
279
|
+
},
|
|
280
|
+
requiresAuth: true,
|
|
281
|
+
handler: async (args, client) => client.request("POST", `/api/monitors/${encodeURIComponent(requireString(args, "id"))}/check`)
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
name: "vs_monitors_uptime",
|
|
285
|
+
description: "Fetch the uptime percentage and outage breakdown for a monitor over a window (default last 30 days).",
|
|
286
|
+
inputSchema: {
|
|
287
|
+
type: "object",
|
|
288
|
+
properties: {
|
|
289
|
+
id: { ...STR, description: "Monitor id." },
|
|
290
|
+
window: {
|
|
291
|
+
...STR,
|
|
292
|
+
description: "Window: 24h, 7d, 30d, 90d, 365d. Default 30d."
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
required: ["id"],
|
|
296
|
+
additionalProperties: false
|
|
297
|
+
},
|
|
298
|
+
requiresAuth: true,
|
|
299
|
+
handler: async (args, client) => client.request("GET", `/api/monitors/${encodeURIComponent(requireString(args, "id"))}/uptime`, {
|
|
300
|
+
query: { window: pickString(args, "window") }
|
|
301
|
+
})
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
name: "vs_incidents_list",
|
|
305
|
+
description: "List incidents for the authenticated organization. Filter by status (OPEN/RESOLVED), monitor id, or paginate.",
|
|
306
|
+
inputSchema: {
|
|
307
|
+
type: "object",
|
|
308
|
+
properties: {
|
|
309
|
+
status: { ...STR, description: "OPEN or RESOLVED." },
|
|
310
|
+
monitorId: { ...STR, description: "Filter to one monitor." },
|
|
311
|
+
page: { ...INT },
|
|
312
|
+
limit: { ...INT, description: "Default 50, max 200." }
|
|
313
|
+
},
|
|
314
|
+
additionalProperties: false
|
|
315
|
+
},
|
|
316
|
+
requiresAuth: true,
|
|
317
|
+
handler: async (args, client) => client.request("GET", "/api/incidents", {
|
|
318
|
+
query: {
|
|
319
|
+
status: pickString(args, "status"),
|
|
320
|
+
monitorId: pickString(args, "monitorId"),
|
|
321
|
+
page: pickNumber(args, "page"),
|
|
322
|
+
limit: pickNumber(args, "limit")
|
|
323
|
+
}
|
|
324
|
+
})
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
name: "vs_incidents_get",
|
|
328
|
+
description: "Fetch a single incident with its full check history and root-cause hints.",
|
|
329
|
+
inputSchema: {
|
|
330
|
+
type: "object",
|
|
331
|
+
properties: {
|
|
332
|
+
id: { ...STR, description: "Incident id." }
|
|
333
|
+
},
|
|
334
|
+
required: ["id"],
|
|
335
|
+
additionalProperties: false
|
|
336
|
+
},
|
|
337
|
+
requiresAuth: true,
|
|
338
|
+
handler: async (args, client) => client.request("GET", `/api/incidents/${encodeURIComponent(requireString(args, "id"))}`)
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
name: "vs_alerts_list",
|
|
342
|
+
description: "List alerts for the authenticated organization. Filter by status (UNACKNOWLEDGED/ACKNOWLEDGED/RESOLVED) or paginate.",
|
|
343
|
+
inputSchema: {
|
|
344
|
+
type: "object",
|
|
345
|
+
properties: {
|
|
346
|
+
status: { ...STR, description: "UNACKNOWLEDGED, ACKNOWLEDGED, or RESOLVED." },
|
|
347
|
+
page: { ...INT },
|
|
348
|
+
limit: { ...INT, description: "Default 50, max 200." }
|
|
349
|
+
},
|
|
350
|
+
additionalProperties: false
|
|
351
|
+
},
|
|
352
|
+
requiresAuth: true,
|
|
353
|
+
handler: async (args, client) => client.request("GET", "/api/alerts", {
|
|
354
|
+
query: {
|
|
355
|
+
status: pickString(args, "status"),
|
|
356
|
+
page: pickNumber(args, "page"),
|
|
357
|
+
limit: pickNumber(args, "limit")
|
|
358
|
+
}
|
|
359
|
+
})
|
|
360
|
+
},
|
|
361
|
+
{
|
|
362
|
+
name: "vs_alerts_acknowledge",
|
|
363
|
+
description: "Acknowledge an alert by id. Acknowledgement is recorded with the calling API key's user.",
|
|
364
|
+
inputSchema: {
|
|
365
|
+
type: "object",
|
|
366
|
+
properties: {
|
|
367
|
+
id: { ...STR, description: "Alert id." },
|
|
368
|
+
note: { ...STR, description: "Optional acknowledgement note (visible in alert history)." }
|
|
369
|
+
},
|
|
370
|
+
required: ["id"],
|
|
371
|
+
additionalProperties: false
|
|
372
|
+
},
|
|
373
|
+
requiresAuth: true,
|
|
374
|
+
handler: async (args, client) => client.request("POST", `/api/alerts/${encodeURIComponent(requireString(args, "id"))}/acknowledge`, {
|
|
375
|
+
body: { note: pickString(args, "note") }
|
|
376
|
+
})
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
name: "vs_status_pages_list",
|
|
380
|
+
description: "List public-facing status pages owned by the authenticated organization.",
|
|
381
|
+
inputSchema: {
|
|
382
|
+
type: "object",
|
|
383
|
+
properties: {},
|
|
384
|
+
additionalProperties: false
|
|
385
|
+
},
|
|
386
|
+
requiresAuth: true,
|
|
387
|
+
handler: async (_args, client) => client.request("GET", "/api/status-pages")
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
name: "vs_servers_list",
|
|
391
|
+
description: "List servers (origins) registered in the authenticated organization.",
|
|
392
|
+
inputSchema: {
|
|
393
|
+
type: "object",
|
|
394
|
+
properties: {
|
|
395
|
+
page: { ...INT },
|
|
396
|
+
limit: { ...INT, description: "Default 50." }
|
|
397
|
+
},
|
|
398
|
+
additionalProperties: false
|
|
399
|
+
},
|
|
400
|
+
requiresAuth: true,
|
|
401
|
+
handler: async (args, client) => client.request("GET", "/api/servers", {
|
|
402
|
+
query: { page: pickNumber(args, "page"), limit: pickNumber(args, "limit") }
|
|
403
|
+
})
|
|
404
|
+
}
|
|
405
|
+
];
|
|
406
|
+
function findTool(name) {
|
|
407
|
+
return TOOLS.find((t) => t.name === name);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// src/index.ts
|
|
411
|
+
var SERVER_NAME = "visualsentinel";
|
|
412
|
+
var SERVER_VERSION = "0.1.0";
|
|
413
|
+
function toMcpToolDescriptor(tool) {
|
|
414
|
+
return {
|
|
415
|
+
name: tool.name,
|
|
416
|
+
description: tool.description,
|
|
417
|
+
inputSchema: tool.inputSchema
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
async function main() {
|
|
421
|
+
const client = new ApiClient();
|
|
422
|
+
const server = new Server(
|
|
423
|
+
{ name: SERVER_NAME, version: SERVER_VERSION },
|
|
424
|
+
{ capabilities: { tools: {} } }
|
|
425
|
+
);
|
|
426
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
427
|
+
tools: TOOLS.map(toMcpToolDescriptor)
|
|
428
|
+
}));
|
|
429
|
+
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
430
|
+
const tool = findTool(req.params.name);
|
|
431
|
+
if (!tool) {
|
|
432
|
+
return {
|
|
433
|
+
isError: true,
|
|
434
|
+
content: [{ type: "text", text: `Unknown tool: ${req.params.name}` }]
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
try {
|
|
438
|
+
const result = await tool.handler(req.params.arguments ?? {}, client);
|
|
439
|
+
return {
|
|
440
|
+
content: [
|
|
441
|
+
{
|
|
442
|
+
type: "text",
|
|
443
|
+
text: JSON.stringify(result, null, 2)
|
|
444
|
+
}
|
|
445
|
+
]
|
|
446
|
+
};
|
|
447
|
+
} catch (err) {
|
|
448
|
+
const message = err instanceof VsApiError ? `Visual Sentinel API error (${err.status}): ${err.message}
|
|
449
|
+
${typeof err.body === "string" ? err.body : JSON.stringify(err.body, null, 2)}` : err instanceof Error ? err.message : String(err);
|
|
450
|
+
return {
|
|
451
|
+
isError: true,
|
|
452
|
+
content: [{ type: "text", text: message }]
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
});
|
|
456
|
+
const transport = new StdioServerTransport();
|
|
457
|
+
await server.connect(transport);
|
|
458
|
+
}
|
|
459
|
+
main().catch((err) => {
|
|
460
|
+
console.error("[visualsentinel-mcp-server] fatal:", err);
|
|
461
|
+
process.exit(1);
|
|
462
|
+
});
|
|
463
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/api-client.ts","../src/tools.ts"],"sourcesContent":["/**\n * Visual Sentinel MCP server (stdio transport).\n *\n * Run via:\n * VS_API_KEY=vs_live_... npx @visualsentinel/mcp-server\n *\n * The server registers all tools defined in `./tools.ts` with the MCP SDK\n * and answers tools/list and tools/call requests. Public tools (health,\n * DNS check, SSL check, speed test, website check) work without an API key;\n * everything else returns a clear error if VS_API_KEY is not set.\n */\n\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n} from '@modelcontextprotocol/sdk/types.js';\n\nimport { ApiClient, VsApiError } from './api-client.js';\nimport { TOOLS, findTool } from './tools.js';\n\nconst SERVER_NAME = 'visualsentinel';\nconst SERVER_VERSION = '0.1.0';\n\nfunction toMcpToolDescriptor(tool: (typeof TOOLS)[number]) {\n return {\n name: tool.name,\n description: tool.description,\n inputSchema: tool.inputSchema,\n };\n}\n\nasync function main(): Promise<void> {\n const client = new ApiClient();\n const server = new Server(\n { name: SERVER_NAME, version: SERVER_VERSION },\n { capabilities: { tools: {} } },\n );\n\n server.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: TOOLS.map(toMcpToolDescriptor),\n }));\n\n server.setRequestHandler(CallToolRequestSchema, async (req) => {\n const tool = findTool(req.params.name);\n if (!tool) {\n return {\n isError: true,\n content: [{ type: 'text', text: `Unknown tool: ${req.params.name}` }],\n };\n }\n\n try {\n const result = await tool.handler(req.params.arguments ?? {}, client);\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(result, null, 2),\n },\n ],\n };\n } catch (err) {\n const message = err instanceof VsApiError\n ? `Visual Sentinel API error (${err.status}): ${err.message}\\n${\n typeof err.body === 'string' ? err.body : JSON.stringify(err.body, null, 2)\n }`\n : err instanceof Error\n ? err.message\n : String(err);\n return {\n isError: true,\n content: [{ type: 'text', text: message }],\n };\n }\n });\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n}\n\nmain().catch((err) => {\n // eslint-disable-next-line no-console\n console.error('[visualsentinel-mcp-server] fatal:', err);\n process.exit(1);\n});\n","/**\n * Tiny HTTP client for the Visual Sentinel public API.\n *\n * Reads the API key from `VS_API_KEY` (or `VISUALSENTINEL_API_KEY`) once at\n * construction time. All requests are routed at `https://visualsentinel.com`\n * unless `VS_API_BASE` overrides it (useful for testing against a local\n * dev instance).\n */\n\nconst DEFAULT_BASE = 'https://visualsentinel.com';\n\nexport interface ApiClientOptions {\n apiKey?: string | undefined;\n baseUrl?: string | undefined;\n userAgent?: string;\n}\n\nexport class VsApiError extends Error {\n constructor(\n message: string,\n public readonly status: number,\n public readonly body: unknown,\n ) {\n super(message);\n this.name = 'VsApiError';\n }\n}\n\nexport class ApiClient {\n private readonly apiKey: string | undefined;\n private readonly baseUrl: string;\n private readonly userAgent: string;\n\n constructor(opts: ApiClientOptions = {}) {\n this.apiKey =\n opts.apiKey ??\n process.env.VS_API_KEY ??\n process.env.VISUALSENTINEL_API_KEY ??\n undefined;\n this.baseUrl = (opts.baseUrl ?? process.env.VS_API_BASE ?? DEFAULT_BASE).replace(\n /\\/+$/,\n '',\n );\n this.userAgent = opts.userAgent ?? '@visualsentinel/mcp-server';\n }\n\n hasApiKey(): boolean {\n return typeof this.apiKey === 'string' && this.apiKey.length > 0;\n }\n\n async request<T = unknown>(\n method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE',\n path: string,\n options: { query?: Record<string, string | number | boolean | undefined>; body?: unknown; auth?: boolean } = {},\n ): Promise<T> {\n const auth = options.auth ?? true;\n if (auth && !this.hasApiKey()) {\n throw new VsApiError(\n 'Visual Sentinel API key required. Set VS_API_KEY (https://visualsentinel.com/settings/api-keys).',\n 401,\n null,\n );\n }\n\n const url = new URL(path.startsWith('/') ? path : `/${path}`, this.baseUrl);\n if (options.query) {\n for (const [k, v] of Object.entries(options.query)) {\n if (v !== undefined && v !== null) url.searchParams.set(k, String(v));\n }\n }\n\n const headers: Record<string, string> = {\n Accept: 'application/json',\n 'User-Agent': this.userAgent,\n };\n if (auth && this.apiKey) headers['X-API-Key'] = this.apiKey;\n if (options.body !== undefined) headers['Content-Type'] = 'application/json';\n\n const res = await fetch(url, {\n method,\n headers,\n body: options.body !== undefined ? JSON.stringify(options.body) : undefined,\n });\n\n const text = await res.text();\n let parsed: unknown = null;\n if (text.length > 0) {\n try {\n parsed = JSON.parse(text);\n } catch {\n parsed = text;\n }\n }\n\n if (!res.ok) {\n throw new VsApiError(\n `Visual Sentinel API ${method} ${path} returned ${res.status}`,\n res.status,\n parsed,\n );\n }\n\n return parsed as T;\n }\n}\n","/**\n * Tool registry for the Visual Sentinel MCP server.\n *\n * Each tool wraps a single Visual Sentinel REST endpoint with a JSON Schema\n * input and a structured handler. Public-tool handlers (DNS check, SSL check,\n * speed test, website check, health) run without authentication; everything\n * else requires `VS_API_KEY`.\n */\n\nimport { ApiClient } from './api-client.js';\n\ninterface ToolInput {\n type: 'object';\n properties: Record<string, unknown>;\n required?: string[];\n additionalProperties?: boolean;\n}\n\nexport interface ToolDefinition {\n name: string;\n description: string;\n inputSchema: ToolInput;\n requiresAuth: boolean;\n handler: (args: Record<string, unknown>, client: ApiClient) => Promise<unknown>;\n}\n\nconst STR = { type: 'string' as const };\nconst NUM = { type: 'number' as const };\nconst INT = { type: 'integer' as const };\nconst BOOL = { type: 'boolean' as const };\n\nfunction pickString(args: Record<string, unknown>, key: string): string | undefined {\n const v = args[key];\n return typeof v === 'string' && v.length > 0 ? v : undefined;\n}\n\nfunction pickNumber(args: Record<string, unknown>, key: string): number | undefined {\n const v = args[key];\n return typeof v === 'number' && Number.isFinite(v) ? v : undefined;\n}\n\nfunction requireString(args: Record<string, unknown>, key: string): string {\n const v = pickString(args, key);\n if (!v) throw new Error(`Argument \"${key}\" (string) is required.`);\n return v;\n}\n\nexport const TOOLS: ToolDefinition[] = [\n // -------------------------------------------------------------------------\n // Public tools (no auth)\n // -------------------------------------------------------------------------\n {\n name: 'vs_health',\n description:\n 'Check whether Visual Sentinel itself is up. Returns the service health status. No authentication required.',\n inputSchema: {\n type: 'object',\n properties: {},\n additionalProperties: false,\n },\n requiresAuth: false,\n handler: async (_args, client) => client.request('GET', '/api/health', { auth: false }),\n },\n\n {\n name: 'vs_dns_check',\n description:\n 'Resolve DNS records (A, AAAA, MX, NS, TXT, CNAME) for a domain using Visual Sentinel\\'s public DNS lookup tool. No authentication required.',\n inputSchema: {\n type: 'object',\n properties: {\n domain: { ...STR, description: 'Domain to resolve, e.g. example.com (without protocol).' },\n recordType: {\n ...STR,\n description: 'Optional: limit to one record type. One of: A, AAAA, MX, NS, TXT, CNAME, SOA. Default: all.',\n },\n },\n required: ['domain'],\n additionalProperties: false,\n },\n requiresAuth: false,\n handler: async (args, client) =>\n client.request('GET', '/api/tools/dns-check', {\n auth: false,\n query: {\n domain: requireString(args, 'domain'),\n recordType: pickString(args, 'recordType'),\n },\n }),\n },\n\n {\n name: 'vs_ssl_check',\n description:\n 'Inspect a TLS/SSL certificate for a hostname: issuer, subject, validity dates, SAN list, key algorithm, and certificate chain. No authentication required.',\n inputSchema: {\n type: 'object',\n properties: {\n host: { ...STR, description: 'Hostname to inspect, e.g. example.com (without protocol).' },\n port: { ...INT, description: 'TCP port. Default 443.' },\n },\n required: ['host'],\n additionalProperties: false,\n },\n requiresAuth: false,\n handler: async (args, client) =>\n client.request('GET', '/api/tools/ssl-check', {\n auth: false,\n query: { host: requireString(args, 'host'), port: pickNumber(args, 'port') },\n }),\n },\n\n {\n name: 'vs_speed_test',\n description:\n 'Run a one-shot performance check against a URL: TTFB, total load time, transfer size, status code, and redirect chain. No authentication required.',\n inputSchema: {\n type: 'object',\n properties: {\n url: { ...STR, description: 'Full URL to test, including protocol (https://...).' },\n },\n required: ['url'],\n additionalProperties: false,\n },\n requiresAuth: false,\n handler: async (args, client) =>\n client.request('GET', '/api/tools/speed-test', {\n auth: false,\n query: { url: requireString(args, 'url') },\n }),\n },\n\n {\n name: 'vs_website_check',\n description:\n 'Quick health check for a URL: HTTP status, response time, server header, content snippet, and basic SSL state. No authentication required.',\n inputSchema: {\n type: 'object',\n properties: {\n url: { ...STR, description: 'Full URL to test, including protocol (https://...).' },\n },\n required: ['url'],\n additionalProperties: false,\n },\n requiresAuth: false,\n handler: async (args, client) =>\n client.request('GET', '/api/tools/website-check', {\n auth: false,\n query: { url: requireString(args, 'url') },\n }),\n },\n\n // -------------------------------------------------------------------------\n // Authenticated tools (require VS_API_KEY)\n // -------------------------------------------------------------------------\n {\n name: 'vs_monitors_list',\n description:\n 'List monitors in the authenticated organization. Optional filters narrow by status, type, or paginate.',\n inputSchema: {\n type: 'object',\n properties: {\n status: { ...STR, description: 'Filter: UP, DOWN, PAUSED, MAINTENANCE.' },\n type: {\n ...STR,\n description:\n 'Filter by monitor type: HTTP, HTTPS, KEYWORD, PING, PORT, DNS, SSL, VISUAL, CONTENT, API, GRAPHQL, WEBSOCKET.',\n },\n page: { ...INT, description: 'Page number (default 1).' },\n limit: { ...INT, description: 'Items per page (default 50, max 200).' },\n },\n additionalProperties: false,\n },\n requiresAuth: true,\n handler: async (args, client) =>\n client.request('GET', '/api/monitors', {\n query: {\n status: pickString(args, 'status'),\n type: pickString(args, 'type'),\n page: pickNumber(args, 'page'),\n limit: pickNumber(args, 'limit'),\n },\n }),\n },\n\n {\n name: 'vs_monitors_get',\n description:\n 'Fetch a single monitor by id, including its current status, last check, and configuration.',\n inputSchema: {\n type: 'object',\n properties: {\n id: { ...STR, description: 'Monitor id (cuid).' },\n },\n required: ['id'],\n additionalProperties: false,\n },\n requiresAuth: true,\n handler: async (args, client) =>\n client.request('GET', `/api/monitors/${encodeURIComponent(requireString(args, 'id'))}`),\n },\n\n {\n name: 'vs_monitors_create',\n description:\n 'Create a new monitor. Provide at least name, url, and type. Returns the created monitor including its assigned id.',\n inputSchema: {\n type: 'object',\n properties: {\n name: { ...STR, description: 'Display name for the monitor.' },\n url: { ...STR, description: 'Full URL to monitor (or hostname for PING/PORT/DNS/SSL).' },\n type: {\n ...STR,\n description:\n 'Monitor type: HTTP, HTTPS, KEYWORD, PING, PORT, DNS, SSL, VISUAL, CONTENT, API, GRAPHQL, WEBSOCKET.',\n },\n interval: { ...INT, description: 'Check interval in seconds (60, 300, 600, 1800, 3600).' },\n timeout: { ...INT, description: 'Per-check timeout in seconds (default 30).' },\n method: { ...STR, description: 'HTTP method for HTTP/HTTPS monitors (default GET).' },\n keyword: { ...STR, description: 'Keyword to assert for KEYWORD monitors.' },\n port: { ...INT, description: 'TCP port for PORT monitors.' },\n },\n required: ['name', 'url', 'type'],\n additionalProperties: false,\n },\n requiresAuth: true,\n handler: async (args, client) =>\n client.request('POST', '/api/monitors', {\n body: {\n name: requireString(args, 'name'),\n url: requireString(args, 'url'),\n type: requireString(args, 'type'),\n interval: pickNumber(args, 'interval'),\n timeout: pickNumber(args, 'timeout'),\n method: pickString(args, 'method'),\n keyword: pickString(args, 'keyword'),\n port: pickNumber(args, 'port'),\n },\n }),\n },\n\n {\n name: 'vs_monitors_check_now',\n description:\n 'Trigger an immediate check for a monitor (in addition to its scheduled cadence). Returns the freshly-collected check result.',\n inputSchema: {\n type: 'object',\n properties: {\n id: { ...STR, description: 'Monitor id.' },\n },\n required: ['id'],\n additionalProperties: false,\n },\n requiresAuth: true,\n handler: async (args, client) =>\n client.request('POST', `/api/monitors/${encodeURIComponent(requireString(args, 'id'))}/check`),\n },\n\n {\n name: 'vs_monitors_uptime',\n description:\n 'Fetch the uptime percentage and outage breakdown for a monitor over a window (default last 30 days).',\n inputSchema: {\n type: 'object',\n properties: {\n id: { ...STR, description: 'Monitor id.' },\n window: {\n ...STR,\n description: 'Window: 24h, 7d, 30d, 90d, 365d. Default 30d.',\n },\n },\n required: ['id'],\n additionalProperties: false,\n },\n requiresAuth: true,\n handler: async (args, client) =>\n client.request('GET', `/api/monitors/${encodeURIComponent(requireString(args, 'id'))}/uptime`, {\n query: { window: pickString(args, 'window') },\n }),\n },\n\n {\n name: 'vs_incidents_list',\n description:\n 'List incidents for the authenticated organization. Filter by status (OPEN/RESOLVED), monitor id, or paginate.',\n inputSchema: {\n type: 'object',\n properties: {\n status: { ...STR, description: 'OPEN or RESOLVED.' },\n monitorId: { ...STR, description: 'Filter to one monitor.' },\n page: { ...INT },\n limit: { ...INT, description: 'Default 50, max 200.' },\n },\n additionalProperties: false,\n },\n requiresAuth: true,\n handler: async (args, client) =>\n client.request('GET', '/api/incidents', {\n query: {\n status: pickString(args, 'status'),\n monitorId: pickString(args, 'monitorId'),\n page: pickNumber(args, 'page'),\n limit: pickNumber(args, 'limit'),\n },\n }),\n },\n\n {\n name: 'vs_incidents_get',\n description: 'Fetch a single incident with its full check history and root-cause hints.',\n inputSchema: {\n type: 'object',\n properties: {\n id: { ...STR, description: 'Incident id.' },\n },\n required: ['id'],\n additionalProperties: false,\n },\n requiresAuth: true,\n handler: async (args, client) =>\n client.request('GET', `/api/incidents/${encodeURIComponent(requireString(args, 'id'))}`),\n },\n\n {\n name: 'vs_alerts_list',\n description:\n 'List alerts for the authenticated organization. Filter by status (UNACKNOWLEDGED/ACKNOWLEDGED/RESOLVED) or paginate.',\n inputSchema: {\n type: 'object',\n properties: {\n status: { ...STR, description: 'UNACKNOWLEDGED, ACKNOWLEDGED, or RESOLVED.' },\n page: { ...INT },\n limit: { ...INT, description: 'Default 50, max 200.' },\n },\n additionalProperties: false,\n },\n requiresAuth: true,\n handler: async (args, client) =>\n client.request('GET', '/api/alerts', {\n query: {\n status: pickString(args, 'status'),\n page: pickNumber(args, 'page'),\n limit: pickNumber(args, 'limit'),\n },\n }),\n },\n\n {\n name: 'vs_alerts_acknowledge',\n description: 'Acknowledge an alert by id. Acknowledgement is recorded with the calling API key\\'s user.',\n inputSchema: {\n type: 'object',\n properties: {\n id: { ...STR, description: 'Alert id.' },\n note: { ...STR, description: 'Optional acknowledgement note (visible in alert history).' },\n },\n required: ['id'],\n additionalProperties: false,\n },\n requiresAuth: true,\n handler: async (args, client) =>\n client.request('POST', `/api/alerts/${encodeURIComponent(requireString(args, 'id'))}/acknowledge`, {\n body: { note: pickString(args, 'note') },\n }),\n },\n\n {\n name: 'vs_status_pages_list',\n description: 'List public-facing status pages owned by the authenticated organization.',\n inputSchema: {\n type: 'object',\n properties: {},\n additionalProperties: false,\n },\n requiresAuth: true,\n handler: async (_args, client) => client.request('GET', '/api/status-pages'),\n },\n\n {\n name: 'vs_servers_list',\n description: 'List servers (origins) registered in the authenticated organization.',\n inputSchema: {\n type: 'object',\n properties: {\n page: { ...INT },\n limit: { ...INT, description: 'Default 50.' },\n },\n additionalProperties: false,\n },\n requiresAuth: true,\n handler: async (args, client) =>\n client.request('GET', '/api/servers', {\n query: { page: pickNumber(args, 'page'), limit: pickNumber(args, 'limit') },\n }),\n },\n];\n\nexport function findTool(name: string): ToolDefinition | undefined {\n return TOOLS.find((t) => t.name === name);\n}\n\n// Suppress unused-warning suppression: BOOL/NUM are reserved for future tools.\nvoid BOOL;\nvoid NUM;\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@visualsentinel/mcp-server",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Model Context Protocol server for Visual Sentinel: six-layer website monitoring (uptime, performance, SSL, DNS, visual regression, content change) accessible from Claude Desktop, Claude Code, Cline, Continue, and any MCP-compatible client.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"visualsentinel-mcp-server": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "dist/index.js",
|
|
10
|
+
"types": "dist/index.d.ts",
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"README.md",
|
|
14
|
+
"LICENSE",
|
|
15
|
+
"CHANGELOG.md"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsup",
|
|
19
|
+
"dev": "tsup --watch",
|
|
20
|
+
"start": "node dist/index.js",
|
|
21
|
+
"lint": "tsc --noEmit",
|
|
22
|
+
"test": "node --test tests/*.test.mjs",
|
|
23
|
+
"prepublishOnly": "npm run lint && npm run build"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"mcp",
|
|
27
|
+
"model-context-protocol",
|
|
28
|
+
"visual-sentinel",
|
|
29
|
+
"website-monitoring",
|
|
30
|
+
"uptime",
|
|
31
|
+
"ssl",
|
|
32
|
+
"dns",
|
|
33
|
+
"visual-regression",
|
|
34
|
+
"claude",
|
|
35
|
+
"claude-desktop",
|
|
36
|
+
"cline"
|
|
37
|
+
],
|
|
38
|
+
"author": "Visual Sentinel <support@visualsentinel.com>",
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"homepage": "https://github.com/VisualSentinel/mcp-server#readme",
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "git+https://github.com/VisualSentinel/mcp-server.git"
|
|
44
|
+
},
|
|
45
|
+
"bugs": {
|
|
46
|
+
"url": "https://github.com/VisualSentinel/mcp-server/issues"
|
|
47
|
+
},
|
|
48
|
+
"engines": {
|
|
49
|
+
"node": ">=18"
|
|
50
|
+
},
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
53
|
+
"zod": "^3.23.0"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@types/node": "^22.0.0",
|
|
57
|
+
"tsup": "^8.0.0",
|
|
58
|
+
"typescript": "^5.4.0"
|
|
59
|
+
},
|
|
60
|
+
"publishConfig": {
|
|
61
|
+
"access": "public"
|
|
62
|
+
}
|
|
63
|
+
}
|