mcp

package
v0.7.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 21, 2025 License: MIT Imports: 6 Imported by: 1

README

MCP Adapter for LangGraphGo

This package provides an adapter to use MCP (Model Context Protocol) tools with LangGraphGo and LangChainGo.

Features

  • Seamless Integration: Convert MCP tools to langchaingo/tools.Tool interface
  • Multiple Server Support: Connect to multiple MCP servers simultaneously
  • Configuration Loading: Load MCP server configs from Claude's standard config file
  • Type Safety: Full Go type safety and error handling

Installation

This package is part of the LangGraphGo repository and uses github.com/smallnest/goskills/mcp for MCP protocol support.

Usage

Basic Usage
package main

import (
    "context"
    "fmt"

    "github.com/smallnest/langgraphgo/adapter/mcp"
    "github.com/smallnest/langgraphgo/prebuilt"
)

func main() {
    ctx := context.Background()

    // Create MCP client from Claude's config file
    mcpClient, err := mcp.NewClientFromConfig(ctx, "~/.claude.json")
    if err != nil {
        panic(err)
    }
    defer mcpClient.Close()

    // Convert MCP tools to langchaingo tools
    tools, err := mcp.MCPToTools(ctx, mcpClient)
    if err != nil {
        panic(err)
    }

    // Use with LangGraphGo prebuilt agents
    agent, err := prebuilt.CreateAgent(
        ctx,
        llm,                    // Your LLM instance
        tools,                  // MCP tools
        nil,                    // No memory
        "You are a helpful assistant",
        nil,                    // Default options
    )
    if err != nil {
        panic(err)
    }

    // Run the agent
    result, err := agent.Invoke(ctx, map[string]any{
        "input": "Search for information about Go",
    })
    if err != nil {
        panic(err)
    }

    fmt.Println(result["output"])
}
Advanced Usage
Using Specific MCP Servers
import (
    "context"

    mcpclient "github.com/smallnest/goskills/mcp"
    "github.com/smallnest/langgraphgo/adapter/mcp"
)

func main() {
    ctx := context.Background()

    // Load config
    config, err := mcpclient.LoadConfig("~/.claude.json")
    if err != nil {
        panic(err)
    }

    // Create client
    client, err := mcpclient.NewClient(ctx, config)
    if err != nil {
        panic(err)
    }
    defer client.Close()

    // Convert to tools
    tools, err := mcp.MCPToTools(ctx, client)
    if err != nil {
        panic(err)
    }

    // Use tools...
}
Direct OpenAI Tool Format

If you need OpenAI tool definitions directly:

openaiTools, err := mcp.MCPToolsToOpenAI(ctx, mcpClient)
if err != nil {
    panic(err)
}

// Use with OpenAI API directly
Inspecting Tool Schema
for _, tool := range tools {
    if schema, ok := mcp.GetToolSchema(tool); ok {
        fmt.Printf("Tool %s has schema: %+v\n", tool.Name(), schema)
    }
}

Configuration

MCP tools are configured in ~/.claude.json (or any other path you specify). Example configuration:

{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/allowed/files"]
    },
    "postgres": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-postgres", "postgresql://localhost/mydb"]
    },
    "custom-sse-server": {
      "type": "sse",
      "url": "http://localhost:3000/sse",
      "headers": {
        "Authorization": "Bearer your-token"
      }
    }
  }
}
Configuration Options

Each MCP server can have the following fields:

  • command: The command to execute (for stdio transport)
  • args: Arguments for the command (for stdio transport)
  • env: Environment variables to set (optional)
  • type: Transport type - "stdio" (default) or "sse"
  • url: The SSE endpoint URL (for sse transport)
  • headers: HTTP headers for SSE requests (optional)

Tool Naming

MCP tools are automatically prefixed with their server name to avoid conflicts. For example, a tool named read_file from the filesystem server will be available as filesystem__read_file.

Error Handling

The adapter provides detailed error messages for common issues:

  • Configuration loading errors
  • Connection failures to MCP servers
  • Tool invocation errors
  • Invalid input/output format errors

All errors are wrapped with context for easy debugging.

Integration with LangGraphGo

MCP tools work seamlessly with all LangGraphGo features:

  • Prebuilt Agents: Use with CreateAgent, CreateReactAgent, etc.
  • Tool Nodes: Use in custom graph workflows
  • Tool Execution: Automatic serialization/deserialization
  • State Management: Full state tracking for tool calls

