Skip to content

Commit d9ba305

Browse files
dcramercodex
andauthored
fix(mcp-core): Expose get_sentry_resource by default (#852)
Promote get_sentry_resource to the stable tool surface and hide the legacy issue and trace detail tools behind it. This fixes the confusion from GH-850 where get_sentry_resource and breadcrumbs were only reachable in experimental mode. The legacy detail handlers still exist so get_sentry_resource can compose them internally, but they are now marked internal-only and filtered out of server registration, generated definitions, plugin allowedTools, and use_sentry's embedded tool surface. I also updated public guidance and plugin prompts to point to get_sentry_resource instead of the legacy tool names, and regenerated the derived definitions so the stable and experimental plugin manifests stay in sync. Fixes GH-850 --------- Co-authored-by: Codex <codex@openai.com>
1 parent 1991b83 commit d9ba305

35 files changed

Lines changed: 145 additions & 159 deletions

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ claude plugin install sentry-mcp@sentry-mcp
2323

2424
This provides a `sentry-mcp` subagent that Claude automatically delegates to when you ask about Sentry errors, issues, traces, or performance.
2525

26-
For experimental features:
26+
For forward-looking tool variants and features:
2727

2828
```shell
2929
claude plugin install sentry-mcp@sentry-mcp-experimental

docs/claude-code-plugin.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ Two variants are published:
1010

1111
| Plugin | MCP URL | Purpose |
1212
|--------|---------|---------|
13-
| `sentry-mcp` | `https://mcp.sentry.dev/mcp` | Stable tools only |
14-
| `sentry-mcp-experimental` | `https://mcp.sentry.dev/mcp?experimental=1` | Includes experimental tools |
13+
| `sentry-mcp` | `https://mcp.sentry.dev/mcp` | Stable tool defaults |
14+
| `sentry-mcp-experimental` | `https://mcp.sentry.dev/mcp?experimental=1` | Forward-looking tool variants and experimental features |
1515

1616
## Directory Layout
1717

docs/error-handling.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ return agentTool({
187187
#### Example 2: Server Error
188188

189189
```
190-
1. User calls get_issue_details tool
190+
1. User calls get_sentry_resource for an issue
191191
2. Tool calls apiService.getIssue() directly (no withApiErrorHandling)
192192
3. API returns 502 Bad Gateway
193193
4. API client creates ApiServerError via createApiError factory and throws

packages/mcp-cloudflare/src/client/components/animation/TerminalAnimation.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ const steps: Step[] = [
5252
},
5353
{
5454
type: "[toolcall]",
55-
label: "get_issue_details()",
55+
label: "get_sentry_resource()",
5656
description: "MCP performs a toolcall to fetch issue details",
5757
startTime: 40,
5858
pauseMs: 1750,

packages/mcp-cloudflare/src/server/app.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ When scoped, tools automatically default to the constrained org/project and unne
3939
4040
### Query Parameters
4141
42-
- \`?experimental=1\` — Enable experimental, forward-looking features and tools
42+
- \`?experimental=1\` — Enable forward-looking tool variants and experimental features
4343
- \`?agent=1\` — Agent mode: exposes a single \`use_sentry\` tool that handles natural language requests via an embedded AI agent (roughly doubles response time)
4444
4545
Parameters can be combined: \`${baseUrl}/mcp/my-org/my-project?experimental=1\`

packages/mcp-cloudflare/src/server/oauth/callback.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,8 @@ describe("oauth callback routes", () => {
248248
const cookie = await approveClient(oauthApp, testEnv, client.clientId);
249249
const signedState = await createSignedCallbackState(client.clientId);
250250
const [signature, payload] = signedState.split(".");
251-
const tamperedState = `${signature === "" ? "a" : `${signature.slice(0, -1)}a`}.${payload}`;
251+
const tamperedDigit = signature.endsWith("a") ? "b" : "a";
252+
const tamperedState = `${signature.slice(0, -1)}${tamperedDigit}.${payload}`;
252253

253254
const response = await callCallback(oauthApp, testEnv, {
254255
state: tamperedState,

packages/mcp-cloudflare/src/server/routes/__tests__/mcp-discovery.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@ describe("/.mcp discovery routes", () => {
4444
// Verify tool structure
4545
const toolNames = json.map((t) => t.name);
4646
expect(toolNames).toContain("find_organizations");
47-
expect(toolNames).toContain("get_issue_details");
47+
expect(toolNames).toContain("get_sentry_resource");
48+
expect(toolNames).not.toContain("get_issue_details");
49+
expect(toolNames).not.toContain("get_trace_details");
4850
expect(toolNames).toContain("search_events");
4951
});
5052
});

packages/mcp-cloudflare/src/server/routes/chat.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -339,8 +339,8 @@ When testing Sentry MCP:
339339
Start conversations by exploring what's available in their account. Use tools like:
340340
- \`find_organizations\` to see what orgs they have access to
341341
- \`find_projects\` to list their projects
342-
- \`find_issues\` to show recent problems
343-
- \`get_issue_details\` to dive deep into specific errors
342+
- \`search_issues\` to show recent problems
343+
- \`get_sentry_resource\` to dive deep into a specific issue, event, or trace
344344
345345
Remember: You're a test assistant, not a general-purpose helper. Stay focused on testing the MCP integration with their real data.`,
346346
maxOutputTokens: 2000,

packages/mcp-core/scripts/generate-definitions.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ function byName<T extends { name: string }>(a: T, b: T) {
4949
return a.name.localeCompare(b.name);
5050
}
5151

52+
function isNonNull<T>(value: T | null): value is T {
53+
return value !== null;
54+
}
55+
5256
// Tools
5357
function generateToolDefinitions() {
5458
const toolsDefault = toolsModule.default as
@@ -66,7 +70,11 @@ function generateToolDefinitions() {
6670
description: string;
6771
inputSchema: Record<string, ZodTypeAny>;
6872
requiredScopes: string[]; // must exist on all tools (can be empty)
73+
internalOnly?: boolean;
6974
};
75+
if (t.internalOnly) {
76+
return null;
77+
}
7078
if (!Array.isArray(t.requiredScopes)) {
7179
throw new Error(`Tool '${t.name}' is missing requiredScopes array`);
7280
}
@@ -80,7 +88,7 @@ function generateToolDefinitions() {
8088
requiredScopes: t.requiredScopes,
8189
};
8290
});
83-
return defs.sort(byName);
91+
return defs.filter(isNonNull).sort(byName);
8492
}
8593

8694
// Skills
@@ -131,8 +139,13 @@ async function generateSkillDefinitions() {
131139
description: string;
132140
skills: string[];
133141
requiredScopes: string[];
142+
internalOnly?: boolean;
134143
};
135144

145+
if (t.internalOnly) {
146+
continue;
147+
}
148+
136149
// Check if this tool is enabled by this skill
137150
if (Array.isArray(t.skills) && t.skills.includes(skill.id)) {
138151
skillTools.push({
@@ -266,10 +279,10 @@ async function main() {
266279
Object.values(
267280
toolsModule.default as Record<
268281
string,
269-
{ name: string; agentOnly?: boolean }
282+
{ name: string; agentOnly?: boolean; internalOnly?: boolean }
270283
>,
271284
)
272-
.filter((t) => t.agentOnly)
285+
.filter((t) => t.agentOnly || t.internalOnly)
273286
.map((t) => t.name),
274287
);
275288
const toolNames = tools

packages/mcp-core/src/server.test.ts

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,22 @@ describe("buildServer", () => {
132132
expect(toolNames).toContain("tool_with_false");
133133
expect(toolNames).toContain("tool_without_flag");
134134
});
135+
136+
it("filters internal-only tools regardless of mode", () => {
137+
const server = buildServer({
138+
context: baseContext,
139+
tools: {
140+
public_tool: createMockTool("public_tool"),
141+
internal_tool: createMockTool("internal_tool", {
142+
internalOnly: true,
143+
}),
144+
},
145+
});
146+
147+
const toolNames = getRegisteredToolNames(server);
148+
expect(toolNames).toContain("public_tool");
149+
expect(toolNames).not.toContain("internal_tool");
150+
});
135151
});
136152

137153
describe("capability-based tool filtering (experimental)", () => {
@@ -427,7 +443,10 @@ describe("buildServer", () => {
427443
const toolNames = getRegisteredToolNames(server);
428444
// Should have standard tools like whoami
429445
expect(toolNames).toContain("whoami");
430-
// Currently no tools are marked as experimental, so all should be present
446+
expect(toolNames).toContain("get_sentry_resource");
447+
expect(toolNames).not.toContain("get_issue_details");
448+
expect(toolNames).not.toContain("get_trace_details");
449+
// Currently no default tools are gated behind experimental visibility
431450
expect(toolNames.length).toBeGreaterThan(0);
432451
});
433452

@@ -441,8 +460,11 @@ describe("buildServer", () => {
441460
});
442461

443462
const toolNames = getRegisteredToolNames(server);
444-
// Should still have tools (none are currently experimental)
463+
// Should still have tools, including get_sentry_resource in stable mode
445464
expect(toolNames).toContain("whoami");
465+
expect(toolNames).toContain("get_sentry_resource");
466+
expect(toolNames).not.toContain("get_issue_details");
467+
expect(toolNames).not.toContain("get_trace_details");
446468
});
447469

448470
it("includes all default tools when experimentalMode is true", () => {
@@ -454,6 +476,24 @@ describe("buildServer", () => {
454476
const toolNames = getRegisteredToolNames(server);
455477
// Should have the standard tools
456478
expect(toolNames).toContain("whoami");
479+
expect(toolNames).toContain("get_sentry_resource");
480+
expect(toolNames).not.toContain("get_issue_details");
481+
expect(toolNames).not.toContain("get_trace_details");
482+
});
483+
484+
it("keeps get_sentry_resource available for legacy triage and seer skills", () => {
485+
for (const grantedSkills of [["triage"], ["seer"]] as const) {
486+
const server = buildServer({
487+
context: {
488+
...baseContext,
489+
grantedSkills: new Set(grantedSkills),
490+
},
491+
});
492+
493+
const toolNames = getRegisteredToolNames(server);
494+
expect(toolNames).toContain("get_sentry_resource");
495+
expect(toolNames).not.toContain("get_issue_details");
496+
}
457497
});
458498
});
459499
});

0 commit comments

Comments
 (0)