通过MCP(模型上下文协议)对Elixir项目进行语义代码搜索和分析
Codicil是一个Elixir库,它为AI编码助手提供了对代码库的深度语义理解。你可以用自然语言提问,根据行为查找函数,追踪依赖关系,并理解代码之间的关系——这一切都通过模型上下文协议实现。
想象一下向你的AI编码助手提问:
Codicil通过以下方式使这些成为可能:
这些步骤适用于Phoenix和非Phoenix项目。
在你的mix.exs中添加Codicil:
def deps do
[
# ... 你现有的依赖项
{:codicil, "~> 0.4", only: [:dev, :test]}
]
end
然后安装:
mix deps.get
Codicil使用SQLite存储索引代码。初始化它:
mix codicil.setup
创建或编辑你的.env文件(或在shell中设置):
# 必需:选择你的LLM提供商
export CODICIL_LLM_PROVIDER=openai # 或:anthropic, cohere, google, grok
# 必需:为选定的提供商添加你的API密钥
export OPENAI_API_KEY=your_key_here
# 或
export ANTHROPIC_API_KEY=your_key_here
# 或
export COHERE_API_KEY=your_key_here
# 或
export GOOGLE_API_KEY=your_key_here
# 可选:嵌入式提供者(默认为openai)
# 如果你想使用Voyage AI作为嵌入式:
# export CODICIL_EMBEDDING_PROVIDER=voyage
# export VOYAGE_API_KEY=your_voyage_key_here
加载环境变量:
source .env
编辑你的mix.exs以在开发环境中启用Codicil的跟踪器:
def project do
[
app: :my_app,
version: "0.1.0",
elixir: "~> 1.14",
# 在开发和测试中启用Codicil跟踪器
elixirc_options: elixirc_options(Mix.env()),
deps: deps()
]
end
defp elixirc_options(:prod), do: []
defp elixirc_options(_env), do: [tracers: [Codicil.Tracer]]
这确保了跟踪器仅在开发和测试中运行,而不是在生产中。
如果你使用的是Phoenix,请继续执行以下步骤。
在你的Phoenix路由器中添加Codicil MCP端点。编辑lib/my_app_web/router.ex:
defmodule MyAppWeb.Endpoint do
# ... 你的端点内容
# Codicil MCP端点(只有当Codicil被加载时可用)
if Code.ensure_loaded?(Codicil) do
plug Codicil.Plug
end
end
启动你的Phoenix服务器:
mix phx.server
MCP服务器将在http://localhost:4000/codicil/mcp(或你配置的Phoenix端口)上可用。
编译你的项目以触发索引:
# 在另一个终端
mix compile --force
查看Phoenix日志以查看正在被索引的函数!
如果你不使用Phoenix,请继续执行以下步骤。
添加Bandit以提供MCP端点。编辑mix.exs:
def deps do
[
# ... 你现有的依赖项
{:codicil, "~> 0.4", only: [:dev, :test]},
{:bandit, "~> 1.6", only: :dev} # 用于MCP的HTTP服务器
]
end
安装:
mix deps.get
添加一个Mix别名以启动MCP服务器。编辑你的mix.exs:
def project do
[
# ... 其他配置
aliases: aliases()
]
end
defp aliases do
[
# ... 你现有的别名(如果有)
codicil: "run --no-halt -e 'Bandit.start_link(plug: Codicil.Plug, port: 4700)'"
]
end
如果你想同时运行多个MCP服务器(例如Codicil + Tidewave),可以在单个Mix别名中组合它们:
defp aliases do
[
# ... 你现有的别名(如果有)
mcp: "run --no-halt -e 'Agent.start(fn -> Bandit.start_link(plug: Codicil.Plug, port: 4700); Bandit.start_link(plug: Tidewave, port: 4000) end)'"
]
end
这将在同一个代理中启动两个MCP服务器:
http://localhost:4700/codicil/mcphttp://localhost:4700/tidewave/mcp注意:每个MCP服务器需要自己的端口。根据需要调整端口号。
启动MCP服务器:
mix codicil
MCP服务器将在http://localhost:4700/codicil/mcp上启动。
在第二个终端中编译你的项目以触发索引:
mix compile --force
在终端1中查看日志以查看正在被索引的函数!
一旦Codicil运行起来,配置你的AI助手(如Claude Desktop、Cline等)以连接到MCP服务器:
Phoenix项目:
http://localhost:4000/codicil/mcp(使用你的Phoenix端口)非Phoenix项目:
http://localhost:4700/codicil/mcp现在可以使用以下MCP工具:
find_similar_functions - 通过描述进行语义搜索list_function_callers - 查找调用特定函数的内容(调试和重构时有用)list_function_callees - 查找函数调用的内容(调试执行路径和重构时有用)list_module_dependencies - 分析模块依赖关系get_function_source_code - 获取带有上下文的完整函数源代码(代替grep)Phoenix项目:
# 检查MCP端点是否响应
curl http://localhost:4000/codicil/mcp
# 检查已索引的函数
sqlite3 deps/codicil/priv/codicil.db "SELECT module, name, arity FROM functions LIMIT 10;"
非Phoenix项目:
# 检查MCP服务器是否响应
curl http://localhost:4700/codicil/mcp
# 检查已索引的函数
sqlite3 deps/codicil/priv/codicil.db "SELECT module, name, arity FROM functions LIMIT 10;"
在编译期间,Codicil:
Codicil.Tracer捕获模块/函数定义。然后,你的AI助手通过MCP工具查询这些数据。
编译 → 跟踪器 → ModuleTracer GenServer → 速率限制器 → LLM/嵌入
↓
SQLite数据库
(函数、模块,
调用图、向量)
↓
MCP工具
↓
AI助手
技术栈:
sqlite-vec扩展用于向量搜索通过向量相似性搜索查找具有语义描述的函数:
{
"description": "验证电子邮件地址的函数",
"limit": 10
}
查找调用特定函数的内容(调试和重构影响分析时有用):
{
"moduleName": "MyApp.User",
"functionName": "create",
"arity": 1
}
查找函数调用的内容(调试执行路径和重构时有用):
{
"moduleName": "MyApp.Orders",
"functionName": "process",
"arity": 1
}
分析模块依赖关系(导入、别名、使用、需要以及运行时调用):
{
"moduleName": "MyApp.Accounts"
}
获取带有模块指令和位置的完整函数源代码。使用此方法代替grep或文件读取以获得完整上下文。
{
"moduleName": "MyApp.User",
"functionName": "create",
"arity": 1
}
你可以在编译时覆盖默认的MCP工具描述,以优化你的AI助手如何使用这些工具。不同的模型(Claude、GPT-4、Codex等)可能有不同的偏好,因此自定义描述可以提高工具选择的准确性并减少不必要的工具调用。
要查看当前默认描述,请使用IEx:
iex -S mix
iex> Codicil.MCP.Tool.get_description(Codicil.MCP.Tools.FindSimilarFunctions)
要自定义描述,请在你的config/config.exs(或config/dev.exs)中添加:
config :codicil, Codicil.MCP.Tools.FindSimilarFunctions, """
通过语义描述查找函数。当你按行为而非名称搜索功能时使用此工具。返回排名结果及代码片段。
"""
# 其他可配置的工具模块:
# - Codicil.MCP.Tools.ListFunctionCallers
# - Codicil.MCP.Tools.ListFunctionCallees
# - Codicil.MCP.Tools.ListModuleDependencies
# - Codicil.MCP.Tools.GetFunctionSourceCode
更改工具描述后,重新编译:
mix clean
mix compile
覆盖默认模型:
export CODICIL_LLM_MODEL=claude-3-5-sonnet-20241022
export CODICIL_EMBEDDING_MODEL=voyage-3
使用不同的提供者进行嵌入:
export CODICIL_LLM_PROVIDER=anthropic
export CODICIL_EMBEDDING_PROVIDER=openai
export ANTHROPIC_API_KEY=your_claude_key
export OPENAI_API_KEY=your_openai_key
export CODICIL_LLM_PROVIDER=openai
export OPENAI_API_KEY=dummy
export OPENAI_BASE_URL=http://localhost:11434/v1 # 例如Ollama
export CODICIL_LLM_MODEL=llama3
解决方案:
echo $CODICIL_LLM_PROVIDERcurl http://localhost:4000/codicil/mcp(Phoenix)或curl http://localhost:4700/codicil/mcp(非Phoenix)mix compile --force解决方案:
mix codicil.setup解决方案:
lsof -i :4700解决方案:确保你在Phoenix路由器中添加了forward "/codicil/mcp", Codicil.Plug路由并重启了服务器。
解决方案:验证跟踪器是否在你的mix.exs中启用:
defp elixirc_options(_env), do: [tracers: [Codicil.Tracer]]
强制干净地重新编译:
mix clean
mix compile
贡献给Codicil?这是如何设置开发环境的:
# 克隆仓库
git clone https://github.com/yourusername/codicil.git
cd codicil
# 安装依赖项
mix deps.get
# 设置数据库
mix codicil.setup
# 运行测试
mix test
# 格式化代码
mix format
Codicil使用SQLite,具有以下表:
向量搜索由sqlite-vec扩展提供动力。
切勿将Codicil部署到生产环境。 Codicil是一个开发工具,它:
始终将Codicil作为仅限dev的依赖项包括:
{:codicil, "~> 0.6", only: [:dev, :test]}
上述跟踪器配置(elixirc_options(:prod), do: [])确保在生产构建中禁用Codicil。
完整的文档可在以下位置找到:
Codicil受到GraphSense的设计模式启发,GraphSense是一个TypeScript/Node.js语义代码搜索工具。GraphSense率先采用了结合向量相似性搜索和LLM验证的方法,以实现准确的代码发现。
MIT许可证 - 详情见LICENSE文件。