Reference: MCP Overview

In preparation for building an Obsidian MCP Server, I’m going to follow the official Anthropic guide to build my first MCP server.

This will be a very simple server with two tools: get_alerts and get_forecast. We will connect it to the Claude for Desktop client. MCP supports 3 Primitives, but we will only be implementing Tools

I’m choosing to build in Python, the SDK can be found here.


Setup Env w/ UV

First, let’s install uv (a python package manager)

curl -LsSf https://astral.sh/uv/install.sh | sh

And then set up our environment

# Create a new directory for our project
uv init weather
cd weather
 
# Create virtual environment and activate it
uv venv
source .venv/bin/activate
 
# Install dependencies
uv add "mcp[cli]" httpx
 
# Create our server file
touch weather.py

FastMCP

For more info on FastMCP

While continuing the guide, I noticed:

# Initialize FastMCP server 
mcp = FastMCP("weather")

The FastMCP class uses Python type hints and docstrings to automatically generate tool definitions, making it easy to create and maintain Model Context Protocol tools.

Helper Functions

We added 2 helper functions for querying and formatting data

async def make_nws_request(url: str) -> dict[str], Any] | None:
def format_alert(feature: dict) -> str:

Tools

Next we implement the logic for the tool using a tool execution handler. We will be implementing 2 tools

@mcp.tool() 
async def get_alerts(state: str) -> str:
@mcp.tool() 
async def get_forecast(latitude: float, longitude: float) -> str:

Run server

Finally you just run the server from main method

def main():
    # Initialize and run the server
    mcp.run(transport='stdio')
 
if __name__ == "__main__":
    main()

And then start the MCP server with

uv run weather.py

Client Access w/ Claude Desktop

First, you need to configure Claude for Desktop. To do this, open the config file at ~/Library/Application Support/Claude/claude_desktop_config.json. Create the file if it doesn’t already exist

Then add your server in the mcpServers key. The MCP UI elements will only show up in Claude for Desktop if at least one server is properly configured.

{
  "mcpServers": {
    "weather": {
      "command": "uv",
      "args": [
        "--directory",
        "/ABSOLUTE/PATH/TO/PARENT/FOLDER/weather",
        "run",
        "weather.py"
      ]
    }
  }
}

This tells Claude for Desktop:

  1. There’s an MCP server named “weather”
  2. To launch it by running uv --directory /ABSOLUTE/PATH/TO/PARENT/FOLDER/weather run weather.py

FAILED! It did not work, here’s what I saw:

You can navigate to your logs (Found here: /Users/naderbaradar/Library/Logs/Claude) to debug. Here’s what I saw:

naderbaradar@Naders-MacBook-Air Claude % ls     
claude.ai-web.log mcp-server-weather.log unknown-window.log
main.log mcp.log window.log
naderbaradar@Naders-MacBook-Air Claude % cat mcp-server-weather.log 
2025-10-24T16:29:46.621Z [weather] [info] Initializing server... { metadata: undefined }
2025-10-24T16:29:46.631Z [weather] [info] Using MCP server command: uv with args and path: {
  metadata: {
    args: [
      '--directory',
      '/ABSOLUTE/PATH/TO/PARENT/FOLDER/weather',
      'run',
      'weather.py',
      [length]: 4
    ],
 
    paths: [
      '/Users/naderbaradar/.nvm/versions/node/v22.13.0/bin',
      '/Users/naderbaradar/.nvm/versions/node/v22.16.0/bin',
      '/usr/local/bin',
      '/opt/homebrew/bin',
      '/usr/bin',
      '/usr/bin',
      '/bin',
      '/usr/sbin',
      '/sbin',
      [length]: 9
    ]
  }
} %o
2025-10-24T16:29:46.632Z [weather] [error] spawn uv ENOENT {
  metadata: {
    context: 'connection',
    stack: 'Error: spawn uv ENOENT\n' +
      '    at ChildProcess._handle.onexit (node:internal/child_process:285:19)\n' +
      '    at onErrorNT (node:internal/child_process:483:16)\n' +
      '    at process.processTicksAndRejections (node:internal/process/task_queues:90:21)'
  }
}

Looks like Claude Desktop does not have access to uv so it can’t run the server. Let’s just give it the full path to uv instead since maybe Claude Desktop doesn’t have access to PATH executables due to being a GUI app.

So we’ll just update the JSON entry for the mcp config like so:

{
  "mcpServers": {
    "weather": {
      "command": "/Users/naderbaradar/.local/bin/uv",
      "args": [
        "--directory",
        "/Users/naderbaradar/development_workspace/mcp/servers/build_server_intro_guide/weather",
        "run",
        "weather.py"
      ]
    }
  }
}

Now let’s try again…

It works!