Skip to content

cppalliance/pinecone-read-only-mcp-typescript

Repository files navigation

Pinecone Read-Only MCP (TypeScript)

npm version Node.js Version License: BSL-1.0 CI

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

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
  • TypeScript Support: Full TypeScript support with type definitions

Installation

As a Package

npm install @will-cppa/pinecone-read-only-mcp

Or using yarn:

yarn add @will-cppa/pinecone-read-only-mcp

Or using pnpm:

pnpm add @will-cppa/pinecone-read-only-mcp

Global Installation

npm install -g @will-cppa/pinecone-read-only-mcp

From Source

git clone https://github.com/CppDigest/pinecone-read-only-mcp-typescript.git
cd pinecone-read-only-mcp-typescript
npm install
npm run build

Configuration

The server requires a Pinecone API key and supports the following configuration options:

Environment Variables

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

Claude Desktop Configuration

Add to your claude_desktop_config.json:

{
  "mcpServers": {
    "pinecone-search": {
      "command": "npx",
      "args": ["-y", "@will-cppa/pinecone-read-only-mcp"],
      "env": {
        "PINECONE_API_KEY": "your-api-key-here"
      }
    }
  }
}

Or with explicit options:

{
  "mcpServers": {
    "pinecone-search": {
      "command": "npx",
      "args": [
        "-y",
        "@will-cppa/pinecone-read-only-mcp",
        "--api-key",
        "your-api-key-here",
        "--index-name",
        "your-index-name",
        "--rerank-model",
        "bge-reranker-v2-m3"
      ]
    }
  }
}

For a global installation:

{
  "mcpServers": {
    "pinecone-search": {
      "command": "pinecone-read-only-mcp",
      "args": ["--api-key", "your-api-key-here"]
    }
  }
}

Usage

Command Line

Run the server using npx (no installation required):

npx @will-cppa/pinecone-read-only-mcp --api-key YOUR_API_KEY

Or if installed globally:

pinecone-read-only-mcp --api-key YOUR_API_KEY

Or if installed locally in your project:

node node_modules/@will-cppa/pinecone-read-only-mcp/dist/index.js --api-key YOUR_API_KEY

Available Options

--api-key TEXT           Pinecone API key (or set PINECONE_API_KEY env var)
--index-name TEXT        Pinecone index name [default: rag-hybrid]
--rerank-model TEXT      Reranking model [default: bge-reranker-v2-m3]
--log-level TEXT         Logging level [default: INFO]
--help, -h               Show help message

Deployment

Production Readiness Defaults

  • Build now fails fast on TypeScript errors (npm run build no longer suppresses failures).
  • CI validates typecheck, lint, format, build, smoke run, tests, and package dry-run.
  • list_namespaces data is cached in-memory for 30 minutes to reduce repeated Pinecone calls.
  • Query/count flow has guardrails (suggest_query_params before execution) to prevent wasteful calls.

Deploy with npm Package

# install
npm i @will-cppa/pinecone-read-only-mcp

# run
npx @will-cppa/pinecone-read-only-mcp --api-key YOUR_API_KEY

Deploy with Docker

# build image
docker build -t pinecone-read-only-mcp:latest .

# run (stdio MCP server)
docker run --rm -i \
  -e PINECONE_API_KEY=YOUR_API_KEY \
  -e PINECONE_INDEX_NAME=rag-hybrid \
  pinecone-read-only-mcp:latest

Release Gate (recommended)

Before tagging/releasing:

npm run release:check

This runs full CI-equivalent checks and validates publish contents with npm pack --dry-run.

API Documentation

The server exposes the following tools via MCP:

list_namespaces

Discovers and lists all available namespaces in the configured Pinecone index, including metadata fields and record counts for each namespace.

Parameters: None

Returns: JSON object with namespace details including available metadata fields

metadata_fields values represent inferred field types from sampled records. Common values include: string, number, boolean, string[], and array.

Example response:

{
  "status": "success",
  "count": 3,
  "namespaces": [
    {
      "name": "namespace1",
      "record_count": 1500,
      "metadata_fields": {
        "author": "string",
        "year": "number",
        "category": "string"
      }
    },
    {
      "name": "namespace2",
      "record_count": 850,
      "metadata_fields": {
        "title": "string",
        "date": "string"
      }
    }
  ]
}

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.

Parameters:

Parameter Type Required Description
namespace string Yes Namespace to query (must match a name from list_namespaces)
user_query string Yes User’s question or intent (e.g. "list papers by John Doe with titles", "how many papers by Wong?")

Returns: suggested_fields (only fields that exist in that namespace), use_count_tool, recommended_tool, explanation, and namespace_found.

Example response:

{
  "status": "success",
  "suggested_fields": ["document_number", "title", "url", "author"],
  "use_count_tool": false,
  "recommended_tool": "query_fast",
  "explanation": "User asked for a list or browse; use minimal fields (no chunk_text) for smaller payload and cost.",
  "namespace_found": true
}

Use suggested_fields as the fields parameter when calling query tools.

guided_query

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.

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

Parameters:

Parameter Type Required Default Description
user_query string Yes - User question/intent
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
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.

generate_urls

Generates URLs for retrieved records when metadata does not contain url and URL is required.

Supported namespaces:

  • mailing
  • slack-Cpplang

Rules:

  • mailing: uses doc_id or thread_id
    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}

Parameters:

Parameter Type Required Description
namespace string Yes Namespace for URL-generation logic
records array Yes Retrieved records; each item can be either metadata itself or an object with metadata field

Returns: Per-record generated URL, generation method, and reason if unavailable.

count

Returns the unique document count matching a metadata filter and semantic query. Use for questions like "how many papers by John Doe?" instead of the query tool. For performance, the count tool uses semantic (dense) search only (no hybrid or lexical) and requests only document identifiers (document_number, url, doc_id)—no chunk content—then deduplicates by document.

Parameters:

Parameter Type Required Description
namespace string Yes Namespace to count in (use list_namespaces to discover)
query_text string Yes Search query; use a broad term (e.g. "paper", "document") when counting by metadata only
metadata_filter object No Same operators as query (e.g. {"author": {"$in": ["John Doe"]}} for wg21-papers)

Returns: JSON with count (unique documents, up to 10,000), and truncated: true if there are at least 10,000 matches.

Example response:

{
  "status": "success",
  "count": 42,
  "truncated": false,
  "namespace": "wg21-papers",
  "metadata_filter": { "author": { "$in": ["John Doe"] } }
}

keyword_search

Performs keyword (lexical/sparse-only) search over the dedicated sparse index (default: rag-hybrid-sparse, i.e. {PINECONE_INDEX_NAME}-sparse). Use for exact or keyword-style queries. Does not use the dense index or semantic reranking. Call list_namespaces first to discover namespaces; suggest_query_params is optional.

Parameters:

Parameter Type Required Default Description
query_text string Yes - Search query text (keyword/lexical match)
namespace string Yes - Namespace to search (use list_namespaces to discover)
top_k integer No 10 Number of results to return (1-100)
metadata_filter object No - Optional metadata filter (same operators as query)
fields string[] No - Optional field names to return; omit for all fields

Returns: JSON with status, query, namespace, index (sparse index name), result_count, and results (ids, metadata, scores). Result rows match the query tool shape (e.g. paper_number, title, author, url, content, score, reranked: false).

Example response:

{
  "status": "success",
  "query": "contracts C++",
  "namespace": "wg21-papers",
  "index": "rag-hybrid-sparse",
  "result_count": 5,
  "results": [
    {
      "paper_number": "P0548",
      "title": "Contracts for C++",
      "author": "John Doe",
      "url": "https://...",
      "content": "...",
      "score": 0.85,
      "reranked": false
    }
  ]
}

query

Performs hybrid semantic search over the specified namespace in the Pinecone index with optional metadata filtering. For count questions, use the count tool instead.

Parameters:

Parameter Type Required Default Description
query_text string Yes - Search query text
namespace string Yes - Namespace to search (use list_namespaces to discover)
top_k integer No 10 Number of results (1-100)
use_reranking boolean No true Enable semantic reranking
metadata_filter object No - Metadata filter to narrow results (e.g., {"author": "John", "year": 2023})
fields string[] No - Field names to return (e.g. ["document_number", "title", "url"]). Omit for all fields; include chunk_text for content. Reduces payload and cost.

Returns: JSON object with search results (only requested fields when fields is set), relevance scores, and metadata

Example response:

{
  "status": "success",
  "query": "your search query",
  "namespace": "namespace1",
  "metadata_filter": { "author": "John Doe" },
  "result_count": 10,
  "results": [
    {
      "paper_number": "DOC-001",
      "title": "Document Title",
      "author": "John Doe",
      "url": "https://example.com/doc",
      "content": "Document content preview...",
      "score": 0.9234,
      "reranked": true
    }
  ]
}

Using Metadata Filters:

Metadata filters allow you to narrow down search results based on document properties. First, use list_namespaces to see available metadata fields, then apply filters.

Supported Operators (10 total):

Operator Syntax Description Example
Equal $eq or value directly Exact match {"status": "published"} or {"status": {"$eq": "published"}}
Not Equal $ne Not equal to {"status": {"$ne": "draft"}}
Greater Than $gt Greater than {"year": {"$gt": 2022}}
Greater Than or Equal $gte Greater than or equal {"timestamp": {"$gte": 1704067200}}
Less Than $lt Less than {"score": {"$lt": 0.5}}
Less Than or Equal $lte Less than or equal {"priority": {"$lte": 3}}
In Array $in Value is in array field {"tags": {"$in": ["cpp", "contracts"]}} (only for array-type fields)
Not In Array $nin Value not in array field {"tags": {"$nin": ["draft", "archived"]}} (only for array-type fields)

