Generated Computers
Locals는 Apple Silicon 엔진으로 로컬 LLM을 최대 3배 빠르게 실행하는 Generated Computers 제품
Serve django-service-specs specs to agents over the Model Context Protocol, with no API framework.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다.
README 전문 보기 ↗Serve django-service-specs
services and selectors as a Model Context Protocol
(MCP) server, conforming to MCP 2026-07-28 and 2025-11-25 (Streamable HTTP).
Coming from djangorestframework-mcp-server (drf-mcp)? Read
Moving from drf-mcp before the first deploy:
the settings key, the stored names and the registration calls all change.
Register kernel specs directly. The unit of registration is a ServiceSpec
or a SelectorSpec from django-service-specs, not a view: the spec says what
an operation takes, who may run it and what it returns, and this package serves
it over MCP. The same spec can be served by any other transport on the kernel.
from dataclasses import dataclass
from django.urls import path
from django_service_specs import (
DataclassPresenter,
DataclassValidator,
Parameter,
Parameters,
SelectorKind,
SelectorSpec,
ServiceSpec,
Unrestricted,
)
from django_spec_mcp import MCPServer
@dataclass
class InvoiceInput:
number: str
amount_cents: int
@dataclass
class InvoiceOutput:
id: int
number: str
amount_cents: int
server = MCPServer(name="my-app", auth_backend=my_auth_backend)
server.register_tool(
ServiceSpec(
service=create_invoice, # def create_invoice(*, number, amount_cents)
validator=DataclassValidator(InvoiceInput),
presenter=DataclassPresenter(InvoiceOutput),
permissions=[Unrestricted()],
),
name="invoices.create",
description="Create an invoice.",
)
server.register_resource(
SelectorSpec(
kind=SelectorKind.RETRIEVE,
selector=get_invoice, # def get_invoice(*, pk)
reads=Parameters([Parameter("pk", "integer", required=True)]),
presenter=DataclassPresenter(InvoiceOutput),
permissions=[Unrestricted()],
),
name="invoice",
uri_template="invoices://{pk}",
)
urlpatterns = [path("mcp/", server.urls)]
See the quickstart for the full end-to-end recipe.
register_tool; the spec's class says which kind of tool it is.RETRIEVE selector → an MCP resource, through register_resource./mcp endpoint speaks Streamable HTTP, with its protected-resource
metadata mounted alongside. server.well_known_urls() also serves that document
at RFC 9728's location, from the project's root URLconf.tools/list, tools/call for register_tool, over a kernel
ServiceSpec (mutations) or SelectorSpec (reads, with an optional
FilterSet, ordering and pagination). register_tools(registry, overrides=...) registers every spec in a kernel SpecRegistry.server_arguments=ServerArguments(values=..., withheld=...) at registration answers a declared argument for the caller:
the name leaves the tool's schema, a caller's value for it is dropped and
logged by name, and the binding is checked when it is registered. Route and
query values a tool asks for are read from the spec's own reads.resources/list, resources/templates/list,
resources/read against a kernel RETRIEVE SelectorSpec, presented by
its own presenter; RFC 6570 templated URIs whose variables are the spec's
reads.prompts/list, prompts/get against render callables
returning strings, PromptMessages, or async coroutines.MCPServer.call_tool / acall_tool and list_tools / alist_tools
drive the same dispatch and permission checks as the wire path, for
embedding in agent bridges, toolsets, or management commands.
unavailable_tools / aunavailable_tools name each tool the listing leaves
out with the reason, so a bridge can tell its model why one is missing.annotations= at registration (or rely on the
read/mutation default) to advertise MCP hints like readOnlyHint /
destructiveHint on tools/list._meta — pass meta= at registration to populate the base
protocol's free-form _meta object on a tool, resource, or prompt's
listing entry (and on the contents of resources/read). Passed through
verbatim, so protocol extensions have somewhere to live.register_ui_resource(...) declares an
HTML view with typed CSP / permission metadata; ui=UIToolMeta(...) on a
tool links its result to that view, and a host renders it inline in the chat.
The render payload is the you already emit, and a view's
own s inherit your auth, permissions and rate limits. An
extension over base MCP, so no protocol bump. We declare; the host sandboxes
and renders.pip install django-spec-mcp # base install
pip install "django-spec-mcp[redis]" # +Redis SSE broker for multi-worker ASGI
pip install "django-spec-mcp[otel]" # +OpenTelemetry instrumentation
pip install "django-spec-mcp[redis,otel]" # everything
…or with uv:
uv add django-spec-mcp # base install
uv add "django-spec-mcp[redis]" # +Redis SSE broker for multi-worker ASGI
uv add "django-spec-mcp[otel]" # +OpenTelemetry instrumentation
uv add "django-spec-mcp[redis,otel]" # everything
Install mcp-inspector and point it at your dev server:
npx @modelcontextprotocol/inspector --url http://localhost:8000/mcp/
Inspector lists tools, fills in arguments from the generated JSON Schema, and walks the OAuth auth flow against your configured Authorization Server.
MIT.
structuredContenttools/callMCPAuthBackend verifies the bearer token;
AllowAnyBackend (dev only) ships, and bearer_challenge builds the 401
challenge a backend of your own answers with. A tool or resource is guarded by its spec's own kernel permissions, and
scopes= at registration adds an OAuth scope gate.audience_matches, which a backend
calls; RFC 9728 PRM served from the configured backend.MCPRateLimit Protocol with
FixedWindowRateLimit, SlidingWindowRateLimit, and
TokenBucketRateLimit implementations shipped.urls for WSGI,
async_urls for ASGI; MCPServer.notify(session_id, payload)
pushes JSON-RPC frames on the session's SSE stream. Per-worker
InMemorySSEBroker or cross-worker RedisSSEBroker (behind
[redis]); Last-Event-ID resume via
InMemorySSEReplayBuffer / RedisSSEReplayBuffer.mcp.tools.call,
mcp.resources.read, mcp.prompts.get spans (no-op without the
[otel] extra installed).2026-07-28 and later
declare version, identity and capabilities on every request and hold no
session; 2025-11-25 and earlier negotiate once through initialize. Both
are served concurrently on one endpoint, because a legacy client has no
fall-forward mechanism.