
shnax0210/documents-vector-search
Developer toolsIndex documents from Jira, Confluence, or local files into a local vector database and search them. All data stays on your machine.
Overview
Index documents from Jira, Confluence, or local files into a local vector database and search them. All data stays on your machine.
README
Local Vector Search for Jira, Confluence & Files (with MCP support)
Overview
Index documents from Jira, Confluence, or local files into a local vector database and search them. All data stays on your machine.
Key features:
- Jira & Confluence (Server/Data Center and Cloud). Jira ticket = document, Confluence page = document
- Local files (.pdf, .pptx, .docx, etc.) via Unstructured
- No data sent to third parties (except when used as MCP with a remote AI agent)
- Hybrid search: vector search + BM25 keyword search, merged by Reciprocal Rank Fusion
- Incremental updates: no need to rebuild the full index each time
- Filter results by metafields (space, project, date, etc.)
- Ability to extend: add more data sources, search engines, embeddings, etc.
Technologies: SQLite (sqlite-vec and BM25), ChromaDB, sentence-transformers, Unstructured, LangChain
More context: Medium article
Contacts:
- Like it? Please star the repo
- Found a bug? Open an issue
- Want to contribute? - feel free to do it via fork and sending a pull request
- Want to chat? LinkedIn
Updates
- Check UPDATES.md for major updates.
- Some minor updates can be not added the file, so they can be found only in git history.
How it works
flowchart TD
A[1. Create collection] -->|loads & indexes documents| B["Collection stored in ./data/collections/${name}"]
B --> C{What next?}
C --> D[2. Update collection]
C --> E[3. Search via CLI]
C --> F[4. Search via MCP]
D -->|indexes only new/changed docs| B
E -->|search| B
F -->|search| B
- Create a collection — load and index documents from Jira, Confluence, or local files. Stored in
./data/collections/{name} - Update — re-index only new or changed documents (much faster than full creation)
- Search — find documents by text query via CLI
- MCP — expose search as a tool for AI agents
Collection structure
graph TD
A["./data/collections/${name}/"] --> B["documents/"]
A --> C["indexes/"]
A --> D["manifest.json"]
B --- B1["Loaded and converted documents"]
C --- C1["Vector and keyword index files"]
D --- D1["Collection metadata: name, last update time, reader config, index list"]
See ./main/core/documents_collection_creator.py for creation/update details and ./main/core/documents_collection_searcher.py for search details.
Indexers configuration
When you create a collection, you can specify a list of indexers like: --indexers "indexer_SqlLiteVector__embeddings_sentence-transformers_slash_all-MiniLM-L6-v2", "indexer_SqlLiteBM25" (it’s also the default value). The indexers define what vector/keyword databases and embedding models are used. Database and embedding model are separated by __. For example:
indexer_SqlLiteVector__embeddings_sentence-transformers_slash_all-MiniLM-L6-v2means that SqlLite withsqlite-vecextension is used as vector database andsentence-transformers/all-MiniLM-L6-v2is used as the embedding model. You can use any embedding model from next list, you only need to add prefixembeddings_and replace slash symbols with_slash_. For example, if you want to use it with BAAI/bge-m3 embedder model, indexer name should be:indexer_SqlLiteVector__embeddings_BAAI_slash_bge-m3;indexer_SqlLiteBM25means that SqlLite BM25 is used as search engine.indexer_ChromaDb__embeddings_sentence-transformers_slash_all-MiniLM-L6-v2means thatChromaDbis used as vector database andsentence-transformers/all-MiniLM-L6-v2is used as the embedding model. Embedding model names follow the same rules as forindexer_SqlLiteVector.
You can define as many indexers as you want, their search results will be combined by Reciprocal Rank Fusion.
Setup
Env setup
Either uv or docker can be used to run the project.
Local setup
- Clone the repository
- Install uv
- Run
uv syncin the project root
Docker setup
A Dockerfile is included for running the tool without installing Python or uv locally.
Build the image:
docker build -t documents-vector-search .
Run any command by passing it as arguments and mounting a local data/ folder:
docker run --rm \
-v $(pwd)/data:/app/data \
-e CONF_TOKEN="${yourToken}" \
documents-vector-search \
uv run dvs.py create-confluence \
--collection "confluence" \
--url "${baseConfluenceUrl}" \
--cql "${confluenceQuery}"
- Mount
-v $(pwd)/data:/app/dataso collections and caches are persisted on your host machine - Pass credentials as
-e ENV_VAR=value(see Authentication) - The
data/folder structure (collections/,caches/,local_file_input/) is created automatically inside the container
Run the unified MCP HTTP server with Docker:
docker run --rm \
-p 8000:8000 \
-v $(pwd)/data:/app/data \
documents-vector-search \
uv run dvs.py mcp --http --httpPort 8000
Then configure your MCP client to connect to http://localhost:8000/mcp.
Commands
All functionality is available through a single script:
uv run dvs.py [arguments]
| Operation | Description |
|---|---|
create-confluence |
Create a collection from Confluence pages |
create-jira |
Create a collection from Jira tickets |
create-files |
Create a collection from local files |
update |
Re-index new and changed documents of an existing collection |
search |
Search in a collection from the command line |
fetch |
Fetch a document content from a collection by its id |
mcp |
Run unified MCP server (all collections, stdio or HTTP) |
mcp-single |
Run MCP stdio server for a single collection |
Every operation has its own arguments, each with a short and a long form (e.g. -c / --collection). Run uv run dvs.py --help to see all of them.
Authentication
Set environment variables before creating or updating Jira/Confluence collections (not needed for local files):
| Platform | Type | Environment Variables |
|---|---|---|
| Confluence Server/DC | Bearer token (recommended) | CONF_TOKEN |
| Confluence Server/DC | Login/Password | CONF_LOGIN, CONF_PASSWORD |
| Confluence Cloud | Email/API token | ATLASSIAN_EMAIL, ATLASSIAN_TOKEN (get token) |
| Jira Server/DC | Bearer token (recommended) | JIRA_TOKEN |
| Jira Server/DC | Login/Password | JIRA_LOGIN, JIRA_PASSWORD |
| Jira Cloud | Email/API token | ATLASSIAN_EMAIL, ATLASSIAN_TOKEN (get token) |
Cloud vs Server is auto-detected: URLs ending with .atlassian.net are treated as Cloud.
Create collection for Confluence
uv run dvs.py create-confluence \
--collection "confluence" \
--url "${baseConfluenceUrl}" \
--cql "${confluenceQuery}"
--collection— name of the collection (used later for update/search). Data stored in./data/collections/{name}--url— Confluence base URL (e.g.,https://confluence.example.comorhttps://your-domain.atlassian.net)--cql— Confluence query, e.g.,"(space = 'MySpace') AND (lastModified >= '2025-01-01')"
Create collection for Jira
uv run dvs.py create-jira \
--collection "jira" \
--url "${baseJiraUrl}" \
--jql "${jiraQuery}"
--url— Jira base URL (e.g.,https://jira.example.comorhttps://your-domain.atlassian.net)--jql— Jira query, e.g.,"project = MyProject AND created >= -183d"
Create collection for local files
uv run dvs.py create-files --basePath "${pathToFolder}"
- Collection name defaults to the last folder name. Override with
--collection {name} - Unreadable files are skipped by default. Use
--failFastto stop on first error - Filter files with
--includePatterns "regex1" "regex2"and--excludePatterns "regex1" "regex2" - Uses Unstructured for parsing. Some formats may need extra software
Update collection
uv run dvs.py update --collection "${collectionName}"
Search
uv run dvs.py search \
--collection "${collectionName}" \
--query "How to set up react project locally"
--includeMatchedChunksText— include matched text chunks in results--filter— filter by metafields (see below)--rrfK— RRF constant for multi-index fusion (default:60)
Filtering by metafields
Works with ChromaDB, SQLite BM25 and SQLite Vector indexes.
Syntax:
field operator "value" and/or field operator "value"
Operators: =, !=, >, >=, <, <=. Use and / or to join conditions (mixing both is not supported).
Confluence metafields:
| Field | Description |
|---|---|
space |
Space key |
createdAt |
Page creation date |
createdBy |
Creator email (lowercase) |
lastModifiedAt |
Last update date |
Examples:
--filter 'space = "SPACE_KEY"'
--filter 'space = "SPACE_KEY" and lastModifiedAt > "2026-01-01"'
--filter '(space = "SPACE_KEY1" or space = "SPACE_KEY2") and lastModifiedAt > "2026-01-01"'
Jira metafields:
| Field | Description |
|---|---|
project |
Project key |
type |
Issue type (Bug, Task, Story, …) |
status |
Status (Open, In Progress, Done, …) |
priority |
Priority (High, Medium, Low, …) |
epic |
Epic or parent issue key |
assignee |
Assignee email (lowercase) |
createdAt |
Issue creation date |
createdBy |
Creator email (lowercase) |
lastModifiedAt |
Last update date |
Examples:
--filter 'project = "PROJ"'
--filter 'project = "PROJ" and lastModifiedAt > "2026-01-01"'
--filter '(project = "PROJ1" or project = "PROJ2") and lastModifiedAt > "2026-01-01"'
Files metafields:
| Field | Description |
|---|---|
createdAt |
File creation date |
lastModifiedAt |
Last modified date |
folder1 |
First subfolder of the file path |
folder2 |
Second subfolder of the file path |
folderN |
Nth subfolder of the file path (only present if path has N or more subfolders) |
Examples:
--filter 'folder1 = "docs"'
--filter 'folder1 = "docs" and folder2 = "api"'
--filter '(folder2 = "api" or folder2 = "presentations") and lastModifiedAt > "2026-01-01"'
Fetch
uv run dvs.py fetch \
--collection "${collectionName}" \
--id "${documentId}"
--id— document ID to fetch (required)--startLine/--endLine— line range to return (default: 1–200)--format— output format:json,json_with_indent(default), ortoon
Set up MCP
There are two MCP server operations:
| Operation | Best for | Key differences |
|---|---|---|
dvs.py mcp |
Modern AI models | All collections in one server. AI model chooses collection, filter and number of chunks. Supports stdio and HTTP transport. |
dvs.py mcp-single |
Simpler AI models or restricted setups | One collection per server. Collection, filter and other settings are hardcoded via CLI args. Stdio only. |
Unified MCP (recommended)
Add to your MCP config (e.g., .vscode/mcp.json for VS Code + GitHub Copilot):
{
"servers": {
"dvs": {
"type": "stdio",
"command": "uv",
"args": [
"--directory", "${fullPathToRootProjectFolder}",
"run", "dvs.py", "mcp"
]
}
}
}
- All collections from
./data/collections/are available automatically - Use
--collections "name1" "name2"to limit which collections are exposed - Use
--rrfK {number}to tune Reciprocal Rank Fusion behavior for multi-index search - Use
--defaultNumberOfChunks {number}and--maxNumberOfChunks {number}to tune the number of text chunks returned by a single search
Or start as http server:
uv run dvs.py mcp --http --httpPort 8000
And setup mcp like:
{
"servers": {
"dvs": {
"type": "http",
"url": "http://localhost:8000/mcp",
}
}
}
Simple MCP
{
"servers": {
"search_${collectionName}": {
"type": "stdio",
"command": "uv",
"args": [
"--directory", "${fullPathToRootProjectFolder}",
"run", "dvs.py", "mcp-single",
"--collection", "${collectionName}"
]
}
}
}
- Replace
${collectionName}and${fullPathToRootProjectFolder}with real values - Use
--maxNumberOfChunks {number}to control how many text chunks are returned (more = better search, but may exceed model context window) - Use
--rrfK {number}to tune Reciprocal Rank Fusion behavior for multi-index search - Use
--filterfor metafield filtering (details)
Prompt examples:
- “Find info about AI use cases, search on Confluence, include all used links”
- “Find info about PDP carousel, search on Jira, include all used links”
Run unit tests
If you develop the tool, you can run unit tests:
uv run pytest
Good to know
- Incremental updates — only new/changed documents are re-indexed. Uses
lastModifiedDocumentTimefrommanifest.json(5 mins for Jira and Confluence buffer to avoid missing concurrent updates); - Caching — Jira/Confluence collection creation caches downloaded documents in
./data/caches/{hash}. Same parameters = same cache. If you need fresh data, either run an update after creation, or delete the cache folder manually; - there are more parameters in each operation, use
uv run dvs.py --helpto get more.
Install
docker run --rm \