{"record":{"id":"111d848bef8301ba","repo":"siyuan-note/siyuan","slug":"mcp-request-operation-scope-is-closed","errorCode":null,"errorMessage":"MCP request operation scope is closed","messagePattern":"MCP request operation scope is closed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/mcp/request_scope.go","lineNumber":33,"sourceCode":"// along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\npackage mcp\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"net/http\"\n\t\"sync\"\n\n\t\"github.com/google/uuid\"\n\tmcpsdk \"github.com/modelcontextprotocol/go-sdk/mcp\"\n\t\"github.com/siyuan-note/siyuan/kernel/model\"\n)\n\nconst requestOperationScopeHeader = \"X-Siyuan-Mcp-Request-Scope\"\n\nvar (\n\terrMCPRequestOperationScopeClosed = errors.New(\"MCP request operation scope is closed\")\n\trequestOperationScopes            sync.Map // 键为请求 ID，值为 context.Context。\n)\n\n// withEncryptedBoxOperationScope 为当前 HTTP 响应注册加密笔记本操作作用域。\nfunc withEncryptedBoxOperationScope(handler http.Handler) http.Handler {\n\treturn http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {\n\t\trequestContext, release := model.WithEncryptedBoxOperationScope(request.Context())\n\t\trequestID := uuid.NewString()\n\t\tscopedRequest := request.WithContext(requestContext)\n\t\tscopedRequest.Header = request.Header.Clone()\n\t\tscopedRequest.Header.Set(requestOperationScopeHeader, requestID)\n\t\trequestOperationScopes.Store(requestID, requestContext)\n\t\tdefer func() {\n\t\t\trequestOperationScopes.Delete(requestID)\n\t\t\trelease()\n\t\t}()\n\n\t\thandler.ServeHTTP(writer, scopedRequest)","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/mcp/request_scope.go#L15-L51","documentation":"requestOperationContext looks up the per-request operation scope (a context.Context) registered by request ID in a sync.Map. The declared error errMCPRequestOperationScopeClosed is returned when no scope is registered for the request ID or the stored value is not a context.Context, meaning the scope was never created or already removed/closed.","triggerScenarios":"An MCP handler (or TestRequestOperationScopeBridge) calls requestOperationContext with a request ID that has no entry in requestOperationScopes — e.g. the HTTP middleware withEncryptedBoxOperationScope did not run for that request, the scope was deleted after the request finished, or the stored value failed the type assertion.","commonSituations":"Calling encrypted-box scoped operations outside an MCP HTTP request (background jobs, direct tool invocation bypassing the middleware); a race where the scope was cleaned up before the handler finished.","solutions":["Ensure requests flow through the middleware that registers the operation scope (withEncryptedBoxOperationScope)","Don't retain or reuse the request context after the request completes; fetch it within the request lifetime","If you need the scope in async work, capture values from the context before the scope closes instead of calling requestOperationContext later"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"func scopeOpen(requestID string) bool {\n    v, ok := requestOperationScopes.Load(requestID)\n    _, isCtx := v.(context.Context)\n    return ok && isCtx\n}","tryCatchPattern":"ctx, err := requestOperationContext(requestID)\nif err != nil {\n    if errors.Is(err, errMCPRequestOperationScopeClosed) { /* skip scope work or re-register via middleware */ }\n}","preventionTips":["Route all MCP requests through the scope-registering middleware","Never cache the request-scoped context beyond the request lifetime","Perform scoped encrypted-box work synchronously inside the handler"],"tags":["context","mcp","lifecycle"],"backgroundTag":"invalid-state-transition","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}