Skip to content
Merged
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
2 changes: 1 addition & 1 deletion TELEMETRY.md
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ Attributes: `gen_ai.provider.name`, `gen_ai.request.model`,
| ------------------------- | -------------------------------------------- | --------------------------------------- |
| `VITE_SENTRY_DSN` | Cloudflare Sentry telemetry | disabled when unset |
| `VITE_SENTRY_ENVIRONMENT` | Cloudflare Sentry environment | development |
| `SENTRY_DSN` | stdio and Node telemetry | disabled when unset |
| `SENTRY_CLI_NO_TELEMETRY` | stdio telemetry off when `1` | on, to the CLI's project |
| `SENTRY_RELEASE` | stdio release tag | unset |
| `SENTRY_HOST` | upstream Sentry host and `app.upstream.host` | `sentry.io` |
| `NODE_ENV` | stdio environment | production fallback in packaged runtime |
Expand Down
17 changes: 9 additions & 8 deletions docs/operations/monitoring.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ Different Sentry SDKs for different environments:

- **Core server**: `@sentry/core` (platform-agnostic)
- **Cloudflare Workers**: `@sentry/cloudflare`
- **Node.js stdio**: `@sentry/node`. Without `--sentry-dsn`/`SENTRY_DSN`, the
stdio server reports to the Sentry CLI's project and honors the CLI's
opt-outs (`SENTRY_CLI_NO_TELEMETRY=1`, `DO_NOT_TRACK=1`, or
`sentry cli defaults telemetry off`).
- **Node.js stdio**: `@sentry/node`. The stdio server always reports to the
Sentry CLI's project (`SENTRY_CLI_DSN`) and honors the CLI's opt-outs
(`SENTRY_CLI_NO_TELEMETRY=1`, `DO_NOT_TRACK=1`, or
`sentry cli defaults telemetry off`). It never reads `SENTRY_DSN`: a server
started from a project shell or IDE would inherit the app's DSN.
- **React client**: `@sentry/react`

