Skip to content
forked from llmcontext/gomcp

Unofficial Golang SDK for Anthropic Model Context Protocol

License

Notifications You must be signed in to change notification settings

cloudbees/gomcp

 
 

Repository files navigation

gomcp

Description

An unofficial Golang implementation of the Model Context Protocol defined by Anthropic.

The officialannouncement of the Model Context Protocol is here.

The Model Context Protocol (MCP) provides a standardized, secure mechanism for AI models to interact with external tools and data sources.

By defining a precise interface and communication framework, MCP allows AI assistants, such as the Claude desktop application, to safely extend their capabilities.

Reference documentation

  • the full documentation of the Model Context Protocol is here
  • the official TypeScript SDK is available here
  • the official Python SDK is available here

Installation

go get github.com/llmcontext/gomcp

Direct dependencies:

Usage

Let's consider a simple example where we want to use the mcp package to create a server that can retrieve the content of a Notion page so that you can use it in a Claude chat.

The way to do this is to define a set of tools that can be exposed to the LLM through the Model Context Protocol and to implement them in Go.

The first step is to define the tools that will be exposed to the LLM.

In Mcp, you define a set of Tool providers and each provider is a set of Tools.

In our case, we have a single provider called notion that has a single tool to retrieve the content of a Notion page.

configuration file

Your mcp server needs a configuration file to start.

An example of configuration file would be:

{
    "serverInfo": {
        "name": "gomcp",
        "version": "0.1.0"
    },
    "logging": {
        "file": "/var/log/gomcp/mcpnotion.log",
        "level": "debug",
        "withStderr": false
    },
    "prompts": {
        "file": "/etc/gomcp/prompts.yaml"
    },
    "tools": [
        {
            "name": "notion",
            "description": "Get a notion document",
            "configuration": {
                "notionToken": "ntn_<redacted>"
            }
        }
    ]
}

The serverInfo section is used to identify the server and its version, it is mandatory as they are used in the MCP protocolto identify the server.

The logging section is used to configure the logging system. The file field is the path to the log file, the level field is the logging level (debug, info, warn, error) and the withStderr field is used to redirect the logging to the standard error stream.

The prompts section is used to define the path to the YAML file containing the prompts to expose to the LLM. See below for a description of the YAML syntax to define the prompts.

The tools section is used to define the tools that will be exposed to the LLM. This is an array of tool providers, each provider is an object with a name and a description field. The configuration field is an object that contains the configuration for the tool provider.

In our case, we have a single tool provider called notion that has a single tool to retrieve the content of a Notion page.

The configuration for the notion tool provider is the Notion token.

This configuration must be backed by a Golang struct that will be used to parse the configuration file:

type NotionGetDocumentConfiguration struct {
	NotionToken string `json:"notionToken" jsonschema_description:"the notion token for the Notion client."`
}

The tags here (json and jsonschema_description) are used to generate the JSON Schema for the configuration data. If the configuration is invalid, the mcp command will fail to start.

You then create a function that will use those configuration data to generate a Tool Context:

func NotionToolInit(ctx context.Context, config *NotionGetDocumentConfiguration) (*NotionGetDocumentContext, error) {
	client := notionapi.NewClient(notionapi.Token(config.NotionToken))

	// we need to initialize the Notion client
	return &NotionGetDocumentContext{NotionClient: client}, nil
}

And the definition of the Tool Context:

type NotionGetDocumentContext struct {
	// The Notion client.
	NotionClient *notionapi.Client
}

This time, you don't need to add tags to your type as this struct is internal to the tool provider: the instance of this struct is created by the ToolInit function and is passed to the functions implementing the tool(s).

A tool function is defined like this:

func NotionGetPage(
        ctx context.Context, 
        toolCtx *NotionGetDocumentContext, 
        input *NotionGetDocumentInput, 
        output types.ToolCallResult) error {
	logger := gomcp.GetLogger(ctx)
	logger.Info("NotionGetPage", types.LogArg{
		"pageId": input.PageId,
	})

	content, err := getPageContent(ctx, toolCtx.NotionClient, input.PageId)
	if err != nil {
		return err
	}
	output.AddTextContent(strings.Join(content, "\n"))

	return nil
}

The first parameter is the context, it is mandatory as it is used to retrieve the logger.

The second parameter is the tool context, it is the instance of the struct created by the ToolInit function.

The third parameter is the input, it is an object that contains the input data for the tool call. Those input parameter are provided by the LLM when the tool is called.

The last parameter is the output, it is an interface that allows the function to construct the data it wants to return to the LLM.

Here again, the input type must be tagged appropriately:

type NotionGetDocumentInput struct {
	PageId string `json:"pageId" jsonschema_description:"the ID of the Notion page to retrieve."`
}

Those tags will be used to generate the JSON Schema for the input data:

  • they will be returned to the LLM during the discovery phase of the MCP protocol
  • they will be used to validate the input data when the tool is called

Once those typea and functions are defined, you can bind them in the MCP server by calling the RegisterTool function:

func RegisterTools(toolRegistry types.ToolRegistry) error {
	toolProvider, err := toolRegistry.DeclareToolProvider("notion", NotionToolInit)
	if err != nil {
		return err
	}
	err = toolProvider.AddTool("notion_get_page", "Get the markdown content of a notion page", NotionGetPage)
	if err != nil {
		return err
	}
	return nil
}

This RegisterTools function is called from the main() function of your MCP server:

package main

import (
	"flag"
	"fmt"
	"os"

	"github.com/llmcontext/gomcp"
	"github.com/llmcontext/mcpnotion/tools"
)

func main() {
	configFile := flag.String("configFile", "", "config file path (required)")
	flag.Parse()

	if *configFile == "" {
		fmt.Println("Config file is required")
		flag.PrintDefaults()
		os.Exit(1)
	}

	mcp, err := gomcp.NewModelContextProtocolServer(*configFile)
	if err != nil {
		fmt.Println("Error creating MCP server:", err)
		os.Exit(1)
	}
	toolRegistry := mcp.GetToolRegistry()

	err = tools.RegisterTools(toolRegistry)
	if err != nil {
		fmt.Println("Error registering tools:", err)
		os.Exit(1)
	}

	transport := mcp.StdioTransport()

	mcp.Start(transport)
}
  • gomcp.NewModelContextProtocolServer(*configFile) creates a new MCP server with the configuration file
  • mcp.GetToolRegistry() returns the tool registry, it is used to register the tools. The tools.RegisterTools is the function we defined earlier and that binds the tools to the MCP server.
  • mcp.StdioTransport() creates a new transport based on standard input/output streams. That's the transport used to integrate with the Claude desktop application.
  • mcp.Start(transport) starts the MCP server with the given transport

prompts definition file

The prompts definition file is a YAML file that defines the prompts to expose to the LLM.

Here is an example of a dummy prompts definition file:

prompts:
  - name: "hello"
    description: "Says hello"
    arguments:
      - name: "name"
        description: "The name to say hello to"
        required: true
    prompt: >
      Hello {{.name}}, how are you?
      Please respond in a friendly and engaging manner.

This file is self-explanatory:

  • the prompts section is an array of prompts
  • each prompt is an object with a name and a description field
  • the arguments section is an array of arguments, each argument is an object with a name, a description and a required field
  • the prompt section is the prompt to expose to the LLM. It uses the Go template syntax to embed the arguments in the prompt

Check the documentation here for more information on how to access the prompts from Claude.

integration with Claude desktop application

Check the README of the mcpnotion project for more information on how to integrate your MCP server with the Claude desktop application.

TODO

  • better error management: eg. failure when init functionc fails

Changelog

0.5.0

  • addtool to detect dead code: go install golang.org/x/tools/cmd/deadcode@latest

0.4.0

  • Add support for the proxy/multiplexer. In the configuration file, set the proxy.enabled field to true to enable the proxy. The proxy is a tool that allows you to proxy messages between any MCP server and the LLM.
  • Add pterm for the output of the proxy
  • Refactor the JSON protocol messages to handle both client and server messages
  • remove the fifo option for logging

0.3.0 - 2024-12-08

  • Add support for protocol debug file. In the configuration file, set the logging.protocolDebugFile field to the path to the file where to write the protocol debug information.
  • Add support for the inspector. In the configuration file, set the inspector.enabled and inspector.listenAddress fields to set the inspector server address. The inspector is a tool that allows you to inspect the messages exchanged between the MCP server and the LLM. Still in development.
  • Kill the MCP server when the parent process is init. Even though we use a context to stop the server and the goroutines, it seems that the parent process is not always killed when the MCP server is stopped. This is why we now kill the MCP server when the parent process is init.

0.2.0 - 2024-12-07

  • Change signature of mcp.Start(serverName, serverVersion, transport) to mcp.Start(transport), the server name and version are now read from the configuration file
  • Add support for prompts stored in a YAML file. File path is read from the configuration file.

[0.1.0] - deleted

  • Initial release
  • Support Tools for the Model Context Protocol

About

Unofficial Golang SDK for Anthropic Model Context Protocol

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Go 99.1%
  • Makefile 0.9%