# Directory Structure
```
├── .github
│   ├── actions
│   │   ├── npm-release
│   │   │   └── action.yml
│   │   └── setup
│   │       └── action.yml
│   ├── ISSUE_TEMPLATE
│   │   ├── bug_report.md
│   │   ├── config.yml
│   │   ├── documentation.md
│   │   └── feature_request.md
│   ├── pull_request_template.md
│   └── workflows
│       └── release-prod.yml
├── .gitignore
├── .prettierrc
├── CONTRIBUTING.md
├── gemini-extension.json
├── LICENSE
├── package-lock.json
├── package.json
├── README.md
├── src
│   ├── constants.ts
│   ├── error.ts
│   ├── index.ts
│   ├── server.ts
│   ├── tools
│   │   ├── database
│   │   │   ├── cascading-search.ts
│   │   │   ├── common
│   │   │   │   ├── rerank-model.ts
│   │   │   │   └── search-query.ts
│   │   │   ├── create-index-for-model.ts
│   │   │   ├── describe-index-stats.ts
│   │   │   ├── describe-index.ts
│   │   │   ├── index.ts
│   │   │   ├── list-indexes.ts
│   │   │   ├── rerank-documents.ts
│   │   │   ├── search-records.ts
│   │   │   └── upsert-records.ts
│   │   └── docs
│   │       ├── index.ts
│   │       └── search-docs.ts
│   └── version.ts
└── tsconfig.json
```
# Files
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
```
.DS_Store
.vscode
dist
node_modules
```
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
```
{
  "trailingComma": "all",
  "tabWidth": 2,
  "semi": true,
  "singleQuote": true,
  "bracketSpacing": false,
  "printWidth": 100
}
```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
```markdown
# Pinecone Developer MCP Server
The [Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP) is a standard that allows coding assistants and other AI tools to interact with platforms like Pinecone. The Pinecone Developer MCP Server allows you to connect these tools with Pinecone projects and documentation.
Once connected, AI tools can:
* Search [Pinecone documentation](https://docs.pinecone.io) to answer questions accurately.
* Help you configure indexes based on your application's needs.
* Generate code informed by your index configuration and data, as well as Pinecone documentation and examples.
* Upsert and search for data in indexes, allowing you to test queries and evaluate results within your dev environment.
See the [docs](https://docs.pinecone.io/guides/operations/mcp-server) for more detailed information.
This MCP server is focused on improving the experience of developers working with Pinecone as part of their technology stack. It is intended for use with coding assistants. Pinecone also offers the [Assistant MCP](https://github.com/pinecone-io/assistant-mcp), which is designed to provide AI assistants with relevant context sourced from your knowledge base.
## Setup
To configure the MCP server to access your Pinecone project, you will need to generate an API key using the [console](https://app.pinecone.io). Without an API key, your AI tool will still be able to search documentation. However, it will not be able to manage or query your indexes.
The MCP server requires [Node.js](https://nodejs.org). Ensure that `node` and `npx` are available in your `PATH`.
Next, you will need to configure your AI assistant to use the MCP server.
### Configure Cursor
To add the Pinecone MCP server to a project, create a `.cursor/mcp.json` file in the project root (if it doesn't already exist) and add the following configuration:
```
{
  "mcpServers": {
    "pinecone": {
      "command": "npx",
      "args": [
        "-y", "@pinecone-database/mcp"
      ],
      "env": {
        "PINECONE_API_KEY": "<your pinecone api key>"
      }
    }
  }
}
```
You can check the status of the server in **Cursor Settings > MCP**.
To enable the server globally, add the configuration to the `.cursor/mcp.json` in your home directory instead.
It is recommended to use rules to instruct Cursor on proper usage of the MCP server. Check out the [docs](https://docs.pinecone.io/guides/operations/mcp-server#configure-cursor) for some suggestions.
### Configure Claude desktop
Use Claude desktop to locate the `claude_desktop_config.json` file by navigating to **Settings > Developer > Edit Config**. Add the following configuration:
```
{
  "mcpServers": {
    "pinecone": {
      "command": "npx",
      "args": [
        "-y", "@pinecone-database/mcp"
      ],
      "env": {
        "PINECONE_API_KEY": "<your pinecone api key>"
      }
    }
  }
}
```
Restart Claude desktop. On the new chat screen, you should see a hammer (MCP) icon appear with the new MCP tools available.
### Use as a Gemini CLI extension
To install this as a [Gemini CLI](https://github.com/google-gemini/gemini-cli) extension, run the following command:
```
gemini extensions install https://github.com/pinecone-io/pinecone-mcp
```
You will need to provide your Pinecone API key in the `PINECONE_API_KEY` environment variable.
```
export PINECONE_API_KEY=<your pinecone api key>
```
When you run `gemini` and press `ctrl+t`, `pinecone` should now be shown in the list of installed MCP servers.
## Usage
Once configured, your AI tool will automatically make use of the MCP to interact with Pinecone. You may be prompted for permission before a tool can be used. Try asking your AI assistant to set up an example index, upload sample data, or search for you!
### Tools
Pinecone Developer MCP Server provides the following tools for AI assistants to use:
- `search-docs`: Search the official Pinecone documentation.
- `list-indexes`: Lists all Pinecone indexes.
- `describe-index`: Describes the configuration of an index.
- `describe-index-stats`: Provides statistics about the data in the index, including the  number of records and available namespaces.
- `create-index-for-model`: Creates a new index that uses an integrated inference model to embed text as vectors.
- `upsert-records`: Inserts or updates records in an index with integrated inference.
- `search-records`: Searches for records in an index based on a text query, using integrated inference for embedding. Has options for metadata filtering and reranking.
- `cascading-search`: Searches for records across multiple indexes, deduplicating and reranking the results.
- `rerank-documents`: Reranks a collection of records or text documents using a specialized reranking model.
### Limitations
Only indexes with integrated inference are supported. Assistants, indexes without integrated inference, standalone embeddings, and vector search are not supported.
## Contributing
We welcome your collaboration in improving the developer MCP experience. Please submit issues in the [GitHub issue tracker](https://github.com/pinecone-io/pinecone-mcp/issues). Information about contributing can be found in [CONTRIBUTING.md](CONTRIBUTING.md).
```
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
```markdown
# Contributing to Pinecone Developer MCP Server
We welcome community contributions to the Pinecone Developer MCP Server! This document provides guidelines and instructions for contributing to this project.
## Getting Started
1. Fork the repository.
2. Clone your fork: `git clone https://github.com/your-username/pinecone-mcp.git`
3. Install dependencies: `npm install`
4. Build the MCP server: `npm run build`
## Running the MCP server
To enable database features, you will need to generate an API key in the [Pinecone console](https://app.pinecone.io). Replace `<your-api-key>` in the following instructions with the API key value.
Run the server:
```
PINECONE_API_KEY=<your-api-key> npm start
```
Using MCP Inspector:
```
npx @modelcontextprotocol/inspector -e PINECONE_API_KEY=<your-api-key> npm start
```
Test with an AI tool or coding assistant:
```
{
  "mcpServers": {
    "pinecone": {
      "command": "node",
      "args": [
        "/path/to/pinecone-mcp/dist/index.js"
      ],
      "env": {
        "PINECONE_API_KEY": "<your-api-key>",
      }
    }
  }
}
```
## Development process
1. Create a new branch for your changes.
2. Make your changes.
3. Test your changes with MCP Inspector or an AI tool.
4. Run `npm run format` to format your code.
5. Submit a pull request.
## Pull request guidelines
- Follow existing code style.
- Update documentation as needed.
- Keep changes focused.
- Provide a clear description of changes.
## Reporting issues
- Use the [GitHub issue tracker](https://github.com/pinecone-io/pinecone-mcp/issues).
- Search for existing issues before creating a new one.
- Provide clear reproduction steps.
## License
By contributing to this project, you agree that your contributions will be licensed under the [Apache License version 2.0](LICENSE).
```
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
```yaml
blank_issues_enabled: false
contact_links:
  - name: Pinecone Community Forum
    url: https://community.pinecone.io/
    about: For support, please see the community forum.
```
--------------------------------------------------------------------------------
/gemini-extension.json:
--------------------------------------------------------------------------------
```json
{
  "name": "pinecone-mcp",
  "version": "1.0.0",
  "mcpServers": {
    "pinecone": {
      "command": "npx",
      "args": [
        "-y", "@pinecone-database/mcp"
      ]
    }
  }
}
```
--------------------------------------------------------------------------------
/src/tools/docs/index.ts:
--------------------------------------------------------------------------------
```typescript
import {McpServer} from '@modelcontextprotocol/sdk/server/mcp.js';
import {addSearchDocsTool} from './search-docs.js';
export default async function addDocsTools(server: McpServer) {
  addSearchDocsTool(server);
}
```
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
```markdown
### Overview
> Describe what was changed and why.
### Link to issue
> Include a link to any issue addressed by this PR.
### Type
- [ ] Bug fix
- [ ] New feature
- [ ] Improvement
- [ ] Infrastructure change
- [ ] Documentation
```
--------------------------------------------------------------------------------
/src/constants.ts:
--------------------------------------------------------------------------------
```typescript
const DOCS_ASSISTANT_BASE_URL = 'https://prod-1-data.ke.pinecone.io';
const DOCS_ASSISTANT_NAME = 'pinecone-docs';
export const DOCS_MCP_URL = `${DOCS_ASSISTANT_BASE_URL}/mcp/assistants/${DOCS_ASSISTANT_NAME}`;
export const {PINECONE_API_KEY} = process.env;
```
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/documentation.md:
--------------------------------------------------------------------------------
```markdown
---
name: Documentation
about: Report an issue in our docs
title: '[Docs] '
labels: 'documentation'
assignees: ''
---
**Description**
Describe the issue with our documentation.
**Suggested solution**
Describe how this issue could be fixed or improved.
**Link to page**
Add a link to the documentation page where the issue occurred.
```
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
```json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "Node16",
    "moduleResolution": "Node16",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}
```
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
```typescript
#!/usr/bin/env node
import {StdioServerTransport} from '@modelcontextprotocol/sdk/server/stdio.js';
import setupServer from './server.js';
async function main() {
  const server = await setupServer();
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error('Pinecone MCP Server running on stdio');
}
main().catch((error) => {
  console.error('Fatal error in main():', error);
  process.exit(1);
});
```
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
```markdown
---
name: Feature request
about: Suggest an idea for this project
title: '[Feature Request]'
labels: 'enhancement'
assignees: ''
---
**Motivation**
Explain why you are requesting this feature. E.g. "Being able to do x would allow me to...".
**Suggested solution**
Describe what you want to happen.
**Alternatives**
List any alternative solutions or features you've considered.
**Additional context**
Add any other context about the feature request here.
```
--------------------------------------------------------------------------------
/src/tools/database/list-indexes.ts:
--------------------------------------------------------------------------------
```typescript
import {McpServer} from '@modelcontextprotocol/sdk/server/mcp.js';
import {Pinecone} from '@pinecone-database/pinecone';
const INSTRUCTIONS = `List all Pinecone indexes`;
export function addListIndexesTool(server: McpServer, pc: Pinecone) {
  server.tool('list-indexes', INSTRUCTIONS, {}, async ({}) => {
    const indexes = await pc.listIndexes();
    return {
      content: [
        {
          type: 'text',
          text: JSON.stringify(indexes, null, 2),
        },
      ],
    };
  });
}
```
--------------------------------------------------------------------------------
/src/version.ts:
--------------------------------------------------------------------------------
```typescript
import {readFileSync} from 'fs';
import {dirname, join} from 'path';
import {fileURLToPath} from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
function getVersion(): string {
  try {
    const packageJsonPath = join(__dirname, '../package.json');
    const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
    return packageJson.version || 'unknown';
  } catch (err) {
    console.error('Unable to retrieve version:', err);
    return 'unknown';
  }
}
export const PINECONE_MCP_VERSION = getVersion();
```
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
```markdown
---
name: Bug
about: Report an issue with the MCP server
title: '[Bug] '
labels: bug
assignees: ''
---
**Bug description**
Describe what happened, and what you expected to happen instead.
**Error information**
Include any error messages, stack traces, or relevant logs.
**Steps to reproduce the issue**
Include steps to reproduce the behavior. If you have sample code or a script that can be used to replicate the bug, please include that as well.
**Environment**
- OS and version:
- Node version:
- MCP client or AI tool:
**Additional context**
Add any other relevant information here.
```
--------------------------------------------------------------------------------
/src/tools/database/describe-index.ts:
--------------------------------------------------------------------------------
```typescript
import {McpServer} from '@modelcontextprotocol/sdk/server/mcp.js';
import {Pinecone} from '@pinecone-database/pinecone';
import {z} from 'zod';
const INSTRUCTIONS = 'Describe the configuration of a Pinecone index';
const SCHEMA = {
  name: z.string().describe('The index to describe.'),
};
export function addDescribeIndexTool(server: McpServer, pc: Pinecone) {
  server.tool('describe-index', INSTRUCTIONS, SCHEMA, async ({name}) => {
    const indexInfo = await pc.describeIndex(name);
    return {
      content: [
        {
          type: 'text',
          text: JSON.stringify(indexInfo, null, 2),
        },
      ],
    };
  });
}
```
--------------------------------------------------------------------------------
/src/tools/database/common/search-query.ts:
--------------------------------------------------------------------------------
```typescript
import { z } from 'zod';
export const SEARCH_QUERY_SCHEMA = z
  .object({
    topK: z.number().describe('The number of results to return.'),
    inputs: z.object({
      text: z.string().describe('The text to search for.'),
    }),
    filter: z
      .object({})
      .passthrough()
      .optional()
      .describe(
        `A filter can be used to narrow down results. Use the syntax of
        MongoDB's query and projection operators: $eq, $ne, $gt, $gte, $lt,
        $lte, $in, $nin, $exists, $and, $or. Make sure the records in the index
        contain the fields that you are filtering on.`,
      ),
  })
  .describe('A query to search for records.');
```
--------------------------------------------------------------------------------
/src/tools/database/common/rerank-model.ts:
--------------------------------------------------------------------------------
```typescript
import {z} from 'zod';
export const RERANK_MODEL_SCHEMA = z.enum([
  'cohere-rerank-3.5',
  'bge-reranker-v2-m3',
  'pinecone-rerank-v0',
]).describe(`Choose a reranking model:
- "cohere-rerank-3.5" is Cohere's leading reranking model, balancing performance
and latency for a wide range of enterprise search applications.
- "bge-reranker-v2-m3" is a high-performance, multilingual reranking model that
works well on messy data and short queries expected to return medium-length
passages of text (1-2 paragraphs).
- "pinecone-rerank-v0" is a state of the art reranking model that out-performs
competitors on widely accepted benchmarks. It can handle chunks up to 512 tokens
(1-2 paragraphs).`);
```
--------------------------------------------------------------------------------
/src/tools/database/describe-index-stats.ts:
--------------------------------------------------------------------------------
```typescript
import {McpServer} from '@modelcontextprotocol/sdk/server/mcp.js';
import {Pinecone} from '@pinecone-database/pinecone';
import {z} from 'zod';
const INSTRUCTIONS = 'Describe the statistics of a Pinecone index and its namespaces';
const SCHEMA = {
  name: z.string().describe('The index to describe.'),
};
export function addDescribeIndexStatsTool(server: McpServer, pc: Pinecone) {
  server.tool('describe-index-stats', INSTRUCTIONS, SCHEMA, async ({name}) => {
    const index = pc.index(name);
    const indexStats = await index.describeIndexStats();
    return {
      content: [
        {
          type: 'text',
          text: JSON.stringify({...indexStats, indexFullness: undefined}, null, 2),
        },
      ],
    };
  });
}
```
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
```json
{
  "name": "@pinecone-database/mcp",
  "version": "0.1.17",
  "type": "module",
  "main": "dist/index.js",
  "bin": {
    "pinecone-mcp": "dist/index.js"
  },
  "private": false,
  "files": [
    "dist"
  ],
  "license": "Apache-2.0",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/pinecone-io/pinecone-mcp.git"
  },
  "scripts": {
    "build": "tsc && chmod +x dist/index.js",
    "format": "prettier --write 'src/**/*.ts' '*.json' '.prettierrc'",
    "start": "node dist/index.js"
  },
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.18.0",
    "@pinecone-database/pinecone": "^5.1.1",
    "zod": "^3.24.3"
  },
  "devDependencies": {
    "@types/node": "^22.14.1",
    "prettier": "^3.5.3",
    "typescript": "^5.8.3"
  }
}
```
--------------------------------------------------------------------------------
/.github/actions/setup/action.yml:
--------------------------------------------------------------------------------
```yaml
name: Setup
description: Node setup, install, and build
inputs:
  git_email:
    description: Git email
    required: true
  git_username:
    description: Git author name
    required: true
  node_version:
    description: Node.js version
    required: true
