Full Code of zueai/mcp-manager for AI

main 0713b36f75aa cached
33 files
53.7 KB
17.3k tokens
30 symbols
1 requests
Download .txt
Repository: zueai/mcp-manager
Branch: main
Commit: 0713b36f75aa
Files: 33
Total size: 53.7 KB

Directory structure:
gitextract_oi45e3m7/

├── .cursorrules
├── .gitignore
├── LICENSE
├── README.md
├── biome.json
├── bun.lockb
├── eslint.config.js
├── index.html
├── package.json
├── postcss.config.js
├── public/
│   └── robots.txt
├── src/
│   ├── App.tsx
│   ├── components/
│   │   ├── applying-instructions.tsx
│   │   ├── loading-instructions.tsx
│   │   ├── mcp-server-card.tsx
│   │   ├── mcp-servers.tsx
│   │   ├── server-configs/
│   │   │   ├── env-config.tsx
│   │   │   ├── filesystem-config.tsx
│   │   │   ├── obsidian-config.tsx
│   │   │   ├── postgres-config.tsx
│   │   │   ├── sentry-config.tsx
│   │   │   └── sqlite-config.tsx
│   │   └── terminal-command.tsx
│   ├── index.css
│   ├── main.tsx
│   ├── server-configs.ts
│   ├── utils.ts
│   └── vite-env.d.ts
├── tailwind.config.ts
├── tsconfig.app.json
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts

================================================
FILE CONTENTS
================================================

================================================
FILE: .cursorrules
================================================
this is a simple react and vite project that allows users to manage a JSON file on their computer with a nice UI.

the file path is " ~/Library/Application\ Support/Claude/claude_desktop_config.json"

styling is done with tailwindcss and daisyui whenever possible

never use "any" as a type

always add the type=button property to buttons

always use bun over npm for this project

JSX elements without children should be marked as self-closing


================================================
FILE: .gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-electron
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2024 Zue AI

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
<h1 align="center">MCP Manager for Claude Desktop</h1>

<p align="center">A simple web GUI to manage Model Context Protocol (MCP) servers for the Claude Desktop app on MacOS easily. Just follow the instructions and paste a few commands to give your Claude app instant superpowers. </p>

