@aristanetworks/eos-mcp-server 0.6.1-beta

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.
Files changed (170) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/LICENSE +203 -0
  3. package/README.md +652 -0
  4. package/dist/cli.d.ts +16 -0
  5. package/dist/cli.js +188 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/commands/printServerInfo.d.ts +2 -0
  8. package/dist/commands/printServerInfo.js +14 -0
  9. package/dist/commands/printServerInfo.js.map +1 -0
  10. package/dist/commands/probe.d.ts +2 -0
  11. package/dist/commands/probe.js +26 -0
  12. package/dist/commands/probe.js.map +1 -0
  13. package/dist/commands/validateInventory.d.ts +9 -0
  14. package/dist/commands/validateInventory.js +59 -0
  15. package/dist/commands/validateInventory.js.map +1 -0
  16. package/dist/config/loadConfig.d.ts +3 -0
  17. package/dist/config/loadConfig.js +84 -0
  18. package/dist/config/loadConfig.js.map +1 -0
  19. package/dist/config/schema.d.ts +151 -0
  20. package/dist/config/schema.js +54 -0
  21. package/dist/config/schema.js.map +1 -0
  22. package/dist/configuration/getRunningConfig.d.ts +24 -0
  23. package/dist/configuration/getRunningConfig.js +38 -0
  24. package/dist/configuration/getRunningConfig.js.map +1 -0
  25. package/dist/connection/eosDeviceReader.d.ts +18 -0
  26. package/dist/connection/eosDeviceReader.js +13 -0
  27. package/dist/connection/eosDeviceReader.js.map +1 -0
  28. package/dist/connection/resolveConnection.d.ts +4 -0
  29. package/dist/connection/resolveConnection.js +58 -0
  30. package/dist/connection/resolveConnection.js.map +1 -0
  31. package/dist/connection/validateStartupConnections.d.ts +4 -0
  32. package/dist/connection/validateStartupConnections.js +28 -0
  33. package/dist/connection/validateStartupConnections.js.map +1 -0
  34. package/dist/core/errors.d.ts +8 -0
  35. package/dist/core/errors.js +22 -0
  36. package/dist/core/errors.js.map +1 -0
  37. package/dist/core/version.d.ts +2 -0
  38. package/dist/core/version.js +7 -0
  39. package/dist/core/version.js.map +1 -0
  40. package/dist/eapi/client.d.ts +8 -0
  41. package/dist/eapi/client.js +159 -0
  42. package/dist/eapi/client.js.map +1 -0
  43. package/dist/eapi/commands.d.ts +2 -0
  44. package/dist/eapi/commands.js +25 -0
  45. package/dist/eapi/commands.js.map +1 -0
  46. package/dist/eapi/transport.d.ts +21 -0
  47. package/dist/eapi/transport.js +143 -0
  48. package/dist/eapi/transport.js.map +1 -0
  49. package/dist/eapi/types.d.ts +49 -0
  50. package/dist/eapi/types.js +15 -0
  51. package/dist/eapi/types.js.map +1 -0
  52. package/dist/facts/getFacts.d.ts +33 -0
  53. package/dist/facts/getFacts.js +50 -0
  54. package/dist/facts/getFacts.js.map +1 -0
  55. package/dist/index.d.ts +2 -0
  56. package/dist/index.js +66 -0
  57. package/dist/index.js.map +1 -0
  58. package/dist/inventory/buildModel.d.ts +3 -0
  59. package/dist/inventory/buildModel.js +79 -0
  60. package/dist/inventory/buildModel.js.map +1 -0
  61. package/dist/inventory/effectiveVars.d.ts +2 -0
  62. package/dist/inventory/effectiveVars.js +52 -0
  63. package/dist/inventory/effectiveVars.js.map +1 -0
  64. package/dist/inventory/graph.d.ts +6 -0
  65. package/dist/inventory/graph.js +81 -0
  66. package/dist/inventory/graph.js.map +1 -0
  67. package/dist/inventory/internalTypes.d.ts +17 -0
  68. package/dist/inventory/internalTypes.js +2 -0
  69. package/dist/inventory/internalTypes.js.map +1 -0
  70. package/dist/inventory/internalUtils.d.ts +10 -0
  71. package/dist/inventory/internalUtils.js +68 -0
  72. package/dist/inventory/internalUtils.js.map +1 -0
  73. package/dist/inventory/listInventoryView.d.ts +23 -0
  74. package/dist/inventory/listInventoryView.js +22 -0
  75. package/dist/inventory/listInventoryView.js.map +1 -0
  76. package/dist/inventory/loadInventory.d.ts +4 -0
  77. package/dist/inventory/loadInventory.js +87 -0
  78. package/dist/inventory/loadInventory.js.map +1 -0
  79. package/dist/inventory/normalize.d.ts +4 -0
  80. package/dist/inventory/normalize.js +251 -0
  81. package/dist/inventory/normalize.js.map +1 -0
  82. package/dist/inventory/parse.d.ts +6 -0
  83. package/dist/inventory/parse.js +31 -0
  84. package/dist/inventory/parse.js.map +1 -0
  85. package/dist/inventory/policy.d.ts +11 -0
  86. package/dist/inventory/policy.js +27 -0
  87. package/dist/inventory/policy.js.map +1 -0
  88. package/dist/inventory/resolveTarget.d.ts +14 -0
  89. package/dist/inventory/resolveTarget.js +55 -0
  90. package/dist/inventory/resolveTarget.js.map +1 -0
  91. package/dist/inventory/types.d.ts +50 -0
  92. package/dist/inventory/types.js +2 -0
  93. package/dist/inventory/types.js.map +1 -0
  94. package/dist/inventory/validateNormalized.d.ts +2 -0
  95. package/dist/inventory/validateNormalized.js +64 -0
  96. package/dist/inventory/validateNormalized.js.map +1 -0
  97. package/dist/inventory/varValidation.d.ts +2 -0
  98. package/dist/inventory/varValidation.js +60 -0
  99. package/dist/inventory/varValidation.js.map +1 -0
  100. package/dist/inventory/writePolicy.d.ts +2 -0
  101. package/dist/inventory/writePolicy.js +36 -0
  102. package/dist/inventory/writePolicy.js.map +1 -0
  103. package/dist/logging/loggingQuery.d.ts +15 -0
  104. package/dist/logging/loggingQuery.js +38 -0
  105. package/dist/logging/loggingQuery.js.map +1 -0
  106. package/dist/logging/showLogging.d.ts +28 -0
  107. package/dist/logging/showLogging.js +37 -0
  108. package/dist/logging/showLogging.js.map +1 -0
  109. package/dist/mcp/createServer.d.ts +3 -0
  110. package/dist/mcp/createServer.js +82 -0
  111. package/dist/mcp/createServer.js.map +1 -0
  112. package/dist/mcp/toolResult.d.ts +7 -0
  113. package/dist/mcp/toolResult.js +13 -0
  114. package/dist/mcp/toolResult.js.map +1 -0
  115. package/dist/mcp/tools/getFacts.d.ts +21 -0
  116. package/dist/mcp/tools/getFacts.js +17 -0
  117. package/dist/mcp/tools/getFacts.js.map +1 -0
  118. package/dist/mcp/tools/getRunningConfig.d.ts +21 -0
  119. package/dist/mcp/tools/getRunningConfig.js +17 -0
  120. package/dist/mcp/tools/getRunningConfig.js.map +1 -0
  121. package/dist/mcp/tools/getServerInfo.d.ts +10 -0
  122. package/dist/mcp/tools/getServerInfo.js +9 -0
  123. package/dist/mcp/tools/getServerInfo.js.map +1 -0
  124. package/dist/mcp/tools/listInventory.d.ts +16 -0
  125. package/dist/mcp/tools/listInventory.js +15 -0
  126. package/dist/mcp/tools/listInventory.js.map +1 -0
  127. package/dist/mcp/tools/probeDevices.d.ts +21 -0
  128. package/dist/mcp/tools/probeDevices.js +14 -0
  129. package/dist/mcp/tools/probeDevices.js.map +1 -0
  130. package/dist/mcp/tools/runShow.d.ts +30 -0
  131. package/dist/mcp/tools/runShow.js +34 -0
  132. package/dist/mcp/tools/runShow.js.map +1 -0
  133. package/dist/mcp/tools/showLogging.d.ts +24 -0
  134. package/dist/mcp/tools/showLogging.js +20 -0
  135. package/dist/mcp/tools/showLogging.js.map +1 -0
  136. package/dist/operations/readExecution.d.ts +57 -0
  137. package/dist/operations/readExecution.js +132 -0
  138. package/dist/operations/readExecution.js.map +1 -0
  139. package/dist/probe/probeDevices.d.ts +24 -0
  140. package/dist/probe/probeDevices.js +24 -0
  141. package/dist/probe/probeDevices.js.map +1 -0
  142. package/dist/serverInfo/buildServerInfo.d.ts +2 -0
  143. package/dist/serverInfo/buildServerInfo.js +49 -0
  144. package/dist/serverInfo/buildServerInfo.js.map +1 -0
  145. package/dist/serverInfo/types.d.ts +46 -0
  146. package/dist/serverInfo/types.js +2 -0
  147. package/dist/serverInfo/types.js.map +1 -0
  148. package/dist/show/runShow.d.ts +29 -0
  149. package/dist/show/runShow.js +40 -0
  150. package/dist/show/runShow.js.map +1 -0
  151. package/dist/utils/json.d.ts +1 -0
  152. package/dist/utils/json.js +4 -0
  153. package/dist/utils/json.js.map +1 -0
  154. package/dist/utils/path.d.ts +2 -0
  155. package/dist/utils/path.js +14 -0
  156. package/dist/utils/path.js.map +1 -0
  157. package/dist/utils/value.d.ts +6 -0
  158. package/dist/utils/value.js +28 -0
  159. package/dist/utils/value.js.map +1 -0
  160. package/docs/CHECKPOINT.md +46 -0
  161. package/docs/CODE_WALKTHROUGH.md +112 -0
  162. package/docs/DESIGN.md +56 -0
  163. package/docs/DEVELOPMENT.md +72 -0
  164. package/docs/EXAMPLES.md +178 -0
  165. package/docs/IMPLEMENTATION_PLAN.md +32 -0
  166. package/docs/QUICKSTART.md +152 -0
  167. package/example-inventories/canonical-fabric.yaml +45 -0
  168. package/example-inventories/multi-password.yaml +35 -0
  169. package/example-inventories/simplified-lab.yaml +33 -0
  170. package/package.json +57 -0
