Cortex
Build MCP Servers Declaratively in Golang
目录
- 工作室 - HTTP与SSE - 多协议 - 测试和调试
概述
模型上下文协议允许应用程序以标准化的方式为LLM提供上下文,将提供上下文的关注点与实际的LLM交互分开。Cortex实现了完整的MCP规范,使其易于:
- 构建公开资源和工具的MCP服务器
- 使用标准传输方式,如stdio和服务器发送事件(SSE)
- 处理所有MCP协议消息和生命周期事件
- 遵循Go最佳实践和干净架构原则
- 将Cortex嵌入到现有的服务器和应用程序中
注: Cortex始终更新以符合最新的MCP规范 spec.modelcontextprotocol.io/最新
安装
go get github.com/FreePeak/cortex快速启动
让我们创建一个简单的MCP服务器,它公开了一个echo工具:
package main
import (
"context"
"fmt"
"log"
"os"
"github.com/FreePeak/cortex/pkg/server"
"github.com/FreePeak/cortex/pkg/tools"
)
func main() {
// Create a logger that writes to stderr instead of stdout
// This is critical for STDIO servers as stdout must only contain JSON-RPC messages
logger := log.New(os.Stderr, "[cortex] ", log.LstdFlags)
// Create the server
mcpServer := server.NewMCPServer("Echo Server Example", "1.0.0", logger)
// Create an echo tool
echoTool := tools.NewTool("echo",
tools.WithDescription("Echoes back the input message"),
tools.WithString("message",
tools.Description("The message to echo back"),
tools.Required(),
),
)
// Example of a tool with array parameter
arrayExampleTool := tools.NewTool("array_example",
tools.WithDescription("Example tool with array parameter"),
tools.WithArray("values",
tools.Description("Array of string values"),
tools.Required(),
tools.Items(map[string]interface{}{
"type": "string",
}),
),
)
// Add the tools to the server with handlers
ctx := context.Background()
err := mcpServer.AddTool(ctx, echoTool, handleEcho)
if err != nil {
logger.Fatalf("Error adding tool: %v", err)
}
err = mcpServer.AddTool(ctx, arrayExampleTool, handleArrayExample)
if err != nil {
logger.Fatalf("Error adding array example tool: %v", err)
}
// Write server status to stderr instead of stdout to maintain clean JSON protocol
fmt.Fprintf(os.Stderr, "Starting Echo Server...\n")
fmt.Fprintf(os.Stderr, "Send JSON-RPC messages via stdin to interact with the server.\n")
fmt.Fprintf(os.Stderr, `Try: {"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"echo","parameters":{"message":"Hello, World!"}}}\n`)
// Serve over stdio
if err := mcpServer.ServeStdio(); err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
}
// Echo tool handler
func handleEcho(ctx context.Context, request server.ToolCallRequest) (interface{}, error) {
// Extract the message parameter
message, ok := request.Parameters["message"].(string)
if !ok {
return nil, fmt.Errorf("missing or invalid 'message' parameter")
}
// Return the echo response in the format expected by the MCP protocol
return map[string]interface{}{
"content": []map[string]interface{}{
{
"type": "text",
"text": message,
},
},
}, nil
}
// Array example tool handler
func handleArrayExample(ctx context.Context, request server.ToolCallRequest) (interface{}, error) {
// Extract the values parameter
values, ok := request.Parameters["values"].([]interface{})
if !ok {
return nil, fmt.Errorf("missing or invalid 'values' parameter")
}
// Convert values to string array
stringValues := make([]string, len(values))
for i, v := range values {
stringValues[i] = v.(string)
}
// Return the array response in the format expected by the MCP protocol
return map[string]interface{}{
"content": stringValues,
}, nil
}什么是MCP?
这 模型上下文协议(MCP) 是一种标准化协议,允许应用程序以安全有效的方式为LLM提供上下文。它将提供上下文和工具的关注点与实际的LLM交互分开。MCP服务器可以:
- 通过以下方式公开数据 资源 (只读数据端点)
- 通过以下方式提供功能 工具 (可执行功能)
- 通过以下方式定义交互模式 提示 (可重复使用的模板)
- 支持多种传输方式(stdio、HTTP/SSE)
核心概念
服务器
MCP服务器是MCP协议的核心接口。它处理连接管理、协议合规性和消息路由:
// Create a new MCP server with logger
mcpServer := server.NewMCPServer("My App", "1.0.0", logger)工具
工具允许LLM通过您的服务器执行操作。与资源不同,工具被期望执行计算并具有副作用:
// Define a calculator tool
calculatorTool := tools.NewTool("calculator",
tools.WithDescription("Performs basic math operations"),
tools.WithString("operation",
tools.Description("The operation to perform (add, subtract, multiply, divide)"),
tools.Required(),
),
tools.WithNumber("a",
tools.Description("First operand"),
tools.Required(),
),
tools.WithNumber("b",
tools.Description("Second operand"),
tools.Required(),
),
)
// Add the tool to the server with a handler
mcpServer.AddTool(ctx, calculatorTool, handleCalculator)提供商
提供者允许您将相关的工具和资源组合到一个可以轻松向服务器注册的包中:
// Create a weather provider
weatherProvider, err := weather.NewWeatherProvider(logger)
if err != nil {
logger.Fatalf("Failed to create weather provider: %v", err)
}
// Register the provider with the server
err = mcpServer.RegisterProvider(ctx, weatherProvider)
if err != nil {
logger.Fatalf("Failed to register weather provider: %v", err)
}资源
资源是您向LLM公开数据的方式。它们类似于REST API中的GET端点-它们提供数据,但不应执行显著的计算或产生副作用:
// Create a resource (Currently using the internal API)
resource := &domain.Resource{
URI: "sample://hello-world",
Name: "Hello World Resource",
Description: "A sample resource for demonstration purposes",
MIMEType: "text/plain",
}提示
提示是可重用的模板,可帮助LLM与您的服务器有效交互:
// Create a prompt (Currently using the internal API)
codeReviewPrompt := &domain.Prompt{
Name: "review-code",
Description: "A prompt for code review",
Template: "Please review this code:\n\n{{.code}}",
Parameters: []domain.PromptParameter{
{
Name: "code",
Description: "The code to review",
Type: "string",
Required: true,
},
},
}
// Note: Prompt support is being updated in the public API运行服务器
Go中的MCP服务器可以根据您的用例连接到不同的传输方式:
工作室
对于命令行工具和直接集成:
// Start a stdio server
if err := mcpServer.ServeStdio(); err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}重要提示:使用STDIO时,所有日志都必须定向到stderr,以在stdout上维护干净的JSON-RPC协议:
// Create a logger that writes to stderr
logger := log.New(os.Stderr, "[cortex] ", log.LstdFlags)
// All debug/status messages should use stderr
fmt.Fprintf(os.Stderr, "Server starting...\n")HTTP与SSE
对于web应用程序,您可以使用服务器发送事件(SSE)进行实时通信:
// Configure the HTTP address
mcpServer.SetAddress(":8080")
// Start an HTTP server with SSE support
if err := mcpServer.ServeHTTP(); err != nil {
log.Fatalf("HTTP server error: %v", err)
}
// For graceful shutdown
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := mcpServer.Shutdown(ctx); err != nil {
log.Fatalf("Server shutdown error: %v", err)
}多协议
您还可以使用goroutines同时运行多个协议服务器:
// Start an HTTP server
go func() {
if err := mcpServer.ServeHTTP(); err != nil {
log.Fatalf("HTTP server error: %v", err)
}
}()
// Start a STDIO server
go func() {
if err := mcpServer.ServeStdio(); err != nil {
log.Fatalf("STDIO server error: %v", err)
}
}()
// Wait for shutdown signal
stop := make(chan os.Signal, 1)
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
<-stop测试和调试
有关测试和调试Cortex服务器的更多详细信息,请参阅 测试指导.
嵌入皮质
Cortex可以嵌入到现有的应用程序中,以添加MCP功能,而无需运行单独的服务器。这对于与现有的web框架或PocketBase等应用程序集成非常有用。
HTTP服务器集成
您可以轻松地将Cortex与任何Go HTTP服务器集成:
package main
import (
"log"
"net/http"
"os"
"github.com/FreePeak/cortex/pkg/server"
"github.com/FreePeak/cortex/pkg/tools"
)
func main() {
// Create a logger
logger := log.New(os.Stderr, "[cortex] ", log.LstdFlags)
// Create an MCP server
mcpServer := server.NewMCPServer("Embedded MCP Server", "1.0.0", logger)
// Add some tools
echoTool := tools.NewTool("echo",
tools.WithDescription("Echoes back the input message"),
tools.WithString("message",
tools.Description("The message to echo back"),
tools.Required(),
),
)
// Add the tool to the server
mcpServer.AddTool(context.Background(), echoTool, func(ctx context.Context, request server.ToolCallRequest) (interface{}, error) {
message := request.Parameters["message"].(string)
return map[string]interface{}{
"content": []map[string]interface{}{
{
"type": "text",
"text": message,
},
},
}, nil
})
// Create an HTTP adapter for the MCP server
adapter := server.NewHTTPAdapter(mcpServer, server.WithPath("/api/mcp"))
// Use the adapter in your HTTP server
http.Handle("/api/mcp/", adapter.Handler())
// Add your other routes
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello from the main server!"))
})
// Start the server
logger.Println("Starting server on :8080")
http.ListenAndServe(":8080", nil)
}PocketBase集成
Cortex可以与 PocketBase,一个具有数据库、身份验证和管理UI的开源后端:
package main
import (
"log"
"github.com/pocketbase/pocketbase"
"github.com/pocketbase/pocketbase/core"
"github.com/FreePeak/cortex/pkg/integration/pocketbase"
"github.com/FreePeak/cortex/pkg/tools"
)
func main() {
// Create a new PocketBase app
app := pocketbase.New()
// Initialize Cortex plugin
plugin := pocketbase.NewCortexPlugin(
pocketbase.WithName("PocketBase MCP Server"),
pocketbase.WithVersion("1.0.0"),
pocketbase.WithBasePath("/api/mcp"),
)
// Add tools to the plugin
echoTool := tools.NewTool("echo",
tools.WithDescription("Echoes back the input message"),
tools.WithString("message",
tools.Description("The message to echo back"),
tools.Required(),
),
)
// Add the tool with a handler
plugin.AddTool(echoTool, func(ctx context.Context, request pocketbase.ToolCallRequest) (interface{}, error) {
message := request.Parameters["message"].(string)
return map[string]interface{}{
"content": []map[string]interface{}{
{
"type": "text",
"text": message,
},
},
}, nil
})
// Register the plugin with PocketBase
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
// Register the plugin
return plugin.RegisterWithPocketBase(app)
})
// Start the PocketBase app
if err := app.Start(); err != nil {
log.Fatal(err)
}
}有关嵌入Cortex的更多详细文档,请参阅 嵌入指南.
例子
基本示例
该存储库包括几个基本示例 examples 目录:
- STDIO服务器:一个通过STDIO通信的简单MCP服务器(
examples/stdio-server) - SSE服务器:使用HTTP与服务器发送事件进行通信的服务器(
examples/sse-server) - 多协议:可以同时在多个协议上运行的服务器(
examples/multi-protocol)
高级示例
示例目录还包括更高级的用例:
- 提供商:如何创建和使用提供者来组织相关工具的示例(
examples/providers)
- 天气提供商:演示如何为天气相关工具创建提供程序 - 数据库提供程序:显示如何为数据库操作创建提供程序
插件系统
Cortex包括一个用于扩展服务器功能的插件系统:
// Create a new provider based on the BaseProvider
type MyProvider struct {
*plugin.BaseProvider
}
// Create a new provider instance
func NewMyProvider(logger *log.Logger) (*MyProvider, error) {
info := plugin.ProviderInfo{
ID: "my-provider",
Name: "My Provider",
Version: "1.0.0",
Description: "A custom provider for my tools",
Author: "Your Name",
URL: "https://github.com/yourusername/myrepo",
}
baseProvider := plugin.NewBaseProvider(info, logger)
provider := &MyProvider{
BaseProvider: baseProvider,
}
// Register tools with the provider
// ...
return provider, nil
}封装结构
Cortex代码库被组织成几个包:
pkg/server:核心服务器实施pkg/tools:工具创建和管理pkg/plugin:用于扩展服务器功能的插件系统pkg/types:常见类型和接口pkg/builder:用于创建复杂对象的构建器
贡献
欢迎投稿!请随时提交拉取请求。
- 分叉存储库
- 创建功能分支(
git checkout -b feature/amazing-feature) - 提交您的更改(
git commit -m 'Add some amazing feature') - 推到分支(
git push origin feature/amazing-feature) - 打开拉取请求
许可证
此项目在Apache License 2.0下获得许可-有关详细信息,请参阅License文件。
支持与联系
- 如有疑问或问题,请发送电子邮件至 mnhatlinh.doan@gmail.com
- 直接打开问题: 问题追踪
- 如果Cortex有助于您的工作,请考虑支持:

