diff --git a/docs/integrations/claude.md b/docs/integrations/claude.md index dda5792bb..c60bd5fa1 100644 --- a/docs/integrations/claude.md +++ b/docs/integrations/claude.md @@ -172,6 +172,10 @@ GET https:///.well-known/oauth-protected-resource/mcp/ GET https:///.well-known/oauth-authorization-server ``` +### Test with the MCP Inspector + +The [MCP Inspector](https://github.com/modelcontextprotocol/inspector) shows what this server exposes from the outside: `tools/list`, input schemas, a test call and the raw JSON-RPC messages. On the server page, open **Quick Connect → MCP Inspector** and run the command it shows (`npx @modelcontextprotocol/inspector --server-url --transport http`, Node 22.19+); the Inspector opens at http://localhost:6274 already pointed at this server. Sign in with OAuth, or add an `X-API-Key` header in the Inspector's server settings. Passing `--header "X-API-Key: "` also works, but leaves the key in your shell history. Never put a key in a link. + --- ## Troubleshooting diff --git a/packages/frontend/src/app/mcp-server/[id]/page.tsx b/packages/frontend/src/app/mcp-server/[id]/page.tsx index 14e1d64f3..234485471 100644 --- a/packages/frontend/src/app/mcp-server/[id]/page.tsx +++ b/packages/frontend/src/app/mcp-server/[id]/page.tsx @@ -12,6 +12,7 @@ import { cn } from '@/lib/utils'; import { ConnectionCheck } from '@/components/connection-check'; import { copyText } from '@/lib/clipboard'; import { CLAUDE_DIRECTORY_URL } from '@/lib/marketing'; +import { INSPECTOR_URL, inspectorCommand } from '@/lib/inspector-url'; // Opens claude.ai straight on its "Add custom connector" dialog. Connectors // moved from Settings to Customize → Connectors; the old settings URL now only @@ -355,6 +356,7 @@ export default function McpServerDetailPage() { { id: 'muse', name: 'Meta Muse', init: 'Mu', tone: 'info' }, { id: 'gemini', name: 'Gemini CLI', init: 'Ge', tone: 'pink' }, { id: 'windsurf', name: 'Windsurf', init: 'Wi', tone: 'success' }, + { id: 'inspector', name: 'MCP Inspector', init: 'In', tone: 'neutral' }, ]; const codeBlock = (text: string, label: string) => ( @@ -618,6 +620,36 @@ Auth: OAuth`; {codeBlock(windsurfConfig, 'windsurf')} ); + case 'inspector': { + const cmd = inspectorCommand(endpointUrl); + return ( +
+

+ Run this command in your terminal (Node 22.19 or later). It starts the Inspector already pointed at this server: +

+
+ {cmd} + +
+

+ The Inspector opens in your browser. If it doesn't, open it here: +

+ {linkAction(INSPECTOR_URL, 'Open in MCP Inspector', true)} +

+ The Inspector hands its session token (MCP_INSPECTOR_API_TOKEN) to the page it opens, so you don't need to copy it. +

+

+ Sign in with OAuth when the Inspector asks (connect, sign in and approve access), or use an API key: set an X-API-Key header in the server's settings in the Inspector. You can also add --header "X-API-Key: <your key>" to the command, but the key then stays in your shell history. +

+ {endpointRow('modal-inspector-url')} +
+ ); + } default: return null; } diff --git a/packages/frontend/src/lib/inspector-url.ts b/packages/frontend/src/lib/inspector-url.ts new file mode 100644 index 000000000..0ce30ff7b --- /dev/null +++ b/packages/frontend/src/lib/inspector-url.ts @@ -0,0 +1,14 @@ +/** Where `npx @modelcontextprotocol/inspector` serves its web UI (v2 default port). */ +export const INSPECTOR_URL = 'http://localhost:6274/'; + +/** + * Command that starts the MCP Inspector already pointed at this server. + * + * Inspector v2 ignores `?serverUrl=` deep links unless they also carry the + * per-launch session token (`autoConnect`), which is secret, so we pre-fill the + * connection through CLI flags instead. Only the server URL goes in here: + * never API keys or tokens. + */ +export function inspectorCommand(endpointUrl: string): string { + return `npx @modelcontextprotocol/inspector --server-url "${endpointUrl}" --transport http`; +} diff --git a/packages/frontend/tests/e2e/mcp-server-connect-clients.spec.ts b/packages/frontend/tests/e2e/mcp-server-connect-clients.spec.ts index bd0ca2fe3..c8501e5b0 100644 --- a/packages/frontend/tests/e2e/mcp-server-connect-clients.spec.ts +++ b/packages/frontend/tests/e2e/mcp-server-connect-clients.spec.ts @@ -96,4 +96,22 @@ test.describe('MCP server Quick Connect', () => { await expect(modal).toContainText('US and Canada'); await expect(modal.getByTestId('claude-directory')).toHaveCount(0); }); + + for (const mode of ['cloud', 'self-hosted'] as const) { + test(`MCP Inspector shows a pre-filled command, a plain link and sign-in steps (${mode})`, async ({ page }) => { + await openServerPage(page, mode); + await openClient(page, 'MCP Inspector'); + + const modal = page.locator('div.fixed').filter({ hasText: 'Connect to MCP Inspector' }); + + // The command points the Inspector at exactly the endpoint shown on the page. + const endpoint = (await modal.locator('code', { hasText: /\/mcp\/s1$/ }).textContent())!.trim(); + const command = modal.locator('code', { hasText: 'npx @modelcontextprotocol/inspector' }); + await expect(command).toHaveText(`npx @modelcontextprotocol/inspector --server-url "${endpoint}" --transport http`); + + // The link opens the local Inspector with no query string, so no credentials can leak. + await expect(modal.getByRole('link', { name: 'Open in MCP Inspector' })).toHaveAttribute('href', 'http://localhost:6274/'); + await expect(modal).toContainText('X-API-Key'); + }); + } });