• About Us
  • Privacy Policy
  • Disclaimer
  • Contact Us
AimactGrow
  • Home
  • Technology
  • AI
  • SEO
  • Coding
  • Gaming
  • Cybersecurity
  • Digital marketing
No Result
View All Result
  • Home
  • Technology
  • AI
  • SEO
  • Coding
  • Gaming
  • Cybersecurity
  • Digital marketing
No Result
View All Result
AimactGrow
No Result
View All Result

Construct an MCP Server in Go: A Manufacturing-Prepared Tutorial for the Mannequin Context Protocol

Admin by Admin
June 30, 2026
Home Coding
Share on FacebookShare on Twitter

The Mannequin Context Protocol (MCP) is a standardized interface for connecting AI fashions to exterior instruments and information sources. Constructing an MCP server in Go presents distinct benefits for manufacturing deployments, combining Go’s concurrency mannequin and single-binary compilation with a protocol designed to finish the fragmented, vendor-specific integration sample that has held again AI tooling. This tutorial walks by means of the entire means of establishing a production-ready MCP server, from preliminary scaffolding to swish shutdown, utilizing the mcp-go SDK.

Methods to Construct an MCP Server in Go

  1. Initialize a Go module and pin the mcp-go SDK to a selected model with go get.
  2. Create the server entry level utilizing server.NewMCPServer with functionality choices for instruments, assets, and prompts.
  3. Outline software schemas with mcp.NewTool, specifying enter parameters, varieties, and descriptions.
  4. Implement software handlers that validate inputs, name exterior APIs with timeouts, and return outcomes by way of CallToolResult.
  5. Register assets and immediate templates to reveal read-only information and reusable interplay patterns.
  6. Configure structured logging to stderr and add enter validation with allowlist patterns for safety.
  7. Add swish shutdown utilizing OS sign seize and context cancellation as an alternative of os.Exit.
  8. Begin the server on stdio transport for native shoppers or HTTP/SSE transport for distant deployment.

Desk of Contents

What Is the Mannequin Context Protocol (MCP) and Why Does It Matter?

The Drawback MCP Solves

Massive language fashions are remoted. With out exterior integrations, they can’t entry real-time information, work together with APIs, or carry out actions on this planet. Earlier than MCP, connecting an AI mannequin to a software or information supply required constructing vendor-specific integrations for every mixture of mannequin and repair. This created an M×N drawback: M fashions occasions N instruments, every requiring a customized connector.

MCP eliminates this by defining a single, open protocol that any AI mannequin can use to speak with any appropriate server. The protocol is open-source. Purchasers throughout the ecosystem have adopted it, together with Claude Desktop, VS Code with GitHub Copilot, Cursor, and different MCP-compatible shoppers.

MCP Structure at a Look

MCP follows a client-server structure with three distinct roles. Hosts are the AI functions (like Claude Desktop) that provoke connections. Purchasers are protocol-level connectors maintained throughout the host, every establishing a one-to-one session with a server. Servers expose capabilities to the AI mannequin by means of three core primitives.

Instruments are executable features the mannequin can invoke, roughly analogous to POST endpoints. Learn-only information entry comes by means of Assets, that are recognized by URIs and behave like GET endpoints. Prompts are reusable template messages with arguments that construction how the mannequin interacts with the server’s capabilities.

Communication occurs over one in all a number of transport mechanisms: stdio (commonplace enter/output) for native processes, HTTP with Server-Despatched Occasions (SSE), or the newer Streamable HTTP transport for stateless distant deployments.

MCP eliminates this by defining a single, open protocol that any AI mannequin can use to speak with any appropriate server.

Why Construct Your MCP Server in Go?

Go’s Strengths for MCP Servers

Go’s goroutine-based concurrency mannequin maps naturally to MCP’s requirement for dealing with a number of simultaneous shopper connections and power invocations. A single MCP server could subject concurrent requests from a number of AI periods. Goroutines begin with a stack of roughly 2-8 KB, in comparison with the 1-8 MB typical of OS thread stacks, so a server can maintain hundreds of concurrent handlers with out important reminiscence strain.

The only-binary compilation mannequin simplifies deployment significantly. An MCP server inbuilt Go compiles to a standalone executable with no runtime dependencies, making it straightforward to distribute, containerize, or reference from a shopper’s configuration file. Go’s commonplace library supplies HTTP primitives with configurable timeouts, connection pooling, and HTTP/2 help, which the mcp-go SDK builds upon for MCP’s transport layer. Go’s sort system enforces handler interface compliance at compile time; software enter schema validation happens at runtime throughout the SDK.

