npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ecubelabs/elasticsearch-mcp

v1.0.0

Published

Elasticsearch **직접 조회** MCP. ECK 클러스터의 ES HTTP 서비스로 `kubectl port-forward`를 **MCP가 스스로 열어** 붙는다. Kibana를 거치지 않고 ES REST를 직접 호출하므로 `_search` 외에 `_mapping`·`_resolve/index`·`_settings`·`_alias` 같은 비검색 API까지 쓸 수 있는 게 존재 이유다.

Readme

@ecubelabs/elasticsearch-mcp

Elasticsearch 직접 조회 MCP. ECK 클러스터의 ES HTTP 서비스로 kubectl port-forward를 MCP가 스스로 열어 붙는다. Kibana를 거치지 않고 ES REST를 직접 호출하므로 _search 외에 _mapping·_resolve/index·_settings·_alias 같은 비검색 API까지 쓸 수 있는 게 존재 이유다.

조회 도구는 전부 읽기 전용이고, 쓰기는 rollover 하나뿐이다 — 삭제/reindex/close 도구는 없다.

붙을 클러스터

서버 하나가 kube 컨텍스트 하나를 본다. --context <이름> 으로 지정하고, 생략하면 kubeconfig 의 현재 컨텍스트를 따른다(사용자가 kubectl config use-context 로 바꾸면 터널을 다시 세운다). 여러 클러스터를 쓰려면 서버를 그만큼 등록한다. 어느 클러스터였는지는 모든 응답의 context 필드에 담겨 나온다.

터널 생존 판정은 포트 열림이 아니라 실제 ES 요청(/_security/_authenticate)으로 한다 — port-forward는 파드가 재시작돼도 로컬 포트를 열어두기 때문에 포트만 보면 죽은 터널을 계속 쓰게 된다. 죽었으면 죽이고 다시 띄운다. 로컬 포트는 kubectl 이 빈 것을 골라 주고, 서버 종료 시 터널을 정리한다.

도구

| 도구 | 용도 | | -------------- | --------------------------------------------------------------------------- | | search | 인덱스 조회 — filters(정확 매칭)·query(Lucene)·페이지네이션, 실행 DSL 반환 | | get_mapping | 매핑을 필드별 { type, keyword, ignore_above } 로 평탄화 (전체 덤프 안 함) | | list_indices | 패턴 → 인덱스·별칭·data stream 해석 (_resolve/index) | | count | 문서 수 (+ groupBy 로 값 분포) | | get_document | _id 로 문서 한 건 | | rollover | 데이터스트림/별칭 롤오버 — 유일한 쓰기 도구 |

권한 롤(developer) 기준 허용 엔드포인트: <index>/_search·_mapping·_settings, /_resolve/index, /_alias. /·_cluster/health·_cat/*·_analyze는 403이라 도구로 만들지 않았다. 그래서 list_indices는 _cat/indices가 아니라 _resolve/index를 쓴다.

롤오버 (rollover)

POST /<index>/_rollover와 같다. 데이터스트림의 현재 write 인덱스를 봉인하고 새 backing 인덱스를 연다 — 기존 문서는 삭제되지 않고 봉인된 인덱스에 남아 계속 검색된다. 매핑 충돌(같은 필드가 다른 타입으로 들어와 색인이 거부됨)·필드 수 한도 초과·샤드 비대처럼 "새 인덱스에서 다시 시작"해야 풀리는 상황에 쓴다. ILM이 붙어 있으면 보통 자동이고, 이 도구는 "지금 당장 끊어야 할 때"용이다.

rollover({ index: "logs-filebeat-container", dryRun: true })          // 조건 평가만
rollover({ index: "logs-filebeat-container" })                        // 조건 없이 즉시 롤오버
rollover({ index: "logs-filebeat-container", conditions: { max_age: "7d" } })
  • conditions 생략 → 무조건 롤오버. 주면 ES가 조건을 평가해 충족할 때만 굴린다.
  • dryRun: true → 굴리지 않고 조건 평가 결과만. 권한 확인용으로 먼저 돌려볼 것.
  • index는 패턴이 아니라 이름 하나여야 한다(경로 한 세그먼트로 인코딩해 보낸다).
  • 응답은 { oldIndex, newIndex, rolledOver, dryRun, conditions }.

권한 주의: 위 developer 롤 허용 목록에 manage가 포함된다는 보장이 없다. 없으면 ES 403 POST /<index>/_rollover ...로 떨어진다. 실행 전 dryRun으로 확인하는 게 안전하다.

검색 규칙 (로그 인덱스)

logs-filebeat-ecs 등 로그 인덱스의 text 필드는 .keyword 서브필드가 전부 ignore_above: 256. 즉 256자를 넘는 값은 keyword로 색인되지 않아 term/wildcard로 못 찾는다.

  • 짧은 ID 정밀 매칭: .keyword term 사용 — trace.id.keyword(32자), http.request.id, url.path, service.name.
  • 긴 값 속 ID: message·url.query처럼 길고 URL 인코딩으로 잘리는 값은 풀-ID phrase 검색이 0건 난다. text 필드에 조각 wildcard(*23eac3ee*)를 써야 걸린다.
  • 어떤 필드에 .keyword가 있고 ignore_above가 얼마인지는 get_mapping으로 먼저 확인.

search는 실행한 실제 DSL을 항상 응답에 담고, 0건이면 위 규칙을 진단 힌트로 붙인다.

시간 범위와 정렬은 호출자가 정한다

인덱스 이름으로 성격을 추측하지 않는다. from/to 를 주지 않으면 시간 필터가 걸리지 않고, sort 를 주지 않으면 정렬 없이 돌려준다. 시계열 인덱스를 조회할 때는 둘 다 지정하는 것이 좋다 — 전 기간 스캔은 백킹 인덱스가 수백 개인 데이터스트림에서 비싸다.

search({ index: "logs-filebeat-ecs", fields: ["message"],
         from: "now-24h", sort: [{ "@timestamp": "desc" }] })

searchAfter 로 페이지를 넘길 때는 문서마다 고유한 필드를 tiebreaker 로 sort 에 포함해야 한다. 정렬 값이 겹치면 페이지 경계에서 문서가 누락되거나 중복될 수 있다.

안전장치

  • fields 사실상 필수 — _source 전체 덤프/PII 노출 방지. 전체가 필요하면 fields: ["*"]로 명시.
  • size: 기본 200, 상한 10000. 그 이상은 searchAfter 페이지네이션.
  • track_total_hits 기본 off, timeout + 선택적 terminate_after.
  • 응답이 커지면(≈256KB 초과) 전체를 임시파일로 쓰고 프리뷰만 반환.

설정

비밀은 환경변수로, 어느 클러스터를 볼지는 실행 인자로 받는다. 넣는 위치는 클라이언트마다 다르지만 모양은 같다.

| 항목 | 통로 | 비고 | | ----------------------------- | ---- | ------------------------------------ | | ES_API_KEY | env | 있으면 ApiKey 인증으로 붙는다 | | ES_USERNAME / ES_PASSWORD | env | API 키가 없을 때 Basic 인증에 쓴다 | | --context <이름> | args | 생략하면 kubeconfig 의 현재 컨텍스트 |

Claude Code — .mcp.json:

{
    "mcpServers": {
        "elasticsearch-stage": {
            "type": "stdio",
            "command": "npx",
            "args": ["-y", "@ecubelabs/elasticsearch-mcp", "--context", "ecubelabs2-stage.k8s.local"],
            "env": { "ES_USERNAME": "...", "ES_PASSWORD": "..." }
        },
        "elasticsearch-prod": {
            "type": "stdio",
            "command": "npx",
            "args": ["-y", "@ecubelabs/elasticsearch-mcp", "--context", "ecubelabs2.k8s.local"],
            "env": { "ES_USERNAME": "...", "ES_PASSWORD": "..." }
        }
    }
}

Codex — ~/.codex/config.toml:

[mcp_servers.elasticsearch-stage]
command = "npx"
args = ["-y", "@ecubelabs/elasticsearch-mcp", "--context", "ecubelabs2-stage.k8s.local"]
env = { ES_USERNAME = "...", ES_PASSWORD = "..." }

나머지 클러스터 설정은 코드에 있다

아래 값들은 ECK 배포가 정하는 사실이라 모든 사용자에게 같고, 바뀐다면 코드를 고쳐 릴리스하는 게 맞다는 판단으로 src/config.ts 에 고정돼 있다.

| 항목 | 값 | | ------------------ | ------------------------------------- | | 로컬 포트 | 열 때마다 kubectl 이 빈 포트를 고른다 | | 네임스페이스 | elastic-system | | 서비스 | elasticsearch-es-http | | 리모트 포트 / 스킴 | 9200 / http (TLS 미사용) | | kubectl | PATH 에서 찾는다 | | 반환 행 하드캡 | 10000 |

로컬 테스트

publish 전 코드를 확인할 때는 빌드 결과를 직접 띄운다.

1. 빌드

yarn workspace @ecubelabs/elasticsearch-mcp build

2. MCP 설정

레포 루트 .mcp.json에 빌드 결과를 가리키는 stage 서버가 이미 등록돼 있다. 빌드하지 않았거나 자격증명이 없으면 skynet 세션에서 이 서버가 연결에 실패한다.

{
    "mcpServers": {
        "elasticsearch": {
            "type": "stdio",
            "command": "node",
            "args": ["./packages/elasticsearch-mcp/dist/index.js", "--context", "ecubelabs2-stage.k8s.local"]
        }
    }
}

자격증명(ES_USERNAME/ES_PASSWORD 또는 ES_API_KEY)은 MCP 클라이언트를 실행하는 셸의 환경변수로 두거나, 위 항목에 env 로 넣는다.

3. 반영

코드를 고친 뒤에는 다시 빌드하고, MCP 클라이언트에서 해당 서버를 재연결해야 새 코드가 뜬다.

전제

  • kubectl이 PATH에 있고 대상 컨텍스트에 접근 가능해야 한다. 첫 port-forward는 SAML 롤 assume 때문에 몇 초 걸릴 수 있다.
  • svc/ port-forward는 kubectl이 클라이언트에서 팟으로 바꿔 주는 4단계다 — services get → pods list → pods get → pods/portforward create. 어느 단계든 권한이 빠지면 막힌다. pods/portforward는 ES 팟 이름 목록으로 한정돼 있어, 팟이 새로 뜨면 그 팟에 걸린 실행만 실패해 간헐적으로 보인다.
  • kubectl 실패는 보통 컨텍스트 이름 불일치 / 위 네 단계 중 하나의 권한 없음 / 서비스명 오타다. 에러 메시지가 무엇인지 짚어준다.
  • prod 클러스터는 실 데이터다. prod 컨텍스트를 가리키는 서버는 그 사실이 드러나는 이름으로 등록하고(예: elasticsearch-prod), 응답의 context 필드로 어디를 봤는지 확인할 것.