runs:
  using: composite
  steps:
    - name: Setup Node
      uses: actions/setup-node@v4
      with:
        node-version: 20.x
        registry-url: https://registry.npmjs.org
        cache: npm
    - name: Configure git user
      shell: bash
      run: |
        git config --global user.email "${{ inputs.git_email }}"
        git config --global user.name "${{ inputs.git_username }}"
    - name: Install npm packages
      shell: bash
      run: npm ci --ignore-scripts
    - name: Build typescript
      shell: bash
      run: npm run build
```
--------------------------------------------------------------------------------
/.github/actions/npm-release/action.yml:
--------------------------------------------------------------------------------
```yaml
name: 'npm release'
description: 'Publish code changes to npmjs.org'
inputs:
  npm_token:
    description: 'npm token for publishing. Login to npmjs.org to generate this.'
    required: true
  release_type:
    description: 'The semver release type to perform [major, minor, patch]'
    required: true
outputs:
  release_tag:
    description: 'The name of the created git tag'
    value: ${{ steps.release-tag-step.outputs.release-tag }}
runs:
  using: 'composite'
  steps:
    - name: 'Bump version'
      shell: bash
      run: npm version ${{ inputs.release_type }} -m "Publish release %s"
    - name: 'Output release tag'
      shell: bash
      id: release-tag-step
      run: echo "release-tag=$(git describe --tags)" >> $GITHUB_OUTPUT
    - name: 'Publish to npm'
      run: npm publish
      shell: bash
      env:
        NODE_AUTH_TOKEN: ${{ inputs.npm_token }}
    - name: 'Push changes'
      shell: bash
      run: git push --follow-tags
