MCP实战:从零开始写基于 Python 的 MCP 服务(附源码)

项目源码地址:obsidian-mcp-python

介绍

现在mcp相关信息满天飞,但大多都在搞理论。不如动手搞个mcp服务,让它跑起来才能真正感受到mcp是个啥。另外,目前的免费教程大多数是用 js/ts 写的,python版本还真是少之又少。刚好自己第一个mcp服务是python写的,虽然小但也方便理解。重点即便今天搜了mcp python相关资料,依旧很少,那就水一篇吧。后续会系统写下如何基于fastMCP开发MCP服务。

开发

MCP 前置知识

关于什么是mcp,太多人讲了,可以自行查下。我这里也推荐几篇,其中的实战是基于js的,不过不妨碍整理理解mcp的核心概念和能力

Python 开发工具

如果是第一次使用python的话,工具篇可以直接选uv,类似JS生态中的npm,关于 UV,可以看下我写的这个:Python管理工具UV

python中有 虚拟环境 的概念对于前端同学可能比较陌生,简单讲,虚拟环境 相当于nvm + npm 管理下的前端项目,一个python项目跑在一个指定的虚拟环境下,有指定的python版本和依赖库,这样可以使得不同项目有自己专门的python版本和依赖库版本。

MCP 服务开发

先简单介绍下该mcp的功能:可以操作obsidian中的markdown文件,如读取文件数量、返回文件内容、新建文件。

下面是具体开发过程:

  • pip install uv 安装uv工具
  • uv init obsidian-mcp-python 创建项目目录
  • cd obsidian-mcp-python
  • uv venv 新建虚拟环境
  • source .venv/bin/activate 开启虚拟环境
  • uv add "mcp[cli]" 安装指定依赖,mcp这个包提供了fastMCP FastMCP是官方推荐的一个基于python的mcp开发框架,使用类似nestjs中装饰器的风格提供tools、resources、prompt等能力,不过我们这个例子中只设计tools的使用。
  • 新建一个main.py文件,所有的功能都在里面了。
python 复制代码
import os
import glob
from mcp.server.fastmcp import FastMCP
mcp = FastMCP()

# 从环境变量中获取 Obsidian 根目录路径,默认为当前目录
OBSIDIAN_ROOT = os.environ.get("OBSIDIAN_PATH", ".")

@mcp.tool()
def count_markdown_files():
	"""获取 Obsidian 库中所有 Markdown 文件的数量"""
	md_files = glob.glob(os.path.join(OBSIDIAN_ROOT, "**/*.md"), recursive=True)
	return len(md_files)

@mcp.tool()
def get_all_markdown_contents():
	"""获取 Obsidian 库中所有 Markdown 文件的内容"""
	md_files = glob.glob(os.path.join(OBSIDIAN_ROOT, "**/*.md"), recursive=True)
	result = []
	for file_path in md_files:
		try:
			with open(file_path, 'r', encoding='utf-8') as f:
			content = f.read()
			# 获取相对路径作为文件标识符
			relative_path = os.path.relpath(file_path, OBSIDIAN_ROOT)
			result.append({
				"file": relative_path,
				"content": content
			})
		except Exception as e:
			result.append({
				"file": os.path.relpath(file_path, OBSIDIAN_ROOT),
				"error": str(e)
			})
	return result


@mcp.tool()
def create_markdown_file(filename, content=""):
	"""在 Obsidian 库中创建一个新的 Markdown 文件
	
	Args:
		filename: Markdown 文件名 (不需要 .md 后缀,会自动添加)
		content: 文件的初始内容 (可选)
	"""

	# 确保有 .md 后缀
	if not filename.endswith('.md'):
		filename += '.md'
	
	# 创建完整路径
	file_path = os.path.join(OBSIDIAN_ROOT, filename)
	# 检查文件是否已存在
	if os.path.exists(file_path):
		return {"success": False, "error": f"文件 {filename} 已存在"}
	
	try:
		# 确保目录存在
		os.makedirs(os.path.dirname(file_path), exist_ok=True)
		# 创建文件
		with open(file_path, 'w', encoding='utf-8') as f:
		f.write(content)
		return {"success": True, "path": os.path.relpath(file_path, OBSIDIAN_ROOT)}

	except Exception as e:
		return {"success": False, "error": str(e)}

if __name__ == "__main__":
	print(f"Obsidian MCP 服务已启动,使用路径: {OBSIDIAN_ROOT}")
	mcp.run(transport="stdio")

现在我们借助 @mcp.tool() 写了3个工具函数,这几个工具是让llm去调的,比如在cursor的ai会话中,当你让他操作obsidian时,他会自动找到可用的mcp,判断应该调哪个工具。

接下来就是写mcp 的配置json,我就把我自己配的原样输出了(更便于理解),地址部分改成自己本地地址就行

json 复制代码
{
	"mcpServers": {
		"obsidian-mcp-python": {
			"command": "uv",
			"args": [
				"--directory",
				"/Users/ran/Code/Github/zhangran/obsidian-mcp-python",
				"run",
				"main.py"
			],
			"env": {
				"OBSIDIAN_PATH": "/Users/ran/Documents/ObsidianNotes"
			}
		}
	}
}

使用

上面的配置就是适合cursor的,直接copy下贴到cursor的mcp配置里就行。

简单贴下效果:

可以看出能调用查询和新建工具了:count_markdown_files create_markdown_file

然后再打开我的obsidian,发现文章已经写进来了,如下图:

结束~

参考的几个mcp相关资料 FastMCP框架 FastMCP 官网Welcome to FastMCP 2.0 FastMCP脚手架 fastmcp-boilerplate: 简单的FastMCP脚手架基于Typescript FastMCP Typescript版 mcp-obsidian: 一个基于restful的obsidian mcp server

相关推荐
安冬的码畜日常14 分钟前
【AI 加持下的 Python 编程实战 2_13】第九章:繁琐任务的自动化(中)——自动批量合并 PDF 文档
人工智能·python·自动化·ai编程·ai辅助编程
@十八子德月生38 分钟前
第三阶段—8天Python从入门到精通【itheima】-143节(pyspark实战——数据计算——flatmap方法)
大数据·开发语言·python·数据分析·pyspark·好好学习,天天向上·question answer
孫治AllenSun41 分钟前
【Java】使用模板方法模式设计EasyExcel批量导入导出
java·python·模板方法模式
爱编码的程序员42 分钟前
python 处理json、excel、然后将内容转化为DSL语句,适用于数据处理(实用版)
人工智能·python·ai·json·excel·数据处理·dsl
ashcn200142 分钟前
vim 组件 使用pysocket进行sock连接
python·vim·excel
Stitch .1 小时前
AWS开源 Agent 框架 Strands Agents 速成班(实验手册)
jupyter·云计算·aws·亚马逊·vpc·智能体·mcp
摘星编程1 小时前
MCP安全机制深度剖析:权限控制与数据保护最佳实践
jwt令牌·多因素认证·mcp·安全机制·tls1.3
王国强20091 小时前
Pydantic 深度指南:Python 类型安全与数据建模的现代解决方案
python
天宁2 小时前
TCP粘包问题解决方案
python
站大爷IP2 小时前
Python循环嵌套:从入门到实战的完整指南
python