Stipulations and Mission Setup

What You may Want

This tutorial requires Go 1.21 or later put in and out there on the system PATH (confirm with go model). It is best to know Go modules, structs, and interface patterns. Any editor with Go help will work, although VS Code with the Go extension or GoLand supplies jump-to-definition for SDK varieties, which helps when exploring the API floor.

You want an MCP-compatible shopper for testing. Claude Desktop requires solely a JSON config entry pointing to the binary plus a restart, making it the quickest choice for native stdio servers. VS Code with GitHub Copilot’s agent mode additionally helps MCP server connections.

Initializing the Mission

mkdir mcp-go-server
cd mcp-go-server
go mod init github.com/yourname/mcp-go-server
go get github.com/mark3labs/mcp-go@v0.26.0

Word: Pin the SDK model explicitly. Change v0.26.0 with the present secure launch listed at github.com/mark3labs/mcp-go/releases. Working go get with out a model tag fetches the most recent, which can introduce breaking adjustments after this tutorial was revealed.

The mcp-go SDK by Mark3Labs is a well-adopted Go implementation of the MCP specification. It supplies server building primitives, transport handlers, and helper features for outlining software schemas.

Constructing Your First MCP Server with stdio Transport

Creating the Server Entry Level

The minimal MCP server requires initialization with a reputation and model, then startup on a transport. The stdio transport communicates over commonplace enter and output, which is the anticipated mechanism when an MCP shopper launches the server as a subprocess.

bundle essential

import (
	"fmt"
	"os"

	"github.com/mark3labs/mcp-go/mcp"
	"github.com/mark3labs/mcp-go/server"
)

func essential() {
	s := server.NewMCPServer(
		"my-mcp-server",
		"1.0.0",
		server.WithToolCapabilities(true),              
		server.WithResourceCapabilities(true, false),   
		server.WithPromptCapabilities(true),
	)

	if err := server.ServeStdio(s); err != nil {
		fmt.Fprintf(os.Stderr, "Server error: %v
", err)
		os.Exit(1)
	}
}

The server.NewMCPServer operate takes the server identify and model as its first two arguments. The choice features declare which MCP capabilities this server helps. WithToolCapabilities(true) advertises that the server can notify shoppers when its software record adjustments. WithResourceCapabilities(true, false) allows useful resource itemizing however not useful resource subscriptions. The server.ServeStdio name blocks, studying JSON-RPC messages from stdin and writing responses to stdout.

Registering Your First Instrument

Instruments are the first mechanism by means of which an AI mannequin takes motion by way of an MCP server. Every software requires a schema defining its inputs and a handler operate that executes the logic.

bundle essential

import (
	"context"
	"fmt"
	"os"

	"github.com/mark3labs/mcp-go/mcp"
	"github.com/mark3labs/mcp-go/server"
)

func essential() {
	s := server.NewMCPServer(
		"my-mcp-server",
		"1.0.0",
		server.WithToolCapabilities(true),
	)

	helloTool := mcp.NewTool("hiya",
		mcp.WithDescription("Greets a person by identify"),
		mcp.WithString("identify",
			mcp.Required(),
			mcp.Description("The identify of the individual to greet"),
		),
	)

	s.AddTool(helloTool, helloHandler)

	if err := server.ServeStdio(s); err != nil {
		fmt.Fprintf(os.Stderr, "Server error: %v
", err)
		os.Exit(1)
	}
}

func helloHandler(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
	identify, okay := request.Params.Arguments["name"].(string)
	if !okay || identify == "" {
		return mcp.NewToolResultError("identify parameter is required"), nil
	}

	greeting := fmt.Sprintf("Good day, %s! Welcome to the MCP server.", identify)
	return mcp.NewToolResultText(greeting), nil
}

The mcp.NewTool operate creates a software definition with a JSON Schema for its inputs. Helper features like mcp.WithString, mcp.Required(), and mcp.Description() construct the schema declaratively. The handler receives a CallToolRequest and returns a *mcp.CallToolResult. Word that the "context" import is required for the handler’s context.Context parameter.

Testing with an MCP Consumer

To check with Claude Desktop, add a server entry to the configuration file. On macOS, discover it at ~/Library/Software Help/Claude/claude_desktop_config.json. On Home windows, it lives at %APPDATApercentClaudeclaude_desktop_config.json. On Linux, it lives at ~/.config/Claude/claude_desktop_config.json.

{
  "mcpServers": {
    "my-mcp-server": {
      "command": "/absolute/path/to/mcp-go-server"
    }
  }
}

Compile the binary first with go construct -o mcp-go-server . and use absolutely the path to the ensuing executable. After restarting Claude Desktop, the software ought to seem within the out there instruments record. Asking Claude to “say hiya to Alice” ought to set off the software invocation. If the software would not seem, examine Claude Desktop’s MCP log output for configuration or path errors.

Including Assets and Prompts

Exposing Assets

MCP assets characterize read-only information that the AI mannequin can retrieve. Every useful resource is recognized by a URI and returns content material with a specified MIME sort. Assets are appropriate for exposing configuration information, documentation, or database information.

s.AddResource(mcp.NewResource(
	"config://app/settings",
	"Software Settings",
	mcp.WithResourceDescription("Present utility configuration"),
	mcp.WithMIMEType("utility/json"),
), func(ctx context.Context, request mcp.ReadResourceRequest) ([]mcp.ResourceContents, error) {
	settings := `{"debug": false, "max_connections": 100, "area": "us-east-1"}`
	return []mcp.ResourceContents{
		mcp.NewTextResourceContents(request.Params.URI, "utility/json", settings),
	}, nil
})

The useful resource handler returns a slice of ResourceContents, permitting a single URI to return a number of items of content material. For dynamic assets the place the URI incorporates variable segments, mcp.NewResourceTemplate can be utilized with URI templates following RFC 6570 syntax.

Defining Immediate Templates

Prompts present reusable message templates that information how the AI mannequin interacts with the server. They settle for arguments and return structured message sequences.

s.AddPrompt(mcp.NewPrompt("summarize_issue",
	mcp.WithPromptDescription("Summarize a GitHub challenge for a standing report"),
	mcp.WithArgument("issue_title",
		mcp.ArgumentDescription("The title of the difficulty"),
		mcp.RequiredArgument(),
	),
	mcp.WithArgument("issue_body",
		mcp.ArgumentDescription("The physique content material of the difficulty"),
	),
), func(ctx context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) {
	title, okay := request.Params.Arguments["issue_title"].(string)
	if !okay || title == "" {
		return nil, fmt.Errorf("issue_title is required and have to be a non-empty string")
	}
	physique, _ := request.Params.Arguments["issue_body"].(string)

	return &mcp.GetPromptResult{
		Description: "Summarize a GitHub challenge",
		Messages: []mcp.PromptMessage{
			{
				Function: mcp.RoleUser,
				Content material: mcp.TextContent{
					Kind: "textual content",
					Textual content: fmt.Sprintf("Summarize this GitHub challenge for a standing report.

Title: %s

Physique: %s", title, physique),
				},
			},
		},
	}, nil
})

The immediate handler returns a GetPromptResult containing a sequence of messages with outlined roles. This permits servers to supply structured interplay patterns that shoppers can current to customers or inject into mannequin conversations.

Designing the Instrument Schema

A sensible MCP software demonstrates the combination sample for exterior APIs. A GitHub challenge lookup software wants three parameters: the repository proprietor, repository identify, and challenge quantity.

Implementing the Instrument Handler

import "regexp"


var validIdentifier = regexp.MustCompile(`^[a-zA-Z0-9_.-]{1,100}$`)

const maxBodyBytes = 1 << 20 

var githubHTTPClient = &http.Consumer{
	Timeout: 10 * time.Second,
	Transport: &http.Transport{
		ResponseHeaderTimeout: 5 * time.Second,
		TLSHandshakeTimeout:   5 * time.Second,
		MaxIdleConnsPerHost:   10,
	},
}

func githubIssueTool() (mcp.Instrument, server.ToolHandlerFunc) {
	software := mcp.NewTool("github_issue_lookup",
		mcp.WithDescription("Lookup a GitHub challenge by proprietor, repo, and challenge quantity"),
		mcp.WithString("proprietor", mcp.Required(), mcp.Description("Repository proprietor")),
		mcp.WithString("repo", mcp.Required(), mcp.Description("Repository identify")),
		mcp.WithNumber("issue_number", mcp.Required(), mcp.Description("Difficulty quantity")),
	)

	handler := func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
		proprietor, _ := request.Params.Arguments["owner"].(string)
		repo, _ := request.Params.Arguments["repo"].(string)
		issueNum, _ := request.Params.Arguments["issue_number"].(float64)

		if proprietor == "" || repo == "" || issueNum < 1 {
			return mcp.NewToolResultError("proprietor, repo, and issue_number are required (issue_number have to be >= 1)"), nil
		}

		
		if !validIdentifier.MatchString(proprietor) || !validIdentifier.MatchString(repo) {
			return mcp.NewToolResultError("proprietor and repo should include solely alphanumeric characters, hyphens, underscores, or dots (max 100 chars)"), nil
		}

		slog.Information("software invoked", "software", "github_issue_lookup", "proprietor", proprietor, "repo", repo, "issue_number", int(issueNum))

		apiURL := fmt.Sprintf("https://api.github.com/repos/%s/%s/points/%d", proprietor, repo, int(issueNum))

		req, err := http.NewRequestWithContext(ctx, http.MethodGet, apiURL, nil)
		if err != nil {
			return mcp.NewToolResultError(fmt.Sprintf("didn't construct request: %v", err)), nil
		}
		req.Header.Set("Consumer-Agent", "mcp-go-server/1.0.0")
		req.Header.Set("Settle for", "utility/vnd.github+json")

		
		if token := os.Getenv("GITHUB_TOKEN"); token != "" {
			req.Header.Set("Authorization", "Bearer "+token)
		}

		resp, err := githubHTTPClient.Do(req)
		if err != nil {
			return mcp.NewToolResultError(fmt.Sprintf("API request failed: %v", err)), nil
		}
		defer resp.Physique.Shut()

		
		
		if resp.StatusCode == http.StatusForbidden {
			remaining := resp.Header.Get("X-RateLimit-Remaining")
			if remaining == "0" {
				return &mcp.CallToolResult{
					Content material: []mcp.Content material{mcp.TextContent{
						Kind: "textual content",
						Textual content: "GitHub API main price restrict exceeded. Set GITHUB_TOKEN to lift limits.",
					}},
					IsError: true,
				}, nil
			}
			return mcp.NewToolResultError("GitHub API returned 403 Forbidden (examine token permissions or repo visibility)"), nil
		}
		if resp.StatusCode == http.StatusTooManyRequests {
			return &mcp.CallToolResult{
				Content material: []mcp.Content material{mcp.TextContent{
					Kind: "textual content",
					Textual content: "GitHub API secondary price restrict hit. Wait earlier than retrying.",
				}},
				IsError: true,
			}, nil
		}

		if resp.StatusCode != http.StatusOK {
			return mcp.NewToolResultError(fmt.Sprintf("GitHub API returned standing %d", resp.StatusCode)), nil
		}

		var challenge struct {
			Title  string `json:"title"`
			State  string `json:"state"`
			Physique   string `json:"physique"`
			Labels []struct {
				Title string `json:"identify"`
			} `json:"labels"`
		}

		limitedBody := io.LimitReader(resp.Physique, maxBodyBytes)
		if err := json.NewDecoder(limitedBody).Decode(&challenge); err != nil {
			return mcp.NewToolResultError(fmt.Sprintf("didn't parse response: %v", err)), nil
		}

		labelNames := make([]string, len(challenge.Labels))
		for i, l := vary challenge.Labels {
			labelNames[i] = l.Title
		}

		end result := fmt.Sprintf("Title: %s
State: %s
Labels: %s

%s",
			challenge.Title, challenge.State, strings.Be part of(labelNames, ", "), challenge.Physique)
		return mcp.NewToolResultText(end result), nil
	}

	return software, handler
}

Word that issue_number arrives as float64 as a result of JSON numbers are unmarshaled to float64 in Go’s interface{} sort system. It is a frequent supply of bugs when working with JSON-RPC in Go.

Essential: GitHub’s API requires a Consumer-Agent header on all requests. Calls with out one could obtain an HTTP 403 response. The handler above units a Consumer-Agent and in addition helps an non-compulsory GITHUB_TOKEN setting variable to lift price limits from 60 requests/hour (unauthenticated) to five,000 requests/hour.

Dealing with Errors Gracefully

The software handler above already consists of rate-limit detection, however the sample is price highlighting explicitly:



if resp.StatusCode == http.StatusForbidden {
	remaining := resp.Header.Get("X-RateLimit-Remaining")
	if remaining == "0" {
		return &mcp.CallToolResult{
			Content material: []mcp.Content material{mcp.TextContent{
				Kind: "textual content",
				Textual content: "GitHub API main price restrict exceeded. Set GITHUB_TOKEN to lift limits.",
			}},
			IsError: true,
		}, nil
	}
	return mcp.NewToolResultError("GitHub API returned 403 Forbidden (examine token permissions or repo visibility)"), nil
}
if resp.StatusCode == http.StatusTooManyRequests {
	return &mcp.CallToolResult{
		Content material: []mcp.Content material{mcp.TextContent{
			Kind: "textual content",
			Textual content: "GitHub API secondary price restrict hit. Wait earlier than retrying.",
		}},
		IsError: true,
	}, nil
}

MCP error reporting makes use of the IsError: true subject on CallToolResult reasonably than returning a Go error. Returning a Go error from the handler alerts a protocol-level failure, whereas IsError: true alerts an application-level error that the mannequin can cause about and probably retry or clarify to the person.

Returning a Go error from the handler alerts a protocol-level failure, whereas IsError: true alerts an application-level error that the mannequin can cause about and probably retry or clarify to the person.

Manufacturing Hardening

Structured Logging

When utilizing stdio transport, stdout is solely reserved for MCP protocol messages. Any log output written to stdout will corrupt the JSON-RPC stream and break communication. Direct all logging to stderr or a file.

logger := slog.New(slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{
	Stage: slog.LevelInfo,
}))
slog.SetDefault(logger)


slog.Information("software invoked", "software", "github_issue_lookup", "proprietor", proprietor, "repo", repo)

Go’s slog bundle, out there since Go 1.21, supplies structured logging with JSON output that integrates properly with log aggregation methods. Writing to stderr retains the stdio transport clear whereas offering full observability.

Enter Validation and Safety

All software inputs have to be validated earlier than use. String parameters destined for URL building must be validated in opposition to a strict allowlist sample (e.g., ^[a-zA-Z0-9_.-]{1,100}$) reasonably than a denylist of particular characters, as denylist approaches miss URL-encoded variants and different bypass strategies. When software outputs embody content material retrieved from exterior sources, that content material may include immediate injection makes an attempt. Servers shouldn’t try and sanitize this content material, as that’s the mannequin’s duty, however must be conscious that software outputs circulation immediately into the mannequin’s context window.

Add price limiting (e.g., a per-client token bucket) for any HTTP-transported server serving a number of shoppers. The mcp-go SDK doesn’t present built-in price limiting (confirm in opposition to the SDK model you might be utilizing), so middleware or exterior options are wanted.

Swish Shutdown

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

sigChan := make(chan os.Sign, 1)
sign.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)

