trackmcp
Back to directory

A Go implementation of the Model Context Protocol (MCP), enabling seamless integration between LLM applications and external data sources and tools.

7,557 stars GoAI & Machine Learning Updated Nov 4, 2025

Documentation

go
package main

import (
    "context"
    "fmt"

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

func main() {
    // Create a new MCP server
    s := server.NewMCPServer(
        "Demo ๐Ÿš€",
        "1.0.0",
        server.WithToolCapabilities(false),
    )

    // Add tool
    tool := mcp.NewTool("hello_world",
        mcp.WithDescription("Say hello to someone"),
        mcp.WithString("name",
            mcp.Required(),
            mcp.Description("Name of the person to greet"),
        ),
    )

    // Add tool handler
    s.AddTool(tool, helloHandler)

    // Start the stdio server
    if err := server.ServeStdio(s); err != nil {
        fmt.Printf("Server error: %v\n", err)
    }
}

func helloHandler(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
    name, err := request.RequireString("name")
    if err != nil {
        return mcp.NewToolResultError(err.Error()), nil
    }

    return mcp.NewToolResultText(fmt.Sprintf("Hello, %s!", name)), nil
}

That's it!

MCP Go handles all the complex protocol details and server management, so you can focus on building great tools. It aims to be high-level and easy to use.

Key features:

  • Fast: High-level interface means less code and faster development
  • Simple: Build MCP servers with minimal boilerplate
  • Complete*: MCP Go aims to provide a full implementation of the core MCP specification

(\*emphasis on *aims*)

๐Ÿšจ ๐Ÿšง ๐Ÿ—๏ธ *MCP Go is under active development, as is the MCP specification itself. Core features are working but some advanced capabilities are still in progress.*

Table of Contents

Installation

bash
go get github.com/mark3labs/mcp-go

Quickstart

Let's create a simple MCP server that exposes a calculator tool and some data:

go
package main

import (
    "context"
    "fmt"

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

func main() {
    // Create a new MCP server
    s := server.NewMCPServer(
        "Calculator Demo",
        "1.0.0",
        server.WithToolCapabilities(false),
        server.WithRecovery(),
    )

    // Add a calculator tool
    calculatorTool := mcp.NewTool("calculate",
        mcp.WithDescription("Perform basic arithmetic operations"),
        mcp.WithString("operation",
            mcp.Required(),
            mcp.Description("The operation to perform (add, subtract, multiply, divide)"),
            mcp.Enum("add", "subtract", "multiply", "divide"),
        ),
        mcp.WithNumber("x",
            mcp.Required(),
            mcp.Description("First number"),
        ),
        mcp.WithNumber("y",
            mcp.Required(),
            mcp.Description("Second number"),
        ),
    )

    // Add the calculator handler
    s.AddTool(calculatorTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
        // Using helper functions for type-safe argument access
        op, err := request.RequireString("operation")
        if err != nil {
            return mcp.NewToolResultError(err.Error()), nil
        }
        
        x, err := request.RequireFloat("x")
        if err != nil {
            return mcp.NewToolResultError(err.Error()), nil
        }
        
        y, err := request.RequireFloat("y")
        if err != nil {
            return mcp.NewToolResultError(err.Error()), nil
        }

        var result float64
        switch op {
        case "add":
            result = x + y
        case "subtract":
            result = x - y
        case "multiply":
            result = x * y
        case "divide":
            if y == 0 {
                return mcp.NewToolResultError("cannot divide by zero"), nil
            }
            result = x / y
        }

        return mcp.NewToolResultText(fmt.Sprintf("%.2f", result)), nil
    })

    // Start the server
    if err := server.ServeStdio(s); err != nil {
        fmt.Printf("Server error: %v\n", err)
    }
}

What is MCP?

The Model Context Protocol (MCP) lets you build servers that expose data and functionality to LLM applications in a secure, standardized way. Think of it like a web API, but specifically designed for LLM interactions.

MCP servers can:

  • Expose data through Resources (think of these sort of like GET endpoints; they are used to load information into the LLM's context)
  • Provide functionality through Tools (sort of like POST endpoints; they are used to execute code or otherwise produce a side effect)
  • Define interaction patterns through Prompts (reusable templates for LLM interactions)
  • And more!

mcp-go implements the Model Context Protocol specification version 2025-11-25, with backward compatibility for versions 2025-06-18, 2025-03-26, and 2024-11-05.

Core Concepts

Server

Show Server Examples

The server is your core interface to the MCP protocol. It handles connection management, protocol compliance, and message routing:

go
// Create a basic server
s := server.NewMCPServer(
    "My Server",  // Server name
    "1.0.0",     // Version
)

// Start the server using stdio
if err := server.ServeStdio(s); err != nil {
    log.Fatalf("Server error: %v", err)
}

Resources

Show Resource Examples

Resources are how you expose data to LLMs. They can be anything - files, API responses, database queries, system information, etc. Resources can be:

  • Static (fixed URI)
  • Dynamic (using URI templates)

Here's a simple example of a static resource:

go
// Static resource example - exposing a README file
resource := mcp.NewResource(
    "docs://readme",
    "Project README",
    mcp.WithResourceDescription("The project's README file"), 
    mcp.WithMIMEType("text/markdown"),
)

// Add resource with its handler
s.AddResource(resource, func(ctx context.Context, request mcp.ReadResourceRequest) ([]mcp.ResourceContents, error) {
    content, err := os.ReadFile("README.md")
    if err != nil {
        return nil, err
    }
    
    return []mcp.ResourceContents{
        mcp.TextResourceContents{
            URI:      "docs://readme",
            MIMEType: "text/markdown",
            Text:     string(content),
        },
    }, nil
})

And here's an example of a dynamic resource using a template:

go
// Dynamic resource example - user profiles by ID
template := mcp.NewResourceTemplate(
    "users://{id}/profile",
    "User Profile",
    mcp.WithTemplateDescription("Returns user profile information"),
    mcp.WithTemplateMIMEType("application/json"),
)

// Add template with its handler
s.AddResourceTemplate(template, func(ctx context.Context, request mcp.ReadResourceRequest) ([]mcp.ResourceContents, error) {
    // Extract ID from the URI using regex matching
    // The server automatically matches URIs to templates
    userID := extractIDFromURI(request.Params.URI)
    
    profile, err := getUserProfile(userID)  // Your DB/API call here
    if err != nil {
        return nil, err
    }
    
    return []mcp.ResourceContents{
        mcp.TextResourceContents{
            URI:      request.Params.URI,
            MIMEType: "application/json",
            Text:     profile,
        },
    }, nil
})

The examples are simple but demonstrate the core concepts. Resources can be much more sophisticated - serving multiple contents, integrating with databases or external APIs, etc.

Tools

Show Tool Examples

Tools let LLMs take actions through your server. Unlike resources, tools are expected to perform computation and have side effects. They're similar to POST endpoints in a REST API.

Task-Augmented Tools

Task-augmented tools execute asynchronously and return results via polling. This is useful for long-running operations that would otherwise block or time out. Task tools support three modes:

  • TaskSupportForbidden (default): The tool cannot be invoked as a task
  • TaskSupportOptional: The tool can be invoked as a task or synchronously
  • TaskSupportRequired: The tool must be invoked as a task
go
// Example: A tool that requires task execution
processBatchTool := mcp.NewTool("process_batch",
    mcp.WithDescription("Process a batch of items asynchronously"),
    mcp.WithTaskSupport(mcp.TaskSupportRequired),
    mcp.WithArray("items",
        mcp.Description("Array of items to process"),
        mcp.WithStringItems(),
        mcp.Required(),
    ),
)

// Task tool handler returns CreateTaskResult instead of CallToolResult
s.AddTaskTool(processBatchTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CreateTaskResult, error) {
    items := request.GetStringSlice("items", []string{})
    
    // Long-running work here
    for i, item := range items {
        select {
        case 

### Prompts

Show Prompt Examples

Prompts are reusable templates that help LLMs interact with your server effectively. They're like "best practices" encoded into your server. Here are some examples:

// Simple greeting prompt

s.AddPrompt(mcp.NewPrompt("greeting",

mcp.WithPromptDescription("A friendly greeting prompt"),

mcp.WithArgument("name",

mcp.ArgumentDescription("Name of the person to greet"),

),

), func(ctx context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) {

name := request.Params.Arguments["name"]

if name == "" {

name = "friend"

}

return mcp.NewGetPromptResult(

"A friendly greeting",

[]mcp.PromptMessage{

mcp.NewPromptMessage(

mcp.RoleAssistant,

mcp.NewTextContent(fmt.Sprintf("Hello, %s! How can I help you today?", name)),

),

},

), nil

})

// Code review prompt with embedded resource

s.AddPrompt(mcp.NewPrompt("code_review",

mcp.WithPromptDescription("Code review assistance"),

mcp.WithArgument("pr_number",

mcp.ArgumentDescription("Pull request number to review"),

mcp.RequiredArgument(),

),

), func(ctx context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) {

prNumber := request.Params.Arguments["pr_number"]

if prNumber == "" {

return nil, fmt.Errorf("pr_number is required")

}

return mcp.NewGetPromptResult(

"Code review assistance",

[]mcp.PromptMessage{

mcp.NewPromptMessage(

mcp.RoleUser,

mcp.NewTextContent("Review the changes and provide constructive feedback."),

),

mcp.NewPromptMessage(

mcp.RoleAssistant,

mcp.NewEmbeddedResource(mcp.ResourceContents{

URI: fmt.Sprintf("git://pulls/%s/diff", prNumber),

MIMEType: "text/x-diff",

}),

),

},

), nil

})

// Database query builder prompt

s.AddPrompt(mcp.NewPrompt("query_builder",

mcp.WithPromptDescription("SQL query builder assistance"),

mcp.WithArgument("table",

mcp.ArgumentDescription("Name of the table to query"),

mcp.RequiredArgument(),

),

), func(ctx context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) {

tableName := request.Params.Arguments["table"]

if tableName == "" {

return nil, fmt.Errorf("table name is required")

}

return mcp.NewGetPromptResult(

"SQL query builder assistance",

[]mcp.PromptMessage{

mcp.NewPromptMessage(

mcp.RoleUser,

mcp.NewTextContent("Help construct efficient and safe queries for the provided schema."),

),

mcp.NewPromptMessage(

mcp.RoleUser,

mcp.NewEmbeddedResource(mcp.ResourceContents{

URI: fmt.Sprintf("db://schema/%s", tableName),

MIMEType: "application/json",

}),

),

},

), nil

})

code
Prompts can include:
- System instructions
- Required arguments
- Embedded resources
- Multiple messages
- Different content types (text, images, etc.)
- Custom URI schemes

## Examples

For examples, see the [`examples/`](examples/) directory.

Key examples include:
- [`examples/task_tool/`](examples/task_tool/) - Demonstrates task-augmented tools with TaskSupportRequired and TaskSupportOptional modes
- [`examples/structured_input_and_output/`](examples/structured_input_and_output/) - Shows how to use struct-based input/output schemas with type-safe tool handlers
- [`examples/typed_tools/`](examples/typed_tools/) - Demonstrates type-safe tool handlers with strongly-typed arguments
- [`examples/custom_context/`](examples/custom_context/) - Shows how to use custom contexts in tool handlers
- Additional examples covering resources, prompts, and more in the examples directory

## Extras

### Transports

MCP-Go supports stdio, SSE and streamable-HTTP transport layers. For SSE transport, you can use `SetConnectionLostHandler()` to detect and handle disconnections for implementing reconnection logic.

### Embedding StreamableHTTP in non-net/http frameworks

`StreamableHTTPServer` is an `http.Handler`, so it can be mounted in any
router that speaks `net/http`. To embed it in a framework that does **not**
go through `net/http` (e.g. [fasthttp](https://github.com/valyala/fasthttp)
or [fiber](https://gofiber.io/)) without buffering the response through an
adaptor, use the transport-agnostic `Handle` entry point:

func (s *StreamableHTTPServer) Handle(w HTTPResponseWriter, r *HTTPRequest)

code
`HTTPRequest` is a plain struct (`Method`, `URL`, `Header`, `Body`,
`Context`) and `HTTPResponseWriter` is a small interface (`Header`,
`WriteHeader`, `Write`, `Flush`, `CanStream`). Implementations whose
underlying transport cannot stream MUST return `false` from `CanStream`;
the server will then reject GET (SSE listening) with `405 Method Not
Allowed` and keep POST responses as buffered `application/json` instead of
upgrading to `text/event-stream`.

See the [HTTP transport docs](https://mcp-go.dev/transports/http#embedding-in-non-nethttp-frameworks)
for a full fasthttp/fiber adapter example. `ServeHTTP` is unchanged and
remains the conventional `net/http` entry point.

### OAuth Protected Resource Metadata

Servers that require OAuth can advertise their authorization requirements
via the [RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728)
`/.well-known/oauth-protected-resource` endpoint referenced by the
[MCP authorization spec](https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization).
Use `server.WithProtectedResourceMetadata` (or
`server.WithSSEProtectedResourceMetadata`) to auto-mount the endpoint, or
`server.NewProtectedResourceMetadataHandler` to wire it into a custom router.
See the [HTTP transport docs](https://mcp-go.dev/transports/http#oauth-protected-resource-metadata-rfc-9728) for examples.

httpServer := server.NewStreamableHTTPServer(mcpServer,

server.WithProtectedResourceMetadata(server.ProtectedResourceMetadataConfig{

Resource: "https://my-mcp-server.com",

AuthorizationServers: []string{"https://auth.example.com"},

ScopesSupported: []string{"mcp:read", "mcp:write"},

}),

)

code
### CORS for browser-based clients

Servers exposed to browser-based MCP clients can opt into Cross-Origin
Resource Sharing handling on either HTTP transport. CORS is disabled by
default; configure it explicitly via `server.WithStreamableHTTPCORS` or
`server.WithSSECORS`:

httpServer := server.NewStreamableHTTPServer(mcpServer,

server.WithEndpointPath("/mcp"),

server.WithStreamableHTTPCORS(

server.WithCORSAllowedOrigins("https://my-ai-app.com", "http://localhost:3000"),

server.WithCORSAllowCredentials(),

server.WithCORSMaxAge(300),

),

)

code
The transport answers preflight (`OPTIONS`) requests directly and decorates
simple responses with the appropriate `Access-Control-Allow-Origin`,
`Access-Control-Allow-Credentials`, `Access-Control-Expose-Headers` and
`Vary` headers. Sensible defaults are used when the corresponding option is
omitted (`GET, POST, DELETE, OPTIONS` for methods; `Content-Type,
Mcp-Session-Id, Last-Event-ID, Authorization` for request headers;
`Mcp-Session-Id` for exposed headers). Combining `WithCORSAllowedOrigins("*")`
with `WithCORSAllowCredentials()` echoes the request `Origin` to remain
spec-compliant.

### DNS rebinding protection for localhost servers

Both HTTP transports automatically protect local servers against
[DNS rebinding attacks](https://modelcontextprotocol.io/specification/2025-11-25/basic/security_best_practices#local-mcp-server-compromise):
requests arriving over a loopback connection (`127.0.0.1`, `[::1]`) whose
`Host` header is not a localhost value are rejected with `403 Forbidden`.
The check is derived from the connection's local address at runtime, so it
applies whether the server listens on `localhost` or `0.0.0.0`, and never
affects requests arriving via non-loopback addresses.

If a reverse proxy on the same host forwards requests via localhost while
preserving the original `Host` header, configure the proxy to rewrite the
`Host` header to localhost, or opt out explicitly:

httpServer := server.NewStreamableHTTPServer(mcpServer,

// Or server.WithSSEDisableLocalhostProtection(true) on NewSSEServer.

server.WithDisableLocalhostProtection(true),

)

code
See the [HTTP transport docs](https://mcp-go.dev/transports/http#dns-rebinding-protection)
for details, including the caveat for the framework-agnostic `Handle` entry
point.

### Session Management

MCP-Go provides a robust session management system that allows you to:
- Maintain separate state for each connected client
- Register and track client sessions
- Send notifications to specific clients
- Provide per-session tool customization

Show Session Management Examples

#### Basic Session Handling

// Create a server with session capabilities

s := server.NewMCPServer(

"Session Demo",

"1.0.0",

server.WithToolCapabilities(true),

)

// Implement your own ClientSession

type MySession struct {

id string

notifChannel chan mcp.JSONRPCNotification

isInitialized bool

// Add custom fields for your application

}

// Implement the ClientSession interface

func (s *MySession) SessionID() string {

return s.id

}

func (s *MySession) NotificationChannel() chan

Request Hooks

Hook into the request lifecycle by creating a `Hooks` object with your

selection among the possible callbacks. This enables telemetry across all

functionality, and observability of various facts, for example the ability

to count improperly-formatted requests, or to log the agent identity during

initialization.

Add the `Hooks` to the server at the time of creation using the

`server.WithHooks` option.

Tool Handler Middleware

Add middleware to tool call handlers using the `server.WithToolHandlerMiddleware` option. Middlewares can be registered on server creation and are applied on every tool call.

A recovery middleware option is available to recover from panics in a tool call and can be added to the server with the `server.WithRecovery` option.

Prompt Handler Middleware

Add middleware to prompt handlers using the `server.WithPromptHandlerMiddleware` option. Middlewares can be registered on server creation and are applied on every `prompts/get` call.

Prompt Filtering

Filter prompts based on context using the `server.WithPromptFilter` option. This works the same way as tool filtering but applies to `prompts/list` results.

Regenerating Server Code

Server hooks and request handlers are generated. Regenerate them by running:

bash
go generate ./...

You need `go` installed and the `goimports` tool available. The generator runs

`goimports` automatically to format and fix imports.

Auto-completions

When users are filling in argument values for a specific prompt (identified by name) or resource template (identified by URI), servers can provide contextual suggestions.

To enable completion support, use the `server.WithCompletions()` option when creating your server.

Completion Providers

You can provide completion logic for both prompt arguments and resource template arguments by implementing the respective interfaces and passing them to the server as options.

Show Completion Provider Examples

go
type MyPromptCompletionProvider struct{}

func (p *MyPromptCompletionProvider) CompletePromptArgument(
    ctx context.Context,
    promptName string,
    argument mcp.CompleteArgument,
    context mcp.CompleteContext,
) (*mcp.Completion, error) {
    // Example: provide style suggestions for a "code_review" prompt
    if promptName == "code_review" && argument.Name == "style" {
        styles := []string{"formal", "casual", "technical", "creative"}
        var suggestions []string
        
        // Filter based on current input
        for _, style := range styles {
            if strings.HasPrefix(style, argument.Value) {
                suggestions = append(suggestions, style)
            }
        }
        
        return &mcp.Completion{
            Values: suggestions,
        }, nil
    }
    
    // Return empty suggestions for unhandled cases
    return &mcp.Completion{Values: []string{}}, nil
}

type MyResourceCompletionProvider struct{}

func (p *MyResourceCompletionProvider) CompleteResourceArgument(
    ctx context.Context,
    uri string,
    argument mcp.CompleteArgument,
    context mcp.CompleteContext,
) (*mcp.Completion, error) {
    // Example: provide file path completions
    if uri == "file:///{path}" && argument.Name == "path" {
        // You can access previously completed arguments from context.Arguments
        // context.Arguments is a map[string]string of already-resolved arguments
        
        paths := getMatchingPaths(argument.Value) // Your custom logic
        
        return &mcp.Completion{
            Values:  paths[:min(len(paths), 100)], // Max 100 items
            Total:   len(paths),                    // Total available matches
            HasMore: len(paths) > 100,              // More results available
        }, nil
    }
    
    return &mcp.Completion{Values: []string{}}, nil
}

// Register the provider
mcpServer := server.NewMCPServer(
    "my-server",
    "1.0.0",
    server.WithCompletions(),
    server.WithPromptCompletionProvider(&MyPromptCompletionProvider{}),
    server.WithResourceCompletionProvider(&MyResourceCompletionProvider{}),
)

Completion Context

For prompts or resource templates with multiple arguments, the `CompleteContext` parameter provides access to previously completed arguments. This allows you to provide contextual suggestions based on earlier choices.

Show Completion Context Example

go
func (p *MyProvider) CompleteResourceArgument(
    ctx context.Context,
    uri string,
    argument mcp.CompleteArgument,
    context mcp.CompleteContext,
) (*mcp.Completion, error) {
    // Access previously completed arguments
    if previousValue, ok := context.Arguments["previous_arg"]; ok {
        // Provide suggestions based on previous_arg value
        return getSuggestionsFor(argument.Value, previousValue), nil
    }
    
    return &mcp.Completion{Values: []string{}}, nil
}

Response Constraints

When returning completion results:

  • Maximum 100 items per response
  • Use `Total` to indicate the total number of available matches
  • Use `HasMore` to signal if additional results exist beyond the returned values

Frequently asked questions

What is mcp-go?

mcp-go is A Go implementation of the Model Context Protocol (MCP), enabling seamless integration between LLM applications and external data sources and tools.

How do I install mcp-go?

Open the GitHub repository and follow its README. Most MCP servers are added to your client's MCP config, then called by your agent.

Is mcp-go open source?

Yes โ€” it is hosted on GitHub at https://github.com/mark3labs/mcp-go and has 7,557 stars.

Related MCP tools

Run your own MCP server? See who uses it and what to fix.

Measure it with TrackMCP