Notes MCP Server
An MCP server that authenticates agents against Keycloak: it validates Keycloak-issued access tokens for audience, per-tool scope (e.g. notes:read / notes:write), and DPoP key binding, while requiring a client certificate from a trusted CA via mTLS. Includes scripts to set up a Keycloak realm with X.509 client auth and DPoP-bound tokens, plus a demo agent that obtains tokens from Keycloak and calls the server's tools.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Notes MCP Servercreate a note reminding me to renew the TLS certificate next week"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
An MCP server that only trusts agents with a certificate from your CA, and only accepts Keycloak tokens that are meant for it, scoped per tool, and bound to the calling agent's key. An agent built on the MCP TypeScript SDK that has no client secret at all. Tested on Keycloak 26.4.0, Node 22 and @modelcontextprotocol/sdk 1.32.1.
Path | What it is |
| Demo PKI: a CA, one server cert, |
| Realm |
| Notes MCP server: mTLS check, protected resource metadata, token + DPoP proof + audience + scope checks |
| The agent: discovers Keycloak from the 401, gets a token with its cert + a DPoP proof, calls tools |
| Signs a valid DPoP proof with someone else's key, to show a leaked token is rejected |
npm install
./make-certs.sh
docker network create kc-agents
docker run -d --name keycloak --network kc-agents -p 9443:8443 -v ./certs:/certs \
-e KC_BOOTSTRAP_ADMIN_USERNAME=admin -e KC_BOOTSTRAP_ADMIN_PASSWORD=admin \
-e KC_HTTPS_CERTIFICATE_FILE=/certs/server.crt -e KC_HTTPS_CERTIFICATE_KEY_FILE=/certs/server.key \
-e KC_HTTPS_TRUST_STORE_FILE=/certs/truststore.p12 -e KC_HTTPS_TRUST_STORE_PASSWORD=changeit \
-e KC_HTTPS_CLIENT_AUTH=request -e KC_HOSTNAME=https://localhost:9443 \
quay.io/keycloak/keycloak:26.4.0 start-dev
KC_ADMIN_PASSWORD=admin ./setup-realm.sh
docker run -d --name notes-mcp --network kc-agents -p 9444:9444 -v .:/app -w /app \
-e ISSUER=https://localhost:9443/realms/acme -e JWKS_URL=https://keycloak:8443/realms/acme/protocol/openid-connect/certs \
node:22-alpine node mcp-server/server.mjs
mkdir -p logs && DEBUG_LOG=logs/agent.log AGENT=notes-agent node agent/agent.mjsDEBUG_LOG makes the agent write its token to a log file, the kind of leak that happens in real deployments. Use it to try the attacks in the handbook playbook.
What gets refused
Attempt | Result |
|
|
Leaked token, no client certificate | 403 |
Leaked token, another trusted agent's cert, as | 401 |
Leaked token, another agent's cert + its own DPoP key | 401 |
Rogue agent (untrusted CA) at the MCP server | 403 |
Rogue agent at Keycloak | TLS refused ( |
| 401 |
Related MCP server: agent-identity-control-plane
Gotcha: the MCP SDK and DPoP challenges
The SDK's extractWWWAuthenticateParams only reads Bearer challenges. A server that requires DPoP answers with a DPoP challenge, so the agent parses resource_metadata itself, then uses the SDK's discoverOAuthProtectedResourceMetadata and discoverAuthorizationServerMetadata as normal.
Builds on
Keycloak mTLS client auth (episode 3): certificates instead of client secrets
sashyo/keycloak-dpop-demo (episode 4): DPoP-bound tokens
Demo only: start-dev, a throwaway CA, world-readable keys in certs/, and an in-memory replay cache.
License
Apache 2.0.
This server cannot be deployed
Maintenance
Related MCP Connectors
Agent-native notes, tasks, dev-docs, vaults, sync & handoffs. MCP + OpenAPI dual surface.
Shared Markdown notes for MCP-compatible AI tools.
MCP-native notes and memory for ChatGPT, Claude, and other AI tools.
Google Keep-style notes app with an MCP server for AI agents to read/write notes.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceEnables MCP-compatible clients to create, list, search, and delete notes, read all notes as markdown, and use prompts to summarize or clean up notes.MIT
- AlicenseNot gradedqualityCmaintenanceSecures MCP tools as an OAuth 2.1 resource server, validating short-lived delegated tokens, enforcing policy-as-code, and auditing AI agent actions.MIT
- AlicenseNot gradedqualityAmaintenanceEnables MCP-capable agents to add, search, edit, archive, and delete locally stored notes over streamable HTTP without using the web UI.MIT
- AlicenseNot gradedqualityCmaintenanceEnables MCP clients to connect over stateless Streamable HTTP and authenticate through an existing OAuth 2.1 / OpenID Connect authorization server, with JWT validation, per-tool scope enforcement, and step-up challenges. It ships example scoped tools for reading and creating per-user notes that developers replace with their own.MIT