Lazaro549

πŸ—’οΈ DevNotes MCP

Community Lazaro549
Updated

πŸ—’οΈ DevNotes MCP

A notes server built to exercise every advanced feature of the Model Context Protocol.

Follow-up project for Anthropic Academy's Model Context Protocol: Advanced Topics course.

πŸŽ“ What this is

DevNotes is a small, in-memory notes server β€” think a minimal Notion, exposed over MCP insteadof a web UI. It's deliberately not trying to be a real product; every tool exists to be a clean,runnable example of one specific advanced MCP feature, and the notes themselves are just theexcuse to have something worth calling a tool on.

Course topic Where it lives
Sampling β€” server asks the client's LLM to do the work summarize-note
Elicitation β€” server pauses to ask the user something create-note (priority, if omitted)
Roots β€” client grants the server a specific local folder index-roots
Progress & logging notifications also index-roots, via sendNotification/sendLoggingMessage
Pagination search-notes, cursor-based
Structured output every tool returns structuredContent alongside its text
stdio vs. Streamable HTTP transports stdio.ts / http.ts
Resumable transport eventStore.ts, stateful HTTP mode
Stateless transport http.ts --stateless
OAuth 2.1 + PKCE authorization auth.ts, http.ts --oauth

πŸ—οΈ Architecture

flowchart LR
    subgraph client["interactiveClient.ts"]
        cap["declares: sampling, roots, elicitation"]
    end

    subgraph transport["pick one"]
        stdio["stdio.ts"]
        http["http.ts (Streamable HTTP)"]
    end

    subgraph server["createServer.ts"]
        t1["create-note<br/><i>elicitation</i>"]
        t2["search-notes<br/><i>pagination</i>"]
        t3["summarize-note<br/><i>sampling</i>"]
        t4["index-roots<br/><i>roots + notifications</i>"]
        store[("NotesStore<br/>(in-memory)")]
    end

    auth["auth.ts<br/>OAuth 2.1 + PKCE"]
    evt["eventStore.ts<br/>resumability"]

    client <-->|JSON-RPC over stdio or HTTP| transport
    transport --> server
    t1 & t2 & t3 & t4 --> store
    http -. "--oauth protects" .-> auth
    http -. "stateful mode uses" .-> evt

Sampling is the one genuinely bidirectional flow in the demo β€” the server calls back intowhichever process holds the model credentials β€” so it's worth seeing on its own:

sequenceDiagram
    participant You
    participant Client as interactiveClient.ts
    participant Server as DevNotes server
    You->>Client: summarize-note
    Client->>Server: tools/call
    Server->>Client: sampling/createMessage
    alt ANTHROPIC_API_KEY set
        Client->>Anthropic API: messages.create()
        Anthropic API-->>Client: completion
    else no key configured
        Client->>Client: return a mock completion
    end
    Client-->>Server: CreateMessageResult
    Server-->>Client: tool result (the summary)
    Client-->>You: print it

The server never holds a model API key itself β€” that's the point of sampling. Only theclient in this diagram needs one, and only optionally (see Quick start).

πŸš€ Quick start

npm install
cp .env.example .env   # optional β€” see below

Everything runs with zero configuration. .env only matters if you want real Claudecompletions for summarize-note instead of a mocked one β€” set ANTHROPIC_API_KEY there.

Local (stdio) β€” what Claude Desktop and the MCP Inspector use:

npm run client            # spawns the server itself, opens an interactive prompt
# or, to poke at it directly:
npm run inspector

Remote (Streamable HTTP):

npm run start:http                # stateful, resumable β€” default
npm run start:http:stateless      # no session id, horizontally scalable
npm run start:http:oauth          # requires a bearer token (see below)
npm run start:http:oauth-strict   # + RFC 8707 resource-indicator enforcement

npm run client:http               # talk to whichever one is running on :3000

Try roots by pointing the client at the bundled example notes:

npm run client -- --root ./examples
# then call index-roots from the prompt

Try the full OAuth flow: run npm run start:http:oauth, then open the MCP Inspector(npx @modelcontextprotocol/inspector) and connect to http://127.0.0.1:3000/mcp β€” itsbuilt-in "Connect" flow walks through dynamic client registration, the authorizationredirect, and the PKCE token exchange for you. auth.ts has a header-comment listingexactly what it does and doesn't do β€” it's a real OAuth 2.1 authorization server, but anintentionally minimal one (in-memory storage, no real login screen); the same file explainswhat a production deployment needs to add back.

πŸ“ Project structure

src/
  server/
    createServer.ts     factory: builds one McpServer + registers all four tools
    store.ts             the in-memory NotesStore (create/search/importFromRoot)
    stdio.ts              entrypoint: stdio transport
    http.ts                entrypoint: Streamable HTTP, stateful/stateless/oauth
    auth.ts               demo OAuth 2.1 + PKCE authorization server
    eventStore.ts        in-memory EventStore for resumable HTTP
    tools/
      createNote.ts        elicitation
      searchNotes.ts       pagination
      summarizeNote.ts     sampling
      indexRoots.ts        roots + progress/logging notifications
  client/
    interactiveClient.ts  demo client: implements the handlers a server can call back into
examples/                sample notes for the index-roots demo
assets/                   the certificate this repo is a follow-up to

πŸ”’ Notes on the security-relevant bits

  • Roots are enforced entirely by the client β€” store.importFromRoot only ever readsthe file:// URI it's handed, one directory level deep, capped at 200 files / 200 KB each.The server never picks its own path.
  • createMcpExpressApp enables DNS-rebinding protection automatically for 127.0.0.1 β€”see http.ts.
  • auth.ts is explicitly a demo authorization server (see the header comment in thatfile for the exact list of what it skips). In a real deployment you'd almost always pointverifyAccessToken at an existing IdP rather than writing one.
  • Every server-initiated request (createMessage, listRoots, elicitInput) passesrelatedRequestId, so responses route to the right session under concurrent HTTP loadinstead of the first one that happens to be listening.

πŸ—ΊοΈ Learning path

Part of a series built while working through Anthropic Academy:

  1. claude-api-starter β€” Building with the Claude API
  2. mcp-client-starter β€” Introduction to Model Context Protocol
  3. mcp-advanced-starter β€” MCP: Advanced Topics (this repo)
  4. Claude Code in Action β€” next up

License

MIT β€” see LICENSE.

MCP Server Β· Populars

MCP Server Β· New

    ihuzaifashoukat

    x-use

    Browser-native AI agents for X (Twitter): multi-account, MCP-ready, no X API key required.

    Community ihuzaifashoukat
    hongnoul

    hwatu

    Visual verification browser for AI coding agents: daemon-based WebKitGTK, real rendering, ~13ms window spawn

    Community hongnoul
    SeanFDZ

    AgentBridge

    Let AI agents control Classic Mac OS through a shared folder protocol.

    Community SeanFDZ
    livecontext-ai

    LiveContext

    The AI automation platform, self-hosted. Describe the job in chat and LiveContext builds it: readable workflows, scoped AI agents, and small apps your team uses. Chat, Workflow, Agent and App on one canvas.

    Community livecontext-ai
    timescale

    rsigma

    A complete Sigma detection engineering toolkit: parser, linter, evaluator, correlation engine, conversion framework, streaming daemon, MCP and LSP servers :crab:

    Community timescale