By using this site, you agree to the Privacy Policy and Terms of Use.
Accept
World of SoftwareWorld of SoftwareWorld of Software
  • News
  • Software
  • Mobile
  • Computing
  • Gaming
  • Videos
  • More
    • Gadget
    • Web Stories
    • Trending
    • Press Release
Search
  • Privacy
  • Terms
  • Advertise
  • Contact
Copyright © All Rights Reserved. World of Software.
Reading: I Built a Custom MCP Server in Golang To Make Claude Smarter—Here’s How | HackerNoon
Share
Sign In
Notification Show More
Font ResizerAa
World of SoftwareWorld of Software
Font ResizerAa
  • Software
  • Mobile
  • Computing
  • Gadget
  • Gaming
  • Videos
Search
  • News
  • Software
  • Mobile
  • Computing
  • Gaming
  • Videos
  • More
    • Gadget
    • Web Stories
    • Trending
    • Press Release
Have an existing account? Sign In
Follow US
  • Privacy
  • Terms
  • Advertise
  • Contact
Copyright © All Rights Reserved. World of Software.
World of Software > Computing > I Built a Custom MCP Server in Golang To Make Claude Smarter—Here’s How | HackerNoon
Computing

I Built a Custom MCP Server in Golang To Make Claude Smarter—Here’s How | HackerNoon

News Room
Last updated: 2025/08/04 at 3:46 PM
News Room Published 4 August 2025
Share
SHARE

I feel like we’re at a point where the Model Context Protocol (MCP) feels almost synonymous with GenAI engineering. Anthropic introduced MCP in November 2024, revolutionizing GenAI engineering. It brought us to the point where, as engineers, we can implement various tools based on our use case, and direct the LLM of our choice to use those tools from the comfort of our favorite IDEs or desktop clients like Claude. Ever since its advent, there have been numerous MCP servers developed by enthusiastic engineers. This was possible due to the rapid development of MCP SDKs in various languages, including Python, TypeScript, and most recently, Golang. (yay 🚀). I saw this official proposal for a Golang SDK, and I knew I had to take it for a spin!

What is MCP, you ask?

Let’s start with a quick intro to MCP, which is short for Model Context Protocol. Until recently, AI engineering required a careful evaluation of the capabilities of various LLMs to select the right one. With MCP, you can select the LLM of your choice, and extend its capabilities by implementing custom tools and connecting to external data sources yourself! The main constructs of the protocol are:

  • MCP client
  • MCP server
  • Capabilities (Tools, Resources, Prompts)
  • The LLM

An MCP server has certain capabilities determined by:

  • Tools – The functions it can execute, such as list all emails from a sender, lookup a PR on Github
  • Resources – External data source, such as a list of documents that you want the LLM to refer to
  • Prompts – A set of templates that help users get desired answers fast

There are many MCP servers already available that you can start using for your applications. You can refer to this compilation of awesome MCP servers: https://github.com/punkpeye/awesome-mcp-servers

BYOM (Bring your own MCP server)

In this post, I want to go over how we can develop our own MCP server using Golang. A few days back, for whatever reaso,n I wanted to go over all repositories in the Kubernetes GitHub org.

Now I could have used the official GitHub MCP server, but while it offers a great toolset for repositories and organizations, it didn’t have a direct tool for listing all repositories in an organization. That’s why this seemed like good opportunity to learn how to develop an MCP server for the specific tool I needed while using Golang.

Developing an MCP server in Golang

This is the official Golang MCP SDK: https://github.com/modelcontextprotocol/go-sdk. The README and examples/ folders contain great examples on developing an MCP server and client.

Following those examples, I set out to create an MCP server as follows:

	server := mcp.NewServer(&mcp.Implementation{
		Name:    "demo-github-mcp",
		Title:   "A demo github mcp server",
		Version: "0.0.1",
	}, nil)

The next step was to provide the server with the capability of listing all repositories in a GitHub org. This can be done by implementing a tool of type ToolHandler as provided by the SDK

type ToolHandlerFor[In, Out any] func(context.Context, *ServerSession, *CallToolParamsFor[In]) (*CallToolResultFor[Out], error)

