splunkctl 0.4.1__tar.gz → 0.5.0__tar.gz

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 (70) hide show
  1. splunkctl-0.5.0/PKG-INFO +256 -0
  2. splunkctl-0.5.0/README.md +218 -0
  3. {splunkctl-0.4.1 → splunkctl-0.5.0}/pyproject.toml +7 -2
  4. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/__init__.py +1 -1
  5. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/commands/doctor.py +43 -25
  6. splunkctl-0.5.0/splunkctl/commands/mcp_cmd.py +47 -0
  7. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/main.py +2 -2
  8. splunkctl-0.5.0/splunkctl/mcp/__init__.py +0 -0
  9. splunkctl-0.5.0/splunkctl/mcp/resources.py +35 -0
  10. splunkctl-0.5.0/splunkctl/mcp/server.py +328 -0
  11. splunkctl-0.5.0/splunkctl/mcp/tools.py +233 -0
  12. splunkctl-0.5.0/splunkctl.egg-info/PKG-INFO +256 -0
  13. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl.egg-info/SOURCES.txt +5 -2
  14. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl.egg-info/requires.txt +1 -0
  15. {splunkctl-0.4.1 → splunkctl-0.5.0}/tests/test_version.py +1 -1
  16. splunkctl-0.4.1/PKG-INFO +0 -194
  17. splunkctl-0.4.1/README.md +0 -157
  18. splunkctl-0.4.1/splunkctl/commands/skill_cmd.py +0 -43
  19. splunkctl-0.4.1/splunkctl/skill/SKILL.md +0 -1046
  20. splunkctl-0.4.1/splunkctl.egg-info/PKG-INFO +0 -194
  21. {splunkctl-0.4.1 → splunkctl-0.5.0}/LICENSE +0 -0
  22. {splunkctl-0.4.1 → splunkctl-0.5.0}/setup.cfg +0 -0
  23. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/__main__.py +0 -0
  24. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/client.py +0 -0
  25. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/commands/__init__.py +0 -0
  26. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/commands/alerts.py +0 -0
  27. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/commands/apps.py +0 -0
  28. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/commands/audit.py +0 -0
  29. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/commands/audit_parse.py +0 -0
  30. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/commands/commands_meta.py +0 -0
  31. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/commands/common.py +0 -0
  32. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/commands/conf.py +0 -0
  33. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/commands/conf_ops.py +0 -0
  34. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/commands/config_cmd.py +0 -0
  35. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/commands/dashboards.py +0 -0
  36. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/commands/datamodels.py +0 -0
  37. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/commands/es.py +0 -0
  38. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/commands/hec.py +0 -0
  39. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/commands/indexes.py +0 -0
  40. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/commands/info.py +0 -0
  41. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/commands/inputs.py +0 -0
  42. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/commands/knowledge.py +0 -0
  43. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/commands/kvstore.py +0 -0
  44. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/commands/lookups.py +0 -0
  45. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/commands/lookups_wiring.py +0 -0
  46. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/commands/parsers.py +0 -0
  47. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/commands/parsers_io.py +0 -0
  48. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/commands/rules.py +0 -0
  49. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/commands/rules_io.py +0 -0
  50. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/commands/search.py +0 -0
  51. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/commands/server.py +0 -0
  52. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/commands/state.py +0 -0
  53. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/commands/state_io.py +0 -0
  54. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/commands/state_io_blobs.py +0 -0
  55. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/commands/state_io_confs.py +0 -0
  56. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/commands/state_types.py +0 -0
  57. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/commands/users.py +0 -0
  58. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/config.py +0 -0
  59. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/errors.py +0 -0
  60. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/guard.py +0 -0
  61. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl/output.py +0 -0
  62. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl.egg-info/dependency_links.txt +0 -0
  63. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl.egg-info/entry_points.txt +0 -0
  64. {splunkctl-0.4.1 → splunkctl-0.5.0}/splunkctl.egg-info/top_level.txt +0 -0
  65. {splunkctl-0.4.1 → splunkctl-0.5.0}/tests/test_client.py +0 -0
  66. {splunkctl-0.4.1 → splunkctl-0.5.0}/tests/test_config.py +0 -0
  67. {splunkctl-0.4.1 → splunkctl-0.5.0}/tests/test_guard.py +0 -0
  68. {splunkctl-0.4.1 → splunkctl-0.5.0}/tests/test_main_errors.py +0 -0
  69. {splunkctl-0.4.1 → splunkctl-0.5.0}/tests/test_main_hoisting.py +0 -0
  70. {splunkctl-0.4.1 → splunkctl-0.5.0}/tests/test_output.py +0 -0
