Repository navigation
feat(mcp): serve the MCP endpoint from the Go server - #238
Merged
Merged
Conversation
Serve the Model Context Protocol at /mcp (Streamable HTTP, stateless) with the official Go SDK, so AI agents need nothing installed. The eight read-only tools of the Python server are kept by name; they call the event, catalog and lock services in process under the matching RPC name, so permissions, the per-service team scope and the forced password change apply as on the REST API. A refused credential answers 401 before the MCP exchange. Lists are paginated by the MCP layer, events newest first. MCP_ENABLED (default true) turns the endpoint off. The Python server is deprecated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Tracker now serves the Model Context Protocol itself, at
/mcpon the HTTP port. AI agents (Claude Code, Cursor, Kiro…) connect with a URL and an API key: no Python, nouv, nothing to install.claude mcp add --transport http tracker https://tracker.example.com/mcp \ --header "Authorization: Bearer trk_..."The Python server in
mcp-server/is marked deprecated. It also sent no credential at all, so it stopped working as soon as anonymous access was restricted.How
server/mcp.go, official SDKgithub.com/modelcontextprotocol/go-sdkv1.8.0, Streamable HTTP in stateless mode. Each MCP request runs with the context of its own HTTP request, so it carries the principal thatauth.HTTPMiddlewareresolved for that request. In stateful mode the principal would be frozen when the session is created.Event,CatalogandLockin process, under the name of the matching RPC (rpcContext(ctx, EventService_ListEvents_FullMethodName)).authz.Authorize, the per-service team scope (feat(auth): per-service team scope #216) and the forced password change (fix(auth): enforce the forced password change on the server #217) therefore apply unchanged; nothing in the auth code is MCP-specific.WWW-Authenticate: Bearerbefore any MCP exchange, so clients report an authentication error instead of eight failing tools.HandlePathon the gateway mux./mcpis added to the SPA pass-through, and to the route guard table (httpRouteSites) with its reason.MCP_ENABLED(defaulttrue) turns it off.Tools
Same eight read-only tools and names as the Python server, all annotated
readOnlyHint:list_events,today_events,search_events,get_event,list_catalog,get_catalog_service,list_locks,get_lock. Responses are protojson (camelCase, enum names), like the REST API.Differences with the Python server:
per_pagedefault 10, max 100,page,totalCount), because the list RPCs ignoreper_page/pageand return everything. Events come newest first, the catalog by name.list_eventsfilters (type,service,status) now apply (throughSearchEvents). The Python server sent them to/events/list, which ignores them.search_eventsdropsimpactandslack_id, whichutils.CreateFilternever applied.get_eventalready accepts a Slack id.list_catalog,get_catalog_service,get_eventandget_lockwork now: the Python server called/catalog/list,/catalog/{name},/events/{id}and/locks/{id}, which do not exist.unknown environment "prod").Checks
go test -raceon all packages against MongoDB 7: pass. New e2e tests (server/mcp_test.go) use the SDK client through the real auth middleware:AUTH_ANONYMOUS_PERMISSIONS;golangci-lint run ./...: 0 issues.go vet: clean.tools/listandtools/callwith curl, 401 on a bad key, 405 onGET /mcp, SPA routes still serveindex.html.govulncheckreportsgolang.org/x/netv0.58.0 (fixed in v0.60.0) andcrypto/x509from the local Go toolchain. Both are already onmain; neither comes from the SDK.Notes
golang-jwt/jwt/v5from 5.3.0 to 5.3.1 (its minimum).main,CreateEventpanics with a nil pointer when the body has nolinksobject (server/event.go:136). The HTTP server recovers, but the client gets an empty reply.Follow-ups
*:writepermissions.mcp-server/, and its Dependabot entry from ci: include dependabot updates in release-please #237.🤖 Generated with Claude Code