Metadata-Version: 2.4
Name: lumoz-mcp
Version: 0.4.2
Summary: Lumoz MCP server for observability data, signals, and problem/RCA investigation and resolution
Author-email: Lumoz AI <support@lumoz.ai>
Project-URL: Homepage, https://lumoz.ai
Keywords: mcp,observability,rca,lumoz
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Requires-Dist: mcp<2.0.0,>=1.0.0
Requires-Dist: requests>=2.28.0
Requires-Dist: python-dotenv>=1.0.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0.0; extra == "dev"
Requires-Dist: pre-commit>=3.0.0; extra == "dev"

# Lumoz MCP Server

MCP server for Lumoz observability data and RCA resolution reporting. Connect
it to Claude, Cursor, GitHub Copilot, Codex, or any other MCP-compatible
client to query traces, signals, and problems, and to drive RCA generation
and fix reporting directly from your AI tool.

## Getting an API Key

1. Log in to the [Lumoz console](https://console.lumoz.ai).
2. Go to **Settings → API Keys** (org admin required).
3. Click **Create Key**, name it (e.g. `MCP - my laptop`), and save it.
4. Copy the key shown as `client_id:client_secret` — you won't be able to see
   the secret again after closing the dialog.

The default scopes granted (`read:telemetry`, `write:telemetry`) are
sufficient for every tool in this server, including the ones that write RCA
feedback and fix reports.

## Claude / Cursor / Codex / Copilot Config

Requires [`uv`](https://docs.astral.sh/uv/getting-started/installation/)
installed locally — `uvx` runs the server without a separate install step.

```json
{
  "mcpServers": {
    "lumoz": {
      "command": "uvx",
      "args": ["lumoz-mcp"],
      "env": {
        "LUMOZ_API_KEY": "client_id:client_secret"
      }
    }
  }
}
```

Paste in the key from the step above and you're done — the server talks to
Lumoz's production API by default. Add this block to your client's MCP
config file (e.g. Claude Desktop's `claude_desktop_config.json`, or the
equivalent settings file for Cursor/Copilot/Codex), then restart the client.

## Tools

- Data: `list_apps`, `list_traces`, `search_traces`, `get_trace`, `get_trace_spans`, `get_span`
- Signals: `list_signal_definitions`, `list_signals`, `get_signal`, `list_traces_for_signal`, `list_trace_signals`
- Business Tasks: `list_business_tasks`, `list_business_task_signals`
- Problems: `list_problems`, `get_problem`, `list_problem_signals`, `generate_rca`, `report_fix`, `submit_problem_feedback`
- RCA: `list_rcas`, `get_rca`, `submit_rca_feedback`
- Eval Suggestions: `list_app_eval_suggestions`, `list_problem_eval_suggestions`, `get_problem_eval_suggestion`, `list_business_task_eval_suggestions`, `get_business_task_eval_suggestion`

Version 0.4.0 uses `app_id` and `app_name` throughout the MCP interface. Saved
tool calls from earlier versions need the tool and field names shown above.

## Inventory Discovery

Use `list_apps()` without an `environment` argument to discover all app
and environment combinations visible to the authenticated tenant. Omitting
`environment` is intentional: pages include apps across all environments.

Hosts should call this first when they need valid `app_id` and `environment`
values:

```json
{}
```

By default, each row contains only `app_id` (a string), `app_name`, and
`environment`. Results are ordered by app ID, name, then environment, with
50 rows per page (`limit` can be set up to 500). Pass `meta.next_cursor` as
`cursor` with the same environment filter to continue. Stop when
`meta.has_more` is false or you find the mapping you need. Reuse known
mappings during the conversation.

Pass `environment` to filter inventory to one environment. Use `detail="full"`
for timestamps and inventory counts. `include_summary=true` requires a specific
environment and returns full rows with summary metrics. Both options support
the same pagination.

```json
{"cursor": "<meta.next_cursor from the previous page>", "limit": 50}
```

## Trace Search

Use `search_traces(app_id, query, environment)` for natural-language
questions about trace content — "traces where the agent refused a refund",
"slow checkout flows" — that don't map to a known session/user/agent/tool/
model/Business Task value. For exact-match structured filters, use `list_traces`
instead.

```json
{
  "app_id": "123",
  "environment": "prod",
  "query": "agent refused a refund request",
  "vector_target": "response"
}
```

`vector_target` narrows which observed text is embedded and matched:

- `all` (default) — both the user's input and the agent/model's output.
- `request` — only the user's input text.
- `response` — only the agent/model's output text.

Optional exact-match filters narrow results further: `agent_name`,
`tools_called`, `models_used`, `task_name`, `outcome`, and the boolean
`had_error`. The API rejects unknown filter keys, so only pass the ones
you've actually set.

Each result carries the same rich per-trace metadata as `list_traces`
(`first_user_input`/`last_assistant_output`, token counts, `has_error`,
Business Task/outcome classification, multi-app participant metrics) plus a
`relevance_score` and `keyword_match` flag from the search ranking.

## Signal Discovery

Use `list_signals(app_id, environment)` to discover valid `signal_key`
values. Signals are backed by classifier results, but hosts should use the
signal vocabulary in tool calls.

Common flows:

```json
{
  "app_id": "123",
  "environment": "prod"
}
```

- `list_traces_for_signal(app_id, signal_key="error_detection", environment=environment)`
  lists traces where the error signal fired.
- `list_traces_for_signal(app_id, signal_key="business_task_anomaly", environment=environment)`
  lists traces matching the Business Task anomaly signal.
- `list_trace_signals(app_id, trace_id, environment)` lists all signals
  attached to one trace, including error_detection. Each signal's `details`
  usually already carries `error_message`/`error_code`/`span_id`.
- `get_span(app_id, trace_id, span_id, environment)` gets full detail
  (text fields, exception_stacktraces) for one specific span — use the
  `span_id` from a signal's `details` rather than scanning every span via
  `get_trace_spans`.

Use `list_signal_definitions()` to look up what a `signal_key`/`classifier_key`
actually means — each row has a human-readable `description`, `category`
(`builtin` or `custom`), `match_type`, and `polarity`. It returns the full
catalog (built-in signals plus this tenant's custom ones), not just signals
that have fired, so call it whenever a problem, RCA, or signal result
references a `signal_key` you need to explain to a user, e.g. while writing up
or acting on `get_rca` output. Pass `app_id`/`environment` to narrow custom
signals to one app/env; built-ins are always included.

Built-in signals also break down into `subtypes` — the specific sub-reason a
signal fired (e.g. `loop_detection` → `exact_tool_call_loop`,
`retry_storm_loop`, `reason_act_thrash`), each with its own `description` and
`default_severity`. `subtype_source_field` names which field on the signal
record (`primary_subtype` or `primary_event_key`) holds the value to match
against a subtype's `key`. When a signal record has a subtype, quote that
subtype's description instead of the classifier's general one — it explains
the actual mechanism, not just the category.

## Business Task Discovery

Use `list_business_tasks(app_id, environment)` to discover an app's
registered Business Tasks and their rollup stats for the range (`trace_count`,
success/failure/pending, tokens, p50 latency, `models`, `avg_cost_usd`,
`signal_counts`). Each row includes `task_id`, `task_name`, `task_version`,
and `task_description`. Its `task_id` scopes three other tools to that task:

- `list_traces(app_id, environment, task_id=...)` — that task's traces
  in a time window (`range`) with a cap (`limit`). `task_name` is also an
  optional name filter; `task_id` disambiguates tasks with the same name.
- `list_business_task_signals(app_id, task_id, environment)` — signal
  summaries (counts, severity, scores) for that task.
- `list_problems(app_id, environment, task_id=...)` — problems whose
  signals were detected in that task.
- `list_business_task_eval_suggestions(app_id, task_id, environment)` —
  workflow-level eval ideas generated from that workflow's traces/spans.

`list_app_eval_suggestions` also accepts `task_id`. `search_traces`
accepts `task_name` as an exact-match filter. Trace and problem results expose
`task_id` and `task_name`; nested task details use `business_task`.

```json
{
  "app_id": "123",
  "environment": "prod"
}
```

## Problem and RCA Discovery

Problems are groups of detected trace signals sharing the same signature.
Drill down progressively:

1. `list_problems(app_id, environment)` — paginated, newest-first, each
   row includes a `latest_rca` summary if one has been generated.
2. `get_problem(app_id, problem_id, environment)` — full detail, including
   every generated RCA (`rcas`) and the lifecycle/feedback audit trail (`events`).
3. `get_rca(rca_id, app_id, environment)` — the complete RCA writeup (root
   cause, evidence pattern, recommended fixes), plus the trace `signals` it
   covers and its own feedback/lifecycle audit trail (`events`).

`list_rcas(app_id, environment)` browses generated RCAs directly, across
all problems, without going through `list_problems` first.

If a problem has no RCA yet, `generate_rca(problem_id, app_id, environment)`
creates one (or returns the existing one if already generated).

## Eval Suggestion Discovery

Lumoz can generate coding-agent handoff specs for evals after RCA is available.
The problem/RCA eval suggestion tools are read-only: they fetch persisted
suggestions but do not create or regenerate them. (Business Task eval
suggestions are different — see below.)

Use `list_app_eval_suggestions(app_id, environment)` as the app-level
discovery entry point. By default it returns one flat list of summaries, each
with `problem_id` and `eval_suggestion_id` for the detail lookup. Rows retain
the title, summary, eval type, readiness, source signal, Business Task context,
and limitations. Repeated app metadata and duplicate grouped rows are omitted:

```json
{
  "app_id": "123",
  "environment": "prod",
  "classifier_key": "tool_correctness",
  "readiness": "high"
}
```

If the caller already has a problem ID from `list_problems`, `get_problem`,
`generate_rca`, or `get_rca`, use the problem-scoped tools instead:

- `list_problem_eval_suggestions(app_id, problem_id, environment)` —
  compact suggestions for one problem.
- `get_problem_eval_suggestion(app_id, problem_id, eval_suggestion_id,
  environment)` — one suggestion's full detail.

Both list tools accept `cursor` and `detail="summary"` (default) or
`detail="full"`. Pass the returned top-level `next_cursor` as `cursor`, keeping
the app, environment, problem, range, and filters unchanged. Stop when
`meta.has_more` is false. Pages use a stable suggestion order; the app-level
time window stays fixed while paging. Both tools default to 50 rows; the
maximum is 500 for the app list and 200 for the problem list.

`detail="full"` restores the previous list metadata, including
`suggestions_by_problem` on the app list. It still omits the eval spec, trace
set, and implementation prompt; fetch selected suggestions with
`get_problem_eval_suggestion` before implementing them.

The full payload includes the eval spec, trace set, implementation prompt,
target frameworks, limitations, and source signal metadata. Coding agents
should adapt this payload to the repository's existing test/eval framework
rather than assuming a fixed test harness.


Workflow eval suggestions do not require a problem or RCA. Use
`list_business_task_eval_suggestions(app_id, task_id, environment)` after
`list_business_tasks`; it returns existing active suggestions or generates them
when missing. The default `detail="summary"` returns compact rows containing
`eval_suggestion_id`, `title`, `summary`, `eval_type`, `readiness`,
`assertion_types`, and `limitations`. Readiness describes the available
implementation context, not whether an eval will pass.

Before implementing a selected suggestion, call
`get_business_task_eval_suggestion(app_id, task_id, eval_suggestion_id,
environment)` for its full spec, trace set, implementation prompt, and
provenance. Alternatively, pass `detail="full"` to the list tool to return
full payloads for every suggestion. Both tools accept `task_version`; use
the version returned by the list when fetching a selected suggestion.

## Problem and RCA Feedback

`submit_problem_feedback` and `submit_rca_feedback` record a `thumbs_up` or
`thumbs_down` vote (optionally with `note`/`reason`) against a problem or an
RCA, respectively:

```json
{
  "app_id": "123",
  "rca_id": "rca-1",
  "environment": "prod",
  "vote": "thumbs_down",
  "reason": "Recommended fix didn't address the root cause."
}
```

## RCA Fix Reporting

`report_fix` marks an existing problem resolved and records the fix
description. This is one-way — there is no unresolve/reopen action:

```json
{
  "app_id": "123",
  "problem_id": "problem-1",
  "environment": "prod",
  "description": "Added timeout handling around vector search fallback."
}
```

## Troubleshooting

- **`environment is required`** — every tool that scopes to an app needs
  an explicit `app_id`/`environment` on each call; there is no environment
  or app ID fallback. Call `list_apps()` first to find a valid pair, then pass
  both on subsequent calls.
- **`App not found`** — call `list_apps()` and use an `app_id` and `environment`
  pair from its response.
- **Client doesn't pick up the server after editing config** — most MCP
  clients only read their config file at startup; fully restart the client,
  don't just reload a window.
