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: 2 additions & 0 deletions packages/mcp-core/scripts/generate-definitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ type DefinitionTool = {
inputSchema: Record<string, ZodTypeAny>;
outputSchema?: ZodTypeAny;
skills: string[];
includeInSkillDefinitions?: boolean;
requiredScopes: string[];
experimental?: boolean;
hideInExperimentalMode?: boolean;
Expand Down Expand Up @@ -253,6 +254,7 @@ async function generateSkillDefinitions() {
const t = tool as DefinitionTool;
return (
isSkillDefinitionTool(t) &&
t.includeInSkillDefinitions !== false &&
Array.isArray(t.skills) &&
t.skills.includes(skill.id)
);
Expand Down
28 changes: 26 additions & 2 deletions packages/mcp-core/src/api-client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { USER_AGENT } from "../version";
import { apiPath } from "./api-path";
import { ApiNotFoundError, ApiValidationError, createApiError } from "./errors";
import {
AgenticOnboardingRunSchema,
AIConversationDetailsResponseSchema,
AIConversationSummaryListSchema,
ApiErrorSchema,
Expand Down Expand Up @@ -92,6 +93,8 @@ import {
UserSchema,
} from "./schema";
import type {
AgenticOnboardingRun,
AgenticOnboardingStatusUpdate,
AIConversationDetails,
AIConversationSpanList,
AIConversationSummary,
Expand Down Expand Up @@ -1858,6 +1861,28 @@ export class SentryApiService {
* @param opts Request options
* @returns Updated project data
*/
async updateAgenticOnboardingStatus(
{
organizationSlug,
update,
}: {
organizationSlug: string;
update: AgenticOnboardingStatusUpdate;
},
opts?: RequestOptions,
): Promise<AgenticOnboardingRun> {
const body = await this.requestJSON(
apiPath`/organizations/${organizationSlug}/onboarding/agent/status/`,
{
method: "POST",
body: JSON.stringify(update),
},
opts,
);

return AgenticOnboardingRunSchema.parse(body);
}

async updateProject(
{
organizationSlug,
Expand Down Expand Up @@ -2904,8 +2929,7 @@ export class SentryApiService {
}
this.applyTimeParams(searchQuery, effectiveStatsPeriod, start, end);

const path =
apiPath`/projects/${organizationSlug}/${projectSlug}/uptime/${uptimeMonitorId}/checks/`;
const path = apiPath`/projects/${organizationSlug}/${projectSlug}/uptime/${uptimeMonitorId}/checks/`;
const body = await this.requestJSON(
searchQuery.toString() ? `${path}?${searchQuery.toString()}` : path,
undefined,
Expand Down
74 changes: 74 additions & 0 deletions packages/mcp-core/src/api-client/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2076,3 +2076,77 @@ export const TransactionProfileSchema = z
.optional(),
})
.passthrough();

export const AgenticOnboardingStageSchema = z.enum([
"connect_mcp",
"analyze_project",
"create_project",
"instrument_app",
"plan_test_error",
"send_verification_error",
"receive_verification_error",
"prepare_production",
"check_stack_trace_quality",
]);

export const AgenticOnboardingStageStatusSchema = z.enum([
"active",
"waiting",
"completed",
"skipped",
"failed",
"bypassed",
]);

export const AgenticOnboardingStageStatusUpdateSchema = z.enum([
"active",
"waiting",
"completed",
"skipped",
"failed",
]);

export const AgenticOnboardingStageStateSchema = z.object({
stage: AgenticOnboardingStageSchema,
status: AgenticOnboardingStageStatusSchema.nullable(),
eventNote: z.string().nullable(),
});

export const AgenticOnboardingRunStatusSchema = z.enum([
"active",
"completed",
"failed",
"cancelled",
]);

export const AgenticOnboardingRunStatusUpdateSchema = z.enum([
"completed",
"failed",
]);

export const AgenticOnboardingStatusUpdateSchema = z.object({
schemaVersion: z.literal(1),
runToken: z.string().regex(/^[A-Za-z0-9]{10}$/),
stage: AgenticOnboardingStageSchema,
status: AgenticOnboardingStageStatusUpdateSchema,
runStatus: AgenticOnboardingRunStatusUpdateSchema.optional(),
eventNote: z.string().trim().min(1).max(256).optional(),
projectSlugs: z.array(z.string().trim().min(1)).min(1).max(100).optional(),
issueIds: z.array(z.string().trim().min(1)).min(1).max(100).optional(),
});

export const AgenticOnboardingRunSchema = z.object({
schemaVersion: z.literal(1),
runId: z.string(),
channelId: z.string(),
clientRunId: z.string(),
createdAt: z.string(),
updatedAt: z.string(),
sequence: z.number().int().nonnegative(),
expiresAt: z.string(),
continueUpdates: z.boolean(),
runStatus: AgenticOnboardingRunStatusSchema,
projectSlugs: z.array(z.string()),
issueIds: z.array(z.string()),
stages: z.array(AgenticOnboardingStageStateSchema),
});
6 changes: 6 additions & 0 deletions packages/mcp-core/src/api-client/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
*/
import type { z } from "zod";
import type {
AgenticOnboardingRunSchema,
AgenticOnboardingStatusUpdateSchema,
AssignedToSchema,
AIConversationSummaryListSchema,
AIConversationSummarySchema,
Expand Down Expand Up @@ -125,6 +127,10 @@ import type {
UserReportListSchema,
} from "./schema";

export type AgenticOnboardingRun = z.infer<typeof AgenticOnboardingRunSchema>;
export type AgenticOnboardingStatusUpdate = z.infer<
typeof AgenticOnboardingStatusUpdateSchema
>;
export type User = z.infer<typeof UserSchema>;
export type Organization = z.infer<typeof OrganizationSchema>;
export type Team = z.infer<typeof TeamSchema>;
Expand Down
8 changes: 8 additions & 0 deletions packages/mcp-core/src/skills.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ function getGeneratedSkillToolDescription(skillId: string, toolName: string) {
}

describe("skills module", () => {
it("does not advertise workflow-only tools in generated skill prompts", () => {
for (const skill of skillDefinitions) {
expect(skill.tools?.map((tool) => tool.name) ?? []).not.toContain(
"onboarding_status_update",
);
}
});

describe("SKILLS registry", () => {
it("has all expected skills", () => {
expect(SKILLS.inspect).toBeDefined();
Expand Down
3 changes: 3 additions & 0 deletions packages/mcp-core/src/skills.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ export async function getSkillsArrayWithCounts(): Promise<SkillDefinition[]> {
if (isWrapperTool(toolName) || isCatalogInfrastructureTool(toolName)) {
continue;
}
if (tool.includeInSkillDefinitions === false) {
continue;
}
if (Array.isArray(tool.skills)) {
for (const skill of tool.skills) {
counts.set(skill as Skill, (counts.get(skill as Skill) || 0) + 1);
Expand Down
85 changes: 85 additions & 0 deletions packages/mcp-core/src/toolDefinitions.json
Original file line number Diff line number Diff line change
Expand Up @@ -3779,6 +3779,91 @@
"skills": ["inspect"],
"surface": "catalog"
},
{
"name": "onboarding_status_update",
"description": "Update the progress shown in Sentry's agentic onboarding UI.\n\nUse this tool only when the Sentry getting started skill provides a run token. Call it at the workflow boundaries described by that skill.\n\n<hints>\n- Progress updates are operational UI state, not user analytics.\n- Keep eventNote brief and limited to context useful in the progress UI.\n- Do not include source code, repository paths, credentials, error output, or other sensitive data.\n- status is required for every update.\n- Set runStatus to completed or failed only when the entire onboarding run reaches that state.\n- A failed status requires a brief eventNote explaining the failure.\n</hints>",
"inputSchema": {
"type": "object",
"properties": {
"organizationSlug": {
"type": "string",
"description": "The organization's slug. You can find a existing list of organizations you have access to using the `find_organizations()` tool."
},
"regionUrl": {
"default": null,
"anyOf": [
{
"type": "string",
"description": "The region URL for the organization you're querying, if known. For Sentry's Cloud Service (sentry.io), this is typically the region-specific URL like 'https://us.sentry.io'. For self-hosted Sentry installations, this parameter is usually not needed and should be omitted. You can find the correct regionUrl from the organization details using the `find_organizations()` tool."
},
{
"type": "null"
}
]
},
"runToken": {
"type": "string",
"pattern": "^[A-Za-z0-9]{10}$",
"description": "The 10-character onboarding run token supplied by Sentry."
},
"stage": {
"type": "string",
"enum": [
"connect_mcp",
"analyze_project",
"create_project",
"instrument_app",
"plan_test_error",
"send_verification_error",
"receive_verification_error",
"prepare_production",
"check_stack_trace_quality"
],
"description": "The onboarding stage being updated."
},
"status": {
"type": "string",
"enum": ["active", "waiting", "completed", "skipped", "failed"],
"description": "The stage's current status."
},
"runStatus": {
"description": "Set only when the entire onboarding run has completed or failed.",
"type": "string",
"enum": ["completed", "failed"]
},
"eventNote": {
"description": "Short context to display with this progress event. Required when status is failed.",
"type": "string",
"minLength": 1,
"maxLength": 256
},
"projectSlugs": {
"description": "Validated Sentry project slugs. Send only for create_project. Values accumulate across updates; include each project as it becomes usable while active, then all known projects when completed.",
"minItems": 1,
"maxItems": 100,
"type": "array",
"items": {
"type": "string",
"minLength": 1
}
},
"issueIds": {
"description": "Validated Sentry issue IDs returned by MCP. Send only for receive_verification_error. Values accumulate across updates; include each matching issue as it is confirmed, then all known issues when completed.",
"minItems": 1,
"maxItems": 100,
"type": "array",
"items": {
"type": "string",
"minLength": 1
}
}
},
"required": ["organizationSlug", "runToken", "stage", "status"]
},
"requiredScopes": ["org:read"],
"skills": ["inspect", "seer", "docs", "triage", "project-management"],
"surface": "catalog"
},
{
"name": "remove_team_from_project",
"description": "Revoke a team's access to an existing Sentry project.\n\nUse this tool when you need to:\n- Remove a team from a project\n- Revoke team access without changing project metadata\n- Check project team assignments before removing access\n\nBe careful when using this tool because it revokes project access.\n\n<examples>\nremove_team_from_project(organizationSlug='my-organization', projectSlug='my-project', teamSlug='my-team')\n</examples>\n\n<hints>\n- The team must already be assigned to the project.\n- This tool will not remove the last team assigned to a project.\n</hints>",
Expand Down
2 changes: 2 additions & 0 deletions packages/mcp-core/src/tools/catalog/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import getLatestBaseSnapshot from "./get-latest-base-snapshot";
import getAIConversationDetails from "./get-ai-conversation-details";
import searchAIConversations from "./search-ai-conversations";
import addIssueNote from "./add-issue-note";
import onboardingStatusUpdate from "./onboarding-status-update";
import type { ToolConfig } from "../types";

/**
Expand Down Expand Up @@ -112,6 +113,7 @@ const catalogTools = {
get_ai_conversation_details: getAIConversationDetails,
search_ai_conversations: searchAIConversations,
Comment thread
sentry-warden[bot] marked this conversation as resolved.
add_issue_note: addIssueNote,
onboarding_status_update: onboardingStatusUpdate,
} as const satisfies Record<string, ToolConfig<any>>;

export default catalogTools;
Expand Down
Loading
Loading