Skip to content

Commit c07279f

Browse files
jonathanMLDevzhocursoragent
authored
Feat/oss product hardening (#67)
* addressed review-library.md * fix: track cli, retry, and config-context modules for post-rebase tree Co-authored-by: Cursor <cursoragent@cursor.com> * feat: post-rebase hardening, query tool presets, and CI coverage scope - Unify MCP query surface behind one tool with fast/detailed/full presets; update guided flow and docs. - Abort-aware withTimeout (reject-before-abort race, swallow stray fn rejections); add retry tests. - Pinecone client: shared hit mapping, keyword index namespace discriminated result, metadata filter and logger fixes; builtin URL generators register in setupServer. - README/CHANGELOG/.npmignore align with packaging and deployment model; vitest excludes glue layers from coverage gates. Co-authored-by: Cursor <cursoragent@cursor.com> * chore: enforce LF line endings for Prettier and Git checkouts Co-authored-by: Cursor <cursoragent@cursor.com> * added examples * added data into gitignore * fixed ci * fixed test errors * fixed CHANGELOG.md * fixed test errors * addressed ai reviews * fixed ci error --------- Co-authored-by: zho <jornathanm910923@gmail.com> Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 2b05008 commit c07279f

44 files changed

Lines changed: 1820 additions & 433 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.example

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,22 @@ PINECONE_RERANK_MODEL=bge-reranker-v2-m3
1313
PINECONE_TOP_K=10
1414

1515
# Optional: Logging level (default: INFO)
16-
# Options: DEBUG, INFO, WARNING, ERROR, CRITICAL
16+
# Options: DEBUG, INFO, WARN, ERROR
1717
PINECONE_READ_ONLY_MCP_LOG_LEVEL=INFO
18+
19+
# Optional: Logging format (default: text)
20+
# Options: text, json
21+
PINECONE_READ_ONLY_MCP_LOG_FORMAT=text
22+
23+
# Optional: Sparse index name (default: ${PINECONE_INDEX_NAME}-sparse)
24+
# PINECONE_SPARSE_INDEX_NAME=rag-hybrid-sparse
25+
26+
# Optional: Namespace + suggestion-flow cache TTL in seconds (default: 1800 = 30min)
27+
# PINECONE_CACHE_TTL_SECONDS=1800
28+
29+
# Optional: Disable the suggest_query_params flow gate (default: false).
30+
# WARNING: turning this on bypasses the safety guard. A WARN log fires at startup.
31+
# PINECONE_DISABLE_SUGGEST_FLOW=false
32+
33+
# Optional: Request timeout for Pinecone calls, in milliseconds (default: 15000)
34+
# PINECONE_REQUEST_TIMEOUT_MS=15000

.gitattributes

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Match EditorConfig / Prettier so Linux CI and Windows devs agree on line endings.
2+
* text=auto eol=lf

.github/dependabot.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,21 @@ updates:
1010
labels:
1111
- "dependencies"
1212
- "automated"
13+
groups:
14+
vitest:
15+
patterns:
16+
- "vitest"
17+
- "@vitest/*"
18+
typescript-eslint:
19+
patterns:
20+
- "typescript-eslint"
21+
- "@typescript-eslint/*"
22+
eslint-prettier:
23+
patterns:
24+
- "eslint"
25+
- "@eslint/*"
26+
- "prettier"
27+
- "eslint-config-prettier"
1328

1429
- package-ecosystem: "github-actions"
1530
directory: "/"

.github/workflows/ci.yml

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,18 @@ on:
55
branches: [main]
66
push:
77
branches: [main]
8-
workflow_call: # Allow this workflow to be called by other workflows
8+
workflow_call:
99

1010
jobs:
1111
build-and-test:
12-
runs-on: ubuntu-latest
12+
runs-on: ${{ matrix.os }}
1313
strategy:
14+
fail-fast: false
1415
matrix:
15-
node-version: [18.x, 20.x, 22.x]
16+
os: [ubuntu-latest, windows-latest, macos-latest]
17+
# Do not add 18.x: Vitest 4 → rolldown imports `styleText` from `node:util` (Node >=20.12 only).
18+
# `@vitest/coverage-v8` needs `node:inspector/promises` (Node >=19). Matches `package.json` engines.
19+
node-version: [20.x, 22.x]
1620

1721
steps:
1822
- uses: actions/checkout@v6
@@ -41,8 +45,24 @@ jobs:
4145
- name: Smoke test CLI
4246
run: node dist/index.js --help
4347

44-
- name: Run tests
45-
run: npm test
48+
- name: Run tests with coverage
49+
run: npm run test:coverage
50+
51+
- name: Generate CycloneDX SBOM (lockfile-based)
52+
run: npx --yes @cyclonedx/cyclonedx-npm --package-lock-only --output-file sbom.cdx.json
53+
54+
- name: Upload SBOM artifact
55+
uses: actions/upload-artifact@v4
56+
with:
57+
name: sbom-cyclonedx-${{ matrix.os }}-node-${{ matrix.node-version }}
58+
path: sbom.cdx.json
59+
60+
- name: Upload coverage to Codecov
61+
if: matrix.os == 'ubuntu-latest' && matrix.node-version == '20.x'
62+
uses: codecov/codecov-action@v5
63+
with:
64+
files: ./coverage/lcov.info
65+
fail_ci_if_error: false
4666

4767
quality:
4868
runs-on: ubuntu-latest

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ dist/
88
build/
99
*.tsbuildinfo
1010

11+
# data
12+
data/
13+
14+
1115
# Environment
1216
.env
1317
.env.local

.npmignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ PUBLISHING.md
3131
node_modules/
3232
.vscode/
3333
.idea/
34-
dist/
34+
docs/
3535

3636
# Testing
3737
coverage/

.prettierrc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@
66
"tabWidth": 2,
77
"useTabs": false,
88
"arrowParens": "always",
9-
"bracketSpacing": true
9+
"bracketSpacing": true,
10+
"endOfLine": "lf"
1011
}

CHANGELOG.md

Lines changed: 41 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,38 +4,53 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
Tagged releases are published to npm from GitHub Actions when a **GitHub Release** is published (see `.github/workflows/publish.yml`).
78

89
## [Unreleased]
910

1011
### Added
1112

12-
- `list_namespaces` tool: discovers all namespaces with record counts and sampled metadata fields
13-
- `namespace_router` tool: ranks namespaces by relevance to a natural-language query
14-
- `suggest_query_params` tool: mandatory flow gate that recommends fields and tool variant before queries
15-
- `count` tool: counts unique documents matching a query, with `truncated` flag when results exceed `COUNT_TOP_K`
16-
- `query_fast` tool: lightweight chunk-level search with minimal field set
17-
- `query_detailed` tool: chunk-level search with optional semantic reranking
18-
- `query` tool: unified query entry-point supporting `query_fast` and `query_detailed` modes
19-
- `guided_query` tool: single-call orchestrator that runs routing → suggestion → execution, returning a `decision_trace`
20-
- `generate_urls` tool: synthesizes URLs for records whose metadata lacks a `url` field
21-
- `query_documents` tool: reassembles full documents from chunks grouped by document identifier
22-
- Centralized structured logger (`src/logger.ts`) with level-gated stderr output and stack-trace capture
23-
- Dockerfile for containerised deployment
24-
- `About.md` project documentation covering architecture, tools, and operating principles
13+
- `.coderabbit.yaml` sets the pre-merge **docstring coverage** threshold to **79%** (default **80%**) so marginal documentation-only gaps do not block merges; adjust upward as coverage improves.
14+
- `registerBuiltinUrlGenerators()` for built-in URL generators; `setupServer()` invokes it so CLI/library parity stays default.
15+
- Discriminated result type for `listNamespacesFromKeywordIndex()` (`KeywordIndexNamespacesResult`).
16+
- Unit tests for `withRetry` / `withTimeout` in `src/server/retry.test.ts`.
17+
- `SERVER_VERSION` is now read from `package.json` at runtime so MCP `serverInfo` always matches the published package version.
18+
- `--version` CLI flag prints the package version and exits.
19+
- `list_namespaces` response now includes `expires_at_iso` so clients see the cache expiry as an ISO-8601 timestamp without converting `cache_ttl_seconds`.
20+
- `examples/README.md` describing the library embedding sample.
21+
- GitHub Actions **CI** matrix across **ubuntu-latest**, **windows-latest**, and **macos-latest**, each with **Node.js** **20.x** and **22.x**: typecheck, lint, Prettier, build, `test:coverage`, **CycloneDX** SBOM artifact upload (per job), **Codecov** upload (**Ubuntu** + Node **20.x** only), plus a separate **quality** job (`npm audit`, `npm pack --dry-run`).
22+
- `npm run test:coverage` with Vitest coverage thresholds (see `vitest.config.ts`).
23+
- `@vitest/coverage-v8` devDependency for coverage reports (`lcov`, `json-summary`, HTML).
2524

2625
### Changed
2726

