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 README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ claude plugin install sentry-mcp@sentry-mcp-experimental

While this repository is focused on acting as an MCP service, we also support a `stdio` transport. This is still a work in progress, but is the easiest way to adapt run the MCP against a self-hosted Sentry install.

**Note:** The AI-powered search tools (`search_events`, `search_issues`, etc.) require an LLM provider (OpenAI, Azure OpenAI, Anthropic, or OpenRouter). These tools use natural language processing to translate queries into Sentry's query syntax. Without a configured provider, these specific tools will be unavailable, but all other tools will function normally.
**Note:** The AI-powered search tools (`search_errors`, `search_traces`, `search_logs`, `search_issues`, etc.) require an LLM provider (OpenAI, Azure OpenAI, Anthropic, or OpenRouter). These tools use natural language processing to translate queries into Sentry's query syntax. Without a configured provider, these specific tools will be unavailable, but all other tools will function normally.

To utilize the `stdio` transport, you'll need to create an User Auth Token in Sentry with the necessary scopes. As of writing this is:

Expand Down
4 changes: 2 additions & 2 deletions docs/architecture/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ Execute actions and retrieve data:

## Two-Tier Agent Architecture

Some tools (`search_events` and `search_issues`) implement a two-tier agent pattern:
Some tools (the dataset search tools like `search_errors`/`search_traces`, and `search_issues`) implement a two-tier agent pattern:

### Tier 1: Calling Agent (Claude/Cursor)
- Decides when to use search tools
Expand All @@ -304,7 +304,7 @@ Some tools (`search_events` and `search_issues`) implement a two-tier agent patt
```
1. User: "Show me errors from yesterday"
↓
2. Claude: Calls search_events(query="errors from yesterday")
2. Claude: Calls search_errors(query="errors from yesterday")
↓
3. MCP Tool Handler: Receives request
↓
Expand Down
2 changes: 1 addition & 1 deletion docs/contributing/adding-tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ pnpm eval your-tool

## Agent-in-Tool Pattern

Some tools (`search_events`, `search_issue_events`, and `search_issues`) embed
Some tools (the dataset search tools such as `search_errors`, plus `search_issue_events` and `search_issues`) embed
AI agents to normalize search parameters before the handler calls Sentry. Treat
the agent as a repair step for a structured request, not only as a natural
language query translator. The agent may rewrite the query string, but it may
Expand Down
2 changes: 1 addition & 1 deletion docs/operations/embedded-agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Configuration guide for embedded AI agents used by AI-powered search tools in Se
## Overview

Sentry MCP uses embedded AI agents for the following tools:
- `search_events` - Natural language search across events, metrics, and session replays
- `search_errors`, `search_logs`, `search_traces`, `search_metrics`, `search_profiles`, `search_replays` - Natural language search over one dataset each (share one handler; `search_events` remains as a deprecated catalog alias)
- `search_issues` - Natural language search across issues
- `search_issue_events` - Search events within a specific issue

Expand Down
75 changes: 48 additions & 27 deletions docs/specs/search-events.md
Original file line number Diff line number Diff line change
@@ -1,58 +1,79 @@
# search_events Tool Specification
# Dataset Search Tools Specification

## Overview

A unified search tool that accepts natural language queries and translates them to Sentry's discover endpoint parameters using the configured embedded LLM provider. Replaces `find_errors` and `find_transactions` with a single, more flexible interface.
Natural-language event search is exposed as one tool per dataset:

| Tool | Dataset | Seer strategy |
| --- | --- | --- |
| `search_errors` | `errors` | `Errors` |
| `search_logs` | `logs` | `Logs` |
| `search_traces` | `spans` | `Traces` |
| `search_metrics` | `metrics` | `Metrics` |
| `search_profiles` | `profiles` | — |
| `search_replays` | `replays` | — |

All six share one handler (`tools/support/search-events/search.ts`). Each tool
fixes its dataset, so the caller never chooses a `dataset` parameter and the
embedded agent is told it cannot switch datasets (`lockDataset`). This removes
the most common routing mistake from the old multi-dataset `search_events` tool,
where clients omitted `dataset`, silently got `errors`, and skipped Seer.

`search_events` stays in the catalog as a deprecated alias for backward
compatibility (reachable via `execute_sentry_tool`). It is no longer on the
direct MCP surface and is excluded from skill definitions.

## Motivation

- **Before**: Two separate tools with rigid parameters, users must know Sentry query syntax
- **After**: Single tool with natural language input, AI handles translation to Sentry syntax
- **Benefits**: Better UX, reduced tool count (20 → 19), accessible to non-technical users
- **Before**: One `search_events` tool with an optional `dataset` enum; agents
often omitted or mis-picked it.
- **After**: Tool selection picks the dataset. Each description only documents
its own dataset, so descriptions are shorter and more specific.
- **Cross-event**: same-trace co-occurrence questions ("slow checkout requests
that also logged an error") route to `search_traces`, the only Seer strategy
that supports cross-event filters.

## Interface

```typescript
interface SearchEventsParams {
organizationSlug: string; // Required
query: string; // Natural language search description
dataset?: "spans" | "errors" | "logs" | "metrics"; // Dataset to search (default: "errors")
projectSlug?: string; // Optional - limit to specific project
regionUrl?: string;
// search_errors / search_logs / search_traces / search_metrics / search_profiles
interface DatasetSearchParams {
organizationSlug: string;
query?: string; // Natural language (preferred) or Sentry search syntax
projectSlug?: string;
fields?: string[];
sort?: string;
period?: string; // e.g. "24h", "7d"
limit?: number; // Default: 10, Max: 100
includeExplanation?: boolean; // Include translation explanation
includeExplanation?: boolean;
regionUrl?: string;
}

// search_replays drops `fields` and adds a separate `environment` parameter.
```

### Examples

```typescript
// Find errors (errors dataset is default)
search_events({
search_errors({
organizationSlug: "my-org",
query: "database timeouts in checkout flow from last hour"
})

// Find slow transactions
search_events({
search_traces({
organizationSlug: "my-org",
query: "API calls taking over 5 seconds",
projectSlug: "backend",
dataset: "spans"
projectSlug: "backend"
})

// Find logs
search_events({
search_logs({
organizationSlug: "my-org",
query: "warning logs about memory usage",
dataset: "logs"
query: "warning logs about memory usage"
})

// Find request duration metrics
search_events({
search_metrics({
organizationSlug: "my-org",
query: "p95 request duration by transaction this week",
dataset: "metrics"
query: "p95 request duration by transaction this week"
})
```

Expand Down Expand Up @@ -150,7 +171,7 @@ find_errors({
})

// After
search_events({
search_errors({
organizationSlug: "sentry",
query: "unresolved errors in checkout.js"
})
Expand Down
2 changes: 1 addition & 1 deletion docs/testing/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ pnpm -w run cli --access-token=TOKEN "query"
- Testing OAuth flows
- Debugging tool interactions
- Validating real API responses
- Testing AI-powered tools (search_events, search_issues, search_issue_events)
- Testing AI-powered tools (search_errors/search_traces/search_logs etc., search_issues, search_issue_events)

**Note:** The CLI defaults to `http://localhost:5173` for easier local development. Override with `--mcp-host` or set `MCP_URL` environment variable to test against different servers.

Expand Down
10 changes: 5 additions & 5 deletions docs/testing/stdio.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,14 +198,14 @@ This opens the MCP Inspector at `http://localhost:6274`
1. **List Tools** - Verify expected tools appear
2. **Call a tool** - Start with `execute_sentry_tool` using `name="whoami"` and `arguments={}`
3. **Test with parameters** - Try `find_organizations()`
4. **Test complex operations** - Try `search_events(query="errors in the last hour")`
4. **Test complex operations** - Try `search_errors(query="errors in the last hour")`

**Example test sequence:**
```
1. execute_sentry_tool(name="whoami", arguments={})
2. find_organizations()
3. find_projects(organizationSlug="your-org")
4. search_events(
4. search_errors(
organizationSlug="your-org",
query="errors from yesterday"
)
Expand Down Expand Up @@ -425,7 +425,7 @@ SENTRY_HOST=sentry.example.com
MCP_SKILLS=inspect,docs,triage # Limit to specific skills

# AI features
OPENAI_API_KEY=your-key # For AI-powered search tools like search_events/search_issues
OPENAI_API_KEY=your-key # For AI-powered search tools like search_errors/search_traces/search_issues

# Sentry reporting
SENTRY_DSN=your-dsn
Expand Down Expand Up @@ -481,9 +481,9 @@ pnpm start --access-token=TOKEN --skills=inspect,seer,docs
# With OpenAI API key
OPENAI_API_KEY=your-key pnpm start --access-token=TOKEN

# Test search_events and search_issues work
# Test the dataset search tools and search_issues work
# In MCP Inspector:
# - Call search_events(query="errors in production")
# - Call search_errors(query="errors in production")
# - Call search_issues(query="unresolved crashes")
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ export default function StdioSetup() {
</p>
<p>
<strong>AI-powered search:</strong> If you want the
<code>search_events</code> and <code>search_issues</code> tools to
event search tools (<code>search_errors</code>,{" "}
<code>search_traces</code>, <code>search_logs</code>, and so on) and{" "}
<code>search_issues</code> to
translate natural language queries, add an
<code>OPENAI_API_KEY</code> next to your Sentry token. The rest of the
MCP server works without it, so you can skip this step if you do not
Expand Down Expand Up @@ -113,7 +115,8 @@ export default function StdioSetup() {
</dt>
<dd className="text-slate-300">
Optional for the standard tools, but required for the AI-powered
search tools (<code>search_events</code> /{" "}
search tools (<code>search_errors</code>,{" "}
<code>search_traces</code>, <code>search_logs</code>, etc. and{" "}
<code>search_issues</code>). When unset, those tools stay hidden
but everything else works as usual.
</dd>
Expand Down
8 changes: 4 additions & 4 deletions packages/mcp-cloudflare/src/server/lib/mcp-handler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ describe("MCP Handler", () => {
}>(response);
const toolNames = body.result?.tools.map((tool) => tool.name) ?? [];

expect(toolNames).toContain("search_events");
expect(toolNames).toContain("search_traces");
expect(toolNames).not.toContain("search_docs");
});

Expand Down Expand Up @@ -343,7 +343,7 @@ describe("MCP Handler", () => {
}>(response);
const toolNames = body.result?.tools.map((tool) => tool.name) ?? [];

expect(toolNames).toContain("search_events");
expect(toolNames).toContain("search_traces");
expect(toolNames).toContain("update_issue");
});

Expand Down Expand Up @@ -480,7 +480,7 @@ describe("MCP Handler", () => {
}>(response);
const toolNames = body.result?.tools.map((tool) => tool.name) ?? [];

expect(toolNames).toContain("search_events");
expect(toolNames).toContain("search_traces");
expect(toolNames).not.toContain("update_issue");
});

Expand Down Expand Up @@ -508,7 +508,7 @@ describe("MCP Handler", () => {
}>(response);
const toolNames = body.result?.tools.map((tool) => tool.name) ?? [];

expect(toolNames).toContain("search_events");
expect(toolNames).toContain("search_traces");
expect(toolNames).not.toContain("update_issue");
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,12 @@ describe("/.mcp discovery routes", () => {
surface: "direct",
}),
);
expect(toolsByName.get("search_events")).toEqual(
expect(toolsByName.get("search_traces")).toEqual(
expect.objectContaining({
surface: "direct",
}),
);
expect(toolsByName.get("search_events")?.surface).not.toBe("direct");
expect(toolsByName.get("get_issue_details")).toEqual(
expect.objectContaining({
inputSchema: expect.any(Object),
Expand Down
2 changes: 1 addition & 1 deletion packages/mcp-core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ This package is primarily for running the `stdio` MCP server. If you do not know
<https://mcp.sentry.dev>

**Note:** Some tools require additional configuration:
- **AI-powered search tools** (`search_events` and `search_issues`): These tools use a configured LLM provider to translate natural language queries into Sentry's query syntax. Set one provider key, such as `OPENAI_API_KEY` or `OPENROUTER_API_KEY`. Without a provider key, these specific tools will be unavailable, but all other tools will function normally.
- **AI-powered search tools** (the dataset search tools such as `search_errors`/`search_traces`/`search_logs`, and `search_issues`): These tools use a configured LLM provider to translate natural language queries into Sentry's query syntax. Set one provider key, such as `OPENAI_API_KEY` or `OPENROUTER_API_KEY`. Without a provider key, these specific tools will be unavailable, but all other tools will function normally.

## Authorization

Expand Down
6 changes: 2 additions & 4 deletions packages/mcp-core/src/internal/formatting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2326,10 +2326,9 @@ export function formatIssueOutput({
"Full distributed trace lookup is not available in this session",
});
const spanSearchInstruction = formatToolCallInstruction({
toolName: "search_events",
toolName: "search_traces",
arguments: {
organizationSlug,
dataset: "spans",
query: `trace:${traceId}`,
},
experimentalMode: experimentalMode ?? false,
Expand All @@ -2339,10 +2338,9 @@ export function formatIssueOutput({
"Related span search is not available in this session",
});
const logSearchInstruction = formatToolCallInstruction({
toolName: "search_events",
toolName: "search_logs",
arguments: {
organizationSlug,
dataset: "logs",
query: `trace:${traceId}`,
},
experimentalMode: experimentalMode ?? false,
Expand Down
2 changes: 1 addition & 1 deletion packages/mcp-core/src/internal/tool-helpers/seer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ describe("seer-utils", () => {

expect(message).toContain("Seer Analysis Not Available");
expect(message).toContain("MCP-SERVER-EQE");
expect(message).toContain("search_events");
expect(message).toContain("search_metrics");
expect(message).not.toContain("Starting new analysis");
});
});
Expand Down
2 changes: 1 addition & 1 deletion packages/mcp-core/src/internal/tool-helpers/seer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export function getSeerUnsupportedIssueMessage(
"**Suggested alternatives:**",
"- Use `get_issue_details` or `get_sentry_resource` to inspect the metric alert rule and threshold details",
"- Use `search_issues` to find related error issues that may explain the metric spike",
"- Use `search_events` to query the underlying metric data",
"- Use `search_metrics` (or `search_traces` for span-based alerts) to query the underlying data",
].join("\n");
}

Expand Down
7 changes: 6 additions & 1 deletion packages/mcp-core/src/server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,14 @@ const DEFAULT_DIRECT_TOOL_NAMES = [
"find_organizations",
"find_projects",
"get_sentry_resource",
"search_events",
"search_errors",
"search_issues",
"search_logs",
"search_metrics",
"search_profiles",
"search_replays",
"search_sentry_tools",
"search_traces",
"update_issue",
].sort();

Expand Down
Loading
Loading