Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,22 @@ PINECONE_RERANK_MODEL=bge-reranker-v2-m3
PINECONE_TOP_K=10

# Optional: Logging level (default: INFO)
# Options: DEBUG, INFO, WARNING, ERROR, CRITICAL
# Options: DEBUG, INFO, WARN, ERROR
PINECONE_READ_ONLY_MCP_LOG_LEVEL=INFO

# Optional: Logging format (default: text)
# Options: text, json
PINECONE_READ_ONLY_MCP_LOG_FORMAT=text

# Optional: Sparse index name (default: ${PINECONE_INDEX_NAME}-sparse)
# PINECONE_SPARSE_INDEX_NAME=rag-hybrid-sparse

# Optional: Namespace + suggestion-flow cache TTL in seconds (default: 1800 = 30min)
# PINECONE_CACHE_TTL_SECONDS=1800

# Optional: Disable the suggest_query_params flow gate (default: false).
# WARNING: turning this on bypasses the safety guard. A WARN log fires at startup.
# PINECONE_DISABLE_SUGGEST_FLOW=false

# Optional: Request timeout for Pinecone calls, in milliseconds (default: 15000)
# PINECONE_REQUEST_TIMEOUT_MS=15000
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Match EditorConfig / Prettier so Linux CI and Windows devs agree on line endings.
* text=auto eol=lf
15 changes: 15 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,21 @@ updates:
labels:
- "dependencies"
- "automated"
groups:
vitest:
patterns:
- "vitest"
- "@vitest/*"
typescript-eslint:
patterns:
- "typescript-eslint"
- "@typescript-eslint/*"
eslint-prettier:
patterns:
- "eslint"
- "@eslint/*"
- "prettier"
- "eslint-config-prettier"

- package-ecosystem: "github-actions"
directory: "/"
Expand Down
30 changes: 25 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@ on:
branches: [main]
push:
branches: [main]
workflow_call: # Allow this workflow to be called by other workflows
workflow_call:

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

steps:
- uses: actions/checkout@v6
Expand Down Expand Up @@ -41,8 +45,24 @@ jobs:
- name: Smoke test CLI
run: node dist/index.js --help

- name: Run tests
run: npm test
- name: Run tests with coverage
run: npm run test:coverage

- name: Generate CycloneDX SBOM (lockfile-based)
run: npx --yes @cyclonedx/cyclonedx-npm --package-lock-only --output-file sbom.cdx.json

- name: Upload SBOM artifact
uses: actions/upload-artifact@v4
with:
name: sbom-cyclonedx-${{ matrix.os }}-node-${{ matrix.node-version }}
path: sbom.cdx.json

- name: Upload coverage to Codecov
if: matrix.os == 'ubuntu-latest' && matrix.node-version == '20.x'
uses: codecov/codecov-action@v5
with:
files: ./coverage/lcov.info
fail_ci_if_error: false

quality:
runs-on: ubuntu-latest
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ dist/
build/
*.tsbuildinfo

# data
data/


# Environment
.env
.env.local
Expand Down
2 changes: 1 addition & 1 deletion .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ PUBLISHING.md
node_modules/
.vscode/
.idea/
dist/
docs/

# Testing
coverage/
Expand Down
3 changes: 2 additions & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@
"tabWidth": 2,
"useTabs": false,
"arrowParens": "always",
"bracketSpacing": true
"bracketSpacing": true,
"endOfLine": "lf"
}
66 changes: 41 additions & 25 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,53 @@ All notable changes to this project will be documented in this file.

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

## [Unreleased]

### Added

- `list_namespaces` tool: discovers all namespaces with record counts and sampled metadata fields
- `namespace_router` tool: ranks namespaces by relevance to a natural-language query
- `suggest_query_params` tool: mandatory flow gate that recommends fields and tool variant before queries
- `count` tool: counts unique documents matching a query, with `truncated` flag when results exceed `COUNT_TOP_K`
- `query_fast` tool: lightweight chunk-level search with minimal field set
- `query_detailed` tool: chunk-level search with optional semantic reranking
- `query` tool: unified query entry-point supporting `query_fast` and `query_detailed` modes
- `guided_query` tool: single-call orchestrator that runs routing → suggestion → execution, returning a `decision_trace`
- `generate_urls` tool: synthesizes URLs for records whose metadata lacks a `url` field
- `query_documents` tool: reassembles full documents from chunks grouped by document identifier
- Centralized structured logger (`src/logger.ts`) with level-gated stderr output and stack-trace capture
- Dockerfile for containerised deployment
- `About.md` project documentation covering architecture, tools, and operating principles
- `.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.
- `registerBuiltinUrlGenerators()` for built-in URL generators; `setupServer()` invokes it so CLI/library parity stays default.
- Discriminated result type for `listNamespacesFromKeywordIndex()` (`KeywordIndexNamespacesResult`).
- Unit tests for `withRetry` / `withTimeout` in `src/server/retry.test.ts`.
- `SERVER_VERSION` is now read from `package.json` at runtime so MCP `serverInfo` always matches the published package version.
- `--version` CLI flag prints the package version and exits.
- `list_namespaces` response now includes `expires_at_iso` so clients see the cache expiry as an ISO-8601 timestamp without converting `cache_ttl_seconds`.
- `examples/README.md` describing the library embedding sample.
- 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`).
- `npm run test:coverage` with Vitest coverage thresholds (see `vitest.config.ts`).
- `@vitest/coverage-v8` devDependency for coverage reports (`lcov`, `json-summary`, HTML).

### Changed

- Modularised server into focused files under `src/server/` (tools, caches, formatters, suggestion flow)
- CI workflow updated: multi-node matrix (`18.x`, `20.x`, `22.x`), separate quality job with `continue-on-error`
- Replaced all `console.error` calls in `pinecone-client.ts` with typed `logInfo` / `logDebug` / `logError`

### Fixed

- Timestamp-based metadata filter now correctly handles numeric epoch values

### Security

- N/A
- **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.
- **Breaking (MCP):** Single hybrid `query` tool with `preset` (`fast` | `detailed` | `full`); removed separate `query_fast` / `query_detailed` tool registrations.
- `resolveConfig()` throws if the Pinecone API key is missing (after trim); library callers must supply `apiKey` via overrides or set `PINECONE_API_KEY`.
- `withTimeout` aborts an internal `AbortSignal` on deadline (cooperative cancellation).
- `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 }`.
- Metadata filter manual validation accepts primitive arrays for `$in`/`$nin` including numbers (matches Zod).
- README: deployment model for process-global gate/cache/registry; adjusted feature wording vs pre-1.0 semver.
- `.npmignore` no longer excludes `dist/` (still shipped via `package.json` `files`).
- `.env.example` log-level options corrected to the four levels actually supported (`DEBUG`, `INFO`, `WARN`, `ERROR`); the stale `WARNING`/`CRITICAL` values are gone.
- README Slack URL example now matches the generator output (`https://app.slack.com/client/{team_id}/{channel_id}/p{messageId}`).
- 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.
- `npm run ci` now runs `test:coverage` so merges are gated on coverage thresholds.
- **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**.
- Dependabot groups related **vitest**, **typescript-eslint**, and **eslint/prettier** updates.

### Removed

- Dead `test:mcp` npm script (referenced a `test-mcp-server.js` file that has never existed).

## [0.1.6] - 2026-04-24

Historical 0.1.x releases (0.1.0 → 0.1.6) shipped the full tool surface
(`list_namespaces`, `namespace_router`, `suggest_query_params`, `count`,
`query`, `query_fast`, `query_detailed`, `keyword_search`, `query_documents`,
`guided_query`, `generate_urls`), the structured `src/logger.ts`, the
`Dockerfile`, and the modularised `src/server/` layout. See git history for
details. Newer shipped changes are recorded in this changelog by version.

## [0.1.1] - 2026-01-27

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

[Unreleased]: https://github.com/CppDigest/pinecone-read-only-mcp-typescript/compare/v0.1.1...HEAD
[Unreleased]: https://github.com/CppDigest/pinecone-read-only-mcp-typescript/compare/v0.1.6...HEAD
[0.1.6]: https://github.com/CppDigest/pinecone-read-only-mcp-typescript/compare/v0.1.1...v0.1.6
[0.1.1]: https://github.com/CppDigest/pinecone-read-only-mcp-typescript/compare/v0.1.0...v0.1.1
[0.1.0]: https://github.com/CppDigest/pinecone-read-only-mcp-typescript/releases/tag/v0.1.0
64 changes: 47 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,34 @@

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