28-
- Modularised server into focused files under `src/server/` (tools, caches, formatters, suggestion flow)
29-
- CI workflow updated: multi-node matrix (`18.x`, `20.x`, `22.x`), separate quality job with `continue-on-error`
30-
- Replaced all `console.error` calls in `pinecone-client.ts` with typed `logInfo` / `logDebug` / `logError`
31-
32-
### Fixed
33-
34-
- Timestamp-based metadata filter now correctly handles numeric epoch values
35-
36-
### Security
37-
38-
- N/A
27+
- **Breaking (MCP):** `suggest_query_params` and in-process suggestion flow now emit `recommended_tool` as `count` | `fast` | `detailed` | `full` (aligned with the unified `query` tool `preset`), not legacy `query_fast` / `query_detailed` strings.
28+
- **Breaking (MCP):** Single hybrid `query` tool with `preset` (`fast` | `detailed` | `full`); removed separate `query_fast` / `query_detailed` tool registrations.
29+
- `resolveConfig()` throws if the Pinecone API key is missing (after trim); library callers must supply `apiKey` via overrides or set `PINECONE_API_KEY`.
30+
- `withTimeout` aborts an internal `AbortSignal` on deadline (cooperative cancellation).
31+
- `PineconeClient`: shared hit-field extraction, safer merge dedup without empty `_id` collisions, metadata sampling skips zero-vector probe when dimension is unknown, `listNamespacesFromKeywordIndex` surfaces errors via `{ ok: false }`.
32+
- Metadata filter manual validation accepts primitive arrays for `$in`/`$nin` including numbers (matches Zod).
33+
- README: deployment model for process-global gate/cache/registry; adjusted feature wording vs pre-1.0 semver.
34+
- `.npmignore` no longer excludes `dist/` (still shipped via `package.json` `files`).
35+
- `.env.example` log-level options corrected to the four levels actually supported (`DEBUG`, `INFO`, `WARN`, `ERROR`); the stale `WARNING`/`CRITICAL` values are gone.
36+
- README Slack URL example now matches the generator output (`https://app.slack.com/client/{team_id}/{channel_id}/p{messageId}`).
37+
- README "Comparison with Python Version" no longer claims an identical API; the new TypeScript-only tools (`guided_query`, `query_documents`, `keyword_search`, `namespace_router`, `suggest_query_params`, `count`, `generate_urls`) are listed explicitly.
38+
- `npm run ci` now runs `test:coverage` so merges are gated on coverage thresholds.
39+
- **Breaking (runtime / tooling):** `engines.node` is now **>=20.12.0**. Vitest **4** (bundled **rolldown**) imports `util.styleText` from `node:util` (added in Node **20.12**), and **`@vitest/coverage-v8`** uses `node:inspector/promises` (Node **≥19**). CI tests only **20.x** and **22.x**.
40+
- Dependabot groups related **vitest**, **typescript-eslint**, and **eslint/prettier** updates.
41+
42+
### Removed
43+
44+
- Dead `test:mcp` npm script (referenced a `test-mcp-server.js` file that has never existed).
45+
46+
## [0.1.6] - 2026-04-24
47+
48+
Historical 0.1.x releases (0.1.0 → 0.1.6) shipped the full tool surface
49+
(`list_namespaces`, `namespace_router`, `suggest_query_params`, `count`,
50+
`query`, `query_fast`, `query_detailed`, `keyword_search`, `query_documents`,
51+
`guided_query`, `generate_urls`), the structured `src/logger.ts`, the
52+
`Dockerfile`, and the modularised `src/server/` layout. See git history for
53+
details. Newer shipped changes are recorded in this changelog by version.
3954

4055
## [0.1.1] - 2026-01-27
4156

@@ -69,6 +84,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6984
- Environment variable support
7085
- Full documentation and examples
7186

72-
[Unreleased]: https://github.com/CppDigest/pinecone-read-only-mcp-typescript/compare/v0.1.1...HEAD
87+
[Unreleased]: https://github.com/CppDigest/pinecone-read-only-mcp-typescript/compare/v0.1.6...HEAD
88+
[0.1.6]: https://github.com/CppDigest/pinecone-read-only-mcp-typescript/compare/v0.1.1...v0.1.6
7389
[0.1.1]: https://github.com/CppDigest/pinecone-read-only-mcp-typescript/compare/v0.1.0...v0.1.1
7490
[0.1.0]: https://github.com/CppDigest/pinecone-read-only-mcp-typescript/releases/tag/v0.1.0

README.md

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,34 @@
77

