closedmcp

package module
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 13, 2026 License: Apache-2.0 Imports: 14 Imported by: 0

README

ClosedMCP Go SDK

A small net/http wrapper for the official Go MCP SDK. It checks each signed pass, serves protected-resource metadata, and attaches the verified person to the tool's context.

Install with go get github.com/canpainc/closedmcp-sdk-go@v0.1.1. The module path is github.com/canpainc/closedmcp-sdk-go.

For an existing official *mcp.Server, call closedmcp.NewHandler(existingServer, options) and serve the returned handler. For an existing mark3labs/mcp-go server, add these imports and wrap its Streamable HTTP handler:

import (
    "log"
    "os"
    "strings"
    closedmcp "github.com/canpainc/closedmcp-sdk-go"
    markserver "github.com/mark3labs/mcp-go/server"
)

httpServer := markserver.NewStreamableHTTPServer(existingServer, markserver.WithStateLess(true))
handler, err := closedmcp.NewHTTPHandler(httpServer, closedmcp.Options{
    Resource: os.Getenv("MCP_URL"),
    AllowedDomains: strings.Split(os.Getenv("ALLOWED_DOMAINS"), ","),
})
if err != nil { log.Fatal(err) }

Mount handler at your MCP path. PersonFrom(ctx) exposes the caller in both frameworks.

Hello

package main

import (
    "context"
    "log"
    "net/http"
    closedmcp "github.com/canpainc/closedmcp-sdk-go"
    "github.com/modelcontextprotocol/go-sdk/mcp"
)

func main() {
server := mcp.NewServer(&mcp.Implementation{Name: "hello", Version: "1"}, nil)
mcp.AddTool(server, &mcp.Tool{Name: "hello"},
    func(ctx context.Context, _ *mcp.CallToolRequest, _ struct{}) (*mcp.CallToolResult, any, error) {
        person, _ := closedmcp.PersonFrom(ctx)
        return &mcp.CallToolResult{Content: []mcp.Content{
            &mcp.TextContent{Text: "Hello " + person.Email},
        }}, nil, nil
    })
handler, err := closedmcp.NewHandler(server, closedmcp.Options{
    Resource: "https://example.com/mcp", AllowedDomains: []string{"example.com"},
})
if err != nil { log.Fatal(err) }
log.Fatal(http.ListenAndServe(":8787", handler))
}

Resource must be the exact public MCP URL registered with ClosedMCP. AllowedDomains is required and checks the verified company domain, case insensitively. PersonFrom(ctx) gives the current call's ID, Email, Company, Org, and optional AIApp; it returns false outside a verified call. AIApp is supplied by the client and is for display only.

Mount the handler directly or in any net/http mux. It serves /.well-known/oauth-protected-resource/mcp for a /mcp resource, plus the generic metadata path. Unauthenticated MCP requests receive 401 with a WWW-Authenticate metadata link. The official transport handles stateless Streamable HTTP calls concurrently.

The runnable template reads MCP_URL, ALLOWED_DOMAINS (comma separated), and optional PORT (default 8787). From the repository root, run:

MCP_URL=https://example.com/mcp ALLOWED_DOMAINS=example.com go run ./template

For local integration tests, set CLOSEDMCP_ORIGIN and CLOSEDMCP_JWKS_URL. By default, passes from https://closedmcp.com and the legacy https://closedmcp.canpai.io issuer use their respective JWKS endpoints. An explicit issuer accepts only that issuer. The verifier caches keys for five minutes and limits unknown key ID refetches to once every 30 seconds per issuer.

See the ClosedMCP documentation.

Documentation

Overview

Package closedmcp protects an official MCP HTTP server with ClosedMCP passes.

Index

Constants

View Source
const Issuer = "https://closedmcp.com"

Variables

This section is empty.

Functions

func NewHTTPHandler

func NewHTTPHandler(mcpHandler http.Handler, opts Options) (http.Handler, error)

NewHTTPHandler protects an existing Streamable HTTP handler, including mcp-go. The underlying handler receives the verified Person in the request context.

func NewHandler

func NewHandler(server *mcp.Server, opts Options) (http.Handler, error)

NewHandler protects every MCP request and serves OAuth protected-resource metadata. The supplied server can serve concurrent stateless requests.

Types

type Options

type Options struct {
	Resource       string
	AllowedDomains []string
	Issuer         string // Defaults to the public ClosedMCP issuer.
	JWKSURL        string // Defaults to the selected issuer's JWKS endpoint.
	HTTPClient     *http.Client
}

type Person

type Person struct {
	ID      string `json:"id"`
	Email   string `json:"email"`
	Company string `json:"company"`
	Org     string `json:"org"`
	AIApp   string `json:"aiApp,omitempty"`
}

func PersonFrom

func PersonFrom(ctx context.Context) (Person, bool)

PersonFrom returns the verified person attached to this MCP request.

type Verifier

type Verifier struct {
	// contains filtered or unexported fields
}

func NewVerifier

func NewVerifier(opts Options) (*Verifier, error)

NewVerifier validates configuration before accepting requests.

func (*Verifier) Verify

func (v *Verifier) Verify(ctx context.Context, authorization string) (Person, error)

Verify checks a Bearer pass and returns its identity.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL