diff --git a/src/aws-kb-retrieval-server/README.md b/src/aws-kb-retrieval-server/README.md index ed19f1ee..84855568 100644 --- a/src/aws-kb-retrieval-server/README.md +++ b/src/aws-kb-retrieval-server/README.md @@ -34,7 +34,7 @@ Add this to your `claude_desktop_config.json`: "mcpServers": { "aws-kb-retrieval": { "command": "docker", - "args": [ "run", "-i", "--rm", "-e", "AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID", "-e", "AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY", "-e", "AWS_REGION=$AWS_REGION", "ai/mcp-aws-kb-retrieval-server" ], + "args": [ "run", "-i", "--rm", "-e", "AWS_ACCESS_KEY_ID", "-e", "AWS_SECRET_ACCESS_KEY", "-e", "AWS_REGION", "ai/mcp-aws-kb-retrieval-server" ], "env": { "AWS_ACCESS_KEY_ID": "YOUR_ACCESS_KEY_HERE", "AWS_SECRET_ACCESS_KEY": "YOUR_SECRET_ACCESS_KEY_HERE", diff --git a/src/brave-search/README.md b/src/brave-search/README.md index d94ecdf1..1907fc9c 100644 --- a/src/brave-search/README.md +++ b/src/brave-search/README.md @@ -48,7 +48,7 @@ Add this to your `claude_desktop_config.json`: "-i", "--rm", "-e", - "BRAVE_API_KEY=$BRAVE_API_KEY", + "BRAVE_API_KEY", "ai/mcp-brave-search" ], "env": { diff --git a/src/everart/README.md b/src/everart/README.md index 04b07578..94c0e33b 100644 --- a/src/everart/README.md +++ b/src/everart/README.md @@ -17,7 +17,7 @@ Add to Claude Desktop config: "mcpServers": { "everart": { "command": "docker", - "args": ["run", "-i", "--rm", "-e", "EVERART_API_KEY=$EVERART_API_KEY", "ai/mcp-everart"], + "args": ["run", "-i", "--rm", "-e", "EVERART_API_KEY", "ai/mcp-everart"], "env": { "EVERART_API_KEY": "your_key_here" } diff --git a/src/filesystem/Dockerfile b/src/filesystem/Dockerfile index d762d39e..0cfa3718 100644 --- a/src/filesystem/Dockerfile +++ b/src/filesystem/Dockerfile @@ -22,4 +22,6 @@ ENV NODE_ENV=production RUN npm ci --ignore-scripts --omit-dev -ENTRYPOINT ["node", "dist/index.js"] \ No newline at end of file +WORKDIR /projects + +ENTRYPOINT ["node", "/app/dist/index.js"] \ No newline at end of file diff --git a/src/filesystem/README.md b/src/filesystem/README.md index d7e1fdc9..74b83593 100644 --- a/src/filesystem/README.md +++ b/src/filesystem/README.md @@ -109,6 +109,7 @@ Add this to your `claude_desktop_config.json`: Note: you can provide sandboxed directories to the server by mounting them to `/projects`. Adding the `ro` flag will make the directory readonly by the server. ### Docker +Note: all directories must be mounted to `/projects` by default. ```json { @@ -122,7 +123,6 @@ Note: you can provide sandboxed directories to the server by mounting them to `/ "--mount", "type=bind,src=/Users/username/Desktop,dst=/projects/Desktop", "--mount", "type=bind,src=/path/to/other/allowed/dir,dst=/projects/other/allowed/dir,ro", "--mount", "type=bind,src=/path/to/file.txt,dst=/projects/path/to/file.txt", - "--env", "DOCKER_ROOT_WORKSPACE=/projects", "ai/mcp-filesystem" ] } diff --git a/src/filesystem/index.ts b/src/filesystem/index.ts index e67887e7..f073a83e 100644 --- a/src/filesystem/index.ts +++ b/src/filesystem/index.ts @@ -20,8 +20,15 @@ const args = process.argv.slice(2); let allowedDirectories: string[] = []; -if (process.env.DOCKER_ROOT_WORKSPACE) { - allowedDirectories = await fs.readdir(process.env.DOCKER_ROOT_WORKSPACE); +let hasProjectsDirectory = false; + +// If the projects directory has contents, use it as the root workspace +const projectsContents = await fs.readdir('/projects'); +if (projectsContents.length > 0) { + hasProjectsDirectory = true; + allowedDirectories = projectsContents.map(dir => + normalizePath(path.resolve(expandHome(dir))) + ); } else { if (args.length === 0) { @@ -51,7 +58,7 @@ function expandHome(filepath: string): string { await Promise.all(allowedDirectories.map(async (dir) => { try { const stats = await fs.stat(dir); - if (!stats.isDirectory()) { + if (!stats.isDirectory() && !hasProjectsDirectory) { console.error(`Error: ${dir} is not a directory`); process.exit(1); } @@ -262,7 +269,7 @@ function createUnifiedDiff(originalContent: string, newContent: string, filepath async function applyFileEdits( filePath: string, - edits: Array<{oldText: string, newText: string}>, + edits: Array<{ oldText: string, newText: string }>, dryRun = false ): Promise { // Read file content and normalize line endings @@ -398,10 +405,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => { { name: "directory_tree", description: - "Get a recursive tree view of files and directories as a JSON structure. " + - "Each entry includes 'name', 'type' (file/directory), and 'children' for directories. " + - "Files have no children array, while directories always have a children array (which may be empty). " + - "The output is formatted with 2-space indentation for readability. Only works within allowed directories.", + "Get a recursive tree view of files and directories as a JSON structure. " + + "Each entry includes 'name', 'type' (file/directory), and 'children' for directories. " + + "Files have no children array, while directories always have a children array (which may be empty). " + + "The output is formatted with 2-space indentation for readability. Only works within allowed directories.", inputSchema: zodToJsonSchema(DirectoryTreeArgsSchema) as ToolInput, }, { @@ -538,49 +545,49 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { }; } - case "directory_tree": { - const parsed = DirectoryTreeArgsSchema.safeParse(args); - if (!parsed.success) { - throw new Error(`Invalid arguments for directory_tree: ${parsed.error}`); - } - - interface TreeEntry { - name: string; - type: 'file' | 'directory'; - children?: TreeEntry[]; - } - - async function buildTree(currentPath: string): Promise { - const validPath = await validatePath(currentPath); - const entries = await fs.readdir(validPath, {withFileTypes: true}); - const result: TreeEntry[] = []; + case "directory_tree": { + const parsed = DirectoryTreeArgsSchema.safeParse(args); + if (!parsed.success) { + throw new Error(`Invalid arguments for directory_tree: ${parsed.error}`); + } - for (const entry of entries) { - const entryData: TreeEntry = { - name: entry.name, - type: entry.isDirectory() ? 'directory' : 'file' - }; + interface TreeEntry { + name: string; + type: 'file' | 'directory'; + children?: TreeEntry[]; + } - if (entry.isDirectory()) { - const subPath = path.join(currentPath, entry.name); - entryData.children = await buildTree(subPath); - } + async function buildTree(currentPath: string): Promise { + const validPath = await validatePath(currentPath); + const entries = await fs.readdir(validPath, { withFileTypes: true }); + const result: TreeEntry[] = []; - result.push(entryData); - } + for (const entry of entries) { + const entryData: TreeEntry = { + name: entry.name, + type: entry.isDirectory() ? 'directory' : 'file' + }; - return result; + if (entry.isDirectory()) { + const subPath = path.join(currentPath, entry.name); + entryData.children = await buildTree(subPath); } - const treeData = await buildTree(parsed.data.path); - return { - content: [{ - type: "text", - text: JSON.stringify(treeData, null, 2) - }], - }; + result.push(entryData); + } + + return result; } + const treeData = await buildTree(parsed.data.path); + return { + content: [{ + type: "text", + text: JSON.stringify(treeData, null, 2) + }], + }; + } + case "move_file": { const parsed = MoveFileArgsSchema.safeParse(args); if (!parsed.success) { @@ -614,9 +621,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { const validPath = await validatePath(parsed.data.path); const info = await getFileStats(validPath); return { - content: [{ type: "text", text: Object.entries(info) - .map(([key, value]) => `${key}: ${value}`) - .join("\n") }], + content: [{ + type: "text", text: Object.entries(info) + .map(([key, value]) => `${key}: ${value}`) + .join("\n") + }], }; } diff --git a/src/github/README.md b/src/github/README.md index d20ba9cc..a63e803b 100644 --- a/src/github/README.md +++ b/src/github/README.md @@ -236,7 +236,7 @@ To use this with Claude Desktop, add the following to your `claude_desktop_confi "-i", "--rm", "-e", - "GITHUB_PERSONAL_ACCESS_TOKEN=$GITHUB_PERSONAL_ACCESS_TOKEN", + "GITHUB_PERSONAL_ACCESS_TOKEN", "ai/mcp-github" ], "env": { diff --git a/src/gitlab/README.md b/src/gitlab/README.md index 7feb06b4..d2dff023 100644 --- a/src/gitlab/README.md +++ b/src/gitlab/README.md @@ -118,9 +118,9 @@ Add the following to your `claude_desktop_config.json`: "args": [ "run", "-e", - "GITLAB_PERSONAL_ACCESS_TOKEN=$GITLAB_PERSONAL_ACCESS_TOKEN", + "GITLAB_PERSONAL_ACCESS_TOKEN", "-e", - "GITLAB_API_URL=$GITLAB_API_URL", + "GITLAB_API_URL", "ai/mcp-gitlab" ], "env": { diff --git a/src/google-maps/README.md b/src/google-maps/README.md index 46a55c72..867aa2f5 100644 --- a/src/google-maps/README.md +++ b/src/google-maps/README.md @@ -71,7 +71,7 @@ Add the following to your `claude_desktop_config.json`: "-i", "--rm", "-e", - "GOOGLE_MAPS_API_KEY=$GOOGLE_MAPS_API_KEY", + "GOOGLE_MAPS_API_KEY", "ai/mcp-google-maps" ], "env": { diff --git a/src/puppeteer/README.md b/src/puppeteer/README.md index 715f9d0a..d13ef2a5 100644 --- a/src/puppeteer/README.md +++ b/src/puppeteer/README.md @@ -72,7 +72,7 @@ Here's the Claude Desktop configuration to use the Puppeter server: "mcpServers": { "puppeteer": { "command": "docker", - "args": ["run", "-i", "--rm", "--init", "ai/mcp-puppeteer"] + "args": ["run", "-i", "--rm", "--init", "-e", "DOCKER_CONTAINER=true", "ai/mcp-puppeteer"] } } } diff --git a/src/puppeteer/index.ts b/src/puppeteer/index.ts index 2dc98371..92b6c3ed 100644 --- a/src/puppeteer/index.ts +++ b/src/puppeteer/index.ts @@ -108,7 +108,9 @@ const screenshots = new Map(); async function ensureBrowser() { if (!browser) { - browser = await puppeteer.launch({ headless: true, args: ["--no-sandbox"] }); + const npx_args = { headless: false } + const docker_args = { headless: true, args: ["--no-sandbox", "--single-process", "--no-zygote"] } + browser = await puppeteer.launch(process.env.DOCKER_CONTAINER ? docker_args : npx_args); const pages = await browser.pages(); page = pages[0]; @@ -283,8 +285,8 @@ async function handleToolCall(name: string, args: any): Promise window.mcpHelper.logs.push(`[${method}] ${args.join(' ')}`); (window.mcpHelper.originalConsole as any)[method](...args); }; - } ); - } ); + }); + }); const result = await page.evaluate( args.script ); diff --git a/src/slack/README.md b/src/slack/README.md index b0523ee3..74d3dada 100644 --- a/src/slack/README.md +++ b/src/slack/README.md @@ -121,9 +121,9 @@ Add the following to your `claude_desktop_config.json`: "-i", "--rm", "-e", - "SLACK_BOT_TOKEN=$SLACK_BOT_TOKEN", + "SLACK_BOT_TOKEN", "-e", - "SLACK_TEAM_ID=$SLACK_TEAM_ID", + "SLACK_TEAM_ID", "ai/mcp-slack" ], "env": {