From 33c687539e52197bf85457aa98e4b111c9d3cb91 Mon Sep 17 00:00:00 2001 From: zho Date: Tue, 2 Jun 2026 06:34:46 +0800 Subject: [PATCH 1/4] fix: require PINECONE_INDEX_NAME in core config --- .env.example | 8 ++--- CHANGELOG.md | 5 +++ README.md | 28 +++++++++++------ docs/CONFIGURATION.md | 17 ++++++---- docs/MIGRATION.md | 19 +++++++++++ examples/alliance/.env.example | 14 +++++++++ examples/alliance/README.md | 6 ++-- examples/alliance/library-embedding-demo.ts | 2 +- examples/alliance/preset.ts | 5 +++ src/alliance/config.test.ts | 27 +++++++++++++--- src/alliance/config.ts | 30 +++++++++++------- src/alliance/index.ts | 3 +- src/alliance/setup.ts | 6 ++-- src/cli.ts | 8 ++--- src/constants.ts | 2 +- src/core/config.test.ts | 33 +++++++++++-------- src/core/config.ts | 35 +++++++++++---------- src/core/index.ts | 2 +- src/core/server/config-context.ts | 4 +-- src/index.ts | 7 +++-- src/types.ts | 6 ++-- 21 files changed, 181 insertions(+), 86 deletions(-) create mode 100644 examples/alliance/.env.example create mode 100644 examples/alliance/preset.ts diff --git a/.env.example b/.env.example index 55df84a..f2c12c3 100644 --- a/.env.example +++ b/.env.example @@ -4,11 +4,11 @@ # Required: Your Pinecone API key PINECONE_API_KEY=your-pinecone-api-key-here -# Optional: Dense (hybrid) Pinecone index name (default: rag-hybrid when unset) -# PINECONE_INDEX_NAME=rag-hybrid +# Required: Dense (hybrid) Pinecone index name +PINECONE_INDEX_NAME=your-index-name -# Optional: Reranking model (CLI defaults to bge-reranker-v2-m3 when unset; see docs/CONFIGURATION.md) -# PINECONE_RERANK_MODEL=bge-reranker-v2-m3 +# Optional: Reranking model (omit to disable rerank in core; Alliance CLI defaults when unset — see docs/CONFIGURATION.md) +# PINECONE_RERANK_MODEL=your-rerank-model # Optional: Default number of results to return (default: 10) PINECONE_TOP_K=10 diff --git a/CHANGELOG.md b/CHANGELOG.md index bbe88af..b2724ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,11 @@ Tagged releases are published to npm from GitHub Actions when a **GitHub Release ## [Unreleased] +### Changed + +- **Breaking (core):** `resolveConfig` requires a Pinecone index name and no longer applies Alliance index/rerank defaults. Removed exported `DEFAULT_INDEX_NAME` and `DEFAULT_RERANK_MODEL` from the package root. Rerank is opt-in when `PINECONE_RERANK_MODEL` / `rerankModel` is unset. +- **Alliance CLI / `resolveAllianceConfig`:** When index or rerank env/CLI values are omitted, defaults remain `rag-hybrid` and `bge-reranker-v2-m3` (API-key-only MCP configs unchanged). See [examples/alliance/.env.example](examples/alliance/.env.example). + ## [0.2.0] - 2026-05-29 ### Changed diff --git a/README.md b/README.md index b7bd3d4..1345c41 100644 --- a/README.md +++ b/README.md @@ -103,15 +103,15 @@ The codebase is split into two layers: ## Configuration -You need a **Pinecone API key**. **Index** uses `PINECONE_INDEX_NAME` when set, else defaults to `rag-hybrid`; sparse index defaults to `{index}-sparse`. **Reranking** uses `PINECONE_RERANK_MODEL` when set, else `bge-reranker-v2-m3`. MCP configs with only `PINECONE_API_KEY` keep prior Alliance defaults. See [docs/CONFIGURATION.md](docs/CONFIGURATION.md) for every variable and CLI flag. +You need a **Pinecone API key**. **Index** (`PINECONE_INDEX_NAME` or `--index-name`) is required for core/library use; the **published CLI** defaults to `rag-hybrid` when unset (Alliance deployment). Sparse index defaults to `{index}-sparse`. **Rerank:** set `PINECONE_RERANK_MODEL` to enable; the CLI defaults to `bge-reranker-v2-m3` when unset. See [docs/CONFIGURATION.md](docs/CONFIGURATION.md) (core vs Alliance table). -Quick reference: +Quick reference (published CLI / Alliance — core embedders require index, no index/rerank defaults): -| Variable | Required | Default | +| Variable | Required | Default (Alliance CLI) | | ----------------------------------- | ----------------------- | --------------------------------- | | `PINECONE_API_KEY` | Yes (for live Pinecone) | — | -| `PINECONE_INDEX_NAME` | No | `rag-hybrid` when unset | -| `PINECONE_RERANK_MODEL` | No | `bge-reranker-v2-m3` when unset | +| `PINECONE_INDEX_NAME` | No (CLI) / Yes (core) | `rag-hybrid` (CLI only) | +| `PINECONE_RERANK_MODEL` | No | `bge-reranker-v2-m3` (CLI only) | | `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) | @@ -228,7 +228,7 @@ For a global installation: "mcpServers": { "pinecone-search": { "command": "pinecone-read-only-mcp", - "args": ["--api-key", "your-api-key-here"] + "args": ["--api-key", "your-api-key-here", "--index-name", "your-index-name"] } } } @@ -241,19 +241,19 @@ For a global installation: Run the server using npx (no installation required): ```bash -npx @will-cppa/pinecone-read-only-mcp@0.2.0 --api-key YOUR_API_KEY +npx @will-cppa/pinecone-read-only-mcp@0.2.0 --api-key YOUR_API_KEY --index-name YOUR_INDEX ``` Or if installed globally: ```bash -pinecone-read-only-mcp --api-key YOUR_API_KEY +pinecone-read-only-mcp --api-key YOUR_API_KEY --index-name YOUR_INDEX ``` Or if installed locally in your project: ```bash -node node_modules/@will-cppa/pinecone-read-only-mcp/dist/index.js --api-key YOUR_API_KEY +node node_modules/@will-cppa/pinecone-read-only-mcp/dist/index.js --api-key YOUR_API_KEY --index-name YOUR_INDEX ``` ### Available Options @@ -736,11 +736,19 @@ Other benefits: ### API Key Issues -If you see "Pinecone API key is required" error: +If you see "Missing Pinecone API key" at startup: 1. Ensure `PINECONE_API_KEY` environment variable is set, OR 2. Pass `--api-key` option when running the server +### Missing Index Name + +If you see "Missing Pinecone index name" at startup: + +1. Set `PINECONE_INDEX_NAME` in your MCP config or `.env`, OR +2. Pass `--index-name` when running the server +3. Alliance deployers: see [examples/alliance/.env.example](examples/alliance/.env.example) + ### Index Not Found If you see index-related errors: diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md index 0bcd04a..71407ef 100644 --- a/docs/CONFIGURATION.md +++ b/docs/CONFIGURATION.md @@ -1,6 +1,6 @@ # Configuration -Configuration is built from **CLI flags** (when using the binary), **environment variables**, and **defaults**. Library callers use `resolveConfig(overrides)` with optional `ConfigOverrides`. +Configuration is built from **CLI flags** (when using the binary), **environment variables**, and **defaults**. Library callers use `resolveConfig(overrides)` (core) or `resolveAllianceConfig(overrides)` (Alliance CLI / `setupAllianceServer`) with optional `ConfigOverrides`. ## Precedence @@ -15,9 +15,9 @@ Configuration is built from **CLI flags** (when using the binary), **environment | Field | Source | Default / notes | | ----- | ------ | --------------- | | `apiKey` | `apiKey` / `PINECONE_API_KEY` | **Required** (non-empty after trim) | -| `indexName` | `indexName` / `PINECONE_INDEX_NAME` | `rag-hybrid` when env and overrides omit it | +| `indexName` | `indexName` / `PINECONE_INDEX_NAME` | **Required** (non-empty after trim) | | `sparseIndexName` | `sparseIndexName` / `PINECONE_SPARSE_INDEX_NAME` | `{indexName}-sparse` | -| `rerankModel` | `rerankModel` / `PINECONE_RERANK_MODEL` | `bge-reranker-v2-m3` when env and overrides omit it | +| `rerankModel` | `rerankModel` / `PINECONE_RERANK_MODEL` | **Core:** omitted when unset (rerank disabled). **Alliance CLI:** `bge-reranker-v2-m3` when unset | | `defaultTopK` | `defaultTopK` / `PINECONE_TOP_K` | `10` (positive int) | | `logLevel` | `logLevel` / `PINECONE_READ_ONLY_MCP_LOG_LEVEL` | `INFO` (`DEBUG`–`ERROR`) | | `logFormat` | `logFormat` / `PINECONE_READ_ONLY_MCP_LOG_FORMAT` | `text` or `json` | @@ -26,13 +26,18 @@ Configuration is built from **CLI flags** (when using the binary), **environment | `disableSuggestFlow` | `disableSuggestFlow` / `PINECONE_DISABLE_SUGGEST_FLOW` | `false` (bool parsing: true/1/yes/on) | | `checkIndexes` | `checkIndexes` / `PINECONE_CHECK_INDEXES` | `false` | -**Throws** if `apiKey` is missing after trim. +**Throws** if `apiKey` or `indexName` is missing after trim. For the full Alliance tool surface (including `suggest_query_params`, `guided_query`, and built-in URL generators), import from `@will-cppa/pinecone-read-only-mcp/alliance` and call `setupAllianceServer(config)`. -### Rerank model +### Core vs Alliance resolvers -`resolveConfig` uses `PINECONE_INDEX_NAME` / `PINECONE_RERANK_MODEL` when set; otherwise **`rag-hybrid`** and **`bge-reranker-v2-m3`**. MCP configs that only set `PINECONE_API_KEY` keep the same defaults as before. +| Resolver | When to use | Index when unset | Rerank when unset | +| -------- | ------------- | ---------------- | ----------------- | +| `resolveConfig` | Package root, `setupCoreServer`, quickstart | **Throws** | Omitted (no rerank) | +| `resolveAllianceConfig` | Published CLI, `setupAllianceServer` | `rag-hybrid` | `bge-reranker-v2-m3` | + +C++ Alliance deployers can copy [examples/alliance/.env.example](../examples/alliance/.env.example). Constants: `ALLIANCE_DEFAULT_INDEX_NAME` / `ALLIANCE_DEFAULT_RERANK_MODEL` from `@will-cppa/pinecone-read-only-mcp/alliance`. --- diff --git a/docs/MIGRATION.md b/docs/MIGRATION.md index 120f1c0..862597d 100644 --- a/docs/MIGRATION.md +++ b/docs/MIGRATION.md @@ -4,6 +4,25 @@ This guide is for **library and MCP client authors** upgrading from earlier **0. Under [semver 0.y.z](https://semver.org/spec/v2.0.0.html#spec-item-4), **0.1.x → 0.2.0 is a breaking minor** — pin `@0.2.0` only after reading this guide. +## Unreleased: core vs Alliance config defaults + +**Rationale:** Generic npm consumers must not silently connect to Alliance infrastructure or inherit Alliance rerank settings when using `resolveConfig` from the package root. + +**Migration (core / `setupCoreServer`):** + +1. Add `PINECONE_INDEX_NAME` to MCP `env` blocks, `.env`, or Docker `-e`, or pass `indexName` in `ConfigOverrides`. +2. Set `PINECONE_RERANK_MODEL` only when you want reranking; omit it to skip rerank (previously defaulted to `bge-reranker-v2-m3` in core). +3. Code that imported `DEFAULT_INDEX_NAME` or `DEFAULT_RERANK_MODEL` from the package root should use your own constants or [examples/alliance/preset.ts](../examples/alliance/preset.ts) for Alliance values. + +Core `resolveConfig` throws `Missing Pinecone index name: …` when the index is unset (same pattern as the API key error). + +**Alliance CLI / `setupAllianceServer` (unchanged for typical MCP configs):** + +- The binary uses `resolveAllianceConfig`; API-key-only configs still default to `rag-hybrid` and `bge-reranker-v2-m3`. +- Explicit env overrides still win. Copy [examples/alliance/.env.example](../examples/alliance/.env.example) to document Alliance conventions. + +--- + ## Migrating to v0.2.0 ### Namespace trimming and suggest-flow diff --git a/examples/alliance/.env.example b/examples/alliance/.env.example new file mode 100644 index 0000000..fafc37a --- /dev/null +++ b/examples/alliance/.env.example @@ -0,0 +1,14 @@ +# C++ Alliance Pinecone deployment (examples/alliance) +# Copy to .env and fill in your API key. +# The published CLI (resolveAllianceConfig) applies these defaults when variables are omitted. + +PINECONE_API_KEY=your-pinecone-api-key-here + +# Alliance dense index (CLI default when unset: rag-hybrid) +PINECONE_INDEX_NAME=rag-hybrid + +# Optional: sparse index (default: {PINECONE_INDEX_NAME}-sparse) +# PINECONE_SPARSE_INDEX_NAME=rag-hybrid-sparse + +# Alliance rerank model (CLI default when unset: bge-reranker-v2-m3) +# PINECONE_RERANK_MODEL=bge-reranker-v2-m3 diff --git a/examples/alliance/README.md b/examples/alliance/README.md index 9da51e9..8e7f89a 100644 --- a/examples/alliance/README.md +++ b/examples/alliance/README.md @@ -10,12 +10,14 @@ They assume a Pinecone index you control with compatible data (not necessarily t ## Required environment +Copy [`.env.example`](./.env.example) to `.env` and set your API key. For C++ Alliance infrastructure, use `PINECONE_INDEX_NAME=rag-hybrid` as documented there. + | Variable | Required | Notes | | -------- | -------- | ----- | | `PINECONE_API_KEY` | Yes | For live runs | -| `PINECONE_INDEX_NAME` | Yes | Your dense index name | +| `PINECONE_INDEX_NAME` | Yes | Dense index name (Alliance: `rag-hybrid`) | -Optional: `PINECONE_RERANK_MODEL`, `PINECONE_SPARSE_INDEX_NAME`, etc. See [docs/CONFIGURATION.md](../../docs/CONFIGURATION.md). CLI-only configs that set only `PINECONE_API_KEY` still use documented defaults (`rag-hybrid`, `bge-reranker-v2-m3`). +Optional: `PINECONE_RERANK_MODEL`, `PINECONE_SPARSE_INDEX_NAME`, etc. See [docs/CONFIGURATION.md](../../docs/CONFIGURATION.md). The **published CLI** uses `resolveAllianceConfig` and defaults to `rag-hybrid` / `bge-reranker-v2-m3` when those env vars are omitted. Demo constants: [`preset.ts`](./preset.ts). ## Files diff --git a/examples/alliance/library-embedding-demo.ts b/examples/alliance/library-embedding-demo.ts index e0c2146..bdde491 100644 --- a/examples/alliance/library-embedding-demo.ts +++ b/examples/alliance/library-embedding-demo.ts @@ -2,7 +2,7 @@ * Library embedding: build the MCP server from a Node script (not the CLI). * * Pattern (mirrors `src/index.ts`): - * 1. `resolveConfig({ apiKey, indexName, ... })` — env rerank model or default bge-reranker-v2-m3. + * 1. `resolveAllianceConfig({ apiKey, indexName, ... })` — Alliance index/rerank defaults when unset. * 2. `new PineconeClient({ ... })` + `setPineconeClient(client)`. * 3. `await setupAllianceServer(config)` then `server.connect(transport)`. * diff --git a/examples/alliance/preset.ts b/examples/alliance/preset.ts new file mode 100644 index 0000000..f8e294f --- /dev/null +++ b/examples/alliance/preset.ts @@ -0,0 +1,5 @@ +/** + * Alliance deployment constants for copy-paste in demos. Not imported by `src/`. + */ +export const ALLIANCE_INDEX_NAME = 'rag-hybrid'; +export const ALLIANCE_RERANK_MODEL = 'bge-reranker-v2-m3'; diff --git a/src/alliance/config.test.ts b/src/alliance/config.test.ts index 098184f..ed4c910 100644 --- a/src/alliance/config.test.ts +++ b/src/alliance/config.test.ts @@ -1,14 +1,25 @@ import { describe, expect, it } from 'vitest'; -import { DEFAULT_RERANK_MODEL } from '../core/config.js'; -import { resolveAllianceConfig } from './config.js'; +import { + ALLIANCE_DEFAULT_INDEX_NAME, + ALLIANCE_DEFAULT_RERANK_MODEL, + resolveAllianceConfig, +} from './config.js'; describe('resolveAllianceConfig', () => { - it('applies Alliance rerank default when env and overrides omit rerankModel', () => { + it('applies Alliance index and rerank defaults when env omits both', () => { + const cfg = resolveAllianceConfig({ apiKey: 'sk-test' }, { PINECONE_API_KEY: 'sk-test' }); + expect(cfg.indexName).toBe(ALLIANCE_DEFAULT_INDEX_NAME); + expect(cfg.sparseIndexName).toBe(`${ALLIANCE_DEFAULT_INDEX_NAME}-sparse`); + expect(cfg.rerankModel).toBe(ALLIANCE_DEFAULT_RERANK_MODEL); + }); + + it('applies Alliance rerank default when env and overrides omit rerankModel but index is set', () => { const cfg = resolveAllianceConfig( { apiKey: 'sk-test', indexName: 'my-index' }, { PINECONE_API_KEY: 'sk-test', PINECONE_INDEX_NAME: 'my-index' } ); - expect(cfg.rerankModel).toBe(DEFAULT_RERANK_MODEL); + expect(cfg.indexName).toBe('my-index'); + expect(cfg.rerankModel).toBe(ALLIANCE_DEFAULT_RERANK_MODEL); }); it('preserves explicit rerankModel from overrides', () => { @@ -31,4 +42,12 @@ describe('resolveAllianceConfig', () => { ); expect(cfg.rerankModel).toBe('env-reranker'); }); + + it('preserves explicit index from env over Alliance default', () => { + const cfg = resolveAllianceConfig( + { apiKey: 'sk-test' }, + { PINECONE_API_KEY: 'sk-test', PINECONE_INDEX_NAME: 'custom-index' } + ); + expect(cfg.indexName).toBe('custom-index'); + }); }); diff --git a/src/alliance/config.ts b/src/alliance/config.ts index d659bda..619ea33 100644 --- a/src/alliance/config.ts +++ b/src/alliance/config.ts @@ -1,27 +1,35 @@ /** - * Alliance entry re-exports core config. Rerank default lives in {@link resolveConfig} - * (`PINECONE_RERANK_MODEL` when set, else `bge-reranker-v2-m3`). + * Alliance config resolver: applies C++ Alliance deployment defaults, then delegates to core {@link resolveConfig}. */ import { - DEFAULT_RERANK_MODEL, resolveConfig, + trimOptional, type ConfigOverrides, type ServerConfig, } from '../core/config.js'; -/** @deprecated Use {@link DEFAULT_RERANK_MODEL} from core config. */ -export const DEFAULT_ALLIANCE_RERANK_MODEL = DEFAULT_RERANK_MODEL; +/** C++ Alliance default dense index when env/CLI omit `PINECONE_INDEX_NAME`. */ +export const ALLIANCE_DEFAULT_INDEX_NAME = 'rag-hybrid'; -/** @deprecated No-op; {@link resolveConfig} already applies the rerank default. */ -export function applyAllianceRerankDefault(config: ServerConfig): ServerConfig { - return config; -} +/** C++ Alliance default rerank model when env/CLI omit `PINECONE_RERANK_MODEL`. */ +export const ALLIANCE_DEFAULT_RERANK_MODEL = 'bge-reranker-v2-m3'; + +/** @deprecated Use {@link ALLIANCE_DEFAULT_RERANK_MODEL}. */ +export const DEFAULT_ALLIANCE_RERANK_MODEL = ALLIANCE_DEFAULT_RERANK_MODEL; -/** Alias for {@link resolveConfig} (Alliance CLI and `setupAllianceServer`). */ +/** + * Build {@link ServerConfig} for Alliance CLI and `setupAllianceServer`. + * Fills index and rerank from Alliance defaults when unset, then calls core `resolveConfig`. + */ export function resolveAllianceConfig( overrides: ConfigOverrides = {}, env: NodeJS.ProcessEnv = process.env ): ServerConfig { - return resolveConfig(overrides, env); + const indexName = + trimOptional(overrides.indexName ?? env['PINECONE_INDEX_NAME']) ?? ALLIANCE_DEFAULT_INDEX_NAME; + const rerankModel = + trimOptional(overrides.rerankModel ?? env['PINECONE_RERANK_MODEL']) ?? + ALLIANCE_DEFAULT_RERANK_MODEL; + return resolveConfig({ ...overrides, indexName, rerankModel }, env); } diff --git a/src/alliance/index.ts b/src/alliance/index.ts index 659b47c..05fb654 100644 --- a/src/alliance/index.ts +++ b/src/alliance/index.ts @@ -5,7 +5,8 @@ export * from '../core/index.js'; export { - applyAllianceRerankDefault, + ALLIANCE_DEFAULT_INDEX_NAME, + ALLIANCE_DEFAULT_RERANK_MODEL, DEFAULT_ALLIANCE_RERANK_MODEL, resolveAllianceConfig, } from './config.js'; diff --git a/src/alliance/setup.ts b/src/alliance/setup.ts index e470ba1..3b1ad93 100644 --- a/src/alliance/setup.ts +++ b/src/alliance/setup.ts @@ -1,6 +1,6 @@ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import type { ServerConfig } from '../core/config.js'; -import { resolveConfig } from '../core/config.js'; +import { resolveAllianceConfig } from './config.js'; import { setupCoreServer } from '../core/setup.js'; import { registerBuiltinUrlGenerators } from './url-builtins.js'; import { registerGuidedQueryTool } from './tools/guided-query-tool.js'; @@ -10,10 +10,10 @@ import { registerSuggestQueryParamsTool } from './tools/suggest-query-params-too * Create and configure the MCP server with the full Alliance tool surface: * all core tools plus `suggest_query_params`, `guided_query`, and built-in URL generators. * - * When `config` is omitted, resolves env via {@link resolveConfig} (rerank: env or default). + * When `config` is omitted, resolves env via {@link resolveAllianceConfig} (Alliance index/rerank defaults when unset). */ export async function setupAllianceServer(config?: ServerConfig): Promise { - const server = await setupCoreServer(config ?? resolveConfig({})); + const server = await setupCoreServer(config ?? resolveAllianceConfig({})); registerBuiltinUrlGenerators(); registerSuggestQueryParamsTool(server); registerGuidedQueryTool(server); diff --git a/src/cli.ts b/src/cli.ts index e5eedb6..89bbb95 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -136,9 +136,9 @@ Usage: pinecone-read-only-mcp [options] Options: --api-key TEXT Pinecone API key (or PINECONE_API_KEY) - --index-name TEXT Dense index (default: rag-hybrid, or PINECONE_INDEX_NAME) + --index-name TEXT Dense index (PINECONE_INDEX_NAME; Alliance CLI default: rag-hybrid) --sparse-index-name TEXT Sparse index [default: {index-name}-sparse] - --rerank-model TEXT Reranker model (default: bge-reranker-v2-m3, or PINECONE_RERANK_MODEL) + --rerank-model TEXT Reranker (PINECONE_RERANK_MODEL; Alliance CLI default: bge-reranker-v2-m3) --top-k N Default top-k for queries [env: PINECONE_TOP_K] --log-level LEVEL DEBUG | INFO | WARN | ERROR [default: INFO] --log-format FORMAT text | json [default: text] @@ -152,8 +152,8 @@ Options: Environment variables are documented in README.md (CLI overrides win when both are set). Examples: - pinecone-read-only-mcp --api-key YOUR_KEY - export PINECONE_API_KEY=YOUR_KEY && pinecone-read-only-mcp --index-name my-index + export PINECONE_API_KEY=YOUR_KEY && pinecone-read-only-mcp + pinecone-read-only-mcp --api-key YOUR_KEY --index-name my-index `); } diff --git a/src/constants.ts b/src/constants.ts index fad6ffd..3a0e43a 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -32,7 +32,7 @@ export const QUERY_DOCUMENTS_MAX_CHUNKS = 500; export const SERVER_NAME = 'Pinecone Read-Only MCP'; export { SERVER_VERSION } from './server-version.js'; -export const SERVER_INSTRUCTIONS = `Quickstart for AI clients: for most user questions, call \`guided_query\` with the user's question — it does namespace routing, suggestion, and execution in one shot and returns a decision_trace you can show the user. For manual flows, call \`list_namespaces\` -> \`suggest_query_params\` -> \`query\` (use preset fast/detailed/full per suggestion) or \`count\` (the suggest step is a mandatory gate). Use \`query_documents\` for full-document reading, \`keyword_search\` for exact-keyword retrieval against the sparse index, and \`generate_urls\` when records need URLs synthesized from metadata. +export const SERVER_INSTRUCTIONS = `Quickstart for AI clients: for most user questions, call \`guided_query\` with the user's question — it does namespace routing, suggestion, and execution in one shot and returns a decision_trace you can show the user. The server process requires \`PINECONE_INDEX_NAME\` (or \`--index-name\`) in addition to \`PINECONE_API_KEY\`; misconfiguration surfaces at startup, not as tool errors. For manual flows, call \`list_namespaces\` -> \`suggest_query_params\` -> \`query\` (use preset fast/detailed/full per suggestion) or \`count\` (the suggest step is a mandatory gate). Use \`query_documents\` for full-document reading, \`keyword_search\` for exact-keyword retrieval against the sparse index, and \`generate_urls\` when records need URLs synthesized from metadata. A semantic search server that provides hybrid search capabilities over Pinecone vector indexes with automatic namespace discovery. diff --git a/src/core/config.test.ts b/src/core/config.test.ts index d6da729..f63b17b 100644 --- a/src/core/config.test.ts +++ b/src/core/config.test.ts @@ -1,15 +1,24 @@ import { describe, expect, it } from 'vitest'; -import { DEFAULT_INDEX_NAME, DEFAULT_RERANK_MODEL, resolveConfig } from './config.js'; +import { resolveConfig } from './config.js'; describe('resolveConfig', () => { it('throws when API key is missing', () => { expect(() => resolveConfig({}, { PINECONE_API_KEY: '' })).toThrow(/Missing Pinecone API key/); }); - it('uses DEFAULT_INDEX_NAME when env and overrides omit indexName', () => { - const cfg = resolveConfig({ apiKey: 'sk-test' }, { PINECONE_API_KEY: 'sk-test' }); - expect(cfg.indexName).toBe(DEFAULT_INDEX_NAME); - expect(cfg.sparseIndexName).toBe(`${DEFAULT_INDEX_NAME}-sparse`); + it('throws when index name is missing', () => { + expect(() => resolveConfig({ apiKey: 'sk-test' }, { PINECONE_API_KEY: 'sk-test' })).toThrow( + /Missing Pinecone index name/ + ); + }); + + it('throws when PINECONE_INDEX_NAME is whitespace', () => { + expect(() => + resolveConfig( + { apiKey: 'sk-test' }, + { PINECONE_API_KEY: 'sk-test', PINECONE_INDEX_NAME: ' ' } + ) + ).toThrow(/Missing Pinecone index name/); }); it('uses PINECONE_INDEX_NAME from env when set', () => { @@ -21,20 +30,18 @@ describe('resolveConfig', () => { expect(cfg.sparseIndexName).toBe('my-index-sparse'); }); - it('uses DEFAULT_INDEX_NAME when env index is empty after trim', () => { - const cfg = resolveConfig( - { apiKey: 'sk-test' }, - { PINECONE_API_KEY: 'sk-test', PINECONE_INDEX_NAME: ' ' } - ); - expect(cfg.indexName).toBe(DEFAULT_INDEX_NAME); + it('uses indexName from overrides when set', () => { + const cfg = resolveConfig({ apiKey: 'sk-test', indexName: 'override-index' }); + expect(cfg.indexName).toBe('override-index'); + expect(cfg.sparseIndexName).toBe('override-index-sparse'); }); - it('uses DEFAULT_RERANK_MODEL when env and overrides omit rerankModel', () => { + it('omits rerankModel when env and overrides omit rerankModel', () => { const cfg = resolveConfig( { apiKey: 'sk-test', indexName: 'my-index' }, { PINECONE_API_KEY: 'sk-test', PINECONE_INDEX_NAME: 'my-index' } ); - expect(cfg.rerankModel).toBe(DEFAULT_RERANK_MODEL); + expect(cfg.rerankModel).toBeUndefined(); }); it('uses PINECONE_RERANK_MODEL from env when set', () => { diff --git a/src/core/config.ts b/src/core/config.ts index 0e2e08a..e2eaa85 100644 --- a/src/core/config.ts +++ b/src/core/config.ts @@ -18,17 +18,20 @@ export type LogFormat = 'text' | 'json'; * Unified runtime configuration for the MCP server. * * Built once by `parseCli()` (or constructed directly by library consumers) - * and threaded through setup. `apiKey` is required; `indexName` defaults via {@link DEFAULT_INDEX_NAME}. + * and threaded through setup. `apiKey` and `indexName` are required. */ export interface ServerConfig { /** Pinecone API key. Required. */ apiKey: string; - /** Dense (hybrid) index name (`PINECONE_INDEX_NAME` or {@link DEFAULT_INDEX_NAME}). */ + /** Dense (hybrid) index name (`PINECONE_INDEX_NAME` or CLI `--index-name`). Required. */ indexName: string; /** Sparse index name. Defaults to `${indexName}-sparse`. */ sparseIndexName: string; - /** Reranker model identifier (`PINECONE_RERANK_MODEL` or {@link DEFAULT_RERANK_MODEL}). */ - rerankModel: string; + /** + * Reranker model identifier when set via env or overrides. + * Omitted when unset — {@link PineconeClient} skips reranking unless a model is provided. + */ + rerankModel?: string; /** Default top-k when callers omit it on `query`. */ defaultTopK: number; /** Minimum log level emitted to stderr. */ @@ -48,12 +51,6 @@ export interface ServerConfig { /** Default per-call timeout for Pinecone requests, in milliseconds. */ export const DEFAULT_REQUEST_TIMEOUT_MS = 15_000; -/** Default Pinecone inference rerank model when `PINECONE_RERANK_MODEL` is unset. */ -export const DEFAULT_RERANK_MODEL = 'bge-reranker-v2-m3'; - -/** Default dense index name when `PINECONE_INDEX_NAME` is unset. */ -export const DEFAULT_INDEX_NAME = 'rag-hybrid'; - function asLogLevel(value: string | undefined, fallback: LogLevel): LogLevel { const allowed: LogLevel[] = ['DEBUG', 'INFO', 'WARN', 'ERROR']; return allowed.includes(value as LogLevel) ? (value as LogLevel) : fallback; @@ -77,7 +74,8 @@ function asBool(value: string | undefined, fallback: boolean): boolean { return fallback; } -function trimOptional(value: string | undefined): string | undefined { +/** Trim env/CLI strings; returns `undefined` for missing or whitespace-only values. */ +export function trimOptional(value: string | undefined): string | undefined { if (value === undefined) return undefined; const t = value.trim(); return t.length > 0 ? t : undefined; @@ -102,7 +100,7 @@ export interface ConfigOverrides { * Build a `ServerConfig` from CLI overrides, environment variables, and defaults. * CLI > env > default precedence is preserved. * - * @throws Error when no API key is provided. + * @throws Error when no API key or index name is provided. */ export function resolveConfig( overrides: ConfigOverrides, @@ -115,15 +113,18 @@ export function resolveConfig( ); } - const indexName = - trimOptional(overrides.indexName ?? env['PINECONE_INDEX_NAME']) ?? DEFAULT_INDEX_NAME; + const indexName = trimOptional(overrides.indexName ?? env['PINECONE_INDEX_NAME']); + if (!indexName) { + throw new Error( + 'Missing Pinecone index name: set PINECONE_INDEX_NAME or pass --index-name (or indexName in ConfigOverrides for library use).' + ); + } const sparseIndexName = trimOptional(overrides.sparseIndexName ?? env['PINECONE_SPARSE_INDEX_NAME']) ?? `${indexName}-sparse`; - const rerankModel = - trimOptional(overrides.rerankModel ?? env['PINECONE_RERANK_MODEL']) ?? DEFAULT_RERANK_MODEL; + const rerankModel = trimOptional(overrides.rerankModel ?? env['PINECONE_RERANK_MODEL']); const defaultTopK = overrides.defaultTopK ?? asPositiveInt(env['PINECONE_TOP_K'], DEFAULT_TOP_K); const logLevel = asLogLevel( @@ -148,7 +149,7 @@ export function resolveConfig( apiKey, indexName, sparseIndexName, - rerankModel, + ...(rerankModel !== undefined ? { rerankModel } : {}), defaultTopK, logLevel, logFormat, diff --git a/src/core/index.ts b/src/core/index.ts index 5b4e6af..99f9964 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -23,7 +23,7 @@ export { hasUrlGenerator, } from './server/url-registry.js'; export type { UrlGenerationResult, UrlGenerator, UrlGeneratorFn } from './server/url-registry.js'; -export { resolveConfig, DEFAULT_INDEX_NAME, DEFAULT_RERANK_MODEL } from './config.js'; +export { resolveConfig, trimOptional } from './config.js'; export type { ServerConfig, LogLevel, LogFormat, ConfigOverrides } from './config.js'; export { PineconeClient } from './pinecone-client.js'; export type { diff --git a/src/core/server/config-context.ts b/src/core/server/config-context.ts index 00e1cd8..4ce47d5 100644 --- a/src/core/server/config-context.ts +++ b/src/core/server/config-context.ts @@ -18,8 +18,8 @@ export function resetServerConfig(): void { * (namespace cache TTL, suggest-flow gate, etc.). * * When setup runs without an explicit config, falls back to `resolveConfig({})` - * (requires `PINECONE_API_KEY` and `PINECONE_INDEX_NAME` or the call throws). Embedders - * should pass `config` into `setupCoreServer(config)` / `setupAllianceServer(config)`. + * (requires `PINECONE_API_KEY` and `PINECONE_INDEX_NAME` or throws). Alliance apps should + * pass config from `resolveAllianceConfig()` into `setupAllianceServer(config)`. */ export function getServerConfig(): ServerConfig { if (!activeConfig) { diff --git a/src/index.ts b/src/index.ts index faa05b5..b02f10d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,14 +3,15 @@ /** * Pinecone Read-Only MCP CLI entry point. * - * Thin composition root: parseCli() -> resolveConfig() -> setupAllianceServer(config) + * Thin composition root: parseCli() -> resolveAllianceConfig() -> setupAllianceServer(config) * -> connect to stdio transport. */ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import * as dotenv from 'dotenv'; import { parseCli, printHelp, printVersion } from './cli.js'; -import { resolveConfig, type ServerConfig } from './core/config.js'; +import type { ServerConfig } from './core/config.js'; +import { resolveAllianceConfig } from './alliance/config.js'; import { PineconeClient } from './core/pinecone-client.js'; import { setPineconeClient } from './core/server/client-context.js'; import { setupAllianceServer } from './alliance/setup.js'; @@ -34,7 +35,7 @@ function buildConfigOrExit(): ServerConfig { } try { - return resolveConfig(parsed.overrides); + return resolveAllianceConfig(parsed.overrides); } catch (err) { const message = err instanceof Error ? err.message : String(err); process.stderr.write(`Error: ${message}\n`); diff --git a/src/types.ts b/src/types.ts index 8cfe2a5..9a1e726 100644 --- a/src/types.ts +++ b/src/types.ts @@ -8,9 +8,9 @@ export type PineconeMetadataValue = string | number | boolean | string[]; /** * Configuration for `new PineconeClient(config)`. * - * `apiKey` is required; `indexName` comes from {@link resolveConfig} (env or default). - * Values are expected to come from - * {@link resolveConfig} / CLI or an equivalent resolved object — `PineconeClient` + * `apiKey` and `indexName` are required via {@link resolveConfig} (env, CLI, or overrides). + * `rerankModel` is optional — omit to disable reranking. Alliance {@link resolveAllianceConfig} supplies a default rerank model when unset. + * Values are expected to come from a resolved `ServerConfig` — `PineconeClient` * does not read `process.env` directly. */ export interface PineconeClientConfig { From b4a0d4aa65501fef67386ab4d323c78f32e03954 Mon Sep 17 00:00:00 2001 From: zho Date: Tue, 2 Jun 2026 07:48:39 +0800 Subject: [PATCH 2/4] addressed AI reviews --- examples/alliance/library-embedding-demo.ts | 15 +++------------ src/alliance/config.ts | 7 +++++-- src/constants.ts | 2 +- src/core/config.test.ts | 2 +- 4 files changed, 10 insertions(+), 16 deletions(-) diff --git a/examples/alliance/library-embedding-demo.ts b/examples/alliance/library-embedding-demo.ts index bdde491..6b70309 100644 --- a/examples/alliance/library-embedding-demo.ts +++ b/examples/alliance/library-embedding-demo.ts @@ -12,12 +12,8 @@ * (tests). For isolated tenants in production, prefer one server per Node process. */ -import { - PineconeClient, - resolveConfig, - setPineconeClient, -} from '@will-cppa/pinecone-read-only-mcp'; -import { setupAllianceServer } from '@will-cppa/pinecone-read-only-mcp/alliance'; +import { PineconeClient, setPineconeClient } from '@will-cppa/pinecone-read-only-mcp'; +import { resolveAllianceConfig, setupAllianceServer } from '@will-cppa/pinecone-read-only-mcp/alliance'; async function main(): Promise { const apiKey = process.env['PINECONE_API_KEY']?.trim(); @@ -27,12 +23,7 @@ async function main(): Promise { ); return; } - const indexName = process.env['PINECONE_INDEX_NAME']?.trim(); - if (!indexName) { - console.log('Set PINECONE_INDEX_NAME to run this example. Skipping live setup in doc-only mode.'); - return; - } - const config = resolveConfig({ apiKey, indexName }); + const config = resolveAllianceConfig({ apiKey }); setPineconeClient( new PineconeClient({ diff --git a/src/alliance/config.ts b/src/alliance/config.ts index 619ea33..95e2cf6 100644 --- a/src/alliance/config.ts +++ b/src/alliance/config.ts @@ -27,9 +27,12 @@ export function resolveAllianceConfig( env: NodeJS.ProcessEnv = process.env ): ServerConfig { const indexName = - trimOptional(overrides.indexName ?? env['PINECONE_INDEX_NAME']) ?? ALLIANCE_DEFAULT_INDEX_NAME; + trimOptional(overrides.indexName) ?? + trimOptional(env['PINECONE_INDEX_NAME']) ?? + ALLIANCE_DEFAULT_INDEX_NAME; const rerankModel = - trimOptional(overrides.rerankModel ?? env['PINECONE_RERANK_MODEL']) ?? + trimOptional(overrides.rerankModel) ?? + trimOptional(env['PINECONE_RERANK_MODEL']) ?? ALLIANCE_DEFAULT_RERANK_MODEL; return resolveConfig({ ...overrides, indexName, rerankModel }, env); } diff --git a/src/constants.ts b/src/constants.ts index 3a0e43a..25910b4 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -32,7 +32,7 @@ export const QUERY_DOCUMENTS_MAX_CHUNKS = 500; export const SERVER_NAME = 'Pinecone Read-Only MCP'; export { SERVER_VERSION } from './server-version.js'; -export const SERVER_INSTRUCTIONS = `Quickstart for AI clients: for most user questions, call \`guided_query\` with the user's question — it does namespace routing, suggestion, and execution in one shot and returns a decision_trace you can show the user. The server process requires \`PINECONE_INDEX_NAME\` (or \`--index-name\`) in addition to \`PINECONE_API_KEY\`; misconfiguration surfaces at startup, not as tool errors. For manual flows, call \`list_namespaces\` -> \`suggest_query_params\` -> \`query\` (use preset fast/detailed/full per suggestion) or \`count\` (the suggest step is a mandatory gate). Use \`query_documents\` for full-document reading, \`keyword_search\` for exact-keyword retrieval against the sparse index, and \`generate_urls\` when records need URLs synthesized from metadata. +export const SERVER_INSTRUCTIONS = `Quickstart for AI clients: for most user questions, call \`guided_query\` with the user's question — it does namespace routing, suggestion, and execution in one shot and returns a decision_trace you can show the user. For the **core** entrypoint (\`setupCoreServer\` / package-root \`resolveConfig\`), \`PINECONE_INDEX_NAME\` (or \`--index-name\`) is required in addition to \`PINECONE_API_KEY\`. The **Alliance** CLI and \`resolveAllianceConfig\` default the index to \`rag-hybrid\` (and rerank to \`bge-reranker-v2-m3\`) when those env vars are omitted. Misconfiguration surfaces at startup, not as tool errors. For manual flows, call \`list_namespaces\` -> \`suggest_query_params\` -> \`query\` (use preset fast/detailed/full per suggestion) or \`count\` (the suggest step is a mandatory gate). Use \`query_documents\` for full-document reading, \`keyword_search\` for exact-keyword retrieval against the sparse index, and \`generate_urls\` when records need URLs synthesized from metadata. A semantic search server that provides hybrid search capabilities over Pinecone vector indexes with automatic namespace discovery. diff --git a/src/core/config.test.ts b/src/core/config.test.ts index f63b17b..0fba111 100644 --- a/src/core/config.test.ts +++ b/src/core/config.test.ts @@ -31,7 +31,7 @@ describe('resolveConfig', () => { }); it('uses indexName from overrides when set', () => { - const cfg = resolveConfig({ apiKey: 'sk-test', indexName: 'override-index' }); + const cfg = resolveConfig({ apiKey: 'sk-test', indexName: 'override-index' }, {}); expect(cfg.indexName).toBe('override-index'); expect(cfg.sparseIndexName).toBe('override-index-sparse'); }); From 323bf666b72ca7057895467484c932757ccaee5f Mon Sep 17 00:00:00 2001 From: zho Date: Wed, 3 Jun 2026 00:56:04 +0800 Subject: [PATCH 3/4] addressed William's reviews --- README.md | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 1345c41..a6dcad8 100644 --- a/README.md +++ b/README.md @@ -127,9 +127,9 @@ The server uses **process-global** memory for the suggest-flow gate (`suggest_qu Treat **`setupCoreServer()` / `setupAllianceServer()` as one logical server per Node process**: they mutate shared module singletons (suggest-flow map, namespaces cache, URL registry, config context, shared `PineconeClient` slot). A **second** setup call in the same process **throws** unless you call **`teardownServer()`** first. - **Generic bridge only:** `import { setupCoreServer, teardownServer, ... } from '@will-cppa/pinecone-read-only-mcp'` -- **Full Alliance surface (CLI parity):** `import { setupAllianceServer } from '@will-cppa/pinecone-read-only-mcp/alliance'` +- **Full Alliance surface (CLI parity):** `import { setupAllianceServer, resolveAllianceConfig } from '@will-cppa/pinecone-read-only-mcp/alliance'` -For the **generic bridge only**, see [examples/quickstart/mcp-demo.ts](examples/quickstart/mcp-demo.ts) (`setupCoreServer`). For the **full Alliance surface**, use `resolveConfig({ apiKey, indexName, ... })` → `setPineconeClient(new PineconeClient(...))` → `await setupAllianceServer(config)` → connect one MCP transport. See [examples/alliance/library-embedding-demo.ts](examples/alliance/library-embedding-demo.ts) and [docs/TOOLS.md](docs/TOOLS.md#suggest-flow-gate). +For the **generic bridge only**, see [examples/quickstart/mcp-demo.ts](examples/quickstart/mcp-demo.ts) (`setupCoreServer` + `resolveConfig` with required `indexName`). For the **full Alliance surface**, use `resolveAllianceConfig({ apiKey, ... })` (Alliance index/rerank defaults when omitted, same as the CLI) → `setPineconeClient(new PineconeClient(...))` → `await setupAllianceServer(config)` → connect one MCP transport. See [examples/alliance/library-embedding-demo.ts](examples/alliance/library-embedding-demo.ts) and [docs/TOOLS.md](docs/TOOLS.md#suggest-flow-gate). ### Custom URL generators @@ -141,15 +141,21 @@ Import `registerUrlGenerator` and types `UrlGeneratorFn` / `UrlGenerationResult` import { PineconeClient, registerUrlGenerator, - resolveConfig, setPineconeClient, type UrlGenerationResult, type UrlGeneratorFn, } from '@will-cppa/pinecone-read-only-mcp'; -import { setupAllianceServer } from '@will-cppa/pinecone-read-only-mcp/alliance'; - -const config = resolveConfig({ apiKey: '...', indexName: 'your-index' }); -setPineconeClient(new PineconeClient({ apiKey: config.apiKey, indexName: config.indexName })); +import { resolveAllianceConfig, setupAllianceServer } from '@will-cppa/pinecone-read-only-mcp/alliance'; + +const config = resolveAllianceConfig({ apiKey: '...' }); // optional: indexName, rerankModel +setPineconeClient( + new PineconeClient({ + apiKey: config.apiKey, + indexName: config.indexName, + sparseIndexName: config.sparseIndexName, + rerankModel: config.rerankModel, + }) +); const server = await setupAllianceServer(config); const myDocs: UrlGeneratorFn = (metadata): UrlGenerationResult => { From 7a0635c7b9ae54cf54417fda601ae74468d57fea Mon Sep 17 00:00:00 2001 From: zho Date: Wed, 3 Jun 2026 02:22:16 +0800 Subject: [PATCH 4/4] fixed LF error --- examples/alliance/.env.example | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/examples/alliance/.env.example b/examples/alliance/.env.example index fafc37a..cb15c32 100644 --- a/examples/alliance/.env.example +++ b/examples/alliance/.env.example @@ -1,14 +1,14 @@ -# C++ Alliance Pinecone deployment (examples/alliance) -# Copy to .env and fill in your API key. -# The published CLI (resolveAllianceConfig) applies these defaults when variables are omitted. - -PINECONE_API_KEY=your-pinecone-api-key-here - -# Alliance dense index (CLI default when unset: rag-hybrid) -PINECONE_INDEX_NAME=rag-hybrid - -# Optional: sparse index (default: {PINECONE_INDEX_NAME}-sparse) -# PINECONE_SPARSE_INDEX_NAME=rag-hybrid-sparse - -# Alliance rerank model (CLI default when unset: bge-reranker-v2-m3) -# PINECONE_RERANK_MODEL=bge-reranker-v2-m3 +# C++ Alliance Pinecone deployment (examples/alliance) +# Copy to .env and fill in your API key. +# The published CLI (resolveAllianceConfig) applies these defaults when variables are omitted. + +PINECONE_API_KEY=your-pinecone-api-key-here + +# Alliance dense index (CLI default when unset: rag-hybrid) +PINECONE_INDEX_NAME=rag-hybrid + +# Optional: sparse index (default: {PINECONE_INDEX_NAME}-sparse) +# PINECONE_SPARSE_INDEX_NAME=rag-hybrid-sparse + +# Alliance rerank model (CLI default when unset: bge-reranker-v2-m3) +# PINECONE_RERANK_MODEL=bge-reranker-v2-m3