Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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