feat: implement server publishing endpoint with Bearer token authentication and update documentation

This commit is contained in:
Avinash Sridhar 2025-04-30 12:34:00 -04:00 • committed by Toby Padilla
commit 9c6eebbed0
4 changed files with 190 additions and 48 deletions

View file

@ -5,23 +5,13 @@ import (
"encoding/json"
"io"
"net/http"
"strings"
"github.com/modelcontextprotocol/registry/internal/auth"
"github.com/modelcontextprotocol/registry/internal/model"
"github.com/modelcontextprotocol/registry/internal/service"
)
// httpError represents an HTTP error with a message and status code
type httpError struct {
msg string
status int
}
// Error returns the error message
func (e *httpError) Error() string {
return e.msg
}
// PublishHandler handles requests to publish new server details to the registry
func PublishHandler(registry service.RegistryService, authService auth.Service) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
@ -62,23 +52,39 @@ func PublishHandler(registry service.RegistryService, authService auth.Service)
return
}
// Validate authentication credentials
if authService != nil {
publishReq.Authentication.RepoRef = serverDetail.Name
valid, err := authService.ValidateAuth(r.Context(), publishReq.Authentication)
if err != nil {
if err == auth.ErrAuthRequired {
http.Error(w, "Authentication is required for publishing", http.StatusUnauthorized)
return
}
http.Error(w, "Authentication failed: "+err.Error(), http.StatusUnauthorized)
return
}
// Get auth token from Authorization header
authHeader := r.Header.Get("Authorization")
if authHeader == "" {
http.Error(w, "Authorization header is required", http.StatusUnauthorized)
return
}
if !valid {
http.Error(w, "Invalid authentication credentials", http.StatusUnauthorized)
// Handle bearer token format (e.g., "Bearer xyz123")
token := authHeader
if len(authHeader) > 7 && strings.ToUpper(authHeader[:7]) == "BEARER " {
token = authHeader[7:]
}
// Setup authentication info
a := model.Authentication{
Method: model.AuthMethodGitHub,
Token: token,
RepoRef: serverDetail.Name,
}
valid, err := authService.ValidateAuth(r.Context(), a)
if err != nil {
if err == auth.ErrAuthRequired {
http.Error(w, "Authentication is required for publishing", http.StatusUnauthorized)
return
}
http.Error(w, "Authentication failed: "+err.Error(), http.StatusUnauthorized)
return
}
if !valid {
http.Error(w, "Invalid authentication credentials", http.StatusUnauthorized)
return
}
// Call the publish method on the registry service

View file

@ -5,6 +5,8 @@
summary: Publish a server to the registry
description: Adds a new server to the MCP registry with authentication
operationId: publishServer
security:
- bearerAuth: []
requestBody:
required: true
content:
@ -14,11 +16,9 @@
properties:
server_detail:
$ref: '#/components/schemas/ServerDetail'
authentication:
$ref: '#/components/schemas/Authentication'
auth_status_token:
repo_ref:
type: string
description: Optional status token from a device authorization flow
description: Repository reference used for authentication (defaults to server name if not provided)
responses:
'201':
description: Server published successfully

View file

@ -101,6 +101,74 @@ paths:
'405':
description: Method not allowed
/v0/publish:
post:
tags:
- servers
summary: Publish a server to the registry
description: Adds a new server to the MCP registry with authentication
operationId: publishServer
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
server_detail:
$ref: '#/components/schemas/ServerDetail'
repo_ref:
type: string
description: Repository reference used for authentication (defaults to server name if not provided)
responses:
'201':
description: Server published successfully
content:
application/json:
schema:
type: object
properties:
message:
type: string
example: Server publication successful
id:
type: string
example: 1234567890abcdef12345678
'400':
description: Invalid request
content:
application/json:
schema:
type: object
properties:
error:
type: string
example: Name is required
'401':
description: Authentication failed
content:
application/json:
schema:
type: object
properties:
error:
type: string
example: Authentication is required for publishing
'405':
description: Method not allowed
'500':
description: Server error
content:
application/json:
schema:
type: object
properties:
error:
type: string
example: Failed to publish server details
/v0/servers/{id}:
get:
tags:
@ -131,6 +199,13 @@ paths:
description: Method not allowed
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
description: "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\""
schemas:
Server:
type: object

View file

@ -20,7 +20,7 @@ type Authentication struct {
// PublishRequest represents a request to publish a server to the registry
type PublishRequest struct {
ServerDetail ServerDetail `json:"server_detail"`
Authentication Authentication `json:"authentication,omitempty"`
Authentication Authentication `json:"-"` // Now provided via Authorization header
AuthStatusToken string `json:"-"` // Used internally for device flows
}