Build a Knowledge Base MCP Server
Build a local knowledge base MCP server and Claude Code Skill.
Introduction
30 Second Summary
We all have research scattered across browser tabs, sticky notes, and random documents. Imagine if your AI assistant could store, search, and organize all that knowledge for you on command.
In this project, you will build an MCP server that gives Claude direct access to a personal knowledge base stored on your computer. You will then create a Claude Code Skill that teaches Claude a structured research capture workflow.
What You'll Build
A Python MCP server that stores, searches, and organizes notes in a local JSON file, connected to both Claude Desktop and Claude Code, with a Skill that layers workflow intelligence on top.
By the end of this project, you'll have:
- A Python MCP server with 5 tools, 1 resource, and 1 prompt template for managing a local knowledge base.
- Claude Desktop and Claude Code both connected to your server via stdio transport.
- A Claude Code Skill that orchestrates your MCP tools into a repeatable research capture workflow.
- Secret Mission: Add analytics capabilities with a statistics tool and knowledge base health check workflow.
Are there any prerequisites?
You should have basic Python knowledge (functions, decorators, and file I/O) and familiarity with JSON. You will also need Python 3.10+, Claude Desktop, Claude Code, and VS Code installed on your machine.
Set Up Your Project
Before you can build an MCP server, you need a project folder, the right package installed, and a file to write your server code in.
In this step, you'll create your project workspace, install FastMCP (the Python library that makes building MCP servers simple), and set up your server file.
In this step, get ready to:
- Create a project folder and open it in VS Code.
- Install and verify FastMCP.
- Create the server file that will hold your MCP server code.
Create your project folder and open VS Code
Your MCP server needs its own folder to keep the server file and data store organized together.
- Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
- Type Terminal (macOS) or Command Prompt (Windows) and press Enter to open it.
- Move to your Desktop by running this command:
cd ~/Desktop
- Create your project folder and move into it by running these commands:
mkdir knowledge-base-mcp
cd knowledge-base-mcp
- Confirm the folder exists by running:
ls
The folder is empty for now, which is expected. You should see no output or an empty listing.
- Open VS Code by pressing Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
- Type VS Code and press Enter to open it.
- In VS Code, click File in the top menu bar.
- Click Open Folder.
- Navigate to your Desktop and select the knowledge-base-mcp folder, then click Open.
You should see knowledge-base-mcp in the VS Code title bar and an empty Explorer sidebar on the left.
- Open a terminal inside VS Code by clicking Terminal in the top menu bar.
- Click New Terminal.
A terminal panel appears at the bottom of VS Code. This terminal is already pointed at your knowledge-base-mcp folder.
VS Code not opening your folder?
Make sure you selected the knowledge-base-mcp folder itself, not a file inside it. If the Explorer sidebar says "No folder opened," try File > Open Folder again.
Help me open my project folder in VS Code
Install FastMCP
FastMCP is the Python library that handles all the MCP protocol details for you. It lets you define tools, resources, and prompts with simple decorators.
- Install FastMCP by running this command in your VS Code terminal:
pip install fastmcp
What does this command do?
pip install fastmcp downloads and installs the FastMCP package (version 3.2.4 at time of writing) from PyPI. This gives you the FastMCP class and CLI tools you need to build and run MCP servers.
You should see output showing packages being downloaded and installed. The final line should say something like "Successfully installed fastmcp" along with its dependencies.
- Verify the installation by running:
fastmcp version
You should see output displaying the FastMCP version, MCP version, and Python version. This confirms everything is installed correctly.
Seeing 'command not found' or 'not recognized'?
This usually means Python's script directory isn't in your system PATH. Try running python -m fastmcp version instead. If that works, the package is installed but the CLI shortcut isn't accessible.
If pip install fastmcp itself failed, make sure you have Python 3.10 or higher installed. Check with python --version.
Help me fix my FastMCP installation
Create the server file
Your MCP server code will live in a single file called server.py. This is the file Claude Desktop and Claude Code will launch when they connect to your server.
- Create the file by running this command in your VS Code terminal:
touch server.py
- Confirm the file was created by running:
ls
You should see server.py listed in the output. You should also see it appear in the VS Code Explorer sidebar on the left.
Windows alternative
If you're on Windows and touch doesn't work, you can create the file by right-clicking in the VS Code Explorer sidebar and selecting New File, then typing server.py.
Don't see server.py in the sidebar?
Make sure your terminal is pointed at the knowledge-base-mcp folder. The VS Code terminal opened inside this folder should show the folder name in the prompt. If it shows a different directory, run cd ~/Desktop/knowledge-base-mcp and try again.
Help me create server.py in the right location
Your project workspace is ready. Next up, you'll write the MCP server code that gives Claude the ability to store, search, and organize your notes.
Build the MCP Server
Your project folder is ready and FastMCP is installed. Now it's time to build the actual server that Claude will connect to.
An MCP server exposes three types of primitives to AI hosts: tools (actions the AI can take), resources (data it can read), and prompts (templates it can use). You'll build all three in this step, creating a fully functional knowledge base that Claude can interact with.
In this step, get ready to:
- Write the server initialization and data persistence helpers.
- Add five tools for managing notes (add, search, filter, list tags, delete).
- Add a resource and a prompt template to complete the server.
Write the server initialization and helpers
Every MCP server needs a name, a data source, and functions to read/write that data. You'll set all of this up first so the tools you build next have a solid foundation.
- Open server.py in VS Code and add the following imports and server initialization:
import json
import os
from datetime import datetime
from fastmcp import FastMCP
# Initialize the MCP server
mcp = FastMCP(
name="knowledge-base",
instructions="A personal knowledge base server for storing, searching, and organizing research notes. Use the available tools to add, search, and manage notes."
)
# Data file path (stored alongside server.py)
NOTES_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "notes.json")
What does this code do?
The imports bring in everything you need: json for reading/writing data, os for file paths, datetime for timestamps, and FastMCP for the server framework.
The FastMCP() constructor creates your server instance. The name identifies it to Claude, and instructions tells Claude how to use it.
The NOTES_FILE path uses os.path.abspath(__file__) to always locate notes.json next to server.py, regardless of which directory launches the server.
- You won't see a change yet. Next, add the helper functions directly below this code.
- Add the data persistence helpers below the NOTES_FILE line:
def load_notes() -> list[dict]:
"""Load notes from the JSON file."""
if not os.path.exists(NOTES_FILE):
return []
with open(NOTES_FILE, "r", encoding="utf-8") as f:
return json.load(f)
def save_notes(notes: list[dict]) -> None:
"""Save notes to the JSON file."""
with open(NOTES_FILE, "w", encoding="utf-8") as f:
json.dump(notes, f, indent=2, ensure_ascii=False)
What does this code do?
These two functions handle all file I/O for your knowledge base. load_notes() reads notes.json and returns its contents as a Python list. If the file doesn't exist yet, it returns an empty list.
save_notes() writes the updated list back to notes.json with pretty formatting (indent=2) so you can read the file manually if needed.
- Save server.py.
- You won't see a visible change yet. The next chunk adds the tools that use these helpers.
Seeing a red underline on FastMCP?
If VS Code shows an import error on from fastmcp import FastMCP, make sure you installed FastMCP in the same Python environment VS Code is using. Check the Python interpreter in the bottom-left status bar and switch it if needed.
help me fix my FastMCP import error in VS Code
Add the five tool functions
MCP tools are functions that Claude can call to take actions. Each tool gets a @mcp.tool decorator, and FastMCP automatically generates the tool's schema from your function's type annotations and docstring.
- Below the save_notes function, add a comment separator and the add_note tool:
# --- Tools ---
@mcp.tool
def add_note(title: str, content: str, tags: list[str] | None = None) -> dict:
"""Add a new note to the knowledge base.
Use this to save research findings, ideas, or any information worth remembering.
"""
notes = load_notes()
note = {
"id": len(notes) + 1,
"title": title,
"content": content,
"tags": tags or [],
"created_at": datetime.now().isoformat(),
}
notes.append(note)
save_notes(notes)
return note
What does this code do?
The @mcp.tool decorator registers this function as a tool Claude can call. FastMCP reads the function name (add_note), type annotations (title: str), and docstring to build the tool's schema automatically.
Each note gets a simple auto-incrementing ID, a timestamp, and optional tags. The function loads existing notes, appends the new one, saves the file, and returns the created note.
- You won't see a change yet. Add the two search tools directly below add_note:
@mcp.tool
def search_notes(query: str) -> list[dict]:
"""Search notes by keyword in title or content.
Returns all notes where the query appears in the title or content (case-insensitive).
"""
notes = load_notes()
query_lower = query.lower()
results = [
note
for note in notes
if query_lower in note["title"].lower()
or query_lower in note["content"].lower()
]
return results
@mcp.tool
def get_notes_by_tag(tag: str) -> list[dict]:
"""Get all notes with a specific tag.
Use this to retrieve notes grouped by topic or category.
"""
notes = load_notes()
tag_lower = tag.lower()
results = [
note
for note in notes
if tag_lower in [t.lower() for t in note.get("tags", [])]
]
return results
What does this code do?
search_notes() performs a case-insensitive keyword search across note titles and content. It uses a list comprehension to filter matches.
get_notes_by_tag() filters notes by a specific tag. Both tools convert strings to lowercase before comparing, so searches are case-insensitive.
- Add the final two utility tools below get_notes_by_tag:
@mcp.tool
def list_tags() -> list[str]:
"""List all unique tags used across all notes.
Useful for discovering what topics have been captured.
"""
notes = load_notes()
tags = set()
for note in notes:
for tag in note.get("tags", []):
tags.add(tag)
return sorted(tags)
@mcp.tool
def delete_note(note_id: int) -> str:
"""Delete a note by its ID.
Use this to remove outdated or incorrect information.
"""
notes = load_notes()
original_length = len(notes)
notes = [note for note in notes if note["id"] != note_id]
if len(notes) == original_length:
return f"No note found with ID {note_id}"
save_notes(notes)
return f"Note {note_id} deleted successfully"
What does this code do?
list_tags() collects every unique tag from all notes into a set (which removes duplicates) and returns them sorted alphabetically.
delete_note() removes a note by its ID. It compares the list length before and after filtering to detect whether the ID existed. If nothing was removed, it returns an error message instead.
- Save server.py.
- You won't see a visible output yet. The tools will be testable once you connect the server to Claude in the next step.
Seeing a syntax error on the type hint?
The list[str] | None syntax in add_note requires Python 3.10 or newer. Check your version by running python --version in the terminal. If you're on an older version, you'll need to upgrade Python.
help me fix my Python version for MCP development
Add the resource and prompt
Tools let Claude take actions, but MCP also supports resources (data Claude can read) and prompts (templates Claude can use). You'll add one of each to round out the server.
- Below the delete_note function, add a comment separator and the resource:
# --- Resources ---
@mcp.resource("notes://recent")
def get_recent_notes() -> str:
"""Get the 5 most recent notes as a formatted summary."""
notes = load_notes()
recent = notes[-5:] if notes else []
if not recent:
return "No notes in the knowledge base yet."
lines = ["Recent Notes:", ""]
for note in reversed(recent):
tags_str = ", ".join(note["tags"]) if note["tags"] else "none"
lines.append(f"[{note['id']}] {note['title']} (tags: {tags_str})")
lines.append(f" {note['content'][:100]}")
lines.append("")
return "\n".join(lines)
What does this code do?
The @mcp.resource("notes://recent") decorator exposes this function as a readable resource at the URI notes://recent. Unlike tools, resources are data Claude can read without taking an action.
This resource returns the 5 most recent notes formatted as human-readable text. It shows each note's ID, title, tags, and the first 100 characters of content.
- Below the resource, add the prompt template and the server entry point:
# --- Prompts ---
@mcp.prompt
def research_summary(topic: str) -> str:
"""Generate a structured research summary prompt for a given topic.
Creates a prompt that helps synthesize notes on a topic into a structured format.
"""
return f"""Please search the knowledge base for notes related to "{topic}" and create a structured research summary.
Follow this format:
1. Key Findings: List the main points discovered
2. Connections: Note any relationships between different pieces of information
3. Gaps: Identify what information is still missing
4. Next Steps: Suggest what to research next
Use the search_notes and get_notes_by_tag tools to gather relevant information before synthesizing."""
if __name__ == "__main__":
mcp.run()
What does this code do?
The @mcp.prompt decorator registers a reusable prompt template. When Claude uses this prompt, it receives a structured instruction for synthesizing research on a given topic.
The if __name__ == "__main__": mcp.run() entry point starts the server using stdio transport. This is the default for FastMCP and is what Claude Desktop and Claude Code expect when launching the server as a subprocess.
- Save server.py.
- Verify the file has no syntax errors by running this command in your terminal:
python -c "from server import mcp; print(f'Server name: {mcp.name}')"
You should see Server name: knowledge-base printed in the terminal. This confirms Python can import your server without errors.
Seeing a ModuleNotFoundError?
If you see ModuleNotFoundError: No module named 'fastmcp', your terminal is using a different Python than the one where you installed FastMCP. Run pip install fastmcp again in the same terminal to fix it.
If you see a SyntaxError, double-check that your code matches the complete file below. A missing colon, extra indent, or unclosed quote is usually the cause.
help me debug my MCP server import error
✔️ Awesome, I've got everything!
Great. Double check you've saved server.py and move on to connecting it to Claude Desktop.
ⓧ I'd like to double check the full code
Here's the complete server.py file. Compare yours against this:
import json
import os
from datetime import datetime
from fastmcp import FastMCP
# Initialize the MCP server
mcp = FastMCP(
name="knowledge-base",
instructions="A personal knowledge base server for storing, searching, and organizing research notes. Use the available tools to add, search, and manage notes."
)
# Data file path (stored alongside server.py)
NOTES_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "notes.json")
def load_notes() -> list[dict]:
"""Load notes from the JSON file."""
if not os.path.exists(NOTES_FILE):
return []
with open(NOTES_FILE, "r", encoding="utf-8") as f:
return json.load(f)
def save_notes(notes: list[dict]) -> None:
"""Save notes to the JSON file."""
with open(NOTES_FILE, "w", encoding="utf-8") as f:
json.dump(notes, f, indent=2, ensure_ascii=False)
# --- Tools ---
@mcp.tool
def add_note(title: str, content: str, tags: list[str] | None = None) -> dict:
"""Add a new note to the knowledge base.
Use this to save research findings, ideas, or any information worth remembering.
"""
notes = load_notes()
note = {
"id": len(notes) + 1,
"title": title,
"content": content,
"tags": tags or [],
"created_at": datetime.now().isoformat(),
}
notes.append(note)
save_notes(notes)
return note
@mcp.tool
def search_notes(query: str) -> list[dict]:
"""Search notes by keyword in title or content.
Returns all notes where the query appears in the title or content (case-insensitive).
"""
notes = load_notes()
query_lower = query.lower()
results = [
note
for note in notes
if query_lower in note["title"].lower()
or query_lower in note["content"].lower()
]
return results
@mcp.tool
def get_notes_by_tag(tag: str) -> list[dict]:
"""Get all notes with a specific tag.
Use this to retrieve notes grouped by topic or category.
"""
notes = load_notes()
tag_lower = tag.lower()
results = [
note
for note in notes
if tag_lower in [t.lower() for t in note.get("tags", [])]
]
return results
@mcp.tool
def list_tags() -> list[str]:
"""List all unique tags used across all notes.
Useful for discovering what topics have been captured.
"""
notes = load_notes()
tags = set()
for note in notes:
for tag in note.get("tags", []):
tags.add(tag)
return sorted(tags)
@mcp.tool
def delete_note(note_id: int) -> str:
"""Delete a note by its ID.
Use this to remove outdated or incorrect information.
"""
notes = load_notes()
original_length = len(notes)
notes = [note for note in notes if note["id"] != note_id]
if len(notes) == original_length:
return f"No note found with ID {note_id}"
save_notes(notes)
return f"Note {note_id} deleted successfully"
# --- Resources ---
@mcp.resource("notes://recent")
def get_recent_notes() -> str:
"""Get the 5 most recent notes as a formatted summary."""
notes = load_notes()
recent = notes[-5:] if notes else []
if not recent:
return "No notes in the knowledge base yet."
lines = ["Recent Notes:", ""]
for note in reversed(recent):
tags_str = ", ".join(note["tags"]) if note["tags"] else "none"
lines.append(f"[{note['id']}] {note['title']} (tags: {tags_str})")
lines.append(f" {note['content'][:100]}")
lines.append("")
return "\n".join(lines)
# --- Prompts ---
@mcp.prompt
def research_summary(topic: str) -> str:
"""Generate a structured research summary prompt for a given topic.
Creates a prompt that helps synthesize notes on a topic into a structured format.
"""
return f"""Please search the knowledge base for notes related to "{topic}" and create a structured research summary.
Follow this format:
1. Key Findings: List the main points discovered
2. Connections: Note any relationships between different pieces of information
3. Gaps: Identify what information is still missing
4. Next Steps: Suggest what to research next
Use the search_notes and get_notes_by_tag tools to gather relevant information before synthesizing."""
if __name__ == "__main__":
mcp.run()
Your MCP server is fully built. It has five tools, one resource, and one prompt template ready for Claude to use. Next up, you'll connect this server to Claude Desktop so you can test it in a real conversation.
Connect to Claude Desktop
Your MCP server is built and ready to run. The next step is giving Claude Desktop a way to launch it.
Claude Desktop connects to MCP servers through a configuration file called claude_desktop_config.json. You will add your server's details there so Claude Desktop can start it automatically using stdio transport.
In this step, get ready to:
- Find your full Python executable path and server.py path.
- Configure Claude Desktop to connect to your MCP server.
- Verify the connection and test a tool from Claude Desktop.
Find your Python and server paths
Claude Desktop needs two pieces of information to launch your server: which Python executable to run, and where your server.py file lives. You need the full absolute paths for both.
Find your Python executable path by running this command in your VS Code terminal:
Windows
- Get the full path to your Python executable by running:
where python
You should see a full path like C:\Users\username\AppData\Local\Programs\Python\Python312\python.exe. Copy this path and save it somewhere handy.
macOS
- Get the full path to your Python executable by running:
which python3
You should see a full path like /usr/local/bin/python3 or /opt/homebrew/bin/python3. Copy this path and save it somewhere handy.
- Now find the full path to your server.py file by running:
Windows
cd $HOME\Desktop\knowledge-base-mcp
echo $PWD\server.py
macOS
cd ~/Desktop/knowledge-base-mcp
echo $PWD/server.py
- Copy the full path to server.py that appears in the output. You will need both paths in the next substep.
Keep both paths accessible
Paste both paths into a temporary text file or sticky note. You will copy them into a JSON config file momentarily.
Edit the Claude Desktop configuration
Claude Desktop reads a JSON configuration file to know which MCP servers to launch. You will add your knowledge-base server entry to this file.
- Open Claude Desktop.
- Click the hamburger menu (three horizontal lines) in the top left corner.
- Select Settings.
- Click Developer in the left sidebar.
- Click Edit Config. This opens the claude_desktop_config.json file in your default text editor.
Where does this file live?
On Windows, the config file is located at %APPDATA%\Claude\claude_desktop_config.json. On macOS, it is at ~/Library/Application Support/Claude/claude_desktop_config.json.
The Edit Config button opens this file for you automatically, so you do not need to navigate there manually.
- Replace the entire contents of the file with the following configuration:
Windows
{
"mcpServers": {
"knowledge-base": {
"command": "[[PYTHON_PATH=\"C:\\\\Users\\\\username\\\\AppData\\\\Local\\\\Programs\\\\Python\\\\Python312\\\\python.exe\"]]",
"args": ["[[SERVER_PATH=\"C:\\\\Users\\\\username\\\\Desktop\\\\knowledge-base-mcp\\\\server.py\"]]"] }
}
}
Why double backslashes?
JSON requires backslashes to be escaped. Every single backslash in a Windows path becomes a double backslash in JSON. For example, C:\Users\ becomes C:\\Users\\ in the config file.
macOS
{
"mcpServers": {
"knowledge-base": {
"command": "[[PYTHON_PATH=\"/usr/local/bin/python3\"]]",
"args": ["[[SERVER_PATH=\"/Users/username/Desktop/knowledge-base-mcp/server.py\"]]"] }
}
}
What does this configuration do?
The mcpServers object lists all MCP servers Claude Desktop should launch. Each key (like knowledge-base) is the server name.
The command field tells Claude Desktop which executable to run. The args array passes your server.py file as the script argument. Claude Desktop launches this as a subprocess and communicates via stdio.
- Save the file and close the text editor.
Already have other MCP servers configured?
If your claude_desktop_config.json already has entries in the mcpServers object, add the knowledge-base key alongside the existing entries. Do not replace the entire file.
help me merge my existing Claude Desktop MCP config with the new knowledge-base server entry
Restart Claude Desktop and verify the connection
Claude Desktop only reads its configuration file at startup. You need to fully restart the app for your changes to take effect.
- Close Claude Desktop completely (quit the app, not just close the window).
- Reopen Claude Desktop.
- Look at the chat input area at the bottom. You should see a hammer icon to the right of the text field. This is the MCP tools icon.
- Click the hammer icon to expand the tools list.
You should see your five knowledge-base tools listed: add_note, search_notes, get_notes_by_tag, list_tags, and delete_note.
✔️ I see the hammer icon and my tools
Your MCP server is connected. Time to test it with a real request.
- Type this message into the Claude Desktop chat:
Add a note titled "MCP Server Test" with the content "My knowledge base MCP server is connected and working" and tag it with "test"
- Claude will ask for permission to use the add_note tool. Click Allow to approve it.
Claude should confirm the note was added and show the note details including an ID, title, content, tags, and timestamp.
ⓧ I don't see the hammer icon
The MCP server failed to connect. Here are the most common causes.
- Check that you saved claude_desktop_config.json before restarting Claude Desktop.
- Verify your JSON syntax is valid. A missing comma, bracket, or quote will break the entire file.
- Confirm the Python path points to an actual executable. Re-run where python (Windows) or which python3 (macOS) and compare the output to what you pasted.
- On Windows, ensure all backslashes in the paths are doubled. A single backslash will cause a JSON parse error.
Windows ENOENT error?
If you see an ENOENT error in the Claude Desktop logs, add an env key to your server configuration:
{
"mcpServers": {
"knowledge-base": {
"command": "C:\\Users\\username\\...\\python.exe",
"args": ["C:\\Users\\username\\...\\server.py"],
"env": {
"APPDATA": "C:\\Users\\username\\AppData\\Roaming"
}
}
}
}
Replace username with your actual Windows username. This is a known Windows-specific issue.
help me debug why my MCP server is not showing up in Claude Desktop
What just happened?
When you restarted Claude Desktop, it read your config file and launched python server.py as a subprocess. Your server is now running in the background, communicating with Claude over stdio.
Every time you send a message that needs a tool, Claude sends a JSON-RPC request to your server. The server processes it, returns the result, and Claude incorporates it into the response.
Your knowledge base server is now live inside Claude Desktop. Next, you will connect the same server to Claude Code so you can use it from the terminal too.
Connect to Claude Code
Your MCP server is live in Claude Desktop. You confirmed it connects, tools appear, and notes get saved to notes.json.
Now you'll connect the same server to Claude Code. Claude Code is a terminal-based AI assistant that can also use MCP servers. Connecting here means your knowledge base becomes accessible from the command line, which is where you'll build the Skill workflow in the next step.
In this step, get ready to:
- Register your MCP server in Claude Code with user scope.
- Verify the connection and test your knowledge base tools from Claude Code.
Register the MCP server in Claude Code
Claude Code has its own MCP registration system, separate from Claude Desktop's config file. You register servers using the claude mcp add command directly in your terminal.
- Open a terminal in VS Code (or use any terminal) and make sure you're in your knowledge-base-mcp project folder.
- Register your MCP server by running this command:
claude mcp add --transport stdio --scope user knowledge-base -- python server.py
What does this command do?
- claude mcp add registers a new MCP server with Claude Code.
- --transport stdio tells Claude Code to launch the server as a subprocess and communicate via standard input/output (the same transport Claude Desktop uses).
- --scope user makes this server available across all your projects, not just the current folder.
- knowledge-base is the name Claude Code uses to identify your server.
- Everything after -- is the command Claude Code runs to start the server: python server.py.
Why user scope?
Claude Code has three MCP scopes: local (only the current project), project (shared via a committed .mcp.json file), and user (available everywhere).
A knowledge base is useful regardless of which project you're working in. The user scope ensures Claude Code can always access your notes.
- You should see a confirmation message indicating the server was added successfully.
Command not recognized?
- Make sure Claude Code is installed. Run claude --version to check.
- Ensure you're running the command from inside your knowledge-base-mcp folder so Claude Code can locate server.py.
Help me troubleshoot the claude mcp add command
Verify the connection and test your tools
With the server registered, you can start Claude Code and confirm it sees your knowledge base tools.
- Start Claude Code by running this command:
claude
- Once Claude Code starts, check your MCP connections by typing:
/mcp
- You should see knowledge-base listed as a connected server with its tools (add_note, search_notes, get_notes_by_tag, list_tags, delete_note) displayed.
Server not showing up in /mcp?
- Exit Claude Code (type /exit) and re-run the claude mcp add command from the previous substep. Make sure you're inside the knowledge-base-mcp folder.
- Confirm Python can run your server directly by running python server.py in your terminal. If you see errors, fix them before retrying.
Help me debug my Claude Code MCP connection
Now test the full round trip. You'll add a note through Claude Code and then search for it.
- In Claude Code, type a message like: "Add a note titled 'Claude Code test' with content 'Testing the knowledge base connection from Claude Code' and tag it with 'testing'."
- Claude Code will ask permission to use the add_note tool. Allow it.
- You should see a confirmation showing the note was created with an ID, title, content, tags, and timestamp.
- Now test search by typing: "Search my notes for 'test'."
- Allow the search_notes tool when prompted. You should see both the note you just added and any test notes from Claude Desktop.
Both clients share the same data
Claude Desktop and Claude Code both connect to the same server.py, which reads and writes to the same notes.json file. Notes you added in Claude Desktop in the previous step are visible here.
Your MCP server is now accessible from both Claude Desktop and Claude Code. Next up, you'll create a Claude Code Skill that teaches Claude a structured workflow for using these tools together.
Create the Claude Code Skill
Your MCP server is now connected to both Claude Desktop and Claude Code. Claude can add notes, search them, and organize your knowledge base. But right now, Claude uses these tools however it sees fit.
A Claude Code Skill teaches Claude a consistent methodology for using those tools. Instead of improvising each time, Claude follows a defined workflow. You will create a Skill that turns raw MCP tool access into a structured research capture process.
In this step, get ready to:
- Create the skill directory structure inside your project folder.
- Write the SKILL.md file with workflow instructions for Claude.
- Test the skill by asking Claude Code to research a topic.
Create the skill directory structure
Claude Code discovers Skills by looking for SKILL.md files inside the .claude/skills/ directory in your project. The folder name becomes the skill's identity.
- Make sure your terminal is in your knowledge-base-mcp folder. Create the skill directory structure by running this command:
mkdir -p .claude/skills/research-capture
- Create the SKILL.md file inside that directory by running:
touch .claude/skills/research-capture/SKILL.md
- Confirm the file exists by running:
ls .claude/skills/research-capture/
You should see SKILL.md listed in the output.
Don't see SKILL.md?
Check that you ran both commands from inside the knowledge-base-mcp folder, not a parent or child directory. Run pwd to confirm your current location.
On Windows, if mkdir -p does not work, use PowerShell's New-Item -ItemType Directory -Path .claude/skills/research-capture -Force instead.
help me create the .claude/skills/research-capture directory
Write the research capture workflow
The SKILL.md file has two parts: YAML frontmatter that tells Claude when to activate the skill, and a markdown body that defines the workflow Claude should follow.
- Open .claude/skills/research-capture/SKILL.md in VS Code by clicking on it in the **Explorer** sidebar.
- Copy and paste the following content into the file:
---
description: Capture and organize research using the knowledge base MCP server. Use this when the user asks to research a topic, save findings, review captured notes, or organize information in the knowledge base.
---
# Research Capture Workflow
When the user asks you to research, capture, or organize information, follow this workflow using the knowledge-base MCP tools.
## Capturing New Research
1. Break the information into individual, focused notes
2. Use `add_note` for each distinct finding with:
- A clear, descriptive title (e.g., "MCP uses JSON-RPC 2.0 for message encoding")
- Detailed content explaining the finding
- Relevant tags for categorization
3. After saving, confirm what was captured with note IDs
## Reviewing Existing Research
1. Start by running `list_tags` to see what topics exist
2. Use `get_notes_by_tag` to pull notes on the requested topic
3. Use `search_notes` for keyword-based lookups when the exact tag is unknown
4. Present findings in a structured format
## Research Summary Format
When summarizing research on a topic, structure your response as:
### Key Findings
- Bullet points of main discoveries
### Connections
- How different notes relate to each other
### Knowledge Gaps
- What is missing or needs further research
### Suggested Next Steps
- What to research or capture next
## Tag Conventions
- Use lowercase with hyphens: `machine-learning` not `Machine Learning`
- Use broad category tags: `python`, `web-dev`, `ai`, `devops`
- Add specificity tags: `fastapi`, `react-hooks`, `prompt-engineering`
- Always include at least one broad and one specific tag per note
## Cleanup
- If the user says information is outdated, use `delete_note` with the note ID
- Before deleting, confirm the note title and ID with the user
What does this file do?
- The description in the frontmatter controls when Claude auto-loads this skill. When your prompt matches phrases like "research a topic" or "save findings," Claude reads the full body.
- The Capturing New Research section teaches Claude to break information into focused notes instead of dumping everything in one entry.
- The Tag Conventions section ensures consistent tagging so searches actually return relevant results.
- The Cleanup section prevents accidental deletions by requiring Claude to confirm before removing notes.
Why a Skill on top of MCP tools?
MCP gives Claude the ability to call your tools. The Skill gives Claude a methodology for how to use them well. Without the Skill, Claude might dump all research into a single note or forget to tag entries. With it, Claude follows a repeatable process every time.
- Save SKILL.md by pressing Cmd+S (macOS) or Ctrl+S (Windows).
Seeing formatting issues?
Make sure the file starts with exactly three dashes (---) on the first line, followed by the description: field, and closed with another three dashes. No blank lines before the opening dashes.
help me fix the YAML frontmatter in my SKILL.md file
✔️ Awesome, I've got everything!
Great. Double check that you have saved your file before moving on.
ⓧ I'd like to double check the full code
Here is the complete SKILL.md file for comparison:
---
description: Capture and organize research using the knowledge base MCP server. Use this when the user asks to research a topic, save findings, review captured notes, or organize information in the knowledge base.
---
# Research Capture Workflow
When the user asks you to research, capture, or organize information, follow this workflow using the knowledge-base MCP tools.
## Capturing New Research
1. Break the information into individual, focused notes
2. Use `add_note` for each distinct finding with:
- A clear, descriptive title (e.g., "MCP uses JSON-RPC 2.0 for message encoding")
- Detailed content explaining the finding
- Relevant tags for categorization
3. After saving, confirm what was captured with note IDs
## Reviewing Existing Research
1. Start by running `list_tags` to see what topics exist
2. Use `get_notes_by_tag` to pull notes on the requested topic
3. Use `search_notes` for keyword-based lookups when the exact tag is unknown
4. Present findings in a structured format
## Research Summary Format
When summarizing research on a topic, structure your response as:
### Key Findings
- Bullet points of main discoveries
### Connections
- How different notes relate to each other
### Knowledge Gaps
- What is missing or needs further research
### Suggested Next Steps
- What to research or capture next
## Tag Conventions
- Use lowercase with hyphens: `machine-learning` not `Machine Learning`
- Use broad category tags: `python`, `web-dev`, `ai`, `devops`
- Add specificity tags: `fastapi`, `react-hooks`, `prompt-engineering`
- Always include at least one broad and one specific tag per note
## Cleanup
- If the user says information is outdated, use `delete_note` with the note ID
- Before deleting, confirm the note title and ID with the user
Test the skill in Claude Code
Claude Code auto-discovers skills based on the description field in the frontmatter. When your prompt matches those trigger phrases, Claude loads the full skill body and follows the workflow.
- Make sure your terminal is inside the knowledge-base-mcp folder (the folder containing .claude/).
- Start Claude Code by running:
claude
- Once Claude Code is running, type this prompt:
Research and capture notes about MCP architecture
Watch how Claude responds. Because your prompt matches the skill's description ("research a topic" and "save findings"), Claude loads the workflow and follows it.
What should I see?
Claude should break the topic into multiple focused notes, call add_note for each one, assign both broad and specific tags (following the lowercase-hyphen convention), and confirm what was captured with note IDs.
This structured behavior comes from the Skill. Without it, Claude would likely dump everything into a single note or skip tagging entirely.
- After Claude finishes, verify the notes were saved by typing:
List all tags in my knowledge base
You should see Claude call the list_tags tool and return the tags it just used when capturing notes about MCP architecture.
Claude isn't following the workflow?
Make sure you started Claude Code from inside the knowledge-base-mcp folder. Skills are discovered relative to the working directory. If you started Claude Code from a different folder, it will not find the .claude/skills/ directory.
Also check that your SKILL.md starts with the YAML frontmatter (the --- delimiters). Without valid frontmatter, Claude Code cannot read the description and will not activate the skill.
help me figure out why Claude Code isn't using my skill
Secret mission
Add a Knowledge Base Health Check
Your knowledge base works, but how healthy is it? Extend your MCP server with a statistics tool and teach your Skill to run a knowledge base health check that identifies coverage gaps and underused tags.
Clean Up Your Resources
Clean Up Your Resources
This project runs entirely locally, so there are no ongoing costs. Decide whether to keep your resources running, pause them to come back later, or delete them entirely.
Resources you used:
- The knowledge-base-mcp project folder containing server.py and notes.json.
- Claude Desktop MCP configuration in claude_desktop_config.json.
- Claude Code MCP server registered at user scope.
- The .claude/skills/research-capture/SKILL.md skill file.
- The fastmcp Python package installed via pip.
Keep everything running
No action needed. Choose this if you're still actively building or want the knowledge base available in Claude Desktop and Claude Code.
- Your MCP server stays registered in both Claude Desktop and Claude Code.
- Your notes persist in notes.json and remain accessible.
- The Skill continues to work in any project opened from the knowledge-base-mcp folder.
Pause - I'll come back to this later
Remove the MCP server from active use while keeping all files intact so you can reconnect later.
- In Claude Desktop, open Settings > Developer > Edit Config. Remove the knowledge-base entry from the mcpServers object, then save and restart Claude Desktop.
- In your terminal, run claude mcp remove knowledge-base to unregister the server from Claude Code.
- Keep the knowledge-base-mcp folder on your Desktop. When you want to reconnect, re-add the config entries from Step 3 and Step 4.
Delete - I don't want to use this again
Remove all project resources and start fresh. This deletes your notes and server code permanently.
- In Claude Desktop, open Settings > Developer > Edit Config. Remove the knowledge-base entry from the mcpServers object, then save and restart Claude Desktop.
- In your terminal, run claude mcp remove knowledge-base to unregister the server from Claude Code.
- Delete the entire knowledge-base-mcp folder from your Desktop.
- Optionally, uninstall the package by running pip uninstall fastmcp in your terminal.
Nice Work!
Nice Work!
You just built a personal knowledge base system that bridges MCP and Claude Skills. You now have a fully functional local knowledge base accessible from both Claude Desktop and Claude Code, powered by a custom MCP server you wrote from scratch.
Here's what you learned:
- Built a Python MCP server with 5 tools, 1 resource, and 1 prompt template using FastMCP.
- Connected the server to both Claude Desktop and Claude Code using stdio transport for seamless access across environments.
- Created a Claude Code Skill that orchestrates MCP tools into a structured research capture workflow.
- Added analytics capabilities with a statistics tool and health check workflow in the Secret Mission.
Ready to quiz yourself?