AI-powered npm package intelligence with grounded code generation using Elastic Serverless and Gemini AI.
Problem: LLMs hallucinate APIs for modern npm packages that weren't in their training data.
Solution: Hybrid search (BM25 + semantic) + grounded code generation that only uses real, verified package APIs.
-
Intent-based Discovery: Search packages by what you want to do
- "edge rate limiting for cloudflare workers" → finds
@upstash/ratelimit - "durable background jobs with retries" → finds
inngest,@trigger.dev/sdk
- "edge rate limiting for cloudflare workers" → finds
-
Grounded Code Generation: Generate TypeScript code using ONLY real APIs from package docs
- Pulls context from Elasticsearch (README, exports, examples)
- Gemini generates code grounded in provided context
- Light TypeScript syntax validation with auto-retry
- Always cites sources or says "insufficient context"
-
MCP Integration: Use as a tool in Cursor/Windsurf IDEs
- Fastify control plane exposing MCP resources/tools (
shared/,api/,workers/) - BullMQ worker trigger for on-demand ingestion
- CLI demo script to list → search → answer → reindex packages
- Fastify control plane exposing MCP resources/tools (
┌──────────────┐
│ Users │
│ (Web / MCP) │
└──────┬───────┘
│
▼
┌────────────────────────────┐
│ MCP Control Plane (Fastify)│
│ • /mcp/resources.list │
│ • /mcp/tools.search │
│ • /mcp/tools.answer │
│ • /mcp/tools.reindex │
└──────────┬─────────────────┘
│
┌─────────┴──────────┐
│ │
▼ ▼
┌──────────────────┐ ┌────────────────────┐
│ Elastic Cloud │ │ BullMQ Workers │
│ • RRF Hybrid │ │ • indexPackages() │
│ • semantic_text │ │ • uses ingestion │
│ • Gemini embed │ └─────────┬──────────┘
└─────────┬────────┘ │
│ │
▼ ▼
┌──────────────┐ ┌─────────────────┐
│ Legacy HTTP │ │ Gemini AI │
│ API (optional) │ (Grounded code)│
└──────────────┘ └─────────────────┘
Key Tech:
- Elastic Serverless with RRF (Reciprocal Rank Fusion) hybrid retrieval
- Gemini embedding-001 for semantic search via
semantic_textfield - Gemini 1.5 Pro for grounded code generation
- TypeScript backend with minimal dependencies
AI/Agent (4):
@composio/core,@composio/client@trigger.dev/sdk,inngest
Edge/Serverless (3):
@upstash/ratelimit,@upstash/redis@planetscale/database
Framework/Utils (5):
hono,elysia,@t3-oss/env-nextjszod,oslo
Optional (2):
@vercel/kv,@effect/schema
Short on time? Here’s the minimum to get the hackathon demo running end-to-end.
# 0. Install deps
npm install
(cd shared && npm install && npm run build)
(cd api && npm install && npm run build)
(cd workers && npm install && npm run build)
# 1. Configure .env (Elastic, Gemini, Redis, MCP token)
cp .env.example .env # then edit values
# 2. Provision Elastic inference + index
npm run setup:inference
npm run setup:index
# 3. Ingest the MVP corpus
npm run ingest
# 4. Start services
npm run dev # legacy HTTP API (search/answer endpoints)
(cd api && npm run dev) # MCP control plane on :4000
(cd workers && npm run dev) # BullMQ reindex worker
# 5. Showcase
npm run mcp:demo -- --answer "generate a zod schema"
cd ui && npm run dev # optional Next.js UI on :3001- Node.js 18+ and npm/pnpm/yarn
- Elastic Cloud account with Serverless instance (sign up free)
- Gemini API Key from Google AI Studio (free tier works)
cd npm-intel
npm installcp .env.example .envEdit .env and add your credentials:
ELASTIC_CLOUD_ID=your-cloud-id-here
ELASTIC_API_KEY=your-api-key-here
GEMINI_API_KEY=your-gemini-api-key-hereHow to get your Elastic credentials:
- Go to Elastic Cloud Console
- Create a Serverless Elasticsearch project (or use existing)
- Copy the Cloud ID from project settings
- Create an API key: Management → Security → API Keys → Create API key
# Step 1: Create Gemini inference endpoint
npm run setup:inference
# Step 2: Create the npm-packages index
npm run setup:indexYou should see:
✅ Successfully created Gemini inference endpoint!
✅ Successfully created index 'npm-packages'!
npm run ingestThis will fetch and index all 12-15 MVP packages from unpkg CDN. Takes ~2-3 minutes.
Expected output:
📊 Ingestion Summary:
✅ Successful: 15/15
❌ Failed: 0/15
npm run test:searchThis runs preset queries and shows RRF hybrid search results:
🔍 Query: "edge rate limiting for cloudflare workers"
────────────────────────────────────────────────────────────
1. @upstash/ratelimit@2.0.3
Score: 0.9842
Description: Rate limiting library for serverless runtimes
Keywords: rate-limit, edge, serverless
| Command | Description |
|---|---|
npm run setup:inference |
Create Gemini inference endpoint in Elastic |
npm run setup:index |
Create npm-packages index with mappings |
npm run ingest |
Fetch and index all MVP packages |
npm run test:search |
Test RRF hybrid search with preset queries |
npm run dev |
Start API server in watch mode (coming soon) |
npm run build |
Compile TypeScript to dist/ |
npm run mcp:demo |
CLI demo for MCP list/search/answer/reindex |
NPM Intel ships a dedicated MCP surface so agents and IDEs can interact with the same data the web UI uses.
shared/: Zod schemas plus the Gemini-grounded answer_service shared by both APIs.api/: Fastify control plane exposing MCP resources and tools (requiresAPI_TOKEN_SECRET).workers/: BullMQ worker that runsindexPackages()for/mcp/tools.reindexjobs.- Auth: Bearer token (
Authorization: Bearer …) orx-api-keymatchingAPI_TOKEN_SECRET.
- Install & build packages:
(cd shared && npm install && npm run build) (cd api && npm install && npm run build) (cd workers && npm install && npm run build)
- Configure root
.env:MCP_PORT=4000 API_TOKEN_SECRET=super-secret-token MCP_API_TOKEN=super-secret-token REDIS_URL=rediss://default:<token>@<host>:6379 QUEUE_NAME=reindex ELASTIC_ENDPOINT=... ELASTIC_API_KEY=... GEMINI_API_KEY=...
- Run services:
(cd api && npm run dev) (cd workers && npm run dev)
- Demo from the CLI:
npm run mcp:demo npm run mcp:demo -- --answer "generate a zod schema" --snippets=5
/mcp/resources.list— discover packages (npm-package://…)./mcp/tools.search— hybrid search scoped to a package./mcp/tools.answer— Gemini-grounded TypeScript sample with citation./mcp/tools.reindex— trigger ingestion, returning a job id./mcp/jobs.status— poll reindex job state until completion.
The CLI demo walks through list → search → (optional) answer → reindex so you can showcase the whole flow straight from a terminal.
-
Fetch from unpkg CDN:
package.json(name, version, description, keywords)README.md(documentation and examples)dist/index.d.ts(TypeScript definitions)
-
Extract:
- Exports (functions, classes, interfaces) via regex
- Code examples from README fenced blocks (
ts,js)
-
Index to Elasticsearch:
- Single
semantic_textfield combines README + code examples - Auto-generates embeddings via Gemini inference endpoint
- Nested
exports[]for symbol lookup (plain text)
- Single
Two retrievers combined with Reciprocal Rank Fusion:
-
BM25 (keyword):
- Searches
description^3,readme_content,keywords^2 - Good for exact terms like "rate limiting" or package names
- Searches
-
Semantic (embeddings):
- Searches
readme_contentvia Gemini embeddings - Good for intent queries like "prevent abuse in edge functions"
- Searches
RRF merges results for best of both worlds (typically +15-25% NDCG improvement).
- User provides:
intent+package name - Backend fetches package doc from Elasticsearch
- Constructs prompt with:
- README excerpt (3500 chars max)
- List of exported symbols
- Code examples
- Gemini generates TypeScript code using ONLY provided context
- Light syntax check via
ts.transpileModule() - If syntax fails, retry once with error message
- Return code + source citation + notes
Key constraint: Gemini is instructed to say "insufficient context" rather than hallucinate.
Perfect for showing judges:
✅ "edge rate limiting for cloudflare workers"
→ @upstash/ratelimit
✅ "durable background jobs with retries"
→ inngest, @trigger.dev/sdk
✅ "create composio agent and register tool"
→ @composio/core, @composio/client
✅ "type-safe env for next.js"
→ @t3-oss/env-nextjs
✅ "serverless mysql client from node"
→ @planetscale/database
✅ "event driven workflows without cron"
→ inngest
✅ "input validation with schema library"
→ zod, @effect/schema
Once your environment is configured, this is the recommended flow for keeping the index, search, and grounded code generation in sync.
-
Provision inference + index
npm run setup:inference npm run setup:index
These commands create the Gemini embedding endpoint in Elastic and (re)build the
npm-packagesindex with the latest mappings. -
Ingest/update packages
# set GITHUB_TOKEN in .env for higher rate limits npm run ingestThe ingestion script fetches README + source, parses symbols, and writes one document per
package@version. Re-running the command overwrites existing documents with fresh metadata and code. -
Verify embeddings and symbols
npm run verify:embeddings # optional detail view: npm run verify:embeddings -- --show-embeddingsConfirms that every package document has semantic-text content for both README and source and shows symbol counts so you can spot empty packages quickly.
-
Evaluate retrieval quality
npm run test:search-simple # metadata + symbol checks npm run test:search # RRF/BM25 smoke tests (falls back if semantic retriever unavailable)
These suites exercise the same hybrid query logic used by the API and give you a pass/fail signal before demos.
-
Run grounded API tests
npm run dev # start the HTTP API locally npm run test:api # in another shell
The test harness hits
/searchand/answerend-to-end, printing the generated TypeScript snippet for each scenario so you can validate grounding manually. -
Launch the demo UI (optional)
cd ui npm install npm run dev # defaults to http://localhost:3001
The Next.js frontend proxies requests to the local API (
/search,/answer). SetNEXT_PUBLIC_API_BASE_URL(andAPI_BASE_URLfor server-side routes) if the API lives on a different origin.
The root page lists all indexed packages; visit/[package](e.g./composio-client) for a package-specific grounded chat.
When you add packages or bump versions, repeat steps 2–5. For production automation, wrap steps 2–4 in a CI job and call the API smoke test as a final gate.
{
"name": "keyword",
"version": "keyword",
"description": "text",
"readme_content": "semantic_text", // ← Auto-embeddings!
"keywords": ["keyword"],
"exports": [{
"kind": "keyword", // function|class|interface|type|const
"name": "keyword",
"signature": "text",
"jsdoc": "text"
}],
"code_examples": "text"
}Why semantic_text?
- Automatically generates embeddings on index (no manual inference calls)
- Stores text + embeddings in single field
- Works seamlessly with
semanticretriever in RRF - Cost-efficient for MVP (single embedding per package)
-
/searchendpoint with RRF -
/answerendpoint with grounded codegen - Basic web UI (search → results → generate code)
- MCP server for Cursor/Windsurf
- Preset query buttons in UI
- Demo video (2-3 min)
- Optional: NDCG evaluation
Some packages might not have a dist/index.d.ts - this is OK, we fallback gracefully.
Run npm run setup:inference again. Make sure GEMINI_API_KEY is valid.
Run npm run setup:index to create the index.
Make sure you ran npm run ingest and it completed successfully.
Ensure you're using Elastic Serverless (8.13+). semantic_text requires inference endpoints.
Before NPM Intel:
// Developer asks AI: "Use inngest to create a scheduled job"
// AI hallucinates:
import { Inngest } from "inngest";
const inngest = new Inngest();
inngest.createScheduledJob({ ... }); // ❌ Doesn't exist!With NPM Intel:
// Grounded on actual inngest docs:
import { Inngest } from "inngest";
const inngest = new Inngest({ id: "my-app" });
inngest.createFunction(
{ id: "scheduled-job" },
{ cron: "0 9 * * *" },
async ({ step }) => { ... }
); // ✅ Real API from docs!- Discovery: ≥3 relevant packages per preset query
- Grounding: Generated code references real APIs (by name)
- Quality: +15-25% NDCG@10 improvement (RRF vs BM25 alone)
- Stability: 90%+ syntax-valid generation
- Symbol-aware queries (boost on
exports.name) - ELSER as 3rd leg in RRF (identifier-rich boosting)
- Expand to 100-500 packages
- Auto-refresh on npm publish
-
/.well-known/llm.jsonpublisher - PR bot for anti-pattern detection
MIT
- Elasticsearch - Search and retrieval
- Google Gemini - Embeddings and code generation
- unpkg - CDN for npm packages
- TypeScript - Type safety
Happy hacking! 🚀
For questions or issues, open a GitHub issue.