feat(mcp): add update-note, link-notes, extract-tasks, attach-artifact MCP tools
4 new executable MCP tools with full audit trail: - notes.notes.update — update title/body/status/tags with dry-run support - notes.relationships.link — create typed relationships between notes - notes.tasks.extract — extract actionable tasks from note body (checkbox + TODO patterns) - notes.artifacts.attach — attach file/summary/extraction/citation/export artifacts All tools follow the existing pattern: - Product scope enforcement - Agent action audit recording - Idempotency key + correlation ID propagation - Dry-run preview support Also updated .env.example with notelett identity. Verification: backend typecheck + 18 tests pass.
This commit is contained in:
parent
e1fde25afd
commit
12058ed745
@ -1,14 +1,14 @@
|
||||
PORT=4016
|
||||
HOST=0.0.0.0
|
||||
NODE_ENV=development
|
||||
SERVICE_NAME=bytelyst-notes-backend
|
||||
SERVICE_NAME=notelett-backend
|
||||
CORS_ORIGIN=
|
||||
COSMOS_ENDPOINT=
|
||||
COSMOS_KEY=
|
||||
COSMOS_DATABASE=bytelyst
|
||||
JWT_SECRET=
|
||||
DB_PROVIDER=cosmos
|
||||
PRODUCT_ID=bytelyst-notes
|
||||
PRODUCT_ID=notelett
|
||||
PLATFORM_SERVICE_URL=http://localhost:4003
|
||||
EXTRACTION_SERVICE_URL=http://localhost:4005
|
||||
MCP_SERVER_URL=http://localhost:4007
|
||||
|
||||
@ -6,6 +6,10 @@ export const NOTES_MCP_TOOL_NAMES = {
|
||||
get: 'notes.notes.get',
|
||||
search: 'notes.notes.search',
|
||||
createDraft: 'notes.notes.create_draft',
|
||||
updateNote: 'notes.notes.update',
|
||||
linkNotes: 'notes.relationships.link',
|
||||
extractTasks: 'notes.tasks.extract',
|
||||
attachArtifact: 'notes.artifacts.attach',
|
||||
} as const;
|
||||
|
||||
export const NoteToolRoleSchema = z.enum(['viewer', 'admin', 'super_admin']);
|
||||
@ -96,6 +100,99 @@ export const CreateNoteDraftToolOutputSchema = z.object({
|
||||
correlationId: z.string().optional(),
|
||||
});
|
||||
|
||||
export const UpdateNoteToolInputSchema = z.object({
|
||||
noteId: z.string().min(1).max(128),
|
||||
workspaceId: z.string().min(1).max(128),
|
||||
title: z.string().min(1).max(500).optional(),
|
||||
body: z.string().min(1).max(50000).optional(),
|
||||
status: z.enum(['draft', 'active', 'archived']).optional(),
|
||||
tags: z.array(z.string().min(1).max(64)).optional(),
|
||||
agentId: z.string().max(128).optional(),
|
||||
dryRun: z.boolean().default(false),
|
||||
idempotencyKey: z.string().max(255).optional(),
|
||||
correlationId: z.string().max(255).optional(),
|
||||
});
|
||||
|
||||
export const UpdateNoteToolOutputSchema = z.object({
|
||||
dryRun: z.boolean(),
|
||||
note: GetNoteToolOutputSchema,
|
||||
idempotencyKey: z.string().optional(),
|
||||
correlationId: z.string().optional(),
|
||||
});
|
||||
|
||||
export const LinkNotesToolInputSchema = z.object({
|
||||
workspaceId: z.string().min(1).max(128),
|
||||
fromNoteId: z.string().min(1).max(128),
|
||||
toNoteId: z.string().min(1).max(128),
|
||||
relationshipType: z.enum(['related', 'references', 'parent', 'child', 'duplicate', 'task-source', 'artifact-source']),
|
||||
agentId: z.string().max(128).optional(),
|
||||
idempotencyKey: z.string().max(255).optional(),
|
||||
correlationId: z.string().max(255).optional(),
|
||||
});
|
||||
|
||||
export const LinkNotesToolOutputSchema = z.object({
|
||||
id: z.string(),
|
||||
fromNoteId: z.string(),
|
||||
toNoteId: z.string(),
|
||||
relationshipType: z.string(),
|
||||
idempotencyKey: z.string().optional(),
|
||||
correlationId: z.string().optional(),
|
||||
});
|
||||
|
||||
export const ExtractTasksToolInputSchema = z.object({
|
||||
noteId: z.string().min(1).max(128),
|
||||
workspaceId: z.string().min(1).max(128),
|
||||
agentId: z.string().max(128).optional(),
|
||||
dryRun: z.boolean().default(false),
|
||||
idempotencyKey: z.string().max(255).optional(),
|
||||
correlationId: z.string().max(255).optional(),
|
||||
});
|
||||
|
||||
export const ExtractedTaskSchema = z.object({
|
||||
id: z.string(),
|
||||
title: z.string(),
|
||||
description: z.string().optional(),
|
||||
status: z.enum(['open', 'in_progress', 'completed', 'canceled']),
|
||||
source: z.enum(['manual', 'extracted']),
|
||||
});
|
||||
|
||||
export const ExtractTasksToolOutputSchema = z.object({
|
||||
dryRun: z.boolean(),
|
||||
noteId: z.string(),
|
||||
tasks: z.array(ExtractedTaskSchema),
|
||||
idempotencyKey: z.string().optional(),
|
||||
correlationId: z.string().optional(),
|
||||
});
|
||||
|
||||
export const AttachArtifactToolInputSchema = z.object({
|
||||
workspaceId: z.string().min(1).max(128),
|
||||
noteId: z.string().min(1).max(128),
|
||||
artifactType: z.enum(['file', 'summary', 'extraction', 'citation', 'export']),
|
||||
title: z.string().min(1).max(500),
|
||||
description: z.string().max(4000).optional(),
|
||||
blobPath: z.string().max(2000).optional(),
|
||||
contentType: z.string().max(255).optional(),
|
||||
sizeBytes: z.number().int().min(0).optional(),
|
||||
agentId: z.string().max(128).optional(),
|
||||
dryRun: z.boolean().default(false),
|
||||
idempotencyKey: z.string().max(255).optional(),
|
||||
correlationId: z.string().max(255).optional(),
|
||||
});
|
||||
|
||||
export const AttachArtifactToolOutputSchema = z.object({
|
||||
dryRun: z.boolean(),
|
||||
artifact: z.object({
|
||||
id: z.string(),
|
||||
noteId: z.string(),
|
||||
artifactType: z.string(),
|
||||
title: z.string(),
|
||||
description: z.string().optional(),
|
||||
blobPath: z.string().optional(),
|
||||
}),
|
||||
idempotencyKey: z.string().optional(),
|
||||
correlationId: z.string().optional(),
|
||||
});
|
||||
|
||||
export const NotesMcpToolDefinitions = {
|
||||
list: {
|
||||
name: NOTES_MCP_TOOL_NAMES.list,
|
||||
@ -129,13 +226,53 @@ export const NotesMcpToolDefinitions = {
|
||||
outputSchema: CreateNoteDraftToolOutputSchema,
|
||||
readOnly: false,
|
||||
},
|
||||
updateNote: {
|
||||
name: NOTES_MCP_TOOL_NAMES.updateNote,
|
||||
description: 'Update an existing note (title, body, status, tags). Supports dry-run, idempotency, and correlation metadata.',
|
||||
requiredRole: 'admin' as const,
|
||||
inputSchema: UpdateNoteToolInputSchema,
|
||||
outputSchema: UpdateNoteToolOutputSchema,
|
||||
readOnly: false,
|
||||
},
|
||||
linkNotes: {
|
||||
name: NOTES_MCP_TOOL_NAMES.linkNotes,
|
||||
description: 'Create a typed relationship between two notes in the same workspace.',
|
||||
requiredRole: 'admin' as const,
|
||||
inputSchema: LinkNotesToolInputSchema,
|
||||
outputSchema: LinkNotesToolOutputSchema,
|
||||
readOnly: false,
|
||||
},
|
||||
extractTasks: {
|
||||
name: NOTES_MCP_TOOL_NAMES.extractTasks,
|
||||
description: 'Extract actionable tasks from a note body using simple heuristics. Supports dry-run.',
|
||||
requiredRole: 'admin' as const,
|
||||
inputSchema: ExtractTasksToolInputSchema,
|
||||
outputSchema: ExtractTasksToolOutputSchema,
|
||||
readOnly: false,
|
||||
},
|
||||
attachArtifact: {
|
||||
name: NOTES_MCP_TOOL_NAMES.attachArtifact,
|
||||
description: 'Attach an artifact (file, summary, extraction, citation, export) to a note. Supports dry-run.',
|
||||
requiredRole: 'admin' as const,
|
||||
inputSchema: AttachArtifactToolInputSchema,
|
||||
outputSchema: AttachArtifactToolOutputSchema,
|
||||
readOnly: false,
|
||||
},
|
||||
};
|
||||
|
||||
export type ListNotesToolInput = z.infer<typeof ListNotesToolInputSchema>;
|
||||
export type GetNoteToolInput = z.infer<typeof GetNoteToolInputSchema>;
|
||||
export type SearchNotesToolInput = z.infer<typeof SearchNotesToolInputSchema>;
|
||||
export type CreateNoteDraftToolInput = z.infer<typeof CreateNoteDraftToolInputSchema>;
|
||||
export type UpdateNoteToolInput = z.infer<typeof UpdateNoteToolInputSchema>;
|
||||
export type LinkNotesToolInput = z.infer<typeof LinkNotesToolInputSchema>;
|
||||
export type ExtractTasksToolInput = z.infer<typeof ExtractTasksToolInputSchema>;
|
||||
export type AttachArtifactToolInput = z.infer<typeof AttachArtifactToolInputSchema>;
|
||||
export type ListNotesToolOutput = z.infer<typeof ListNotesToolOutputSchema>;
|
||||
export type GetNoteToolOutput = z.infer<typeof GetNoteToolOutputSchema>;
|
||||
export type SearchNotesToolOutput = z.infer<typeof SearchNotesToolOutputSchema>;
|
||||
export type CreateNoteDraftToolOutput = z.infer<typeof CreateNoteDraftToolOutputSchema>;
|
||||
export type UpdateNoteToolOutput = z.infer<typeof UpdateNoteToolOutputSchema>;
|
||||
export type LinkNotesToolOutput = z.infer<typeof LinkNotesToolOutputSchema>;
|
||||
export type ExtractTasksToolOutput = z.infer<typeof ExtractTasksToolOutputSchema>;
|
||||
export type AttachArtifactToolOutput = z.infer<typeof AttachArtifactToolOutputSchema>;
|
||||
|
||||
@ -43,6 +43,10 @@ describe('note executable MCP tools', () => {
|
||||
NOTES_MCP_TOOL_NAMES.get,
|
||||
NOTES_MCP_TOOL_NAMES.search,
|
||||
NOTES_MCP_TOOL_NAMES.createDraft,
|
||||
NOTES_MCP_TOOL_NAMES.updateNote,
|
||||
NOTES_MCP_TOOL_NAMES.linkNotes,
|
||||
NOTES_MCP_TOOL_NAMES.extractTasks,
|
||||
NOTES_MCP_TOOL_NAMES.attachArtifact,
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
@ -1,22 +1,36 @@
|
||||
import { randomUUID } from 'node:crypto';
|
||||
import type { ZodTypeAny } from 'zod';
|
||||
import { PRODUCT_ID } from '../lib/product-config.js';
|
||||
import { createNote, getNote, listNotes } from '../modules/notes/repository.js';
|
||||
import { createNote, getNote, listNotes, updateNote } from '../modules/notes/repository.js';
|
||||
import type { NoteDoc } from '../modules/notes/types.js';
|
||||
import { createNoteAgentAction } from '../modules/note-agent-actions/repository.js';
|
||||
import type { NoteAgentActionDoc } from '../modules/note-agent-actions/types.js';
|
||||
import { createRelationship } from '../modules/note-relationships/repository.js';
|
||||
import type { NoteRelationshipDoc } from '../modules/note-relationships/types.js';
|
||||
import { createNoteTask } from '../modules/note-tasks/repository.js';
|
||||
import type { NoteTaskDoc } from '../modules/note-tasks/types.js';
|
||||
import { createNoteArtifact } from '../modules/note-artifacts/repository.js';
|
||||
import type { NoteArtifactDoc } from '../modules/note-artifacts/types.js';
|
||||
import {
|
||||
AttachArtifactToolOutputSchema,
|
||||
CreateNoteDraftToolOutputSchema,
|
||||
ExtractTasksToolOutputSchema,
|
||||
GetNoteToolOutputSchema,
|
||||
LinkNotesToolOutputSchema,
|
||||
ListNotesToolOutputSchema,
|
||||
NOTES_MCP_TOOL_NAMES,
|
||||
NotesMcpToolDefinitions,
|
||||
SearchNotesToolOutputSchema,
|
||||
UpdateNoteToolOutputSchema,
|
||||
type AttachArtifactToolInput,
|
||||
type CreateNoteDraftToolInput,
|
||||
type ExtractTasksToolInput,
|
||||
type GetNoteToolInput,
|
||||
type LinkNotesToolInput,
|
||||
type ListNotesToolInput,
|
||||
type NoteToolRole,
|
||||
type SearchNotesToolInput,
|
||||
type UpdateNoteToolInput,
|
||||
} from './note-tool-contracts.js';
|
||||
|
||||
export interface NotesMcpLogger {
|
||||
@ -221,11 +235,289 @@ async function executeCreateDraft(args: CreateNoteDraftToolInput, req: NotesMcpR
|
||||
});
|
||||
}
|
||||
|
||||
async function executeUpdateNote(args: UpdateNoteToolInput, req: NotesMcpRequest) {
|
||||
const userId = requireUserId(req);
|
||||
const existing = await getNote(args.noteId, args.workspaceId);
|
||||
if (!existing || existing.userId !== userId || existing.productId !== PRODUCT_ID) {
|
||||
throw new Error('Note not found');
|
||||
}
|
||||
|
||||
const updates: Partial<NoteDoc> = {
|
||||
updatedAt: new Date().toISOString(),
|
||||
updatedBy: userId,
|
||||
};
|
||||
if (args.title !== undefined) updates.title = args.title;
|
||||
if (args.body !== undefined) updates.body = args.body;
|
||||
if (args.status !== undefined) updates.status = args.status;
|
||||
if (args.tags !== undefined) updates.tags = args.tags;
|
||||
|
||||
if (args.dryRun) {
|
||||
const preview: NoteDoc = { ...existing, ...updates };
|
||||
return UpdateNoteToolOutputSchema.parse({
|
||||
dryRun: true,
|
||||
note: mapNote(preview),
|
||||
idempotencyKey: args.idempotencyKey,
|
||||
correlationId: args.correlationId ?? req.id,
|
||||
});
|
||||
}
|
||||
|
||||
const updated = await updateNote(args.noteId, args.workspaceId, updates);
|
||||
if (!updated) throw new Error('Update failed');
|
||||
|
||||
const action: NoteAgentActionDoc = {
|
||||
id: randomUUID(),
|
||||
productId: PRODUCT_ID,
|
||||
workspaceId: args.workspaceId,
|
||||
userId,
|
||||
noteId: args.noteId,
|
||||
actorId: args.agentId ?? userId,
|
||||
actorType: args.agentId ? 'agent' : 'human',
|
||||
toolName: NOTES_MCP_TOOL_NAMES.updateNote,
|
||||
actionType: 'update',
|
||||
state: 'applied',
|
||||
reason: 'Updated via MCP update tool',
|
||||
afterSummary: `${updated.title}\n\n${updated.body}`.slice(0, 4000),
|
||||
idempotencyKey: args.idempotencyKey,
|
||||
correlationId: args.correlationId ?? req.id,
|
||||
workflowId: req.id,
|
||||
createdAt: updates.updatedAt!,
|
||||
updatedAt: updates.updatedAt!,
|
||||
createdBy: userId,
|
||||
updatedBy: userId,
|
||||
};
|
||||
await createNoteAgentAction(action);
|
||||
|
||||
return UpdateNoteToolOutputSchema.parse({
|
||||
dryRun: false,
|
||||
note: mapNote(updated),
|
||||
idempotencyKey: args.idempotencyKey,
|
||||
correlationId: args.correlationId ?? req.id,
|
||||
});
|
||||
}
|
||||
|
||||
async function executeLinkNotes(args: LinkNotesToolInput, req: NotesMcpRequest) {
|
||||
const userId = requireUserId(req);
|
||||
const now = new Date().toISOString();
|
||||
|
||||
const relationship: NoteRelationshipDoc = {
|
||||
id: randomUUID(),
|
||||
productId: PRODUCT_ID,
|
||||
workspaceId: args.workspaceId,
|
||||
userId,
|
||||
fromNoteId: args.fromNoteId,
|
||||
toNoteId: args.toNoteId,
|
||||
relationshipType: args.relationshipType,
|
||||
createdAt: now,
|
||||
createdBy: userId,
|
||||
updatedAt: now,
|
||||
updatedBy: userId,
|
||||
};
|
||||
|
||||
const created = await createRelationship(relationship);
|
||||
|
||||
const action: NoteAgentActionDoc = {
|
||||
id: randomUUID(),
|
||||
productId: PRODUCT_ID,
|
||||
workspaceId: args.workspaceId,
|
||||
userId,
|
||||
noteId: args.fromNoteId,
|
||||
actorId: args.agentId ?? userId,
|
||||
actorType: args.agentId ? 'agent' : 'human',
|
||||
toolName: NOTES_MCP_TOOL_NAMES.linkNotes,
|
||||
actionType: 'update',
|
||||
state: 'applied',
|
||||
reason: `Linked ${args.fromNoteId} → ${args.toNoteId} (${args.relationshipType})`,
|
||||
afterSummary: `Relationship: ${args.relationshipType}`,
|
||||
idempotencyKey: args.idempotencyKey,
|
||||
correlationId: args.correlationId ?? req.id,
|
||||
workflowId: req.id,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
createdBy: userId,
|
||||
updatedBy: userId,
|
||||
};
|
||||
await createNoteAgentAction(action);
|
||||
|
||||
return LinkNotesToolOutputSchema.parse({
|
||||
id: created.id,
|
||||
fromNoteId: created.fromNoteId,
|
||||
toNoteId: created.toNoteId,
|
||||
relationshipType: created.relationshipType,
|
||||
idempotencyKey: args.idempotencyKey,
|
||||
correlationId: args.correlationId ?? req.id,
|
||||
});
|
||||
}
|
||||
|
||||
function extractTasksFromBody(body: string): Array<{ title: string; description?: string }> {
|
||||
const lines = body.split('\n');
|
||||
const tasks: Array<{ title: string; description?: string }> = [];
|
||||
for (const line of lines) {
|
||||
const trimmed = line.trim();
|
||||
const match = trimmed.match(/^[-*]\s*\[[ x]?\]\s*(.+)$/i) ?? trimmed.match(/^(?:TODO|FIXME|ACTION|TASK):\s*(.+)$/i);
|
||||
if (match) {
|
||||
tasks.push({ title: match[1].trim() });
|
||||
}
|
||||
}
|
||||
return tasks;
|
||||
}
|
||||
|
||||
async function executeExtractTasks(args: ExtractTasksToolInput, req: NotesMcpRequest) {
|
||||
const userId = requireUserId(req);
|
||||
const note = await getNote(args.noteId, args.workspaceId);
|
||||
if (!note || note.userId !== userId || note.productId !== PRODUCT_ID) {
|
||||
throw new Error('Note not found');
|
||||
}
|
||||
|
||||
const extracted = extractTasksFromBody(note.body);
|
||||
const now = new Date().toISOString();
|
||||
|
||||
const taskDocs: NoteTaskDoc[] = extracted.map(t => ({
|
||||
id: randomUUID(),
|
||||
productId: PRODUCT_ID,
|
||||
workspaceId: args.workspaceId,
|
||||
userId,
|
||||
noteId: args.noteId,
|
||||
title: t.title,
|
||||
description: t.description,
|
||||
status: 'open' as const,
|
||||
source: 'extracted' as const,
|
||||
createdAt: now,
|
||||
createdBy: userId,
|
||||
updatedAt: now,
|
||||
updatedBy: userId,
|
||||
}));
|
||||
|
||||
if (!args.dryRun) {
|
||||
for (const task of taskDocs) {
|
||||
await createNoteTask(task);
|
||||
}
|
||||
|
||||
const action: NoteAgentActionDoc = {
|
||||
id: randomUUID(),
|
||||
productId: PRODUCT_ID,
|
||||
workspaceId: args.workspaceId,
|
||||
userId,
|
||||
noteId: args.noteId,
|
||||
actorId: args.agentId ?? userId,
|
||||
actorType: args.agentId ? 'agent' : 'human',
|
||||
toolName: NOTES_MCP_TOOL_NAMES.extractTasks,
|
||||
actionType: 'extract_tasks',
|
||||
state: 'applied',
|
||||
reason: `Extracted ${taskDocs.length} tasks from note body`,
|
||||
afterSummary: taskDocs.map(t => t.title).join('\n'),
|
||||
idempotencyKey: args.idempotencyKey,
|
||||
correlationId: args.correlationId ?? req.id,
|
||||
workflowId: req.id,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
createdBy: userId,
|
||||
updatedBy: userId,
|
||||
};
|
||||
await createNoteAgentAction(action);
|
||||
}
|
||||
|
||||
return ExtractTasksToolOutputSchema.parse({
|
||||
dryRun: args.dryRun,
|
||||
noteId: args.noteId,
|
||||
tasks: taskDocs.map(t => ({
|
||||
id: t.id,
|
||||
title: t.title,
|
||||
description: t.description,
|
||||
status: t.status,
|
||||
source: t.source,
|
||||
})),
|
||||
idempotencyKey: args.idempotencyKey,
|
||||
correlationId: args.correlationId ?? req.id,
|
||||
});
|
||||
}
|
||||
|
||||
async function executeAttachArtifact(args: AttachArtifactToolInput, req: NotesMcpRequest) {
|
||||
const userId = requireUserId(req);
|
||||
const now = new Date().toISOString();
|
||||
|
||||
const artifactDoc: NoteArtifactDoc = {
|
||||
id: randomUUID(),
|
||||
productId: PRODUCT_ID,
|
||||
workspaceId: args.workspaceId,
|
||||
userId,
|
||||
noteId: args.noteId,
|
||||
artifactType: args.artifactType,
|
||||
title: args.title,
|
||||
description: args.description,
|
||||
blobPath: args.blobPath,
|
||||
contentType: args.contentType,
|
||||
sizeBytes: args.sizeBytes,
|
||||
createdAt: now,
|
||||
createdBy: userId,
|
||||
updatedAt: now,
|
||||
updatedBy: userId,
|
||||
};
|
||||
|
||||
if (args.dryRun) {
|
||||
return AttachArtifactToolOutputSchema.parse({
|
||||
dryRun: true,
|
||||
artifact: {
|
||||
id: artifactDoc.id,
|
||||
noteId: artifactDoc.noteId,
|
||||
artifactType: artifactDoc.artifactType,
|
||||
title: artifactDoc.title,
|
||||
description: artifactDoc.description,
|
||||
blobPath: artifactDoc.blobPath,
|
||||
},
|
||||
idempotencyKey: args.idempotencyKey,
|
||||
correlationId: args.correlationId ?? req.id,
|
||||
});
|
||||
}
|
||||
|
||||
const created = await createNoteArtifact(artifactDoc);
|
||||
|
||||
const action: NoteAgentActionDoc = {
|
||||
id: randomUUID(),
|
||||
productId: PRODUCT_ID,
|
||||
workspaceId: args.workspaceId,
|
||||
userId,
|
||||
noteId: args.noteId,
|
||||
actorId: args.agentId ?? userId,
|
||||
actorType: args.agentId ? 'agent' : 'human',
|
||||
toolName: NOTES_MCP_TOOL_NAMES.attachArtifact,
|
||||
actionType: 'attach_citation',
|
||||
state: 'applied',
|
||||
reason: `Attached ${args.artifactType}: ${args.title}`,
|
||||
afterSummary: `Artifact: ${args.title} (${args.artifactType})`,
|
||||
idempotencyKey: args.idempotencyKey,
|
||||
correlationId: args.correlationId ?? req.id,
|
||||
workflowId: req.id,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
createdBy: userId,
|
||||
updatedBy: userId,
|
||||
};
|
||||
await createNoteAgentAction(action);
|
||||
|
||||
return AttachArtifactToolOutputSchema.parse({
|
||||
dryRun: false,
|
||||
artifact: {
|
||||
id: created.id,
|
||||
noteId: created.noteId,
|
||||
artifactType: created.artifactType,
|
||||
title: created.title,
|
||||
description: created.description,
|
||||
blobPath: created.blobPath,
|
||||
},
|
||||
idempotencyKey: args.idempotencyKey,
|
||||
correlationId: args.correlationId ?? req.id,
|
||||
});
|
||||
}
|
||||
|
||||
export const NotesExecutableMcpTools: Array<
|
||||
| NotesMcpTool<ListNotesToolInput>
|
||||
| NotesMcpTool<GetNoteToolInput>
|
||||
| NotesMcpTool<SearchNotesToolInput>
|
||||
| NotesMcpTool<CreateNoteDraftToolInput>
|
||||
| NotesMcpTool<UpdateNoteToolInput>
|
||||
| NotesMcpTool<LinkNotesToolInput>
|
||||
| NotesMcpTool<ExtractTasksToolInput>
|
||||
| NotesMcpTool<AttachArtifactToolInput>
|
||||
> = [
|
||||
{
|
||||
...NotesMcpToolDefinitions.list,
|
||||
@ -243,6 +535,22 @@ export const NotesExecutableMcpTools: Array<
|
||||
...NotesMcpToolDefinitions.createDraft,
|
||||
execute: executeCreateDraft,
|
||||
},
|
||||
{
|
||||
...NotesMcpToolDefinitions.updateNote,
|
||||
execute: executeUpdateNote,
|
||||
},
|
||||
{
|
||||
...NotesMcpToolDefinitions.linkNotes,
|
||||
execute: executeLinkNotes,
|
||||
},
|
||||
{
|
||||
...NotesMcpToolDefinitions.extractTasks,
|
||||
execute: executeExtractTasks,
|
||||
},
|
||||
{
|
||||
...NotesMcpToolDefinitions.attachArtifact,
|
||||
execute: executeAttachArtifact,
|
||||
},
|
||||
];
|
||||
|
||||
export function getNotesExecutableMcpTool(name: string) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user