@tikoci/centrs 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 +207 -0
- package/GLOSSARY.txt +420 -0
- package/README.md +196 -0
- package/SECURITY.md +30 -0
- package/commands/AGENTS.md +50 -0
- package/commands/btest/README.md +257 -0
- package/commands/btest/examples.md +190 -0
- package/commands/check/README.md +59 -0
- package/commands/devices/README.md +405 -0
- package/commands/devices/examples.md +488 -0
- package/commands/discover/README.md +170 -0
- package/commands/discover/examples.md +98 -0
- package/commands/execute/README.md +208 -0
- package/commands/execute/examples.md +353 -0
- package/commands/mcp/README.md +260 -0
- package/commands/mcp/examples.md +158 -0
- package/commands/retrieve/README.md +209 -0
- package/commands/retrieve/examples.md +306 -0
- package/commands/settings/README.md +69 -0
- package/commands/stream/README.md +70 -0
- package/commands/terminal/README.md +177 -0
- package/commands/terminal/examples.md +144 -0
- package/commands/transfer/README.md +315 -0
- package/commands/transfer/examples.md +368 -0
- package/docs/CONSTITUTION.md +417 -0
- package/docs/MATRIX.md +224 -0
- package/docs/errors/README.md +74 -0
- package/docs/errors/auth/failed.md +9 -0
- package/docs/errors/cdb/already-exists.md +9 -0
- package/docs/errors/cdb/created.md +9 -0
- package/docs/errors/cdb/decrypt-failed.md +9 -0
- package/docs/errors/cdb/empty-group.md +9 -0
- package/docs/errors/cdb/invalid-option.md +9 -0
- package/docs/errors/cdb/invalid-record.md +9 -0
- package/docs/errors/cdb/not-found-target.md +9 -0
- package/docs/errors/cdb/not-found.md +9 -0
- package/docs/errors/cdb/override-applied.md +9 -0
- package/docs/errors/cdb/parse-failed.md +9 -0
- package/docs/errors/cdb/password-not-needed.md +9 -0
- package/docs/errors/cdb/password-required.md +9 -0
- package/docs/errors/cdb/reserved-key.md +9 -0
- package/docs/errors/cdb/reserved-option.md +9 -0
- package/docs/errors/cdb/target-not-registered.md +9 -0
- package/docs/errors/cdb/unknown-field.md +9 -0
- package/docs/errors/cdb/unknown-option.md +9 -0
- package/docs/errors/cdb/write-not-permitted.md +9 -0
- package/docs/errors/discover/broadcast-unavailable.md +9 -0
- package/docs/errors/discover/failed.md +9 -0
- package/docs/errors/identity/ambiguous.md +9 -0
- package/docs/errors/identity/no-match.md +9 -0
- package/docs/errors/input/invalid-command.md +9 -0
- package/docs/errors/input/invalid-match.md +9 -0
- package/docs/errors/input/invalid-path.md +9 -0
- package/docs/errors/input/invalid-routeros-path.md +9 -0
- package/docs/errors/input/invalid-target.md +9 -0
- package/docs/errors/input/local-file-not-found.md +9 -0
- package/docs/errors/input/mac-address.md +9 -0
- package/docs/errors/input/max-results-exceeded.md +9 -0
- package/docs/errors/internal/devices-failed.md +9 -0
- package/docs/errors/internal/mcp-start.md +9 -0
- package/docs/errors/internal/mcp-tool.md +9 -0
- package/docs/errors/internal/native-api-length.md +9 -0
- package/docs/errors/internal/unhandled.md +9 -0
- package/docs/errors/internal/unreachable.md +9 -0
- package/docs/errors/mndp/encode-failed.md +9 -0
- package/docs/errors/mndp/listen-failed.md +9 -0
- package/docs/errors/mndp/malformed.md +9 -0
- package/docs/errors/routeros/api-fatal.md +9 -0
- package/docs/errors/routeros/api-protocol.md +9 -0
- package/docs/errors/routeros/api-trap.md +9 -0
- package/docs/errors/routeros/btest-protocol.md +12 -0
- package/docs/errors/routeros/btest-too-many-sessions.md +9 -0
- package/docs/errors/routeros/command-failed.md +14 -0
- package/docs/errors/routeros/ec-srp5-protocol.md +11 -0
- package/docs/errors/routeros/error.md +9 -0
- package/docs/errors/routeros/invalid-value.md +14 -0
- package/docs/errors/routeros/mac-telnet-error.md +9 -0
- package/docs/errors/routeros/mac-telnet-not-ready.md +9 -0
- package/docs/errors/routeros/mac-telnet-protocol.md +9 -0
- package/docs/errors/routeros/mac-telnet-unsupported-auth.md +9 -0
- package/docs/errors/routeros/protocol-not-implemented.md +9 -0
- package/docs/errors/routeros/request-failed.md +9 -0
- package/docs/errors/routeros/session-closed.md +13 -0
- package/docs/errors/routeros/unknown-attribute.md +13 -0
- package/docs/errors/routeros/unknown-path.md +15 -0
- package/docs/errors/routeros/unsupported-capability.md +9 -0
- package/docs/errors/settings/invalid-boolean.md +9 -0
- package/docs/errors/settings/invalid-format.md +9 -0
- package/docs/errors/settings/invalid-integer.md +9 -0
- package/docs/errors/settings/invalid-timeout.md +9 -0
- package/docs/errors/settings/invalid-via.md +9 -0
- package/docs/errors/settings/unsafe-protocol-blocked.md +9 -0
- package/docs/errors/target/mac-not-in-arp.md +14 -0
- package/docs/errors/target/mac-required.md +23 -0
- package/docs/errors/target/mac-unresolved.md +17 -0
- package/docs/errors/target/unresolved.md +9 -0
- package/docs/errors/transport/auth-failed.md +9 -0
- package/docs/errors/transport/auto-method.md +9 -0
- package/docs/errors/transport/capability-unsupported.md +22 -0
- package/docs/errors/transport/checksum-unavailable.md +9 -0
- package/docs/errors/transport/connection-closed.md +9 -0
- package/docs/errors/transport/connection-refused.md +9 -0
- package/docs/errors/transport/dns.md +9 -0
- package/docs/errors/transport/host-key-mismatch.md +9 -0
- package/docs/errors/transport/incomplete-transfer.md +9 -0
- package/docs/errors/transport/insecure-trust.md +9 -0
- package/docs/errors/transport/local-tool-missing.md +11 -0
- package/docs/errors/transport/network.md +9 -0
- package/docs/errors/transport/timeout.md +9 -0
- package/docs/errors/transport/tls-certificate.md +9 -0
- package/docs/errors/transport/unreachable.md +9 -0
- package/docs/errors/transport/unsupported-operation.md +9 -0
- package/docs/errors/usage/confirmation-required.md +9 -0
- package/docs/errors/usage/conflicting-flags.md +9 -0
- package/docs/errors/usage/invalid-concurrency.md +9 -0
- package/docs/errors/usage/missing-group.md +9 -0
- package/docs/errors/usage/not-implemented.md +9 -0
- package/docs/errors/usage/target-exists.md +9 -0
- package/docs/errors/usage/timeout-out-of-range.md +9 -0
- package/docs/errors/validation/not-implemented.md +9 -0
- package/docs/errors/validation/option.md +9 -0
- package/docs/errors/validation/syntax.md +9 -0
- package/docs/errors/validation/unknown-attribute.md +9 -0
- package/docs/errors/validation/unknown-path.md +9 -0
- package/package.json +96 -0
- package/src/AGENTS.md +7 -0
- package/src/btest.ts +906 -0
- package/src/cli/btest.ts +455 -0
- package/src/cli/common.ts +53 -0
- package/src/cli/devices.ts +527 -0
- package/src/cli/discover.ts +186 -0
- package/src/cli/execute.ts +333 -0
- package/src/cli/mcp.ts +109 -0
- package/src/cli/retrieve.ts +383 -0
- package/src/cli/terminal.ts +303 -0
- package/src/cli/transfer.ts +471 -0
- package/src/cli.ts +115 -0
- package/src/core/envelope.ts +221 -0
- package/src/core/error-catalog.ts +469 -0
- package/src/core/routeros-errors.ts +268 -0
- package/src/data/AGENTS.md +11 -0
- package/src/data/dude-db.ts +0 -0
- package/src/data/mndp-cache.ts +132 -0
- package/src/data/mndp.ts +405 -0
- package/src/data/winbox-cdb-write.ts +200 -0
- package/src/data/winbox-cdb.ts +913 -0
- package/src/devices-schema.ts +107 -0
- package/src/devices.ts +1717 -0
- package/src/discover.ts +802 -0
- package/src/errors.ts +189 -0
- package/src/execute.ts +1350 -0
- package/src/index.ts +353 -0
- package/src/mcp/config.ts +66 -0
- package/src/mcp/resources.ts +98 -0
- package/src/mcp/safety.ts +119 -0
- package/src/mcp/server.ts +225 -0
- package/src/mcp/tools.ts +657 -0
- package/src/mcp.ts +33 -0
- package/src/protocols/AGENTS.md +14 -0
- package/src/protocols/adapter.ts +866 -0
- package/src/protocols/btest-session.ts +1493 -0
- package/src/protocols/btest.ts +447 -0
- package/src/protocols/ec-srp5.ts +466 -0
- package/src/protocols/index.ts +118 -0
- package/src/protocols/mac-telnet-console.ts +655 -0
- package/src/protocols/mac-telnet.ts +1299 -0
- package/src/protocols/mtwei.ts +47 -0
- package/src/protocols/native-api.ts +796 -0
- package/src/protocols/sftp.ts +427 -0
- package/src/protocols/ssh.ts +297 -0
- package/src/resolver/cdb.ts +580 -0
- package/src/resolver/comment-kv.ts +360 -0
- package/src/resolver/index.ts +74 -0
- package/src/resolver/mac.ts +227 -0
- package/src/resolver/settings.ts +228 -0
- package/src/resolver/target.ts +442 -0
- package/src/retrieve-fanout.ts +533 -0
- package/src/retrieve.ts +1215 -0
- package/src/terminal.ts +444 -0
- package/src/transfer.ts +1554 -0
- package/src/tui.ts +22 -0
- package/src/webproxy.ts +16 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The runnable centrs MCP server (stdio).
|
|
3
|
+
*
|
|
4
|
+
* Wires the scoped-verb tool handlers (`./tools.ts`) and the read-only
|
|
5
|
+
* resources (`./resources.ts`) onto an `McpServer`, with `instructions` that
|
|
6
|
+
* point an agent at the allowlist and error catalog. Transport is stdio only —
|
|
7
|
+
* HTTP/remote access is the proxy surface's job, not the MCP server's.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
11
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
12
|
+
import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
|
|
13
|
+
import { type CentrsMcpConfig, resolveMcpConfig } from "./config.ts";
|
|
14
|
+
import {
|
|
15
|
+
buildDevicesResource,
|
|
16
|
+
buildErrorsResource,
|
|
17
|
+
DEVICES_RESOURCE_URI,
|
|
18
|
+
ERRORS_RESOURCE_URI,
|
|
19
|
+
} from "./resources.ts";
|
|
20
|
+
import {
|
|
21
|
+
devicesInputShape,
|
|
22
|
+
discoverInputShape,
|
|
23
|
+
executeInputShape,
|
|
24
|
+
explainInputShape,
|
|
25
|
+
handleDevices,
|
|
26
|
+
handleDiscover,
|
|
27
|
+
handleExecute,
|
|
28
|
+
handleExplain,
|
|
29
|
+
handleRetrieve,
|
|
30
|
+
handleValidate,
|
|
31
|
+
retrieveInputShape,
|
|
32
|
+
validateInputShape,
|
|
33
|
+
} from "./tools.ts";
|
|
34
|
+
|
|
35
|
+
const SERVER_INSTRUCTIONS = [
|
|
36
|
+
"centrs is a RouterOS interaction hub. These tools reach real MikroTik devices",
|
|
37
|
+
"through a validated core, gated by the CDB device allowlist.",
|
|
38
|
+
"",
|
|
39
|
+
"Safety model:",
|
|
40
|
+
"- Tools act only on devices registered in the CDB (the allowlist). An",
|
|
41
|
+
" unregistered target is rejected with `cdb/target-not-registered`; register",
|
|
42
|
+
" it via centrs_devices (op add) first. Credentials live in the CDB and are",
|
|
43
|
+
" never returned by this interface.",
|
|
44
|
+
"- RouterOS writes are double-gated: the device's CDB record must be `mcp=rw`",
|
|
45
|
+
" (else `cdb/write-not-permitted`) and centrs_execute must be called with",
|
|
46
|
+
" confirm:true (else `usage/confirmation-required`). CDB mutations through",
|
|
47
|
+
" centrs_devices and centrs_discover save also require confirm:true and never",
|
|
48
|
+
" return saved passwords.",
|
|
49
|
+
"",
|
|
50
|
+
"Recommended flow: centrs_explain (offline canonicalize) → centrs_validate",
|
|
51
|
+
"(dry-run :parse + /console/inspect, never mutates) → centrs_retrieve (read)",
|
|
52
|
+
"or centrs_execute (run). Use centrs_discover to find MNDP neighbors and",
|
|
53
|
+
"centrs_devices to curate the CDB allowlist. Prefer validate before any write.",
|
|
54
|
+
"",
|
|
55
|
+
"Resources:",
|
|
56
|
+
`- ${DEVICES_RESOURCE_URI}: the known devices on the allowlist and each`,
|
|
57
|
+
" record's MCP write policy (no passwords).",
|
|
58
|
+
`- ${ERRORS_RESOURCE_URI}: the error-code catalog with details URLs; consult`,
|
|
59
|
+
" it to resolve any envelope `error.code`.",
|
|
60
|
+
"",
|
|
61
|
+
"Every tool returns the standard centrs envelope { ok, data?|error, warnings,",
|
|
62
|
+
"meta } as JSON text. On failure, read `error.code` and `error.remediation`.",
|
|
63
|
+
].join("\n");
|
|
64
|
+
|
|
65
|
+
function jsonContent(value: unknown): {
|
|
66
|
+
content: { type: "text"; text: string }[];
|
|
67
|
+
} {
|
|
68
|
+
return {
|
|
69
|
+
content: [{ type: "text", text: JSON.stringify(value, null, 2) }],
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Build a configured centrs MCP server. The returned `McpServer` is transport
|
|
75
|
+
* agnostic; call `.connect(transport)` (stdio in production, an in-memory pair
|
|
76
|
+
* in tests).
|
|
77
|
+
*/
|
|
78
|
+
export function createCentrsMcpServer(
|
|
79
|
+
config: CentrsMcpConfig = resolveMcpConfig(),
|
|
80
|
+
): McpServer {
|
|
81
|
+
const server = new McpServer(
|
|
82
|
+
{ name: "centrs-mcp", version: "0.1.0" },
|
|
83
|
+
{ instructions: SERVER_INSTRUCTIONS },
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
server.registerTool(
|
|
87
|
+
"centrs_explain",
|
|
88
|
+
{
|
|
89
|
+
title: "Explain (canonicalize) a RouterOS command",
|
|
90
|
+
description:
|
|
91
|
+
"Offline: canonicalize a RouterOS CLI string to { path, verb, attributes, queries, mode, writeShaped }. No device or CDB needed.",
|
|
92
|
+
inputSchema: explainInputShape,
|
|
93
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
94
|
+
},
|
|
95
|
+
(args) => jsonContent(handleExplain(args)),
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
server.registerTool(
|
|
99
|
+
"centrs_validate",
|
|
100
|
+
{
|
|
101
|
+
title: "Validate a RouterOS command (dry-run)",
|
|
102
|
+
description:
|
|
103
|
+
"Dry-run a command through :parse + /console/inspect against a registered device. Never mutates. Catches parser/flag-shape errors schema inspection misses.",
|
|
104
|
+
inputSchema: validateInputShape,
|
|
105
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
106
|
+
},
|
|
107
|
+
async (args) => jsonContent(await handleValidate(args, config)),
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
server.registerTool(
|
|
111
|
+
"centrs_retrieve",
|
|
112
|
+
{
|
|
113
|
+
title: "Retrieve RouterOS state",
|
|
114
|
+
description:
|
|
115
|
+
"Read RouterOS state over the API for a registered target or group, with attribute projection and group fanout. Read-only.",
|
|
116
|
+
inputSchema: retrieveInputShape,
|
|
117
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
118
|
+
},
|
|
119
|
+
async (args) => jsonContent(await handleRetrieve(args, config)),
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
server.registerTool(
|
|
123
|
+
"centrs_execute",
|
|
124
|
+
{
|
|
125
|
+
title: "Execute a RouterOS command",
|
|
126
|
+
description:
|
|
127
|
+
"Run a read- or write-shaped command against a registered device. Writes require the CDB record to be mcp=rw and confirm:true.",
|
|
128
|
+
inputSchema: executeInputShape,
|
|
129
|
+
// destructiveHint is conservative: this one tool serves both read- and
|
|
130
|
+
// write-shaped commands, and a static annotation can't vary per call, so
|
|
131
|
+
// it advertises the worst case. Real enforcement is the canonicalizer's
|
|
132
|
+
// write-shape check plus the CDB mcp=rw/confirm gates in handleExecute;
|
|
133
|
+
// clients wanting a truly read-only path should use centrs_retrieve.
|
|
134
|
+
annotations: {
|
|
135
|
+
readOnlyHint: false,
|
|
136
|
+
destructiveHint: true,
|
|
137
|
+
openWorldHint: true,
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
async (args) => jsonContent(await handleExecute(args, config)),
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
server.registerTool(
|
|
144
|
+
"centrs_devices",
|
|
145
|
+
{
|
|
146
|
+
title: "Inspect or mutate the CDB device registry",
|
|
147
|
+
description:
|
|
148
|
+
"Read or update the CDB allowlist. op=list/show/groups are read-only; op=add/edit/set/remove write the CDB and require confirm:true.",
|
|
149
|
+
inputSchema: devicesInputShape,
|
|
150
|
+
annotations: {
|
|
151
|
+
readOnlyHint: false,
|
|
152
|
+
destructiveHint: true,
|
|
153
|
+
openWorldHint: false,
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
async (args) => jsonContent(await handleDevices(args, config)),
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
server.registerTool(
|
|
160
|
+
"centrs_discover",
|
|
161
|
+
{
|
|
162
|
+
title: "Discover MNDP neighbors",
|
|
163
|
+
description:
|
|
164
|
+
"Listen for MNDP neighbors and optionally save them into the active CDB. save=true writes the CDB and requires confirm:true.",
|
|
165
|
+
inputSchema: discoverInputShape,
|
|
166
|
+
annotations: {
|
|
167
|
+
readOnlyHint: false,
|
|
168
|
+
destructiveHint: true,
|
|
169
|
+
openWorldHint: true,
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
async (args) => jsonContent(await handleDiscover(args, config)),
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
server.registerResource(
|
|
176
|
+
"devices",
|
|
177
|
+
DEVICES_RESOURCE_URI,
|
|
178
|
+
{
|
|
179
|
+
title: "Known devices (CDB allowlist)",
|
|
180
|
+
description:
|
|
181
|
+
"The devices registered in the active CDB and each record's MCP write policy. No passwords.",
|
|
182
|
+
mimeType: "application/json",
|
|
183
|
+
},
|
|
184
|
+
async (uri) => ({
|
|
185
|
+
contents: [
|
|
186
|
+
{
|
|
187
|
+
uri: uri.href,
|
|
188
|
+
mimeType: "application/json",
|
|
189
|
+
text: JSON.stringify(await buildDevicesResource(config), null, 2),
|
|
190
|
+
},
|
|
191
|
+
],
|
|
192
|
+
}),
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
server.registerResource(
|
|
196
|
+
"errors",
|
|
197
|
+
ERRORS_RESOURCE_URI,
|
|
198
|
+
{
|
|
199
|
+
title: "centrs error catalog",
|
|
200
|
+
description:
|
|
201
|
+
"Known centrs error codes with stable details URLs, including the MCP-specific cdb/target-not-registered and cdb/write-not-permitted.",
|
|
202
|
+
mimeType: "application/json",
|
|
203
|
+
},
|
|
204
|
+
async (uri) => ({
|
|
205
|
+
contents: [
|
|
206
|
+
{
|
|
207
|
+
uri: uri.href,
|
|
208
|
+
mimeType: "application/json",
|
|
209
|
+
text: JSON.stringify(buildErrorsResource(), null, 2),
|
|
210
|
+
},
|
|
211
|
+
],
|
|
212
|
+
}),
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
return server;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** Run the centrs MCP server over stdio until the transport closes. */
|
|
219
|
+
export async function runMcpStdio(
|
|
220
|
+
config: CentrsMcpConfig = resolveMcpConfig(),
|
|
221
|
+
transport: Transport = new StdioServerTransport(),
|
|
222
|
+
): Promise<void> {
|
|
223
|
+
const server = createCentrsMcpServer(config);
|
|
224
|
+
await server.connect(transport);
|
|
225
|
+
}
|