```
--------------------------------------------------------------------------------
/src/tools/docs/search-docs.ts:
--------------------------------------------------------------------------------
```typescript
import {Client} from '@modelcontextprotocol/sdk/client/index.js';
import {StreamableHTTPClientTransport} from '@modelcontextprotocol/sdk/client/streamableHttp.js';
import {McpServer} from '@modelcontextprotocol/sdk/server/mcp.js';
import {z} from 'zod';
import {DOCS_MCP_URL} from '../../constants.js';
import {PINECONE_MCP_VERSION} from '../../version.js';
const INSTRUCTIONS = 'Search Pinecone documentation for relevant information';
const SCHEMA = {
  query: z.string().describe('The text to search for.'),
};
type SearchDocsResult = {
  content: {
    type: 'text';
    text: string;
  }[];
};
export function addSearchDocsTool(server: McpServer) {
  server.tool('search-docs', INSTRUCTIONS, SCHEMA, async ({query}) => {
    const httpTransport = new StreamableHTTPClientTransport(new URL(DOCS_MCP_URL));
    const client = new Client({
      name: 'pinecone-docs',
      version: PINECONE_MCP_VERSION,
    });
    await client.connect(httpTransport);
    return (await client.callTool({
      name: 'get_context',
      arguments: {query},
    })) as SearchDocsResult;
  });
}
```
--------------------------------------------------------------------------------
/.github/workflows/release-prod.yml:
--------------------------------------------------------------------------------
```yaml
name: 'Release'
on:
  workflow_dispatch:
    inputs:
      release_type:
        description: 'Release type'
        required: true
        type: choice
        default: 'patch'
        options:
          - 'patch' # bug fixes
          - 'minor' # new features, backwards compatible
          - 'major' # breaking changes
