mcpField
S
Scaled MCP Server

Scaled MCP Server

ローカル
scaledmcp.com開発ツール4363 週間前に更新

Scaled MCP Server について

Scaled MCP Server は水平スケーリングに対応した Go ライブラリで、MCP 2025-03 仕様を実装し、ロードバランサー配下でのデプロイをサポートします。Redis によるセッション管理を備えたMCP サーバーを自社アプリケーションに組み込みたい Go 開発者に向いています。

主な機能

  1. 01/mcp エンドポイントとオプションの SSE による HTTP トランスポート
  2. 02Redis またはインメモリによるセッション管理
  3. 03Actor ベースのセッションとメッセージルーティング
  4. 04複数ノードでのロードバランス構成
  5. 05構造体タグによるツール定義

活用シーン

  1. 01複数ノードにまたがる MCP サーバーの運用
  2. 02既存の Go アプリケーションのルーターに MCP サーバーを組み込む

MCP 対応

MCP 2025-03 仕様を実装し、HTTP エンドポイント経由で MCP クライアントからツールなどを利用できるサーバーを構築します。

設定

ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。

インストール方法は未掲載です

このサーバーのパッケージやリモートエンドポイントはまだ取得できていません。プロジェクトの README の手順に従ってください。

リポジトリ

ツール

ツール一覧はまだ取得されていません

ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。

詳細

Scaled MCP Server

CI Status Go Reference Go Report Card codecov License

ロードバランスされたデプロイメントに対応する、水平スケーリング可能な MCP(Message Context Protocol)サーバーの実装です。

概要

Scaled MCP Server は、水平スケーリングに対応した MCP 2025-03 仕様を実装する Go ライブラリです。アプリケーションへの組み込みを想定して設計されており、柔軟な設定オプションを提供します。

機能

  • HTTP トランスポート:メインの /mcp エンドポイント、オプションの SSE エンドポイント、機能ネゴシエーションを備えた柔軟な HTTP トランスポート
  • セッション管理:Redis またはインメモリのオプションによる分散セッション管理
  • アクターシステム:セッション管理とメッセージルーティングにアクターベースのアーキテクチャを使用
  • 水平スケーリング:複数のノードにまたがるロードバランスされたデプロイメントに対応

インストール

go get github.com/traego/scaled-mcp@latest

注: このライブラリには Go 1.24 以降が必要です。

使用方法

静的ツールを使った基本的なサーバー
package main

import (
	"context"
	"fmt"
	"log/slog"
	"os"
	"os/signal"
	"syscall"

	"github.com/traego/scaled-mcp/pkg/config"
	"github.com/traego/scaled-mcp/pkg/resources"
	"github.com/traego/scaled-mcp/pkg/server"
)

func main() {
	// Configure logging
	logHandler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug})
	slog.SetDefault(slog.New(logHandler))

	// Create a server with default configuration
	cfg := config.DefaultConfig()
	
	// Use in-memory session store for simplicity
	cfg.Session.UseInMemory = true
	
	// Create a static tool registry
	registry := resources.NewStaticToolRegistry()
	
	// Define and register a simple calculator tool
	calculatorTool := resources.NewTool("calculator").
		WithDescription("Performs basic arithmetic operations").
		WithInputs([]resources.ToolInput{
			{
				Name:        "operation",
				Type:        "string",
				Description: "Operation to perform (add, subtract, multiply, divide)",
				Required:    true,
			},
			{
				Name:        "a",
				Type:        "number",
				Description: "First operand",
				Required:    true,
			},
			{
				Name:        "b",
				Type:        "number",
				Description: "Second operand",
				Required:    true,
			},
		}).
		Build()
	
	// Register the tool with the registry
	registry.RegisterTool(calculatorTool)
	
	// Define a prompt for the server
	prompt := "You are a helpful AI assistant that can perform calculations using the calculator tool."
	
	// Create the server with the tool registry and prompt
	srv, err := server.NewMcpServer(cfg,
		server.WithToolRegistry(registry),
		server.WithServerInfo("Example MCP Server", "1.0.0"),
		server.WithPrompt(prompt),
	)
	if err != nil {
		slog.Error("Failed to create server", "error", err)
		os.Exit(1)
	}
	
	// Set up the tool handler
	registry.SetToolHandler("calculator", func(ctx context.Context, params map[string]interface{}) (interface{}, error) {
		// Extract parameters
		operation, ok := params["operation"].(string)
		if !ok {
			return nil, fmt.Errorf("%w: operation must be a string", resources.ErrInvalidParams)
		}
		
		a, ok := params["a"].(float64)
		if !ok {
			return nil, fmt.Errorf("%w: a must be a number", resources.ErrInvalidParams)
		}
		
		b, ok := params["b"].(float64)
		if !ok {
			return nil, fmt.Errorf("%w: b must be a number", resources.ErrInvalidParams)
		}
		
		// Perform the calculation
		var result float64
		switch operation {
		case "add":
			result = a + b
		case "subtract":
			result = a - b
		case "multiply":
			result = a * b
		case "divide":
			if b == 0 {
				return nil, fmt.Errorf("%w: division by zero", resources.ErrInvalidParams)
			}
			result = a / b
		default:
			return nil, fmt.Errorf("%w: unknown operation %s", resources.ErrInvalidParams, operation)
		}
		
		return map[string]interface{}{
			"result": result,
		}, nil
	})
	
	// Start the server in a goroutine
	go func() {
		if err := srv.Start(context.Background()); err != nil {
			slog.Error("Failed to start server", "error", err)
			os.Exit(1)
		}
	}()
	
	slog.Info("Server started", "host", cfg.HTTP.Host, "port", cfg.HTTP.Port)
	
	// Wait for termination signal
	sig := make(chan os.Signal, 1)
	signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
	<-sig
	
	// Shutdown the server
	slog.Info("Shutting down server...")
	shutdownCtx, cancel := context.WithTimeout(context.Background(), cfg.HTTP.ShutdownTimeout)
	defer cancel()
	
	if err := srv.Stop(shutdownCtx); err != nil {
		slog.Error("Failed to stop server", "error", err)
	}
	
	slog.Info("Server stopped")
}

### Using an External HTTP Server

You can use your own HTTP server with the MCP transport:

```go
package main

import (
	"context"
	"log"
	"net/http"

	"github.com/go-chi/chi/v5"
	"github.com/go-chi/chi/v5/middleware"
	"github.com/go-chi/cors"
	"github.com/traego/scaled-mcp/pkg/config"
	"github.com/traego/scaled-mcp/pkg/server"
	"github.com/traego/scaled-mcp/pkg/transport"
)

func main() {
	// Create a server with default configuration
	cfg := config.DefaultConfig()
	cfg.Session.UseInMemory = true
	
	// Create the MCP server but don't start the HTTP server
	srv, err := server.NewServer(cfg)
	if err != nil {
		log.Fatalf("Failed to create server: %v", err)
	}
	
	// Create a custom router
	r := chi.NewRouter()
	
	// Add middleware
	r.Use(middleware.RequestID)
	r.Use(middleware.RealIP)
	r.Use(middleware.Logger)
	r.Use(middleware.Recoverer)
	
	// Add CORS middleware - important when using an external server
	r.Use(cors.Handler(cors.Options{
		AllowedOrigins:   []string{"*"},
		AllowedMethods:   []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
		AllowedHeaders:   []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"},
		ExposedHeaders:   []string{"Link"},
		AllowCredentials: true,
		MaxAge:           300,
	}))
	
	// Add your custom routes
	r.Get("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("Welcome to the MCP server!"))
	})
	
	// Create the HTTP transport with the custom router
	httpTransport := transport.NewHTTPTransport(
		cfg,
		srv.GetActorSystem(),
		srv.GetSessionManager(),
		transport.WithExternalRouter(r),
	)
	
	// Start the MCP server without HTTP
	if err := srv.Start(context.Background()); err != nil {
		log.Fatalf("Failed to start server: %v", err)
	}
	
	// Start the HTTP transport
	if err := httpTransport.Start(); err != nil {
		log.Fatalf("Failed to start HTTP transport: %v", err)
	}
	
	// Start your HTTP server
	log.Printf("Starting HTTP server on %s:%d", cfg.HTTP.Host, cfg.HTTP.Port)
	if err := http.ListenAndServe(":8080", r); err != nil {
		log.Fatalf("HTTP server error: %v", err)
	}
}

## Dynamic Tool Registry Example

This library supports both static and dynamic tool registries. Here's an client_example of using a dynamic tool registry:

```go
// Create a custom tool provider
toolProvider := NewExampleToolProvider()

// Create a dynamic tool registry with the provider
registry := resources.NewDynamicToolRegistry(toolProvider)

// Create server with the dynamic tool registry
cfg := config.DefaultConfig()
mcpServer, err := server.NewMcpServer(cfg,
    server.WithToolRegistry(registry),
)
ツールの定義

このライブラリでは、ツールの入力を定義する方法が 3 つあります。

1. 構造体ベースのリフレクションを使う(推奨)
// Define input struct with mcp tags
type CalculatorInput struct {
    Operation string  `mcp:"operation,The operation to perform (add subtract multiply divide),required"`
    A         float64 `mcp:"a,First operand,required"`
    B         float64 `mcp:"b,Second operand,required"`
}

// Type-safe handler function
func calculatorHandler(ctx context.Context, input *CalculatorInput) (interface{}, error) {
    // Direct access to typed fields: input.Operation, input.A, input.B
    switch input.Operation {
    case "add":
        return map[string]interface{}{"result": input.A + input.B}, nil
    // ...
    }
}

// Register with automatic schema generation (method 1)
calculatorType := reflect.TypeOf(CalculatorInput{})
registry.RegisterStructToolWithHandler("calculator", "Performs arithmetic operations", calculatorType, calculatorHandler)

// Or use the convenience function (method 2)
resources.RegisterStructTool(registry, "calculator", "Performs arithmetic operations", calculatorHandler)

### Generics-Based Tool Registration (New in June 2025)

For even better type safety, you can use the new generics-based approach with two type parameters for input and output:

