UUID everywhere: MCP tools use project_id instead of project_slug
All tools (tasks, files, projects) now accept project_id (UUID). TrackerClient methods renamed accordingly.
This commit is contained in:
parent
fc86592a73
commit
1322a0b480
@ -3,35 +3,35 @@ import type { ToolDefinition } from '@mariozechner/pi-coding-agent';
|
||||
import type { ToolContext } from './types.js';
|
||||
|
||||
const ListFilesParams = Type.Object({
|
||||
project_slug: Type.String({ description: 'Project slug' }),
|
||||
project_id: Type.String({ description: 'Project ID (UUID)' }),
|
||||
search: Type.Optional(Type.String({ description: 'Search by filename' })),
|
||||
});
|
||||
|
||||
const GetFileParams = Type.Object({
|
||||
project_slug: Type.String({ description: 'Project slug' }),
|
||||
project_id: Type.String({ description: 'Project ID (UUID)' }),
|
||||
file_id: Type.String({ description: 'File UUID' }),
|
||||
});
|
||||
|
||||
const DownloadFileParams = Type.Object({
|
||||
project_slug: Type.String({ description: 'Project slug' }),
|
||||
project_id: Type.String({ description: 'Project ID (UUID)' }),
|
||||
file_id: Type.String({ description: 'File UUID' }),
|
||||
});
|
||||
|
||||
const UploadFileParams = Type.Object({
|
||||
project_slug: Type.String({ description: 'Project slug' }),
|
||||
project_id: Type.String({ description: 'Project ID (UUID)' }),
|
||||
filename: Type.String({ description: 'File name (e.g. "spec.md")' }),
|
||||
content: Type.String({ description: 'File content as base64 string' }),
|
||||
description: Type.Optional(Type.String({ description: 'File description' })),
|
||||
});
|
||||
|
||||
const UpdateFileParams = Type.Object({
|
||||
project_slug: Type.String({ description: 'Project slug' }),
|
||||
project_id: Type.String({ description: 'Project ID (UUID)' }),
|
||||
file_id: Type.String({ description: 'File UUID' }),
|
||||
description: Type.String({ description: 'New description' }),
|
||||
});
|
||||
|
||||
const DeleteFileParams = Type.Object({
|
||||
project_slug: Type.String({ description: 'Project slug' }),
|
||||
project_id: Type.String({ description: 'Project ID (UUID)' }),
|
||||
file_id: Type.String({ description: 'File UUID' }),
|
||||
});
|
||||
|
||||
@ -47,7 +47,7 @@ export function createFileTools(ctx: ToolContext): ToolDefinition<any>[] {
|
||||
description: 'List files in a project. Optionally search by filename.',
|
||||
parameters: ListFilesParams,
|
||||
async execute(_id: string, params: any) {
|
||||
const files = await ctx.trackerClient.listProjectFiles(params.project_slug, params.search);
|
||||
const files = await ctx.trackerClient.listProjectFiles(params.project_id, params.search);
|
||||
if (files.length === 0) return ok('No files found.');
|
||||
const list = files.map((f: any) => `- ${f.filename} (${f.size} bytes) ${f.description ? `— ${f.description}` : ''} [id: ${f.id}]`).join('\n');
|
||||
return ok(`${files.length} file(s):\n${list}`);
|
||||
@ -59,7 +59,7 @@ export function createFileTools(ctx: ToolContext): ToolDefinition<any>[] {
|
||||
description: 'Get metadata of a project file by ID.',
|
||||
parameters: GetFileParams,
|
||||
async execute(_id: string, params: any) {
|
||||
const file = await ctx.trackerClient.getProjectFile(params.project_slug, params.file_id);
|
||||
const file = await ctx.trackerClient.getProjectFile(params.project_id, params.file_id);
|
||||
return ok(JSON.stringify(file, null, 2));
|
||||
},
|
||||
},
|
||||
@ -69,7 +69,7 @@ export function createFileTools(ctx: ToolContext): ToolDefinition<any>[] {
|
||||
description: 'Download the content of a project file (returns text content).',
|
||||
parameters: DownloadFileParams,
|
||||
async execute(_id: string, params: any) {
|
||||
const content = await ctx.trackerClient.downloadProjectFile(params.project_slug, params.file_id);
|
||||
const content = await ctx.trackerClient.downloadProjectFile(params.project_id, params.file_id);
|
||||
return ok(content);
|
||||
},
|
||||
},
|
||||
@ -80,7 +80,7 @@ export function createFileTools(ctx: ToolContext): ToolDefinition<any>[] {
|
||||
parameters: UploadFileParams,
|
||||
async execute(_id: string, params: any) {
|
||||
const result = await ctx.trackerClient.uploadProjectFile(
|
||||
params.project_slug,
|
||||
params.project_id,
|
||||
params.filename,
|
||||
params.content,
|
||||
params.description,
|
||||
@ -94,7 +94,7 @@ export function createFileTools(ctx: ToolContext): ToolDefinition<any>[] {
|
||||
description: 'Update the description of a project file.',
|
||||
parameters: UpdateFileParams,
|
||||
async execute(_id: string, params: any) {
|
||||
await ctx.trackerClient.updateProjectFile(params.project_slug, params.file_id, { description: params.description });
|
||||
await ctx.trackerClient.updateProjectFile(params.project_id, params.file_id, { description: params.description });
|
||||
return ok('Description updated.');
|
||||
},
|
||||
},
|
||||
@ -104,7 +104,7 @@ export function createFileTools(ctx: ToolContext): ToolDefinition<any>[] {
|
||||
description: 'Delete a project file from disk and database.',
|
||||
parameters: DeleteFileParams,
|
||||
async execute(_id: string, params: any) {
|
||||
await ctx.trackerClient.deleteProjectFile(params.project_slug, params.file_id);
|
||||
await ctx.trackerClient.deleteProjectFile(params.project_id, params.file_id);
|
||||
return ok('File deleted.');
|
||||
},
|
||||
},
|
||||
|
||||
@ -3,7 +3,7 @@ import type { ToolDefinition } from '@mariozechner/pi-coding-agent';
|
||||
import type { ToolContext } from './types.js';
|
||||
|
||||
const GetProjectParams = Type.Object({
|
||||
slug: Type.String({ description: 'Project slug' }),
|
||||
project_id: Type.String({ description: 'Project ID (UUID)' }),
|
||||
});
|
||||
|
||||
function ok(text: string) {
|
||||
@ -15,7 +15,7 @@ export function createProjectTools(ctx: ToolContext): ToolDefinition<any>[] {
|
||||
{
|
||||
name: 'list_projects',
|
||||
label: 'List Projects',
|
||||
description: 'List all projects the agent has access to.',
|
||||
description: 'List all projects the agent has access to. Returns id, slug, name, chat_id for each.',
|
||||
parameters: Type.Object({}),
|
||||
async execute() {
|
||||
const projects = await ctx.trackerClient.listProjects();
|
||||
@ -25,10 +25,10 @@ export function createProjectTools(ctx: ToolContext): ToolDefinition<any>[] {
|
||||
{
|
||||
name: 'get_project',
|
||||
label: 'Get Project',
|
||||
description: 'Get project details by slug, including chat_id.',
|
||||
description: 'Get project details by ID (UUID), including chat_id.',
|
||||
parameters: GetProjectParams,
|
||||
async execute(_id: string, params: any) {
|
||||
const project = await ctx.trackerClient.getProject(params.slug);
|
||||
const project = await ctx.trackerClient.getProject(params.project_id);
|
||||
return ok(JSON.stringify(project, null, 2));
|
||||
},
|
||||
},
|
||||
|
||||
@ -3,7 +3,7 @@ import type { ToolDefinition } from '@mariozechner/pi-coding-agent';
|
||||
import type { ToolContext } from './types.js';
|
||||
|
||||
const ListTasksParams = Type.Object({
|
||||
project_slug: Type.Optional(Type.String({ description: 'Filter by project slug' })),
|
||||
project_id: Type.Optional(Type.String({ description: 'Filter by project ID (UUID)' })),
|
||||
status: Type.Optional(Type.String({ description: 'Filter by status: backlog|todo|in_progress|in_review|done' })),
|
||||
assignee_id: Type.Optional(Type.String({ description: 'Filter by assignee ID (UUID)' })),
|
||||
});
|
||||
@ -13,7 +13,7 @@ const GetTaskParams = Type.Object({
|
||||
});
|
||||
|
||||
const CreateTaskParams = Type.Object({
|
||||
project_slug: Type.String({ description: 'Project slug' }),
|
||||
project_id: Type.String({ description: 'Project ID (UUID)' }),
|
||||
title: Type.String({ description: 'Task title' }),
|
||||
description: Type.Optional(Type.String({ description: 'Task description (markdown)' })),
|
||||
priority: Type.Optional(Type.String({ description: 'Priority: low|medium|high|critical' })),
|
||||
@ -51,11 +51,11 @@ export function createTaskTools(ctx: ToolContext): ToolDefinition<any>[] {
|
||||
{
|
||||
name: 'list_tasks',
|
||||
label: 'List Tasks',
|
||||
description: 'List tasks with optional filters (project_slug, status, assignee_id). Returns array of task objects.',
|
||||
description: 'List tasks with optional filters (project_id, status, assignee_id). Returns array of task objects.',
|
||||
parameters: ListTasksParams,
|
||||
async execute(_id: string, params: any) {
|
||||
const query: Record<string, string> = {};
|
||||
if (params.project_slug) query.project_slug = params.project_slug;
|
||||
if (params.project_id) query.project_id = params.project_id;
|
||||
if (params.status) query.status = params.status;
|
||||
if (params.assignee_id) query.assignee_id = params.assignee_id;
|
||||
const tasks = await ctx.trackerClient.listTasks(query);
|
||||
@ -78,8 +78,8 @@ export function createTaskTools(ctx: ToolContext): ToolDefinition<any>[] {
|
||||
description: 'Create a new task in a project. Returns the created task with generated key (e.g. TE-1).',
|
||||
parameters: CreateTaskParams,
|
||||
async execute(_id: string, params: any) {
|
||||
const { project_slug, ...taskData } = params;
|
||||
const task = await ctx.trackerClient.createTask(project_slug, taskData);
|
||||
const { project_id, ...taskData } = params;
|
||||
const task = await ctx.trackerClient.createTask(project_id, taskData);
|
||||
return ok(JSON.stringify(task, null, 2));
|
||||
},
|
||||
},
|
||||
@ -102,7 +102,7 @@ export function createTaskTools(ctx: ToolContext): ToolDefinition<any>[] {
|
||||
async execute(_id: string, params: any) {
|
||||
ctx.selfAssignedTasks.add(params.task_id);
|
||||
await ctx.trackerClient.takeTask(params.task_id);
|
||||
return ok(`Task ${params.task_id} taken by ${ctx.agentSlug}`);
|
||||
return ok(`Task ${params.task_id} taken`);
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@ -58,8 +58,8 @@ export class TrackerClient {
|
||||
return this.request('GET', `/api/v1/tasks/${taskId}`);
|
||||
}
|
||||
|
||||
async createTask(projectSlug: string, task: Record<string, unknown>): Promise<Record<string, unknown>> {
|
||||
return this.request('POST', `/api/v1/tasks?project_slug=${encodeURIComponent(projectSlug)}`, task);
|
||||
async createTask(projectId: string, task: Record<string, unknown>): Promise<Record<string, unknown>> {
|
||||
return this.request('POST', `/api/v1/tasks?project_id=${encodeURIComponent(projectId)}`, task);
|
||||
}
|
||||
|
||||
async updateTask(taskId: string, fields: Record<string, unknown>): Promise<void> {
|
||||
@ -109,17 +109,17 @@ export class TrackerClient {
|
||||
|
||||
// --- Project Files ---
|
||||
|
||||
async listProjectFiles(projectSlug: string, search?: string): Promise<Record<string, unknown>[]> {
|
||||
async listProjectFiles(projectId: string, search?: string): Promise<Record<string, unknown>[]> {
|
||||
const qs = search ? `?search=${encodeURIComponent(search)}` : '';
|
||||
return this.request('GET', `/api/v1/projects/${projectSlug}/files${qs}`);
|
||||
return this.request('GET', `/api/v1/projects/${projectId}/files${qs}`);
|
||||
}
|
||||
|
||||
async getProjectFile(projectSlug: string, fileId: string): Promise<Record<string, unknown>> {
|
||||
return this.request('GET', `/api/v1/projects/${projectSlug}/files/${fileId}`);
|
||||
async getProjectFile(projectId: string, fileId: string): Promise<Record<string, unknown>> {
|
||||
return this.request('GET', `/api/v1/projects/${projectId}/files/${fileId}`);
|
||||
}
|
||||
|
||||
async downloadProjectFile(projectSlug: string, fileId: string): Promise<string> {
|
||||
const url = `${this.baseUrl}/api/v1/projects/${projectSlug}/files/${fileId}/download`;
|
||||
async downloadProjectFile(projectId: string, fileId: string): Promise<string> {
|
||||
const url = `${this.baseUrl}/api/v1/projects/${projectId}/files/${fileId}/download`;
|
||||
const res = await fetch(url, {
|
||||
headers: { 'Authorization': `Bearer ${this.token}` },
|
||||
});
|
||||
@ -127,8 +127,8 @@ export class TrackerClient {
|
||||
return res.text();
|
||||
}
|
||||
|
||||
async uploadProjectFile(projectSlug: string, filename: string, content: string, description?: string): Promise<Record<string, unknown>> {
|
||||
const url = `${this.baseUrl}/api/v1/projects/${projectSlug}/files`;
|
||||
async uploadProjectFile(projectId: string, filename: string, content: string, description?: string): Promise<Record<string, unknown>> {
|
||||
const url = `${this.baseUrl}/api/v1/projects/${projectId}/files`;
|
||||
const formData = new FormData();
|
||||
formData.append('file', new Blob([Buffer.from(content, 'base64')]), filename);
|
||||
if (description) formData.append('description', description);
|
||||
@ -145,12 +145,12 @@ export class TrackerClient {
|
||||
return res.json();
|
||||
}
|
||||
|
||||
async updateProjectFile(projectSlug: string, fileId: string, data: { description?: string }): Promise<Record<string, unknown>> {
|
||||
return this.request('PATCH', `/api/v1/projects/${projectSlug}/files/${fileId}`, data);
|
||||
async updateProjectFile(projectId: string, fileId: string, data: { description?: string }): Promise<Record<string, unknown>> {
|
||||
return this.request('PATCH', `/api/v1/projects/${projectId}/files/${fileId}`, data);
|
||||
}
|
||||
|
||||
async deleteProjectFile(projectSlug: string, fileId: string): Promise<void> {
|
||||
await this.request('DELETE', `/api/v1/projects/${projectSlug}/files/${fileId}`);
|
||||
async deleteProjectFile(projectId: string, fileId: string): Promise<void> {
|
||||
await this.request('DELETE', `/api/v1/projects/${projectId}/files/${fileId}`);
|
||||
}
|
||||
|
||||
// --- Projects ---
|
||||
@ -159,8 +159,8 @@ export class TrackerClient {
|
||||
return this.request('GET', '/api/v1/projects');
|
||||
}
|
||||
|
||||
async getProject(slug: string): Promise<Record<string, unknown>> {
|
||||
return this.request('GET', `/api/v1/projects/${slug}`);
|
||||
async getProject(projectId: string): Promise<Record<string, unknown>> {
|
||||
return this.request('GET', `/api/v1/projects/${projectId}`);
|
||||
}
|
||||
|
||||
// --- Members ---
|
||||
|
||||
Loading…
Reference in New Issue
Block a user