mcp-lazy 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +187 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1037 -0
- package/dist/index.js.map +1 -0
- package/docs/README.ko.md +173 -0
- package/package.json +45 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/config.ts","../src/version.ts","../src/proxy/registry.ts","../src/proxy/loader.ts","../src/proxy/server.ts","../src/utils/mcp-client.ts","../src/index.ts","../src/agents/index.ts","../src/cli/add.ts","../src/cli/doctor.ts"],"sourcesContent":["import { z } from \"zod\";\nimport { readFileSync, writeFileSync, existsSync, mkdirSync } from \"node:fs\";\nimport { resolve, dirname } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { createHash } from \"node:crypto\";\n\n// Schema for a single MCP server config entry\n// Supports stdio servers (command+args) and URL-based servers (url)\nconst ServerConfigSchema = z.object({\n command: z.string().optional(),\n args: z.array(z.string()).default([]),\n url: z.string().optional(),\n serverUrl: z.string().optional(),\n headers: z.record(z.string()).optional(),\n env: z.record(z.string()).optional(),\n description: z.string().optional(),\n});\n\nexport type ServerConfig = z.infer<typeof ServerConfigSchema>;\n\n// Schema for .mcp.json format (used by agents)\nconst McpJsonSchema = z.object({\n mcpServers: z.record(ServerConfigSchema),\n});\n\nfunction getBackupPath(): string {\n return resolve(homedir(), \".mcp-lazy\", \"servers.json\");\n}\n\n/**\n * Save servers to ~/.mcp-lazy/servers.json (merges with existing)\n */\nexport function saveServersBackup(servers: Record<string, ServerConfig>): void {\n const existing = loadServersBackup();\n const merged = { ...existing, ...servers };\n // Never include mcp-lazy itself\n delete merged[\"mcp-lazy\"];\n\n const dir = dirname(getBackupPath());\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n writeFileSync(getBackupPath(), JSON.stringify({ servers: merged }, null, 2) + \"\\n\");\n}\n\n/**\n * Load servers from ~/.mcp-lazy/servers.json\n */\nexport function loadServersBackup(): Record<string, ServerConfig> {\n if (!existsSync(getBackupPath())) {\n return {};\n }\n try {\n const raw = JSON.parse(readFileSync(getBackupPath(), \"utf-8\"));\n return raw.servers ?? {};\n } catch {\n return {};\n }\n}\n\n/**\n * Convert a URL-based server config to a mcp-remote stdio command.\n * e.g., { url: \"https://mcp.notion.com/mcp\", headers: { \"Auth\": \"Bearer x\" } }\n * → { command: \"npx\", args: [\"-y\", \"mcp-remote\", \"https://mcp.notion.com/mcp\", \"--header\", \"Auth:Bearer x\"] }\n */\nexport function convertUrlToMcpRemote(url: string, headers?: Record<string, string>): ServerConfig {\n const args = [\"-y\", \"mcp-remote\", url];\n if (headers) {\n for (const [key, value] of Object.entries(headers)) {\n args.push(\"--header\", `${key}:${value}`);\n }\n }\n return { command: \"npx\", args };\n}\n\n/**\n * Extract MCP servers from a TOML config file (Codex format)\n * Parses [mcp_servers.XXX] sections and extracts command/args.\n * Filters out mcp-lazy entries.\n */\nexport function extractServersFromToml(content: string): Record<string, ServerConfig> {\n const servers: Record<string, ServerConfig> = {};\n // Match all [mcp_servers.XXX] section headers\n const sectionRegex = /^\\[mcp_servers\\.([^\\]]+)\\]/gm;\n let match: RegExpExecArray | null;\n\n while ((match = sectionRegex.exec(content)) !== null) {\n const name = match[1];\n if (name === \"mcp-lazy\") continue;\n\n const sectionStart = match.index + match[0].length;\n // Find next section header (any [xxx]) or end of string\n const nextSection = /^\\[[^\\]]+\\]/m.exec(content.slice(sectionStart));\n const sectionContent = nextSection\n ? content.slice(sectionStart, sectionStart + nextSection.index)\n : content.slice(sectionStart);\n\n // Extract command\n const cmdMatch = /^\\s*command\\s*=\\s*\"([^\"]+)\"/m.exec(sectionContent);\n const command = cmdMatch ? cmdMatch[1] : undefined;\n\n // Extract url (for HTTP/SSE-based servers)\n const urlMatch = /^\\s*url\\s*=\\s*\"([^\"]*)\"/m.exec(sectionContent);\n const serverUrlMatch = /^\\s*serverUrl\\s*=\\s*\"([^\"]*)\"/m.exec(sectionContent);\n const url = urlMatch ? urlMatch[1] : (serverUrlMatch ? serverUrlMatch[1] : undefined);\n\n // Extract args array: args = [\"a\", \"b\", ...]\n const argsMatch = /^\\s*args\\s*=\\s*\\[([^\\]]*)\\]/m.exec(sectionContent);\n const args: string[] = [];\n if (argsMatch) {\n const inner = argsMatch[1];\n const itemRegex = /\"([^\"]*)\"/g;\n let item: RegExpExecArray | null;\n while ((item = itemRegex.exec(inner)) !== null) {\n args.push(item[1]);\n }\n }\n\n // Extract env from [mcp_servers.NAME.env] subsection\n const env: Record<string, string> = {};\n const envSectionRegex = new RegExp(`^\\\\[mcp_servers\\\\.${name}\\\\.env\\\\]`, \"m\");\n if (envSectionRegex.test(content)) {\n const envStart = content.indexOf(`[mcp_servers.${name}.env]`) + `[mcp_servers.${name}.env]`.length;\n const envEnd = /^\\[[^\\]]+\\]/m.exec(content.slice(envStart));\n const envContent = envEnd\n ? content.slice(envStart, envStart + envEnd.index)\n : content.slice(envStart);\n\n const envPairRegex = /^\\s*(\\w+)\\s*=\\s*\"([^\"]*)\"/gm;\n let envMatch: RegExpExecArray | null;\n while ((envMatch = envPairRegex.exec(envContent)) !== null) {\n env[envMatch[1]] = envMatch[2];\n }\n }\n\n // Extract headers from [mcp_servers.NAME.http_headers] subsection\n const headers: Record<string, string> = {};\n const headersSectionRegex = new RegExp(`^\\\\[mcp_servers\\\\.${name}\\\\.http_headers\\\\]`, \"m\");\n if (headersSectionRegex.test(content)) {\n const headersStart = content.indexOf(`[mcp_servers.${name}.http_headers]`) + `[mcp_servers.${name}.http_headers]`.length;\n const headersEnd = /^\\[[^\\]]+\\]/m.exec(content.slice(headersStart));\n const headersContent = headersEnd\n ? content.slice(headersStart, headersStart + headersEnd.index)\n : content.slice(headersStart);\n const headerPairRegex = /^\\s*(\\S+)\\s*=\\s*\"([^\"]*)\"/gm;\n let headerMatch: RegExpExecArray | null;\n while ((headerMatch = headerPairRegex.exec(headersContent)) !== null) {\n headers[headerMatch[1]] = headerMatch[2];\n }\n }\n\n // Extract bearer_token_env_var and resolve to Authorization header\n const bearerMatch = /^\\s*bearer_token_env_var\\s*=\\s*\"([^\"]*)\"/m.exec(sectionContent);\n if (bearerMatch) {\n const envVarName = bearerMatch[1];\n const token = process.env[envVarName];\n if (token) {\n headers[\"Authorization\"] = `Bearer ${token}`;\n }\n }\n\n if (command) {\n servers[name] = { command, args, ...(url && { url }), ...(Object.keys(env).length > 0 && { env }), ...(Object.keys(headers).length > 0 && { headers }) };\n } else if (url) {\n servers[name] = { args, url, ...(Object.keys(env).length > 0 && { env }), ...(Object.keys(headers).length > 0 && { headers }) };\n }\n }\n\n return servers;\n}\n\n/**\n * Extract MCP servers from an agent's config file (JSON format)\n */\nexport function extractServersFromConfig(configPath: string): Record<string, ServerConfig> {\n if (!existsSync(configPath)) return {};\n try {\n const raw = JSON.parse(readFileSync(configPath, \"utf-8\"));\n const parsed = McpJsonSchema.safeParse(raw);\n if (!parsed.success) return {};\n const servers = { ...parsed.data.mcpServers };\n delete servers[\"mcp-lazy\"];\n // Normalize serverUrl → url\n for (const config of Object.values(servers)) {\n if (config.serverUrl && !config.url) {\n config.url = config.serverUrl;\n }\n delete (config as any).serverUrl;\n }\n return servers;\n } catch {\n return {};\n }\n}\n\nfunction getToolCachePath(): string {\n return resolve(homedir(), \".mcp-lazy\", \"tool-cache.json\");\n}\n\n/**\n * Compute a fingerprint for a set of server configs.\n * Any change to server names, commands, or args invalidates the cache.\n */\nexport function computeServerFingerprint(servers: Record<string, ServerConfig>): string {\n const parts = Object.keys(servers)\n .sort()\n .map((name) => {\n const s = servers[name];\n return `${name}:${s.command ?? \"\"}:${(s.args ?? []).join(\",\")}`;\n });\n return createHash(\"sha256\").update(parts.join(\"|\")).digest(\"hex\");\n}\n\n/**\n * Load the tool cache from ~/.mcp-lazy/tool-cache.json.\n * Returns null if the file does not exist or cannot be parsed.\n */\nexport function loadToolCache(): { fingerprint: string; tools: any[] } | null {\n const cachePath = getToolCachePath();\n if (!existsSync(cachePath)) return null;\n try {\n const raw = JSON.parse(readFileSync(cachePath, \"utf-8\"));\n if (typeof raw.fingerprint === \"string\" && Array.isArray(raw.tools)) {\n return raw as { fingerprint: string; tools: any[] };\n }\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * Save the tool cache to ~/.mcp-lazy/tool-cache.json.\n */\nexport function saveToolCache(fingerprint: string, tools: any[]): void {\n const cachePath = getToolCachePath();\n const dir = dirname(cachePath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n writeFileSync(cachePath, JSON.stringify({ fingerprint, tools }, null, 2) + \"\\n\");\n}\n\n/**\n * Extract MCP servers from Opencode's config format.\n * Opencode uses { mcp: { name: { type, command: [...], environment } } }\n */\nexport function extractServersFromOpencodeConfig(configPath: string): Record<string, ServerConfig> {\n if (!existsSync(configPath)) return {};\n try {\n const raw = JSON.parse(readFileSync(configPath, \"utf-8\"));\n const mcpSection = raw.mcp;\n if (!mcpSection || typeof mcpSection !== \"object\") return {};\n\n const servers: Record<string, ServerConfig> = {};\n for (const [name, config] of Object.entries(mcpSection)) {\n if (name === \"mcp-lazy\") continue;\n const cfg = config as any;\n\n if (cfg.type === \"local\" && Array.isArray(cfg.command) && cfg.command.length > 0) {\n // Stdio server: command is array, first element is command, rest are args\n servers[name] = {\n command: cfg.command[0],\n args: cfg.command.slice(1),\n ...(cfg.environment && { env: cfg.environment }),\n };\n } else if (cfg.type === \"remote\" && cfg.url) {\n // HTTP/SSE server\n servers[name] = {\n url: cfg.url,\n args: [],\n ...(cfg.headers && { headers: cfg.headers }),\n };\n }\n }\n return servers;\n } catch {\n return {};\n }\n}\n","export const VERSION = \"0.1.0\";\n","export interface ToolEntry {\n name: string;\n description: string;\n server: string;\n serverDescription: string;\n inputSchema: Record<string, unknown>;\n keywords: string[];\n}\n\nexport interface SearchResult {\n tool_name: string;\n server_name: string;\n description: string;\n relevance_score: number;\n}\n\nexport class ToolRegistry {\n private tools: ToolEntry[] = [];\n\n addTool(entry: ToolEntry): void {\n this.tools.push(entry);\n }\n\n addTools(entries: ToolEntry[]): void {\n this.tools.push(...entries);\n }\n\n getToolCount(): number {\n return this.tools.length;\n }\n\n getAllTools(): ToolEntry[] {\n return [...this.tools];\n }\n\n getServerNames(): string[] {\n return [...new Set(this.tools.map((t) => t.server))];\n }\n\n getToolsByServer(serverName: string): ToolEntry[] {\n return this.tools.filter((t) => t.server === serverName);\n }\n\n findTool(toolName: string, serverName: string): ToolEntry | undefined {\n return this.tools.find(\n (t) => t.name === toolName && t.server === serverName\n );\n }\n\n search(query: string, limit: number = 5): SearchResult[] {\n const queryLower = query.toLowerCase();\n const queryTokens = queryLower.split(/\\s+/).filter(Boolean);\n\n const scored: { entry: ToolEntry; score: number }[] = [];\n\n for (const entry of this.tools) {\n let score = 0;\n const nameLower = entry.name.toLowerCase();\n const descLower = entry.description.toLowerCase();\n const serverDescLower = entry.serverDescription.toLowerCase();\n\n // 1. tool_name exact match\n if (nameLower === queryLower) {\n score += 1.0;\n }\n // 2. tool_name partial match\n else if (\n nameLower.includes(queryLower) ||\n queryTokens.some((t) => nameLower.includes(t))\n ) {\n score += 0.8;\n }\n\n // 3. description keyword match (per token)\n for (const token of queryTokens) {\n if (descLower.includes(token)) {\n score += 0.6;\n }\n }\n\n // 4. server description match\n for (const token of queryTokens) {\n if (serverDescLower.includes(token)) {\n score += 0.4;\n break; // Only count once for server description\n }\n }\n\n // Also check keywords\n for (const token of queryTokens) {\n if (entry.keywords.some((k) => k.toLowerCase().includes(token))) {\n score += 0.3;\n }\n }\n\n if (score > 0) {\n scored.push({ entry, score });\n }\n }\n\n return scored\n .sort((a, b) => b.score - a.score)\n .slice(0, limit)\n .map(({ entry, score }) => ({\n tool_name: entry.name,\n server_name: entry.server,\n description: entry.description,\n relevance_score: Math.round(score * 100) / 100,\n }));\n }\n\n clear(): void {\n this.tools = [];\n }\n}\n\n// Helper to extract keywords from tool name and description\nexport function extractKeywords(name: string, description: string): string[] {\n const text = `${name} ${description}`;\n const words = text\n .replace(/[_-]/g, \" \")\n .replace(/([a-z])([A-Z])/g, \"$1 $2\")\n .toLowerCase()\n .split(/\\s+/)\n .filter((w) => w.length > 2);\n return [...new Set(words)];\n}\n","import { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport { StdioClientTransport } from \"@modelcontextprotocol/sdk/client/stdio.js\";\nimport type { ServerConfig } from \"../utils/config.js\";\nimport { VERSION } from \"../version.js\";\n\ninterface LoadedServer {\n client: Client;\n transport: unknown;\n loadedAt: Date;\n}\n\nexport class ServerLoader {\n private servers = new Map<string, LoadedServer>();\n private serverConfigs: Record<string, ServerConfig>;\n private loading = new Map<string, Promise<Client>>();\n\n constructor(serverConfigs: Record<string, ServerConfig>) {\n this.serverConfigs = serverConfigs;\n }\n\n async getClient(serverName: string): Promise<Client> {\n // Return cached client\n const existing = this.servers.get(serverName);\n if (existing) {\n return existing.client;\n }\n\n // Deduplicate concurrent loads\n const pendingLoad = this.loading.get(serverName);\n if (pendingLoad) {\n return pendingLoad;\n }\n\n const loadPromise = this.loadServer(serverName);\n this.loading.set(serverName, loadPromise);\n\n try {\n const client = await loadPromise;\n return client;\n } finally {\n this.loading.delete(serverName);\n }\n }\n\n private async loadServer(serverName: string): Promise<Client> {\n const config = this.serverConfigs[serverName];\n if (!config) {\n throw new Error(`Unknown server: ${serverName}`);\n }\n\n try {\n return await this.attemptConnect(serverName, config);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (message.includes(\"timed out\")) {\n try {\n return await this.attemptConnect(serverName, config);\n } catch (retryError) {\n throw retryError;\n }\n }\n throw error;\n }\n }\n\n private async attemptConnect(serverName: string, config: ServerConfig): Promise<Client> {\n const timeoutMs = 30000;\n\n if (!config.command) {\n throw new Error(`Server ${serverName} has no command configured`);\n }\n\n const client = new Client({\n name: `mcp-lazy-proxy/${serverName}`,\n version: VERSION,\n });\n\n const env = { ...process.env, ...config.env } as Record<string, string>;\n const transport = new StdioClientTransport({\n command: config.command,\n args: config.args,\n env,\n });\n\n const connectPromise = client.connect(transport);\n const timeoutPromise = new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error(`Server ${serverName} timed out after ${timeoutMs}ms`)), timeoutMs)\n );\n\n await Promise.race([connectPromise, timeoutPromise]);\n\n this.servers.set(serverName, {\n client,\n transport: transport as unknown,\n loadedAt: new Date(),\n });\n\n return client;\n }\n\n getLoadedServers(): string[] {\n return [...this.servers.keys()];\n }\n\n isLoaded(serverName: string): boolean {\n return this.servers.has(serverName);\n }\n\n async closeServer(serverName: string): Promise<void> {\n const server = this.servers.get(serverName);\n if (server) {\n await server.client.close();\n this.servers.delete(serverName);\n }\n }\n\n async closeAll(): Promise<void> {\n const closePromises = [...this.servers.keys()].map((name) =>\n this.closeServer(name)\n );\n await Promise.allSettled(closePromises);\n }\n\n hasConfig(serverName: string): boolean {\n return serverName in this.serverConfigs;\n }\n}\n","import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { z } from \"zod\";\nimport { ToolRegistry } from \"./registry.js\";\nimport { ServerLoader } from \"./loader.js\";\nimport { VERSION } from \"../version.js\";\n\nexport async function createProxyServer(\n registry: ToolRegistry,\n loader: ServerLoader\n): Promise<McpServer> {\n const server = new McpServer({\n name: \"mcp-lazy\",\n version: VERSION,\n });\n\n // Tool 1: mcp_search_tools\n server.tool(\n \"mcp_search_tools\",\n `Search available MCP tools by keyword.\nUse this BEFORE calling any MCP tool.\nReturns matching tool names, server names, and descriptions.\nExample: mcp_search_tools(\"query database\") → postgres-mcp.query_database`,\n {\n query: z.string().describe(\"What you want to do in natural language\"),\n limit: z.number().optional().default(5).describe(\"Max results to return (default: 5)\"),\n },\n async ({ query, limit }) => {\n const results = registry.search(query, limit);\n\n if (results.length === 0) {\n // Suggest similar tools\n const allServers = registry.getServerNames();\n return {\n content: [\n {\n type: \"text\" as const,\n text: JSON.stringify({\n results: [],\n suggestion: `No tools found for \"${query}\". Available servers: ${allServers.join(\", \")}. Try different keywords.`,\n }),\n },\n ],\n };\n }\n\n return {\n content: [\n {\n type: \"text\" as const,\n text: JSON.stringify({ results }),\n },\n ],\n };\n }\n );\n\n // Tool 2: mcp_execute_tool\n server.tool(\n \"mcp_execute_tool\",\n `Execute a specific MCP tool.\nUse tool_name and server_name from mcp_search_tools results.`,\n {\n tool_name: z.string().describe(\"Tool name from mcp_search_tools\"),\n server_name: z.string().describe(\"Server name from mcp_search_tools\"),\n arguments: z.record(z.unknown()).optional().describe(\"Tool arguments\"),\n },\n async ({ tool_name, server_name, arguments: args }) => {\n // Verify tool exists in registry\n const tool = registry.findTool(tool_name, server_name);\n if (!tool) {\n return {\n content: [\n {\n type: \"text\" as const,\n text: JSON.stringify({\n error: `Tool \"${tool_name}\" not found in server \"${server_name}\". Use mcp_search_tools first.`,\n }),\n },\n ],\n isError: true,\n };\n }\n\n // Verify server config exists\n if (!loader.hasConfig(server_name)) {\n return {\n content: [\n {\n type: \"text\" as const,\n text: JSON.stringify({\n error: `Server \"${server_name}\" is not configured.`,\n }),\n },\n ],\n isError: true,\n };\n }\n\n try {\n // Lazy-load server (cached after first call)\n const client = await loader.getClient(server_name);\n\n // Call the actual tool\n const result = await client.callTool({\n name: tool_name,\n arguments: args ?? {},\n });\n\n return {\n content: [\n {\n type: \"text\" as const,\n text: JSON.stringify(result),\n },\n ],\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n const alternatives = registry.search(tool_name, 3);\n return {\n content: [\n {\n type: \"text\" as const,\n text: JSON.stringify({\n error: `Failed to execute ${tool_name} on ${server_name}: ${message}`,\n alternatives: alternatives.length > 0 ? alternatives : undefined,\n }),\n },\n ],\n isError: true,\n };\n }\n }\n );\n\n return server;\n}\n\nexport async function startProxyServer(\n registry: ToolRegistry,\n loader: ServerLoader\n): Promise<void> {\n const server = await createProxyServer(registry, loader);\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n // Graceful shutdown\n process.on(\"SIGINT\", async () => {\n await loader.closeAll();\n process.exit(0);\n });\n process.on(\"SIGTERM\", async () => {\n await loader.closeAll();\n process.exit(0);\n });\n}\n","import { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport { StdioClientTransport } from \"@modelcontextprotocol/sdk/client/stdio.js\";\nimport { VERSION } from \"../version.js\";\n\nexport interface ToolDefinition {\n name: string;\n description?: string;\n inputSchema: Record<string, unknown>;\n}\n\nexport interface ServerConnection {\n client: Client;\n transport: unknown;\n}\n\nexport async function connectToServer(\n command: string,\n args: string[],\n env?: Record<string, string>\n): Promise<ServerConnection> {\n const client = new Client({\n name: \"mcp-lazy-proxy\",\n version: VERSION,\n });\n\n const mergedEnv = { ...process.env, ...env } as Record<string, string>;\n\n const transport = new StdioClientTransport({\n command,\n args,\n env: mergedEnv,\n });\n\n await client.connect(transport);\n return { client, transport };\n}\n\nexport async function listServerTools(\n client: Client\n): Promise<ToolDefinition[]> {\n const allTools: ToolDefinition[] = [];\n let cursor: string | undefined;\n\n do {\n const result = await client.listTools({ cursor });\n for (const tool of result.tools) {\n allTools.push({\n name: tool.name,\n description: tool.description,\n inputSchema: tool.inputSchema as Record<string, unknown>,\n });\n }\n cursor = result.nextCursor;\n } while (cursor);\n\n return allTools;\n}\n\nexport async function callServerTool(\n client: Client,\n toolName: string,\n args?: Record<string, unknown>\n): Promise<unknown> {\n const result = await client.callTool({\n name: toolName,\n arguments: args,\n });\n return result;\n}\n\nexport async function disconnectServer(\n connection: ServerConnection\n): Promise<void> {\n await connection.client.close();\n}\n","#!/usr/bin/env node\n\nimport { Command } from \"commander\";\nimport { runAdd } from \"./cli/add.js\";\nimport { runDoctor } from \"./cli/doctor.js\";\nimport { VERSION } from \"./version.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"mcp-lazy\")\n .description(\"MCP lazy loading proxy - reduce context window token usage by 90%+\")\n .version(VERSION);\n\nprogram\n .command(\"add\")\n .description(\"Register mcp-lazy proxy with an agent\")\n .option(\"--cursor\", \"Register with Cursor\")\n .option(\"--opencode\", \"Register with Opencode\")\n .option(\"--antigravity\", \"Register with Antigravity\")\n .option(\"--codex\", \"Register with Codex\")\n .option(\"--all\", \"Register with all agents\")\n .action(async (options) => {\n await runAdd(options);\n });\n\nprogram\n .command(\"doctor\")\n .description(\"Check installation status and token savings\")\n .action(async () => {\n await runDoctor();\n });\n\nprogram\n .command(\"serve\")\n .description(\"Start the mcp-lazy proxy server (stdio mode)\")\n .action(async () => {\n await runServe();\n });\n\nasync function runServe(): Promise<void> {\n const { loadServersBackup, computeServerFingerprint, loadToolCache, saveToolCache } = await import(\"./utils/config.js\");\n const { ToolRegistry, extractKeywords } = await import(\"./proxy/registry.js\");\n const { ServerLoader } = await import(\"./proxy/loader.js\");\n const { startProxyServer } = await import(\"./proxy/server.js\");\n const { connectToServer, listServerTools, disconnectServer } = await import(\n \"./utils/mcp-client.js\"\n );\n\n // Load servers from ~/.mcp-lazy/servers.json\n const servers = loadServersBackup();\n const serverNames = Object.keys(servers);\n\n if (serverNames.length === 0) {\n console.error(\"No MCP servers found. Check your MCP configurations.\");\n process.exit(1);\n }\n\n // Build the tool registry by connecting to each server once\n const registry = new ToolRegistry();\n const startMs = Date.now();\n\n const fingerprint = computeServerFingerprint(servers);\n const cached = loadToolCache();\n\n if (cached && cached.fingerprint === fingerprint) {\n // Cache hit: load tools directly, skip all connections\n for (const entry of cached.tools) {\n registry.addTool(entry);\n }\n const elapsed = Date.now() - startMs;\n console.error(`mcp-lazy: loaded ${registry.getToolCount()} tools from cache in ${elapsed}ms`);\n } else {\n // Cache miss or config changed: connect to all servers in parallel\n const results = await Promise.allSettled(\n serverNames.map(async (name) => {\n const serverConfig = servers[name];\n if (!serverConfig.command) {\n console.error(`Warning: ${name} has no command configured, skipping`);\n return [];\n }\n const conn = await connectToServer(serverConfig.command, serverConfig.args, serverConfig.env);\n const tools = await listServerTools(conn.client);\n await disconnectServer(conn);\n return tools.map((tool) => ({\n name: tool.name,\n description: tool.description ?? \"\",\n server: name,\n serverDescription: serverConfig.description ?? \"\",\n inputSchema: tool.inputSchema,\n keywords: extractKeywords(tool.name, tool.description ?? \"\"),\n }));\n })\n );\n\n let successCount = 0;\n for (let i = 0; i < results.length; i++) {\n const result = results[i];\n if (result.status === \"fulfilled\") {\n for (const entry of result.value) {\n registry.addTool(entry);\n }\n if (result.value.length > 0) successCount++;\n } else {\n const message = result.reason instanceof Error ? result.reason.message : String(result.reason);\n console.error(`Warning: could not connect to ${serverNames[i]}: ${message}`);\n }\n }\n\n const elapsed = Date.now() - startMs;\n console.error(`mcp-lazy: discovered ${registry.getToolCount()} tools from ${successCount} servers in ${elapsed}ms`);\n\n // Save cache for next startup\n saveToolCache(fingerprint, registry.getAllTools());\n }\n\n if (registry.getToolCount() === 0) {\n console.error(\"No tools discovered from any server. Check your MCP configurations.\");\n process.exit(1);\n }\n\n // Create the loader for lazy re-connections during execution\n const loader = new ServerLoader(servers);\n\n // Start the proxy server (stdio)\n await startProxyServer(registry, loader);\n}\n\nprogram.parse();\n","import { existsSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { resolve, dirname } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { extractServersFromConfig, extractServersFromToml, extractServersFromOpencodeConfig, saveServersBackup, convertUrlToMcpRemote, type ServerConfig } from \"../utils/config.js\";\n\nexport interface AgentInfo {\n name: string;\n displayName: string;\n configPaths: string[];\n format?: \"json\" | \"toml\" | \"opencode\";\n note?: string;\n}\n\nfunction resolvePath(p: string): string {\n const home = homedir();\n const cwd = process.cwd();\n return p.startsWith(\"~\") ? resolve(home, p.slice(2)) : resolve(cwd, p);\n}\n\nexport const AGENTS: AgentInfo[] = [\n {\n name: \"cursor\",\n displayName: \"Cursor\",\n configPaths: [\"~/.cursor/mcp.json\"],\n },\n {\n name: \"opencode\",\n displayName: \"Opencode\",\n configPaths: [\"~/.config/opencode/config.json\"],\n format: \"opencode\",\n },\n {\n name: \"antigravity\",\n displayName: \"Antigravity\",\n configPaths: [\"~/.gemini/antigravity/mcp_config.json\"],\n },\n {\n name: \"codex\",\n displayName: \"Codex\",\n configPaths: [\"~/.codex/config.toml\"],\n format: \"toml\",\n },\n];\n\nexport function detectInstalledAgents(): AgentInfo[] {\n return AGENTS.filter((agent) =>\n agent.configPaths.some((p) => existsSync(resolvePath(p)))\n );\n}\n\nexport function getAgentByName(name: string): AgentInfo | undefined {\n return AGENTS.find((a) => a.name === name);\n}\n\nexport function findAgentConfig(agent: AgentInfo): string | null {\n for (const p of agent.configPaths) {\n const resolved = resolvePath(p);\n if (existsSync(resolved)) {\n return resolved;\n }\n }\n return null;\n}\n\nfunction generateProxyEntry(): Record<string, unknown> {\n return {\n command: \"npx\",\n args: [\"-y\", \"mcp-lazy\", \"serve\"],\n };\n}\n\nexport function registerProxy(\n agent: AgentInfo\n): { configPath: string; created: boolean; serverCount: number } {\n const existing = findAgentConfig(agent);\n const fallback = agent.configPaths[0];\n let targetPath = existing ?? resolvePath(fallback);\n\n let created = false;\n let serverCount = 0;\n\n if (agent.format === \"opencode\") {\n if (existsSync(targetPath)) {\n try {\n const fullConfig = JSON.parse(readFileSync(targetPath, \"utf-8\"));\n const mcpSection = fullConfig.mcp || {};\n\n const serversToBackup: Record<string, ServerConfig> = {};\n\n for (const [name, cfg] of Object.entries(mcpSection)) {\n if (name === \"mcp-lazy\") continue;\n const c = cfg as any;\n if (c.type === \"remote\" && c.url) {\n // Convert URL to mcp-remote\n serversToBackup[name] = convertUrlToMcpRemote(c.url, c.headers);\n } else if (c.type === \"local\" && Array.isArray(c.command) && c.command.length > 0) {\n // Stdio: normalize from opencode format\n serversToBackup[name] = {\n command: c.command[0],\n args: c.command.slice(1),\n ...(c.environment && { env: c.environment }),\n };\n }\n }\n\n serverCount = Object.keys(serversToBackup).length;\n if (serverCount > 0) {\n saveServersBackup(serversToBackup);\n }\n\n // Write: only mcp-lazy (preserve non-mcp keys)\n fullConfig.mcp = {\n \"mcp-lazy\": {\n type: \"local\",\n command: [\"npx\", \"-y\", \"mcp-lazy\", \"serve\"],\n },\n };\n writeFileSync(targetPath, JSON.stringify(fullConfig, null, 2) + \"\\n\");\n } catch {\n // If can't parse, write fresh\n const config = { mcp: { \"mcp-lazy\": { type: \"local\", command: [\"npx\", \"-y\", \"mcp-lazy\", \"serve\"] } } };\n writeFileSync(targetPath, JSON.stringify(config, null, 2) + \"\\n\");\n }\n } else {\n created = true;\n const dir = dirname(targetPath);\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n const config = { mcp: { \"mcp-lazy\": { type: \"local\", command: [\"npx\", \"-y\", \"mcp-lazy\", \"serve\"] } } };\n writeFileSync(targetPath, JSON.stringify(config, null, 2) + \"\\n\");\n }\n return { configPath: targetPath, created, serverCount };\n }\n\n if (agent.format === \"toml\") {\n if (!existsSync(targetPath)) {\n created = true;\n const dir = dirname(targetPath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n }\n\n const existingContent = existsSync(targetPath)\n ? readFileSync(targetPath, \"utf-8\")\n : \"\";\n\n // Extract all servers\n const allServers = extractServersFromToml(existingContent);\n const serversToBackup: Record<string, ServerConfig> = {};\n\n for (const [name, cfg] of Object.entries(allServers)) {\n if (cfg.command) {\n serversToBackup[name] = cfg;\n } else if (cfg.url) {\n // Convert URL to mcp-remote\n serversToBackup[name] = convertUrlToMcpRemote(cfg.url, cfg.headers);\n }\n }\n\n serverCount = Object.keys(serversToBackup).length;\n if (serverCount > 0) {\n saveServersBackup(serversToBackup);\n }\n\n // Strip ALL mcp_servers sections (including URL ones)\n let cleaned = existingContent.replace(\n /\\n?\\[mcp_servers\\.[^\\]]+\\](?:\\n(?!\\[)[^\\n]*)*/g,\n \"\"\n ).trimEnd();\n\n const tomlBlock =\n `\\n\\n[mcp_servers.mcp-lazy]\\n` +\n `command = \"npx\"\\n` +\n `args = [\"-y\", \"mcp-lazy\", \"serve\"]\\n`;\n\n writeFileSync(targetPath, cleaned + tomlBlock);\n\n return { configPath: targetPath, created, serverCount };\n }\n\n // JSON agents: extract ALL servers → convert URL to mcp-remote → backup ALL → write only mcp-lazy\n if (existsSync(targetPath)) {\n // Read raw config to get original field names\n let rawConfig: any = {};\n try {\n rawConfig = JSON.parse(readFileSync(targetPath, \"utf-8\"));\n } catch {}\n const rawServers = rawConfig.mcpServers ?? {};\n\n const serversToBackup: Record<string, ServerConfig> = {};\n\n // Extract normalized servers via Zod\n const normalizedServers = extractServersFromConfig(targetPath);\n\n for (const [name, serverCfg] of Object.entries(rawServers)) {\n if (name === \"mcp-lazy\") continue;\n const cfg = serverCfg as any;\n\n if (cfg.url || cfg.serverUrl) {\n // URL server → convert to mcp-remote stdio command\n const url = cfg.url || cfg.serverUrl;\n const headers = cfg.headers;\n serversToBackup[name] = convertUrlToMcpRemote(url, headers);\n } else if (cfg.command && normalizedServers[name]) {\n // Stdio server → use normalized version\n serversToBackup[name] = normalizedServers[name];\n }\n }\n\n serverCount = Object.keys(serversToBackup).length;\n if (serverCount > 0) {\n saveServersBackup(serversToBackup);\n }\n\n // Write config with ONLY mcp-lazy (no URL servers kept)\n const config = {\n mcpServers: {\n \"mcp-lazy\": generateProxyEntry(),\n },\n };\n\n writeFileSync(targetPath, JSON.stringify(config, null, 2) + \"\\n\");\n } else {\n created = true;\n const dir = dirname(targetPath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n const config = {\n mcpServers: {\n \"mcp-lazy\": generateProxyEntry(),\n },\n };\n\n writeFileSync(targetPath, JSON.stringify(config, null, 2) + \"\\n\");\n }\n\n return { configPath: targetPath, created, serverCount };\n}\n\nexport function isProxyRegistered(agent: AgentInfo): boolean {\n const configPath = findAgentConfig(agent);\n if (!configPath) return false;\n\n if (agent.format === \"opencode\") {\n try {\n const config = JSON.parse(readFileSync(configPath, \"utf-8\"));\n return !!(config.mcp && config.mcp[\"mcp-lazy\"]);\n } catch {\n return false;\n }\n }\n\n if (agent.format === \"toml\") {\n try {\n const content = readFileSync(configPath, \"utf-8\");\n return content.includes(\"[mcp_servers.mcp-lazy]\");\n } catch {\n return false;\n }\n }\n\n try {\n const config = JSON.parse(readFileSync(configPath, \"utf-8\"));\n return !!(config.mcpServers && config.mcpServers[\"mcp-lazy\"]);\n } catch {\n return false;\n }\n}\n","import {\n AGENTS,\n getAgentByName,\n registerProxy,\n type AgentInfo,\n} from \"../agents/index.js\";\n\nconst BANNER = `\n\\x1b[36m\\x1b[1m ███╗ ███╗ ██████╗██████╗ ██╗ █████╗ ███████╗██╗ ██╗\n ████╗ ████║██╔════╝██╔══██╗ ██║ ██╔══██╗╚══███╔╝╚██╗ ██╔╝\n ██╔████╔██║██║ ██████╔╝█████╗██║ ███████║ ███╔╝ ╚████╔╝\n ██║╚██╔╝██║██║ ██╔═══╝ ╚════╝██║ ██╔══██║ ███╔╝ ╚██╔╝\n ██║ ╚═╝ ██║╚██████╗██║ ███████╗██║ ██║███████╗ ██║\n ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚══════╝╚═╝ ╚═╝╚══════╝ ╚═╝\\x1b[0m\n`;\n\ninterface AddOptions {\n cursor?: boolean;\n opencode?: boolean;\n antigravity?: boolean;\n codex?: boolean;\n all?: boolean;\n}\n\nexport async function runAdd(options: AddOptions): Promise<void> {\n console.log(BANNER);\n\n // Determine which agents to register\n let targets: AgentInfo[] = [];\n\n if (options.all) {\n targets = [...AGENTS];\n } else {\n const flagMap: Record<string, boolean | undefined> = {\n cursor: options.cursor,\n opencode: options.opencode,\n antigravity: options.antigravity,\n codex: options.codex,\n };\n\n for (const [name, enabled] of Object.entries(flagMap)) {\n if (enabled) {\n const agent = getAgentByName(name);\n if (agent) {\n targets.push(agent);\n }\n }\n }\n }\n\n if (targets.length === 0) {\n console.log(\" No agent specified. Use one of:\");\n console.log(\" mcp-lazy add --cursor\");\n console.log(\" mcp-lazy add --opencode\");\n console.log(\" mcp-lazy add --antigravity\");\n console.log(\" mcp-lazy add --codex\");\n console.log(\" mcp-lazy add --all\\n\");\n process.exit(1);\n }\n\n console.log(\" Registering mcp-lazy proxy...\\n\");\n\n for (const agent of targets) {\n try {\n const { configPath, created, serverCount } = registerProxy(agent);\n const action = created ? \"created\" : \"updated\";\n const servers = serverCount > 0 ? ` (${serverCount} servers captured)` : \"\";\n console.log(` ✓ ${agent.displayName}: ${action} ${configPath}${servers}`);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.log(` ✗ ${agent.displayName}: failed - ${message}`);\n }\n }\n\n console.log(\"\\n Done! Restart your agents to activate mcp-lazy.\\n\");\n}\n","import { loadServersBackup } from \"../utils/config.js\";\nimport { AGENTS, isProxyRegistered } from \"../agents/index.js\";\n\nconst TOKENS_PER_TOOL = 650;\nconst PROXY_BASE_TOKENS = 350;\n\nexport async function runDoctor(): Promise<void> {\n let hasIssues = false;\n\n console.log(\"\\nmcp-lazy status check\\n\");\n\n // 1. Check Node.js version\n const nodeVersion = process.versions.node;\n const major = parseInt(nodeVersion.split(\".\")[0], 10);\n if (major >= 18) {\n console.log(` ✓ Node.js ${nodeVersion}`);\n } else {\n console.log(` ✗ Node.js ${nodeVersion} (requires >= 18)`);\n hasIssues = true;\n }\n\n // 2. Check servers backup\n const servers = loadServersBackup();\n const serverCount = Object.keys(servers).length;\n\n if (serverCount > 0) {\n const serverNames = Object.keys(servers);\n console.log(` ✓ ${serverCount} MCP server(s) registered`);\n for (const name of serverNames) {\n console.log(` - ${name}`);\n }\n } else {\n console.log(` ✗ No MCP servers registered -> run 'mcp-lazy add --<agent>'`);\n hasIssues = true;\n }\n\n // 3. Check agent registrations\n console.log(\"\");\n let registeredCount = 0;\n for (const agent of AGENTS) {\n const registered = isProxyRegistered(agent);\n if (registered) {\n console.log(` ✓ ${agent.displayName} registered`);\n registeredCount++;\n } else {\n console.log(` - ${agent.displayName} not registered -> mcp-lazy add --${agent.name}`);\n }\n }\n\n if (registeredCount === 0) {\n console.log(\"\\n No agents registered. Run 'mcp-lazy add --<agent>' to register.\");\n hasIssues = true;\n }\n\n // 4. Token savings estimation\n if (serverCount > 0) {\n const estimatedTools = serverCount * 15;\n const estimatedTokens = estimatedTools * TOKENS_PER_TOOL;\n const savings = estimatedTokens > 0\n ? Math.round(((estimatedTokens - PROXY_BASE_TOKENS) / estimatedTokens) * 100)\n : 0;\n\n console.log(`\\n Token savings estimate:`);\n console.log(` ${serverCount} server(s) registered`);\n console.log(` Without mcp-lazy: ~${estimatedTokens.toLocaleString()} tokens`);\n console.log(` With mcp-lazy: ${PROXY_BASE_TOKENS.toLocaleString()} tokens`);\n console.log(` Estimated savings: ${savings}%`);\n }\n\n console.log(\"\");\n if (!hasIssues) {\n console.log(\" All checks passed.\\n\");\n } else {\n console.log(\" Some issues found. See above for details.\\n\");\n }\n}\n"],"mappings":"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","names":["z","Client","StdioClientTransport","existsSync","readFileSync","writeFileSync","mkdirSync","resolve","dirname","homedir","existsSync","readFileSync","writeFileSync","dirname","mkdirSync","loadServersBackup","computeServerFingerprint","loadToolCache","saveToolCache","ToolRegistry","extractKeywords","ServerLoader","startProxyServer","connectToServer","listServerTools","disconnectServer"]}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# mcp-lazy
|
|
2
|
+
|
|
3
|
+
[English](../README.md)
|
|
4
|
+
|
|
5
|
+
> MCP 컨텍스트 윈도우 토큰 사용량을 90% 이상 절감합니다. 명령어 한 줄이면 끝.
|
|
6
|
+
|
|
7
|
+
- MCP 서버들은 시작할 때 모든 툴 정의를 컨텍스트 윈도우에 로딩합니다 — 사용하기도 전에요. 서버가 5~10개면 컨텍스트 윈도우의 30~50%를 차지할 수 있습니다. **mcp-lazy**는 모든 MCP 서버를 하나의 경량 프록시로 묶어서, 필요할 때만 툴을 로딩합니다.
|
|
8
|
+
|
|
9
|
+
## 빠른 시작
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx mcp-lazy add --cursor
|
|
13
|
+
npx mcp-lazy add --codex
|
|
14
|
+
npx mcp-lazy add --antigravity
|
|
15
|
+
npx mcp-lazy add --all # 또는 한번에 전부 등록
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
- 끝! `add` 명령어가 에이전트의 기존 MCP 설정을 읽어서 모든 서버 정의를 `~/.mcp-lazy/servers.json`에 저장하고, 에이전트 설정을 mcp-lazy 프록시 항목 하나로 교체합니다.
|
|
19
|
+
|
|
20
|
+
> **Tip:** 새로운 MCP 서버를 설치했다면 `npx mcp-lazy add --<agent>`를 다시 실행하세요 — 추가 작업 없이 반영됩니다.
|
|
21
|
+
|
|
22
|
+
<br>
|
|
23
|
+
|
|
24
|
+
## 작동 원리
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
mcp-lazy 적용 전:
|
|
28
|
+
에이전트 → MCP서버A (50툴) + 서버B (30툴) + 서버C (20툴)
|
|
29
|
+
= 시작 시 100개 툴 전부 로딩 (~67,000 토큰)
|
|
30
|
+
|
|
31
|
+
mcp-lazy 적용 후:
|
|
32
|
+
에이전트 → mcp-lazy 프록시 (2개 툴만, ~350 토큰)
|
|
33
|
+
↓ 필요할 때만
|
|
34
|
+
서버A / B / C (on-demand 로딩)
|
|
35
|
+
URL 서버 (Notion, Slack 등) — mcp-remote 브릿지 경유
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
프록시는 단 2개의 툴만 노출합니다:
|
|
39
|
+
|
|
40
|
+
- **mcp_search_tools** — 키워드로 사용 가능한 툴 검색
|
|
41
|
+
- **mcp_execute_tool** — 툴 실행 (최초 호출 시 서버 자동 시작)
|
|
42
|
+
|
|
43
|
+

