mcp直接调用,无Claude Desktop

TwoAdmin 2025-9-17 119 9/17

这种架构是完全可行且在某些场景下更简洁高效。这就是 "瘦客户端 + 胖服务端" 的架构模式。

直接调用架构

优势对比

✅ 直接调用的优势

  • 更简单:减少中间层,降低复杂度

  • 更可控:完全掌控调用逻辑和错误处理

  • 更高效:减少不必要的协议转换

  • 更灵活:不受 MCP 协议限制,可以自定义通信格式

⚠️ 使用 Claude Desktop 的优势

  • 开箱即用:无需自己写客户端

  • 对话集成:天然与 AI 对话流集成

  • 生态丰富:可以利用现有的 MCP 工具生态

直接调用实现方案

方案1:简单的 HTTP 客户端

go

// direct_client.go
package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"bytes"
)

// 直接调用 MCP Server 的客户端
type DirectMCPClient struct {
	baseURL    string
	httpClient *http.Client
	apiKey     string
}

func NewDirectMCPClient(baseURL, apiKey string) *DirectMCPClient {
	return &DirectMCPClient{
		baseURL:    baseURL,
		httpClient: &http.Client{},
		apiKey:     apiKey,
	}
}

// 直接调用工具方法
func (c *DirectMCPClient) CallTool(toolName string, arguments map[string]interface{}) (map[string]interface{}, error) {
	payload := map[string]interface{}{
		"method": "callTool",
		"params": map[string]interface{}{
			"name":      toolName,
			"arguments": arguments,
		},
	}

	return c.sendRequest(payload)
}

// 直接读取资源
func (c *DirectMCPClient) ReadResource(resourceName string, arguments map[string]interface{}) (map[string]interface{}, error) {
	payload := map[string]interface{}{
		"method": "readResource",
		"params": map[string]interface{}{
			"name":      resourceName,
			"arguments": arguments,
		},
	}

	return c.sendRequest(payload)
}

// 发送 HTTP 请求
func (c *DirectMCPClient) sendRequest(payload map[string]interface{}) (map[string]interface{}, error) {
	jsonData, err := json.Marshal(payload)
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest("POST", c.baseURL+"/mcp/http", bytes.NewBuffer(jsonData))
	if err != nil {
		return nil, err
	}

	req.Header.Set("Content-Type", "application/json")
	if c.apiKey != "" {
		req.Header.Set("Authorization", "Bearer "+c.apiKey)
	}

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("server returned status: %d", resp.StatusCode)
	}

	var result map[string]interface{}
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		return nil, err
	}

	return result, nil
}

// 使用示例
func main() {
	client := NewDirectMCPClient("http://localhost:8080", "your-api-key")

	// 直接调用工具
	result, err := client.CallTool("query-data", map[string]interface{}{
		"query": "SELECT * FROM users WHERE active = true",
	})
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}

	fmt.Printf("Result: %+v\n", result)

	// 直接读取资源
	resource, err := client.ReadResource("database", map[string]interface{}{
		"name": "db:///users",
	})
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}

	fmt.Printf("Resource: %+v\n", resource)
}

方案2:WebSocket 客户端

go

// websocket_client.go
package main

import (
	"encoding/json"
	"fmt"
	"log"
	"time"

	"github.com/gorilla/websocket"
)

type WebsocketMCPClient struct {
	conn    *websocket.Conn
	baseURL string
}

func NewWebsocketMCPClient(baseURL, apiKey string) (*WebsocketMCPClient, error) {
	wsURL := "ws" + baseURL[4:] + "/mcp/ws" // 将 http 转换为 ws
	if apiKey != "" {
		wsURL += "?api_key=" + apiKey
	}

	conn, _, err := websocket.DefaultDialer.Dial(wsURL, nil)
	if err != nil {
		return nil, err
	}

	return &WebsocketMCPClient{
		conn:    conn,
		baseURL: baseURL,
	}, nil
}

func (c *WebsocketMCPClient) SendRequest(method string, params map[string]interface{}) (map[string]interface{}, error) {
	request := map[string]interface{}{
		"jsonrpc": "2.0",
		"id":      time.Now().UnixNano(),
		"method":  method,
		"params":  params,
	}

	if err := c.conn.WriteJSON(request); err != nil {
		return nil, err
	}

	_, message, err := c.conn.ReadMessage()
	if err != nil {
		return nil, err
	}

	var response map[string]interface{}
	if err := json.Unmarshal(message, &response); err != nil {
		return nil, err
	}

	return response, nil
}

func (c *WebsocketMCPClient) Close() {
	c.conn.Close()
}

方案3:简化的自定义 API(推荐)