@@ -0,0 +1,256 @@
1
+ Metadata-Version: 2.4
2
+ Name: splunkctl
3
+ Version: 0.5.0
4
+ Summary: CLI tool for Splunk Enterprise SIEM operations.
5
+ Author: dannyota
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/dannyota/splunkctl
8
+ Project-URL: Repository, https://github.com/dannyota/splunkctl
9
+ Project-URL: Issues, https://github.com/dannyota/splunkctl/issues
10
+ Keywords: splunk,siem,security,cli,detection,spl
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: System Administrators
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Topic :: Security
18
+ Classifier: Topic :: System :: Systems Administration
19
+ Requires-Python: >=3.13
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Requires-Dist: splunk-sdk>=2.0
23
+ Requires-Dist: click>=8.2
24
+ Requires-Dist: pyyaml>=6.0
25
+ Requires-Dist: tabulate>=0.9
26
+ Requires-Dist: requests>=2.32
27
+ Requires-Dist: defusedxml>=0.7
28
+ Requires-Dist: mcp>=1.23
29
+ Provides-Extra: dev
30
+ Requires-Dist: pytest>=8.0; extra == "dev"
31
+ Requires-Dist: ruff>=0.6; extra == "dev"
32
+ Requires-Dist: mypy>=1.11; extra == "dev"
33
+ Requires-Dist: types-requests; extra == "dev"
34
+ Requires-Dist: types-PyYAML; extra == "dev"
35
+ Requires-Dist: types-tabulate; extra == "dev"
36
+ Requires-Dist: types-defusedxml; extra == "dev"
37
+ Dynamic: license-file
38
+
39
+ <div align="center">
40
+
41
+ <a href="https://splunk.danny.vn"><img src="docs/assets/banner.svg" alt="splunkctl — operate Splunk Enterprise as code" width="600"></a>
42
+
43
+ # splunkctl
44
+
45
+ **Operate Splunk Enterprise as code — for SOC teams, detection engineers, and AI agents.**
46
+
47
+ [Docs](https://splunk.danny.vn) · [Catalog](docs/design/catalog.md) · [Releases](https://github.com/dannyota/splunkctl/releases)
48
+
49
+ </div>
50
+
51
+ ---
52
+
53
+ A Python CLI that queries, inspects, and manages a **remote** Splunk
54
+ Enterprise instance over the REST API. Built on the
55
+ [splunk-sdk-python](https://github.com/dannyota/splunk-sdk-python/tree/splunkctl)
56
+ fork with [Click](https://click.palletsprojects.com/). The core loop is
57
+ **pull live state → review the diff → push it back** — one `state` engine
58
+ covering rules, parsers, macros, lookups, and dashboards, with a
59
+ change-evidence report artifact for bank change tickets. It's built for
60
+ humans and LLM agents alike: deterministic flags, `--json` everywhere,
61
+ structured error envelopes, and a built-in MCP server
62
+ (`splunkctl mcp serve`) with progressive tool discovery.
63
+
64
+ > **Every mutation is dry-run by default.** Nothing changes until you pass
65
+ > `--yes`. Always preview, read it, then apply.
66
+
67
+ ## What it does
68
+
69
+ - **Config as code** — `state pull` → edit → `state diff` → `state push`
70
+ across rules, parsers, macros, lookups, and dashboards (diff-only). Push
71
+ writes a before→after JSON report artifact usable as change-ticket
72
+ evidence. Push never deletes.
73
+ - **Detection engineering** — rules CRUD + YAML import/export, macros,
74
+ eventtypes, tags, data model acceleration health, lookup definitions +
75
+ automatic lookups (transforms.conf/props.conf wiring), and first-class
76
+ `--email-to`/`--webhook-url` alert-action flags.
77
+ - **ES incident review** — `es notables list/get/update` for the SOC
78
+ triage loop (status, owner, urgency, disposition, comment via
79
+ `notable_update`); feature-detected on Enterprise Security.
80
+ - **Compliance & audit** — `audit changes` normalizes both `_audit` event
81
+ shapes into one schema; `audit rbac` produces a users × roles ×
82
+ capabilities attestation view for access recertification.
83
+ - **KV store** — collection + document CRUD, JSONL import/export with
84
+ 500-doc batch chunking, query with server-side filtering.
85
+ - **Topology health** — `server cluster/shcluster/deployment` reads
86
+ distinguish "no threat" from "an indexer is down" in clustered
87
+ deployments.
88
+ - **Agent reliability** — structured JSON error envelope with typed
89
+ taxonomy (`auth`/`permission`/`not_found`/`timeout`/...), uniform
90
+ `--limit`/`--offset`/`--filter` on every list surface, multi-instance
91
+ profiles with a bank-safety guard banner
92
+ (`(profile: uat @ host:port)`).
93
+ - **Built for agents** — built-in MCP server with 129 auto-generated tools,
94
+ progressive discovery (5 meta-tools + focus/unfocus), 22 guide resources,
95
+ guard markers on every mutation, dual output (TTY = table, pipe = JSON).
96
+
97
+ ## Install
98
+
99
+ ```bash
100
+ pip install splunkctl
101
+ pip install git+https://github.com/dannyota/splunk-sdk-python@splunkctl
102
+ ```
103
+
104
+ Requires Python 3.13+. The second line installs the
105
+ [forked SDK](https://github.com/dannyota/splunk-sdk-python/tree/splunkctl)
106
+ which adds dashboard, lookup, and HEC token entity classes. Without it,
107
+ core commands (search, rules, alerts, indexes, inputs, apps, users) still
108
+ work.
109
+
110
+ ### Development
111
+
112
+ ```bash
113
+ git clone https://github.com/dannyota/splunkctl
114
+ cd splunkctl
115
+ pip install -e '.[dev]'
116
+ splunkctl --version
117
+ ```
118
+
119
+ ## Quickstart
120
+
121
+ ```bash
122
+ splunkctl config init # interactive setup
123
+ splunkctl doctor # check connection, auth, permissions
124
+ splunkctl search run 'index=main | head 10' # run a search
125
+ splunkctl rules list # list detection rules
126
+ splunkctl commands --json # discover every verb
127
+ ```
128
+
129
+ ## CLI usage
130
+
131
+ ```bash
132
+ # Read
133
+ splunkctl rules list --app Splunk_Security_Essentials --json
134
+ splunkctl alerts list --json
135
+ splunkctl datamodels acceleration
136
+ splunkctl audit rbac --format csv --out rbac.csv
137
+
138
+ # Mutate (dry-run first, --yes to apply)
139
+ splunkctl rules disable 'My Rule' # preview
140
+ splunkctl rules disable 'My Rule' --yes # apply
141
+ splunkctl es notables update <id> --status closed --owner analyst --yes
142
+
143
+ # Config-as-code
144
+ splunkctl state pull --dir config/ # snapshot live state
145
+ splunkctl state diff --dir config/ # structured drift report
146
+ splunkctl state push --dir config/ --report r.json --yes # deploy + evidence
147
+ ```
148
+
149
+ ## Commands
150
+
151
+ | Group | Description |
152
+ |---|---|
153
+ | `doctor` | Connection, auth, health, and permissions check |
154
+ | `config` | Setup, profiles (dev/UAT/prod), test connectivity |
155
+ | `info` | Server info (version, OS, license) |
156
+ | `search` | Run, export, oneshot, upload, job management |
157
+ | `rules` | Detection rules — CRUD, import/export (YAML), alert-action flags |
158
+ | `alerts` | Fired alerts, alert actions, suppression |
159
+ | `dashboards` | Dashboard CRUD (XML/JSON) |
160
+ | `indexes` | Index management |
161
+ | `inputs` | Data inputs (monitor, tcp, udp, script, http) |
162
+ | `lookups` | Lookup tables, definitions, automatic lookups |
163
+ | `hec` | HEC token management |
164
+ | `parsers` | Source types, field extractions, import/export |
165
+ | `apps` | App install (.spl/.tar.gz), uninstall, update |
166
+ | `users` | User and role management |
167
+ | `server` | Messages, license, KV store, cluster/SHC/deployment health |
168
+ | `es` | ES notable-event triage (feature-detected) |
169
+ | `audit` | Change audit + RBAC attestation |
170
+ | `kvstore` | KV store collection + document CRUD |
171
+ | `conf` | Generic conf file/stanza editor (any .conf) |
172
+ | `macros` | Search macros — list, get, set |
173
+ | `eventtypes` | Event types — list, get |
174
+ | `tags` | Tags — list, get |
175
+ | `datamodels` | Data model definitions + acceleration health |
176
+ | `state` | Config-as-code pull/diff/push with change-evidence reports |
177
+ | `commands` | Machine-readable command tree (JSON) |
178
+ | `mcp` | Built-in MCP server for AI agent integration |
179
+
180
+ ## Global flags
181
+
182
+ ```
183
+ --json Force JSON output
184
+ --format FMT Output format: table, json, csv, jsonl
185
+ --fields f1,f2 Project specific fields
186
+ --out FILE Write output to file
187
+ --yes / -y Apply mutations (skip dry-run preview)
188
+ --timeout N Request timeout in seconds (default 30)
189
+ --config FILE Config file path
190
+ --profile NAME Named profile (dev/UAT/prod)
191
+ --debug HTTP request/response logging
192
+ ```
193
+
194
+ ## Dry-run by default
195
+
196
+ All write operations preview what would change. Pass `--yes` to apply.
197
+ Every preview and confirmation includes the target profile and host so an
198
+ agent never mistakes UAT for prod.
199
+
200
+ ```bash
201
+ splunkctl rules delete 'My Rule'
202
+ # [DRY RUN] Delete saved search 'My Rule' (profile: uat @ uat.splunk.internal:8089)
203
+ # Pass --yes to apply.
204
+
205
+ splunkctl rules delete 'My Rule' --yes
206
+ # Applying: Delete saved search 'My Rule' (profile: uat @ uat.splunk.internal:8089)
207
+ # Deleted saved search 'My Rule'.
208
+ ```
209
+
210
+ ## Structured errors
211
+
212
+ Under `--json` or piped output, errors emit a single-line JSON envelope on
213
+ stderr with a typed `kind` for programmatic branching:
214
+
215
+ ```json
216
+ {"error": {"kind": "not_found", "http_status": 404, "message": "..."}}
217
+ ```
218
+
219
+ Kinds: `auth`, `permission`, `not_found`, `conflict`, `http`, `connection`,
220
+ `timeout`, `error` (fallback).
221
+
222
+ ## SDK fork
223
+
224
+ splunkctl depends on a
225
+ [fork of splunk-sdk-python](https://github.com/dannyota/splunk-sdk-python/tree/splunkctl)
226
+ that adds entity classes missing from the upstream SDK:
227
+
228
+ | Entity | Service property | Purpose |
229
+ |---|---|---|
230
+ | `Dashboard` | `service.dashboards` | Dashboard CRUD |
231
+ | `LookupTableFile` | `service.lookup_table_files` | Lookup table metadata + download |
232
+ | `HECToken` | `service.hec_tokens` | HEC token management |
233
+
234
+ ```bash
235
+ pip install git+https://github.com/dannyota/splunk-sdk-python@splunkctl
236
+ ```
237
+
238
+ ## Agent integration (MCP)
239
+
240
+ splunkctl ships with a built-in
241
+ [MCP](https://modelcontextprotocol.io) server for AI agent integration:
242
+
243
+ ```bash
244
+ splunkctl mcp install # register in .mcp.json
245
+ splunkctl mcp serve # start stdio MCP server
246
+ ```
247
+
248
+ The MCP server auto-generates 129 typed tools from the Click command tree
249
+ with progressive discovery — agents start with 5 meta-tools (`help`,
250
+ `usage`, `focus`, `unfocus`, `run`) and dynamically load typed schemas per
251
+ command group. 22 guide resources are served as `guide://` URIs. Mutations
252
+ are guarded: `yes=true` to apply (dry-run by default).
253
+
254
+ ## License
255
+
256
+ Apache-2.0
@@ -0,0 +1,218 @@
1
+ <div align="center">
2
+
3
+ <a href="https://splunk.danny.vn"><img src="docs/assets/banner.svg" alt="splunkctl — operate Splunk Enterprise as code" width="600"></a>
4
+
5
+ # splunkctl
6
+
7
+ **Operate Splunk Enterprise as code — for SOC teams, detection engineers, and AI agents.**
8
+
9
+ [Docs](https://splunk.danny.vn) · [Catalog](docs/design/catalog.md) · [Releases](https://github.com/dannyota/splunkctl/releases)
10
+
11
+ </div>
12
+
13
+ ---
14
+
15
+ A Python CLI that queries, inspects, and manages a **remote** Splunk
16
+ Enterprise instance over the REST API. Built on the
17
+ [splunk-sdk-python](https://github.com/dannyota/splunk-sdk-python/tree/splunkctl)
18
+ fork with [Click](https://click.palletsprojects.com/). The core loop is
19
+ **pull live state → review the diff → push it back** — one `state` engine
20
+ covering rules, parsers, macros, lookups, and dashboards, with a
21
+ change-evidence report artifact for bank change tickets. It's built for
22
+ humans and LLM agents alike: deterministic flags, `--json` everywhere,
23
+ structured error envelopes, and a built-in MCP server
24
+ (`splunkctl mcp serve`) with progressive tool discovery.
25
+
26
+ > **Every mutation is dry-run by default.** Nothing changes until you pass
27
+ > `--yes`. Always preview, read it, then apply.
28
+
29
+ ## What it does
30
+
31
+ - **Config as code** — `state pull` → edit → `state diff` → `state push`
32
+ across rules, parsers, macros, lookups, and dashboards (diff-only). Push
33
+ writes a before→after JSON report artifact usable as change-ticket
34
+ evidence. Push never deletes.
35
+ - **Detection engineering** — rules CRUD + YAML import/export, macros,
36
+ eventtypes, tags, data model acceleration health, lookup definitions +
37
+ automatic lookups (transforms.conf/props.conf wiring), and first-class
38
+ `--email-to`/`--webhook-url` alert-action flags.
39
+ - **ES incident review** — `es notables list/get/update` for the SOC
40
+ triage loop (status, owner, urgency, disposition, comment via
41
+ `notable_update`); feature-detected on Enterprise Security.
42
+ - **Compliance & audit** — `audit changes` normalizes both `_audit` event
43
+ shapes into one schema; `audit rbac` produces a users × roles ×
44
+ capabilities attestation view for access recertification.
45
+ - **KV store** — collection + document CRUD, JSONL import/export with
46
+ 500-doc batch chunking, query with server-side filtering.
47
+ - **Topology health** — `server cluster/shcluster/deployment` reads
48
+ distinguish "no threat" from "an indexer is down" in clustered
49
+ deployments.
50
+ - **Agent reliability** — structured JSON error envelope with typed
51
+ taxonomy (`auth`/`permission`/`not_found`/`timeout`/...), uniform
52
+ `--limit`/`--offset`/`--filter` on every list surface, multi-instance
53
+ profiles with a bank-safety guard banner
54
+ (`(profile: uat @ host:port)`).
55
+ - **Built for agents** — built-in MCP server with 129 auto-generated tools,
56
+ progressive discovery (5 meta-tools + focus/unfocus), 22 guide resources,
57
+ guard markers on every mutation, dual output (TTY = table, pipe = JSON).
58
+
59
+ ## Install
60
+
61
+ ```bash
62
+ pip install splunkctl
63
+ pip install git+https://github.com/dannyota/splunk-sdk-python@splunkctl
64
+ ```
65
+
66
+ Requires Python 3.13+. The second line installs the
67
+ [forked SDK](https://github.com/dannyota/splunk-sdk-python/tree/splunkctl)
68
+ which adds dashboard, lookup, and HEC token entity classes. Without it,
69
+ core commands (search, rules, alerts, indexes, inputs, apps, users) still
70
+ work.
71
+
72
+ ### Development
73
+
74
+ ```bash
75
+ git clone https://github.com/dannyota/splunkctl
76
+ cd splunkctl
77
+ pip install -e '.[dev]'
78
+ splunkctl --version
79
+ ```
80
+
81
+ ## Quickstart
82
+
83
+ ```bash
84
+ splunkctl config init # interactive setup
85
+ splunkctl doctor # check connection, auth, permissions
86
+ splunkctl search run 'index=main | head 10' # run a search
87
+ splunkctl rules list # list detection rules
88
+ splunkctl commands --json # discover every verb
89
+ ```
90
+
91
+ ## CLI usage
92
+
93
+ ```bash
94
+ # Read
95
+ splunkctl rules list --app Splunk_Security_Essentials --json
96
+ splunkctl alerts list --json
97
+ splunkctl datamodels acceleration
98
+ splunkctl audit rbac --format csv --out rbac.csv
99
+
100
+ # Mutate (dry-run first, --yes to apply)
101
+ splunkctl rules disable 'My Rule' # preview
102
+ splunkctl rules disable 'My Rule' --yes # apply
103
+ splunkctl es notables update <id> --status closed --owner analyst --yes
104
+
105
+ # Config-as-code
106
+ splunkctl state pull --dir config/ # snapshot live state
107
+ splunkctl state diff --dir config/ # structured drift report
108
+ splunkctl state push --dir config/ --report r.json --yes # deploy + evidence
109
+ ```
110
+
111
+ ## Commands
112
+
113
+ | Group | Description |
114
+ |---|---|
115
+ | `doctor` | Connection, auth, health, and permissions check |
116
+ | `config` | Setup, profiles (dev/UAT/prod), test connectivity |
117
+ | `info` | Server info (version, OS, license) |
118
+ | `search` | Run, export, oneshot, upload, job management |
119
+ | `rules` | Detection rules — CRUD, import/export (YAML), alert-action flags |
120
+ | `alerts` | Fired alerts, alert actions, suppression |
121
+ | `dashboards` | Dashboard CRUD (XML/JSON) |
122
+ | `indexes` | Index management |
123
+ | `inputs` | Data inputs (monitor, tcp, udp, script, http) |
124
+ | `lookups` | Lookup tables, definitions, automatic lookups |
125
+ | `hec` | HEC token management |
126
+ | `parsers` | Source types, field extractions, import/export |
127
+ | `apps` | App install (.spl/.tar.gz), uninstall, update |
128
+ | `users` | User and role management |
129
+ | `server` | Messages, license, KV store, cluster/SHC/deployment health |
130
+ | `es` | ES notable-event triage (feature-detected) |
131
+ | `audit` | Change audit + RBAC attestation |
132
+ | `kvstore` | KV store collection + document CRUD |
133
+ | `conf` | Generic conf file/stanza editor (any .conf) |
134
+ | `macros` | Search macros — list, get, set |
135
+ | `eventtypes` | Event types — list, get |
136
+ | `tags` | Tags — list, get |
137
+ | `datamodels` | Data model definitions + acceleration health |
138
+ | `state` | Config-as-code pull/diff/push with change-evidence reports |
139
+ | `commands` | Machine-readable command tree (JSON) |
140
+ | `mcp` | Built-in MCP server for AI agent integration |
141
+
142
+ ## Global flags
143
+
144
+ ```
145
+ --json Force JSON output
146
+ --format FMT Output format: table, json, csv, jsonl
147
+ --fields f1,f2 Project specific fields
148
+ --out FILE Write output to file
149
+ --yes / -y Apply mutations (skip dry-run preview)
150
+ --timeout N Request timeout in seconds (default 30)
151
+ --config FILE Config file path
152
+ --profile NAME Named profile (dev/UAT/prod)
153
+ --debug HTTP request/response logging
154
+ ```
155
+
156
+ ## Dry-run by default
157
+
158
+ All write operations preview what would change. Pass `--yes` to apply.
159
+ Every preview and confirmation includes the target profile and host so an
160
+ agent never mistakes UAT for prod.
161
+
162
+ ```bash
163
+ splunkctl rules delete 'My Rule'
164
+ # [DRY RUN] Delete saved search 'My Rule' (profile: uat @ uat.splunk.internal:8089)
165
+ # Pass --yes to apply.
166
+
167
+ splunkctl rules delete 'My Rule' --yes
168
+ # Applying: Delete saved search 'My Rule' (profile: uat @ uat.splunk.internal:8089)
169
+ # Deleted saved search 'My Rule'.
170
+ ```
171
+
172
+ ## Structured errors
173
+
174
+ Under `--json` or piped output, errors emit a single-line JSON envelope on
175
+ stderr with a typed `kind` for programmatic branching:
176
+
177
+ ```json
178
+ {"error": {"kind": "not_found", "http_status": 404, "message": "..."}}
179
+ ```
180
+
181
+ Kinds: `auth`, `permission`, `not_found`, `conflict`, `http`, `connection`,
182
+ `timeout`, `error` (fallback).
183
+
184
+ ## SDK fork
185
+
186
+ splunkctl depends on a
187
+ [fork of splunk-sdk-python](https://github.com/dannyota/splunk-sdk-python/tree/splunkctl)
188
+ that adds entity classes missing from the upstream SDK:
189
+
190
+ | Entity | Service property | Purpose |
191
+ |---|---|---|
192
+ | `Dashboard` | `service.dashboards` | Dashboard CRUD |
193
+ | `LookupTableFile` | `service.lookup_table_files` | Lookup table metadata + download |
194
+ | `HECToken` | `service.hec_tokens` | HEC token management |
195
+
196
+ ```bash
197
+ pip install git+https://github.com/dannyota/splunk-sdk-python@splunkctl
198
+ ```
199
+
200
+ ## Agent integration (MCP)
201
+
202
+ splunkctl ships with a built-in
203
+ [MCP](https://modelcontextprotocol.io) server for AI agent integration:
204
+
205
+ ```bash
206
+ splunkctl mcp install # register in .mcp.json
207
+ splunkctl mcp serve # start stdio MCP server
208
+ ```
209
+
210
+ The MCP server auto-generates 129 typed tools from the Click command tree
211
+ with progressive discovery — agents start with 5 meta-tools (`help`,
212
+ `usage`, `focus`, `unfocus`, `run`) and dynamically load typed schemas per
213
+ command group. 22 guide resources are served as `guide://` URIs. Mutations
214
+ are guarded: `yes=true` to apply (dry-run by default).
215
+
216
+ ## License
217
+
218
+ Apache-2.0
@@ -29,6 +29,7 @@ dependencies = [
29
29
  "tabulate>=0.9",
30
30
  "requests>=2.32",
31
31
  "defusedxml>=0.7",
32
+ "mcp>=1.23",
32
33
  ]
33
34
 
34
35
  [project.optional-dependencies]
@@ -51,13 +52,13 @@ Repository = "https://github.com/dannyota/splunkctl"
51
52
  Issues = "https://github.com/dannyota/splunkctl/issues"
52
53
 
53
54
  [tool.setuptools]
54
- packages = ["splunkctl", "splunkctl.commands", "splunkctl.skill"]
55
+ packages = ["splunkctl", "splunkctl.commands", "splunkctl.mcp"]
55
56
 
56
57
  [tool.setuptools.dynamic]
57
58
  version = { attr = "splunkctl.__version__" }
58
59
 
59
60
  [tool.setuptools.package-data]
60
- "splunkctl.skill" = ["SKILL.md"]
61
+ "splunkctl.mcp" = ["py.typed"]
61
62
 
62
63
  # --- Ruff (lint + format) ---
63
64
 
@@ -102,6 +103,10 @@ warn_unused_configs = true
102
103
  module = "splunklib.*"
103
104
  ignore_missing_imports = true
104
105
 
106
+ [[tool.mypy.overrides]]
107
+ module = "mcp.*"
108
+ ignore_missing_imports = true
109
+
105
110
  # --- pytest ---
106
111
 
107
112
  [tool.pytest.ini_options]
@@ -1,3 +1,3 @@
1
1
  """CLI tool for Splunk Enterprise SIEM operations."""
2
2
 
3
- __version__ = "0.4.1"
3
+ __version__ = "0.5.0"
@@ -2,7 +2,6 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- import importlib.resources
6
5
  import json
7
6
  import ssl
8
7
  import urllib.request
@@ -34,11 +33,10 @@ _HINTS: dict[str, str] = {
34
33
  "Dismiss with 'splunkctl server messages --dismiss NAME --yes'."
35
34
  ),
36
35
  "Web UI reachable": ("Web UI is optional; lookup-upload and some ops need it."),
37
- "Skill freshness": ("Run 'splunkctl skill install' to update the installed guide."),
36
+ "KV Store": ("Ask your Splunk admin to restart Splunk or check mongod.log."),
37
+ "MCP registered": "Run 'splunkctl mcp install' to register the MCP server.",
38
38
  }
39
39
 
40
- _INSTALL_DIR = Path.home() / ".claude" / "skills" / "splunkctl"
41
-
42
40
 
43
41
  def _check(
44
42
  label: str,
@@ -160,7 +158,22 @@ def doctor_cmd(ctx: click.Context, *, strict: bool = False) -> None:
160
158
  else:
161
159
  results.append(_check("System messages", True, "no errors"))
162
160
 
163
- # --- 5. User permissions ---
161
+ # --- 5. KV Store ---
162
+ click.echo("[KV Store]", err=True)
163
+ try:
164
+ resp = svc.get("/services/kvstore/status", output_mode="json")
165
+ kv_data = json.loads(resp.body.read())
166
+ kv_status = kv_data["entry"][0]["content"].get("current", {})
167
+ kv_state = kv_status.get("status", "unknown")
168
+ kv_ok = kv_state == "ready"
169
+ kv_detail = kv_state
170
+ if not kv_ok and kv_state == "failed":
171
+ kv_detail = "failed — restart Splunk or check mongod.log"
172
+ results.append(_check("KV Store", kv_ok, kv_detail, warn=not kv_ok))
173
+ except Exception:
174
+ results.append(_check("KV Store", False, "could not query status", warn=True))
175
+
176
+ # --- 6. User permissions ---
164
177
  click.echo("[Permissions]", err=True)
165
178
  try:
166
179
  user = svc.users[svc.username]
@@ -181,7 +194,7 @@ def doctor_cmd(ctx: click.Context, *, strict: bool = False) -> None:
181
194
  except Exception as exc:
182
195
  results.append(_check("User lookup", False, str(exc)))
183
196
 
184
- # --- 6. Web UI ---
197
+ # --- 7. Web UI ---
185
198
  click.echo("[Web UI]", err=True)
186
199
  try:
187
200
  web_conf = svc.confs["web"]["settings"]
@@ -216,31 +229,36 @@ def doctor_cmd(ctx: click.Context, *, strict: bool = False) -> None:
216
229
  )
217
230
  )
218
231
 
219
- # --- 7. Skill freshness ---
220
- click.echo("[Skill]", err=True)
221
- _check_skill_freshness(results)
232
+ # --- 8. MCP registration ---
233
+ click.echo("[MCP]", err=True)
234
+ _check_mcp_registered(results)
222
235
 
223
236
  _finish(ctx, results, strict=strict)
224
237
 
225
238
 
226
- def _check_skill_freshness(results: list[dict[str, str]]) -> None:
227
- installed = _INSTALL_DIR / "SKILL.md"
228
- if not installed.exists():
229
- results.append(_check("Skill freshness", False, "not installed", warn=True))
230
- return
231
- try:
232
- ref = importlib.resources.files("splunkctl.skill").joinpath("SKILL.md")
233
- embedded = ref.read_text(encoding="utf-8")
234
- except FileNotFoundError:
235
- results.append(_check("Skill freshness", False, "embedded SKILL.md missing"))
236
- return
237
- installed_text = installed.read_text(encoding="utf-8")
238
- if installed_text == embedded:
239
- results.append(_check("Skill freshness", True, "up to date"))
240
- else:
239
+ def _check_mcp_registered(results: list[dict[str, str]]) -> None:
240
+ mcp_json = Path.cwd() / ".mcp.json"
241
+ if not mcp_json.exists():
241
242
  results.append(
242
- _check("Skill freshness", False, "installed copy is stale", warn=True)
243
+ _check("MCP registered", False, ".mcp.json not found", warn=True)
243
244
  )
245
+ return
246
+ try:
247
+ data = json.loads(mcp_json.read_text(encoding="utf-8"))
248
+ servers = data.get("mcpServers", {})
249
+ if "splunkctl" in servers:
250
+ results.append(_check("MCP registered", True, ".mcp.json"))
251
+ else:
252
+ results.append(
253
+ _check(
254
+ "MCP registered",
255
+ False,
256
+ "splunkctl entry missing from .mcp.json",
257
+ warn=True,
258
+ )
259
+ )
260
+ except Exception as exc:
261
+ results.append(_check("MCP registered", False, str(exc)))
244
262
 
245
263
 
246
264
  def _finish(
@@ -0,0 +1,47 @@
1
+ """MCP server commands — serve and install."""
2
+
3
+ import json
4
+ import sys
5
+ from pathlib import Path
6
+
7
+ import click
8
+
9
+ from splunkctl import output
10
+
11
+
12
+ @click.group("mcp")
13
+ def mcp_group() -> None:
14
+ """Built-in MCP server for agent integration."""
15
+
16
+
17
+ @mcp_group.command("serve")
18
+ def mcp_serve() -> None:
19
+ """Start the MCP server on stdio."""
20
+ from splunkctl.mcp.server import run_server
21
+
22
+ run_server()
23
+
24
+
25
+ @mcp_group.command("install")
26
+ def mcp_install() -> None:
27
+ """Write .mcp.json for Claude Code registration."""
28
+ exe = sys.executable
29
+ config = {
30
+ "mcpServers": {
31
+ "splunkctl": {
32
+ "command": exe,
33
+ "args": ["-m", "splunkctl", "mcp", "serve"],
34
+ },
35
+ },
36
+ }
37
+
38
+ dest = Path.cwd() / ".mcp.json"
39
+ if dest.exists():
40
+ existing = json.loads(dest.read_text(encoding="utf-8"))
41
+ existing.setdefault("mcpServers", {})["splunkctl"] = config["mcpServers"][
42
+ "splunkctl"
43
+ ]
44
+ config = existing
45
+
46
+ dest.write_text(json.dumps(config, indent=2) + "\n", encoding="utf-8")
47
+ output.info(f"Wrote MCP config to {dest}")