Skip to content

feat(UI): Add gateways table#4537

Merged
gcgoncalves merged 2 commits intoepic/ui-rewritefrom
4172-gateways-table
May 1, 2026
Merged

feat(UI): Add gateways table#4537
gcgoncalves merged 2 commits intoepic/ui-rewritefrom
4172-gateways-table

Conversation

@gcgoncalves
Copy link
Copy Markdown
Collaborator

@gcgoncalves gcgoncalves commented Apr 30, 2026

closes #4172

Screenshot 2026-04-30 at 10 24 22 Screenshot 2026-04-30 at 14 44 28

@gcgoncalves gcgoncalves force-pushed the 4172-gateways-table branch from 08b7b7b to dc986f0 Compare April 30, 2026 09:47
@gcgoncalves gcgoncalves marked this pull request as ready for review April 30, 2026 09:49
@gcgoncalves gcgoncalves marked this pull request as draft April 30, 2026 13:12
@gcgoncalves gcgoncalves force-pushed the 4172-gateways-table branch from dc986f0 to deb55e4 Compare April 30, 2026 13:43
@gcgoncalves gcgoncalves marked this pull request as ready for review April 30, 2026 13:44
@gcgoncalves gcgoncalves force-pushed the 4172-gateways-table branch 3 times, most recently from 93c1415 to e47f730 Compare April 30, 2026 14:14
Copy link
Copy Markdown
Collaborator

@vishu-bh vishu-bh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice progress on the gateways table. I found a couple of blocking runtime issues worth tightening before merge:

  • client/src/pages/Servers.tsx - The table pagination looks like it does not match the current /gateways API contract. The UI sends page / per_page and then derives total_pages: 1, while the backend route appears to use cursor/limit pagination. As a result, users may only ever see the first backend page and won’t be able to reach later gateways. It may be worth switching the UI to use limit + nextCursor, or explicitly adding page pagination support to the API route.

This one is worth checking -

  • client/src/pages/Servers.tsx - The “Test Connection” action calls POST /gateways/{id}/test, but I couldn’t find a matching backend route for that endpoint. The existing gateway test endpoint seems to be POST /admin/gateways/test, with a different request shape. As-is, this action looks like it will fail at runtime for every gateway.

Signed-off-by: Gabriel Costa <gabrielcg@proton.me>
@gcgoncalves gcgoncalves force-pushed the 4172-gateways-table branch from e47f730 to 495cb1d Compare April 30, 2026 14:33
@gcgoncalves gcgoncalves requested a review from vishu-bh April 30, 2026 14:50
@gcgoncalves gcgoncalves force-pushed the 4172-gateways-table branch from c6bc408 to 495cb1d Compare April 30, 2026 15:47
Copy link
Copy Markdown
Collaborator

@marekdano marekdano left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some comments

Comment thread client/package.json Outdated
"@testing-library/react": "^16.1.0",
"@testing-library/user-event": "^14.5.2",
"@types/node": "^25.6.0",
"@types/prop-types": "^15.7.15",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to have prop-types dep? The Prop types should be handled by TS.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unforfunately, TS was complaining about the className type not being defined. I accepted the tradeoff as a dev dependancy.

Comment thread package-lock.json
"requires": true,
"packages": {
"": {
"name": "mcp-context-forge",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this removed?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no idea, tbh. It was removed automatically, I'll put it back. 👍

Comment thread client/src/auth/AuthContext.tsx Outdated
}, [state.user]);

const login = useCallback(async (email: string, password: string): Promise<void> => {
/* prettier-ignore */ const login = useCallback(async (email: string, password: string): Promise<void> => { // pragma: allowlist secret
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to have /* prettier-ignore */ here?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, for the "pragma: allowlist secret", otherwise prettier pushes that to the next line and we have to add that to .secres.baseline. :(

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, found a way to get it through without the ignore:

  const login = useCallback(
    async (
      email: string,
      password: string, // pragma: allowlist secret
    ): Promise<void> => {
      const data = await api.post<LoginResponse>(
        "/auth/login",
        { email, password },
        { unauthenticated: true },
      );

      setToken(data.access_token);
      setState({ user: data.user, isAuthenticated: true });
    },
    [],
  );

Comment thread client/src/components/ui/table.tsx Outdated
Comment on lines +70 to +73
TableHead.displayName = "TableHead";
TableHead.propTypes = {
className: PropTypes.string,
};
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think all displayName and propTypes should be handled by TS instead of PropTypes dep.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll try to resolve without PropTypes, then get back here with the results.

Comment on lines +20 to +26
vi.mock("@/api/client", () => ({
api: {
get: vi.fn(),
delete: vi.fn(),
post: vi.fn(),
},
}));
Copy link
Copy Markdown
Collaborator

@marekdano marekdano May 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can remove this mock with MSW one, where we can define the responses.
We can mock the response in the file /client/src/test/mocks/handlers.ts

Copy link
Copy Markdown
Collaborator Author

@gcgoncalves gcgoncalves May 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TL;DR: MSW (Mock Service Worker) has compatibility issues with AbortSignal in Node.js test environments, causing tests to fail. We've switched to direct API client mocking as a workaround.

Technical Details

The Problem:

  1. Our API client (@/api/client) uses AbortSignal for request cancellation (standard fetch API feature)
  2. MSW intercepts HTTP requests at the network level in Node.js using the http/https modules
  3. Node.js's native HTTP modules don't fully support AbortSignal in the same way browsers do
  4. When MSW tries to intercept requests with AbortSignal, it causes compatibility errors in the test environment

The Solution:
Instead of using MSW to mock HTTP responses, we:

  1. Mock the @/api/client module directly using Vitest's vi.mock()
  2. Control the mock responses at the API client level, before any HTTP layer is involved
  3. This bypasses the AbortSignal incompatibility entirely

Code Example:
1 // Mock the entire api client module
2 vi.mock("@/api/client", () => ({
3 api: {
4 get: vi.fn(),
5 delete: vi.fn(),
6 post: vi.fn(),
7 },
8 }));
9
10 // Then control responses in tests
11 vi.mocked(api.get).mockResolvedValue({ data: [...] });

Trade-offs:

  • ✅ Tests run reliably without AbortSignal issues
  • ✅ Faster test execution (no network layer simulation)
  • ✅ Simpler test setup
  • ⚠️ We're not testing the actual HTTP client behavior (but that's covered by integration tests)

This is a pragmatic solution that keeps unit tests focused on component logic while avoiding Node.js/MSW compatibility issues.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what bob says.

Comment thread client/src/pages/Servers.test.tsx Outdated
Comment on lines +11 to +17
vi.mock("@/router", () => ({
useRouter: () => ({
navigate: mockNavigate,
path: "/app/servers",
params: {},
}),
}));
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to use the real router instead of mocking it?

Comment thread client/src/pages/Servers.tsx Outdated
page: currentPage,
per_page: perPage,
total: response.gateways.length,
total_pages: 1,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we replace this hardcoded value with the value from API?

Comment thread client/src/pages/Servers.tsx Outdated
? {
page: currentPage,
per_page: perPage,
total: response.gateways.length,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we not getting the per_page value from API?


export interface ServersResponse {
gateways: MCPServer[];
nextCursor?: string | null;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it the correct type string? I'd expect number instead.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From our openapi.json:

  {
      "CursorPaginatedGatewaysResponse": {
        "properties": {
          "gateways": {
            "items": {
              "$ref": "#/components/schemas/GatewayRead"
            },
            "type": "array",
            "title": "Gateways",
            "description": "List of gateways for this page"
          },
          "nextCursor": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Nextcursor",
            "description": "Cursor for the next page, null if no more pages"
          }
        },
        "type": "object",
        "required": [
          "gateways"
        ],
        "title": "CursorPaginatedGatewaysResponse",
        "description": "Cursor-paginated response for gateways list endpoint."
      }
  }

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great! 👍

@gcgoncalves gcgoncalves force-pushed the 4172-gateways-table branch from 0ca827d to e888144 Compare May 1, 2026 12:38
@gcgoncalves gcgoncalves requested a review from marekdano May 1, 2026 12:38
@gcgoncalves gcgoncalves force-pushed the 4172-gateways-table branch from e888144 to b816e92 Compare May 1, 2026 12:44
Comment thread client/package-lock.json Outdated
"@testing-library/react": "^16.1.0",
"@testing-library/user-event": "^14.5.2",
"@types/node": "^25.6.0",
"@types/prop-types": "^15.7.15",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prop-types dep is still here. I think you need to run npm install to remove it

Signed-off-by: Gabriel Costa <gabrielcg@proton.me>
@gcgoncalves gcgoncalves force-pushed the 4172-gateways-table branch from b816e92 to 349d5bb Compare May 1, 2026 13:46
Copy link
Copy Markdown
Collaborator

@marekdano marekdano left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks good!

LGTM 🚀

@gcgoncalves gcgoncalves merged commit 29a8e2f into epic/ui-rewrite May 1, 2026
3 checks passed
@gcgoncalves gcgoncalves deleted the 4172-gateways-table branch May 1, 2026 14:14
gcgoncalves added a commit that referenced this pull request May 1, 2026
* feat(ui): Add gateways table

Signed-off-by: Gabriel Costa <gabrielcg@proton.me>

* fix(ui): change pagination to cursor pattern

Signed-off-by: Gabriel Costa <gabrielcg@proton.me>

---------

Signed-off-by: Gabriel Costa <gabrielcg@proton.me>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants