Repository: idosal/git-mcp Branch: main Commit: ed56be437f10 Files: 127 Total size: 1.9 MB Directory structure: gitextract_khbd3ulr/ ├── .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/ │ │ ├── $.tsx │ │ ├── _index.tsx │ │ └── api.chat.ts │ └── routes.ts ├── biome.json ├── components.json ├── package.json ├── playwright.config.ts ├── postcss.config.mjs ├── 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/ │ └── README.md ├── 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 ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/CONTRIBUTING.md ================================================ # Contributing to GitMCP 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. ## Table of Contents - [Getting Started](#getting-started) - [Local Development](#local-development) - [Prerequisites](#prerequisites) - [Setting Up Your Development Environment](#setting-up-your-development-environment) - [Running the Project Locally](#running-the-project-locally) - [Testing](#testing) - [Code Formatting](#code-formatting) - [Development Workflow](#development-workflow) - [Project Structure](#project-structure) - [How to Contribute](#how-to-contribute) - [Reporting Bugs](#reporting-bugs) - [Suggesting Enhancements](#suggesting-enhancements) - [Submitting Pull Requests](#submitting-pull-requests) - [Style Guidelines](#style-guidelines) - [Code Style](#code-style) - [Commit Messages](#commit-messages) - [Additional Resources](#additional-resources) ## Getting Started 1. Fork the repository and clone it to your local machine. 2. Set up the development environment. 3. Explore the codebase, run tests, and verify that everything works as expected. ## Local Development ### Prerequisites Before you start working on GitMCP, make sure you have the following installed: - [Node.js](https://nodejs.org/) (version 18 or higher recommended) - [pnpm](https://pnpm.io/) (version 8.15.7 or higher) - Git ### Setting Up Your Development Environment 1. Clone your forked repository: ```bash git clone https://github.com/your-username/git-mcp.git cd git-mcp ``` 2. Install dependencies: ```bash pnpm install ``` 3. Set up environment variables: - Create a `.env.local` file in the root directory - Add any necessary environment variables (ask project maintainers if you need access to specific API keys) ### Running the Project Locally To start the development server: ```bash pnpm vercel dev ``` This will start the Next.js development server, typically at http://localhost:3000. For running with the MCP Inspector (useful for debugging MCP endpoints): ```bash pnpm run inspector ``` ### Testing To run tests: ```bash pnpm test ``` GitMCP uses Vitest as the testing framework. When adding new features, please include appropriate tests. ### Code Formatting 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. To manually format your code: ```bash pnpm prettier --write . ``` ### Development Workflow 1. Create a new branch for your feature/bugfix 2. Make your changes 3. Add tests for your changes when applicable 4. Run the tests to ensure they pass 5. Commit your changes following the commit message guidelines 6. Push your branch and open a pull request ### Project Structure - `api/`: Contains the server-side code and MCP implementation - `tools/`: MCP tools implementation - `utils/`: Utility functions for the API - `app/`: Next.js app directory with React components - `pages/`: Additional Next.js pages - `public/`: Static assets - `shared/`: Shared utilities used across the codebase ## How to Contribute ### Reporting Bugs 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. ### Suggesting Enhancements 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. ### Submitting Pull Requests 1. Create a new branch for your feature or bugfix. Use a descriptive name like `feature/your-feature-name` or `fix/your-bugfix-name`. 2. Make your changes, following the [Style Guidelines](#style-guidelines) below. 3. Test your changes and ensure that they don't introduce new issues or break existing functionality. 4. Commit your changes, following the [commit message guidelines](#commit-messages). 5. Push your branch to your fork on GitHub. 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. ## Style Guidelines ### Code Style GitMCP uses [ESLint](https://eslint.org/) as its code style guide. Please ensure that your code follows these guidelines. ### Commit Messages 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". ## Additional Resources - [GitHub Help](https://help.github.com/) - [GitHub Pull Request Documentation](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests) - [ESLint Style Guide](https://eslint.org/) 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! ================================================ FILE: .github/workflows/e2e-tests.yml ================================================ name: E2E Tests on: pull_request: paths: - 'src/**' - 'tests/e2e/**' - 'package.json' - 'pnpm-lock.yaml' - 'wrangler.jsonc' - '.github/workflows/e2e-tests.yml' jobs: e2e_test: runs-on: ubuntu-latest timeout-minutes: 15 # Adjust timeout as needed permissions: contents: read pull-requests: write # Required to post the preview URL comment back to the PR steps: - name: Checkout repository uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' # Match your project's Node.js version requirement - name: Setup PNPM uses: pnpm/action-setup@v4 - name: Get pnpm store directory shell: bash run: | echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV - name: Setup pnpm cache uses: actions/cache@v4 with: path: ${{ env.STORE_PATH }} key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} restore-keys: | ${{ runner.os }}-pnpm-store- - name: Install dependencies run: pnpm install --frozen-lockfile - name: Install Playwright Browsers run: npx playwright install chromium --with-deps - name: Create .dev.vars with test environment run: echo "ENVIRONMENT=test" > .dev.vars - name: Run Playwright tests run: pnpm run test:e2e ================================================ FILE: .github/workflows/run-tests.yml ================================================ name: Run Tests on: push: branches: [ main ] pull_request: branches: [ main ] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' - name: Setup pnpm uses: pnpm/action-setup@v3 with: version: '10.7.1' - name: Get pnpm store directory id: pnpm-cache run: | echo "pnpm_cache_dir=$(pnpm store path)" >> $GITHUB_OUTPUT - name: Setup pnpm cache uses: actions/cache@v4 with: path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }} key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} restore-keys: | ${{ runner.os }}-pnpm-store- - name: Install dependencies run: pnpm install - name: Run tests run: pnpm test ================================================ FILE: .gitignore ================================================ node_modules build .nx .idea .vscode .zed # Logs logs _.log npm-debug.log_ yarn-debug.log* yarn-error.log* lerna-debug.log* .pnpm-debug.log* # Diagnostic reports (https://nodejs.org/api/report.html) report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json # Runtime data pids _.pid _.seed \*.pid.lock # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage \*.lcov # nyc test coverage .nyc_output # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) .grunt # Bower dependency directory (https://bower.io/) bower_components # node-waf configuration .lock-wscript # Compiled binary addons (https://nodejs.org/api/addons.html) build/Release # Dependency directories node_modules/ jspm_packages/ # Snowpack dependency directory (https://snowpack.dev/) web_modules/ # TypeScript cache \*.tsbuildinfo # Optional npm cache directory .npm # Optional eslint cache .eslintcache # Optional stylelint cache .stylelintcache # Microbundle cache .rpt2_cache/ .rts2_cache_cjs/ .rts2_cache_es/ .rts2_cache_umd/ # Optional REPL history .node_repl_history # Output of 'npm pack' \*.tgz # Yarn Integrity file .yarn-integrity # dotenv environment variable files .env .env.development.local .env.test.local .env.production.local .env.local # parcel-bundler cache (https://parceljs.org/) .cache .parcel-cache # Next.js build output .next out # Nuxt.js build / generate output .nuxt dist # Gatsby files .cache/ # Comment in the public line in if your project uses Gatsby and not Next.js # https://nextjs.org/blog/next-9-1#public-directory-support # public # vuepress build output .vuepress/dist # vuepress v2.x temp and cache directory .temp .cache # Docusaurus cache and generated files .docusaurus # Serverless directories .serverless/ # FuseBox cache .fusebox/ # DynamoDB Local files .dynamodb/ # TernJS port file .tern-port # Stores VSCode versions used for testing VSCode extensions .vscode-test # yarn v2 .yarn/cache .yarn/unplugged .yarn/build-state.yml .yarn/install-state.gz .pnp.\* # wrangler project .dev.vars .wrangler/ # playwright playwright-results/ playwright-report/ test-results/ ================================================ FILE: .husky/pre-commit ================================================ pnpm lint-staged ================================================ FILE: .prettierrc ================================================ { "semi": true, "trailingComma": "all", "singleQuote": false, "printWidth": 80, "tabWidth": 2, "useTabs": false, "bracketSpacing": true, "arrowParens": "always", "endOfLine": "lf" } ================================================ FILE: .react-router/types/+register.ts ================================================ import "react-router"; declare module "react-router" { interface Register { params: Params; } } type Params = { "/": {}; "/api/chat": {}; "/*": { "*": string; }; }; ================================================ FILE: .react-router/types/+virtual.d.ts ================================================ declare module "virtual:react-router/server-build" { import { ServerBuild } from "react-router"; export const assets: ServerBuild["assets"]; export const assetsBuildDirectory: ServerBuild["assetsBuildDirectory"]; export const basename: ServerBuild["basename"]; export const entry: ServerBuild["entry"]; export const future: ServerBuild["future"]; export const isSpaMode: ServerBuild["isSpaMode"]; export const prerender: ServerBuild["prerender"]; export const publicPath: ServerBuild["publicPath"]; export const routes: ServerBuild["routes"]; export const ssr: ServerBuild["ssr"]; export const unstable_getCriticalCss: ServerBuild["unstable_getCriticalCss"]; } ================================================ FILE: .react-router/types/app/+types/root.ts ================================================ // React Router generated types for route: // root.tsx import type * as T from "react-router/route-module" type Module = typeof import("../root.js") export type Info = { parents: [], id: "root" file: "root.tsx" path: "" params: {} & { [key: string]: string | undefined } module: Module loaderData: T.CreateLoaderData actionData: T.CreateActionData } export namespace Route { export type LinkDescriptors = T.LinkDescriptors export type LinksFunction = () => LinkDescriptors export type MetaArgs = T.CreateMetaArgs export type MetaDescriptors = T.MetaDescriptors export type MetaFunction = (args: MetaArgs) => MetaDescriptors export type HeadersArgs = T.HeadersArgs export type HeadersFunction = (args: HeadersArgs) => Headers | HeadersInit export type unstable_MiddlewareFunction = T.CreateServerMiddlewareFunction export type unstable_ClientMiddlewareFunction = T.CreateClientMiddlewareFunction export type LoaderArgs = T.CreateServerLoaderArgs export type ClientLoaderArgs = T.CreateClientLoaderArgs export type ActionArgs = T.CreateServerActionArgs export type ClientActionArgs = T.CreateClientActionArgs export type HydrateFallbackProps = T.CreateHydrateFallbackProps export type ComponentProps = T.CreateComponentProps export type ErrorBoundaryProps = T.CreateErrorBoundaryProps } ================================================ FILE: .react-router/types/app/routes/+types/$.ts ================================================ // React Router generated types for route: // routes/$.tsx import type * as T from "react-router/route-module" import type { Info as Parent0 } from "../../+types/root.js" type Module = typeof import("../$.js") export type Info = { parents: [Parent0], id: "routes/$" file: "routes/$.tsx" path: "*" params: {"*": string} & { [key: string]: string | undefined } module: Module loaderData: T.CreateLoaderData actionData: T.CreateActionData } export namespace Route { export type LinkDescriptors = T.LinkDescriptors export type LinksFunction = () => LinkDescriptors export type MetaArgs = T.CreateMetaArgs export type MetaDescriptors = T.MetaDescriptors export type MetaFunction = (args: MetaArgs) => MetaDescriptors export type HeadersArgs = T.HeadersArgs export type HeadersFunction = (args: HeadersArgs) => Headers | HeadersInit export type unstable_MiddlewareFunction = T.CreateServerMiddlewareFunction export type unstable_ClientMiddlewareFunction = T.CreateClientMiddlewareFunction export type LoaderArgs = T.CreateServerLoaderArgs export type ClientLoaderArgs = T.CreateClientLoaderArgs export type ActionArgs = T.CreateServerActionArgs export type ClientActionArgs = T.CreateClientActionArgs export type HydrateFallbackProps = T.CreateHydrateFallbackProps export type ComponentProps = T.CreateComponentProps export type ErrorBoundaryProps = T.CreateErrorBoundaryProps } ================================================ FILE: .react-router/types/app/routes/+types/_index.ts ================================================ // React Router generated types for route: // routes/_index.tsx import type * as T from "react-router/route-module" import type { Info as Parent0 } from "../../+types/root.js" type Module = typeof import("../_index.js") export type Info = { parents: [Parent0], id: "routes/_index" file: "routes/_index.tsx" path: "undefined" params: {} & { [key: string]: string | undefined } module: Module loaderData: T.CreateLoaderData actionData: T.CreateActionData } export namespace Route { export type LinkDescriptors = T.LinkDescriptors export type LinksFunction = () => LinkDescriptors export type MetaArgs = T.CreateMetaArgs export type MetaDescriptors = T.MetaDescriptors export type MetaFunction = (args: MetaArgs) => MetaDescriptors export type HeadersArgs = T.HeadersArgs export type HeadersFunction = (args: HeadersArgs) => Headers | HeadersInit export type unstable_MiddlewareFunction = T.CreateServerMiddlewareFunction export type unstable_ClientMiddlewareFunction = T.CreateClientMiddlewareFunction export type LoaderArgs = T.CreateServerLoaderArgs export type ClientLoaderArgs = T.CreateClientLoaderArgs export type ActionArgs = T.CreateServerActionArgs export type ClientActionArgs = T.CreateClientActionArgs export type HydrateFallbackProps = T.CreateHydrateFallbackProps export type ComponentProps = T.CreateComponentProps export type ErrorBoundaryProps = T.CreateErrorBoundaryProps } ================================================ FILE: .react-router/types/app/routes/+types/api.chat.ts ================================================ // React Router generated types for route: // routes/api.chat.ts import type * as T from "react-router/route-module" import type { Info as Parent0 } from "../../+types/root.js" type Module = typeof import("../api.chat.js") export type Info = { parents: [Parent0], id: "routes/api.chat" file: "routes/api.chat.ts" path: "api/chat" params: {} & { [key: string]: string | undefined } module: Module loaderData: T.CreateLoaderData actionData: T.CreateActionData } export namespace Route { export type LinkDescriptors = T.LinkDescriptors export type LinksFunction = () => LinkDescriptors export type MetaArgs = T.CreateMetaArgs export type MetaDescriptors = T.MetaDescriptors export type MetaFunction = (args: MetaArgs) => MetaDescriptors export type HeadersArgs = T.HeadersArgs export type HeadersFunction = (args: HeadersArgs) => Headers | HeadersInit export type unstable_MiddlewareFunction = T.CreateServerMiddlewareFunction export type unstable_ClientMiddlewareFunction = T.CreateClientMiddlewareFunction export type LoaderArgs = T.CreateServerLoaderArgs export type ClientLoaderArgs = T.CreateClientLoaderArgs export type ActionArgs = T.CreateServerActionArgs export type ClientActionArgs = T.CreateClientActionArgs export type HydrateFallbackProps = T.CreateHydrateFallbackProps export type ComponentProps = T.CreateComponentProps export type ErrorBoundaryProps = T.CreateErrorBoundaryProps } ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2025 GitMCP Authors (Ido Salomon and Liad Yosef) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: README.md ================================================ # GitMCP

