@taiga-ai/mcp-server 0.0.1-rc.70 → 0.0.1-rc.71

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/client.d.ts CHANGED
@@ -40,29 +40,6 @@ export declare class TaigaClient {
40
40
  }): Promise<{
41
41
  data: unknown;
42
42
  }>;
43
- listTeamDocuments(teamId: string, opts?: {
44
- limit?: number;
45
- }): Promise<{
46
- data: unknown[];
47
- nextCursor: string | null;
48
- }>;
49
- getTeamDocument(teamId: string, type: string): Promise<{
50
- data: unknown;
51
- }>;
52
- saveTeamDocumentDraft(teamId: string, type: string, body: {
53
- data: Record<string, unknown>;
54
- }): Promise<{
55
- data: unknown;
56
- }>;
57
- listProjectDocuments(projectId: string, opts?: {
58
- limit?: number;
59
- }): Promise<{
60
- data: unknown[];
61
- nextCursor: string | null;
62
- }>;
63
- getProjectDocument(projectId: string, type: string): Promise<{
64
- data: unknown;
65
- }>;
66
43
  listSkills(opts?: {
67
44
  scope?: string;
68
45
  scopeId?: string;
@@ -74,19 +51,6 @@ export declare class TaigaClient {
74
51
  getSkill(id: string): Promise<{
75
52
  data: unknown;
76
53
  }>;
77
- updateSkill(id: string, body: {
78
- name?: string;
79
- description?: string;
80
- content?: string;
81
- }): Promise<{
82
- data: unknown;
83
- }>;
84
- /**
85
- * Fetch the full context for a project in parallel: project metadata,
86
- * team documents, project documents, and team skills.
87
- * Returns a structured bundle optimized for coding agent consumption.
88
- */
89
- getProjectContext(projectId: string): Promise<Record<string, unknown>>;
90
54
  listBacklogItems(projectId: string, opts?: {
91
55
  limit?: number;
92
56
  }): Promise<{
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAExD;;;GAGG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,aAAa,CAAoC;gBAE7C,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE;YAKvC,OAAO;IA0Bf,UAAU,IAAI,OAAO,CAAC,mBAAmB,CAAC;IAMhD,gBAAgB,IAAI,mBAAmB,GAAG,IAAI;IAI9C,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IASpC,YAAY,CAAC,IAAI,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;cAMhD,OAAO,EAAE;oBAAc,MAAM,GAAG,IAAI;;IAG5D,UAAU,CAAC,EAAE,EAAE,MAAM;cACG,OAAO;;IAO/B,mBAAmB,CAAC,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;cAIrB,OAAO,EAAE;oBAAc,MAAM,GAAG,IAAI;;IAM5D,iBAAiB,CAAC,IAAI,EAAE,MAAM;cACN,OAAO;;IAG/B,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE;cACrD,OAAO;;IAO/B,UAAU,CAAC,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;cAM9C,OAAO,EAAE;oBAAc,MAAM,GAAG,IAAI;;IAG5D,QAAQ,CAAC,EAAE,EAAE,MAAM;cACK,OAAO;;IAO/B,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;cAIrC,OAAO,EAAE;oBAAc,MAAM,GAAG,IAAI;;IAM5D,cAAc,CAAC,MAAM,EAAE,MAAM;cACL,OAAO;;IAO/B,gBAAgB,CAAC,SAAS,EAAE,MAAM;cACV,OAAO,EAAE;;IAOjC,SAAS,CAAC,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;cAIX,OAAO,EAAE;oBAAc,MAAM,GAAG,IAAI;;CAEnE"}
package/dist/client.js CHANGED
@@ -78,35 +78,6 @@ export class TaigaClient {
78
78
  return this.request('PUT', `/tenants/current/documents/${type}/draft`, body);
79
79
  }
80
80
  // ---------------------------------------------------------------------------
81
- // Team Documents
82
- // ---------------------------------------------------------------------------
83
- async listTeamDocuments(teamId, opts) {
84
- const params = new URLSearchParams();
85
- if (opts?.limit)
86
- params.set('limit', String(opts.limit));
87
- const qs = params.toString();
88
- return this.request('GET', `/teams/${teamId}/documents${qs ? `?${qs}` : ''}`);
89
- }
90
- async getTeamDocument(teamId, type) {
91
- return this.request('GET', `/teams/${teamId}/documents/${type}`);
92
- }
93
- async saveTeamDocumentDraft(teamId, type, body) {
94
- return this.request('PUT', `/teams/${teamId}/documents/${type}/draft`, body);
95
- }
96
- // ---------------------------------------------------------------------------
97
- // Project Documents
98
- // ---------------------------------------------------------------------------
99
- async listProjectDocuments(projectId, opts) {
100
- const params = new URLSearchParams();
101
- if (opts?.limit)
102
- params.set('limit', String(opts.limit));
103
- const qs = params.toString();
104
- return this.request('GET', `/projects/${projectId}/documents${qs ? `?${qs}` : ''}`);
105
- }
106
- async getProjectDocument(projectId, type) {
107
- return this.request('GET', `/projects/${projectId}/documents/${type}`);
108
- }
109
- // ---------------------------------------------------------------------------
110
81
  // Skills (agent skills)
111
82
  // ---------------------------------------------------------------------------
112
83
  async listSkills(opts) {
@@ -123,79 +94,6 @@ export class TaigaClient {
123
94
  async getSkill(id) {
124
95
  return this.request('GET', `/agent-skills/${id}`);
125
96
  }
126
- async updateSkill(id, body) {
127
- return this.request('PATCH', `/agent-skills/${id}`, body);
128
- }
129
- // ---------------------------------------------------------------------------
130
- // Team Documents
131
- // ---------------------------------------------------------------------------
132
- /**
133
- * Fetch the full context for a project in parallel: project metadata,
134
- * team documents, project documents, and team skills.
135
- * Returns a structured bundle optimized for coding agent consumption.
136
- */
137
- async getProjectContext(projectId) {
138
- // First fetch the project to get the teamId
139
- const project = await this.request('GET', `/projects/${projectId}`);
140
- const teamId = project.data.teamId;
141
- // Fetch everything in parallel
142
- const [projectDocs, teamDocs, skills] = await Promise.all([
143
- this.listProjectDocuments(projectId, { limit: 20 }).catch(() => ({ data: [] })),
144
- teamId
145
- ? this.listTeamDocuments(teamId, { limit: 20 }).catch(() => ({ data: [] }))
146
- : Promise.resolve({ data: [] }),
147
- this.listSkills({ limit: 100 }).catch(() => ({ data: [] })),
148
- ]);
149
- // Fetch full content for key documents (in parallel)
150
- const keyProjectDocTypes = ['specification', 'architecture', 'technology_decisions'];
151
- const keyTeamDocTypes = ['domain_context', 'tech_preferences', 'architecture_constraints'];
152
- const docFetches = [];
153
- for (const docType of keyProjectDocTypes) {
154
- const exists = projectDocs.data.some((d) => d.type === docType && !d.isDraft);
155
- if (exists) {
156
- docFetches.push(this.getProjectDocument(projectId, docType)
157
- .then((r) => ({ type: docType, scope: 'project', data: r.data }))
158
- .catch(() => ({ type: docType, scope: 'project', data: null })));
159
- }
160
- }
161
- if (teamId) {
162
- for (const docType of keyTeamDocTypes) {
163
- const exists = teamDocs.data.some((d) => d.type === docType && !d.isDraft);
164
- if (exists) {
165
- docFetches.push(this.getTeamDocument(teamId, docType)
166
- .then((r) => ({ type: docType, scope: 'team', data: r.data }))
167
- .catch(() => ({ type: docType, scope: 'team', data: null })));
168
- }
169
- }
170
- }
171
- const fetchedDocs = await Promise.all(docFetches);
172
- // Assemble context bundle
173
- const context = {
174
- project: project.data,
175
- documents: {
176
- project: Object.fromEntries(fetchedDocs.filter((d) => d.scope === 'project' && d.data).map((d) => [d.type, d.data])),
177
- team: Object.fromEntries(fetchedDocs.filter((d) => d.scope === 'team' && d.data).map((d) => [d.type, d.data])),
178
- },
179
- available_documents: {
180
- project: projectDocs.data.map((d) => ({
181
- type: d.type,
182
- name: d.name,
183
- version: d.version,
184
- isDraft: d.isDraft,
185
- })),
186
- team: teamDocs.data.map((d) => ({
187
- type: d.type,
188
- name: d.name,
189
- version: d.version,
190
- isDraft: d.isDraft,
191
- })),
192
- },
193
- skills_summary: skills.data
194
- .filter((s) => !s.teamId || s.teamId === teamId)
195
- .map((s) => ({ id: s.id, name: s.name, slug: s.slug, description: s.description })),
196
- };
197
- return context;
198
- }
199
97
  // ---------------------------------------------------------------------------
200
98
  // Backlog
201
99
  // ---------------------------------------------------------------------------
package/dist/index.js CHANGED
@@ -20,39 +20,10 @@ async function main() {
20
20
  console.error('Check that your TAIGA_API_KEY is valid and not expired.');
21
21
  process.exit(1);
22
22
  });
23
- // Create MCP server with dynamic tool registration and instructions
23
+ // Create MCP server with dynamic tool registration
24
24
  const server = new McpServer({
25
25
  name: 'taiga',
26
26
  version: '0.0.1',
27
- }, {
28
- instructions: [
29
- 'You have access to Taiga -- a policy-aware software development platform.',
30
- "Taiga provides your organization's engineering knowledge base:",
31
- '',
32
- '**Company policies** (taiga_list_documents / taiga_get_document):',
33
- 'Security, data privacy, SDLC, architecture, and compliance rules that apply to all code.',
34
- 'Read the relevant policy BEFORE writing code that touches security, data handling, or infrastructure.',
35
- '',
36
- '**Team documents** (taiga_list_team_documents / taiga_get_team_document):',
37
- 'Domain context (ubiquitous language, entities, business rules), tech preferences (approved stack, patterns),',
38
- 'architecture constraints, and SLO targets. Read these to understand HOW this team builds software.',
39
- '',
40
- '**Project documents** (taiga_list_project_documents / taiga_get_project_document):',
41
- "Specification (what to build), architecture (how it's designed), threat model (security risks),",
42
- 'technology decisions, and data flow. Read these to understand the specific project context.',
43
- '',
44
- '**Skills** (taiga_list_skills / taiga_get_skill):',
45
- 'Coding standards, security requirements, API design patterns, testing strategy, database patterns,',
46
- 'and more. These are detailed engineering guidelines with code examples.',
47
- '',
48
- "Workflow: Start with taiga_list_teams to find your team, then read the team's domain_context",
49
- 'and tech_preferences. For project-specific work, use taiga_list_projects to find the project,',
50
- 'then read its specification and architecture. Use skills for coding patterns.',
51
- '',
52
- 'When you notice a policy, skill, or team document is outdated, use the update tools',
53
- '(taiga_update_document, taiga_update_skill, taiga_update_team_document) to fix it.',
54
- 'This keeps the knowledge base accurate for everyone.',
55
- ].join('\n'),
56
27
  });
57
28
  registerAllTools(server, client);
58
29
  const toolCount = introspection.capabilities.length;
@@ -17,19 +17,17 @@ const ALL_CAPABILITIES = [
17
17
  ];
18
18
  /** All tool names the server can register (sorted for deterministic comparison). */
19
19
  const ALL_TOOL_NAMES = [
20
+ 'taiga_get_backlog_item',
20
21
  'taiga_get_document',
21
- 'taiga_get_project_document',
22
+ 'taiga_get_project',
22
23
  'taiga_get_skill',
23
- 'taiga_get_team_document',
24
+ 'taiga_list_backlog_items',
24
25
  'taiga_list_documents',
25
- 'taiga_list_project_documents',
26
+ 'taiga_list_environments',
26
27
  'taiga_list_projects',
27
28
  'taiga_list_skills',
28
- 'taiga_list_team_documents',
29
29
  'taiga_list_teams',
30
30
  'taiga_update_document',
31
- 'taiga_update_skill',
32
- 'taiga_update_team_document',
33
31
  ];
34
32
  function makeFetchRouter(routes) {
35
33
  return vi.fn().mockImplementation(async (url, init) => {
@@ -112,7 +110,7 @@ describe('MCP server integration', () => {
112
110
  // the API key's capabilities. This is the primary security contract.
113
111
  // -------------------------------------------------------------------------
114
112
  describe('tool discovery', () => {
115
- it('should expose all 13 tools when API key has full capabilities', async () => {
113
+ it('should expose all 11 tools when API key has full capabilities', async () => {
116
114
  const { client } = await bootMcpPair({ capabilities: ALL_CAPABILITIES });
117
115
  const { tools } = await client.listTools();
118
116
  expect(toolNames(tools)).toEqual(ALL_TOOL_NAMES);
@@ -124,15 +122,15 @@ describe('MCP server integration', () => {
124
122
  const { client } = await bootMcpPair({ capabilities: [] });
125
123
  await expect(client.listTools()).rejects.toThrow(/Method not found/);
126
124
  });
127
- it('should expose only team tools for a teams:read-only key', async () => {
128
- const { client } = await bootMcpPair({ capabilities: ['teams:read'] });
125
+ it('should expose only project tools for a projects:read-only key', async () => {
126
+ const { client } = await bootMcpPair({ capabilities: ['projects:read'] });
129
127
  const { tools } = await client.listTools();
130
128
  const names = toolNames(tools);
131
- expect(names).toContain('taiga_list_teams');
132
- expect(names).toContain('taiga_list_team_documents');
133
- expect(names).toContain('taiga_get_team_document');
129
+ expect(names).toContain('taiga_list_projects');
130
+ expect(names).toContain('taiga_get_project');
131
+ expect(names).toContain('taiga_list_environments');
132
+ expect(names).not.toContain('taiga_list_teams');
134
133
  expect(names).not.toContain('taiga_list_documents');
135
- expect(names).not.toContain('taiga_update_document');
136
134
  });
137
135
  it('should expose read-only document tools without write capability', async () => {
138
136
  const { client } = await bootMcpPair({ capabilities: ['tenant:documents:read'] });
@@ -144,39 +142,35 @@ describe('MCP server integration', () => {
144
142
  expect(names).toContain('taiga_get_skill');
145
143
  expect(names).not.toContain('taiga_update_document');
146
144
  });
147
- it('should expose write tools when documents:write is also granted', async () => {
145
+ it('should expose update tool when documents:write is also granted', async () => {
148
146
  const { client } = await bootMcpPair({
149
147
  capabilities: ['tenant:documents:read', 'tenant:documents:write'],
150
148
  });
151
149
  const { tools } = await client.listTools();
152
- const names = toolNames(tools);
153
- expect(names).toContain('taiga_update_document');
154
- expect(names).toContain('taiga_update_skill');
150
+ expect(toolNames(tools)).toContain('taiga_update_document');
155
151
  });
156
- it('should expose team document write when both teams:read and documents:write are granted', async () => {
157
- const { client } = await bootMcpPair({
158
- capabilities: ['teams:read', 'tenant:documents:write'],
159
- });
152
+ it('should expose backlog tools only with backlog:read', async () => {
153
+ const { client } = await bootMcpPair({ capabilities: ['backlog:read'] });
160
154
  const { tools } = await client.listTools();
161
155
  const names = toolNames(tools);
162
- expect(names).toContain('taiga_update_team_document');
163
- expect(names).toContain('taiga_list_teams');
156
+ expect(names).toContain('taiga_list_backlog_items');
157
+ expect(names).toContain('taiga_get_backlog_item');
158
+ expect(names).not.toContain('taiga_list_environments');
164
159
  });
165
160
  it('should combine capabilities additively', async () => {
166
161
  const { client } = await bootMcpPair({
167
- capabilities: ['teams:read', 'tenant:documents:read'],
162
+ capabilities: ['projects:read', 'backlog:read'],
168
163
  });
169
164
  const { tools } = await client.listTools();
170
165
  const names = toolNames(tools);
171
- // teams:read tools present
172
- expect(names).toContain('taiga_list_teams');
173
- expect(names).toContain('taiga_list_team_documents');
174
- // tenant:documents:read tools present
175
- expect(names).toContain('taiga_list_documents');
176
- expect(names).toContain('taiga_list_skills');
177
- // write tools absent
178
- expect(names).not.toContain('taiga_update_document');
179
- expect(names).not.toContain('taiga_update_skill');
166
+ // projects:read tools present
167
+ expect(names).toContain('taiga_list_projects');
168
+ expect(names).toContain('taiga_list_environments');
169
+ // backlog:read tools present
170
+ expect(names).toContain('taiga_list_backlog_items');
171
+ // unrelated capabilities absent
172
+ expect(names).not.toContain('taiga_list_documents');
173
+ expect(names).not.toContain('taiga_list_teams');
180
174
  });
181
175
  it('should provide a description and input schema for every tool', async () => {
182
176
  const { client } = await bootMcpPair({ capabilities: ALL_CAPABILITIES });
@@ -195,10 +189,11 @@ describe('MCP server integration', () => {
195
189
  // -------------------------------------------------------------------------
196
190
  describe('capability-tool contract', () => {
197
191
  const CONTRACT = {
198
- 'projects:read': ['taiga_list_projects', 'taiga_list_project_documents', 'taiga_get_project_document'],
199
- 'teams:read': ['taiga_list_teams', 'taiga_list_team_documents', 'taiga_get_team_document'],
192
+ 'projects:read': ['taiga_list_projects', 'taiga_get_project', 'taiga_list_environments'],
193
+ 'teams:read': ['taiga_list_teams'],
200
194
  'tenant:documents:read': ['taiga_list_documents', 'taiga_get_document', 'taiga_list_skills', 'taiga_get_skill'],
201
- 'tenant:documents:write': ['taiga_update_document', 'taiga_update_skill'],
195
+ 'tenant:documents:write': ['taiga_update_document'],
196
+ 'backlog:read': ['taiga_list_backlog_items', 'taiga_get_backlog_item'],
202
197
  };
203
198
  for (const [capability, expectedTools] of Object.entries(CONTRACT)) {
204
199
  it(`${capability} should gate: ${expectedTools.join(', ')}`, async () => {
@@ -217,33 +212,31 @@ describe('MCP server integration', () => {
217
212
  // -> (mocked) API -> response back through MCP protocol.
218
213
  // -------------------------------------------------------------------------
219
214
  describe('tool execution', () => {
220
- it('should return team list as JSON text content', async () => {
221
- const teams = [
222
- { id: 't1', name: 'Product' },
223
- { id: 't2', name: 'Platform' },
215
+ it('should return project list as JSON text content', async () => {
216
+ const projects = [
217
+ { id: 'p1', name: 'Alpha' },
218
+ { id: 'p2', name: 'Beta' },
224
219
  ];
225
220
  const { client } = await bootMcpPair({
226
221
  capabilities: ALL_CAPABILITIES,
227
- routes: { 'GET /v1/teams': { data: teams, nextCursor: null } },
222
+ routes: { 'GET /v1/projects': { data: projects, nextCursor: null } },
228
223
  });
229
- const result = await client.callTool({ name: 'taiga_list_teams', arguments: {} });
224
+ const result = await client.callTool({ name: 'taiga_list_projects', arguments: {} });
230
225
  expect(result.content).toHaveLength(1);
231
- expect(parseToolResult(result)).toEqual(teams);
226
+ expect(parseToolResult(result)).toEqual(projects);
232
227
  });
233
- it('should send PATCH when updating a skill', async () => {
234
- const skillId = '00000000-0000-0000-0000-000000000001';
235
- const updated = { id: skillId, name: 'Updated Skill', content: '# Updated' };
236
- const { client, fetchSpy } = await bootMcpPair({
228
+ it('should pass project id through to the API', async () => {
229
+ const projectId = '00000000-0000-0000-0000-000000000001';
230
+ const project = { id: projectId, name: 'Alpha', status: 'active' };
231
+ const { client } = await bootMcpPair({
237
232
  capabilities: ALL_CAPABILITIES,
238
- routes: { [`PATCH /v1/agent-skills/${skillId}`]: { data: updated } },
233
+ routes: { [`GET /v1/projects/${projectId}`]: { data: project } },
239
234
  });
240
- await client.callTool({
241
- name: 'taiga_update_skill',
242
- arguments: { id: skillId, content: '# Updated' },
235
+ const result = await client.callTool({
236
+ name: 'taiga_get_project',
237
+ arguments: { id: projectId },
243
238
  });
244
- const patchCall = fetchSpy.mock.calls.find((c) => c[1]?.method === 'PATCH');
245
- expect(patchCall).toBeDefined();
246
- expect(patchCall[0]).toContain(`/v1/agent-skills/${skillId}`);
239
+ expect(parseToolResult(result)).toEqual(project);
247
240
  });
248
241
  it('should return document content through MCP protocol', async () => {
249
242
  const docs = [{ id: 'd1', title: 'Security Policy' }];
@@ -269,21 +262,18 @@ describe('MCP server integration', () => {
269
262
  expect(putCall).toBeDefined();
270
263
  expect(putCall[0]).toContain(`/v1/tenants/current/documents/${docType}/draft`);
271
264
  });
272
- it('should send PUT when updating a team document draft', async () => {
273
- const teamId = '00000000-0000-0000-0000-000000000001';
274
- const docType = 'domain_context';
275
- const updated = { type: docType, name: 'Domain Context' };
276
- const { client, fetchSpy } = await bootMcpPair({
265
+ it('should pass projectId to backlog items endpoint', async () => {
266
+ const projectId = '00000000-0000-0000-0000-000000000001';
267
+ const items = [{ id: 'bi1', name: 'Feature X' }];
268
+ const { client } = await bootMcpPair({
277
269
  capabilities: ALL_CAPABILITIES,
278
- routes: { [`PUT /v1/teams/${teamId}/documents/${docType}/draft`]: { data: updated } },
270
+ routes: { [`GET /v1/projects/${projectId}/backlog`]: { data: items, nextCursor: null } },
279
271
  });
280
- await client.callTool({
281
- name: 'taiga_update_team_document',
282
- arguments: { teamId, type: docType, data: { sections: { overview: 'Updated' } } },
272
+ const result = await client.callTool({
273
+ name: 'taiga_list_backlog_items',
274
+ arguments: { projectId },
283
275
  });
284
- const putCall = fetchSpy.mock.calls.find((c) => c[1]?.method === 'PUT' && c[0].includes('/teams/'));
285
- expect(putCall).toBeDefined();
286
- expect(putCall[0]).toContain(`/v1/teams/${teamId}/documents/${docType}/draft`);
276
+ expect(parseToolResult(result)).toEqual(items);
287
277
  });
288
278
  it('should return skills through MCP protocol', async () => {
289
279
  const skills = [{ id: 's1', name: 'React Standards' }];
@@ -1,4 +1,4 @@
1
1
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  import type { TaigaClient } from '../client.js';
3
- export declare function registerBacklogTools(_server: McpServer, _client: TaigaClient): void;
3
+ export declare function registerBacklogTools(server: McpServer, client: TaigaClient): void;
4
4
  //# sourceMappingURL=backlogs.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"backlogs.d.ts","sourceRoot":"","sources":["../../src/tools/backlogs.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,QAG5E"}
1
+ {"version":3,"file":"backlogs.d.ts","sourceRoot":"","sources":["../../src/tools/backlogs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,QAgD1E"}
@@ -1,6 +1,33 @@
1
- // Backlog tools are reserved for post-beta.
2
- // The beta MCP server focuses on the knowledge base (policies, skills, team docs).
3
- export function registerBacklogTools(_server, _client) {
4
- // No backlog tools in beta -- will be added when the MCP server
5
- // expands beyond the knowledge base use case.
1
+ import { z } from 'zod';
2
+ export function registerBacklogTools(server, client) {
3
+ if (client.hasCapability('backlog:read')) {
4
+ server.tool('taiga_list_backlog_items', 'List backlog items (features, tasks, stories) for a project. ' +
5
+ 'Returns item name, status, planning status, and priority.', {
6
+ projectId: z.string().uuid().describe('Project ID'),
7
+ limit: z.number().min(1).max(100).optional().describe('Max results (default 25)'),
8
+ }, async (params) => {
9
+ const result = await client.listBacklogItems(params.projectId, { limit: params.limit });
10
+ return {
11
+ content: [{ type: 'text', text: JSON.stringify(result.data, null, 2) }],
12
+ };
13
+ });
14
+ server.tool('taiga_get_backlog_item', 'Get detailed information about a specific backlog item, including its active plan and steps.', {
15
+ itemId: z.string().uuid().describe('Backlog item ID'),
16
+ }, async (params) => {
17
+ const result = await client.getBacklogItem(params.itemId);
18
+ return {
19
+ content: [{ type: 'text', text: JSON.stringify(result.data, null, 2) }],
20
+ };
21
+ });
22
+ }
23
+ if (client.hasCapability('projects:read')) {
24
+ server.tool('taiga_list_environments', 'List deployment environments (dev, staging, production) for a project.', {
25
+ projectId: z.string().uuid().describe('Project ID'),
26
+ }, async (params) => {
27
+ const result = await client.listEnvironments(params.projectId);
28
+ return {
29
+ content: [{ type: 'text', text: JSON.stringify(result.data, null, 2) }],
30
+ };
31
+ });
32
+ }
6
33
  }
@@ -1 +1 @@
1
- {"version":3,"file":"documents.d.ts","sourceRoot":"","sources":["../../src/tools/documents.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,QA6I3E"}
1
+ {"version":3,"file":"documents.d.ts","sourceRoot":"","sources":["../../src/tools/documents.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,QAkD3E"}
@@ -1,30 +1,19 @@
1
1
  import { z } from 'zod';
2
- import { summarizeList } from './summarize.js';
3
2
  export function registerDocumentTools(server, client) {
4
- // ── Company-level policy documents ──────────────────────────────────
5
3
  if (client.hasCapability('tenant:documents:read')) {
6
- server.tool('taiga_list_documents', 'List company-level policy documents (names and types only). ' +
7
- 'Policies define security, data privacy, SDLC, operations, architecture, and compliance rules ' +
8
- 'that apply across all teams and projects. ' +
9
- 'Use taiga_get_document with the document type to read the full policy content.', {
4
+ server.tool('taiga_list_documents', 'List company-level policy documents (security, data privacy, SDLC, etc). ' +
5
+ 'These are the governance documents that guide all projects.', {
10
6
  limit: z.number().min(1).max(100).optional().describe('Max results (default 25)'),
11
- }, { readOnlyHint: true, openWorldHint: false }, async (params) => {
7
+ }, async (params) => {
12
8
  const result = await client.listTenantDocuments(params);
13
- const summaries = summarizeList(result.data);
14
9
  return {
15
- content: [{ type: 'text', text: JSON.stringify(summaries, null, 2) }],
10
+ content: [{ type: 'text', text: JSON.stringify(result.data, null, 2) }],
16
11
  };
17
12
  });
18
- server.tool('taiga_get_document', 'Get the full content of a company policy document. ' +
19
- 'Use this when you need to understand security requirements, data handling rules, ' +
20
- 'architecture standards, or compliance controls that apply to your code. ' +
21
- 'Types: policy_security, policy_data, policy_sdlc, policy_ops, policy_tic, ' +
22
- 'policy_risk, policy_architecture, policy_supply_chain, policy_acceptable_use, policy_ip.', {
23
- type: z
24
- .string()
25
- .describe('Document type -- get available types from taiga_list_documents. ' +
26
- 'Common: policy_security, policy_data, policy_sdlc, policy_architecture'),
27
- }, { readOnlyHint: true, openWorldHint: false }, async (params) => {
13
+ server.tool('taiga_get_document', 'Get the full content of a specific policy document by type. ' +
14
+ 'Common types: security_policy, data_privacy_policy, sdlc_policy, architecture_standards.', {
15
+ type: z.string().describe('Document type (e.g. security_policy, sdlc_policy)'),
16
+ }, async (params) => {
28
17
  const result = await client.getTenantDocument(params.type);
29
18
  return {
30
19
  content: [{ type: 'text', text: JSON.stringify(result.data, null, 2) }],
@@ -32,12 +21,10 @@ export function registerDocumentTools(server, client) {
32
21
  });
33
22
  }
34
23
  if (client.hasCapability('tenant:documents:write')) {
35
- server.tool('taiga_update_document', 'Update the draft content of a company policy document. ' +
36
- 'Only updates the draft -- does not publish. Use this when you notice a policy ' +
37
- 'is outdated or needs corrections while reviewing code.', {
38
- type: z.string().describe('Document type (e.g. policy_security, policy_sdlc)'),
39
- data: z.record(z.string(), z.unknown()).describe('Document content to save as JSON'),
40
- }, { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false }, async (params) => {
24
+ server.tool('taiga_update_document', 'Update the draft content of a policy document. Only updates the draft -- does not publish.', {
25
+ type: z.string().describe('Document type (e.g. security_policy)'),
26
+ data: z.record(z.string(), z.unknown()).describe('Document content to save'),
27
+ }, async (params) => {
41
28
  const { type, ...body } = params;
42
29
  const result = await client.saveTenantDocumentDraft(type, body);
43
30
  return {
@@ -45,53 +32,4 @@ export function registerDocumentTools(server, client) {
45
32
  };
46
33
  });
47
34
  }
48
- // ── Team documents ──────────────────────────────────────────────────
49
- if (client.hasCapability('teams:read')) {
50
- server.tool('taiga_list_team_documents', 'List documents for a specific team (names and types only). ' +
51
- 'Team documents define how the team works: domain context (entities, ubiquitous language), ' +
52
- 'tech preferences (approved stack), architecture constraints, SLO targets, ' +
53
- 'definition of done, and team charter. ' +
54
- 'Use taiga_get_team_document to read the full content.', {
55
- teamId: z.string().uuid().describe('Team ID -- get from taiga_list_teams'),
56
- limit: z.number().min(1).max(100).optional().describe('Max results (default 25)'),
57
- }, { readOnlyHint: true, openWorldHint: false }, async (params) => {
58
- const result = await client.listTeamDocuments(params.teamId, { limit: params.limit });
59
- const summaries = summarizeList(result.data);
60
- return {
61
- content: [{ type: 'text', text: JSON.stringify(summaries, null, 2) }],
62
- };
63
- });
64
- server.tool('taiga_get_team_document', 'Get the full content of a specific team document. ' +
65
- 'Use this when you need the team domain context (ubiquitous language, entities, business rules), ' +
66
- 'tech preferences (approved stack, patterns), or architecture constraints. ' +
67
- 'Types: team_charter, definition_of_done, tech_preferences, ' +
68
- 'architecture_constraints, slo_targets, domain_context.', {
69
- teamId: z.string().uuid().describe('Team ID -- get from taiga_list_teams'),
70
- type: z
71
- .string()
72
- .describe('Document type -- get available types from taiga_list_team_documents. ' +
73
- 'Most useful: domain_context (entities, language), tech_preferences (stack, patterns)'),
74
- }, { readOnlyHint: true, openWorldHint: false }, async (params) => {
75
- const result = await client.getTeamDocument(params.teamId, params.type);
76
- return {
77
- content: [{ type: 'text', text: JSON.stringify(result.data, null, 2) }],
78
- };
79
- });
80
- }
81
- // Team document write requires both teams:read (to find the team) and tenant:documents:write
82
- if (client.hasCapability('teams:read') && client.hasCapability('tenant:documents:write')) {
83
- server.tool('taiga_update_team_document', 'Update the draft content of a team document. ' +
84
- 'Only updates the draft -- does not publish. Use this when you notice team conventions, ' +
85
- 'domain context, or tech preferences need updating while working on the codebase.', {
86
- teamId: z.string().uuid().describe('Team ID -- get from taiga_list_teams'),
87
- type: z.string().describe('Document type (e.g. domain_context, tech_preferences)'),
88
- data: z.record(z.string(), z.unknown()).describe('Document content to save as JSON'),
89
- }, { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false }, async (params) => {
90
- const { teamId, type, ...body } = params;
91
- const result = await client.saveTeamDocumentDraft(teamId, type, body);
92
- return {
93
- content: [{ type: 'text', text: JSON.stringify(result.data, null, 2) }],
94
- };
95
- });
96
- }
97
35
  }
@@ -4,9 +4,6 @@ import type { TaigaClient } from '../client.js';
4
4
  * Register all MCP tools based on the API key's effective capabilities.
5
5
  * Only tools matching the key's scopes are registered -- the AI assistant
6
6
  * never sees tools it can't use.
7
- *
8
- * Beta scope: Knowledge base access (policies, skills, team documents).
9
- * Post-beta: Projects, backlog items, environments, composite tools.
10
7
  */
11
8
  export declare function registerAllTools(server: McpServer, client: TaigaClient): void;
12
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAMhD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI,CAK7E"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAMhD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI,CAK7E"}
@@ -6,9 +6,6 @@ import { registerSkillTools } from './skills.js';
6
6
  * Register all MCP tools based on the API key's effective capabilities.
7
7
  * Only tools matching the key's scopes are registered -- the AI assistant
8
8
  * never sees tools it can't use.
9
- *
10
- * Beta scope: Knowledge base access (policies, skills, team documents).
11
- * Post-beta: Projects, backlog items, environments, composite tools.
12
9
  */
13
10
  export function registerAllTools(server, client) {
14
11
  registerProjectTools(server, client);
@@ -1 +1 @@
1
- {"version":3,"file":"projects.d.ts","sourceRoot":"","sources":["../../src/tools/projects.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,QA0F1E"}
1
+ {"version":3,"file":"projects.d.ts","sourceRoot":"","sources":["../../src/tools/projects.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,QAgD1E"}