Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 33 additions & 2 deletions helicone-mcp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ A Model Context Protocol (MCP) server for querying Helicone observability platfo
"command": "npx",
"args": ["@helicone/mcp@latest"],
"env": {
"HELICONE_API_KEY": "sk-helicone-xxxxxxx-xxxxxxx-xxxxxxx-xxxxxxx"
"HELICONE_API_KEY": "sk-helicone-xxxxxxx-xxxxxxx-xxxxxxx-xxxxxxx",
"HELICONE_BASE_URL": "https://api.helicone.ai"
}
}
}
Expand Down Expand Up @@ -66,6 +67,36 @@ Query sessions with search, time range filtering, and advanced filters.

Both tools support full filter capabilities including model/provider, status/error, time, cost/latency, custom properties, and complex AND/OR combinations.

## API Key
## Configuration

### API Key

Get your API key from [Settings → API Keys](https://us.helicone.ai/settings/api-keys) (or for [EU](https://eu.helicone.ai/settings/api-keys)) and set it as `HELICONE_API_KEY` in your MCP configuration.

### Environment Variables

| Variable | Default | Description |
|---|---|---|
| `HELICONE_API_KEY` | *(required)* | Your Helicone API key |
| `HELICONE_BASE_URL` | `https://api.helicone.ai` | Base URL for the Helicone API. Set to `https://eu.api.helicone.ai` for EU users. |
| `HELICONE_AI_GATEWAY_BASE_URL` | `https://ai-gateway.helicone.ai` | Base URL for the AI Gateway. |

### EU Configuration

For EU users, set `HELICONE_BASE_URL` to point to the EU API endpoint:

```json
{
"mcpServers": {
"helicone": {
"type": "stdio",
"command": "npx",
"args": ["@helicone/mcp@latest"],
"env": {
"HELICONE_API_KEY": "sk-helicone-eu-xxxxxxx-xxxxxxx-xxxxxxx-xxxxxxx",
"HELICONE_BASE_URL": "https://eu.api.helicone.ai"
}
}
}
}
```
1 change: 1 addition & 0 deletions helicone-mcp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"dev": "tsx src/index.ts",
"format": "biome format --write",
"lint:fix": "biome lint --fix",
"test": "vitest run",
"type-check": "tsc --noEmit",
"flatten-types": "tsx flatten-types.ts",
"generate-zod": "npm run flatten-types && ts-to-zod src/types/flat.ts src/types/generated-zod.ts && npm run build"
Expand Down
79 changes: 79 additions & 0 deletions helicone-mcp/src/__tests__/helicone-client.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";

describe("helicone-client base URL configuration", () => {
const originalEnv = process.env;

beforeEach(() => {
vi.resetModules();
process.env = { ...originalEnv };
});

afterEach(() => {
process.env = originalEnv;
});

it("uses default US API base URL when HELICONE_BASE_URL is not set", async () => {
delete process.env.HELICONE_BASE_URL;
const fetchSpy = vi.fn().mockResolvedValue({
ok: true,
json: () => Promise.resolve({ data: [] }),
});
vi.stubGlobal("fetch", fetchSpy);

const { fetchRequests } = await import("../lib/helicone-client.js");
await fetchRequests("test-key", { filter: {}, limit: 1 });

expect(fetchSpy).toHaveBeenCalledWith(
"https://api.helicone.ai/v1/request/query-clickhouse",
expect.any(Object)
);
});

it("uses custom base URL from HELICONE_BASE_URL env var", async () => {
process.env.HELICONE_BASE_URL = "https://eu.api.helicone.ai";
const fetchSpy = vi.fn().mockResolvedValue({
ok: true,
json: () => Promise.resolve({ data: [] }),
});
vi.stubGlobal("fetch", fetchSpy);

const { fetchRequests } = await import("../lib/helicone-client.js");
await fetchRequests("test-key", { filter: {}, limit: 1 });

expect(fetchSpy).toHaveBeenCalledWith(
"https://eu.api.helicone.ai/v1/request/query-clickhouse",
expect.any(Object)
);
});

it("uses custom gateway URL from HELICONE_AI_GATEWAY_BASE_URL env var", async () => {
process.env.HELICONE_AI_GATEWAY_BASE_URL = "https://eu.ai-gateway.helicone.ai";
const fetchSpy = vi.fn().mockResolvedValue({
ok: true,
json: () => Promise.resolve({ id: "test" }),
});
vi.stubGlobal("fetch", fetchSpy);

const { useAiGateway } = await import("../lib/helicone-client.js");
await useAiGateway("test-key", { model: "gpt-4o", messages: [{ role: "user", content: "hi" }] });

expect(fetchSpy).toHaveBeenCalledWith(
"https://eu.ai-gateway.helicone.ai/v1/chat/completions",
expect.any(Object)
);
});

it("sends the API key in the Authorization header", async () => {
const fetchSpy = vi.fn().mockResolvedValue({
ok: true,
json: () => Promise.resolve({ data: [] }),
});
vi.stubGlobal("fetch", fetchSpy);

const { fetchRequests } = await import("../lib/helicone-client.js");
await fetchRequests("sk-helicone-eu-test", { filter: {}, limit: 1 });

const callArgs = fetchSpy.mock.calls[0][1];
expect(callArgs.headers.Authorization).toBe("Bearer sk-helicone-eu-test");
});
});
4 changes: 2 additions & 2 deletions helicone-mcp/src/lib/helicone-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ type SessionQueryParams = components["schemas"]["SessionQueryParams"];
type HeliconeRequest = components["schemas"]["HeliconeRequest"];
type SessionResult = components["schemas"]["SessionResult"];

const HELICONE_API_BASE = "https://api.helicone.ai";
const HELICONE_AI_GATEWAY_BASE = "https://ai-gateway.helicone.ai";
const HELICONE_API_BASE = process.env.HELICONE_BASE_URL || "https://api.helicone.ai";
const HELICONE_AI_GATEWAY_BASE = process.env.HELICONE_AI_GATEWAY_BASE_URL || "https://ai-gateway.helicone.ai";
const REQUEST_BODY_CACHE = new Map<string, { request?: any; response?: any }>();
const CACHE_MAX_SIZE = 10000;

Expand Down