Stop Losing Your Dev Notes: Build a Markdown-Powered MCP Server with TypeScript
Your notes are not useless. Your system is.
Stop Losing Your Dev Notes: Build a Markdown-Powered MCP Server with TypeScript
Your notes are not useless. Your system is.

Created on Copilot
Last month, I wasted nearly forty minutes searching for a command I had already written down.
Not an advanced command. Not some deep distributed-systems wizardry. Just a small deployment note.
I checked my old project README. Then my Notion page. Then a random notes.md file. Then another folder called final-notes, which, as every developer knows, is never final. Then I searched Slack. Then I searched my terminal history like an archaeologist brushing dust off ancient ruins.
And there it was.
A tiny Markdown note from six months ago.
The worst part?
I had written it clearly.
The note was good. My ability to find and reuse it was terrible.
That is the real problem with developer notes. We do not lose knowledge because we do not write things down. We lose it because our notes live like abandoned furniture in random folders.
Markdown files. README drafts. TODO lists. API decisions. Bug fixes. Setup commands. Architecture thoughts. Copy-pasted Stack Overflow lessons. Tiny discoveries. Painful lessons written after painful debugging sessions.
All sitting there.
Quietly judging us.
So here is my opinion: if you are a developer and you already write notes in Markdown, you should stop treating them like static documents.
Turn them into a tool.
Better: turn them into context for an AI agent.
In this article, we will build a simple Markdown-powered MCP server using TypeScript. The goal is not to create some over-engineered productivity monster that needs twelve Docker containers and emotional support.
The goal is simple
Make your Markdown notes searchable, usable, and available to an AI coding assistant through MCP.
Because your notes should work for you.
Not rot in a folder called misc.
What Are We Building?
We are going to build a local MCP server in TypeScript that exposes your Markdown notes as tools.
An AI agent can then ask your server questions like
- “Find notes about authentication.”
- “Summarise this project decision.”
- “Show TODOs from my Markdown files.”
- “Find commands related to deployment.”
- “Generate a README section based on my notes.”
This is not magic.
It is structured access.
That distinction matters.
A lot of people think AI agents become useful because they are “smart.” That is only half true. Agents become useful when they have access to the right context at the right time.
Your Markdown folder is context.
Your AI agent is the interface.
Your MCP server is the bridge.
That is the whole idea.
Simple. Useful. Not ridiculous.
Reality Check: Your Notes Are Already a Database
Developers love pretending they do not have a knowledge base.
Then you look at their machine and find this
notes/
react-hooks.md
deployment.md
bugs-i-never-want-to-see-again.md
docker-commands.md
project-ideas.md
auth-flow.md
api-design.md
random.md
random-final.md
random-final-2.md
Congratulations.
That is a database.
A chaotic one, yes. But still a database.
The problem is not Markdown. Markdown is great. It is plain text. It works with Git. It is readable forever. You can open it in any editor. You do not need a subscription to remember how your own code works.
The problem is retrieval.
Finding the right note at the right moment is still too manual.
Search helps. But search is dumb in the most annoying way.
You search for “JWT” but your note says “token issue.”
You search for “deploy” but the note says “production push.”
You search for “bug” and get 300 results because apparently your entire career is bugs.
This is where an MCP server becomes useful.
Not because MCP is trendy.
Not because AI agents are fashionable.
But because MCP gives your AI assistant a structured way to use your local tools and data.
What Is MCP, Without the Buzzword Soup?
MCP stands for Model Context Protocol.
In plain English, it is a standard way for AI applications to connect with external tools and data sources.
Think of it like this
A normal chatbot is like a smart person sitting in an empty room.
An MCP-connected agent is like that same person with access to your files, tools, scripts, APIs, database, docs, and project context.
That is a huge difference.
Without MCP
“I think your deployment command might be…”
With MCP
“I found your deployment note from
deployment.md. Here is the exact command and the warning you wrote last time.”
See the difference?
One is guessing.
The other is grounded.
And developers should care about grounded answers.
We have enough hallucinations already. Half our error messages look like poetry written by a broken toaster.
The official MCP TypeScript SDK exists for building MCP servers and clients in TypeScript. That is useful because many web developers already live in TypeScript anyway. No need to switch languages just to build a local tool.
Where Google Antigravity Fits In
Google Antigravity is part of the newer wave of agent-first development tools.
The important idea is not “autocomplete but shinier.”
The important idea is that coding assistants are moving from passive suggestion tools to agentic workflows.
That means the assistant is not just completing a line of code. It can reason through tasks, inspect files, run commands, and use tools.
But here is the uncomfortable truth
An agent without your real context is just a confident intern with Wi-Fi.
It may sound helpful. It may even be helpful sometimes. But it does not know your project decisions, your weird setup notes, your naming conventions, your previous mistakes, or why the payment module has a file named do-not-touch-this.ts.
Your Markdown notes contain that context.
So instead of expecting the agent to “just know,” we give it a proper tool.
That tool is our MCP server.
The Opinionated Architecture
We are going to keep this small.
No database.
No vector search.
No cloud.
No login.
No dashboard.
No “enterprise-grade scalable knowledge intelligence layer.”
Please.
We are building something useful, not pitching a startup.
The architecture
AI Agent / MCP Client
|
| calls tools
v
TypeScript MCP Server
|
| reads local files
v
Markdown Notes Folder
Our MCP server will expose a few tools
search_notes
summarise_note
find_todos
list_notes
get_note
Each tool does one clear thing.
This matters because tool design is where many developers mess up.
They create one giant tool called
do_everything()
Then they wonder why the AI agent behaves like a raccoon inside a keyboard factory.
Good tools are boring. Specific. Predictable.
That is the point.
Project Setup
Create a new folder
mkdir markdown-mcp-server
cd markdown-mcp-server
npm init -y
Install the MCP SDK and TypeScript tools
npm install @modelcontextprotocol/sdk zod
npm install -D typescript tsx @types/node
Create a TypeScript config
npx tsc --init
Your folder can look like this
markdown-mcp-server/
notes/
deployment.md
auth.md
react.md
src/
index.ts
package.json
tsconfig.json
Add a few sample notes.
notes/deployment.md
# Deployment Notes
## Production Deploy
Use this command:
npm run build && npm run deploy
## Warning
Always check environment variables before deploying.
## TODO
- [ ] Automate deployment checklist
- [ ] Add rollback command
notes/auth.md
# Authentication Notes
JWT access tokens should be short-lived.
Refresh tokens should be stored securely.
Common bug:
If the user keeps getting logged out, check token expiry and clock mismatch.
notes/react.md
# React Notes
Avoid putting expensive calculations directly inside render logic.
Use memoization only when there is a real performance issue.
Do not use useEffect as a dumping ground for every problem.
That last sentence is not documentation.
It is therapy.
Build the Markdown Loader
Before touching MCP, let us write basic file utilities.
Create src/index.ts.
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import fs from "node:fs/promises";
import path from "node:path";
const NOTES_DIR = path.resolve(process.cwd(), "notes");
type Note = {
filename: string;
path: string;
content: string;
};
async function loadMarkdownNotes(): Promise<Note[]> {
const files = await fs.readdir(NOTES_DIR);
const markdownFiles = files.filter((file) => file.endsWith(".md"));
const notes = await Promise.all(
markdownFiles.map(async (filename) => {
const filePath = path.join(NOTES_DIR, filename);
const content = await fs.readFile(filePath, "utf-8");
return {
filename,
path: filePath,
content,
};
})
);
return notes;
}
Nothing fancy here.
We read files from a notes directory. We filter Markdown files. We return filename, path, and content.
The boring code is usually the useful code.
Create the MCP Server
Now create the server
const server = new McpServer({
name: "markdown-notes-server",
version: "1.0.0",
});
This server will expose tools. Each tool becomes something an AI agent can call.
The idea is simple
“Here are the things this server can do.”
That is far better than dumping all your notes into a chat window and hoping the model behaves.
Context windows are not filing cabinets.
Tool 1: List All Notes
Let us start with the simplest tool.
server.tool(
"list_notes",
"List all available Markdown notes",
{},
async () => {
const notes = await loadMarkdownNotes();
const result = notes.map((note) => note.filename).join("\n");
return {
content: [
{
type: "text",
text: result || "No Markdown notes found.",
},
],
};
}
);
This gives the agent visibility.
It can ask
“What notes exist?”
And the server replies with the list.
Small tool. Huge usefulness.
Because most of the time, the AI does not need more intelligence. It needs a table of contents.
Tool 2: Get a Specific Note
Now let the agent read a file.
server.tool(
"get_note",
"Read a specific Markdown note by filename",
{
filename: z.string().describe("The Markdown filename, for example deployment.md"),
},
async ({ filename }) => {
const notes = await loadMarkdownNotes();
const note = notes.find((item) => item.filename === filename);
if (!note) {
return {
content: [
{
type: "text",
text: `Note not found: ${filename}`,
},
],
};
}
return {
content: [
{
type: "text",
text: note.content,
},
],
};
}
);
This is where things start feeling practical.
You can ask your agent
“Read my deployment notes and create a safer deployment checklist.”
The agent can call get_note, read deployment.md, and use your actual notes.
Not vibes.
Not guesswork.
Your notes.
Tool 3: Search Notes
Now for the main feature.
We want to search Markdown files by keyword.
This is intentionally simple. We are not building semantic search here. Not yet.
server.tool(
"search_notes",
"Search Markdown notes for a keyword or phrase",
{
query: z.string().describe("The keyword or phrase to search for"),
},
async ({ query }) => {
const notes = await loadMarkdownNotes();
const lowerQuery = query.toLowerCase();
const matches = notes
.map((note) => {
const lines = note.content.split("\n");
const matchedLines = lines
.map((line, index) => ({
lineNumber: index + 1,
text: line,
}))
.filter((line) => line.text.toLowerCase().includes(lowerQuery));
return {
filename: note.filename,
matches: matchedLines,
};
})
.filter((result) => result.matches.length > 0);
if (matches.length === 0) {
return {
content: [
{
type: "text",
text: `No notes found for query: ${query}`,
},
],
};
}
const output = matches
.map((result) => {
const lines = result.matches
.map((match) => `Line ${match.lineNumber}: ${match.text}`)
.join("\n");
return `File: ${result.filename}\n${lines}`;
})
.join("\n\n");
return {
content: [
{
type: "text",
text: output,
},
],
};
}
);
This tool searches all Markdown files and returns matching lines with filenames and line numbers.
Is it basic?
Yes.
Is it useful?
Also yes.
Developers love jumping straight to embeddings, vector databases, and advanced retrieval pipelines.
Sometimes you need that.
Often, you do not.
Start with boring search. Then upgrade only when boring search fails.
That is engineering.
Not résumé-driven development.
Tool 4: Find TODOs
Every developer has TODOs.
Some are useful.
Some are ancient fossils.
Some are lies.
server.tool(
"find_todos",
"Find TODO items in Markdown notes",
{},
async () => {
const notes = await loadMarkdownNotes();
const todos = notes.flatMap((note) => {
return note.content
.split("\n")
.map((line, index) => ({
filename: note.filename,
lineNumber: index + 1,
text: line.trim(),
}))
.filter((line) => {
const text = line.text.toLowerCase();
return (
text.includes("todo") ||
text.startsWith("- [ ]") ||
text.startsWith("* [ ]")
);
});
});
if (todos.length === 0) {
return {
content: [
{
type: "text",
text: "No TODOs found.",
},
],
};
}
const output = todos
.map((todo) => `${todo.filename}:${todo.lineNumber} - ${todo.text}`)
.join("\n");
return {
content: [
{
type: "text",
text: output,
},
],
};
}
);
Now your AI agent can ask
“What unfinished tasks are hidden in my notes?”
And your Markdown folder can finally confess.
This is one of those tiny features that feels boring until you use it.
Then you realise your notes contain half your project roadmap.
Tool 5: Summarise a Note Without Pretending the Server Is an LLM
Important point.
Our MCP server should not summarise text itself unless we add a local summarisation model or algorithm.
The server is not the AI.
The server provides context to the AI.
That separation is healthy.
So instead of creating a fake summarise_note function that does poor summarisation, we create a tool that returns the note content in a structured way and lets the connected AI agent summarise it.
server.tool(
"prepare_note_for_summary",
"Return a Markdown note so the AI agent can summarise it",
{
filename: z.string().describe("The Markdown filename to summarise"),
},
async ({ filename }) => {
const notes = await loadMarkdownNotes();
const note = notes.find((item) => item.filename === filename);
if (!note) {
return {
content: [
{
type: "text",
text: `Note not found: ${filename}`,
},
],
};
}
return {
content: [
{
type: "text",
text: `Summarise the following Markdown note clearly:\n\n${note.content}`,
},
],
};
}
);
This is a good design habit.
Do not make your MCP server responsible for everything.
Let the server fetch and structure data.
Let the AI reason over it.
Let each part do its job.
Wild concept, I know.
Start the Server
At the bottom of src/index.ts, add
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
}
main().catch((error) => {
console.error("Server failed to start:", error);
process.exit(1);
});
Your full server now
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import fs from "node:fs/promises";
import path from "node:path";
const NOTES_DIR = path.resolve(process.cwd(), "notes");
type Note = {
filename: string;
path: string;
content: string;
};
async function loadMarkdownNotes(): Promise<Note[]> {
const files = await fs.readdir(NOTES_DIR);
const markdownFiles = files.filter((file) => file.endsWith(".md"));
const notes = await Promise.all(
markdownFiles.map(async (filename) => {
const filePath = path.join(NOTES_DIR, filename);
const content = await fs.readFile(filePath, "utf-8");
return {
filename,
path: filePath,
content,
};
})
);
return notes;
}
const server = new McpServer({
name: "markdown-notes-server",
version: "1.0.0",
});
server.tool(
"list_notes",
"List all available Markdown notes",
{},
async () => {
const notes = await loadMarkdownNotes();
return {
content: [
{
type: "text",
text: notes.map((note) => note.filename).join("\n") || "No Markdown notes found.",
},
],
};
}
);
server.tool(
"get_note",
"Read a specific Markdown note by filename",
{
filename: z.string().describe("The Markdown filename, for example deployment.md"),
},
async ({ filename }) => {
const notes = await loadMarkdownNotes();
const note = notes.find((item) => item.filename === filename);
return {
content: [
{
type: "text",
text: note ? note.content : `Note not found: ${filename}`,
},
],
};
}
);
server.tool(
"search_notes",
"Search Markdown notes for a keyword or phrase",
{
query: z.string().describe("The keyword or phrase to search for"),
},
async ({ query }) => {
const notes = await loadMarkdownNotes();
const lowerQuery = query.toLowerCase();
const matches = notes
.map((note) => {
const matchedLines = note.content
.split("\n")
.map((line, index) => ({
lineNumber: index + 1,
text: line,
}))
.filter((line) => line.text.toLowerCase().includes(lowerQuery));
return {
filename: note.filename,
matches: matchedLines,
};
})
.filter((result) => result.matches.length > 0);
if (matches.length === 0) {
return {
content: [
{
type: "text",
text: `No notes found for query: ${query}`,
},
],
};
}
const output = matches
.map((result) => {
const lines = result.matches
.map((match) => `Line ${match.lineNumber}: ${match.text}`)
.join("\n");
return `File: ${result.filename}\n${lines}`;
})
.join("\n\n");
return {
content: [
{
type: "text",
text: output,
},
],
};
}
);
server.tool(
"find_todos",
"Find TODO items in Markdown notes",
{},
async () => {
const notes = await loadMarkdownNotes();
const todos = notes.flatMap((note) => {
return note.content
.split("\n")
.map((line, index) => ({
filename: note.filename,
lineNumber: index + 1,
text: line.trim(),
}))
.filter((line) => {
const text = line.text.toLowerCase();
return (
text.includes("todo") ||
text.startsWith("- [ ]") ||
text.startsWith("* [ ]")
);
});
});
if (todos.length === 0) {
return {
content: [
{
type: "text",
text: "No TODOs found.",
},
],
};
}
return {
content: [
{
type: "text",
text: todos
.map((todo) => `${todo.filename}:${todo.lineNumber} - ${todo.text}`)
.join("\n"),
},
],
};
}
);
server.tool(
"prepare_note_for_summary",
"Return a Markdown note so the AI agent can summarise it",
{
filename: z.string().describe("The Markdown filename to summarise"),
},
async ({ filename }) => {
const notes = await loadMarkdownNotes();
const note = notes.find((item) => item.filename === filename);
if (!note) {
return {
content: [
{
type: "text",
text: `Note not found: ${filename}`,
},
],
};
}
return {
content: [
{
type: "text",
text: `Summarise the following Markdown note clearly:\n\n${note.content}`,
},
],
};
}
);
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
}
main().catch((error) => {
console.error("Server failed to start:", error);
process.exit(1);
});
Add scripts to package.json
{
"scripts": {
"dev": "tsx src/index.ts",
"build": "tsc"
}
}
Now run
npm run dev
The server should start over stdio.
In a real MCP client, you would configure this server command so the client can launch it and call its tools.
Practical Use Cases That Actually Matter
Let us stop being abstract.
What can this thing do for a web developer?
1. Turn scattered notes into project memory
You can ask
“Search my notes for anything related to authentication bugs.”
The agent calls search_notes.
It finds
auth.md: Common bug:
If the user keeps getting logged out, check token expiry and clock mismatch.
That is useful.
Not revolutionary. Useful.
Useful beats revolutionary most days.
2. Generate better README sections
Most READMEs are either empty, outdated, or written like the author was being chased.
If your notes contain setup steps, environment variables, and commands, your agent can use them to draft a README section.
Prompt
Use my Markdown notes to generate a clean Installation section for this project.
The agent can call
list_notes
get_note
search_notes
Then produce something based on your actual project notes.
That is better than asking AI
“Write a README for my app.”
Because without context, it will write the same generic soup
npm install
npm start
Thank you, robot. Very enlightening.
3. Build a personal debugging memory
Every developer has bugs they solve and then forget.
That is tragic.
You suffer once, write the note, forget it, suffer again, then rediscover your own note like it was left by a wiser ancestor.
With this MCP server, you can keep files like
bugs.md
production-issues.md
css-weirdness.md
api-errors.md
Then ask
“Have I seen this CORS issue before?”
Your AI agent can search your notes before inventing advice.
That is the difference between generic AI help and personal engineering memory.
4. Find unfinished work
The find_todos tool is embarrassingly useful.
Ask
“What TODOs are hiding in my notes?”
You may discover
deployment.md:12 — - [ ] Add rollback command
auth.md:20 — TODO: Document refresh token expiry
react.md:9 — TODO: Profile dashboard performance
This is both helpful and mildly insulting.
Which is exactly what good tooling should be.
5. Create weekly developer summaries
If you keep weekly notes
week-01.md
week-02.md
week-03.md
Your agent can summarise what you worked on.
Prompt
Read my notes from this week and create a short engineering update.
Useful for
- standups
- portfolio updates
- team reports
- personal reflection
- remembering what you did before Friday erased your brain
Why Markdown Is the Perfect Starting Point
People keep trying to replace Markdown.
They should stop.
Markdown is boring in the best possible way.
It is
- portable
- readable
- Git-friendly
- easy to edit
- easy to parse
- easy to back up
- not locked inside someone’s SaaS prison
A Markdown file does not ask you to upgrade.
It does not change pricing tiers.
It does not “sunset” itself.
It just sits there and works.
That makes it perfect for developer notes.
The trick is not replacing Markdown.
The trick is giving Markdown better access points.
That is what this MCP server does.
Reality Check: Do Not Put Secrets in Your Notes
Now for the adult part.
Do not put secrets in your Markdown notes.
No API keys.
No passwords.
No private tokens.
No .env dumps.
No “temporary production password” because temporary secrets have a funny way of becoming permanent disasters.
If your MCP server can read your notes, then any connected agent may be able to access that content through tool calls.
So treat your notes folder carefully.
Good notes
Use STRIPE_SECRET_KEY from environment variables.
Bad notes
STRIPE_SECRET_KEY=sk_live_please_no
Security is not paranoia.
Security is remembering that future-you is tired and will make mistakes.
Better Tool Design: Small Tools Beat Smart Tools
Here is a strong opinion
Most AI tool integrations are bad because developers make tools too broad.
They expose one giant function and expect the model to figure it out.
Bad tool
manage_notes
What does it do?
Search? Edit? Delete? Summarise? Start a podcast?
Who knows.
Better tools
list_notes
get_note
search_notes
find_todos
Each tool has a narrow job.
This makes the agent more predictable.
Predictability is underrated.
Developers often chase “powerful” systems, then complain when those systems do surprising things.
Do not build surprising tools.
Build boring tools that work.
Your future self will clap silently.
Should You Add Semantic Search?
Maybe.
But not first.
This is where developers love making life harder.
They start with
Markdown notes
Then immediately jump to
Embeddings
Vector database
Chunking strategy
Reranking
Hybrid retrieval
Local model
Docker Compose
Observability dashboard
Sir, you wanted to find your Docker note.
Relax.
Start with keyword search.
If that becomes limiting, then improve.
A sensible upgrade path
Level 1: Keyword search
Level 2: Frontmatter metadata
Level 3: Tags and categories
Level 4: Better ranking
Level 5: Semantic search
Level 6: Local embeddings
Do not start at Level 6 because you watched one conference talk.
Start where the pain is.
That is how useful tools get built.
Add Tags to Your Markdown Notes
A simple improvement is adding metadata at the top of your Markdown files.
Example
---
title: Deployment Notes
tags: [deployment, production, rollback]
project: client-dashboard
---
# Deployment Notes
Always check environment variables before deploying.
Then your server can parse tags later.
You do not need to implement this immediately, but it is a smart habit.
Why?
Because future retrieval becomes easier.
Instead of relying only on text search, you can filter by
project
tag
date
type
status
Your notes become more structured without becoming annoying.
That is the sweet spot.
Structure enough to help.
Not so much that writing notes feels like filling tax forms.
Add a Search Limit
Another practical improvement: limit search results.
You do not want your tool returning 900 matching lines.
That is not context.
That is a hostage situation.
Update the schema
server.tool(
"search_notes",
"Search Markdown notes for a keyword or phrase",
{
query: z.string().describe("The keyword or phrase to search for"),
limit: z.number().optional().describe("Maximum number of matches to return"),
},
async ({ query, limit = 20 }) => {
const notes = await loadMarkdownNotes();
const lowerQuery = query.toLowerCase();
const matches = notes.flatMap((note) => {
return note.content
.split("\n")
.map((line, index) => ({
filename: note.filename,
lineNumber: index + 1,
text: line,
}))
.filter((line) => line.text.toLowerCase().includes(lowerQuery));
});
const limitedMatches = matches.slice(0, limit);
if (limitedMatches.length === 0) {
return {
content: [
{
type: "text",
text: `No notes found for query: ${query}`,
},
],
};
}
return {
content: [
{
type: "text",
text: limitedMatches
.map((match) => `${match.filename}:${match.lineNumber} - ${match.text}`)
.join("\n"),
},
],
};
}
);
Small change. Better behavior.
A good MCP server should return useful context, not flood the agent with a wall of text.
Add File Safety
Right now, our server only reads files from NOTES_DIR.
Good.
Keep it that way.
Do not let the agent request arbitrary paths like
../../.ssh/id_rsa
That would be bad.
Very bad.
The kind of bad that makes your laptop feel haunted.
When exposing local file tools, always restrict access to a specific directory.
If you later add support for subfolders, still validate paths.
Example
function safeResolveNotePath(filename: string) {
const resolvedPath = path.resolve(NOTES_DIR, filename);
if (!resolvedPath.startsWith(NOTES_DIR)) {
throw new Error("Invalid file path.");
}
return resolvedPath;
}
Then use this when reading files.
Security is not something you sprinkle on at the end like parsley.
It belongs in the design.
When This Becomes Really Powerful
The real value appears when your Markdown notes reflect your actual workflow.
For example
Project notes
project-decisions.md
api-contracts.md
database-changes.md
Ask
“What decisions did we make about the user profile API?”
Learning notes
typescript.md
react-patterns.md
css-layout.md
Ask
“Find my notes about React performance.”
Debugging notes
bugs.md
incident-notes.md
deployment-errors.md
Ask
“Have I documented this build error before?”
Writing notes
article-ideas.md
README-drafts.md
release-notes.md
Ask
“Turn my rough release notes into a clean changelog.”
This is not about replacing your brain.
It is about giving your brain a better index.
Because memory is unreliable.
Especially developer memory.
One minute you understand the system.
The next minute you are staring at code you wrote three weeks ago thinking:
Who allowed this?
Bad news: you did.
Why This Is Better Than Dumping Notes Into Chat
You might ask
“Why not just paste my Markdown notes into an AI chat?”
You can.
For small things, that is fine.
But it does not scale well.
Problems
- You have to manually copy files.
- You might paste too much.
- You might paste the wrong note.
- You lose structure.
- You repeat the same process every time.
- The AI cannot discover what notes exist.
An MCP server turns your notes into callable tools.
That is the difference.
Instead of you feeding context manually, the agent can request the context it needs.
That is a better workflow.
Less babysitting.
More leverage.
And yes, I know “leverage” is one of those words people overuse on LinkedIn while standing next to a rented plant.
But here it actually fits.
The Developer Habit This Encourages
The hidden benefit is not technical.
It is behavioral.
When your notes become useful, you write better notes.
That is the real win.
If notes disappear into a void, you stop caring.
You write vague nonsense like
Fix auth later
Excellent. Truly a masterpiece.
But if you know your AI agent can search and reuse your notes, you start writing notes like this
# Auth Bug: User Logged Out Too Early
Problem:
Users were logged out after 5 minutes even though refresh tokens were valid.
Cause:
The frontend checked access token expiry using local machine time.
Some users had incorrect system clocks.
Fix:
Use server-issued expiry timestamps and refresh before expiry.
Related files:
- src/auth/session.ts
- src/api/refreshToken.ts
That note is gold.
Not because it is long.
Because it is reusable.
Good notes are not diaries.
Good notes are tools.
What I Would Add Next
If I were improving this project, I would add these features in this order
1. Recursive folder support
Most developers organise notes into folders.
notes/
projects/
bugs/
snippets/
learning/
Add recursive file reading.
2. Markdown frontmatter
Support metadata like
---
tags: [react, performance]
status: active
---
Then expose tools like
search_by_tag
list_projects
3. Snippet extraction
If your notes contain code blocks, extract them.
Example tool
find_code_snippets
Useful prompt
“Find my saved TypeScript snippets related to fetch.”
4. Edit support
Careful with this one.
Reading notes is safe.
Writing notes is more dangerous.
If you add writing tools, start with append-only actions
append_note
create_note
Avoid destructive edits until you trust the workflow.
Never begin with delete.
Delete is where productivity tools become crime scenes.
5. Better ranking
Keyword search works, but ranking makes it nicer.
You can rank matches by
- filename relevance
- heading relevance
- tag match
- number of occurrences
- recent modification time
Again, do this only when needed.
Do not build a spaceship for a bicycle problem.
A Simple Rule for MCP Projects
Here is my rule
Build MCP servers for workflows you repeat.
Not for demos.
Not for hype.
Not because everyone on tech Twitter suddenly discovered protocols.
Ask yourself
“What do I repeatedly ask, search, copy, explain, summarise, or check?”
That is where MCP shines.
Good candidates
- Markdown notes
- project docs
- ticket summaries
- local scripts
- API references
- internal commands
- changelog generation
- debugging history
- test commands
- deployment checklists
Bad candidates
- things you use once
- things that do not need automation
- things that require risky permissions
- things you do not understand manually yet
Automation should come after understanding.
Otherwise you are just making confusion faster.
The Big Takeaway
Your Markdown notes are probably more valuable than you think.
The issue is that they are passive.
They wait for you to remember they exist.
That is a terrible strategy.
A Markdown-powered MCP server changes the relationship.
Your notes become available to your AI agent as structured tools.
The agent can search them, read them, summarise them, and help you reuse your own knowledge.
That is the point.
Not hype.
Not magic.
Not “AI will replace developers by next Tuesday.”
Just a practical workflow improvement.
And honestly, that is the kind of AI tooling I want more of.
Less drama.
More usefulness.
Less “revolutionary platform.”
More “I found the note you forgot existed.”
That is real productivity.
Finally,
Developers do not need more places to store notes.
We need better ways to use the notes we already have.
So build the small MCP server.
Point it at your Markdown folder.
Connect it to your agentic development workflow.
Then ask it what you already know.
You might be surprised how much useful knowledge is sitting inside your own files, waiting to be useful again.
And if you build something better than this, good.
Please do.
Add semantic search. Add tags. Add snippet extraction. Add project-specific workflows.
Then tell the rest of us, because we are all tired of searching for the same command we wrote down six months ago.
If this article gave you an idea, save it for later. Share it with a developer friend who has 47 Markdown files and no system. Or drop a comment and argue with me about whether keyword search is enough.
I am ready.
Probably with a note about it somewhere.
메타데이터
- post_id
- d3ca70fafae4
- slug
- stop-losing-your-dev-notes-build-a-markdown-powered-mcp-server-with-typescript-d3ca70fafae4
- url
- https://medium.com/@julias3/stop-losing-your-dev-notes-build-a-markdown-powered-mcp-server-with-typescript-d3ca70fafae4
- canonical_url
- https://medium.com/@julias3/stop-losing-your-dev-notes-build-a-markdown-powered-mcp-server-with-typescript-d3ca70fafae4
- author_url
- https://medium.com/@julias3
- status
- ok
- fetched_at
- 2026-06-14 11:28:49