@taiga-ai/mcp-server 0.4.0-dev.306 → 0.4.0-dev.308
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +8 -8
- package/package.json +1 -1
- package/src/client.test.ts +6 -6
- package/src/client.ts +2 -2
- package/src/server.integration.test.ts +7 -7
- package/src/tools/documents.ts +4 -5
- package/src/tools/products.ts +3 -5
- package/taiga-ai-mcp-server-0.4.0-dev.308.tgz +0 -0
- package/taiga-ai-mcp-server-0.4.0-dev.306.tgz +0 -0
package/dist/index.js
CHANGED
|
@@ -106,7 +106,7 @@ var TaigaClient = class {
|
|
|
106
106
|
"architecture",
|
|
107
107
|
"technology_decisions"
|
|
108
108
|
];
|
|
109
|
-
const keyFactoryDocTypes = ["
|
|
109
|
+
const keyFactoryDocTypes = ["factory_instructions"];
|
|
110
110
|
const docFetches = [];
|
|
111
111
|
for (const docType of keyProductDocTypes) if (productDocs.data.some((d) => d.type === docType && !d.isDraft)) docFetches.push(this.getProductDocument(productId, docType).then((r) => ({
|
|
112
112
|
type: docType,
|
|
@@ -197,7 +197,7 @@ function summarizeList(items) {
|
|
|
197
197
|
//#endregion
|
|
198
198
|
//#region src/tools/products.ts
|
|
199
199
|
function registerProductTools(server, client) {
|
|
200
|
-
if (client.hasCapability("
|
|
200
|
+
if (client.hasCapability("factories:read")) server.tool("taiga_list_factories", "List all factories in the organization. A factory is the group that owns a set of products. Use this to find factory IDs for reading factory instructions. Factory instructions carry the factory's standing guidance: domain context, tech preferences, and the conventions its products follow.", { limit: z.number().min(1).max(100).optional().describe("Max results (default 25)") }, {
|
|
201
201
|
readOnlyHint: true,
|
|
202
202
|
openWorldHint: false
|
|
203
203
|
}, async (params) => {
|
|
@@ -207,7 +207,7 @@ function registerProductTools(server, client) {
|
|
|
207
207
|
text: JSON.stringify(result.data, null, 2)
|
|
208
208
|
}] };
|
|
209
209
|
});
|
|
210
|
-
if (client.hasCapability("
|
|
210
|
+
if (client.hasCapability("products:read")) {
|
|
211
211
|
server.tool("taiga_list_products", "List products in the organization. Use this to find the product you are working on so you can read its specification, architecture, and threat model. Can filter by factory; a status filter also exists but no longer distinguishes anything (see its description).", {
|
|
212
212
|
factoryId: z.string().uuid().optional().describe("Filter by factory ID -- get factory IDs from taiga_list_factories"),
|
|
213
213
|
status: z.enum([
|
|
@@ -239,7 +239,7 @@ function registerProductTools(server, client) {
|
|
|
239
239
|
text: JSON.stringify(summaries, null, 2)
|
|
240
240
|
}] };
|
|
241
241
|
});
|
|
242
|
-
server.tool("taiga_get_product_document", "Get the full content of a specific product document. Use this when you need to understand what to build (specification), how the system is designed (architecture), what security risks exist (threat model), or what technology choices were made (technology_decisions). Types: specification, architecture, technology_decisions, data_flow, privacy_assessment, threat_model, risk_register, user_flow, service_blueprint, ui_direction, codebase_snapshot
|
|
242
|
+
server.tool("taiga_get_product_document", "Get the full content of a specific product document. Use this when you need to understand what to build (specification), how the system is designed (architecture), what security risks exist (threat model), or what technology choices were made (technology_decisions). Types: specification, architecture, technology_decisions, data_flow, privacy_assessment, threat_model, risk_register, user_flow, service_blueprint, ui_direction, codebase_snapshot, and product_instructions (the product's standing instructions).", {
|
|
243
243
|
productId: z.string().uuid().describe("Product ID -- get from taiga_list_products"),
|
|
244
244
|
type: z.string().describe("Document type -- get available types from taiga_list_product_documents. Common: specification, architecture, threat_model, technology_decisions")
|
|
245
245
|
}, {
|
|
@@ -295,7 +295,7 @@ function registerDocumentTools(server, client) {
|
|
|
295
295
|
text: JSON.stringify(result.data, null, 2)
|
|
296
296
|
}] };
|
|
297
297
|
});
|
|
298
|
-
if (client.hasCapability("
|
|
298
|
+
if (client.hasCapability("factories:read")) {
|
|
299
299
|
server.tool("taiga_list_factory_documents", "List documents for a specific factory (names and types only). A factory has one document: its instructions, the factory's free-form standing guidance (domain context, tech preferences, conventions) that applies to every product it owns. Use taiga_get_factory_document to read the full content.", {
|
|
300
300
|
factoryId: z.string().uuid().describe("Factory ID -- get from taiga_list_factories"),
|
|
301
301
|
limit: z.number().min(1).max(100).optional().describe("Max results (default 25)")
|
|
@@ -311,7 +311,7 @@ function registerDocumentTools(server, client) {
|
|
|
311
311
|
});
|
|
312
312
|
server.tool("taiga_get_factory_document", "Get the full content of a factory's instructions. Use this when you need the domain context, tech preferences, or conventions the factory expects every product it owns to follow.", {
|
|
313
313
|
factoryId: z.string().uuid().describe("Factory ID -- get from taiga_list_factories"),
|
|
314
|
-
type: z.string().describe("Document type -- get available types from taiga_list_factory_documents. The factory instructions document is `
|
|
314
|
+
type: z.string().describe("Document type -- get available types from taiga_list_factory_documents. The factory instructions document is `factory_instructions`.")
|
|
315
315
|
}, {
|
|
316
316
|
readOnlyHint: true,
|
|
317
317
|
openWorldHint: false
|
|
@@ -323,9 +323,9 @@ function registerDocumentTools(server, client) {
|
|
|
323
323
|
}] };
|
|
324
324
|
});
|
|
325
325
|
}
|
|
326
|
-
if (client.hasCapability("
|
|
326
|
+
if (client.hasCapability("factories:read") && client.hasCapability("tenant:documents:write")) server.tool("taiga_update_factory_document", "Update the draft of a factory's instructions. Only updates the draft -- does not publish. Use this when you notice the factory conventions, domain context, or tech preferences need updating while working on the codebase.", {
|
|
327
327
|
factoryId: z.string().uuid().describe("Factory ID -- get from taiga_list_factories"),
|
|
328
|
-
type: z.string().describe("Document type (`
|
|
328
|
+
type: z.string().describe("Document type (`factory_instructions`)"),
|
|
329
329
|
data: z.record(z.string(), z.unknown()).describe("Document content to save as JSON")
|
|
330
330
|
}, {
|
|
331
331
|
readOnlyHint: false,
|
package/package.json
CHANGED
package/src/client.test.ts
CHANGED
|
@@ -165,7 +165,7 @@ describe('TaigaClient', () => {
|
|
|
165
165
|
tenantId: 't1',
|
|
166
166
|
email: 'test@example.com',
|
|
167
167
|
role: 'member',
|
|
168
|
-
capabilities: ['
|
|
168
|
+
capabilities: ['products:read', 'factories:read'],
|
|
169
169
|
};
|
|
170
170
|
stubFetch(200, { data: expected });
|
|
171
171
|
const client = makeClient();
|
|
@@ -181,14 +181,14 @@ describe('TaigaClient', () => {
|
|
|
181
181
|
it('should report no capabilities before introspect is called', () => {
|
|
182
182
|
const client = makeClient();
|
|
183
183
|
|
|
184
|
-
expect(client.hasCapability('
|
|
184
|
+
expect(client.hasCapability('products:read')).toBe(false);
|
|
185
185
|
});
|
|
186
186
|
|
|
187
187
|
it('should correctly report granted vs missing capabilities', async () => {
|
|
188
|
-
const client = await clientWithCapabilities(['
|
|
188
|
+
const client = await clientWithCapabilities(['products:read', 'factories:read']);
|
|
189
189
|
|
|
190
|
-
expect(client.hasCapability('
|
|
191
|
-
expect(client.hasCapability('
|
|
190
|
+
expect(client.hasCapability('products:read')).toBe(true);
|
|
191
|
+
expect(client.hasCapability('factories:read')).toBe(true);
|
|
192
192
|
expect(client.hasCapability('backlog:read')).toBe(false);
|
|
193
193
|
expect(client.hasCapability('tenant:documents:write')).toBe(false);
|
|
194
194
|
});
|
|
@@ -196,7 +196,7 @@ describe('TaigaClient', () => {
|
|
|
196
196
|
it('should report no capabilities when key has empty scope', async () => {
|
|
197
197
|
const client = await clientWithCapabilities([]);
|
|
198
198
|
|
|
199
|
-
expect(client.hasCapability('
|
|
199
|
+
expect(client.hasCapability('products:read')).toBe(false);
|
|
200
200
|
});
|
|
201
201
|
});
|
|
202
202
|
|
package/src/client.ts
CHANGED
|
@@ -159,8 +159,8 @@ export class TaigaClient {
|
|
|
159
159
|
|
|
160
160
|
// Fetch full content for key documents (in parallel)
|
|
161
161
|
const keyProductDocTypes = ['specification', 'architecture', 'technology_decisions'];
|
|
162
|
-
// The factory instructions document
|
|
163
|
-
const keyFactoryDocTypes = ['
|
|
162
|
+
// The factory instructions document.
|
|
163
|
+
const keyFactoryDocTypes = ['factory_instructions'];
|
|
164
164
|
|
|
165
165
|
const docFetches: Promise<{ type: string; scope: string; data: unknown }>[] = [];
|
|
166
166
|
|
|
@@ -11,8 +11,8 @@ import { registerAllTools } from './tools/index.js';
|
|
|
11
11
|
|
|
12
12
|
/** All capabilities the MCP server understands. */
|
|
13
13
|
const ALL_CAPABILITIES = [
|
|
14
|
-
'
|
|
15
|
-
'
|
|
14
|
+
'products:read',
|
|
15
|
+
'factories:read',
|
|
16
16
|
'tenant:documents:read',
|
|
17
17
|
'tenant:documents:write',
|
|
18
18
|
'backlog:read',
|
|
@@ -147,7 +147,7 @@ describe('MCP server integration', () => {
|
|
|
147
147
|
});
|
|
148
148
|
|
|
149
149
|
it('should expose only factory tools for a teams:read-only key', async () => {
|
|
150
|
-
const { client } = await bootMcpPair({ capabilities: ['
|
|
150
|
+
const { client } = await bootMcpPair({ capabilities: ['factories:read'] });
|
|
151
151
|
|
|
152
152
|
const { tools } = await client.listTools();
|
|
153
153
|
const names = toolNames(tools);
|
|
@@ -183,7 +183,7 @@ describe('MCP server integration', () => {
|
|
|
183
183
|
|
|
184
184
|
it('should expose factory document write when both teams:read and documents:write are granted', async () => {
|
|
185
185
|
const { client } = await bootMcpPair({
|
|
186
|
-
capabilities: ['
|
|
186
|
+
capabilities: ['factories:read', 'tenant:documents:write'],
|
|
187
187
|
});
|
|
188
188
|
|
|
189
189
|
const { tools } = await client.listTools();
|
|
@@ -195,7 +195,7 @@ describe('MCP server integration', () => {
|
|
|
195
195
|
|
|
196
196
|
it('should combine capabilities additively', async () => {
|
|
197
197
|
const { client } = await bootMcpPair({
|
|
198
|
-
capabilities: ['
|
|
198
|
+
capabilities: ['factories:read', 'tenant:documents:read'],
|
|
199
199
|
});
|
|
200
200
|
|
|
201
201
|
const { tools } = await client.listTools();
|
|
@@ -231,8 +231,8 @@ describe('MCP server integration', () => {
|
|
|
231
231
|
|
|
232
232
|
describe('capability-tool contract', () => {
|
|
233
233
|
const CONTRACT: Record<string, string[]> = {
|
|
234
|
-
'
|
|
235
|
-
'
|
|
234
|
+
'products:read': ['taiga_list_products', 'taiga_list_product_documents', 'taiga_get_product_document'],
|
|
235
|
+
'factories:read': ['taiga_list_factories', 'taiga_list_factory_documents', 'taiga_get_factory_document'],
|
|
236
236
|
'tenant:documents:read': ['taiga_list_documents', 'taiga_get_document'],
|
|
237
237
|
'tenant:documents:write': ['taiga_update_document'],
|
|
238
238
|
};
|
package/src/tools/documents.ts
CHANGED
|
@@ -73,9 +73,8 @@ export function registerDocumentTools(server: McpServer, client: TaigaClient) {
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
// ── Factory instructions ────────────────────────────────────────────
|
|
76
|
-
// Capability strings still say teams until the database rename (PRO-1671).
|
|
77
76
|
|
|
78
|
-
if (client.hasCapability('
|
|
77
|
+
if (client.hasCapability('factories:read')) {
|
|
79
78
|
server.tool(
|
|
80
79
|
'taiga_list_factory_documents',
|
|
81
80
|
'List documents for a specific factory (names and types only). ' +
|
|
@@ -107,7 +106,7 @@ export function registerDocumentTools(server: McpServer, client: TaigaClient) {
|
|
|
107
106
|
.string()
|
|
108
107
|
.describe(
|
|
109
108
|
'Document type -- get available types from taiga_list_factory_documents. ' +
|
|
110
|
-
'The factory instructions document is `
|
|
109
|
+
'The factory instructions document is `factory_instructions`.'
|
|
111
110
|
),
|
|
112
111
|
},
|
|
113
112
|
{ readOnlyHint: true, openWorldHint: false },
|
|
@@ -121,7 +120,7 @@ export function registerDocumentTools(server: McpServer, client: TaigaClient) {
|
|
|
121
120
|
}
|
|
122
121
|
|
|
123
122
|
// Factory document write requires both teams:read (to find the factory) and tenant:documents:write
|
|
124
|
-
if (client.hasCapability('
|
|
123
|
+
if (client.hasCapability('factories:read') && client.hasCapability('tenant:documents:write')) {
|
|
125
124
|
server.tool(
|
|
126
125
|
'taiga_update_factory_document',
|
|
127
126
|
"Update the draft of a factory's instructions. " +
|
|
@@ -129,7 +128,7 @@ export function registerDocumentTools(server: McpServer, client: TaigaClient) {
|
|
|
129
128
|
'domain context, or tech preferences need updating while working on the codebase.',
|
|
130
129
|
{
|
|
131
130
|
factoryId: z.string().uuid().describe('Factory ID -- get from taiga_list_factories'),
|
|
132
|
-
type: z.string().describe('Document type (`
|
|
131
|
+
type: z.string().describe('Document type (`factory_instructions`)'),
|
|
133
132
|
data: z.record(z.string(), z.unknown()).describe('Document content to save as JSON'),
|
|
134
133
|
},
|
|
135
134
|
{ readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
package/src/tools/products.ts
CHANGED
|
@@ -3,9 +3,8 @@ import { z } from 'zod';
|
|
|
3
3
|
import type { TaigaClient } from '../client.js';
|
|
4
4
|
import { summarizeList } from './summarize.js';
|
|
5
5
|
|
|
6
|
-
// Capability strings still say teams/projects until the database rename (PRO-1671).
|
|
7
6
|
export function registerProductTools(server: McpServer, client: TaigaClient) {
|
|
8
|
-
if (client.hasCapability('
|
|
7
|
+
if (client.hasCapability('factories:read')) {
|
|
9
8
|
server.tool(
|
|
10
9
|
'taiga_list_factories',
|
|
11
10
|
'List all factories in the organization. A factory is the group that owns a set of products. ' +
|
|
@@ -25,7 +24,7 @@ export function registerProductTools(server: McpServer, client: TaigaClient) {
|
|
|
25
24
|
);
|
|
26
25
|
}
|
|
27
26
|
|
|
28
|
-
if (client.hasCapability('
|
|
27
|
+
if (client.hasCapability('products:read')) {
|
|
29
28
|
server.tool(
|
|
30
29
|
'taiga_list_products',
|
|
31
30
|
'List products in the organization. Use this to find the product you are working on ' +
|
|
@@ -85,8 +84,7 @@ export function registerProductTools(server: McpServer, client: TaigaClient) {
|
|
|
85
84
|
'or what technology choices were made (technology_decisions). ' +
|
|
86
85
|
'Types: specification, architecture, technology_decisions, data_flow, ' +
|
|
87
86
|
'privacy_assessment, threat_model, risk_register, user_flow, service_blueprint, ' +
|
|
88
|
-
|
|
89
|
-
'`project_instructions` (its stored type keeps the old name).',
|
|
87
|
+
"ui_direction, codebase_snapshot, and product_instructions (the product's standing instructions).",
|
|
90
88
|
{
|
|
91
89
|
productId: z.string().uuid().describe('Product ID -- get from taiga_list_products'),
|
|
92
90
|
type: z
|
|
Binary file
|
|
Binary file
|