# prevent concurrent releases
concurrency:
  group: npm-release
  cancel-in-progress: true
jobs:
  version-and-release:
    name: Release to NPM
    runs-on: ubuntu-latest
    outputs:
      tagName: ${{ steps.npm-release.outputs.release_tag }}
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Setup
        uses: ./.github/actions/setup
        with:
          git_email: [email protected]
          git_username: ${{ github.actor }}
          node_version: 20.x
      - name: Version and publish to npm
        id: npm-release
        uses: ./.github/actions/npm-release
        with:
          npm_token: ${{ secrets.NPM_TOKEN }}
          release_type: ${{ github.event.inputs.release_type }}
      - run: echo "${{ steps.npm-release.outputs.release_tag }} was published"
```
--------------------------------------------------------------------------------
/src/tools/database/index.ts:
--------------------------------------------------------------------------------
```typescript
import {McpServer} from '@modelcontextprotocol/sdk/server/mcp.js';
import {Pinecone} from '@pinecone-database/pinecone';
import {PINECONE_API_KEY} from '../../constants.js';
import {PINECONE_MCP_VERSION} from '../../version.js';
import {addCascadingSearchTool} from './cascading-search.js';
import {addCreateIndexForModelTool} from './create-index-for-model.js';
import {addDescribeIndexStatsTool} from './describe-index-stats.js';
import {addDescribeIndexTool} from './describe-index.js';
import {addListIndexesTool} from './list-indexes.js';
import {addRerankDocumentsTool} from './rerank-documents.js';
import {addSearchRecordsTool} from './search-records.js';
import {addUpsertRecordsTool} from './upsert-records.js';
export default function addDatabaseTools(server: McpServer) {
  if (!PINECONE_API_KEY) {
    console.error('Skipping database tools -- PINECONE_API_KEY environment variable is not set.');
    return;
  }
  const pc = new Pinecone({
    apiKey: PINECONE_API_KEY,
    sourceTag: `pinecone-mcp@${PINECONE_MCP_VERSION}`,
  });
  addListIndexesTool(server, pc);
  addDescribeIndexTool(server, pc);
  addDescribeIndexStatsTool(server, pc);
  addCreateIndexForModelTool(server, pc);
  addUpsertRecordsTool(server, pc);
  addSearchRecordsTool(server, pc);
  addRerankDocumentsTool(server, pc);
  addCascadingSearchTool(server, pc);
}
```
--------------------------------------------------------------------------------
/src/tools/database/rerank-documents.ts:
--------------------------------------------------------------------------------
```typescript
import {McpServer} from '@modelcontextprotocol/sdk/server/mcp.js';
import {Pinecone} from '@pinecone-database/pinecone';
import {z} from 'zod';
import {RERANK_MODEL_SCHEMA} from './common/rerank-model.js';
const INSTRUCTIONS = `Rerank a set of documents based on a query`;
export const RerankDocumentsOptions = z
  .object({
    topN: z.number().describe('The number of results to return after reranking.'),
    rankFields: z
      .array(z.string())
      .optional()
      .describe(
        `The fields to rerank on. This should only be included if the documents
        are records. The "bge-reranker-v2-m3" and "pinecone-rerank-v0" models
        support only a single rerank field. "cohere-rerank-3.5" supports
        multiple rerank fields.`,
      ),
  })
  .optional();
