@havesomecode/kibana-mcp-server 1.3.0 → 1.4.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.
Files changed (86) hide show
  1. package/README.md +54 -490
  2. package/dist/src/bin/kibana-mcp-server.d.ts +2 -0
  3. package/dist/src/bin/kibana-mcp-server.js +2 -53
  4. package/dist/src/bin/run_cli.d.ts +4 -0
  5. package/dist/src/bin/run_cli.js +49 -0
  6. package/dist/src/commands/context.d.ts +18 -0
  7. package/dist/src/commands/serve.d.ts +4 -0
  8. package/dist/src/commands/serve.js +30 -6
  9. package/dist/src/commands/setup.d.ts +3 -0
  10. package/dist/src/commands/setup.js +2 -2
  11. package/dist/src/config/config_file.d.ts +59 -0
  12. package/dist/src/config/config_file.js +76 -0
  13. package/dist/src/config/environment.d.ts +100 -0
  14. package/dist/src/{config.js → config/environment.js} +26 -24
  15. package/dist/src/config/startup.d.ts +31 -0
  16. package/dist/src/config/startup.js +86 -0
  17. package/dist/src/kibana/client.d.ts +28 -0
  18. package/dist/src/kibana/schema_catalog.d.ts +11 -0
  19. package/dist/src/{schema_catalog.js → kibana/schema_catalog.js} +33 -11
  20. package/dist/src/kibana/source_catalog.d.ts +10 -0
  21. package/dist/src/mcp_entry.d.ts +2 -0
  22. package/dist/src/mcp_entry.js +1 -1
  23. package/dist/src/profiles/context.d.ts +4 -0
  24. package/dist/src/profiles/context.js +15 -0
  25. package/dist/src/profiles/names.d.ts +10 -0
  26. package/dist/src/profiles/names.js +35 -0
  27. package/dist/src/profiles/paths.d.ts +9 -0
  28. package/dist/src/profiles/runtime.d.ts +43 -0
  29. package/dist/src/profiles/runtime.js +150 -0
  30. package/dist/src/profiles/secret_store.d.ts +20 -0
  31. package/dist/src/profiles/state_lock.d.ts +13 -0
  32. package/dist/src/{state_lock.js → profiles/state_lock.js} +14 -5
  33. package/dist/src/profiles/store.d.ts +34 -0
  34. package/dist/src/{profile_store.js → profiles/store.js} +7 -5
  35. package/dist/src/query/compiler.d.ts +9 -0
  36. package/dist/src/query/cursor.d.ts +8 -0
  37. package/dist/src/query/normalize.d.ts +2 -0
  38. package/dist/src/server.d.ts +29 -0
  39. package/dist/src/server.js +40 -50
  40. package/dist/src/setup/bootstrap.d.ts +50 -0
  41. package/dist/src/{bootstrap.js → setup/bootstrap.js} +10 -36
  42. package/dist/src/setup/client_registration.d.ts +38 -0
  43. package/dist/src/setup/setup_flow.d.ts +26 -0
  44. package/dist/src/{setup_flow.js → setup/setup_flow.js} +6 -6
  45. package/dist/src/stores/catalog_store.d.ts +38 -0
  46. package/dist/src/stores/catalog_store.js +22 -0
  47. package/dist/src/stores/file.d.ts +5 -0
  48. package/dist/src/stores/file.js +48 -0
  49. package/dist/src/stores/ioredis.d.ts +21 -0
  50. package/dist/src/stores/ioredis.js +33 -0
  51. package/dist/src/stores/mongodb.d.ts +42 -0
  52. package/dist/src/stores/mongodb.js +57 -0
  53. package/dist/src/stores/postgres.d.ts +24 -0
  54. package/dist/src/stores/postgres.js +65 -0
  55. package/dist/src/stores/redis.d.ts +24 -0
  56. package/dist/src/stores/redis.js +31 -0
  57. package/dist/src/stores/shared.d.ts +25 -0
  58. package/dist/src/stores/shared.js +88 -0
  59. package/dist/src/stores/sqlite.d.ts +26 -0
  60. package/dist/src/stores/sqlite.js +63 -0
  61. package/dist/src/tools/configure.d.ts +59 -0
  62. package/dist/src/tools/configure.js +1 -1
  63. package/dist/src/tools/configure_index.d.ts +35 -0
  64. package/dist/src/tools/configure_index.js +75 -66
  65. package/dist/src/tools/describe_fields.d.ts +29 -0
  66. package/dist/src/tools/discover.d.ts +27 -0
  67. package/dist/src/tools/filter.d.ts +180 -0
  68. package/dist/src/tools/query.d.ts +181 -0
  69. package/dist/src/transports/http.d.ts +15 -0
  70. package/dist/src/transports/http.js +291 -0
  71. package/dist/src/transports/stdio.d.ts +1 -0
  72. package/dist/src/transports/stdio.js +46 -0
  73. package/dist/src/types.d.ts +254 -0
  74. package/dist/src/utils/cli_io.d.ts +11 -0
  75. package/dist/src/utils/cli_prompts.d.ts +9 -0
  76. package/dist/src/utils/cli_ui.d.ts +6 -0
  77. package/dist/src/utils/fs.d.ts +3 -0
  78. package/dist/src/utils/fs.js +30 -0
  79. package/package.json +58 -1
  80. package/skills/kibana-log-investigation/SKILL.md +36 -5
  81. package/dist/src/mcp_runtime.js +0 -23
  82. /package/dist/src/{kibana_client.js → kibana/client.js} +0 -0
  83. /package/dist/src/{source_catalog.js → kibana/source_catalog.js} +0 -0
  84. /package/dist/src/{profile_paths.js → profiles/paths.js} +0 -0
  85. /package/dist/src/{secret_store.js → profiles/secret_store.js} +0 -0
  86. /package/dist/src/{client_registration.js → setup/client_registration.js} +0 -0