image

What is GitMCPFeaturesGetting StartedHow It WorksBadgeExamplesFAQPrivacyContributingLicense

[![GitMCP](https://img.shields.io/endpoint?url=https://gitmcp.io/badge/idosal/git-mcp)](https://gitmcp.io/idosal/git-mcp) [![Twitter Follow](https://img.shields.io/twitter/follow/idosal1?style=social)](https://twitter.com/idosal1) [![Twitter Follow](https://img.shields.io/twitter/follow/liadyosef?style=social)](https://twitter.com/liadyosef)
Pulse MCP Badge
## 🤔 What is GitMCP? **Stop vibe-hallucinating and start vibe-coding!** [GitMCP](https://gitmcp.io) is a free, open-source, remote [Model Context Protocol (MCP)](https://docs.anthropic.com/en/docs/agents-and-tools/mcp) server that transforms **any** GitHub project (repositories or GitHub pages) into a documentation hub. It enables AI tools like Cursor to access up-to-date documentation and code, even if the LLM has never encountered them, thereby eliminating code hallucinations seamlessly. GitMCP supports **two flavors** - * **Specific Repository (`gitmcp.io/{owner}/{repo}` or `{owner}.gitmcp.io/{repo}`):** Use these when you primarily work with a select number of libraries. This ensures your AI assistant always targets the correct project, enhancing security and relevance by preventing access to unintended repositories. * **Generic Server (`gitmcp.io/docs`):** Use this for maximum flexibility when you need to switch between different repositories frequently. The AI assistant will prompt you (or decide based on context) which repository to access for each request. Be mindful that this relies on correctly identifying the target repository each time. **With GitMCP:** * AI assistants access the *latest* documentation and code directly from the source. * Get accurate API usage and reliable code examples. * Work effectively even with niche, new, or rapidly changing libraries. * Significantly reduced hallucinations and improved code correctness. For example, this side-by-side comparison shows the result for the same one-shot prompt in Cursor when creating a [three.js](https://github.com/mrdoob/three.js) scene - https://github.com/user-attachments/assets/fbf1b4a7-f9f0-4c0e-831c-4d64faae2c45 ## ✨ Features - 😎 **Latest Documentation on ANY GitHub Project**: Grant your AI assistant seamless access to the GitHub project's documentation and code. The built-in smart search capabilities help find exactly what the AI needs without using too many tokens! - 🧠 **No More Hallucinations**: With GitMCP, your AI assistant can provide accurate and relevant answers to your questions. - ☁️ **Zero Setup**: GitMCP runs in the cloud. Simply add the chosen GitMCP URL as an MCP server in your IDE — no downloads, installations, signups, or changes are required. - 💬 **Embedded Chat**: Start quickly by chatting directly with the repository's documentation through our in-browser chat! - ✅ **Open, Free, and Private**: GitMCP is open-source and completely free to use. It doesn't collect personal information or store queries. You can even self-host it! ## 🚀 Getting Started Using GitMCP is easy! Simply follow these steps: ### Step 1: Choose the type of server you want Choose one of these URL formats depending on what you want to connect to: - For GitHub repositories: `gitmcp.io/{owner}/{repo}` - For GitHub Pages sites: `{owner}.gitmcp.io/{repo}` - For a generic tool that supports any repository (dynamic): `gitmcp.io/docs` Replace `{owner}` with the GitHub username or organization name, and `{repo}` with the repository name. For your convenience, you can also use the conversion tool on the landing page to format the GitHub URL into an MCP URL! ### Step 2: Connect your AI assistant Select your AI assistant from the options below and follow the configuration instructions: #### Connecting Cursor Update your Cursor configuration file at `~/.cursor/mcp.json`: ```json { "mcpServers": { "gitmcp": { "url": "https://gitmcp.io/{owner}/{repo}" } } } ``` #### Connecting Claude Desktop 1. In Claude Desktop, go to Settings > Developer > Edit Config 2. Replace the configuration with: ```json { "mcpServers": { "gitmcp": { "command": "npx", "args": [ "mcp-remote", "https://gitmcp.io/{owner}/{repo}" ] } } } ``` #### Connecting Windsurf Update your Windsurf configuration file at `~/.codeium/windsurf/mcp_config.json`: ```json { "mcpServers": { "gitmcp": { "serverUrl": "https://gitmcp.io/{owner}/{repo}" } } } ``` #### Connecting VSCode Update your VSCode configuration file at `.vscode/mcp.json`: ```json { "servers": { "gitmcp": { "type": "sse", "url": "https://gitmcp.io/{owner}/{repo}" } } } ``` #### Connecting Cline Update your Cline configuration file at `~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`: ```json { "mcpServers": { "gitmcp": { "url": "https://gitmcp.io/{owner}/{repo}", "disabled": false, "autoApprove": [] } } } ``` #### Connecting Highlight AI 1. Open Highlight AI and click the plugins icon (@ symbol) in the sidebar 2. Click **Installed Plugins** at the top of the sidebar 3. Select **Custom Plugin** 4. Click **Add a plugin using a custom SSE URL** Plugin name: `gitmcp` SSE URL: `https://gitmcp.io/{owner}/{repo}` For more details on adding custom MCP servers to HighlightAI, refer to [the documentation](https://docs.highlightai.com/plugins/custom). #### Connecting Augment Code 1. Open Augment Code settings 2. Navigate to the MCP section 3. Add a new MCP server with the following details: Name the MCP server: `git-mcp Docs` Use this command: ```bash npx mcp-remote https://gitmcp.io/{owner}/{repo} ``` Or use the following configuration: ```json { "mcpServers": { "git-mcp Docs": { "command": "npx", "args": [ "mcp-remote", "https://gitmcp.io/{owner}/{repo}" ] } } } ``` #### Connecting Msty AI 1. Open Msty Studio 2. Go to Tools > Import Tools from JSON Clipboard 3. Paste the following configuration: ```json { "mcpServers": { "git-mcp Docs": { "command": "npx", "args": [ "mcp-remote", "https://gitmcp.io/{owner}/{repo}" ] } } } ``` For more details on configuring MCP servers in Augment Code, visit [the Augment Code documentation](https://docs.augmentcode.com/setup-augment/mcp). > **Note:** Remember to replace `{owner}` and `{repo}` with the actual GitHub username/organization and repository name. You can also use the dynamic endpoint `https://gitmcp.io/docs` to allow your AI to access any repository on demand. ## ⚙ How It Works GitMCP connects your AI assistant to GitHub repositories using the Model Context Protocol (MCP), a standard that lets AI tools request additional information from external sources. What happens when you use GitMCP: 1. **You provide the GitMCP URL** to your AI assistant (e.g., `gitmcp.io/microsoft/typescript`). GitMCP exposes tools like documentation fetching, smart search, code search, etc. 2. **Prompt the AI assistant** on documentation/code-related questions. 3. **Your AI sends requests** to GitMCP to use its tools (with your approval). 4. **GitMCP executes the AI's request** and returns the requested data. 5. **Your AI receives the information** and generates a more accurate, grounded response without hallucinations. ### Supported Documentation GitMCP currently supports the following documents (in order of priority): 1. [llms.txt](https://llmstxt.org) 2. AI-optimized version of the project's documentation 3. `README.md`/root ## 💡 Examples Here are some examples of how to use GitMCP with different AI assistants and repositories: ### Example 1: Using Windsurf with a specific repository For the GitHub repository `https://github.com/microsoft/playwright-mcp`, add `https://gitmcp.io/microsoft/playwright-mcp` as an MCP server to Windsurf. **Prompt to Claude:** > "How do I use the Playwright MCP" Windsurf will pull the relevant documentation from GitMCP to implement the memory feature correctly. ### Example 2: Using Cursor with a GitHub Pages site For the GitHub Pages site `langchain-ai.github.io/langgraph`, add `https://langchain-ai.gitmcp.io/langgraph` as an MCP server to Cursor. **Prompt to Cursor:** > "Add memory to my LangGraph agent" Cursor will pull the relevant documentation and code from GitMCP to correctly implement the memory feature. ### Example 3: Using Claude Desktop with the dynamic endpoint You don't have to pick specific repositories. The generic `gitmcp.io/docs` endpoint allows AI to pick the GitHub project on the fly! **Prompt to any AI assistant:** > "I want to learn about the OpenAI Whisper speech recognition model. Explain how it works." Claude will pull the data from GitMCP and answer the question. ## 🛠️ Tools GitMCP provides AI assistants with several valuable tools to help them access, understand, and query GitHub repositories. ### `fetch__documentation` This tool gets the primary documentation from a GitHub repository. It works by retrieving relevant documentation (e.g., `llms.txt`). This gives the AI a good overview of what the project is about **When it's useful:** For general questions about a project's purpose, features, or how to get started ### `search__documentation` This tool lets the AI search through a repository's documentation by providing a specific search query. Instead of loading all the documentation (which could be very large), it uses intelligent search to find just the relevant parts. **When it's useful:** For specific questions about particular features, functions, or concepts within a project ### `fetch_url_content` This tool helps the AI get information from links mentioned in the documentation. It retrieves the content from those links and converts it to a format the AI can easily read. **When it's useful:** When documentation references external information that would help answer your question ### `search__code` This tool searches through the actual code in the repository using GitHub's code search. It helps AI find specific code examples or implementation details. **When it's useful:** When you want examples of how something is implemented or need technical details not covered in documentation > **Note:** When using the dynamic endpoint (`gitmcp.io/docs`), these tools are named slightly differently (`fetch_generic_documentation`, `search_generic_code`, and `search_generic_documentation`) and need additional information about which repository to access. ## 📊 Badge GitMCP has a badge for your repository's README. It allows users to quickly access your documentation through their IDE or browser (using the embedded chat). It also showcases how many times your documentation has been accessed through GitMCP. Example (`idosal/git-mcp`): [![GitMCP](https://img.shields.io/endpoint?url=https://gitmcp.io/badge/idosal/git-mcp)](https://gitmcp.io/idosal/git-mcp) ### Adding the Badge to Your Repository Add the following to your `README.md`: ```markdown [![GitMCP](https://img.shields.io/endpoint?url=https://gitmcp.io/badge/OWNER/REPO)](https://gitmcp.io/OWNER/REPO) ``` Replace `OWNER` with your GitHub username or organization, and `REPO` with your repository name. ### How We Count Views Increment for each tool call on the specific repository. ### Customizing the Badge You can customize the badge's appearance with parameters: | Parameter | Description | Default | Example | |-----------|-------------|---------|---------| | `color` | Color for the badge value | `aquamarine` | `?color=green` | | `label` | Badge label | `GitMCP` | `Documentation` Please reach out if you need help! ## ❓ FAQ ### What is the Model Context Protocol? The [Model Context Protocol](https://modelcontextprotocol.io/introduction) is a standard that allows AI assistants to request and receive additional context from external sources in a structured manner, enhancing their understanding and performance. ### Does GitMCP work with any AI assistant? Yes, GitMCP is compatible with any AI assistant supporting the Model Context Protocol, including tools like Cursor, VSCode, Claude, etc. ### Is GitMCP compatible with all GitHub projects? Absolutely! GitMCP works with any public GitHub repository without requiring any modifications. It prioritizes the `llms.txt` file and falls back to `README.md` or other pages if the former is unavailable. Future updates aim to support additional documentation methods and even generate content dynamically. ### Does GitMCP cost money? No, GitMCP is a free service to the community with no associated costs. ## 🔒 Privacy GitMCP is deeply committed to its users' privacy. The service doesn't have access to or store any personally identifiable information as it doesn't require authentication. In addition, it doesn't store any queries sent by the agents. Moreover, as GitMCP is an open-source project, it can be deployed independently in your environment. GitMCP only accesses content that is already publicly available and only when queried by a user. GitMCP does not automatically scrape repositories. Before accessing any GitHub Pages site, the code checks for `robots.txt` rules and follows the directives set by site owners, allowing them to opt out. Please note that GitMCP doesn't permanently store data regarding the GitHub projects or their content. ## 👥 Contributing We welcome contributions, feedback, and ideas! Please review our [contribution](https://github.com/idosal/git-mcp/blob/main/.github/CONTRIBUTING.md) guidelines. ### Local Development Setup 1. **Clone the repository** ```bash git clone https://github.com/idosal/git-mcp.git cd git-mcp ``` 2. **Install dependencies** ```bash pnpm install ``` 3. **Run locally for development** ```bash npm run dev # or pnpm dev ``` #### Using MCP Inspector for Testing 1. Install the MCP Inspector tool: ```bash npx @modelcontextprotocol/inspector ``` 2. In the inspector interface: - Set Transport Type to `SSE` - Enter your GitMCP URL (e.g., `http://localhost:5173/docs`) - Click "Connect" ## 📄 License This project is licensed under the [Apache License 2.0](LICENSE). ## Disclaimer GitMCP is provided "as is" without warranty of any kind. While we strive to ensure the reliability and security of our service, we are not responsible for any damages or issues that may arise from its use. GitHub projects accessed through GitMCP are subject to their respective owners' terms and conditions. GitMCP is not affiliated with GitHub or any of the mentioned AI tools. ## Star History [![Star History Chart](https://api.star-history.com/svg?repos=idosal/git-mcp&type=Timeline)](https://www.star-history.com/#idosal/git-mcp&Timeline) ================================================ FILE: SECURITY.md ================================================ # Security Policy GitMCP is committed to maintaining the security and privacy of its users. We welcome vulnerability disclosures, suggestions, and feedback that can improve it. ## Reporting a Vulnerability Please report vulnerabilities using this private [form](https://forms.gle/mMoVv69XFyPqDUTX8). All vulnerabilities will be patched as soon as possible. ================================================ FILE: app/app.css ================================================ @import "tailwindcss"; @theme { --font-sans: "Inter", ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; } html, body { @apply bg-white dark:bg-gray-950; @media (prefers-color-scheme: dark) { color-scheme: dark; } } ================================================ FILE: app/chat/ai/providers.server.ts ================================================ import { createOpenAI } from "@ai-sdk/openai"; import { createGroq } from "@ai-sdk/groq"; import { createAnthropic } from "@ai-sdk/anthropic"; import { createXai } from "@ai-sdk/xai"; import { customProvider, wrapLanguageModel, extractReasoningMiddleware, type LanguageModel, } from "ai"; import type { modelID, StorageKey } from "./providers.shared"; const middleware = extractReasoningMiddleware({ tagName: "think", }); export const getModel = ( env: CloudflareEnvironment, apiKeys: Partial>, ) => { // Helper to get API keys from environment variables first, then localStorage const getApiKey = (key: StorageKey): string | undefined => { // Check for environment variables first if (env[key]) { return env[key] || undefined; } // Check for API keys in localStorage if (apiKeys[key]) { return apiKeys[key] || undefined; } return undefined; }; // Create provider instances with API keys from env/user/localStorage const openaiClient = createOpenAI({ apiKey: getApiKey("OPENAI_API_KEY"), }); const anthropicClient = createAnthropic({ apiKey: getApiKey("ANTHROPIC_API_KEY"), }); const groqClient = createGroq({ apiKey: getApiKey("GROQ_API_KEY"), }); const xaiClient = createXai({ apiKey: getApiKey("XAI_API_KEY"), }); const languageModels: Record = { "gpt-4.1-mini": openaiClient("gpt-4.1-mini"), "claude-3-7-sonnet": anthropicClient("claude-3-7-sonnet-20250219"), "qwen-qwq": wrapLanguageModel({ model: groqClient("qwen-qwq-32b"), middleware, }), "grok-3-mini": xaiClient("grok-3-mini-latest"), }; const model = customProvider({ languageModels, }); return model; }; ================================================ FILE: app/chat/ai/providers.shared.ts ================================================ export interface ModelInfo { provider: string; name: string; description: string; apiVersion: string; capabilities: string[]; } export type StorageKey = | "OPENAI_API_KEY" | "ANTHROPIC_API_KEY" | "GROQ_API_KEY" | "XAI_API_KEY"; export type modelID = | "gpt-4.1-mini" | "claude-3-7-sonnet" | "qwen-qwq" | "grok-3-mini"; export const modelDetails: Record = { "gpt-4.1-mini": { provider: "OpenAI", name: "GPT-4.1 Mini", description: "Compact version of OpenAI's GPT-4.1 with good balance of capabilities, including vision.", apiVersion: "gpt-4.1-mini", capabilities: ["Balance", "Creative", "Vision"], }, "claude-3-7-sonnet": { provider: "Anthropic", name: "Claude 3.7 Sonnet", description: "Latest version of Anthropic's Claude 3.7 Sonnet with strong reasoning and coding capabilities.", apiVersion: "claude-3-7-sonnet-20250219", capabilities: ["Reasoning", "Efficient", "Agentic"], }, "qwen-qwq": { provider: "Groq", name: "Qwen QWQ", description: "Latest version of Alibaba's Qwen QWQ with strong reasoning and coding capabilities.", apiVersion: "qwen-qwq", capabilities: ["Reasoning", "Efficient", "Agentic"], }, "grok-3-mini": { provider: "XAI", name: "Grok 3 Mini", description: "Latest version of XAI's Grok 3 Mini with strong reasoning and coding capabilities.", apiVersion: "grok-3-mini-latest", capabilities: ["Reasoning", "Efficient", "Agentic"], }, }; export const MODELS = Object.keys(modelDetails); export const defaultModel: modelID = "qwen-qwq"; ================================================ FILE: app/chat/components/api-key-manager.tsx ================================================ import { useState, useCallback } from "react"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "~/chat/components/ui/dialog"; import { Button } from "~/chat/components/ui/button"; import { Input } from "~/chat/components/ui/input"; import { Label } from "~/chat/components/ui/label"; import { toast } from "sonner"; import { STORAGE_KEYS } from "~/chat/lib/constants"; import type { StorageKey } from "../ai/providers.shared"; import { useApiKeys } from "./api-keys-provider"; // API key configuration export interface ApiKeyConfig { name: string; key: string; storageKey: StorageKey; label: string; placeholder: string; } // Available API keys configuration export const API_KEYS_CONFIG: readonly ApiKeyConfig[] = [ { name: "OpenAI", key: "openai", storageKey: "OPENAI_API_KEY", label: "OpenAI API Key", placeholder: "sk-...", }, { name: "Anthropic", key: "anthropic", storageKey: "ANTHROPIC_API_KEY", label: "Anthropic API Key", placeholder: "sk-ant-...", }, { name: "Groq", key: "groq", storageKey: "GROQ_API_KEY", label: "Groq API Key", placeholder: "gsk_...", }, { name: "XAI", key: "xai", storageKey: "XAI_API_KEY", label: "XAI API Key", placeholder: "xai-...", }, ] as const; interface ApiKeyManagerProps { open: boolean; onOpenChange: (open: boolean) => void; } export function ApiKeyManager({ open, onOpenChange }: ApiKeyManagerProps) { // State to store API keys const { apiKeys, setApiKeys } = useApiKeys(); const [localApiKeys, setLocalApiKeys] = useState>>(apiKeys); // Update API key in state const handleApiKeyChange = useCallback( (storageKey: StorageKey, value: string) => { setLocalApiKeys((prev) => ({ ...prev, [storageKey]: value, })); }, [], ); // Save API keys to localStorage const handleSaveApiKeys = useCallback(() => { try { setApiKeys(localApiKeys); localStorage.setItem(STORAGE_KEYS.API_KEYS, JSON.stringify(localApiKeys)); toast.success("API keys saved successfully"); onOpenChange(false); } catch (error) { console.error("Error saving API keys:", error); toast.error("Failed to save API keys"); } }, [localApiKeys, setApiKeys, onOpenChange]); // Clear all API keys const handleClearApiKeys = useCallback(() => { try { setLocalApiKeys({}); setApiKeys({}); toast.success("All API keys cleared"); } catch (error) { console.error("Error clearing API keys:", error); toast.error("Failed to clear API keys"); } }, [setApiKeys, localApiKeys]); return ( API Key Settings Enter your own API keys for different AI providers. Keys are stored securely in your browser's local storage.
{API_KEYS_CONFIG.map((config) => (
handleApiKeyChange(config.storageKey, e.target.value) } placeholder={config.placeholder} />
))}
); } ================================================ FILE: app/chat/components/api-keys-provider.tsx ================================================ import { createContext, useContext } from "react"; import type { StorageKey } from "../ai/providers.shared"; import { STORAGE_KEYS } from "../lib/constants"; import { useLocalStorage } from "../lib/hooks/use-local-storage"; const ApiKeysContext = createContext<{ apiKeys: Partial>; setApiKeys: (apiKeys: Partial>) => void; }>({ apiKeys: {}, setApiKeys: () => {}, }); export function ApiKeysProvider({ children }: { children: React.ReactNode }) { const [apiKeys, setApiKeys] = useLocalStorage< Partial> >(STORAGE_KEYS.API_KEYS, {}); return ( {children} ); } export function useApiKeys() { return useContext(ApiKeysContext); } ================================================ FILE: app/chat/components/chat-sidebar.tsx ================================================ "use client"; import { useState } from "react"; import { ServerIcon, Settings, Sparkles, ChevronsUpDown, Github, Key, } from "lucide-react"; import { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupContent, SidebarHeader, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarMenuBadge, useSidebar, } from "~/chat/components/ui/sidebar"; import { Badge } from "~/chat/components/ui/badge"; import { MCPServerManager } from "./mcp-server-manager"; import { ApiKeyManager } from "./api-key-manager"; import { ThemeToggle } from "./theme-toggle"; import { cn } from "~/chat/lib/utils"; import { useMCP } from "~/chat/lib/context/mcp-context"; export function ChatSidebar() { const [mcpSettingsOpen, setMcpSettingsOpen] = useState(false); const [apiKeySettingsOpen, setApiKeySettingsOpen] = useState(false); const { state } = useSidebar(); const isCollapsed = state === "collapsed"; // Get MCP server data from context const { mcpServers, setMcpServers, selectedMcpServers, setSelectedMcpServers, } = useMCP(); // Get active MCP servers status const activeServersCount = selectedMcpServers.length; return (
GitMCP Logo
{!isCollapsed && (
GitMCP Chat
)}
setMcpSettingsOpen(true)} className={cn( "w-full flex items-center gap-2 transition-all", "hover:bg-secondary/50 active:bg-secondary/70 cursor-pointer", )} tooltip={isCollapsed ? "GitMCP Servers" : undefined} > 0 ? "text-primary" : "text-muted-foreground", )} /> {!isCollapsed && ( GitMCP Servers )} {activeServersCount > 0 && !isCollapsed ? ( {activeServersCount} ) : activeServersCount > 0 && isCollapsed ? ( {activeServersCount} ) : null}
setApiKeySettingsOpen(true)} className="w-full flex items-center gap-2 transition-all hover:bg-secondary/50 active:bg-secondary/70 cursor-pointer" > API Keys window.open("https://git.new/gitmcp", "_blank") } className="w-full flex items-center gap-2 transition-all hover:bg-secondary/50 active:bg-secondary/70 cursor-pointer" > GitHub
Theme
{!isCollapsed && (

Built with{" "} Scira Chat

)}
); } ================================================ FILE: app/chat/components/chat.tsx ================================================ "use client"; import { defaultModel, type modelID } from "~/chat/ai/providers.shared"; import { useChat } from "@ai-sdk/react"; import { Textarea } from "./textarea"; import { ProjectOverview } from "./project-overview"; import { Messages } from "./messages"; import { toast } from "sonner"; import { useLocalStorage } from "~/chat/lib/hooks/use-local-storage"; import { useMCP } from "~/chat/lib/context/mcp-context"; import { useCallback } from "react"; import { useApiKeys } from "./api-keys-provider"; const CHAT_API_URL = "https://chat-api-worker.idosalomon.workers.dev/api/chat"; export default function Chat() { const [selectedModel, setSelectedModel] = useLocalStorage( "selectedModel", defaultModel, ); const { apiKeys } = useApiKeys(); // Get MCP server data from context const { mcpServersForApi } = useMCP(); const { messages, input, handleInputChange, handleSubmit, status, stop } = useChat({ api: CHAT_API_URL, maxSteps: 20, body: { selectedModel, mcpServers: mcpServersForApi, apiKeys, }, experimental_throttle: 500, onError: (error) => { toast.error( error.message.length > 0 ? error.message : "An error occurred, please try again later.", { position: "top-center", richColors: true }, ); }, }); // Custom submit handler const handleFormSubmit = useCallback( (e: React.FormEvent) => { e.preventDefault(); handleSubmit(e); }, [input, handleSubmit], ); const isLoading = status === "streaming" || status === "submitted"; return (
{messages.length === 0 ? (