const Documents = z
  .union([
    z
      .array(z.string())
      .describe('An array of text documents to rerank.'),
    z
      .array(z.record(z.string(), z.string()))
      .describe('An array of records to rerank.'),
  ])
  .describe(
    `A set of documents to rerank. Can either be an array of text documents
    (strings) or an array of records.`,
  );
export const SCHEMA = {
  model: RERANK_MODEL_SCHEMA,
  query: z.string().describe('The query to rerank documents against.'),
  documents: Documents,
  options: RerankDocumentsOptions,
};
export function addRerankDocumentsTool(server: McpServer, pc: Pinecone) {
  server.tool(
    'rerank-documents',
    INSTRUCTIONS,
    SCHEMA,
    async ({model, query, documents, options}) => {
      const results = pc.inference.rerank(model, query, documents, options);
      return {
        content: [{type: 'text', text: JSON.stringify(results, null, 2)}],
      };
    },
  );
}
```
--------------------------------------------------------------------------------
/src/server.ts:
--------------------------------------------------------------------------------
```typescript
import {McpServer} from '@modelcontextprotocol/sdk/server/mcp.js';
import {z} from 'zod';
import {errorMap} from './error.js';
import addDatabaseTools from './tools/database/index.js';
import addDocsTools from './tools/docs/index.js';
import {PINECONE_MCP_VERSION} from './version.js';
const SERVER_INSTRUCTIONS = `Pinecone is a vector database that provides AI
tools and applications with fast, scalable, and flexible vector search.
Instructions for usage:
- Always search the documentation before attempting to explain or write code for
Pinecone. When writing code, always refer to examples from the documentation. Do
not make assumptions about usage. Always use the latest SDK version.
- If the code uses an index or namespace, make sure you use the correct names I
have configured or help me create new ones.
- Always use a consistent schema for records in an index. Do not use different
field names in the same index. Always put the text content in the field named in
the index's "fieldMap". Do not use objects as field values. Do not include a
"metadata" field.
- When searching for records, make sure to use a query that accurately reflects
my needs. Only use a filter if you are sure it will help me find the records I
need. Craft filters knowing the schema of the data in the index.
- If you receive an error, read the error response to understand what went
wrong. "MCP error -32602" means your input was wrong. Correct your input by
following the instructions carefully.`;
export default async function setupServer() {
  z.setErrorMap(errorMap);
  const server = new McpServer(
    {
      name: 'pinecone-mcp',
      version: PINECONE_MCP_VERSION,
    },
    {
      instructions: SERVER_INSTRUCTIONS,
    },
  );
  await addDocsTools(server);
  addDatabaseTools(server);
  return server;
}
```
--------------------------------------------------------------------------------
/src/error.ts:
--------------------------------------------------------------------------------
```typescript
import {z} from 'zod';
const FIELD_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$';
const FIELD_START_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$';
const isFieldName = (name: string) => {
  if (name.length === 0) {
    return false;
  }
  return (
    FIELD_START_CHARS.includes(name[0]) && [...name].every((c: string) => FIELD_CHARS.includes(c))
  );
};
const displayPath = (path: (string | number)[]) => {
  let display = `${path[0]}`;
  for (const part of path.slice(1)) {
    if (typeof part === 'number') {
      display += `[${part}]`;
    } else if (isFieldName(part)) {
      display += `.${part}`;
    } else {
      display += `["${part}"]`;
    }
  }
  return display;
};
const formatUnionError = (issue: z.ZodInvalidUnionIssue) => {
  const invalidTypeIssues = issue.unionErrors.flatMap((e) =>
    e.issues.filter((i) => i.code === z.ZodIssueCode.invalid_type),
  );
  const expectedTypes = invalidTypeIssues.map((i) => i.expected).join(' | ');
  const receivedType = invalidTypeIssues[0].received;
  return `Expected ${expectedTypes}, received ${receivedType}`;
};
export const errorMap: z.ZodErrorMap = (issue, ctx) => {
  let message = ctx.defaultError;
  if (issue.code === z.ZodIssueCode.invalid_type) {
    message = `Expected ${issue.expected}, received ${issue.received}`;
  } else if (issue.code === z.ZodIssueCode.invalid_enum_value) {
    message = `Expected ${issue.options.map((o) => `"${o}"`).join(' | ')}`;
  } else if (issue.code === z.ZodIssueCode.unrecognized_keys) {
    message =
      `Unrecognized key${issue.keys.length === 1 ? '' : 's'}: ` +
      `${issue.keys.map((k) => `"${k}"`).join(', ')}`;
  } else if (issue.code === z.ZodIssueCode.invalid_union) {
    message = formatUnionError(issue);
  } else {
    message = ctx.defaultError;
  }
  return {message: `${displayPath(issue.path)}: ${message}`};
};
```
--------------------------------------------------------------------------------
/src/tools/database/search-records.ts:
--------------------------------------------------------------------------------
```typescript
import {McpServer} from '@modelcontextprotocol/sdk/server/mcp.js';
import {Pinecone} from '@pinecone-database/pinecone';
import {z} from 'zod';
import {RERANK_MODEL_SCHEMA} from './common/rerank-model.js';
import {SEARCH_QUERY_SCHEMA} from './common/search-query.js';
const INSTRUCTIONS = 'Search an index for records that are similar to the query text';
const RERANK_SCHEMA = z
  .object({
    model: RERANK_MODEL_SCHEMA,
    topN: z
      .number()
      .optional()
      .describe(
        `The number of results to return after reranking. Must be less than or
        equal to the value of "query.topK".`,
      ),
    rankFields: z.array(z.string()).describe(
      `The fields to rerank on. This should include the field name specified
      in the index's "fieldMap". The "bge-reranker-v2-m3" and
      "pinecone-rerank-v0" models support only a single rerank field.
      "cohere-rerank-3.5" supports multiple rerank fields.`,
    ),
    query: z
      .string()
      .optional()
      .describe(
        `An optional query to rerank documents against. If not specified, the
        same query will be used for both the initial search and the reranking.`,
      ),
  })
  .optional()
  .describe(
    `Reranking can help determine which of the returned records are most
    relevant. When reranking, use a "query" with a "topK" that returns more
    results than you need; then use "rerank" to select the most relevant
    "topN" results.`,
  );
