Thank you for your interest in contributing to the Elastic Security MCP App.
- Node.js 22+
- npm (included with Node.js)
- Elasticsearch 8.x or 9.x with Security enabled (for runtime testing)
- Kibana 8.x or 9.x (for cases, rules, and attack discovery)
git clone https://github.com/elastic/example-mcp-app-security.git
cd example-mcp-app-security
npm install
cp .env.example .env
# Edit .env and set CLUSTERS_JSON (or CLUSTERS_FILE) for your cluster(s) — see docs/setup-local.md#cluster-configurationnpm run dev # Watch mode (rebuilds server + views on change)
npm run typecheck # Type-check only (no emit)
npm run build # Full build: typecheck → tsc → Vite views
npm run build:server # Build server only (tsc)
npm run build:views # Build views only (Vite)The dev server runs on http://localhost:3001/mcp in HTTP mode. Use npm run start:stdio to test stdio transport locally.
| Path | Description |
|---|---|
main.ts |
Entry point — HTTP and stdio transport |
src/server.ts |
MCP server factory — registers all tool modules |
src/elastic/ |
Elasticsearch and Kibana API clients |
src/tools/ |
MCP tool definitions (model-facing + app-only) |
src/views/ |
React UIs (one per capability, bundled as single HTML files) |
src/shared/ |
Shared UI components, types, and utilities |
skills/ |
Claude Desktop Skills (SKILL.md per capability) |
The project supports three distribution formats. All start from the same build pipeline.
npm run buildThis runs the TypeScript compiler (type-check + emit to dist/) and builds each React view into a self-contained HTML file under dist/views/.
MCPB is a packaging format for MCP servers — a .mcpb file that users double-click to install in Claude Desktop with zero prerequisites (Node.js ships bundled with Claude Desktop).
npm run mcpb:packThis script (scripts/build-mcpb.sh) does three things:
- Runs
npm run build(TypeScript + Vite views) - Bundles the server into a single file with esbuild (
dist/main.bundle.mjs) — nonode_modulesneeded at runtime - Runs
mcpb pack .which readsmanifest.jsonand.mcpbignoreto produce the.mcpbarchive
The resulting file is example-mcp-app-security.mcpb in the repo root.
Key files:
manifest.json— MCPB spec v0.3 manifest declaring server config, user-configurable credentials, tool metadata, and compatibility.mcpbignore— controls which files are excluded from the bundle (keeps it lean by only including the esbuild bundle + views)
Single-cluster only in the manifest (for now):
manifest.jsonexposes only the three required single-cluster fields (elasticsearch_url,elasticsearch_api_key,kibana_url) and assembles them into a single-entryCLUSTERS_JSONenv var. The server itself supports multi-cluster config (CLUSTERS_JSON/CLUSTERS_FILE, seesrc/elastic/credential-client/create-credential-client.ts), but theclusters_filepicker andconfiguration_acknowledgedcheckbox have been removed from the manifest until full multi-cluster support (per-tool cluster routing, file-picker UX) lands. The MCPB spec doesn't support conditional/either-or required fields (modelcontextprotocol/mcpb#196, closednot_planned), so re-introducing a clusters-file alternative will need a different UX.
The release workflow produces two .tgz tarballs via npm pack and attaches them to the GitHub release: a version-less elastic-security-mcp-app.tgz (stable URL for docs) and a versioned elastic-security-mcp-app-<version>.tgz (for pinning). Users install via npx pointing at the tarball URL -- no npm registry publishing required.
To build a tarball locally:
npm run build
npm packThis produces elastic-security-mcp-app-<version>.tgz in the repo root. The bin, main, and files fields in package.json control what gets included.
Each skill in skills/ is packaged as an individual .zip for upload to Claude Desktop's Skills UI.
npm run skills:zipThis script (scripts/build-skill-zips.sh) iterates over skills/*/, zipping each directory that contains a SKILL.md. The resulting files are written to dist/skills/ (e.g. dist/skills/alert-triage.zip).
Releases are automated via GitHub Actions (.github/workflows/release.yml), triggered by pushing a vX.Y.Z tag. main is a protected branch, so the version bump must be merged via PR before the tag is created — otherwise the tag can end up pointing at a commit that never lands on main.
npm version patch --no-git-tag-version # or minor/major — bumps package.json + manifest.json, no commit/tag yet
git add package.json manifest.json
git commit -m "vX.Y.Z"
git push origin HEAD # push the branch and open a PR
# after the PR is merged into main:
git checkout main && git pull
git tag vX.Y.Z && git push origin vX.Y.ZThe workflow will:
- Build the project and create the esbuild bundle
- Pack the
.mcpbbundle (for Claude Desktop) - Pack the
.tgztarball (for VS Code / npx) - Build skill zips (one
.zipper skill indist/skills/) - Create a GitHub release with all files attached
- Create the Elastic API client functions in
src/elastic/ - Create the tool registration module in
src/tools/usingregisterAppToolfrom@modelcontextprotocol/ext-apps/server - Register the module in
src/server.ts - If the tool has a UI, create a new view directory under
src/views/withmcp-app.htmlandApp.tsx - Update
manifest.jsonif the tool is model-facing (add to thetoolsarray) - Run
npm run typecheckto verify
- TypeScript strict mode is enabled
- Views use React 19 with Tailwind CSS 4
- Each view is bundled into a single self-contained HTML file (no external assets)
- Tool results should be compact summaries — the UI loads full data independently via app-only tools
- When adding a Kibana API query/body param, verify the exact casing against the Zod/OpenAPI schema in the Kibana source (not by assumption) and cite the schema file in a comment
By contributing, you agree that your contributions will be licensed under the Apache-2.0 license.