🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@typesensekit/mcp

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@typesensekit/mcp - npm Package Compare versions

Comparing version
1.1.3
to
1.2.0
+1861
dist/chunk-KY4R2JA7.js
#!/usr/bin/env node
// src/server.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
// ../core/src/client.ts
import Typesense from "typesense";
// ../core/src/config.ts
import { z } from "zod";
var nodeConfigSchema = z.object({
host: z.string().min(1),
port: z.number().int().positive().optional(),
protocol: z.enum(["http", "https"]).optional(),
path: z.string().optional()
});
var serverConfigSchema = z.object({
url: z.string().url(),
apiKey: z.string().min(1),
connectionTimeoutSeconds: z.number().positive().optional(),
nearestNode: nodeConfigSchema.optional(),
numRetries: z.number().int().nonnegative().optional(),
retryIntervalSeconds: z.number().positive().optional(),
healthcheckIntervalSeconds: z.number().positive().optional()
});
// ../core/src/client.ts
function nodeFromUrl(url) {
const parsed = new URL(url);
const node = {
host: parsed.hostname,
port: parsed.port ? Number(parsed.port) : parsed.protocol === "https:" ? 443 : 80,
protocol: parsed.protocol.replace(":", "")
};
if (parsed.pathname !== "/") node.path = parsed.pathname;
return node;
}
function normalizeNearestNode(node) {
if (!node) return void 0;
return {
host: node.host,
port: node.port ?? (node.protocol === "http" ? 80 : 443),
protocol: node.protocol ?? "https",
path: node.path
};
}
function createClient(config) {
const parsed = serverConfigSchema.parse(config);
const clientConfig = {
nodes: [nodeFromUrl(parsed.url)],
apiKey: parsed.apiKey,
connectionTimeoutSeconds: parsed.connectionTimeoutSeconds,
nearestNode: normalizeNearestNode(parsed.nearestNode),
numRetries: parsed.numRetries,
retryIntervalSeconds: parsed.retryIntervalSeconds,
healthcheckIntervalSeconds: parsed.healthcheckIntervalSeconds
};
return new Typesense.Client(clientConfig);
}
// ../core/src/redaction.ts
var REDACTED = "[REDACTED]";
var CIRCULAR = "[Circular]";
var SECRET_KEYS = /* @__PURE__ */ new Set([
"api_key",
"apikey",
"authorization",
"cookie",
"secret",
"setcookie",
"token",
"xtypesenseapikey"
]);
function shouldRedactKey(key) {
const normalized = key.toLowerCase().replace(/[-_\s]/g, "");
return SECRET_KEYS.has(normalized) || normalized.endsWith("apikey") || normalized.endsWith("token") || normalized.endsWith("secret");
}
function isTypesenseApiKeyShape(value) {
return Array.isArray(value.actions) && Array.isArray(value.collections);
}
function redactText(value) {
return value.replace(
/(["']?authorization["']?\s*[:=]\s*)(["']?)(?:Bearer|Basic)?\s*[-A-Za-z0-9._~+/=]+(["']?)/gi,
(_match, prefix, openQuote) => `${prefix}${openQuote}${REDACTED}${openQuote}`
).replace(
/(["']?(?:x-typesense-api-key|api[_-]?key|apikey|token|secret|cookie|set-cookie)["']?\s*[:=]\s*)(["']?)([^"',\s}\]]+)(["']?)/gi,
(_match, prefix, openQuote, _secret) => `${prefix}${openQuote}${REDACTED}${openQuote}`
).replace(/\b(Bearer|Basic)\s+[A-Za-z0-9._~+/=-]+/gi, `$1 ${REDACTED}`);
}
function redactError(error, seen) {
const output = {};
for (const key of Object.getOwnPropertyNames(error)) {
output[key] = redactValue(
error[key],
seen
);
}
if (!("name" in output)) output.name = error.name;
if (!("message" in output)) output.message = redactText(error.message);
return output;
}
function redactValue(value, seen) {
if (typeof value === "string") return redactText(value);
if (Array.isArray(value)) {
return value.map((item) => redactValue(item, seen));
}
if (typeof value !== "object" || value === null) {
return value;
}
if (seen.has(value)) return CIRCULAR;
seen.add(value);
if (value instanceof Error) {
return redactError(value, seen);
}
const record = value;
return Object.fromEntries(
Object.entries(record).map(([key, child]) => [
key,
shouldRedactKey(key) || key === "value" && isTypesenseApiKeyShape(record) ? REDACTED : redactValue(child, seen)
])
);
}
function redactSecrets(value) {
return redactValue(value, /* @__PURE__ */ new WeakSet());
}
// ../core/src/errors.ts
var NETWORK_ERROR_CODES = /* @__PURE__ */ new Set([
"EAI_AGAIN",
"ECONNABORTED",
"ECONNREFUSED",
"ECONNRESET",
"ENOTFOUND",
"ETIMEDOUT"
]);
function readErrorLike(error) {
return typeof error === "object" && error !== null ? error : {};
}
function findNetworkErrorCode(error) {
const errorLike = readErrorLike(error);
if (typeof errorLike.code === "string" && NETWORK_ERROR_CODES.has(errorLike.code.toUpperCase())) {
return errorLike.code.toUpperCase();
}
if (typeof errorLike.message === "string") {
const match = errorLike.message.match(
/\b(EAI_AGAIN|ECONNABORTED|ECONNREFUSED|ECONNRESET|ENOTFOUND|ETIMEDOUT)\b/i
);
if (match?.[1]) return match[1].toUpperCase();
}
return errorLike.cause === void 0 ? void 0 : findNetworkErrorCode(errorLike.cause);
}
function endpointLabel(error) {
const errorLike = readErrorLike(error);
const host = typeof errorLike.hostname === "string" ? errorLike.hostname : typeof errorLike.host === "string" ? errorLike.host : typeof errorLike.address === "string" ? errorLike.address : void 0;
const port = typeof errorLike.port === "number" || typeof errorLike.port === "string" ? String(errorLike.port) : void 0;
if (host && port) return `${host}:${port}`;
if (host) return host;
return errorLike.cause === void 0 ? void 0 : endpointLabel(errorLike.cause);
}
function conciseNetworkErrorMessage(error) {
const code = findNetworkErrorCode(error);
if (!code) return void 0;
const endpoint = endpointLabel(error);
return `Request failed: ${code}${endpoint ? ` ${endpoint}` : ""}`;
}
function normalizeTypesenseError(error) {
if (error instanceof Error) {
const errorLike = error;
const status = errorLike.httpStatus ?? errorLike.status;
return {
code: typeof status === "number" ? String(status) : error.name || "TypesenseError",
message: redactText(error.message),
details: redactSecrets(error)
};
}
if (typeof error === "object" && error !== null) {
const errorLike = error;
return {
code: typeof errorLike.status === "number" ? String(errorLike.status) : "TypesenseError",
message: typeof errorLike.message === "string" ? redactText(errorLike.message) : "Unknown Typesense error",
details: redactSecrets(error)
};
}
return { code: "TypesenseError", message: redactText(String(error)) };
}
function formatTypesenseErrorMessage(error, options = {}) {
const normalized = normalizeTypesenseError(error);
const message = conciseNetworkErrorMessage(error) ?? normalized.message;
if (!options.debug) return message;
return [
message,
"",
"Debug details:",
JSON.stringify(normalized.details ?? normalized, null, 2)
].join("\n");
}
// ../core/src/operations/aliases.ts
import { z as z2 } from "zod";
// ../core/src/operations/http.ts
function api(client) {
return client.apiCall;
}
function enc(value) {
return encodeURIComponent(value);
}
function collectionPath(collection) {
return `/collections/${enc(collection)}`;
}
// ../core/src/operations/aliases.ts
var aliasesOperations = [
{
name: "aliases.list",
summary: "List aliases",
category: "aliases",
input: z2.object({}),
execute: async (client) => api(client).get("/aliases")
},
{
name: "aliases.create",
summary: "Create or upsert an alias",
category: "aliases",
input: z2.object({
name: z2.string(),
value: z2.object({ collection_name: z2.string() })
}),
execute: async (client, input) => api(client).put(`/aliases/${enc(input.name)}`, input.value)
},
{
name: "aliases.retrieve",
summary: "Retrieve an alias",
category: "aliases",
input: z2.object({ name: z2.string() }),
execute: async (client, input) => api(client).get(`/aliases/${enc(input.name)}`)
},
{
name: "aliases.delete",
summary: "Delete an alias",
category: "aliases",
input: z2.object({ name: z2.string() }),
execute: async (client, input) => api(client).delete(`/aliases/${enc(input.name)}`)
}
];
// ../core/src/operations/analytics.ts
import { z as z3 } from "zod";
var analyticsOperations = [
{
name: "analytics.rules.list",
summary: "List analytics rules",
category: "analytics",
input: z3.object({ ruleTag: z3.string().optional() }),
execute: async (client, input) => api(client).get(
"/analytics/rules",
input.ruleTag ? { rule_tag: input.ruleTag } : void 0
)
},
{
name: "analytics.rules.create",
summary: "Create one or more analytics rules",
category: "analytics",
input: z3.object({
value: z3.union([
z3.record(z3.unknown()),
z3.array(z3.record(z3.unknown())).min(1)
])
}),
execute: async (client, input) => api(client).post("/analytics/rules", input.value)
},
{
name: "analytics.rules.upsert",
summary: "Create or update an analytics rule",
category: "analytics",
input: z3.object({ name: z3.string(), value: z3.record(z3.unknown()) }),
execute: async (client, input) => api(client).put(`/analytics/rules/${enc(input.name)}`, input.value)
},
{
name: "analytics.rules.delete",
summary: "Delete an analytics rule",
category: "analytics",
input: z3.object({ name: z3.string() }),
execute: async (client, input) => api(client).delete(`/analytics/rules/${enc(input.name)}`)
},
{
name: "analytics.rules.retrieve",
summary: "Retrieve an analytics rule",
category: "analytics",
input: z3.object({ name: z3.string() }),
execute: async (client, input) => api(client).get(`/analytics/rules/${enc(input.name)}`)
},
{
name: "analytics.events.create",
summary: "Create an analytics event",
category: "analytics",
input: z3.object({
type: z3.string(),
name: z3.string(),
data: z3.record(z3.unknown())
}),
execute: async (client, input) => api(client).post("/analytics/events", input)
},
{
name: "analytics.events.list",
summary: "Retrieve recent analytics events for a user and rule",
category: "analytics",
input: z3.object({
userId: z3.string().min(1),
name: z3.string().min(1),
limit: z3.number().int().positive().max(1e3)
}),
execute: async (client, input) => api(client).get("/analytics/events", {
user_id: input.userId,
name: input.name,
n: input.limit
})
},
{
name: "analytics.flush",
summary: "Flush in-memory analytics data to persistent storage",
category: "analytics",
input: z3.object({}),
execute: async (client) => api(client).post("/analytics/flush")
},
{
name: "analytics.status",
summary: "Retrieve analytics subsystem status",
category: "analytics",
input: z3.object({}),
execute: async (client) => api(client).get("/analytics/status")
}
];
// ../core/src/operations/api.ts
import { z as z4 } from "zod";
var methodSchema = z4.enum(["get", "post", "put", "patch", "delete"]);
var normalizedMethodSchema = z4.preprocess(
(value) => typeof value === "string" ? value.toLowerCase() : value,
methodSchema
);
var apiOperations = [
{
name: "api.call",
summary: "Call any Typesense API endpoint not yet covered by a first-class operation",
category: "api",
input: z4.object({
method: normalizedMethodSchema,
path: z4.string().startsWith("/"),
params: z4.record(z4.unknown()).optional(),
body: z4.unknown().optional()
}),
execute: async (client, input) => {
const request = api(client);
if (input.method === "get") return request.get(input.path, input.params);
if (input.method === "delete")
return request.delete(input.path, input.params);
if (input.method === "post")
return request.post(input.path, input.body, input.params);
if (input.method === "put")
return request.put(input.path, input.body, input.params);
return request.patch(input.path, input.body, input.params);
}
}
];
// ../core/src/operations/collections.ts
import { z as z5 } from "zod";
var DEFAULT_WAIT_TIMEOUT_MS = 3e4;
var DEFAULT_WAIT_INTERVAL_MS = 1e3;
var baseFieldSchema = z5.object({
name: z5.string(),
facet: z5.boolean().optional(),
index: z5.boolean().optional(),
optional: z5.boolean().optional(),
sort: z5.boolean().optional(),
locale: z5.string().optional(),
infix: z5.boolean().optional(),
stem: z5.boolean().optional()
}).passthrough();
var createFieldSchema = baseFieldSchema.extend({
type: z5.string()
});
var patchFieldSchema = baseFieldSchema.extend({
type: z5.string().optional(),
drop: z5.boolean().optional()
});
var fieldLifecycleInputSchema = z5.object({
collection: z5.string(),
field: z5.string().optional(),
yes: z5.boolean().optional(),
numDim: z5.coerce.number().int().positive().optional(),
vecDist: z5.string().optional(),
hnswM: z5.coerce.number().int().positive().optional(),
hnswEfConstruction: z5.coerce.number().int().positive().optional(),
embedFrom: z5.union([z5.string(), z5.array(z5.string())]).optional(),
embedModel: z5.string().optional(),
embedApiKey: z5.string().optional(),
timeoutMs: z5.coerce.number().int().nonnegative().default(DEFAULT_WAIT_TIMEOUT_MS),
intervalMs: z5.coerce.number().int().nonnegative().default(DEFAULT_WAIT_INTERVAL_MS)
}).passthrough();
var waitInputSchema = z5.object({
collection: z5.string(),
fieldPresent: z5.string().optional(),
fieldMissing: z5.string().optional(),
fieldEmbedFrom: z5.string().optional(),
timeoutMs: z5.coerce.number().int().nonnegative().default(DEFAULT_WAIT_TIMEOUT_MS),
intervalMs: z5.coerce.number().int().nonnegative().default(DEFAULT_WAIT_INTERVAL_MS)
});
var FIELD_LIFECYCLE_KEYS = /* @__PURE__ */ new Set([
"collection",
"field",
"yes",
"numDim",
"vecDist",
"hnswM",
"hnswEfConstruction",
"embedFrom",
"embedModel",
"embedApiKey",
"timeoutMs",
"intervalMs"
]);
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
function isObject(value) {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
function requestedFieldName(input) {
const name = input.field ?? input.name;
if (typeof name !== "string" || !name) {
throw new Error("A field name is required. Pass --field or include name.");
}
return name;
}
function requiresConfirmation(collection) {
return /^production(?:__|-|$)/i.test(collection) || /^prod(?:__|-|$)/i.test(collection);
}
function assertDestructiveConfirmed(input) {
if (requiresConfirmation(input.collection) && !input.yes) {
throw new Error(
`Refusing to modify production collection ${input.collection} without --yes.`
);
}
}
function findField(collection, fieldName) {
return collection.fields?.find((field) => {
if (!isObject(field)) return false;
return field.name === fieldName;
});
}
async function retrieveCollection(client, collection) {
return api(client).get(collectionPath(collection));
}
function buildFieldDefinition(input) {
const field = Object.fromEntries(
Object.entries(input).filter(
([key, value]) => value !== void 0 && !FIELD_LIFECYCLE_KEYS.has(key)
)
);
field.name = requestedFieldName(input);
if (input.numDim !== void 0) field.num_dim = input.numDim;
if (input.vecDist !== void 0) field.vec_dist = input.vecDist;
if (input.hnswM !== void 0 || input.hnswEfConstruction !== void 0) {
const hnswParams = isObject(field.hnsw_params) ? field.hnsw_params : {};
if (input.hnswM !== void 0) hnswParams.M = input.hnswM;
if (input.hnswEfConstruction !== void 0) {
hnswParams.ef_construction = input.hnswEfConstruction;
}
field.hnsw_params = hnswParams;
}
if (input.embedFrom !== void 0 || input.embedModel !== void 0 || input.embedApiKey !== void 0) {
const embed = isObject(field.embed) ? field.embed : {};
const modelConfig = isObject(embed.model_config) ? embed.model_config : {};
if (input.embedFrom !== void 0) {
embed.from = Array.isArray(input.embedFrom) ? input.embedFrom : [input.embedFrom];
}
if (input.embedModel !== void 0)
modelConfig.model_name = input.embedModel;
if (input.embedApiKey !== void 0)
modelConfig.api_key = input.embedApiKey;
embed.model_config = modelConfig;
field.embed = embed;
}
if (typeof field.type !== "string" || !field.type) {
throw new Error("A field type is required. Pass --type or include type.");
}
return field;
}
function parseEmbedFromCondition(condition) {
const separator = condition.indexOf(":");
if (separator === -1) {
throw new Error("--field-embed-from must use FIELD:SOURCE format.");
}
return {
fieldName: condition.slice(0, separator),
source: condition.slice(separator + 1)
};
}
function isTransientCollectionUpdateError(error) {
const message = error instanceof Error ? error.message : String(error);
return /another collection update operation is in progress/i.test(message) || /timeout of \d+ms exceeded/i.test(message) || /econnaborted/i.test(message);
}
function evaluateWaitCondition(collection, input) {
const conditions = [
input.fieldPresent,
input.fieldMissing,
input.fieldEmbedFrom
].filter(Boolean);
if (conditions.length !== 1) {
throw new Error(
"Pass exactly one wait condition: --field-present, --field-missing, or --field-embed-from."
);
}
if (input.fieldPresent) {
return {
ok: Boolean(findField(collection, input.fieldPresent)),
condition: "field-present",
field: input.fieldPresent
};
}
if (input.fieldMissing) {
return {
ok: !findField(collection, input.fieldMissing),
condition: "field-missing",
field: input.fieldMissing
};
}
const { fieldName, source } = parseEmbedFromCondition(
input.fieldEmbedFrom ?? ""
);
const field = findField(collection, fieldName);
const from = isObject(field) && isObject(field.embed) && Array.isArray(field.embed.from) ? field.embed.from : [];
return {
ok: from.includes(source),
condition: "field-embed-from",
field: fieldName,
source
};
}
async function waitForCollectionCondition(client, input) {
const startedAt = Date.now();
let lastError;
let attempts = 0;
while (Date.now() - startedAt <= input.timeoutMs) {
attempts += 1;
try {
const collection = await retrieveCollection(client, input.collection);
const result = evaluateWaitCondition(collection, input);
if (result.ok) {
return {
collection: input.collection,
attempts,
...result
};
}
} catch (error) {
if (!isTransientCollectionUpdateError(error)) throw error;
lastError = error;
}
await sleep(input.intervalMs);
}
const detail = lastError instanceof Error ? ` Last error: ${lastError.message}` : "";
throw new Error(
`Timed out waiting for collection ${input.collection} schema condition.${detail}`
);
}
async function dropField(client, input) {
assertDestructiveConfirmed(input);
const fieldName = requestedFieldName(input);
const collection = await retrieveCollection(client, input.collection);
const existingField = findField(collection, fieldName);
if (!existingField) {
return {
ok: true,
collection: input.collection,
field: fieldName,
alreadyMissing: true
};
}
const result = await api(client).patch(collectionPath(input.collection), {
fields: [{ name: fieldName, drop: true }]
});
return {
ok: true,
collection: input.collection,
field: fieldName,
before: existingField,
result
};
}
async function addField(client, input) {
const field = buildFieldDefinition(input);
const result = await api(client).patch(collectionPath(input.collection), {
fields: [field]
});
return {
ok: true,
collection: input.collection,
field: field.name,
added: field,
result
};
}
var collectionOperations = [
{
name: "collections.create",
summary: "Create a collection",
category: "collections",
input: z5.object({
name: z5.string(),
fields: z5.array(createFieldSchema),
default_sorting_field: z5.string().optional(),
token_separators: z5.array(z5.string()).optional(),
symbols_to_index: z5.array(z5.string()).optional(),
enable_nested_fields: z5.boolean().optional()
}),
execute: async (client, input) => api(client).post("/collections", input)
},
{
name: "collections.list",
summary: "List collections",
category: "collections",
input: z5.object({}),
execute: async (client) => api(client).get("/collections")
},
{
name: "collections.retrieve",
summary: "Retrieve a collection",
category: "collections",
input: z5.object({ collection: z5.string() }),
execute: async (client, input) => api(client).get(collectionPath(input.collection))
},
{
name: "collections.update",
summary: "Update a collection schema",
category: "collections",
input: z5.object({
collection: z5.string(),
fields: z5.array(patchFieldSchema).optional()
}),
execute: async (client, input) => api(client).patch(collectionPath(input.collection), {
fields: input.fields
})
},
{
name: "collections.wait",
summary: "Wait for a collection schema condition",
category: "collections",
input: waitInputSchema,
execute: waitForCollectionCondition
},
{
name: "collections.fields.add",
summary: "Add a field to a collection",
category: "collections",
input: fieldLifecycleInputSchema,
execute: addField
},
{
name: "collections.fields.drop",
summary: "Drop a field from a collection",
category: "collections",
input: fieldLifecycleInputSchema,
execute: dropField
},
{
name: "collections.fields.replace",
summary: "Safely replace a collection field",
category: "collections",
input: fieldLifecycleInputSchema,
execute: async (client, input) => {
const fieldName = requestedFieldName(input);
const replacement = buildFieldDefinition(input);
const dropResult = await dropField(client, input);
await waitForCollectionCondition(client, {
collection: input.collection,
fieldMissing: fieldName,
timeoutMs: input.timeoutMs,
intervalMs: input.intervalMs
});
try {
await api(client).patch(collectionPath(input.collection), {
fields: [replacement]
});
} catch (error) {
throw new Error(
[
error instanceof Error ? error.message : String(error),
"",
"The original field was dropped, but adding the replacement failed.",
`Recover with: tsk collections.fields.add --collection ${input.collection} --input field.json`
].join("\n")
);
}
const waitResult = await waitForCollectionCondition(client, {
collection: input.collection,
fieldPresent: fieldName,
timeoutMs: input.timeoutMs,
intervalMs: input.intervalMs
});
const collection = await retrieveCollection(client, input.collection);
return {
ok: true,
collection: input.collection,
field: fieldName,
before: "before" in dropResult ? dropResult.before : void 0,
after: findField(collection, fieldName),
wait: waitResult
};
}
},
{
name: "collections.delete",
summary: "Delete a collection",
category: "collections",
input: z5.object({ collection: z5.string() }),
execute: async (client, input) => api(client).delete(collectionPath(input.collection))
}
];
// ../core/src/operations/conversations.ts
import { z as z6 } from "zod";
var conversationOperations = [
{
name: "conversations.models.list",
summary: "List conversation models",
category: "conversations",
input: z6.object({}),
execute: async (client) => api(client).get("/conversations/models")
},
{
name: "conversations.models.create",
summary: "Create a conversation model",
category: "conversations",
input: z6.object({ value: z6.record(z6.unknown()) }),
execute: async (client, input) => api(client).post("/conversations/models", input.value)
},
{
name: "conversations.models.retrieve",
summary: "Retrieve a conversation model",
category: "conversations",
input: z6.object({ id: z6.string() }),
execute: async (client, input) => api(client).get(`/conversations/models/${enc(input.id)}`)
},
{
name: "conversations.models.delete",
summary: "Delete a conversation model",
category: "conversations",
input: z6.object({ id: z6.string() }),
execute: async (client, input) => api(client).delete(`/conversations/models/${enc(input.id)}`)
},
{
name: "conversations.history.retrieve",
summary: "Retrieve conversation history",
category: "conversations",
input: z6.object({ conversation_id: z6.string().optional() }),
execute: async (client, input) => api(client).get(
"/conversations/history",
input.conversation_id ? { conversation_id: input.conversation_id } : void 0
)
}
];
// ../core/src/operations/curation-sets.ts
import { z as z7 } from "zod";
function base(name) {
return name ? `/curation_sets/${enc(name)}` : "/curation_sets";
}
function itemPath(name, id) {
const path = `${base(name)}/items`;
return id ? `${path}/${enc(id)}` : path;
}
var curationSetOperations = [
{
name: "curation_sets.list",
summary: "List global curation sets",
category: "curations",
input: z7.object({}),
execute: async (client) => api(client).get(base())
},
{
name: "curation_sets.upsert",
summary: "Create or update a global curation set",
category: "curations",
input: z7.object({
name: z7.string(),
value: z7.object({ items: z7.array(z7.record(z7.unknown())) })
}),
execute: async (client, input) => api(client).put(base(input.name), input.value)
},
{
name: "curation_sets.retrieve",
summary: "Retrieve a global curation set",
category: "curations",
input: z7.object({ name: z7.string() }),
execute: async (client, input) => api(client).get(base(input.name))
},
{
name: "curation_sets.delete",
summary: "Delete a global curation set",
category: "curations",
input: z7.object({ name: z7.string() }),
execute: async (client, input) => api(client).delete(base(input.name))
},
{
name: "curation_sets.items.list",
summary: "List items in a global curation set",
category: "curations",
input: z7.object({ name: z7.string() }),
execute: async (client, input) => api(client).get(itemPath(input.name))
},
{
name: "curation_sets.items.upsert",
summary: "Create or update an item in a global curation set",
category: "curations",
input: z7.object({
name: z7.string(),
id: z7.string(),
value: z7.record(z7.unknown())
}),
execute: async (client, input) => api(client).put(itemPath(input.name, input.id), input.value)
},
{
name: "curation_sets.items.retrieve",
summary: "Retrieve an item in a global curation set",
category: "curations",
input: z7.object({ name: z7.string(), id: z7.string() }),
execute: async (client, input) => api(client).get(itemPath(input.name, input.id))
},
{
name: "curation_sets.items.delete",
summary: "Delete an item in a global curation set",
category: "curations",
input: z7.object({ name: z7.string(), id: z7.string() }),
execute: async (client, input) => api(client).delete(itemPath(input.name, input.id))
}
];
// ../core/src/operations/documents.ts
import { z as z8 } from "zod";
var documentSchema = z8.record(z8.unknown());
var idsSchema = z8.array(z8.string().min(1)).min(1);
var searchParams = z8.record(
z8.union([
z8.string(),
z8.number(),
z8.boolean(),
z8.array(z8.string()),
z8.array(z8.number())
])
);
var documentOperations = [
{
name: "documents.index",
summary: "Index a document",
category: "documents",
input: z8.object({ collection: z8.string(), document: documentSchema }),
execute: async (client, input) => api(client).post(
`${collectionPath(input.collection)}/documents`,
input.document
)
},
{
name: "documents.upsert",
summary: "Upsert a document",
category: "documents",
input: z8.object({ collection: z8.string(), document: documentSchema }),
execute: async (client, input) => api(client).post(
`${collectionPath(input.collection)}/documents`,
input.document,
{ action: "upsert" }
)
},
{
name: "documents.get",
summary: "Get a document by id",
category: "documents",
input: z8.object({ collection: z8.string(), id: z8.string() }),
execute: async (client, input) => api(client).get(
`${collectionPath(input.collection)}/documents/${enc(input.id)}`
)
},
{
name: "documents.get_many",
summary: "Get multiple documents by id",
category: "documents",
input: z8.object({ collection: z8.string(), ids: idsSchema }),
execute: async (client, input) => {
const request = api(client);
return Promise.all(
input.ids.map(
(id) => request.get(
`${collectionPath(input.collection)}/documents/${enc(id)}`
)
)
);
}
},
{
name: "documents.update",
summary: "Update a document by id",
category: "documents",
input: z8.object({
collection: z8.string(),
id: z8.string(),
document: documentSchema
}),
execute: async (client, input) => api(client).patch(
`${collectionPath(input.collection)}/documents/${enc(input.id)}`,
input.document
)
},
{
name: "documents.delete",
summary: "Delete a document by id",
category: "documents",
input: z8.object({ collection: z8.string(), id: z8.string() }),
execute: async (client, input) => api(client).delete(
`${collectionPath(input.collection)}/documents/${enc(input.id)}`
)
},
{
name: "documents.import",
summary: "Import documents into a collection",
category: "documents",
input: z8.object({
collection: z8.string(),
documents: z8.union([z8.string(), z8.array(documentSchema)]),
action: z8.enum(["create", "upsert", "update", "emplace"]).optional()
}),
execute: async (client, input) => api(client).post(
`${collectionPath(input.collection)}/documents/import`,
Array.isArray(input.documents) ? input.documents.map((doc) => JSON.stringify(doc)).join("\n") : input.documents,
input.action ? { action: input.action } : void 0
)
},
{
name: "documents.export",
summary: "Export documents from a collection",
category: "documents",
input: z8.object({
collection: z8.string(),
params: searchParams.optional()
}),
execute: async (client, input) => api(client).get(
`${collectionPath(input.collection)}/documents/export`,
input.params
)
},
{
name: "documents.search",
summary: "Search within a collection",
category: "documents",
input: z8.object({ collection: z8.string(), params: searchParams }),
execute: async (client, input) => api(client).get(
`${collectionPath(input.collection)}/documents/search`,
input.params
)
}
];
// ../core/src/operations/keys.ts
import { z as z9 } from "zod";
var keysOperations = [
{
name: "keys.list",
summary: "List API keys",
category: "keys",
input: z9.object({}),
execute: async (client) => api(client).get("/keys")
},
{
name: "keys.create",
summary: "Create an API key",
category: "keys",
input: z9.object({ value: z9.record(z9.unknown()) }),
execute: async (client, input) => api(client).post("/keys", input.value)
},
{
name: "keys.retrieve",
summary: "Retrieve an API key",
category: "keys",
input: z9.object({ id: z9.union([z9.string(), z9.number()]) }),
execute: async (client, input) => api(client).get(`/keys/${enc(String(input.id))}`)
},
{
name: "keys.delete",
summary: "Delete an API key",
category: "keys",
input: z9.object({ id: z9.union([z9.string(), z9.number()]) }),
execute: async (client, input) => api(client).delete(`/keys/${enc(String(input.id))}`)
}
];
// ../core/src/operations/nl-search-models.ts
import { z as z10 } from "zod";
var modelConfigSchema = z10.object({
id: z10.string().optional(),
model_name: z10.string().optional(),
api_key: z10.string().optional(),
api_url: z10.string().url().optional(),
max_bytes: z10.number().int().positive().optional(),
temperature: z10.number().optional(),
system_prompt: z10.string().optional(),
top_p: z10.number().optional(),
top_k: z10.number().int().optional(),
stop_sequences: z10.array(z10.string()).optional(),
api_version: z10.string().optional(),
project_id: z10.string().optional(),
access_token: z10.string().optional(),
refresh_token: z10.string().optional(),
client_id: z10.string().optional(),
client_secret: z10.string().optional(),
region: z10.string().optional(),
max_output_tokens: z10.number().int().positive().optional(),
account_id: z10.string().optional()
}).passthrough();
function modelPath(id) {
return `/nl_search_models/${enc(id)}`;
}
var nlSearchModelOperations = [
{
name: "nl_search_models.list",
summary: "List natural language search models",
category: "nl_search_models",
input: z10.object({}),
execute: async (client) => api(client).get("/nl_search_models")
},
{
name: "nl_search_models.create",
summary: "Create a natural language search model",
category: "nl_search_models",
input: z10.object({ value: modelConfigSchema }),
execute: async (client, input) => api(client).post("/nl_search_models", input.value)
},
{
name: "nl_search_models.retrieve",
summary: "Retrieve a natural language search model",
category: "nl_search_models",
input: z10.object({ id: z10.string().min(1) }),
execute: async (client, input) => api(client).get(modelPath(input.id))
},
{
name: "nl_search_models.update",
summary: "Update a natural language search model",
category: "nl_search_models",
input: z10.object({ id: z10.string().min(1), value: modelConfigSchema }),
execute: async (client, input) => api(client).put(modelPath(input.id), input.value)
},
{
name: "nl_search_models.delete",
summary: "Delete a natural language search model",
category: "nl_search_models",
input: z10.object({ id: z10.string().min(1) }),
execute: async (client, input) => api(client).delete(modelPath(input.id))
}
];
// ../core/src/operations/overrides.ts
import { z as z11 } from "zod";
var overridesOperations = [
{
name: "overrides.list",
summary: "List overrides",
category: "overrides",
input: z11.object({ collection: z11.string() }),
execute: async (client, input) => api(client).get(`${collectionPath(input.collection)}/overrides`)
},
{
name: "overrides.create",
summary: "Create or upsert a override",
category: "overrides",
input: z11.object({
collection: z11.string(),
name: z11.string(),
value: z11.record(z11.unknown())
}),
execute: async (client, input) => api(client).put(
`${collectionPath(input.collection)}/overrides/${enc(input.name)}`,
input.value
)
},
{
name: "overrides.retrieve",
summary: "Retrieve a override",
category: "overrides",
input: z11.object({ collection: z11.string(), name: z11.string() }),
execute: async (client, input) => api(client).get(
`${collectionPath(input.collection)}/overrides/${enc(input.name)}`
)
},
{
name: "overrides.delete",
summary: "Delete a override",
category: "overrides",
input: z11.object({ collection: z11.string(), name: z11.string() }),
execute: async (client, input) => api(client).delete(
`${collectionPath(input.collection)}/overrides/${enc(input.name)}`
)
}
];
// ../core/src/operations/presets.ts
import { z as z12 } from "zod";
var presetsOperations = [
{
name: "presets.list",
summary: "List presets",
category: "presets",
input: z12.object({}),
execute: async (client) => api(client).get("/presets")
},
{
name: "presets.create",
summary: "Create or upsert a preset",
category: "presets",
input: z12.object({
name: z12.string(),
value: z12.record(z12.unknown())
}),
execute: async (client, input) => api(client).put(`/presets/${enc(input.name)}`, {
value: input.value
})
},
{
name: "presets.retrieve",
summary: "Retrieve a preset",
category: "presets",
input: z12.object({ name: z12.string() }),
execute: async (client, input) => api(client).get(`/presets/${enc(input.name)}`)
},
{
name: "presets.delete",
summary: "Delete a preset",
category: "presets",
input: z12.object({ name: z12.string() }),
execute: async (client, input) => api(client).delete(`/presets/${enc(input.name)}`)
}
];
// ../core/src/operations/search.ts
import { z as z13 } from "zod";
var searchParams2 = z13.record(z13.unknown());
var facetBySchema = z13.union([z13.string().min(1), z13.array(z13.string().min(1))]);
function withoutUndefined(params) {
return Object.fromEntries(
Object.entries(params).filter(([, value]) => value !== void 0)
);
}
var searchOperations = [
{
name: "search",
summary: "Search a collection",
category: "search",
input: z13.object({ collection: z13.string(), params: searchParams2 }),
execute: async (client, input) => api(client).get(
`${collectionPath(input.collection)}/documents/search`,
input.params
)
},
{
name: "multi_search",
summary: "Run a Typesense multi-search",
category: "search",
input: z13.object({
searches: z13.array(searchParams2),
commonParams: searchParams2.optional()
}),
execute: async (client, input) => api(client).post(
"/multi_search",
{ searches: input.searches },
input.commonParams
)
},
{
name: "search.facets",
summary: "Explore facet counts for a collection",
category: "search",
input: z13.object({
collection: z13.string(),
facetBy: facetBySchema,
q: z13.string().optional().default("*"),
queryBy: z13.string().optional(),
filterBy: z13.string().optional(),
maxFacetValues: z13.number().int().positive().optional(),
perPage: z13.number().int().nonnegative().optional().default(0)
}),
execute: async (client, input) => api(client).get(
`${collectionPath(input.collection)}/documents/search`,
withoutUndefined({
q: input.q,
query_by: input.queryBy,
filter_by: input.filterBy,
facet_by: Array.isArray(input.facetBy) ? input.facetBy.join(",") : input.facetBy,
max_facet_values: input.maxFacetValues,
per_page: input.perPage
})
)
},
{
name: "search.suggestions",
summary: "Fetch prefix search suggestions from a collection",
category: "search",
input: z13.object({
collection: z13.string(),
q: z13.string(),
queryBy: z13.string(),
filterBy: z13.string().optional(),
includeFields: z13.union([z13.string(), z13.array(z13.string())]).optional(),
limit: z13.number().int().positive().max(50).optional().default(5),
prefix: z13.boolean().optional().default(true)
}),
execute: async (client, input) => api(client).get(
`${collectionPath(input.collection)}/documents/search`,
withoutUndefined({
q: input.q,
query_by: input.queryBy,
filter_by: input.filterBy,
include_fields: Array.isArray(input.includeFields) ? input.includeFields.join(",") : input.includeFields,
per_page: input.limit,
prefix: input.prefix
})
)
}
];
// ../core/src/operations/stemming.ts
import { z as z14 } from "zod";
var wordMappingSchema = z14.object({
word: z14.string().min(1),
root: z14.string().min(1)
});
var stemmingOperations = [
{
name: "stemming.dictionaries.list",
summary: "List stemming dictionaries",
category: "stemming",
input: z14.object({}),
execute: async (client) => api(client).get("/stemming/dictionaries")
},
{
name: "stemming.dictionaries.retrieve",
summary: "Retrieve a stemming dictionary",
category: "stemming",
input: z14.object({ id: z14.string().min(1) }),
execute: async (client, input) => api(client).get(`/stemming/dictionaries/${enc(input.id)}`)
},
{
name: "stemming.dictionaries.import",
summary: "Import or replace a stemming dictionary from word mappings",
category: "stemming",
input: z14.object({
id: z14.string().min(1),
words: z14.union([z14.string().min(1), z14.array(wordMappingSchema).min(1)])
}),
execute: async (client, input) => api(client).post(
"/stemming/dictionaries/import",
Array.isArray(input.words) ? input.words.map(
(mapping) => JSON.stringify(mapping)
).join("\n") : input.words,
{ id: input.id }
)
}
];
// ../core/src/operations/stopwords.ts
import { z as z15 } from "zod";
var stopwordsOperations = [
{
name: "stopwords.list",
summary: "List stopwords",
category: "stopwords",
input: z15.object({}),
execute: async (client) => api(client).get("/stopwords")
},
{
name: "stopwords.create",
summary: "Create or upsert a stopword",
category: "stopwords",
input: z15.object({
name: z15.string(),
value: z15.record(z15.unknown())
}),
execute: async (client, input) => api(client).put(`/stopwords/${enc(input.name)}`, input.value)
},
{
name: "stopwords.retrieve",
summary: "Retrieve a stopword",
category: "stopwords",
input: z15.object({ name: z15.string() }),
execute: async (client, input) => api(client).get(`/stopwords/${enc(input.name)}`)
},
{
name: "stopwords.delete",
summary: "Delete a stopword",
category: "stopwords",
input: z15.object({ name: z15.string() }),
execute: async (client, input) => api(client).delete(`/stopwords/${enc(input.name)}`)
}
];
// ../core/src/operations/synonym-sets.ts
import { z as z16 } from "zod";
function base2(name) {
return name ? `/synonym_sets/${enc(name)}` : "/synonym_sets";
}
function itemPath2(name, id) {
const path = `${base2(name)}/items`;
return id ? `${path}/${enc(id)}` : path;
}
var synonymSetOperations = [
{
name: "synonym_sets.list",
summary: "List global synonym sets",
category: "synonyms",
input: z16.object({}),
execute: async (client) => api(client).get(base2())
},
{
name: "synonym_sets.create",
summary: "Create or upsert a global synonym set",
category: "synonyms",
input: z16.object({
name: z16.string(),
value: z16.object({
items: z16.array(z16.record(z16.unknown()))
})
}),
execute: async (client, input) => api(client).put(base2(input.name), input.value)
},
{
name: "synonym_sets.retrieve",
summary: "Retrieve a global synonym set",
category: "synonyms",
input: z16.object({ name: z16.string() }),
execute: async (client, input) => api(client).get(base2(input.name))
},
{
name: "synonym_sets.delete",
summary: "Delete a global synonym set",
category: "synonyms",
input: z16.object({ name: z16.string() }),
execute: async (client, input) => api(client).delete(base2(input.name))
},
{
name: "synonym_sets.items.list",
summary: "List items in a global synonym set",
category: "synonyms",
input: z16.object({ name: z16.string() }),
execute: async (client, input) => api(client).get(itemPath2(input.name))
},
{
name: "synonym_sets.items.create",
summary: "Create or upsert an item in a global synonym set",
category: "synonyms",
input: z16.object({
name: z16.string(),
id: z16.string(),
value: z16.record(z16.unknown())
}),
execute: async (client, input) => api(client).put(itemPath2(input.name, input.id), input.value)
},
{
name: "synonym_sets.items.retrieve",
summary: "Retrieve an item in a global synonym set",
category: "synonyms",
input: z16.object({ name: z16.string(), id: z16.string() }),
execute: async (client, input) => api(client).get(itemPath2(input.name, input.id))
},
{
name: "synonym_sets.items.delete",
summary: "Delete an item in a global synonym set",
category: "synonyms",
input: z16.object({ name: z16.string(), id: z16.string() }),
execute: async (client, input) => api(client).delete(itemPath2(input.name, input.id))
}
];
// ../core/src/operations/synonyms.ts
import { z as z17 } from "zod";
function isNotFound(error) {
if (typeof error !== "object" || error === null) return false;
const { httpStatus, status } = error;
return httpStatus === 404 || status === 404;
}
function synonymSetNames(collection) {
return Array.isArray(collection.synonym_sets) ? collection.synonym_sets.filter(
(name) => typeof name === "string"
) : [];
}
function globalSynonymGuidance(collection, sets) {
return [
`Collection-level synonyms are unavailable for ${collection}.`,
sets.length > 0 ? `This collection is linked to global synonym sets: ${sets.join(", ")}.` : "This Typesense version may use global synonym sets.",
"Use synonym_sets.list to inspect global synonym sets:",
"tsk synonym_sets.list --input '{}' --json"
].join("\n");
}
var synonymsOperations = [
{
name: "synonyms.list",
summary: "List synonyms",
category: "synonyms",
input: z17.object({ collection: z17.string() }),
execute: async (client, input) => {
const request = api(client);
try {
return await request.get(
`${collectionPath(input.collection)}/synonyms`
);
} catch (error) {
if (!isNotFound(error)) throw error;
const collection = await request.get(
collectionPath(input.collection)
);
throw new Error(
globalSynonymGuidance(input.collection, synonymSetNames(collection))
);
}
}
},
{
name: "synonyms.create",
summary: "Create or upsert a synonym",
category: "synonyms",
input: z17.object({
collection: z17.string(),
name: z17.string(),
value: z17.record(z17.unknown())
}),
execute: async (client, input) => api(client).put(
`${collectionPath(input.collection)}/synonyms/${enc(input.name)}`,
input.value
)
},
{
name: "synonyms.retrieve",
summary: "Retrieve a synonym",
category: "synonyms",
input: z17.object({ collection: z17.string(), name: z17.string() }),
execute: async (client, input) => api(client).get(
`${collectionPath(input.collection)}/synonyms/${enc(input.name)}`
)
},
{
name: "synonyms.delete",
summary: "Delete a synonym",
category: "synonyms",
input: z17.object({ collection: z17.string(), name: z17.string() }),
execute: async (client, input) => api(client).delete(
`${collectionPath(input.collection)}/synonyms/${enc(input.name)}`
)
}
];
// ../core/src/operations/system.ts
import { z as z18 } from "zod";
var systemOperations = [
{
name: "operations.schema_changes",
summary: "List in-progress collection schema changes",
category: "system",
input: z18.object({}),
execute: async (client) => api(client).get("/operations/schema_changes")
},
{
name: "operations.snapshot",
summary: "Create a point-in-time server snapshot",
category: "system",
input: z18.object({ snapshotPath: z18.string().min(1) }),
execute: async (client, input) => api(client).post("/operations/snapshot", void 0, {
snapshot_path: input.snapshotPath
})
},
{
name: "operations.vote",
summary: "Trigger leader re-election on a follower node",
category: "system",
input: z18.object({}),
execute: async (client) => api(client).post("/operations/vote")
},
{
name: "operations.cache.clear",
summary: "Clear cached search responses",
category: "system",
input: z18.object({}),
execute: async (client) => api(client).post("/operations/cache/clear")
},
{
name: "operations.db.compact",
summary: "Compact the on-disk Typesense database",
category: "system",
input: z18.object({}),
execute: async (client) => api(client).post("/operations/db/compact")
},
{
name: "operations.slow_requests.configure",
summary: "Configure the slow-request logging threshold",
category: "system",
input: z18.object({ thresholdMs: z18.number().int().min(-1) }),
execute: async (client, input) => api(client).post("/config", {
"log-slow-requests-time-ms": input.thresholdMs
})
},
{
name: "health",
summary: "Check Typesense cluster health",
category: "system",
input: z18.object({}),
execute: async (client) => api(client).get("/health")
},
{
name: "metrics",
summary: "Retrieve Typesense metrics",
category: "system",
input: z18.object({}),
execute: async (client) => api(client).get("/metrics.json")
},
{
name: "stats",
summary: "Retrieve Typesense stats",
category: "system",
input: z18.object({}),
execute: async (client) => api(client).get("/stats.json")
},
{
name: "debug",
summary: "Retrieve Typesense debug info",
category: "system",
input: z18.object({}),
execute: async (client) => api(client).get("/debug")
}
];
// ../core/src/operations/index.ts
var operations = [
...collectionOperations,
...documentOperations,
...searchOperations,
...aliasesOperations,
...synonymsOperations,
...synonymSetOperations,
...curationSetOperations,
...overridesOperations,
...keysOperations,
...analyticsOperations,
...presetsOperations,
...stopwordsOperations,
...stemmingOperations,
...conversationOperations,
...nlSearchModelOperations,
...apiOperations,
...systemOperations
];
// package.json
var package_default = {
name: "@typesensekit/mcp",
version: "1.2.0",
type: "module",
main: "dist/server.js",
types: "dist/server.d.ts",
bin: {
"typesensekit-mcp": "dist/cli.js",
"typesensekit-mcp-http": "dist/http.js"
},
exports: {
".": {
types: "./dist/server.d.ts",
import: "./dist/server.js"
}
},
files: [
"dist",
"README.md"
],
scripts: {
build: "tsup",
dev: "tsup --watch",
typecheck: "tsc --noEmit",
test: "vitest run"
},
dependencies: {
"@modelcontextprotocol/sdk": "^1.29.0",
zod: "^3.25.76",
typesense: "^3.0.6"
},
devDependencies: {
tsup: "^8.5.1",
"@types/node": "^22.10.0",
"@typesensekit/core": "workspace:*"
},
publishConfig: {
access: "public"
},
description: "MCP stdio server exposing Typesense API operations as tools.",
license: "MIT",
author: "Akshit Kr Nagpal",
repository: {
type: "git",
url: "git+https://github.com/akshitkrnagpal/typesensekit.git",
directory: "packages/mcp"
},
bugs: {
url: "https://github.com/akshitkrnagpal/typesensekit/issues"
},
homepage: "https://github.com/akshitkrnagpal/typesensekit#readme",
keywords: [
"typesense",
"cli",
"mcp",
"model-context-protocol",
"search"
]
};
// src/read-only.ts
var READ_ONLY_OPERATION_NAMES = /* @__PURE__ */ new Set([
"aliases.list",
"aliases.retrieve",
"analytics.events.list",
"analytics.rules.list",
"analytics.rules.retrieve",
"analytics.status",
"collections.list",
"collections.retrieve",
"collections.wait",
"conversations.history.retrieve",
"conversations.models.list",
"conversations.models.retrieve",
"curation_sets.items.list",
"curation_sets.items.retrieve",
"curation_sets.list",
"curation_sets.retrieve",
"debug",
"documents.export",
"documents.get",
"documents.get_many",
"documents.search",
"health",
"metrics",
"multi_search",
"nl_search_models.list",
"nl_search_models.retrieve",
"overrides.list",
"overrides.retrieve",
"operations.schema_changes",
"presets.list",
"presets.retrieve",
"search",
"search.facets",
"search.suggestions",
"stats",
"stemming.dictionaries.list",
"stemming.dictionaries.retrieve",
"stopwords.list",
"stopwords.retrieve",
"synonym_sets.items.list",
"synonym_sets.items.retrieve",
"synonym_sets.list",
"synonym_sets.retrieve",
"synonyms.list",
"synonyms.retrieve"
]);
function isReadOnlyOperation(operation) {
return READ_ONLY_OPERATION_NAMES.has(operation.name);
}
function filterMcpOperations(operations2, readOnly) {
return readOnly ? operations2.filter(isReadOnlyOperation) : operations2;
}
function readOnlyFromEnv(value) {
if (value === void 0) return true;
return !["0", "false", "no", "off"].includes(value.toLowerCase());
}
// src/env.ts
function readEnvConfig() {
return serverConfigSchema.parse({
url: process.env.TYPESENSE_URL,
apiKey: process.env.TYPESENSE_API_KEY,
connectionTimeoutSeconds: process.env.TYPESENSE_CONNECTION_TIMEOUT_SECONDS ? Number(process.env.TYPESENSE_CONNECTION_TIMEOUT_SECONDS) : void 0
});
}
function readMcpOptions() {
return {
readOnly: readOnlyFromEnv(process.env.TYPESENSEKIT_READ_ONLY)
};
}
// src/resources.ts
import {
ResourceTemplate
} from "@modelcontextprotocol/sdk/server/mcp.js";
function jsonContents(uri, value) {
return {
contents: [
{
uri,
mimeType: "application/json",
text: JSON.stringify(value, null, 2)
}
]
};
}
function textContents(uri, value) {
return {
contents: [{ uri, mimeType: "text/plain", text: value }]
};
}
function operationSummary(operation) {
return {
name: operation.name,
summary: operation.summary,
category: operation.category,
readOnly: READ_ONLY_OPERATION_NAMES.has(operation.name)
};
}
function operationManifest(activeOperations, readOnly) {
return {
readOnly,
operations: activeOperations.map(operationSummary)
};
}
function singleVariable(value, variable) {
if (typeof value === "string") return value;
throw new Error(`Missing ${variable} resource variable`);
}
async function readOperationResource(client, operationName, input, uri) {
const operation = operations.find(
(candidate) => candidate.name === operationName
);
if (!operation) throw new Error(`${operationName} not found`);
try {
const result = await operation.execute(
client,
operation.input.parse(input)
);
return jsonContents(uri, result);
} catch (error) {
return textContents(uri, formatTypesenseErrorMessage(error));
}
}
function registerTypesenseResources(server, client, activeOperations, readOnly) {
server.registerResource(
"typesensekit-operations",
"typesensekit://operations",
{
title: "TypesenseKit Operations",
description: "Operations currently exposed by this MCP server.",
mimeType: "application/json"
},
async (uri) => jsonContents(uri.href, operationManifest(activeOperations, readOnly))
);
server.registerResource(
"typesensekit-read-only-tools",
"typesensekit://read-only-tools",
{
title: "TypesenseKit Read-only Tools",
description: "Operation names included in default read-only MCP mode.",
mimeType: "application/json"
},
async (uri) => jsonContents(uri.href, {
operations: [...READ_ONLY_OPERATION_NAMES].sort()
})
);
server.registerResource(
"typesense-collection-schema",
new ResourceTemplate("typesense://collections/{collection}/schema", {
list: void 0
}),
{
title: "Typesense Collection Schema",
description: "Retrieve a Typesense collection schema by collection name.",
mimeType: "application/json"
},
async (uri, variables) => readOperationResource(
client,
"collections.retrieve",
{ collection: singleVariable(variables.collection, "collection") },
uri.href
)
);
server.registerResource(
"typesense-document",
new ResourceTemplate(
"typesense://collections/{collection}/documents/{id}",
{
list: void 0
}
),
{
title: "Typesense Document",
description: "Retrieve a Typesense document by collection and document id.",
mimeType: "application/json"
},
async (uri, variables) => readOperationResource(
client,
"documents.get",
{
collection: singleVariable(variables.collection, "collection"),
id: singleVariable(variables.id, "id")
},
uri.href
)
);
}
// src/server.ts
function toToolShape(input) {
const objectInput = input;
return objectInput.shape;
}
function createTypesenseMcpServer(options = {}) {
const server = new McpServer({
name: "typesensekit",
version: package_default.version
});
const client = createClient(readEnvConfig());
const mcpOptions = { ...readMcpOptions(), ...options };
const activeOperations = filterMcpOperations(operations, mcpOptions.readOnly);
registerTypesenseResources(
server,
client,
activeOperations,
mcpOptions.readOnly
);
for (const operation of activeOperations) {
server.registerTool(
operation.name,
{
title: operation.name,
description: operation.summary,
inputSchema: toToolShape(operation.input)
},
async (args) => {
try {
const input = operation.input.parse(args);
const result = await operation.execute(client, input);
return {
content: [
{
type: "text",
text: JSON.stringify(redactSecrets(result), null, 2)
}
]
};
} catch (error) {
const message = formatTypesenseErrorMessage(error);
return { isError: true, content: [{ type: "text", text: message }] };
}
}
);
}
return server;
}
export {
createTypesenseMcpServer
};
+1
-1
#!/usr/bin/env node
import {
createTypesenseMcpServer
} from "./chunk-MZXJKIHS.js";
} from "./chunk-KY4R2JA7.js";

@@ -6,0 +6,0 @@ // src/cli.ts

#!/usr/bin/env node
import {
createTypesenseMcpServer
} from "./chunk-MZXJKIHS.js";
} from "./chunk-KY4R2JA7.js";

@@ -6,0 +6,0 @@ // src/http.ts

@@ -74,3 +74,3 @@ // src/server.ts

const normalized = key.toLowerCase().replace(/[-_\s]/g, "");
return SECRET_KEYS.has(normalized) || normalized.endsWith("apikey");
return SECRET_KEYS.has(normalized) || normalized.endsWith("apikey") || normalized.endsWith("token") || normalized.endsWith("secret");
}

@@ -253,6 +253,21 @@ function isTypesenseApiKeyShape(value) {

category: "analytics",
input: z3.object({}),
execute: async (client) => api(client).get("/analytics/rules")
input: z3.object({ ruleTag: z3.string().optional() }),
execute: async (client, input) => api(client).get(
"/analytics/rules",
input.ruleTag ? { rule_tag: input.ruleTag } : void 0
)
},
{
name: "analytics.rules.create",
summary: "Create one or more analytics rules",
category: "analytics",
input: z3.object({
value: z3.union([
z3.record(z3.unknown()),
z3.array(z3.record(z3.unknown())).min(1)
])
}),
execute: async (client, input) => api(client).post("/analytics/rules", input.value)
},
{
name: "analytics.rules.upsert",

@@ -272,2 +287,9 @@ summary: "Create or update an analytics rule",

{
name: "analytics.rules.retrieve",
summary: "Retrieve an analytics rule",
category: "analytics",
input: z3.object({ name: z3.string() }),
execute: async (client, input) => api(client).get(`/analytics/rules/${enc(input.name)}`)
},
{
name: "analytics.events.create",

@@ -282,2 +304,31 @@ summary: "Create an analytics event",

execute: async (client, input) => api(client).post("/analytics/events", input)
},
{
name: "analytics.events.list",
summary: "Retrieve recent analytics events for a user and rule",
category: "analytics",
input: z3.object({
userId: z3.string().min(1),
name: z3.string().min(1),
limit: z3.number().int().positive().max(1e3)
}),
execute: async (client, input) => api(client).get("/analytics/events", {
user_id: input.userId,
name: input.name,
n: input.limit
})
},
{
name: "analytics.flush",
summary: "Flush in-memory analytics data to persistent storage",
category: "analytics",
input: z3.object({}),
execute: async (client) => api(client).post("/analytics/flush")
},
{
name: "analytics.status",
summary: "Retrieve analytics subsystem status",
category: "analytics",
input: z3.object({}),
execute: async (client) => api(client).get("/analytics/status")
}

@@ -717,13 +768,88 @@ ];

// ../core/src/operations/curation-sets.ts
import { z as z7 } from "zod";
function base(name) {
return name ? `/curation_sets/${enc(name)}` : "/curation_sets";
}
function itemPath(name, id) {
const path = `${base(name)}/items`;
return id ? `${path}/${enc(id)}` : path;
}
var curationSetOperations = [
{
name: "curation_sets.list",
summary: "List global curation sets",
category: "curations",
input: z7.object({}),
execute: async (client) => api(client).get(base())
},
{
name: "curation_sets.upsert",
summary: "Create or update a global curation set",
category: "curations",
input: z7.object({
name: z7.string(),
value: z7.object({ items: z7.array(z7.record(z7.unknown())) })
}),
execute: async (client, input) => api(client).put(base(input.name), input.value)
},
{
name: "curation_sets.retrieve",
summary: "Retrieve a global curation set",
category: "curations",
input: z7.object({ name: z7.string() }),
execute: async (client, input) => api(client).get(base(input.name))
},
{
name: "curation_sets.delete",
summary: "Delete a global curation set",
category: "curations",
input: z7.object({ name: z7.string() }),
execute: async (client, input) => api(client).delete(base(input.name))
},
{
name: "curation_sets.items.list",
summary: "List items in a global curation set",
category: "curations",
input: z7.object({ name: z7.string() }),
execute: async (client, input) => api(client).get(itemPath(input.name))
},
{
name: "curation_sets.items.upsert",
summary: "Create or update an item in a global curation set",
category: "curations",
input: z7.object({
name: z7.string(),
id: z7.string(),
value: z7.record(z7.unknown())
}),
execute: async (client, input) => api(client).put(itemPath(input.name, input.id), input.value)
},
{
name: "curation_sets.items.retrieve",
summary: "Retrieve an item in a global curation set",
category: "curations",
input: z7.object({ name: z7.string(), id: z7.string() }),
execute: async (client, input) => api(client).get(itemPath(input.name, input.id))
},
{
name: "curation_sets.items.delete",
summary: "Delete an item in a global curation set",
category: "curations",
input: z7.object({ name: z7.string(), id: z7.string() }),
execute: async (client, input) => api(client).delete(itemPath(input.name, input.id))
}
];
// ../core/src/operations/documents.ts
import { z as z7 } from "zod";
var documentSchema = z7.record(z7.unknown());
var idsSchema = z7.array(z7.string().min(1)).min(1);
var searchParams = z7.record(
z7.union([
z7.string(),
z7.number(),
z7.boolean(),
z7.array(z7.string()),
z7.array(z7.number())
import { z as z8 } from "zod";
var documentSchema = z8.record(z8.unknown());
var idsSchema = z8.array(z8.string().min(1)).min(1);
var searchParams = z8.record(
z8.union([
z8.string(),
z8.number(),
z8.boolean(),
z8.array(z8.string()),
z8.array(z8.number())
])

@@ -736,3 +862,3 @@ );

category: "documents",
input: z7.object({ collection: z7.string(), document: documentSchema }),
input: z8.object({ collection: z8.string(), document: documentSchema }),
execute: async (client, input) => api(client).post(

@@ -747,3 +873,3 @@ `${collectionPath(input.collection)}/documents`,

category: "documents",
input: z7.object({ collection: z7.string(), document: documentSchema }),
input: z8.object({ collection: z8.string(), document: documentSchema }),
execute: async (client, input) => api(client).post(

@@ -759,3 +885,3 @@ `${collectionPath(input.collection)}/documents`,

category: "documents",
input: z7.object({ collection: z7.string(), id: z7.string() }),
input: z8.object({ collection: z8.string(), id: z8.string() }),
execute: async (client, input) => api(client).get(

@@ -769,3 +895,3 @@ `${collectionPath(input.collection)}/documents/${enc(input.id)}`

category: "documents",
input: z7.object({ collection: z7.string(), ids: idsSchema }),
input: z8.object({ collection: z8.string(), ids: idsSchema }),
execute: async (client, input) => {

@@ -786,5 +912,5 @@ const request = api(client);

category: "documents",
input: z7.object({
collection: z7.string(),
id: z7.string(),
input: z8.object({
collection: z8.string(),
id: z8.string(),
document: documentSchema

@@ -801,3 +927,3 @@ }),

category: "documents",
input: z7.object({ collection: z7.string(), id: z7.string() }),
input: z8.object({ collection: z8.string(), id: z8.string() }),
execute: async (client, input) => api(client).delete(

@@ -811,6 +937,6 @@ `${collectionPath(input.collection)}/documents/${enc(input.id)}`

category: "documents",
input: z7.object({
collection: z7.string(),
documents: z7.union([z7.string(), z7.array(documentSchema)]),
action: z7.enum(["create", "upsert", "update", "emplace"]).optional()
input: z8.object({
collection: z8.string(),
documents: z8.union([z8.string(), z8.array(documentSchema)]),
action: z8.enum(["create", "upsert", "update", "emplace"]).optional()
}),

@@ -827,4 +953,4 @@ execute: async (client, input) => api(client).post(

category: "documents",
input: z7.object({
collection: z7.string(),
input: z8.object({
collection: z8.string(),
params: searchParams.optional()

@@ -841,3 +967,3 @@ }),

category: "documents",
input: z7.object({ collection: z7.string(), params: searchParams }),
input: z8.object({ collection: z8.string(), params: searchParams }),
execute: async (client, input) => api(client).get(

@@ -851,3 +977,3 @@ `${collectionPath(input.collection)}/documents/search`,

// ../core/src/operations/keys.ts
import { z as z8 } from "zod";
import { z as z9 } from "zod";
var keysOperations = [

@@ -858,3 +984,3 @@ {

category: "keys",
input: z8.object({}),
input: z9.object({}),
execute: async (client) => api(client).get("/keys")

@@ -866,3 +992,3 @@ },

category: "keys",
input: z8.object({ value: z8.record(z8.unknown()) }),
input: z9.object({ value: z9.record(z9.unknown()) }),
execute: async (client, input) => api(client).post("/keys", input.value)

@@ -874,3 +1000,3 @@ },

category: "keys",
input: z8.object({ id: z8.union([z8.string(), z8.number()]) }),
input: z9.object({ id: z9.union([z9.string(), z9.number()]) }),
execute: async (client, input) => api(client).get(`/keys/${enc(String(input.id))}`)

@@ -882,3 +1008,3 @@ },

category: "keys",
input: z8.object({ id: z8.union([z8.string(), z8.number()]) }),
input: z9.object({ id: z9.union([z9.string(), z9.number()]) }),
execute: async (client, input) => api(client).delete(`/keys/${enc(String(input.id))}`)

@@ -888,4 +1014,68 @@ }

// ../core/src/operations/nl-search-models.ts
import { z as z10 } from "zod";
var modelConfigSchema = z10.object({
id: z10.string().optional(),
model_name: z10.string().optional(),
api_key: z10.string().optional(),
api_url: z10.string().url().optional(),
max_bytes: z10.number().int().positive().optional(),
temperature: z10.number().optional(),
system_prompt: z10.string().optional(),
top_p: z10.number().optional(),
top_k: z10.number().int().optional(),
stop_sequences: z10.array(z10.string()).optional(),
api_version: z10.string().optional(),
project_id: z10.string().optional(),
access_token: z10.string().optional(),
refresh_token: z10.string().optional(),
client_id: z10.string().optional(),
client_secret: z10.string().optional(),
region: z10.string().optional(),
max_output_tokens: z10.number().int().positive().optional(),
account_id: z10.string().optional()
}).passthrough();
function modelPath(id) {
return `/nl_search_models/${enc(id)}`;
}
var nlSearchModelOperations = [
{
name: "nl_search_models.list",
summary: "List natural language search models",
category: "nl_search_models",
input: z10.object({}),
execute: async (client) => api(client).get("/nl_search_models")
},
{
name: "nl_search_models.create",
summary: "Create a natural language search model",
category: "nl_search_models",
input: z10.object({ value: modelConfigSchema }),
execute: async (client, input) => api(client).post("/nl_search_models", input.value)
},
{
name: "nl_search_models.retrieve",
summary: "Retrieve a natural language search model",
category: "nl_search_models",
input: z10.object({ id: z10.string().min(1) }),
execute: async (client, input) => api(client).get(modelPath(input.id))
},
{
name: "nl_search_models.update",
summary: "Update a natural language search model",
category: "nl_search_models",
input: z10.object({ id: z10.string().min(1), value: modelConfigSchema }),
execute: async (client, input) => api(client).put(modelPath(input.id), input.value)
},
{
name: "nl_search_models.delete",
summary: "Delete a natural language search model",
category: "nl_search_models",
input: z10.object({ id: z10.string().min(1) }),
execute: async (client, input) => api(client).delete(modelPath(input.id))
}
];
// ../core/src/operations/overrides.ts
import { z as z9 } from "zod";
import { z as z11 } from "zod";
var overridesOperations = [

@@ -896,3 +1086,3 @@ {

category: "overrides",
input: z9.object({ collection: z9.string() }),
input: z11.object({ collection: z11.string() }),
execute: async (client, input) => api(client).get(`${collectionPath(input.collection)}/overrides`)

@@ -904,6 +1094,6 @@ },

category: "overrides",
input: z9.object({
collection: z9.string(),
name: z9.string(),
value: z9.record(z9.unknown())
input: z11.object({
collection: z11.string(),
name: z11.string(),
value: z11.record(z11.unknown())
}),

@@ -919,3 +1109,3 @@ execute: async (client, input) => api(client).put(

category: "overrides",
input: z9.object({ collection: z9.string(), name: z9.string() }),
input: z11.object({ collection: z11.string(), name: z11.string() }),
execute: async (client, input) => api(client).get(

@@ -929,3 +1119,3 @@ `${collectionPath(input.collection)}/overrides/${enc(input.name)}`

category: "overrides",
input: z9.object({ collection: z9.string(), name: z9.string() }),
input: z11.object({ collection: z11.string(), name: z11.string() }),
execute: async (client, input) => api(client).delete(

@@ -938,3 +1128,3 @@ `${collectionPath(input.collection)}/overrides/${enc(input.name)}`

// ../core/src/operations/presets.ts
import { z as z10 } from "zod";
import { z as z12 } from "zod";
var presetsOperations = [

@@ -945,3 +1135,3 @@ {

category: "presets",
input: z10.object({}),
input: z12.object({}),
execute: async (client) => api(client).get("/presets")

@@ -953,5 +1143,5 @@ },

category: "presets",
input: z10.object({
name: z10.string(),
value: z10.record(z10.unknown())
input: z12.object({
name: z12.string(),
value: z12.record(z12.unknown())
}),

@@ -966,3 +1156,3 @@ execute: async (client, input) => api(client).put(`/presets/${enc(input.name)}`, {

category: "presets",
input: z10.object({ name: z10.string() }),
input: z12.object({ name: z12.string() }),
execute: async (client, input) => api(client).get(`/presets/${enc(input.name)}`)

@@ -974,3 +1164,3 @@ },

category: "presets",
input: z10.object({ name: z10.string() }),
input: z12.object({ name: z12.string() }),
execute: async (client, input) => api(client).delete(`/presets/${enc(input.name)}`)

@@ -981,5 +1171,5 @@ }

// ../core/src/operations/search.ts
import { z as z11 } from "zod";
var searchParams2 = z11.record(z11.unknown());
var facetBySchema = z11.union([z11.string().min(1), z11.array(z11.string().min(1))]);
import { z as z13 } from "zod";
var searchParams2 = z13.record(z13.unknown());
var facetBySchema = z13.union([z13.string().min(1), z13.array(z13.string().min(1))]);
function withoutUndefined(params) {

@@ -995,3 +1185,3 @@ return Object.fromEntries(

category: "search",
input: z11.object({ collection: z11.string(), params: searchParams2 }),
input: z13.object({ collection: z13.string(), params: searchParams2 }),
execute: async (client, input) => api(client).get(

@@ -1006,4 +1196,4 @@ `${collectionPath(input.collection)}/documents/search`,

category: "search",
input: z11.object({
searches: z11.array(searchParams2),
input: z13.object({
searches: z13.array(searchParams2),
commonParams: searchParams2.optional()

@@ -1021,10 +1211,10 @@ }),

category: "search",
input: z11.object({
collection: z11.string(),
input: z13.object({
collection: z13.string(),
facetBy: facetBySchema,
q: z11.string().optional().default("*"),
queryBy: z11.string().optional(),
filterBy: z11.string().optional(),
maxFacetValues: z11.number().int().positive().optional(),
perPage: z11.number().int().nonnegative().optional().default(0)
q: z13.string().optional().default("*"),
queryBy: z13.string().optional(),
filterBy: z13.string().optional(),
maxFacetValues: z13.number().int().positive().optional(),
perPage: z13.number().int().nonnegative().optional().default(0)
}),

@@ -1047,10 +1237,10 @@ execute: async (client, input) => api(client).get(

category: "search",
input: z11.object({
collection: z11.string(),
q: z11.string(),
queryBy: z11.string(),
filterBy: z11.string().optional(),
includeFields: z11.union([z11.string(), z11.array(z11.string())]).optional(),
limit: z11.number().int().positive().max(50).optional().default(5),
prefix: z11.boolean().optional().default(true)
input: z13.object({
collection: z13.string(),
q: z13.string(),
queryBy: z13.string(),
filterBy: z13.string().optional(),
includeFields: z13.union([z13.string(), z13.array(z13.string())]).optional(),
limit: z13.number().int().positive().max(50).optional().default(5),
prefix: z13.boolean().optional().default(true)
}),

@@ -1071,4 +1261,43 @@ execute: async (client, input) => api(client).get(

// ../core/src/operations/stemming.ts
import { z as z14 } from "zod";
var wordMappingSchema = z14.object({
word: z14.string().min(1),
root: z14.string().min(1)
});
var stemmingOperations = [
{
name: "stemming.dictionaries.list",
summary: "List stemming dictionaries",
category: "stemming",
input: z14.object({}),
execute: async (client) => api(client).get("/stemming/dictionaries")
},
{
name: "stemming.dictionaries.retrieve",
summary: "Retrieve a stemming dictionary",
category: "stemming",
input: z14.object({ id: z14.string().min(1) }),
execute: async (client, input) => api(client).get(`/stemming/dictionaries/${enc(input.id)}`)
},
{
name: "stemming.dictionaries.import",
summary: "Import or replace a stemming dictionary from word mappings",
category: "stemming",
input: z14.object({
id: z14.string().min(1),
words: z14.union([z14.string().min(1), z14.array(wordMappingSchema).min(1)])
}),
execute: async (client, input) => api(client).post(
"/stemming/dictionaries/import",
Array.isArray(input.words) ? input.words.map(
(mapping) => JSON.stringify(mapping)
).join("\n") : input.words,
{ id: input.id }
)
}
];
// ../core/src/operations/stopwords.ts
import { z as z12 } from "zod";
import { z as z15 } from "zod";
var stopwordsOperations = [

@@ -1079,3 +1308,3 @@ {

category: "stopwords",
input: z12.object({}),
input: z15.object({}),
execute: async (client) => api(client).get("/stopwords")

@@ -1087,5 +1316,5 @@ },

category: "stopwords",
input: z12.object({
name: z12.string(),
value: z12.record(z12.unknown())
input: z15.object({
name: z15.string(),
value: z15.record(z15.unknown())
}),

@@ -1098,3 +1327,3 @@ execute: async (client, input) => api(client).put(`/stopwords/${enc(input.name)}`, input.value)

category: "stopwords",
input: z12.object({ name: z12.string() }),
input: z15.object({ name: z15.string() }),
execute: async (client, input) => api(client).get(`/stopwords/${enc(input.name)}`)

@@ -1106,3 +1335,3 @@ },

category: "stopwords",
input: z12.object({ name: z12.string() }),
input: z15.object({ name: z15.string() }),
execute: async (client, input) => api(client).delete(`/stopwords/${enc(input.name)}`)

@@ -1113,8 +1342,8 @@ }

// ../core/src/operations/synonym-sets.ts
import { z as z13 } from "zod";
function base(name) {
import { z as z16 } from "zod";
function base2(name) {
return name ? `/synonym_sets/${enc(name)}` : "/synonym_sets";
}
function itemPath(name, id) {
const path = `${base(name)}/items`;
function itemPath2(name, id) {
const path = `${base2(name)}/items`;
return id ? `${path}/${enc(id)}` : path;

@@ -1127,4 +1356,4 @@ }

category: "synonyms",
input: z13.object({}),
execute: async (client) => api(client).get(base())
input: z16.object({}),
execute: async (client) => api(client).get(base2())
},

@@ -1135,9 +1364,9 @@ {

category: "synonyms",
input: z13.object({
name: z13.string(),
value: z13.object({
items: z13.array(z13.record(z13.unknown()))
input: z16.object({
name: z16.string(),
value: z16.object({
items: z16.array(z16.record(z16.unknown()))
})
}),
execute: async (client, input) => api(client).put(base(input.name), input.value)
execute: async (client, input) => api(client).put(base2(input.name), input.value)
},

@@ -1148,4 +1377,4 @@ {

category: "synonyms",
input: z13.object({ name: z13.string() }),
execute: async (client, input) => api(client).get(base(input.name))
input: z16.object({ name: z16.string() }),
execute: async (client, input) => api(client).get(base2(input.name))
},

@@ -1156,4 +1385,4 @@ {

category: "synonyms",
input: z13.object({ name: z13.string() }),
execute: async (client, input) => api(client).delete(base(input.name))
input: z16.object({ name: z16.string() }),
execute: async (client, input) => api(client).delete(base2(input.name))
},

@@ -1164,4 +1393,4 @@ {

category: "synonyms",
input: z13.object({ name: z13.string() }),
execute: async (client, input) => api(client).get(itemPath(input.name))
input: z16.object({ name: z16.string() }),
execute: async (client, input) => api(client).get(itemPath2(input.name))
},

@@ -1172,8 +1401,8 @@ {

category: "synonyms",
input: z13.object({
name: z13.string(),
id: z13.string(),
value: z13.record(z13.unknown())
input: z16.object({
name: z16.string(),
id: z16.string(),
value: z16.record(z16.unknown())
}),
execute: async (client, input) => api(client).put(itemPath(input.name, input.id), input.value)
execute: async (client, input) => api(client).put(itemPath2(input.name, input.id), input.value)
},

@@ -1184,4 +1413,4 @@ {

category: "synonyms",
input: z13.object({ name: z13.string(), id: z13.string() }),
execute: async (client, input) => api(client).get(itemPath(input.name, input.id))
input: z16.object({ name: z16.string(), id: z16.string() }),
execute: async (client, input) => api(client).get(itemPath2(input.name, input.id))
},

@@ -1192,4 +1421,4 @@ {

category: "synonyms",
input: z13.object({ name: z13.string(), id: z13.string() }),
execute: async (client, input) => api(client).delete(itemPath(input.name, input.id))
input: z16.object({ name: z16.string(), id: z16.string() }),
execute: async (client, input) => api(client).delete(itemPath2(input.name, input.id))
}

@@ -1199,3 +1428,3 @@ ];

// ../core/src/operations/synonyms.ts
import { z as z14 } from "zod";
import { z as z17 } from "zod";
function isNotFound(error) {

@@ -1224,3 +1453,3 @@ if (typeof error !== "object" || error === null) return false;

category: "synonyms",
input: z14.object({ collection: z14.string() }),
input: z17.object({ collection: z17.string() }),
execute: async (client, input) => {

@@ -1247,6 +1476,6 @@ const request = api(client);

category: "synonyms",
input: z14.object({
collection: z14.string(),
name: z14.string(),
value: z14.record(z14.unknown())
input: z17.object({
collection: z17.string(),
name: z17.string(),
value: z17.record(z17.unknown())
}),

@@ -1262,3 +1491,3 @@ execute: async (client, input) => api(client).put(

category: "synonyms",
input: z14.object({ collection: z14.string(), name: z14.string() }),
input: z17.object({ collection: z17.string(), name: z17.string() }),
execute: async (client, input) => api(client).get(

@@ -1272,3 +1501,3 @@ `${collectionPath(input.collection)}/synonyms/${enc(input.name)}`

category: "synonyms",
input: z14.object({ collection: z14.string(), name: z14.string() }),
input: z17.object({ collection: z17.string(), name: z17.string() }),
execute: async (client, input) => api(client).delete(

@@ -1281,9 +1510,55 @@ `${collectionPath(input.collection)}/synonyms/${enc(input.name)}`

// ../core/src/operations/system.ts
import { z as z15 } from "zod";
import { z as z18 } from "zod";
var systemOperations = [
{
name: "operations.schema_changes",
summary: "List in-progress collection schema changes",
category: "system",
input: z18.object({}),
execute: async (client) => api(client).get("/operations/schema_changes")
},
{
name: "operations.snapshot",
summary: "Create a point-in-time server snapshot",
category: "system",
input: z18.object({ snapshotPath: z18.string().min(1) }),
execute: async (client, input) => api(client).post("/operations/snapshot", void 0, {
snapshot_path: input.snapshotPath
})
},
{
name: "operations.vote",
summary: "Trigger leader re-election on a follower node",
category: "system",
input: z18.object({}),
execute: async (client) => api(client).post("/operations/vote")
},
{
name: "operations.cache.clear",
summary: "Clear cached search responses",
category: "system",
input: z18.object({}),
execute: async (client) => api(client).post("/operations/cache/clear")
},
{
name: "operations.db.compact",
summary: "Compact the on-disk Typesense database",
category: "system",
input: z18.object({}),
execute: async (client) => api(client).post("/operations/db/compact")
},
{
name: "operations.slow_requests.configure",
summary: "Configure the slow-request logging threshold",
category: "system",
input: z18.object({ thresholdMs: z18.number().int().min(-1) }),
execute: async (client, input) => api(client).post("/config", {
"log-slow-requests-time-ms": input.thresholdMs
})
},
{
name: "health",
summary: "Check Typesense cluster health",
category: "system",
input: z15.object({}),
input: z18.object({}),
execute: async (client) => api(client).get("/health")

@@ -1295,3 +1570,3 @@ },

category: "system",
input: z15.object({}),
input: z18.object({}),
execute: async (client) => api(client).get("/metrics.json")

@@ -1303,3 +1578,3 @@ },

category: "system",
input: z15.object({}),
input: z18.object({}),
execute: async (client) => api(client).get("/stats.json")

@@ -1311,3 +1586,3 @@ },

category: "system",
input: z15.object({}),
input: z18.object({}),
execute: async (client) => api(client).get("/debug")

@@ -1325,2 +1600,3 @@ }

...synonymSetOperations,
...curationSetOperations,
...overridesOperations,

@@ -1331,3 +1607,5 @@ ...keysOperations,

...stopwordsOperations,
...stemmingOperations,
...conversationOperations,
...nlSearchModelOperations,
...apiOperations,

@@ -1340,3 +1618,3 @@ ...systemOperations

name: "@typesensekit/mcp",
version: "1.1.3",
version: "1.2.0",
type: "module",

@@ -1403,2 +1681,6 @@ main: "dist/server.js",

"aliases.retrieve",
"analytics.events.list",
"analytics.rules.list",
"analytics.rules.retrieve",
"analytics.status",
"collections.list",

@@ -1410,2 +1692,6 @@ "collections.retrieve",

"conversations.models.retrieve",
"curation_sets.items.list",
"curation_sets.items.retrieve",
"curation_sets.list",
"curation_sets.retrieve",
"debug",

@@ -1419,4 +1705,7 @@ "documents.export",

"multi_search",
"nl_search_models.list",
"nl_search_models.retrieve",
"overrides.list",
"overrides.retrieve",
"operations.schema_changes",
"presets.list",

@@ -1428,2 +1717,4 @@ "presets.retrieve",

"stats",
"stemming.dictionaries.list",
"stemming.dictionaries.retrieve",
"stopwords.list",

@@ -1430,0 +1721,0 @@ "stopwords.retrieve",

{
"name": "@typesensekit/mcp",
"version": "1.1.3",
"version": "1.2.0",
"type": "module",

@@ -5,0 +5,0 @@ "main": "dist/server.js",

#!/usr/bin/env node
// src/server.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
// ../core/src/client.ts
import Typesense from "typesense";
// ../core/src/config.ts
import { z } from "zod";
var nodeConfigSchema = z.object({
host: z.string().min(1),
port: z.number().int().positive().optional(),
protocol: z.enum(["http", "https"]).optional(),
path: z.string().optional()
});
var serverConfigSchema = z.object({
url: z.string().url(),
apiKey: z.string().min(1),
connectionTimeoutSeconds: z.number().positive().optional(),
nearestNode: nodeConfigSchema.optional(),
numRetries: z.number().int().nonnegative().optional(),
retryIntervalSeconds: z.number().positive().optional(),
healthcheckIntervalSeconds: z.number().positive().optional()
});
// ../core/src/client.ts
function nodeFromUrl(url) {
const parsed = new URL(url);
const node = {
host: parsed.hostname,
port: parsed.port ? Number(parsed.port) : parsed.protocol === "https:" ? 443 : 80,
protocol: parsed.protocol.replace(":", "")
};
if (parsed.pathname !== "/") node.path = parsed.pathname;
return node;
}
function normalizeNearestNode(node) {
if (!node) return void 0;
return {
host: node.host,
port: node.port ?? (node.protocol === "http" ? 80 : 443),
protocol: node.protocol ?? "https",
path: node.path
};
}
function createClient(config) {
const parsed = serverConfigSchema.parse(config);
const clientConfig = {
nodes: [nodeFromUrl(parsed.url)],
apiKey: parsed.apiKey,
connectionTimeoutSeconds: parsed.connectionTimeoutSeconds,
nearestNode: normalizeNearestNode(parsed.nearestNode),
numRetries: parsed.numRetries,
retryIntervalSeconds: parsed.retryIntervalSeconds,
healthcheckIntervalSeconds: parsed.healthcheckIntervalSeconds
};
return new Typesense.Client(clientConfig);
}
// ../core/src/redaction.ts
var REDACTED = "[REDACTED]";
var CIRCULAR = "[Circular]";
var SECRET_KEYS = /* @__PURE__ */ new Set([
"api_key",
"apikey",
"authorization",
"cookie",
"secret",
"setcookie",
"token",
"xtypesenseapikey"
]);
function shouldRedactKey(key) {
const normalized = key.toLowerCase().replace(/[-_\s]/g, "");
return SECRET_KEYS.has(normalized) || normalized.endsWith("apikey");
}
function isTypesenseApiKeyShape(value) {
return Array.isArray(value.actions) && Array.isArray(value.collections);
}
function redactText(value) {
return value.replace(
/(["']?authorization["']?\s*[:=]\s*)(["']?)(?:Bearer|Basic)?\s*[-A-Za-z0-9._~+/=]+(["']?)/gi,
(_match, prefix, openQuote) => `${prefix}${openQuote}${REDACTED}${openQuote}`
).replace(
/(["']?(?:x-typesense-api-key|api[_-]?key|apikey|token|secret|cookie|set-cookie)["']?\s*[:=]\s*)(["']?)([^"',\s}\]]+)(["']?)/gi,
(_match, prefix, openQuote, _secret) => `${prefix}${openQuote}${REDACTED}${openQuote}`
).replace(/\b(Bearer|Basic)\s+[A-Za-z0-9._~+/=-]+/gi, `$1 ${REDACTED}`);
}
function redactError(error, seen) {
const output = {};
for (const key of Object.getOwnPropertyNames(error)) {
output[key] = redactValue(
error[key],
seen
);
}
if (!("name" in output)) output.name = error.name;
if (!("message" in output)) output.message = redactText(error.message);
return output;
}
function redactValue(value, seen) {
if (typeof value === "string") return redactText(value);
if (Array.isArray(value)) {
return value.map((item) => redactValue(item, seen));
}
if (typeof value !== "object" || value === null) {
return value;
}
if (seen.has(value)) return CIRCULAR;
seen.add(value);
if (value instanceof Error) {
return redactError(value, seen);
}
const record = value;
return Object.fromEntries(
Object.entries(record).map(([key, child]) => [
key,
shouldRedactKey(key) || key === "value" && isTypesenseApiKeyShape(record) ? REDACTED : redactValue(child, seen)
])
);
}
function redactSecrets(value) {
return redactValue(value, /* @__PURE__ */ new WeakSet());
}
// ../core/src/errors.ts
var NETWORK_ERROR_CODES = /* @__PURE__ */ new Set([
"EAI_AGAIN",
"ECONNABORTED",
"ECONNREFUSED",
"ECONNRESET",
"ENOTFOUND",
"ETIMEDOUT"
]);
function readErrorLike(error) {
return typeof error === "object" && error !== null ? error : {};
}
function findNetworkErrorCode(error) {
const errorLike = readErrorLike(error);
if (typeof errorLike.code === "string" && NETWORK_ERROR_CODES.has(errorLike.code.toUpperCase())) {
return errorLike.code.toUpperCase();
}
if (typeof errorLike.message === "string") {
const match = errorLike.message.match(
/\b(EAI_AGAIN|ECONNABORTED|ECONNREFUSED|ECONNRESET|ENOTFOUND|ETIMEDOUT)\b/i
);
if (match?.[1]) return match[1].toUpperCase();
}
return errorLike.cause === void 0 ? void 0 : findNetworkErrorCode(errorLike.cause);
}
function endpointLabel(error) {
const errorLike = readErrorLike(error);
const host = typeof errorLike.hostname === "string" ? errorLike.hostname : typeof errorLike.host === "string" ? errorLike.host : typeof errorLike.address === "string" ? errorLike.address : void 0;
const port = typeof errorLike.port === "number" || typeof errorLike.port === "string" ? String(errorLike.port) : void 0;
if (host && port) return `${host}:${port}`;
if (host) return host;
return errorLike.cause === void 0 ? void 0 : endpointLabel(errorLike.cause);
}
function conciseNetworkErrorMessage(error) {
const code = findNetworkErrorCode(error);
if (!code) return void 0;
const endpoint = endpointLabel(error);
return `Request failed: ${code}${endpoint ? ` ${endpoint}` : ""}`;
}
function normalizeTypesenseError(error) {
if (error instanceof Error) {
const errorLike = error;
const status = errorLike.httpStatus ?? errorLike.status;
return {
code: typeof status === "number" ? String(status) : error.name || "TypesenseError",
message: redactText(error.message),
details: redactSecrets(error)
};
}
if (typeof error === "object" && error !== null) {
const errorLike = error;
return {
code: typeof errorLike.status === "number" ? String(errorLike.status) : "TypesenseError",
message: typeof errorLike.message === "string" ? redactText(errorLike.message) : "Unknown Typesense error",
details: redactSecrets(error)
};
}
return { code: "TypesenseError", message: redactText(String(error)) };
}
function formatTypesenseErrorMessage(error, options = {}) {
const normalized = normalizeTypesenseError(error);
const message = conciseNetworkErrorMessage(error) ?? normalized.message;
if (!options.debug) return message;
return [
message,
"",
"Debug details:",
JSON.stringify(normalized.details ?? normalized, null, 2)
].join("\n");
}
// ../core/src/operations/aliases.ts
import { z as z2 } from "zod";
// ../core/src/operations/http.ts
function api(client) {
return client.apiCall;
}
function enc(value) {
return encodeURIComponent(value);
}
function collectionPath(collection) {
return `/collections/${enc(collection)}`;
}
// ../core/src/operations/aliases.ts
var aliasesOperations = [
{
name: "aliases.list",
summary: "List aliases",
category: "aliases",
input: z2.object({}),
execute: async (client) => api(client).get("/aliases")
},
{
name: "aliases.create",
summary: "Create or upsert an alias",
category: "aliases",
input: z2.object({
name: z2.string(),
value: z2.object({ collection_name: z2.string() })
}),
execute: async (client, input) => api(client).put(`/aliases/${enc(input.name)}`, input.value)
},
{
name: "aliases.retrieve",
summary: "Retrieve an alias",
category: "aliases",
input: z2.object({ name: z2.string() }),
execute: async (client, input) => api(client).get(`/aliases/${enc(input.name)}`)
},
{
name: "aliases.delete",
summary: "Delete an alias",
category: "aliases",
input: z2.object({ name: z2.string() }),
execute: async (client, input) => api(client).delete(`/aliases/${enc(input.name)}`)
}
];
// ../core/src/operations/analytics.ts
import { z as z3 } from "zod";
var analyticsOperations = [
{
name: "analytics.rules.list",
summary: "List analytics rules",
category: "analytics",
input: z3.object({}),
execute: async (client) => api(client).get("/analytics/rules")
},
{
name: "analytics.rules.upsert",
summary: "Create or update an analytics rule",
category: "analytics",
input: z3.object({ name: z3.string(), value: z3.record(z3.unknown()) }),
execute: async (client, input) => api(client).put(`/analytics/rules/${enc(input.name)}`, input.value)
},
{
name: "analytics.rules.delete",
summary: "Delete an analytics rule",
category: "analytics",
input: z3.object({ name: z3.string() }),
execute: async (client, input) => api(client).delete(`/analytics/rules/${enc(input.name)}`)
},
{
name: "analytics.events.create",
summary: "Create an analytics event",
category: "analytics",
input: z3.object({
type: z3.string(),
name: z3.string(),
data: z3.record(z3.unknown())
}),
execute: async (client, input) => api(client).post("/analytics/events", input)
}
];
// ../core/src/operations/api.ts
import { z as z4 } from "zod";
var methodSchema = z4.enum(["get", "post", "put", "patch", "delete"]);
var normalizedMethodSchema = z4.preprocess(
(value) => typeof value === "string" ? value.toLowerCase() : value,
methodSchema
);
var apiOperations = [
{
name: "api.call",
summary: "Call any Typesense API endpoint not yet covered by a first-class operation",
category: "api",
input: z4.object({
method: normalizedMethodSchema,
path: z4.string().startsWith("/"),
params: z4.record(z4.unknown()).optional(),
body: z4.unknown().optional()
}),
execute: async (client, input) => {
const request = api(client);
if (input.method === "get") return request.get(input.path, input.params);
if (input.method === "delete")
return request.delete(input.path, input.params);
if (input.method === "post")
return request.post(input.path, input.body, input.params);
if (input.method === "put")
return request.put(input.path, input.body, input.params);
return request.patch(input.path, input.body, input.params);
}
}
];
// ../core/src/operations/collections.ts
import { z as z5 } from "zod";
var DEFAULT_WAIT_TIMEOUT_MS = 3e4;
var DEFAULT_WAIT_INTERVAL_MS = 1e3;
var baseFieldSchema = z5.object({
name: z5.string(),
facet: z5.boolean().optional(),
index: z5.boolean().optional(),
optional: z5.boolean().optional(),
sort: z5.boolean().optional(),
locale: z5.string().optional(),
infix: z5.boolean().optional(),
stem: z5.boolean().optional()
}).passthrough();
var createFieldSchema = baseFieldSchema.extend({
type: z5.string()
});
var patchFieldSchema = baseFieldSchema.extend({
type: z5.string().optional(),
drop: z5.boolean().optional()
});
var fieldLifecycleInputSchema = z5.object({
collection: z5.string(),
field: z5.string().optional(),
yes: z5.boolean().optional(),
numDim: z5.coerce.number().int().positive().optional(),
vecDist: z5.string().optional(),
hnswM: z5.coerce.number().int().positive().optional(),
hnswEfConstruction: z5.coerce.number().int().positive().optional(),
embedFrom: z5.union([z5.string(), z5.array(z5.string())]).optional(),
embedModel: z5.string().optional(),
embedApiKey: z5.string().optional(),
timeoutMs: z5.coerce.number().int().nonnegative().default(DEFAULT_WAIT_TIMEOUT_MS),
intervalMs: z5.coerce.number().int().nonnegative().default(DEFAULT_WAIT_INTERVAL_MS)
}).passthrough();
var waitInputSchema = z5.object({
collection: z5.string(),
fieldPresent: z5.string().optional(),
fieldMissing: z5.string().optional(),
fieldEmbedFrom: z5.string().optional(),
timeoutMs: z5.coerce.number().int().nonnegative().default(DEFAULT_WAIT_TIMEOUT_MS),
intervalMs: z5.coerce.number().int().nonnegative().default(DEFAULT_WAIT_INTERVAL_MS)
});
var FIELD_LIFECYCLE_KEYS = /* @__PURE__ */ new Set([
"collection",
"field",
"yes",
"numDim",
"vecDist",
"hnswM",
"hnswEfConstruction",
"embedFrom",
"embedModel",
"embedApiKey",
"timeoutMs",
"intervalMs"
]);
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
function isObject(value) {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
function requestedFieldName(input) {
const name = input.field ?? input.name;
if (typeof name !== "string" || !name) {
throw new Error("A field name is required. Pass --field or include name.");
}
return name;
}
function requiresConfirmation(collection) {
return /^production(?:__|-|$)/i.test(collection) || /^prod(?:__|-|$)/i.test(collection);
}
function assertDestructiveConfirmed(input) {
if (requiresConfirmation(input.collection) && !input.yes) {
throw new Error(
`Refusing to modify production collection ${input.collection} without --yes.`
);
}
}
function findField(collection, fieldName) {
return collection.fields?.find((field) => {
if (!isObject(field)) return false;
return field.name === fieldName;
});
}
async function retrieveCollection(client, collection) {
return api(client).get(collectionPath(collection));
}
function buildFieldDefinition(input) {
const field = Object.fromEntries(
Object.entries(input).filter(
([key, value]) => value !== void 0 && !FIELD_LIFECYCLE_KEYS.has(key)
)
);
field.name = requestedFieldName(input);
if (input.numDim !== void 0) field.num_dim = input.numDim;
if (input.vecDist !== void 0) field.vec_dist = input.vecDist;
if (input.hnswM !== void 0 || input.hnswEfConstruction !== void 0) {
const hnswParams = isObject(field.hnsw_params) ? field.hnsw_params : {};
if (input.hnswM !== void 0) hnswParams.M = input.hnswM;
if (input.hnswEfConstruction !== void 0) {
hnswParams.ef_construction = input.hnswEfConstruction;
}
field.hnsw_params = hnswParams;
}
if (input.embedFrom !== void 0 || input.embedModel !== void 0 || input.embedApiKey !== void 0) {
const embed = isObject(field.embed) ? field.embed : {};
const modelConfig = isObject(embed.model_config) ? embed.model_config : {};
if (input.embedFrom !== void 0) {
embed.from = Array.isArray(input.embedFrom) ? input.embedFrom : [input.embedFrom];
}
if (input.embedModel !== void 0)
modelConfig.model_name = input.embedModel;
if (input.embedApiKey !== void 0)
modelConfig.api_key = input.embedApiKey;
embed.model_config = modelConfig;
field.embed = embed;
}
if (typeof field.type !== "string" || !field.type) {
throw new Error("A field type is required. Pass --type or include type.");
}
return field;
}
function parseEmbedFromCondition(condition) {
const separator = condition.indexOf(":");
if (separator === -1) {
throw new Error("--field-embed-from must use FIELD:SOURCE format.");
}
return {
fieldName: condition.slice(0, separator),
source: condition.slice(separator + 1)
};
}
function isTransientCollectionUpdateError(error) {
const message = error instanceof Error ? error.message : String(error);
return /another collection update operation is in progress/i.test(message) || /timeout of \d+ms exceeded/i.test(message) || /econnaborted/i.test(message);
}
function evaluateWaitCondition(collection, input) {
const conditions = [
input.fieldPresent,
input.fieldMissing,
input.fieldEmbedFrom
].filter(Boolean);
if (conditions.length !== 1) {
throw new Error(
"Pass exactly one wait condition: --field-present, --field-missing, or --field-embed-from."
);
}
if (input.fieldPresent) {
return {
ok: Boolean(findField(collection, input.fieldPresent)),
condition: "field-present",
field: input.fieldPresent
};
}
if (input.fieldMissing) {
return {
ok: !findField(collection, input.fieldMissing),
condition: "field-missing",
field: input.fieldMissing
};
}
const { fieldName, source } = parseEmbedFromCondition(
input.fieldEmbedFrom ?? ""
);
const field = findField(collection, fieldName);
const from = isObject(field) && isObject(field.embed) && Array.isArray(field.embed.from) ? field.embed.from : [];
return {
ok: from.includes(source),
condition: "field-embed-from",
field: fieldName,
source
};
}
async function waitForCollectionCondition(client, input) {
const startedAt = Date.now();
let lastError;
let attempts = 0;
while (Date.now() - startedAt <= input.timeoutMs) {
attempts += 1;
try {
const collection = await retrieveCollection(client, input.collection);
const result = evaluateWaitCondition(collection, input);
if (result.ok) {
return {
collection: input.collection,
attempts,
...result
};
}
} catch (error) {
if (!isTransientCollectionUpdateError(error)) throw error;
lastError = error;
}
await sleep(input.intervalMs);
}
const detail = lastError instanceof Error ? ` Last error: ${lastError.message}` : "";
throw new Error(
`Timed out waiting for collection ${input.collection} schema condition.${detail}`
);
}
async function dropField(client, input) {
assertDestructiveConfirmed(input);
const fieldName = requestedFieldName(input);
const collection = await retrieveCollection(client, input.collection);
const existingField = findField(collection, fieldName);
if (!existingField) {
return {
ok: true,
collection: input.collection,
field: fieldName,
alreadyMissing: true
};
}
const result = await api(client).patch(collectionPath(input.collection), {
fields: [{ name: fieldName, drop: true }]
});
return {
ok: true,
collection: input.collection,
field: fieldName,
before: existingField,
result
};
}
async function addField(client, input) {
const field = buildFieldDefinition(input);
const result = await api(client).patch(collectionPath(input.collection), {
fields: [field]
});
return {
ok: true,
collection: input.collection,
field: field.name,
added: field,
result
};
}
var collectionOperations = [
{
name: "collections.create",
summary: "Create a collection",
category: "collections",
input: z5.object({
name: z5.string(),
fields: z5.array(createFieldSchema),
default_sorting_field: z5.string().optional(),
token_separators: z5.array(z5.string()).optional(),
symbols_to_index: z5.array(z5.string()).optional(),
enable_nested_fields: z5.boolean().optional()
}),
execute: async (client, input) => api(client).post("/collections", input)
},
{
name: "collections.list",
summary: "List collections",
category: "collections",
input: z5.object({}),
execute: async (client) => api(client).get("/collections")
},
{
name: "collections.retrieve",
summary: "Retrieve a collection",
category: "collections",
input: z5.object({ collection: z5.string() }),
execute: async (client, input) => api(client).get(collectionPath(input.collection))
},
{
name: "collections.update",
summary: "Update a collection schema",
category: "collections",
input: z5.object({
collection: z5.string(),
fields: z5.array(patchFieldSchema).optional()
}),
execute: async (client, input) => api(client).patch(collectionPath(input.collection), {
fields: input.fields
})
},
{
name: "collections.wait",
summary: "Wait for a collection schema condition",
category: "collections",
input: waitInputSchema,
execute: waitForCollectionCondition
},
{
name: "collections.fields.add",
summary: "Add a field to a collection",
category: "collections",
input: fieldLifecycleInputSchema,
execute: addField
},
{
name: "collections.fields.drop",
summary: "Drop a field from a collection",
category: "collections",
input: fieldLifecycleInputSchema,
execute: dropField
},
{
name: "collections.fields.replace",
summary: "Safely replace a collection field",
category: "collections",
input: fieldLifecycleInputSchema,
execute: async (client, input) => {
const fieldName = requestedFieldName(input);
const replacement = buildFieldDefinition(input);
const dropResult = await dropField(client, input);
await waitForCollectionCondition(client, {
collection: input.collection,
fieldMissing: fieldName,
timeoutMs: input.timeoutMs,
intervalMs: input.intervalMs
});
try {
await api(client).patch(collectionPath(input.collection), {
fields: [replacement]
});
} catch (error) {
throw new Error(
[
error instanceof Error ? error.message : String(error),
"",
"The original field was dropped, but adding the replacement failed.",
`Recover with: tsk collections.fields.add --collection ${input.collection} --input field.json`
].join("\n")
);
}
const waitResult = await waitForCollectionCondition(client, {
collection: input.collection,
fieldPresent: fieldName,
timeoutMs: input.timeoutMs,
intervalMs: input.intervalMs
});
const collection = await retrieveCollection(client, input.collection);
return {
ok: true,
collection: input.collection,
field: fieldName,
before: "before" in dropResult ? dropResult.before : void 0,
after: findField(collection, fieldName),
wait: waitResult
};
}
},
{
name: "collections.delete",
summary: "Delete a collection",
category: "collections",
input: z5.object({ collection: z5.string() }),
execute: async (client, input) => api(client).delete(collectionPath(input.collection))
}
];
// ../core/src/operations/conversations.ts
import { z as z6 } from "zod";
var conversationOperations = [
{
name: "conversations.models.list",
summary: "List conversation models",
category: "conversations",
input: z6.object({}),
execute: async (client) => api(client).get("/conversations/models")
},
{
name: "conversations.models.create",
summary: "Create a conversation model",
category: "conversations",
input: z6.object({ value: z6.record(z6.unknown()) }),
execute: async (client, input) => api(client).post("/conversations/models", input.value)
},
{
name: "conversations.models.retrieve",
summary: "Retrieve a conversation model",
category: "conversations",
input: z6.object({ id: z6.string() }),
execute: async (client, input) => api(client).get(`/conversations/models/${enc(input.id)}`)
},
{
name: "conversations.models.delete",
summary: "Delete a conversation model",
category: "conversations",
input: z6.object({ id: z6.string() }),
execute: async (client, input) => api(client).delete(`/conversations/models/${enc(input.id)}`)
},
{
name: "conversations.history.retrieve",
summary: "Retrieve conversation history",
category: "conversations",
input: z6.object({ conversation_id: z6.string().optional() }),
execute: async (client, input) => api(client).get(
"/conversations/history",
input.conversation_id ? { conversation_id: input.conversation_id } : void 0
)
}
];
// ../core/src/operations/documents.ts
import { z as z7 } from "zod";
var documentSchema = z7.record(z7.unknown());
var idsSchema = z7.array(z7.string().min(1)).min(1);
var searchParams = z7.record(
z7.union([
z7.string(),
z7.number(),
z7.boolean(),
z7.array(z7.string()),
z7.array(z7.number())
])
);
var documentOperations = [
{
name: "documents.index",
summary: "Index a document",
category: "documents",
input: z7.object({ collection: z7.string(), document: documentSchema }),
execute: async (client, input) => api(client).post(
`${collectionPath(input.collection)}/documents`,
input.document
)
},
{
name: "documents.upsert",
summary: "Upsert a document",
category: "documents",
input: z7.object({ collection: z7.string(), document: documentSchema }),
execute: async (client, input) => api(client).post(
`${collectionPath(input.collection)}/documents`,
input.document,
{ action: "upsert" }
)
},
{
name: "documents.get",
summary: "Get a document by id",
category: "documents",
input: z7.object({ collection: z7.string(), id: z7.string() }),
execute: async (client, input) => api(client).get(
`${collectionPath(input.collection)}/documents/${enc(input.id)}`
)
},
{
name: "documents.get_many",
summary: "Get multiple documents by id",
category: "documents",
input: z7.object({ collection: z7.string(), ids: idsSchema }),
execute: async (client, input) => {
const request = api(client);
return Promise.all(
input.ids.map(
(id) => request.get(
`${collectionPath(input.collection)}/documents/${enc(id)}`
)
)
);
}
},
{
name: "documents.update",
summary: "Update a document by id",
category: "documents",
input: z7.object({
collection: z7.string(),
id: z7.string(),
document: documentSchema
}),
execute: async (client, input) => api(client).patch(
`${collectionPath(input.collection)}/documents/${enc(input.id)}`,
input.document
)
},
{
name: "documents.delete",
summary: "Delete a document by id",
category: "documents",
input: z7.object({ collection: z7.string(), id: z7.string() }),
execute: async (client, input) => api(client).delete(
`${collectionPath(input.collection)}/documents/${enc(input.id)}`
)
},
{
name: "documents.import",
summary: "Import documents into a collection",
category: "documents",
input: z7.object({
collection: z7.string(),
documents: z7.union([z7.string(), z7.array(documentSchema)]),
action: z7.enum(["create", "upsert", "update", "emplace"]).optional()
}),
execute: async (client, input) => api(client).post(
`${collectionPath(input.collection)}/documents/import`,
Array.isArray(input.documents) ? input.documents.map((doc) => JSON.stringify(doc)).join("\n") : input.documents,
input.action ? { action: input.action } : void 0
)
},
{
name: "documents.export",
summary: "Export documents from a collection",
category: "documents",
input: z7.object({
collection: z7.string(),
params: searchParams.optional()
}),
execute: async (client, input) => api(client).get(
`${collectionPath(input.collection)}/documents/export`,
input.params
)
},
{
name: "documents.search",
summary: "Search within a collection",
category: "documents",
input: z7.object({ collection: z7.string(), params: searchParams }),
execute: async (client, input) => api(client).get(
`${collectionPath(input.collection)}/documents/search`,
input.params
)
}
];
// ../core/src/operations/keys.ts
import { z as z8 } from "zod";
var keysOperations = [
{
name: "keys.list",
summary: "List API keys",
category: "keys",
input: z8.object({}),
execute: async (client) => api(client).get("/keys")
},
{
name: "keys.create",
summary: "Create an API key",
category: "keys",
input: z8.object({ value: z8.record(z8.unknown()) }),
execute: async (client, input) => api(client).post("/keys", input.value)
},
{
name: "keys.retrieve",
summary: "Retrieve an API key",
category: "keys",
input: z8.object({ id: z8.union([z8.string(), z8.number()]) }),
execute: async (client, input) => api(client).get(`/keys/${enc(String(input.id))}`)
},
{
name: "keys.delete",
summary: "Delete an API key",
category: "keys",
input: z8.object({ id: z8.union([z8.string(), z8.number()]) }),
execute: async (client, input) => api(client).delete(`/keys/${enc(String(input.id))}`)
}
];
// ../core/src/operations/overrides.ts
import { z as z9 } from "zod";
var overridesOperations = [
{
name: "overrides.list",
summary: "List overrides",
category: "overrides",
input: z9.object({ collection: z9.string() }),
execute: async (client, input) => api(client).get(`${collectionPath(input.collection)}/overrides`)
},
{
name: "overrides.create",
summary: "Create or upsert a override",
category: "overrides",
input: z9.object({
collection: z9.string(),
name: z9.string(),
value: z9.record(z9.unknown())
}),
execute: async (client, input) => api(client).put(
`${collectionPath(input.collection)}/overrides/${enc(input.name)}`,
input.value
)
},
{
name: "overrides.retrieve",
summary: "Retrieve a override",
category: "overrides",
input: z9.object({ collection: z9.string(), name: z9.string() }),
execute: async (client, input) => api(client).get(
`${collectionPath(input.collection)}/overrides/${enc(input.name)}`
)
},
{
name: "overrides.delete",
summary: "Delete a override",
category: "overrides",
input: z9.object({ collection: z9.string(), name: z9.string() }),
execute: async (client, input) => api(client).delete(
`${collectionPath(input.collection)}/overrides/${enc(input.name)}`
)
}
];
// ../core/src/operations/presets.ts
import { z as z10 } from "zod";
var presetsOperations = [
{
name: "presets.list",
summary: "List presets",
category: "presets",
input: z10.object({}),
execute: async (client) => api(client).get("/presets")
},
{
name: "presets.create",
summary: "Create or upsert a preset",
category: "presets",
input: z10.object({
name: z10.string(),
value: z10.record(z10.unknown())
}),
execute: async (client, input) => api(client).put(`/presets/${enc(input.name)}`, {
value: input.value
})
},
{
name: "presets.retrieve",
summary: "Retrieve a preset",
category: "presets",
input: z10.object({ name: z10.string() }),
execute: async (client, input) => api(client).get(`/presets/${enc(input.name)}`)
},
{
name: "presets.delete",
summary: "Delete a preset",
category: "presets",
input: z10.object({ name: z10.string() }),
execute: async (client, input) => api(client).delete(`/presets/${enc(input.name)}`)
}
];
// ../core/src/operations/search.ts
import { z as z11 } from "zod";
var searchParams2 = z11.record(z11.unknown());
var facetBySchema = z11.union([z11.string().min(1), z11.array(z11.string().min(1))]);
function withoutUndefined(params) {
return Object.fromEntries(
Object.entries(params).filter(([, value]) => value !== void 0)
);
}
var searchOperations = [
{
name: "search",
summary: "Search a collection",
category: "search",
input: z11.object({ collection: z11.string(), params: searchParams2 }),
execute: async (client, input) => api(client).get(
`${collectionPath(input.collection)}/documents/search`,
input.params
)
},
{
name: "multi_search",
summary: "Run a Typesense multi-search",
category: "search",
input: z11.object({
searches: z11.array(searchParams2),
commonParams: searchParams2.optional()
}),
execute: async (client, input) => api(client).post(
"/multi_search",
{ searches: input.searches },
input.commonParams
)
},
{
name: "search.facets",
summary: "Explore facet counts for a collection",
category: "search",
input: z11.object({
collection: z11.string(),
facetBy: facetBySchema,
q: z11.string().optional().default("*"),
queryBy: z11.string().optional(),
filterBy: z11.string().optional(),
maxFacetValues: z11.number().int().positive().optional(),
perPage: z11.number().int().nonnegative().optional().default(0)
}),
execute: async (client, input) => api(client).get(
`${collectionPath(input.collection)}/documents/search`,
withoutUndefined({
q: input.q,
query_by: input.queryBy,
filter_by: input.filterBy,
facet_by: Array.isArray(input.facetBy) ? input.facetBy.join(",") : input.facetBy,
max_facet_values: input.maxFacetValues,
per_page: input.perPage
})
)
},
{
name: "search.suggestions",
summary: "Fetch prefix search suggestions from a collection",
category: "search",
input: z11.object({
collection: z11.string(),
q: z11.string(),
queryBy: z11.string(),
filterBy: z11.string().optional(),
includeFields: z11.union([z11.string(), z11.array(z11.string())]).optional(),
limit: z11.number().int().positive().max(50).optional().default(5),
prefix: z11.boolean().optional().default(true)
}),
execute: async (client, input) => api(client).get(
`${collectionPath(input.collection)}/documents/search`,
withoutUndefined({
q: input.q,
query_by: input.queryBy,
filter_by: input.filterBy,
include_fields: Array.isArray(input.includeFields) ? input.includeFields.join(",") : input.includeFields,
per_page: input.limit,
prefix: input.prefix
})
)
}
];
// ../core/src/operations/stopwords.ts
import { z as z12 } from "zod";
var stopwordsOperations = [
{
name: "stopwords.list",
summary: "List stopwords",
category: "stopwords",
input: z12.object({}),
execute: async (client) => api(client).get("/stopwords")
},
{
name: "stopwords.create",
summary: "Create or upsert a stopword",
category: "stopwords",
input: z12.object({
name: z12.string(),
value: z12.record(z12.unknown())
}),
execute: async (client, input) => api(client).put(`/stopwords/${enc(input.name)}`, input.value)
},
{
name: "stopwords.retrieve",
summary: "Retrieve a stopword",
category: "stopwords",
input: z12.object({ name: z12.string() }),
execute: async (client, input) => api(client).get(`/stopwords/${enc(input.name)}`)
},
{
name: "stopwords.delete",
summary: "Delete a stopword",
category: "stopwords",
input: z12.object({ name: z12.string() }),
execute: async (client, input) => api(client).delete(`/stopwords/${enc(input.name)}`)
}
];
// ../core/src/operations/synonym-sets.ts
import { z as z13 } from "zod";
function base(name) {
return name ? `/synonym_sets/${enc(name)}` : "/synonym_sets";
}
function itemPath(name, id) {
const path = `${base(name)}/items`;
return id ? `${path}/${enc(id)}` : path;
}
var synonymSetOperations = [
{
name: "synonym_sets.list",
summary: "List global synonym sets",
category: "synonyms",
input: z13.object({}),
execute: async (client) => api(client).get(base())
},
{
name: "synonym_sets.create",
summary: "Create or upsert a global synonym set",
category: "synonyms",
input: z13.object({
name: z13.string(),
value: z13.object({
items: z13.array(z13.record(z13.unknown()))
})
}),
execute: async (client, input) => api(client).put(base(input.name), input.value)
},
{
name: "synonym_sets.retrieve",
summary: "Retrieve a global synonym set",
category: "synonyms",
input: z13.object({ name: z13.string() }),
execute: async (client, input) => api(client).get(base(input.name))
},
{
name: "synonym_sets.delete",
summary: "Delete a global synonym set",
category: "synonyms",
input: z13.object({ name: z13.string() }),
execute: async (client, input) => api(client).delete(base(input.name))
},
{
name: "synonym_sets.items.list",
summary: "List items in a global synonym set",
category: "synonyms",
input: z13.object({ name: z13.string() }),
execute: async (client, input) => api(client).get(itemPath(input.name))
},
{
name: "synonym_sets.items.create",
summary: "Create or upsert an item in a global synonym set",
category: "synonyms",
input: z13.object({
name: z13.string(),
id: z13.string(),
value: z13.record(z13.unknown())
}),
execute: async (client, input) => api(client).put(itemPath(input.name, input.id), input.value)
},
{
name: "synonym_sets.items.retrieve",
summary: "Retrieve an item in a global synonym set",
category: "synonyms",
input: z13.object({ name: z13.string(), id: z13.string() }),
execute: async (client, input) => api(client).get(itemPath(input.name, input.id))
},
{
name: "synonym_sets.items.delete",
summary: "Delete an item in a global synonym set",
category: "synonyms",
input: z13.object({ name: z13.string(), id: z13.string() }),
execute: async (client, input) => api(client).delete(itemPath(input.name, input.id))
}
];
// ../core/src/operations/synonyms.ts
import { z as z14 } from "zod";
function isNotFound(error) {
if (typeof error !== "object" || error === null) return false;
const { httpStatus, status } = error;
return httpStatus === 404 || status === 404;
}
function synonymSetNames(collection) {
return Array.isArray(collection.synonym_sets) ? collection.synonym_sets.filter(
(name) => typeof name === "string"
) : [];
}
function globalSynonymGuidance(collection, sets) {
return [
`Collection-level synonyms are unavailable for ${collection}.`,
sets.length > 0 ? `This collection is linked to global synonym sets: ${sets.join(", ")}.` : "This Typesense version may use global synonym sets.",
"Use synonym_sets.list to inspect global synonym sets:",
"tsk synonym_sets.list --input '{}' --json"
].join("\n");
}
var synonymsOperations = [
{
name: "synonyms.list",
summary: "List synonyms",
category: "synonyms",
input: z14.object({ collection: z14.string() }),
execute: async (client, input) => {
const request = api(client);
try {
return await request.get(
`${collectionPath(input.collection)}/synonyms`
);
} catch (error) {
if (!isNotFound(error)) throw error;
const collection = await request.get(
collectionPath(input.collection)
);
throw new Error(
globalSynonymGuidance(input.collection, synonymSetNames(collection))
);
}
}
},
{
name: "synonyms.create",
summary: "Create or upsert a synonym",
category: "synonyms",
input: z14.object({
collection: z14.string(),
name: z14.string(),
value: z14.record(z14.unknown())
}),
execute: async (client, input) => api(client).put(
`${collectionPath(input.collection)}/synonyms/${enc(input.name)}`,
input.value
)
},
{
name: "synonyms.retrieve",
summary: "Retrieve a synonym",
category: "synonyms",
input: z14.object({ collection: z14.string(), name: z14.string() }),
execute: async (client, input) => api(client).get(
`${collectionPath(input.collection)}/synonyms/${enc(input.name)}`
)
},
{
name: "synonyms.delete",
summary: "Delete a synonym",
category: "synonyms",
input: z14.object({ collection: z14.string(), name: z14.string() }),
execute: async (client, input) => api(client).delete(
`${collectionPath(input.collection)}/synonyms/${enc(input.name)}`
)
}
];
// ../core/src/operations/system.ts
import { z as z15 } from "zod";
var systemOperations = [
{
name: "health",
summary: "Check Typesense cluster health",
category: "system",
input: z15.object({}),
execute: async (client) => api(client).get("/health")
},
{
name: "metrics",
summary: "Retrieve Typesense metrics",
category: "system",
input: z15.object({}),
execute: async (client) => api(client).get("/metrics.json")
},
{
name: "stats",
summary: "Retrieve Typesense stats",
category: "system",
input: z15.object({}),
execute: async (client) => api(client).get("/stats.json")
},
{
name: "debug",
summary: "Retrieve Typesense debug info",
category: "system",
input: z15.object({}),
execute: async (client) => api(client).get("/debug")
}
];
// ../core/src/operations/index.ts
var operations = [
...collectionOperations,
...documentOperations,
...searchOperations,
...aliasesOperations,
...synonymsOperations,
...synonymSetOperations,
...overridesOperations,
...keysOperations,
...analyticsOperations,
...presetsOperations,
...stopwordsOperations,
...conversationOperations,
...apiOperations,
...systemOperations
];
// package.json
var package_default = {
name: "@typesensekit/mcp",
version: "1.1.3",
type: "module",
main: "dist/server.js",
types: "dist/server.d.ts",
bin: {
"typesensekit-mcp": "dist/cli.js",
"typesensekit-mcp-http": "dist/http.js"
},
exports: {
".": {
types: "./dist/server.d.ts",
import: "./dist/server.js"
}
},
files: [
"dist",
"README.md"
],
scripts: {
build: "tsup",
dev: "tsup --watch",
typecheck: "tsc --noEmit",
test: "vitest run"
},
dependencies: {
"@modelcontextprotocol/sdk": "^1.29.0",
zod: "^3.25.76",
typesense: "^3.0.6"
},
devDependencies: {
tsup: "^8.5.1",
"@types/node": "^22.10.0",
"@typesensekit/core": "workspace:*"
},
publishConfig: {
access: "public"
},
description: "MCP stdio server exposing Typesense API operations as tools.",
license: "MIT",
author: "Akshit Kr Nagpal",
repository: {
type: "git",
url: "git+https://github.com/akshitkrnagpal/typesensekit.git",
directory: "packages/mcp"
},
bugs: {
url: "https://github.com/akshitkrnagpal/typesensekit/issues"
},
homepage: "https://github.com/akshitkrnagpal/typesensekit#readme",
keywords: [
"typesense",
"cli",
"mcp",
"model-context-protocol",
"search"
]
};
// src/read-only.ts
var READ_ONLY_OPERATION_NAMES = /* @__PURE__ */ new Set([
"aliases.list",
"aliases.retrieve",
"collections.list",
"collections.retrieve",
"collections.wait",
"conversations.history.retrieve",
"conversations.models.list",
"conversations.models.retrieve",
"debug",
"documents.export",
"documents.get",
"documents.get_many",
"documents.search",
"health",
"metrics",
"multi_search",
"overrides.list",
"overrides.retrieve",
"presets.list",
"presets.retrieve",
"search",
"search.facets",
"search.suggestions",
"stats",
"stopwords.list",
"stopwords.retrieve",
"synonym_sets.items.list",
"synonym_sets.items.retrieve",
"synonym_sets.list",
"synonym_sets.retrieve",
"synonyms.list",
"synonyms.retrieve"
]);
function isReadOnlyOperation(operation) {
return READ_ONLY_OPERATION_NAMES.has(operation.name);
}
function filterMcpOperations(operations2, readOnly) {
return readOnly ? operations2.filter(isReadOnlyOperation) : operations2;
}
function readOnlyFromEnv(value) {
if (value === void 0) return true;
return !["0", "false", "no", "off"].includes(value.toLowerCase());
}
// src/env.ts
function readEnvConfig() {
return serverConfigSchema.parse({
url: process.env.TYPESENSE_URL,
apiKey: process.env.TYPESENSE_API_KEY,
connectionTimeoutSeconds: process.env.TYPESENSE_CONNECTION_TIMEOUT_SECONDS ? Number(process.env.TYPESENSE_CONNECTION_TIMEOUT_SECONDS) : void 0
});
}
function readMcpOptions() {
return {
readOnly: readOnlyFromEnv(process.env.TYPESENSEKIT_READ_ONLY)
};
}
// src/resources.ts
import {
ResourceTemplate
} from "@modelcontextprotocol/sdk/server/mcp.js";
function jsonContents(uri, value) {
return {
contents: [
{
uri,
mimeType: "application/json",
text: JSON.stringify(value, null, 2)
}
]
};
}
function textContents(uri, value) {
return {
contents: [{ uri, mimeType: "text/plain", text: value }]
};
}
function operationSummary(operation) {
return {
name: operation.name,
summary: operation.summary,
category: operation.category,
readOnly: READ_ONLY_OPERATION_NAMES.has(operation.name)
};
}
function operationManifest(activeOperations, readOnly) {
return {
readOnly,
operations: activeOperations.map(operationSummary)
};
}
function singleVariable(value, variable) {
if (typeof value === "string") return value;
throw new Error(`Missing ${variable} resource variable`);
}
async function readOperationResource(client, operationName, input, uri) {
const operation = operations.find(
(candidate) => candidate.name === operationName
);
if (!operation) throw new Error(`${operationName} not found`);
try {
const result = await operation.execute(
client,
operation.input.parse(input)
);
return jsonContents(uri, result);
} catch (error) {
return textContents(uri, formatTypesenseErrorMessage(error));
}
}
function registerTypesenseResources(server, client, activeOperations, readOnly) {
server.registerResource(
"typesensekit-operations",
"typesensekit://operations",
{
title: "TypesenseKit Operations",
description: "Operations currently exposed by this MCP server.",
mimeType: "application/json"
},
async (uri) => jsonContents(uri.href, operationManifest(activeOperations, readOnly))
);
server.registerResource(
"typesensekit-read-only-tools",
"typesensekit://read-only-tools",
{
title: "TypesenseKit Read-only Tools",
description: "Operation names included in default read-only MCP mode.",
mimeType: "application/json"
},
async (uri) => jsonContents(uri.href, {
operations: [...READ_ONLY_OPERATION_NAMES].sort()
})
);
server.registerResource(
"typesense-collection-schema",
new ResourceTemplate("typesense://collections/{collection}/schema", {
list: void 0
}),
{
title: "Typesense Collection Schema",
description: "Retrieve a Typesense collection schema by collection name.",
mimeType: "application/json"
},
async (uri, variables) => readOperationResource(
client,
"collections.retrieve",
{ collection: singleVariable(variables.collection, "collection") },
uri.href
)
);
server.registerResource(
"typesense-document",
new ResourceTemplate(
"typesense://collections/{collection}/documents/{id}",
{
list: void 0
}
),
{
title: "Typesense Document",
description: "Retrieve a Typesense document by collection and document id.",
mimeType: "application/json"
},
async (uri, variables) => readOperationResource(
client,
"documents.get",
{
collection: singleVariable(variables.collection, "collection"),
id: singleVariable(variables.id, "id")
},
uri.href
)
);
}
// src/server.ts
function toToolShape(input) {
const objectInput = input;
return objectInput.shape;
}
function createTypesenseMcpServer(options = {}) {
const server = new McpServer({
name: "typesensekit",
version: package_default.version
});
const client = createClient(readEnvConfig());
const mcpOptions = { ...readMcpOptions(), ...options };
const activeOperations = filterMcpOperations(operations, mcpOptions.readOnly);
registerTypesenseResources(
server,
client,
activeOperations,
mcpOptions.readOnly
);
for (const operation of activeOperations) {
server.registerTool(
operation.name,
{
title: operation.name,
description: operation.summary,
inputSchema: toToolShape(operation.input)
},
async (args) => {
try {
const input = operation.input.parse(args);
const result = await operation.execute(client, input);
return {
content: [
{
type: "text",
text: JSON.stringify(redactSecrets(result), null, 2)
}
]
};
} catch (error) {
const message = formatTypesenseErrorMessage(error);
return { isError: true, content: [{ type: "text", text: message }] };
}
}
);
}
return server;
}
export {
createTypesenseMcpServer
};