Following that, I created a ListRepositories tool, which accepts the following arguments related to the Github org as input:

// User can pass in either the name of the org (example: kubernetes), or its URL (example: https://github.com/kubernetes)
type GithubOrgArgs struct {
	Name string
	URL  string
}

func ListRepositories(ctx context.Context, ss *mcp.ServerSession, params *mcp.CallToolParamsFor[GithubOrgArgs]) (*mcp.CallToolResultFor[struct{}], error) {

Now let’s go over the body of ListRepositories step-by-step:

  1. Input verification to ensure that we only accept valid arguments
    if params == nil {
		return nil, fmt.Errorf("empty params")
	}

	args := params.Arguments
	if args.Name == "" && args.URL == "" {
		return nil, fmt.Errorf("empty args")
	}
  1. Forming the GitHub API URL from the input based on GitHub API docs
	var apiURL string
	var organization string
	if args.URL != "" {
		// If URL is provided, extract org name and build API URL
		url := strings.TrimPrefix(args.URL, "https://")
		url = strings.TrimPrefix(url, "http://")
		url = strings.TrimPrefix(url, "github.com/")
		url = strings.TrimSuffix(url, "/")

		orgName := strings.Split(url, "/")[0]
		apiURL = fmt.Sprintf("https://api.github.com/orgs/%s/repos", orgName)
		organization = orgName
	} else {
		// Use the provided organization name
		apiURL = fmt.Sprintf("https://api.github.com/orgs/%s/repos", args.Name)
		organization = args.Name
	}
	apiURL = apiURL + "?per_page=100"
  1. Send the request and receive a response
	client := &http.Client{Timeout: 10 * time.Second}
	req, err := http.NewRequestWithContext(ctx, "GET", apiURL, nil)
	if err != nil {
		return nil, err
	}

	req.Header.Add("Accept", "application/vnd.github.v3+json")

	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		body, _ := io.ReadAll(resp.Body)
		return nil, fmt.Errorf("GitHub API error (status %d): %s", resp.StatusCode, string(body))
	}
  1. Parse the response from the GitHub API
	type repository struct {
		Name     string `json:"name"`
		FullName string `json:"full_name"`
		HTMLURL  string `json:"html_url"`
		Private  bool   `json:"private"`
	}

	// Parse the JSON response
	var repositories []repository
	if err := json.NewDecoder(resp.Body).Decode(&repositories); err != nil {
		return nil, fmt.Errorf("failed to parse response: %w", err)
	}
  1. Return the response as a textual context
	var result strings.Builder
	result.WriteString(fmt.Sprintf("Repositories for organization %s:", organization))
	for _, repo := range repositories {
		result.WriteString(fmt.Sprintf("Name: %s, URL: %s", repo.Name, repo.HTMLURL))
	}

	return &mcp.CallToolResultFor[struct{}]{
		Content: []mcp.Content{
			&mcp.TextContent{Text: result.String()},
		},
	}, nil

After defining the tool, the next step is to register it with the MCP server:

	mcp.AddTool(server, &mcp.Tool{
		Name:        "list-repositories",
		Description: "A tool to list all repositories in a Github org",
		InputSchema: &jsonschema.Schema{
			Type: "object",
			Properties: map[string]*jsonschema.Schema{
				"name": {
					Type:        "string",
					Description: "GitHub organization name (e.g., kubernetes)",
				},
				"url": {
					Type:        "string",
					Description: "GitHub organization URL (e.g., https://github.com/kubernetes)",
				},
			},
		},
	}, ListRepositories)

Next, it’s time to start the server! For this demo MCP server, I’m using the stdio transport, which allows the server to communicate via STDIN and STDOUT. This is the standard approach for local MCP integrations with clients like Claude Desktop or VSCode.

	t := mcp.NewLoggingTransport(mcp.NewStdioTransport(), os.Stderr)
	log.Println("🚀 MCP server starting up...")
	if err := server.Run(context.Background(), t); err != nil {
		log.Printf("Server failed: %v", err)
	}
	log.Println("🚀 MCP server shutting down...")

This is what the code looks like with everything put together:

func main() {
	server := mcp.NewServer(&mcp.Implementation{
		Name:    "demo-github-mcp",
		Title:   "A demo github mcp server",
		Version: "0.0.1",
	}, nil)

	mcp.AddTool(server, &mcp.Tool{
		Name:        "list-repositories",
		Description: "A tool to list all repositories in a Github org",
		InputSchema: &jsonschema.Schema{
			Type: "object",
			Properties: map[string]*jsonschema.Schema{
				"name": {
					Type:        "string",
					Description: "GitHub organization name (e.g., kubernetes)",
				},
				"url": {
					Type:        "string",
					Description: "GitHub organization URL (e.g., https://github.com/kubernetes)",
				},
			},
		},
	}, ListRepositories)

	t := mcp.NewLoggingTransport(mcp.NewStdioTransport(), os.Stderr)
	log.Println("🚀 MCP server starting up...")
	if err := server.Run(context.Background(), t); err != nil {
		log.Printf("Server failed: %v", err)
	}
	log.Println("🚀 MCP server shutting down...")
}

type GithubOrgArgs struct {
	Name string
	URL  string
}

func ListRepositories(ctx context.Context, ss *mcp.ServerSession, params *mcp.CallToolParamsFor[GithubOrgArgs]) (*mcp.CallToolResultFor[struct{}], error) {
	if params == nil {
		return nil, fmt.Errorf("empty params")
	}

	args := params.Arguments
	if args.Name == "" && args.URL == "" {
		return nil, fmt.Errorf("empty args")
	}

	var apiURL string
	var organization string
	if args.URL != "" {
		// If URL is provided, extract org name and build API URL
		url := strings.TrimPrefix(args.URL, "https://")
		url = strings.TrimPrefix(url, "http://")
		url = strings.TrimPrefix(url, "github.com/")
		url = strings.TrimSuffix(url, "/")

		orgName := strings.Split(url, "/")[0]
		apiURL = fmt.Sprintf("https://api.github.com/orgs/%s/repos", orgName)
		organization = orgName
	} else {
		// Use the provided organization name
		apiURL = fmt.Sprintf("https://api.github.com/orgs/%s/repos", args.Name)
		organization = args.Name
	}
	apiURL = apiURL + "?per_page=100"

	client := &http.Client{Timeout: 10 * time.Second}
	req, err := http.NewRequestWithContext(ctx, "GET", apiURL, nil)
	if err != nil {
		return nil, err
	}

	req.Header.Add("Accept", "application/vnd.github.v3+json")

	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		body, _ := io.ReadAll(resp.Body)
		return nil, fmt.Errorf("GitHub API error (status %d): %s", resp.StatusCode, string(body))
	}

	type repository struct {
		Name     string `json:"name"`
		FullName string `json:"full_name"`
		HTMLURL  string `json:"html_url"`
		Private  bool   `json:"private"`
	}

	// Parse the JSON response
	var repositories []repository
	if err := json.NewDecoder(resp.Body).Decode(&repositories); err != nil {
		return nil, fmt.Errorf("failed to parse response: %w", err)
	}

	var result strings.Builder
	result.WriteString(fmt.Sprintf("Repositories for organization %s:", organization))
	for _, repo := range repositories {
		result.WriteString(fmt.Sprintf("Name: %s, URL: %s", repo.Name, repo.HTMLURL))
	}

	return &mcp.CallToolResultFor[struct{}]{
		Content: []mcp.Content{
			&mcp.TextContent{Text: result.String()},
		},
	}, nil
}

Now the final step is to compile this and generate the executable with:

go build

Using the MCP server with a client like Claude

Let’s look at how to add this server to Claude’s desktop.

  1. Open the Claude desktop app, and go to Claude -> Settings -> Developer.
  2. Here you will see a section called Local MCP Servers. Click on the button below called Edit Config. This will open up the Claude config file.
  3. Edit the file to add the MCP server as follows:
{
    "mcpServers": {
        "demo-github-mcp": {
            "command": "/path/to/executable/generated/from/go build",
            "args": []
        }
    }
}
  1. Restart the Claude app (Currently, this is the only way to refresh MCP servers within Claude)

