π§ Semantic Function Clustering Analysis
Analysis of repository: github/gh-aw-mcpg
Analysis run: 2026-07-29 | Workflow run Β§30491488955
Executive Summary
A full semantic analysis was performed on all 166 non-test Go source files across 26 packages, cataloguing 1,010 functions and methods. The codebase is generally well-structured with clear separation of concerns. However, several refactoring opportunities were identified:
- Two parallel
responseWriter implementations β httputil.BaseResponseWriter and server.responseWriter correctly use composition, but the server layer re-declares WriteHeader/Write wrappers that only add logging, creating an incomplete abstraction boundary.
- Rate-limit parsing logic split across
githubhttp and server β The detection pipeline spans two packages without a clear boundary contract.
internal/util contains heterogeneous concerns β format.go, netutil.go, toolname.go, json.go, truncate.go, collections.go, and random.go all live together; network utilities sit beside string truncation and JSON deep-clone.
internal/config/validation_shared.go is empty of functions β It only holds package-level vars; this is unusual for a file named _shared.
internal/server/difc_log.go is a server-layer file entirely dedicated to DIFC presentation logic β It could sit closer to the difc package, but its dependency on logger.FilteredItemLogEntry makes it a genuine presentation layer, not a true outlier.
Identified Issues
1. internal/util β Heterogeneous Utility File Scatter
Pattern: The util package mixes seven unrelated responsibilities into one flat directory with no sub-grouping. Each file is individually coherent, but the aggregate package is a catch-all that will grow unbounded.
| File |
Responsibility |
format.go |
String/time formatting for logging |
truncate.go |
String truncation utilities |
collections.go |
Generic collection helpers |
random.go |
Cryptographic random generation |
json.go |
JSON deep-clone |
netutil.go |
Network address normalization |
toolname.go |
MCP tool name parsing |
Recommendation: The files themselves are well-written and individually small. No immediate merge is needed, but the package should be documented as a utility staging area with a policy that functions graduate to more specific packages when callers extend beyond one package. Specifically:
netutil.go (ClientAddr, ClientHost) is only used by the server/cmd layer β consider internal/netutil or moving into internal/httputil.
toolname.go (ParseServerIDFromToolName) encodes server tool-name protocol β consider moving to internal/mcp or internal/server where the separator constant is most relevant.
json.go (DeepCloneJSON) is used in middleware/guard β consider moving to internal/middleware if callers narrow.
Estimated effort: Low β documentation + deferred migration on next related change.
2. Rate-Limit Detection Split Between githubhttp and server
Pattern: Rate-limit detection logic is divided between two packages without a clear contract:
internal/githubhttp/rate_limit.go
- ParseRateLimitResetHeader(value string) time.Time
- ParseRateLimitResetFromText(text string) time.Time
- IsRateLimitText(text string) bool
- RateLimitSignal(resp *http.Response) (bool, string, string)
internal/server/rate_limit.go
- extractRateLimitErrorText(result interface{}) string
- isRateLimitToolResult(result interface{}) (bool, time.Time)
server/rate_limit.go calls githubhttp.IsRateLimitText and githubhttp.ParseRateLimitResetFromText β which is fine. However, extractRateLimitErrorText extracts text from a raw interface{} tool result (a different input shape than an HTTP response), making it a distinct concern that belongs exclusively in the server circuit-breaker layer. The split is acceptable but the file names are misleading.
Recommendation: Rename internal/server/rate_limit.go to internal/server/circuit_breaker_rate_limit.go or move its two private functions directly into circuit_breaker.go, which is the only caller. This makes the ownership boundary explicit.
Estimated effort: 30 minutes β file rename or content merge.
3. internal/config/validation_shared.go β Empty Function File
Pattern: validation_shared.go contains zero functions β only package-level variable declarations (logValidation and customSchemaCache).
// validation_shared.go β current contents
package config
var logValidation = logger.ForFile()
var customSchemaCache sync.Map
Issue: A file named _shared.go with no functions creates a misleading navigation target. Developers opening this file expect shared helper functions, not just package-scoped state variables.
Recommendation: Either:
- Rename to
validation_state.go or validation_vars.go to signal it holds shared state, OR
- Move the two vars into
validation_rules.go or validation_server.go (whichever owns the most validation logic) and delete the file.
Estimated effort: 15 minutes β rename or inline.
4. internal/httputil/response_writer.go vs internal/server/response_writer.go
Pattern: The codebase deliberately uses httputil.BaseResponseWriter as an embedded type in server.responseWriter. This is good composition. However, the server layer re-declares WriteHeader and Write wrappers that only add logResponseWriter.Printf(...) calls:
// server/response_writer.go
func (w *responseWriter) WriteHeader(statusCode int) {
logResponseWriter.Printf("Setting response status code: %d", statusCode)
w.BaseResponseWriter.WriteHeader(statusCode) // just calls through
}
func (w *responseWriter) Write(b []byte) (int, error) {
logResponseWriter.Printf("Writing response body: %d bytes", len(b))
w.body.Write(b)
return w.BaseResponseWriter.Write(b) // calls through after buffering
}
The Write override is necessary (adds body buffering). The WriteHeader override is only for a debug log line, which is unnecessary noise.
Recommendation: Remove the WriteHeader override from server.responseWriter β the base class already logs internally. This reduces the override surface area and makes the intent clearer (responseWriter adds body buffering, not status-code behavior).
Estimated effort: 5 minutes β delete 4 lines.
5. internal/mcp/connection_logging.go β Thin Wrapper File
Pattern: connection_logging.go contains 3 small private functions that wrap the logger package:
func logOutboundRPCRequest(serverID, method string, payload []byte, snapshot *AgentTagsSnapshot)
func logInboundRPCResponse(serverID string, payload []byte, err error, snapshot *AgentTagsSnapshot)
func logInboundRPCResponseFromResult(serverID string, result *Response, err error, snapshot *AgentTagsSnapshot) (*Response, error)
These are thin wrappers with 3-10 lines each. They exist in their own file but are only called from connection.go and connection_methods.go.
Recommendation: Merge into connection.go or connection_methods.go (whichever uses them most) to reduce file count in the mcp package. The mcp package currently has 11 files; consolidating logging helpers would reduce it to 10.
Estimated effort: 15 minutes β inline 3 small functions.
Well-Organized Patterns (No Action Needed)
The following patterns show exemplary organization worth noting:
| Pattern |
Location |
Quality |
| TLS split: generation vs protocol/file-loading |
proxy/tls.go + httputil/tls.go |
β
Clearly documented split with architectural rule comments |
| Validation split by concern |
config/validation_*.go (7 files) |
β
Each file has a single coherent responsibility |
| Guard split by lifecycle |
guard/wasm_lifecycle.go, wasm_exec.go, wasm_parse.go, etc. |
β
Clean functional decomposition |
| Sanitize vs Redact |
sanitize/sanitize.go + sanitize/redact.go |
β
Pattern-based sanitization vs known-secret redaction are correctly separated |
envutil.ExpandEnvArgs vs config.expandVariables |
Different packages |
β
Comment in ExpandEnvArgs explicitly notes the behavioral difference (soft vs hard env expansion) |
Function Clustering Summary
| Cluster |
Files |
Functions |
Organization Quality |
| Validation |
config/validation_*.go (7 files) |
~75 |
β
Excellent β single-purpose files |
| Logging |
logger/*.go (16 files) |
~100 |
β
Good β each logger type has its own file |
| Guard execution |
guard/wasm_*.go (5 files) |
~35 |
β
Good β lifecycle/exec/parse/labels/payload separated |
| DIFC |
difc/*.go (11 files) |
~110 |
β
Good β evaluator, labels, agent, pipeline, resource separated |
| Server |
server/*.go (22 files) |
~130 |
β
Good β feature-per-file pattern |
| Config |
config/*.go (20 files) |
~140 |
β οΈ One empty function file (validation_shared.go) |
| Utility |
util/*.go (7 files) |
~35 |
β οΈ Mixed concerns β acceptable now, policy needed to prevent drift |
| MCP client |
mcp/*.go (11 files) |
~85 |
β οΈ connection_logging.go could be merged |
| Rate limit |
server/rate_limit.go + githubhttp/rate_limit.go |
6 |
β οΈ Boundary is acceptable but file naming could be clearer |
Implementation Checklist
Analysis Metadata
| Metric |
Value |
| Total Go files analyzed |
166 |
| Total functions catalogued |
1,010 |
| Packages analyzed |
26 |
| Function clusters identified |
9 |
| True outliers found |
0 (no functions clearly in wrong files) |
| Near-outliers / boundary issues |
4 |
| Exact code duplicates |
0 |
| Analysis method |
Static grep-based function inventory + semantic naming pattern analysis |
References:
Generated by Semantic Function Refactoring Β· sonnet46 Β· 81.3 AIC Β· β 9.9K Β· β·
π§ Semantic Function Clustering Analysis
Analysis of repository: github/gh-aw-mcpg
Analysis run: 2026-07-29 | Workflow run Β§30491488955
Executive Summary
A full semantic analysis was performed on all 166 non-test Go source files across 26 packages, cataloguing 1,010 functions and methods. The codebase is generally well-structured with clear separation of concerns. However, several refactoring opportunities were identified:
responseWriterimplementations βhttputil.BaseResponseWriterandserver.responseWritercorrectly use composition, but the server layer re-declaresWriteHeader/Writewrappers that only add logging, creating an incomplete abstraction boundary.githubhttpandserverβ The detection pipeline spans two packages without a clear boundary contract.internal/utilcontains heterogeneous concerns βformat.go,netutil.go,toolname.go,json.go,truncate.go,collections.go, andrandom.goall live together; network utilities sit beside string truncation and JSON deep-clone.internal/config/validation_shared.gois empty of functions β It only holds package-level vars; this is unusual for a file named_shared.internal/server/difc_log.gois a server-layer file entirely dedicated to DIFC presentation logic β It could sit closer to thedifcpackage, but its dependency onlogger.FilteredItemLogEntrymakes it a genuine presentation layer, not a true outlier.Identified Issues
1.
internal/utilβ Heterogeneous Utility File ScatterPattern: The
utilpackage mixes seven unrelated responsibilities into one flat directory with no sub-grouping. Each file is individually coherent, but the aggregate package is a catch-all that will grow unbounded.format.gotruncate.gocollections.gorandom.gojson.gonetutil.gotoolname.goRecommendation: The files themselves are well-written and individually small. No immediate merge is needed, but the package should be documented as a utility staging area with a policy that functions graduate to more specific packages when callers extend beyond one package. Specifically:
netutil.go(ClientAddr,ClientHost) is only used by the server/cmd layer β considerinternal/netutilor moving intointernal/httputil.toolname.go(ParseServerIDFromToolName) encodes server tool-name protocol β consider moving tointernal/mcporinternal/serverwhere the separator constant is most relevant.json.go(DeepCloneJSON) is used in middleware/guard β consider moving tointernal/middlewareif callers narrow.Estimated effort: Low β documentation + deferred migration on next related change.
2. Rate-Limit Detection Split Between
githubhttpandserverPattern: Rate-limit detection logic is divided between two packages without a clear contract:
server/rate_limit.gocallsgithubhttp.IsRateLimitTextandgithubhttp.ParseRateLimitResetFromTextβ which is fine. However,extractRateLimitErrorTextextracts text from a rawinterface{}tool result (a different input shape than an HTTP response), making it a distinct concern that belongs exclusively in the server circuit-breaker layer. The split is acceptable but the file names are misleading.Recommendation: Rename
internal/server/rate_limit.gotointernal/server/circuit_breaker_rate_limit.goor move its two private functions directly intocircuit_breaker.go, which is the only caller. This makes the ownership boundary explicit.Estimated effort: 30 minutes β file rename or content merge.
3.
internal/config/validation_shared.goβ Empty Function FilePattern:
validation_shared.gocontains zero functions β only package-level variable declarations (logValidationandcustomSchemaCache).Issue: A file named
_shared.gowith no functions creates a misleading navigation target. Developers opening this file expect shared helper functions, not just package-scoped state variables.Recommendation: Either:
validation_state.goorvalidation_vars.goto signal it holds shared state, ORvalidation_rules.goorvalidation_server.go(whichever owns the most validation logic) and delete the file.Estimated effort: 15 minutes β rename or inline.
4.
internal/httputil/response_writer.govsinternal/server/response_writer.goPattern: The codebase deliberately uses
httputil.BaseResponseWriteras an embedded type inserver.responseWriter. This is good composition. However, the server layer re-declaresWriteHeaderandWritewrappers that only addlogResponseWriter.Printf(...)calls:The
Writeoverride is necessary (adds body buffering). TheWriteHeaderoverride is only for a debug log line, which is unnecessary noise.Recommendation: Remove the
WriteHeaderoverride fromserver.responseWriterβ the base class already logs internally. This reduces the override surface area and makes the intent clearer (responseWriteradds body buffering, not status-code behavior).Estimated effort: 5 minutes β delete 4 lines.
5.
internal/mcp/connection_logging.goβ Thin Wrapper FilePattern:
connection_logging.gocontains 3 small private functions that wrap theloggerpackage:These are thin wrappers with 3-10 lines each. They exist in their own file but are only called from
connection.goandconnection_methods.go.Recommendation: Merge into
connection.goorconnection_methods.go(whichever uses them most) to reduce file count in themcppackage. Themcppackage currently has 11 files; consolidating logging helpers would reduce it to 10.Estimated effort: 15 minutes β inline 3 small functions.
Well-Organized Patterns (No Action Needed)
The following patterns show exemplary organization worth noting:
proxy/tls.go+httputil/tls.goconfig/validation_*.go(7 files)guard/wasm_lifecycle.go,wasm_exec.go,wasm_parse.go, etc.sanitize/sanitize.go+sanitize/redact.goenvutil.ExpandEnvArgsvsconfig.expandVariablesExpandEnvArgsexplicitly notes the behavioral difference (soft vs hard env expansion)Function Clustering Summary
config/validation_*.go(7 files)logger/*.go(16 files)guard/wasm_*.go(5 files)difc/*.go(11 files)server/*.go(22 files)config/*.go(20 files)validation_shared.go)util/*.go(7 files)mcp/*.go(11 files)connection_logging.gocould be mergedserver/rate_limit.go+githubhttp/rate_limit.goImplementation Checklist
internal/config/validation_shared.goβvalidation_state.goWriteHeaderoverride ininternal/server/response_writer.gointernal/server/rate_limit.goβcircuit_breaker_rate_limit.goor merge intocircuit_breaker.gointernal/mcp/connection_logging.gointoconnection.goorconnection_methods.gointernal/utilestablishing graduation policy; tracknetutil.goandtoolname.gofor future migrationAnalysis Metadata
References: