├── .env.example ├── .github ├── CONTRIBUTING.md └── workflows │ ├── e2e-tests.yml │ └── run-tests.yml ├── .gitignore ├── .husky └── pre-commit ├── .prettierrc ├── .react-router └── types │ ├── +register.ts │ ├── +virtual.d.ts │ └── app │ ├── +types │ └── root.ts │ └── routes │ └── +types │ ├── $.ts │ ├── _index.ts │ └── api.chat.ts ├── LICENSE ├── README.md ├── SECURITY.md ├── app ├── app.css ├── chat │ ├── ai │ │ ├── providers.server.ts │ │ └── providers.shared.ts │ ├── components │ │ ├── api-key-manager.tsx │ │ ├── api-keys-provider.tsx │ │ ├── chat-sidebar.tsx │ │ ├── chat.tsx │ │ ├── copy-button.tsx │ │ ├── icons.tsx │ │ ├── input.tsx │ │ ├── markdown.tsx │ │ ├── mcp-server-manager.tsx │ │ ├── message.tsx │ │ ├── messages.tsx │ │ ├── model-picker.tsx │ │ ├── project-overview.tsx │ │ ├── suggested-prompts.tsx │ │ ├── textarea.tsx │ │ ├── theme-provider.tsx │ │ ├── theme-toggle.tsx │ │ ├── tool-invocation.tsx │ │ └── ui │ │ │ ├── accordion.tsx │ │ │ ├── avatar.tsx │ │ │ ├── badge.tsx │ │ │ ├── button.tsx │ │ │ ├── dialog.tsx │ │ │ ├── dropdown-menu.tsx │ │ │ ├── input.tsx │ │ │ ├── label.tsx │ │ │ ├── popover.tsx │ │ │ ├── scroll-area.tsx │ │ │ ├── select.tsx │ │ │ ├── separator.tsx │ │ │ ├── sheet.tsx │ │ │ ├── sidebar.tsx │ │ │ ├── skeleton.tsx │ │ │ ├── sonner.tsx │ │ │ ├── text-morph.tsx │ │ │ ├── textarea.tsx │ │ │ └── tooltip.tsx │ ├── hooks │ │ └── use-mobile.ts │ └── lib │ │ ├── constants.ts │ │ ├── context │ │ └── mcp-context.tsx │ │ ├── db │ │ └── schema.ts │ │ ├── hooks │ │ ├── use-copy.ts │ │ ├── use-local-storage.ts │ │ └── use-scroll-to-bottom.tsx │ │ ├── user-id.ts │ │ └── utils.ts ├── components │ ├── .client │ │ ├── chatPage.client.tsx │ │ └── chatPage.css │ ├── chatPage.tsx │ └── content.tsx ├── entry.server.tsx ├── globals.css ├── root.tsx ├── routes.ts └── routes │ ├── $.tsx │ ├── _index.tsx │ └── api.chat.ts ├── biome.json ├── components.json ├── dist ├── server.d.ts ├── server.js ├── server.js.map └── tools │ ├── index.d.ts │ ├── index.js │ └── index.js.map ├── img ├── GitMCP_PW.mp4 ├── GitMCP_final.mp4 ├── OG.png ├── available-tools.png ├── claude-does-math-the-fancy-way.png ├── cover.png ├── icon.png ├── icon_black.png ├── icon_black_cropped.png ├── icon_cropped.png ├── mcp-inspector-oauth-success.png ├── mcp-inspector-sse-config.png ├── mcp-inspector-successful-tool-call.png └── mcp-login.png ├── package.json ├── playwright.config.ts ├── pnpm-lock.yaml ├── postcss.config.mjs ├── public └── img │ ├── GitMCP_PW.mp4 │ ├── GitMCP_final.mp4 │ ├── OG.png │ ├── available-tools.png │ ├── claude-does-math-the-fancy-way.png │ ├── cover.png │ ├── highlight-add-custom-plugin.png │ ├── highlight-sse-plugin-setup.png │ ├── icon.png │ ├── icon_black.png │ ├── icon_black_cropped.png │ ├── icon_cropped.png │ ├── icon_cropped.svg │ ├── mcp-inspector-oauth-success.png │ ├── mcp-inspector-sse-config.png │ ├── mcp-inspector-successful-tool-call.png │ └── mcp-login.png ├── react-router.config.ts ├── src ├── api │ ├── test-setup.ts │ ├── tools │ │ ├── commonTools.test.ts │ │ ├── commonTools.ts │ │ ├── index.test.ts │ │ ├── index.ts │ │ └── repoHandlers │ │ │ ├── DefaultRepoHandler.ts │ │ │ ├── GenericRepoHandler.ts │ │ │ ├── ReactRouterRepoHandler.ts │ │ │ ├── RepoHandler.ts │ │ │ ├── ThreejsRepoHandler.ts │ │ │ ├── generic │ │ │ ├── generic.test.ts │ │ │ └── static-mapping.json │ │ │ ├── handlers.test.ts │ │ │ ├── handlers.ts │ │ │ ├── test │ │ │ └── utils.ts │ │ │ └── threejs │ │ │ ├── __snapshots__ │ │ │ └── utils.test.ts.snap │ │ │ ├── utils.test.ts │ │ │ └── utils.ts │ └── utils │ │ ├── ViewCounterDO.ts │ │ ├── badge.ts │ │ ├── cache.ts │ │ ├── github.ts │ │ ├── githubClient.ts │ │ ├── helpers.ts │ │ ├── r2.ts │ │ ├── robotsTxt.ts │ │ └── vectorStore.ts ├── index.ts ├── shared │ ├── nameUtils.ts │ ├── repoData.test.ts │ ├── repoData.ts │ └── urlUtils.ts ├── test │ ├── ViewCounterDO.test.ts │ └── badge.test.ts └── utils.ts ├── static ├── GitMCP_PW.mp4 ├── GitMCP_final.mp4 ├── OG.png ├── README.md ├── cover.png ├── icon.png ├── icon_black.png ├── icon_black_cropped.png ├── icon_cropped.png └── img ├── tailwind.config.js ├── tests ├── e2e │ └── inspection.spec.ts └── global-setup.ts ├── tsconfig.cloudflare.json ├── tsconfig.json ├── tsconfig.node.json ├── vite.config.ts ├── vitest.config.ts ├── worker-configuration.d.ts └── wrangler.jsonc /.env.example: -------------------------------------------------------------------------------- 1 | XAI_API_KEY="" 2 | OPENAI_API_KEY= 3 | DATABASE_URL="postgresql://username:password@host:port/database" 4 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to GitMCP 2 | 3 | First of all, thank you for your interest in contributing to GitMCP! We appreciate the time and effort you're willing to invest in improving the project. This document provides guidelines and information to make the contribution process as smooth as possible. 4 | 5 | ## Table of Contents 6 | 7 | - [Getting Started](#getting-started) 8 | - [Local Development](#local-development) 9 | - [Prerequisites](#prerequisites) 10 | - [Setting Up Your Development Environment](#setting-up-your-development-environment) 11 | - [Running the Project Locally](#running-the-project-locally) 12 | - [Testing](#testing) 13 | - [Code Formatting](#code-formatting) 14 | - [Development Workflow](#development-workflow) 15 | - [Project Structure](#project-structure) 16 | - [How to Contribute](#how-to-contribute) 17 | - [Reporting Bugs](#reporting-bugs) 18 | - [Suggesting Enhancements](#suggesting-enhancements) 19 | - [Submitting Pull Requests](#submitting-pull-requests) 20 | - [Style Guidelines](#style-guidelines) 21 | - [Code Style](#code-style) 22 | - [Commit Messages](#commit-messages) 23 | - [Additional Resources](#additional-resources) 24 | 25 | ## Getting Started 26 | 27 | 1. Fork the repository and clone it to your local machine. 28 | 2. Set up the development environment. 29 | 3. Explore the codebase, run tests, and verify that everything works as expected. 30 | 31 | ## Local Development 32 | 33 | ### Prerequisites 34 | 35 | Before you start working on GitMCP, make sure you have the following installed: 36 | 37 | - [Node.js](https://nodejs.org/) (version 18 or higher recommended) 38 | - [pnpm](https://pnpm.io/) (version 8.15.7 or higher) 39 | - Git 40 | 41 | ### Setting Up Your Development Environment 42 | 43 | 1. Clone your forked repository: 44 | ```bash 45 | git clone https://github.com/your-username/git-mcp.git 46 | cd git-mcp 47 | ``` 48 | 49 | 2. Install dependencies: 50 | ```bash 51 | pnpm install 52 | ``` 53 | 54 | 3. Set up environment variables: 55 | - Create a `.env.local` file in the root directory 56 | - Add any necessary environment variables (ask project maintainers if you need access to specific API keys) 57 | 58 | ### Running the Project Locally 59 | 60 | To start the development server: 61 | 62 | ```bash 63 | pnpm vercel dev 64 | ``` 65 | 66 | This will start the Next.js development server, typically at http://localhost:3000. 67 | 68 | For running with the MCP Inspector (useful for debugging MCP endpoints): 69 | 70 | ```bash 71 | pnpm run inspector 72 | ``` 73 | 74 | ### Testing 75 | 76 | To run tests: 77 | 78 | ```bash 79 | pnpm test 80 | ``` 81 | 82 | GitMCP uses Vitest as the testing framework. When adding new features, please include appropriate tests. 83 | 84 | ### Code Formatting 85 | 86 | GitMCP uses Prettier for code formatting and lint-staged to ensure code is properly formatted before committing. Pre-commit hooks are set up with Husky to run these checks automatically. 87 | 88 | To manually format your code: 89 | 90 | ```bash 91 | pnpm prettier --write . 92 | ``` 93 | 94 | ### Development Workflow 95 | 96 | 1. Create a new branch for your feature/bugfix 97 | 2. Make your changes 98 | 3. Add tests for your changes when applicable 99 | 4. Run the tests to ensure they pass 100 | 5. Commit your changes following the commit message guidelines 101 | 6. Push your branch and open a pull request 102 | 103 | ### Project Structure 104 | 105 | - `api/`: Contains the server-side code and MCP implementation 106 | - `tools/`: MCP tools implementation 107 | - `utils/`: Utility functions for the API 108 | - `app/`: Next.js app directory with React components 109 | - `pages/`: Additional Next.js pages 110 | - `public/`: Static assets 111 | - `shared/`: Shared utilities used across the codebase 112 | 113 | ## How to Contribute 114 | 115 | ### Reporting Bugs 116 | 117 | If you encounter a bug or issue while using GitMCP, please open a new issue on the [GitHub Issues](https://github.com/idosal/git-mcp/issues) page. Provide a clear and concise description of the problem, steps to reproduce it, and any relevant error messages or logs. 118 | 119 | ### Suggesting Enhancements 120 | 121 | We welcome ideas for improvements and new features. To suggest an enhancement, open a new issue on the [GitHub Issues](https://github.com/idosal/git-mcp/issues) page. Describe the enhancement in detail, explain the use case, and outline the benefits it would bring to the project. 122 | 123 | ### Submitting Pull Requests 124 | 125 | 1. Create a new branch for your feature or bugfix. Use a descriptive name like `feature/your-feature-name` or `fix/your-bugfix-name`. 126 | 2. Make your changes, following the [Style Guidelines](#style-guidelines) below. 127 | 3. Test your changes and ensure that they don't introduce new issues or break existing functionality. 128 | 4. Commit your changes, following the [commit message guidelines](#commit-messages). 129 | 5. Push your branch to your fork on GitHub. 130 | 6. Open a new pull request against the `main` branch of the Wolverine repository. Include a clear and concise description of your changes, referencing any related issues. 131 | 132 | ## Style Guidelines 133 | 134 | ### Code Style 135 | 136 | GitMCP uses [ESLint](https://eslint.org/) as its code style guide. Please ensure that your code follows these guidelines. 137 | 138 | ### Commit Messages 139 | 140 | Write clear and concise commit messages that briefly describe the changes made in each commit. Use the imperative mood and start with a capitalized verb, e.g., "Add new feature" or "Fix bug in function". 141 | 142 | ## Additional Resources 143 | 144 | - [GitHub Help](https://help.github.com/) 145 | - [GitHub Pull Request Documentation](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests) 146 | - [ESLint Style Guide](https://eslint.org/) 147 | 148 | Thank you once again for your interest in contributing to GitMCP. We look forward to collaborating with you and creating an even better project together! 149 | 150 | -------------------------------------------------------------------------------- /.github/workflows/e2e-tests.yml: -------------------------------------------------------------------------------- 1 | name: E2E Tests 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - 'src/**' 7 | - 'tests/e2e/**' 8 | - 'package.json' 9 | - 'pnpm-lock.yaml' 10 | - 'wrangler.jsonc' 11 | - '.github/workflows/e2e-tests.yml' 12 | 13 | jobs: 14 | e2e_test: 15 | runs-on: ubuntu-latest 16 | timeout-minutes: 15 # Adjust timeout as needed 17 | permissions: 18 | contents: read 19 | pull-requests: write # Required to post the preview URL comment back to the PR 20 | 21 | steps: 22 | - name: Checkout repository 23 | uses: actions/checkout@v4 24 | 25 | - name: Setup Node.js 26 | uses: actions/setup-node@v4 27 | with: 28 | node-version: '20' # Match your project's Node.js version requirement 29 | 30 | - name: Setup PNPM 31 | uses: pnpm/action-setup@v4 32 | 33 | - name: Get pnpm store directory 34 | shell: bash 35 | run: | 36 | echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV 37 | 38 | - name: Setup pnpm cache 39 | uses: actions/cache@v4 40 | with: 41 | path: ${{ env.STORE_PATH }} 42 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} 43 | restore-keys: | 44 | ${{ runner.os }}-pnpm-store- 45 | 46 | - name: Install dependencies 47 | run: pnpm install --frozen-lockfile 48 | 49 | - name: Install Playwright Browsers 50 | run: npx playwright install chromium --with-deps 51 | 52 | - name: Create .dev.vars with test environment 53 | run: echo "ENVIRONMENT=test" > .dev.vars 54 | 55 | - name: Run Playwright tests 56 | run: pnpm run test:e2e -------------------------------------------------------------------------------- /.github/workflows/run-tests.yml: -------------------------------------------------------------------------------- 1 | name: Run Tests 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | test: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v4 15 | 16 | - name: Setup Node.js 17 | uses: actions/setup-node@v4 18 | with: 19 | node-version: '20' 20 | 21 | - name: Setup pnpm 22 | uses: pnpm/action-setup@v3 23 | with: 24 | version: '10.7.1' 25 | 26 | - name: Get pnpm store directory 27 | id: pnpm-cache 28 | run: | 29 | echo "pnpm_cache_dir=$(pnpm store path)" >> $GITHUB_OUTPUT 30 | 31 | - name: Setup pnpm cache 32 | uses: actions/cache@v4 33 | with: 34 | path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }} 35 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} 36 | restore-keys: | 37 | ${{ runner.os }}-pnpm-store- 38 | 39 | - name: Install dependencies 40 | run: pnpm install 41 | 42 | - name: Run tests 43 | run: pnpm test 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | 4 | .nx 5 | .idea 6 | .vscode 7 | .zed 8 | # Logs 9 | 10 | logs 11 | _.log 12 | npm-debug.log_ 13 | yarn-debug.log* 14 | yarn-error.log* 15 | lerna-debug.log* 16 | .pnpm-debug.log* 17 | 18 | # Diagnostic reports (https://nodejs.org/api/report.html) 19 | 20 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json 21 | 22 | # Runtime data 23 | 24 | pids 25 | _.pid 26 | _.seed 27 | \*.pid.lock 28 | 29 | # Directory for instrumented libs generated by jscoverage/JSCover 30 | 31 | lib-cov 32 | 33 | # Coverage directory used by tools like istanbul 34 | 35 | coverage 36 | \*.lcov 37 | 38 | # nyc test coverage 39 | 40 | .nyc_output 41 | 42 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 43 | 44 | .grunt 45 | 46 | # Bower dependency directory (https://bower.io/) 47 | 48 | bower_components 49 | 50 | # node-waf configuration 51 | 52 | .lock-wscript 53 | 54 | # Compiled binary addons (https://nodejs.org/api/addons.html) 55 | 56 | build/Release 57 | 58 | # Dependency directories 59 | 60 | node_modules/ 61 | jspm_packages/ 62 | 63 | # Snowpack dependency directory (https://snowpack.dev/) 64 | 65 | web_modules/ 66 | 67 | # TypeScript cache 68 | 69 | \*.tsbuildinfo 70 | 71 | # Optional npm cache directory 72 | 73 | .npm 74 | 75 | # Optional eslint cache 76 | 77 | .eslintcache 78 | 79 | # Optional stylelint cache 80 | 81 | .stylelintcache 82 | 83 | # Microbundle cache 84 | 85 | .rpt2_cache/ 86 | .rts2_cache_cjs/ 87 | .rts2_cache_es/ 88 | .rts2_cache_umd/ 89 | 90 | # Optional REPL history 91 | 92 | .node_repl_history 93 | 94 | # Output of 'npm pack' 95 | 96 | \*.tgz 97 | 98 | # Yarn Integrity file 99 | 100 | .yarn-integrity 101 | 102 | # dotenv environment variable files 103 | 104 | .env 105 | .env.development.local 106 | .env.test.local 107 | .env.production.local 108 | .env.local 109 | 110 | # parcel-bundler cache (https://parceljs.org/) 111 | 112 | .cache 113 | .parcel-cache 114 | 115 | # Next.js build output 116 | 117 | .next 118 | out 119 | 120 | # Nuxt.js build / generate output 121 | 122 | .nuxt 123 | dist 124 | 125 | # Gatsby files 126 | 127 | .cache/ 128 | 129 | # Comment in the public line in if your project uses Gatsby and not Next.js 130 | 131 | # https://nextjs.org/blog/next-9-1#public-directory-support 132 | 133 | # public 134 | 135 | # vuepress build output 136 | 137 | .vuepress/dist 138 | 139 | # vuepress v2.x temp and cache directory 140 | 141 | .temp 142 | .cache 143 | 144 | # Docusaurus cache and generated files 145 | 146 | .docusaurus 147 | 148 | # Serverless directories 149 | 150 | .serverless/ 151 | 152 | # FuseBox cache 153 | 154 | .fusebox/ 155 | 156 | # DynamoDB Local files 157 | 158 | .dynamodb/ 159 | 160 | # TernJS port file 161 | 162 | .tern-port 163 | 164 | # Stores VSCode versions used for testing VSCode extensions 165 | 166 | .vscode-test 167 | 168 | # yarn v2 169 | 170 | .yarn/cache 171 | .yarn/unplugged 172 | .yarn/build-state.yml 173 | .yarn/install-state.gz 174 | .pnp.\* 175 | 176 | # wrangler project 177 | 178 | .dev.vars 179 | .wrangler/ 180 | 181 | # playwright 182 | 183 | playwright-results/ 184 | playwright-report/ 185 | test-results/ -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | pnpm lint-staged 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "trailingComma": "all", 4 | "singleQuote": false, 5 | "printWidth": 80, 6 | "tabWidth": 2, 7 | "useTabs": false, 8 | "bracketSpacing": true, 9 | "arrowParens": "always", 10 | "endOfLine": "lf" 11 | } 12 | -------------------------------------------------------------------------------- /.react-router/types/+register.ts: -------------------------------------------------------------------------------- 1 | import "react-router"; 2 | 3 | declare module "react-router" { 4 | interface Register { 5 | params: Params; 6 | } 7 | } 8 | 9 | type Params = { 10 | "/": {}; 11 | "/api/chat": {}; 12 | "/*": { 13 | "*": string; 14 | }; 15 | }; -------------------------------------------------------------------------------- /.react-router/types/+virtual.d.ts: -------------------------------------------------------------------------------- 1 | declare module "virtual:react-router/server-build" { 2 | import { ServerBuild } from "react-router"; 3 | export const assets: ServerBuild["assets"]; 4 | export const assetsBuildDirectory: ServerBuild["assetsBuildDirectory"]; 5 | export const basename: ServerBuild["basename"]; 6 | export const entry: ServerBuild["entry"]; 7 | export const future: ServerBuild["future"]; 8 | export const isSpaMode: ServerBuild["isSpaMode"]; 9 | export const prerender: ServerBuild["prerender"]; 10 | export const publicPath: ServerBuild["publicPath"]; 11 | export const routes: ServerBuild["routes"]; 12 | export const ssr: ServerBuild["ssr"]; 13 | export const unstable_getCriticalCss: ServerBuild["unstable_getCriticalCss"]; 14 | } -------------------------------------------------------------------------------- /.react-router/types/app/+types/root.ts: -------------------------------------------------------------------------------- 1 | // React Router generated types for route: 2 | // root.tsx 3 | 4 | import type * as T from "react-router/route-module" 5 | 6 | 7 | 8 | type Module = typeof import("../root.js") 9 | 10 | export type Info = { 11 | parents: [], 12 | id: "root" 13 | file: "root.tsx" 14 | path: "" 15 | params: {} & { [key: string]: string | undefined } 16 | module: Module 17 | loaderData: T.CreateLoaderData 18 | actionData: T.CreateActionData 19 | } 20 | 21 | export namespace Route { 22 | export type LinkDescriptors = T.LinkDescriptors 23 | export type LinksFunction = () => LinkDescriptors 24 | 25 | export type MetaArgs = T.CreateMetaArgs 26 | export type MetaDescriptors = T.MetaDescriptors 27 | export type MetaFunction = (args: MetaArgs) => MetaDescriptors 28 | 29 | export type HeadersArgs = T.HeadersArgs 30 | export type HeadersFunction = (args: HeadersArgs) => Headers | HeadersInit 31 | 32 | export type unstable_MiddlewareFunction = T.CreateServerMiddlewareFunction 33 | export type unstable_ClientMiddlewareFunction = T.CreateClientMiddlewareFunction 34 | export type LoaderArgs = T.CreateServerLoaderArgs 35 | export type ClientLoaderArgs = T.CreateClientLoaderArgs 36 | export type ActionArgs = T.CreateServerActionArgs 37 | export type ClientActionArgs = T.CreateClientActionArgs 38 | 39 | export type HydrateFallbackProps = T.CreateHydrateFallbackProps 40 | export type ComponentProps = T.CreateComponentProps 41 | export type ErrorBoundaryProps = T.CreateErrorBoundaryProps 42 | } -------------------------------------------------------------------------------- /.react-router/types/app/routes/+types/$.ts: -------------------------------------------------------------------------------- 1 | // React Router generated types for route: 2 | // routes/$.tsx 3 | 4 | import type * as T from "react-router/route-module" 5 | 6 | import type { Info as Parent0 } from "../../+types/root.js" 7 | 8 | type Module = typeof import("../$.js") 9 | 10 | export type Info = { 11 | parents: [Parent0], 12 | id: "routes/$" 13 | file: "routes/$.tsx" 14 | path: "*" 15 | params: {"*": string} & { [key: string]: string | undefined } 16 | module: Module 17 | loaderData: T.CreateLoaderData 18 | actionData: T.CreateActionData 19 | } 20 | 21 | export namespace Route { 22 | export type LinkDescriptors = T.LinkDescriptors 23 | export type LinksFunction = () => LinkDescriptors 24 | 25 | export type MetaArgs = T.CreateMetaArgs 26 | export type MetaDescriptors = T.MetaDescriptors 27 | export type MetaFunction = (args: MetaArgs) => MetaDescriptors 28 | 29 | export type HeadersArgs = T.HeadersArgs 30 | export type HeadersFunction = (args: HeadersArgs) => Headers | HeadersInit 31 | 32 | export type unstable_MiddlewareFunction = T.CreateServerMiddlewareFunction 33 | export type unstable_ClientMiddlewareFunction = T.CreateClientMiddlewareFunction 34 | export type LoaderArgs = T.CreateServerLoaderArgs 35 | export type ClientLoaderArgs = T.CreateClientLoaderArgs 36 | export type ActionArgs = T.CreateServerActionArgs 37 | export type ClientActionArgs = T.CreateClientActionArgs 38 | 39 | export type HydrateFallbackProps = T.CreateHydrateFallbackProps 40 | export type ComponentProps = T.CreateComponentProps 41 | export type ErrorBoundaryProps = T.CreateErrorBoundaryProps 42 | } -------------------------------------------------------------------------------- /.react-router/types/app/routes/+types/_index.ts: -------------------------------------------------------------------------------- 1 | // React Router generated types for route: 2 | // routes/_index.tsx 3 | 4 | import type * as T from "react-router/route-module" 5 | 6 | import type { Info as Parent0 } from "../../+types/root.js" 7 | 8 | type Module = typeof import("../_index.js") 9 | 10 | export type Info = { 11 | parents: [Parent0], 12 | id: "routes/_index" 13 | file: "routes/_index.tsx" 14 | path: "undefined" 15 | params: {} & { [key: string]: string | undefined } 16 | module: Module 17 | loaderData: T.CreateLoaderData 18 | actionData: T.CreateActionData 19 | } 20 | 21 | export namespace Route { 22 | export type LinkDescriptors = T.LinkDescriptors 23 | export type LinksFunction = () => LinkDescriptors 24 | 25 | export type MetaArgs = T.CreateMetaArgs 26 | export type MetaDescriptors = T.MetaDescriptors 27 | export type MetaFunction = (args: MetaArgs) => MetaDescriptors 28 | 29 | export type HeadersArgs = T.HeadersArgs 30 | export type HeadersFunction = (args: HeadersArgs) => Headers | HeadersInit 31 | 32 | export type unstable_MiddlewareFunction = T.CreateServerMiddlewareFunction 33 | export type unstable_ClientMiddlewareFunction = T.CreateClientMiddlewareFunction 34 | export type LoaderArgs = T.CreateServerLoaderArgs 35 | export type ClientLoaderArgs = T.CreateClientLoaderArgs 36 | export type ActionArgs = T.CreateServerActionArgs 37 | export type ClientActionArgs = T.CreateClientActionArgs 38 | 39 | export type HydrateFallbackProps = T.CreateHydrateFallbackProps 40 | export type ComponentProps = T.CreateComponentProps 41 | export type ErrorBoundaryProps = T.CreateErrorBoundaryProps 42 | } -------------------------------------------------------------------------------- /.react-router/types/app/routes/+types/api.chat.ts: -------------------------------------------------------------------------------- 1 | // React Router generated types for route: 2 | // routes/api.chat.ts 3 | 4 | import type * as T from "react-router/route-module" 5 | 6 | import type { Info as Parent0 } from "../../+types/root.js" 7 | 8 | type Module = typeof import("../api.chat.js") 9 | 10 | export type Info = { 11 | parents: [Parent0], 12 | id: "routes/api.chat" 13 | file: "routes/api.chat.ts" 14 | path: "api/chat" 15 | params: {} & { [key: string]: string | undefined } 16 | module: Module 17 | loaderData: T.CreateLoaderData 18 | actionData: T.CreateActionData 19 | } 20 | 21 | export namespace Route { 22 | export type LinkDescriptors = T.LinkDescriptors 23 | export type LinksFunction = () => LinkDescriptors 24 | 25 | export type MetaArgs = T.CreateMetaArgs 26 | export type MetaDescriptors = T.MetaDescriptors 27 | export type MetaFunction = (args: MetaArgs) => MetaDescriptors 28 | 29 | export type HeadersArgs = T.HeadersArgs 30 | export type HeadersFunction = (args: HeadersArgs) => Headers | HeadersInit 31 | 32 | export type unstable_MiddlewareFunction = T.CreateServerMiddlewareFunction 33 | export type unstable_ClientMiddlewareFunction = T.CreateClientMiddlewareFunction 34 | export type LoaderArgs = T.CreateServerLoaderArgs 35 | export type ClientLoaderArgs = T.CreateClientLoaderArgs 36 | export type ActionArgs = T.CreateServerActionArgs 37 | export type ClientActionArgs = T.CreateClientActionArgs 38 | 39 | export type HydrateFallbackProps = T.CreateHydrateFallbackProps 40 | export type ComponentProps = T.CreateComponentProps 41 | export type ErrorBoundaryProps = T.CreateErrorBoundaryProps 42 | } -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | GitMCP is committed to maintaining the security and privacy of its users. We welcome vulnerability disclosures, suggestions, and feedback that can improve it. 4 | 5 | ## Reporting a Vulnerability 6 | 7 | Please report vulnerabilities using this private [form](https://forms.gle/mMoVv69XFyPqDUTX8). All vulnerabilities will be patched as soon as possible. 8 | -------------------------------------------------------------------------------- /app/app.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss"; 2 | 3 | @theme { 4 | --font-sans: "Inter", ui-sans-serif, system-ui, sans-serif, 5 | "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; 6 | } 7 | 8 | html, 9 | body { 10 | @apply bg-white dark:bg-gray-950; 11 | 12 | @media (prefers-color-scheme: dark) { 13 | color-scheme: dark; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/chat/ai/providers.server.ts: -------------------------------------------------------------------------------- 1 | import { createOpenAI } from "@ai-sdk/openai"; 2 | import { createGroq } from "@ai-sdk/groq"; 3 | import { createAnthropic } from "@ai-sdk/anthropic"; 4 | import { createXai } from "@ai-sdk/xai"; 5 | 6 | import { 7 | customProvider, 8 | wrapLanguageModel, 9 | extractReasoningMiddleware, 10 | type LanguageModel, 11 | } from "ai"; 12 | import type { modelID, StorageKey } from "./providers.shared"; 13 | 14 | const middleware = extractReasoningMiddleware({ 15 | tagName: "think", 16 | }); 17 | 18 | export const getModel = ( 19 | env: CloudflareEnvironment, 20 | apiKeys: Partial>, 21 | ) => { 22 | // Helper to get API keys from environment variables first, then localStorage 23 | const getApiKey = (key: StorageKey): string | undefined => { 24 | // Check for environment variables first 25 | if (env[key]) { 26 | return env[key] || undefined; 27 | } 28 | 29 | // Check for API keys in localStorage 30 | if (apiKeys[key]) { 31 | return apiKeys[key] || undefined; 32 | } 33 | 34 | return undefined; 35 | }; 36 | 37 | // Create provider instances with API keys from env/user/localStorage 38 | const openaiClient = createOpenAI({ 39 | apiKey: getApiKey("OPENAI_API_KEY"), 40 | }); 41 | 42 | const anthropicClient = createAnthropic({ 43 | apiKey: getApiKey("ANTHROPIC_API_KEY"), 44 | }); 45 | 46 | const groqClient = createGroq({ 47 | apiKey: getApiKey("GROQ_API_KEY"), 48 | }); 49 | 50 | const xaiClient = createXai({ 51 | apiKey: getApiKey("XAI_API_KEY"), 52 | }); 53 | 54 | const languageModels: Record = { 55 | "gpt-4.1-mini": openaiClient("gpt-4.1-mini"), 56 | "claude-3-7-sonnet": anthropicClient("claude-3-7-sonnet-20250219"), 57 | "qwen-qwq": wrapLanguageModel({ 58 | model: groqClient("qwen-qwq-32b"), 59 | middleware, 60 | }), 61 | "grok-3-mini": xaiClient("grok-3-mini-latest"), 62 | }; 63 | 64 | const model = customProvider({ 65 | languageModels, 66 | }); 67 | 68 | return model; 69 | }; 70 | -------------------------------------------------------------------------------- /app/chat/ai/providers.shared.ts: -------------------------------------------------------------------------------- 1 | export interface ModelInfo { 2 | provider: string; 3 | name: string; 4 | description: string; 5 | apiVersion: string; 6 | capabilities: string[]; 7 | } 8 | 9 | export type StorageKey = 10 | | "OPENAI_API_KEY" 11 | | "ANTHROPIC_API_KEY" 12 | | "GROQ_API_KEY" 13 | | "XAI_API_KEY"; 14 | 15 | export type modelID = 16 | | "gpt-4.1-mini" 17 | | "claude-3-7-sonnet" 18 | | "qwen-qwq" 19 | | "grok-3-mini"; 20 | 21 | export const modelDetails: Record = { 22 | "gpt-4.1-mini": { 23 | provider: "OpenAI", 24 | name: "GPT-4.1 Mini", 25 | description: 26 | "Compact version of OpenAI's GPT-4.1 with good balance of capabilities, including vision.", 27 | apiVersion: "gpt-4.1-mini", 28 | capabilities: ["Balance", "Creative", "Vision"], 29 | }, 30 | "claude-3-7-sonnet": { 31 | provider: "Anthropic", 32 | name: "Claude 3.7 Sonnet", 33 | description: 34 | "Latest version of Anthropic's Claude 3.7 Sonnet with strong reasoning and coding capabilities.", 35 | apiVersion: "claude-3-7-sonnet-20250219", 36 | capabilities: ["Reasoning", "Efficient", "Agentic"], 37 | }, 38 | "qwen-qwq": { 39 | provider: "Groq", 40 | name: "Qwen QWQ", 41 | description: 42 | "Latest version of Alibaba's Qwen QWQ with strong reasoning and coding capabilities.", 43 | apiVersion: "qwen-qwq", 44 | capabilities: ["Reasoning", "Efficient", "Agentic"], 45 | }, 46 | "grok-3-mini": { 47 | provider: "XAI", 48 | name: "Grok 3 Mini", 49 | description: 50 | "Latest version of XAI's Grok 3 Mini with strong reasoning and coding capabilities.", 51 | apiVersion: "grok-3-mini-latest", 52 | capabilities: ["Reasoning", "Efficient", "Agentic"], 53 | }, 54 | }; 55 | 56 | export const MODELS = Object.keys(modelDetails); 57 | 58 | export const defaultModel: modelID = "qwen-qwq"; 59 | -------------------------------------------------------------------------------- /app/chat/components/api-key-manager.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useCallback } from "react"; 2 | import { 3 | Dialog, 4 | DialogContent, 5 | DialogDescription, 6 | DialogFooter, 7 | DialogHeader, 8 | DialogTitle, 9 | } from "~/chat/components/ui/dialog"; 10 | import { Button } from "~/chat/components/ui/button"; 11 | import { Input } from "~/chat/components/ui/input"; 12 | import { Label } from "~/chat/components/ui/label"; 13 | import { toast } from "sonner"; 14 | import { STORAGE_KEYS } from "~/chat/lib/constants"; 15 | import type { StorageKey } from "../ai/providers.shared"; 16 | import { useApiKeys } from "./api-keys-provider"; 17 | 18 | // API key configuration 19 | export interface ApiKeyConfig { 20 | name: string; 21 | key: string; 22 | storageKey: StorageKey; 23 | label: string; 24 | placeholder: string; 25 | } 26 | 27 | // Available API keys configuration 28 | export const API_KEYS_CONFIG: readonly ApiKeyConfig[] = [ 29 | { 30 | name: "OpenAI", 31 | key: "openai", 32 | storageKey: "OPENAI_API_KEY", 33 | label: "OpenAI API Key", 34 | placeholder: "sk-...", 35 | }, 36 | { 37 | name: "Anthropic", 38 | key: "anthropic", 39 | storageKey: "ANTHROPIC_API_KEY", 40 | label: "Anthropic API Key", 41 | placeholder: "sk-ant-...", 42 | }, 43 | { 44 | name: "Groq", 45 | key: "groq", 46 | storageKey: "GROQ_API_KEY", 47 | label: "Groq API Key", 48 | placeholder: "gsk_...", 49 | }, 50 | { 51 | name: "XAI", 52 | key: "xai", 53 | storageKey: "XAI_API_KEY", 54 | label: "XAI API Key", 55 | placeholder: "xai-...", 56 | }, 57 | ] as const; 58 | 59 | interface ApiKeyManagerProps { 60 | open: boolean; 61 | onOpenChange: (open: boolean) => void; 62 | } 63 | 64 | export function ApiKeyManager({ open, onOpenChange }: ApiKeyManagerProps) { 65 | // State to store API keys 66 | const { apiKeys, setApiKeys } = useApiKeys(); 67 | const [localApiKeys, setLocalApiKeys] = 68 | useState>>(apiKeys); 69 | 70 | // Update API key in state 71 | const handleApiKeyChange = useCallback( 72 | (storageKey: StorageKey, value: string) => { 73 | setLocalApiKeys((prev) => ({ 74 | ...prev, 75 | [storageKey]: value, 76 | })); 77 | }, 78 | [], 79 | ); 80 | 81 | // Save API keys to localStorage 82 | const handleSaveApiKeys = useCallback(() => { 83 | try { 84 | setApiKeys(localApiKeys); 85 | localStorage.setItem(STORAGE_KEYS.API_KEYS, JSON.stringify(localApiKeys)); 86 | 87 | toast.success("API keys saved successfully"); 88 | onOpenChange(false); 89 | } catch (error) { 90 | console.error("Error saving API keys:", error); 91 | toast.error("Failed to save API keys"); 92 | } 93 | }, [localApiKeys, setApiKeys, onOpenChange]); 94 | 95 | // Clear all API keys 96 | const handleClearApiKeys = useCallback(() => { 97 | try { 98 | setLocalApiKeys({}); 99 | setApiKeys({}); 100 | toast.success("All API keys cleared"); 101 | } catch (error) { 102 | console.error("Error clearing API keys:", error); 103 | toast.error("Failed to clear API keys"); 104 | } 105 | }, [setApiKeys, localApiKeys]); 106 | 107 | return ( 108 | 109 | 110 | 111 | API Key Settings 112 | 113 | Enter your own API keys for different AI providers. Keys are stored 114 | securely in your browser's local storage. 115 | 116 | 117 | 118 |
119 | {API_KEYS_CONFIG.map((config) => ( 120 |
121 | 122 | 127 | handleApiKeyChange(config.storageKey, e.target.value) 128 | } 129 | placeholder={config.placeholder} 130 | /> 131 |
132 | ))} 133 |
134 | 135 | 136 | 139 |
140 | 143 | 144 |
145 |
146 |
147 |
148 | ); 149 | } 150 | -------------------------------------------------------------------------------- /app/chat/components/api-keys-provider.tsx: -------------------------------------------------------------------------------- 1 | import { createContext, useContext } from "react"; 2 | import type { StorageKey } from "../ai/providers.shared"; 3 | import { STORAGE_KEYS } from "../lib/constants"; 4 | import { useLocalStorage } from "../lib/hooks/use-local-storage"; 5 | 6 | const ApiKeysContext = createContext<{ 7 | apiKeys: Partial>; 8 | setApiKeys: (apiKeys: Partial>) => void; 9 | }>({ 10 | apiKeys: {}, 11 | setApiKeys: () => {}, 12 | }); 13 | 14 | export function ApiKeysProvider({ children }: { children: React.ReactNode }) { 15 | const [apiKeys, setApiKeys] = useLocalStorage< 16 | Partial> 17 | >(STORAGE_KEYS.API_KEYS, {}); 18 | 19 | return ( 20 | 21 | {children} 22 | 23 | ); 24 | } 25 | 26 | export function useApiKeys() { 27 | return useContext(ApiKeysContext); 28 | } 29 | -------------------------------------------------------------------------------- /app/chat/components/chat.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { defaultModel, type modelID } from "~/chat/ai/providers.shared"; 4 | import { useChat } from "@ai-sdk/react"; 5 | import { Textarea } from "./textarea"; 6 | import { ProjectOverview } from "./project-overview"; 7 | import { Messages } from "./messages"; 8 | import { toast } from "sonner"; 9 | import { useLocalStorage } from "~/chat/lib/hooks/use-local-storage"; 10 | import { useMCP } from "~/chat/lib/context/mcp-context"; 11 | import { useCallback } from "react"; 12 | import { useApiKeys } from "./api-keys-provider"; 13 | 14 | const CHAT_API_URL = "https://chat-api-worker.idosalomon.workers.dev/api/chat"; 15 | 16 | export default function Chat() { 17 | const [selectedModel, setSelectedModel] = useLocalStorage( 18 | "selectedModel", 19 | defaultModel, 20 | ); 21 | 22 | const { apiKeys } = useApiKeys(); 23 | 24 | // Get MCP server data from context 25 | const { mcpServersForApi } = useMCP(); 26 | 27 | const { messages, input, handleInputChange, handleSubmit, status, stop } = 28 | useChat({ 29 | api: CHAT_API_URL, 30 | maxSteps: 20, 31 | body: { 32 | selectedModel, 33 | mcpServers: mcpServersForApi, 34 | apiKeys, 35 | }, 36 | experimental_throttle: 500, 37 | onError: (error) => { 38 | toast.error( 39 | error.message.length > 0 40 | ? error.message 41 | : "An error occurred, please try again later.", 42 | { position: "top-center", richColors: true }, 43 | ); 44 | }, 45 | }); 46 | 47 | // Custom submit handler 48 | const handleFormSubmit = useCallback( 49 | (e: React.FormEvent) => { 50 | e.preventDefault(); 51 | handleSubmit(e); 52 | }, 53 | [input, handleSubmit], 54 | ); 55 | 56 | const isLoading = status === "streaming" || status === "submitted"; 57 | 58 | return ( 59 |
60 | {messages.length === 0 ? ( 61 |
62 | 63 |
64 |