|
|
44
|
+
|
|
45
|
+
<br>
|
|
46
|
+
|
|
47
|
+
## `add` 명령어가 하는 일
|
|
48
|
+
|
|
49
|
+
`npx mcp-lazy add --<agent>`를 실행하면:
|
|
50
|
+
|
|
51
|
+
1. 에이전트의 기존 MCP 서버 설정을 읽습니다
|
|
52
|
+
2. 모든 서버 정의를 추출합니다 (stdio 및 URL 기반 모두)
|
|
53
|
+
3. URL 서버(Notion, Slack 등 OAuth 인증이 필요한 서비스)를 `npx mcp-remote <url>` stdio 명령어로 자동 변환합니다
|
|
54
|
+
4. 모든 내용을 `~/.mcp-lazy/servers.json`에 저장합니다
|
|
55
|
+
5. 에이전트 설정을 mcp-lazy 프록시 항목 하나로 교체합니다
|
|
56
|
+
|
|
57
|
+
프록시는 실행 시 `~/.mcp-lazy/servers.json`을 읽습니다 — 설정 완료 후 모든 서버 정의는 이 파일에 저장됩니다.
|
|
58
|
+
|
|
59
|
+
<br>
|
|
60
|
+
|
|
61
|
+
## 지원 에이전트
|
|
62
|
+
|
|
63
|
+
| 에이전트 | 상태 |
|
|
64
|
+
| ----------- | ---------------------- |
|
|
65
|
+
| Cursor | ✓ 지원 |
|
|
66
|
+
| Opencode | ✓ 지원 |
|
|
67
|
+
| Antigravity | ✓ 지원 |
|
|
68
|
+
| Codex | ✓ 지원 |
|
|
69
|
+
| Claude Code | 네이티브 지원 (불필요) |
|
|
70
|
+
|
|
71
|
+
<br>
|
|
72
|
+
|
|
73
|
+
## 명령어
|
|
74
|
+
|
|
75
|
+
### `npx mcp-lazy add`
|
|
76
|
+
|
|
77
|
+
- 에이전트에 프록시를 등록합니다:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
npx mcp-lazy add --cursor # Cursor에 등록
|
|
81
|
+
npx mcp-lazy add --antigravity # Antigravity에 등록
|
|
82
|
+
npx mcp-lazy add --all # 모든 에이전트에 등록
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
- 옵션:
|
|
86
|
+
- `--cursor`, `--opencode`, `--antigravity`, `--codex` — 대상 에이전트
|
|
87
|
+
- `--all` — 모든 에이전트에 등록
|
|
88
|
+
|
|
89
|
+
### `npx mcp-lazy doctor`
|
|
90
|
+
|
|
91
|
+
- 설치 상태를 진단합니다:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
$ npx mcp-lazy doctor
|
|
95
|
+
|
|
96
|
+
✓ Node.js 18+ 설치됨
|
|
97
|
+
✓ 7개 MCP 서버 등록됨
|
|
98
|
+
- github, notion, slack, postgres, filesystem, memory, puppeteer
|
|
99
|
+
✓ Cursor: 등록됨
|
|
100
|
+
|
|
101
|
+
토큰 절감: 67,300 → 350 (99.5% 절감)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
<br>
|
|
105
|
+
|
|
106
|
+
## URL 및 OAuth 지원
|
|
107
|
+
|
|
108
|
+
일부 MCP 서버는 URL로 호스팅되며 OAuth 인증이 필요합니다 (Notion, Slack, Linear 등). `add` 명령어는 이런 서버들을 `npx mcp-remote`를 사용하는 stdio 명령어로 자동 변환합니다:
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
URL 서버: https://mcp.notion.com/sse
|
|
112
|
+
↓ 자동 변환
|
|
113
|
+
stdio: npx mcp-remote https://mcp.notion.com/sse
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
즉, 로컬 stdio 서버와 OAuth 인증이 필요한 원격 URL 서버 모두 수동 변환 없이 mcp-lazy를 통해 프록시됩니다.
|
|
117
|
+
|
|
118
|
+
<br>
|
|
119
|
+
|
|
120
|
+
## 검색 알고리즘
|
|
121
|
+
|
|
122
|
+
에이전트가 `mcp_search_tools("DB 쿼리 실행")`을 호출하면, 프록시가 등록된 모든 툴에서 가중치 기반 검색을 수행합니다:
|
|
123
|
+
|
|
124
|
+
| 매칭 유형 | 점수 |
|
|
125
|
+
| ----------------- | ---- |
|
|
126
|
+
| 툴 이름 정확 일치 | +1.0 |
|
|
127
|
+
| 툴 이름 부분 일치 | +0.8 |
|
|
128
|
+
| 설명 키워드 일치 | +0.6 |
|
|
129
|
+
| 서버 설명 일치 | +0.4 |
|
|
130
|
+
|
|
131
|
+
결과는 관련도 순으로 정렬되어 에이전트에 반환됩니다.
|
|
132
|
+
|
|
133
|
+
<br>
|
|
134
|
+
|
|
135
|
+
## FAQ
|
|
136
|
+
|
|
137
|
+
### Q: 설치 중 "Error: Unexpected error"가 발생합니다
|
|
138
|
+
|
|
139
|
+
- 설정 파일 디렉토리에 대한 읽기/쓰기 권한이 있는지 확인하세요. 예시:
|
|
140
|
+
|
|
141
|
+
> Cursor: `~/.cursor/mcp.json` <br>
|
|
142
|
+
> Codex: `~/.codex/config.toml` <br>
|
|
143
|
+
> Opencode: `~/.config/opencode/config.json`<br>
|
|
144
|
+
> Antigravity: `~/.gemini/antigravity/mcp_config.json`
|
|
145
|
+
|
|
146
|
+
- `ls -la` 명령어로 해당 경로의 권한을 확인하세요. 필요 시 `chmod 644 <경로>`로 수정할 수 있습니다.
|
|
147
|
+
|
|
148
|
+
### Q: mcp-lazy 설정 이후 새로운 MCP 서버를 추가했습니다. 어떻게 반영하나요?
|
|
149
|
+
|
|
150
|
+
- 새 서버를 에이전트의 MCP 설정에 평소처럼 추가한 뒤, add 명령어를 다시 실행하면 됩니다:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
npx mcp-lazy add --cursor # 새 서버를 자동으로 감지하여 반영
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
- mcp-lazy가 새 서버를 감지하여 `~/.mcp-lazy/servers.json`에 추가하고, 프록시 설정은 그대로 유지합니다.
|
|
157
|
+
|
|
158
|
+
<br>
|
|
159
|
+
|
|
160
|
+
## 지원 범위
|
|
161
|
+
|
|
162
|
+
- mcp-lazy는 현재 **글로벌 MCP 설정만 지원**합니다 (예: `~/.cursor/mcp.json`). 프로젝트 레벨 MCP 설정 (예: 프로젝트 루트의 `.cursor/mcp.json`)은 아직 지원하지 않습니다.
|
|
163
|
+
|
|
164
|
+
<br>
|
|
165
|
+
|
|
166
|
+
## 요구사항
|
|
167
|
+
|
|
168
|
+
- Node.js 18+
|
|
169
|
+
- 기존 MCP 서버 설정 (글로벌 스코프)
|
|
170
|
+
|
|
171
|
+
## 라이선스
|
|
172
|
+
|
|
173
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mcp-lazy",
|
|
3
|
+
"version": "0.1.4",
|
|
4
|
+
"description": "MCP lazy loading proxy for AI agents - reduce context window token usage by 90%+",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"mcp-lazy": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"docs",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsup",
|
|
17
|
+
"dev": "tsx src/index.ts",
|
|
18
|
+
"test": "vitest run",
|
|
19
|
+
"test:watch": "vitest",
|
|
20
|
+
"lint": "tsc --noEmit"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"mcp",
|
|
24
|
+
"lazy-loading",
|
|
25
|
+
"proxy",
|
|
26
|
+
"ai-agent",
|
|
27
|
+
"cursor"
|
|
28
|
+
],
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
32
|
+
"commander": "^13.1.0",
|
|
33
|
+
"zod": "^3.24.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"tsup": "^8.4.0",
|
|
37
|
+
"tsx": "^4.19.0",
|
|
38
|
+
"typescript": "^5.7.0",
|
|
39
|
+
"vitest": "^3.1.0",
|
|
40
|
+
"@types/node": "^22.0.0"
|
|
41
|
+
},
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=18.0.0"
|
|
44
|
+
}
|
|
45
|
+
}
|