Examples

See the examples/ directory for complete examples:

  • examples/mcp_basic/ - Basic MCP tool usage
  • examples/mcp_agent/ - Using MCP with prebuilt agents
  • examples/mcp_custom/ - Custom MCP server integration

Dependencies

  • github.com/smallnest/goskills/mcp - MCP protocol implementation
  • github.com/tmc/langchaingo/tools - LangChain tool interface
  • github.com/sashabaranov/go-openai - OpenAI types

License

Same as LangGraphGo main repository.

Documentation

Overview

Package mcp provides an adapter for integrating Model Context Protocol (MCP) tools with LangGraph Go agents.

MCP is an open protocol that allows AI assistants to securely connect to external data sources and tools. This adapter enables LangGraph agents to use MCP-compliant tools and services, providing access to a growing ecosystem of MCP integrations including databases, APIs, file systems, and more.

Core Components

## MCPTool The main adapter that wraps MCP protocol tools as LangChain-compatible tools:

import (
	"github.com/smallnest/langgraphgo/adapter/mcp"
	"github.com/smallnest/langgraphgo/prebuilt"
)

// Connect to MCP server
client, err := mcp.NewMCPClient("stdio", []string{"python", "mcp_server.py"})
if err != nil {
	return err
}
defer client.Close()

// List available tools
tools, err := client.ListTools()
if err != nil {
	return err
}

// Convert MCP tools to LangChain tools
langchainTools := make([]tools.Tool, len(tools))
for i, mcpTool := range tools {
	langchainTools[i] = &mcp.MCPTool{
		name:        mcpTool.Name,
		description: mcpTool.Description,
		client:      client,
		parameters:  mcpTool.InputSchema,
	}
}

// Use with ReAct agent
agent, err := prebuilt.CreateReactAgent(llm, langchainTools, 10)

MCP Server Integration

## Standard Input/Output (stdio) Most common connection type for local MCP servers:

client, err := mcp.NewMCPClient("stdio", []string{
	"python",
	"-m",
	"mcp_server_sqlite",
	"--db-path",
	"/path/to/database.db",
})

## HTTP Transport Connect to remote MCP servers via HTTP:

client, err := mcp.NewMCPClientWithConfig(mcp.Config{
	Transport: "http",
	URL:      "http://localhost:8080/mcp",
	Headers: map[string]string{
		"Authorization": "Bearer your-token",
	},
})

## WebSocket Transport Real-time bidirectional communication:

client, err := mcp.NewMCPClientWithConfig(mcp.Config{
	Transport: "websocket",
	URL:      "ws://localhost:8080/ws",
})

Available MCP Tools

## Database Tools Query databases through MCP:

// SQLite MCP server
client, _ := mcp.NewMCPClient("stdio", []string{
	"sqlite-mcp",
	"--db-path", "./data.db",
})

// Use database tools
agent, _ := prebuilt.CreateReactAgent(llm, mcpTools, 10)

result, _ := agent.Invoke(ctx, map[string]any{
	"messages": []llms.MessageContent{
		{
			Role: llms.ChatMessageTypeHuman,
			Parts: []llms.ContentPart{
				llms.TextPart("Show me all users from the database"),
			},
		},
	},
})

## File System Tools Access file systems through MCP:

// File system MCP server
client, _ := mcp.NewMCPClient("stdio", []string{
	"filesystem-mcp",
	"--root", "/allowed/path",
})

// Agent can now read/write files
result, _ := agent.Invoke(ctx, map[string]any{
	"messages": []llms.MessageContent{
		{
			Role: llms.ChatMessageTypeHuman,
			Parts: []llms.ContentPart{
				llms.TextPart("Read the config.yaml file and update the port to 8080"),
			},
		},
	},
})

## Web API Tools Connect to web services through MCP:

// GitHub MCP server
client, _ := mcp.NewMCPClient("stdio", []string{
	"github-mcp",
	"--token", os.Getenv("GITHUB_TOKEN"),
})

// Agent can interact with GitHub
result, _ := agent.Invoke(ctx, map[string]any{
	"messages": []llms.MessageContent{
		{
			Role: llms.ChatMessageTypeHuman,
			Parts: []llms.ContentPart{
				llms.TextPart("List all pull requests in the langgraph-go repository"),
			},
		},
	},
})

Integration Examples

