yokozuna-mcp
An MCP server that gives coding agents programmatic access to Sumo Logic logs via the Search Job API, enabling issue triage in preview deployments and production without leaving the editor.
README
yokozuna-mcp
<img src="https://raw.githubusercontent.com/mbe24/yokozuna-mcp/main/assets/logo.png" alt="yokozuna-mcp logo: a levitating, meditating sumo inside an enso circle, orbited by log lines" width="160" align="right">
An MCP (Model Context Protocol) server that gives coding agents (Claude Code, Claude Desktop, …) programmatic access to Sumo Logic logs via the Search Job API — so issues in preview deployments and production can be found and triaged without leaving the editor.
- Transport: stdio (local, per-developer; credentials via env vars)
- Deployment default: EU (
api.eu.sumologic.com), configurable - Token-economical by default: lean output with explicit levers (
detail,fields,dedupe), bulk data goes to files instead of your context window
This README is self-sufficient: following it top-to-bottom from a checkout of this repo yields a working, registered MCP server.
Prerequisites
- Node.js >= 20 (
node --versionto check) and npm. Node 20+ is required for the built-infetchand fornode --env-file. - A Sumo Logic Enterprise account (the Search Job API is Enterprise-only).
- A Sumo Logic access ID / access key pair, ideally for a dedicated read-only
service account whose role has these capabilities:
- Data Management → Download Search Results
- Data Management → View Collectors
- optionally Alerting → View Monitors (only needed for
sumo_list_monitors) - plus search access to the indexes/partitions holding your preview & prod logs.
- Know your deployment region (e.g.
eu). If unsure, start witheu— a wrong region produces an HTTP 301 error that names the correct one (see Troubleshooting).
Step 1 — Install and build
Once the package is published to the npm registry, no checkout is needed —
npx yokozuna-mcp fetches and runs it directly (see launch form 4 in Step 4).
Until then (or to run from source), build locally from this repo:
cd <path-to-this-repo> # e.g. C:/Users/you/Development/yokozuna
npm install
npm run build
The build produces dist/index.js (the server entry point). Note its absolute path
(e.g. C:/Users/you/Development/yokozuna/dist/index.js) — you need it for client config.
Optionally, put a yokozuna-mcp binary on your PATH (enables launch form 3 below):
npm link
Step 2 — Provide credentials (environment variables)
IMPORTANT: the server does NOT read a
.envfile by itself. Environment variables must reach the server process via one of:
- the
envblock of your MCP client config (recommended — see Step 4),- exported shell variables, or
node --env-file=.env dist/index.js(Node's built-in flag; used by the smoke test below). Copy.env.exampleto.envand fill in the two required values for this.
| Var | Required | Default | Notes |
|---|---|---|---|
SUMO_ACCESS_ID |
yes | — | Access ID. |
SUMO_ACCESS_KEY |
yes | — | Access key. Never logged or echoed. |
SUMO_DEPLOYMENT |
no | eu |
One of au,ca,ch,de,eu,fed,in,jp,kr,us1,us2. |
SUMO_ENDPOINT |
no | derived | Explicit API base URL override (e.g. https://api.eu.sumologic.com/api/); accepts host with/without /api/; https only. Takes precedence over SUMO_DEPLOYMENT. |
SUMO_UI_BASE_URL |
no | service.<code>.sumologic.com |
UI origin for "open in Sumo UI" deep links only (not the API). Set to your company host, e.g. https://<org>.<deployment>.sumologic.com, so links match your actual UI. |
SUMO_DEFAULT_TIMEZONE |
no | UTC |
IANA tz used when a tool call omits timeZone. |
SUMO_DEFAULT_SOURCE_CATEGORY |
no | — | Embedded into tool descriptions as the recommended query prefix. Queries are never mutated. |
YOKOZUNA_EXPORT_DIR |
no | OS temp dir | Where sumo_export_results writes NDJSON files. |
YOKOZUNA_DEFAULT_DETAIL |
no | compact |
summary | compact | full | raw. |
YOKOZUNA_DEFAULT_LIMIT |
no | 100 |
Default inline result limit. |
YOKOZUNA_MAX_MESSAGE_CHARS |
no | 10000 |
Safety cap for the message field. |
YOKOZUNA_LEVEL_EXPR |
no | log.levelname |
JSON path (inside _raw) of the log level — used by detail: "summary" side-aggregates, sumo_error_digest, and the levelname facet. |
YOKOZUNA_SETTLE_MARGIN_SECONDS |
no | 180 |
sumo_new_since freshness lag: poll windows end at now − margin so late-arriving logs are never skipped (complete but that many seconds stale). |
YOKOZUNA_FACET_DIMENSIONS |
no | _sourcecategory,_sourcehost,levelname,status,path |
Default sumo_facets dimensions (comma-separated). _-prefixed = native fields; anything else is parsed as log.<dimension>. |
YOKOZUNA_MAX_RESPONSE_CHARS |
no | 200000 |
Whole-response safety cap (chars) for inline tool results. Oversized responses are tail-truncated with a pointer to sumo_export_results; header/count lines come first and always survive. |
YOKOZUNA_KEEPALIVE_IDLE_MINUTES |
no | 10 |
Minutes a kept job (sumo_create_search_job / keepJob: true) may sit idle before the server deletes it. Any access (status/messages/records) resets the timer. |
YOKOZUNA_KEEPALIVE_MAX_JOBS |
no | 20 |
Max jobs the keepalive tracks at once; beyond the cap the stalest job is evicted (logged to stderr with the job id) and left to expire server-side. |
See .env.example for a commented template. Never commit a filled-in .env.
Step 3 — Smoke-test the server (optional but recommended)
With a filled-in .env in the repo root, this sends a real MCP handshake plus
tools/list over stdio and exits. Expect a tools array with 12 tools on stdout and
only [yokozuna-mcp] server started (stdio) on stderr.
bash / Git Bash:
printf '%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"smoke","version":"0"}}}' \
'{"jsonrpc":"2.0","method":"notifications/initialized"}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
| node --env-file=.env dist/index.js
PowerShell:
@'
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"smoke","version":"0"}}}
{"jsonrpc":"2.0","method":"notifications/initialized"}
{"jsonrpc":"2.0","id":2,"method":"tools/list"}
'@ | node --env-file=.env dist/index.js
If instead the process exits immediately with Missing required environment variable(s),
the env vars did not reach the process — see Step 2.
Step 4 — Register in an MCP client
Three equivalent local launch forms (all require Step 1 first):
- Direct node (most explicit):
"command": "node", "args": ["<abs>/dist/index.js"] - npx with a local path (no registry involved):
"command": "npx", "args": ["<abs-project-dir>"] - Linked bin (after
npm link):"command": "yokozuna-mcp"or"command": "npx", "args": ["yokozuna-mcp"]
Plus one registry form (works once the package is published to npm — no checkout or build needed):
- npx from the registry:
"command": "npx", "args": ["-y", "yokozuna-mcp"]
On Windows, use forward slashes (C:/Users/...) or escaped backslashes (C:\\Users\\...)
in JSON — never single backslashes.
Claude Code
One command (replace the path and credentials):
claude mcp add yokozuna --env SUMO_ACCESS_ID=suXXXX --env SUMO_ACCESS_KEY=<key> --env SUMO_DEPLOYMENT=eu -- node C:/Users/you/Development/yokozuna/dist/index.js
By default this registers for the current project only; add --scope user to make it
available in every project. Alternatively, create/extend .mcp.json in the root of the
project where you want to use it (shared, project scope):
{
"mcpServers": {
"yokozuna": {
"command": "node",
"args": ["C:/Users/you/Development/yokozuna/dist/index.js"],
"env": {
"SUMO_ACCESS_ID": "suXXXX",
"SUMO_ACCESS_KEY": "<key>",
"SUMO_DEPLOYMENT": "eu"
}
}
}
}
Verify with claude mcp list — yokozuna should show as connected. In a Claude Code
session, /mcp shows the server, its 12 tools, and the triage prompt
(available as /mcp__yokozuna__triage).
Do not commit a
.mcp.jsoncontaining the real access key to a shared repo — preferclaude mcp add(local scope) or user-level config for real credentials.
Claude Desktop
Edit claude_desktop_config.json (create it if missing):
- Windows:
%APPDATA%\Claude\claude_desktop_config.json - macOS:
~/Library/Application Support/Claude/claude_desktop_config.json
The npx <local-path> or linked-bin forms avoid brittle absolute dist paths:
{
"mcpServers": {
"yokozuna": {
"command": "npx",
"args": ["C:\\Users\\you\\Development\\yokozuna"],
"env": {
"SUMO_ACCESS_ID": "suXXXX",
"SUMO_ACCESS_KEY": "<key>",
"SUMO_DEPLOYMENT": "eu"
}
}
}
}
Then fully quit and restart Claude Desktop (system-tray quit on Windows, Cmd-Q on macOS — closing the window is not enough). The tools appear under the tools icon in the chat input.
Tools
| Tool | Purpose | Key inputs |
|---|---|---|
sumo_run_search |
Workhorse. Create job → wait → fetch first N results → auto-delete. One call for the common case. | query, time range, limit (≤5000), detail, fields, dedupe, sort, format, extract, keepJob |
sumo_create_search_job |
Primitive: create only, returns the job id (kept alive by the server in the background). | query, time range |
sumo_get_search_job_status |
Primitive: poll state/counts. | id |
sumo_get_messages |
Primitive: page raw messages (non-aggregate jobs). | id, offset, limit (≤10000), detail, fields, sort, … |
sumo_get_records |
Primitive: page aggregate records (aggregate jobs). | id, offset, limit (≤10000), format |
sumo_delete_search_job |
Primitive: delete a job (idempotent; already-gone is OK). | id |
sumo_export_results |
Run a search and stream all results (up to 100k) to an NDJSON file; returns the path, not the payload. Lines are chronological (oldest→newest by _messagetime; the server appends | sort by _messagetime asc to non-aggregate queries — a PARTIAL/timed-out export may not be fully ordered). |
query, time range, maxMessages, extract |
sumo_facets |
See the shape first: one count by aggregate per dimension (concurrent, auto-deleted), ranked top-N table each. _-prefixed dims are native fields; others parse log.<dim> from _raw. One failing dimension = an error line, never a total failure. |
query, time range, dimensions, limit (top-N per dim, default 15) |
sumo_error_digest |
One-call triage: filter to ERROR/WARNING (via YOKOZUNA_LEVEL_EXPR), group by normalized signature, return top-N with count, first/last seen, a sample request_id, and _sourcecategory. |
query (default _sourcecategory=<SUMO_DEFAULT_SOURCE_CATEGORY>), time range, levels, limit, maxScan (default 5000) |
sumo_new_since |
Stateless monitoring cursor: returns messages that arrived since the last call plus a cursor=<epoch ms> line; pass it back as since for contiguous, gap-free windows. byReceiptTime forced true; aggregate queries rejected. |
query, since (cursor), lookback (baseline, default "15m"), limit, detail/fields/dedupe/sort/format |
sumo_trend |
See when things happened: | timeslice counts per bucket split into series (default: log level via YOKOZUNA_LEVEL_EXPR), rendered as one sparkline + per-bucket counts per series. One aggregate job, auto-deleted. |
query (plain scope, no | aggregates), time range, interval (default auto ≤40 buckets), by (levelname | _native field | none | log.<field> name), maxSeries (default 8) |
sumo_list_monitors |
Read-only list of the org's native Sumo Monitors (24/7 prod alerting): name, folder path, type, enabled/disabled, current status, trigger types, notification destinations. Requires the View Monitors capability (clear error otherwise); no search jobs involved. | query (name/content filter), limit (default 100) |
Time range (all search tools): exactly one of last ("15m", "2h", "1d"; units
s/m/h/d) or both from+to (ISO-8601 like 2026-07-02T18:28:00, or epoch ms). Optional
timeZone (IANA) and byReceiptTime (recommended true for very recent windows).
Example sumo_run_search call (the common case):
{
"query": "_sourcecategory=kubernetes/myservice/*/backend \"myservice-preview.dev.example.com\"",
"last": "15m",
"byReceiptTime": true,
"detail": "compact",
"limit": 100
}
Example sumo_export_results call (bulk to file, not to context; the file comes out
oldest→newest so a coding agent can read it as a chronological trace):
{
"query": "_sourcecategory=kubernetes/myservice/*/backend | json field=_raw \"log.levelname\" as levelname nodrop | where levelname in (\"ERROR\",\"WARNING\")",
"last": "2h"
}
The optional extract parameter (also on sumo_run_search) pulls extra JSON fields
out of _raw without hand-writing the clauses — {"status": "log.status"} appends
| json field=_raw "log.status" as status nodrop server-side (one chained clause per
field — the comma multi-extract form is broken in Sumo). Aliases must be simple
identifiers; non-aggregate queries only.
Example sumo_trend call (when did it start/spike? — one sparkline per level):
{
"query": "_sourcecategory=kubernetes/myservice/*/backend",
"last": "2h",
"byReceiptTime": true
}
trend by levelname: 2026-07-03T16:00:00.000Z .. 2026-07-03T18:00:00.000Z, interval=5m, buckets=24 (…)
INFO total=53210 ▅▅▆▅▅▅▄▅▅█▅▅▅▄▅▅▅▅▅▅▄▅▅▅ [2226 2221 …]
ERROR total=12 ▁▁▁▁▁▁▁▁▁█▁▁▂▁▁▁▁▁▁▁▁▁▁▁ [0 0 …]
interval defaults to the smallest nice step (10s…1d) giving ≤40 buckets; by
accepts levelname (default; parsed via YOKOZUNA_LEVEL_EXPR), a _native field
(e.g. _sourcecategory), any log.<field> name (e.g. status), or none for one
total series.
Example sumo_facets call (see the shape before reading messages — where do matching
logs come from, which levels/statuses/paths dominate):
{
"query": "_sourcecategory=kubernetes/myservice/backend",
"last": "30m",
"byReceiptTime": true
}
Default dimensions: _sourcecategory, _sourcehost, levelname, status, path
(override per call with dimensions, or globally with YOKOZUNA_FACET_DIMENSIONS).
Each dimension is one small concurrent aggregate job, auto-deleted; a dimension that
fails renders as an error line without failing the rest.
Example sumo_error_digest call (deduplicated "what is broken" summary — counts,
first/last occurrence, and a sample request_id per distinct problem):
{
"query": "_sourcecategory=kubernetes/myservice/*/backend",
"last": "2h",
"levels": ["ERROR", "WARNING"],
"limit": 20
}
Omit query to fall back to _sourcecategory=<SUMO_DEFAULT_SOURCE_CATEGORY>. The level
filter is appended automatically (parsing YOKOZUNA_LEVEL_EXPR, default log.levelname)
— pass only the scope, no | operators.
Example sumo_new_since polling loop (stateless monitoring — e.g. "tell me when new
errors show up in the preview deployment"):
-
First call — baseline (no
since):{ "query": "_sourcecategory=kubernetes/myservice/*/backend \"myservice-preview.dev.example.com\"", "lookback": "15m" }The response starts with a header plus a cursor line like
cursor=1783017420000. -
Every following call passes the last cursor as
since:{ "query": "_sourcecategory=kubernetes/myservice/*/backend \"myservice-preview.dev.example.com\"", "since": 1783017420000 }Windows are half-open
[since, now − settle margin)over receipt time (byReceiptTimeis forced), so consecutive calls tile with no gaps and no duplicates — the server keeps no state; the cursor in your hand is everything.Freshness note: results trail real time by the settle margin (default 180 s,
YOKOZUNA_SETTLE_MARGIN_SECONDS) so logs that arrive late are not skipped — complete but ~3 minutes stale. Polling faster than the margin just returns a "not settled yet" note with the cursor unchanged (no job is created).In Claude Code this pairs well with
/loop, e.g.:/loop 5m check sumo_new_since for new backend errors (keep passing the returned cursor as since) and summarize anything new. Aggregate queries (| count …) are rejected — usesumo_run_searchfor those.
Example primitive flow (only needed when paging beyond one call):
sumo_create_search_job {"query": "...", "last": "1h"} → returns id=XXX →
sumo_get_search_job_status {"id": "XXX"} until DONE GATHERING RESULTS →
sumo_get_messages {"id": "XXX", "offset": 0, "limit": 100} (or sumo_get_records for
aggregate queries) → sumo_delete_search_job {"id": "XXX"} when done.
Successful searches include a Sumo UI deep link
(https://service.eu.sumologic.com/log-search/create?...) to open the same query in the
browser.
sumo_list_monitors (read-only) lists the org's native Sumo Monitors for discovering
what 24/7 prod alerting already exists — name, folder path, type, enabled/disabled,
current status, trigger types, and notification destinations. It needs the
View Monitors capability on the access key and creates no search jobs.
MCP prompt: triage
The server also registers a triage MCP prompt (in Claude Code:
/mcp__yokozuna__triage, with an optional problem argument). It encodes the
recommended workflow — shape first (sumo_facets/sumo_trend/detail:"summary") →
narrow (sumo_error_digest, then compact reads) → trace (quoted request_id,
no other filters) → bulk export — plus the full query cookbook (severity filtering
via log.levelname, the hostname-keyword caveat, noise exclusion, extract, and
byReceiptTime). The cookbook lives in the prompt rather than in every tool
description to keep per-call token cost down.
Token economy
Log messages are huge (~33 metadata fields + a nested JSON log object). The tools are
lean by default and give the agent explicit levers:
detail—summary(exact whole-job per-level counts via a side aggregate, compact histogram sparkline, top message signatures; sections computed from the fetched page are labeled— sample— cheapest) ·compact(default: timestamp, level,request_id,_sourcecategory, the fullmessage, plusmethod/path/statuswhen present on request logs) ·full(compact +duration_s/logger/client_ip) ·raw(verbatim_raw— returns logs exactly as the application emitted them, including anything sensitive the app logged).fields— explicit projection from the flattened namespace (level/request_idare always kept for cross-referencing).dedupe— group repeated messages globally by (level, normalized signature): timestamps, UUIDs, hex runs and numbers are normalized away, so the same log statement with varying values collapses intofirst_ts..last_ts LEVEL ×N message.sort—asc(default: oldest→newest, best for tracing) ordescby_messagetime. Client-side: it orders only the returned result set — raiselimitor narrow the query for full ordering. Not applicable to aggregate records.maxMessageChars— safety cap only;messageis never truncated by default.limit— inline max 5000; anything bulk belongs insumo_export_results(file, not context).- Whole-response cap — independent of the levers above, no inline response exceeds
YOKOZUNA_MAX_RESPONSE_CHARS(default 200k chars): the tail is truncated with a note pointing at narrowing orsumo_export_results. Header/count/cursor lines come first in every tool's output, so they survive truncation.
Recommended triage pattern: detail:"summary" to see the shape → narrow the query →
compact to read messages → full/raw on the few that matter → sumo_export_results
when a coding agent should chew through everything.
Example queries (the real workflow)
How to scope a search (read this first). Free-text terms match the raw log text
(_raw), so guessing at source-code identifiers — Java/Python class names, function names —
is unreliable and usually matches nothing. Scope and filter using these instead:
- Where:
_sourcecategory=<path>(e.g.kubernetes/myservice/*/backend) — the primary scoping dimension. Discover categories from the[in brackets]in any result line, or with| count by _sourcecategory. - Which environment: add the deployment hostname as a keyword (matches
request_url) — but see the request-logs-only caveat below. - Severity: parse
log.levelname(see #2) — not a class name, not_loglevel. - A specific request/entity: the
request_id(or any correlation key your logs carry, e.g.session_id,client_ip) as a quoted keyword (see #3).
Backend logs here are structured JSON (_raw = {stream, timestamp, log:{…}}); the fields
worth filtering on are log.levelname, log.request_id, log.status, log.path,
log.logger, log.pathname — parse them with | json field=_raw "log.<field>" as <alias>.
-
Search by preview deployment URL / hostname keyword (wait a few minutes for ingestion; use
byReceiptTime: truefor very recent windows):_sourcecategory=kubernetes/myservice/*/backend "myservice-preview.dev.example.com"Caveat: a hostname keyword matches only request logs (the hostname lives in
log.request_url) — startup/Celery/Redis lines and most errors/exceptions carry no hostname and get excluded (live-verified: a host-keyword error search returned 0 while the same search by_sourcecategoryfound them). Hunt errors by_sourcecategory, never by hostname keyword. -
Errors/warnings — note
stream:"stderr"is NOT the error signal; the reliable level islog.levelnameparsed from_raw:_sourcecategory=kubernetes/myservice/*/backend | json field=_raw "log.levelname" as levelname nodrop | where levelname in ("ERROR","WARNING")Do not filter on the top-level
_loglevel: it is often empty on warnings (~78% observed) and usesWARNwherelevelnamesaysWARNING, so| where _loglevel in ("ERROR","WARN")silently misses most warnings.Count by level (aggregate):
_sourcecategory=kubernetes/myservice/*/backend | json field=_raw "log.levelname" as levelname nodrop | count by levelnameExclude noise with negation:
_sourcecategory=kubernetes/myservice/*/backend !"health check" -
Cross-reference / trace one request: take the
req=id from any result line and run a new search with just the quoted id and NO source/host filter — ids are full-text indexed even though they live inside_raw:"74ec29d7-3420-41f9-8a71-4d91f0b263a6"Other correlation keys (e.g.
session_id,client_ip) work the same way. Results are returned oldest→newest by default (sort: "asc"), so the trace reads chronologically. Remember the hostname caveat above: don't add a hostname keyword when the trace may include errors. -
Export everything for a coding agent: call
sumo_export_resultswith the same query and a wider window — it streams flattened NDJSON to a file (chronological, oldest→newest) and returns the path.
Limits & troubleshooting
- Server exits with
Missing required environment variable(s)— the env vars did not reach the process. The server does not read.envon its own: set them in the MCP client'senvblock, export them in the shell, or launch vianode --env-file=.env. - HTTP 401 — wrong
SUMO_ACCESS_ID/SUMO_ACCESS_KEY(or the key was revoked). Keys are region-bound: also checkSUMO_DEPLOYMENT. - HTTP 301 = wrong deployment endpoint — the error message names the correct
SUMO_DEPLOYMENT(parsed from the redirectLocation). - HTTP 404 on a job = the job expired server-side, was cancelled, or was deleted — re-create it. Job ids do not survive a server restart (the keepalive registry and session state are in-memory).
- 100,000 messages max per search (
FORCE PAUSEDstate = a non-aggregate query hit the cap; results are available but truncated — split the time range). - 10,000 per page max; requesting more silently returns exactly 10,000.
- Rate limits: 4 requests/s, 10 concurrent per key, 200 active search jobs per org. The server rate-limits itself and always deletes finished jobs.
- Aggregate vs non-aggregate: records ↔ messages are mutually exclusive per job; a mismatched fetch returns 400. The tools detect this and route (or tell you which tool to use).
- Ingestion lag: logs appear minutes after they happen. Wait a few minutes and/or set
byReceiptTime: truefor windows covering the last few minutes — it surfaces more recent logs. - No results but the query looks right — check the time range, the exact
_sourcecategoryspelling, and ingestion lag; the 0-result response lists any server warnings (e.g. unknown partition). - Jobs created via
sumo_create_search_job(orkeepJob: true) are background-polled (kept alive) by the server and auto-deleted afterYOKOZUNA_KEEPALIVE_IDLE_MINUTES(default 10) minutes idle; any access (status/messages/records) resets the idle timer. At mostYOKOZUNA_KEEPALIVE_MAX_JOBS(default 20) jobs are tracked — beyond that the stalest is evicted from keepalive (logged to stderr with its job id). Delete jobs explicitly when done. sumo_run_searchthat shows fewer messages thanmessageCountincludes a hint: raiselimit(max 5000), or re-run withkeepJob: trueand page viasumo_get_messagesoffset/limit.
Development
npm run dev # tsx watch (needs SUMO_* in the shell env)
npm test # unit tests (no network)
npm run test:integration # opt-in; needs SUMO_ACCESS_ID/KEY in env; creates+deletes 1 tiny job
npm run lint
npm run typecheck
Full local quality gate (no CI is configured — the repo is not hosted on GitHub for now):
npm run typecheck && npm run lint && npm run build && npm test
Layout: src/config.ts (env → typed config) · src/http/ (rate-limited fetch client,
errors, cookies) · src/sumo/ (Search Job API, job lifecycle/keepalive, time ranges) ·
src/format/ (log flattening + token-economical rendering) · src/tools/ (MCP tool
definitions) · src/server.ts/src/index.ts (wiring + stdio entry). plan/ holds the
task specs the implementation was built from.
Publishing
The package is publish-ready: files allowlist (dist, README.md, LICENSE only —
no sources, tests, or env files in the tarball), a bin entry with a shebang that
survives the build, and a prepublishOnly hook that rebuilds dist on publish. Verify
the tarball contents with npm pack --dry-run; publish with npm publish. Once
published, npx -y yokozuna-mcp works as-is (launch form 4 above). Until then, use the
local forms.
Brand assets live in assets/ (logo) and .github/ (social preview); the SVGs are the
source of truth and npm run assets re-rasterizes the PNGs (dev-only
@resvg/resvg-js, excluded from the published tarball).
Security
- Credentials come from environment variables only; the access key is never logged, echoed, or included in error messages (unit-tested).
- Use a read-only service account scoped to the log indexes you need.
- Nothing is network-exposed: stdio transport only. stdout carries only MCP protocol JSON; all diagnostics go to stderr.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.