如果你不需要严格的 MCP 协议兼容,可以创建更简单的 API:

go

// simple_api_server.go
package main

import (
	"encoding/json"
	"fmt"
	"net/http"
)

// 简化的 API 端点
func SimpleQueryHandler(w http.ResponseWriter, r *http.Request) {
	var request struct {
		Query string `json:"query"`
		Type  string `json:"type"` // "sql", "api", "calculation"
	}

	if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
		http.Error(w, "Invalid JSON", http.StatusBadRequest)
		return
	}

	// 直接处理请求,不需要 MCP 协议包装
	result, err := processDirectQuery(request.Query, request.Type)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(result)
}

func processDirectQuery(query, queryType string) (map[string]interface{}, error) {
	// 直接处理逻辑
	switch queryType {
	case "sql":
		return executeSQLQuery(query)
	case "api":
		return callExternalAPI(query)
	case "calculation":
		return performCalculation(query)
	default:
		return nil, fmt.Errorf("unknown query type: %s", queryType)
	}
}

func executeSQLQuery(query string) (map[string]interface{}, error) {
	// 直接执行 SQL 查询
	return map[string]interface{}{
		"data":    []map[string]interface{}{{"id": 1, "name": "John"}},
		"count":   1,
		"success": true,
	}, nil
}

客户端集成示例

前端直接调用

javascript

// 前端直接调用 MCP Server
async function callMCPServer(toolName, arguments) {
    try {
        const response = await fetch('http://your-mcp-server:8080/mcp/http', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': 'Bearer your-token'
            },
            body: JSON.stringify({
                method: 'callTool',
                params: {
                    name: toolName,
                    arguments: arguments
                }
            })
        });

        const result = await response.json();
        return result;
    } catch (error) {
        console.error('MCP Server call failed:', error);
        throw error;
    }
}

// 使用示例
const salesData = await callMCPServer('get-sales-data', {
    period: '2024-01',
    region: 'north'
});

命令行工具集成

bash

#!/bin/bash
# mcp-cli.sh

# 直接调用 MCP Server 的命令行工具
function mcp-tool {
    local tool_name=$1
    shift
    local args=$@
    
    curl -X POST http://localhost:8080/mcp/http \
        -H "Content-Type: application/json" \
        -d "{
            \"method\": \"callTool\",
            \"params\": {
                \"name\": \"$tool_name\",
                \"arguments\": {$args}
            }
        }"
}

# 使用示例
mcp-tool "file-analysis" "\"path\": \"/home/user/document.txt\""

什么时候选择直接调用?

✅ 适合直接调用的场景

  1. 自定义客户端:你自己开发的应用程序

  2. 自动化脚本:CI/CD 流程、定时任务

  3. 内部工具:企业内部使用的工具链

  4. 性能敏感:需要最小化延迟的场景

  5. 简单集成:不需要复杂对话交互的场景

✅ 适合 Claude Desktop 的场景

  1. 对话式交互:需要与 AI 自然语言交互

  2. 快速原型:想要快速测试 MCP 功能

  3. 生态利用:想要利用现有的 MCP 工具

  4. 非技术用户:为不懂编程的用户提供界面

迁移建议

如果你已经从 Claude Desktop 开始,想要迁移到直接调用:

  1. 保持 API 兼容:确保直接调用的接口与 MCP 协议兼容

  2. 逐步迁移:先并行运行,再逐步切换

  3. 客户端封装:创建统一的客户端库来封装调用逻辑

go

// 统一的客户端封装
type UniversalClient struct {
	// 可以支持多种调用方式
	useDirect bool
	baseURL   string
	// ... 其他配置
}

func (c *UniversalClient) CallTool(toolName string, args map[string]interface{}) (interface{}, error) {
	if c.useDirect {
		return c.directCall(toolName, args)
	} else {
		return c.mcpProtocolCall(toolName, args)
	}
}

总结

完全可以不要 Claude Desktop,直接让你的客户端调用 MCP Server。这种架构:

  • ✅ 更简单直接:减少中间层

  • ✅ 更高效:降低延迟和复杂度

  • ✅ 更灵活:不受特定客户端限制

  • ✅ 更可控:完全掌控调用逻辑

选择依据:

  • 如果需要对话式AI交互 → 使用 Claude Desktop

  • 如果需要程序化调用 → 直接调用 MCP Server

  • 如果两者都需要 → 实现兼容两种方式的接口

直接调用是更传统和灵活的集成方式,特别适合企业应用和自动化场景。

- THE END -
Tag:

TwoAdmin

9月17日20:11

最后修改:2025年9月17日
0

非特殊说明,本博所有文章均为博主原创。