Gate MCP endpoint behind MCP_ENABLED flag
This commit is contained in:
commit
2b7eb29a0e
5 files changed
+46
-2
No files matched your search
@@ -54,6 +54,9 @@ Optional: set `TWO_FACTOR_ENCRYPTION_KEY` to a 64-character hex string to enable
|
|||||||
authentication. Generate one with `openssl rand -hex 32`. Two-factor authentication is unavailable
|
authentication. Generate one with `openssl rand -hex 32`. Two-factor authentication is unavailable
|
||||||
and cannot be required until this key is set.
|
and cannot be required until this key is set.
|
||||||
|
|
||||||
|
MCP is disabled by default. Set `MCP_ENABLED=1` to enable the `/mcp` endpoint, then
|
||||||
|
authenticate with an API key generated under Settings → API keys.
|
||||||
|
|
||||||
The connection URL format:
|
The connection URL format:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ x-umami-env: &umami-env
|
|||||||
DISABLE_BOT_CHECK: "1"
|
DISABLE_BOT_CHECK: "1"
|
||||||
DISABLE_TELEMETRY: "1"
|
DISABLE_TELEMETRY: "1"
|
||||||
DISABLE_UPDATES: "1"
|
DISABLE_UPDATES: "1"
|
||||||
|
MCP_ENABLED: "1"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
db:
|
db:
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ Authorization: Bearer umami_<your-api-key>
|
|||||||
Use a client that supports bearer tokens or custom authorization headers. The endpoint accepts
|
Use a client that supports bearer tokens or custom authorization headers. The endpoint accepts
|
||||||
self-hosted API keys; browser login tokens are not supported. Tools are read-only and respect
|
self-hosted API keys; browser login tokens are not supported. Tools are read-only and respect
|
||||||
the key owner's existing user/team permissions. Revoke the key in Settings to disconnect access.
|
the key owner's existing user/team permissions. Revoke the key in Settings to disconnect access.
|
||||||
Set `MCP_DISABLED=1` to disable the endpoint.
|
MCP is disabled by default. Set `MCP_ENABLED=1` to enable the endpoint.
|
||||||
|
|
||||||
## Local / stdio
|
## Local / stdio
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import { afterEach, beforeEach, expect, test, vi } from 'vitest';
|
||||||
|
import { authenticateMcpRequest } from '@/lib/mcp/auth';
|
||||||
|
import { DELETE, GET, POST } from './route';
|
||||||
|
|
||||||
|
vi.mock('@umami/mcp', () => ({
|
||||||
|
createUmamiMcpHttpHandler: () => ({ fetch: vi.fn() }),
|
||||||
|
}));
|
||||||
|
vi.mock('@/lib/mcp/auth', () => ({
|
||||||
|
authenticateMcpRequest: vi.fn(),
|
||||||
|
mcpAuthErrorResponse: () => new Response(null, { status: 401 }),
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.mocked(authenticateMcpRequest).mockReset();
|
||||||
|
vi.mocked(authenticateMcpRequest).mockResolvedValue({
|
||||||
|
ok: false,
|
||||||
|
status: 401,
|
||||||
|
error: 'invalid_request',
|
||||||
|
description: 'Missing bearer API key.',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => vi.unstubAllEnvs());
|
||||||
|
|
||||||
|
test.each([undefined, '', '0', 'true'])('MCP stays disabled for MCP_ENABLED=%s', async value => {
|
||||||
|
vi.stubEnv('MCP_ENABLED', value);
|
||||||
|
for (const handler of [GET, POST, DELETE]) {
|
||||||
|
const response = await handler(new Request('http://localhost/mcp'));
|
||||||
|
expect(response.status).toBe(404);
|
||||||
|
}
|
||||||
|
expect(authenticateMcpRequest).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('MCP_ENABLED=1 enables API-key authentication', async () => {
|
||||||
|
vi.stubEnv('MCP_ENABLED', '1');
|
||||||
|
const request = new Request('http://localhost/mcp');
|
||||||
|
const response = await POST(request);
|
||||||
|
expect(response.status).toBe(401);
|
||||||
|
expect(authenticateMcpRequest).toHaveBeenCalledWith(request);
|
||||||
|
});
|
||||||
@@ -38,7 +38,7 @@ const handler = createUmamiMcpHttpHandler({
|
|||||||
});
|
});
|
||||||
|
|
||||||
async function handle(request: Request) {
|
async function handle(request: Request) {
|
||||||
if (process.env.MCP_DISABLED === '1') {
|
if (process.env.MCP_ENABLED !== '1') {
|
||||||
return new Response(null, { status: 404 });
|
return new Response(null, { status: 404 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user