@splicr/mcp-server 0.14.0 → 0.14.2

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/cli.js CHANGED
@@ -198,6 +198,9 @@ async function main() {
198
198
  case 'index':
199
199
  await runIndex();
200
200
  break;
201
+ case 'webhook':
202
+ await webhookCommand();
203
+ break;
201
204
  case 'dashboard':
202
205
  case 'open': {
203
206
  const url = 'https://splicr.dev/dashboard';
@@ -1145,6 +1148,59 @@ function printManualConfig() {
1145
1148
  console.error(' }');
1146
1149
  console.error(' }\n');
1147
1150
  }
1151
+ // ─── splicr webhook ───
1152
+ async function webhookCommand() {
1153
+ const { hasAuth } = await import('./auth.js');
1154
+ if (!hasAuth()) {
1155
+ console.error('Not authenticated. Run: splicr setup');
1156
+ process.exit(1);
1157
+ }
1158
+ if (subCommand !== 'setup') {
1159
+ console.error(`
1160
+ Splicr Webhooks
1161
+
1162
+ Commands:
1163
+ webhook setup Generate webhook secret + show GitHub config instructions
1164
+
1165
+ Auto-extracts patterns from PR review comments.
1166
+ When a reviewer says "use X, not Y", Splicr registers it as a team pattern.
1167
+ `);
1168
+ return;
1169
+ }
1170
+ const { listTeams, setupWebhook } = await import('./lib/api-client.js');
1171
+ // Get user's teams
1172
+ let teams;
1173
+ try {
1174
+ teams = await listTeams();
1175
+ }
1176
+ catch {
1177
+ console.error('Failed to load teams.');
1178
+ process.exit(1);
1179
+ }
1180
+ if (!teams || teams.length === 0) {
1181
+ console.error('You need a team first. Create one:');
1182
+ console.error(' splicr team create "My Team"');
1183
+ process.exit(1);
1184
+ }
1185
+ const team = teams[0];
1186
+ console.error(`Setting up GitHub webhook for team "${team.name}"...\n`);
1187
+ try {
1188
+ const result = await setupWebhook(team.id);
1189
+ console.error('Webhook secret generated. Configure GitHub:\n');
1190
+ console.error(` 1. Go to your repo -> Settings -> Webhooks -> Add webhook`);
1191
+ console.error(` 2. Payload URL: ${result.webhook_url}`);
1192
+ console.error(` 3. Content type: application/json`);
1193
+ console.error(` 4. Secret: ${result.webhook_secret}`);
1194
+ console.error(` 5. Events: Select "Pull request review comments"`);
1195
+ console.error(` 6. Click "Add webhook"\n`);
1196
+ console.error('Once configured, Splicr auto-extracts patterns from PR reviews.');
1197
+ console.error('Every agent on the team will follow these patterns.\n');
1198
+ }
1199
+ catch (err) {
1200
+ console.error(`Failed: ${err.message}`);
1201
+ process.exit(1);
1202
+ }
1203
+ }
1148
1204
  // ─── splicr index ───
1149
1205
  async function runIndex() {
1150
1206
  const target = process.argv[3];
@@ -1346,6 +1402,7 @@ function printHelp() {
1346
1402
  team join <code> Join a team by invite code
1347
1403
  index <path> Index local files into your knowledge base
1348
1404
  index <dir> Index all supported files in a directory
1405
+ webhook setup Set up GitHub PR webhook (auto-extract patterns from reviews)
1349
1406
  dashboard Open knowledge dashboard in browser
1350
1407
  uninstall Remove Splicr from all coding agents
1351
1408
 
package/dist/index.js CHANGED
@@ -17,6 +17,7 @@ import { getDecisionsSchema, handleGetDecisions } from './tools/get-decisions.js
17
17
  import { getTeamStatusSchema, handleGetTeamStatus } from './tools/get-team-status.js';
18
18
  import { reviewCodeSchema, handleReviewCode } from './tools/review-code.js';
19
19
  import { regenerateBriefSchema, handleRegenerateBrief } from './tools/regenerate-brief.js';
20
+ import { managePatternsSchema, handleManagePatterns } from './tools/manage-patterns.js';
20
21
  import { completeSession } from './lib/api-client.js';
21
22
  // Prevent unhandled errors from crashing the MCP server
22
23
  process.on('uncaughtException', (err) => {
@@ -72,6 +73,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
72
73
  getTeamStatusSchema,
73
74
  reviewCodeSchema,
74
75
  regenerateBriefSchema,
76
+ managePatternsSchema,
75
77
  ],
76
78
  }));
77
79
  // Handle tool calls with per-tool timeout
@@ -94,6 +96,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
94
96
  get_team_status: handleGetTeamStatus,
95
97
  review_code: handleReviewCode,
96
98
  regenerate_brief: handleRegenerateBrief,
99
+ manage_patterns: handleManagePatterns,
97
100
  }[name];
98
101
  if (!handler) {
99
102
  return { content: [{ type: 'text', text: `Unknown tool: ${name}` }], isError: true };
@@ -50,6 +50,21 @@ export declare function indexFile(params: {
50
50
  title: string;
51
51
  status: 'created' | 'updated';
52
52
  }>;
53
+ export declare function listPatterns(projectName: string, status?: string): Promise<any>;
54
+ export declare function deprecatePattern(params: {
55
+ project_name: string;
56
+ pattern_id?: string;
57
+ pattern_name?: string;
58
+ }): Promise<{
59
+ deprecated: boolean;
60
+ pattern_name: string;
61
+ pattern_id: string;
62
+ }>;
63
+ export declare function listTeams(): Promise<any[]>;
64
+ export declare function setupWebhook(teamId: string): Promise<{
65
+ webhook_secret: string;
66
+ webhook_url: string;
67
+ }>;
53
68
  export declare function resolveProject(params: {
54
69
  local_path?: string;
55
70
  git_remote_url?: string;
@@ -61,6 +61,19 @@ export async function saveFromAgent(params) {
61
61
  export async function indexFile(params) {
62
62
  return await apiRequest('POST', '/mcp/index', params);
63
63
  }
64
+ export async function listPatterns(projectName, status = 'all') {
65
+ return await apiRequest('GET', `/mcp/patterns?project_name=${encodeURIComponent(projectName)}&status=${status}`);
66
+ }
67
+ export async function deprecatePattern(params) {
68
+ return await apiRequest('POST', '/mcp/patterns/deprecate', params);
69
+ }
70
+ export async function listTeams() {
71
+ const data = await apiRequest('GET', '/teams');
72
+ return data ?? [];
73
+ }
74
+ export async function setupWebhook(teamId) {
75
+ return await apiRequest('POST', `/teams/${teamId}/webhook`, {});
76
+ }
64
77
  export async function resolveProject(params) {
65
78
  return await apiRequest('POST', '/mcp/resolve-project', params);
66
79
  }
@@ -0,0 +1,32 @@
1
+ export declare const managePatternsSchema: {
2
+ name: "manage_patterns";
3
+ description: string;
4
+ inputSchema: {
5
+ type: "object";
6
+ properties: {
7
+ action: {
8
+ type: "string";
9
+ enum: string[];
10
+ description: string;
11
+ };
12
+ pattern_name: {
13
+ type: "string";
14
+ description: string;
15
+ };
16
+ pattern_id: {
17
+ type: "string";
18
+ description: string;
19
+ };
20
+ status: {
21
+ type: "string";
22
+ enum: string[];
23
+ description: string;
24
+ };
25
+ project: {
26
+ type: "string";
27
+ description: string;
28
+ };
29
+ };
30
+ };
31
+ };
32
+ export declare function handleManagePatterns(args: Record<string, unknown>): Promise<string>;
@@ -0,0 +1,96 @@
1
+ import { detectProject } from '../lib/project-detector.js';
2
+ import { listPatterns, deprecatePattern } from '../lib/api-client.js';
3
+ import * as session from '../lib/session-state.js';
4
+ export const managePatternsSchema = {
5
+ name: 'manage_patterns',
6
+ description: `List or deprecate project patterns. Patterns are team conventions enforced across all agent sessions.
7
+
8
+ Use when:
9
+ - The user asks to see current patterns ("what patterns do we have?")
10
+ - A pattern is outdated or wrong and should be removed ("deprecate the X pattern")
11
+ - You want to check if a pattern exists before creating a new one
12
+ - After a refactor that invalidates existing patterns
13
+
14
+ Actions:
15
+ - "list": Show all active patterns for the current project (default)
16
+ - "deprecate": Mark a pattern as deprecated (it stops being enforced)`,
17
+ inputSchema: {
18
+ type: 'object',
19
+ properties: {
20
+ action: {
21
+ type: 'string',
22
+ enum: ['list', 'deprecate'],
23
+ description: 'Action: "list" to show patterns, "deprecate" to remove one',
24
+ },
25
+ pattern_name: {
26
+ type: 'string',
27
+ description: 'Pattern name to deprecate (for deprecate action)',
28
+ },
29
+ pattern_id: {
30
+ type: 'string',
31
+ description: 'Pattern ID to deprecate (alternative to pattern_name)',
32
+ },
33
+ status: {
34
+ type: 'string',
35
+ enum: ['active', 'deprecated', 'all'],
36
+ description: 'Filter by status (for list action). Default: active',
37
+ },
38
+ project: {
39
+ type: 'string',
40
+ description: 'Project name. Default: auto-detect from cwd',
41
+ },
42
+ },
43
+ },
44
+ };
45
+ export async function handleManagePatterns(args) {
46
+ const action = args.action || 'list';
47
+ const patternName = args.pattern_name;
48
+ const patternId = args.pattern_id;
49
+ const status = args.status || 'active';
50
+ const projectArg = args.project;
51
+ // Detect project
52
+ const detected = projectArg
53
+ ? { name: projectArg }
54
+ : await detectProject(process.cwd()).catch(() => null);
55
+ if (!detected) {
56
+ return 'Could not detect project. Specify a project name.';
57
+ }
58
+ session.recordToolCall();
59
+ if (action === 'deprecate') {
60
+ if (!patternName && !patternId) {
61
+ return 'Specify pattern_name or pattern_id to deprecate.';
62
+ }
63
+ try {
64
+ const result = await deprecatePattern({
65
+ project_name: detected.name,
66
+ pattern_id: patternId,
67
+ pattern_name: patternName,
68
+ });
69
+ return `*Pattern deprecated:* "${result.pattern_name}"\n\nThis pattern will no longer be enforced in agent sessions. The brief will update on next regeneration.`;
70
+ }
71
+ catch (err) {
72
+ return `Failed to deprecate pattern: ${err.message}`;
73
+ }
74
+ }
75
+ // Default: list
76
+ try {
77
+ const data = await listPatterns(detected.name, status);
78
+ const patterns = data.patterns || [];
79
+ if (patterns.length === 0) {
80
+ return `No ${status === 'all' ? '' : status + ' '}patterns for ${detected.name}.`;
81
+ }
82
+ const lines = patterns.map((p, i) => {
83
+ const statusBadge = p.status === 'deprecated' ? ' [DEPRECATED]' : '';
84
+ const source = p.established_by === 'pr_review' ? ' (from PR review)'
85
+ : p.established_by === 'bootstrap' ? ' (from bootstrap)'
86
+ : p.established_by === 'agent_auto' ? ' (auto-extracted)'
87
+ : p.established_by === 'agent_manual' ? ' (agent-registered)'
88
+ : '';
89
+ return `${i + 1}. **${p.name}**${statusBadge}${source}\n ${p.description}\n _id: ${p.id}_`;
90
+ });
91
+ return `**Patterns for ${detected.name}** (${status}):\n\n${lines.join('\n\n')}`;
92
+ }
93
+ catch (err) {
94
+ return `Failed to list patterns: ${err.message}`;
95
+ }
96
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@splicr/mcp-server",
3
- "version": "0.14.0",
3
+ "version": "0.14.2",
4
4
  "description": "Splicr MCP server — route what you read to what you're building",
5
5
  "type": "module",
6
6
  "bin": "./dist/cli.js",