@@ -0,0 +1,46 @@
1
+ import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
2
+ import { DEFAULT_PROFILE_NAME, loadStartupConfiguration } from "../config/startup.js";
3
+ import { createApplication } from "../server.js";
4
+ export async function startMcpServer(envInput = process.env) {
5
+ // stdio serves one global configuration: the default profile, or the legacy 'configure' flow.
6
+ // It has a single caller, so per-profile catalogs fall back to one well-known profile.
7
+ const { startupConfiguration } = await loadStartupConfiguration(envInput);
8
+ let server;
9
+ let closing;
10
+ const close = () => {
11
+ closing ??= Promise.resolve().then(async () => {
12
+ process.stdin.off("end", onClose);
13
+ process.stdin.off("close", onClose);
14
+ try {
15
+ await server?.close();
16
+ }
17
+ finally {
18
+ await startupConfiguration.close();
19
+ }
20
+ });
21
+ return closing;
22
+ };
23
+ const onClose = () => {
24
+ void close().catch(() => {
25
+ process.stderr.write("Failed to close the MCP server or its catalog store.\n");
26
+ process.exitCode = 1;
27
+ });
28
+ };
29
+ try {
30
+ const defaultConfig = startupConfiguration.defaultConfig ??
31
+ (await startupConfiguration.loadProfileConfig?.(DEFAULT_PROFILE_NAME));
32
+ ({ server } = createApplication(defaultConfig));
33
+ const transport = new StdioServerTransport();
34
+ transport.onclose = onClose;
35
+ // The SDK transport handles data/errors, but does not close itself on stdin EOF.
36
+ process.stdin.once("end", onClose);
37
+ process.stdin.once("close", onClose);
38
+ await server.connect(transport);
39
+ if (process.stdin.readableEnded || process.stdin.destroyed)
40
+ await close();
41
+ }
42
+ catch (error) {
43
+ await close().catch(() => { });
44
+ throw error;
45
+ }
46
+ }
@@ -0,0 +1,254 @@
1
+ import type { CatalogHandle } from "./stores/catalog_store.js";
2
+ export type BackendKind = "elasticsearch_search" | "kibana_internal_search_es";
3
+ export type SchemaBackendKind = "elasticsearch_field_caps" | "kibana_data_views_fields" | "kibana_index_patterns_fields";
4
+ export interface FieldHint {
5
+ name: string;
6
+ type?: string;
7
+ description?: string;
8
+ aliases?: string[];
9
+ }
10
+ export interface NormalizedFieldHint extends FieldHint {
11
+ aliases: string[];
12
+ }
13
+ export interface SourceFieldDescriptor {
14
+ name: string;
15
+ type?: string;
16
+ description?: string;
17
+ aliases?: string[];
18
+ searchable?: boolean;
19
+ aggregatable?: boolean;
20
+ subfields: string[];
21
+ nested_path?: string;
22
+ object_array_path?: string;
23
+ multi_field_parent?: string;
24
+ preferred_exact_field?: string;
25
+ }
26
+ export interface SourceBackendConfig {
27
+ kind: BackendKind;
28
+ path: string;
29
+ index?: string | string[];
30
+ }
31
+ export interface SourceSchemaBackendConfig {
32
+ kind: SchemaBackendKind;
33
+ path?: string;
34
+ index?: string | string[];
35
+ }
36
+ export interface SourceDefinition {
37
+ id: string;
38
+ name: string;
39
+ description?: string;
40
+ tags: string[];
41
+ timeField: string;
42
+ backend: SourceBackendConfig;
43
+ schema?: SourceSchemaBackendConfig;
44
+ fieldHints: FieldHint[];
45
+ defaultTextFields: string[];
46
+ evidenceFields: string[];
47
+ }
48
+ export interface KibanaConnectionConfig {
49
+ baseUrl: string;
50
+ username: string;
51
+ password: string;
52
+ timeoutMs: number;
53
+ }
54
+ export interface AppConfig {
55
+ kibana: KibanaConnectionConfig;
56
+ sources: SourceDefinition[];
57
+ }
58
+ export interface ResolvedAppConfig extends AppConfig {
59
+ profileName?: string;
60
+ /** Set when the catalog of the profile lives in a catalog store rather than at a plain path. */
61
+ catalog?: CatalogHandle;
62
+ sourceCatalogPath?: string;
63
+ sourceCatalogOrigin?: "environment" | "profile" | "runtime_default" | "static_default";
64
+ }
65
+ export interface SavedProfile {
66
+ id: string;
67
+ name: string;
68
+ baseUrl: string;
69
+ timeoutMs: number;
70
+ sourceCatalogPath: string;
71
+ createdAt: string;
72
+ updatedAt: string;
73
+ }
74
+ export interface SavedProfileStoreState {
75
+ version: 1;
76
+ defaultProfileId: string | null;
77
+ profiles: SavedProfile[];
78
+ }
79
+ export interface SavedSecret {
80
+ username: string;
81
+ password: string;
82
+ }
83
+ export interface DiscoverSourceResult {
84
+ id: string;
85
+ name: string;
86
+ description?: string;
87
+ tags: string[];
88
+ time_field: string;
89
+ field_hints: NormalizedFieldHint[];
90
+ }
91
+ export interface QueryFilter {
92
+ field: string;
93
+ value: string | number | boolean;
94
+ }
95
+ export interface NestedQueryFilter extends QueryFilter {
96
+ path: string;
97
+ }
98
+ export type QueryMode = "hits" | "count" | "histogram" | "terms" | "stats" | "grouped_top_hits";
99
+ export type QuerySort = "asc" | "desc";
100
+ export interface QueryRequest {
101
+ source_ids: string[];
102
+ start_time: string;
103
+ end_time: string;
104
+ text?: string;
105
+ filters?: QueryFilter[];
106
+ nested_filters?: NestedQueryFilter[];
107
+ cursor?: string;
108
+ mode?: QueryMode;
109
+ sort?: QuerySort;
110
+ sort_by?: string;
111
+ limit?: number;
112
+ extract_nested?: boolean;
113
+ stats_field?: string;
114
+ top_hits_size?: number;
115
+ histogram_interval?: string;
116
+ group_by?: string;
117
+ }
118
+ export interface ResolvedQueryFilter extends QueryFilter {
119
+ resolved_field: string;
120
+ }
121
+ export interface ResolvedNestedQueryFilter extends NestedQueryFilter {
122
+ resolved_field: string;
123
+ query_strategy?: "nested" | "flat_object_path";
124
+ }
125
+ export interface FieldResolutionAdvisory {
126
+ kind: "preferred_exact_field" | "schema_unavailable" | "non_nested_object_array";
127
+ source_id: string;
128
+ purpose: "filter" | "sort" | "group_by";
129
+ requested_field: string;
130
+ resolved_field: string;
131
+ reason: string;
132
+ }
133
+ export interface CompiledSourceQuery {
134
+ source: SourceDefinition;
135
+ request: {
136
+ body: Record<string, unknown>;
137
+ };
138
+ resolvedFilters: ResolvedQueryFilter[];
139
+ resolvedNestedFilters: ResolvedNestedQueryFilter[];
140
+ resolvedSortBy: string;
141
+ advisories: FieldResolutionAdvisory[];
142
+ }
143
+ export interface QueryPlan {
144
+ mode: QueryMode;
145
+ startTime: string;
146
+ endTime: string;
147
+ sort: QuerySort;
148
+ sortBy?: string;
149
+ cursor?: string;
150
+ limit: number;
151
+ text?: string;
152
+ nestedFilters?: ResolvedNestedQueryFilter[];
153
+ statsField?: string;
154
+ topHitsSize?: number;
155
+ histogramInterval?: string;
156
+ groupBy?: string;
157
+ sourceIds: string[];
158
+ sourceQueries: CompiledSourceQuery[];
159
+ }
160
+ export interface KibanaSearchExecutionResult {
161
+ source: SourceDefinition;
162
+ rawResponse: Record<string, unknown>;
163
+ }
164
+ export interface NormalizedHit {
165
+ source_id: string;
166
+ timestamp: string | null;
167
+ summary: string;
168
+ document_id: string | null;
169
+ index: string | null;
170
+ selected_fields: Record<string, unknown>;
171
+ nested_matches?: Array<{
172
+ path: string;
173
+ documents: Record<string, unknown>[];
174
+ }>;
175
+ raw_document: Record<string, unknown>;
176
+ }
177
+ export interface HistogramBucket {
178
+ key: number | string;
179
+ key_as_string?: string;
180
+ count: number;
181
+ }
182
+ export interface TermsBucket {
183
+ key: string;
184
+ count: number;
185
+ }
186
+ export interface QueryStructuredResponse {
187
+ query_echo: {
188
+ source_ids: string[];
189
+ start_time: string;
190
+ end_time: string;
191
+ text?: string;
192
+ filters: ResolvedQueryFilter[];
193
+ nested_filters?: ResolvedNestedQueryFilter[];
194
+ cursor?: string;
195
+ mode: QueryMode;
196
+ sort: QuerySort;
197
+ sort_by?: string;
198
+ resolved_sort_by_by_source?: Array<{
199
+ source_id: string;
200
+ resolved_sort_by: string;
201
+ }>;
202
+ advisories?: FieldResolutionAdvisory[];
203
+ limit: number;
204
+ stats_field?: string;
205
+ top_hits_size?: number;
206
+ histogram_interval?: string;
207
+ group_by?: string;
208
+ truncated: boolean;
209
+ };
210
+ total: number;
211
+ next_cursor?: string;
212
+ hits?: NormalizedHit[];
213
+ counts_by_source?: Array<{
214
+ source_id: string;
215
+ count: number;
216
+ }>;
217
+ histograms?: Array<{
218
+ source_id: string;
219
+ buckets: HistogramBucket[];
220
+ }>;
221
+ groups?: Array<{
222
+ source_id: string;
223
+ field: string;
224
+ buckets: TermsBucket[];
225
+ }>;
226
+ stats?: Array<{
227
+ source_id: string;
228
+ field: string;
229
+ summary: {
230
+ count: number;
231
+ min: number | null;
232
+ max: number | null;
233
+ avg: number | null;
234
+ sum: number;
235
+ p50: number | null;
236
+ p95: number | null;
237
+ p99: number | null;
238
+ };
239
+ }>;
240
+ grouped_hits?: Array<{
241
+ source_id: string;
242
+ group_by: string;
243
+ buckets: Array<{
244
+ key: string;
245
+ count: number;
246
+ hits: NormalizedHit[];
247
+ }>;
248
+ }>;
249
+ }
250
+ export interface DescribeFieldsResult {
251
+ source_id: string;
252
+ total: number;
253
+ fields: SourceFieldDescriptor[];
254
+ }
@@ -0,0 +1,11 @@
1
+ import type { Readable } from "node:stream";
2
+ export interface CliIo {
3
+ stdout: (text: string) => void;
4
+ stderr: (text: string) => void;
5
+ stdin: Readable;
6
+ stdoutStream: NodeJS.WriteStream;
7
+ stdinIsTTY?: boolean;
8
+ env: NodeJS.ProcessEnv;
9
+ isStdoutOverridden: boolean;
10
+ }
11
+ export declare function createCliIo(overrides?: Partial<CliIo>): CliIo;
@@ -0,0 +1,9 @@
1
+ import type { SetupPrompter } from "../setup/setup_flow.js";
2
+ import type { CliIo } from "./cli_io.js";
3
+ export interface PromptIo extends SetupPrompter {
4
+ close(): Promise<void>;
5
+ }
6
+ export declare class PromptCancelledError extends Error {
7
+ constructor();
8
+ }
9
+ export declare function createPromptIo(io: CliIo): Promise<PromptIo>;
@@ -0,0 +1,6 @@
1
+ import type { CliIo } from "./cli_io.js";
2
+ export interface CliUi {
3
+ success: (message: string) => void;
4
+ error: (message: string) => void;
5
+ }
6
+ export declare function createCliUi(io: CliIo): CliUi;
@@ -0,0 +1,3 @@
1
+ export declare function isMissingFileError(error: unknown): error is NodeJS.ErrnoException;
2
+ export declare function readOptionalFile(path: string): Promise<string | undefined>;
3
+ export declare function writeTextAtomically(path: string, content: string): Promise<void>;
@@ -0,0 +1,30 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
3
+ import { dirname } from "node:path";
4
+ export function isMissingFileError(error) {
5
+ return Boolean(error && typeof error === "object" && "code" in error && error.code === "ENOENT");
6
+ }
7
+ export async function readOptionalFile(path) {
8
+ try {
9
+ return await readFile(path, "utf8");
10
+ }
11
+ catch (error) {
12
+ if (isMissingFileError(error))
13
+ return undefined;
14
+ throw error;
15
+ }
16
+ }
17
+ export async function writeTextAtomically(path, content) {
18
+ const temporaryPath = `${path}.${process.pid}.${randomUUID()}.tmp`;
19
+ await mkdir(dirname(path), { recursive: true });
20
+ try {
21
+ await writeFile(temporaryPath, content, {
22
+ encoding: "utf8",
23
+ mode: 0o600,
24
+ });
25
+ await rename(temporaryPath, path);
26
+ }
27
+ finally {
28
+ await rm(temporaryPath, { force: true }).catch(() => { });
29
+ }
30
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@havesomecode/kibana-mcp-server",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "license": "MIT",
5
5
  "description": "Read-only MCP server for Kibana-backed log investigation and schema-aware querying",
6
6
  "homepage": "https://havesomecode.github.io/kibana-mcp-server/",
@@ -10,6 +10,34 @@
10
10
  },
