GitHub Tools
The tools that search, open and comment on the GitHub issues of The Smarter Project.
search_issues: the GitHub issues of the repositories, live. Anyone may call it.read_issue: one issue and its latest comments, live. Anyone may call it.create_issue: opens a GitHub issue in a repository, as the caller, optionally with labels.comment_on_issue: adds a comment to an existing issue, as the caller.close_issue: closes an issue that a merged pull request resolved, with a comment, as the caller.create_pull_request: commits a change to a new branch, and opens a pull request, as the caller.
Each tool class validates its arguments with a Pydantic model, calls GitHub through
smarter.apps.mcpserver.services.github.issues or smarter.apps.mcpserver.services.github.pulls, and
formats the answer as Markdown for the LLM. The GitHub work, the checks for credentials and the
explanations of GitHub’s errors are in those modules: this one only turns arguments into a call,
and a result into text.
search_issues and read_issue are read-only, and their results are cached for
ISSUE_SEARCH_CACHE_TTL seconds. create_issue, comment_on_issue, close_issue
and create_pull_request write to GitHub, as the caller: they are served only when the request
carries the caller’s GitHub classic personal access token, and their results are never cached.
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.
- class smarter.apps.mcpserver.protocol.tools.github.CloseIssueTool(scope, github_token=None)[source]
Bases:
PublicToolThe
close_issuetool: close an issue that a merged pull request resolved, as the caller.Reads: the pull request, from GitHub, to confirm that it was merged.
Writes: a public comment that says why the issue is resolved, then the issue’s state, closed as
completed, with the caller’s own GitHub token (seeclose_issue()). Both notify the issue’s participants.Result: a sentence with the issue, the author and the pull request, the issue’s url, and the comment’s url.
Side effects: three requests to GitHub. The tool is served only to a request that carries the caller’s GitHub token, and its result is never cached: every call writes.
- arguments_model
alias of
CloseIssueArguments
- description: ClassVar[str] = "Close an open GitHub issue of a repository of The Smarter Project, under the user's own GitHub account, when a merged pull request resolved it. It adds the comment, which says why, and closes the issue as completed, only if GitHub confirms that the pull request was merged. Read the issue and the pull request with read_issue first, and close the issue only if the pull request fixes exactly what the issue reports. It is public, and the issue's participants are notified: call it only when the user asks you to."
What the tool does, and when to call it.
The LLM reads it to decide which tool to call.
- open_world: ClassVar[bool] = True
Whether the tool reaches beyond the platform’s own data, e.g. to GitHub.
- read_only: ClassVar[bool] = False
Whether the tool only reads.
A tool that writes is annotated so, with
readOnlyHintfalse, so that an MCP client can ask its user before it calls it.
- requires_github_token: ClassVar[bool] = True
Whether the tool is served only to a request that carries the caller’s GitHub token.
- requires_repository: ClassVar[bool] = True
Whether a call to the public endpoint of every repository must name one.
- run(arguments)[source]
Close the issue that the call names, if its pull request was merged, with the comment.
- Parameters:
arguments (
PublicToolArguments) – The validatedCloseIssueArguments.- Return type:
- Returns:
The closed issue, the author, the pull request, and the urls of the issue and the comment.
- Raises:
SmarterMCPServerToolError – If the repository is unknown, the token is not a classic personal access token, the comment holds a credential, the pull request was not found or not merged, or GitHub could not be reached, or refused the comment or the closing.
- class smarter.apps.mcpserver.protocol.tools.github.CommentOnIssueTool(scope, github_token=None)[source]
Bases:
PublicToolThe
comment_on_issuetool: add a comment to a GitHub issue, as the caller.Writes: a public comment, with the caller’s own GitHub token (see
comment_on_issue()), which notifies the issue’s participants.Result: a sentence with the issue, the author, and the comment’s url.
Side effects: one request to GitHub that creates the comment. The tool is served only to a request that carries the caller’s GitHub token, and its result is never cached: every call writes.
- arguments_model
alias of
CommentOnIssueArguments
- description: ClassVar[str] = "Add a comment to an existing GitHub issue of a repository of The Smarter Project, under the user's own GitHub account, e.g. to confirm a bug that search_issues found, rather than open it again. Read the issue with read_issue first, and comment only to add what it does not already say. It is public, and the issue's participants are notified: call it only when the user asks you to, after showing them the issue and the comment, and never more than once for the same comment."
What the tool does, and when to call it.
The LLM reads it to decide which tool to call.
- name: ClassVar[str] = 'comment_on_issue'
The tool’s name, which the LLM calls it by, e.g.
search_code.
- open_world: ClassVar[bool] = True
Whether the tool reaches beyond the platform’s own data, e.g. to GitHub.
- read_only: ClassVar[bool] = False
Whether the tool only reads.
A tool that writes is annotated so, with
readOnlyHintfalse, so that an MCP client can ask its user before it calls it.
- requires_github_token: ClassVar[bool] = True
Whether the tool is served only to a request that carries the caller’s GitHub token.
- requires_repository: ClassVar[bool] = True
Whether a call to the public endpoint of every repository must name one.
- run(arguments)[source]
Add the comment that the call gives to the issue that it names.
- Parameters:
arguments (
PublicToolArguments) – The validatedCommentOnIssueArguments.- Return type:
- Returns:
The issue, the author, and the new comment’s url.
- Raises:
SmarterMCPServerToolError – If the repository is unknown, the token is not a classic personal access token, the comment holds a credential, or GitHub could not be reached, or did not create the comment, e.g. the issue is locked.
- class smarter.apps.mcpserver.protocol.tools.github.CreateIssueTool(scope, github_token=None)[source]
Bases:
PublicToolThe
create_issuetool: open a GitHub issue in a repository, as the caller.Writes: a public issue, with the caller’s own GitHub token (see
create_issue()), which notifies the repository’s watchers.Result: a sentence with the new issue’s number, repository, author and title, and its url.
Side effects: one request to GitHub that creates the issue. The tool is served only to a request that carries the caller’s GitHub token, and its result is never cached: every call writes. Its description tells the LLM to search first, and to call it only when the user asks.
- arguments_model
alias of
CreateIssueArguments
- description: ClassVar[str] = "Open a GitHub issue in a repository of The Smarter Project, under the user's own GitHub account, to report a bug or request a feature. It creates a public issue that maintainers are notified of: call it only when the user asks you to, after showing them the repository, title and body, and never more than once for the same issue. Search the code and documentation first, so that the issue is specific, choose the repository that owns the problem, and call search_issues first: if the issue already exists, use comment_on_issue instead."
What the tool does, and when to call it.
The LLM reads it to decide which tool to call.
- open_world: ClassVar[bool] = True
Whether the tool reaches beyond the platform’s own data, e.g. to GitHub.
- read_only: ClassVar[bool] = False
Whether the tool only reads.
A tool that writes is annotated so, with
readOnlyHintfalse, so that an MCP client can ask its user before it calls it.
- requires_github_token: ClassVar[bool] = True
Whether the tool is served only to a request that carries the caller’s GitHub token.
- requires_repository: ClassVar[bool] = True
Whether a call to the public endpoint of every repository must name one.
- run(arguments)[source]
Open the issue that the call describes.
Labels are stripped of surrounding whitespace, and blank ones are dropped.
- Parameters:
arguments (
PublicToolArguments) – The validatedCreateIssueArguments.- Return type:
- Returns:
The new issue’s number, repository, author, title and url.
- Raises:
SmarterMCPServerToolError – If the repository is unknown, a label is longer than
MAX_LABEL_LENGTH, the token is not a classic personal access token, the text holds a credential, or GitHub could not be reached, or did not create the issue.
- class smarter.apps.mcpserver.protocol.tools.github.CreatePullRequestTool(scope, github_token=None)[source]
Bases:
PublicToolThe
create_pull_requesttool: commit a change to a new branch, and open a pull request, as the caller.Reads: the base branch, its commit, and the folders of the changed files, from GitHub.
Writes: a tree, a commit, a new branch and a public pull request, with the caller’s own GitHub token (see
create_pull_request()). It never changes an existing branch, nor a file under.github/.Result: a sentence with the new pull request’s number, repository, author and title, its url, its branches, and its commit.
Side effects: several requests to GitHub. The tool is served only to a request that carries the caller’s GitHub token, and its result is never cached: every call writes.
- arguments_model
alias of
CreatePullRequestArguments
- description: ClassVar[str] = "Propose a change to a repository of The Smarter Project, under the user's own GitHub account: one commit, with each changed file's whole new content, on a new branch from base, and a pull request from it. It never changes an existing branch, nor files under .github/. It is public, and maintainers are notified: call it only when the user asks you to, after showing them the files, title and description, and never twice for the same change. Read every file that you change with read_file first, from the base branch, and keep the change as small as the fix."
What the tool does, and when to call it.
The LLM reads it to decide which tool to call.
- name: ClassVar[str] = 'create_pull_request'
The tool’s name, which the LLM calls it by, e.g.
search_code.
- open_world: ClassVar[bool] = True
Whether the tool reaches beyond the platform’s own data, e.g. to GitHub.
- read_only: ClassVar[bool] = False
Whether the tool only reads.
A tool that writes is annotated so, with
readOnlyHintfalse, so that an MCP client can ask its user before it calls it.
- requires_github_token: ClassVar[bool] = True
Whether the tool is served only to a request that carries the caller’s GitHub token.
- requires_repository: ClassVar[bool] = True
Whether a call to the public endpoint of every repository must name one.
- run(arguments)[source]
Open the pull request that the call describes.
The branch names and the text are stripped of surrounding whitespace. A blank
basemeans the repository’s default branch.- Parameters:
arguments (
PublicToolArguments) – The validatedCreatePullRequestArguments.- Return type:
- Returns:
The new pull request’s number, repository, author, title, url, branches and commit.
- Raises:
SmarterMCPServerToolError – If the repository is unknown, the token is not a classic personal access token, a branch name or a file is not valid, the change holds a credential, or GitHub could not be reached, or refused a step, e.g. because the branch exists.
- class smarter.apps.mcpserver.protocol.tools.github.ReadIssueTool(scope, github_token=None)[source]
Bases:
PublicToolThe
read_issuetool: read a GitHub issue and its latest comments, live.Reads: the issue and the last page of its comments, from GitHub (see
read_issue()), with the caller’s GitHub token if it is a classic token, or anonymously.Result: Markdown: a heading with the issue’s state, author, dates and labels, its body, and its latest comments, oldest first, within
MAX_READ_CHARACTERS.Side effects: one or two requests to GitHub. The result is cached for
ISSUE_SEARCH_CACHE_TTLseconds.- arguments_model
alias of
ReadIssueArguments
- cache_ttl: ClassVar[int | None] = 60
Seconds that a read-only tool’s result is cached, or None for
TOOL_RESULT_CACHE_TTL.
- description: ClassVar[str] = 'Read one GitHub issue of a repository of The Smarter Project: its state, labels and body, and its latest comments, oldest first. Call it before comment_on_issue, so that a comment adds what the issue does not already say, and to see whether a closed issue was fixed or declined.'
What the tool does, and when to call it.
The LLM reads it to decide which tool to call.
- static format_comment(comment)[source]
Return one comment, as Markdown: a heading with its author and date, then its text.
- Parameters:
comment (
GitHubIssueComment) – The comment.- Return type:
- Returns:
The comment, e.g.
### octocat, 2026-11-02and its body. A comment whose author’s account was deleted is byunknown.
- classmethod format_thread(repository, thread)[source]
Return the issue and its comments, as Markdown, within
MAX_READ_CHARACTERS.When the result is too long, the issue’s body is shortened first, to at most half of what is left after the heading, and then the oldest comments are left out, because the latest say what is known now. A notice says how many earlier comments are not shown, counting those that GitHub was not asked for.
- Parameters:
repository (
ProjectRepository) – The issue’s repository.thread (
GitHubIssueThread) – The issue and its latest comments.
- Return type:
- Returns:
The heading, the body, the notice of left-out comments, if any, and the comments that fit, oldest first, separated by blank lines.
- open_world: ClassVar[bool] = True
Whether the tool reaches beyond the platform’s own data, e.g. to GitHub.
- requires_repository: ClassVar[bool] = True
Whether a call to the public endpoint of every repository must name one.
- run(arguments)[source]
Read the issue that the call names, with its latest comments.
- Parameters:
arguments (
PublicToolArguments) – The validatedReadIssueArguments.- Return type:
- Returns:
The issue and its comments, as Markdown (see
format_thread()).- Raises:
SmarterMCPServerToolError – If the repository is unknown, the issue was not found, or GitHub could not be reached, or refused the request.
- class smarter.apps.mcpserver.protocol.tools.github.SearchIssuesTool(scope, github_token=None)[source]
Bases:
PublicToolThe
search_issuestool: search the repositories’ GitHub issues, live.Reads: GitHub’s issue search (see
search_issues()), with the caller’s GitHub token if it is a classic token, or anonymously.Result: Markdown: one numbered entry per issue, best match first, with its title, repository and number, state, number of comments, last update, url and an excerpt of its body.
Side effects: one request to GitHub. The result is cached for
ISSUE_SEARCH_CACHE_TTLseconds, because issues are public, so the result does not depend on who asks.- arguments_model
alias of
SearchIssuesArguments
- cache_ttl: ClassVar[int | None] = 60
Seconds that a read-only tool’s result is cached, or None for
TOOL_RESULT_CACHE_TTL.
- description: ClassVar[str] = "Search the GitHub issues of The Smarter Project's repositories: known bugs, feature requests, and whether they are fixed. Returns the best matches, with their state, number of comments, and url. Use it when a user reports a problem, and always before create_issue, so that an issue is not opened twice: comment on an existing one instead."
What the tool does, and when to call it.
The LLM reads it to decide which tool to call.
- static format_hit(number, hit)[source]
Return one issue of the search result, as a numbered Markdown list item.
- Parameters:
number (
int) – The issue’s position in the result, from 1.hit (
GitHubIssueHit) – The issue.
- Return type:
- Returns:
Its title, repository and number, state, number of comments, last update and url, and, on a third line, the excerpt of its body, if it has one.
- name: ClassVar[str] = 'search_issues'
The tool’s name, which the LLM calls it by, e.g.
search_code.
- open_world: ClassVar[bool] = True
Whether the tool reaches beyond the platform’s own data, e.g. to GitHub.
- run(arguments)[source]
Search the issues of the repository that the call names, or of every repository of the scope.
- Parameters:
arguments (
PublicToolArguments) – The validatedSearchIssuesArguments.- Return type:
- Returns:
The matching issues, as Markdown, or a sentence that says none matched.
- Raises:
SmarterMCPServerToolError – If the repository is unknown, or GitHub could not be reached or refused the search, e.g. its rate limit was reached.