```go
type CalculatorInput struct {
    Operation string  `mcp:"operation,The operation to perform,required"`
    A         float64 `mcp:"a,First operand,required"`
    B         float64 `mcp:"b,Second operand,required"`
}

type CalculatorOutput struct {
    Result    float64 `mcp:"result,The calculation result,required"`
    Operation string  `mcp:"operation,The operation performed,required"`
}

func calculatorHandler(ctx context.Context, input *CalculatorInput) (*CalculatorOutput, error) {
    result := input.A + input.B
    return &CalculatorOutput{
        Result:    result,
        Operation: input.Operation,
    }, nil
}

// Register with compile-time type safety for both input and output
err := resources.RegisterStructToolWithTypes(registry, "calculator", "Performs arithmetic", calculatorHandler)

この方法には、次の利点があります。

  • 入力と出力の両方でコンパイル時の型安全性を確保
  • 入力型と出力型の両方のスキーマを自動生成
  • 登録時のリフレクションが不要(代わりにジェネリクスを使用)
  • 完全な型チェックによる IDE サポートの向上

#### 2. Using WithInputs

```go
weatherTool := resources.NewTool("weather").
    WithDescription("Get weather information for a location").
    WithInputs([]resources.ToolInput{
        {
            Name:        "location",
            Type:        "string",
            Description: "The location to get weather for",
            Required:    true,
        },
        {
            Name:        "units",
            Type:        "string",
            Description: "Temperature units (celsius or fahrenheit)",
            Default:     "celsius",
        },
    }).
    Build()
3. 個別のパラメータメソッドを使う
calculatorTool := resources.NewTool("calculator").
    WithDescription("Performs basic arithmetic operations").
    WithString("operation").
    Required().
    Description("Operation to perform (add, subtract, multiply, divide)").
    Add().
    WithNumber("a").
    Required().
    Description("First operand").
    Add().
    WithNumber("b").
    Required().
    Description("Second operand").
    Add().
    Build()
構造体タグの形式

mcp 構造体タグの形式は、mcp:"name,description,required,default=value" です。

  • name:スキーマ内のフィールド名(デフォルトではフィールド名を小文字にしたもの)
  • description:フィールドの人間が読める説明
  • required:フィールドを必須として指定
  • default=value:オプションのフィールドにデフォルト値を設定
  • -:フィールドをスキップ(スキーマに含めない)

対応する Go の型:

  • string → "string"
  • int、int64 など → "integer"
  • float64、float32 → "number"
  • bool → "boolean"
  • []T、[N]T → "array"
  • struct、map → "object"
  • *T → T と同じ(ポインター型)

重要な注意事項

CORS の設定

MCP トランスポートで外部 HTTP サーバーを使用する場合は、ルーターで CORS 設定を行う必要があります。上記の例のように外部ルーターを使用するとき、MCP トランスポートは CORS 設定を適用しません。

セッション管理

本番環境へのデプロイメントでは、水平スケーリングに対応するため、セッション管理に Redis を使用することを推奨します。インメモリのセッションストアは、開発またはテストでのみ使用してください。

今後の予定

  • 認可の例と認証コンテキストの引き継ぎ
  • メトリクスエンドポイント(prometheus)。アクターの開始/停止、平均セッション時間などを対象とします
  • セッションアクターフック
  • MCP 仕様
    • リスト変更通知
    • サンプリング
    • ルート
    • 補完
    • ロギング
  • A2A 仕様
  • プラグインをサポートするパイプライン処理
  • K8S クラスタリングの接続とテスト
  • 検索のサポート(https://github.com/modelcontextprotocol/modelcontextprotocol/pull/322)
  • すべての機能(リソース、プロンプトなど)の全面的なテスト
  • テストカバレッジを 80% にする
  • 新しいサーバー API を少し整理して、起動しやすくする
  • より適切なデフォルト設定
  • goakt の vNext に移行し、カスタムのメッセージングスケジュールを置き換える(バグ回避策として使用していました)

貢献

貢献を歓迎します。お気軽にプルリクエストを送信してください。

  1. リポジトリをフォークします
  2. フィーチャーブランチを作成します(git checkout -b feature/amazing-feature)
  3. 変更をコミットします(git commit -m 'Add some amazing feature')
  4. ブランチにプッシュします(git push origin feature/amazing-feature)
  5. プルリクエストを作成します

開発

テスト

テストを実行します:

go test ./...

カバレッジ付きでテストを実行します:

go test -race -coverprofile=coverage.txt -covermode=atomic ./...

ブラウザーでカバレッジレポートを表示します:

go tool cover -html=coverage.txt
コードカバレッジ

このプロジェクトでは、コードカバレッジのレポートに Codecov を使用しています。カバレッジレポートは CI の実行時に自動的に生成され、アップロードされます。

カバレッジダッシュボードを表示するには、codecov.io/gh/traego/scaled-mcp にアクセスしてください。

ライセンス

このプロジェクトは MIT License の下でライセンスされています。

ドキュメント

API の詳細なドキュメントについては、GoDoc を参照してください。

コメント

MCP は初めてですか?

プロトコルの概要から最初のサーバーの導入まで、よくある疑問にまとめて答えます。

AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。