feat: Add MCP server#876
Conversation
104203d to
56a6372
Compare
56a6372 to
4269638
Compare
- BookSearchTool: collapse else-if(!semanticAvailable)+else into single else with ternary to remove constant-condition CodeQL finding - BookGuidelinesTool: use pattern match (is int / is GuidelineType) to extract nullable locals before lambda capture, removing nullable dereference warnings at lines 38 and 41 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…elect - Remove GetBookMetadata() MCP tool and trim BookMetadata class to SiteUrl only - Fix PR #876 feedback: refactor foreach+clone into codeLines.Select(line => line.CloneNode(deep:true)) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add McpTokenService with JWT token generation/validation - Add McpTokenController for token management UI - Add MCP Access page to account management - Configure MCP server with BookSearchTool - Update ModelContextProtocol packages to 0.9.0-preview.2 - Add Microsoft.AspNetCore.Authentication.JwtBearer - Fix MCP client API: use concrete McpClient type - Add toolCallDepth guard to prevent infinite recursion Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add IGuidelinesService + GuidelinesService (singleton) for guidelines.json - Add BookListingTool: GetListingSourceCode, SearchListingsByCode - Add BookGuidelinesTool: GetCSharpGuidelines, GetGuidelinesByTopic - Add BookContentTool: GetSectionContent, GetListingWithContext, GetNavigationContext, GetChapterSummary - Extend BookSearchTool: GetChapterSections, GetDirectContentUrl, GetBookMetadata, LookupConcept, CheckTopicCoverage, FindBookHelpForDiagnostic, FindRelatedSections - Register all new tools in Program.cs MCP block Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove Destructive = false from all 17 MCP tool annotations (default per spec) - Fix FindBookHelpForDiagnostic description to accurately reflect guidelines output - Add 500-char max length validation to SearchBookContent, LookupConcept, CheckTopicCoverage, FindBookHelpForDiagnostic - Add missing empty check to SearchBookContent - Make GetSectionContent async (File.ReadAllTextAsync + doc.LoadHtml, removes TOCTOU File.Exists) - Add path canonicalization via Path.GetRelativePath (robust cross-platform, handles drive-root edge case) - Add .html file extension allowlist check on resolved path - Add AnchorId validation via [GeneratedRegex] before XPath interpolation - Separate IOException catches: FileNotFoundException/DirectoryNotFoundException show 'not generated yet', UnauthorizedAccessException and IOException show generic messages (no ex.Message leak) - Make BookContentTool a partial class for GeneratedRegex support Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The MCP spec (and C# SDK) default destructiveHint to true. Explicitly setting Destructive = false is required so clients know these read-only book search tools are non-destructive. Note: per spec, destructiveHint is 'only meaningful when readOnlyHint == false', but the SDK still emits the value on the wire so explicit false is the safe, correct choice. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- BookSearchTool: collapse else-if(!semanticAvailable)+else into single else with ternary to remove constant-condition CodeQL finding - BookGuidelinesTool: use pattern match (is int / is GuidelineType) to extract nullable locals before lambda capture, removing nullable dereference warnings at lines 38 and 41 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…all sites The helper class only had one method (NormalizeExtension) used in 3 places. The VB.NET/F# cases were dead code — all 1,396 real book listings are .cs. Simplified to a ternary inline at each call site; non-cs extensions pass through unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…elect - Remove GetBookMetadata() MCP tool and trim BookMetadata class to SiteUrl only - Fix PR #876 feedback: refactor foreach+clone into codeLines.Select(line => line.CloneNode(deep:true)) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ea81960 to
2d5678f
Compare
…h tools - AISearchService.ExecuteVectorSearch gains optional top param (default 5, clamped to 1-10) - SearchBookContent, LookupConcept, FindRelatedSections expose maxResults to MCP clients - Raises default from 3→5 for better coverage of broad C# concepts Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
danOIntellitect
left a comment
There was a problem hiding this comment.
Looks good to me generally, biggest thing I noticed is the usage of stringbuilders in the mcp tool themselves, this is leading to lots of replication of string patterns which could be refactored in a couple different ways.
-
Use structuredcontent which allows you to just return a C# object instead. For some of these outputs this would look nice because you have basically done a pseudo-markdown approach.
[McpServerTool(UseStructuredContent = true)] -
If you still want the string output for token reasons (which is a good reason) then I would still look at wrapping the output in classes and putting the stringbuilder logic there, and then just at the end doing a object.ToMcpString() or something similar. I view mcp tools very similar to ASP.NET controllers where I want very little processing or formatting at that level, they should handle input validation, authentication, authorization, etc. Not the output itself
- Issue 1: Replace anonymous rate-limit body with typed McpRateLimitErrorEnvelope records; use JsonSerializer.SerializeAsync instead of WriteAsync(Serialize(...)) - Issue 2: Strip prose default values from all tool Description attributes - Issue 3: Merge GetGuidelinesByTopic into GetCSharpGuidelines via optional 'topic' param; delete redundant GetGuidelinesByTopic tool - Issue 4: Make /mcp-setup page dynamic — inject IEnumerable<McpServerTool> into McpSetupController; replace 330+ lines of static HTML cards with Razor @foreach loop - Issue 5: Add listing search pattern minimum validation (>=2 alphanumeric chars or recognized C# operator) - Issues 6+7: Extract duplicated FormatGuidelineType/FormatType to shared GuidelineTypeExtensions.ToDisplayString() extension method; delete private copies Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Issue 1: Replace anonymous rate-limit body with typed McpRateLimitErrorEnvelope records; use JsonSerializer.SerializeAsync instead of WriteAsync(Serialize(...)) - Issue 2: Strip prose default values from all tool Description attributes - Issue 3: Merge GetGuidelinesByTopic into GetCSharpGuidelines via optional 'topic' param; delete redundant GetGuidelinesByTopic tool - Issue 4: Make /mcp-setup page dynamic — inject IEnumerable<McpServerTool> into McpSetupController; replace 330+ lines of static HTML cards with Razor @foreach loop - Issue 5: Add listing search pattern minimum validation (>=2 alphanumeric chars or recognized C# operator) - Issues 6+7: Extract duplicated FormatGuidelineType/FormatType to shared GuidelineTypeExtensions.ToDisplayString() extension method; delete private copies Fix 3 subagent-found bugs: trimmedPattern in search, Razor HTML escaping, whitespace-only type guard - BookListingTool.cs: use trimmedPattern (not pattern) in Contains() search call - Index.cshtml: fix Razor HTML escaping of optional badge via @if block - BookGuidelinesTool.cs: use IsNullOrWhiteSpace guard for type param - BookGuidelinesTool.cs: fix description to say substring match not exact text Use MCP SDK JsonRpcError/JsonRpcErrorDetail instead of custom records Replace McpRateLimitErrorEnvelope and McpRateLimitErrorDetail private records with the SDK's built-in ModelContextProtocol.Protocol types, as suggested in PR review.
fbc042e to
cfba677
Compare
Fixes #761