Filter Examples:

// Exact match (implicit $eq) - works for single-value string fields
{"status": "published"}

// Exact string match - NOTE: requires full exact match
{"author": "John Doe"}  // Only matches if author field is exactly "John Doe"

// Array field contains value (use $in only for array-type fields)
{"tags": {"$in": ["cpp", "contracts"]}}  // Only if tags is stored as an array

// Numeric comparison
{"year": {"$gte": 2023}}

// Timestamp range (papers from last 2 years)
{"timestamp": {"$gte": 1704067200}}

// Multiple conditions on same field
{"score": {"$gt": 0.8, "$lt": 1.0}}
{"timestamp": {"$gte": 1704067200, "$lte": 1735689600}}

// Multiple fields (AND logic)
{
  "year": {"$gte": 2023},
  "status": "published",
  "timestamp": {"$gte": 1704067200}
}

// Array field not in list (only for array-type fields)
{"tags": {"$nin": ["draft", "template"]}}

Important Limitations:

  • String fields require EXACT match - No wildcards, partial matches, or substring searches
  • Comma-separated strings: If a field contains "John Doe, Herb Sutter", you cannot filter for just "John Doe"
    • You must match the entire string: {"author": "John Doe, Herb Sutter"}
    • To filter by individual authors, the data must be stored as an array field
  • $in and $nin operators: Only work on array-type fields, not comma-separated strings
  • Unsupported operators are rejected: Unknown operators (for example $regex) return a validation error
  • $in and $nin must use arrays: {"tags": {"$in": "cpp"}} is invalid; use {"tags": {"$in": ["cpp"]}}
  • Multiple conditions at the top level are combined with AND logic
  • Use comparison operators ($gt, $gte, $lt, $lte) for numeric and timestamp fields
  • Direct value assignment implies $eq (exact match)

How It Works

  1. Namespace Discovery: The list_namespaces tool queries your Pinecone index stats to discover available namespaces
  2. Hybrid Search: When querying, the tool searches both dense and sparse indexes in parallel
  3. Result Merging: Results from both indexes are merged and deduplicated
  4. Reranking (optional): The merged results are reranked using a semantic reranker for improved relevance

Development

Setup Development Environment

git clone https://github.com/CppDigest/pinecone-read-only-mcp-typescript.git
cd pinecone-read-only-mcp-typescript
npm install

Build

npm run build

Run Tests

npm test

Testing the keyword_search tool

  1. Connectivity and keyword search (script):
    Run the search test script (includes a keyword search step against the sparse index):

    PINECONE_API_KEY=your-key npm run test:search

    If the sparse index (rag-hybrid-sparse by default) does not exist or has no data, the keyword search step is skipped with a warning.

  2. Via MCP client:
    Start the server and call the keyword_search tool with query_text, namespace (from list_namespaces), and optional top_k or metadata_filter. Response shape is the same as the query tool (e.g. results with ids, metadata, scores; reranked is always false).

Code Quality

# Run linting
npm run lint

# Fix linting issues
npm run lint:fix

# Check formatting
npm run format:check

# Format code
npm run format

# Type check
npm run typecheck

Development Server

Run the server in development mode with auto-reload:

npm run dev -- --api-key YOUR_API_KEY

Contribution Guidelines

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature-name
  3. Make your changes and add tests
  4. Ensure all tests pass: npm test
  5. Ensure code quality checks pass: npm run lint && npm run format:check && npm run typecheck
  6. Commit your changes: git commit -am 'Add some feature'
  7. Push to the branch: git push origin feature-name
  8. Submit a pull request

Dependencies

Production Dependencies

Development Dependencies

Comparison with Python Version

This TypeScript implementation provides the same functionality as the Python version with the following benefits:

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

Troubleshooting

API Key Issues

If you see "Pinecone API key is required" error:

  1. Ensure PINECONE_API_KEY environment variable is set, OR
  2. Pass --api-key option when running the server

Index Not Found

If you see index-related errors:

  1. Verify your index name is correct
  2. Ensure your API key has access to the index
  3. Check that both your-index-name and your-index-name-sparse indexes exist

Connection Issues

If you experience connection issues:

  1. Check your internet connection
  2. Verify Pinecone service status
  3. Ensure firewall/proxy settings allow connections to Pinecone

License

This project is licensed under the Boost Software License 1.0 - see the LICENSE file for details.

Authors

Acknowledgements

This project uses:

  • Pinecone for vector storage and retrieval
  • Model Context Protocol for standardized AI integration
  • Hybrid search approach combining dense embeddings with sparse BM25-style retrieval

Related Projects

Support

For issues and questions:

Changelog

See CHANGELOG.md for a list of changes in each version.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors