@havesomecode/kibana-mcp-server 1.3.1 → 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.
- package/README.md +54 -490
- package/dist/src/bin/kibana-mcp-server.d.ts +2 -0
- package/dist/src/bin/run_cli.d.ts +4 -0
- package/dist/src/bin/run_cli.js +1 -1
- package/dist/src/commands/context.d.ts +18 -0
- package/dist/src/commands/serve.d.ts +4 -0
- package/dist/src/commands/serve.js +30 -6
- package/dist/src/commands/setup.d.ts +3 -0
- package/dist/src/commands/setup.js +2 -2
- package/dist/src/config/config_file.d.ts +59 -0
- package/dist/src/config/config_file.js +76 -0
- package/dist/src/config/environment.d.ts +100 -0
- package/dist/src/{config.js → config/environment.js} +26 -24
- package/dist/src/config/startup.d.ts +31 -0
- package/dist/src/config/startup.js +86 -0
- package/dist/src/kibana/client.d.ts +28 -0
- package/dist/src/kibana/schema_catalog.d.ts +11 -0
- package/dist/src/{schema_catalog.js → kibana/schema_catalog.js} +33 -11
- package/dist/src/kibana/source_catalog.d.ts +10 -0
- package/dist/src/mcp_entry.d.ts +2 -0
- package/dist/src/mcp_entry.js +1 -1
- package/dist/src/profiles/context.d.ts +4 -0
- package/dist/src/profiles/context.js +15 -0
- package/dist/src/profiles/names.d.ts +10 -0
- package/dist/src/profiles/names.js +35 -0
- package/dist/src/profiles/paths.d.ts +9 -0
- package/dist/src/profiles/runtime.d.ts +43 -0
- package/dist/src/profiles/runtime.js +150 -0
- package/dist/src/profiles/secret_store.d.ts +20 -0
- package/dist/src/profiles/state_lock.d.ts +13 -0
- package/dist/src/{state_lock.js → profiles/state_lock.js} +14 -5
- package/dist/src/profiles/store.d.ts +34 -0
- package/dist/src/{profile_store.js → profiles/store.js} +7 -5
- package/dist/src/query/compiler.d.ts +9 -0
- package/dist/src/query/cursor.d.ts +8 -0
- package/dist/src/query/normalize.d.ts +2 -0
- package/dist/src/server.d.ts +29 -0
- package/dist/src/server.js +40 -50
- package/dist/src/setup/bootstrap.d.ts +50 -0
- package/dist/src/{bootstrap.js → setup/bootstrap.js} +10 -36
- package/dist/src/setup/client_registration.d.ts +38 -0
- package/dist/src/setup/setup_flow.d.ts +26 -0
- package/dist/src/{setup_flow.js → setup/setup_flow.js} +6 -6
- package/dist/src/stores/catalog_store.d.ts +38 -0
- package/dist/src/stores/catalog_store.js +22 -0
- package/dist/src/stores/file.d.ts +5 -0
- package/dist/src/stores/file.js +48 -0
- package/dist/src/stores/ioredis.d.ts +21 -0
- package/dist/src/stores/ioredis.js +33 -0
- package/dist/src/stores/mongodb.d.ts +42 -0
- package/dist/src/stores/mongodb.js +57 -0
- package/dist/src/stores/postgres.d.ts +24 -0
- package/dist/src/stores/postgres.js +65 -0
- package/dist/src/stores/redis.d.ts +24 -0
- package/dist/src/stores/redis.js +31 -0
- package/dist/src/stores/shared.d.ts +25 -0
- package/dist/src/stores/shared.js +88 -0
- package/dist/src/stores/sqlite.d.ts +26 -0
- package/dist/src/stores/sqlite.js +63 -0
- package/dist/src/tools/configure.d.ts +59 -0
- package/dist/src/tools/configure.js +1 -1
- package/dist/src/tools/configure_index.d.ts +35 -0
- package/dist/src/tools/configure_index.js +75 -66
- package/dist/src/tools/describe_fields.d.ts +29 -0
- package/dist/src/tools/discover.d.ts +27 -0
- package/dist/src/tools/filter.d.ts +180 -0
- package/dist/src/tools/query.d.ts +181 -0
- package/dist/src/transports/http.d.ts +15 -0
- package/dist/src/transports/http.js +291 -0
- package/dist/src/transports/stdio.d.ts +1 -0
- package/dist/src/transports/stdio.js +46 -0
- package/dist/src/types.d.ts +254 -0
- package/dist/src/utils/cli_io.d.ts +11 -0
- package/dist/src/utils/cli_prompts.d.ts +9 -0
- package/dist/src/utils/cli_ui.d.ts +6 -0
- package/dist/src/utils/fs.d.ts +3 -0
- package/dist/src/utils/fs.js +30 -0
- package/package.json +58 -1
- package/skills/kibana-log-investigation/SKILL.md +36 -5
- package/dist/src/mcp_runtime.js +0 -23
- /package/dist/src/{kibana_client.js → kibana/client.js} +0 -0
- /package/dist/src/{source_catalog.js → kibana/source_catalog.js} +0 -0
- /package/dist/src/{profile_paths.js → profiles/paths.js} +0 -0
- /package/dist/src/{secret_store.js → profiles/secret_store.js} +0 -0
- /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,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
|
+
"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.
|
|
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
|
-
-
|
|
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
|
|
package/dist/src/mcp_runtime.js
DELETED
|
@@ -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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|