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.
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 | No typed args. No enums. No hidden |
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)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 --> HOne file, two doors. Same compiled tools.
Python app | Chat / IDE host | |
Install |
|
|
Call |
|
|
Process | In-process. No subprocess. | The host spawns the CLI (MCP stdio or HTTP) |
Mix-in | Your | Other |
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: 50vectorsmith 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
| For | Typical tool |
| Semantic retrieve + filters |
|
| Exact id, limit 1 |
|
| “How many overdue?” |
|
| Filter / page, no ANN | list-style tools |
| Retrieve → | 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 |
|
|
| same tools; LangGraph graph |
|
|
|
|
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 Code |
| |
OpenAI Codex |
| |
Cursor |
| |
claude.ai |
|
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-embedLocalhost 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 |
|
Tenant isolation | Hidden |
Which tools |
|
Secrets |
|
Quotas |
|
Health |
|
Observability |
|
Hardening |
|
Search quality | Pluggable embedders, |
Many catalogs | Extra YAML args, |
Drain |
|
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 |
|
|
| Shared OAuth tokens and Redis rate limits |
| OTLP traces from |
|
|
| Hosted embedders (and Cohere rerank) |
|
|
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.exampleTickets are a second file / second MCP name: tools.tickets.yaml → --name tickets.
CLI
Command | Does |
| Write a starter |
| Compile + lint. |
| Call one compiled tool without serving |
| MCP stdio (Desktop / Codex / Cursor; |
| Collection / field metadata to |
| Introspect live collections and write pending schema-backed drafts without changing |
| Execute checked-in tool-call scenarios and write row/isolation/score invariant results. |
| Compare a metadata-only schema export with live introspection; report suggestions without auto-promotion. |
|
|
|
|
|
|
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 | |
Compare vector-store capabilities and support levels | |
See exactly what is tested per backend | |
Understand every | |
Plug into Claude, Codex, Cursor, LangChain, … | |
Look up a CLI flag | |
See every extra, route, and exception | |
Call tools from Python | |
JWT / tenancy / RBAC / credentials / audit | |
| |
Traces, metrics, JSON logs, audit sinks | |
Embedders and rerank | |
Helm / probes / Redis auth store | |
Fix Desktop disconnect / env / HTTP auth | |
Copy a host config | |
See agent apps |
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 --volumesWorkspace: packages/core (vectorsmith_core, unpublished) · packages/cli (published vectorsmith). Core must not import the CLI.
Contributing · Support · Security · Changelog · Code of conduct
Forge the tools. Keep the store.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/kjgpta/vectorsmith'
If you have feedback or need assistance with the MCP directory API, please join our Discord server