GitHub Client

The GitHub REST api, as the MCPServer’s public tools call it.

github_request() sends one request to api.github.com, with the caller’s own GitHub token, or anonymously. Its callers are smarter.apps.mcpserver.services.github.issues, which searches and reads issues, and opens issues and comments as the caller, and smarter.apps.mcpserver.services.github.pulls, which opens pull requests as the caller. refuse_secrets() stops every one of them from writing a credential to GitHub.

The caller’s token. An MCP client sends the user’s GitHub classic personal access token in the request’s Authorization: Bearer header (see smarter.apps.mcpserver.protocol.views). validate_token() accepts only a classic token, ghp_..., which can write to any public repository with the public_repo scope. A fine-grained token, github_pat_..., can write only to the repositories of the account or organization that it was created for, so it is refused with an explanation. The platform holds no GitHub credential that can write.

The token is sent only to api.github.com, without redirects, and is never logged, cached, stored, or passed as a tool argument, which the LLM would see. explain_failure() turns GitHub’s errors into messages for the LLM, e.g. a token without the public_repo scope.

Note

Experimental. The MCPServer was designed and coded by Claude Code (Anthropic’s Claude Opus 5.5), with Lawrence McDaniel as co-author. It is experimental, and will be documented.

smarter.apps.mcpserver.services.github.client.explain_failure(response, repository, action, not_found=None)[source]

Explain why GitHub refused a request, for the LLM.

GitHub’s status codes are ambiguous, e.g. a 404 may mean that the repository does not exist, or that the token lacks the scope to see it. The explanation is a sentence that says what went wrong and, where it can, what the user can do about it, e.g. add the public_repo scope or wait for the rate limit. It never quotes the token. Nothing is logged here: the callers log the status.

Parameters:
  • response (Response) – GitHub’s response, which was not a success.

  • repository (ProjectRepository) – The repository that the request was for.

  • action (str) – What the request would have done, e.g. open the issue.

  • not_found (str | None) – What a 404 means, if not that the repository was not found, e.g. that an issue number does not exist.

Return type:

str

Returns:

The explanation.

smarter.apps.mcpserver.services.github.client.github_request(method, path, token=None, json=None, params=None)[source]

Send one request to the GitHub REST api.

Every GitHub call of the MCP server’s tools goes through this function, so that each one has the same headers, timeout and error handling, and so that the caller’s token is sent only to api.github.com: redirects are not followed, because a redirect could carry the Authorization header to another host.

Side effects: one HTTP request to api.github.com, which writes to GitHub when method is e.g. POST or PATCH. A failure to reach GitHub is logged as a warning, by the class of the exception only, so that neither the token nor the request is logged.

Parameters:
  • method (str) – The HTTP method, e.g. GET or POST.

  • path (str) – The path, e.g. /repos/smarter-sh/smarter/issues.

  • token (str | None) – The caller’s token, from validate_token() or optional_token(), or None to send the request anonymously.

  • json (TypeAliasType | None) – The request’s JSON body, if any.

  • params (dict[str, str] | None) – The request’s query parameters, if any.

Return type:

Response

Returns:

GitHub’s response, whatever its status. The caller checks the status, and explains a failure with explain_failure().

Raises:

SmarterMCPServerServiceError – If GitHub could not be reached, e.g. a timeout or a connection error.

smarter.apps.mcpserver.services.github.client.optional_token(token)[source]

Return a caller’s GitHub token for a request that also works anonymously, e.g. a search.

A token raises GitHub’s rate limit for its owner, so it is sent when the caller has one. Only a classic personal access token is sent, so that a secret that is not GitHub’s, e.g. a Smarter api key in the Authorization header, never reaches GitHub. Any other value is ignored, and the request is sent anonymously, rather than refused.

Parameters:

token (SecretStr | None) – The token, from the request’s Authorization: Bearer header, or None.

Return type:

str | None

Returns:

The token’s value, or None to send the request anonymously.

smarter.apps.mcpserver.services.github.client.refuse_secrets(token, *texts)[source]

Refuse to write a credential to GitHub, where everything that the tools write is public.

The text of an issue, a comment or a pull request is often written from material that the LLM read, e.g. a log, which may hold a credential. Two kinds of credential are refused: the caller’s own GitHub token, and every credential that the Guardrail’s secret detectors (SECRET_DETECTORS) recognize, i.e. api keys, AWS access keys, JWTs and private keys. The error names the kind of credential, never the credential itself, because the error is shown to the LLM and may be logged.

Nothing is written, and GitHub is not called: this is a check that runs before every write.

Parameters:
  • token (str) – The caller’s GitHub token, from validate_token(), or an empty string.

  • texts (str) – The texts that would be written, e.g. a title, a body and labels.

Raises:

SmarterMCPServerServiceError – If a text holds a credential.

Return type:

None

smarter.apps.mcpserver.services.github.client.validate_token(token)[source]

Return a caller’s GitHub token, if it is a classic personal access token.

The tools that write to GitHub (issues, comments, pull requests) call it first. Only a classic token, ghp_..., is accepted, because only a classic token with the public_repo scope can write to another account’s public repositories. Each refusal explains to the LLM what the user must do, without quoting the token.

Parameters:

token (SecretStr | None) – The token, from the request’s Authorization: Bearer header, or None.

Return type:

str

Returns:

The token’s value, without surrounding whitespace.

Raises:

SmarterMCPServerServiceError – If there is no token, it is a fine-grained personal access token, or it does not look like a classic personal access token.