@havesomecode/kibana-mcp-server 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +486 -0
- package/config/sources.example.json +97 -0
- package/dist/src/cli.js +160 -0
- package/dist/src/config.js +204 -0
- package/dist/src/index.js +10 -0
- package/dist/src/kibana_client.js +441 -0
- package/dist/src/mcp_entry.js +6 -0
- package/dist/src/mcp_runtime.js +23 -0
- package/dist/src/profile_paths.js +36 -0
- package/dist/src/profile_store.js +136 -0
- package/dist/src/query/compiler.js +430 -0
- package/dist/src/query/cursor.js +7 -0
- package/dist/src/query/normalize.js +383 -0
- package/dist/src/schema_catalog.js +91 -0
- package/dist/src/secret_store.js +261 -0
- package/dist/src/server.js +87 -0
- package/dist/src/setup_flow.js +160 -0
- package/dist/src/source_catalog.js +47 -0
- package/dist/src/tools/configure.js +53 -0
- package/dist/src/tools/describe_fields.js +55 -0
- package/dist/src/tools/discover.js +47 -0
- package/dist/src/tools/filter.js +141 -0
- package/dist/src/tools/query.js +287 -0
- package/dist/src/types.js +1 -0
- package/docs/project/compatibility-matrix.md +22 -0
- package/docs/project/distribution-strategy.md +78 -0
- package/docs/project/npm-publishing.md +57 -0
- package/docs/project/release-checklist.md +51 -0
- package/docs/project/support-policy.md +52 -0
- package/package.json +65 -0
- package/plugins/kibana-log-investigation/.codex-plugin/plugin.json +31 -0
- package/plugins/kibana-log-investigation/.mcp.json +10 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Zacaria
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
# Kibana Log Investigation MCP
|
|
2
|
+
|
|
3
|
+
Read-only MCP server for agent-driven log investigation against Kibana-backed search endpoints protected with basic auth.
|
|
4
|
+
|
|
5
|
+
The server is intentionally small:
|
|
6
|
+
|
|
7
|
+
- `setup` saves one or more machine-level Kibana profiles for later threads
|
|
8
|
+
- `configure` still exists for advanced MCP clients that want to drive credentials and source catalogs at runtime
|
|
9
|
+
- `describe_fields` exposes effective field capabilities for one configured source
|
|
10
|
+
- `discover` lists configured logical sources and field hints
|
|
11
|
+
- `filter` runs an exact-field filter when the field name is already known
|
|
12
|
+
- `query` searches one or more sources over an absolute time window in `hits`, `count`, `histogram`, `terms`, `stats`, or `grouped_top_hits` mode
|
|
13
|
+
|
|
14
|
+
It is designed as a general-purpose log investigation MCP for environments where operators can expose a small set of useful logical sources.
|
|
15
|
+
|
|
16
|
+
## Requirements
|
|
17
|
+
|
|
18
|
+
- Node.js 22+
|
|
19
|
+
- A Kibana-compatible read endpoint reachable with basic auth
|
|
20
|
+
- A source catalog JSON file describing the logical log sources to expose, or the bundled `config/sources.example.json`
|
|
21
|
+
|
|
22
|
+
## Project Status
|
|
23
|
+
|
|
24
|
+
This repo targets external adoption and AI-agent usability. It is safe for real investigations, but the install and release posture is still evolving.
|
|
25
|
+
|
|
26
|
+
- Guaranteed: repo-local Codex plugin install (see `INSTALL.md`)
|
|
27
|
+
- Guaranteed: guided machine setup through `npm run setup`
|
|
28
|
+
- In progress: package binary contract for agent-friendly `npx` execution
|
|
29
|
+
- In progress: semantic-release automation for tags, npm publish, and GitHub Releases
|
|
30
|
+
- Planned: first public package release after trusted publishing is enabled
|
|
31
|
+
- Support posture and compatibility details live in `docs/project/support-policy.md`
|
|
32
|
+
- Contributing and security reporting live in `CONTRIBUTING.md` and `SECURITY.md`
|
|
33
|
+
|
|
34
|
+
## Support and Releases
|
|
35
|
+
|
|
36
|
+
- Distribution strategy: `docs/project/distribution-strategy.md`
|
|
37
|
+
- Compatibility matrix: `docs/project/compatibility-matrix.md`
|
|
38
|
+
- Release checklist: `docs/project/release-checklist.md`
|
|
39
|
+
- npm publishing setup: `docs/project/npm-publishing.md`
|
|
40
|
+
- GitHub Releases are the canonical release notes and version record
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npm install
|
|
46
|
+
npm run build
|
|
47
|
+
npm run setup
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Use In Codex
|
|
51
|
+
|
|
52
|
+
This repo includes a repo-scoped Codex plugin so a cloned checkout can install the MCP without any extra packaging or hosted infrastructure.
|
|
53
|
+
|
|
54
|
+
The packaged CLI surface is also prepared for a future public install path. Once npm publishing is enabled, AI agents will be able to run the server with `npx -y @havesomecode/kibana-mcp-server` instead of cloning the repo first.
|
|
55
|
+
|
|
56
|
+
### Quick path
|
|
57
|
+
|
|
58
|
+
1. Clone the repository.
|
|
59
|
+
2. Make sure Node.js 22+ is available.
|
|
60
|
+
- if it is missing, ask Codex to use the plugin skill `ensure-node-runtime`
|
|
61
|
+
- the repo pins Node major `22` in `.node-version`
|
|
62
|
+
3. From the repo root, run:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npm install
|
|
66
|
+
npm run build
|
|
67
|
+
npm run setup
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
4. Open the cloned repo in Codex.
|
|
71
|
+
5. Open the plugin directory in Codex and install `Kibana Log Investigation` from the repo marketplace.
|
|
72
|
+
- if the current model cannot complete that install itself, do the Codex UI click manually and let the agent continue with configuration afterward
|
|
73
|
+
6. Restart Codex if the new MCP server does not appear immediately.
|
|
74
|
+
7. Run guided setup once:
|
|
75
|
+
- `npm run setup`
|
|
76
|
+
- provide the environment name, Kibana base URL, username, password, and a source catalog file to import
|
|
77
|
+
- use the bundled `config/sources.example.json` unless you already have a better catalog JSON
|
|
78
|
+
8. Let later threads reuse the saved default profile automatically.
|
|
79
|
+
- if you add more than one environment during setup, select non-default ones with `KIBANA_PROFILE=<PROFILE_NAME>`
|
|
80
|
+
|
|
81
|
+
Repo-scoped plugin files:
|
|
82
|
+
|
|
83
|
+
- `.agents/plugins/marketplace.json`
|
|
84
|
+
- `plugins/kibana-log-investigation/.codex-plugin/plugin.json`
|
|
85
|
+
- `plugins/kibana-log-investigation/.mcp.json`
|
|
86
|
+
|
|
87
|
+
### Minimal handoff prompt
|
|
88
|
+
|
|
89
|
+
If you are handing only the repo link to another Codex agent, this usually works:
|
|
90
|
+
|
|
91
|
+
```text
|
|
92
|
+
Clone this repo, ensure Node.js 22+ is installed at user level for the current OS, run npm install, npm run build, and npm run setup, install the repo plugin named "Kibana Log Investigation", import a source catalog during setup, and verify discover plus one query work in Codex without needing a later manual configure step.
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Configuration
|
|
96
|
+
|
|
97
|
+
The preferred path is guided machine setup:
|
|
98
|
+
|
|
99
|
+
- `npm run setup` or `node dist/src/index.js setup`
|
|
100
|
+
- saves non-secret profile metadata in the user’s machine-level app config directory
|
|
101
|
+
- saves credentials in the platform credential store
|
|
102
|
+
- macOS Keychain
|
|
103
|
+
- Windows Credential Manager
|
|
104
|
+
- Linux Secret Service when available
|
|
105
|
+
- imports the selected source catalog into machine-local state so later threads do not depend on the repo checkout
|
|
106
|
+
|
|
107
|
+
The server also supports two advanced compatibility paths:
|
|
108
|
+
|
|
109
|
+
- bootstrap at process start through environment variables plus a JSON source catalog
|
|
110
|
+
- runtime from your MCP client through the `configure` tool
|
|
111
|
+
|
|
112
|
+
When `configure` is called, the source catalog is still persisted to disk so restarts do not wipe the logical source setup.
|
|
113
|
+
|
|
114
|
+
### Machine profiles
|
|
115
|
+
|
|
116
|
+
After guided setup, the default saved profile loads automatically on startup.
|
|
117
|
+
|
|
118
|
+
If you saved more than one environment, select a non-default profile with:
|
|
119
|
+
|
|
120
|
+
- `KIBANA_PROFILE=<PROFILE_NAME>`
|
|
121
|
+
|
|
122
|
+
Example:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
KIBANA_PROFILE=staging npm run dev
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Bootstrap configuration
|
|
129
|
+
|
|
130
|
+
The server can still read connection details from environment variables and source definitions from a JSON file.
|
|
131
|
+
|
|
132
|
+
Environment variables:
|
|
133
|
+
|
|
134
|
+
- `KIBANA_BASE_URL`
|
|
135
|
+
- `KIBANA_USERNAME`
|
|
136
|
+
- `KIBANA_PASSWORD`
|
|
137
|
+
- `KIBANA_PROFILE` optional, selects a saved non-default machine profile
|
|
138
|
+
- `KIBANA_TIMEOUT_MS` optional, default `10000`
|
|
139
|
+
- `KIBANA_SOURCE_CATALOG_PATH` optional, default `config/sources.runtime.json`
|
|
140
|
+
|
|
141
|
+
`KIBANA_BASE_URL` is the Kibana base prefix that the server joins with each configured backend or schema path.
|
|
142
|
+
|
|
143
|
+
Use:
|
|
144
|
+
|
|
145
|
+
- `https://kibana.example.com`
|
|
146
|
+
- `https://gateway.example.com/logs` when Kibana is reverse-proxied under `/logs`
|
|
147
|
+
|
|
148
|
+
Do not use:
|
|
149
|
+
|
|
150
|
+
- `https://kibana.example.com/internal/search/es`
|
|
151
|
+
- `https://gateway.example.com/logs/internal/search/es`
|
|
152
|
+
|
|
153
|
+
The source definition already carries endpoint paths such as `/internal/search/es`, so including a full API path in `KIBANA_BASE_URL` usually produces a bad combined URL and a `404`.
|
|
154
|
+
|
|
155
|
+
For the env-bootstrap path, start from `config/sources.example.json` and copy it to `config/sources.json`.
|
|
156
|
+
If no explicit `KIBANA_SOURCE_CATALOG_PATH` is set, env bootstrap will try `config/sources.runtime.json` first and then fall back to `config/sources.json`.
|
|
157
|
+
|
|
158
|
+
### Multiple environments
|
|
159
|
+
|
|
160
|
+
The recommended path is to save multiple machine profiles during setup.
|
|
161
|
+
|
|
162
|
+
Then:
|
|
163
|
+
|
|
164
|
+
- the default profile loads automatically
|
|
165
|
+
- extra MCP entries can select another saved environment with `KIBANA_PROFILE`
|
|
166
|
+
- you do not need to redefine `KIBANA_BASE_URL`, credentials, or source-catalog paths per thread
|
|
167
|
+
|
|
168
|
+
Example:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
KIBANA_PROFILE=staging npm run dev
|
|
172
|
+
KIBANA_PROFILE=prod npm run dev
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
If you still prefer env-bootstrap entries, keep these distinct per environment:
|
|
176
|
+
|
|
177
|
+
- `KIBANA_BASE_URL`
|
|
178
|
+
- `KIBANA_USERNAME`
|
|
179
|
+
- `KIBANA_PASSWORD`
|
|
180
|
+
- `KIBANA_SOURCE_CATALOG_PATH`
|
|
181
|
+
|
|
182
|
+
Using separate source-catalog paths matters because the `configure` tool persists sources to disk, and one shared runtime file would cause the environments to overwrite each other.
|
|
183
|
+
|
|
184
|
+
Each source definition should provide:
|
|
185
|
+
|
|
186
|
+
- stable `id`
|
|
187
|
+
- human-readable `name`
|
|
188
|
+
- `timeField`
|
|
189
|
+
- `tags` and optional `description`
|
|
190
|
+
- `fieldHints` so agents can form exact-match filters
|
|
191
|
+
- `defaultTextFields` for free-text search
|
|
192
|
+
- `evidenceFields` to elevate likely report-worthy fields
|
|
193
|
+
- `backend` describing how the source is queried
|
|
194
|
+
- optional `schema` describing how field introspection is performed for `describe_fields` and other schema-dependent features
|
|
195
|
+
|
|
196
|
+
Supported backend kinds:
|
|
197
|
+
|
|
198
|
+
- `kibana_internal_search_es`
|
|
199
|
+
- POSTs to a Kibana endpoint like `/internal/search/es`
|
|
200
|
+
- wraps the query body inside `{ "params": { "index": ..., "body": ... } }`
|
|
201
|
+
- adds `kbn-xsrf`
|
|
202
|
+
- `elasticsearch_search`
|
|
203
|
+
- POSTs the compiled Elasticsearch body directly to the configured path
|
|
204
|
+
|
|
205
|
+
Supported schema backend kinds:
|
|
206
|
+
|
|
207
|
+
- `kibana_data_views_fields`
|
|
208
|
+
- tries Kibana wildcard field-metadata endpoints, preferring `/internal/data_views/_fields_for_wildcard`
|
|
209
|
+
- adds `pattern=...` from `schema.index` or `backend.index`
|
|
210
|
+
- if all known field-metadata endpoints return `404`, falls back to sampling fields through the configured search backend
|
|
211
|
+
- `kibana_index_patterns_fields`
|
|
212
|
+
- tries the legacy wildcard field-metadata endpoint `/api/index_patterns/_fields_for_wildcard` and related fallbacks
|
|
213
|
+
- adds `pattern=...` from `schema.index` or `backend.index`
|
|
214
|
+
- if all known field-metadata endpoints return `404`, falls back to sampling fields through the configured search backend
|
|
215
|
+
- `elasticsearch_field_caps`
|
|
216
|
+
- GETs an Elasticsearch field capabilities endpoint such as `/<index-pattern>/_field_caps?fields=*`
|
|
217
|
+
- uses `schema.path` when provided or derives the default from `schema.index`
|
|
218
|
+
|
|
219
|
+
Deployment note:
|
|
220
|
+
|
|
221
|
+
- some Kibana deployments expose search transport but not field-metadata endpoints
|
|
222
|
+
- when direct schema endpoints such as `/api/data_views/fields_for_wildcard`, `/api/index_patterns/_fields_for_wildcard`, or `/<index>/_field_caps` return `404`, the server falls back to sampling fields through the configured search backend
|
|
223
|
+
- this fallback is heuristic rather than authoritative, but it is often sufficient to recover `.keyword` promotion and object-path discovery in search-only environments
|
|
224
|
+
|
|
225
|
+
Example source shape for an application log stream:
|
|
226
|
+
|
|
227
|
+
```json
|
|
228
|
+
{
|
|
229
|
+
"id": "app_logs",
|
|
230
|
+
"name": "Application Logs",
|
|
231
|
+
"timeField": "@timestamp",
|
|
232
|
+
"backend": {
|
|
233
|
+
"kind": "kibana_internal_search_es",
|
|
234
|
+
"path": "/internal/search/es",
|
|
235
|
+
"index": "app-logs-*"
|
|
236
|
+
},
|
|
237
|
+
"schema": {
|
|
238
|
+
"kind": "kibana_data_views_fields",
|
|
239
|
+
"index": "app-logs-*"
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Keep `schema.path` omitted for this setup. The server will try the known Kibana metadata endpoints first and then fall back to search-transport sampling if those endpoints still return `404`.
|
|
245
|
+
|
|
246
|
+
## Running
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
KIBANA_BASE_URL=https://kibana.example.com \
|
|
250
|
+
KIBANA_USERNAME=elastic \
|
|
251
|
+
KIBANA_PASSWORD=secret \
|
|
252
|
+
KIBANA_SOURCE_CATALOG_PATH=config/sources.json \
|
|
253
|
+
npm run dev
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Or, after guided setup:
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
npm run dev
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
## Tool Shapes
|
|
263
|
+
|
|
264
|
+
### `configure`
|
|
265
|
+
|
|
266
|
+
Input:
|
|
267
|
+
|
|
268
|
+
```json
|
|
269
|
+
{
|
|
270
|
+
"kibana": {
|
|
271
|
+
"baseUrl": "https://kibana.example.com",
|
|
272
|
+
"username": "elastic",
|
|
273
|
+
"password": "secret",
|
|
274
|
+
"timeoutMs": 10000
|
|
275
|
+
},
|
|
276
|
+
"sources": [
|
|
277
|
+
{
|
|
278
|
+
"id": "app_logs",
|
|
279
|
+
"name": "Application Logs",
|
|
280
|
+
"tags": ["app", "logs", "production"],
|
|
281
|
+
"timeField": "@timestamp",
|
|
282
|
+
"backend": {
|
|
283
|
+
"kind": "kibana_internal_search_es",
|
|
284
|
+
"path": "/internal/search/es",
|
|
285
|
+
"index": ["app-logs-*"]
|
|
286
|
+
},
|
|
287
|
+
"schema": {
|
|
288
|
+
"kind": "kibana_data_views_fields",
|
|
289
|
+
"index": ["app-logs-*"]
|
|
290
|
+
},
|
|
291
|
+
"fieldHints": [
|
|
292
|
+
{
|
|
293
|
+
"name": "traceId",
|
|
294
|
+
"aliases": ["trace_id"]
|
|
295
|
+
}
|
|
296
|
+
],
|
|
297
|
+
"defaultTextFields": ["message"],
|
|
298
|
+
"evidenceFields": ["traceId"]
|
|
299
|
+
}
|
|
300
|
+
]
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
If you do not bootstrap via env vars and do not have a saved default profile, call `configure` before `discover` or `query`.
|
|
305
|
+
Calling `configure` also persists the provided sources to the runtime source-catalog file so later restarts can reload them automatically.
|
|
306
|
+
If you want `describe_fields`, exact-field auto-resolution in `query`, or nested-filter validation, configure `schema` for each relevant source.
|
|
307
|
+
In deployments where schema endpoints are routed indirectly or return `404`, prefer `schema.kind = "kibana_data_views_fields"` with `schema.index` set and omit `schema.path`.
|
|
308
|
+
|
|
309
|
+
### `discover`
|
|
310
|
+
|
|
311
|
+
Input:
|
|
312
|
+
|
|
313
|
+
```json
|
|
314
|
+
{
|
|
315
|
+
"query": "application",
|
|
316
|
+
"limit": 10
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
Output includes:
|
|
321
|
+
|
|
322
|
+
- source id
|
|
323
|
+
- source name
|
|
324
|
+
- time field
|
|
325
|
+
- tags
|
|
326
|
+
- configured field hints
|
|
327
|
+
|
|
328
|
+
### `describe_fields`
|
|
329
|
+
|
|
330
|
+
Use `describe_fields` when the agent needs to know whether a field is safe for exact term matching, grouping, or sorting.
|
|
331
|
+
If the source does not configure a schema backend, `describe_fields` fails clearly instead of guessing.
|
|
332
|
+
When direct schema endpoints are unavailable, the server will infer fields from sample hits via the configured search backend.
|
|
333
|
+
|
|
334
|
+
Input:
|
|
335
|
+
|
|
336
|
+
```json
|
|
337
|
+
{
|
|
338
|
+
"source_id": "workflow_metrics",
|
|
339
|
+
"query": "event",
|
|
340
|
+
"limit": 50
|
|
341
|
+
}
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
Output includes:
|
|
345
|
+
|
|
346
|
+
- field name
|
|
347
|
+
- field type
|
|
348
|
+
- `searchable`
|
|
349
|
+
- `aggregatable`
|
|
350
|
+
- available subfields
|
|
351
|
+
- `preferred_exact_field` when a safer exact-match variant exists, such as `.keyword`
|
|
352
|
+
|
|
353
|
+
### `query`
|
|
354
|
+
|
|
355
|
+
Input:
|
|
356
|
+
|
|
357
|
+
```json
|
|
358
|
+
{
|
|
359
|
+
"source_ids": ["app_logs", "workflow_metrics"],
|
|
360
|
+
"start_time": "2026-04-02T12:00:00Z",
|
|
361
|
+
"end_time": "2026-04-02T12:15:00Z",
|
|
362
|
+
"text": "SERVICE_RELOAD",
|
|
363
|
+
"filters": [
|
|
364
|
+
{ "field": "region", "value": "us-east-1" },
|
|
365
|
+
{ "field": "traceId", "value": "trace-12345" }
|
|
366
|
+
],
|
|
367
|
+
"nested_filters": [
|
|
368
|
+
{
|
|
369
|
+
"path": "steps",
|
|
370
|
+
"field": "name",
|
|
371
|
+
"value": "CACHE_REFRESH"
|
|
372
|
+
}
|
|
373
|
+
],
|
|
374
|
+
"extract_nested": true,
|
|
375
|
+
"mode": "hits",
|
|
376
|
+
"sort_by": "total_duration_ms",
|
|
377
|
+
"sort": "desc",
|
|
378
|
+
"limit": 50
|
|
379
|
+
}
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
Supported modes:
|
|
383
|
+
|
|
384
|
+
- `hits`
|
|
385
|
+
- `count`
|
|
386
|
+
- `histogram`
|
|
387
|
+
- requires `histogram_interval`
|
|
388
|
+
- `terms`
|
|
389
|
+
- requires `group_by`
|
|
390
|
+
- `stats`
|
|
391
|
+
- requires `stats_field`
|
|
392
|
+
- can also take `group_by`
|
|
393
|
+
- `grouped_top_hits`
|
|
394
|
+
- requires `group_by`
|
|
395
|
+
- requires `sort_by`
|
|
396
|
+
- accepts `top_hits_size`
|
|
397
|
+
|
|
398
|
+
Every response includes a `query_echo` section so the investigating agent can explain which sources, bounds, filters, and mode were actually used.
|
|
399
|
+
For `hits` mode, you can also pass `sort_by` to sort on a chosen field. In `query`, exact filters and grouped bucket fields use field-hint alias resolution plus schema-aware exact-field preference when metadata is available. When the server rewrites a logical field such as `event` to a safer exact field such as `event.keyword`, the response includes an advisory. If schema metadata is unavailable, the query proceeds with the requested field and returns a `schema_unavailable` advisory instead of silently pretending exact-safe resolution happened.
|
|
400
|
+
|
|
401
|
+
For very large single-source hit sets, `query` also supports cursor pagination:
|
|
402
|
+
|
|
403
|
+
```json
|
|
404
|
+
{
|
|
405
|
+
"source_ids": ["workflow_metrics"],
|
|
406
|
+
"start_time": "2026-04-02T12:00:00Z",
|
|
407
|
+
"end_time": "2026-04-02T12:15:00Z",
|
|
408
|
+
"mode": "hits",
|
|
409
|
+
"limit": 100,
|
|
410
|
+
"cursor": "REPLACE_ME_NEXT_CURSOR"
|
|
411
|
+
}
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
Cursor pagination is currently supported only for single-source `hits` queries.
|
|
415
|
+
|
|
416
|
+
Nested filtering also depends on schema metadata. If a source does not expose a working `schema` backend, nested requests fail early with a clear configuration error instead of reaching Kibana and failing with a backend 404.
|
|
417
|
+
|
|
418
|
+
Example `stats` query:
|
|
419
|
+
|
|
420
|
+
```json
|
|
421
|
+
{
|
|
422
|
+
"source_ids": ["workflow_metrics"],
|
|
423
|
+
"start_time": "2026-04-02T12:00:00Z",
|
|
424
|
+
"end_time": "2026-04-02T12:15:00Z",
|
|
425
|
+
"mode": "stats",
|
|
426
|
+
"stats_field": "total_duration_ms",
|
|
427
|
+
"group_by": "trace_id",
|
|
428
|
+
"limit": 25
|
|
429
|
+
}
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
Example `grouped_top_hits` query:
|
|
433
|
+
|
|
434
|
+
```json
|
|
435
|
+
{
|
|
436
|
+
"source_ids": ["workflow_metrics"],
|
|
437
|
+
"start_time": "2026-04-02T12:00:00Z",
|
|
438
|
+
"end_time": "2026-04-02T12:15:00Z",
|
|
439
|
+
"mode": "grouped_top_hits",
|
|
440
|
+
"group_by": "trace_id",
|
|
441
|
+
"sort_by": "total_duration_ms",
|
|
442
|
+
"top_hits_size": 1,
|
|
443
|
+
"limit": 25
|
|
444
|
+
}
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
### `filter`
|
|
448
|
+
|
|
449
|
+
Use `filter` when you already know the backend field you want to target and want to avoid alias resolution.
|
|
450
|
+
|
|
451
|
+
Input:
|
|
452
|
+
|
|
453
|
+
```json
|
|
454
|
+
{
|
|
455
|
+
"source_ids": ["workflow_metrics"],
|
|
456
|
+
"start_time": "2026-04-02T12:00:00Z",
|
|
457
|
+
"end_time": "2026-04-02T12:15:00Z",
|
|
458
|
+
"field": "eventName.keyword",
|
|
459
|
+
"value": "WORKFLOW_RELOAD_STATS",
|
|
460
|
+
"nested_filters": [
|
|
461
|
+
{
|
|
462
|
+
"path": "steps",
|
|
463
|
+
"field": "steps.name.keyword",
|
|
464
|
+
"value": "CACHE_REFRESH"
|
|
465
|
+
}
|
|
466
|
+
],
|
|
467
|
+
"extract_nested": true,
|
|
468
|
+
"mode": "hits",
|
|
469
|
+
"sort_by": "total_duration_ms",
|
|
470
|
+
"limit": 50
|
|
471
|
+
}
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
`filter` returns the same structured output envelope as `query`, but it does not apply field-hint alias rewrites.
|
|
475
|
+
If schema metadata is available and you pass a logical text field such as `event`, the server may still promote it to a preferred exact field such as `event.keyword` for exact term matching. If you pass an exact backend field like `event.keyword`, it is used as-is.
|
|
476
|
+
`sort_by` is supported for `hits` and `grouped_top_hits`.
|
|
477
|
+
|
|
478
|
+
## Development
|
|
479
|
+
|
|
480
|
+
```bash
|
|
481
|
+
npm run lint
|
|
482
|
+
npm run format:check
|
|
483
|
+
npm run check:types
|
|
484
|
+
npm test
|
|
485
|
+
npm run verify
|
|
486
|
+
```
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
{
|
|
2
|
+
"sources": [
|
|
3
|
+
{
|
|
4
|
+
"id": "app_logs",
|
|
5
|
+
"name": "Application logs",
|
|
6
|
+
"description": "Application-side execution logs for core workflows.",
|
|
7
|
+
"tags": ["application", "logs", "runtime"],
|
|
8
|
+
"timeField": "@timestamp",
|
|
9
|
+
"backend": {
|
|
10
|
+
"kind": "kibana_internal_search_es",
|
|
11
|
+
"path": "/internal/search/es",
|
|
12
|
+
"index": ["app-logs-*"]
|
|
13
|
+
},
|
|
14
|
+
"schema": {
|
|
15
|
+
"kind": "kibana_data_views_fields",
|
|
16
|
+
"index": ["app-logs-*"]
|
|
17
|
+
},
|
|
18
|
+
"fieldHints": [
|
|
19
|
+
{
|
|
20
|
+
"name": "traceId",
|
|
21
|
+
"aliases": ["trace_id", "correlation_id"],
|
|
22
|
+
"description": "Correlation id for one workflow execution."
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"name": "region",
|
|
26
|
+
"aliases": ["location"],
|
|
27
|
+
"description": "Region or deployment segment for the event."
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"name": "tenantId",
|
|
31
|
+
"aliases": ["tenant_id"],
|
|
32
|
+
"description": "Tenant or customer identifier involved in the workflow."
|
|
33
|
+
}
|
|
34
|
+
],
|
|
35
|
+
"defaultTextFields": ["message", "event.original"],
|
|
36
|
+
"evidenceFields": ["traceId", "region", "tenantId", "operation", "duration_ms"]
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"id": "api_logs",
|
|
40
|
+
"name": "API request logs",
|
|
41
|
+
"description": "Request and response logs for HTTP APIs.",
|
|
42
|
+
"tags": ["api", "http", "latency"],
|
|
43
|
+
"timeField": "@timestamp",
|
|
44
|
+
"backend": {
|
|
45
|
+
"kind": "kibana_internal_search_es",
|
|
46
|
+
"path": "/internal/search/es",
|
|
47
|
+
"index": ["api-logs-*"]
|
|
48
|
+
},
|
|
49
|
+
"schema": {
|
|
50
|
+
"kind": "kibana_data_views_fields",
|
|
51
|
+
"index": ["api-logs-*"]
|
|
52
|
+
},
|
|
53
|
+
"fieldHints": [
|
|
54
|
+
{
|
|
55
|
+
"name": "route",
|
|
56
|
+
"description": "Normalized route or path pattern."
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"name": "statusCode",
|
|
60
|
+
"aliases": ["status_code"],
|
|
61
|
+
"description": "HTTP response code."
|
|
62
|
+
}
|
|
63
|
+
],
|
|
64
|
+
"defaultTextFields": ["message"],
|
|
65
|
+
"evidenceFields": ["route", "statusCode", "duration_ms"]
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"id": "workflow_metrics",
|
|
69
|
+
"name": "Workflow metrics logs",
|
|
70
|
+
"description": "Structured log events such as WORKFLOW_RELOAD_STATS and CACHE_REFRESH_PHASES.",
|
|
71
|
+
"tags": ["metrics", "workflow", "structured"],
|
|
72
|
+
"timeField": "@timestamp",
|
|
73
|
+
"backend": {
|
|
74
|
+
"kind": "kibana_internal_search_es",
|
|
75
|
+
"path": "/internal/search/es",
|
|
76
|
+
"index": ["workflow-metrics-*"]
|
|
77
|
+
},
|
|
78
|
+
"schema": {
|
|
79
|
+
"kind": "kibana_data_views_fields",
|
|
80
|
+
"index": ["workflow-metrics-*"]
|
|
81
|
+
},
|
|
82
|
+
"fieldHints": [
|
|
83
|
+
{
|
|
84
|
+
"name": "eventName",
|
|
85
|
+
"aliases": ["event_name"],
|
|
86
|
+
"description": "Logical event marker for metrics-style logs."
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"name": "step",
|
|
90
|
+
"description": "Workflow step name."
|
|
91
|
+
}
|
|
92
|
+
],
|
|
93
|
+
"defaultTextFields": ["message", "eventName"],
|
|
94
|
+
"evidenceFields": ["eventName", "step", "items_scanned", "items_reloaded", "duration_ms"]
|
|
95
|
+
}
|
|
96
|
+
]
|
|
97
|
+
}
|