const SCHEMA = {
  name: z.string().describe('The index to search.'),
  namespace: z.string().describe('The namespace to search.'),
  query: SEARCH_QUERY_SCHEMA,
  rerank: RERANK_SCHEMA,
};
export function addSearchRecordsTool(server: McpServer, pc: Pinecone) {
  server.tool('search-records', INSTRUCTIONS, SCHEMA, async ({name, namespace, query, rerank}) => {
    const ns = pc.index(name).namespace(namespace);
    const results = await ns.searchRecords({query, rerank});
    return {
      content: [
        {
          type: 'text',
          text: JSON.stringify(results, null, 2),
        },
      ],
    };
  });
}
```
--------------------------------------------------------------------------------
/src/tools/database/upsert-records.ts:
--------------------------------------------------------------------------------
```typescript
import {McpServer} from '@modelcontextprotocol/sdk/server/mcp.js';
import {Pinecone} from '@pinecone-database/pinecone';
import {z} from 'zod';
const INSTRUCTIONS = 'Insert or update records in a Pinecone index';
const FIELD_VALUE_SCHEMA = z
  .any()
  .refine(
    (value) => {
      if (typeof value === 'object' && !Array.isArray(value) && value !== null) {
        return false;
      }
      return true;
    },
    {
      message: 'A record must not contain nested objects.',
    },
  )
  .refine(
    (value) => {
      if (value === null) {
        return false;
      }
      return true;
    },
    {
      message: 'A field value must not be null.',
    },
  )
  .refine(
    (value) => {
      if (Array.isArray(value)) {
        return value.every((item) => typeof item === 'string');
      }
      return true;
    },
    {
      message: 'Array field values must contain only strings.',
    },
  )
  .describe(
    `A field value. Must be a string, number, boolean, or array of strings.
    Nested objects are not permitted.`,
  );
