Thirty new tools (54 in all) for what the API gained since the MCP: create a project and read its public showcase; sprints (list, progress and burndown, plan, change, start, close with carry-over, delete, move a ticket in or out); epics and the backlog (list epics with progress, set a ticket's parent, rank); bulk edit of up to 100 tickets by key, saying which refused; the trash; checklists; following a ticket, removing a relation, editing and deleting comments; editing and archiving releases, drafting and saving their notes; notifications. Searching filters by epic, sprint and dates and sorts by backlog order; get_issue shows parent, checklist, commits and followers; create and update take parent and start date. API: `unreadOnly` and `dryRun` were documented as objects in the OpenAPI; they are booleans now. Chapter 9 lists every tool; Word and PDF regenerated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
71 lines
1.9 KiB
TypeScript
71 lines
1.9 KiB
TypeScript
import { McpContext, Resolver, Tool } from '@nestjs-mcp/server';
|
|
import { z } from 'zod';
|
|
import { TaskStreamApi } from '../api/taskstream-api';
|
|
import { limit } from './params';
|
|
import { toolResult } from './tool-result';
|
|
|
|
@Resolver()
|
|
export class NotificationsResolver {
|
|
constructor(private readonly api: TaskStreamApi) {}
|
|
|
|
@Tool({
|
|
name: 'my_notifications',
|
|
description:
|
|
"The user's notifications, newest first: assignments, status changes, failed QA, comments, mentions, followed tickets, reminders.",
|
|
paramsSchema: z.object({
|
|
unreadOnly: z.boolean().default(true),
|
|
limit: limit(100, 30),
|
|
}),
|
|
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
})
|
|
mine(
|
|
{ unreadOnly, limit }: { unreadOnly: boolean; limit: number },
|
|
ctx: McpContext,
|
|
) {
|
|
return toolResult(async () => {
|
|
const page = await this.api.data(
|
|
this.api.as(ctx).GET('/api/me/notifications', {
|
|
params: { query: { unreadOnly, limit } },
|
|
}),
|
|
);
|
|
return {
|
|
unread: page.unread,
|
|
items: page.items.map((n) => ({
|
|
id: n.id,
|
|
kind: n.kind,
|
|
project: n.projectKey,
|
|
issue: n.issueKey,
|
|
who: n.actor?.name ?? null,
|
|
summary: n.summary,
|
|
read: n.readAt !== null,
|
|
at: n.createdAt,
|
|
})),
|
|
};
|
|
});
|
|
}
|
|
|
|
@Tool({
|
|
name: 'mark_notifications_read',
|
|
description: 'Mark notifications as read: these ids, or all of them.',
|
|
paramsSchema: z.object({
|
|
ids: z
|
|
.array(z.string())
|
|
.optional()
|
|
.describe('Notification ids; absent: all'),
|
|
}),
|
|
annotations: {
|
|
readOnlyHint: false,
|
|
destructiveHint: false,
|
|
idempotentHint: true,
|
|
openWorldHint: false,
|
|
},
|
|
})
|
|
markRead({ ids }: { ids?: string[] }, ctx: McpContext) {
|
|
return toolResult(() =>
|
|
this.api.data(
|
|
this.api.as(ctx).POST('/api/me/notifications/read', { body: { ids } }),
|
|
),
|
|
);
|
|
}
|
|
}
|