Public Tool Base Class

The base class of the MCPServer’s public tools, the scope that they work on, and the limits of their results.

Each tool is a PublicTool: a name, a description, a Pydantic model of its arguments, from which its MCP inputSchema is generated, and PublicTool.run(), which returns its result as Markdown text for the LLM. PublicTool.call() validates the arguments, and caches a read-only tool’s result.

A tool works on the repositories of its RepositoryScope. The endpoint of one repository serves the tools without a repository argument, and the endpoint of all of the repositories adds one, whose values are the repositories’ names.

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.protocol.tools.public.DEFAULT_SEARCH_LIMIT = 8

The number of results that a search tool returns when the LLM does not give a limit.

smarter.apps.mcpserver.protocol.tools.public.MAX_SEARCH_LIMIT = 20

The most results that a search tool returns, which keeps a result within the LLM’s context.

class smarter.apps.mcpserver.protocol.tools.public.PublicTool(scope, github_token=None)[source]

Bases: MCPToolBase

A public tool of the MCPServer.

A subclass sets name, title, description and arguments_model, the class attributes that say which endpoints serve it and how it takes a repository, and implements run(). call() validates the arguments and caches a read-only tool’s result, so run() only does the work.

Parameters:
  • scope (RepositoryScope) – The repositories of the endpoint.

  • github_token (SecretStr | None) – The caller’s GitHub token, from the request’s Authorization: Bearer header, or None. Only a tool that requires_github_token uses it.

__init__(scope, github_token=None)[source]

Keep the endpoint’s scope, and the caller’s GitHub token.

Parameters:
  • scope (RepositoryScope) – The repositories of the endpoint.

  • github_token (SecretStr | None) – The caller’s GitHub token, or None.

arguments_model

The Pydantic model of the tool’s arguments, a subclass of PublicToolArguments.

alias of PublicToolArguments

cache_ttl: ClassVar[int | None] = None

Seconds that a read-only tool’s result is cached, or None for TOOL_RESULT_CACHE_TTL.

call(arguments)[source]

Validate a tool call’s arguments, and run the tool.

The endpoint of one repository, and a tool that does not uses_repository, drop a repository argument, which their schema does not offer, rather than refuse it.

A read-only tool’s result is cached for an hour, or its cache_ttl, until an index changes. See smarter.apps.mcpserver.protocol.caching. The cache key is the platform’s version, the index generation, the endpoint’s repository (* for all of them), the tool’s name and its validated arguments, so a new release or a new index never serves a stale result. An error is not cached. A tool that writes runs on every call.

Side effects: reads, and on a miss writes, the Django cache, with cached_tool_call().

Parameters:

arguments (dict[str, Any] | None) – The tool call’s arguments.

Return type:

str

Returns:

The result, as Markdown.

Raises:

SmarterMCPServerToolError – If the arguments are not valid, or the tool fails.

description: ClassVar[str]

What the tool does, and when to call it.

The LLM reads it to decide which tool to call.

input_schema()[source]

Return the tool’s MCP inputSchema.

Return type:

dict[str, Any]

Returns:

A JSON schema of type object. The public endpoint of one repository omits repository, and that of all of them lists the repositories’ names.

name: ClassVar[str]

The tool’s name, which the LLM calls it by, e.g. search_code.

project_wide: ClassVar[bool] = False

Whether only the public endpoint of every repository serves the tool.

requires_github_token: ClassVar[bool] = False

Whether the tool is served only to a request that carries the caller’s GitHub token.

requires_repository: ClassVar[bool] = False

Whether a call to the public endpoint of every repository must name one.

abstractmethod run(arguments)[source]

Run the tool.

call() has already validated the arguments, and checked the cache.

Parameters:

arguments (PublicToolArguments) – The validated arguments, an instance of arguments_model.

Return type:

str

Returns:

The result, as Markdown.

Raises:

SmarterMCPServerToolError – If the tool fails in a way that the LLM should be told about, e.g. a file that does not exist.

run_service(arguments)[source]

Run the tool, and turn a service’s error into a tool error.

Parameters:

arguments (PublicToolArguments) – The validated arguments.

Return type:

str

Returns:

The result, as Markdown.

Raises:

SmarterMCPServerToolError – If the tool, or a service that it calls, fails.

classmethod serves(scope, has_github_token=False)[source]

Whether the endpoint of a scope serves the tool.

A tool that requires_github_token is served only to a request that carries one, and a project_wide tool only by the public endpoint of every repository.

Parameters:
  • scope (RepositoryScope) – The endpoint’s repositories.

  • has_github_token (bool) – Whether the request carries the caller’s GitHub token.

Return type:

bool

Returns:

True if the endpoint lists the tool, and lets the LLM call it.

title: ClassVar[str]

The tool’s human-readable name, which an MCP client may show.

uses_repository: ClassVar[bool] = True

Whether the tool takes a repository argument on the public endpoint of every repository.

class smarter.apps.mcpserver.protocol.tools.public.PublicToolArguments(**data)[source]

Bases: BaseModel

The arguments of every tool: repository, which only the public endpoint of every repository offers.

A tool’s own arguments model subclasses it. Arguments that the model does not define are refused (extra="forbid"), so that a misspelled argument is reported to the LLM rather than ignored.

model_config = {'extra': 'forbid'}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

repository: str | None

The name of the repository to work on, e.g. smarter-cli.

PublicTool.input_schema() replaces its schema with an enum of the scope’s repositories, or removes it from the endpoint of one repository.

class smarter.apps.mcpserver.protocol.tools.public.RepositoryScope(**data)[source]

Bases: BaseModel

The repositories that one public endpoint serves.

The endpoint creates its scope once, and gives it to each of its tools, which call resolve() to turn a call’s repository argument into a repository. It is frozen, because the tools share it.

Parameters:
  • repository – The one repository of the endpoint, or None for all of them.

  • repositories – The repositories that the endpoint serves: (repository,), or every repository of The Smarter Project.

property is_single: bool

Whether the endpoint serves one repository.

model_config = {'frozen': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

property names: list[str]

The names of the repositories.

repositories: tuple[ProjectRepository, ...]

The repositories that the endpoint serves, in their configured order.

repository: ProjectRepository | None

The one repository of the endpoint, or None for the endpoint of every repository.

resolve(name, required)[source]

Return the repository that a tool call names.

Parameters:
  • name (str | None) – The tool call’s repository argument.

  • required (bool) – Whether the tool needs one repository.

Return type:

ProjectRepository | None

Returns:

The endpoint’s one repository, the named repository, or None for all of them.

Raises:

SmarterMCPServerToolError – If the name is not one of the scope’s repositories, or a required name is missing.

smarter.apps.mcpserver.protocol.tools.public.simplify_schema(schema)[source]

Simplify a Pydantic JSON schema for an LLM.

Pydantic’s schema carries what helps a form, not an LLM: a title for the model and for each field, the model’s docstring as its description, and "default": null for each optional field. They cost tokens in every tools/list result, and the docstring is written for developers, so they are removed. An optional field’s anyOf: [<type>, {"type": "null"}] becomes its type, which every MCP client understands.

Parameters:

schema (dict[str, Any]) – The schema, from model_json_schema(). It is not changed: the function works on a deep copy.

Return type:

dict[str, Any]

Returns:

The simplified schema.