go func() {
	choose {
	case sig := <-sigChan:
		slog.Information("shutdown sign obtained", "sign", sig.String())
		cancel()
	case <-ctx.Carried out():
		
	}
}()

This sample captures interrupt and termination alerts and cancels the context, which must be threaded by means of to energetic software handlers to allow cooperative cancellation of in-flight requests. The choose on each sigChan and ctx.Carried out() ensures the goroutine exits cleanly even when the server shuts down for a cause apart from an OS sign. After cancel() known as, the ServeStdio name (or HTTP server) ought to detect the cancelled context and return, permitting deferred cleanup features in essential to execute usually.

Warning: Keep away from calling os.Exit() contained in the sign goroutine. os.Exit terminates the method instantly, bypassing all deferred features, that means database connections, file handles, and log buffers won’t be cleaned up. As an alternative, cancel the context and let the server shut down by means of its regular return path.

Switching to HTTP/SSE Transport for Distant Deployment

When to Use HTTP vs. stdio

The stdio transport is acceptable when the MCP shopper spawns the server as a neighborhood subprocess. That is the usual mode for Claude Desktop and most IDE integrations. For distant deployments, multi-client entry, or cloud-hosted servers, HTTP-based transports are required.

Word: HTTP and stdio transports are mutually unique startup paths. Use one or the opposite in a given binary invocation, not each.


sseServer := server.NewSSEServer(s, server.WithBaseURL("http://localhost:8080"))
if err := sseServer.Begin(":8080"); err != nil {
	slog.Error("SSE server failed", "error", err)
}


httpServer := server.NewStreamableHTTPServer(s)
if err := httpServer.Begin(":8080"); err != nil {
	slog.Error("HTTP server failed", "error", err)
}

For distant or manufacturing deployments, use your TLS-terminated HTTPS URL as the bottom URL (e.g., server.WithBaseURL("https://your-server.instance.com")). TLS termination may be dealt with by a reverse proxy resembling Nginx or Caddy, as mentioned in Subsequent Steps beneath.

The SSE transport maintains a persistent connection for server-to-client notifications, whereas Streamable HTTP helps stateless request-response patterns appropriate for serverless and load-balanced deployments. The server logic stays equivalent throughout transports; solely the startup name adjustments.

Manufacturing-Prepared Implementation Guidelines

Earlier than deploying an MCP server, confirm every of this stuff:

  • Go module initialized with a pinned mcp-go model in go.mod
  • Server identify and model set in ServerInfo by way of NewMCPServer
  • All instruments have full enter schemas with descriptions for each parameter
  • Instrument handlers validate all inputs earlier than use, utilizing allowlist patterns for URL-destined strings
  • HTTP requests to exterior APIs embody a Consumer-Agent header and a timeout
  • Response our bodies from exterior APIs are size-limited (e.g., by way of io.LimitReader)
  • Report errors by way of IsError: true on CallToolResult, by no means panics
  • Direct logging to stderr (stdio transport) or a structured logger (HTTP transport)
  • Swish shutdown on OS alerts applied by way of context cancellation (not os.Exit)
  • Assets use acceptable MIME varieties
  • If deploying by way of HTTP, add price limiting on the server or reverse-proxy stage
  • Transport chosen primarily based on deployment goal: stdio for native, HTTP/SSE for distant
  • Examined with at the least one MCP shopper (Claude Desktop or VS Code)
  • Construct and take a look at the binary on course OS/structure by way of GOOS and GOARCH

