Files
kan/packages/mcp/src/tools/workspace.ts
Morfixx 81b67df9e3 feat: kan mcp server (#485)
* feat: kan mcp server initial attempt

* fix: card was missing options and added default fallbacks

* fix: label creating with better information for colors and presets
2026-06-10 09:10:22 +01:00

122 lines
4.5 KiB
TypeScript

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { kanRequest } from "../client.js";
export function registerWorkspaceTools(server: McpServer): void {
server.tool(
"list_workspaces",
"List all workspaces the authenticated user belongs to. Call this first to resolve a workspace name to its publicId before calling any other workspace-scoped tool.",
{},
async () => {
const data = await kanRequest("GET", "/workspaces");
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
},
);
server.tool(
"find_workspace_by_name",
"Find a workspace by its name (case-insensitive). Returns the matching workspace including its publicId. Use this whenever you only know the workspace name and need its publicId.",
{ name: z.string().describe("Workspace name to search for") },
async ({ name }) => {
const workspaces = await kanRequest<{ publicId: string; name: string }[]>("GET", "/workspaces");
const match = workspaces.find(
(w) => w.name.toLowerCase() === name.toLowerCase(),
);
if (!match) {
const names = workspaces.map((w) => w.name).join(", ");
return {
content: [
{
type: "text",
text: `No workspace found with name "${name}". Available workspaces: ${names}`,
},
],
};
}
return { content: [{ type: "text", text: JSON.stringify(match, null, 2) }] };
},
);
server.tool(
"get_workspace",
"Get a workspace by its public ID, including its members",
{ workspacePublicId: z.string().describe("The workspace's public ID") },
async ({ workspacePublicId }) => {
const data = await kanRequest("GET", `/workspaces/${workspacePublicId}`);
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
},
);
server.tool(
"get_workspace_by_slug",
"Get a workspace by its slug, including its boards",
{ workspaceSlug: z.string().describe("The workspace slug") },
async ({ workspaceSlug }) => {
const data = await kanRequest("GET", `/workspaces/${workspaceSlug}`);
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
},
);
server.tool(
"create_workspace",
"Create a new workspace",
{
name: z.string().describe("Workspace name"),
slug: z.string().optional().describe("URL-friendly slug (auto-generated if omitted)"),
},
async ({ name, slug }) => {
const data = await kanRequest("POST", "/workspaces", { name, slug });
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
},
);
server.tool(
"update_workspace",
"Update a workspace's name or slug",
{
workspacePublicId: z.string().describe("The workspace's public ID"),
name: z.string().optional().describe("New workspace name"),
slug: z.string().optional().describe("New workspace slug"),
},
async ({ workspacePublicId, name, slug }) => {
const data = await kanRequest("PUT", `/workspaces/${workspacePublicId}`, { name, slug });
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
},
);
server.tool(
"delete_workspace",
"Permanently delete a workspace",
{ workspacePublicId: z.string().describe("The workspace's public ID") },
async ({ workspacePublicId }) => {
const data = await kanRequest("DELETE", `/workspaces/${workspacePublicId}`);
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
},
);
server.tool(
"search_workspace",
"Search for boards and cards by title within a workspace",
{
workspacePublicId: z.string().describe("The workspace's public ID"),
query: z.string().describe("Search query string"),
},
async ({ workspacePublicId, query }) => {
const params = new URLSearchParams({ query });
const data = await kanRequest("GET", `/workspaces/${workspacePublicId}/search?${params}`);
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
},
);
server.tool(
"check_workspace_slug_availability",
"Check whether a workspace slug is available",
{ slug: z.string().describe("Slug to check") },
async ({ slug }) => {
const params = new URLSearchParams({ slug });
const data = await kanRequest("GET", `/workspaces/check-slug-availability?${params}`);
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
},
);
}