package/README.md ADDED
@@ -0,0 +1,652 @@
1
+ # eos-mcp-server BETA
2
+
3
+ Read-only MCP server for Arista EOS eAPI (JSON-RPC over HTTPS).
4
+
5
+ > **Beta status:** this project is under active development.
6
+ > **Support:** best-effort only. This open source project is not currently supported by Arista TAC.
7
+
8
+ This server provides a set of **read-only** tools. It never exposes configuration-changing operations. The server can:
9
+
10
+ - introspect its own runtime and inventory
11
+ - list inventory hosts/groups
12
+ - probe EOS device readiness
13
+ - run `show` commands
14
+ - retrieve bounded logging output for troubleshooting
15
+ - collect a fixed set of device facts
16
+ - retrieve running configuration
17
+
18
+ > **Security note:** LLMs have occasionally tried to bypass the MCP server and make configuration changes through other means (for example, SSH). As a best practice, use AAA and scope the authentication credentials used by this server to read-only access.
19
+
20
+ ## Contents
21
+
22
+ - [What's included](#whats-included)
23
+ - [Requirements](#requirements)
24
+ - [Install and get started](#install-and-get-started)
25
+ - [Server config file](#server-config-file)
26
+ - [Connect to an MCP client](#connect-to-an-mcp-client)
27
+ - [CLI usage](#cli-usage)
28
+ - [MCP tool reference](#mcp-tool-reference)
29
+ - [Inventory formats](#inventory-formats)
30
+ - [Connection and authentication](#connection-and-authentication)
31
+ - [TLS behavior](#tls-behavior)
32
+ - [Common troubleshooting](#common-troubleshooting)
33
+ - [EOS version compatibility](#eos-version-compatibility)
34
+
35
+ ## What's included
36
+
37
+ Implemented MCP tools:
38
+
39
+ - `eos_get_server_info`
40
+ - `eos_list_inventory`
41
+ - `eos_probe_devices`
42
+ - `eos_run_show`
43
+ - `eos_show_logging`
44
+ - `eos_get_facts`
45
+ - `eos_get_running_config`
46
+
47
+ Implemented local CLI commands:
48
+
49
+ - `serve`
50
+ - `validate-inventory`
51
+ - `print-server-info`
52
+ - `probe`
53
+
54
+ ## Requirements
55
+
56
+ - Node.js 24+
57
+ - Arista EOS devices reachable via eAPI over HTTPS
58
+ - EOS 4.20 or later
59
+ - Inventory in one of the supported YAML formats
60
+
61
+ ## Install and get started
62
+
63
+ 1. Install the package globally from npm:
64
+
65
+ ```bash
66
+ npm install -g @aristanetworks/eos-mcp-server
67
+ ```
68
+
69
+ Prereleases are published under the `next` tag (`npm install -g @aristanetworks/eos-mcp-server@next`). Each [GitHub release](https://github.com/aristanetworks/eos-mcp-server/releases) also attaches the package as a `.tgz`, which you can install with `npm install -g ./aristanetworks-eos-mcp-server-<version>.tgz`.
70
+
71
+ 2. Confirm that the command is available:
72
+
73
+ ```bash
74
+ eos-mcp-server --help
75
+ ```
76
+
77
+ The global install places `eos-mcp-server` on your `PATH`. To remove it later:
78
+
79
+ ```bash
80
+ npm uninstall -g @aristanetworks/eos-mcp-server
81
+ ```
82
+
83
+ Next, create an inventory, provide the device password, and validate connectivity:
84
+
85
+ ```bash
86
+ export EOS_MCP_PASSWORD='replace-with-device-password'
87
+
88
+ eos-mcp-server validate-inventory --inventory inventory.yml
89
+ eos-mcp-server probe --inventory inventory.yml --target leaf1
90
+ ```
91
+
92
+ See [Quickstart](docs/QUICKSTART.md) for a complete inventory example and MCP client setup. For building or contributing from source, see the [developer workflow](docs/DEVELOPMENT.md).
93
+
94
+ ## Server config file
95
+
96
+ The server needs an inventory to start. At minimum, provide one of:
97
+
98
+ - `--inventory <path>` directly, or
99
+ - `--config <path>` pointing at a YAML config file that itself sets `inventory: <path>`
100
+
101
+ Beyond that minimum, a config file is optional — use one when you want to set additional options such as timeouts, request limits, or a default connection. CLI flags always override config-file values.
102
+
103
+ Example:
104
+
105
+ ```yaml
106
+ version: 1
107
+ inventory: ./inventory.yml
108
+ actor: lab-user
109
+ readTimeoutMs: 10000
110
+ overallOperationTimeoutMs: 30000
111
+ deviceConcurrency: 5
112
+ maxReadTargets: 50
113
+ maxShowCommandsPerRequest: 5
114
+ maxLoggingMessagesPerRequest: 1000
115
+ maxResponseSizeBytes: 1048576
116
+ eapiVersion: latest
117
+ secretEnvPrefixes:
118
+ - EOS_MCP_
119
+ defaultConnection:
120
+ ansibleUser: admin
121
+ mcpPasswordEnv: EOS_MCP_PASSWORD
122
+ mcpValidateCerts: false
123
+ ```
124
+
125
+ Supported config fields:
126
+
127
+ - `inventory`
128
+ - `actor`
129
+ - `caFile`
130
+ - `readTimeoutMs`
131
+ - `overallOperationTimeoutMs`
132
+ - `deviceConcurrency`
133
+ - `maxReadTargets`
134
+ - `maxShowCommandsPerRequest`
135
+ - `maxLoggingMessagesPerRequest`
136
+ - `maxResponseSizeBytes`
137
+ - `eapiVersion`
138
+ - `secretEnvPrefixes`
139
+ - `defaultConnection.ansibleUser`
140
+ - `defaultConnection.ansibleHttpapiPort`
141
+ - `defaultConnection.mcpValidateCerts`
142
+ - `defaultConnection.mcpPasswordEnv`
143
+
144
+ `readTimeoutMs` applies to each device eAPI request. `overallOperationTimeoutMs`, when set, bounds the whole tool call and aborts in-flight device requests once the limit is reached. `maxLoggingMessagesPerRequest` caps `eos_show_logging.message_count` and defaults to 1000. `maxResponseSizeBytes` limits both the buffered HTTP response from each device and the final serialized read-tool result. `eapiVersion` selects the eAPI JSON output schema sent as the `runCmds` `version` parameter: `latest` (the default) returns the current schema for each command, and `1` pins the original schema for consumers that depend on it. It only affects JSON output, so `eos_get_running_config` and `eos_show_logging` are unchanged.
145
+
146
+ `defaultConnection.mcpPasswordEnv` is a fallback password source. A host or inherited inventory value for `mcp_password_env` or `ansible_password` overrides it. Setting both `mcp_password_env` and `ansible_password` for the same host remains invalid.
147
+
148
+ ## Connect to an MCP client
149
+
150
+ These examples assume you've already installed `eos-mcp-server` per [Install and get started](#install-and-get-started) above, and have an inventory file (and optionally a [server config file](#server-config-file)) ready.
151
+
152
+ ### Generic stdio example
153
+
154
+ If `eos-mcp-server` is installed on your `PATH`, point it at just an inventory file:
155
+
156
+ ```json
157
+ {
158
+ "mcpServers": {
159
+ "eos": {
160
+ "command": "eos-mcp-server",
161
+ "args": ["serve", "--inventory", "/absolute/path/to/inventory.yml"]
162
+ }
163
+ }
164
+ }
165
+ ```
166
+
167
+ Or use a [server config file](#server-config-file) if you want to set additional options:
168
+
169
+ ```json
170
+ {
171
+ "mcpServers": {
172
+ "eos": {
173
+ "command": "eos-mcp-server",
174
+ "args": ["serve", "--config", "/absolute/path/to/eos-mcp-server.yml"]
175
+ }
176
+ }
177
+ }
178
+ ```
179
+
180
+ If you built the project locally instead of installing the package, replace `"command": "eos-mcp-server"` with `"command": "node"` and put `/absolute/path/to/eos-mcp-server/dist/index.js` first in `args`.
181
+
182
+ ### Claude Code
183
+
184
+ With just an inventory file:
185
+
186
+ ```bash
187
+ claude mcp add eos -- eos-mcp-server serve --inventory /absolute/path/to/inventory.yml
188
+ ```
189
+
190
+ Or with a [server config file](#server-config-file) for additional options:
191
+
192
+ ```bash
193
+ claude mcp add eos -- eos-mcp-server serve --config /absolute/path/to/eos-mcp-server.yml
194
+ ```
195
+
196
+ Add `-s user` before `eos` in either command to make the server available across all your projects instead of just the current one:
197
+
198
+ ```bash
199
+ claude mcp add -s user eos -- eos-mcp-server serve --inventory /absolute/path/to/inventory.yml
200
+ ```
201
+
202
+ ### Codex
203
+
204
+ Add the server to `.codex/config.toml` in a trusted project, or to
205
+ `~/.codex/config.toml` for all projects.
206
+
207
+ With just an inventory file:
208
+
209
+ ```toml
210
+ [mcp_servers.eos]
211
+ command = "eos-mcp-server"
212
+ args = ["serve", "--inventory", "/absolute/path/to/inventory.yml"]
213
+ env_vars = ["EOS_MCP_PASSWORD"]
214
+ ```
215
+
216
+ Or with a [server config file](#server-config-file) for additional options:
217
+
218
+ ```toml
219
+ [mcp_servers.eos]
220
+ command = "eos-mcp-server"
221
+ args = ["serve", "--config", "/absolute/path/to/eos-mcp-server.yml"]
222
+ env_vars = ["EOS_MCP_PASSWORD"]
223
+ ```
224
+
225
+ `serve` is optional because it is the default command, but is shown explicitly
226
+ here for clarity. You can also add the server with:
227
+
228
+ ```bash
229
+ codex mcp add eos -- eos-mcp-server serve --inventory /absolute/path/to/inventory.yml
230
+ ```
231
+
232
+ Then add `env_vars = ["EOS_MCP_PASSWORD"]` to the generated
233
+ `[mcp_servers.eos]` table.
234
+
235
+ ### Passing secrets to the MCP server
236
+
237
+ Prefer exporting secrets in the environment that launches the MCP client:
238
+
239
+ ```bash
240
+ export EOS_MCP_PASSWORD='super-secret'
241
+ ```
242
+
243
+ For Codex, `env_vars = ["EOS_MCP_PASSWORD"]` forwards that exported variable to
244
+ the server. Other MCP clients may support per-server `env` blocks. Avoid
245
+ committing secret values to client config files when possible.
246
+
247
+ ## Inventory formats
248
+
249
+ The server supports exactly one inventory file loaded at startup. The inventory is not watched for changes — to pick up inventory edits, restart the server. Most MCP clients will do this automatically when you restart the client or re-launch the MCP connection.
250
+
251
+ Supported formats:
252
+
253
+ - canonical Ansible-style YAML
254
+ - simplified YAML
255
+
256
+ Canonical inventories must have `all` as the only top-level key. Simplified inventories may use only `version`, `vars`, `hosts`, and `groups` at the top level. Unknown structural keys are rejected; unknown keys inside `vars` remain allowed.
257
+
258
+ ### Canonical Ansible-style example
259
+
260
+ ```yaml
261
+ all:
262
+ children:
263
+ eos:
264
+ vars:
265
+ ansible_user: admin
266
+ ansible_network_os: eos
267
+ mcp_password_env: EOS_MCP_PASSWORD
268
+ hosts:
269
+ leaf1:
270
+ ansible_host: 192.0.2.11
271
+ leaf2:
272
+ ansible_host: 192.0.2.12
273
+ ```
274
+
275
+ ### Simplified YAML example
276
+
277
+ ```yaml
278
+ version: 1
279
+ vars:
280
+ ansible_user: admin
281
+ mcp_password_env: EOS_MCP_PASSWORD
282
+
283
+ hosts:
284
+ leaf1:
285
+ ansible_host: 192.0.2.11
286
+ ansible_network_os: eos
287
+ leaf2:
288
+ ansible_host: 192.0.2.12
289
+ ansible_network_os: eos
290
+
291
+ groups:
292
+ leafs:
293
+ hosts: [leaf1, leaf2]
294
+ ```
295
+
296
+ For an inventory where devices do not share a password, see
297
+ [`example-inventories/multi-password.yaml`](example-inventories/multi-password.yaml).
298
+ Each host sets its own `mcp_password_env` reference, so the actual passwords
299
+ remain in the environment rather than in the inventory file. For example:
300
+
301
+ ```bash
302
+ export EOS_MCP_SPINE1_PASSWORD='spine1-password'
303
+ export EOS_MCP_SPINE2_PASSWORD='spine2-password'
304
+ export EOS_MCP_LEAF1_PASSWORD='leaf1-password'
305
+ export EOS_MCP_LEAF2_PASSWORD='leaf2-password'
306
+
307
+ eos-mcp-server validate-inventory \
308
+ --inventory example-inventories/multi-password.yaml
309
+ eos-mcp-server serve --inventory example-inventories/multi-password.yaml
310
+ ```
311
+
312
+ The password environment variable name is resolved independently for each
313
+ host. Host-level values override inherited group or global values, so devices
314
+ can be queried together through the `FABRIC`, `SPINES`, or `LEAFS` targets while
315
+ still using their individual credentials.
316
+
317
+ ## Important inventory rules
318
+
319
+ - Targets must be **inventory host or group names**, not IP addresses.
320
+ - Host/group names must match `^[A-Za-z0-9_.-]+$`.
321
+ - Duplicate YAML keys are rejected.
322
+ - The simplified schema reserves `all`; do not define `groups.all` there.
323
+ - Group graphs must be acyclic.
324
+ - EOS eligibility comes from either:
325
+ - `ansible_network_os: eos`
326
+ - `mcp_platform: arista_eos`
327
+ - `mcp_read_allowed` defaults to `true` for eligible EOS hosts. Set it to `false` — inherited like any other variable — to explicitly deny read access to a host or group.
328
+ - Read operations fail closed if the resolved target contains ineligible or read-denied hosts.
329
+
330
+ ## Connection and authentication
331
+
332
+ ### Supported connection fields
333
+
334
+ The read path currently uses these effective fields:
335
+
336
+ - `ansible_host` - device hostname/IP for HTTPS connection
337
+ - `ansible_httpapi_port` - optional eAPI port, default `443`
338
+ - `ansible_user` - username
339
+ - `mcp_password_env` - preferred password source, treated as an **environment variable name**
340
+ - `ansible_password` - literal password source
341
+ - `mcp_validate_certs` - optional boolean to disable TLS validation for a lab target; `ansible_httpapi_validate_certs` is also accepted as a fallback
342
+
343
+ ### Password source rules
344
+
345
+ Exactly one password source must resolve per EOS host:
346
+
347
+ - `mcp_password_env`
348
+ - `ansible_password`
349
+
350
+ Do **not** set both.
351
+
352
+ If you use `mcp_password_env`, the value must be the **name of an environment variable**, not the password itself.
353
+
354
+ Correct:
355
+
356
+ ```yaml
357
+ mcp_password_env: EOS_MCP_PASSWORD
358
+ ```
359
+
360
+ Incorrect:
361
+
362
+ ```yaml
363
+ mcp_password_env: admin
364
+ ```
365
+
366
+ The default allowed env var prefix is `EOS_MCP_`, so names like `EOS_MCP_PASSWORD` work by default.
367
+
368
+ Example:
369
+
370
+ ```bash
371
+ export EOS_MCP_PASSWORD='replace-with-device-password'
372
+ ```
373
+
374
+ ## TLS behavior
375
+
376
+ - HTTPS only
377
+ - certificate validation is **on by default**
378
+ - optional custom CA bundle via config file `caFile`
379
+ - per-target or inherited inventory override via `mcp_validate_certs: false` (or `ansible_httpapi_validate_certs: false`)
380
+
381
+ There is currently **no CLI `--insecure` flag**.
382
+
383
+ ### Disable cert validation for a lab inventory
384
+
385
+ ```yaml
386
+ all:
387
+ children:
388
+ eos:
389
+ vars:
390
+ ansible_user: admin
391
+ ansible_network_os: eos
392
+ mcp_password_env: EOS_MCP_PASSWORD
393
+ mcp_validate_certs: false
394
+ hosts:
395
+ leaf1:
396
+ ansible_host: 192.0.2.11
397
+ ```
398
+
399
+ ### Prefer a CA bundle when possible
400
+
401
+ Server config:
402
+
403
+ ```yaml
404
+ inventory: inventory.yml
405
+ caFile: /path/to/ca.pem
406
+ ```
407
+
408
+ ## CLI usage
409
+
410
+ If no subcommand is provided, the CLI defaults to `serve`.
411
+
412
+ ### `serve`
413
+
414
+ Start the MCP server over stdio.
415
+
416
+ ```bash
417
+ eos-mcp-server serve --inventory inventory.yml
418
+ eos-mcp-server serve --config eos-mcp-server.yml
419
+ ```
420
+
421
+ On startup, `serve` loads the inventory and validates startup connection requirements for EOS-eligible hosts.
422
+
423
+ ### `validate-inventory`
424
+
425
+ Validate the inventory file.
426
+
427
+ Default behavior performs:
428
+
429
+ - inventory parsing and structural validation
430
+ - effective inventory validation
431
+ - startup-style connection validation for EOS-eligible hosts using the loaded config/default connection settings
432
+
433
+ Use `--inventory-only` to skip the startup connection checks.
434
+
435
+ ### `print-server-info`
436
+
437
+ Print the server's sanitized runtime/config summary.
438
+
439
+ ```bash
440
+ eos-mcp-server print-server-info --inventory inventory.yml
441
+ eos-mcp-server print-server-info --config eos-mcp-server.yml --json
442
+ ```
443
+
444
+ ### `probe`
445
+
446
+ Probe a host or group target using the same logic as the MCP tool.
447
+
448
+ ```bash
449
+ eos-mcp-server probe --inventory inventory.yml --target leaf1
450
+ eos-mcp-server probe --inventory inventory.yml --target leafs --json
451
+ ```
452
+
453
+ Human-readable example output:
454
+
455
+ ```text
456
+ Probe target: leaf1
457
+ Devices: 1/1 succeeded
458
+ - leaf1: success
459
+ ```
460
+
461
+ ## MCP tool reference
462
+
463
+ ### `eos_get_server_info`
464
+
465
+ Returns a sanitized summary of server runtime, capabilities, limits, TLS/config posture, and inventory summary.
466
+
467
+ Input:
468
+
469
+ ```json
470
+ {}
471
+ ```
472
+
473
+ ### `eos_list_inventory`
474
+
475
+ Returns a sanitized view of the loaded inventory.
476
+
477
+ Input:
478
+
479
+ ```json
480
+ {
481
+ "include_ineligible": false
482
+ }
483
+ ```
484
+
485
+ ### `eos_probe_devices`
486
+
487
+ Checks operational readiness for a host or group by contacting devices and running a harmless command.
488
+
489
+ Input:
490
+
491
+ ```json
492
+ {
493
+ "target": "leaf1",
494
+ "include_raw": false
495
+ }
496
+ ```
497
+
498
+ ### `eos_run_show`
499
+
500
+ Runs one or more `show` commands.
501
+
502
+ Rules:
503
+
504
+ - exactly one of `command` or `commands`
505
+ - every command is trimmed, must be a single line, and must be `show` or begin with `show`
506
+ - commands with CLI output modifiers or shell metacharacters such as `|`, `>`, `<`, `;`, `&`, backticks, or `$` are rejected before device contact
507
+ - `output_format` is one of `auto`, `json`, `text`
508
+ - `include_raw` optionally attaches each command's raw eAPI result entry as `raw_entry` inside `command_results`
509
+
510
+ Input examples:
511
+
512
+ ```json
513
+ {
514
+ "target": "leaf1",
515
+ "command": "show version",
516
+ "output_format": "auto"
517
+ }
518
+ ```
519
+
520
+ ```json
521
+ {
522
+ "target": "leafs",
523
+ "commands": ["show version", "show interfaces status"],
524
+ "output_format": "json",
525
+ "include_raw": false
526
+ }
527
+ ```
528
+
529
+ ### `eos_show_logging`
530
+
531
+ Retrieves bounded EOS logging output for a host or group target. The tool always uses text output and generates `show logging threshold <minimum_severity> <message_count>`. `minimum_severity` is a threshold, so `warnings` includes warning and more urgent log messages. Defaults are `minimum_severity: "warnings"` and `message_count: 100`; `message_count` is capped by `maxLoggingMessagesPerRequest`.
532
+
533
+ Input example:
534
+
535
+ ```json
536
+ {
537
+ "target": "leaf1",
538
+ "minimum_severity": "warnings",
539
+ "message_count": 100
540
+ }
541
+ ```
542
+
543
+ Allowed severities are `emergencies`, `alerts`, `critical`, `errors`, `warnings`, `notifications`, `informational`, and `debugging`.
544
+
545
+ ### `eos_get_facts`
546
+
547
+ Collects a small, fixed core facts set from `show version`.
548
+
549
+ Input:
550
+
551
+ ```json
552
+ {
553
+ "target": "leaf1",
554
+ "include_raw": false
555
+ }
556
+ ```
557
+
558
+ ### `eos_get_running_config`
559
+
560
+ Returns running config text for a host target, or for a group target when `section` is provided. Automatically enters enable mode via eAPI since `show running-config` requires privileged access. `section` is trimmed and must be a single-line EOS section selector without CLI output modifiers or shell metacharacters.
561
+
562
+ Input examples:
563
+
564
+ ```json
565
+ {
566
+ "target": "leaf1"
567
+ }
568
+ ```
569
+
570
+ ```json
571
+ {
572
+ "target": "leafs",
573
+ "section": "router bgp"
574
+ }
575
+ ```
576
+
577
+ ## Common troubleshooting
578
+
579
+ ### `Password env var ... does not match any allowed prefix`
580
+
581
+ `mcp_password_env` must contain an **environment variable name** like `EOS_MCP_PASSWORD`, not the password itself.
582
+
583
+ ### `Password env var ... is not set`
584
+
585
+ Export the referenced environment variable before launching `probe` or `serve`.
586
+
587
+ ### Certificate validation failure
588
+
589
+ Use one of:
590
+
591
+ - `caFile` in the server config to trust your lab CA
592
+ - `mcp_validate_certs: false` (or `ansible_httpapi_validate_certs: false`) in inventory for a lab/dev target
593
+
594
+ There is no CLI TLS-disable flag today.
595
+
596
+ ### `Unknown inventory target ...`
597
+
598
+ Tool and CLI targets must be inventory names, not `ansible_host` IPs.
599
+
600
+ ### Group running-config request failed without `section`
601
+
602
+ For `eos_get_running_config`, group targets require a `section` value.
603
+
604
+ ### Command or section rejected as invalid
605
+
606
+ `eos_run_show` accepts only single-line `show` commands. `eos_get_running_config.section` must also be a single-line selector. Newlines, other control characters, CLI output modifiers, and shell metacharacters are rejected before any device contact.
607
+
608
+ ### Response too large
609
+
610
+ Read tools are bounded by `maxResponseSizeBytes`. The server rejects oversized device HTTP responses before buffering them fully, and also rejects oversized aggregate tool results with narrowing guidance. For `eos_show_logging`, reduce `message_count`, target fewer devices, or raise `minimum_severity`.
611
+
612
+ ### Policy denied when probing a device
613
+
614
+ Example:
615
+
616
+ ```
617
+ eos-mcp-server probe --inventory clab/clab-testlab/ansible-inventory.yml --target clab-testlab-node1-1
618
+ Policy denied: target clab-testlab-node1-1 includes host(s) not permitted for read operations: clab-testlab-node1-1
619
+ ```
620
+
621
+ This means the target resolved to at least one host that is either not marked as EOS eligible (`ansible_network_os: eos` or `mcp_platform: arista_eos`) or has `mcp_read_allowed: false` set.
622
+
623
+ ## EOS version compatibility
624
+
625
+ The read-path tools have been validated against EOS 4.34.3M. The minimum supported EOS version for read operations is 4.20, which is when eAPI JSON-RPC became stable and `show` commands reliably produce structured JSON output.
626
+
627
+ ## Design notes
628
+
629
+ - inventory is the trust boundary
630
+ - reads are fail-closed on ineligible or denied targets
631
+ - the server exposes only documented read-only operations
632
+
633
+ ## Project docs
634
+
635
+ Additional design and planning docs in the `docs/` directory:
636
+
637
+ - `docs/design-overview.md` — design overview for evaluators
638
+ - `docs/DESIGN.md` — detailed design specification
639
+ - `docs/IMPLEMENTATION_PLAN.md` — implemented scope and maintenance priorities
640
+ - `docs/CHECKPOINT.md` — current implementation status
641
+ - `docs/CODE_WALKTHROUGH.md` — source layout and module guide
642
+ - `docs/QUICKSTART.md` — quick start tutorial
643
+ - `docs/EXAMPLES.md` — inventory and usage examples
644
+
645
+ ## License
646
+
647
+ Copyright 2026 Arista Networks, Inc.
648
+
649
+ This project is licensed under the [Apache License, Version 2.0](LICENSE).
650
+ Apache-2.0 applies to each released version of this repository. Arista may
651
+ offer later versions under additional or different terms, but cannot revoke
652
+ the Apache-2.0 rights granted for versions already released under it.
package/dist/cli.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ export type CommandName = "serve" | "validate-inventory" | "print-server-info" | "probe";
2
+ export interface CliOptions {
3
+ command: CommandName;
4
+ explicitCommand?: boolean;
5
+ help?: boolean;
6
+ version?: boolean;
7
+ configPath?: string;
8
+ inventoryPath?: string;
9
+ actor?: string;
10
+ json?: boolean;
11
+ inventoryOnly?: boolean;
12
+ target?: string;
13
+ }
14
+ export declare function parseCliArgs(argv: string[]): CliOptions;
15
+ export declare function printHelp(cli: CliOptions): void;
16
+ export declare function printVersion(): void;