splunkctl 0.4.0__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.0 → splunkctl-0.5.0}/pyproject.toml +7 -2
  4. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/__init__.py +1 -1
  5. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/commands/common.py +23 -0
  6. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/commands/conf.py +13 -17
  7. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/commands/doctor.py +43 -25
  8. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/commands/knowledge.py +15 -32
  9. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/commands/lookups.py +3 -13
  10. splunkctl-0.5.0/splunkctl/commands/mcp_cmd.py +47 -0
  11. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/commands/rules.py +1 -1
  12. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/commands/rules_io.py +3 -17
  13. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/commands/search.py +1 -1
  14. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/commands/state_io.py +2 -1
  15. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/commands/users.py +19 -14
  16. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/main.py +2 -2
  17. splunkctl-0.5.0/splunkctl/mcp/__init__.py +0 -0
  18. splunkctl-0.5.0/splunkctl/mcp/resources.py +35 -0
  19. splunkctl-0.5.0/splunkctl/mcp/server.py +328 -0
  20. splunkctl-0.5.0/splunkctl/mcp/tools.py +233 -0
  21. splunkctl-0.5.0/splunkctl.egg-info/PKG-INFO +256 -0
  22. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl.egg-info/SOURCES.txt +5 -2
  23. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl.egg-info/requires.txt +1 -0
  24. {splunkctl-0.4.0 → splunkctl-0.5.0}/tests/test_version.py +1 -1
  25. splunkctl-0.4.0/PKG-INFO +0 -194
  26. splunkctl-0.4.0/README.md +0 -157
  27. splunkctl-0.4.0/splunkctl/commands/skill_cmd.py +0 -43
  28. splunkctl-0.4.0/splunkctl/skill/SKILL.md +0 -1045
  29. splunkctl-0.4.0/splunkctl.egg-info/PKG-INFO +0 -194
  30. {splunkctl-0.4.0 → splunkctl-0.5.0}/LICENSE +0 -0
  31. {splunkctl-0.4.0 → splunkctl-0.5.0}/setup.cfg +0 -0
  32. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/__main__.py +0 -0
  33. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/client.py +0 -0
  34. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/commands/__init__.py +0 -0
  35. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/commands/alerts.py +0 -0
  36. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/commands/apps.py +0 -0
  37. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/commands/audit.py +0 -0
  38. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/commands/audit_parse.py +0 -0
  39. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/commands/commands_meta.py +0 -0
  40. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/commands/conf_ops.py +0 -0
  41. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/commands/config_cmd.py +0 -0
  42. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/commands/dashboards.py +0 -0
  43. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/commands/datamodels.py +0 -0
  44. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/commands/es.py +0 -0
  45. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/commands/hec.py +0 -0
  46. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/commands/indexes.py +0 -0
  47. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/commands/info.py +0 -0
  48. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/commands/inputs.py +0 -0
  49. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/commands/kvstore.py +0 -0
  50. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/commands/lookups_wiring.py +0 -0
  51. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/commands/parsers.py +0 -0
  52. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/commands/parsers_io.py +0 -0
  53. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/commands/server.py +0 -0
  54. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/commands/state.py +0 -0
  55. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/commands/state_io_blobs.py +0 -0
  56. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/commands/state_io_confs.py +0 -0
  57. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/commands/state_types.py +0 -0
  58. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/config.py +0 -0
  59. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/errors.py +0 -0
  60. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/guard.py +0 -0
  61. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl/output.py +0 -0
  62. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl.egg-info/dependency_links.txt +0 -0
  63. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl.egg-info/entry_points.txt +0 -0
  64. {splunkctl-0.4.0 → splunkctl-0.5.0}/splunkctl.egg-info/top_level.txt +0 -0
  65. {splunkctl-0.4.0 → splunkctl-0.5.0}/tests/test_client.py +0 -0
  66. {splunkctl-0.4.0 → splunkctl-0.5.0}/tests/test_config.py +0 -0
  67. {splunkctl-0.4.0 → splunkctl-0.5.0}/tests/test_guard.py +0 -0
  68. {splunkctl-0.4.0 → splunkctl-0.5.0}/tests/test_main_errors.py +0 -0
  69. {splunkctl-0.4.0 → splunkctl-0.5.0}/tests/test_main_hoisting.py +0 -0
  70. {splunkctl-0.4.0 → 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.0"
3
+ __version__ = "0.5.0"
@@ -354,6 +354,29 @@ _REQUIRED_ACTION_FIELDS: dict[str, str] = {
354
354
  }
355
355
 
356
356
 
357
+ def app_scope(app: str | None) -> dict[str, str]:
358
+ """Build the ``app``/``owner`` kwargs for an app-scoped list call.
359
+
360
+ Unscoped by default (the connection's current namespace); ``owner="-"``
361
+ when an app is given so app-private stanzas owned by other users aren't
362
+ silently excluded.
363
+ """
364
+ return {} if app is None else {"app": app, "owner": "-"}
365
+
366
+
367
+ def trunc(value: str, limit: int = 60) -> str:
368
+ """Shorten a long text field for table display.
369
+
370
+ Never emits a bare ellipsis — a truncated value always says how many
371
+ characters were hidden, e.g. ``foo… [+57 chars]``.
372
+ """
373
+ if len(value) <= limit:
374
+ return value
375
+ kept = value[: limit - 1]
376
+ hidden = len(value) - len(kept)
377
+ return f"{kept}… [+{hidden} chars]"
378
+
379
+
357
380
  def warn_missing_action_fields(
358
381
  actions: str,
359
382
  kwargs: dict[str, Any],
@@ -15,7 +15,7 @@ import click
15
15
  from splunkctl import guard, output
16
16
  from splunkctl.client import get_client
17
17
  from splunkctl.commands import conf_ops
18
- from splunkctl.commands.common import fetch_page, list_options, parse_set
18
+ from splunkctl.commands.common import app_scope, fetch_page, list_options, parse_set
19
19
 
20
20
 
21
21
  def _not_found_message(exc: KeyError, file: str, stanza: str) -> str:
@@ -35,18 +35,6 @@ def _not_found_message(exc: KeyError, file: str, stanza: str) -> str:
35
35
  return f"Stanza '{stanza}' not found in {file}.conf."
36
36
 
37
37
 
38
- def _app_scope(app: str | None) -> dict[str, str]:
39
- """Build the ``app``/``owner`` kwargs for an app-scoped list call.
40
-
41
- Mirrors the ``rules``/``dashboards`` scoping convention: unscoped by
42
- default (matches ``parsers``' existing default, i.e. whatever the
43
- connection's current namespace resolves to), ``owner="-"`` when an
44
- app is given so app-private stanzas owned by other users aren't
45
- silently excluded.
46
- """
47
- return {} if app is None else {"app": app, "owner": "-"}
48
-
49
-
50
38
  @click.group("conf")
51
39
  def conf_group() -> None:
52
40
  """Generic conf file/stanza editor (any conf, including sensitive ones)."""
@@ -70,7 +58,7 @@ def files(
70
58
  ) -> None:
71
59
  """List conf files known to the server."""
72
60
  client = get_client(ctx)
73
- scope = _app_scope(app)
61
+ scope = app_scope(app)
74
62
  items = fetch_page(
75
63
  lambda **pg: client.service.confs.list(**scope, **pg),
76
64
  limit=limit,
@@ -102,7 +90,7 @@ def list_stanzas(
102
90
  ) -> None:
103
91
  """List stanzas in a conf file."""
104
92
  client = get_client(ctx)
105
- scope = _app_scope(app)
93
+ scope = app_scope(app)
106
94
  items = fetch_page(
107
95
  lambda **pg: client.service.confs[file].list(**scope, **pg),
108
96
  limit=limit,
@@ -123,13 +111,21 @@ def list_stanzas(
123
111
  @conf_group.command("get")
124
112
  @click.argument("file")
125
113
  @click.argument("stanza")
114
+ @click.option(
115
+ "--app",
116
+ default=None,
117
+ help="App to resolve the stanza in, when the same stanza exists in "
118
+ "more than one app (default: current namespace).",
119
+ )
126
120
  @click.option("--key", default=None, help="Show only this key's value.")
127
121
  @click.pass_context
128
- def get(ctx: click.Context, file: str, stanza: str, key: str | None) -> None:
122
+ def get(
123
+ ctx: click.Context, file: str, stanza: str, app: str | None, key: str | None
124
+ ) -> None:
129
125
  """Show a conf stanza's keys, or one key's value with --key."""
130
126
  client = get_client(ctx)
131
127
  try:
132
- entity = conf_ops.get_stanza(client, file, stanza)
128
+ entity = conf_ops.get_stanza(client, file, stanza, app=app)
133
129
  except KeyError as exc:
134
130
  output.error(_not_found_message(exc, file, stanza), kind="not_found")
135
131
  ctx.exit(1)