11
11
  "type": "module",
12
12
  "main": "dist/src/mcp_entry.js",
13
+ "exports": {
14
+ ".": "./dist/src/mcp_entry.js",
15
+ "./config": {
16
+ "types": "./dist/src/config/config_file.d.ts",
17
+ "default": "./dist/src/config/config_file.js"
18
+ },
19
+ "./stores/ioredis": {
20
+ "types": "./dist/src/stores/ioredis.d.ts",
21
+ "default": "./dist/src/stores/ioredis.js"
22
+ },
23
+ "./stores/mongodb": {
24
+ "types": "./dist/src/stores/mongodb.d.ts",
25
+ "default": "./dist/src/stores/mongodb.js"
26
+ },
27
+ "./stores/postgres": {
28
+ "types": "./dist/src/stores/postgres.d.ts",
29
+ "default": "./dist/src/stores/postgres.js"
30
+ },
31
+ "./stores/redis": {
32
+ "types": "./dist/src/stores/redis.d.ts",
33
+ "default": "./dist/src/stores/redis.js"
34
+ },
35
+ "./stores/sqlite": {
36
+ "types": "./dist/src/stores/sqlite.d.ts",
37
+ "default": "./dist/src/stores/sqlite.js"
38
+ },
39
+ "./package.json": "./package.json"
40
+ },
13
41
  "bin": {
14
42
  "kibana-mcp-server": "dist/src/bin/kibana-mcp-server.js"
15
43
  },
