Model Context Protocol server for Qdrant Collections & Points APIs
npm install qdrant-api-mcpA Model Context Protocol (MCP) server that wraps the Qdrant Collections and Points API.
MCP tools are built straight from the official qdrant openapi schema files here: https://github.com/qdrant/qdrant/tree/master/openapi
``json`
{
"mcpServers": {
"qdrant-api": {
"command": "node",
"args": [
"/
],
"env": {
"QDRANT_URL": "http://localhost:6333",
"QDRANT_API_KEY": "my-secret-key-or-blank"
}
}
}
}
The snippet above is the minimal setup and works for single-cluster projects. If you want the multi-cluster workflow described in the Quickstart section, configure Windsurf/Cursor with the exported cluster profile JSON string instead:
`json`
{
"mcpServers": {
"qdrant-api": {
"command": "node",
"args": [
"/
],
"env": {
"QDRANT_CLUSTER_PROFILES": "[{\"name\":\"prod\",\"url\":\"https://prod.example\",\"apiKey\":\"\",\"description\":\"Production search\"},{\"name\":\"metric-media\",\"url\":\"https://metric-media.example\",\"apiKey\":\"\",\"labels\":[\"metrics\",\"readonly\"]},{\"name\":\"test\",\"url\":\"http://localhost:6333\"}]",
"QDRANT_DEFAULT_CLUSTER": "prod"
}
}
}
}
Prefer not to keep a local checkout? Once qdrant-api-mcp is published to npm you can have Windsurf/Cursor invoke it through npx:
`json`
{
"mcpServers": {
"qdrant-api": {
"command": "npx",
"args": ["qdrant-api-mcp"],
"env": {
"QDRANT_URL": "http://localhost:6333",
"QDRANT_API_KEY": "my-secret-key-or-blank"
}
}
}
}
The same approach works for the multi-cluster example—just reuse the JSON string for QDRANT_CLUSTER_PROFILES:
`json`
{
"mcpServers": {
"qdrant-api": {
"command": "npx",
"args": ["qdrant-api-mcp"],
"env": {
"QDRANT_CLUSTER_PROFILES": "[{\"name\":\"prod\",\"url\":\"https://prod.example\",\"apiKey\":\"\",\"description\":\"Production search\"},{\"name\":\"metric-media\",\"url\":\"https://metric-media.example\",\"apiKey\":\"\",\"labels\":[\"metrics\",\"readonly\"]},{\"name\":\"test\",\"url\":\"http://localhost:6333\"}]",
"QDRANT_DEFAULT_CLUSTER": "prod"
}
}
}
}
Test the published package without editing config files by having MCP Inspector spawn the binary directly:
`bash`
npx @modelcontextprotocol/inspector npx -y qdrant-api-mcp -e QDRANT_URL=http://localhost:6333
This downloads qdrant-api-mcp via npx, points it at your local Qdrant instance, and drops you into the Inspector CLI so you can call tools interactively.
- Implements the MCP JSON-RPC specification with structured JSON logs (Pino).
- Responds to the MCP initialize handshake with cluster metadata so stdio and HTTP transports (Inspector, MCP runners) can connect without custom shims.resources/list
- Resource discovery (, resources/read, resources/templates/list) advertises every configured cluster profile so MCP-aware tooling can “ping” the server before invoking tools.QDRANT_CLUSTER_PROFILES
- Multi-cluster operation via optional and a switch_cluster tool. Change clusters without restarting the process.MCP_RATE_LIMIT_MAX_REQUESTS
- Built-in request throttling (configurable via /MCP_RATE_LIMIT_WINDOW_MS) to prevent runaway hybrid queries.describe_point
- Tools cover the full Collections/Points API plus new ergonomics:
- : combines payload, vector, and shard/cluster insights for a single point.scroll_points_paginated
- : emits resumable cursors for large scroll jobs.switch_cluster
- : inspects or updates the active cluster.list_collections
- All existing collection & point tools remain available: , create_collection, get_collection, delete_collection, update_collection, upsert_points, search_points, scroll_points, count_points, recommend_points, get_point, delete_point, delete_points, set_payload, overwrite_payload, delete_payload, clear_payload.
For applications that discover clusters at runtime (e.g., from a database), you can provide cluster credentials directly in tool calls instead of pre-configuring profiles:
`json`
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "list_collections",
"arguments": {
"cluster_url": "https://xyz-cluster.cloud.qdrant.io:6333",
"cluster_api_key": "your-api-key-here"
}
}
}
How it works:
- When you provide cluster_url (and optionally cluster_api_key), the server automatically registers the cluster with a stable generated namecluster_url
- The cluster remains cached for the lifetime of the server process
- Subsequent calls to the same reuse the cached connectionhttps://example.com/
- URLs are normalized (lowercased hostname, trailing slashes removed, default ports stripped) so and https://example.com register as the same cluster
When to use:
- ✅ Multi-tenant applications where each user has their own Qdrant cluster
- ✅ Dynamic environments where cluster URLs are stored in a database
- ✅ Testing against ephemeral clusters that change frequently
- ❌ Static configurations with a known set of clusters (use QDRANT_CLUSTER_PROFILES instead)
Important notes:
- cluster_url and cluster parameters are mutually exclusive—you cannot use both in the same callscroll_points_paginated
- Dynamic clusters do not support the cursor feature (this is a stateful pagination tool that preserves scroll position across calls; the cursor state does not preserve dynamic credentials for security reasons)QDRANT_CLUSTER_PROFILES
- Security: Dynamic cluster credentials are passed in tool arguments and may appear in logs or error messages. For production workloads handling sensitive data, pre-configured profiles are recommended to reduce credential exposure
- For production workloads with a stable set of clusters, pre-configured profiles via are recommended for better performance (connection pooling, optimized rate limiting) and observability
Example with all cluster options:
`json
// Option 1: Use pre-configured profile
{
"name": "list_collections",
"arguments": {
"cluster": "prod"
}
}
// Option 2: Use dynamic cluster URL
{
"name": "list_collections",
"arguments": {
"cluster_url": "https://customer-123.qdrant.io",
"cluster_api_key": "secret-key"
}
}
// Option 3: Use default cluster (no cluster parameter)
{
"name": "list_collections",
"arguments": {}
}
`
- Node.js (v16 or higher)
- npm or yarn
- Qdrant server (local or remote)
1. Clone the repository
2. Install dependencies:
`bash`
npm install
3. Configure environment variables in .env:
``
QDRANT_URL=http://localhost:6333
QDRANT_API_KEY=your_api_key_if_needed
PORT=3000
HOST=localhost
1. Install dependencies and compile once:
`bash`
npm install
npm run build
dist/mcp-server.js
2. Point your MCP-compatible IDE/agent at the compiled entrypoint () or run the published binary directly with npx qdrant-api-mcp. A sample config lives in qdrant-mcp-config.json.`
bash`
QDRANT_URL=http://localhost:6333 npx qdrant-api-mcp
`
3. (Optional) Define multiple clusters in your environment:
bash`
export QDRANT_CLUSTER_PROFILES='[
{"name":"prod","url":"https://prod.example","apiKey":"*","description":"Production search"},
{"name":"metric-media","url":"https://metric-media.example","apiKey":"*","labels":["metrics","readonly"]},
{"name":"test","url":"http://localhost:6333"}
]'
export QDRANT_DEFAULT_CLUSTER=prod
`
4. Launch the MCP server (dev hot reload shown):
bash`
npm run dev:mcp
switch_cluster
5. From your MCP client, call (with no args) to verify the active cluster, or provide {"cluster":"test"} to pivot to a different backend without restarting.
Run the server in development mode:
`bash`
npm run dev
Build and run the server:
`bash`
npm run build
npm start
Integration tests run against a lightweight mock Qdrant server:
`bash`
npm test
Use npm run test:watch for a persistent Vitest watcher while iterating locally.
- Rate limiting: MCP_RATE_LIMIT_MAX_REQUESTS (default 10) and MCP_RATE_LIMIT_WINDOW_MS (default 1000) bound the number of tool calls per cluster/tool combination. Tune these per environment to protect production clusters from runaway agents.LOG_LEVEL=debug
- Structured logs: Set (or info, warn, etc.). Each MCP tool call is logged with {event, tool, cluster, durationMs} so Qdrant audit events can be correlated easily.resources/list
- Resource discovery: advertises every configured cluster as qdrant://clusters/. Reading the resource returns a JSON overview (active flag, collection preview, rate limits, safety hints).
Switch clusters without restarting the process:
`json`
{
"jsonrpc": "2.0",
"id": 42,
"method": "tools/call",
"params": {
"name": "switch_cluster",
"arguments": { "cluster": "metric-media" }
}
}
Resume a long scroll using the new pagination helper:
`json`
{
"jsonrpc": "2.0",
"id": 99,
"method": "tools/call",
"params": {
"name": "scroll_points_paginated",
"arguments": {
"collection_name": "hybrid-docs",
"limit": 128,
"with_payload": true
}
}
}
The response includes cursor; feed it back into the next call to continue from the previous next_page_offset.
Deep dive into a single point (payload + vector + shard metadata):
`json`
{
"jsonrpc": "2.0",
"id": 7,
"method": "tools/call",
"params": {
"name": "describe_point",
"arguments": {
"cluster": "prod",
"collection_name": "docs",
"point_id": "doc-123"
}
}
}
- Keep destructive tools (collection/point mutation) disabled unless you have explicit approval for the target environment. The sample config only whitelists read-only tools by default.
- Use switch_cluster to make sure you are operating on the intended cluster before issuing search/scroll commands.MCP_RATE_LIMIT_MAX_REQUESTS
- Lower for clusters that back user-facing workloads.
- Scrub or truncate payload data before pasting MCP responses into bug reports—cluster resources can include sensitive labels.
- Rotate API keys regularly and store them only in environment variables; the MCP resource summaries intentionally omit raw credentials.
- POST /mcp: The main MCP JSON-RPC endpointPOST /
- : Alternate path for HTTP proxies (e.g., fastmcp runner); the server automatically accepts a leading slug.
Example request:
`json`
{
"jsonrpc": "2.0",
"id": 1,
"method": "list_collections",
"params": {}
}
Example response:
`json`
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"collections": ["collection1", "collection2"]
}
}
- GET /health: Returns server health statusGET /
- : Health endpoint behind an HTTP slug
Lists all collections.
`json`
{
"jsonrpc": "2.0",
"id": 1,
"method": "list_collections",
"params": {}
}
Creates a new collection.
`json`
{
"jsonrpc": "2.0",
"id": 1,
"method": "create_collection",
"params": {
"collection_name": "my_collection",
"vectors": {
"size": 384,
"distance": "Cosine"
}
}
}
Gets detailed information about a collection.
`json`
{
"jsonrpc": "2.0",
"id": 1,
"method": "get_collection",
"params": {
"collection_name": "my_collection"
}
}
Deletes a collection.
`json`
{
"jsonrpc": "2.0",
"id": 1,
"method": "delete_collection",
"params": {
"collection_name": "my_collection"
}
}
Updates collection parameters.
`json`
{
"jsonrpc": "2.0",
"id": 1,
"method": "update_collection",
"params": {
"collection_name": "my_collection",
"optimizers_config": {
"deleted_threshold": 0.2,
"vacuum_min_vector_number": 1000
}
}
}
Insert or update points in a collection.
`json`
{
"jsonrpc": "2.0",
"id": 1,
"method": "upsert_points",
"params": {
"collection_name": "my_collection",
"points": [
{
"id": 1,
"vector": [0.1, 0.2, 0.3, 0.4],
"payload": {
"category": "example",
"value": 42
}
}
]
}
}
Search for similar points in a collection.
`json`
{
"jsonrpc": "2.0",
"id": 1,
"method": "search_points",
"params": {
"collection_name": "my_collection",
"vector": [0.1, 0.2, 0.3, 0.4],
"limit": 10,
"with_payload": true,
"with_vector": false
}
}
Scroll through points in a collection.
`json`
{
"jsonrpc": "2.0",
"id": 1,
"method": "scroll_points",
"params": {
"collection_name": "my_collection",
"limit": 10,
"with_payload": true
}
}
Count points in a collection.
`json`
{
"jsonrpc": "2.0",
"id": 1,
"method": "count_points",
"params": {
"collection_name": "my_collection",
"filter": {
"must": [
{
"key": "category",
"match": {
"value": "example"
}
}
]
}
}
}
Get point recommendations based on positive and negative examples.
`json`
{
"jsonrpc": "2.0",
"id": 1,
"method": "recommend_points",
"params": {
"collection_name": "my_collection",
"positive": [1, 2],
"negative": [3],
"limit": 10
}
}
```
qdrant_mcp/
├── src/
│ ├── api/ # API-related code (for future expansion)
│ ├── config/ # Configuration files
│ ├── generated/ # Generated client code
│ ├── lib/ # Library code (Qdrant client)
│ ├── mcp/ # MCP server implementation
│ ├── types/ # TypeScript type definitions
│ └── index.ts # Application entry point
├── .env # Environment variables
├── .gitignore # Git ignore file
├── package.json # Project dependencies
├── tsconfig.json # TypeScript configuration
└── README.md # Project documentation
MIT © 2025 Capitol AI. See LICENSE for details.