Views

The Django views of the MCPServer’s two endpoints, over MCP’s Streamable HTTP transport.

PublicEndpointView is the endpoint of the public tools, PublicEndpoint. It is mounted at:

  • /api/v1/mcp/smarter-project/: every repository of The Smarter Project.

  • /api/v1/mcp/smarter-project/<repository>/: one repository, e.g. smarter-cli.

StaffEndpointView is the endpoint of the staff-only tools, StaffEndpoint, at /api/v1/mcp/smarter-operations/. It serves only the platform’s staff (is_platform_staff(): the active staff users of the Smarter account, not the admins of other accounts), in one of two ways:

  • a request whose Authorization: Token header carries a staff user’s api key (a SmarterAuthToken), e.g. from Claude Code, or

  • a request that this platform signed, whose X-Smarter-Acting-User header names the staff user who is chatting with an LLMClient, e.g. smarter_sysadmin: see acting_user. One that the platform signed for nobody in particular, e.g. the background refresh of an MCPClient’s catalog, may list the tools, but every tool refuses to run.

Without a valid credential it answers 401 Unauthorized, and for a user who is not one of the platform’s staff 403 Forbidden, before it reads the request’s JSON-RPC. The checks are in smarter.apps.mcpserver.protocol.auth.

Only POST is served. A POST whose messages are all notifications is answered 202 Accepted, with no body, and every other POST with application/json. The server issues no session, and offers no GET stream, so GET and DELETE are answered 405 Method Not Allowed, which MCP clients expect of a stateless server.

The public endpoint requires no authentication: its read-only tools serve the public repositories of The Smarter Project. A client may send the user’s GitHub classic personal access token in an Authorization: Bearer header, and is then also served the tools that write as that user: create_issue, comment_on_issue, close_issue and create_pull_request. The token is passed to the tool as a SecretStr, and is never logged. Each client IP address may send at most smarter_settings.mcpserver_rate_limit requests a minute: see smarter.apps.mcpserver.protocol.ratelimit.

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.views.PublicEndpointView(**kwargs)[source]

Bases: SmarterView

The view of the MCPServer’s public endpoint, over MCP’s Streamable HTTP transport.

It is public: no authentication, but a rate limit for each IP address. CSRF does not apply, because MCP clients are not browsers with a session, and responses are never cached, because each one answers a different JSON-RPC message.

Parameters:

repository – From the url: the repository’s name, or None for every repository.

dispatch(request, *args, **kwargs)
property formatted_class_name: str

The module and class name, formatted for the start of a log message.

http_method_names = ['post', 'options']

GET and DELETE are 405 Method Not Allowed, as MCP clients expect of a stateless server.

Type:

Only POST and OPTIONS

post(request, repository=None)[source]

Answer the JSON-RPC message, or batch of messages, in the request body.

Parameters:
  • request (HttpRequest) – The request.

  • repository (str | None) – The repository’s name, or None for every repository.

Return type:

HttpResponse

Returns:

The JSON-RPC response, or 202 Accepted if no message needs one. An unknown repository is 404 Not Found, a body that is not JSON is 400 Bad Request, and a client over the rate limit is 429 Too Many Requests.

class smarter.apps.mcpserver.protocol.views.StaffEndpointView(**kwargs)[source]

Bases: SmarterView

The view of the MCPServer’s staff-only endpoint, over MCP’s Streamable HTTP transport, for staff only.

It checks the rate limit first, then the caller: the signed X-Smarter-Acting-User header that the platform sends for the user who is chatting with an LLMClient, with acting_staff_user(), or else the api key of the Authorization: Token header, with staff_user(). It reads the JSON-RPC only for one of the platform’s staff, or for a request that the platform signed itself, so that nobody else learns even which tools the server has. The server’s tools record each call in the audit trail, OperationsToolCall, under that user.

dispatch(request, *args, **kwargs)
property formatted_class_name: str

The module and class name, formatted for the start of a log message.

http_method_names = ['post', 'options']

GET and DELETE are 405 Method Not Allowed, as MCP clients expect of a stateless server.

Type:

Only POST and OPTIONS

post(request)[source]

Answer the JSON-RPC message, or batch of messages, in the request body, for a staff user.

Parameters:

request (HttpRequest) – The request.

Return type:

HttpResponse

Returns:

The JSON-RPC response, or 202 Accepted if no message needs one. A request without a valid api key or signed acting user is 401 Unauthorized, one whose user is not one of the platform’s staff is 403 Forbidden, a client over the rate limit is 429 Too Many Requests, and a body that is not JSON is 400 Bad Request.

smarter.apps.mcpserver.protocol.views.answer(request, server)[source]

Answer the JSON-RPC message, or batch of messages, of a request’s body with a server.

Both views end with this, once they have checked the rate limit and, for the staff-only endpoint, the caller.

Parameters:
Return type:

HttpResponse

Returns:

The JSON-RPC response, 202 Accepted if no message needs one, or 400 Bad Request if the body is not JSON.

smarter.apps.mcpserver.protocol.views.rate_limited(view, request)[source]

Return a 429 Too Many Requests response if the request’s IP address is over the rate limit.

Each call counts the request against its IP address’s limit, in the cache (Redis), which every pod shares: see is_rate_limited(). A refused request is logged as a warning, and its response carries a JSON-RPC error and a Retry-After header, in seconds.

Parameters:
Return type:

JsonResponse | None

Returns:

The response, or None if the request may go on.