---
title: "pinecone-io/skills"
description: "Pinecone's official Agent Skills library, for use with agentic IDEs such as Cursor, Github Copilot, Antigravity, Gemini CLI and more."
source: https://github.com/pinecone-io/skills
ref: main
license: MIT
licenseName: "MIT License"
canonical: https://skillsdocs.com/pinecone-io/skills
base: https://github.com/pinecone-io/skills/blob/main/
provenance: authored
chapters: 9
inlined: 9
withheld: 0
words: 9260
updated: 2026-09-18T17:34:57Z
generator: "Skills Docs"
---

> **pinecone-io/skills** — every Agent Skill in this repository, inlined verbatim.
>
> Canonical HTML: https://skillsdocs.com/pinecone-io/skills
> Per-skill Markdown: https://skillsdocs.com/pinecone-io/skills/<skill>.md
> Machine manifest: https://skillsdocs.com/pinecone-io/skills/.well-known/agent-skills/index.json
> JSON: https://skillsdocs.com/api/v1/books/pinecone-io/skills
> Install: `npx skills add pinecone-io/skills`
> Upstream: https://github.com/pinecone-io/skills @ `main`
> Licence: MIT
>
> Content is mirrored from GitHub and © its authors, served unmodified. Takedown: https://github.com/DreambaseAI/skillsdocs/issues/new?labels=takedown&title=Takedown+request

# pinecone-io/skills

Pinecone's official Agent Skills library, for use with agentic IDEs such as Cursor, Github Copilot, Antigravity, Gemini CLI and more.

- **Skills:** 9
- **Inlined:** 9 (licence detected)
- **Words:** 9,260
- **Reading time:** 43 min
- **Stars:** 15

## Table of contents

