UNPKG

lynkr

Version:

Self-hosted LLM gateway and tier-routing proxy for Claude Code, Cursor, and Codex. Routes across Ollama, AWS Bedrock, OpenRouter, Databricks, Azure OpenAI, llama.cpp, and LM Studio with prompt caching, MCP tools, and 60-80% cost savings.

454 lines (445 loc) 16.8 kB
/** * Standard tool definitions for Claude Code * These tools are injected when the client doesn't send tools in passthrough mode */ const STANDARD_TOOLS = [ { name: "Write", description: "Writes a file to the local filesystem. Overwrites existing files. ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.", input_schema: { type: "object", properties: { file_path: { type: "string", description: "Relative path within workspace (e.g., 'hello.cpp', 'src/main.py'). DO NOT use absolute paths." }, content: { type: "string", description: "The content to write to the file" } }, required: ["file_path", "content"] } }, { name: "Read", description: "Reads a file from the local filesystem. You can access any file directly by using this tool. For files outside the workspace, the user must approve access first.", input_schema: { type: "object", properties: { file_path: { type: "string", description: "Path to the file. Use relative paths for workspace files (e.g., 'src/index.ts'). For files outside the workspace use absolute paths or ~ for the home directory (e.g., '~/Documents/notes.md', '/etc/hosts'). Each call reads ONE file only — do not pass multiple paths." }, user_approved: { type: "boolean", description: "Set to true ONLY after the user has explicitly approved reading a file outside the workspace. Never set this to true without asking the user first." }, limit: { type: "number", description: "The number of lines to read. Only provide if the file is too large to read at once." }, offset: { type: "number", description: "The line number to start reading from. Only provide if the file is too large to read at once" } }, required: ["file_path"] } }, { name: "Edit", description: "Performs exact string replacements in files. You must use your Read tool at least once before editing. The edit will FAIL if old_string is not unique in the file.", input_schema: { type: "object", properties: { file_path: { type: "string", description: "Relative path within workspace (e.g., 'app.py', 'src/utils.js'). DO NOT use absolute paths." }, old_string: { type: "string", description: "The text to replace" }, new_string: { type: "string", description: "The text to replace it with (must be different from old_string)" }, replace_all: { type: "boolean", description: "Replace all occurences of old_string (default false)" } }, required: ["file_path", "old_string", "new_string"] } }, { name: "Bash", description: "Executes a bash command in a persistent shell session. Use for terminal operations like git, npm, docker, listing files (ls), etc. PREFERRED for listing directory contents - use 'ls' command. DO NOT use for reading file contents - use Read tool instead.", input_schema: { type: "object", properties: { command: { type: "string", description: "The command to execute" }, description: { type: "string", description: "Clear, concise description of what this command does in 5-10 words" }, timeout: { type: "number", description: "Optional timeout in milliseconds (max 600000)" } }, required: ["command"] } }, { name: "Glob", description: "File pattern matching for finding files by name pattern. Use ONLY when you need to find files matching a specific pattern like '**/*.js'. For simple directory listing, use Bash with 'ls' instead.", input_schema: { type: "object", properties: { pattern: { type: "string", description: "The glob pattern to match files against" }, path: { type: "string", description: "The directory to search in. If not specified, the current working directory will be used." } }, required: ["pattern"] } }, { name: "Grep", description: "A powerful search tool built on ripgrep. Supports full regex syntax. Filter files with glob parameter or type parameter.", input_schema: { type: "object", properties: { pattern: { type: "string", description: "The regular expression pattern to search for in file contents" }, path: { type: "string", description: "File or directory to search in. Defaults to current working directory." }, glob: { type: "string", description: "Glob pattern to filter files (e.g. '*.js', '*.{ts,tsx}')" }, output_mode: { type: "string", enum: ["content", "files_with_matches", "count"], description: "Output mode: 'content' shows matching lines, 'files_with_matches' shows file paths, 'count' shows match counts" }, "-i": { type: "boolean", description: "Case insensitive search" } }, required: ["pattern"] } }, { name: "MultiEdit", description: "Makes multiple edits to a single file in one atomic operation. More efficient than calling Edit multiple times. Each edit is an exact string replacement.", input_schema: { type: "object", properties: { file_path: { type: "string", description: "Relative path within workspace. DO NOT use absolute paths." }, edits: { type: "array", description: "Array of edits to apply to the file", items: { type: "object", properties: { old_string: { type: "string", description: "The text to replace" }, new_string: { type: "string", description: "The text to replace it with" } }, required: ["old_string", "new_string"] } } }, required: ["file_path", "edits"] } }, { name: "LS", description: "Lists files and directories in a given path. Returns a structured listing with file types and sizes. Use for quick directory overview.", input_schema: { type: "object", properties: { path: { type: "string", description: "The directory to list. Defaults to current working directory." } }, required: [] } }, { name: "NotebookRead", description: "Reads and displays the contents of a Jupyter notebook (.ipynb file), including all cells with their outputs, combining code, text, and visualizations.", input_schema: { type: "object", properties: { notebook_path: { type: "string", description: "Relative path to the Jupyter notebook (e.g., 'analysis.ipynb'). DO NOT use absolute paths." } }, required: ["notebook_path"] } }, { name: "TodoWrite", description: "Create and manage a structured task list for tracking progress and organizing complex tasks. Use proactively for multi-step tasks or when user provides multiple tasks.", input_schema: { type: "object", properties: { todos: { type: "array", description: "Array of todo items with status tracking", items: { type: "object", properties: { content: { type: "string", description: "Task description in imperative form (e.g., 'Run tests', 'Build project')" }, status: { type: "string", enum: ["pending", "in_progress", "completed"], description: "Task status: pending (not started), in_progress (currently working), completed (finished)" }, activeForm: { type: "string", description: "Present continuous form shown during execution (e.g., 'Running tests', 'Building project')" } }, required: ["content", "status", "activeForm"] } } }, required: ["todos"] } }, { name: "Task", description: `Launch a specialized agent to handle complex, multi-step tasks autonomously. YOU MUST USE THIS TOOL WHEN the user asks to: - "explore", "dig into", "understand", or "analyze" a codebase use subagent_type="Explore" - "plan", "design", or "architect" something use subagent_type="Plan" - perform complex multi-file research or investigation use subagent_type="general-purpose" AVAILABLE AGENTS: - Explore: Fast codebase exploration using Glob, Grep, Read. Use for searching, finding files, understanding project structure. - Plan: Implementation planning and architecture design. Use for planning features or refactoring. - general-purpose: Complex multi-step tasks with all tools available. EXAMPLE: User says "explore this project" Call Task with subagent_type="Explore", prompt="Explore the codebase structure, find main entry points, read key files, and summarize what this project does"`, input_schema: { type: "object", properties: { description: { type: "string", description: "A short (3-5 word) description of the task, e.g., 'Explore project structure'" }, prompt: { type: "string", description: "Detailed instructions for the agent. Be specific about what to find, read, or analyze." }, subagent_type: { type: "string", enum: ["general-purpose", "Explore", "Plan", "claude-code-guide"], description: "Agent type: Explore (search/read codebase), Plan (design/architecture), general-purpose (complex research)" }, model: { type: "string", enum: ["sonnet", "opus", "haiku"], description: "Optional model override. Default is appropriate for each agent type." } }, required: ["prompt"] } }, { name: "AskUserQuestion", description: "Ask the user questions to gather preferences, clarify requirements, or get decisions on implementation choices. Supports multiple choice questions.", input_schema: { type: "object", properties: { questions: { type: "array", description: "Questions to ask the user (1-4 questions)", minItems: 1, maxItems: 4, items: { type: "object", properties: { question: { type: "string", description: "The complete question to ask. Should be clear, specific, and end with a question mark." }, header: { type: "string", description: "Very short label (max 12 chars). Examples: 'Auth method', 'Library', 'Approach'" }, options: { type: "array", description: "Available choices (2-4 options). Each should be distinct and mutually exclusive.", minItems: 2, maxItems: 4, items: { type: "object", properties: { label: { type: "string", description: "Display text for this option (1-5 words)" }, description: { type: "string", description: "Explanation of what this option means or implications" } }, required: ["label", "description"] } }, multiSelect: { type: "boolean", description: "Set to true to allow multiple selections instead of just one" } }, required: ["question", "header", "options", "multiSelect"] } } }, required: ["questions"] } }, { name: "WebSearch", description: "Search the web for current information beyond the model's knowledge cutoff. Returns search results that can inform responses. Always include sources in your response.", input_schema: { type: "object", properties: { query: { type: "string", description: "The search query to use", minLength: 2 }, allowed_domains: { type: "array", description: "Only include search results from these domains", items: { type: "string" } }, blocked_domains: { type: "array", description: "Never include search results from these domains", items: { type: "string" } } }, required: ["query"] } }, { name: "WebFetch", description: "Fetches content from a specified URL and processes it using AI. Takes a URL and a prompt describing what information to extract from the page.", input_schema: { type: "object", properties: { url: { type: "string", description: "The URL to fetch content from (must be fully-formed valid URL)", format: "uri" }, prompt: { type: "string", description: "The prompt describing what information you want to extract from the page" } }, required: ["url", "prompt"] } }, { name: "WebAgent", description: "Launches a browser agent to navigate a website and accomplish a goal. Use when you need to interact with dynamic web content (click buttons, fill forms, extract data from JS-rendered pages) beyond what a simple HTTP fetch can do. Returns structured JSON. Takes 10-60 seconds.", input_schema: { type: "object", properties: { url: { type: "string", description: "Target URL to navigate to" }, goal: { type: "string", description: "What to accomplish on the page. Be specific about what data to extract or actions to take." }, browser_profile: { type: "string", enum: ["lite", "stealth"], description: "lite (default, faster) or stealth (for bot-protected sites)" } }, required: ["url", "goal"] } }, { name: "NotebookEdit", description: "Completely replaces the contents of a specific cell in a Jupyter notebook (.ipynb file). Use for editing interactive documents that combine code, text, and visualizations.", input_schema: { type: "object", properties: { notebook_path: { type: "string", description: "Relative path to the Jupyter notebook within workspace (e.g., 'analysis.ipynb', 'notebooks/data.ipynb'). DO NOT use absolute paths." }, new_source: { type: "string", description: "The new source for the cell" }, cell_id: { type: "string", description: "The ID of the cell to edit. When inserting, new cell will be inserted after this cell." }, cell_type: { type: "string", enum: ["code", "markdown"], description: "The type of the cell. Required when using edit_mode=insert." }, edit_mode: { type: "string", enum: ["replace", "insert", "delete"], description: "The type of edit to make. Defaults to replace." } }, required: ["notebook_path", "new_source"] } } ]; // Pre-computed name list to avoid re-mapping on every log call const STANDARD_TOOL_NAMES = STANDARD_TOOLS.map(t => t.name); // Tools that cannot work through a proxy (require bidirectional user interaction). // All other tools are safe per-client filtering via CLIENT_TOOL_MAPPINGS in // openai-router.js handles excluding tools that specific clients don't support // (e.g. Codex has no equivalent for Task, WebFetch, NotebookEdit). const IDE_UNSUPPORTED_TOOLS = new Set(['AskUserQuestion']); // Filtered tool set for IDE clients excludes tools with no IDE equivalent const IDE_SAFE_TOOLS = STANDARD_TOOLS.filter(t => !IDE_UNSUPPORTED_TOOLS.has(t.name)); const IDE_SAFE_TOOL_NAMES = IDE_SAFE_TOOLS.map(t => t.name); module.exports = { STANDARD_TOOLS, STANDARD_TOOL_NAMES, IDE_SAFE_TOOLS, IDE_SAFE_TOOL_NAMES, IDE_UNSUPPORTED_TOOLS };