vectorsmith

mcp
Security Audit
Warn
Health Warn
  • License — License: Apache-2.0
  • Description — Repository has a description
  • Active repo — Last push 0 days ago
  • Low visibility — Only 8 GitHub stars
Code Pass
  • Code scan — Scanned 12 files during light audit, no dangerous patterns found
Permissions Pass
  • Permissions — No dangerous permissions requested

No AI report is available for this listing yet.

SUMMARY

Your vector database, as typed tools. Write a tools.yaml, then load_tools in Python or serve over MCP.

README.md
VectorSmith

VectorSmith

Your vector database, forged into tools an agent can actually use.

Write a tools.yaml. VectorSmith compiles it into typed, tenant-guarded tools — then you either import them in Python or serve them over MCP.

License
Python 3.11+
TDS
MCP
Version
Backend status
Docs

What it is · How it works · Write YAML · Python · Claude / Codex / Cursor · Production HTTP · Backend evidence · Try it · Docs


Why this exists

Agents that talk to your invoices, tickets, or catalog usually get one of two bad options:

Typical approach What goes wrong
Vendor MCP (Qdrant / Pinecone / …) Cluster admin tools. Upsert, delete, create-collection. The model can wander.
Hand-bind JSON schemas to LangChain / the OpenAI SDK You re-implement filters, limits, and tenant isolation in Python. Every agent copies it.
“Just embed and search() in the system prompt” No typed args. No enums. No hidden tenant = acme.

VectorSmith is the third option: the data store stays yours. The tools are a YAML contract. The compiler turns that contract into MCP schemas or in-process tools. The agent never sees the URL, the API key, or the tenant filter.

  you write                         VectorSmith                    the agent sees
─────────────                   ─────────────────                ────────────────
 tools.yaml          ──▶   interpolate → validate → compile  ──▶  search_invoices
 tenant: acme                    Engine stays internal            query, client, status
 ${QDRANT_URL}                                                    (no tenant, no URL)

VectorSmith demo: YAML schema tools in Claude (~50s)
Watch the 50s demo · YAML → Claude / Cursor / Python


How it works

flowchart LR
  subgraph author["You"]
    Y["tools.yaml"]
    E[".env / ${VAR}"]
  end
  subgraph vs["VectorSmith"]
    L["load + secret lint"]
    V["validate VBxxxx"]
    C["compile schemas + plan"]
  end
  subgraph out["Consume once"]
    P["load_tools() / connect()"]
    M["vectorsmith serve"]
  end
  subgraph hosts["Hosts"]
    A["LangChain · LangGraph · Agents SDK · Anthropic"]
    H["Claude · Codex · Cursor · claude.ai"]
  end
  Y --> L
  E --> L
  L --> V --> C
  C --> P --> A
  C --> M --> H

One file, two doors. Same compiled tools.

Python app Chat / IDE host
Install pip install "vectorsmith[qdrant,langchain]" pip install "vectorsmith[qdrant]" so vectorsmith is on PATH
Call from vectorsmith import load_tools vectorsmith serve tools.yaml --name invoices
Process In-process. No subprocess. The host spawns the CLI (MCP stdio or HTTP)
Mix-in Your @tools + Slack/GitHub via an MCP client Other mcpServers keys sit next to it

You do not import an executor. You do not copy inputSchema into the LLM SDK.


Write a tool, not a prompt

A tool is a name, a description (so the model picks it), a collection, optional text search, parameters the model may pass, and filters it must never see:

tds_version: "1"

connections:
  invoices:
    backend: qdrant
    url: ${QDRANT_URL}              # secrets only here, only as ${VAR}
    api_key: ${QDRANT_API_KEY:-}

tools:
  - name: search_invoices
    kind: search
    description: >
      Search invoices by free text and filter by client, status, or amount.
      Use when the user asks about invoices, billing, or payments.
    target: { connection: invoices, collection: invoices }
    query: { param: query, required: false }
    static_filters:
      - { path: tenant, op: eq, value: acme }    # hidden from the model
    parameters:
      - { name: client, path: client_name, dtype: keyword, op: eq }
      - { name: status, path: status, dtype: keyword, op: in,
          enum: [draft, sent, paid, overdue] }
      - { name: min_amount, path: amount, dtype: float, op: gte }
    output:
      fields: [invoice_id, client_name, status, amount]
      limit_default: 10
      limit_max: 50

vectorsmith init ./demo writes a starter file. The full field list — kinds, operators, pipelines, built-ins, every backend — is in docs/tools-yaml-reference.md.

What the model sees

{
  "name": "search_invoices",
  "description": "Search invoices by free text and filter by client, status, or amount. …",
  "inputSchema": {
    "type": "object",
    "properties": {
      "query": { "type": "string" },
      "client": { "type": "string" },
      "status": {
        "type": "array",
        "items": { "type": "string", "enum": ["draft", "sent", "paid", "overdue"] }
      },
      "min_amount": { "type": "number" },
      "limit": { "type": "integer", "minimum": 1, "maximum": 50, "default": 10 }
    }
  }
}

tenant: acme is not in that schema. The engine ANDs it on every call. Credentials never leave connections.