And now it’s time to test this out! This is the prompt I gave:

List all repositories in the Kubernetes github org

Claude recognized the locally running demo-github-mcp server and asked if it could use that!

As soon as I approved it, Claude listed the repositories in the org and even displayed the tool used (list-repositories) at the start of the response:

And there we go! We saw how we can develop a simple MCP server using Golang, and use it through MCP clients such as Claude!

The “AHA” moment

As I was developing this server, I kept asking myself: “If I’m writing the code for listing repositories, what is the LLM going to do?”, and then suddenly it clicked – the LLM enables communication to this server via natural language!

When Claude Sonnet 4 read my prompt – “List all repositories in the Kubernetes GitHub org” – and on its own recognized that the locally running MCP server would work best for the prompt, my mind was blown away 😀. At that point, I didn’t have to provide it with any information on the tool name or the parameters it accepted. I asked my question through a simple sentence, and it figured out which tool to use, how to call the tool, and got the job done!

Production Considerations

While our MCP server works great for demo purposes, there are several important features missing for production use:

  • Pagination handling: GitHub’s API returns paginated results, the default being 30 entries per page. In the above code I’ve increased that to 100 with the per_page query parameter. For production code, you’d process the response header Link to get information about the next page and total number of pages. (Refer to this)
  • Rate limiting: The above code does not take into account GitHub API rate limits
  • Authentication: Authenticated requests have a higher rate limit than unauthenticated requests. Authentication is also required for requests to private GitHub repositories/enterprise GitHub.

What’s next?

This demo GitHub MCP server was a good start. But the main reason I’m writing this is to demonstrate how easy it can be to customize your favorite GenAI tool to your liking! It’s also surprisingly simple to get started with an MCP server in Golang for your own projects! I can’t wait to see the powerful services created from the combination of MCP’s flexibility and Golang’s high-performing nature!

Sign Up For Daily Newsletter

Be keep up! Get the latest breaking news delivered straight to your inbox.
By signing up, you agree to our Terms of Use and acknowledge the data practices in our Privacy Policy. You may unsubscribe at any time.
Share This Article
Facebook Twitter Email Print
Share
What do you think?
Love0
Sad0
Happy0
Sleepy0
Angry0
Dead0
Wink0
Previous Article Tesla CEO Elon Musk reveals ideal timeline for insane self-driving feature
Next Article Android may soon use AI to organize your phone’s notifications
Leave a comment

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Stay Connected

248.1k Like
69.1k Follow
134k Pin
54.3k Follow

Latest News

Today's NYT Connections: Sports Edition Hints, Answers for Aug. 5 #316
News
Boca Raton Trailblazers Partners with BTZO Exchange in Max60 Caribbean Cricket League 2025 | HackerNoon
Computing
Proliferation of on-premise GenAI platforms is widening security risks | Computer Weekly
News
Before You Spring for More Gear, Here’s Which Original Switch Accessories Work With the Switch 2
News

You Might also Like

Computing

Boca Raton Trailblazers Partners with BTZO Exchange in Max60 Caribbean Cricket League 2025 | HackerNoon

2 Min Read
Computing

Seattle leaders pass tax hike for big companies, cuts for small biz – but voters get final word

5 Min Read
Computing

How to Circumvent Impossibility Theorems in Blockchain Mechanism Design | HackerNoon

13 Min Read
Computing

EU Doubles Down on AI Red Tape With a Rulebook for the Rulebook | HackerNoon

4 Min Read
//

World of Software is your one-stop website for the latest tech news and updates, follow us now to get the news that matters to you.

Quick Link

  • Privacy Policy
  • Terms of use
  • Advertise
  • Contact

Topics

  • Computing
  • Software
  • Press Release
  • Trending

Sign Up for Our Newsletter

Subscribe to our newsletter to get our newest articles instantly!

World of SoftwareWorld of Software
Follow US
Copyright © All Rights Reserved. World of Software.
Welcome Back!

Sign in to your account

Lost your password?