## Multi-Tool Agent with Multiple MCP Servers

// Connect to multiple MCP servers
sqliteClient, _ := mcp.NewMCPClient("stdio", []string{
	"sqlite-mcp",
	"--db-path", "./data.db",
})
defer sqliteClient.Close()

fsClient, _ := mcp.NewMCPClient("stdio", []string{
	"filesystem-mcp",
	"--root", "/data",
})
defer fsClient.Close()

// Collect all tools
var allTools []tools.Tool

sqliteTools, _ := sqliteClient.ListTools()
for _, t := range sqliteTools {
	allTools = append(allTools, &mcp.MCPTool{
		name:        t.Name,
		description: t.Description,
		client:      sqliteClient,
		parameters:  t.InputSchema,
	})
}

fsTools, _ := fsClient.ListTools()
for _, t := range fsTools {
	allTools = append(allTools, &mcp.MCPTool{
		name:        t.Name,
		description: t.Description,
		client:      fsClient,
		parameters:  t.InputSchema,
	})
}

// Create agent with all MCP tools
agent, _ := prebuilt.CreateReactAgent(llm, allTools, 20)

## Dynamic Tool Discovery

// Discover tools at runtime
client, _ := mcp.NewMCPClient("stdio", []string{"dynamic-mcp-server"})

// Periodically refresh tool list
ticker := time.NewTicker(5 * time.Minute)
go func() {
	for range ticker.C {
		tools, _ := client.ListTools()
		// Update agent's tool list
		updateAgentTools(agent, tools)
	}
}()

MCP Configuration

## Client Configuration

config := mcp.Config{
	Transport: "stdio",
	Command:   []string{"python", "server.py"},
	Env: map[string]string{
		"API_KEY": "your-api-key",
		"DEBUG":   "true",
	},
	Timeout:    30 * time.Second,
	MaxRetries: 3,
	Headers: map[string]string{
		"User-Agent": "LangGraph-Go/1.0",
	},
}

client, _ := mcp.NewMCPClientWithConfig(config)

## Tool Configuration

// Configure individual tools
mcpTool := &mcp.MCPTool{
	name:        "database_query",
	description: "Execute SQL queries",
	client:      client,
	parameters: map[string]any{
		"type": "object",
		"properties": map[string]any{
			"query": map[string]any{
				"type":        "string",
				"description": "SQL query to execute",
			},
		},
		"required": []string{"query"},
	},
}

Error Handling

The adapter provides comprehensive error handling:

result, err := mcpTool.Call(ctx, input)
if err != nil {
	var mcpErr *mcp.MCPError
	if errors.As(err, &mcpErr) {
		fmt.Printf("MCP Error: %s (Code: %d)\n", mcpErr.Message, mcpErr.Code)
		fmt.Printf("Tool: %s\n", mcpErr.Tool)
		fmt.Printf("Data: %v\n", mcpErr.Data)
	}
}

Security Features

  • Authentication and authorization
  • Request/response validation
  • Rate limiting
  • Audit logging
  • Secure transport layers
  • Permission scopes

Performance Optimization

  • Connection pooling
  • Request batching
  • Response caching
  • Compression
  • Keep-alive connections

Best Practices

  1. Use appropriate transport for your use case (stdio for local, HTTP for remote)
  2. Set reasonable timeouts for tool execution
  3. Handle MCP errors gracefully
  4. Close clients when done
  5. Validate tool parameters before calling
  6. Use environment variables for sensitive configuration
  7. Monitor tool usage and performance
  8. Implement retry logic for transient errors

Advanced Features

## Tool Streaming For long-running operations:

client, _ := mcp.NewMCPClient("stdio", cmd)

// Enable streaming for specific tools
mcpTool := &mcp.MCPTool{
	name:        "long_running_task",
	description: "Execute long-running task with streaming",
	client:      client,
	streaming:   true,
}

// Handle streaming response
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()

result, err := mcpTool.CallWithStream(ctx, input, func(chunk string) {
	fmt.Printf("Progress: %s\n", chunk)
})

## Tool Composition Combine multiple MCP tools:

// Create a composite tool that uses multiple MCP tools
type CompositeMCPTool struct {
	tools []*mcp.MCPTool
}

func (t *CompositeMCPTool) Name() string {
	return "composite_operation"
}

func (t *CompositeMCPTool) Description() string {
	return "Performs complex operation using multiple tools"
}

