Skip to content

feat: support Go modules as a package type#1321

Open
koriyoshi2041 wants to merge 1 commit into
modelcontextprotocol:mainfrom
koriyoshi2041:feat/go-package-type
Open

feat: support Go modules as a package type#1321
koriyoshi2041 wants to merge 1 commit into
modelcontextprotocol:mainfrom
koriyoshi2041:feat/go-package-type

Conversation

@koriyoshi2041
Copy link
Copy Markdown

Closes #1307.

Adds "registryType": "go" so MCP servers distributed as go install-able modules can be published to the registry. Design discussed in #1307.

Ownership reuses the publisher's GitHub namespace rather than inventing an mcpName-style marker (which wouldn't be idiomatic in a go.mod). A Go module path is its source location, so a server published under io.github.<owner> must use a module path rooted at github.com/<owner>/.... The owner is compared case-insensitively (GitHub owners are case-insensitive). Since the publisher already authenticates for the io.github.<owner> namespace, matching the module owner proves ownership. Non-GitHub module paths are intentionally left for a follow-up (a sentinel-file/go.mod-directive check can be added later without changing this).

Existence is verified against the Go module proxy (https://proxy.golang.org) — the canonical source of truth, with no auth/rate-limit concerns. The module path and version are case-encoded per the goproxy protocol (uppercase → !+lowercase), and ./.. path elements are rejected so a crafted identifier can't escape the proxy path.

Changes

  • pkg/model/constants.go: RegistryTypeGo + RegistryURLGoProxy.
  • internal/validators/package.go: dispatch go to ValidateGo.
  • internal/validators/registries/go.go: ValidateGo + ValidateGoModuleOwnership + module-proxy existence check + EscapeGoModulePath.
  • internal/validators/registries/go_test.go: ownership matrix (incl. case-insensitivity and mismatch), path-escaping/traversal, and ValidateGo guard errors — all deterministic, no network.
  • docs/.../package-types.mdx: Go section.

Example

{
  "registryType": "go",
  "identifier": "github.com/username/audit-mcp",
  "version": "v1.0.0"
}

go test ./internal/validators/... passes; gofmt/go vet clean. The existence path is exercised against the live proxy on a real publish; the unit tests cover the ownership and escaping logic without network. Happy to adjust the identifier semantics (module path vs. go install sub-path) if you'd prefer a different convention.

Adds "registryType": "go" so MCP servers distributed as go-install-able
modules can be published. Ownership reuses the publisher's GitHub namespace:
a server under io.github.<owner> must use a module path rooted at
github.com/<owner>/... (compared case-insensitively, since GitHub owners are
case-insensitive). Existence is verified against the Go module proxy
(proxy.golang.org). Non-GitHub module paths are left for a follow-up.

Closes modelcontextprotocol#1307
@P4ST4S
Copy link
Copy Markdown

P4ST4S commented May 30, 2026

This looks excellent, thanks for moving so fast on this. A few notes from a careful read, none of them blocking:

Things I especially liked:

  • The case-encoding matches the official Go module proxy spec exactly ("uppercase letter → '!' + lowercase"), and the test cases mirror the canonical examples from golang.org/x/mod/module#EscapePath.
  • The . / .. path element rejection and control-character guard are exactly the right paranoia for an identifier that flows into a proxy URL.
  • Running ownership validation before the network call (verified by the ownership failure returns before network test) is the correct order: a malicious identifier never triggers an outbound request, and we don't leak existence of private modules to namespace-squatters.
  • Case-insensitive owner comparison is exactly what GitHub semantics require, appreciate that you picked this up from the issue discussion.

Small things worth considering, not blocking:

  1. golang.org/x/mod/module.EscapePath does effectively the same case-encoding plus the additional CheckPath validation (Windows reserved names like CON/com1, semver /vN suffix structure, etc.). The inline implementation is correct for current spec, but a future edge case in module path validation would need to be patched here manually. Trade-off: pulling in x/mod adds a dependency for one function. Either way is defensible, just noting the alternative exists.

  2. No coverage on the HTTP path in validateGoModuleExists. Totally understandable (mocking would require extracting the client; hitting the real proxy.golang.org would be flaky), but a future regression where someone adds, say, case 503: return nil would slip through. If you want, that's solvable with a small httptest.Server fixture, happy to follow up in a separate PR if useful.

  3. Hardcoded 10 * time.Second timeout, minor, but pulling it to a package-level const would make it tweakable without grep.

Thanks again, this is high-quality work and the design choice to anchor ownership in GitHub namespace authentication is exactly the right shape for v1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support for Go modules (go install) as a package type

2 participants