This file provides guidance for AI assistants working with the Apache SkyWalking MCP codebase.
Apache SkyWalking MCP — an MCP (Model Context Protocol) server that bridges AI agents with Apache SkyWalking OAP via GraphQL. It exposes SkyWalking's observability data (traces, logs, metrics, topology, alarms, events) as MCP tools, prompts, and resources. Binary name: swmcp.
skywalking-mcp/
├── cmd/skywalking-mcp/ # Entry point (cobra/viper CLI, three subcommands)
├── internal/
│ ├── config/ # Config structs for each transport mode
│ ├── swmcp/ # MCP server factory + transport adapters (stdio/sse/streamable)
│ ├── tools/ # MCP tool implementations (16 tools, grouped by domain)
│ ├── prompts/ # MCP prompt definitions (10 prompts, three groups)
│ └── resources/ # MCP resources (embedded MQE docs + dynamic metrics)
└── dist/ # Distribution license files
make build # Build binary to bin/swmcp
make lint # Run golangci-lint (22 linters)
make fix-lint # Auto-fix lint issues
make license-header # Check Apache 2.0 license headers
make fix-license # Fix license headers and dependency licenses
make build-image # Build Docker image skywalking-mcp:latest
make clean # Remove build artifactsUnit tests exist for selected transport/context behavior. CI runs license checks, lint, and docker build.
Three MCP transport modes as cobra subcommands: stdio, sse, streamable.
The SkyWalking OAP URL is resolved in priority order:
- All transports:
--sw-urlflag >http://localhost:12800/graphql
SSE and HTTP transports always use the configured server URL.
Basic auth is configured via --sw-username / --sw-password flags. The startup flags support ${ENV_VAR} syntax to resolve credentials from environment variables (e.g. --sw-password ${MY_SECRET}). If a referenced env var is not set, a warning is logged and the credential is treated as empty.
TLS verification is enforced by default. Use --sw-insecure to skip verification (development/self-signed certs only).
Each transport injects the OAP URL, insecure flag, and auth into the request context via WithSkyWalkingURLAndInsecure() and WithSkyWalkingAuth(). Tools extract them downstream using skywalking-cli's contextkey.BaseURL{}, contextkey.Insecure{}, contextkey.Username{}, and contextkey.Password{}.
sse and streamable transports support --allowed-origins (comma-separated). When set, requests with an Origin header not in the list are rejected with 403 Forbidden. CORS response headers are set for allowed origins. When the flag is empty (default), all origins are permitted. The middleware is injected via WithHTTPServer / WithStreamableHTTPServer so the MCP handler is wrapped rather than forked.
newMCPServer() is the central registration point — it creates the MCP server and calls all Add*Tools(), Add*Resources(), and Add*Prompts() functions. New capabilities must be registered here.
Tool[T, R] is a typed generic wrapper over MCP's untyped interface. ConvertTool() bridges typed handlers into MCP by auto-binding JSON arguments to T and marshaling R back to JSON. If R is already *mcp.CallToolResult, it passes through directly. All tools are marked idempotent by default.
- Most tools use
skywalking-clipackages (pkg/graphql/...) which communicate via GraphQL - MQE tools use direct HTTP calls to the OAP
/graphqlendpoint viaexecuteGraphQLWithContext()inmqe.go. The HTTP client readscontextkey.Insecure{}to configure TLS and validates the URL scheme (http/httpsonly) before each request. - Time handling:
common.goprovidesBuildDurationWithContext()andGetTimeContext()which fetch the OAP server's time/timezone for accurate duration calculations
All MQE tool inputs are validated before use:
validateMQETextField: UTF-8, max length, no control characters — applied to all entity fieldsvalidateLayerField: additionally enforces^[A-Z0-9_]+$forlayer/dest_layervalidateMQEExpression: UTF-8, max 2048 chars, no control characters, max nesting depth 12validateMetricName:^[A-Za-z0-9_.:-]+$pattern, max 128 charsvalidateRegexComplexity: parses the regex AST viaregexp/syntaxand rejects patterns with >50 nodesvalidateURLScheme(common.go): rejects non-http/https OAP URLs before HTTP requests
- Create or edit a file in
internal/tools/(group by domain, e.g.event.go) - Define request struct with
jsontags, write handler usingNewTool(), createAdd*Tools()function - Register in
newMCPServer()inserver.go - Follow existing tools (e.g.
event.go) as reference for the pattern
- Add handler in
internal/prompts/(analysis, trace, or utility group) - Register via
s.AddPrompt()in the corresponding group function inregistry.go
- For static content: embed files with
//go:embedininternal/resources/ - For dynamic content: call internal tool functions in the resource handler
- Register via
s.AddResource()inAddMQEResources()or a new registration function
All .go files must have the Apache 2.0 license header (17-line block). Run make fix-license to auto-fix.
- Max function length: 100 lines / 50 statements
- Cyclomatic complexity: 15
- Line length: 150 chars
- Imports:
goimportswith local prefixmygithub.libinneed.workers.dev/apache/skywalking-mcp - Import order: stdlib, third-party, blank line, local packages
- 22 linters enabled including gosec, errcheck, dupl, gocritic
Tool handlers should return (mcp.NewToolResultError(...), nil) for expected query failures (bad input, OAP errors), not (nil, err). Reserve Go errors for truly unexpected failures. Use the ErrMarshalFailed constant for JSON marshal errors.
Squash-merge only. PRs to main require 1 approval and passing Required status check (license + lint + docker build). Go 1.25.