Skip to content

Join keys

Find the label that names the same entity on both the Log10x side and your customer metric backend, so the two can be compared safely. Returns the best-matching label with per-candidate overlap, or "no shared label" instead of guessing. Used as a prerequisite step before the three-step cross-pillar flow (metrics_that_movedrank_by_shape_similaritymetric_overlay).

Example

You

join key for logs ↔ metrics

Log10x

Found service (matches in 87% of overlap). Also considered: pod (54%, too many gone replicas), namespace (100%, too coarse with only 3 values), tenant (no shared values in window).

More to ask

  • "re-run join keys, force refresh"
  • "why did pod lose to service?"
  • "join keys with 10m window"

Prerequisites

LOG10X_CUSTOMER_METRICS_URL configured.

Schema and samples

Input example

Real call against the demo env (captured by scripts/capture-tool-envelopes.mjs).

{
  "view": "summary"
}
Input schema

Agent-facing JSON Schema (the canonical shape the MCP server publishes via tools/list):

{
  "type": "object",
  "properties": {
    "force_refresh": {
      "type": "boolean",
      "default": false,
      "description": "When true, bypass the session cache and re-run the Jaccard pass against the live backends."
    },
    "minimum_jaccard": {
      "type": "number",
      "minimum": 0,
      "maximum": 1,
      "default": 0.7,
      "description": "Minimum Jaccard similarity to accept as a primary join. Default 0.7. Lower to 0.5 for exploratory discovery, 0.3 for noisy environments with historical stale values."
    },
    "candidate_labels": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Optional subset of customer-side labels to probe. When omitted, all labels from the customer backend are probed in preferred-first order."
    },
    "window": {
      "type": "string",
      "description": "Time window for label value enumeration (e.g., \"10m\", \"1h\", \"30m\"). When set, both the Log10x and customer backends are queried with [now - window, now] filtering, excluding stale label values from series that stopped emitting samples. CRITICAL for environments with historical replay data, decommissioned pods, or otherwise orphan label values — stale values drag Jaccard down and cause false `no_join_available` refusals. Recommended: \"10m\" for steady-state clusters, \"1h\" for bursty traffic. Omit to include all-time values (default Prometheus behavior). Alias: `timeRange`."
    },
    "timeRange": {
      "type": "string",
      "description": "Alias for `window` for consistency with other Log10x tools."
    },
    "environment": {
      "type": "string",
      "description": "Environment nickname (for multi-env setups)."
    }
  },
  "additionalProperties": false
}

Source: src/tools/discover-join.ts.

Output example

Real envelope from the demo env. view: "summary" returns the full StructuredOutput with typed data. Long arrays + base64 PNG bodies trimmed for readability; the real call returns them in full.

Headline (the 1-line agent-facing answer):

Join key: k8s_namespace ↔ k8s_namespace (Jaccard 1.000, 5 runner-ups).

{
  "schema_version": "1.0",
  "schema_epoch": "2026-05-25",
  "tool": "log10x_discover_join",
  "generated_at": "2026-05-26T15:38:01.892Z",
  "view": "summary",
  "summary": {
    "headline": "Join key: k8s_namespace ↔ k8s_namespace (Jaccard 1.000, 5 runner-ups)."
  },
  "data": {
    "status": "joined",
    "backend": "log10x",
    "endpoint": "https://prometheus.log10x.com",
    "cached": true,
    "labels_probed_log10x": [
      "tenx_user_service",
      "k8s_pod",
      "k8s_namespace",
      "... 3 more elided"
    ],
    "labels_probed_customer": [
      "TENX_Tenant",
      "http_code",
      "http_message",
      "... 18 more elided"
    ],
    "join_key": {
      "log10x_side": "k8s_namespace",
      "customer_side": "k8s_namespace",
      "jaccard": 1,
      "shared_values": 1,
      "log10x_only_values": 0,
      "customer_only_values": 0
    },
    "runner_ups": [
      {
        "log10x_side": "severity_level",
        "customer_side": "severity_level",
        "jaccard": 0.8333333333333334,
        "shared_values": 5,
        "log10x_only_values": 0,
        "customer_only_values": 1
      },
      {
        "log10x_side": "tenx_user_service",
        "customer_side": "k8s_container",
        "jaccard": 0.7391304347826086,
        "shared_values": 17,
        "log10x_only_values": 0,
        "customer_only_values": 6
      },
      {
        "log10x_side": "tenx_user_service",
        "customer_side": "tenx_user_service",
        "jaccard": 0.7391304347826086,
        "shared_values": 17,
        "log10x_only_values": 0,
        "customer_only_values": 6
      },
      "... 2 more elided"
    ],
    "top_below_threshold": []
  },
  "actions": [
    {
      "tool": "log10x_metrics_that_moved",
      "args": {
        "anchor_type": "log10x_pattern"
      },
      "reason": "step 1 of cross-pillar flow: find which customer metrics moved with a log10x pattern"
    },
    {
      "tool": "log10x_metrics_that_moved",
      "args": {
        "anchor_type": "customer_metric"
      },
      "reason": "reverse direction: find which log10x patterns moved with a customer metric"
    }
  ],
  "truncated": false,
  "warnings": []
}
Output schema

The data block inside the StructuredOutput envelope:

interface ToolData {
  status: string;
  backend: string;
  endpoint: string;
  cached: boolean;
  labels_probed_log10x: string[];
  labels_probed_customer: string[];
  join_key: { log10x_side: string; customer_side: string; jaccard: number; shared_values: number; log10x_only_values: number; customer_only_values: number };
  runner_ups: Array<{
    log10x_side: string;
    customer_side: string;
    jaccard: number;
    shared_values: number;
    log10x_only_values: number;
    customer_only_values: number;
  }>;
  top_below_threshold: unknown[];
}

Envelope-level fields the agent should also read: summary.headline (1-line answer), actions[] (next-call chain hints as {tool, args, reason}), truncated: boolean, images[] (PNG attachments where applicable), schema_epoch (engine-ID stability boundary).