1. [pinecone-assistant](https://skillsdocs.com/pinecone-io/skills/pinecone-assistant.md) — Create, manage, and chat with Pinecone Assistants for document Q&A with citations. Handles all assistant operations - create, upload, sync, chat, context retri…
2. [pinecone-cli](https://skillsdocs.com/pinecone-io/skills/pinecone-cli.md) — Guide for using the Pinecone CLI (pc) to manage Pinecone resources from the terminal. The CLI supports ALL index types (standard, integrated, sparse) and all v…
3. [pinecone-docs](https://skillsdocs.com/pinecone-io/skills/pinecone-docs.md) — Curated documentation reference for developers building with Pinecone. Contains links to official docs organized by topic and data format references. Use when…
4. [pinecone-full-text-search](https://skillsdocs.com/pinecone-io/skills/pinecone-full-text-search.md) — Create, ingest into, and query a Pinecone full-text-search (FTS) document index using the graduated document-schema API (Python SDK 10.0.0, API version 2026-07…
5. [pinecone-help](https://skillsdocs.com/pinecone-io/skills/pinecone-help.md) — Overview of all available Pinecone skills and what a user needs to get started. Invoke when a user asks what skills are available, how to get started with Pine…
6. [pinecone-mcp](https://skillsdocs.com/pinecone-io/skills/pinecone-mcp.md) — Reference for the Pinecone MCP server tools. Documents all available tools - list-indexes, describe-index, describe-index-stats, create-index-for-model, upsert…
7. [pinecone-n8n](https://skillsdocs.com/pinecone-io/skills/pinecone-n8n.md) — Build n8n workflows using the Pinecone Assistant node or Pinecone Vector Store node. Use when building RAG pipelines, chat-with-docs workflows, configuring Pin…
8. [pinecone-query](https://skillsdocs.com/pinecone-io/skills/pinecone-query.md) — Query integrated indexes using text with Pinecone MCP. IMPORTANT - This skill ONLY works with integrated indexes (indexes with built-in Pinecone embedding mode…
9. [pinecone-quickstart](https://skillsdocs.com/pinecone-io/skills/pinecone-quickstart.md) — Interactive Pinecone quickstart for new developers. Choose between two paths - Database (create an integrated index, upsert data, and query using Pinecone MCP…


## Front matter

_The repository README, verbatim except that relative links are resolved against https://github.com/pinecone-io/skills/blob/main/._

# Pinecone Agent Skills

Pinecone is the leading vector database for building accurate and performant AI applications at scale in production. Use it to build semantic search, retrieval augmented generation, recommendation systems, and agentic applications.

This is Pinecone's official Agent Skills library, compatible with agentic IDEs such as Cursor, GitHub Copilot, Windsurf, Gemini CLI, and more. Skills follow the [Agent Skills standard](https://agentskills.io).

---

## Installation

```bash
npx skills add pinecone-io/skills
```

Using Claude Code? Try our [official plugin](https://github.com/pinecone-io/pinecone-claude-code-plugin) instead.

---

## Skills

| Skill | Description |
|---|---|
| [`pinecone-quickstart`](https://github.com/pinecone-io/skills/blob/main/skills/pinecone-quickstart/SKILL.md) | Step-by-step onboarding — create an index, upload data, and run your first search |
| [`pinecone-query`](https://github.com/pinecone-io/skills/blob/main/skills/pinecone-query/SKILL.md) | Search integrated indexes using natural language text via the Pinecone MCP |
| [`pinecone-cli`](https://github.com/pinecone-io/skills/blob/main/skills/pinecone-cli/SKILL.md) | Use the Pinecone CLI (`pc`) for terminal-based index and vector management across all index types |
| [`pinecone-assistant`](https://github.com/pinecone-io/skills/blob/main/skills/pinecone-assistant/SKILL.md) | Create, manage, and chat with Pinecone Assistants for document Q&A with citations |
| [`pinecone-mcp`](https://github.com/pinecone-io/skills/blob/main/skills/pinecone-mcp/SKILL.md) | Reference for all Pinecone MCP server tools and their parameters |
| [`pinecone-full-text-search`](https://github.com/pinecone-io/skills/blob/main/skills/pinecone-full-text-search/SKILL.md) | Build a full-text-search index — schema design, safe bulk ingestion, and query construction (`text` / `query_string` / dense / sparse scoring with text-match and metadata filters) |
| [`pinecone-docs`](https://github.com/pinecone-io/skills/blob/main/skills/pinecone-docs/SKILL.md) | Curated links to official Pinecone documentation, organized by topic |
| [`pinecone-n8n`](https://github.com/pinecone-io/skills/blob/main/skills/pinecone-n8n/SKILL.md) | Build n8n workflows with the Pinecone Assistant node or Pinecone Vector Store node, including best practices and full workflow JSON generation |
| [`pinecone-help`](https://github.com/pinecone-io/skills/blob/main/skills/pinecone-help/SKILL.md) | Overview of all skills and what you need to get started |

---

## Prerequisites

- **Pinecone account** — free at [app.pinecone.io](https://app.pinecone.io/?sessionType=signup)
- **API key** — create one in the console, then `export PINECONE_API_KEY="your-key"`
- **Pinecone MCP** *(optional)* — enables the `query` skill and agent-native index operations. [Setup guide](https://docs.pinecone.io/guides/operations/mcp-server#tools)
- **Pinecone CLI** *(optional)* — enables the `cli` skill. `brew install pinecone-io/tap/pinecone`
- **uv** *(optional)* — runs the bundled Python scripts. [Install uv](https://docs.astral.sh/uv/getting-started/installation/)

---

## Development

Validate all skills against the Agent Skills spec:
```bash
uv run tools/check-skills.py --skills-dir skills
```

Check for broken links:
```bash
uv run tools/check-links.py --skills-dir skills
```

Check source tag conventions in scripts:
```bash
uv run tools/check-source-tags.py --dir skills
```

---

<!-- chapter:begin slug=pinecone-assistant position=1 -->

## 1. pinecone-assistant

- **Source:** https://github.com/pinecone-io/skills/blob/main/skills/pinecone-assistant/SKILL.md
- **Raw:** https://raw.githubusercontent.com/pinecone-io/skills/main/skills/pinecone-assistant/SKILL.md
- **Markdown:** https://skillsdocs.com/pinecone-io/skills/pinecone-assistant.md
- **Licence:** MIT — https://spdx.org/licenses/MIT.html

Bundled files (12), referenced from this skill's directory:
  - `references/chat.md` — https://raw.githubusercontent.com/pinecone-io/skills/main/skills/pinecone-assistant/references/chat.md
  - `references/context.md` — https://raw.githubusercontent.com/pinecone-io/skills/main/skills/pinecone-assistant/references/context.md
  - `references/create.md` — https://raw.githubusercontent.com/pinecone-io/skills/main/skills/pinecone-assistant/references/create.md
  - `references/list.md` — https://raw.githubusercontent.com/pinecone-io/skills/main/skills/pinecone-assistant/references/list.md
  - `references/sync.md` — https://raw.githubusercontent.com/pinecone-io/skills/main/skills/pinecone-assistant/references/sync.md
  - `references/upload.md` — https://raw.githubusercontent.com/pinecone-io/skills/main/skills/pinecone-assistant/references/upload.md
  - `scripts/chat.py` — https://raw.githubusercontent.com/pinecone-io/skills/main/skills/pinecone-assistant/scripts/chat.py
  - `scripts/context.py` — https://raw.githubusercontent.com/pinecone-io/skills/main/skills/pinecone-assistant/scripts/context.py
  - `scripts/create.py` — https://raw.githubusercontent.com/pinecone-io/skills/main/skills/pinecone-assistant/scripts/create.py
  - `scripts/list.py` — https://raw.githubusercontent.com/pinecone-io/skills/main/skills/pinecone-assistant/scripts/list.py
  - `scripts/sync.py` — https://raw.githubusercontent.com/pinecone-io/skills/main/skills/pinecone-assistant/scripts/sync.py
  - `scripts/upload.py` — https://raw.githubusercontent.com/pinecone-io/skills/main/skills/pinecone-assistant/scripts/upload.py

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: pinecone-assistant
description: Create, manage, and chat with Pinecone Assistants for document Q&A with citations. Handles all assistant operations - create, upload, sync, chat, context retrieval, and list. Recognizes natural language like "create an assistant from my docs", "ask my assistant about X", or "upload my docs to Pinecone".
---

# Pinecone Assistant

Pinecone Assistant is a fully managed RAG service. Upload documents, ask questions, get cited answers. No embedding pipelines or infrastructure required.

> All scripts are in `scripts/` relative to this skill directory.
> Run with: `uv run scripts/script_name.py [arguments]`

<<clarify_style>>

## Operations

| What to do | Script | Key args |
|---|---|---|
| Create an assistant | `scripts/create.py` | `--name` `--instructions` `--region` |
| Upload files | `scripts/upload.py` | `--assistant` `--source` `--patterns` |
| Sync files (incremental) | `scripts/sync.py` | `--assistant` `--source` `--delete-missing` `--dry-run` |
| Chat / ask a question | `scripts/chat.py` | `--assistant` `--message` |
| Get context snippets | `scripts/context.py` | `--assistant` `--query` `--top-k` |
| List assistants | `scripts/list.py` | `--files` `--json` |

For full workflow details on any operation, read the relevant file in `references/`.

---

## Natural Language Recognition

Proactively handle these patterns without requiring explicit commands:

**Create:** "create an assistant", "make an assistant called X", "set up an assistant for my docs"
→ See [references/create.md](references/create.md)

**Upload:** "upload my docs", "add files to my assistant", "index my documentation"
→ See [references/upload.md](references/upload.md)

**Sync:** "sync my docs", "update my assistant", "keep assistant in sync", "refresh from ./docs"
→ See [references/sync.md](references/sync.md)

**Chat:** "ask my assistant about X", "what does my assistant know about X", "chat with X"
→ See [references/chat.md](references/chat.md)

**Context:** "search my assistant for X", "find context about X"
→ See [references/context.md](references/context.md)

**List:** "show my assistants", "what assistants do I have"
→ Run `uv run scripts/list.py`

---

## Conversation Memory

Track the last assistant used within the conversation:
- When a user creates or first uses an assistant, remember its name
- If user says "my assistant", "it", or "the assistant" → use the last one
- Briefly confirm which assistant you're using: "Asking docs-bot..."
- If ambiguous and multiple exist → ask the user to clarify

---

## Multi-Step Requests

Handle chained requests naturally. Example:

> "Create an assistant called docs-bot, upload my ./docs folder, and ask what the main features are"

1. `uv run scripts/create.py --name docs-bot`
2. `uv run scripts/upload.py --assistant docs-bot --source ./docs`
3. `uv run scripts/chat.py --assistant docs-bot --message "what are the main features?"`

---

## Prerequisites

- `PINECONE_API_KEY` must be available:
<<api_key_setup>>
- `uv` must be installed — [install uv](https://docs.astral.sh/uv/getting-started/installation/)
- Get a free API key at: https://app.pinecone.io/?sessionType=signup

<!-- chapter:end slug=pinecone-assistant -->

---

<!-- chapter:begin slug=pinecone-cli position=2 -->

## 2. pinecone-cli

- **Source:** https://github.com/pinecone-io/skills/blob/main/skills/pinecone-cli/SKILL.md
- **Raw:** https://raw.githubusercontent.com/pinecone-io/skills/main/skills/pinecone-cli/SKILL.md
- **Markdown:** https://skillsdocs.com/pinecone-io/skills/pinecone-cli.md
- **Licence:** MIT — https://spdx.org/licenses/MIT.html

Bundled files (2), referenced from this skill's directory:
  - `references/command-reference.md` — https://raw.githubusercontent.com/pinecone-io/skills/main/skills/pinecone-cli/references/command-reference.md
  - `references/troubleshooting.md` — https://raw.githubusercontent.com/pinecone-io/skills/main/skills/pinecone-cli/references/troubleshooting.md

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: pinecone-cli
description: Guide for using the Pinecone CLI (pc) to manage Pinecone resources from the terminal. The CLI supports ALL index types (standard, integrated, sparse) and all vector operations — unlike the MCP which only supports integrated indexes. Use for batch operations, vector management, backups, namespaces, CI/CD automation, and full control over Pinecone resources.
argument-hint: install | auth | index [op] | vector [op] | backup | namespace
---

# Pinecone CLI (`pc`)

Manage Pinecone from the terminal. The CLI is especially valuable for vector operations across **all index types** — something the MCP currently can't do.

## CLI vs MCP

| | CLI | MCP |
|---|---|---|
| Index types | All (standard, integrated, sparse) | Integrated only |
| Vector ops (upsert, query, fetch, update, delete) | ✅ | ❌ |
| Text search on integrated indexes | ✅ | ✅ |
| Backups, namespaces, org/project mgmt | ✅ | ❌ |
| CI/CD / scripting | ✅ | ❌ |

---

## Setup

### Install (macOS)
```bash
brew tap pinecone-io/tap
brew install pinecone-io/tap/pinecone
```

Other platforms (Linux, Windows) — download from [GitHub Releases](https://github.com/pinecone-io/cli/releases).

### Authenticate

```bash
# Interactive (recommended for local dev)
pc login
pc target -o "my-org" -p "my-project"

# Service account (recommended for CI/CD)
pc auth configure --client-id "$PINECONE_CLIENT_ID" --client-secret "$PINECONE_CLIENT_SECRET"

# API key (quick testing)
pc config set-api-key $PINECONE_API_KEY
```

Check status: `pc auth status` · `pc target --show`

> **Note for agent sessions**: If you need to run `pc login` inside an agent loop, the browser auth link may not surface correctly. It's best to authenticate **before** starting an agent session. Run `pc login` in your terminal directly, then invoke the agent once you're authenticated.

### Authenticating the CLI does not set `PINECONE_API_KEY`

`pc login` authenticates the CLI tool itself — it does **not** set `PINECONE_API_KEY` in your environment. Python scripts, Node.js SDKs, and other tools that use the Pinecone SDK need `PINECONE_API_KEY` set separately.

Use the CLI to create a key and export it in one step:

```bash
KEY=$(pc api-key create --name agent-sdk-key --json | jq -r '.value')
export PINECONE_API_KEY="$KEY"
```

Without `jq`: run `pc api-key create --name agent-sdk-key --json` and copy the `"value"` field manually.

---

## Common Commands

| Task | Command |
|---|---|
| List indexes | `pc index list` |
| Create serverless index | `pc index create -n my-index -d 1536 -m cosine -c aws -r us-east-1` |
| Index stats | `pc index stats -n my-index` |
| Upload vectors from file | `pc index vector upsert -n my-index --file ./vectors.json` |
| Query by vector | `pc index vector query -n my-index --vector '[0.1, ...]' -k 10 --include-metadata` |
| Query by vector ID | `pc index vector query -n my-index --id "doc-123" -k 10` |
| Fetch vectors by ID | `pc index vector fetch -n my-index --ids '["vec1","vec2"]'` |
| List vector IDs | `pc index vector list -n my-index` |
| Delete vectors by filter | `pc index vector delete -n my-index --filter '{"genre":"classical"}'` |
| List namespaces | `pc index namespace list -n my-index` |
| Create backup | `pc backup create -i my-index -n "my-backup"` |
| JSON output (for scripting) | Add `-j` to any command |

---

## Interesting Things You Can Do

### Query with custom vectors (not just text)
Unlike the MCP, the CLI lets you query any index with raw vector values — useful when you generate embeddings externally (OpenAI, HuggingFace, etc.):
```bash
pc index vector query -n my-index \
  --vector '[0.1, 0.2, ..., 0.9]' \
  --filter '{"source":{"$eq":"docs"}}' \
  -k 20 --include-metadata
```

### Pipe embeddings directly into queries
```bash
jq -c '.embedding' doc.json | pc index vector query -n my-index --vector - -k 10
```

### Bulk metadata update with preview
```bash
# Preview first
pc index vector update -n my-index \
  --filter '{"env":{"$eq":"staging"}}' \
  --metadata '{"env":"production"}' \
  --dry-run

# Apply
pc index vector update -n my-index \
  --filter '{"env":{"$eq":"staging"}}' \
  --metadata '{"env":"production"}'
```

### Backup and restore
```bash
# Snapshot before a migration
pc backup create -i my-index -n "pre-migration"

# Restore to a new index if something goes wrong
pc backup restore -i <backup-uuid> -n my-index-restored
```

### Automate in CI/CD
```bash
export PINECONE_CLIENT_ID="..."
export PINECONE_CLIENT_SECRET="..."
pc auth configure --client-id "$PINECONE_CLIENT_ID" --client-secret "$PINECONE_CLIENT_SECRET"
pc index vector upsert -n my-index --file ./vectors.jsonl --batch-size 1000
```

### Script against JSON output
```bash
# Get all index names as a list
pc index list -j | jq -r '.[] | .name'

# Check if an index exists before creating
if ! pc index describe -n my-index -j 2>/dev/null | jq -e '.name' > /dev/null; then
  pc index create -n my-index -d 1536 -m cosine -c aws -r us-east-1
fi
```

---

## Reference Files

- [Full command reference](references/command-reference.md) — all commands with flags and examples
- [Troubleshooting & best practices](references/troubleshooting.md)

## Documentation

- [CLI Quickstart](https://docs.pinecone.io/reference/cli/quickstart)
- [Command Reference](https://docs.pinecone.io/reference/cli/command-reference)
- [Authentication](https://docs.pinecone.io/reference/cli/authentication)
- [Target Context](https://docs.pinecone.io/reference/cli/target-context)
- [GitHub Releases](https://github.com/pinecone-io/cli/releases)

<!-- chapter:end slug=pinecone-cli -->

---

<!-- chapter:begin slug=pinecone-docs position=3 -->

## 3. pinecone-docs

- **Source:** https://github.com/pinecone-io/skills/blob/main/skills/pinecone-docs/SKILL.md
- **Raw:** https://raw.githubusercontent.com/pinecone-io/skills/main/skills/pinecone-docs/SKILL.md
- **Markdown:** https://skillsdocs.com/pinecone-io/skills/pinecone-docs.md
- **Licence:** MIT — https://spdx.org/licenses/MIT.html

Bundled files (1), referenced from this skill's directory:
  - `references/data-formats.md` — https://raw.githubusercontent.com/pinecone-io/skills/main/skills/pinecone-docs/references/data-formats.md

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: pinecone-docs
description: Curated documentation reference for developers building with Pinecone. Contains links to official docs organized by topic and data format references. Use when writing Pinecone code, looking up API parameters, or needing the correct format for vectors or records.
---

# Pinecone Developer Reference

A curated index of Pinecone documentation. Fetch the relevant page(s) for the task at hand rather than relying on training data.

---

## NOTE TO AGENT
Please attempt to fetch the url listed when relevant. If you run into an error, please attempt to append ".md" to the url to retrieve the markdown version of the Docs page.

In case you need it: A full reference to ALL relevant URLs can be found here: https://docs.pinecone.io/llms.txt

Use this as a last resort if you cannot find the relevant page below.

---

## Getting Started

| Topic | URL |
|---|---|
| Quickstart for all languages and coding environments (Cursor, Claude Code, n8n, Python, JavaScript, Java, Go, C#) | https://docs.pinecone.io/guides/get-started/quickstart |
| Pinecone concepts — namespaces, terminology, and key database concepts | https://docs.pinecone.io/guides/get-started/concepts |
| Data modeling for text and vectors | https://docs.pinecone.io/guides/index-data/data-modeling |
| Architecture of Pinecone | https://docs.pinecone.io/guides/get-started/database-architecture |
| Pinecone Assistant overview | https://docs.pinecone.io/guides/assistant/overview |

---

## Indexes

| Topic | URL |
|---|---|
| Create an index | https://docs.pinecone.io/guides/index-data/create-an-index |
| Index types and conceptual overview | https://docs.pinecone.io/guides/index-data/indexing-overview |
| Integrated inference (built-in embedding models) | https://docs.pinecone.io/guides/index-data/indexing-overview#integrated-embedding |
| Dedicated read nodes — predictable low-latency performance at high query volumes | https://docs.pinecone.io/guides/index-data/dedicated-read-nodes |

---

## Upsert & Data

| Topic | URL |
|---|---|
| Upsert vectors and text | https://docs.pinecone.io/guides/index-data/upsert-data |
| Multitenancy with namespaces | https://docs.pinecone.io/guides/index-data/implement-multitenancy |

---

## Search

| Topic | URL |
|---|---|
| Semantic search | https://docs.pinecone.io/guides/search/semantic-search |
| Hybrid search | https://docs.pinecone.io/guides/search/hybrid-search |
| Lexical search | https://docs.pinecone.io/guides/search/lexical-search |
| Full-text search (preview) — document-schema FTS indexes with `text` / `query_string` / dense / sparse scoring | https://docs.pinecone.io/guides/search/full-text-search |
| Metadata filtering — narrow results and speed up searches | https://docs.pinecone.io/guides/search/filter-by-metadata |

---

## API & SDK Reference

| Topic | URL |
|---|---|
| Python SDK reference | https://docs.pinecone.io/reference/sdks/python/overview |
| Example Colab notebooks | https://docs.pinecone.io/examples/notebooks |

---

## Production

| Topic | URL |
|---|---|
| Production checklist — preparing your index for production | https://docs.pinecone.io/guides/production/production-checklist |
| Common errors and what they mean | https://docs.pinecone.io/guides/production/error-handling |
| Targeting indexes correctly — don't use index names in prod | https://docs.pinecone.io/guides/manage-data/target-an-index#target-by-index-host-recommended |

---

## Data Formats

See [references/data-formats.md](references/data-formats.md) for vector and record schemas.

<!-- chapter:end slug=pinecone-docs -->

---

<!-- chapter:begin slug=pinecone-full-text-search position=4 -->

## 4. pinecone-full-text-search

- **Source:** https://github.com/pinecone-io/skills/blob/main/skills/pinecone-full-text-search/SKILL.md
- **Raw:** https://raw.githubusercontent.com/pinecone-io/skills/main/skills/pinecone-full-text-search/SKILL.md
- **Markdown:** https://skillsdocs.com/pinecone-io/skills/pinecone-full-text-search.md
- **Licence:** MIT — https://spdx.org/licenses/MIT.html

Bundled files (5), referenced from this skill's directory:
  - `references/ingestion.md` — https://raw.githubusercontent.com/pinecone-io/skills/main/skills/pinecone-full-text-search/references/ingestion.md
  - `references/onboarding-walkthrough.md` — https://raw.githubusercontent.com/pinecone-io/skills/main/skills/pinecone-full-text-search/references/onboarding-walkthrough.md
  - `references/querying.md` — https://raw.githubusercontent.com/pinecone-io/skills/main/skills/pinecone-full-text-search/references/querying.md
  - `references/schema-design.md` — https://raw.githubusercontent.com/pinecone-io/skills/main/skills/pinecone-full-text-search/references/schema-design.md
  - `scripts/ingest.py` — https://raw.githubusercontent.com/pinecone-io/skills/main/skills/pinecone-full-text-search/scripts/ingest.py

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: pinecone-full-text-search
description: Create, ingest into, and query a Pinecone full-text-search (FTS) document index using the graduated document-schema API (Python SDK 10.0.0, API version 2026-07). Use when the user or agent asks to build a text search index on Pinecone, add dense or sparse vector fields, ingest documents, construct score_by clauses (text / query_string / dense_vector / sparse_vector), or compose with text-match filters ($match_phrase / $match_all / $match_any). Ships `scripts/ingest.py` for safe bulk ingestion (batch_upsert + error inspection + readiness polling); query construction is documented inline in this skill — write `documents.search(...)` calls directly, validated against `pc.indexes.describe(...)` output.
---

# Pinecone Full-Text Search

> **Requires `pinecone` Python SDK ≥ 10.0.0** (`pip install pinecone>=10.0.0`). The document-schema API graduated out of `pinecone.preview` in 10.0.0 — it is now a first-class, SemVer-covered part of the SDK, reachable directly off `pc` (`pc.indexes`, `pc.index(...)`). If you land on this skill from an older habit of importing `pinecone.preview`, stop: that package is deleted outright in 10.0.0 (`ModuleNotFoundError`, no shim). The packaged helper script pins `pinecone==10.0.0` via PEP 723 inline metadata; if you're writing your own code against this skill, pin at least that version. The wire API version is `2026-07`.

> **Authoritative reference (last resort).** If you hit a question this skill and its `references/*.md` files don't answer, the official Pinecone FTS docs are at <https://docs.pinecone.io/guides/search/full-text-search>. Prefer this skill's content for anything covered here — the docs may describe surfaces (e.g. classic vector API, or the older `pinecone.preview` shape) that don't apply to the graduated document-schema path. Consult the link only when you're genuinely stuck.

> **Tell the user up front:** "This skill ships a helper at `scripts/ingest.py` that handles bulk ingestion safely (batched upsert, error inspection, readiness polling). When we get to the ingest step, I'll use it." Surface this at the start of the conversation so the user knows the helper exists. Query construction is hand-written `documents.search(...)` per the **Querying** section below — there is no query helper.

A workflow skill for building a Pinecone full-text-search index with the graduated document-schema API (`pc.indexes`, `pc.index(name)`, API version `2026-07`). Covers schema design (text, dense vector, sparse vector, filterable metadata), ingestion (including async indexing and polling), and query construction (`text` / `query_string` / `dense_vector` / `sparse_vector` scoring; `$match_phrase` / `$match_all` / `$match_any` text-match filters; `$eq` / `$in` / `$gte` / `$exists` / `$and` / `$or` / `$not` metadata filters).

## Scope — this skill is for the document-schema FTS API only

This skill covers `pc.indexes.create(..., schema=...)`, `pc.index(name)`, `idx.documents.upsert(...)` / `idx.documents.batch_upsert(...)` / `idx.documents.search(...)`. If you find yourself reaching for any of the following, **stop** — those are different Pinecone APIs and this skill's guidance and helpers won't apply:

- **Classic vector / records API**: `pc.Index(name)`, `index.upsert(vectors=[...])`, `index.query(vector=..., sparse_vector=...)`, `pc.create_index(dimension=..., metric=..., spec=ServerlessSpec(...))`. This is the *deprecated sugar* path in 10.0.0 — it still runs, but it creates a schemaless index served by the vector data plane, addressing the vector by the reserved `_values` field. It cannot hold `full_text_search` fields.
- **Integrated-embedding / records indexes**: `pc.create_index_for_model(...)` / `pc.indexes.create_for_model(...)` with `embed={...}`. Pinecone vectorizes text server-side, and the resulting `semantic_text` field is served by the **records** API (`upsert_records` / `search_records`), not the documents API. Different upsert/search shapes. A `semantic_text` field cannot be combined with `full_text_search` fields in the same index.

If the user already has a non-document-schema index, they can stand up a separate document-schema index alongside it — the two are independent — but you can't add FTS fields to a classic or integrated-embedding index after the fact, and a document-schema index only ever serves reads and writes through `index.documents.*` — never `index.upsert` / `index.query` / `index.upsert_records` (those calls are refused with "This index has a document schema, so writes must go through the documents API").

## Querying — construct `documents.search(...)` calls

For any task that asks you to query an FTS index, you write a `documents.search(...)` call directly. The schema is authoritative — describe the index live before constructing the call so you know which fields are FTS-enabled, which are filterable, and which are vectors.

**Workflow:**

1. **Discover the schema.** Call `pc.indexes.describe(<index>)` and read the `schema.fields` dict. Each field's class indicates its type (`StringField`, `FloatField`, `DenseVectorField`, etc.); attributes tell you whether it's FTS-enabled (`full_text_search`), filterable, or carries a `dimension`. Skip this step only if you've already seen the schema in this conversation.
2. **Construct the call** matching the rules below — one scoring type per request, hard requirements in `filter`, ranking signals in `score_by`, `include_fields` explicit on every call.
3. **Execute** with `idx = pc.index(name=<index>); resp = idx.documents.search(...)` and read `resp.matches`.

**Canonical shapes:**

```python
# Pure BM25 keyword search
resp = idx.documents.search(
    namespace="__default__",
    top_k=10,
    score_by=[{"type": "text", "field": "body", "query": "machine learning"}],
    filter={"year": {"$gt": 2024}, "category": {"$eq": "ai"}},  # optional
    include_fields=["*"],   # always pass explicitly
)

# Hybrid: dense ranking with a lexical filter (one type in score_by + filter narrows)
resp = idx.documents.search(
    namespace="__default__",
    top_k=10,
    score_by=[{"type": "dense_vector", "field": "embedding", "values": query_embedding}],
    filter={"body": {"$match_all": "TensorFlow"}, "year": {"$gt": 2024}},
    include_fields=["*"],
)
```

**Key rules** (the server enforces these; following them locally keeps the agent loop tight):

- `score_by` is a list of clauses, but **exactly one scoring type per request** (server rejects mixed types). Multi-field BM25 is the one exception: multiple `text` clauses, or one `query_string` with `fields: [...]`. To combine BM25 + dense signals, restrict the dense search with a text-match filter (`$match_all` / `$match_phrase` / `$match_any`); do NOT mix scoring types in `score_by`.
- `filter` keys are field names (must exist in schema, or be an auto-indexed metadata field from upserted documents — see **Filterable metadata isn't declared in the schema** below) OR logical operators (`$and`, `$or`, `$not`). Field values are operator dicts (`{"$gt": 5}`, NOT bare values).
- `include_fields` is required on every call. Pass `["*"]` for all stored fields, `[]` for ids+score only, or a list of names. Omitting it on some SDK/backend builds 400s.

**Clause shapes** (for `score_by`):

| `type` | Required keys | When to pick this |
|---|---|---|
| `text` | `field` (string FTS), `query` | Open-ended keyword search; BM25 ranking on one field |
| `query_string` | `query` (Lucene), `fields` optional | Lucene boost (`^N`), proximity (`~N`), cross-field boolean, phrase prefix |
| `dense_vector` | `field` (dense_vector), `values` (list of floats) | Semantic / mood / topic ranking |
| `sparse_vector` | `field` (sparse_vector), `sparse_values` ({indices, values}) | Custom sparse-encoder ranking |

`text` / `dense_vector` / `sparse_vector` use singular `field`. Only `query_string` accepts a `fields` array (and also accepts singular `field` as an alias). `sparse_vector` uses `sparse_values` (NOT `values`) — distinct from dense.

**Filter operators by field type:**

| Field type | Legal operators |
|---|---|
| `string` with FTS | `$match_phrase`, `$match_all`, `$match_any` |
| filterable metadata (string / auto-indexed) | `$eq`, `$ne`, `$in`, `$nin`, `$exists` |
| `string_list` filterable (auto-indexed, not schema-declared) | `$in`, `$nin`, `$exists` |
| `float` filterable (auto-indexed, not schema-declared) | `$eq`, `$ne`, `$gt`, `$gte`, `$lt`, `$lte`, `$exists` |
| `boolean` filterable (auto-indexed, not schema-declared) | `$eq`, `$exists` |
| logical wrappers | `$and: [filters]`, `$or: [filters]`, `$not: filter` |

**Match shape on response:**

```python
for m in resp.matches:
    m._id        # document id
    m._score     # match score (NOT `score`)
    m.to_dict()  # full doc payload (when include_fields includes the field)
```

For deeper coverage — multi-field BM25, Lucene patterns, hybrid composition, RRF merges, common error symptoms — see `references/querying.md`. For schema field types and what they enable on the query side, see `references/schema-design.md`.

## Ingesting — use the packaged helper

For **any task that asks you to bulk-ingest a JSONL file into an existing FTS index**, the canonical path is to invoke the bundled helper, NOT to hand-write a Python script. **Do not read the script's source** — everything you need is in this section.

The script does three things bare-LLM ingest code reliably skips, each of which corresponds to a silent production failure:

1. **Bulk-upserts in batches.** No per-doc `upsert` loops.
2. **Inspects every batch result.** `batch_upsert` returns 202 even when individual documents fail; the failures live in `result.errors` / `result.has_errors`. Without inspection, "100 docs ingested" silently becomes "73 docs ingested + 27 lost."
3. **Polls until searchable.** After upsert, Pinecone is still building the inverted index. A `documents.search` call during that window returns empty. Without the poll, the user debugs their *query* code for an hour without finding the indexing race.

You provide a prepared, schema-conformant JSONL file and the index name; the script does the rest. Schema validation is upstream concerns (your prep pipeline, or `prepare_documents.py` when it lands) — `ingest.py` trusts what you hand it.

**Invocation:**

```bash
uv run --script scripts/ingest.py \
  --data processed.jsonl \
  --index <index_name> \
  --sentinel-field <fts_field>
```

**Flags:**

| Flag | Short | Required | Purpose |
|---|---|---|---|
| `--data` | `-d` | yes | Path to JSONL file with prepared documents (one per line) |
| `--index` | `-i` | yes | Pinecone index name (must already exist) |
| `--sentinel-field` | `-f` | yes | An FTS-enabled field on the index, used for the readiness-poll query. Pick the longest free-text field on your schema. |
| `--namespace` | `-n` | no | Default `__default__` |
| `--batch-size` | `-b` | no | Default 50 (matches the SDK's own `batch_upsert` default). **Reduce for large dense vectors.** A 50-doc batch with 3072-dim float vectors lands ~5-10 MB and can be rejected; drop to `--batch-size 25` (or lower) at high dimensions. |
| `--max-concurrency` | — | no | Default 4. Parallel HTTP connections used to upload batches. |
| `--poll-deadline` | — | no | Default 300 (seconds). Time to wait for documents to become searchable before giving up. |
| `--sentinel` | `-s` | no | Token used for the readiness-poll query. Default: first whitespace-separated token of `doc[0][sentinel-field]`. |

**What the script prints:**

```
Loading processed.jsonl ...
Loaded 5000 document(s).
Sentinel: body='The'

Upserting in batches of 50 ...
  batch @     0:   50 docs in  0.31s  (total: 50/5000)
  batch @    50:   50 docs in  0.29s  (total: 100/5000)
  ...

Upsert complete: 5000 doc(s) in 21.4s.

Polling for searchability (deadline 300s) ...
Searchable after 12.3s (3 probe(s)).

Done — total 33.7s.
```

If a batch fails, the script prints every error message and exits non-zero. If the poll deadline expires, the script prints a hint about why (sentinel field isn't FTS-enabled, deadline too tight, docs structurally upserted but rejected by the inverted-index builder) and exits non-zero. **Don't suppress these errors** — they're surfacing real problems with the data or the index.

**When you should NOT use the script:**

- The user is doing per-doc patch updates. Use `documents.update(...)` for partial field updates (see **Updating documents** in `references/ingestion.md`) — the script is for bulk loads, not per-record operations.
- The user is ingesting from a non-JSONL source (CSV, Parquet, Postgres dump). Convert to JSONL first; the script doesn't parse other formats.
- The user explicitly asks you to write the ingestion code from scratch (teaching context). Honor the request and follow the canonical pattern: `documents.batch_upsert` + `result.has_errors` inspection + `documents.search` polling with sentinel and deadline.

The script lives at `scripts/ingest.py` relative to this skill directory. PEP 723 inline-metadata script — `uv run --script` installs `typer` and `pinecone` automatically on first invocation. No setup needed.

## Use cases

Three concrete shapes to model your task on. Match the user's request to the closest one and follow its steps; improvise if the task is genuinely a hybrid.

### UC-1: Index a new corpus end-to-end

**Trigger.** "Index this CSV / JSONL / folder for search," "build a search backend over [my articles / products / tickets / transcripts]," "make my [dataset] searchable."

**For unprocessed / messy data, load the onboarding walkthrough first.** If the user is showing up with raw data (unclear field types, possibly long text fields exceeding FTS limits, comma-separated tag strings, dates as strings, possibly duplicate IDs, etc.) and they haven't given you an explicit schema, **read `references/onboarding-walkthrough.md` and follow it stage-by-stage.** It's a conversational guide — meet the data, surface the processing decisions to the user, propose a schema, confirm before creating, then process+ingest+verify together. The walkthrough exists because schemas are immutable and "onboarding a new corpus" is a high-stakes flow that benefits from explicit user buy-in at each decision point.

If the user already gave you a clean JSONL + a schema spec, follow the abbreviated steps below.

**Steps (when data is already prepared and the schema is decided):**
1. Inspect the corpus shape — text fields, structured metadata, do you also need a vector? Match it to one of the canonical shapes in `references/schema-design.md` (articles, products, tickets, image library, code).
2. Pick analyzer settings on each text field — `language`, `stemming`, `stop_words`. Stemming on for long prose, off for proper nouns / identifiers. **Decide which fields are FTS and which are filterable-only metadata** — see **Filterable metadata isn't declared in the schema** below; filterable-only metadata is a documents-side decision, not a schema field.
3. Assemble the schema with `SchemaBuilder` and **confirm it with the user before calling `indexes.create`** — schemas are immutable in `2026-07`, so a wrong call costs a re-ingest.
4. Create the index. `pc.indexes.create(...)` polls until the index is ready by default — no separate wait loop needed unless you passed `timeout=-1`.
5. **Run `scripts/ingest.py --data <jsonl> --index <name> --sentinel-field <fts_field>`** — see the **Ingesting — use the packaged helper** section above. The script handles `batch_upsert` + per-batch error inspection + post-upsert readiness polling in one invocation. Don't hand-write the loop unless the user explicitly asks you to.
6. (The script polls automatically — by the time it exits cleanly, the index is searchable. If you skip the script and roll your own, you must poll `documents.search` with a sentinel query and a deadline; `batch_upsert` returning ≠ searchable — this is a *document*-indexing wait, separate from and in addition to the index-creation wait in step 4.)
7. Validate with one or two probe queries against fields you know contain the sentinel content.

**Result.** A working `documents.search` call against the user's data, returning ranked matches.

### UC-2: Add a dense (or sparse) signal to a text-only corpus

**Trigger.** "Add semantic search," "add embeddings," "make this hybrid," or any prompt that describes a query pattern text alone can't serve (visual similarity, mood, cross-modal "looks like").

**Steps.**
1. Confirm the new signal represents a **modality or signal text can't express** — image / audio / external score, *or* a different corpus than the existing FTS field. Re-encoding the same text into a dense field is an anti-pattern (`references/schema-design.md` → "When to add a dense field at all").
2. Because schemas are immutable, **plan a new index, not a migration**. Get user confirmation before recreating. A hybrid index must declare its `sparse_vector` field explicitly at creation — there is no way to add one later.
3. Pick an embedding provider and pin its output dimension at schema time. Beware payload-size pitfalls at native dimensions — Gemini-3072 etc. need truncation (`references/ingestion.md` → "Dense-vector payload size").
4. Schema → create (blocks until ready by default) → ingest with embeddings inline or pre-cached.
5. Validate with a **hybrid query**: `dense_vector` score_by + text-match filter (`$match_phrase` / `$match_all`). That's the supported single-call cross-modal shape.

**Result.** One index, two retrieval shapes — pure text *and* dense+filter hybrid — both runnable without further setup.

### UC-3: Build a `documents.search` call from a natural-language user prompt (agent mode)

**Trigger.** Agent receives a user prompt like "find articles about machine learning that mention TensorFlow and were published after 2024" or "documents about climate policy ranked by similarity to this paragraph." The index already exists.

**Steps.**
1. **(Optional) Discover the schema** by calling `pc.indexes.describe(<NAME>)` and reading `schema.fields`. Skip if you already know the field types from earlier in the conversation.
2. **Decompose the user's prompt** into `score_by` / `filter` shapes using the agent-mode decomposition table below. (Hard requirements → `filter`. Ranking signals → `score_by`. Always include `include_fields` explicitly.)
3. **Construct the `documents.search(...)` call** following the rules in the Querying section above — one scoring type per request, operator/field-type matching, `include_fields` always set.
4. **Execute** the call. The response carries `resp.matches`; iterate to get `m._id`, `m._score`, and field values via `m.to_dict()`. Use the matches in whatever shape the user asked for.
5. If results come back empty or wrong, walk the failure tree in `Common gotchas`.

**Result.** Live search results matching the user's intent.

**The four common UC-3 mistakes** to actively avoid:
- Mixing scoring types in `score_by` (server rejects). Put hard requirements in `filter`; rank by one signal in `score_by`.
- Putting hard requirements in `score_by` as BM25 terms instead of in `filter` as `$match_all` / `$match_phrase` (returns ranked results that don't *guarantee* the term is present).
- Operator/field-type mismatches (e.g. `$match_all` on a float field, `$gt` on a string field). Consult the operator table in the Querying section.
- Omitting `include_fields` (some SDK/backend builds 400). Always pass it explicitly.

## Agent-mode query decomposition

Map user prompt cues to API shapes. Read top-down — identify the cue, copy the corresponding shape.

| User prompt cue | API shape |
|---|---|
| Open-ended keywords ("articles about machine learning", search-bar query) | `score_by=[{"type": "text", "field": "<field>", "query": "<terms>"}]` — BM25 token-OR |
| Exact phrase, drives ranking ("rank by 'beautifully written'") | `score_by=[{"type": "query_string", "query": '<field>:("phrase here")'}]` |
| Exact phrase, hard requirement ("must contain 'machine learning'") | `filter={"<field>": {"$match_phrase": "machine learning"}}` |
| Required tokens, any order ("must mention TensorFlow", "must be about Illinois") | `filter={"<field>": {"$match_all": "tokens space-separated"}}` — preferred over `query_string` `+token` because it's a true hard filter, doesn't contribute to score |
| At least one of these tokens ("contains AI or ML or robotics") | `filter={"<field>": {"$match_any": "AI ML robotics"}}` |
| Excluded tokens ("not about deprecated", "no opinion pieces") | `filter={"$not": {"<field>": {"$match_any": "deprecated opinion"}}}` — or `-token` inside `query_string` |
| Boolean / boost / slop / phrase-prefix ("weight 'eagle' 3x", "within N words") | `score_by=[{"type": "query_string", "query": '<expr with ^N / ~N / "…"*>'}]` — only Lucene supports these |
| Cross-field boolean ("title or body contains X") | `score_by=[{"type": "query_string", "query": 'title:(X) OR body:(X)'}]` |
| Numeric / date / range / boolean metadata ("after 2024", "rating > 4", "in stock") | `filter={"<field>": {"$gt": ..., "$gte": ..., "$eq": ..., "$exists": true}}` |
| Category / tag / list membership ("category = fiction", "tagged X") | `filter={"<field>": {"$in": [...]}}` (works on plain filterable metadata and `string_list` filterable fields) |
| Semantic similarity / mood / topic ("articles about ML", "documents that feel sombre") | `score_by=[{"type": "dense_vector", "field": "<embedding_field>", "values": embed(<text>)}]` — requires a `dense_vector` field |
| Visual appearance / cross-modal text query against an image corpus | Same dense_vector shape, with the embedding model that produced the stored image vectors. Multimodal embedders (Gemini-2 etc.) map a text query into the image space. |
| Hybrid: lexical requirement + semantic ranking ("articles about ML that mention TensorFlow") | Lexical → `filter` (`$match_all` / `$match_phrase`); semantic → `score_by` (`dense_vector`). Single call. |

**Two structural rules the agent must enforce, no exceptions:**

- **One scoring type per request.** `score_by` accepts `text` / `query_string` / `dense_vector` / `sparse_vector`, but a request ranks by *one*. Don't mix dense + text in `score_by` — the server rejects it. Multi-field BM25 is the only "list" pattern that's allowed (multiple `text` clauses, or one cross-field `query_string`).
- **Hybrid = filter + score_by, not two `score_by` clauses.** When a prompt has both a lexical requirement and a semantic ranking signal, lexical goes in `filter` (via `$match_*` operators) and semantic goes in `score_by`. If both signals genuinely need to drive *ranking*, run two searches and merge IDs client-side.

## Filterable metadata isn't declared in the schema at all

This is the single biggest shape change from the old `pinecone.preview` API, and it's easy to get only half right.

On a **managed** index (the deployment type every example in this skill uses — `{"deployment_type": "managed", "cloud": ..., "region": ...}`, which is also the default when `deployment=` is omitted), the schema may **only** declare fields that participate in **search**: `dense_vector`, `sparse_vector`, and `string` fields with `full_text_search` enabled. **Every other field type — `string` (filterable, no FTS), `string_list`, `float`, `boolean` — is rejected at create time with a 400 if it appears in the schema.** This is confirmed live, not just documented: the server's own error names all four types explicitly — *"The schema only accepts fields used for search (field types `dense_vector`, `sparse_vector`, and `string` with `full_text_search` configuration). To use field '&lt;name&gt;' for filtering (field types `boolean`, `float`, `string`, or `string_list`), omit it from the schema and include it in documents."* (That restriction is specific to managed/BYOC deployments — schema-declared filterable metadata is only legal on **pod** deployments, which this skill doesn't cover.) The `SchemaBuilder` methods `add_float_field`, `add_boolean_field`, and `add_string_list_field` still exist and still work correctly for a pod deployment; for the managed deployments this skill always uses, don't call any of them.

Instead: **don't declare any filterable-only field in the schema, of any type.** Just include the field in the documents you upsert — Pinecone indexes whatever's present on an upserted document for filtering automatically (exact-match on strings and numbers/booleans, membership on lists), whether or not it appears in the schema, with no configuration needed.

```python
# WRONG on a managed index — the server 400s on EVERY one of these, not just category:
schema = (
    SchemaBuilder()
    .add_string_field("body", full_text_search={"language": "en"})
    .add_string_field("category", filterable=True)      # <-- rejected
    .add_float_field("year", filterable=True)            # <-- also rejected
    .add_string_list_field("tags", filterable=True)      # <-- also rejected
    .build()
)

# RIGHT — the schema declares only search fields. category/year/tags are
# simply included on upserted documents and auto-index for filtering.
schema = (
    SchemaBuilder()
    .add_string_field("body", full_text_search={"language": "en"})
    .build()
)

idx.documents.upsert(namespace=NAMESPACE, documents=[{
    "_id": "doc-1",
    "body": "...",
    "year": 2025.0,          # not in the schema — still filterable via $gt / $gte / $eq
    "tags": ["classic"],     # not in the schema — still filterable via $in / $nin
    "category": "fiction",   # not in the schema — still filterable via $eq / $in / $exists
}])
```

The **forward-looking note** in `references/schema-design.md` from the old preview docs — "declare metadata fields today to be future-proof" — no longer applies; declaring any filterable-only field is now actively wrong, not just unnecessary.

## Workflow at a glance

Three phases. Each has its own reference file — consult it before writing code for that phase.

1. **Design the schema.** Decide which string fields are full-text-searchable (declared in the schema), whether you need a `dense_vector` field (and whether it earns its place), and whether you also need a `sparse_vector` field — those are the *only* things that ever go in the schema. Every filterable metadata field — string, string_list, float, boolean alike — is NOT declared; it's just included on upserted documents. Schemas are **fixed at index creation** in `2026-07` — plan carefully. → `references/schema-design.md`
2. **Ingest documents.** For bulk loads from a prepared JSONL, run the bundled `scripts/ingest.py` helper (it does `batch_upsert` + error inspection + readiness polling correctly by construction — see the **Ingesting — use the packaged helper** section above). For per-doc patch updates, use `documents.update(...)`. Either way, documents are indexed asynchronously after the HTTP call returns; `batch_upsert` returning 202 ≠ searchable. → `references/ingestion.md` for the canonical pattern in detail.
3. **Query the index.** A single search request ranks by **one** scoring type — pass exactly one of `text`, `query_string`, `dense_vector`, or `sparse_vector` in `score_by` (multi-field BM25 is supported via multiple `text` clauses or a cross-field `query_string`). Layer `filter={...}` for text-match (`$match_phrase` / `$match_all` / `$match_any`) and metadata filters (`$eq` / `$in` / `$gte` / `$exists` / `$and` / `$or` / `$not`). Control the response payload with `include_fields`. → `references/querying.md`

## Quick template

End-to-end skeleton for a minimal text + filterable-metadata index. Copy it and edit every spot marked `# TODO:`. The template deliberately omits external embedding calls so it stays generic; see `references/ingestion.md` for dense / sparse field patterns and embedding-provider integration, and `references/querying.md` for the four scoring shapes plus text-match and metadata filters.

```python
import time
from pinecone import Pinecone, SchemaBuilder

INDEX_NAME = "my-fts-index"        # TODO: name your index (lowercase alphanumeric + hyphens, 1-45 chars)
NAMESPACE = "__default__"          # TODO: pick a namespace; auto-created on first upsert

pc = Pinecone()                    # reads PINECONE_API_KEY
# TODO: preprod backends require an x-environment header on the client:
#   pc = Pinecone(additional_headers={"x-environment": "preprod-aws-0"})

# 1. Schema — one FTS string field. That's the only kind of field that goes
#    here: `category` and `year` below are deliberately NOT in the schema —
#    see "Filterable metadata isn't declared in the schema at all" in
#    SKILL.md. Field names must NOT start with `_` (reserved for `_id` /
#    `_score`) or `$` (reserved for filter operators), and are limited to 64
#    bytes.
schema = (
    SchemaBuilder()
    .add_string_field("body", full_text_search={"language": "en"})  # TODO: rename for your content
    .build()
)

# 2. Create the index. Polls until ready by default (pass timeout=-1 to
#    return immediately instead). Deployment defaults to managed/aws/us-east-1
#    when omitted; pass `deployment=` explicitly to pick a different region.
#    read_capacity defaults to {"mode": "OnDemand"}; pass
#    {"mode": "Dedicated", ...} only if you specifically want provisioned reads.
if not pc.indexes.exists(INDEX_NAME):
    pc.indexes.create(
        name=INDEX_NAME,
        schema=schema,
        deployment={"deployment_type": "managed", "cloud": "aws", "region": "us-east-1"},
    )

idx = pc.index(name=INDEX_NAME)

# 3. Upsert a single document. `_id` is required, every other field is optional.
#    `category` and `year` aren't in the schema but are still filterable —
#    see above.
#    upsert REPLACES the document on conflict; use documents.update(...) for
#    per-field patches (references/ingestion.md).
idx.documents.upsert(
    namespace=NAMESPACE,
    documents=[{
        "_id": "doc-1",
        "body": "Full-text search is great for keyword queries.",
        "category": "intro",
        "year": 2025.0,
    }],
)

# 4. Poll until the FTS side is searchable (upsert returns BEFORE docs are indexed).
deadline = time.time() + 300
while time.time() < deadline:
    resp = idx.documents.search(
        namespace=NAMESPACE, top_k=1,
        score_by=[{"type": "text", "field": "body", "query": "search"}],  # TODO: sentinel query likely to hit
        include_fields=[],          # required on every search; [] = lightest payload (ids + _score only)
    )
    if resp.matches:
        break
    time.sleep(5)

# 5. Search — text scoring composed with metadata filter.
resp = idx.documents.search(
    namespace=NAMESPACE,
    top_k=5,
    score_by=[{"type": "text", "field": "body", "query": "keyword queries"}],
    filter={"year": {"$gte": 2024}},        # TODO: adjust filter or drop it
    include_fields=["*"],                    # "*" = all stored fields; [] = `_id` + `_score` only
)
for m in resp.matches:
    print(m._id, m._score, m.to_dict())
```

## Common gotchas

- **No filterable metadata field goes in the schema on managed indexes — string, string_list, float, and boolean alike.** Only `dense_vector`, `sparse_vector`, and FTS-enabled `string` fields are legal in `schema=`; every other field type is rejected with `400`. Confirmed live against the real API, not just documented. Omit it and let it auto-index from upserted documents instead. See **Filterable metadata isn't declared in the schema at all** above — this is the change most likely to break code carried over from the old `pinecone.preview` API, where such fields were declarable.
- **One scoring type per search request.** `score_by` accepts `text`, `query_string`, `dense_vector`, or `sparse_vector` — but a request ranks by *one* type. Multi-field BM25 is fine (pass several `text` clauses, or a single cross-field `query_string`). To combine BM25 ranking with a `dense_vector` (or `sparse_vector`) signal, restrict the dense search with a text-match `filter` operator (`$match_phrase` / `$match_all` / `$match_any`) on the lexical field, *not* by mixing types in `score_by`. The "blend a dense vector and a text clause in `score_by`" pattern is rejected by the server.
- **Text-match filter operators are the cross-modal hinge.** `$match_phrase` (exact phrase), `$match_all` (every token, any order), `$match_any` (at least one token) are filter-side operators on `full_text_search` fields. Each takes a single string (max 128 tokens). They reuse the field's tokenizer / stemmer, compose under `$and` / `$or` / `$not`, and are the supported way to compose lexical pre-filtering with dense or sparse ranking. **Phrase slop (`"…"~N`), term boost (`^N`), and phrase prefix (`"… word"*`) are scoring-only — they live in `query_string`, not in `filter`.**
- **Preprod backends need `additional_headers={"x-environment": "..."}` on the `Pinecone()` client.** Missing the header lands you on prod and you'll see "index not found" / empty-result symptoms that look like code bugs but aren't.
- **`include_fields` is required on every `documents.search(...)` call.** Pass `["*"]` for all stored fields or a list of names to project. Omitting it on some SDK/backend builds yields `400` instead of a sane default; always pass it explicitly to avoid surprises.
- **Match score is `_score`; doc id is `_id`.** The system match score is always on the `_score` field so a user metadata field literally named `score` can coexist. Always read `m._score`, never `m.score`.
- **Reserved field names: leading `_` and `$`, max 64 bytes.** `_` is for system fields (`_id`, `_score`); `$` is for filter operators. Schema validation rejects names that violate either rule. Length cap is bytes, not characters — be careful with non-ASCII names.
- **Vector-field cardinality: at most one `dense_vector` and at most one `sparse_vector` per index** in `2026-07`. Multiple text fields are fine.
- **A hybrid index must declare its `sparse_vector` field at create time — there's no adding one later.** `metric="dotproduct"` on the dense field is NOT a hybrid declaration by itself in `2026-07` (it was in the old preview API). The create call succeeds either way; if the `sparse_vector` field is missing, only the *sparse writes* are refused, later, often from a different part of the codebase. If you're porting an old preview schema, audit every `metric="dotproduct"` dense field for a missing sparse field before recreating it.
- **`batch_upsert` failures are silent by default.** The return value carries `has_errors`, `failed_batch_count`, and a list of `BatchError` objects with `error_message`. If you don't inspect them, you'll see "Uploaded 0 / N" and an indefinite "not yet indexed" poll — with the real cause (payload-too-large, schema mismatch, reserved field name) hidden. Always print `result.errors[*].error_message` before downstream steps.
- **Dense-vector payload size matters at batch time.** A 50-doc batch with 3072-dim float vectors lands around 5–10 MB and can be rejected. If every batch fails, try reducing the embedding dimension via your provider's truncation knob (e.g. Gemini's `output_dimensionality=768`) before debugging schema.
- **Async indexing: `batch_upsert` returning ≠ searchable.** The server builds inverted indexes in the background after the HTTP call returns. If you query immediately you'll see empty result sets. Always poll `documents.search` with a sentinel query and a deadline (pattern in `references/ingestion.md`). This is separate from — and in addition to — `pc.indexes.create()`'s own default polling for the *index* becoming ready.
- **String FTS field shape is `full_text_search={...}` (dict), or `True` for server defaults.** **User-settable sub-fields:** `language`, `stemming`, `stop_words`, `ngram`. **Server-applied** (visible in `describe()` responses but NOT settable at index creation): `lowercase` (default `true`) and `max_term_len`. Stemming is opt-in (default `false`) and required if `stop_words=True` is set. A string field is *either* FTS-enabled *or* filterable, never both on a managed index — passing `filterable=True` alongside `full_text_search` makes the server silently keep the filter and drop the search config.
- **Schemas are fixed at index creation in `2026-07`.** Adding, removing, or retyping fields after creation is not supported. Changing dimension or metric on an existing vector field requires a new index. Plan the schema once.
- **Per-field updates are supported: `documents.update(...)`.** Pass `documents=[{"_id": ..., "set_fields": {...}}]`-style records, or `filter=` + `set_fields=`/`remove_fields=` to patch many documents at once by metadata match. `documents.upsert` still fully replaces a document on conflicting `_id` if that's what you want instead. See `references/ingestion.md` → "Updating documents".
- **Document operations: search, fetch, and delete all support `filter` now.** `documents.fetch` and `documents.delete` both gained a `filter` parameter — pass exactly one of `ids`, `filter`, or (for delete) `delete_all`. A filtered `fetch` is paginated (up to 10,000 docs per page via `pagination_token`); an ID-based fetch is never paginated. `documents.delete` returns a response object with `matched_records` (a point-in-time count for filtered deletes; `None` for ID-list or `delete_all` deletes — the delete itself is applied asynchronously).
- **`documents.list(...)` enumerates document IDs in a namespace, with no equivalent in the old preview API.** Lazily-paginated, sorted by ID, optionally filtered by `prefix`. See `references/querying.md` → "`documents.list` — enumerate document IDs".
- **Namespaces auto-create on first upsert.** Pass any namespace string to `documents.upsert` / `batch_upsert` and the namespace is created on the fly; documents from different namespaces are fully isolated. Use `"__default__"` if you don't need partitioning.
- **Namespace management and `describe_index_stats` now work on document-schema indexes.** `idx.create_namespace(name=...)`, `idx.list_namespaces()`, `idx.describe_namespace(name=...)`, `idx.delete_namespace(name=...)`, and `idx.describe_index_stats()` are all available and confirmed working — see `references/ingestion.md` → "Namespace management" for signatures and examples.
- **Document and request size limits**: per-document max **2 MB**; per-request max **2 MB and 1000 documents**; per FTS-enabled `string` field max **100 KB and 10,000 tokens** (tokens > 256 bytes are truncated by the analyzer); per-document filterable metadata (everything *not* in an FTS field) max **40 KB**. A schema can declare up to **100 FTS string fields**. For long-prose corpora, chunk before ingest — see `references/ingestion.md`.
- **`score_by` clause shape — singular `field` is canonical for `text`/`dense_vector`/`sparse_vector`; only `query_string` takes a `fields` array.**
    - `text`: `{"type":"text", "field":"<fts_field>", "query":"<terms>"}`.
    - `query_string`: `{"type":"query_string", "query":"<lucene>", "fields":["<a>","<b>"]}` (the optional `fields` array; `query_string` also accepts a bare `"fields":"body"` string and the legacy `"field":"body"` as an alias).
    - `dense_vector`: `{"type":"dense_vector", "field":"<dense_field>", "values":[/*floats*/]}`.
    - `sparse_vector`: `{"type":"sparse_vector", "field":"<sparse_field>", "sparse_values":{"indices":[...],"values":[...]}}` — note `sparse_values` (NOT `values`) for sparse clauses.
- **Single-term prefix wildcards aren't supported.** `auto*` doesn't work in `query_string`; use phrase prefix (`"machine lea"*` — phrase must contain at least two terms, last term is matched as prefix).
- **Indexes can't be created in CMEK-enabled projects alongside any `full_text_search` field, no backup/restore** for document-shaped indexes in `2026-07`. If either of these is a hard requirement, the document-schema FTS surface isn't yet ready.
- **Fuzzy (`term~N`) and regex (`field:/pattern/`) search are supported**, but only under `type: "query_string"` — see [Query syntax](https://docs.pinecone.io/guides/search/full-text-search/query-syntax) and `references/querying.md`. Neither works under `type: "text"`.
- **Bulk import from object storage is supported by Pinecone for document-shaped indexes** (see [Import data](https://docs.pinecone.io/guides/index-data/import-data)) — this skill just doesn't implement it. Use `documents.upsert` / `documents.batch_upsert` (via `scripts/ingest.py`, see above) for ingestion here, or a dedicated import skill when one exists.

## Extension points

Currently shipped under `scripts/`:

- `scripts/ingest.py` — bulk-ingest a prepared JSONL into an existing FTS index. Handles `batch_upsert` in safe-sized chunks, inspects every batch's `result.errors` and aborts loudly on failure, then polls `documents.search` with a sentinel + deadline until docs are searchable. Schema-agnostic: takes only `--data`, `--index`, `--sentinel-field`. Usage in **Ingesting — use the packaged helper** section above.

Query construction does NOT have a packaged helper — write `documents.search(...)` calls directly per the **Querying** section above.

<!-- chapter:end slug=pinecone-full-text-search -->

---

<!-- chapter:begin slug=pinecone-help position=5 -->

## 5. pinecone-help

- **Source:** https://github.com/pinecone-io/skills/blob/main/skills/pinecone-help/SKILL.md
- **Raw:** https://raw.githubusercontent.com/pinecone-io/skills/main/skills/pinecone-help/SKILL.md
- **Markdown:** https://skillsdocs.com/pinecone-io/skills/pinecone-help.md
- **Licence:** MIT — https://spdx.org/licenses/MIT.html

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: pinecone-help
description: Overview of all available Pinecone skills and what a user needs to get started. Invoke when a user asks what skills are available, how to get started with Pinecone, or what they need to set up before using any Pinecone skill.
---

# Pinecone Skills — Help & Overview

Pinecone is the leading vector database for building accurate and performant AI applications at scale in production. It's useful for building semantic search, retrieval augmented generation, recommendation systems, and agentic applications.

Here's everything you need to get started and a summary of all available skills.

<<invoke_any_skill>>

---

## What You Need

### Required
- **Pinecone account** — free to create at https://app.pinecone.io/?sessionType=signup
- **API key** — create one in the Pinecone console after signing up, then make it
  available to this environment:
  <<api_key_setup>>

### Optional (unlock more capabilities)

| Tool | What it enables | Install |
|---|---|---|
| **Pinecone MCP server** | Use Pinecone directly inside your AI agent/IDE without writing code | [Setup guide](https://docs.pinecone.io/guides/operations/mcp-server#tools) |
| **Pinecone CLI (`pc`)** | Manage all index types from the terminal, batch operations, backups, CI/CD | `brew tap pinecone-io/tap && brew install pinecone-io/tap/pinecone` |
| **uv** | Run the packaged Python scripts included in these skills | [Install uv](https://docs.astral.sh/uv/getting-started/installation/) |

---

## Available Skills

| Skill | What it does |
|---|---|
| `pinecone-quickstart` | Step-by-step onboarding — create an index, upload data, and run your first search |
| `pinecone-query` | Search integrated indexes using natural language text via the Pinecone MCP |
| `pinecone-cli` | Use the Pinecone CLI (`pc`) for terminal-based index and vector management |
| `pinecone-assistant` | Create, manage, and chat with Pinecone Assistants for document Q&A with citations |
| `pinecone-mcp` | Reference for all Pinecone MCP server tools and their parameters |
| `pinecone-full-text-search` | Build a full-text-search index — schema design, safe bulk ingestion, and query construction (`text` / `query_string` / dense / sparse scoring with text-match and metadata filters). **Document-schema API (`2026-07`); requires `pinecone` Python SDK ≥ 10.0.0.** |
| `pinecone-docs` | Curated links to official Pinecone documentation, organized by topic |
| `pinecone-n8n` | Build n8n workflows with the Pinecone Assistant node or Pinecone Vector Store node, including best practices and full workflow JSON generation |

---

## Which skill should I use?

**Just getting started?** → `pinecone-quickstart`

**Want to search an index you already have?**
- Integrated index (built-in embedding model) → `pinecone-query` (uses MCP)
- Any other index type → `pinecone-cli`

**Working with documents and Q&A?** → `pinecone-assistant`

**Building a full-text search index (BM25-style keyword/phrase matching, optionally combined with dense or sparse vectors)?** → `pinecone-full-text-search` (document-schema API, needs `pinecone` Python SDK ≥ 10.0.0)

**Building an n8n workflow with Pinecone (RAG pipeline, chat with docs)?** → `pinecone-n8n`

**Need to manage indexes, bulk upload vectors, or automate workflows?** → `pinecone-cli`

**Looking up API parameters or SDK usage?** → `pinecone-docs`

**Need to understand what MCP tools are available?** → `pinecone-mcp`

<!-- chapter:end slug=pinecone-help -->

---

<!-- chapter:begin slug=pinecone-mcp position=6 -->

## 6. pinecone-mcp

- **Source:** https://github.com/pinecone-io/skills/blob/main/skills/pinecone-mcp/SKILL.md
- **Raw:** https://raw.githubusercontent.com/pinecone-io/skills/main/skills/pinecone-mcp/SKILL.md
- **Markdown:** https://skillsdocs.com/pinecone-io/skills/pinecone-mcp.md
- **Licence:** MIT — https://spdx.org/licenses/MIT.html

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: pinecone-mcp
description: Reference for the Pinecone MCP server tools. Documents all available tools - list-indexes, describe-index, describe-index-stats, create-index-for-model, upsert-records, search-records, cascading-search, and rerank-documents. Use when an agent needs to understand what Pinecone MCP tools are available, how to use them, or what parameters they accept.
---

# Pinecone MCP Tools Reference

The Pinecone MCP server exposes the following tools to AI agents and IDEs. For setup and installation instructions, see the [MCP server guide](https://docs.pinecone.io/guides/operations/mcp-server#tools).

> **Key Limitation:** The Pinecone MCP only supports **integrated indexes** — indexes created with a built-in Pinecone embedding model. It does not work with standard indexes using external embedding models. For those, use the Pinecone CLI.

---

## `list-indexes`

List all indexes in the current Pinecone project.

---

## `describe-index`

Get configuration details for a specific index — cloud, region, dimension, metric, embedding model, field map, and status.

**Parameters:**
- `name` (required) — Index name

---

## `describe-index-stats`

Get statistics for an index including total record count and per-namespace breakdown.

**Parameters:**
- `name` (required) — Index name

---

## `create-index-for-model`

Create a new serverless index with an integrated embedding model. Pinecone handles embedding automatically — no external model needed.

**Parameters:**
- `name` (required) — Index name
- `cloud` (required) — `aws`, `gcp`, or `azure`
- `region` (required) — Cloud region (e.g. `us-east-1`)
- `embed.model` (required) — Embedding model: `llama-text-embed-v2`, `multilingual-e5-large`, or `pinecone-sparse-english-v0`
- `embed.fieldMap.text` (required) — The record field that contains text to embed (e.g. `chunk_text`)

---

## `upsert-records`

Insert or update records in an integrated index. Records are automatically embedded using the index's configured model.

**Parameters:**
- `name` (required) — Index name
- `namespace` (required) — Namespace to upsert into
- `records` (required) — Array of records. Each record must have an `id` or `_id` field and contain the text field specified in the index's `fieldMap`. Do not nest fields under `metadata` — put them directly on the record.

**Example record:**
```json
{ "_id": "rec1", "chunk_text": "The Eiffel Tower was built in 1889.", "category": "architecture" }
```

---

## `search-records`

Semantic text search against an integrated index. Pass plain text — the MCP embeds the query automatically using the index's model.

**Parameters:**
- `name` (required) — Index name
- `namespace` (required) — Namespace to search
- `query.inputs.text` (required) — The text query
- `query.topK` (required) — Number of results to return
- `query.filter` (optional) — Metadata filter using MongoDB-style operators (`$eq`, `$ne`, `$in`, `$gt`, `$gte`, `$lt`, `$lte`)
- `rerank.model` (optional) — Reranking model: `bge-reranker-v2-m3`, `cohere-rerank-3.5`, or `pinecone-rerank-v0`
- `rerank.rankFields` (optional) — Fields to rerank on (e.g. `["chunk_text"]`)
- `rerank.topN` (optional) — Number of results to return after reranking

---

## `cascading-search`

Search across multiple indexes simultaneously, then deduplicate and rerank results into a single ranked list.

**Parameters:**
- `indexes` (required) — Array of `{ name, namespace }` objects to search across
- `query.inputs.text` (required) — The text query
- `query.topK` (required) — Number of results to retrieve per index before reranking
- `rerank.model` (required) — Reranking model: `bge-reranker-v2-m3`, `cohere-rerank-3.5`, or `pinecone-rerank-v0`
- `rerank.rankFields` (required) — Fields to rerank on
- `rerank.topN` (optional) — Final number of results to return after reranking

---

## `rerank-documents`

Rerank a set of documents or records against a query without performing a vector search first.

**Parameters:**
- `model` (required) — `bge-reranker-v2-m3`, `cohere-rerank-3.5`, or `pinecone-rerank-v0`
- `query` (required) — The query to rerank against
- `documents` (required) — Array of strings or records to rerank
- `options.topN` (required) — Number of results to return
- `options.rankFields` (optional) — If documents are records, the field(s) to rerank on

<!-- chapter:end slug=pinecone-mcp -->

---

<!-- chapter:begin slug=pinecone-n8n position=7 -->

## 7. pinecone-n8n

- **Source:** https://github.com/pinecone-io/skills/blob/main/skills/pinecone-n8n/SKILL.md
- **Raw:** https://raw.githubusercontent.com/pinecone-io/skills/main/skills/pinecone-n8n/SKILL.md
- **Markdown:** https://skillsdocs.com/pinecone-io/skills/pinecone-n8n.md
- **Licence:** MIT — https://spdx.org/licenses/MIT.html

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: pinecone-n8n
description: Build n8n workflows using the Pinecone Assistant node or Pinecone Vector Store node. Use when building RAG pipelines, chat-with-docs workflows, configuring Pinecone nodes in n8n, troubleshooting Pinecone n8n nodes, or asking about best practices for Pinecone in n8n.
---

# Pinecone n8n Workflow Skill

<<invoke_this_skill>>

This skill helps you build n8n workflows with Pinecone nodes following best practices. It covers two Pinecone nodes:
- **Pinecone Assistant** (`@pinecone-database/n8n-nodes-pinecone-assistant`) — recommended for most use cases
- **Pinecone Vector Store** (`@n8n/n8n-nodes-langchain.vectorStorePinecone`) — for advanced control

**Core rule:** Always use the node's built-in resources and operations. Never suggest using the HTTP node to call the Pinecone REST API directly.

---

## Step 1: Understand the user's scenario

Ask the user what they're trying to do:
- Build a new workflow from scratch
- Configure or understand a specific Pinecone node
- Debug a workflow that isn't working
- Review an existing workflow for best practices

---

## Step 2: Node selection (for new workflows and configuration questions)

Always present the Pinecone Assistant node as the recommended choice first. Do NOT skip this step based on your own inference about which node fits better — even if the use case mentions specific triggers (Google Drive, webhooks, etc.) or file types (text, markdown, PDF), those details do not determine which node to use.

**Only skip this step if:**
- The user explicitly names a specific node (e.g. "I want to use the Vector Store node", "help me set up pineconeAssistant")
- The user is debugging or configuring an existing workflow that already has a specific Pinecone node in it

**If the user has not named a node, always ask or recommend the Assistant node first.** If the user said "use defaults" or you cannot ask, default to the Pinecone Assistant node and proceed with the Assistant path.

Ask the user which node they want to use, presenting these two options:

**Pinecone Assistant (Recommended)**
- Fully managed RAG — Pinecone handles chunking, embedding, and indexing automatically
- Built-in citations with file names and URLs
- Simpler setup: no embedding model or text splitter needed in n8n
- Great for: document Q&A, chat with files, knowledge base search

**Pinecone Vector Store**
- Full control over embedding model, chunking strategy, and metadata
- Works with any embedding model (OpenAI, Cohere, HuggingFace, etc.)
- Required when: you need custom embeddings, have an existing Pinecone index, need metadata filtering, or need fine-grained control over chunking

---

## Pinecone Assistant Node — Best Practices and Workflow Generation

### Node package names
- File operations (upload, list, delete): `@pinecone-database/n8n-nodes-pinecone-assistant.pineconeAssistant`
- Chat/retrieval as AI Agent tool: `@pinecone-database/n8n-nodes-pinecone-assistant.pineconeAssistantTool`

### Prerequisites
- Create a Pinecone Assistant in the Pinecone Console at https://app.pinecone.io/organizations/-/projects/-/assistant before running the workflow
- Set up a Pinecone credential in n8n with your API key

### Workflow architecture
The standard pattern is a two-phase workflow:

**Phase 1 — Ingestion** (run once or on a schedule):
```
Manual Trigger → Set file URLs → Split Out → HTTP Request (download) → Pinecone Assistant (uploadFile)
```

**Phase 2 — Chat**:
```
Chat Trigger → AI Agent ← Pinecone Assistant Tool (connected as ai_tool)
                        ← OpenAI Chat Model (connected as ai_languageModel)
```

### Key configuration rules
1. **assistantData parameter**: Always include BOTH `name` and `host` fields:
   ```json
   {"name": "your-assistant-name", "host": "https://your-assistant-host.pinecone.io"}
   ```
   Find your assistant's host in the Pinecone Console: open the assistant detail page and copy the host URL (format: `https://<region>-data.<subdomain>.pinecone.io`).
2. **sourceTag**: Always include in `additionalFields`:
   ```json
   {"sourceTag": "n8n:n8n_nodes_pinecone_assistant:pinecone_n8n_skill"}
   ```
3. **Connection type**: The Assistant Tool connects to the AI Agent via the `ai_tool` connection — NOT `main`
4. **externalFileId**: Set this to the file URL expression so Pinecone stores it as a reference for citations
5. **Credential**: Use `pineconeApi` credential type for both node variants
6. **File metadata on upload**: Add key-value metadata via `additionalFields.metadata.metadataValues` — an array of `{"key": "...", "value": "..."}` objects. The `externalFileId` is automatically added to metadata; do not include it manually. Example:
   ```json
   "additionalFields": {
     "metadata": {"metadataValues": [{"key": "department", "value": "legal"}]}
   }
   ```
7. **Metadata filtering on listFiles**: Use `additionalFields.metadataFilter.metadataValues` (same `{key, value}` array) for simple equality filters, or `additionalFields.advancedMetadataFilter` (a JSON string) for operators like `$or`, `$ne`, `$in`. Cannot set both at once. Example simple filter:
   ```json
   "additionalFields": {
     "metadataFilter": {"metadataValues": [{"key": "department", "value": "legal"}]}
   }
   ```
8. **Multimodal PDF upload**: Set `additionalFields.multimodalFile: true` on the `uploadFile` node when the PDF contains images or charts that should be indexed for visual retrieval. This is required for images to be retrievable later — it is not the default.

### Generating workflow JSON for the Assistant path

Build the workflow to match what the user actually describes — their triggers, models, data sources, and structure. Ask about anything structurally significant they haven't mentioned. Only fall back to the defaults below when the user hasn't specified a value:
- Assistant name: `n8n-assistant` (use `n8n-assistant-1`, `n8n-assistant-2`, etc. for multiples; must match an existing assistant in the Pinecone Console)
- File URLs: sample Pinecone release notes PDFs
- LLM model: `gpt-5-mini`
- System message: generic prompt about retrieving from the assistant with citations

The JSON below is a **reference configuration** showing correct parameter values, required fields, and connection types for each node. Use it as a guide for how to configure the nodes — not as a template to copy verbatim. Placeholders to substitute:
- `[ASSISTANT_NAME]` — assistant name
- `[ASSISTANT_HOST]` — assistant host URL from the Pinecone Console (e.g. `https://your-assistant-host.pinecone.io`)
- `[USER_FILE_URLS_ARRAY]` — JSON array of file URL strings, e.g. `["https://example.com/doc.pdf"]`
- `[USER_MODEL]` — LLM model name, e.g. `gpt-5-mini`
- `[USER_TOPIC]` — short description of what the assistant knows, for the system message

```json
{
  "nodes": [
    {
      "parameters": {
        "options": {
          "systemMessage": "You are a helpful assistant. Use the Pinecone Assistant Tool to retrieve data about [USER_TOPIC]. Include the file name and file url in citations wherever referenced in output."
        }
      },
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 2.2,
      "position": [2208, 784],
      "id": "e4c65881-120c-4a7c-854b-138611c8dfa3",
      "name": "AI Agent"
    },
    {
      "parameters": {
        "model": {"__rl": true, "mode": "list", "value": "[USER_MODEL]"},
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "typeVersion": 1.2,
      "position": [2144, 1008],
      "id": "b3ea858d-b62d-4022-8241-8872e403839a",
      "name": "OpenAI Chat Model"
    },
    {
      "parameters": {
        "content": "## 1. Upload files to Pinecone Assistant",
        "height": 384,
        "width": 1104,
        "color": 7
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [1616, 288],
      "typeVersion": 1,
      "id": "9cfcdb71-2986-47a3-8f03-250fcab1048d",
      "name": "Sticky Note1"
    },
    {
      "parameters": {
        "content": "## 2. Chat with your docs",
        "height": 512,
        "width": 1104,
        "color": 7
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [1616, 688],
      "typeVersion": 1,
      "id": "d7f2f4b8-2e45-4902-8949-202b8b2c699b",
      "name": "Sticky Note2"
    },
    {
      "parameters": {"options": {}},
      "type": "@n8n/n8n-nodes-langchain.chatTrigger",
      "typeVersion": 1.3,
      "position": [1840, 784],
      "id": "4d2a6aa1-ca4d-4165-a635-7ef53084636b",
      "name": "Chat input",
      "webhookId": "4672d1f8-d2bb-4059-8761-7aa5792814c0"
    },
    {
      "parameters": {},
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [1760, 432],
      "id": "3e9529b4-d0ae-4ea8-9d27-c96e7cbd6ad9",
      "name": "When clicking 'Execute workflow'"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "d0e724df-685f-4661-b2ec-3cdd3c2ba0f1",
              "name": "urls",
              "value": "[USER_FILE_URLS_ARRAY]",
              "type": "array"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [1920, 432],
      "id": "6b409421-2270-497e-a7fd-b382d192314c",
      "name": "Set file urls"
    },
    {
      "parameters": {"fieldToSplitOut": "urls", "options": {}},
      "type": "n8n-nodes-base.splitOut",
      "typeVersion": 1,
      "position": [2080, 432],
      "id": "e73f6f2c-4d20-48cb-b132-551ff9c3dd61",
      "name": "Split to list"
    },
    {
      "parameters": {
        "url": "={{ $json.urls }}",
        "options": {"response": {"response": {"responseFormat": "file"}}}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [2240, 432],
      "id": "a58f8e1c-943b-4a24-8746-477ed9912ad4",
      "name": "Download file"
    },
    {
      "parameters": {
        "resource": "file",
        "operation": "uploadFile",
        "assistantData": "{\"name\":\"[ASSISTANT_NAME]\",\"host\":\"https://[ASSISTANT_HOST]\"}",
        "externalFileId": "={{ $('Split to list').item.json.urls }}",
        "additionalFields": {"sourceTag": "n8n:n8n_nodes_pinecone_assistant:pinecone_n8n_skill"}
      },
      "type": "@pinecone-database/n8n-nodes-pinecone-assistant.pineconeAssistant",
      "typeVersion": 1.2,
      "position": [2416, 432],
      "id": "196122df-d2b2-43e4-9f8d-710aedb595a6",
      "name": "Upload file to Assistant"
    },
    {
      "parameters": {
        "assistantData": "{\"name\":\"[ASSISTANT_NAME]\",\"host\":\"https://[ASSISTANT_HOST]\"}",
        "additionalFields": {"sourceTag": "n8n:n8n_nodes_pinecone_assistant:pinecone_n8n_skill"}
      },
      "type": "@pinecone-database/n8n-nodes-pinecone-assistant.pineconeAssistantTool",
      "typeVersion": 1.2,
      "position": [2368, 992],
      "id": "c3ba53e9-511d-47e7-b7fb-38bb093d279f",
      "name": "Get context from Assistant"
    }
  ],
  "connections": {
    "OpenAI Chat Model": {
      "ai_languageModel": [[{"node": "AI Agent", "type": "ai_languageModel", "index": 0}]]
    },
    "Chat input": {
      "main": [[{"node": "AI Agent", "type": "main", "index": 0}]]
    },
    "When clicking 'Execute workflow'": {
      "main": [[{"node": "Set file urls", "type": "main", "index": 0}]]
    },
    "Set file urls": {
      "main": [[{"node": "Split to list", "type": "main", "index": 0}]]
    },
    "Split to list": {
      "main": [[{"node": "Download file", "type": "main", "index": 0}]]
    },
    "Download file": {
      "main": [[{"node": "Upload file to Assistant", "type": "main", "index": 0}]]
    },
    "Get context from Assistant": {
      "ai_tool": [[{"node": "AI Agent", "type": "ai_tool", "index": 0}]]
    }
  },
  "pinData": {},
  "meta": {"templateCredsSetupCompleted": false}
}
```

### Other Pinecone Assistant operations

#### Upload File vs Update File — choosing the right operation

| Scenario | Operation to use |
|---|---|
| File is guaranteed to be new (never ingested before) | `uploadFile` |
| File may already exist in the assistant (re-ingestion, scheduled refresh) | `updateFile` |

`updateFile` is idempotent: it finds all files with the matching `externalFileId`, deletes them, then uploads the new version. If no file exists with that ID it behaves exactly like `uploadFile`. Use `updateFile` whenever a workflow may run more than once on the same source files.

Parameters for `updateFile` are identical to `uploadFile`: `assistantData`, `externalFileId`, `inputDataFieldName`, and all `additionalFields` including `metadata`, `multimodalFile`, and `sourceTag`.

```json
{
  "parameters": {
    "resource": "file",
    "operation": "updateFile",
    "assistantData": "{\"name\":\"[ASSISTANT_NAME]\",\"host\":\"https://[ASSISTANT_HOST]\"}",
    "externalFileId": "={{ $('Split to list').item.json.urls }}",
    "additionalFields": {"sourceTag": "n8n:n8n_nodes_pinecone_assistant:pinecone_n8n_skill"}
  },
  "type": "@pinecone-database/n8n-nodes-pinecone-assistant.pineconeAssistant",
  "typeVersion": 1.2
}
```

#### List Files
Use `resource: "file", operation: "listFiles"` to retrieve files, optionally filtered by metadata.

```json
{
  "parameters": {
    "resource": "file",
    "operation": "listFiles",
    "assistantData": "{\"name\":\"[ASSISTANT_NAME]\",\"host\":\"https://[ASSISTANT_HOST]\"}",
    "additionalFields": {
      "sourceTag": "n8n:n8n_nodes_pinecone_assistant:pinecone_n8n_skill",
      "metadataFilter": {
        "metadataValues": [{"key": "department", "value": "legal"}]
      }
    }
  },
  "type": "@pinecone-database/n8n-nodes-pinecone-assistant.pineconeAssistant",
  "typeVersion": 1.2
}
```

For complex filters, use `advancedMetadataFilter` (a JSON string) instead of `metadataFilter` — never both:
```json
"additionalFields": {
  "advancedMetadataFilter": "{\"department\": {\"$in\": [\"legal\", \"finance\"]}}"
}
```

#### Get Context Snippets
Use `resource: "contextSnippet", operation: "getContextSnippets"` to retrieve relevant text or image chunks directly — useful when you need raw retrieved context as workflow data rather than a chat reply.

```json
{
  "parameters": {
    "resource": "contextSnippet",
    "operation": "getContextSnippets",
    "assistantData": "{\"name\":\"[ASSISTANT_NAME]\",\"host\":\"https://[ASSISTANT_HOST]\"}",
    "query": "={{ $json.chatInput }}",
    "additionalFields": {
      "sourceTag": "n8n:n8n_nodes_pinecone_assistant:pinecone_n8n_skill",
      "includeMultimodalContext": true,
      "includeBinaryContent": true,
      "topK": 16,
      "snippetSize": 2048
    }
  },
  "type": "@pinecone-database/n8n-nodes-pinecone-assistant.pineconeAssistant",
  "typeVersion": 1.2
}
```

The same `metadataFilter` / `advancedMetadataFilter` options work on `getContextSnippets` to scope retrieval to files matching specific metadata. The `includeBinaryContent` flag only applies when `includeMultimodalContext` is `true`.

---

## Pinecone Vector Store Node — Best Practices and Workflow Generation

### Node package name
`@n8n/n8n-nodes-langchain.vectorStorePinecone`

### Prerequisites
- Create a Pinecone index in the Pinecone Console at https://app.pinecone.io/organizations/-/projects/-/indexes with the correct name and dimensions before running the workflow
- Set up a Pinecone credential in n8n with your API key
- Set up an OpenAI credential in n8n

### Workflow architecture
Two-phase workflow with two separate `vectorStorePinecone` node instances:

**Phase 1 — Ingestion** (run once or on a schedule):
```
Manual Trigger → Set file URLs → Split Out → HTTP Request (download)
  → Pinecone Vector Store (insert mode)
       ↑ Default Data Loader ← Recursive Character Text Splitter
       ↑ Embeddings OpenAI
```

**Phase 2 — Chat**:
```
Chat Trigger → AI Agent ← Pinecone Vector Store (retrieve-as-tool mode)
                               ↑ Embeddings OpenAI (same model as insert)
                        ← OpenAI Chat Model
```

### Key configuration rules
1. **Two node instances**: Use one `vectorStorePinecone` in `insert` mode for ingestion and a separate one in `retrieve-as-tool` mode for chat
2. **Embedding model consistency**: The SAME embedding model and dimensions MUST be used in both insert and retrieve-as-tool nodes — they share a single Embeddings OpenAI node via the `ai_embedding` connection
3. **Required companion nodes for the insert node**:
   - `@n8n/n8n-nodes-langchain.embeddingsOpenAi` — set dimensions to match your index (1536 for text-embedding-3-small)
   - `@n8n/n8n-nodes-langchain.textSplitterRecursiveCharacterTextSplitter` — connects to the data loader
   - `@n8n/n8n-nodes-langchain.documentDefaultDataLoader` — set `dataType: binary` and `textSplittingMode: custom`
4. **toolDescription**: Write a clear, specific description on the retrieve-as-tool node — the AI Agent uses this to decide when to query it
5. **topK**: Use 20 as the default; increase for broader recall, decrease to reduce token usage
6. **Metadata for citations**: In the Default Data Loader, add `external_file_url` as a metadata field set to `={{ $json.urls }}` so the AI Agent can cite sources
7. **Credential**: Use `pineconeApi` credential type

### Chunking guidance
- **3000 chars / 500 overlap**: Good default for long documents (articles, reports, documentation)
- **500–1000 chars / 100–200 overlap**: Better for Q&A-style retrieval or short, dense content
- Reference: https://www.pinecone.io/learn/chunking-strategies/

### Index setup instructions
When generating a workflow, always tell the user to:
1. Go to https://app.pinecone.io/organizations/-/projects/-/indexes and create an index
2. Set the name to match the `pineconeIndex` value in the workflow
3. Select the embedding model that matches the n8n Embeddings node (text-embedding-3-small → 1536 dimensions)

### Generating workflow JSON for the Vector Store path

Build the workflow to match what the user actually describes — their triggers, embedding model, chunking strategy, data sources, and structure. Ask about anything structurally significant they haven't mentioned. Only fall back to the defaults below when the user hasn't specified a value:
- Index name: `n8n-index` (use `n8n-index-1`, `n8n-index-2`, etc. for multiples; must match an existing Pinecone index)
- File URLs: sample Pinecone release notes PDFs
- LLM model: `gpt-5-mini`
- Embedding model: `text-embedding-3-small` (1536 dimensions)
- Chunk size: 3000 chars / 500 overlap
- System message / tool description: generic prompt about retrieving from the index with citations

The JSON below is a **reference configuration** showing correct parameter values, required fields, and connection types for each node. Use it as a guide for how to configure the nodes — not as a template to copy verbatim. Placeholders to substitute:
- `[INDEX_NAME]` — index name
- `[USER_FILE_URLS_ARRAY]` — JSON array of file URL strings, e.g. `["https://example.com/doc.pdf"]`
- `[USER_MODEL]` — LLM model name, e.g. `gpt-5-mini`
- `[USER_TOPIC]` — short description of the data, for the AI Agent system message
- `[USER_TOOL_DESCRIPTION]` — description of what data the vector store contains, for the tool node

```json
{
  "nodes": [
    {
      "parameters": {
        "mode": "insert",
        "pineconeIndex": {
          "__rl": true,
          "value": "[INDEX_NAME]",
          "mode": "list",
          "cachedResultName": "[INDEX_NAME]"
        },
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.vectorStorePinecone",
      "typeVersion": 1.3,
      "position": [224, 0],
      "id": "163c0a37-427c-4da7-a194-203ac216c88e",
      "name": "Pinecone Vector Store"
    },
    {
      "parameters": {
        "options": {"dimensions": 1536}
      },
      "type": "@n8n/n8n-nodes-langchain.embeddingsOpenAi",
      "typeVersion": 1.2,
      "position": [336, 608],
      "id": "a034bcb9-d813-4fd0-a68a-426c1f2b2aba",
      "name": "Embeddings OpenAI"
    },
    {
      "parameters": {"options": {}},
      "type": "@n8n/n8n-nodes-langchain.chatTrigger",
      "typeVersion": 1.3,
      "position": [-512, 896],
      "id": "56182cf7-2bda-4513-a2f9-18fb70c718ec",
      "name": "When chat message received",
      "webhookId": "30328376-a9e3-4f11-b1eb-2bbaadbea688"
    },
    {
      "parameters": {
        "options": {
          "systemMessage": "You are a helpful assistant. Only use the Pinecone Vector Store Tool to retrieve data about [USER_TOPIC]. Include the file name and file url in citations wherever referenced in output."
        }
      },
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 2.2,
      "position": [-336, 896],
      "id": "36bb8361-edfd-47ff-aaf1-48f3c31fb1f7",
      "name": "AI Agent"
    },
    {
      "parameters": {
        "model": {
          "__rl": true,
          "value": "[USER_MODEL]",
          "mode": "list",
          "cachedResultName": "[USER_MODEL]"
        },
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "typeVersion": 1.2,
      "position": [-336, 1200],
      "id": "22d00f25-44eb-40c1-8dae-b757e4b66690",
      "name": "OpenAI Chat Model"
    },
    {
      "parameters": {
        "mode": "retrieve-as-tool",
        "toolDescription": "[USER_TOOL_DESCRIPTION]",
        "pineconeIndex": {
          "__rl": true,
          "value": "[INDEX_NAME]",
          "mode": "list",
          "cachedResultName": "[INDEX_NAME]"
        },
        "topK": 20,
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.vectorStorePinecone",
      "typeVersion": 1.3,
      "position": [-16, 1104],
      "id": "15c599b7-93bb-44b7-b95c-190200de2a20",
      "name": "Pinecone Vector Store Tool"
    },
    {
      "parameters": {
        "content": "## 1. Process new files, embed, and upsert to Pinecone index",
        "height": 576,
        "width": 1744,
        "color": 7
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [-608, -80],
      "typeVersion": 1,
      "id": "18f22d77-fb10-4fa7-b942-2fafd7ca30c3",
      "name": "Sticky Note"
    },
    {
      "parameters": {
        "content": "## 2. Chat with your docs",
        "height": 640,
        "width": 1744,
        "color": 7
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [-608, 784],
      "typeVersion": 1,
      "id": "7b7be380-821c-4012-8f1a-1606527d2544",
      "name": "Sticky Note1"
    },
    {
      "parameters": {},
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [-512, 0],
      "id": "a0a3b917-765b-445e-9866-d3fc651ef6a8",
      "name": "When clicking 'Execute workflow'"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "d0e724df-685f-4661-b2ec-3cdd3c2ba0f1",
              "name": "urls",
              "value": "[USER_FILE_URLS_ARRAY]",
              "type": "array"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [-352, 0],
      "id": "651439de-5525-4d0a-b055-1d722e81c0aa",
      "name": "Set file urls"
    },
    {
      "parameters": {"fieldToSplitOut": "urls", "options": {}},
      "type": "n8n-nodes-base.splitOut",
      "typeVersion": 1,
      "position": [-192, 0],
      "id": "8878265e-9e6f-4d9a-b533-53100205eb38",
      "name": "Split to list"
    },
    {
      "parameters": {
        "chunkSize": 3000,
        "chunkOverlap": 500,
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.textSplitterRecursiveCharacterTextSplitter",
      "typeVersion": 1,
      "position": [416, 352],
      "id": "347ba65b-9fad-4b92-a90f-6690df8e2a7e",
      "name": "Recursive Character Text Splitter"
    },
    {
      "parameters": {
        "url": "={{ $json.urls }}",
        "options": {"response": {"response": {"responseFormat": "file"}}}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [-32, 0],
      "id": "edf963af-18ee-4605-9b1e-49118c56b52d",
      "name": "Download file"
    },
    {
      "parameters": {
        "dataType": "binary",
        "textSplittingMode": "custom",
        "options": {
          "metadata": {
            "metadataValues": [
              {"name": "external_file_url", "value": "={{ $json.urls }}"}
            ]
          }
        }
      },
      "type": "@n8n/n8n-nodes-langchain.documentDefaultDataLoader",
      "typeVersion": 1.1,
      "position": [336, 192],
      "id": "e9b99301-3af1-4d19-ab78-3fd0e8946fd1",
      "name": "Default data loader"
    }
  ],
  "connections": {
    "Embeddings OpenAI": {
      "ai_embedding": [[
        {"node": "Pinecone Vector Store", "type": "ai_embedding", "index": 0},
        {"node": "Pinecone Vector Store Tool", "type": "ai_embedding", "index": 0}
      ]]
    },
    "When chat message received": {
      "main": [[{"node": "AI Agent", "type": "main", "index": 0}]]
    },
    "OpenAI Chat Model": {
      "ai_languageModel": [[{"node": "AI Agent", "type": "ai_languageModel", "index": 0}]]
    },
    "Pinecone Vector Store Tool": {
      "ai_tool": [[{"node": "AI Agent", "type": "ai_tool", "index": 0}]]
    },
    "When clicking 'Execute workflow'": {
      "main": [[{"node": "Set file urls", "type": "main", "index": 0}]]
    },
    "Set file urls": {
      "main": [[{"node": "Split to list", "type": "main", "index": 0}]]
    },
    "Split to list": {
      "main": [[{"node": "Download file", "type": "main", "index": 0}]]
    },
    "Recursive Character Text Splitter": {
      "ai_textSplitter": [[{"node": "Default data loader", "type": "ai_textSplitter", "index": 0}]]
    },
    "Download file": {
      "main": [[{"node": "Pinecone Vector Store", "type": "main", "index": 0}]]
    },
    "Default data loader": {
      "ai_document": [[{"node": "Pinecone Vector Store", "type": "ai_document", "index": 0}]]
    }
  },
  "pinData": {},
  "meta": {"templateCredsSetupCompleted": false}
}
```

---

## Debugging guidance

When a user reports a broken workflow, ask which node is failing and what error they see, then refer to these common issues:

| Error / Symptom | Cause | Fix |
|---|---|---|
| "Assistant not found" | `assistantData.name` doesn't match an existing assistant | Create the assistant in the Pinecone Console first, then match the name exactly |
| "Invalid credentials" | Pinecone API key not set or wrong credential on the node | Re-select the `pineconeApi` credential on each Pinecone node |
| "Dimension mismatch" | Embedding model dimensions don't match the index | Index and embeddings node must use the same dimensions (e.g., 1536 for text-embedding-3-small) |
| Tool never called by Agent | `toolDescription` is vague | Make the description specific: what data is in the store, what questions it can answer |
| No results returned | Ingestion didn't run, or topK too low | Run Phase 1 first; try increasing topK to 20+ |
| HTTP node calling Pinecone API | Wrong pattern | Replace with `@pinecone-database/n8n-nodes-pinecone-assistant.pineconeAssistant` or `@n8n/n8n-nodes-langchain.vectorStorePinecone` |

---

## Best practices review checklist

When reviewing an existing workflow:
1. **No HTTP nodes calling Pinecone endpoints** — replace any with the proper Pinecone node
2. **Vector Store**: same Embeddings node connected to both insert and retrieve-as-tool instances
3. **Assistant**: `assistantData` includes both `name` and `host`; `sourceTag` is present in `additionalFields`
4. **toolDescription** is specific and descriptive (not generic like "a vector store")
5. **Vector Store metadata**: `external_file_url` stored in the Default Data Loader metadata for source citations
6. **Assistant file metadata**: files uploaded with meaningful key-value metadata when filtering will be needed later
7. **Metadata filter**: `listFiles` and `getContextSnippets` use a metadata filter (not both filter fields at once) when scoping to a subset of files
8. **Multimodal upload**: `multimodalFile: true` is set on `uploadFile` when PDFs contain images or charts — this is required to index visual content and is NOT the default
9. **Multimodal retrieval**: `includeMultimodalContext: true` and `includeBinaryContent: true` on `getContextSnippets` when the workflow needs image chunks alongside text
10. **Chunk size** is appropriate for the content type (see chunking guidance above)
11. **Index exists** with correct dimensions before the workflow runs

<!-- chapter:end slug=pinecone-n8n -->

---

<!-- chapter:begin slug=pinecone-query position=8 -->

## 8. pinecone-query

- **Source:** https://github.com/pinecone-io/skills/blob/main/skills/pinecone-query/SKILL.md
- **Raw:** https://raw.githubusercontent.com/pinecone-io/skills/main/skills/pinecone-query/SKILL.md
- **Markdown:** https://skillsdocs.com/pinecone-io/skills/pinecone-query.md
- **Licence:** MIT — https://spdx.org/licenses/MIT.html

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: pinecone-query
description: Query integrated indexes using text with Pinecone MCP. IMPORTANT - This skill ONLY works with integrated indexes (indexes with built-in Pinecone embedding models like multilingual-e5-large). For standard indexes or advanced vector operations, use the pinecone-cli skill instead. Requires PINECONE_API_KEY environment variable and Pinecone MCP server to be configured.
argument-hint: query [q] index [indexName] namespace [ns] topK [k] reranker [rerankModel]
---

# Pinecone Query Skill

Search for records in Pinecone integrated indexes using natural language text queries via the Pinecone MCP server.

<<clarify_style>>

## What is this skill for?

This skill provides a simple way to query **integrated indexes** (indexes with built-in Pinecone embedding models) using text queries. The MCP server automatically converts your text into embeddings and searches the index.

### Prerequisites

**Required:**
1. ✅ **Pinecone MCP server must be configured** - Check if MCP tools are available
2. ✅ **PINECONE_API_KEY environment variable must be set** - Get a free API key at https://app.pinecone.io/?sessionType=signup
3. ✅ **Index must be an integrated index** - Uses Pinecone embedding models (e.g., multilingual-e5-large, llama-text-embed-v2, pinecone-sparse-english-v0)

### When NOT to use this skill

**Use the pinecone-cli skill instead if:**
- ❌ Your index is a standard index (no integrated embedding model)
- ❌ You need to query with custom vector values (not text)
- ❌ You need advanced vector operations (fetch by ID, list vectors, bulk operations)
- ❌ Your index uses third-party embedding models (OpenAI, HuggingFace, Cohere)

**MCP Limitation**: The Pinecone MCP currently only supports integrated indexes. For all other use cases, use the pinecone-cli skill.

## How it works

Utilize Pinecone MCP's `search-records` tool to search for records within a specified Pinecone integrated index using a text query.

## Workflow

**IMPORTANT: Before proceeding, verify the Pinecone MCP tools are available.** If MCP tools are not accessible:
- Inform the user that the Pinecone MCP server needs to be configured
- Check if `PINECONE_API_KEY` environment variable is set
- Direct them to the MCP setup documentation or the `pinecone-help` skill

1. Parse the user's input for:
   - `query` (required): The text to search for.
   - `index` (required): The name of the Pinecone index to search.
   - `namespace` (optional): The namespace within the index.
   - `reranker` (optional): The reranking model to use for improved relevance.

2. If the user omits required arguments:
   - If only the index name is provided, use the `describe-index` tool to retrieve available namespaces and ask the user to choose.
   - If only a query is provided, use `list-indexes` to get available indexes, ask the user to pick one, then use `describe-index` for namespaces if needed.

3. Call the `search-records` tool with the gathered arguments to perform the search.

4. Format and display the returned results in a clear, readable table including field highlights (such as ID, score, and relevant metadata).

---

## Troubleshooting

**`PINECONE_API_KEY` is required.** Get a free key at https://app.pinecone.io/?sessionType=signup

If you get an access error, the key is likely missing. Ask the user to set it and restart their IDE or agent session:
<<api_key_setup>>

**IMPORTANT** At the moment, the pinecone-query skill can only be used with integrated indexes, which use hosted Pinecone embedding models to embed and search for data.
If a user attempts to query an index that uses a third party API model such as OpenAI, or HuggingFace embedding models, remind them that this capability is not available yet
with the Pinecone MCP server.

- If required arguments are missing, prompt the user to supply them, using Pinecone MCP tools as needed (e.g., `list-indexes`, `describe-index`).
- Guide the user interactively through argument selection until the search can be completed.
- If an invalid value is provided for any argument (e.g., nonexistent index or namespace), surface the error and suggest valid options.

## Tools Reference

- `search-records`: Search records in a given index with optional metadata filtering and reranking.
- `list-indexes`: List all available Pinecone indexes.
- `describe-index`: Get index configuration and namespaces.
- `describe-index-stats`: Get stats including record counts and namespaces.
- `rerank-documents`: Rerank returned documents using a specified reranking model.
- Ask the user interactively to clarify missing information when needed.

---

<!-- chapter:end slug=pinecone-query -->

---

<!-- chapter:begin slug=pinecone-quickstart position=9 -->

## 9. pinecone-quickstart

- **Source:** https://github.com/pinecone-io/skills/blob/main/skills/pinecone-quickstart/SKILL.md
- **Raw:** https://raw.githubusercontent.com/pinecone-io/skills/main/skills/pinecone-quickstart/SKILL.md
- **Markdown:** https://skillsdocs.com/pinecone-io/skills/pinecone-quickstart.md
- **Licence:** MIT — https://spdx.org/licenses/MIT.html

Bundled files (2), referenced from this skill's directory:
  - `scripts/quickstart_complete.py` — https://raw.githubusercontent.com/pinecone-io/skills/main/skills/pinecone-quickstart/scripts/quickstart_complete.py
  - `scripts/upsert.py` — https://raw.githubusercontent.com/pinecone-io/skills/main/skills/pinecone-quickstart/scripts/upsert.py

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: pinecone-quickstart
description: Interactive Pinecone quickstart for new developers. Choose between two paths - Database (create an integrated index, upsert data, and query using Pinecone MCP + Python) or Assistant (create a Pinecone Assistant for document Q&A). Use when a user wants to get started with Pinecone for the first time or wants a guided tour of Pinecone's tools.
---

# Pinecone Quickstart

Welcome! This skill walks you through your first Pinecone experience using the tools available to you. In this quickstart,
you will learn how to do a simple form of semantic search over some example data.

<<clarify_style>>

## Prerequisites

Before starting either path, verify the API key works by calling `list-indexes` via the Pinecone MCP. If it succeeds, proceed. If it fails, ask the user to set their key:

<<api_key_setup>>

Then retry `list-indexes` to confirm.

## Step 0: Choose Your Path

Ask the user which path they want:

- **Database** – Build a vector search index. Best for developers who want to store and search embeddings. Uses the Pinecone MCP + a Python upsert script.
- **Assistant** – Build a document Q&A assistant. Best for users who want to upload files and ask questions with cited answers. No code required.

---

## Path A: Database Quickstart

For each step, explain to the user what will happen. An overview is here:

1. Check if MCP is set
2. Create an integrated index with MCP
3. Upsert sample data using the bundled script (9 sentences across productivity, health, and nature themes)
4. Run a semantic search query and explore further queries
5. Optionally try reranking
6. Offer the complete standalone script

### Step 1 – Verify MCP is Available

The prerequisite check already called `list-indexes`. If it succeeded, the MCP is working — proceed to Step 2.

If it failed because MCP tools were unavailable (not an auth error):
- Tell the user the MCP server needs to be configured
- Point them to: https://docs.pinecone.io/reference/tools/mcp

### Step 2 – Create an Integrated Index

Use the MCP `create-index-for-model` tool to create a serverless index with integrated embeddings:

```
name: quickstart-skills
cloud: aws
region: us-east-1
embed:
  model: llama-text-embed-v2
  fieldMap:
    text: chunk_text
```

**Explain to the user what's happening:**
- An *integrated index* uses a built-in Pinecone embedding model (`llama-text-embed-v2`)
- This means you send plain text and Pinecone handles the embedding automatically
- The `field_map` tells Pinecone which field in your records contains the text to embed

Wait for the index to become ready before proceeding. Waiting a few seconds is sufficient.

### Step 3 – Upsert Sample Data

Run the bundled upsert script to seed the index with sample records.

If `PINECONE_API_KEY` is set in the environment:
```bash
uv run scripts/upsert.py --index quickstart-skills
```

If using a `.env` file:
```bash
uv run --env-file .env scripts/upsert.py --index quickstart-skills
```

**Explain to the user what's happening:**
- The script uploads 9 sample records across three themes: **productivity** (getting work done), **health** (feeling unwell), and **nature** (outdoors/wildlife)
- The dataset is intentionally varied so semantic search can show its value — the queries below use completely different words than the records, but the right ones still surface
- Each record has an `_id`, a `chunk_text` field (the text that gets embedded), and a `category` field
- This is the same structure you'd use for your own data — just replace the records

### Step 4 – Query with the MCP

Use the MCP `search-records` tool to run the first semantic search:

```
index: quickstart-skills
namespace: example-namespace
query:
  topK: 3
  inputs:
    text: "getting things done efficiently"
```

Display the results in a clean table: ID, score, and `chunk_text`.

**Explain to the user what's happening:**
- Notice the query shares no keywords with the records — but it surfaces the productivity sentences
- That's semantic search: it finds meaning, not just matching words
- You sent plain text — Pinecone embedded the query using the same model as the index

**Offer to explore further:** Ask the user if they'd like to try another query to see the effect more clearly:
- Option A: `"feeling under the weather"` — should surface the health records
- Option B: `"wildlife spotting outside"` — should surface the nature records
- Option C: No thanks, move on

Run whichever query they choose and display the results the same way. If they want to try both, do both. After each result, point out which theme surfaced and why.

If they decline or are done exploring, proceed to Step 5 or offer to skip ahead to the complete script.

### Step 5 – Try Reranking (Optional)

Ask the user if they want to try reranking.

If yes, use `search-records` again with reranking enabled:

```
rerank:
  model: bge-reranker-v2-m3
  rankFields: [chunk_text]
  topN: 3
```

**Explain**: Reranking runs a second-pass model over the results to improve relevance ordering.

### Step 6 – Wrap Up

Congratulate the user on completing the quickstart. Ask if they'd like a standalone Python script that does everything in one go — create index, upsert, query, and rerank.

If yes, copy it to their working directory:

```bash
cp scripts/quickstart_complete.py ./pinecone_quickstart.py
```

Tell the user:
- The script is at `./pinecone_quickstart.py`
- Run it with: `uv run pinecone_quickstart.py`
- It uses `uv` inline dependencies — no separate install needed
- They can swap in their own `records` list to build something real

---

## Path B: Assistant Quickstart

Guide the user through the Pinecone Assistant workflow using the existing pinecone-assistant skill:

### Step 1 – Check for Documents

Before anything else, ask the user if they have files to upload. Pinecone Assistant accepts `.pdf`, `.md`, `.txt`, and `.docx` files — a single file or a folder of files both work.

**If they have files:** ask for the path and proceed to Step 2.

**If they don't have files:** offer two options:
- **Generate sample docs** — create a few short markdown files in `./sample-docs/` so they can complete the quickstart right now. Ask what topics they'd like (or default to: a product FAQ, a short how-to guide, and a brief company overview). Write 3 files, each 150–250 words.
- **Come back later** — let them know they can return once they have documents and pick up from Step 2.

### Step 2 – Create an Assistant

Invoke `pinecone-assistant` or run (add `--env-file .env` if using a `.env` file):
```bash
uv run ../pinecone-assistant/scripts/create.py --name my-assistant
```

Explain: The assistant is a fully managed RAG service — upload documents, ask questions, get cited answers.

### Step 3 – Upload Documents

Invoke `pinecone-assistant` or run (add `--env-file .env` if using a `.env` file):
```bash
uv run ../pinecone-assistant/scripts/upload.py --assistant my-assistant --source ./your-docs
```

Explain: Pinecone handles chunking, embedding, and indexing automatically — no configuration needed.

### Step 4 – Chat with the Assistant

Invoke `pinecone-assistant` or run (add `--env-file .env` if using a `.env` file):
```bash
uv run ../pinecone-assistant/scripts/chat.py --assistant my-assistant --message "What are the main topics in these documents?"
```

Explain: Responses include citations with source file and page number.

### Next Steps for Assistant

- Invoke `pinecone-assistant` to keep the assistant up to date as documents change
- Use the pinecone-assistant skill to retrieve raw context snippets for custom workflows
- Every assistant is also an MCP server — see https://docs.pinecone.io/guides/assistant/mcp-server

---

## Troubleshooting

**`PINECONE_API_KEY` not set**

<<api_key_setup>>

Restart your IDE or agent session after setting the key.

**MCP tools not available**
- Verify the Pinecone MCP server is configured in your IDE's MCP settings
- Check that `PINECONE_API_KEY` is set before the MCP server starts

**Index already exists**
- The upsert script is safe to re-run — it will upsert over existing records
- Or delete and recreate: use `pc index delete -n quickstart-skills` via the CLI

**`uv` not installed**
See the [uv installation guide](https://docs.astral.sh/uv/getting-started/installation/).

## Further Reading

- Quickstart docs: https://docs.pinecone.io/guides/get-started/quickstart
- Integrated indexes: https://docs.pinecone.io/guides/index-data/create-an-index
- Python SDK: https://docs.pinecone.io/guides/get-started/python-sdk
- MCP server: https://docs.pinecone.io/reference/tools/mcp

<!-- chapter:end slug=pinecone-quickstart -->