const RECORD_SCHEMA = z
  .record(z.string(), FIELD_VALUE_SCHEMA)
  .refine(
    (record) => {
      const hasId = 'id' in record || '_id' in record;
      return hasId;
    },
    {
      message: 'A record must have an "id" or "_id" field.',
    },
  )
  .describe(
    `A record to upsert. Must have an "id" or "_id" field and contain text in
    the field specified by the index's "fieldMap".`,
  );
const RECORD_SET_SCHEMA = z.array(RECORD_SCHEMA).describe(
  `A set of records to upsert into the index. Use a consistent schema for all
  records in the index.`,
);
const SCHEMA = {
  name: z.string().describe('The index to upsert into.'),
  namespace: z.string().describe('The namespace to upsert into.'),
  records: RECORD_SET_SCHEMA,
};
export function addUpsertRecordsTool(server: McpServer, pc: Pinecone) {
  server.tool('upsert-records', INSTRUCTIONS, SCHEMA, async ({name, namespace, records}) => {
    const ns = pc.index(name).namespace(namespace);
    await ns.upsertRecords(records);
    return {
      content: [{type: 'text', text: 'Data upserted successfully'}],
    };
  });
}
```
--------------------------------------------------------------------------------
/src/tools/database/create-index-for-model.ts:
--------------------------------------------------------------------------------
```typescript
import {McpServer} from '@modelcontextprotocol/sdk/server/mcp.js';
import {Pinecone} from '@pinecone-database/pinecone';
import {z} from 'zod';
const INSTRUCTIONS = 'Create a Pinecone index with integrated inference';
const SCHEMA = {
  name: z.string().describe('A unique name to identify the new index.'),
  embed: z
    .object({
      model: z
        .enum(['multilingual-e5-large', 'llama-text-embed-v2', 'pinecone-sparse-english-v0'])
        .describe(
          `Choose an embedding model:
          - "multilingual-e5-large" is an efficient dense embedding model
          trained on a mixture of multilingual datasets. It works well on messy
          data and short queries expected to return medium-length passages of
          text (1-2 paragraphs).
          - "llama-text-embed-v2" is a high-performance dense embedding model
          optimized for text retrieval and ranking tasks. It is trained on a
          diverse range of text corpora and provides strong performance on
          longer passages and structured documents.
          - "pinecone-sparse-english-v0" is a sparse embedding model for
          converting text to sparse vectors for keyword or hybrid search. The
          model directly estimates the lexical importance of tokens by
          leveraging their context.`,
        ),
      fieldMap: z
        .object({
          text: z.string().describe(
            `The name of the field in the data records that contains the text
            content to embed. Records in the index must contain this field.`,
          ),
        })
        .describe('Identify which field from your data records will be embedded.'),
    })
    .describe('Configure an embedding model that converts text into a vector.'),
};
export function addCreateIndexForModelTool(server: McpServer, pc: Pinecone) {
  server.tool('create-index-for-model', INSTRUCTIONS, SCHEMA, async ({name, embed}) => {
    // Check if the index already exists
    const existingIndexes = await pc.listIndexes();
    const existingIndex = existingIndexes.indexes?.find((index) => index.name === name);
    if (existingIndex) {
      return {
        content: [
          {
            type: 'text',
            text: `Index not created. An index named "${name}" already exists:
                  ${JSON.stringify(existingIndex, null, 2)}`,
          },
        ],
      };
    }
    // Create the new index
    const indexInfo = await pc.createIndexForModel({
      name,
      cloud: 'aws',
      region: 'us-east-1',
      embed,
      tags: {
        source: 'mcp',
        embedding_model: embed.model,
      },
      waitUntilReady: true,
    });
    return {
      content: [
        {
          type: 'text',
          text: JSON.stringify(indexInfo, null, 2),
        },
      ],
    };
  });
}
```
--------------------------------------------------------------------------------
/src/tools/database/cascading-search.ts:
--------------------------------------------------------------------------------
```typescript
import {McpServer} from '@modelcontextprotocol/sdk/server/mcp.js';
import {Pinecone} from '@pinecone-database/pinecone';
import {z} from 'zod';
import {RERANK_MODEL_SCHEMA} from './common/rerank-model.js';
import {SEARCH_QUERY_SCHEMA} from './common/search-query.js';
const INSTRUCTIONS = `Search across multiple indexes for records that are
similar to the query text, deduplicate and rerank the results.`;
const INDEX_SCHEMA = z.object({
  name: z.string().describe('An index to search.'),
  namespace: z.string().describe('A namespace to search.'),
});
const RERANK_SCHEMA = z
  .object({
    model: RERANK_MODEL_SCHEMA,
    topN: z
      .number()
      .optional()
      .describe(
        `The number of results to return after reranking. Must be less than or
        equal to the value of "query.topK".`,
      ),
    rankFields: z.array(z.string()).describe(
      `The fields to rerank on. This should include the field name specified
      in the index's "fieldMap". The "bge-reranker-v2-m3" and
      "pinecone-rerank-v0" models support only a single rerank field.
      "cohere-rerank-3.5" supports multiple rerank fields.`,
    ),
    query: z
      .string()
      .optional()
      .describe(
        `An optional query to rerank documents against. If not specified, the
        same query will be used for both the initial search and the reranking.`,
      ),
  })
  .describe(
    `Specifies how the results should be reranked. Use a "query" with a "topK"
    that returns more results than you need; then use "rerank" to select the
    most relevant "topN" results.`,
  );
export const SCHEMA = {
  indexes: z
    .array(INDEX_SCHEMA)
    .describe('The indexes to search across. Records in each index should share a common schema.'),
  query: SEARCH_QUERY_SCHEMA,
  rerank: RERANK_SCHEMA,
};
export function addCascadingSearchTool(server: McpServer, pc: Pinecone) {
  server.tool('cascading-search', INSTRUCTIONS, SCHEMA, async ({indexes, query, rerank}) => {
    const initialResults = await Promise.all(
      indexes.map(async (index) => {
        const ns = pc.index(index.name).namespace(index.namespace || '');
        const results = await ns.searchRecords({query});
        return results;
      }),
    );
    const deduplicatedResults: Record<string, Record<string, string>> = {};
    for (const results of initialResults) {
      for (const hit of results.result.hits) {
        if (!deduplicatedResults[hit._id]) {
          deduplicatedResults[hit._id] = hit.fields as Record<string, string>;
        }
      }
    }
    const deduplicatedResultsArray = Object.values(deduplicatedResults);
    const rerankedResults =
      deduplicatedResultsArray.length > 0
        ? await pc.inference.rerank(
            rerank.model,
            rerank.query || query.inputs.text,
            deduplicatedResultsArray,
            {
              topN: rerank.topN || query.topK,
              rankFields: rerank.rankFields,
            },
          )
        : [];
    return {
      content: [
        {
          type: 'text',
          text: JSON.stringify(rerankedResults, null, 2),
        },
      ],
    };
  });
}
```