@@ -38,6 +66,9 @@
38
66
  "format:check": "biome check . --linter-enabled=false",
39
67
  "check": "npm run check:types && npm run lint && npm run format:check",
40
68
  "dev": "tsx src/mcp_entry.ts",
69
+ "docs:dev": "vitepress dev website",
70
+ "docs:build": "vitepress build website",
71
+ "docs:preview": "vitepress preview website",
41
72
  "release": "semantic-release",
42
73
  "release:dry-run": "semantic-release --dry-run",
43
74
  "setup": "node dist/src/bin/kibana-mcp-server.js setup",
@@ -55,14 +86,40 @@
55
86
  "commander": "^14.0.3",
56
87
  "zod": "^4.0.0"
57
88
  },
89
+ "peerDependencies": {
90
+ "ioredis": ">=5.0.0",
91
+ "mongodb": "^6.0.0 || ^7.0.0",
92
+ "pg": "^8.0.0",
93
+ "redis": "^6.0.0"
94
+ },
95
+ "peerDependenciesMeta": {
96
+ "ioredis": {
97
+ "optional": true
98
+ },
99
+ "mongodb": {
100
+ "optional": true
101
+ },
102
+ "pg": {
103
+ "optional": true
104
+ },
105
+ "redis": {
106
+ "optional": true
107
+ }
108
+ },
58
109
  "devDependencies": {
59
110
  "@biomejs/biome": "^1.8.3",
60
111
  "@commitlint/cli": "^20.1.0",
61
112
  "@commitlint/config-conventional": "^20.0.0",
62
113
  "@types/node": "^25.5.2",
114
+ "ioredis": "^6.0.0",
115
+ "mongodb": "^7.6.0",
116
+ "pg": "^8.23.0",
117
+ "redis": "^6.2.1",
63
118
  "semantic-release": "^25.0.1",
64
119
  "tsx": "^4.0.0",
65
120
  "typescript": "^7.0.2",
121
+ "vitepress": "^1.6.4",
122
+ "vitepress-plugin-llms": "^1.14.0",
66
123
  "vitest": "^4.1.3"
67
124
  }