Kinds you can declare

kind For Typical tool
search Semantic retrieve + filters search_invoices
lookup Exact id, limit 1 get_invoice
count “How many overdue?” count_invoices
scroll Filter / page, no ANN list-style tools
pipeline Retrieve → post_filter / group_by / sort / project top-N per client

Built-ins (search_<connection>, get_<connection>_by_id, …) are opt-in on the connection. Turn them off if you already named a user tool the same way.


In your agent (Python)

pip install "vectorsmith[qdrant,langchain]"
from vectorsmith import load_tools
from langchain.agents import create_agent

tools = load_tools("tools.invoices.yaml", "tools.tickets.yaml")
agent = create_agent("openai:gpt-4.1", tools)
# … await tools.aclose()

Same YAML, other stacks:

from vectorsmith.langgraph import load_tools      # create_react_agent / ToolNode
from vectorsmith.openai_agents import load_tools  # Agent + Runner
from vectorsmith.anthropic import load_tools      # messages.create(tools=vs.tools)
from vectorsmith import connect                   # await vs.call("search_invoices", {…})

Authenticated Python applications can pass ctx=CallContext(...) to
BoundTools.call(). Supported LangChain/LangGraph, OpenAI Agents, and Anthropic
paths propagate that principal, claims/roles, tenant, deadline, and request ID.
The application is responsible for constructing caller context from an
authenticated request. See the Python API and
security profiles.

Extra Import
vectorsmith[langchain] from vectorsmith import load_tools
vectorsmith[langgraph] same tools; LangGraph graph
vectorsmith[openai-agents] from vectorsmith.openai_agents import load_tools
vectorsmith[anthropic] from vectorsmith.anthropic import load_tools

Worked apps: examples/langchain_agent · langgraph_agent · openai_agents · anthropic_agent.


In Claude, Codex, Cursor

Those products cannot import vectorsmith. They spawn a process. Point them at serve with the same YAML.

{
  "mcpServers": {
    "invoices": {
      "command": "vectorsmith",
      "args": ["serve", "tools.invoices.yaml", "--name", "invoices"]
    }
  }
}

Codex is TOML (~/.codex/config.toml), not JSON. Claude Code uses .mcp.json — it does not read the Desktop file.

Host Config Guide
Claude Desktop claude_desktop_config.json docs/integrations/claude-desktop.md
Claude Code .mcp.json / claude mcp add docs/integrations/claude-code.md
OpenAI Codex ~/.codex/config.toml docs/integrations/openai-codex.md
Cursor .cursor/mcp.json docs/integrations/cursor.md
claude.ai serve --http --auth builtin docs/quickstart-selfhost.md

Copy-paste snippets: examples/mcp_hosts/. Slack, GitHub, filesystem stay separate servers — coexistence.


Production HTTP server

0.2.0 is the production HTTP cut. vectorsmith serve --http is Streamable HTTP MCP (POST /mcp) for claude.ai, gateways, and Kubernetes. Every security.* / observability.* / credential / profiles.enterprise knob in YAML is applied at process start — the same contract connect / load_tools use in-process.

That describes the HTTP runtime, not stable backend status. All six adapters
currently remain experimental; see backend evidence
before making a production support claim.

pip install "vectorsmith[qdrant,auth-jwt,otel]"

vectorsmith serve tools.yaml --http 0.0.0.0:8080 --auth jwt \
  --jwks-url https://auth.example.com/.well-known/jwks.json \
  --jwt-issuer https://auth.example.com --jwt-audience vectorsmith \
  --log-format json --live-embed

Localhost demo: --http 127.0.0.1:8080 --auth none. --auth none off loopback exits 3. Builtin OAuth (--auth builtin, the HTTP default) needs https:// --public-url.

Concern How
Who is calling --auth jwt (JWKS / RS256) · api_key · builtin OAuth. Extra vectorsmith[auth-jwt]
Tenant isolation Hidden static_filters and/or security.tenancy (claim / header). Pick one layer unless you intend both
Which tools security.rbac (roles, deny_tools). Applied on the inner name of run_tool
Secrets connections.*.credentials.provider: env · vault · aws_sm · k8s. Extra vectorsmith[creds-aws]
Quotas security.rate_limit (off by default). In-memory or Redis (vectorsmith[auth-redis]). HTTP 429
Health GET /healthz (liveness). GET /readyz 503 if a connection, required embedder, or JWT JWKS is down
Observability --log-format json (request_id, trace_id, span_id). observability.tracing → OTLP (vectorsmith[otel]). GET /metrics. Audit file / HTTP / OTLP
Hardening profiles.enterprise + validate --enterprise --strict. Refuses serve / connect if tenancy / limits / backends fail
Search quality Pluggable embedders, query.expand, tool.rerank (http / cohere / local cross_encoder)
Many catalogs Extra YAML args, --route-by-claim, --default-project. Duplicate tool names fail at start
Drain --shutdown-grace-s (default 30). New POST /mcp is 503 while draining

Reference catalog: examples/enterprise/. Chart and probes: Kubernetes. Full model: enterprise · hardening · observability.


Stores

