[
  {
    "path": ".github/workflows/npm-publish.yaml",
    "content": "name: Publish to NPM\n\non:\n  push:\n    branches:\n      - main\n  release:\n    types: [created]\n\njobs:\n  build-and-publish:\n    runs-on: ubuntu-latest\n    permissions:\n      contents: write\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          fetch-depth: 0\n          token: ${{ secrets.GITHUB_TOKEN }}\n      \n      - name: Setup Node.js\n        uses: actions/setup-node@v4\n        with:\n          node-version: '18'\n          registry-url: 'https://registry.npmjs.org'\n          \n      - name: Configure Git\n        run: |\n          git config --local user.email \"action@github.com\"\n          git config --local user.name \"GitHub Action\"\n        \n      - name: Install dependencies\n        run: npm ci\n        \n      - name: Bump version\n        if: github.event_name == 'push' && !contains(github.event.head_commit.message, '[skip ci]')\n        run: |\n          npm version patch -m \"Bump version to %s [skip ci]\"\n          git push\n          git push --tags\n        \n      - name: Build\n        run: npm run build\n        \n      - name: Publish to NPM\n        if: success()\n        run: npm publish --access public\n        env:\n          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}"
  },
  {
    "path": ".gitignore",
    "content": "node_modules/\nbuild/\n*.log\n.env\n.env.local\n.env.*.local"
  },
  {
    "path": ".npmignore",
    "content": "# Source\nsrc/\n\n# Development configs\ntsconfig.json\n.github/\n.gitignore\n\n# Dependencies\nnode_modules/\n\n# IDE\n.vscode/\n.idea/\n\n# Logs\n*.log\nnpm-debug.log*\n\n# Test files\n__tests__/\n*.test.ts\n*.spec.ts\n\n# Other\n.DS_Store\n"
  },
  {
    "path": "Dockerfile",
    "content": "# Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile\n# Use the official Node.js 18 image as the base image\nFROM node:18-slim AS build\n\n# Set the working directory in the container\nWORKDIR /app\n\n# Copy package.json and package-lock.json to the working directory\nCOPY package.json package-lock.json ./\n\n# Install the project dependencies\nRUN npm install\n\n# Copy the rest of the application code\nCOPY . .\n\n# Build the application\nRUN npm run build\n\n# Create a new stage for the production image\nFROM node:18-slim\n\n# Set the working directory in the container\nWORKDIR /app\n\n# Copy the built application from the build stage\nCOPY --from=build /app/build /app/build\nCOPY --from=build /app/package.json /app/package.json\nCOPY --from=build /app/package-lock.json /app/package-lock.json\n\n# Install only production dependencies\nRUN npm ci --omit=dev\n\n# Set environment variable for the reports directory\nENV MAIGRET_REPORTS_DIR=/app/reports\n\n# Create the reports directory\nRUN mkdir -p $MAIGRET_REPORTS_DIR\n\n# Expose the port on which the application will run\nEXPOSE 3000\n\n# Command to run the application\nENTRYPOINT [\"node\", \"build/index.js\"]"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2024 Burt The Coder\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Maigret MCP Server\n[![smithery badge](https://smithery.ai/badge/mcp-maigret)](https://smithery.ai/server/mcp-maigret)\n\nA Model Context Protocol (MCP) server for [maigret](https://github.com/soxoj/maigret), a powerful OSINT tool that collects user account information from various public sources. This server provides tools for searching usernames across social networks and analyzing URLs. It is designed to integrate seamlessly with MCP-compatible applications like [Claude Desktop](https://claude.ai).\n\n<a href=\"https://glama.ai/mcp/servers/knnpcz651x\"><img width=\"380\" height=\"200\" src=\"https://glama.ai/mcp/servers/knnpcz651x/badge\" alt=\"mcp-maigret MCP server\" /></a>\n\n\n## ⚠️ Warning\n\nThis tool is designed for legitimate OSINT research purposes. Please:\n- Only search for information that is publicly available\n- Respect privacy and data protection laws\n- Follow the terms of service of the platforms being searched\n- Use responsibly and ethically\n- Be aware that some sites may rate-limit or block automated searches\n\n## Security\n\nThis server implements several security measures to prevent command injection attacks:\n\n### Input Validation\n- **Usernames**: Only alphanumeric characters, underscores, hyphens, and periods are allowed (max 100 characters)\n- **URLs**: Must be valid HTTP/HTTPS URLs without shell metacharacters\n- **Tags**: Only alphanumeric characters, underscores, and hyphens are allowed\n\n### Safe Command Execution\n- Uses `execFile()` instead of `exec()` to prevent shell interpolation\n- All command arguments are passed as arrays, not concatenated strings\n- Docker commands are executed without shell interpretation\n\n### Reporting Security Issues\nIf you discover a security vulnerability, please report it by opening an issue or contacting the maintainers directly. We take security seriously and will respond promptly.\n\n## Requirements\n\n- Node.js (v18 or later)\n- Docker\n- macOS, Linux, or Windows with Docker Desktop installed\n- Write access to the reports directory\n\n## Quick Start\n\n### Installing via Smithery\n\nTo install Maigret for Claude Desktop automatically via [Smithery](https://smithery.ai/server/mcp-maigret):\n\n```bash\nnpx -y @smithery/cli install mcp-maigret --client claude\n```\n\n### Installing Manually\n1. Install Docker:\n   - macOS: Install [Docker Desktop](https://www.docker.com/products/docker-desktop)\n   - Linux: Follow the [Docker Engine installation guide](https://docs.docker.com/engine/install/)\n\n2. Install the server globally via npm:\n```bash\nnpm install -g mcp-maigret\n```\n\n3. Create a reports directory:\n```bash\nmkdir -p /path/to/reports/directory\n```\n\n4. Add to your Claude Desktop configuration file:\n```json\n{\n  \"mcpServers\": {\n    \"maigret\": {\n      \"command\": \"mcp-maigret\",\n      \"env\": {\n        \"MAIGRET_REPORTS_DIR\": \"/path/to/reports/directory\"\n      }\n    }\n  }\n}\n```\n\nConfiguration file location:\n- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`\n- Windows: `%APPDATA%\\Claude\\claude_desktop_config.json`\n\n5. Restart Claude Desktop\n\n## Alternative Setup (From Source)\n\nIf you prefer to run from source or need to modify the code:\n\n1. Clone and build:\n```bash\ngit clone <repository_url>\ncd mcp-maigret\nnpm install\nnpm run build\n```\n\n2. Add to your Claude Desktop configuration:\n```json\n{\n  \"mcpServers\": {\n    \"maigret\": {\n      \"command\": \"node\",\n      \"args\": [\"/absolute/path/to/mcp-maigret/build/index.js\"],\n      \"env\": {\n        \"MAIGRET_REPORTS_DIR\": \"/path/to/reports/directory\"\n      }\n    }\n  }\n}\n```\n\n## Features\n\n- **Username Search**: Search for a username across hundreds of social networks and websites\n- **URL Analysis**: Parse URLs to extract information and search for associated usernames\n- **Multiple Output Formats**: Support for txt, html, pdf, json, csv, and xmind formats\n- **Site Filtering**: Filter searches by site tags (e.g., photo, dating, us)\n- **Docker-based**: Reliable and consistent execution across environments\n\n## Tools\n\n### 1. Username Search Tool\n- Name: `search_username`\n- Description: Search for a username across social networks and sites\n- Parameters:\n  * `username` (required): Username to search for (alphanumeric, underscores, hyphens, periods only; max 100 chars)\n  * `format` (optional, default: \"pdf\"): Output format (txt, html, pdf, json, csv, xmind)\n  * `use_all_sites` (optional, default: false): Use all available sites instead of top 500\n  * `tags` (optional): Array of tags to filter sites (alphanumeric, underscores, hyphens only)\n\nExample:\n```json\n{\n  \"username\": \"test_user123\",\n  \"format\": \"html\",\n  \"use_all_sites\": false,\n  \"tags\": [\"photo\"]\n}\n```\n\n### 2. URL Analysis Tool\n- Name: `parse_url`\n- Description: Parse a URL to extract information and search for associated usernames\n- Parameters:\n  * `url` (required): URL to analyze\n  * `format` (optional, default: \"pdf\"): Output format (txt, html, pdf, json, csv, xmind)\n\nExample:\n```json\n{\n  \"url\": \"https://example.com/profile\",\n  \"format\": \"txt\"\n}\n```\n\n## Troubleshooting\n\n### Docker Issues\n\n1. Verify Docker is installed and running:\n```bash\ndocker --version\ndocker ps\n```\n\n2. Check Docker permissions:\n   - Ensure your user has permissions to run Docker commands\n   - On Linux, add your user to the docker group: `sudo usermod -aG docker $USER`\n\n### Reports Directory Issues\n\n1. Verify the reports directory:\n   - The directory specified in MAIGRET_REPORTS_DIR must exist\n   - Your user must have write permissions to this directory\n   - Check permissions: `ls -la /path/to/reports/directory`\n\n2. Common configuration mistakes:\n   - Missing MAIGRET_REPORTS_DIR environment variable\n   - Directory doesn't exist\n   - Incorrect permissions\n   - Trailing slashes in the path\n\n3. After fixing any issues:\n   - Save the configuration file\n   - Restart Claude Desktop\n\n## Error Messages\n\n- \"Docker is not installed or not running\": Install Docker and start the Docker daemon\n- \"MAIGRET_REPORTS_DIR environment variable must be set\": Add the environment variable to your configuration\n- \"Error creating reports directory\": Check directory permissions and path\n- \"Error executing maigret\": Check Docker logs and ensure the container has proper permissions\n- \"Invalid username\": Username contains invalid characters. Use only alphanumeric, underscores, hyphens, and periods\n- \"Invalid URL\": URL is malformed or contains prohibited characters\n- \"Invalid tag\": Tag contains invalid characters. Use only alphanumeric, underscores, and hyphens\n\n## Contributing\n\n1. Fork the repository\n2. Create a feature branch (`git checkout -b feature/amazing-feature`)\n3. Commit your changes (`git commit -m 'Add amazing feature'`)\n4. Push to the branch (`git push origin feature/amazing-feature`)\n5. Open a Pull Request\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"mcp-maigret\",\n  \"version\": \"1.0.13\",\n  \"description\": \"MCP server for maigret - OSINT username search across social networks\",\n  \"main\": \"build/index.js\",\n  \"type\": \"module\",\n  \"bin\": {\n    \"mcp-maigret\": \"build/index.js\"\n  },\n  \"files\": [\n    \"build\",\n    \"README.md\",\n    \"LICENSE\"\n  ],\n  \"scripts\": {\n    \"build\": \"tsc && chmod +x build/index.js\",\n    \"prepublishOnly\": \"npm run build\"\n  },\n  \"dependencies\": {\n    \"@modelcontextprotocol/sdk\": \"^0.4.0\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20.17.12\",\n    \"typescript\": \"^5.0.0\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/burtthecoder/mcp-maigret.git\"\n  },\n  \"keywords\": [\n    \"mcp\",\n    \"maigret\",\n    \"osint\",\n    \"social-networks\",\n    \"username-search\",\n    \"claude\",\n    \"claude-desktop\"\n  ],\n  \"author\": \"Burt The Coder\",\n  \"license\": \"MIT\",\n  \"engines\": {\n    \"node\": \">=18\"\n  }\n}\n"
  },
  {
    "path": "smithery.yaml",
    "content": "# Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml\n\nstartCommand:\n  type: stdio\n  configSchema:\n    # JSON Schema defining the configuration options for the MCP.\n    type: object\n    required:\n      - maigretReportsDir\n    properties:\n      maigretReportsDir:\n        type: string\n        description: The directory where Maigret will store its reports.\n  commandFunction:\n    # A function that produces the CLI command to start the MCP on stdio.\n    |-\n    (config) => ({command: 'node', args: ['build/index.js'], env: {MAIGRET_REPORTS_DIR: config.maigretReportsDir}})"
  },
  {
    "path": "src/index.ts",
    "content": "#!/usr/bin/env node\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport {\n  CallToolRequestSchema,\n  ErrorCode,\n  ListToolsRequestSchema,\n  McpError,\n} from '@modelcontextprotocol/sdk/types.js';\nimport { execFile } from 'child_process';\nimport { promisify } from 'util';\nimport { platform } from 'os';\nimport { existsSync, mkdirSync } from 'fs';\nimport { join } from 'path';\n\nconst execFileAsync = promisify(execFile);\nconst DOCKER_IMAGE = 'soxoj/maigret:latest';\n\ninterface SearchUsernameArgs {\n  username: string;\n  format?: 'txt' | 'html' | 'pdf' | 'json' | 'csv' | 'xmind';\n  use_all_sites?: boolean;\n  tags?: string[];\n}\n\ninterface ParseUrlArgs {\n  url: string;\n  format?: 'txt' | 'html' | 'pdf' | 'json' | 'csv' | 'xmind';\n}\n\ninterface ExecResult {\n  stdout: string;\n  stderr: string;\n}\n\nfunction sanitizeFilename(filename: string): string {\n  return filename.replace(/[<>:\"/\\\\|?*\\x00-\\x1F]/g, '_');\n}\n\n/**\n * Validates username to prevent command injection.\n * Only allows alphanumeric characters, underscores, hyphens, and periods.\n */\nfunction isValidUsername(username: string): boolean {\n  // Max length check to prevent DoS\n  if (username.length === 0 || username.length > 100) {\n    return false;\n  }\n  // Only allow safe characters commonly found in usernames\n  return /^[a-zA-Z0-9_.-]+$/.test(username);\n}\n\n/**\n * Validates URL to prevent command injection.\n * Must be a valid HTTP/HTTPS URL.\n */\nfunction isValidUrl(urlString: string): boolean {\n  try {\n    const url = new URL(urlString);\n    // Only allow http and https protocols\n    if (url.protocol !== 'http:' && url.protocol !== 'https:') {\n      return false;\n    }\n    // Check for suspicious characters that could be used for injection\n    if (/[;&|`$(){}[\\]<>\\\\]/.test(urlString)) {\n      return false;\n    }\n    return true;\n  } catch {\n    return false;\n  }\n}\n\n/**\n * Validates tags to prevent command injection.\n * Only allows alphanumeric characters, underscores, and hyphens.\n */\nfunction isValidTag(tag: string): boolean {\n  if (tag.length === 0 || tag.length > 50) {\n    return false;\n  }\n  return /^[a-zA-Z0-9_-]+$/.test(tag);\n}\n\nfunction isSearchUsernameArgs(args: unknown): args is SearchUsernameArgs {\n  if (!args || typeof args !== 'object') return false;\n  const a = args as Record<string, unknown>;\n  return typeof a.username === 'string' &&\n    (a.format === undefined || ['txt', 'html', 'pdf', 'json', 'csv', 'xmind'].includes(a.format as string)) &&\n    (a.use_all_sites === undefined || typeof a.use_all_sites === 'boolean') &&\n    (a.tags === undefined || (Array.isArray(a.tags) && a.tags.every(t => typeof t === 'string')));\n}\n\nfunction isParseUrlArgs(args: unknown): args is ParseUrlArgs {\n  if (!args || typeof args !== 'object') return false;\n  const a = args as Record<string, unknown>;\n  return typeof a.url === 'string' &&\n    (a.format === undefined || ['txt', 'html', 'pdf', 'json', 'csv', 'xmind'].includes(a.format as string));\n}\n\nclass MaigretServer {\n  private server: Server;\n  private reportsDir: string;\n\n  constructor() {\n    if (!process.env.MAIGRET_REPORTS_DIR) {\n      throw new Error('MAIGRET_REPORTS_DIR environment variable must be set');\n    }\n\n    this.reportsDir = process.env.MAIGRET_REPORTS_DIR;\n    \n        this.server = new Server({\n            name: 'maigret-server',\n            version: '0.1.0',\n            capabilities: {\n                tools: {}\n            },\n            timeout: 600000  // 10 minutes in milliseconds\n        });\n\n    console.error('Using reports directory:', this.reportsDir);\n    \n    // Create reports directory if it doesn't exist\n    if (!existsSync(this.reportsDir)) {\n      console.error('Creating reports directory...');\n      mkdirSync(this.reportsDir, { recursive: true });\n    }\n    \n    this.setupToolHandlers();\n    \n    this.server.onerror = (error) => console.error('[MCP Error]', error);\n    process.on('SIGINT', async () => {\n      await this.server.close();\n      process.exit(0);\n    });\n\n    // Trigger setup immediately\n    this.ensureSetup().catch(error => {\n      console.error('Failed to setup maigret:', error);\n      process.exit(1);\n    });\n  }\n\n  /**\n   * Executes a command safely using execFile (no shell interpolation).\n   * Arguments are passed as an array to prevent command injection.\n   */\n  private async execCommand(cmd: string, args: string[]): Promise<ExecResult> {\n    console.error('Executing command:', cmd, args.join(' '));\n    try {\n      const result = await execFileAsync(cmd, args, {\n        maxBuffer: 10 * 1024 * 1024\n      });\n      console.error('Command output:', result.stdout);\n      if (result.stderr) console.error('Command stderr:', result.stderr);\n      return result;\n    } catch (error) {\n      console.error('Command failed:', error);\n      throw error;\n    }\n  }\n\n  private async ensureSetup(): Promise<void> {\n    try {\n      console.error('Checking Docker...');\n      try {\n        await this.execCommand('docker', ['--version']);\n      } catch (error) {\n        throw new Error('Docker is not installed or not running. Please install Docker and try again.');\n      }\n\n      console.error('Checking if maigret image exists...');\n      try {\n        await this.execCommand('docker', ['image', 'inspect', DOCKER_IMAGE]);\n        console.error('Maigret image found');\n      } catch (error) {\n        console.error('Maigret image not found, pulling...');\n        await this.execCommand('docker', ['pull', DOCKER_IMAGE]);\n        console.error('Maigret image pulled successfully');\n      }\n    } catch (error) {\n      console.error('Setup failed:', error);\n      throw error;\n    }\n  }\n\n  private setupToolHandlers() {\n    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({\n      tools: [\n        {\n          name: 'search_username',\n          description: 'Search for a username across social networks and sites',\n          inputSchema: {\n            type: 'object',\n            properties: {\n              username: {\n                type: 'string',\n                description: 'Username to search for'\n              },\n              format: {\n                type: 'string',\n                enum: ['txt', 'html', 'pdf', 'json', 'csv', 'xmind'],\n                description: 'Output format',\n                default: 'pdf'\n              },\n              use_all_sites: {\n                type: 'boolean',\n                description: 'Use all available sites instead of top 500',\n                default: false\n              },\n              tags: {\n                type: 'array',\n                items: {\n                  type: 'string'\n                },\n                description: 'Filter sites by tags (e.g. photo, dating, us)',\n                default: []\n              }\n            },\n            required: ['username']\n          }\n        },\n        {\n          name: 'parse_url',\n          description: 'Parse a URL to extract information and search for associated usernames',\n          inputSchema: {\n            type: 'object',\n            properties: {\n              url: {\n                type: 'string',\n                format: 'uri',\n                description: 'URL to parse and analyze'\n              },\n              format: {\n                type: 'string',\n                enum: ['txt', 'html', 'pdf', 'json', 'csv', 'xmind'],\n                description: 'Output format',\n                default: 'pdf'\n              }\n            },\n            required: ['url']\n          }\n        }\n      ]\n    }));\n\n    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {\n      try {\n        await this.ensureSetup();\n\n        switch (request.params.name) {\n          case 'search_username': {\n            if (!isSearchUsernameArgs(request.params.arguments)) {\n              throw new McpError(\n                ErrorCode.InvalidParams,\n                'Invalid arguments for search_username'\n              );\n            }\n\n            const {\n              username,\n              format = 'pdf',\n              use_all_sites = false,\n              tags = []\n            } = request.params.arguments;\n\n            // Security: Validate username to prevent command injection\n            if (!isValidUsername(username)) {\n              throw new McpError(\n                ErrorCode.InvalidParams,\n                'Invalid username. Username must contain only alphanumeric characters, underscores, hyphens, and periods (max 100 characters).'\n              );\n            }\n\n            // Security: Validate all tags\n            for (const tag of tags) {\n              if (!isValidTag(tag)) {\n                throw new McpError(\n                  ErrorCode.InvalidParams,\n                  `Invalid tag: \"${tag}\". Tags must contain only alphanumeric characters, underscores, and hyphens.`\n                );\n              }\n            }\n\n            const safeUsername = sanitizeFilename(username);\n            const reportPath = join(this.reportsDir, `report_${safeUsername}.${format}`);\n\n            // Build docker command arguments (passed as array to prevent shell injection)\n            const dockerArgs = [\n              'run', '--rm',\n              '-v', `${this.reportsDir}:/app/reports`,\n              DOCKER_IMAGE,\n              username,\n              `--${format}`,\n              '--no-color',\n              '--no-progressbar',\n              '-n', '200'\n            ];\n\n            if (use_all_sites) {\n              dockerArgs.push('-a');\n            }\n\n            if (tags.length > 0) {\n              dockerArgs.push('--tags', tags.join(','));\n            }\n\n            // Run maigret in Docker using execFile (safe from shell injection)\n            const { stdout, stderr } = await this.execCommand('docker', dockerArgs);\n\n            return {\n              content: [\n                {\n                  type: 'text',\n                  text: `Report saved to: ${reportPath}\\n\\n${stdout}${stderr ? `\\nErrors:\\n${stderr}` : ''}`\n                }\n              ]\n            };\n          }\n\n          case 'parse_url': {\n            if (!isParseUrlArgs(request.params.arguments)) {\n              throw new McpError(\n                ErrorCode.InvalidParams,\n                'Invalid arguments for parse_url'\n              );\n            }\n\n            const { url, format = 'pdf' } = request.params.arguments;\n\n            // Security: Validate URL to prevent command injection\n            if (!isValidUrl(url)) {\n              throw new McpError(\n                ErrorCode.InvalidParams,\n                'Invalid URL. Must be a valid HTTP or HTTPS URL without special shell characters.'\n              );\n            }\n\n            // Build docker command arguments (passed as array to prevent shell injection)\n            const dockerArgs = [\n              'run', '--rm',\n              '-v', `${this.reportsDir}:/app/reports`,\n              DOCKER_IMAGE,\n              '--parse', url,\n              `--${format}`,\n              '--no-color',\n              '--no-progressbar',\n              '--timeout', '60',\n              '-n', '200'\n            ];\n\n            // Run maigret in Docker using execFile (safe from shell injection)\n            const { stdout, stderr } = await this.execCommand('docker', dockerArgs);\n\n            return {\n              content: [\n                {\n                  type: 'text',\n                  text: stdout + (stderr ? `\\nErrors:\\n${stderr}` : '')\n                }\n              ]\n            };\n          }\n\n          default:\n            throw new McpError(\n              ErrorCode.MethodNotFound,\n              `Unknown tool: ${request.params.name}`\n            );\n        }\n      } catch (error) {\n        const errorMessage = error instanceof Error ? error.message : String(error);\n        return {\n          content: [\n            {\n              type: 'text',\n              text: `Error executing maigret: ${errorMessage}`\n            }\n          ],\n          isError: true\n        };\n      }\n    });\n  }\n\n  async run() {\n    const transport = new StdioServerTransport();\n    await this.server.connect(transport);\n    console.error('Maigret MCP server running on stdio');\n  }\n}\n\nconst server = new MaigretServer();\nserver.run().catch(console.error);\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"module\": \"ES2020\",\n    \"moduleResolution\": \"node\",\n    \"esModuleInterop\": true,\n    \"outDir\": \"build\",\n    \"rootDir\": \"src\",\n    \"strict\": true,\n    \"skipLibCheck\": true,\n    \"forceConsistentCasingInFileNames\": true\n  },\n  \"include\": [\"src/**/*\"],\n  \"exclude\": [\"node_modules\", \"build\"]\n}\n"
  }
]