Full Server Code

The next consolidated implementation combines all parts lined on this tutorial right into a single, copy-paste-ready essential.go file.

bundle essential

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"log/slog"
	"web/http"
	"os"
	"os/sign"
	"regexp"
	"strings"
	"syscall"
	"time"

	"github.com/mark3labs/mcp-go/mcp"
	"github.com/mark3labs/mcp-go/server"
)


var validIdentifier = regexp.MustCompile(`^[a-zA-Z0-9_.-]{1,100}$`)

const maxBodyBytes = 1 << 20 

var githubHTTPClient = &http.Consumer{
	Timeout: 10 * time.Second,
	Transport: &http.Transport{
		ResponseHeaderTimeout: 5 * time.Second,
		TLSHandshakeTimeout:   5 * time.Second,
		MaxIdleConnsPerHost:   10,
	},
}

func essential() {
	logger := slog.New(slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{Stage: slog.LevelInfo}))
	slog.SetDefault(logger)

	s := server.NewMCPServer(
		"go-mcp-production-server",
		"1.0.0",
		server.WithToolCapabilities(true),            
		server.WithResourceCapabilities(true, false), 
		server.WithPromptCapabilities(true),
	)

	
	software, handler := githubIssueTool()
	s.AddTool(software, handler)

	
	s.AddResource(mcp.NewResource(
		"config://app/settings",
		"Software Settings",
		mcp.WithResourceDescription("Present utility configuration"),
		mcp.WithMIMEType("utility/json"),
	), func(ctx context.Context, request mcp.ReadResourceRequest) ([]mcp.ResourceContents, error) {
		return []mcp.ResourceContents{
			mcp.NewTextResourceContents(request.Params.URI, "utility/json",
				`{"debug": false, "max_connections": 100}`),
		}, nil
	})

	
	s.AddPrompt(mcp.NewPrompt("summarize_issue",
		mcp.WithPromptDescription("Summarize a GitHub challenge"),
		mcp.WithArgument("issue_title",
			mcp.ArgumentDescription("The title of the difficulty"),
			mcp.RequiredArgument(),
		),
		mcp.WithArgument("issue_body",
			mcp.ArgumentDescription("The physique content material of the difficulty"),
		),
	), func(ctx context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) {
		title, okay := request.Params.Arguments["issue_title"].(string)
		if !okay || title == "" {
			return nil, fmt.Errorf("issue_title is required and have to be a non-empty string")
		}
		physique, _ := request.Params.Arguments["issue_body"].(string)

		return &mcp.GetPromptResult{
			Messages: []mcp.PromptMessage{{
				Function: mcp.RoleUser,
				Content material: mcp.TextContent{
					Kind: "textual content",
					Textual content: fmt.Sprintf("Summarize: %s

%s", title, physique),
				},
			}},
		}, nil
	})

	
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	sigChan := make(chan os.Sign, 1)
	sign.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
	go func() {
		choose {
		case sig := <-sigChan:
			slog.Information("shutdown sign obtained", "sign", sig.String())
			cancel()
		case <-ctx.Carried out():
			
		}
	}()

	slog.Information("beginning MCP server", "transport", "stdio")
	if err := server.ServeStdio(s, server.WithContext(ctx)); err != nil {
		slog.Error("server error", "error", err)
		
		return
	}
}