## Documentation

| Doc | Description |
|-----|---------------|
| [docs/README.md](docs/README.md) | Index of all guides |
| [docs/TOOLS.md](docs/TOOLS.md) | Tool catalog & flows |
| [docs/CONFIGURATION.md](docs/CONFIGURATION.md) | Env vars, CLI flags, library config |
| [docs/FAQ.md](docs/FAQ.md) | Common questions |
| [docs/MIGRATION.md](docs/MIGRATION.md) | Deprecations & breaking changes |
| [docs/CI_CD.md](docs/CI_CD.md) | GitHub Actions, SBOM, Docker, releases |
| [RELEASING.md](RELEASING.md) | Pointer to the full release guide in `docs/` |
| [CONTRIBUTING.md](CONTRIBUTING.md) | How to contribute |
| [SECURITY.md](SECURITY.md) | Vulnerability reporting |

## Features

- **Hybrid Search**: Combines dense and sparse embeddings for superior recall
- **Semantic Reranking**: Uses BGE reranker model for improved precision
- **Dynamic Namespace Discovery**: Automatically discovers available namespaces in your Pinecone index
- **Metadata Filtering**: Supports optional metadata filters for refined searches
- **Fast & Optimized**: Lazy initialization, connection pooling, and efficient result merging
- **Production Ready**: Input validation, error handling, and configurable logging
- **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).
- **Production-oriented defaults**: Input validation, error handling, and configurable logging (semantic versioning is pre-1.0 — review CHANGELOG before upgrading).
- **TypeScript Support**: Full TypeScript support with type definitions

## Installation

**Node.js [20.12](https://nodejs.org/en/download) or later** is required (`engines` in `package.json`).

### As a Package

```bash
Expand Down Expand Up @@ -54,16 +70,23 @@ npm run build

## Configuration

The server requires a Pinecone API key and supports the following configuration options:
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.

Quick reference:

| Variable | Required | Default |
| -------- | -------- | ------- |
| `PINECONE_API_KEY` | Yes (for live Pinecone) | — |
| `PINECONE_INDEX_NAME` | No | `rag-hybrid` |
| `PINECONE_SPARSE_INDEX_NAME` | No | `{index}-sparse` |
| `PINECONE_READ_ONLY_MCP_LOG_LEVEL` | No | `INFO` (`DEBUG`–`ERROR`) |
| `PINECONE_READ_ONLY_MCP_LOG_FORMAT` | No | `text` (`json` for log pipelines) |

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

| Variable | Required | Default | Description |
| ---------------------------------- | -------- | ---------------------- | ------------------------------------------------ |
| `PINECONE_API_KEY` | Yes | - | Your Pinecone API key |
| `PINECONE_INDEX_NAME` | No | `rag-hybrid` | Pinecone index name (dense + sparse for hybrid) |
| `PINECONE_RERANK_MODEL` | No | `bge-reranker-v2-m3` | Reranking model |
| `PINECONE_READ_ONLY_MCP_LOG_LEVEL` | No | `INFO` | Logging level |
### Deployment model

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.

### Claude Desktop Configuration

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

### `suggest_query_params`

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.
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.

**Parameters:**

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

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

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

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

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

**Parameters:**

Expand Down Expand Up @@ -577,13 +600,20 @@ npm run dev -- --api-key YOUR_API_KEY

## Comparison with Python Version

This TypeScript implementation provides the same functionality as the [Python version](https://github.com/CppDigest/pinecone-read-only-mcp) with the following benefits:
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:

- `guided_query` (single-call orchestrator with decision trace)
- `query_documents` (full-document reassembly from chunks)
- `keyword_search` (sparse-index-only retrieval)
- `namespace_router` and `suggest_query_params` (flow guidance)
- `count` and `generate_urls`

Other benefits:

- Native Node.js integration
- Better npm ecosystem integration
- TypeScript type safety
- Similar performance characteristics
- Same API interface

## Troubleshooting

Expand Down
7 changes: 7 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Examples

| File | Description |
|------|-------------|
| [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()`. |

Run with `npx tsx examples/custom-url-generator.ts` after `npm install` (requires valid Pinecone credentials in env).
Loading
Loading