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
================================================
MCP Manager for Claude Desktop
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.

## 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
---
Contact us for custom AI automation solutions and product development.
================================================
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
================================================
MCP Manager for Claude Desktop
================================================
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 }
>
}>({
mcpServers: {}
})
const [uploadStatus, setUploadStatus] = useState<
"idle" | "success" | "error"
>("idle")
const [isInstructionsOpen, setIsInstructionsOpen] = useState(true)
const handleJsonInput = (e: React.ChangeEvent) => {
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 (
MCP Manager for Claude Desktop
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.{" "}
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{" "}
MCP docs
{" "}
or check out{" "}
Anthropic's announcement
{" "}
to learn more.
This is a simple, web-based GUI to help you install and
manage MCP servers in your Claude App.
This runs client-side in your browser so your data will
never leave your computer.
{Object.keys(jsonContent).length > 0 &&
uploadStatus === "success" && (
}
>
}}
onUpdate={setJsonContent}
onServerAdd={handleServerAdd}
onServerRemove={handleServerRemove}
/>
{Object.keys(jsonContent.mcpServers).length >
0 && (
)}
)}
This project is not affiliated with Anthropic. All logos
are trademarks of their respective owners.
)
}
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
}
type ApplyingInstructionsProps = {
jsonContent: {
mcpServers: Record
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 (
Apply your changes
Step 1: Install Node.js and uv by running these
commands (if not already installed)
Step 2: Save your MCP servers to Claude by
running:
"$HOME_DIR/Library/Application Support/Claude/claude_desktop_config.json"`}
/>
{serversNeedingSetup.length > 0 && (
Step 3: Some servers require additional setup.
Run the following commands:
{serversNeedingSetup.map((serverType) => (
{serverType.charAt(0).toUpperCase() +
serverType.slice(1)}
:
))}
)}
Step {serversNeedingSetup.length > 0 ? "4" : "3"}:
Restart Claude.app
)
}
================================================
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) => 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 (
onOpenChange(e.target.checked)}
aria-label="MacOS Instructions"
/>
MacOS Instructions
Step 1: Run this terminal command to
copy your current MCP config to the
clipboard.
If you've never used MCP before, this
will create a blank config file and copy
its contents.
Step 2: Paste your config below.
{uploadStatus === "success" && (
Valid MCP Configuration
)}
{uploadStatus === "error" && (
Error: Please ensure the content
is valid JSON.
)}
)
}
================================================
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
}
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 (
{iconUrl && (
{
e.currentTarget.style.display = "none"
}}
/>
)}
{serverName}
{isFilesystemServer ? (
) : isPostgresServer ? (
) : isSqliteServer ? (
) : isObsidianServer ? (
) : isSentryServer ? (
) : serverConfig?.env &&
Object.keys(serverConfig.env).length > 0 ? (
) : null}
{serverConfig?.docsUrl ===
"https://github.com/cloudflare/mcp-server-cloudflare" && (
MCP Manager can't update this server directly,
please run this terminal command to modify this
server.
)}
window.open(serverConfig?.docsUrl, "_blank")
}
className="btn btn-sm btn-secondary"
>
Docs
Delete
)
}
================================================
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 (
Your MCP Servers
(
document.getElementById(
"add_server_modal"
) as HTMLDialogElement
)?.showModal()
}
>
Add Server
Add New Server
(
document.getElementById(
"add_server_modal"
) as HTMLDialogElement
)?.close()
}
>
{Object.keys(SERVER_CONFIGS).map((serverType) => (
{
onServerAdd(
serverType as keyof typeof SERVER_CONFIGS
)
;(
document.getElementById(
"add_server_modal"
) as HTMLDialogElement
)?.close()
}}
>
{capitalizeFirstLetter(serverType)}
{
SERVER_CONFIGS[
serverType as keyof typeof SERVER_CONFIGS
].description
}
))}
{hasServers ? (
Object.entries(jsonContent.mcpServers).map(
([name, config]) => {
const serverConfig =
SERVER_CONFIGS[
name as keyof typeof SERVER_CONFIGS
]
return (
)
}
)
) : (
You currently have no MCP servers configured. Add one by
clicking the "Add Server" button.
)}
)
}
================================================
FILE: src/components/server-configs/env-config.tsx
================================================
import { useState } from "react"
type EnvConfigProps = {
env: Record
initialValues: Record
onUpdate: (key: string, value: string) => void
}
export function EnvConfig({ env, initialValues, onUpdate }: EnvConfigProps) {
const [envValues, setEnvValues] =
useState>(initialValues)
const handleEnvChange = (key: string, value: string) => {
setEnvValues((prev) => ({ ...prev, [key]: value }))
onUpdate(key, value)
}
return (
{Object.entries(env).map(([key]) => (
{key}
handleEnvChange(key, e.target.value)
}
/>
))}
)
}
================================================
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(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 (
{filesystemPaths.map((path, index) => (
Allowed Directory Path{" "}
{filesystemPaths.length > 1
? index + 1
: ""}
handleFilesystemPathChange(
e.target.value,
index
)
}
onBlur={handleFilesystemPathBlur}
/>
{filesystemPaths.length > 1 && (
handleRemovePath(index)}
>
)}
))}
Add Another Path
)
}
================================================
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 (
)
}
================================================
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 (
)
}
================================================
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 (
)
}
================================================
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 (
)
}
================================================
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 (
{command}
{hasCopied ? (
) : (
)}
{hasCopied ? "Copied!" : "Copy Command"}
)
}
================================================
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(
)
================================================
FILE: src/server-configs.ts
================================================
export type ServerConfig = {
command?: string
args?: string[]
env?: Record
icon: string
description: string
docsUrl: string
setupCommands?: {
installPath: string
command: string
}
}
export const SERVER_CONFIGS: Record = {
"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
================================================
///
================================================
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")
}
}
})