func githubIssueTool() (mcp.Instrument, server.ToolHandlerFunc) {
	software := mcp.NewTool("github_issue_lookup",
		mcp.WithDescription("Lookup a GitHub challenge"),
		mcp.WithString("proprietor", mcp.Required(), mcp.Description("Repository proprietor")),
		mcp.WithString("repo", mcp.Required(), mcp.Description("Repository identify")),
		mcp.WithNumber("issue_number", mcp.Required(), mcp.Description("Difficulty quantity")),
	)
	return software, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
		proprietor, _ := req.Params.Arguments["owner"].(string)
		repo, _ := req.Params.Arguments["repo"].(string)
		num, _ := req.Params.Arguments["issue_number"].(float64)
		if proprietor == "" || repo == "" || num < 1 {
			return mcp.NewToolResultError("lacking required parameters (issue_number have to be >= 1)"), nil
		}

		
		if !validIdentifier.MatchString(proprietor) || !validIdentifier.MatchString(repo) {
			return mcp.NewToolResultError("proprietor and repo should include solely alphanumeric characters, hyphens, underscores, or dots (max 100 chars)"), nil
		}

		slog.Information("software invoked", "software", "github_issue_lookup", "proprietor", proprietor, "repo", repo, "issue_number", int(num))

		apiURL := fmt.Sprintf("https://api.github.com/repos/%s/%s/points/%d", proprietor, repo, int(num))

		httpReq, err := http.NewRequestWithContext(ctx, http.MethodGet, apiURL, nil)
		if err != nil {
			return mcp.NewToolResultError(fmt.Sprintf("didn't construct request: %v", err)), nil
		}
		httpReq.Header.Set("Consumer-Agent", "mcp-go-server/1.0.0")
		httpReq.Header.Set("Settle for", "utility/vnd.github+json")

		
		if token := os.Getenv("GITHUB_TOKEN"); token != "" {
			httpReq.Header.Set("Authorization", "Bearer "+token)
		}

		resp, err := githubHTTPClient.Do(httpReq)
		if err != nil {
			return mcp.NewToolResultError(fmt.Sprintf("request failed: %v", err)), nil
		}
		defer resp.Physique.Shut()

		
		
		if resp.StatusCode == http.StatusForbidden {
			remaining := resp.Header.Get("X-RateLimit-Remaining")
			if remaining == "0" {
				return &mcp.CallToolResult{
					Content material: []mcp.Content material{mcp.TextContent{
						Kind: "textual content",
						Textual content: "GitHub API main price restrict exceeded. Set GITHUB_TOKEN to lift limits.",
					}},
					IsError: true,
				}, nil
			}
			return mcp.NewToolResultError("GitHub API returned 403 Forbidden (examine token permissions or repo visibility)"), nil
		}
		if resp.StatusCode == http.StatusTooManyRequests {
			return &mcp.CallToolResult{
				Content material: []mcp.Content material{mcp.TextContent{
					Kind: "textual content",
					Textual content: "GitHub API secondary price restrict hit. Wait earlier than retrying.",
				}},
				IsError: true,
			}, nil
		}

		if resp.StatusCode != http.StatusOK {
			return &mcp.CallToolResult{
				Content material: []mcp.Content material{mcp.TextContent{Kind: "textual content", Textual content: fmt.Sprintf("API error: %d", resp.StatusCode)}},
				IsError: true,
			}, nil
		}

		var challenge struct {
			Title  string `json:"title"`
			State  string `json:"state"`
			Physique   string `json:"physique"`
			Labels []struct {
				Title string `json:"identify"`
			} `json:"labels"`
		}

		limitedBody := io.LimitReader(resp.Physique, maxBodyBytes)
		if err := json.NewDecoder(limitedBody).Decode(&challenge); err != nil {
			return mcp.NewToolResultError(fmt.Sprintf("didn't parse response: %v", err)), nil
		}

		labels := make([]string, len(challenge.Labels))
		for i, l := vary challenge.Labels {
			labels[i] = l.Title
		}

		return mcp.NewToolResultText(fmt.Sprintf("Title: %s
State: %s
Labels: %s

%s",
			challenge.Title, challenge.State, strings.Be part of(labels, ", "), challenge.Physique)), nil
	}
}