func (t *CompositeMCPTool) Call(ctx context.Context, input string) (string, error) {
	// Parse input to determine sequence of operations
	var ops []Operation
	json.Unmarshal([]byte(input), &ops)

	// Execute tools in sequence
	var results []any
	for _, op := range ops {
		for _, tool := range t.tools {
			if tool.Name() == op.Tool {
				result, _ := tool.Call(ctx, op.Params)
				results = append(results, result)
			}
		}
	}

	// Return combined results
	return json.Marshal(results)
}

MCP Server Development

Create custom MCP servers:

// server.py
import asyncio
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool

app = Server("my-mcp-server")

@app.list_tools()
async def list_tools() -> list[Tool]:
	return [
		Tool(
			name="my_tool",
			description="Custom tool description",
			inputSchema={
				"type": "object",
				"properties": {
					"param1": {"type": "string"},
				},
				"required": ["param1"],
			},
		),
	]

@app.call_tool()
async def call_tool(name: str, arguments: dict) -> str:
	if name == "my_tool":
		# Custom tool logic
		return f"Processed: {arguments['param1']}"

async def main():
	async with stdio_server() as (read_stream, write_stream):
		await app.run(read_stream, write_stream)

if __name__ == "__main__":
	asyncio.run(main())

Monitoring and Debugging

// Enable MCP logging
client, _ := mcp.NewMCPClientWithConfig(mcp.Config{
	Transport: "stdio",
	Command:   []string{"python", "server.py"},
	LogLevel:  "debug",
	LogFile:   "/tmp/mcp.log",
})

// Get client statistics
stats := client.GetStats()
fmt.Printf("Total requests: %d\n", stats.Requests)
fmt.Printf("Average latency: %v\n", stats.AvgLatency)
fmt.Printf("Error rate: %.2f%%\n", stats.ErrorRate)

Community MCP Servers

Popular MCP servers to integrate:

  • sqlite-mcp: Database access
  • filesystem-mcp: File system operations
  • github-mcp: GitHub API integration
  • slack-mcp: Slack workspace access
  • gmail-mcp: Email management
  • postgres-mcp: PostgreSQL database
  • redis-mcp: Redis operations
  • kubernetes-mcp: Kubernetes cluster management
  • aws-mcp: AWS service integration
  • mongodb-mcp: MongoDB database access

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func GetToolSchema

func GetToolSchema(tool tools.Tool) (any, bool)

GetToolSchema returns the JSON schema for a tool's parameters. This can be useful for debugging or generating documentation.

func MCPToTools

func MCPToTools(ctx context.Context, client *mcpclient.Client) ([]tools.Tool, error)

MCPToTools converts MCP tools from a client to langchaingo tools. It fetches all available tools from the connected MCP servers and wraps them as langchaingo tools.Tool instances.

Example

Example usage documentation

ctx := context.Background()

// Load MCP client from config file
client, err := NewClientFromConfig(ctx, "~/.claude.json")
if err != nil {
	panic(err)
}
defer client.Close()

// Convert MCP tools to langchaingo tools
tools, err := MCPToTools(ctx, client)
if err != nil {
	panic(err)
}

// Use tools with langchaingo or langgraphgo agents
_ = tools

func MCPToolsToOpenAI

func MCPToolsToOpenAI(ctx context.Context, client *mcpclient.Client) ([]openai.Tool, error)

MCPToolsToOpenAI converts MCP tools to OpenAI tool definitions. This is useful when you need to use MCP tools directly with OpenAI's API.

func NewClientFromConfig

func NewClientFromConfig(ctx context.Context, configPath string) (*mcpclient.Client, error)

NewClientFromConfig creates a new MCP client from a config file path. This is a convenience function that loads the config and creates a client.

Example
ctx := context.Background()

// Create MCP client from Claude config file
client, err := NewClientFromConfig(ctx, "~/.claude.json")
if err != nil {
	panic(err)
}
defer client.Close()

// Now you can use the client to get tools or call them directly
_ = client

Types

type MCPTool

type MCPTool struct {
	// contains filtered or unexported fields
}

MCPTool implements tools.Tool for MCP (Model Context Protocol) tools.

func (*MCPTool) Call

func (t *MCPTool) Call(ctx context.Context, input string) (string, error)

func (*MCPTool) Description

func (t *MCPTool) Description() string

func (*MCPTool) Name

func (t *MCPTool) Name() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL