elastic / elastic/example-mcp-app-observability
Server starts silently when ELASTICSEARCH_URL/ELASTICSEARCH_API_KEY are missing — only fails on first tool call
- Dominant language
- TypeScript
- Stars
- 16
- Forks
- 8
- PR merge metrics
- No merged PRs in 30d
Description
## Summary
If a user runs the MCP app server without configuring `ELASTICSEARCH_URL` and `ELASTICSEARCH_API_KEY`, **the server starts up successfully and all tools register** — but every tool invocation fails with `ELASTICSEARCH_URL and ELASTICSEARCH_API_KEY environment variables are required`. The diagnosis only surfaces inside an opaque tool-error path, not at startup.
## Steps to reproduce
```bash
unset ELASTICSEARCH_URL ELASTICSEARCH_API_KEY KIBANA_URL KIBANA_API_KEY
node dist/main.js
# or
node dist/main.js --stdio
```
Server logs:
```
Elastic Observability MCP App server running on http://localhost:3001/mcp
```
…and waits for connections. Nothing in the logs indicates that no tool can actually function. When an LLM eventually calls a tool, the failure surfaces deep in the request pipeline as an unhelpful generic error.
## Expected
One of:
1. **Hard failure at startup** — `createServer()` (or `main.ts`) calls `getConfig()` and exits non-zero with a clear `Missing ELASTICSEARCH_URL / ELASTICSEARCH_API_KEY` message.
2. **Loud warning at startup** — server still starts (useful for the npx-on-first-launch path where Claude Desktop hasn't yet plumbed env vars), but logs a prominent warning that all tools will fail until creds are supplied.
3. **Skill-gap-style notice on tool failure** — wrap the missing-config exception so the tool response includes a structured `_setup_notice` pointing the user at the credential docs (analogous to the existing skill-gap pattern in `src/setup/skill-check.ts`).
Option 1 is cleanest. Option 2 is friendlier for the install paths where the server gets started before user-config substitution completes (Claude Desktop). Option 3 surfaces the diagnosis inside the host UI without crashing.
## Actual behavior
- `main.ts:42` starts the HTTP server (or stdio transport) without ever calling `getConfig()`.
- `src/server.ts` registers tools without validating creds — only checks `isKibanaConfigured()` to decide whether to register `manage-alerts`.
- The throw in `src/elastic/client.ts:40-44` only fires when a tool calls `esRequest()` → `getConfig()` for the first time.
- Result: the failure is hidden until tool-invocation time, and the error text isn't necessarily surfaced cleanly to the user (depends on host-side error rendering).
## Affected install paths
- Most acute on the npx / VS Code / Cursor paths where the user hand-edits a JSON config — easy to forget a key and see no immediate signal.
- Less common on the Claude Desktop `.mcpb` path, since Claude Desktop's `manifest.json` declares the env vars as `user_config` and prompts the user. (And the `cleanEnv` helper in `client.ts:18-22` already handles the un-substituted-placeholder case there.)
## Suggested fix area
`createServer()` in `src/server.ts` or the entry path in `main.ts` should call `getConfig()` (or a validate-only helper) at startup. For HTTP mode this can happen once at `app.listen()` time; for stdio it happens before `server.connect(transport)`.
Bonus: surface the same check via the existing `_setup_notice` infrastructure so when a user *does* hit the failure mid-conversation (e.g., creds expired), the UI banner explains it instead of the LLM having to interpret an opaque error.
## Severity
Low/Medium. Doesn't break working installs, but it makes diagnosis hard for first-time users — exactly the audience least equipped to read a stack trace.
Contributor guide
Assessment
This issue has not been assessed yet.