frisian‑mcp.com
Almost every MCP solution treats context bloat as a client problem. frisian-mcp treats it as a server problem — because that's where the tools live.
The problem
Connect an agent to a production Django app and watch it fail. The context window fills before any real work begins. The agent hallucinates operations it can't call, exhausts tokens on tool schemas it will never use, and crashes on list responses it was never designed to handle.
Client-side workarounds and proxy layers exist, but they come with significant tech debt that compounds as MCP matures. The tools live on the server — the server should control what it exposes and how.
You don't change the browser to fit the website. You change the tool to fit the user. In MCP, the user is an agent.
Five specific failure modes
These aren't architectural preferences. Every pattern was added after observing a specific failure at production scale.
* All numbers below are real — measured on production systems, not projected.
A flat MCP server dumps every tool schema into the agent's context the moment it connects. Before any work begins, the context window is spent. Think of needing a 10mm socket but arriving to find every tool scattered across the floor.
The server will gladly return every record it has, regardless of context cost. A list of 500 objects doesn't just slow the agent down — it crashes it. The context window fills with data the agent may never need.
@mcp_heavy — paginated responses
with metadata. The agent gets count and a next URL; it decides whether to paginate.
Traditional APIs return the full object after every create or update so the client can validate. Agents don't need that — they need a status code and an ID. Echoing full objects after every write burns context on data the agent already sent.
Every call to a dispatcher can return its action tree and help scaffolding. Useful the first time an agent orients — pure overhead on every call after. An agent creating 60 devices in one session re-pays that orientation cost on calls it never needed it.
lite once it's oriented; the dispatcher drops action listings, hint text,
and parameter descriptions, returning only what was asked for. On error, the full schema
is automatically re-included so a drifting agent is re-taught without asking. One
connector, no second path to configure.
Some servers present the entire API surface regardless of what the agent can actually access. The agent wastes context trying to align its task to its real permissions — and learns what it can't do only by failing. The same problem applies at the tool level: a read-only agent should never be handed a help listing that includes write, update, or delete actions.
The name
Frisian is the closest living relative of English. When the Angles and Saxons sailed for Britain, the Frisians stayed on the North Sea coast — and for centuries the two tongues stayed close enough that an old saying still rings true when read aloud:
Bûter, brea en griene tsiis is goed Ingelsk en goed Frysk.
Butter, bread, and green cheese is good English and good Frisian.
That kinship is the idea behind this project: a layer that lets two systems built for different speakers — your Django app and an AI agent — understand each other without either having to abandon its own language.
Frisian is also endangered. Its three surviving branches are protected minority languages, and UNESCO’s Atlas of the World’s Languages in Danger lists North Frisian as severely endangered. The communities keeping it alive — in Fryslân, North Frisia, and Saterland — do real work to pass it on.
If this project’s name brought you here, consider learning a little about the language behind it. You can support the people preserving Frisian through the Afûk in Fryslân or the Nordfriisk Instituut in Germany.
Validation
frisian-mcp was validated against four large, production-grade open-source Django applications that look nothing like each other. Network modeling, document workflows, education, and infrastructure impose entirely different data shapes and permission models. The same mechanism integrated with all of them — without touching a single line of host application code.
Two use cases
If you have a Django app with DRF ViewSets, frisian-mcp exposes it via MCP without refactoring. Your existing REST API becomes agent-accessible. URLs, serializers, permissions — everything works as-is. The host application's source code is untouched.
See an integration walkthrough →Beyond brownfield integrations, frisian-mcp supports designing systems for agents as users from day one. The question shifts from "how do we expose this?" to "how do agents interact with data?" Agents are first-class users, not consumers of a human API.
Read the getting started guide →Get started
pip install frisian-mcp # settings.py
INSTALLED_APPS = [..., 'frisian_mcp']
FRISIAN_MCP_AUTODISCOVER = True # urls.py
from frisian_mcp.views import McpView
urlpatterns = [path('mcp/', McpView.as_view()), ...]
Connect your MCP client to https://your-domain.com/mcp/.
That's the full install for a brownfield app with existing ViewSets.
Technical record
The changelog, architecture decisions, and test reports from real integrations are all publicly available. Nothing is hidden behind a paywall or a sales call.
Every significant design choice — the problem it solved, the alternatives considered, and the consequences.
Browse ADRs →Real integration test runs across Nautobot, NetBox, Paperless-ngx, and Open edX — with actual token counts and agent transcripts.
See test reports →A complete version history of every feature added, behaviour changed, and bug fixed since the project started.
Read the changelog →Point your agent at the live demo endpoint and let it validate the claims directly.
Standards & compliance
frisian-mcp implements the full MCP specification, the OAuth 2.0 RFC stack, and the web standards that production environments require. Every listed specification is directly implemented in the codebase — not referenced, implemented.
| Specification | What it covers | Where implemented |
|---|---|---|
| MCP Specification | Model Context Protocol — Anthropic | Core protocol in McpView. Tool discovery (tools/list), invocation (tools/call), and the initialize handshake. |
| JSON-RPC 2.0 | JSON-RPC 2.0 Specification | Transport format for all MCP traffic. Every request and response is a JSON-RPC 2.0 message (jsonrpc, id, method, result, error). |
| SSE | Server-Sent Events — W3C/WHATWG Living Standard | Optional streaming transport. When the client sends Accept: text/event-stream, McpView wraps responses in an SSE stream. |
| RFC | Title | Where implemented |
|---|---|---|
| RFC 6749 | The OAuth 2.0 Authorization Framework | Foundation for all OAuth flows. §4.4 client credentials (TokenView), §4.1 authorization code (AuthorizeView), §3.1.2 redirect URI validation, §3.3 scope strings. |
| RFC 6750 | Bearer Token Usage | §2.1 Bearer token transmission in the Authorization header. Implemented in OAuthTokenAuthentication and FrieseMcpApiKeyAuthentication. |
| RFC 7235 | HTTP/1.1: Authentication | §2.1 case-insensitive authentication scheme names (e.g. Bearer). Referenced alongside RFC 6750 in authentication.py. |
| RFC 7591 | OAuth 2.0 Dynamic Client Registration | Implemented by RegistrationView at /oauth/register/. §2 grant_types enforcement on OAuthClient. Controlled via FRIESE_MCP_OAUTH_DCR. |
| RFC 7636 | PKCE by OAuth Public Clients | PKCE S256 challenge/verifier in _verify_pkce() and AuthorizeView. Used by native and public clients. Enabled via FRIESE_MCP_OAUTH_PKCE_AUTO_REGISTER. |
| RFC 8252 | OAuth 2.0 for Native Apps | §7.1 custom URI scheme and §7.3 loopback redirect (127.0.0.1) used in AuthorizeView redirect URI validation. |
| RFC 8414 | OAuth 2.0 Authorization Server Metadata | Implemented by WellKnownView at /.well-known/oauth-authorization-server. Advertises endpoints, scopes, and capabilities. |
| RFC 8707 | Resource Indicators for OAuth 2.0 | Per-resource metadata URL construction in wellknown_urls.py. |
| RFC 9728 | OAuth 2.0 Protected Resource Metadata | /.well-known/oauth-protected-resource endpoint. Advertised in WWW-Authenticate 401 responses via resource_metadata= link. |