In a multi-repo enterprise, AI coding agents struggle the moment a question crosses one repo. They grep the open repo, guess names, or fabricate connections. ServiceScout fixes that by giving them an evidence-backed graph of every service, API, datastore, queue, and dependency in the org — built automatically from source code, and served over MCP.
Ask:
"How does checkout work end to end?"
Your agent calls ServiceScout, finds the storefront, BFF, backend services, queues, databases, and owners involved, clones the right repos into context, and answers with file:line citations from real code.
Want to see the dashboard and MCP tools right now? A small, pre-extracted catalog of the public Google Online Boutique demo ships in the repo, so you can explore without an API key or a crawl:
git clone https://github.com/lupuletic/servicescout.git
cd servicescout
make demo # Google Online Boutique (default)
make demo DEMO_WORKSPACE=sock-shop # Weaveworks sock-shop(First run builds the image, then serves the bundled catalog.)
Open http://127.0.0.1:8788; stop with make demo-down.
This indexes nothing of your own — it loads a pre-extracted catalog
(examples/sock-shop-catalog.json or
examples/online-boutique-catalog.json).
To build a catalog from real repos, follow the Quickstart.
A live catalog of the public Online Boutique demo (make demo):
Explorer — the service-flow graph; select any node for its tagline, source
repo, and file:line evidence.
Entity view — a service's capability sheet, dependency neighbourhood, and an Evidence tab that ties every fact back to source.
Activity — extraction run history with per-run cost and duration.
Fastest path — point it at your own org in one command:
git clone https://github.com/lupuletic/servicescout.git && cd servicescout
make quickstart # interactive config on first run, then crawl + servemake quickstart runs the setup wizard (orgs, LLM auth, budget) if you haven't
configured it yet, pulls the prebuilt multi-arch image from Docker Hub
(lupuletic/servicescout — no local build), crawls your orgs, and serves the
dashboard + MCP. The manual steps below are the same thing, broken out.
Prefer a UI? Once the server is up, onboard entirely from the browser at
/onboard: paste a GitHub PAT, pick orgs and journey-seed repos
(storefronts, mobile, entry points), set a budget, and start the crawl — it
follows dependencies downstream from your seeds. See
UI onboarding.
1. Run the server (Docker, HTTP streamable on :8765):
git clone https://github.com/lupuletic/servicescout.git
cd servicescout
cp .env.example .env # fill in WORKSPACE_ROOT, GOOGLE_CLOUD_PROJECT
docker compose pull mcp dashboard # grab the prebuilt image (or omit to build)
docker compose up -d mcp dashboardOpen the operator UI at http://127.0.0.1:8788.
Workspace state is selected by env vars:
WORKSPACE_ROOT: mounted source checkout root.SERVICESCOUT_WORKSPACE_CONFIG: org/exclusion config used by crawler/scheduler.SERVICESCOUT_DATA_DIR: persistent catalog, Kuzu DB, run logs, and decisions.
Keeping SERVICESCOUT_DATA_DIR different per workspace is what prevents an
eval crawl from overwriting your real catalog.
First time here? Try the public eval before your own repos. ServiceScout ships a pinned, isolated sock-shop workspace — its own source root, data dir, and ports — so you can exercise the dashboard and MCP tools against a known open-source microservices demo without ever pointing it at private code. See Switch between workspaces and public evals below. Extraction still calls your configured LLM provider (keep
BUDGET_USDsmall), but everything it indexes is public.
2. Crawl your own GitHub orgs to build the catalog:
cp workspace.json.example workspace.json && $EDITOR workspace.json # add your orgs
docker compose run --rm crawler # extracts + embedsThe crawler is auto-convergent: it clones repos referenced by the catalog,
re-runs extraction, and stops when no new repos are discovered. Budget cap
in .env (BUDGET_USD=100 by default) is a hard stop.
3. Wire it up to your coding agent:
./install.shInteractive — asks which agent (Claude Code / Codex / both) and the MCP
URL (default http://127.0.0.1:8765/mcp). Installs the servicescout and
journey skills into ~/.claude/skills/ and registers the MCP server
with your chosen agent(s).
Remote install is available, but inspect the script first if this is a new machine or a shared environment:
curl -fsSL https://raw.githubusercontent.com/lupuletic/servicescout/main/install.sh | less
curl -fsSL https://raw.githubusercontent.com/lupuletic/servicescout/main/install.sh \
| bash -s -- --agent both --url http://127.0.0.1:8765/mcp --yesOr from a local checkout: ./install.sh (same flags).
Under the hood it runs the equivalent of:
claude mcp add servicescout --scope user --transport http http://127.0.0.1:8765/mcp
codex mcp add servicescout --url http://127.0.0.1:8765/mcpRestart the agent. Ask: "trace the login flow end-to-end" and watch it
call servicescout_search → servicescout_trace → clone repos → answer
with citations.
servicescout/— Python implementation package: crawler, extraction, dashboard API, MCP server, storage backends, harnesses, and static extractors.frontend/— React/Sigma operator UI.evals/— reproducible benchmark workspaces and scoring harnesses.docs/— runbooks, audits, screenshots, and release-readiness notes.deploy/,Dockerfile,docker-compose.yml,Makefile— deployment and local operator entrypoints.
For local Python development (Python 3.11–3.13 — the pinned kuzu wheel is
not yet published for 3.14; on 3.14 use Docker or the JSON backend):
python -m pip install -e .
servicescout-dashboard --catalog data/catalog.json --host 127.0.0.1 --port 8788Module entrypoints also work from a checkout, for example
python -m servicescout.dashboard and python -m servicescout.crawler.
For a new VM or workstation where you want the full product behind one HTTP port, use the bundled nginx edge profile:
git clone https://github.com/lupuletic/servicescout.git
cd servicescout
cp .env.example .env
$EDITOR .env # set WORKSPACE_ROOT, credentials, SERVICESCOUT_HTTP_BIND
cp workspace.json.example workspace.json
$EDITOR workspace.json # add orgs / exclusions
docker compose --profile scheduler --profile edge up -d --buildThat starts a self-contained Compose network:
edgenginx exposes${SERVICESCOUT_HTTP_BIND:-127.0.0.1:8080}.dashboardserves the React operator UI and API.mcpserves streamable HTTP MCP at/mcp.schedulerkeeps the catalog fresh and writes run logs for Activity.${SERVICESCOUT_DATA_DIR:-./data}is the persistent catalog/Kuzu/run-log volume.
After the stack is up:
docker compose run --rm crawlerThen open http://<host>:<port>/ for the UI or point agents at
http://<host>:<port>/mcp. Put TLS/auth in front with your normal load
balancer or reverse proxy; the bundled nginx is intentionally a small internal
edge, not an identity provider.
Extraction shells out to the Codex or Claude Code CLI inside the crawler/scheduler container, with two auth modes:
- Local dev — Compose mounts your already-logged-in
~/.codex/~/.claudeinto the container. Convenient on a laptop only; not a server model. - Headless / VM — set an API key in
.envand the crawler runs with no mounted login state. For codex setCODEX_API_KEY(the crawler then runscodex exec --ignore-user-config, so personal config is ignored and billing is explicit); for claude setANTHROPIC_API_KEY. SettingOPENAI_API_KEYalone also authenticates codex but can silently switch it to API-key billing — preferCODEX_API_KEY.
Verify the whole setup — extractor auth, GitHub access, mounts, embeddings — before a crawl:
docker compose run --rm doctorThe crawler also preflights provider auth and aborts early with a clear, actionable message if it can't authenticate, so a misconfigured run fails fast instead of part-way through.
The repo includes a pinned Weaveworks sock-shop eval workspace under
evals/workspace and an isolated catalog under evals/data. You can run the
same dashboard/MCP stack against it without touching your main catalog:
make eval-setup WORKSPACE=sock-shop
make eval-extract WORKSPACE=sock-shop
python -m servicescout.build_kuzu --catalog evals/data/catalog.json --db evals/data/catalog.kuzu
docker compose --env-file .env.socks-shop.example up -d mcp dashboard
open http://127.0.0.1:8790
python evals/runner.py --workspace sock-shopWatch extraction progress in the terminal: every repo prints
==> extracting microservices-demo/<repo> and ends with an EXTRACTOR_RESULT
line containing status, duration, estimated cost, and output path.
For a one-port remote-style sock-shop stack:
docker compose --env-file .env.socks-shop.example --profile edge up -d --build
open http://127.0.0.1:8081The second public benchmark is Google Online Boutique / microservices-demo.
It is isolated under evals/workspaces/online-boutique:
make eval-setup WORKSPACE=online-boutique
make eval-extract WORKSPACE=online-boutique
make eval-kuzu WORKSPACE=online-boutique
make eval-run WORKSPACE=online-boutique
make eval-audit WORKSPACE=online-boutique
docker compose --env-file .env.online-boutique.example up -d mcp dashboard
open http://127.0.0.1:8792For a one-port remote-style Online Boutique stack:
docker compose --env-file .env.online-boutique.example --profile edge up -d --build
open http://127.0.0.1:8082To switch back to your main/private workspace, use your normal .env or a
copied .env.main based on .env.main.example:
docker compose --env-file .env.main up -d mcp dashboardThe two workspaces stay separate as long as these pairs stay separate:
| Workspace | Source root | Data root | Workspace config |
|---|---|---|---|
| Main/private | WORKSPACE_ROOT=/Users/.../work |
SERVICESCOUT_DATA_DIR=./data |
SERVICESCOUT_WORKSPACE_CONFIG=./workspace.json |
| sock-shop evals | WORKSPACE_ROOT=./evals/workspace |
SERVICESCOUT_DATA_DIR=./evals/data |
SERVICESCOUT_WORKSPACE_CONFIG=./evals/workspace_orgs.json |
| online-boutique evals | WORKSPACE_ROOT=./evals/workspaces/online-boutique/workspace |
SERVICESCOUT_DATA_DIR=./evals/workspaces/online-boutique/data |
SERVICESCOUT_WORKSPACE_CONFIG=./evals/workspaces/online-boutique/workspace_orgs.json |
The example env files also use different local ports:
- Main/private: dashboard
127.0.0.1:8788, MCP127.0.0.1:8765. - sock-shop: dashboard
127.0.0.1:8790, MCP127.0.0.1:8791. - online-boutique: dashboard
127.0.0.1:8792, MCP127.0.0.1:8793.
- A Backstage-shaped catalog of every Component, API, Resource, System, Domain, and Group across your orgs — built from source code, not hand-maintained YAML.
- Hybrid retrieval (BM25 IDF + dense embeddings, fused with Reciprocal Rank Fusion) so any natural-language prompt routes to the right repos.
- Multi-hop journey planning including async messaging chains
(
producesMessage→ topic →consumesMessage), so your agent can trace flows that span 15+ services. - Evidence at every edge — every relation carries
file:linecitations the agent can verify in source. - Two ready-made Claude Code skills:
servicescout(routing) andjourney(multi-hop tracing).
Eight tools. Designed so an agent uses the first one for any new task and rarely needs the operator tool.
| Tool | Purpose |
|---|---|
servicescout_search(query, limit=8) |
Hybrid retrieval. Top entities + matched domain attributes / glossary. |
servicescout_describe(entity) |
One entity's full record + tagline + capability sheet. |
servicescout_neighbors(entity, direction="out"|"in"|"both", depth=1, edge_types=[...]) |
One-step graph traversal. direction="in" answers "who depends on / consumes X". |
servicescout_trace(start, end=None, max_hops=6, include_async=True) |
Multi-hop journey planner. Returns CANDIDATE hops; agent verifies each in code. |
servicescout_evidence(source, target=None) |
File:line citations for an edge. |
servicescout_glossary(term, limit=20) |
Vocabulary lookup across component capability sheets. |
servicescout_owners(entity) |
Fast owner/lifecycle lookup without fetching the full entity. |
servicescout_status() |
Read-only catalog state. |
Write operations are intentionally not exposed over MCP. Operators can trigger crawls from the dashboard Activity page; maintenance jobs still run through Compose/CLI.
ServiceScout's MCP server reads from a Backend — pick the one that fits
your environment. New backends drop into servicescout/storage.py behind the
same interface.
| Backend | When to use | Setup |
|---|---|---|
| KuzuDB (recommended) | Embedded graph DB with HNSW vector index + BM25 FTS. Persistent, fast at any size. | kuzu is pinned in requirements.lock; run python -m servicescout.build_kuzu after each crawl. |
| JSON | Zero-dependency fallback. Reads data/catalog.json into memory. Fine up to ~50k entities. |
No setup — just point at data/catalog.json. |
Select with --backend kuzu|json|auto (default auto — Kuzu when a
database exists, JSON otherwise). The interface is the same eight tools
either way.
A React + Sigma.js dashboard ships with the project at
http://localhost:8788 (Docker) or via
python -m servicescout.dashboard. Primary pages:
- Explorer — interactive force-directed layout of the catalog. Kind, edge, and confidence filters, hover-to-highlight neighbourhood, click-to-inspect.
- Catalog — searchable entity table with facets for kind, owner, lifecycle, runtime, environment, tag, type, and confidence.
- Activity — scheduler status, extraction/crawl run history, drilldown into each run, and trigger-now control.
- Operator — cost trendline, verifier signal, staleness heatmap, and stale repo queue.
- Triage — disconfirmed verifier facts, owner assignment, terminal decisions, external-component decisions, and decision log.
Built with Vite + React + SWR + react-router-dom + Sigma.js
(graphology) — no TanStack — and packaged into the same single
Docker image (Dockerfile runs npm run build automatically). The
FastAPI backend serves the built frontend/dist/ and exposes
/api/state.json, /api/entities, /api/entity/:ref, /api/graph,
/api/triage.json, /api/triage/facts, /api/crawl/*, and
/api/operator/summary.
GitHub orgs ──► crawler ──► LLM extractor ──► JSON-schema validation
│
▼
build_catalog ─► identity reconciliation
│
▼
embed_catalog ─► dense embeddings (Gemini)
│
▼
build_kuzu ─► Kuzu index (FTS + HNSW vectors)
│
▼
MCP server ──► AI coding agent
The extractor runs codex or claude CLI in non-interactive mode against
each repo, asks for a Backstage-shaped JSON document with file:line
evidence, and emits it through a strict JSON schema. The build step merges
per-repo extractions into a single catalog and reconciles aliases (service
names that appear as hostnames, config keys, or generated client classes).
The embed step adds Gemini embeddings to each entity. The Kuzu build step
loads the catalog into an embedded graph DB with a BM25 FTS index and an
HNSW vector index. The MCP server fuses dense + lexical retrieval (RRF,
k=60) and exposes eight tools for agents to navigate the result.
| Variable | Required | Default | Purpose |
|---|---|---|---|
WORKSPACE_ROOT |
yes | — | Path where the crawler clones repos. |
LLM_PROVIDER |
yes | codex |
Extractor harness: codex or claude. |
LLM_MODEL |
yes | gpt-5.4-mini |
Model name passed to the harness. |
OPENAI_API_KEY / ANTHROPIC_API_KEY |
provider-dependent | — | Passed through for provider tooling and future direct API harnesses. Today, still verify the selected CLI is authenticated inside the crawler container. |
GOOGLE_CLOUD_PROJECT |
optional | — | GCP project for Vertex AI embeddings. Empty disables embeddings (lexical-only fallback). |
BUDGET_USD |
optional | 100 |
Hard cost cap per crawl. |
SERVICESCOUT_HTTP_BIND |
optional | 127.0.0.1:8080 |
nginx edge bind address for remote Compose deployments. |
CRAWL_INTERVAL_MINUTES |
optional | 360 |
Continuous scheduler interval. |
CRAWL_TICK_BUDGET_USD |
optional | 20 |
Hard cost cap per scheduler tick. |
Credentials it needs (mounted read-only into the container — see
docker-compose.yml):
- GitHub —
ghCLI auth at~/.config/gh, used to discover and clone repos. Required for crawling. - Extractor — either an authenticated
codex/claudeCLI login or an API key (CODEX_API_KEY/OPENAI_API_KEYfor codex,ANTHROPIC_API_KEYfor claude). The crawler preflights this and exits early with a clear message if neither is present, so you find out before any repo is cloned. - Embeddings (optional) — Google Cloud ADC at
~/.config/gcloudplusGOOGLE_CLOUD_PROJECT. Leave the project empty to skip embeddings and use lexical-only search.
See .env.example for the full list. The first-run wizard
(python -m servicescout.init_wizard) walks you through everything interactively.
For the first controlled indexing run on a private estate, follow
docs/controlled-indexing-runbook.md.
ServiceScout is a local-first operator tool. It is not a hosted identity provider and it does not ship application-level auth in this alpha.
- Default Compose binds the dashboard, MCP server, and nginx edge to
127.0.0.1. - The MCP server exposes read-only catalog tools.
- The dashboard can start crawler and scheduler work. Those jobs can use the mounted GitHub, cloud, Codex, Claude, and LLM API credentials.
- Extraction sends source-derived prompts and snippets to the configured
extractor provider (
codexorclaude). Embeddings send catalog text to the configured embedding provider whenGOOGLE_CLOUD_PROJECTis set. - Do not expose the dashboard or
/mcpdirectly to the public internet. Use a VPN, firewall, SSH tunnel, or authenticated reverse proxy for shared access.
See SECURITY.md before connecting it to private repositories.
Docker builds install Python and Node packages. In networks with TLS
inspection or package-policy blocks, add local CA PEM files under certs/
before building; the Dockerfile splits multi-certificate bundles and trusts
each certificate. If your network blocks public package indexes entirely,
set the usual Docker build environment or mirror variables (PIP_INDEX_URL,
PIP_EXTRA_INDEX_URL, PIP_TRUSTED_HOST, NPM_CONFIG_REGISTRY, proxy
variables) before running Compose. For fully offline or allowlisted builds,
populate vendor/wheels/ on a network that can reach your package source:
python -m pip download -r requirements.lock -d vendor/wheels
PIP_NO_INDEX=1 docker compose buildEquivalent Make targets:
make wheelhouse
make docker-build-offline WORKSPACE=online-boutiqueThe runtime stack can still be started from a pre-built image with
docker compose up -d --no-build mcp dashboard.
Alpha. The pipeline works end-to-end, has public eval coverage, and has been used against a large private workspace. Expect rough edges in the operator path:
python -m servicescout.crawler --resumecan continue a compatible interrupted run by readingcrawler_state.jsonand skipping repos already completed successfully. Scheduler-level incremental re-extraction is commit-aware, but long-running production soak and deleted-repo tombstoning are still tracked separately.- MCP/dashboard have no built-in user auth and the dashboard can trigger crawls using mounted repo/LLM credentials. Keep the default localhost bind for SSH-tunnel use, or put the bundled nginx edge behind your normal VPN, firewall, SSO proxy, or load balancer before binding to a public interface.
- GitHub only — other source-control hosts are out of scope for now.
Issues and PRs welcome.
ServiceScout is built around pluggable interfaces — harnesses/ for
extraction, the embedding-provider hook (today: Gemini / Vertex AI), and the
storage Backend. Those are the intended extension points, and contributions
are welcome.
It is GitHub-only by design; support for other source-control hosts (GitLab, Bitbucket, GitHub Enterprise) is not currently planned.
Onboarding is UI-driven at /onboard in the dashboard: paste a GitHub PAT
to pick from the orgs it can read, select journey-seed repos (storefronts,
mobile apps, other entry points), set a budget, and start the crawl. The crawler
extracts the seeds first, then follows their dependency edges downstream to
the backing APIs and services — so a "product" is its front doors plus
everything they call, not an exhaustive (and expensive) scan of the whole org.
Seeds and crawl bounds live in workspace.json (seeds, scope), so a
UI-built config is also inspectable and reproducible as config-as-code.
The natural next step is to make ServiceScout a centralized platform with a knowledge graph per product:
- Workspace registry + selector. Promote "workspace" from an env-switched
singleton to a first-class, persisted entity —
{name, seeds, orgs, scope, credential-ref, isolated catalog/data}— with a UI to create, select, and manage many. Each product becomes a journey-seeded subgraph. - Credential vault. Today a single operator's PAT is stored server-side
(single-tenant, the same trust model as a
.env). A multi-team platform that accepts many users' tokens needs a real vault: encryption-at-rest, per-workspace scoping, rotation, and audit. This is the load-bearing piece — not the UI. - The fork that sizes it: single-team self-host (one operator, many workspaces) vs. true multi-tenant (many teams, RBAC, isolation). That decision determines most of the cost here and should be settled before the build.
MIT. See LICENSE.



