From 5fa67123855aca741cbb0bbdcf30d99fc4b2d72d Mon Sep 17 00:00:00 2001 From: Val Alexander Date: Mon, 15 Jun 2026 08:51:55 -0500 Subject: [PATCH 1/3] fix(mcp): filter private comments for portal details --- .../lib/server/mcp/__tests__/handler.test.ts | 60 +++++++++++++++++++ apps/web/src/lib/server/mcp/tools.ts | 6 +- 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/apps/web/src/lib/server/mcp/__tests__/handler.test.ts b/apps/web/src/lib/server/mcp/__tests__/handler.test.ts index 27dae6b29..f521eb41b 100644 --- a/apps/web/src/lib/server/mcp/__tests__/handler.test.ts +++ b/apps/web/src/lib/server/mcp/__tests__/handler.test.ts @@ -904,6 +904,66 @@ describe('MCP HTTP Handler', () => { expect(text.comments).toEqual([]) }) + it('should exclude private comments from OAuth portal user post details', async () => { + const { getDeveloperConfig } = await import('@/lib/server/domains/settings/settings.service') + const { getCommentsWithReplies } = await import('@/lib/server/domains/posts/post.query') + vi.mocked(getDeveloperConfig) + .mockResolvedValueOnce({ + mcpEnabled: true, + mcpPortalAccessEnabled: true, + }) + .mockResolvedValueOnce({ + mcpEnabled: true, + mcpPortalAccessEnabled: true, + }) + await setupValidOAuth({ role: 'user', scopes: ['read:feedback'] }) + + const { handleMcpRequest } = await import('../handler') + await handleMcpRequest( + oauthRequest( + jsonRpcRequest('initialize', { + protocolVersion: '2025-03-26', + capabilities: {}, + clientInfo: { name: 'test', version: '1.0' }, + }) + ) + ) + await setupValidOAuth({ role: 'user', scopes: ['read:feedback'] }) + + const response = await handleMcpRequest( + oauthRequest( + jsonRpcRequest('tools/call', { + name: 'get_details', + arguments: { id: 'post_test' }, + }) + ) + ) + + expect(response.status).toBe(200) + expect(vi.mocked(getCommentsWithReplies)).toHaveBeenCalledWith('post_test', undefined, { + includePrivate: false, + }) + }) + + it('should include private comments in team member post details', async () => { + const { getCommentsWithReplies } = await import('@/lib/server/domains/posts/post.query') + const handleMcpRequest = await initializeSession() + + const response = await handleMcpRequest( + mcpRequest( + jsonRpcRequest('tools/call', { + name: 'get_details', + arguments: { id: 'post_test' }, + }) + ) + ) + + expect(response.status).toBe(200) + expect(vi.mocked(getCommentsWithReplies)).toHaveBeenCalledWith('post_test', undefined, { + includePrivate: true, + }) + }) + // ── get_details tool (changelog) ──────────────────────────────────────── it('should handle tools/call for get_details with changelog TypeID', async () => { diff --git a/apps/web/src/lib/server/mcp/tools.ts b/apps/web/src/lib/server/mcp/tools.ts index e834a7e09..e61e44c34 100644 --- a/apps/web/src/lib/server/mcp/tools.ts +++ b/apps/web/src/lib/server/mcp/tools.ts @@ -2043,14 +2043,14 @@ async function searchArticles(args: SearchArgs): Promise { // ============================================================================ async function getPostDetails(postId: PostId, auth: McpAuthContext): Promise { + const includeTeamOnlyFields = isTeamMember(auth.role) + const [post, comments, mergedPosts] = await Promise.all([ getPostWithDetails(postId), - getCommentsWithReplies(postId), + getCommentsWithReplies(postId, undefined, { includePrivate: includeTeamOnlyFields }), getMergedPosts(postId), ]) - const includeTeamOnlyFields = isTeamMember(auth.role) - return jsonResult({ id: post.id, title: post.title, From 01f85773bae9ce1be27fa0e8e062845a92a068c4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 23 Jun 2026 05:35:10 +0000 Subject: [PATCH 2/3] test(mcp): stabilize includePrivate assertions --- .../lib/server/mcp/__tests__/handler.test.ts | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/apps/web/src/lib/server/mcp/__tests__/handler.test.ts b/apps/web/src/lib/server/mcp/__tests__/handler.test.ts index f521eb41b..1c5893e2b 100644 --- a/apps/web/src/lib/server/mcp/__tests__/handler.test.ts +++ b/apps/web/src/lib/server/mcp/__tests__/handler.test.ts @@ -907,15 +907,10 @@ describe('MCP HTTP Handler', () => { it('should exclude private comments from OAuth portal user post details', async () => { const { getDeveloperConfig } = await import('@/lib/server/domains/settings/settings.service') const { getCommentsWithReplies } = await import('@/lib/server/domains/posts/post.query') - vi.mocked(getDeveloperConfig) - .mockResolvedValueOnce({ - mcpEnabled: true, - mcpPortalAccessEnabled: true, - }) - .mockResolvedValueOnce({ - mcpEnabled: true, - mcpPortalAccessEnabled: true, - }) + vi.mocked(getDeveloperConfig).mockResolvedValue({ + mcpEnabled: true, + mcpPortalAccessEnabled: true, + }) await setupValidOAuth({ role: 'user', scopes: ['read:feedback'] }) const { handleMcpRequest } = await import('../handler') @@ -929,6 +924,7 @@ describe('MCP HTTP Handler', () => { ) ) await setupValidOAuth({ role: 'user', scopes: ['read:feedback'] }) + vi.mocked(getCommentsWithReplies).mockClear() const response = await handleMcpRequest( oauthRequest( @@ -940,7 +936,8 @@ describe('MCP HTTP Handler', () => { ) expect(response.status).toBe(200) - expect(vi.mocked(getCommentsWithReplies)).toHaveBeenCalledWith('post_test', undefined, { + expect(vi.mocked(getCommentsWithReplies)).toHaveBeenCalledTimes(1) + expect(vi.mocked(getCommentsWithReplies)).toHaveBeenLastCalledWith('post_test', undefined, { includePrivate: false, }) }) @@ -948,6 +945,7 @@ describe('MCP HTTP Handler', () => { it('should include private comments in team member post details', async () => { const { getCommentsWithReplies } = await import('@/lib/server/domains/posts/post.query') const handleMcpRequest = await initializeSession() + vi.mocked(getCommentsWithReplies).mockClear() const response = await handleMcpRequest( mcpRequest( @@ -959,7 +957,8 @@ describe('MCP HTTP Handler', () => { ) expect(response.status).toBe(200) - expect(vi.mocked(getCommentsWithReplies)).toHaveBeenCalledWith('post_test', undefined, { + expect(vi.mocked(getCommentsWithReplies)).toHaveBeenCalledTimes(1) + expect(vi.mocked(getCommentsWithReplies)).toHaveBeenLastCalledWith('post_test', undefined, { includePrivate: true, }) }) From 99f6f7c34fceefd761e40686b0e024ff4faf7253 Mon Sep 17 00:00:00 2001 From: Val Alexander Date: Mon, 6 Jul 2026 03:07:07 -0500 Subject: [PATCH 3/3] test(mcp): align private comment filter assertions --- apps/web/src/lib/server/mcp/__tests__/handler.test.ts | 9 +++++---- apps/web/src/lib/server/mcp/tools.ts | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/apps/web/src/lib/server/mcp/__tests__/handler.test.ts b/apps/web/src/lib/server/mcp/__tests__/handler.test.ts index 1c5893e2b..d56bbdcb5 100644 --- a/apps/web/src/lib/server/mcp/__tests__/handler.test.ts +++ b/apps/web/src/lib/server/mcp/__tests__/handler.test.ts @@ -1,5 +1,6 @@ import { describe, it, expect, vi, beforeEach } from 'vitest' import type { ApiKey } from '@/lib/server/domains/api-keys' +import type { PostListItem } from '@/lib/server/domains/posts/post.types' import type { PrincipalId, ApiKeyId, UserId } from '@opencoven-feedback/ids' // ── Mocks ────────────────────────────────────────────────────────────────────── @@ -938,7 +939,7 @@ describe('MCP HTTP Handler', () => { expect(response.status).toBe(200) expect(vi.mocked(getCommentsWithReplies)).toHaveBeenCalledTimes(1) expect(vi.mocked(getCommentsWithReplies)).toHaveBeenLastCalledWith('post_test', undefined, { - includePrivate: false, + publicOnly: true, }) }) @@ -959,7 +960,7 @@ describe('MCP HTTP Handler', () => { expect(response.status).toBe(200) expect(vi.mocked(getCommentsWithReplies)).toHaveBeenCalledTimes(1) expect(vi.mocked(getCommentsWithReplies)).toHaveBeenLastCalledWith('post_test', undefined, { - includePrivate: true, + publicOnly: false, }) }) @@ -1733,7 +1734,7 @@ describe('MCP HTTP Handler', () => { voteCount: 1, commentCount: 1, boardId: 'board_test', - board: { name: 'Feedback' }, + board: { id: 'board_test', name: 'Feedback', slug: 'feedback' }, statusId: 'status_test', authorName: 'Portal User', ownerPrincipalId: null, @@ -1743,7 +1744,7 @@ describe('MCP HTTP Handler', () => { isCommentsLocked: false, createdAt: new Date('2026-01-01'), deletedAt: null, - }, + } as unknown as PostListItem, ], nextCursor: null, hasMore: false, diff --git a/apps/web/src/lib/server/mcp/tools.ts b/apps/web/src/lib/server/mcp/tools.ts index e61e44c34..b18efbaae 100644 --- a/apps/web/src/lib/server/mcp/tools.ts +++ b/apps/web/src/lib/server/mcp/tools.ts @@ -2047,7 +2047,7 @@ async function getPostDetails(postId: PostId, auth: McpAuthContext): Promise