88
A Model Context Protocol (MCP) server that provides semantic search over Pinecone vector databases using hybrid search (dense + sparse) with reranking.
99

10+
## Documentation
11+
12+
| Doc | Description |
13+
|-----|---------------|
14+
| [docs/README.md](docs/README.md) | Index of all guides |
15+
| [docs/TOOLS.md](docs/TOOLS.md) | Tool catalog & flows |
16+
| [docs/CONFIGURATION.md](docs/CONFIGURATION.md) | Env vars, CLI flags, library config |
17+
| [docs/FAQ.md](docs/FAQ.md) | Common questions |
18+
| [docs/MIGRATION.md](docs/MIGRATION.md) | Deprecations & breaking changes |
19+
| [docs/CI_CD.md](docs/CI_CD.md) | GitHub Actions, SBOM, Docker, releases |
20+
| [RELEASING.md](RELEASING.md) | Pointer to the full release guide in `docs/` |
21+
| [CONTRIBUTING.md](CONTRIBUTING.md) | How to contribute |
22+
| [SECURITY.md](SECURITY.md) | Vulnerability reporting |
23+
1024
## Features
1125

1226
- **Hybrid Search**: Combines dense and sparse embeddings for superior recall
1327
- **Semantic Reranking**: Uses BGE reranker model for improved precision
1428
- **Dynamic Namespace Discovery**: Automatically discovers available namespaces in your Pinecone index
1529
- **Metadata Filtering**: Supports optional metadata filters for refined searches
16-
- **Fast & Optimized**: Lazy initialization, connection pooling, and efficient result merging
17-
- **Production Ready**: Input validation, error handling, and configurable logging
30+
- **Fast presets**: Lazy initialization, connection pooling, and efficient result merging; use the `query` tool `preset=fast | detailed | full` to trade latency vs quality (no published benchmarks yet — treat descriptions as qualitative).
31+
- **Production-oriented defaults**: Input validation, error handling, and configurable logging (semantic versioning is pre-1.0 — review CHANGELOG before upgrading).
1832
- **TypeScript Support**: Full TypeScript support with type definitions
1933

2034
## Installation
2135

36+
**Node.js [20.12](https://nodejs.org/en/download) or later** is required (`engines` in `package.json`).
37+
2238
### As a Package
2339

2440
```bash
@@ -54,16 +70,23 @@ npm run build
5470

5571
## Configuration
5672

57-
The server requires a Pinecone API key and supports the following configuration options:
73+
You need a **Pinecone API key** and (by default) a **dense** index plus matching **sparse** index; see [docs/CONFIGURATION.md](docs/CONFIGURATION.md) for every environment variable and CLI flag.
74+
75+
Quick reference:
76+
77+
| Variable | Required | Default |
78+
| -------- | -------- | ------- |
79+
| `PINECONE_API_KEY` | Yes (for live Pinecone) ||
80+
| `PINECONE_INDEX_NAME` | No | `rag-hybrid` |
81+
| `PINECONE_SPARSE_INDEX_NAME` | No | `{index}-sparse` |
82+
| `PINECONE_READ_ONLY_MCP_LOG_LEVEL` | No | `INFO` (`DEBUG``ERROR`) |
83+
| `PINECONE_READ_ONLY_MCP_LOG_FORMAT` | No | `text` (`json` for log pipelines) |
5884

59-
### Environment Variables
85+
Run `pinecone-read-only-mcp --help` for CLI equivalents (`--cache-ttl-seconds`, `--request-timeout-ms`, `--disable-suggest-flow`, etc.).
6086

61-
| Variable | Required | Default | Description |
62-
| ---------------------------------- | -------- | ---------------------- | ------------------------------------------------ |
63-
| `PINECONE_API_KEY` | Yes | - | Your Pinecone API key |
64-
| `PINECONE_INDEX_NAME` | No | `rag-hybrid` | Pinecone index name (dense + sparse for hybrid) |
65-
| `PINECONE_RERANK_MODEL` | No | `bge-reranker-v2-m3` | Reranking model |
66-
| `PINECONE_READ_ONLY_MCP_LOG_LEVEL` | No | `INFO` | Logging level |
87+
### Deployment model
88+
89+
The server uses **process-global** memory for the suggest-flow gate (`suggest_query_params` context), namespaces cache, URL generator registry, and active configuration. **Stdio MCP (one client per Node process)** matches this model. If you embed `setupServer` behind a multi-tenant HTTP transport, isolate those structures per session yourself or treat the suggest-flow guard as best-effort only.
6790

6891
### Claude Desktop Configuration
6992

@@ -237,7 +260,7 @@ Discovers and lists all available namespaces in the configured Pinecone index, i
237260

238261
### `suggest_query_params`
239262

240-
Suggests which **fields** to request and which tool to use (`count`, `query_fast`, or `query_detailed`), based on the namespace’s schema (from `list_namespaces`) and the user’s natural language query. This is a mandatory flow step before `count`/`query` tools.
263+
Suggests which **fields** to request and which path to use (`count`, or hybrid query presets **fast** / **detailed** / **full** — same vocabulary as the `query` tool `preset` argument), based on the namespace’s schema (from `list_namespaces`) and the user’s natural language query. This is a mandatory flow step before `count` / `query` tools.
241264

242265
**Parameters:**
243266

@@ -255,7 +278,7 @@ Suggests which **fields** to request and which tool to use (`count`, `query_fast
255278
"status": "success",
256279
"suggested_fields": ["document_number", "title", "url", "author"],
257280
"use_count_tool": false,
258-
"recommended_tool": "query_fast",
281+
"recommended_tool": "fast",
259282
"explanation": "User asked for a list or browse; use minimal fields (no chunk_text) for smaller payload and cost.",
260283
"namespace_found": true
261284
}
@@ -269,7 +292,7 @@ Single orchestrator tool that runs the full flow in one call:
269292

270293
1. namespace routing (if namespace is omitted),
271294
2. query param suggestion,
272-
3. execution via `count`, `query_fast`, or `query_detailed`.
295+
3. execution via `count` or hybrid `query` (`fast` / `detailed` / `full` presets).
273296

274297
It returns both the final result and a `decision_trace` for transparency.
275298

@@ -281,7 +304,7 @@ It returns both the final result and a `decision_trace` for transparency.
281304
| `namespace` | string | No | - | Optional explicit namespace |
282305
| `metadata_filter` | object | No | - | Optional metadata filter |
283306
| `top_k` | integer | No | `10` | Query result size for query paths (1-100) |
284-
| `preferred_tool` | enum | No | `auto` | One of `auto`, `count`, `query_fast`, `query_detailed` |
307+
| `preferred_tool` | enum | No | `auto` | One of `auto`, `count`, `fast`, `detailed`, `full` |
285308
| `enrich_urls` | boolean | No | `true` | Auto-generate URLs for `mailing` and `slack-Cpplang` when `metadata.url` is missing |
286309

287310
**Returns:** JSON containing `decision_trace` and `result`.
@@ -301,7 +324,7 @@ Rules:
301324
Format: `https://lists.boost.org/archives/list/{doc_id_or_thread_id}/`
302325
- **`slack-Cpplang`**: prefer `source` directly if present; otherwise use `team_id`, `channel_id`, and `doc_id`
303326
`message_id = doc_id.replace('.', '')`
304-
Format: `https://app.slack.com/{team_id}/{channel_id}/p{message_id}`
327+
Format: `https://app.slack.com/client/{team_id}/{channel_id}/p{message_id}`
305328

306329
**Parameters:**
307330

@@ -577,13 +600,20 @@ npm run dev -- --api-key YOUR_API_KEY
577600

578601
## Comparison with Python Version
579602

580-
This TypeScript implementation provides the same functionality as the [Python version](https://github.com/CppDigest/pinecone-read-only-mcp) with the following benefits:
603+
This TypeScript implementation grew out of the [Python version](https://github.com/CppDigest/pinecone-read-only-mcp) and now exposes a strict superset of its tool surface, including:
604+
605+
- `guided_query` (single-call orchestrator with decision trace)
606+
- `query_documents` (full-document reassembly from chunks)
607+
- `keyword_search` (sparse-index-only retrieval)
608+
- `namespace_router` and `suggest_query_params` (flow guidance)
609+
- `count` and `generate_urls`
610+
611+
Other benefits:
581612

582613
- Native Node.js integration
583614
- Better npm ecosystem integration
584615
- TypeScript type safety
585616
- Similar performance characteristics
586-
- Same API interface
587617

588618
## Troubleshooting
589619

examples/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Examples
2+
3+
| File | Description |
4+
|------|-------------|
5+
| [custom-url-generator.ts](./custom-url-generator.ts) | Embed the MCP server as a library, register a **custom URL generator** for a namespace, and wire `PineconeClient` + `setupServer()`. |
6+
7+
Run with `npx tsx examples/custom-url-generator.ts` after `npm install` (requires valid Pinecone credentials in env).

0 commit comments

Comments
 (0)