Word on server.WithContext(ctx): In case your model of the mcp-go SDK doesn’t help passing a context to ServeStdio, the sign goroutine can name os.Exit(0) as a fallback, however remember that this bypasses deferred cleanup. Verify the SDK documentation to your pinned model.

Keep away from calling os.Exit() contained in the sign goroutine. os.Exit terminates the method instantly, bypassing all deferred features, that means database connections, file handles, and log buffers won’t be cleaned up.

Wrapping Up and Subsequent Steps

This tutorial confirmed assemble an entire MCP server in Go, from venture scaffolding by means of software registration, useful resource publicity, immediate templates, and manufacturing hardening with structured logging and swish shutdown. The server helps each stdio and HTTP/SSE transports with minimal code adjustments between them.

Pure subsequent steps embody including authentication for HTTP-transported servers (the MCP specification helps OAuth 2.0 flows), deploying behind a reverse proxy like Nginx or Caddy for TLS termination, and implementing Streamable HTTP transport for stateless cloud deployments. The complete MCP specification is on the market at spec.modelcontextprotocol.io. The mcp-go SDK repository at github.com/mark3labs/mcp-go incorporates extra examples and transport choices. For locating present MCP servers and patterns, the registries at mcp.so and Smithery present searchable catalogs of neighborhood implementations.


Tags: BuildContextMCPmodelproductionreadyProtocolServerTutorial
Admin

Admin

Next Post
The Roadmap to Mastering AI Agent Analysis

The Roadmap to Mastering AI Agent Analysis

Leave a Reply Cancel reply

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

Recommended.

Risk Actors Exploiting Victims’ Machines for Bandwidth Monetization

Risk Actors Exploiting Victims’ Machines for Bandwidth Monetization

August 21, 2025
Which USB Port Is Sooner?

Which USB Port Is Sooner?

May 10, 2026

Trending.

Nsfw Chatgpt Options – Examples I’ve Used

Nsfw Chatgpt Options – Examples I’ve Used

October 13, 2025
Digital Detox & Display Time Statistics 2025

Digital Detox & Display Time Statistics 2025

March 28, 2026
How creators and entrepreneurs are utilizing AI to hurry up & succeed [data]

How creators and entrepreneurs are utilizing AI to hurry up & succeed [data]

June 17, 2025
ModeloRAT and Mistic Backdoor Exercise Linked to Ransomware Preliminary Entry Dealer

ModeloRAT and Mistic Backdoor Exercise Linked to Ransomware Preliminary Entry Dealer

June 24, 2026
Cisco Catalyst SD-WAN Zero-Day CVE-2026-20245 Exploited to Acquire Root Entry

Cisco Catalyst SD-WAN Zero-Day CVE-2026-20245 Exploited to Acquire Root Entry

June 25, 2026

AimactGrow

Welcome to AimactGrow, your ultimate source for all things technology! Our mission is to provide insightful, up-to-date content on the latest advancements in technology, coding, gaming, digital marketing, SEO, cybersecurity, and artificial intelligence (AI).

Categories

  • AI
  • Coding
  • Cybersecurity
  • Digital marketing
  • Gaming
  • SEO
  • Technology

Recent News

Netflix is adapting Atlus Persona for brand spanking new live-action TV sequence

Netflix is adapting Atlus Persona for brand spanking new live-action TV sequence

June 30, 2026
Venezuela Earthquake Destruction Revealed in New Satellite tv for pc Photos

Venezuela Earthquake Destruction Revealed in New Satellite tv for pc Photos

June 30, 2026
  • About Us
  • Privacy Policy
  • Disclaimer
  • Contact Us

© 2025 https://blog.aimactgrow.com/ - All Rights Reserved

No Result
View All Result
  • Home
  • Technology
  • AI
  • SEO
  • Coding
  • Gaming
  • Cybersecurity
  • Digital marketing

© 2025 https://blog.aimactgrow.com/ - All Rights Reserved