Local-first document embedding and retrieval platform with a REST API and MCP server for LLM agents. Supports hybrid BM25 + semantic search across workspaces and collections, with pluggable parsers, embedders, and vector stores.
Drift inferred · capture-to-capture
No drift recorded — single capability capture; advisories appear once its surface changes.
transport stdio · streamable-http counts 30 tools · 0 res
· 0 prompts
permission surface via code analysis
tools
-
assign_tag
-
confirm_original_upload
-
confirm_upload
-
create_collection
-
create_tag
-
create_workspace
-
delete_collection
-
delete_document
-
delete_tag
-
delete_workspace
-
download_document
-
get_checksum
-
get_document_chunks
-
get_document_status
-
get_ingestion_stats
-
get_rate_limit
-
ingest_document
-
list_documents
-
list_ingestion_jobs
-
list_tags
-
list_workspaces
-
merge_tags
-
reprocess_document
-
request_original_upload
-
request_upload
-
search_documents
-
unassign_tag
-
update_collection
-
update_tag
-
update_workspace
prompt-surface
shipped agent-instruction files + hidden-content / dangerous-code findings —
quoted from the analyzed source
analyzed commit 2cfa561 · analyzer v33 · 2w ago
skills & prompt files 2
evidence-backed
findings quoted directly from the published source artifact — not inferred
code files: 292
filesystem 23
- fs jaymeklein-embedbase-2cfa561/api/adapters/parsers/docling_adapter.py :24
from pathlib import Path - fs jaymeklein-embedbase-2cfa561/api/adapters/parsers/markdown.py :58
with open(file_path, encoding="utf-8", errors="replace") as fh: - fs jaymeklein-embedbase-2cfa561/api/adapters/parsers/pdf.py :66
with fitz.open(file_path) as doc: - fs jaymeklein-embedbase-2cfa561/api/adapters/parsers/txt.py :27
with open(file_path, "rb") as fh: - fs jaymeklein-embedbase-2cfa561/api/db.py :13
from pathlib import Path - fs jaymeklein-embedbase-2cfa561/api/main.py :3
from pathlib import Path - fs (weak) jaymeklein-embedbase-2cfa561/api/scripts/fetch_reranker_model.py :35
with open(os.path.join(DEST, name), "wb") as fh: - fs jaymeklein-embedbase-2cfa561/api/services/config_env.py :14
from pathlib import Path - fs jaymeklein-embedbase-2cfa561/api/services/config_service.py :25
from pathlib import Path - fs jaymeklein-embedbase-2cfa561/api/services/documents.py :13
from pathlib import Path - fs jaymeklein-embedbase-2cfa561/api/services/storage.py :19
import shutil - fs jaymeklein-embedbase-2cfa561/api/services/upload.py :12
from pathlib import Path - fs (weak) jaymeklein-embedbase-2cfa561/scripts/check_ports.py :20
from pathlib import Path - fs (weak) jaymeklein-embedbase-2cfa561/scripts/export_openapi.py :12
from pathlib import Path - fs (weak) jaymeklein-embedbase-2cfa561/tests/fixtures/docling/generate_fixtures.py :20
from pathlib import Path - fs (weak) jaymeklein-embedbase-2cfa561/tests/integration/test_ingestion.py :9
from pathlib import Path - fs (weak) jaymeklein-embedbase-2cfa561/tests/smoke/test_smoke.py :8
from pathlib import Path - fs (weak) jaymeklein-embedbase-2cfa561/tests/unit/test_migrations.py :10
from pathlib import Path - fs (weak) jaymeklein-embedbase-2cfa561/tests/unit/test_parsers.py :17
doc = fitz.open() - fs (weak) jaymeklein-embedbase-2cfa561/tests/unit/test_parsers_docling.py :13
from pathlib import Path - fs (weak) jaymeklein-embedbase-2cfa561/tests/unit/test_pdf_progress.py :11
doc = fitz.open() - fs jaymeklein-embedbase-2cfa561/worker/config.py :6
from pathlib import Path - fs jaymeklein-embedbase-2cfa561/worker/tasks.py :685
open (0) on a redis error so a redis blip can't wedge ingestion shut."""
network 21
- net jaymeklein-embedbase-2cfa561/api/adapters/embeddings/errors.py :5
import httpx - net jaymeklein-embedbase-2cfa561/api/adapters/embeddings/gemini.py :1
import httpx - net jaymeklein-embedbase-2cfa561/api/adapters/embeddings/ollama.py :3
import httpx - net jaymeklein-embedbase-2cfa561/api/adapters/embeddings/openai_compat.py :1
import httpx - net jaymeklein-embedbase-2cfa561/api/adapters/llm_chat.py :46
import httpx - net jaymeklein-embedbase-2cfa561/api/port_utils.py :3
import socket - net (weak) jaymeklein-embedbase-2cfa561/api/scripts/fetch_reranker_model.py :15
import requests - net jaymeklein-embedbase-2cfa561/api/services/config_reload.py :20
import socket - net jaymeklein-embedbase-2cfa561/api/services/health.py :9
import socket - net (weak) jaymeklein-embedbase-2cfa561/tests/conftest.py :7
from httpx import ASGITransport, AsyncClient - net (weak) jaymeklein-embedbase-2cfa561/tests/integration/test_mcp.py :23
from httpx import ASGITransport, AsyncClient - net (weak) jaymeklein-embedbase-2cfa561/tests/integration/test_search.py :6
from httpx import ASGITransport, AsyncClient - net (weak) jaymeklein-embedbase-2cfa561/tests/integration/test_search_e2e.py :18
from httpx import ASGITransport, AsyncClient - net (weak) jaymeklein-embedbase-2cfa561/tests/unit/test_config_reload.py :7
import socket - net (weak) jaymeklein-embedbase-2cfa561/tests/unit/test_embeddings.py :14
import httpx - net (weak) jaymeklein-embedbase-2cfa561/tests/unit/test_health_service.py :93
import socket as socket_mod - net (weak) jaymeklein-embedbase-2cfa561/tests/unit/test_llm_chat.py :6
model pickers. No network — ``post_json`` / ``httpx.*`` are patched. - net (weak) jaymeklein-embedbase-2cfa561/tests/unit/test_port_utils.py :27
with patch("socket.socket", return_value=_busy_sock()): - net (weak) jaymeklein-embedbase-2cfa561/tests/unit/test_rate_limit.py :11
import httpx - net (weak) jaymeklein-embedbase-2cfa561/tests/unit/test_storage.py :8
from urllib.parse import urlparse - net jaymeklein-embedbase-2cfa561/worker/tasks.py :615
detect a 429, e.g. Gemini) or an ``httpx.HTTPStatusError`` carrying status 429
secrets 2
- secrets (weak) jaymeklein-embedbase-2cfa561/tests/conftest.py :15
os.environ["MASTER_API_KEY"] = "test-master-key-for-testing-only" - secrets (weak) jaymeklein-embedbase-2cfa561/tests/integration/test_pgvector_live.py :47
"password": os.environ.get("EMBEDBASE_TEST_PG_PASSWORD", ""),
database 86
- db jaymeklein-embedbase-2cfa561/api/adapters/vector_store/pgvector.py :212
import asyncpg - db jaymeklein-embedbase-2cfa561/api/alembic/env.py :19
from sqlalchemy import pool - db jaymeklein-embedbase-2cfa561/api/alembic/versions/0001_initial_schema.py :13
import sqlalchemy as sa - db jaymeklein-embedbase-2cfa561/api/alembic/versions/0002_add_processing_started_at.py :14
import sqlalchemy as sa - db jaymeklein-embedbase-2cfa561/api/alembic/versions/0003_add_documents_status.py :14
import sqlalchemy as sa - db jaymeklein-embedbase-2cfa561/api/alembic/versions/0004_add_tags.py :15
import sqlalchemy as sa - db jaymeklein-embedbase-2cfa561/api/alembic/versions/0005_add_documents_embedding_model.py :14
import sqlalchemy as sa - db jaymeklein-embedbase-2cfa561/api/alembic/versions/0006_add_documents_storage_backend.py :15
import sqlalchemy as sa - db jaymeklein-embedbase-2cfa561/api/alembic/versions/0007_add_documents_expires_at.py :17
import sqlalchemy as sa - db jaymeklein-embedbase-2cfa561/api/alembic/versions/0008_add_users_and_permissions.py :20
import sqlalchemy as sa - db jaymeklein-embedbase-2cfa561/api/alembic/versions/0009_add_user_login.py :25
import sqlalchemy as sa - db jaymeklein-embedbase-2cfa561/api/alembic/versions/0010_add_users_rate_limit_rpm.py :17
import sqlalchemy as sa - db jaymeklein-embedbase-2cfa561/api/alembic/versions/0011_users_email_nullable.py :16
import sqlalchemy as sa - db jaymeklein-embedbase-2cfa561/api/alembic/versions/0012_add_documents_original_columns.py :19
import sqlalchemy as sa - db jaymeklein-embedbase-2cfa561/api/db.py :15
from sqlalchemy import event - db jaymeklein-embedbase-2cfa561/api/dependencies.py :5
from sqlalchemy.ext.asyncio import AsyncSession - db jaymeklein-embedbase-2cfa561/api/main.py :184
import redis as redis_lib - db jaymeklein-embedbase-2cfa561/api/routers/auth.py :15
from sqlalchemy.ext.asyncio import AsyncSession - db jaymeklein-embedbase-2cfa561/api/routers/collections.py :2
from sqlalchemy.ext.asyncio import AsyncSession - db jaymeklein-embedbase-2cfa561/api/routers/documents.py :12
from sqlalchemy.ext.asyncio import AsyncSession - db jaymeklein-embedbase-2cfa561/api/routers/graph.py :9
from sqlalchemy.ext.asyncio import AsyncSession - db jaymeklein-embedbase-2cfa561/api/routers/indexing.py :10
from sqlalchemy.ext.asyncio import AsyncSession - db jaymeklein-embedbase-2cfa561/api/routers/jobs.py :15
from sqlalchemy.ext.asyncio import AsyncSession - db jaymeklein-embedbase-2cfa561/api/routers/search.py :4
from sqlalchemy.ext.asyncio import AsyncSession - db jaymeklein-embedbase-2cfa561/api/routers/tags.py :15
from sqlalchemy.ext.asyncio import AsyncSession - db jaymeklein-embedbase-2cfa561/api/routers/users.py :12
from sqlalchemy.ext.asyncio import AsyncSession - db jaymeklein-embedbase-2cfa561/api/routers/workspaces.py :2
from sqlalchemy.ext.asyncio import AsyncSession - db jaymeklein-embedbase-2cfa561/api/routers/ws.py :25
import redis.asyncio as aioredis - db jaymeklein-embedbase-2cfa561/api/services/access.py :33
from sqlalchemy.ext.asyncio import AsyncSession - db jaymeklein-embedbase-2cfa561/api/services/auth.py :36
from sqlalchemy import select, update - db jaymeklein-embedbase-2cfa561/api/services/collections.py :15
from sqlalchemy import delete, func, insert, select, update - db jaymeklein-embedbase-2cfa561/api/services/document_filters.py :22
from sqlalchemy import or_, select - db jaymeklein-embedbase-2cfa561/api/services/documents.py :20
from sqlalchemy import and_, delete, func, insert, or_, select, update - db jaymeklein-embedbase-2cfa561/api/services/filters.py :17
from sqlalchemy.ext.asyncio import AsyncSession - db jaymeklein-embedbase-2cfa561/api/services/graph.py :20
from sqlalchemy import select - db jaymeklein-embedbase-2cfa561/api/services/indexing.py :15
from sqlalchemy import select - db jaymeklein-embedbase-2cfa561/api/services/job_filters.py :14
from sqlalchemy.ext.asyncio import AsyncSession - db jaymeklein-embedbase-2cfa561/api/services/jobs.py :15
from sqlalchemy import func, select - db jaymeklein-embedbase-2cfa561/api/services/mcp/tools.py :22
from sqlalchemy.ext.asyncio import AsyncSession - db jaymeklein-embedbase-2cfa561/api/services/permissions.py :34
from sqlalchemy import delete, insert, select, update - db jaymeklein-embedbase-2cfa561/api/services/search.py :7
from sqlalchemy import select - db jaymeklein-embedbase-2cfa561/api/services/tag_bridge.py :26
from sqlalchemy import select - db jaymeklein-embedbase-2cfa561/api/services/tags.py :17
from sqlalchemy import Table, delete, func, insert, select, update - db jaymeklein-embedbase-2cfa561/api/services/users.py :22
from sqlalchemy import delete, insert, select, update - db jaymeklein-embedbase-2cfa561/api/services/workspaces.py :15
from sqlalchemy import delete, func, insert, select, update - db jaymeklein-embedbase-2cfa561/api/sql_compat.py :13
from sqlalchemy import Table - db jaymeklein-embedbase-2cfa561/api/tables/api_keys.py :1
from sqlalchemy import Column, ForeignKey, Index, String, Table, UniqueConstraint - db jaymeklein-embedbase-2cfa561/api/tables/collections.py :1
from sqlalchemy import Column, ForeignKey, Index, String, Table, Text, UniqueConstraint - db jaymeklein-embedbase-2cfa561/api/tables/documents.py :1
from sqlalchemy import Column, DateTime, ForeignKey, Index, Integer, String, Table - db jaymeklein-embedbase-2cfa561/api/tables/job_records.py :1
from sqlalchemy import Column, DateTime, Integer, String, Table, Text - db jaymeklein-embedbase-2cfa561/api/tables/metadata.py :1
from sqlalchemy import MetaData - db jaymeklein-embedbase-2cfa561/api/tables/permissions.py :17
from sqlalchemy import Column, ForeignKey, Index, String, Table, UniqueConstraint - db jaymeklein-embedbase-2cfa561/api/tables/tags.py :9
from sqlalchemy import ( - db jaymeklein-embedbase-2cfa561/api/tables/users.py :16
from sqlalchemy import Boolean, Column, Index, Integer, String, Table, UniqueConstraint, false, true - db jaymeklein-embedbase-2cfa561/api/tables/workspaces.py :1
from sqlalchemy import Column, String, Table, Text - db (weak) jaymeklein-embedbase-2cfa561/tests/conftest.py :8
from sqlalchemy import event as sa_event - db (weak) jaymeklein-embedbase-2cfa561/tests/integration/test_documents.py :173
from sqlalchemy import update - db (weak) jaymeklein-embedbase-2cfa561/tests/integration/test_indexing_router.py :8
from sqlalchemy import insert - db (weak) jaymeklein-embedbase-2cfa561/tests/integration/test_ingestion.py :16
from sqlalchemy import create_engine, insert, select # noqa: E402 - db (weak) jaymeklein-embedbase-2cfa561/tests/integration/test_jobs.py :110
from sqlalchemy import update - db (weak) jaymeklein-embedbase-2cfa561/tests/integration/test_mcp.py :24
from sqlalchemy import event as sa_event - db (weak) jaymeklein-embedbase-2cfa561/tests/integration/test_search.py :7
from sqlalchemy import event as sa_event - db (weak) jaymeklein-embedbase-2cfa561/tests/integration/test_search_e2e.py :19
from sqlalchemy import event as sa_event - db (weak) jaymeklein-embedbase-2cfa561/tests/integration/test_users.py :271
from sqlalchemy import select - db (weak) jaymeklein-embedbase-2cfa561/tests/integration/test_ws.py :14
from sqlalchemy import event as sa_event - db (weak) jaymeklein-embedbase-2cfa561/tests/unit/test_auth.py :58
from sqlalchemy import insert, select # noqa: E402 - db (weak) jaymeklein-embedbase-2cfa561/tests/unit/test_collections_svc.py :5
from sqlalchemy import insert - db (weak) jaymeklein-embedbase-2cfa561/tests/unit/test_delete_worker.py :118
from sqlalchemy import create_engine, insert, select - db (weak) jaymeklein-embedbase-2cfa561/tests/unit/test_documents_service.py :8
from sqlalchemy import insert, select, update - db (weak) jaymeklein-embedbase-2cfa561/tests/unit/test_ephemeral_ingest.py :17
from sqlalchemy import create_engine, insert, select - db (weak) jaymeklein-embedbase-2cfa561/tests/unit/test_indexing_service.py :8
from sqlalchemy import insert - db (weak) jaymeklein-embedbase-2cfa561/tests/unit/test_ingestion_progress.py :9
from sqlalchemy import create_engine, insert - db (weak) jaymeklein-embedbase-2cfa561/tests/unit/test_jobs_service.py :3
from sqlalchemy import insert, select - db (weak) jaymeklein-embedbase-2cfa561/tests/unit/test_migrations.py :9
import sqlite3 - db (weak) jaymeklein-embedbase-2cfa561/tests/unit/test_permissions.py :5
from sqlalchemy import delete, insert - db (weak) jaymeklein-embedbase-2cfa561/tests/unit/test_pgvector_adapter.py :5
tests never import asyncpg or touch a database — they verify the request/response - db (weak) jaymeklein-embedbase-2cfa561/tests/unit/test_rate_limit.py :13
from sqlalchemy import create_engine, insert, select - db (weak) jaymeklein-embedbase-2cfa561/tests/unit/test_stale_guard.py :11
from sqlalchemy import create_engine, insert - db (weak) jaymeklein-embedbase-2cfa561/tests/unit/test_tag_bridge.py :11
from sqlalchemy import insert - db (weak) jaymeklein-embedbase-2cfa561/tests/unit/test_tags_service.py :11
from sqlalchemy import insert, select - db (weak) jaymeklein-embedbase-2cfa561/tests/unit/test_worker_helpers.py :9
from sqlalchemy import create_engine, insert, select - db (weak) jaymeklein-embedbase-2cfa561/tests/unit/test_worker_storage_delete.py :8
from sqlalchemy import create_engine, insert - db (weak) jaymeklein-embedbase-2cfa561/tests/unit/test_worker_tag_sync.py :10
from sqlalchemy import create_engine, insert - db jaymeklein-embedbase-2cfa561/worker/config_reload.py :31
import redis - db jaymeklein-embedbase-2cfa561/worker/db.py :16
from sqlalchemy import create_engine, event - db jaymeklein-embedbase-2cfa561/worker/tasks.py :19
from sqlalchemy import and_, or_, select, update
tool registrations 30
- list_workspaces jaymeklein-embedbase-2cfa561/api/services/mcp/server.py :54
- search_documents jaymeklein-embedbase-2cfa561/api/services/mcp/server.py :65
- list_documents jaymeklein-embedbase-2cfa561/api/services/mcp/server.py :108
- ingest_document jaymeklein-embedbase-2cfa561/api/services/mcp/server.py :121
- request_upload jaymeklein-embedbase-2cfa561/api/services/mcp/server.py :136
- confirm_upload jaymeklein-embedbase-2cfa561/api/services/mcp/server.py :153
- request_original_upload jaymeklein-embedbase-2cfa561/api/services/mcp/server.py :161
- confirm_original_upload jaymeklein-embedbase-2cfa561/api/services/mcp/server.py :175
- reprocess_document jaymeklein-embedbase-2cfa561/api/services/mcp/server.py :183
- get_document_status jaymeklein-embedbase-2cfa561/api/services/mcp/server.py :191
- download_document jaymeklein-embedbase-2cfa561/api/services/mcp/server.py :205
- get_checksum jaymeklein-embedbase-2cfa561/api/services/mcp/server.py :218
- get_document_chunks jaymeklein-embedbase-2cfa561/api/services/mcp/server.py :234
- delete_document jaymeklein-embedbase-2cfa561/api/services/mcp/server.py :253
- create_workspace jaymeklein-embedbase-2cfa561/api/services/mcp/server.py :265
- update_workspace jaymeklein-embedbase-2cfa561/api/services/mcp/server.py :276
- delete_workspace jaymeklein-embedbase-2cfa561/api/services/mcp/server.py :288
- create_collection jaymeklein-embedbase-2cfa561/api/services/mcp/server.py :296
- update_collection jaymeklein-embedbase-2cfa561/api/services/mcp/server.py :308
- delete_collection jaymeklein-embedbase-2cfa561/api/services/mcp/server.py :321
- list_tags jaymeklein-embedbase-2cfa561/api/services/mcp/server.py :334
- create_tag jaymeklein-embedbase-2cfa561/api/services/mcp/server.py :342
- update_tag jaymeklein-embedbase-2cfa561/api/services/mcp/server.py :353
- delete_tag jaymeklein-embedbase-2cfa561/api/services/mcp/server.py :364
- merge_tags jaymeklein-embedbase-2cfa561/api/services/mcp/server.py :372
- assign_tag jaymeklein-embedbase-2cfa561/api/services/mcp/server.py :381
- unassign_tag jaymeklein-embedbase-2cfa561/api/services/mcp/server.py :400
- list_ingestion_jobs jaymeklein-embedbase-2cfa561/api/services/mcp/server.py :424
- get_ingestion_stats jaymeklein-embedbase-2cfa561/api/services/mcp/server.py :441
- get_rate_limit jaymeklein-embedbase-2cfa561/api/services/mcp/server.py :450
declared dependencies 14
- fastapi@>=0.136.0,<0.138.0
- uvicorn@>=0.29.0
- pydantic@>=2.7.0
- pydantic-settings@>=2.2.0
- s@EmailStr (user email validation); pydantic v2 needs v2+.
- pytest@>=8.0.0
- pytest-asyncio@>=0.23.0
- pytest-cov@>=5.0.0
- httpx@>=0.27.0
- ruff@>=0.4.0
- mypy@>=1.10.0
- types-PyYAML@>=6.0.12
- types-redis@>=4.6.0
- moto@>=5.0