> ## Documentation Index
> Fetch the complete documentation index at: https://langchain-5e9cc07a-preview-fjmorr-1785940779-68b13f9.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Connect GitHub repositories to Managed Deep Agents

> Load GitHub tools from the LangSmith tool server, clone repositories, install the GitHub CLI, and inject credentials into a Managed Deep Agents sandbox.

The GitHub connector gives the agent three independent ways to work with GitHub:

* **Tools**: GitHub is a [tool server integration](/langsmith/managed-deep-agents-connectors/integrations) like Gmail or Linear, so the connector can load LangChain-authored GitHub API tools through LangSmith's gateway. The provider token stays in LangSmith's vault.
* **Sandbox**: the connector also prepares repositories, the `gh` CLI, and credentials inside a [managed sandbox](/langsmith/managed-deep-agents-deploy#configure-a-sandbox), so the agent can inspect or change checkouts directly.
* **Events**: pass `events` to receive GitHub App webhooks. Any webhook event can invoke the agent, which can auto-reply as an issue or pull request comment. See [Receive GitHub App webhooks](#receive-github-app-webhooks).

The GitHub connector requires `managed-deepagents>=0.4.0`.

<Note>
  Managed Deep Agents is in **private [beta](/langsmith/release-stages)**, available on [LangSmith Cloud](/langsmith/cloud) in the US region only. [Join the waitlist](https://www.langchain.com/langsmith-managed-deep-agents-waitlist) to request access.
</Note>

For user OAuth rather than an App installation, see Connect-with-GitHub under [identity](/langsmith/managed-deep-agents-identity).

## Add the connector

Create `connectors/github.py` or `connectors/github.ts` and export a named `connector`:

<CodeGroup>
  ```python connectors/github.py theme={null}
  from managed_deepagents import connectors

  connector = connectors.github(
      repositories=[
          {
              "repo": "acme/api",
              "path": "workspace/api",
              "ref": "main",
              "depth": 1,
              "on_reuse": "fetch",
          }
      ],
  )
  ```

  ```ts connectors/github.ts theme={null}
  import { connectors } from "managed-deepagents";

  export const connector = connectors.github({
    repositories: [
      {
        repo: "acme/api",
        path: "workspace/api",
        ref: "main",
        depth: 1,
        onReuse: "fetch",
      },
    ],
  });
  ```
</CodeGroup>

The connector clones each repository when the sandbox is created. When a thread reuses an existing sandbox, `on_reuse` / `onReuse` controls the checkout (see [Configure options](#configure-options)).

## Tools and the `installCLI` rule

The two halves meet in one rule: `installCLI` decides the default tool surface. With `gh` in the sandbox (the default), the agent already reaches the GitHub API, so the integration's tool definitions stay off. Adding them would be a second route to the same endpoints. Naming tools with `include_tools` / `includeTools`, or setting `installCLI: false`, turns them on. An explicit selection always wins, including `exclude_tools` / `excludeTools` on its own.

A checkout-only project needs no tool config and no connected GitHub integration in the workspace, because the gateway is never called:

<CodeGroup>
  ```python connectors/github.py theme={null}
  from managed_deepagents import connectors

  # Tools on: no gh in the sandbox, so the agent uses the integration's tools
  connector = connectors.github(
      install_cli=False,
      include_tools=["github_create_pull_request"],
  )
  ```

  ```ts connectors/github.ts theme={null}
  import { connectors } from "managed-deepagents";

  // Tools on: no gh in the sandbox, so the agent uses the integration's tools
  export const connector = connectors.github({
    installCLI: false,
    includeTools: ["github_create_pull_request"],
  });
  ```
</CodeGroup>

Tool names are provider-qualified (`github_create_pull_request`), not prefixed. For how the gateway resolves credentials, see [Tool server integrations](/langsmith/managed-deep-agents-connectors/integrations).

## Configure options

| Option (Python / TypeScript)               | Default             | Purpose                                                                                  |
| ------------------------------------------ | ------------------- | ---------------------------------------------------------------------------------------- |
| `repositories`                             | `[]`                | Repository checkouts and their sandbox paths.                                            |
| `install_cli` / `installCLI`               | `true`              | Install the GitHub CLI in the sandbox. `true` also defaults the integration's tools off. |
| `inject_credentials` / `injectCredentials` | `true`              | Expose resolved GitHub credentials to `git` and `gh`.                                    |
| `include_tools` / `includeTools`           | all tools (when on) | Allowlist of integration tool names to load.                                             |
| `exclude_tools` / `excludeTools`           | *(none)*            | Denylist of integration tool names.                                                      |

Each entry in `repositories` accepts these fields:

| Field (Python / TypeScript)    | Default | Purpose                                                                          |
| ------------------------------ | ------- | -------------------------------------------------------------------------------- |
| `repo`                         | —       | Static repository to checkout, as `owner/repo`.                                  |
| `path`                         | —       | Relative sandbox path where the repository appears. Must be relative and unique. |
| `ref`                          | —       | Git ref (branch, tag, or SHA) to checkout.                                       |
| `depth`                        | —       | Shallow clone depth. Must be an integer of `1` or greater.                       |
| `sparse_paths` / `sparsePaths` | —       | Sparse checkout paths, relative to the repository root.                          |
| `submodules`                   | `false` | Initialize submodules.                                                           |
| `write`                        | —       | Use write credentials instead of read credentials for this checkout.             |
| `on_reuse` / `onReuse`         | `fetch` | Reuse behavior for an existing checkout: `keep`, `reset`, or `fetch`.            |

Set `write` to `true` only on checkouts the agent must push to, since it grants write credentials for the repository. Leave it unset for read-only work.

For private repositories, configure GitHub credentials through [identity](/langsmith/managed-deep-agents-identity#downstream-credentials). The runtime resolves the credential, injects it into the sandbox as `GH_TOKEN`, and configures Git credentials for the run. The token is never stored in thread state.

## Receive GitHub App webhooks

Pass `events` to let a GitHub App send webhooks to the agent. The connector name becomes the ingress path (`github` → `POST /connectors/github/events`). The runtime verifies signatures, runs the agent, and can auto-reply as a pull request or issue comment. Webhook ingress requires a root [identity](/langsmith/managed-deep-agents-identity) declaration.

Each entry in `events` is an ordered handler: the first match for a delivery wins. Each needs `on` and a `prompt` callback that builds the human message for that turn. The agent system prompt remains `instructions.md`.

<CodeGroup>
  ```python connectors/github.py theme={null}
  from managed_deepagents import connectors

  connector = connectors.github(
      events=[
          {
              "on": "pull_request.opened",
              "repositories": ["acme/api"],  # optional; omit = any repo
              "auto_reply": True,  # default; comment when address is owner/repo#N
              "prompt": lambda event: (
                  f"Review {event['repository']}#"
                  f"{event.get('issue_or_pull_number')}: "
                  f"{event['payload']['pull_request']['title']}"
              ),
          },
      ],
  )
  ```

  ```ts connectors/github.ts theme={null}
  import type { PullRequestOpenedEvent } from "@octokit/webhooks-types";
  import { connectors } from "managed-deepagents";

  export const connector = connectors.github({
    events: [
      {
        on: "pull_request.opened",
        repositories: ["acme/api"], // optional; omit = any repo
        autoReply: true, // default; comment when address is owner/repo#N
        prompt(event) {
          // MDA keeps payload untyped — narrow with Octokit in the agent project
          const pr = event.payload as PullRequestOpenedEvent;
          return `Review ${event.repository}#${pr.pull_request.number}: ${pr.pull_request.title}`;
        },
      },
    ],
  });
  ```
</CodeGroup>

Pair webhook events with an identity scope that does not require a human caller. The event user is the installation or service principal `github-app:<installationId>`, not the pull request author. Replies use the App installation token; Connect-with-GitHub OAuth is not required for this path.

### Event filters (`on`)

Any GitHub webhook event is accepted. Filter with `on`:

| `on` value              | Matches                              |
| ----------------------- | ------------------------------------ |
| `"pull_request"`        | Any action for that `X-GitHub-Event` |
| `"pull_request.opened"` | Event + `payload.action`             |
| `"*"`                   | Every delivery                       |

Managed Deep Agents does not ship copies of GitHub webhook payload schemas. The envelope passes common routing fields (`eventName` / `event_name`, `action`, `repository`, `issueOrPullNumber` / `issue_or_pull_number`, …) and leaves the verified JSON on `payload` as untyped. In TypeScript, narrow with [`@octokit/webhooks-types`](https://www.npmjs.com/package/@octokit/webhooks-types). In Python, narrow with your own TypedDicts or runtime checks.

### `prompt` vs `instructions.md`

| Source                  | Role                                            |
| ----------------------- | ----------------------------------------------- |
| `instructions.md`       | Agent system prompt (shared across turns)       |
| Handler `prompt(event)` | Human message for that webhook turn (task text) |

### How GitHub webhooks work

```mermaid theme={null}
flowchart LR
    A["GitHub webhook"] --> B["POST /connectors/github/events"]
    B --> C["Verify HMAC + ack 202"]
    C --> D["Match handler + prompt"]
    D --> E["Trusted loopback run"]
    E --> F["Optional issue/PR comment"]

    classDef process fill:#E5F4FF,stroke:#006DDD,stroke-width:2px,color:#030710;
    classDef output fill:#EBD0F0,stroke:#885270,stroke-width:2px,color:#441E33;
    class A,B,C,D,E process;
    class F output;
```

1. GitHub POSTs to `https://<agent-server>/connectors/github/events` (the connector name becomes the path segment).
2. The runtime verifies `X-Hub-Signature-256` against `GITHUB_WEBHOOK_SECRET`, dedupes on `X-GitHub-Delivery`, and returns HTTP 202.
3. It picks the first matching handler, calls `prompt` to build the inbound text, then invokes the graph over trusted loopback with user and source-thread identity (`source.provider: "github"`).
4. When the matched handler has `autoReply` enabled and the conversation address is `owner/repo#N`, it posts the agent response as an issue/PR comment with the App installation token. Events without an issue/PR number skip the comment even when `autoReply` is `true`.

LangGraph auth is bypassed only on `POST /connectors/{name}/events` so GitHub can deliver without an ingress secret; the loopback invoke still uses `MDA_INGRESS_SECRET`.

### Event handler options

| Option (Python / TypeScript) | Default      | Meaning                                                               |
| ---------------------------- | ------------ | --------------------------------------------------------------------- |
| `on`                         | *(required)* | Event filter: `event`, `event.action`, or `*`                         |
| `prompt`                     | *(required)* | Builds the human message for the agent turn from the webhook envelope |
| `repositories`               | *(none)*     | Allowlist of `owner/repo` full names; omit = any repo                 |
| `auto_reply` / `autoReply`   | `true`       | Post the agent response as an issue/PR comment when addressable       |

Compile extracts only `{ on, repositories, autoReply }` into the deploy manifest. Live `prompt` callbacks stay on the imported connector module.

### Required webhook secrets

Put these in the project `.env` (or LangSmith workspace secrets) before `mda deploy`. Deploy preflights the connector's `requiredEnv` from the compiled manifest.

| Variable                 | Required                                                    | Role                                                |
| ------------------------ | ----------------------------------------------------------- | --------------------------------------------------- |
| `GITHUB_WEBHOOK_SECRET`  | Yes                                                         | Verifies `X-Hub-Signature-256`                      |
| `GITHUB_APP_ID`          | Yes                                                         | App id for JWT minting                              |
| `GITHUB_APP_PRIVATE_KEY` | Yes                                                         | PEM private key for the App                         |
| `GITHUB_INSTALLATION_ID` | Yes                                                         | Installation the connector acts as (single-install) |
| `MDA_INGRESS_SECRET`     | Yes when identity uses trusted loopback / `trusted_backend` | Trusted invoke from the Events path into the graph  |

### Configure the GitHub App

1. Create a GitHub App (or reuse one you control) with permissions implied by your handlers (at minimum `metadata:read`; `issues:write` and `pull_requests:read` when any handler has `autoReply` enabled). Tighten App permissions in GitHub settings to match what you actually use.
2. Subscribe the App to the webhook events your handlers need (for example `Pull request` for `pull_request.opened`, or broader events if you use `"*"` / event-level filters).
3. Set the webhook URL to `https://<agent-server>/connectors/github/events` and configure the webhook secret as `GITHUB_WEBHOOK_SECRET`.
4. Install the App on the target org or repositories and copy the installation id into `GITHUB_INSTALLATION_ID`.
5. Copy the App id and private key into `GITHUB_APP_ID` and `GITHUB_APP_PRIVATE_KEY`.

## Test and deploy

Test the project locally with [`mda dev`](/langsmith/managed-deep-agents-cli#develop-locally), then deploy it with [`mda deploy`](/langsmith/managed-deep-agents-deploy). Open deployment traces in LangSmith to inspect model calls, tool calls, errors, and latency.

The sandbox half runs only when the project declares a managed sandbox; without one, it does not run. After startup, confirm the checkout by asking the agent to list the files at the configured path, and confirm credentials by asking it to run `gh auth status` in the sandbox. When you use `events`, trigger a matching webhook (for example open a pull request on an allowed repository) and confirm the agent run appears in LangSmith, along with an issue/PR comment when `autoReply` is `true` and the event has an issue/PR number. For deploy symptoms and fixes, see [Troubleshooting](/langsmith/managed-deep-agents-cli#troubleshooting).

### Webhook troubleshooting

| Symptom                                  | Likely cause                                                                                                                             |
| ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| Webhook deliveries fail signature checks | Wrong `GITHUB_WEBHOOK_SECRET`, or body was rewritten before verification                                                                 |
| Events ACK but agent never runs          | Missing `MDA_INGRESS_SECRET`, no handler matched (`on` / `repositories`), or `prompt` returned empty text                                |
| Deploy fails citing GitHub secrets       | `events` declared but App env vars missing from `.env` / workspace secrets                                                               |
| Auto-reply skipped                       | Handler `autoReply` is `false`, event has no issue/PR number, missing App JWT/installation credentials, or App lacks comment permissions |
| Double comments on Host                  | Delivery dedupe is process-local; GitHub retries can double-invoke on multi-replica Host                                                 |

## Next steps

<CardGroup cols={2}>
  <Card title="Connectors" icon="plug" href="/langsmith/managed-deep-agents-connectors">
    Compare connector types.
  </Card>

  <Card title="Tool server integrations" icon="plug" href="/langsmith/managed-deep-agents-connectors/integrations">
    See how LangSmith-hosted integration tools reach the agent.
  </Card>

  <Card title="Identity" icon="fingerprint" href="/langsmith/managed-deep-agents-identity">
    Scope callers and resolve credentials.
  </Card>

  <Card title="Configure a sandbox" icon="box" href="/langsmith/managed-deep-agents-deploy#configure-a-sandbox">
    Configure sandbox scope and lifecycle.
  </Card>
</CardGroup>

***

<div className="source-links">
  <Callout icon="terminal-2">
    [Connect these docs](/use-these-docs) to Claude, VSCode, and more via MCP for real-time answers.
  </Callout>

  <Callout icon="edit">
    [Edit this page on GitHub](https://github.com/langchain-ai/docs/edit/main/src/langsmith/managed-deep-agents-connectors/github.mdx) or [file an issue](https://github.com/langchain-ai/docs/issues/new/choose).
  </Callout>
</div>