## Core Server Instrumentation
Expand Down Expand Up @@ -132,7 +133,8 @@ export default Sentry.withSentry((env) => getSentryConfig(env), {
import * as Sentry from "@sentry/node";

Sentry.init({
dsn: process.env.SENTRY_DSN,
dsn: SENTRY_CLI_DSN,
enabled: isTelemetryEnabled(),
environment: process.env.NODE_ENV || "production",
integrations: [Sentry.nodeProfilingIntegration()],
tracesSampleRate: 0.1,
Expand Down Expand Up @@ -355,9 +357,8 @@ Interpretation:
VITE_SENTRY_DSN=https://...@sentry.io/...
VITE_SENTRY_ENVIRONMENT=production

# Node.js (runtime)
SENTRY_DSN=https://...@sentry.io/...
NODE_ENV=production
# Node.js stdio (runtime): telemetry goes to the CLI's project; opt out with
SENTRY_CLI_NO_TELEMETRY=1
```

## Best Practices
Expand Down
8 changes: 2 additions & 6 deletions docs/testing/stdio.md
Original file line number Diff line number Diff line change
Expand Up @@ -385,10 +385,6 @@ my-org/my-project # Limit calls to this project
--agent-provider=openai # openai, azure-openai, or anthropic
--openai-base-url=URL # Custom OpenAI endpoint

# Sentry reporting
--sentry-dsn=DSN # Custom Sentry DSN for telemetry
--sentry-dsn= # Disable telemetry

# Help
--help # Show all options
--version, -v # Show version (-v alone; elsewhere it is --verbose)
Expand All @@ -411,8 +407,8 @@ MCP_DISABLE_SKILLS=seer
# AI features
OPENAI_API_KEY=your-key # For AI-powered search tools like search_errors/search_traces/search_issues

# Sentry reporting
SENTRY_DSN=your-dsn
# Telemetry (always sent to the Sentry CLI's project)
SENTRY_CLI_NO_TELEMETRY=1 # Turn it off (DO_NOT_TRACK=1 works too)
```

**Priority:** Command-line flags override environment variables.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ Start a local Sentry MCP server
- `--anthropic-base-url <value> - Anthropic API base URL`
- `--anthropic-model <value> - Anthropic model (default: claude-sonnet-4-5)`
- `--mcp-url <value> - MCP server URL reported in telemetry`
- `--sentry-dsn <value> - DSN for the server's own telemetry`

**Examples:**

Expand Down
2 changes: 0 additions & 2 deletions packages/cli/src/commands/mcp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ export const mcpCommand = buildCommand({
" SENTRY_AUTH_TOKEN Sentry token (same as for other commands)\n" +
" SENTRY_HOST, SENTRY_URL Sentry instance (same as for other commands; http:// for plain HTTP)\n" +
" MCP_URL MCP server URL reported in telemetry\n" +
" SENTRY_DSN DSN for the server's own telemetry\n" +
" OPENAI_API_KEY, ANTHROPIC_API_KEY, OPENROUTER_API_KEY\n" +
" LLM API key for AI-powered search tools\n" +
" OPENAI_MODEL, ANTHROPIC_MODEL, OPENROUTER_MODEL\n" +
Expand Down Expand Up @@ -175,7 +174,6 @@ export const mcpCommand = buildCommand({
"Anthropic model (default: claude-sonnet-4-5)",
),
"mcp-url": stringFlag("MCP server URL reported in telemetry"),
"sentry-dsn": stringFlag("DSN for the server's own telemetry"),
},
},
// oxlint-disable-next-line require-yield -- The server speaks JSON-RPC on stdout; the command yields no output.
Expand Down
13 changes: 3 additions & 10 deletions packages/cli/src/lib/mcp/args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ export type McpFlags = {
/** Deprecated: an `http://` `SENTRY_URL`. */
readonly "insecure-http": boolean;
readonly "mcp-url"?: string;
readonly "sentry-dsn"?: string;
readonly "openai-base-url"?: string;
readonly "openai-model"?: string;
readonly "anthropic-base-url"?: string;
Expand Down Expand Up @@ -151,9 +150,9 @@ function scopeFlag(

/**
* Split `--flag=value` into `--flag value` where Stricli does not read the
* joined form: an empty value (`--sentry-dsn=` turns off the server's
* telemetry) or one with a line break (a token pasted with its newline).
* MCP client configurations have always passed both to `@sentry/mcp-server`.
* joined form: an empty value or one with a line break (a token pasted with
* its newline). MCP client configurations have always passed both to
* `@sentry/mcp-server`.
*/
export function splitInlineFlagValues(args: readonly string[]): string[] {
return args.flatMap((arg) => {
Expand All @@ -170,7 +169,6 @@ export function splitInlineFlagValues(args: readonly string[]): string[] {
/** Values read from the environment. */
export type McpEnvArgs = {
mcpUrl?: string;
sentryDsn?: string;
openaiModel?: string;
anthropicModel?: string;
agentProvider?: string;
Expand All @@ -184,7 +182,6 @@ export type McpMergedArgs = {
insecureHttp: boolean;
url?: string;
mcpUrl?: string;
sentryDsn?: string;
openaiBaseUrl?: string;
openaiModel?: string;
anthropicBaseUrl?: string;
Expand All @@ -211,9 +208,6 @@ export function parseMcpEnv(env: NodeJS.ProcessEnv): McpEnvArgs {
if (env.MCP_URL) {
fromEnv.mcpUrl = env.MCP_URL;
}
if (env.SENTRY_DSN || env.DEFAULT_SENTRY_DSN) {
fromEnv.sentryDsn = env.SENTRY_DSN || env.DEFAULT_SENTRY_DSN;
}
if (env.OPENAI_MODEL) {
fromEnv.openaiModel = env.OPENAI_MODEL;
}
Expand Down Expand Up @@ -243,7 +237,6 @@ export function mergeMcpArgs(
host: flags.host,
insecureHttp: flags["insecure-http"],
mcpUrl: flags["mcp-url"] ?? env.mcpUrl,
sentryDsn: flags["sentry-dsn"] ?? env.sentryDsn,
openaiBaseUrl: flags["openai-base-url"],
openaiModel: flags["openai-model"] ?? env.openaiModel,
anthropicBaseUrl: flags["anthropic-base-url"],
Expand Down
2 changes: 0 additions & 2 deletions packages/cli/src/lib/mcp/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ export type McpServerConfig = {
sentryHost: string;
sentryProtocol: "http" | "https";
mcpUrl?: string;
sentryDsn?: string;
openaiBaseUrl?: string;
openaiModel?: string;
anthropicBaseUrl?: string;
Expand Down Expand Up @@ -179,7 +178,6 @@ export function finalizeMcpConfig(input: McpMergedArgs): McpServerConfig {
sentryHost,
sentryProtocol: input.insecureHttp ? "http" : "https",
mcpUrl: input.mcpUrl,
sentryDsn: input.sentryDsn,
// Anthropic base URLs follow the same rules as OpenAI ones.
openaiBaseUrl: input.openaiBaseUrl
? validated(() => validateOpenAiBaseUrlThrows(input.openaiBaseUrl ?? ""))
Expand Down
16 changes: 5 additions & 11 deletions packages/cli/src/lib/mcp/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,12 +234,6 @@ export function initMcpTelemetry(config: McpServerConfig): void {
return;
}

// An empty `--sentry-dsn=` turns telemetry off. Skip `init` so neither the
// CLI's DSN nor a shell SENTRY_DSN starts the SDK.
if (config.sentryDsn === "") {
return;
}

const { name, packageName } = getAppIdentity();
const scopeContext = {
"app.server.version": CLI_VERSION,
Expand All @@ -250,12 +244,12 @@ export function initMcpTelemetry(config: McpServerConfig): void {
"app.url.full": config.mcpUrl,
};

// Without `--sentry-dsn`/`SENTRY_DSN`, report to the CLI's own project,
// honoring the CLI's telemetry opt-outs.
const useCliDsn = config.sentryDsn === undefined;
// Always the CLI's own project, never a DSN from the environment: an MCP
// server started from a project shell or IDE inherits the app's SENTRY_DSN.
// Only the CLI's telemetry opt-outs turn it off.
init({
dsn: config.sentryDsn ?? SENTRY_CLI_DSN,
enabled: !useCliDsn || isTelemetryEnabled(),
dsn: SENTRY_CLI_DSN,
enabled: isTelemetryEnabled(),
tracesSampleRate: 1,
beforeSend: sentryBeforeSend,
initialScope: {
Expand Down
1 change: 0 additions & 1 deletion packages/cli/test/lib/mcp-command.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,6 @@ describe("running `mcp`", () => {
"--disable-skills",
"--agent-provider",
"--mcp-url",
"--sentry-dsn",
]) {
expect(stdout).toContain(flag);
}
Expand Down
32 changes: 13 additions & 19 deletions packages/cli/test/lib/mcp/args.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ describe("sentry mcp flags", () => {
"--insecure-http",
"--url=https://example.com",
"--mcp-url=https://mcp.example.com",
"--sentry-dsn=dsn",
"--openai-base-url=https://api.example.com/v1",
"--skills=inspect,triage",
"--all-skills",
Expand All @@ -28,7 +27,6 @@ describe("sentry mcp flags", () => {
"insecure-http": true,
url: "https://example.com",
"mcp-url": "https://mcp.example.com",
"sentry-dsn": "dsn",
"openai-base-url": "https://api.example.com/v1",
skills: "inspect,triage",
"all-skills": true,
Expand Down Expand Up @@ -57,13 +55,17 @@ describe("sentry mcp flags", () => {
);

it("accepts empty and multi-line --flag=value forms", async () => {
const flags = await parseMcpFlags(["--sentry-dsn=", "--access-token=a\nb"]);
expect(flags["sentry-dsn"]).toBe("");
const flags = await parseMcpFlags(["--mcp-url=", "--access-token=a\nb"]);
expect(flags["mcp-url"]).toBe("");
expect(flags["access-token"]).toBe("a\nb");
});

it("rejects unknown flags and a second positional", async () => {
await expect(parseMcpFlags(["--unknown"])).rejects.toThrow(/unknown/);
// Removed: MCP telemetry always goes to the CLI's project.
await expect(parseMcpFlags(["--sentry-dsn="])).rejects.toThrow(
/sentry-dsn/,
);
await expect(parseMcpFlags(["acme/", "extra"])).rejects.toThrow(/extra/);
});
});
Expand All @@ -72,14 +74,14 @@ describe("splitInlineFlagValues", () => {
it("splits only the joined forms Stricli does not read", () => {
expect(
splitInlineFlagValues([
"--sentry-dsn=",
"--mcp-url=",
"--access-token=tok\n",
"--host=sentry.io",
"--all-skills",
"x=",
]),
).toEqual([
"--sentry-dsn",
"--mcp-url",
"",
"--access-token",
"tok\n",
Expand All @@ -95,11 +97,9 @@ describe("parseMcpEnv", () => {
const env = parseMcpEnv({
SENTRY_ACCESS_TOKEN: "envtok",
MCP_URL: "envmcp",
SENTRY_DSN: "envdsn",
MCP_SKILLS: "inspect,triage",
});
expect(env.mcpUrl).toBe("envmcp");
expect(env.sentryDsn).toBe("envdsn");
expect(env.skills).toBe("inspect,triage");
});

Expand All @@ -117,21 +117,18 @@ describe("mergeMcpArgs", () => {
const env = parseMcpEnv({
SENTRY_ACCESS_TOKEN: "envtok",
MCP_URL: "envmcp",
SENTRY_DSN: "envdsn",
});
const flags = await parseMcpFlags([
"--access-token=clitok",
"--host=clihost",
"--insecure-http",
"--mcp-url=climcp",
"--sentry-dsn=clidsn",
"--openai-base-url=https://api.cli/v1",
]);
const merged = mergeMcpArgs(flags, env);
expect(merged.host).toBe("clihost");
expect(merged.insecureHttp).toBe(true);
expect(merged.mcpUrl).toBe("climcp");
expect(merged.sentryDsn).toBe("clidsn");
expect(merged.openaiBaseUrl).toBe("https://api.cli/v1");
});

Expand Down Expand Up @@ -183,7 +180,6 @@ describe("@sentry/mcp-server flag parity", () => {
"--insecure-http",
"--url=https://sentry.example.com",
"--mcp-url=https://mcp.example.com",
"--sentry-dsn=dsn",
"--openai-base-url=https://openai.example.com/v1",
"--openai-model=gpt-test",
"--anthropic-base-url=https://anthropic.example.com",
Expand All @@ -201,7 +197,6 @@ describe("@sentry/mcp-server flag parity", () => {
insecureHttp: true,
url: "https://sentry.example.com",
mcpUrl: "https://mcp.example.com",
sentryDsn: "dsn",
openaiBaseUrl: "https://openai.example.com/v1",
openaiModel: "gpt-test",
anthropicBaseUrl: "https://anthropic.example.com",
Expand Down Expand Up @@ -233,7 +228,6 @@ describe("@sentry/mcp-server flag parity", () => {
parseMcpEnv({
SENTRY_ACCESS_TOKEN: "envtok",
MCP_URL: "https://mcp.example.com",
SENTRY_DSN: "dsn",
OPENAI_MODEL: "gpt-test",
ANTHROPIC_MODEL: "claude-test",
EMBEDDED_AGENT_PROVIDER: "openrouter",
Expand All @@ -242,7 +236,6 @@ describe("@sentry/mcp-server flag parity", () => {
}),
).toEqual({
mcpUrl: "https://mcp.example.com",
sentryDsn: "dsn",
openaiModel: "gpt-test",
anthropicModel: "claude-test",
agentProvider: "openrouter",
Expand All @@ -251,10 +244,11 @@ describe("@sentry/mcp-server flag parity", () => {
});
});

it("falls back to DEFAULT_SENTRY_DSN for telemetry", () => {
expect(parseMcpEnv({ DEFAULT_SENTRY_DSN: "default" }).sentryDsn).toBe(
"default",
);
// A server started from a project shell or IDE inherits the app's DSN.
it("ignores SENTRY_DSN and DEFAULT_SENTRY_DSN", () => {
expect(
parseMcpEnv({ SENTRY_DSN: "app", DEFAULT_SENTRY_DSN: "default" }),
).toEqual({});
});

it("leaves the deprecated SENTRY_ACCESS_TOKEN to serveMcp", () => {
Expand Down
2 changes: 0 additions & 2 deletions packages/cli/test/lib/mcp/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,6 @@ describe("finalizeMcpConfig", () => {
it("keeps the static token, constraints, and embedded agent settings", () => {
const cfg = finalize({
mcpUrl: "https://mcp.example.com",
sentryDsn: "dsn",
openaiModel: "gpt-test",
anthropicBaseUrl: "https://anthropic.example.com",
anthropicModel: "claude-test",
Expand All @@ -324,7 +323,6 @@ describe("finalizeMcpConfig", () => {
});
expect(cfg).toMatchObject({
mcpUrl: "https://mcp.example.com",
sentryDsn: "dsn",
openaiModel: "gpt-test",
anthropicBaseUrl: "https://anthropic.example.com/",
anthropicModel: "claude-test",
Expand Down
Loading
Loading