AnnouncementsIntroducing Arkoren

    Menu

    Securing MCP

    Protect the MCP HTTP boundary with verified identity, TLS, Host and Origin policy, and deliberate OAuth integration.

    What's on this page
    1. Protect the HTTP endpoint with TLS, Host and Origin policy, and a verified typed user.
    2. Distinguish endpoint admission, browser CORS, and cookie CSRF protection.
    3. Bound execution and integrate remote OAuth without exposing private admission diagnostics.
    On this page

    A private MCP tool needs an authenticated caller before protocol dispatch. mcpserver verifies no credentials and issues no tokens: keep admission in your HTTP service. Tool availability decides which operations to offer after admission; shared application services still authorize the records those operations use.

    The first server intentionally exposes only a fixed public label on loopback. Do not replace that result with private data without adding the boundaries below.

    Transport Boundary

    Keep private tools on a private network or loopback listener, and use TLS whenever credentials cross a network. Before remote deployment, enforce allowed external Host values and validate Origin using your gateway or outer HTTP middleware. Bearer authentication does not replace Origin validation.

    The pinned SDK enables localhost DNS-rebinding protection: requests arriving through a loopback local address with a non-localhost Host receive 403. This is not a public-host allowlist. The adapter does not enable the SDK’s optional cross-origin protection, so your outer Origin policy remains necessary.

    With router imported from arkoren.dev/core/http/router, adapt your existing boundary func(http.Handler) http.Handler before authentication and mounting:

    routes := router.New()
    routes.Use(router.HTTPMiddleware(boundary))

    Here boundary is application-owned middleware enforcing the deployment’s Host and Origin policy, not an Arkoren API. Behind a proxy, establish which component validates the external values and trust forwarded headers only from that proxy. A private proxy address alone does not make client headers trusted.

    Typed Authentication

    Use your existing bearer verifier to resolve the application’s user, not merely decode claims. Assume User is your identity type and verifyAccessToken has signature func(context.Context, string) (User, error).

    Import arkoren.dev/core/http/auth and add authentication to the router above before mounting the checked endpoint from tool registration:

    routes.Use(auth.Middleware(auth.Bearer(verifyAccessToken)))
    routes.Use(auth.Require[User]())
    routes.Mount("/mcp", endpoint)

    auth.Middleware stores a verified User, but permits anonymous requests when credentials are absent. auth.Require[User]() rejects those requests. Invalid credentials fail authentication. Configure middleware before Mount and serve the router through your existing HTTP lifecycle.

    The mount and adapter preserve request context for policies and handlers. auth.FromContext[User] must use the exact concrete type stored at admission: User and *User occupy different slots. Mismatched lookups fail closed.

    If existing net/http middleware already verifies callers, retain it. Reject anonymous requests and inject the verified identity with auth.NewContext before calling the endpoint rather than adding another MCP-specific bearer parser.

    Endpoint Admission

    Authentication need not grant everyone access to MCP. If your application has an endpoint-wide rule, import arkoren.dev/core/http/auth/gate and insert it after authentication but before the mount:

    routes.Use(gate.Require(canUseMCP))

    canUseMCP is your existing func(context.Context, User) (bool, error) rule. This admits a caller to the whole endpoint; it does not authorize individual tools or records. Capability Access handles those separate decisions.

    Browser Access

    For a trusted browser application on another origin, configure CORS before authentication and mounting. With cors from arkoren.dev/core/http/middleware/cors and standard net/http:

    routes.Use(cors.Middleware(func(policy *cors.Builder) {
    	policy.AllowedOrigins("https://app.example.com")
    	policy.AllowedMethods(http.MethodPost)
    	policy.AllowedHeaders("Accept", "Content-Type", "Authorization", "MCP-Protocol-Version", "Mcp-Method", "Mcp-Name")
    }))

    This is inserted before the authentication block, not after the completed mount. CORS answers preflights and lets the trusted browser read responses; it does not reject requests from disallowed origins. Keep the outer Origin policy. The Mcp-Method and Mcp-Name headers support newer protocol clients.

    A bearer-only endpoint does not rely on browser-attached credentials, so it does not need cookie CSRF protection. If the endpoint also accepts session cookies, add CSRF protection before authentication. Import arkoren.dev/core/http/middleware/csrf and trust only the browser origin that needs access:

    routes.Use(csrf.Middleware(func(protection *csrf.Builder) {
    	protection.TrustedOrigins("https://app.example.com")
    }))

    Cookies can accompany forged browser requests, so CORS alone does not prevent their effects. If you intentionally permit credentialed cross-origin browser requests, also configure policy.AllowCredentials(true) in the CORS callback and an appropriate cookie policy; never combine credentials with a wildcard origin.

    Execution Bounds

    Retain listener header/read/write/idle timeouts and set operation deadlines in outer middleware or application services. The adapter has no tool-duration option. Pass request context into dependencies: a write timeout or client disconnect is not a universal guarantee that work stops, and cancellation does not undo completed effects.

    The transport contract specifies the 4 MiB body limit and protocol-dependent disconnect cancellation. Configure any stricter body limit at your gateway or HTTP boundary. Managed error redaction protects tool responses, not arbitrary admission responses or application logs.

    Remote OAuth

    The bearer example assumes a client already has a token. A remote MCP OAuth deployment also needs protected resource metadata, bearer challenges, and an authorization server issuing tokens for this resource. A login flow or a token verifier alone is not that complete integration.

    oauthauth.Client.Verify from arkoren.dev/driver/oauth/oauthauth verifies OIDC bearer access tokens with configured audiences and returns oauthauth.Identity. Map it to your application User if your policies expect that type; the OIDC bearer guide owns verifier configuration and lifecycle. The driver is neither a token issuer nor a complete MCP authorization server.

    For native integration, the pinned SDK provides RequireBearerToken and ProtectedResourceMetadataHandler in github.com/modelcontextprotocol/go-sdk/auth. These are outer HTTP components, not mcpserver.Builder settings. Configure a real token verifier, accurate public resource metadata, and authorization-server URLs. Preserve required WWW-Authenticate challenges rather than replacing them with ordinary JSON authentication errors.

    SDK TokenInfo is separate from Arkoren’s typed user context. Before managed dispatch, read it with TokenInfoFromContext, resolve your application user, and inject that user with core/http/auth.NewContext. Require the exact identity type your tool policies use. The SDK requires nonzero token expiration by default. Enforce endpoint scopes with RequireBearerTokenOptions.Scopes; advertising supported scopes in metadata does not enforce them.

    The SDK’s bearer middleware returns verifier error text in HTTP failure responses. Return only approved public errors from that verifier and retain private diagnostics in protected local logs. Managed ToolError redaction does not apply to this earlier boundary. Resource metadata is public configuration, not a place for credentials; the SDK metadata handler does not validate its fields.

    Neither endpoint scopes nor metadata authorize a particular shipment or invoice. Keep resource checks in shared services. The CLI connection guide shows how to supply an existing token; the CLI does not log in or refresh it.

    For endpoint settings, see the MCP server API; for tool policy declarations, see the core MCP API.

    Open a bulk documentation archive?

    This file contains every guide in its scope, not API declarations. Loading it all into an agent can exhaust its context window and increase processing time and cost. For coding tasks, start with the index and read only the relevant pages.

    Use the full archive for offline indexing, bulk processing, or deliberate large-context use.

    Browse the index instead
    Open archive