Urchin is a Model Context Protocol (MCP) server library. It implements the transport and protocol; the surrounding deployment (network exposure, TLS, an authorization server, rate limiting) is the operator's responsibility. This document describes what Urchin does and does not protect against, and what you must add before exposing a server publicly.
What Urchin provides
- Origin validation (DNS-rebinding protection). By default only missing-Origin and
localhost are allowed; configure
:allowed_originsfor browser clients. - Cryptographically random session ids (
MCP-Session-Id), visible-ASCII only. - OAuth 2.1 resource-server authorization (optional, off by default). When enabled, it
extracts bearer tokens on every request, delegates the authorization decision to your
configured
Urchin.Auth.Authorizer, and serves RFC 9728 discovery. The authorizer must verify token validity, expiry, issuer, audience/resource binding, scopes and tenant policy. The authorization server itself is external and out of scope. - Error redaction. Unexpected exceptions and malformed handler returns are logged in
full and replaced with a generic message before reaching clients (
:expose_internal_errors, defaultfalse, opts into the detail for development). Deliberate errors —Urchin.Errorvalues and{:error, message}returns — are not redacted; theirmessage/datareach the client unchanged, so keep secrets and internals out of them. Fortools/call, a string{:error, message}is surfaced as aCallToolResultwithisError: truerather than a JSON-RPC error. - Capability-gated server-initiated requests.
sampling/createMessage,elicitation/createandroots/listare only sent when the client advertised the capability. - Declarative per-tool scopes.
tool "name", scopes: [...]enforces scopes againstctx.authbefore the handler runs, failing closed when the request carries no authorization (only meaningful whenctx.authis populated, typically by:author an upstreamUrchin.Auth.Plug). - Argument validation. A DSL tool's
tools/callarguments are validated against itsinput_schemabefore the handler runs (a hand-writtencall_tool/3validates its own arguments). It is a minimal subset of JSON Schema (seeUrchin.Schema), so unsupported keywords andoutput_schemaare still your handler's responsibility. - Bounded request bodies (
@max_body, ~8 MB) and a per-request handler timeout. - Session lifecycle limits (opt-in):
:max_sessions,:session_idle_timeoutand:session_max_lifetime. Without them a session persists until the client sendsDELETE, so configure them on any public endpoint to avoid exhaustion.
What you must add before public exposure
Urchin does not yet provide these; supply them in your deployment:
- TLS. Terminate HTTPS at a reverse proxy or the endpoint. OAuth requires HTTPS in production.
- Authorization. Set
:auth(or front the transport withUrchin.Auth.Plug). An unauthenticated server bound to a public interface exposes every tool to anyone. - Rate limiting / per-session concurrency limits. Per IP, per session, and for
initializeand long-running tools. (Session count and lifetime are covered by the built-in limits above; request-rate and in-flight caps are not yet built in.) - Per-tool authorization beyond scopes. Declarative
scopes:covers scope checks; add app-specific authorization (ownership, tenancy, row-level access) in handlers viactx.auth. - Full input validation. Structural checks against
input_schemarun automatically, but validate unsupported JSON Schema keywords, business rules andoutput_schemain your handler —Urchin.Schemais a minimal subset.
Deployment checklist
- [ ] HTTPS only; redirect URIs are
localhostor HTTPS. - [ ]
:authconfigured with anauthorizerthat verifies signature/introspection, expiry,issuer, audience/resource binding, scopes and tenant policy. - [ ]
:allowed_originsset explicitly (not the localhost default) for browser clients. - [ ]
:ipbound to the intended interface. - [ ]
:expose_internal_errorsleft atfalse. - [ ]
:max_sessions,:session_idle_timeoutand:session_max_lifetimeconfigured. - [ ] Rate limiting in front of the transport.
- [ ] Tokens never forwarded to upstream APIs (use a separate upstream token).
Reporting a vulnerability
Please report security issues privately rather than opening a public issue. Use GitHub's private vulnerability reporting for this repository, or contact the maintainers at Urth Inc. We aim to acknowledge reports promptly and will coordinate a fix and disclosure timeline with you.