![MCP Manager for Claude Desktop](https://assets.zue.ai/mcp-manager-hero.png)

## What is MCP?

The Model Context Protocol (MCP) enables Claude to access private data, APIs, and other services to answer questions and perform actions on your behalf. Learn more about MCP at:

- [modelcontextprotocol.io](https://modelcontextprotocol.io)
- [Anthropic's MCP Announcement](https://www.anthropic.com/news/model-context-protocol)

## Features

- 🚀 Easy-to-use interface for managing MCP servers
- 🔒 Runs entirely client-side - your data never leaves your computer
- ⚡️ Quick setup for popular MCP servers:
  - Apple Notes - Access and search your Apple Notes
  - AWS Knowledge Base - Access and query AWS Knowledge Base for information retrieval
  - Brave Search - Search the web with Brave Search API
  - Browserbase - Let Claude explore the web with Browserbase
  - Cloudflare - Manage your Cloudflare workers and account resources
  - Everart - Interface with Everart API for digital art and design tools
  - Exa - Search the web with Exa
  - Filesystem - Access and manage local filesystem
  - GitHub - Access your GitHub repositories
  - GitLab - Manage GitLab repositories and resources
  - Google Drive - Access and search files in your Google Drive
  - Google Maps - Access Google Maps API for location services
  - Memory - Give Claude memory of previous conversations
  - Obsidian - Read and search files in your Obsidian vault
  - Perplexity - Search the web with Perplexity API
  - PostgreSQL - Connect and interact with PostgreSQL databases
  - Puppeteer - Automate browser interactions
  - Sequential Thinking - Enable step-by-step reasoning
  - Slack - Access your Slack workspace
  - SQLite - Manage SQLite databases
  - Todoist - Access and search your Todoist tasks
  - YouTube Transcript - Access and search YouTube transcripts
- 🛠 Simple configuration of environment variables and server settings
- 📋 One-click copying of terminal commands for installation

## Tech Stack

- **Frontend Framework**: React 18 with TypeScript
- **Build Tool**: Vite
- **Styling**:
  - TailwindCSS for utility-first CSS
  - DaisyUI for component styling
  - Tiempos Font to match the Anthropic Design Language
- **Package Manager**: Bun
- **Deployment**: Cloudflare Pages for <60s build times

## Project Structure

```plaintext
src/
├── components/ # React components
│ ├── server-configs/ # Server-specific configuration components
│ └── ...
├── assets/ # Static assets and fonts
├── App.tsx # Main application component
├── server-configs.ts # MCP server configurations
└── utils.ts # Utility functions
```

## Development

1. Install dependencies:

   ```bash
   bun install
   ```

2. Start the dev server:

   ```bash
   bun dev
   ```

3. Build for production:

   ```bash
   bun run build
   ```

## Contributing

Contributions are extremely welcome! Please open a PR with new MCP servers or any other improvements to the codebase.
PS. I wasnt able to get fetch, time, and sentry working, if you can help me out, that would be great!

## Disclaimer

This project is not affiliated with Anthropic. All logos are trademarks of their respective owners.

## License

MIT

---
<br/>
<br/>
<p align="center">
<a href="https://zue.ai#gh-light-mode-only">
  <img src="https://assets.zue.ai/logo_zue_purple.svg" alt="zue logo" width="200" height="auto" style="display: block; margin: 0 auto;" />
</a>
<a href="https://zue.ai#gh-dark-mode-only">
  <img src="https://assets.zue.ai/logo_zue_yellow.svg" alt="zue logo" width="200" height="auto" style="display: block; margin: 0 auto;" />
</a>
</p>

<p align="center">
<a href="https://zue.ai/talk-to-us">Contact us</a> for custom AI automation solutions and product development.
</p>


================================================
FILE: biome.json
================================================
{
	"files": {
		"ignore": ["dist/**/*", "node_modules/**/*", "public/**/*", "**/*.css"]
	},
	"linter": {
		"enabled": true,
		"rules": {
			"recommended": true,
			"suspicious": {
				"noArrayIndexKey": "off"
			}
		},
		"ignore": ["**/*.md", "**/*.css"]
	},
	"formatter": {
		"enabled": true,
		"indentStyle": "tab",
		"indentWidth": 4
	},
	"javascript": {
		"formatter": {
			"semicolons": "asNeeded",
			"trailingCommas": "none"
		}
	},
	"json": {
		"parser": {
			"allowComments": true
		}
	}
}


================================================
FILE: eslint.config.js
================================================
import js from "@eslint/js"
import reactHooks from "eslint-plugin-react-hooks"
import reactRefresh from "eslint-plugin-react-refresh"
import globals from "globals"
import tseslint from "typescript-eslint"

export default tseslint.config(
	{ ignores: ["dist"] },
	{
		extends: [js.configs.recommended, ...tseslint.configs.recommended],
		files: ["**/*.{ts,tsx}"],
		languageOptions: {
			ecmaVersion: 2020,
			globals: globals.browser
		},
		plugins: {
			"react-hooks": reactHooks,
			"react-refresh": reactRefresh
		},
		rules: {
			...reactHooks.configs.recommended.rules,
			"react-refresh/only-export-components": [
				"warn",
				{ allowConstantExport: true }
			],
			"@typescript-eslint/no-unused-vars": "off"
		}
	}
)


================================================
FILE: index.html
================================================
<!doctype html>
<html lang="en" data-theme="light" class="min-h-screen bg-[#f2f1e9]">

<head>
  <meta charset="UTF-8" />
  <link rel="icon" type="image/ico" href="/mcp-favicon.ico" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <meta name="description"
    content="MCP Manager for Claude Desktop - A simple web GUI to manage Model Context Protocol (MCP) servers. Configure and manage your Claude.app's superpowers with ease." />
  <title>MCP Manager for Claude Desktop</title>
</head>

<body class="min-h-screen h-full">
  <div id="root"></div>
  <script type="module" src="/src/main.tsx"></script>
</body>

</html>

================================================
FILE: package.json
================================================
{
	"name": "mcp-manager",
	"private": true,
	"version": "0.0.0",
	"type": "module",
	"scripts": {
		"dev": "bun check && vite --port 7777",
		"build": "bun check && vite build",
		"lint": "eslint .",
		"preview": "vite preview",
		"check": "tsc --noEmit && biome check --write ."
	},
	"dependencies": {
		"lucide-react": "^0.468.0",
		"react": "^18.3.1",
		"react-dom": "^18.3.1"
	},
	"devDependencies": {
		"@biomejs/biome": "^1.9.4",
		"@eslint/js": "^9.15.0",
		"@types/node": "^22.10.1",
		"@types/react": "^18.3.12",
		"@types/react-dom": "^18.3.1",
		"@vitejs/plugin-react": "^4.3.4",
		"autoprefixer": "^10.4.20",
		"daisyui": "^4.12.14",
		"eslint": "^9.15.0",
		"eslint-plugin-react-hooks": "^5.0.0",
		"eslint-plugin-react-refresh": "^0.4.14",
		"globals": "^15.12.0",
		"postcss": "^8.4.49",
		"tailwindcss": "^3.4.16",
		"typescript": "~5.6.2",
		"typescript-eslint": "^8.15.0",
		"vite": "^6.0.1"
	}
}


================================================
FILE: postcss.config.js
================================================
export default {
	plugins: {
		tailwindcss: {},
		autoprefixer: {}
	}
}


================================================
FILE: public/robots.txt
================================================
User-agent: *
Allow: /

================================================
FILE: src/App.tsx
================================================
import { ApplyingInstructions } from "@/components/applying-instructions"
import { LoadingInstructions } from "@/components/loading-instructions"
import { MCPServers } from "@/components/mcp-servers"
import { SERVER_CONFIGS } from "@/server-configs"
import type React from "react"
import { useState } from "react"

function App() {
	const [jsonContent, setJsonContent] = useState<{
		mcpServers: Record<
			string,
			{ command: string; args: string[]; env?: Record<string, string> }
		>
	}>({
		mcpServers: {}
	})
	const [uploadStatus, setUploadStatus] = useState<
		"idle" | "success" | "error"
	>("idle")
	const [isInstructionsOpen, setIsInstructionsOpen] = useState(true)

	const handleJsonInput = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
		try {
			const content = JSON.parse(e.target.value)
			setJsonContent(content)
			setUploadStatus("success")
			setIsInstructionsOpen(false) // Close accordion on successful upload
		} catch (error) {
			console.error("Error parsing JSON:", error)
			setUploadStatus("error")
		}
	}

	const handleServerAdd = (serverType: keyof typeof SERVER_CONFIGS) => {
		const serverConfig = SERVER_CONFIGS[serverType]

		// Ensure we only add servers with required properties
		const newServer = {
			command: serverConfig.command as string,
			args: serverConfig.args as string[],
			...(serverConfig.env && { env: serverConfig.env })
		}

		setJsonContent({
			...jsonContent,
			mcpServers: {
				...jsonContent.mcpServers,
				[serverType]: newServer
			}
		})
	}

	const handleServerRemove = (serverType: string) => {
		// Remove from mcpServers if present
		if (jsonContent.mcpServers[serverType]) {
			const { [serverType]: _, ...rest } = jsonContent.mcpServers
			setJsonContent({
				...jsonContent,
				mcpServers: rest
			})
		}
	}

	return (
		<main className="max-h-screen p-16">
			<div className="container mx-auto p-4 max-w-4xl">
				<div className="flex justify-center items-center gap-8 mb-16">
					<div className="flex items-center justify-center rounded-2xl h-16 p-8 border-2 border-black/20">
						<img
							src="/mcp-logo.svg"
							alt="MCP Manager"
							className="h-8"
						/>
					</div>

					<div className="flex items-center justify-center rounded-2xl p-8 h-16 border-2 border-primary/30">
						<img
							src="/claude-logo.svg"
							alt="Claude"
							className="h-6"
						/>
					</div>
				</div>
				<h1 className="text-5xl font-light text-center my-8">
					MCP Manager for Claude Desktop
				</h1>

				<div className="flex justify-center">
					<span className="text-md text-center mb-8">
						Give Claude access to private data, APIs, and other
						services using the Model Context Protocol so it can
						answer questions and perform actions on your behalf.{" "}
						<br />
						<br />
						In a nutshell, MCP servers are like plugins that give
						Claude (the "client") prompts, resources, and tools to
						perform actions on your behalf. Read the{" "}
						<a
							href="https://modelcontextprotocol.io"
							className="link"
							target="_blank"
							rel="noreferrer"
						>
							MCP docs
						</a>{" "}
						or check out{" "}
						<a
							href="https://www.anthropic.com/news/model-context-protocol"
							className="link"
							target="_blank"
							rel="noreferrer"
						>
							Anthropic's announcement
						</a>{" "}
						to learn more.
						<br />
						<br />
						This is a simple, web-based GUI to help you install and
						manage MCP servers in your Claude App. <br />
						This runs client-side in your browser so your data will
						never leave your computer.
					</span>
				</div>

				<div className="space-y-6">
					<LoadingInstructions
						isOpen={isInstructionsOpen}
						onOpenChange={setIsInstructionsOpen}
						onJsonInput={handleJsonInput}
						uploadStatus={uploadStatus}
					/>

					{Object.keys(jsonContent).length > 0 &&
						uploadStatus === "success" && (
							<div className="space-y-6">
								<MCPServers
									jsonContent={{
										mcpServers:
											jsonContent.mcpServers as Record<
												string,
												{
													command: string
													args: string[]
													env?: Record<string, string>
												}
											>
									}}
									onUpdate={setJsonContent}
									onServerAdd={handleServerAdd}
									onServerRemove={handleServerRemove}
								/>

								{Object.keys(jsonContent.mcpServers).length >
									0 && (
									<ApplyingInstructions
										jsonContent={jsonContent}
									/>
								)}
							</div>
						)}
				</div>
				<div className="flex justify-center my-16">
					<span className="text-sm text-center text-black/50">
						This project is not affiliated with Anthropic. All logos
						are trademarks of their respective owners.
					</span>
				</div>
			</div>
		</main>
	)
}

export default App


================================================
FILE: src/components/applying-instructions.tsx
================================================
import { TerminalCommand } from "@/components/terminal-command"
import { SERVER_CONFIGS } from "@/server-configs"
import type { ServerConfig } from "@/server-configs"

type RuntimeServerConfig = {
	command: string
	args: string[]
	env?: Record<string, string>
}

type ApplyingInstructionsProps = {
	jsonContent: {
		mcpServers: Record<string, RuntimeServerConfig>
		cloudflare?: unknown
	}
}

export function ApplyingInstructions({
	jsonContent
}: ApplyingInstructionsProps) {
	const serversNeedingSetup = Object.keys(jsonContent.mcpServers).filter(
		(serverType) =>
			SERVER_CONFIGS[serverType as keyof typeof SERVER_CONFIGS]
				?.setupCommands
	)

	// Helper function to modify the JSON content with absolute paths
	const getJsonWithAbsolutePaths = () => {
		const { cloudflare: _, ...modifiedContent } = jsonContent

		for (const [serverType, config] of Object.entries(
			modifiedContent.mcpServers
		)) {
			const serverConfig =
				SERVER_CONFIGS[serverType as keyof typeof SERVER_CONFIGS]
			if (serverConfig?.setupCommands) {
				// Update the args to use the shell variable expansion syntax
				config.args = config.args?.map((arg) => {
					if (arg.includes("index.js")) {
						switch (serverType) {
							case "exa":
								return "$HOME_DIR/mcp-servers/exa-mcp-server-main/build/index.js"
							case "browserbase":
								return "$HOME_DIR/mcp-servers/mcp-server-browserbase-main/browserbase/dist/index.js"
							default:
								return arg
						}
					}
					return arg
				})
			}
		}

		return modifiedContent
	}

	return (
		<div className="join join-vertical w-full">
			<div className="collapse collapse-arrow join-item border border-base-300 bg-white mb-16 p-4">
				<input type="checkbox" />
				<h2 className="collapse-title text-xl font-tiempos-regular my-4">
					Apply your changes
				</h2>
				<div className="collapse-content space-y-4">
					<div className="bg-base-200 rounded-xl p-4">
						<h3 className="text-lg font-tiempos-regular">
							Step 1: Install Node.js and uv by running these
							commands (if not already installed)
						</h3>
						<div className="space-y-4 mt-4">
							<TerminalCommand
								command={
									'curl -fsSL https://fnm.vercel.app/install | bash && source ~/.zshrc && eval "$(fnm env --use-on-cd --shell zsh)" >> ~/.zshrc && source ~/.zshrc && fnm use --install-if-missing 22 && node -v'
								}
							/>
							If the command above fails, install Node.js by
							downloading the installer from{" "}
							<a
								href="https://nodejs.org/en/download/prebuilt-installer"
								target="_blank"
								rel="noopener noreferrer"
								className="link link-primary"
							>
								https://nodejs.org/en/download/prebuilt-installer
							</a>
							<TerminalCommand
								command={
									"curl -LsSf https://astral.sh/uv/install.sh | sh && source $HOME/.cargo/env && uv python install"
								}
							/>
						</div>
					</div>

					<div className="bg-base-200 rounded-xl p-4">
						<div className="space-y-4">
							<div>
								<h3 className="text-lg font-tiempos-regular mb-4">
									Step 2: Save your MCP servers to Claude by
									running:
								</h3>
								<TerminalCommand
									command={`HOME_DIR=$(echo $HOME) && echo '${JSON.stringify(
										getJsonWithAbsolutePaths(),
										null,
										2
									).replace(
										/\$HOME_DIR/g,
										"'\"$HOME_DIR\"'"
									)}' > "$HOME_DIR/Library/Application Support/Claude/claude_desktop_config.json"`}
								/>
							</div>
						</div>
					</div>

					{serversNeedingSetup.length > 0 && (
						<div className="bg-base-200 rounded-xl p-4">
							<h3 className="text-lg font-tiempos-regular mb-4">
								Step 3: Some servers require additional setup.
								Run the following commands:
							</h3>
							{serversNeedingSetup.map((serverType) => (
								<div key={serverType} className="mb-4">
									<p className="text-md mb-2">
										{serverType.charAt(0).toUpperCase() +
											serverType.slice(1)}
										:
									</p>
									<TerminalCommand
										command={
											SERVER_CONFIGS[
												serverType as keyof typeof SERVER_CONFIGS
											]?.setupCommands?.command || ""
										}
									/>
								</div>
							))}
						</div>
					)}

					<div className="bg-base-200 rounded-xl p-4 mt-4">
						<h3 className="text-lg font-tiempos-regular">
							Step {serversNeedingSetup.length > 0 ? "4" : "3"}:
							Restart Claude.app
						</h3>
					</div>
				</div>
			</div>
		</div>
	)
}


================================================
FILE: src/components/loading-instructions.tsx
================================================
import { TerminalCommand } from "@/components/terminal-command"
import { Check, XCircle } from "lucide-react"
import type { ChangeEvent } from "react"

interface LoadingInstructionsProps {
	isOpen: boolean
	onOpenChange: (isOpen: boolean) => void
	onJsonInput: (e: ChangeEvent<HTMLTextAreaElement>) => void
	uploadStatus: "idle" | "success" | "error"
}

export function LoadingInstructions({
	isOpen,
	onOpenChange,
	onJsonInput,
	uploadStatus
}: LoadingInstructionsProps) {
	const command =
		"test -f ~/Library/Application\\ Support/Claude/claude_desktop_config.json && pbcopy < ~/Library/Application\\ Support/Claude/claude_desktop_config.json || (echo '{\\n  \"mcpServers\": {}\\n}' | tee ~/Library/Application\\ Support/Claude/claude_desktop_config.json | pbcopy)"

	return (
		<div className="join join-vertical w-full">
			<div className="collapse collapse-arrow join-item border border-base-300 bg-white p-4">
				<input
					type="checkbox"
					checked={isOpen}
					onChange={(e) => onOpenChange(e.target.checked)}
					aria-label="MacOS Instructions"
				/>
				<h2 className="collapse-title text-xl my-4">
					MacOS Instructions
				</h2>
				<div className="collapse-content">
					<div className="prose">
						<div className="space-y-6">
							<div className="bg-base-200 rounded-xl p-4">
								<div className="space-y-4">
									<h3 className="text-lg font-tiempos-regular">
										Step 1: Run this terminal command to
										copy your current MCP config to the
										clipboard.
									</h3>
									<p className="text-sm opacity-80">
										If you've never used MCP before, this
										will create a blank config file and copy
										its contents.
									</p>

									<TerminalCommand command={command} />
								</div>
							</div>

							<div className="bg-base-200 rounded-xl p-4">
								<div>
									<h3 className="text-lg font-tiempos-regular mb-4">
										Step 2: Paste your config below.
									</h3>
									<textarea
										className="textarea textarea-bordered w-full h-16 font-mono"
										placeholder="Paste the copied JSON content here..."
										onChange={onJsonInput}
									/>
									{uploadStatus === "success" && (
										<div className="mt-2 flex items-center text-primary">
											<Check className="w-5 h-5" />
											<span className="ml-2">
												Valid MCP Configuration
											</span>
										</div>
									)}
									{uploadStatus === "error" && (
										<div className="mt-2 flex items-center text-error">
											<XCircle className="w-5 h-5" />
											<span className="ml-2">
												Error: Please ensure the content
												is valid JSON.
											</span>
										</div>
									)}
								</div>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>
	)
}


================================================
FILE: src/components/mcp-server-card.tsx
================================================
import { EnvConfig } from "@/components/server-configs/env-config"
import { FilesystemConfig } from "@/components/server-configs/filesystem-config"
import { ObsidianConfig } from "@/components/server-configs/obsidian-config"
import { PostgresConfig } from "@/components/server-configs/postgres-config"
import { SentryConfig } from "@/components/server-configs/sentry-config"
import { SQLiteConfig } from "@/components/server-configs/sqlite-config"
import { TerminalCommand } from "@/components/terminal-command"
import { SERVER_CONFIGS } from "@/server-configs"
import { ArrowUpRight, Trash2 } from "lucide-react"

type MCPServerConfig = {
	command: string
	args: string[]
	env?: Record<string, string>
}

type MCPServerCardProps = {
	serverName: string
	config: MCPServerConfig
	icon?: string
	onUpdate: (name: string, newConfig: MCPServerConfig) => void
	onDelete: (name: string) => void
}

export function MCPServerCard({
	serverName,
	config,
	icon,
	onUpdate,
	onDelete
}: MCPServerCardProps) {
	const handleFilesystemUpdate = (paths: string[]) => {
		const newConfig = {
			...config,
			args: [...config.args.slice(0, 2), ...paths]
		}
		onUpdate(serverName, newConfig)
	}

	const handlePostgresUpdate = (url: string) => {
		const newConfig = {
			...config,
			args: [...config.args.slice(0, 2), url]
		}
		onUpdate(serverName, newConfig)
	}

	const handleEnvUpdate = (key: string, value: string) => {
		const newConfig = {
			...config,
			env: {
				...(config.env || {}),
				[key]: value
			}
		}
		onUpdate(serverName, newConfig)
	}

	const handleSqliteUpdate = (dbPath: string) => {
		const newConfig = {
			...config,
			args: [
				"--directory",
				"parent_of_servers_repo/servers/src/sqlite",
				"run",
				"mcp-server-sqlite",
				"--db-path",
				dbPath
			]
		}
		onUpdate(serverName, newConfig)
	}

	const handleObsidianUpdate = (path: string) => {
		const newConfig = {
			...config,
			args: [...config.args.slice(0, 2), path]
		}
		onUpdate(serverName, newConfig)
	}

	const handleSentryUpdate = (token: string) => {
		const newConfig = {
			...config,
			args: [...config.args.slice(0, 2), token]
		}
		onUpdate(serverName, newConfig)
	}

	const handleDelete = (e: React.MouseEvent) => {
		e.stopPropagation()
		onDelete(serverName)
	}

	const serverConfig =
		SERVER_CONFIGS[serverName as keyof typeof SERVER_CONFIGS]
	const isFilesystemServer = serverName === "filesystem"
	const isPostgresServer = serverName === "postgres"
	const isSqliteServer = serverName === "sqlite"
	const isObsidianServer = serverName === "obsidian"
	const isSentryServer = serverName === "sentry"
	const iconUrl = icon || serverConfig?.icon

	return (
		<div className="join join-vertical w-full">
			<div className="collapse collapse-arrow join-item border border-base-300 bg-white p-4">
				<input type="checkbox" defaultChecked />
				<div className="collapse-title">
					<div className="flex items-center">
						<div className="flex items-center gap-2">
							{iconUrl && (
								<img
									src={iconUrl}
									alt={`${serverName} icon`}
									className="w-20 h-12 object-contain"
									onError={(e) => {
										e.currentTarget.style.display = "none"
									}}
								/>
							)}
							<h3 className="text-lg capitalize">{serverName}</h3>
						</div>
					</div>
				</div>
				<div className="collapse-content">
					{isFilesystemServer ? (
						<FilesystemConfig
							initialPaths={[config.args[2] || "/Users/"]}
							onUpdate={handleFilesystemUpdate}
						/>
					) : isPostgresServer ? (
						<PostgresConfig
							initialUrl={config.args[2]}
							onUpdate={handlePostgresUpdate}
						/>
					) : isSqliteServer ? (
						<SQLiteConfig
							initialPath={config.args[5]}
							onUpdate={handleSqliteUpdate}
						/>
					) : isObsidianServer ? (
						<ObsidianConfig
							initialPath={config.args[2]}
							onUpdate={handleObsidianUpdate}
						/>
					) : isSentryServer ? (
						<SentryConfig
							initialToken={config.args[2]}
							onUpdate={handleSentryUpdate}
						/>
					) : serverConfig?.env &&
						Object.keys(serverConfig.env).length > 0 ? (
						<EnvConfig
							env={serverConfig.env}
							initialValues={config.env || {}}
							onUpdate={handleEnvUpdate}
						/>
					) : null}

					{serverConfig?.docsUrl ===
						"https://github.com/cloudflare/mcp-server-cloudflare" && (
						<div className="bg-base-200 rounded-xl p-4 space-y-4">
							<p className="text-sm text-gray-600">
								MCP Manager can't update this server directly,
								please run this terminal command to modify this
								server.
							</p>
							<TerminalCommand
								command={
									serverConfig?.setupCommands?.command ?? ""
								}
							/>
						</div>
					)}
				</div>
				<div className="flex justify-end">
					<div className="flex gap-2 mb-4 mr-2">
						<button
							type="button"
							onClick={() =>
								window.open(serverConfig?.docsUrl, "_blank")
							}
							className="btn btn-sm btn-secondary"
						>
							<ArrowUpRight className="w-4 h-4" />
							<span>Docs</span>
						</button>
					</div>
					<div className="flex gap-2 mb-4 mr-4 justify-end">
						<button
							type="button"
							onClick={handleDelete}
							className="btn btn-sm bg-red-50 hover:bg-red-100"
						>
							<Trash2 className="w-4 h-4" />
							<span>Delete</span>
						</button>
					</div>
				</div>
			</div>
		</div>
	)
}


================================================
FILE: src/components/mcp-servers.tsx
================================================
import { MCPServerCard } from "@/components/mcp-server-card"
import { SERVER_CONFIGS } from "@/server-configs"
import { capitalizeFirstLetter } from "@/utils"
import { Plus, Save, X } from "lucide-react"

type MCPServer = {
	command: string
	args: string[]
}

type MCPServers = {
	[key: string]: MCPServer
}

type MCPConfig = {
	mcpServers: MCPServers
}

type MCPServersProps = {
	jsonContent: MCPConfig
	onUpdate: (newContent: MCPConfig) => void
	onServerAdd: (serverType: keyof typeof SERVER_CONFIGS) => void
	onServerRemove: (serverType: string) => void
}

export function MCPServers({
	jsonContent,
	onUpdate,
	onServerAdd,
	onServerRemove
}: MCPServersProps) {
	const handleServerUpdate = (name: string, newConfig: MCPServer) => {
		const updatedContent = {
			...jsonContent,
			mcpServers: {
				...jsonContent.mcpServers,
				[name]: newConfig
			}
		}
		onUpdate(updatedContent)
	}

	const handleServerDelete = (name: string) => {
		onServerRemove(name)
	}

	const hasServers = Object.keys(jsonContent.mcpServers).length > 0

	return (
		<div className="space-y-4 my-32">
			<div className="flex justify-between items-center mb-8">
				<div className="flex items-center gap-4">
					<h2 className="text-2xl text-center">Your MCP Servers</h2>
					<button
						type="button"
						className="btn btn-primary btn-sm"
						onClick={() =>
							(
								document.getElementById(
									"add_server_modal"
								) as HTMLDialogElement
							)?.showModal()
						}
					>
						<Plus className="w-4 h-4" />
						<span>Add Server</span>
					</button>
				</div>
			</div>

			<dialog id="add_server_modal" className="modal backdrop-blur-sm">
				<div className="modal-box rounded-3xl">
					<div className="flex justify-between items-center mb-4 sticky top-0 py-4 -mt-4 -mx-6 px-6">
						<h3 className="text-xl ml-4">Add New Server</h3>
						<button
							type="button"
							className="btn btn-square btn-ghost"
							onClick={() =>
								(
									document.getElementById(
										"add_server_modal"
									) as HTMLDialogElement
								)?.close()
							}
						>
							<X className="w-4 h-4" />
						</button>
					</div>

					<div className="grid gap-4 py-4 max-h-[70vh] overflow-y-auto px-4">
						{Object.keys(SERVER_CONFIGS).map((serverType) => (
							<button
								key={serverType}
								type="button"
								className="w-full bg-base-200 hover:bg-base-300 rounded-3xl p-4 flex items-center gap-6 h-24"
								onClick={() => {
									onServerAdd(
										serverType as keyof typeof SERVER_CONFIGS
									)
									;(
										document.getElementById(
											"add_server_modal"
										) as HTMLDialogElement
									)?.close()
								}}
							>
								<div className="my-auto mx-2">
									<img
										src={
											SERVER_CONFIGS[
												serverType as keyof typeof SERVER_CONFIGS
											].icon
										}
										alt={`${serverType} icon`}
										className="w-10 h-10 object-contain"
									/>
								</div>
								<div className="flex flex-col text-left w-full">
									<span className="text-xl font-normal mb-1">
										{capitalizeFirstLetter(serverType)}
									</span>
									<p className="text-sm opacity-80">
										{
											SERVER_CONFIGS[
												serverType as keyof typeof SERVER_CONFIGS
											].description
										}
									</p>
								</div>
							</button>
						))}
					</div>
				</div>
				<form method="dialog" className="modal-backdrop">
					<button type="button">close</button>
				</form>
			</dialog>

			<div className="space-y-4">
				{hasServers ? (
					Object.entries(jsonContent.mcpServers).map(
						([name, config]) => {
							const serverConfig =
								SERVER_CONFIGS[
									name as keyof typeof SERVER_CONFIGS
								]

							return (
								<MCPServerCard
									key={name}
									serverName={name}
									config={config}
									icon={serverConfig?.icon}
									onUpdate={handleServerUpdate}
									onDelete={handleServerDelete}
								/>
							)
						}
					)
				) : (
					<p className=" text-gray-500 text-center">
						You currently have no MCP servers configured. Add one by
						clicking the "Add Server" button.
					</p>
				)}
			</div>
		</div>
	)
}


================================================
FILE: src/components/server-configs/env-config.tsx
================================================
import { useState } from "react"

type EnvConfigProps = {
	env: Record<string, string>
	initialValues: Record<string, string>
	onUpdate: (key: string, value: string) => void
}

export function EnvConfig({ env, initialValues, onUpdate }: EnvConfigProps) {
	const [envValues, setEnvValues] =
		useState<Record<string, string>>(initialValues)

	const handleEnvChange = (key: string, value: string) => {
		setEnvValues((prev) => ({ ...prev, [key]: value }))
		onUpdate(key, value)
	}

	return (
		<div className="bg-base-200 rounded-xl p-4 mb-4 space-y-4">
			<div className="space-y-2">
				{Object.entries(env).map(([key]) => (
					<div key={key} className="form-control">
						<label htmlFor={`env-${key}`} className="label">
							<span className="label-text mb-2">{key}</span>
						</label>
						<input
							id={`env-${key}`}
							type="text"
							placeholder={`Paste your ${key} here`}
							className="input input-bordered w-full"
							value={envValues[key] || ""}
							onChange={(e) =>
								handleEnvChange(key, e.target.value)
							}
						/>
					</div>
				))}
			</div>
		</div>
	)
}


================================================
FILE: src/components/server-configs/filesystem-config.tsx
================================================
import { Plus, X } from "lucide-react"
import { useState } from "react"

type FilesystemConfigProps = {
	initialPaths: string[]
	onUpdate: (paths: string[]) => void
}

export function FilesystemConfig({
	initialPaths,
	onUpdate
}: FilesystemConfigProps) {
	const [filesystemPaths, setFilesystemPaths] =
		useState<string[]>(initialPaths)

	const handleFilesystemPathChange = (value: string, index: number) => {
		const newPaths = [...filesystemPaths]
		newPaths[index] = value
		setFilesystemPaths(newPaths)
	}

	const handleFilesystemPathBlur = () => {
		onUpdate(filesystemPaths)
	}

	const handleAddPath = () => {
		const newPaths = [...filesystemPaths, ""]
		setFilesystemPaths(newPaths)
		onUpdate(newPaths)
	}

	const handleRemovePath = (index: number) => {
		const newPaths = filesystemPaths.filter((_, i) => i !== index)
		setFilesystemPaths(newPaths)
		onUpdate(newPaths)
	}

	return (
		<div className="bg-base-200 rounded-xl p-4 mb-4 space-y-4">
			<div className="space-y-4">
				{filesystemPaths.map((path, index) => (
					<div key={index} className="flex items-center gap-2">
						<div className="form-control flex-1">
							<label
								htmlFor={`filesystem-path-${index}`}
								className="label"
							>
								<span className="label-text mb-2">
									Allowed Directory Path{" "}
									{filesystemPaths.length > 1
										? index + 1
										: ""}
								</span>
							</label>
							<input
								id={`filesystem-path-${index}`}
								type="text"
								placeholder="Enter the directory path (e.g., /Users/username/Documents)"
								className="input input-bordered w-full"
								value={path}
								onChange={(e) =>
									handleFilesystemPathChange(
										e.target.value,
										index
									)
								}
								onBlur={handleFilesystemPathBlur}
							/>
						</div>
						{filesystemPaths.length > 1 && (
							<button
								type="button"
								className="btn btn-ghost btn-sm mt-8"
								onClick={() => handleRemovePath(index)}
							>
								<X className="w-4 h-4" />
							</button>
						)}
					</div>
				))}
				<button
					type="button"
					className="btn btn-ghost btn-sm"
					onClick={handleAddPath}
				>
					<Plus className="w-4 h-4" />
					<span>Add Another Path</span>
				</button>
			</div>
		</div>
	)
}


================================================
FILE: src/components/server-configs/obsidian-config.tsx
================================================
import { useState } from "react"

type ObsidianConfigProps = {
	initialPath: string
	onUpdate: (path: string) => void
}

export function ObsidianConfig({ initialPath, onUpdate }: ObsidianConfigProps) {
	const [vaultPath, setVaultPath] = useState(initialPath)

	const handleVaultPathChange = (value: string) => {
		setVaultPath(value)
	}

	const handleVaultPathBlur = () => {
		onUpdate(vaultPath)
	}

	return (
		<div className="bg-base-200 rounded-xl p-4 mb-4 space-y-4">
			<div className="form-control">
				<label htmlFor="vault-path" className="label">
					<span className="label-text mb-2">Obsidian Vault Path</span>
				</label>
				<input
					id="vault-path"
					type="text"
					placeholder="Path to your Obsidian vault, e.g. /Users/yourname/Documents/MyVault"
					className="input input-bordered w-full"
					value={vaultPath}
					onChange={(e) => handleVaultPathChange(e.target.value)}
					onBlur={handleVaultPathBlur}
				/>
			</div>
		</div>
	)
}


================================================
FILE: src/components/server-configs/postgres-config.tsx
================================================
import { useState } from "react"

type PostgresConfigProps = {
	initialUrl: string
	onUpdate: (url: string) => void
}

export function PostgresConfig({ initialUrl, onUpdate }: PostgresConfigProps) {
	const [postgresUrl, setPostgresUrl] = useState(initialUrl)

	const handlePostgresUrlChange = (value: string) => {
		setPostgresUrl(value)
	}

	const handlePostgresUrlBlur = () => {
		onUpdate(postgresUrl)
	}

	return (
		<div className="bg-base-200 rounded-xl p-4 mb-4 space-y-4">
			<div className="form-control">
				<label htmlFor="postgres-url" className="label">
					<span className="label-text mb-2">
						PostgreSQL Connection URL
					</span>
				</label>
				<input
					id="postgres-url"
					type="text"
					placeholder="postgresql://localhost/mydb"
					className="input input-bordered w-full"
					value={postgresUrl}
					onChange={(e) => handlePostgresUrlChange(e.target.value)}
					onBlur={handlePostgresUrlBlur}
				/>
			</div>
		</div>
	)
}


================================================
FILE: src/components/server-configs/sentry-config.tsx
================================================
import { useState } from "react"

type SentryConfigProps = {
	initialToken: string
	onUpdate: (token: string) => void
}

export function SentryConfig({ initialToken, onUpdate }: SentryConfigProps) {
	const [authToken, setAuthToken] = useState(initialToken)

	const handleAuthTokenChange = (value: string) => {
		setAuthToken(value)
	}

	const handleAuthTokenBlur = () => {
		onUpdate(authToken)
	}

	return (
		<div className="bg-base-200 rounded-xl p-4 mb-4 space-y-4">
			<div className="form-control">
				<label htmlFor="sentry-token" className="label">
					<span className="label-text mb-2">
						Sentry Authentication Token
					</span>
				</label>
				<input
					id="sentry-token"
					type="text"
					placeholder="your-auth-token"
					className="input input-bordered w-full"
					value={authToken}
					onChange={(e) => handleAuthTokenChange(e.target.value)}
					onBlur={handleAuthTokenBlur}
				/>
			</div>
		</div>
	)
}


================================================
FILE: src/components/server-configs/sqlite-config.tsx
================================================
import { useState } from "react"

type SQLiteConfigProps = {
	initialPath: string
	onUpdate: (dbPath: string) => void
}

export function SQLiteConfig({ initialPath, onUpdate }: SQLiteConfigProps) {
	const [dbPath, setDbPath] = useState(initialPath)

	const handleDbPathChange = (value: string) => {
		setDbPath(value)
	}

	const handleDbPathBlur = () => {
		onUpdate(dbPath)
	}

	return (
		<div className="bg-base-200 rounded-xl p-4 mb-4 space-y-4">
			<div className="form-control">
				<label htmlFor="sqlite-path" className="label">
					<span className="label-text mb-2">
						SQLite Database Path
					</span>
				</label>
				<input
					id="sqlite-path"
					type="text"
					placeholder="~/my-database.db"
					className="input input-bordered w-full"
					value={dbPath}
					onChange={(e) => handleDbPathChange(e.target.value)}
					onBlur={handleDbPathBlur}
				/>
			</div>
		</div>
	)
}


================================================
FILE: src/components/terminal-command.tsx
================================================
import { Check, Copy } from "lucide-react"
import { useState } from "react"

interface TerminalCommandProps {
	command: string
	className?: string
}

export function TerminalCommand({
	command,
	className = ""
}: TerminalCommandProps) {
	const [hasCopied, setHasCopied] = useState(false)

	const handleCopy = () => {
		navigator.clipboard.writeText(command)
		setHasCopied(true)
		setTimeout(() => setHasCopied(false), 2000)
	}

	return (
		<div className={`bg-base-300 rounded-xl p-4 mb-4 ${className}`}>
			<p className="font-mono text-sm mb-4 break-all">{command}</p>
			<button
				type="button"
				onClick={handleCopy}
				className="btn btn-primary btn-sm"
			>
				{hasCopied ? (
					<Check className="w-4 h-4" />
				) : (
					<Copy className="w-4 h-4" />
				)}
				<span className="ml-2">
					{hasCopied ? "Copied!" : "Copy Command"}
				</span>
			</button>
		</div>
	)
}


================================================
FILE: src/index.css
================================================
@tailwind base;
@tailwind components;
@tailwind utilities;

@font-face {
  font-family: "Tiempos Text";
  src: url("./assets/tiempos-text-web-semibold.woff2") format("woff2");
  font-weight: 600;
  font-display: swap;
}

@font-face {
  font-family: "Tiempos Text";
  src: url("./assets/tiempos-text-web-regular.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}

@layer base {
  h1 {
    @apply font-tiempos-semibold;
  }
  h2 {
    @apply font-tiempos-semibold;
  }
  h3 {
    @apply font-tiempos-regular;
  }
  p {
    @apply font-tiempos-regular;
  }
  span {
    @apply font-tiempos-regular;
    font-weight: 400;
  }
}

.collapse {
  border-radius: 1.5rem;
  box-shadow: 0 10px 10px 0 rgba(0, 0, 0, 0.05);
}


================================================
FILE: src/main.tsx
================================================
import { StrictMode } from "react"
import { createRoot } from "react-dom/client"
import "@/index.css"
import App from "@/App.tsx"

const rootElement = document.getElementById("root")
if (!rootElement) throw new Error("Root element not found")

createRoot(rootElement).render(
	<StrictMode>
		<App />
	</StrictMode>
)


================================================
FILE: src/server-configs.ts
================================================
export type ServerConfig = {
	command?: string
	args?: string[]
	env?: Record<string, string>
	icon: string
	description: string
	docsUrl: string
	setupCommands?: {
		installPath: string
		command: string
	}
}

export const SERVER_CONFIGS: Record<string, ServerConfig> = {
	"brave-search": {
		icon: "https://www.svgrepo.com/show/305818/brave.svg",
		description: "Search the web with Brave Search API",

		docsUrl:
			"https://github.com/modelcontextprotocol/servers/tree/main/src/brave-search/README.md",
		command: "npx",
		args: ["-y", "@modelcontextprotocol/server-brave-search"],
		env: { BRAVE_API_KEY: "" }
	},
	filesystem: {
		icon: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJsdWNpZGUgbHVjaWRlLWZvbGRlci1jbG9zZWQiPjxwYXRoIGQ9Ik0yMCAyMGEyIDIgMCAwIDAgMi0yVjhhMiAyIDAgMCAwLTItMmgtNy45YTIgMiAwIDAgMS0xLjY5LS45TDkuNiAzLjlBMiAyIDAgMCAwIDcuOTMgM0g0YTIgMiAwIDAgMC0yIDJ2MTNhMiAyIDAgMCAwIDIgMloiLz48cGF0aCBkPSJNMiAxMGgyMCIvPjwvc3ZnPg==",
		description:
			"Access and manage local filesystem with specified allowed directories",
		command: "npx",
		args: ["-y", "@modelcontextprotocol/server-filesystem", "/Users/"],

		docsUrl:
			"https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem/README.md"
	},
	memory: {
		icon: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJsdWNpZGUgbHVjaWRlLWJyYWluIj48cGF0aCBkPSJNMTIgNWEzIDMgMCAxIDAtNS45OTcuMTI1IDQgNCAwIDAgMC0yLjUyNiA1Ljc3IDQgNCAwIDAgMCAuNTU2IDYuNTg4QTQgNCAwIDEgMCAxMiAxOFoiLz48cGF0aCBkPSJNMTIgNWEzIDMgMCAxIDEgNS45OTcuMTI1IDQgNCAwIDAgMSAyLjUyNiA1Ljc3IDQgNCAwIDAgMS0uNTU2IDYuNTg4QTQgNCAwIDEgMSAxMiAxOFoiLz48cGF0aCBkPSJNMTUgMTNhNC41IDQuNSAwIDAgMS0zLTQgNC41IDQuNSAwIDAgMS0zIDQiLz48cGF0aCBkPSJNMTcuNTk5IDYuNWEzIDMgMCAwIDAgLjM5OS0xLjM3NSIvPjxwYXRoIGQ9Ik02LjAwMyA1LjEyNUEzIDMgMCAwIDAgNi40MDEgNi41Ii8+PHBhdGggZD0iTTMuNDc3IDEwLjg5NmE0IDQgMCAwIDEgLjU4NS0uMzk2Ii8+PHBhdGggZD0iTTE5LjkzOCAxMC41YTQgNCAwIDAgMSAuNTg1LjM5NiIvPjxwYXRoIGQ9Ik02IDE4YTQgNCAwIDAgMS0xLjk2Ny0uNTE2Ii8+PHBhdGggZD0iTTE5Ljk2NyAxNy40ODRBNCA0IDAgMCAxIDE4IDE4Ii8+PC9zdmc+",
		description: "Give Claude memory of previous conversations",
		command: "npx",
		args: ["-y", "@modelcontextprotocol/server-memory"],

		docsUrl:
			"https://github.com/modelcontextprotocol/servers/tree/main/src/memory/README.md"
	},
	"sequential-thinking": {
		icon: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJsdWNpZGUgbHVjaWRlLXNwYXJrbGUiPjxwYXRoIGQ9Ik05LjkzNyAxNS41QTIgMiAwIDAgMCA4LjUgMTQuMDYzbC02LjEzNS0xLjU4MmEuNS41IDAgMCAxIDAtLjk2Mkw4LjUgOS45MzZBMiAyIDAgMCAwIDkuOTM3IDguNWwxLjU4Mi02LjEzNWEuNS41IDAgMCAxIC45NjMgMEwxNC4wNjMgOC41QTIgMiAwIDAgMCAxNS41IDkuOTM3bDYuMTM1IDEuNTgxYS41LjUgMCAwIDEgMCAuOTY0TDE1LjUgMTQuMDYzYTIgMiAwIDAgMC0xLjQzNyAxLjQzN2wtMS41ODIgNi4xMzVhLjUuNSAwIDAgMS0uOTYzIDB6Ii8+PC9zdmc+",
		description:
			"Enable step-by-step reasoning and sequential problem-solving",
		command: "npx",
		args: ["-y", "@modelcontextprotocol/server-sequential-thinking"],

		docsUrl:
			"https://github.com/modelcontextprotocol/servers/tree/main/src/sequentialthinking/README.md"
	},
	slack: {
		icon: "https://icon.icepanel.io/Technology/svg/Slack.svg",
		description: "Let Claude access your Slack workspace",
		command: "npx",
		args: ["-y", "@modelcontextprotocol/server-slack"],
		env: {
			SLACK_BOT_TOKEN: "",
			SLACK_TEAM_ID: ""
		},

		docsUrl:
			"https://github.com/modelcontextprotocol/servers/tree/main/src/slack/README.md"
	},
	"google-drive": {
		icon: "https://upload.wikimedia.org/wikipedia/commons/1/12/Google_Drive_icon_%282020%29.svg",
		description: "Access and search files in your Google Drive",
		command: "npx",
		args: ["-y", "@modelcontextprotocol/server-gdrive"],

		docsUrl:
			"https://github.com/modelcontextprotocol/servers/tree/main/src/gdrive/README.md"
	},
	// time: {
	// 	icon: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJsdWNpZGUgbHVjaWRlLWNsb2NrIj48Y2lyY2xlIGN4PSIxMiIgY3k9IjEyIiByPSIxMCIvPjxwb2x5bGluZSBwb2ludHM9IjEyIDYgMTIgMTIgMTYgMTQiLz48L3N2Zz4=",
	// 	description: "Current time / time zone conversion utilities",
	// 	command: "uvx",
	// 	args: ["mcp-server-time"],
	// 	docsUrl:
	// 		"https://github.com/modelcontextprotocol/servers/tree/main/src/time/README.md"
	// },
	"google-maps": {
		icon: "https://upload.wikimedia.org/wikipedia/commons/b/bd/Google_Maps_Logo_2020.svg",
		description: "Access Google Maps API for location and mapping services",
		command: "npx",
		args: ["-y", "@modelcontextprotocol/server-google-maps"],
		env: { GOOGLE_MAPS_API_KEY: "" },

		docsUrl:
			"https://github.com/modelcontextprotocol/servers/tree/main/src/google-maps/README.md"
	},
	"youtube-transcript": {
		icon: "https://www.svgrepo.com/show/13671/youtube.svg",
		description: "Access and search YouTube transcripts",
		command: "npx",
		args: ["-y", "@kimtaeyoon83/mcp-server-youtube-transcript"],

		docsUrl: "https://github.com/kimtaeyoon83/mcp-server-youtube-transcript"
	},
	perplexity: {
		icon: "https://seeklogo.com/images/P/perplexity-ai-logo-13120A0AAE-seeklogo.com.png",
		description: "Search the web with Perplexity API",
		command: "uvx",
		args: ["mcp-server-perplexity"],
		env: {
			PERPLEXITY_API_KEY: "your-perplexity-api-key"
		},

		docsUrl: "https://github.com/tanigami/mcp-server-perplexity"
	},
	// fetch: {
	// 	icon: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJsdWNpZGUgbHVjaWRlLWdsb2JlIj48Y2lyY2xlIGN4PSIxMiIgY3k9IjEyIiByPSIxMCIvPjxwYXRoIGQ9Ik0xMiAyYTE0LjUgMTQuNSAwIDAgMCAwIDIwIDE0LjUgMTQuNSAwIDAgMCAwLTIwIi8+PHBhdGggZD0iTTIgMTJoMjAiLz48L3N2Zz4=",
	// 	description: "Let Claude fetch and read a website",
	// 	command: "uvx",
	// 	args: ["mcp-server-fetch"],

	// 	docsUrl:
	// 		"https://github.com/modelcontextprotocol/servers/tree/main/src/fetch/README.md"
	// },
	"apple-notes": {
		icon: "https://upload.wikimedia.org/wikipedia/commons/f/fa/Apple_Notes_icon.svg",
		description: "Access and search your Apple Notes",
		command: "uvx",
		args: ["apple-notes-mcp"],

		docsUrl: "https://github.com/sirmews/apple-notes-mcp"
	},
	exa: {
		icon: "https://media.licdn.com/dms/image/v2/D4D0BAQEGEKPKKLiNvA/company-logo_200_200/company-logo_200_200/0/1721090778302/exa_ai_logo?e=2147483647&v=beta&t=bNJAmBL2v359QkVTUgGTEbdBOqsnYSMaOuCtMDuG920",
		description: "Search the web with Exa",
		command: "npx",
		args: ["exa-mcp-server/build/index.js"],
		env: {
			EXA_API_KEY: "your-api-key-here"
		},
		setupCommands: {
			installPath: "~/mcp-servers",
			command:
				"mkdir -p $(echo $HOME)/mcp-servers && cd $(echo $HOME)/mcp-servers && \
				curl -L https://github.com/exa-labs/exa-mcp-server/archive/refs/heads/main.zip -o exa-mcp-server.zip && \
				unzip -o exa-mcp-server.zip && \
				rm exa-mcp-server.zip && \
				cd exa-mcp-server-main && \
				npm install --save axios dotenv && \
				npm run build && \
				sudo npm link"
		},

		docsUrl: "https://github.com/exa-labs/exa-mcp-server"
	},
	browserbase: {
		icon: "https://opensourcepledge.com/images/members/browserbase/logo.webp",
		description: "Let Claude explore the web with Browserbase",
		command: "node",
		args: ["mcp-server-browserbase-main/browserbase/dist/index.js"],
		env: {
			BROWSERBASE_API_KEY: "your-api-key-here",
			BROWSERBASE_PROJECT_ID: "your-project-id-here"
		},
		setupCommands: {
			installPath: "~/mcp-servers",
			command:
				"mkdir -p $(echo $HOME)/mcp-servers && cd $(echo $HOME)/mcp-servers && \
				curl -L https://github.com/browserbase/mcp-server-browserbase/archive/refs/heads/main.zip -o browserbase-mcp-server.zip && \
				unzip -o browserbase-mcp-server.zip && \
				rm browserbase-mcp-server.zip && \
				cd mcp-server-browserbase-main/browserbase && \
				npm install && \
				npm run build"
		},

		docsUrl:
			"https://github.com/browserbase/mcp-server-browserbase/tree/main/browserbase"
	},
	obsidian: {
		icon: "https://upload.wikimedia.org/wikipedia/commons/1/10/2023_Obsidian_logo.svg",
		description: "Read and search files in your Obsidian vault",
		command: "npx",
		args: ["-y", "mcp-obsidian", ""],

		docsUrl: "https://github.com/calclavia/mcp-obsidian"
	},
	todoist: {
		icon: "https://www.svgrepo.com/show/354452/todoist-icon.svg",
		description: "Access and search your Todoist tasks",
		command: "npx",
		args: ["-y", "@abhiz123/todoist-mcp-server"],
		env: {
			TODOIST_API_TOKEN: "your_api_token_here"
		},

		docsUrl: "https://github.com/abhiz123/todoist-mcp-server"
	},
	cloudflare: {
		icon: "https://icon.icepanel.io/Technology/svg/Cloudflare.svg",
		description: "Manage your Cloudflare workers and account resources",
		docsUrl: "https://github.com/cloudflare/mcp-server-cloudflare",
		setupCommands: {
			installPath: "~/mcp-servers",
			command: "npx @cloudflare/mcp-server-cloudflare init"
		}
	},
	"aws-kb-retrieval": {
		icon: "https://icon.icepanel.io/Technology/svg/AWS.svg",
		description:
			"Access and query AWS Knowledge Base for information retrieval",
		command: "npx",
		args: ["-y", "@modelcontextprotocol/server-aws-kb-retrieval"],
		env: {
			AWS_ACCESS_KEY_ID: "",
			AWS_SECRET_ACCESS_KEY: "",
			AWS_REGION: ""
		},

		docsUrl:
			"https://github.com/modelcontextprotocol/servers/tree/main/src/aws-kb-retrieval/README.md"
	},
	everart: {
		icon: "https://pbs.twimg.com/profile_images/1717719314369789952/AmXarABn_400x400.png",
		description:
			"Interface with Everart API for digital art and design tools",
		command: "npx",
		args: ["-y", "@modelcontextprotocol/server-everart"],
		env: { EVERART_API_KEY: "" },

		docsUrl:
			"https://github.com/modelcontextprotocol/servers/tree/main/src/everart/README.md"
	},
	github: {
		icon: "https://icon.icepanel.io/Technology/svg/GitHub.svg",
		description: "Let Claude access your GitHub repositories",
		command: "npx",
		args: ["-y", "@modelcontextprotocol/server-github"],
		env: { GITHUB_PERSONAL_ACCESS_TOKEN: "" },

		docsUrl:
			"https://github.com/modelcontextprotocol/servers/blob/main/src/github/README.md"
	},
	gitlab: {
		icon: "https://icon.icepanel.io/Technology/svg/GitLab.svg",
		description: "Manage GitLab repositories and resources",
		command: "npx",
		args: ["-y", "@modelcontextprotocol/server-gitlab"],
		env: {
			GITLAB_PERSONAL_ACCESS_TOKEN: "",
			GITLAB_API_URL: "https://gitlab.com/api/v4"
		},

		docsUrl:
			"https://github.com/modelcontextprotocol/servers/blob/main/src/gitlab/README.md"
	},
	postgres: {
		icon: "https://www.svgrepo.com/show/303301/postgresql-logo.svg",
		description: "Connect and interact with PostgreSQL databases",
		command: "npx",
		args: [
			"-y",
			"@modelcontextprotocol/server-postgres",
			"postgresql://localhost/mydb"
		],

		docsUrl:
			"https://github.com/modelcontextprotocol/servers/tree/main/src/postgres/README.md"
	},
	puppeteer: {
		icon: "https://www.svgrepo.com/show/354228/puppeteer.svg",
		description: "Automate browser interactions with Puppeteer",
		command: "npx",
		args: ["-y", "@modelcontextprotocol/server-puppeteer"],

		docsUrl:
			"https://github.com/modelcontextprotocol/servers/tree/main/src/puppeteer/README.md"
	},
	sqlite: {
		icon: "https://icon.icepanel.io/Technology/svg/SQLite.svg",
		description: "Manage SQLite databases in local file storage",
		command: "uv",
		args: [
			"--directory",
			"parent_of_servers_repo/servers/src/sqlite",
			"run",
			"mcp-server-sqlite",
			"--db-path",
			"~/test.db"
		],

		docsUrl:
			"https://github.com/modelcontextprotocol/servers/tree/main/src/sqlite/README.md"
	}
	// sentry: {
	// 	icon: "https://www.svgrepo.com/show/306716/sentry.svg",
	// 	description: "Retrieve and analyze issues from Sentry for debugging",
	// 	command: "uvx",
	// 	args: ["mcp-server-sentry", "--auth-token", ""],

	// 	docsUrl:
	// 		"https://github.com/modelcontextprotocol/servers/tree/main/src/sentry"
	// }
}


================================================
FILE: src/utils.ts
================================================
export const capitalizeFirstLetter = (str: string): string => {
	return str.charAt(0).toUpperCase() + str.slice(1)
}


================================================
FILE: src/vite-env.d.ts
================================================
/// <reference types="vite/client" />


================================================
FILE: tailwind.config.ts
================================================
import daisyui from "daisyui"
import { light } from "daisyui/src/theming/themes"
import type { Config } from "tailwindcss"

export default {
	content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
	theme: {
		extend: {
			fontFamily: {
				"tiempos-semibold": ["Tiempos Text", "serif"],
				"tiempos-regular": ["Tiempos Text", "serif"]
			}
		}
	},
	plugins: [daisyui],
	daisyui: {
		themes: [
			{
				light: {
					...light,
					primary: "#da7756",
					"primary-content": "#ffffff",
					secondary: "#f2f1e9",
					"secondary-content": "#000000"
				}
			}
		]
	}
} satisfies Config


================================================
FILE: tsconfig.app.json
================================================
{
	"compilerOptions": {
		"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
		"target": "ES2020",
		"useDefineForClassFields": true,
		"lib": ["ES2020", "DOM", "DOM.Iterable"],
		"module": "ESNext",
		"skipLibCheck": true,

		/* Bundler mode */
		"moduleResolution": "bundler",
		"allowImportingTsExtensions": true,
		"isolatedModules": true,
		"moduleDetection": "force",
		"noEmit": true,
		"jsx": "react-jsx",

		/* Linting */
		"strict": true,
		"noUnusedLocals": false,
		"noUnusedParameters": true,
		"noFallthroughCasesInSwitch": true,
		"noUncheckedSideEffectImports": true,

		/* Path Aliases */
		"baseUrl": ".",
		"paths": {
			"@/*": ["src/*"]
		}
	},
	"include": ["src"]
}


================================================
FILE: tsconfig.json
================================================
{
	"files": [],
	"references": [
		{ "path": "./tsconfig.app.json" },
		{ "path": "./tsconfig.node.json" }
	]
}


================================================
FILE: tsconfig.node.json
================================================
{
	"compilerOptions": {
		"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
		"target": "ES2022",
		"lib": ["ES2023"],
		"module": "ESNext",
		"skipLibCheck": true,

		/* Bundler mode */
		"moduleResolution": "bundler",
		"allowImportingTsExtensions": true,
		"isolatedModules": true,
		"moduleDetection": "force",
		"noEmit": true,

		/* Linting */
		"strict": true,
		"noUnusedLocals": false,
		"noUnusedParameters": true,
		"noFallthroughCasesInSwitch": true,
		"noUncheckedSideEffectImports": true,

		/* Path Aliases */
		"baseUrl": ".",
		"paths": {
			"@/*": ["src/*"]
		}
	},
	"include": ["vite.config.ts"]
}


================================================
FILE: vite.config.ts
================================================
import { dirname } from "node:path"
import path from "node:path"
import { fileURLToPath } from "node:url"
import react from "@vitejs/plugin-react"
import { defineConfig } from "vite"

const __dirname = dirname(fileURLToPath(import.meta.url))

// https://vite.dev/config/
export default defineConfig({
	plugins: [react()],
	resolve: {
		alias: {
			"@": path.resolve(__dirname, "./src")
		}
	}
})
Download .txt
gitextract_oi45e3m7/

├── .cursorrules
├── .gitignore
├── LICENSE
├── README.md
├── biome.json
├── bun.lockb
├── eslint.config.js
├── index.html
├── package.json
├── postcss.config.js
├── public/
│   └── robots.txt
├── src/
│   ├── App.tsx
│   ├── components/
│   │   ├── applying-instructions.tsx
│   │   ├── loading-instructions.tsx
│   │   ├── mcp-server-card.tsx
│   │   ├── mcp-servers.tsx
│   │   ├── server-configs/
│   │   │   ├── env-config.tsx
│   │   │   ├── filesystem-config.tsx
│   │   │   ├── obsidian-config.tsx
│   │   │   ├── postgres-config.tsx
│   │   │   ├── sentry-config.tsx
│   │   │   └── sqlite-config.tsx
│   │   └── terminal-command.tsx
│   ├── index.css
│   ├── main.tsx
│   ├── server-configs.ts
│   ├── utils.ts
│   └── vite-env.d.ts
├── tailwind.config.ts
├── tsconfig.app.json
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
Download .txt
SYMBOL INDEX (30 symbols across 13 files)

FILE: src/App.tsx
  function App (line 8) | function App() {

FILE: src/components/applying-instructions.tsx
  type RuntimeServerConfig (line 5) | type RuntimeServerConfig = {
  type ApplyingInstructionsProps (line 11) | type ApplyingInstructionsProps = {
  function ApplyingInstructions (line 18) | function ApplyingInstructions({

FILE: src/components/loading-instructions.tsx
  type LoadingInstructionsProps (line 5) | interface LoadingInstructionsProps {
  function LoadingInstructions (line 12) | function LoadingInstructions({

FILE: src/components/mcp-server-card.tsx
  type MCPServerConfig (line 11) | type MCPServerConfig = {
  type MCPServerCardProps (line 17) | type MCPServerCardProps = {
  function MCPServerCard (line 25) | function MCPServerCard({

FILE: src/components/mcp-servers.tsx
  type MCPServer (line 6) | type MCPServer = {
  type MCPServers (line 11) | type MCPServers = {
  type MCPConfig (line 15) | type MCPConfig = {
  type MCPServersProps (line 19) | type MCPServersProps = {
  function MCPServers (line 26) | function MCPServers({

FILE: src/components/server-configs/env-config.tsx
  type EnvConfigProps (line 3) | type EnvConfigProps = {
  function EnvConfig (line 9) | function EnvConfig({ env, initialValues, onUpdate }: EnvConfigProps) {

FILE: src/components/server-configs/filesystem-config.tsx
  type FilesystemConfigProps (line 4) | type FilesystemConfigProps = {
  function FilesystemConfig (line 9) | function FilesystemConfig({

FILE: src/components/server-configs/obsidian-config.tsx
  type ObsidianConfigProps (line 3) | type ObsidianConfigProps = {
  function ObsidianConfig (line 8) | function ObsidianConfig({ initialPath, onUpdate }: ObsidianConfigProps) {

FILE: src/components/server-configs/postgres-config.tsx
  type PostgresConfigProps (line 3) | type PostgresConfigProps = {
  function PostgresConfig (line 8) | function PostgresConfig({ initialUrl, onUpdate }: PostgresConfigProps) {

FILE: src/components/server-configs/sentry-config.tsx
  type SentryConfigProps (line 3) | type SentryConfigProps = {
  function SentryConfig (line 8) | function SentryConfig({ initialToken, onUpdate }: SentryConfigProps) {

FILE: src/components/server-configs/sqlite-config.tsx
  type SQLiteConfigProps (line 3) | type SQLiteConfigProps = {
  function SQLiteConfig (line 8) | function SQLiteConfig({ initialPath, onUpdate }: SQLiteConfigProps) {

FILE: src/components/terminal-command.tsx
  type TerminalCommandProps (line 4) | interface TerminalCommandProps {
  function TerminalCommand (line 9) | function TerminalCommand({

FILE: src/server-configs.ts
  type ServerConfig (line 1) | type ServerConfig = {
  constant SERVER_CONFIGS (line 14) | const SERVER_CONFIGS: Record<string, ServerConfig> = {
Condensed preview — 33 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (66K chars).
[
  {
    "path": ".cursorrules",
    "chars": 445,
    "preview": "this is a simple react and vite project that allows users to manage a JSON file on their computer with a nice UI.\n\nthe f"
  },
  {
    "path": ".gitignore",
    "chars": 267,
    "preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndis"
  },
  {
    "path": "LICENSE",
    "chars": 1063,
    "preview": "MIT License\n\nCopyright (c) 2024 Zue AI\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof "
  },
  {
    "path": "README.md",
    "chars": 4010,
    "preview": "<h1 align=\"center\">MCP Manager for Claude Desktop</h1>\n\n<p align=\"center\">A simple web GUI to manage Model Context Proto"
  },
  {
    "path": "biome.json",
    "chars": 499,
    "preview": "{\n\t\"files\": {\n\t\t\"ignore\": [\"dist/**/*\", \"node_modules/**/*\", \"public/**/*\", \"**/*.css\"]\n\t},\n\t\"linter\": {\n\t\t\"enabled\": tr"
  },
  {
    "path": "eslint.config.js",
    "chars": 727,
    "preview": "import js from \"@eslint/js\"\nimport reactHooks from \"eslint-plugin-react-hooks\"\nimport reactRefresh from \"eslint-plugin-r"
  },
  {
    "path": "index.html",
    "chars": 649,
    "preview": "<!doctype html>\n<html lang=\"en\" data-theme=\"light\" class=\"min-h-screen bg-[#f2f1e9]\">\n\n<head>\n  <meta charset=\"UTF-8\" />"
  },
  {
    "path": "package.json",
    "chars": 915,
    "preview": "{\n\t\"name\": \"mcp-manager\",\n\t\"private\": true,\n\t\"version\": \"0.0.0\",\n\t\"type\": \"module\",\n\t\"scripts\": {\n\t\t\"dev\": \"bun check &&"
  },
  {
    "path": "postcss.config.js",
    "chars": 72,
    "preview": "export default {\n\tplugins: {\n\t\ttailwindcss: {},\n\t\tautoprefixer: {}\n\t}\n}\n"
  },
  {
    "path": "public/robots.txt",
    "chars": 22,
    "preview": "User-agent: *\nAllow: /"
  },
  {
    "path": "src/App.tsx",
    "chars": 4861,
    "preview": "import { ApplyingInstructions } from \"@/components/applying-instructions\"\nimport { LoadingInstructions } from \"@/compone"
  },
  {
    "path": "src/components/applying-instructions.tsx",
    "chars": 4535,
    "preview": "import { TerminalCommand } from \"@/components/terminal-command\"\nimport { SERVER_CONFIGS } from \"@/server-configs\"\nimport"
  },
  {
    "path": "src/components/loading-instructions.tsx",
    "chars": 2822,
    "preview": "import { TerminalCommand } from \"@/components/terminal-command\"\nimport { Check, XCircle } from \"lucide-react\"\nimport typ"
  },
  {
    "path": "src/components/mcp-server-card.tsx",
    "chars": 5416,
    "preview": "import { EnvConfig } from \"@/components/server-configs/env-config\"\nimport { FilesystemConfig } from \"@/components/server"
  },
  {
    "path": "src/components/mcp-servers.tsx",
    "chars": 4245,
    "preview": "import { MCPServerCard } from \"@/components/mcp-server-card\"\nimport { SERVER_CONFIGS } from \"@/server-configs\"\nimport { "
  },
  {
    "path": "src/components/server-configs/env-config.tsx",
    "chars": 1113,
    "preview": "import { useState } from \"react\"\n\ntype EnvConfigProps = {\n\tenv: Record<string, string>\n\tinitialValues: Record<string, st"
  },
  {
    "path": "src/components/server-configs/filesystem-config.tsx",
    "chars": 2300,
    "preview": "import { Plus, X } from \"lucide-react\"\nimport { useState } from \"react\"\n\ntype FilesystemConfigProps = {\n\tinitialPaths: s"
  },
  {
    "path": "src/components/server-configs/obsidian-config.tsx",
    "chars": 967,
    "preview": "import { useState } from \"react\"\n\ntype ObsidianConfigProps = {\n\tinitialPath: string\n\tonUpdate: (path: string) => void\n}\n"
  },
  {
    "path": "src/components/server-configs/postgres-config.tsx",
    "chars": 964,
    "preview": "import { useState } from \"react\"\n\ntype PostgresConfigProps = {\n\tinitialUrl: string\n\tonUpdate: (url: string) => void\n}\n\ne"
  },
  {
    "path": "src/components/server-configs/sentry-config.tsx",
    "chars": 938,
    "preview": "import { useState } from \"react\"\n\ntype SentryConfigProps = {\n\tinitialToken: string\n\tonUpdate: (token: string) => void\n}\n"
  },
  {
    "path": "src/components/server-configs/sqlite-config.tsx",
    "chars": 901,
    "preview": "import { useState } from \"react\"\n\ntype SQLiteConfigProps = {\n\tinitialPath: string\n\tonUpdate: (dbPath: string) => void\n}\n"
  },
  {
    "path": "src/components/terminal-command.tsx",
    "chars": 887,
    "preview": "import { Check, Copy } from \"lucide-react\"\nimport { useState } from \"react\"\n\ninterface TerminalCommandProps {\n\tcommand: "
  },
  {
    "path": "src/index.css",
    "chars": 731,
    "preview": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n@font-face {\n  font-family: \"Tiempos Text\";\n  src: url(\"./as"
  },
  {
    "path": "src/main.tsx",
    "chars": 317,
    "preview": "import { StrictMode } from \"react\"\nimport { createRoot } from \"react-dom/client\"\nimport \"@/index.css\"\nimport App from \"@"
  },
  {
    "path": "src/server-configs.ts",
    "chars": 12752,
    "preview": "export type ServerConfig = {\n\tcommand?: string\n\targs?: string[]\n\tenv?: Record<string, string>\n\ticon: string\n\tdescription"
  },
  {
    "path": "src/utils.ts",
    "chars": 117,
    "preview": "export const capitalizeFirstLetter = (str: string): string => {\n\treturn str.charAt(0).toUpperCase() + str.slice(1)\n}\n"
  },
  {
    "path": "src/vite-env.d.ts",
    "chars": 38,
    "preview": "/// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "tailwind.config.ts",
    "chars": 589,
    "preview": "import daisyui from \"daisyui\"\nimport { light } from \"daisyui/src/theming/themes\"\nimport type { Config } from \"tailwindcs"
  },
  {
    "path": "tsconfig.app.json",
    "chars": 703,
    "preview": "{\n\t\"compilerOptions\": {\n\t\t\"tsBuildInfoFile\": \"./node_modules/.tmp/tsconfig.app.tsbuildinfo\",\n\t\t\"target\": \"ES2020\",\n\t\t\"us"
  },
  {
    "path": "tsconfig.json",
    "chars": 112,
    "preview": "{\n\t\"files\": [],\n\t\"references\": [\n\t\t{ \"path\": \"./tsconfig.app.json\" },\n\t\t{ \"path\": \"./tsconfig.node.json\" }\n\t]\n}\n"
  },
  {
    "path": "tsconfig.node.json",
    "chars": 635,
    "preview": "{\n\t\"compilerOptions\": {\n\t\t\"tsBuildInfoFile\": \"./node_modules/.tmp/tsconfig.node.tsbuildinfo\",\n\t\t\"target\": \"ES2022\",\n\t\t\"l"
  },
  {
    "path": "vite.config.ts",
    "chars": 396,
    "preview": "import { dirname } from \"node:path\"\nimport path from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\nimport react f"
  }
]

// ... and 1 more files (download for full content)

About this extraction

This page contains the full source code of the zueai/mcp-manager GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 33 files (53.7 KB), approximately 17.3k tokens, and a symbol index with 30 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!