Skip to main content
Glama
index.ts15.1 kB
import { z } from "zod"; // Define the static discovery tool schema using Zod export const DiscoverToolsInputSchema = z.object({ server_uuid: z.string().uuid().optional().describe("Specific server UUID (optional, omit for all)"), force_refresh: z.boolean().optional().default(false).describe("Force refresh bypassing cache"), }).describe("Triggers tool discovery for configured MCP servers in the Pluggedin App."); // Define the schema for asking questions to the knowledge base export const AskKnowledgeBaseInputSchema = z.object({ query: z.string() .min(1, "Query cannot be empty") .max(1000, "Query too long") .describe("Question to ask the knowledge base") }).describe("Ask questions and get AI-generated answers from your knowledge base. Returns JSON with answer, sources, and metadata."); // Input schema for send notification validation export const SendNotificationInputSchema = z.object({ title: z.string().optional() .describe("Optional notification title. If not provided, system uses localized default"), message: z.string().min(1, "Message cannot be empty") .describe("The notification message content"), severity: z.enum(["INFO", "SUCCESS", "WARNING", "ALERT"]).default("INFO") .describe("Severity level: INFO (default), SUCCESS, WARNING, or ALERT"), link: z.string().url().optional() .describe("Optional URL link associated with notification"), email: z.boolean().default(false) .describe("Whether to also send notification via email (default: false)"), }); // Input schema for list notifications validation export const ListNotificationsInputSchema = z.object({ limit: z.number().int().min(1).max(100).default(20) .describe("Maximum number of notifications to return (1-100, default: 20)"), unreadOnly: z.boolean().default(false) .describe("Filter to show only unread notifications (default: false)"), severity: z.enum(["INFO", "SUCCESS", "WARNING", "ALERT"]).optional() .describe("Filter by severity level: INFO, SUCCESS, WARNING, or ALERT (optional)"), }); // Input schema for mark notification done validation export const MarkNotificationDoneInputSchema = z.object({ notificationId: z.string().min(1, "Notification ID cannot be empty") .describe("The unique ID of the notification to mark as done"), }); // Input schema for delete notification validation export const DeleteNotificationInputSchema = z.object({ notificationId: z.string().min(1, "Notification ID cannot be empty") .describe("The unique ID of the notification to delete"), }); // Input schema for create document validation export const CreateDocumentInputSchema = z.object({ title: z.string() .min(1, "Title is required") .max(255, "Title too long") .describe("Document title"), content: z.string() .min(1, "Content is required") .describe("Document content"), format: z.enum(["md", "txt", "json", "html"]) .default("md") .describe("Format: md (Markdown), txt (plain text), json, or html"), tags: z.array(z.string()) .max(20, "Maximum 20 tags allowed") .optional() .describe("Tags for categorization"), category: z.enum(["report", "analysis", "documentation", "guide", "research", "code", "other"]) .default("other") .describe("Document category"), metadata: z.object({ model: z.object({ name: z.string() .describe("AI model name"), provider: z.string() .describe("Model provider"), version: z.string() .optional() .describe("Model version"), }).describe("AI model info for attribution"), context: z.string() .optional() .describe("Creation context"), visibility: z.enum(["private", "workspace", "public"]) .default("private") .describe("Visibility: private (only you), workspace (team), public (everyone)"), prompt: z.string() .optional() .describe("User prompt that triggered creation"), conversationContext: z.array(z.string()) .optional() .describe("Previous conversation messages for context"), sourceDocuments: z.array(z.string()) .optional() .describe("UUIDs of referenced documents"), generationParams: z.object({ temperature: z.number() .min(0) .max(2) .optional() .describe("Temperature (0-2)"), maxTokens: z.number() .positive() .optional() .describe("Max tokens"), topP: z.number() .min(0) .max(1) .optional() .describe("Top-p (0-1)"), }) .optional() .describe("Generation parameters"), }).describe("Metadata for attribution and tracking"), }).describe("Create and save an AI-generated document to the user's library with full metadata tracking"); // Input schema for list documents validation export const ListDocumentsInputSchema = z.object({ filters: z.object({ source: z.enum(["all", "upload", "ai_generated", "api"]).default("all") .describe("Filter by document source: all, upload, ai_generated, or api"), modelName: z.string().optional() .describe("Filter by AI model name (optional)"), modelProvider: z.string().optional() .describe("Filter by model provider, e.g., 'anthropic', 'openai' (optional)"), dateFrom: z.string().datetime().optional() .describe("Filter documents created after this date (ISO 8601 format)"), dateTo: z.string().datetime().optional() .describe("Filter documents created before this date (ISO 8601 format)"), tags: z.array(z.string()).optional() .describe("Filter by document tags (optional)"), category: z.enum(["report", "analysis", "documentation", "guide", "research", "code", "other"]).optional() .describe("Filter by document category (optional)"), searchQuery: z.string().optional() .describe("Text search query to filter documents (optional)"), }).optional() .describe("Optional filters to apply when listing documents"), sort: z.enum(["date_desc", "date_asc", "title", "size"]).default("date_desc") .describe("Sort order: date_desc (newest first), date_asc (oldest first), title (alphabetical), or size"), limit: z.number().int().min(1).max(100).default(20) .describe("Maximum number of documents to return (1-100, default: 20)"), offset: z.number().int().min(0).default(0) .describe("Number of documents to skip for pagination (default: 0)"), }); // Define the search documents input schema using Zod export const SearchDocumentsInputSchema = z.object({ query: z.string().min(1).max(500) .describe("Search query text to find documents (1-500 characters)"), filters: z.object({ modelName: z.string().optional() .describe("Filter by AI model name (optional)"), modelProvider: z.string().optional() .describe("Filter by model provider, e.g., 'anthropic', 'openai' (optional)"), dateFrom: z.string().datetime().optional() .describe("Filter documents created after this date (ISO 8601 format)"), dateTo: z.string().datetime().optional() .describe("Filter documents created before this date (ISO 8601 format)"), tags: z.array(z.string()).optional() .describe("Filter by document tags (optional)"), source: z.enum(["all", "upload", "ai_generated", "api"]).default("all") .describe("Filter by document source: all, upload, ai_generated, or api"), }).optional() .describe("Optional filters to refine search results"), limit: z.number().int().min(1).max(50).default(10) .describe("Maximum number of search results to return (1-50, default: 10)"), }); // Define the get document input schema using Zod export const GetDocumentInputSchema = z.object({ documentId: z.string().uuid() .describe("Unique document identifier (UUID format)"), includeContent: z.boolean().default(false) .describe("Include full document content in response (default: false)"), includeVersions: z.boolean().default(false) .describe("Include document version history (default: false)"), }); // Define the update document input schema using Zod export const UpdateDocumentInputSchema = z.object({ documentId: z.string() .uuid() .describe("Document UUID"), operation: z.enum(["replace", "append", "prepend"]) .describe("Operation: replace (overwrite), append (add to end), prepend (add to start)"), content: z.string() .min(1, "Content is required") .describe("New content"), metadata: z.object({ tags: z.array(z.string()) .optional() .describe("Updated tags"), changeSummary: z.string() .optional() .describe("Change summary"), updateReason: z.string() .optional() .describe("Update reason"), changesFromPrompt: z.string() .optional() .describe("User prompt that triggered update"), model: z.object({ name: z.string() .describe("AI model name"), provider: z.string() .describe("Model provider"), version: z.string() .optional() .describe("Model version"), }).describe("AI model info for attribution"), }) .optional() .describe("Update metadata"), }).describe("Update or append to an existing AI-generated document with version tracking"); // ===== Clipboard Schemas ===== // MIME type regex pattern for validation (prevents injection of special characters) const MIME_TYPE_REGEX = /^[\w.-]+\/[\w.+-]+$/; /** Clipboard source types */ export const CLIPBOARD_SOURCES = ['ui', 'sdk', 'mcp'] as const; export type ClipboardSource = (typeof CLIPBOARD_SOURCES)[number]; /** Source used by MCP proxy when writing clipboard entries */ export const MCP_CLIPBOARD_SOURCE: ClipboardSource = 'mcp'; /** * ClipboardEntry interface for type-safe response handling * Used to eliminate `any` types in response processing */ export interface ClipboardEntry { uuid: string; name: string | null; idx: number | null; value: string; contentType: string; encoding: string; sizeBytes: number; visibility: string; createdByTool: string | null; createdByModel: string | null; source: ClipboardSource; createdAt: string; updatedAt: string; expiresAt: string | null; } // Schema for setting a clipboard entry (named or indexed) export const ClipboardSetInputSchema = z.object({ name: z.string().max(255).optional() .describe("Named key for semantic access (e.g., 'customer_context')"), idx: z.number().int().optional() .describe("Numeric index for array-like access (e.g., 0, 1, 2)"), value: z.string() .describe("The content to store"), contentType: z.string() .regex(MIME_TYPE_REGEX, "Invalid MIME type format") .max(256) .default("text/plain") .describe("MIME type (e.g., 'text/plain', 'application/json', 'image/png')"), encoding: z.enum(["utf-8", "base64", "hex"]).default("utf-8") .describe("Content encoding: utf-8 (default), base64 (for binary), or hex"), visibility: z.enum(["private", "workspace", "public"]).default("private") .describe("Visibility scope: private (default), workspace, or public"), createdByTool: z.string().max(255).optional() .describe("Name of the tool that created this entry"), createdByModel: z.string().max(255).optional() .describe("Name of the AI model that created this entry"), ttlSeconds: z.number().int().positive().max(31536000).optional() .describe("Time-to-live in seconds (max: 1 year, default: 24 hours)"), }).refine((data) => data.name !== undefined || data.idx !== undefined, { message: "Either name or idx must be provided", }).refine((data) => { // Image content types require base64 encoding if (data.contentType?.startsWith('image/') && data.encoding !== 'base64') { return false; } return true; }, { message: "Image content types require base64 encoding", }).describe("Set a clipboard entry. Named entries are upserted; indexed entries fail if index exists."); // Schema for getting clipboard entries export const ClipboardGetInputSchema = z.object({ name: z.string().optional() .describe("Get entry by name"), idx: z.number().int().optional() .describe("Get entry by index"), contentType: z.string().optional() .describe("Filter by content type"), limit: z.number().int().min(1).max(100).default(50) .describe("Maximum entries to return (1-100, default: 50)"), offset: z.number().int().min(0).default(0) .describe("Pagination offset (default: 0)"), }).describe("Get clipboard entries. Without name/idx, lists all entries with pagination."); // Schema for deleting clipboard entries export const ClipboardDeleteInputSchema = z.object({ name: z.string().optional() .describe("Delete entry by name"), idx: z.number().int().optional() .describe("Delete entry by index"), clearAll: z.boolean().default(false) .describe("Delete all clipboard entries (default: false)"), }).refine((data) => data.clearAll || data.name !== undefined || data.idx !== undefined, { message: "Either name, idx, or clearAll must be provided", }).describe("Delete clipboard entries by name, index, or clear all."); // Schema for listing clipboard entries (metadata only) export const ClipboardListInputSchema = z.object({ contentType: z.string().optional() .describe("Filter by content type"), limit: z.number().int().min(1).max(100).default(50) .describe("Maximum entries to return (1-100, default: 50)"), offset: z.number().int().min(0).default(0) .describe("Pagination offset (default: 0)"), }).describe("List all clipboard entries with metadata (value truncated for images)."); // Schema for pushing to indexed clipboard (auto-increment) export const ClipboardPushInputSchema = z.object({ value: z.string() .describe("The content to push"), contentType: z.string() .regex(MIME_TYPE_REGEX, "Invalid MIME type format") .max(256) .default("text/plain") .describe("MIME type (e.g., 'text/plain', 'application/json', 'image/png')"), encoding: z.enum(["utf-8", "base64", "hex"]).default("utf-8") .describe("Content encoding: utf-8 (default), base64 (for binary), or hex"), visibility: z.enum(["private", "workspace", "public"]).default("private") .describe("Visibility scope: private (default), workspace, or public"), createdByTool: z.string().max(255).optional() .describe("Name of the tool that created this entry"), createdByModel: z.string().max(255).optional() .describe("Name of the AI model that created this entry"), ttlSeconds: z.number().int().positive().max(31536000).optional() .describe("Time-to-live in seconds (max: 1 year, default: 24 hours)"), }).refine((data) => { // Image content types require base64 encoding if (data.contentType?.startsWith('image/') && data.encoding !== 'base64') { return false; } return true; }, { message: "Image content types require base64 encoding", }).describe("Push a value to the indexed clipboard with auto-incrementing index."); // Schema for popping from indexed clipboard (LIFO) export const ClipboardPopInputSchema = z.object({}).describe("Pop the highest-indexed entry from the clipboard (LIFO behavior).");

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/VeriTeknik/pluggedin-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server