backend on a connection is one of six adapters. All currently have
experimental support status: the advertised read-only surface is usable and
tested, but the complete fault and supported-version matrix required for stable
status is not finished. Unsupported semantics fail validation instead of
silently returning partial or unfiltered results.

qdrant · pgvector · chroma · pinecone · weaviate · milvus

The deterministic local matrix covers hidden tenant filters, exact IDs, counts,
pagination, projection, nested and array filters, hybrid ranking, typed
introspection, server-side embedding, score direction, edge-case payloads,
read-only builtins/drafts, cleanup, and error translation. Current generated
pass/skip counts and tested client/server versions are in
backend conformance for exact server/client versions,
per-backend results, tested behavior, and remaining stability blockers.

The current capability matrix advertises hybrid search only for Qdrant and
Weaviate. pgvector can run in table mode (no vector column) for lookup,
count, scroll, and pipelines. Pinecone intentionally rejects filter-only
scroll, filtered count, hybrid mode, and exact-ID builtins that cannot preserve
metadata guardrails. Full operator and feature details:
vector stores.

Production extras (install what you turn on in YAML):

Extra For
auth-jwt serve --auth jwt
auth-redis Shared OAuth tokens and Redis rate limits
otel OTLP traces from observability.tracing
creds-aws credentials.provider: aws_sm
embed-openai / embed-cohere Hosted embedders (and Cohere rerank)
rerank-local rerank.provider: cross_encoder

Full inventory: library surface.


Try it

The invoice example is a tools.yaml plus an env file. Copy .env.example and set QDRANT_URL to your cluster before validate / test / serve.

# clone, then:
uv sync

uv run vectorsmith validate examples/qdrant_invoices/tools.invoices.yaml \
  --env-file examples/qdrant_invoices/.env.example

uv run vectorsmith test examples/qdrant_invoices/tools.invoices.yaml search_invoices \
  --args '{"query":"Globex invoice","limit":3}' \
  --env-file examples/qdrant_invoices/.env.example

uv run vectorsmith serve examples/qdrant_invoices/tools.invoices.yaml --name invoices \
  --env-file examples/qdrant_invoices/.env.example

Tickets are a second file / second MCP name: tools.tickets.yaml--name tickets.

Example walkthrough


CLI

Command Does
init Write a starter tools.yaml + .env.example
validate Compile + lint. --live pings the store. --live-embed smoke-tests the embedder. --enterprise / --policy / --policy-builtin for production gates. --strict fails on warnings
test Call one compiled tool without serving
serve MCP stdio (Desktop / Codex / Cursor; --watch on by default) or --http HOST:PORT (no watch). HTTP --auth: builtin (needs https --public-url) · jwt · api_key · none (loopback only). --live-embed includes the embedder on /readyz.
introspect Collection / field metadata to --out (default schema.json). Requires --connection.
discover --experimental Introspect live collections and write pending schema-backed drafts without changing tools.yaml.
eval --experimental Execute checked-in tool-call scenarios and write row/isolation/score invariant results.
drift --experimental Compare a metadata-only schema export with live introspection; report suggestions without auto-promotion.
drafts / approve drafts list|reject NAME. Approval preserves YAML formatting, increments the catalog version, records provenance, and supports --dry-run.
auth rotate-secret | revoke for builtin HTTP OAuth
migrate tds_version 1 → 2 (--dry-run / --write)

validate exits 0 / 1 (--strict warnings) / 2 (errors).
Experimental eval and drift use 1 for failed scenarios or detected
drift; discover uses 3 for live/validation failure. test and
introspect also use 3 on live failure. serve --http --auth none off
localhost exits 3.


Documentation

kjgpta.github.io/vectorsmith is the rendered manual (Material for MkDocs). Source is docs/.

I want to… Go here
Get a tool working in five minutes Getting started
Compare vector-store capabilities and support levels Vector stores
See exactly what is tested per backend Backend conformance
Understand every tools.yaml field YAML reference
Plug into Claude, Codex, Cursor, LangChain, … Integrations
Look up a CLI flag CLI
See every extra, route, and exception Library surface
Call tools from Python Python API
JWT / tenancy / RBAC / credentials / audit Enterprise
validate --enterprise at serve time Security hardening
Traces, metrics, JSON logs, audit sinks Observability
Embedders and rerank Embedding providers
Helm / probes / Redis auth store Kubernetes
Fix Desktop disconnect / env / HTTP auth FAQ
Copy a host config examples/mcp_hosts
See agent apps examples/

Develop

uv sync
uv run ruff check .
uv run pytest -m "not conformance"
uv run lint-imports

# Full local backend matrix (Docker services required)
uv sync --group dev --group conformance --frozen
docker compose up -d
PYTHONPATH=packages/core:packages/cli:. \
  uv run pytest tests/conformance --backend all
docker compose down --volumes

Workspace: packages/core (vectorsmith_core, unpublished) · packages/cli (published vectorsmith). Core must not import the CLI.

Contributing · Support · Security · Changelog · Code of conduct


Apache-2.0 · LICENSE · NOTICE

Forge the tools. Keep the store.

Reviews (0)

No results found