Protect an MCP server
Availability: this walkthrough describes a service template available through beta repository access. It is not a public standalone starter. You need an accessible Udibo tenant. For runnable public package examples, see starter projects.
The template runs a stateless MCP server on Deno and Hono. Its team_notes tool
returns sample notes for one configured organization. The server accepts people
whose access token names that organization and carries notes:read.
Register the client and grant access
Choose the server's canonical external URL, ending in /mcp, such as
https://notes.example.com/mcp. In your tenant, define notes:read, create a
role carrying it, and grant that role to the intended people inside the
organization. Follow permissions for role assignment.
Manually register the MCP client's application with these settings:
Type: third-party. Hosted authorization requests consent for scopes the person has not already approved.
Client authentication: public (
confidential: false), with no secret.Grant: authorization code.
Access token format: JWT.
Audience: the exact external MCP URL, including
/mcp.Redirect URI: the client's IP-loopback callback, including the path and any fixed query parameters. Production registrations permit HTTP callbacks on
127.0.0.1or[::1]and allow an ephemeral port. Alocalhosthostname is not the production loopback exception.
See application registration for the dashboard and API workflow. Dynamic client registration is not part of this starter.
Configure your MCP client with its registered client ID and openid profile
scopes. Permission names are separate from OAuth scopes. The client must use
PKCE S256, generate and verify OAuth state, and request the intended
organization with the hosted authorization endpoint's organization slug
parameter. The TypeScript SDK's OAuth provider supports state() but makes it
optional; supply it for Udibo. Sign in and approve the displayed consent.
Configure the server
Set three environment values in the template's .env:
UDIBO_ISSUER: your tenant origin, such ashttps://<tenant-id>.udibo.com.MCP_RESOURCE_URL: the canonical external server URL ending in/mcp.MCP_ORGANIZATION_ID: the ID of the organization whose notes it exposes.
Run deno task serve from templates/mcp-server in the template checkout. The
development defaults listen on 127.0.0.1:8010 and identify the resource as
http://127.0.0.1:8010/mcp. Set DENO_SERVE_ADDRESS for your deployment, serve
HTTPS through your proxy, and preserve the canonical Host header. The server
rejects other Host values and foreign Origin headers; this template is intended
for native MCP clients, not cross-origin browser clients.
Checkpoint: call /mcp without a bearer token. It should return 401 with a
WWW-Authenticate header containing resource_metadata. Fetch that URL: its
resource must be this MCP server's URL, and its authorization_servers must
contain your tenant origin. The client uses those documents to discover the
hosted authorization and token endpoints.
Verify a tool call and a refusal
Connect the MCP client using Streamable HTTP. After authorization, initialize
the connection, list tools and call team_notes. A successful call returns the
configured organization ID and a sample welcome note.
Repeat with a member who has no notes:read grant, and with a permitted person
acting in another organization. Both must receive 403. The organization check
compares the verified ID exactly; another organization's matching slug does not
grant access. A token from another tenant or for another audience must receive
401. Machine credentials cannot supply the required openid scope.
The server creates a fresh transport per POST and returns JSON without a server-side session ID. GET streams and session DELETE return 405. Each request must carry its own bearer token in the Authorization header.
Keep the boundaries when extending it
Replace the sample notes with your application's storage and keep its queries scoped to the configured organization. This starter demonstrates a read-only tool, not tenant administration or resource-level sharing.
Udibo currently mints the application's fixed registered audience. MCP clients
send resource on authorization and token requests, but that parameter does not
select or narrow token issuance. Register a separate application for another
resource; this is not general RFC 8707 resource selection. Never pass the MCP
token onward to UserInfo, /api/check, or another API.
JWT validation uses the tenant's public keys and checks signature, access-token type, issuer, audience and expiry. It does not perform an immediate revocation lookup. Choose an access-token lifetime appropriate for your data and keep host clocks synchronized: the starter allows no clock leeway. See application registration and the production checklist before deploying.
Last verified 2026-09-09.