68
125
  }
@@ -9,32 +9,61 @@ Use this skill when an operator wants to install, configure, or use the read-onl
9
9
 
10
10
  ## Connection-only installation
11
11
 
12
- Do not edit Codex or MCP configuration files by hand. Bootstrap establishes only the Kibana connection and an empty managed source catalog. It must not list, scan, infer, or configure indexes.
12
+ Do not edit Codex, Claude Code, or other MCP configuration files by hand. Bootstrap establishes only the Kibana connection and an empty managed source catalog. It must not list, scan, infer, or configure indexes.
13
13
 
14
14
  1. Verify Node.js 22 or newer is available.
15
15
  2. Resolve connection values from `KIBANA_BASE_URL`, `KIBANA_USERNAME`, and `KIBANA_PASSWORD`, unless the operator supplied safe alternatives.
16
- 3. Run the prompt-free bootstrap:
16
+ 3. Choose the client mode for the agent you are running in (see below).
17
+ 4. Run the prompt-free bootstrap with that mode.
18
+
19
+ ### Choosing the client mode
20
+
21
+ Bootstrap can register the MCP server itself only in Codex. Everywhere else the server is registered by a plugin or by the operator, and bootstrap must only save the connection.
22
+
23
+ | You are running in | The MCP server is registered by | Client mode |
24
+ |---|---|---|
25
+ | Codex | bootstrap | `--client codex` |
26
+ | Claude Code | the `kibana-log-investigation` plugin | `--client none` |
27
+ | Any other agent | the operator's MCP configuration | `--client none` |
28
+
29
+ Codex:
17
30
 
18
31
  ```bash
19
32
  npx -y @havesomecode/kibana-mcp-server bootstrap \
20
33
  --client codex
21
34
  ```
22
35
 
36
+ Claude Code and other agents:
37
+
38
+ ```bash
39
+ npx -y @havesomecode/kibana-mcp-server bootstrap \
40
+ --client none
41
+ ```
42
+
43
+ If you are not running in Codex and the tools of this server (`discover`, `configure_index`) are not available in the session, the server is not registered. Do not write MCP configuration yourself. Ask the operator to register it:
44
+
45
+ - in Claude Code, by installing the plugin: `/plugin marketplace add Havesomecode/kibana-mcp-server`, then `/plugin install kibana-log-investigation@kibana-mcp-server`;
46
+ - in any other agent, by declaring the stdio command `npx -y @havesomecode/kibana-mcp-server serve` in its MCP configuration.
47
+
48
+ With `--client none`, a server that was already running when bootstrap completed still has no connection. Ask the operator to reconnect the MCP server or start a new session before calling its tools.
49
+
50
+ ### What bootstrap does
51
+
23
52
  Bootstrap must:
24
53
 
25
54
  - validate the Kibana base URL and verify the connection through Kibana status only
26
55
  - write an empty managed catalog on first install
27
56
  - preserve previously and explicitly configured generated sources on idempotent reruns
28
57
  - save credentials in the operating-system credential store
29
- - register the exact, version-pinned stdio command under a transport-hashed Codex name
30
- - read the Codex registration back and reject conflicting entries
58
+ - with `--client codex`, register the exact, version-pinned stdio command under a transport-hashed Codex name, read the registration back, and reject conflicting entries
59
+ - with `--client none`, leave every MCP client configuration untouched
31
60
  - restore prior profile state if registration fails
32
61
 
33
62
  A successful command verifies installation, not an index. Do not claim an index is usable until the explicit configuration flow below succeeds.
34
63
 
35
64
  ## Supplying secrets safely
36
65
 
37
- Never put a password directly in a command argument. Prefer a pre-provisioned `KIBANA_PASSWORD` environment variable. When a secret is available on stdin, use:
66
+ Never put a password directly in a command argument. Prefer a pre-provisioned `KIBANA_PASSWORD` environment variable. The examples below use `--client codex`; substitute the client mode chosen above. When a secret is available on stdin, use:
38
67
 
39
68
  ```bash
40
69
  printf '%s\n' "$SECRET_VALUE" | npx -y @havesomecode/kibana-mcp-server bootstrap \
@@ -86,6 +115,8 @@ npx -y @havesomecode/kibana-mcp-server serve --profile prod
86
115
  - Never call `configure_index` until the user explicitly names the index or pattern.
87
116
  - Never add `--replace` merely to make a failed install pass. It intentionally replaces an existing catalog with an empty managed catalog.
88
117
  - Never overwrite a conflicting MCP client registration.
118
+ - Never write or edit an MCP client configuration file to register the server; only bootstrap in Codex, a plugin, or the operator does that.
119
+ - Never use the legacy `configure` tool to supply credentials. If it is the only setup tool offered, the server has no saved connection: run bootstrap, then have the server reconnected.
89
120
  - Never expose a password in logs, summaries, shell history, generated catalogs, or MCP client configuration.
90
121
  - If URL, username, or password is unavailable, report the exact missing prerequisite rather than inventing it.
91
122
 
@@ -1,23 +0,0 @@
1
- import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
2
- import { NoSavedProfileError, PROFILE_NAME_ENV, loadConfigFromEnvironment } from "./config.js";
3
- import { createApplication } from "./server.js";
4
- function hasBootstrapConfig(env) {
5
- return Boolean(env.KIBANA_BASE_URL && env.KIBANA_USERNAME && env.KIBANA_PASSWORD);
6
- }
7
- export async function startMcpServer(envInput = process.env) {
8
- let config = undefined;
9
- try {
10
- config = await loadConfigFromEnvironment(envInput);
11
- }
12
- catch (error) {
13
- if (hasBootstrapConfig(envInput) || envInput[PROFILE_NAME_ENV]?.trim()) {
14
- throw error;
15
- }
16
- if (!(error instanceof NoSavedProfileError)) {
17
- throw error;
18
- }
19
- }
20
- const { server } = createApplication(config);
21
- const transport = new StdioServerTransport();
22
- await server.connect(transport);
23
- }
File without changes