samuraizer 0.1.0 → 0.2.0
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/CHANGELOG.md +14 -0
- package/dist/cli/index.js +13 -0
- package/dist/mcp/server.js +188 -0
- package/dist/tools/context.js +7 -0
- package/dist/tools/types.js +1 -0
- package/package.json +6 -3
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.2.0] - 2026-05-06
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- MCP server support
|
|
12
|
+
- MCP tools for:
|
|
13
|
+
- audio normalization
|
|
14
|
+
- transcription
|
|
15
|
+
- transcript summarization
|
|
16
|
+
- action items extraction
|
|
17
|
+
- decision extraction
|
|
18
|
+
- full recording processing pipeline
|
|
19
|
+
-Compatibility with MCP clients such a Claude Desktop and MCP Inspector
|
|
20
|
+
|
|
21
|
+
|
|
8
22
|
## [0.1.0] - 2026-04-26
|
|
9
23
|
|
|
10
24
|
### Added
|
package/dist/cli/index.js
CHANGED
|
@@ -7,6 +7,7 @@ import { getConfigFilePath } from "../config/paths.js";
|
|
|
7
7
|
import { processMeeting } from "../orchestrators/process-meeting.js";
|
|
8
8
|
import { runTool } from "../shared/tool-definition.js";
|
|
9
9
|
import { tools } from "../shared/tool-registry.js";
|
|
10
|
+
import { startMcpServer } from "../mcp/server.js";
|
|
10
11
|
import { readFileSync } from 'node:fs';
|
|
11
12
|
import { fileURLToPath } from 'node:url';
|
|
12
13
|
import { dirname, join } from 'node:path';
|
|
@@ -177,4 +178,16 @@ configCommand
|
|
|
177
178
|
process.exitCode = 1;
|
|
178
179
|
}
|
|
179
180
|
});
|
|
181
|
+
program
|
|
182
|
+
.command("mcp")
|
|
183
|
+
.description("Start the MCP server (stdio transport)")
|
|
184
|
+
.action(async () => {
|
|
185
|
+
try {
|
|
186
|
+
await startMcpServer();
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
190
|
+
process.exitCode = 1;
|
|
191
|
+
}
|
|
192
|
+
});
|
|
180
193
|
program.parse(process.argv);
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { createContext } from '../tools/context.js';
|
|
5
|
+
import { runTool } from '../shared/tool-definition.js';
|
|
6
|
+
import { tools } from '../shared/tool-registry.js';
|
|
7
|
+
const server = new McpServer({
|
|
8
|
+
name: 'samuraizer',
|
|
9
|
+
version: '0.1.0',
|
|
10
|
+
}, {
|
|
11
|
+
instructions: 'Use process_recording for the full pipeline. ' +
|
|
12
|
+
'Use individual tools (normalize_audio, transcribe_audio, summarize_transcript, ' +
|
|
13
|
+
'extract_action_items, extract_decisions) for step-by-step processing.',
|
|
14
|
+
});
|
|
15
|
+
server.registerTool('normalize_audio', {
|
|
16
|
+
title: 'Normalize Audio',
|
|
17
|
+
description: 'Normalize an audio file to 16kHz mono PCM WAV format required by Whisper.',
|
|
18
|
+
inputSchema: {
|
|
19
|
+
inputPath: z.string().describe('Absolute path to the source audio file'),
|
|
20
|
+
outputPath: z.string().describe('Absolute path for the output WAV file'),
|
|
21
|
+
},
|
|
22
|
+
}, async ({ inputPath, outputPath }) => {
|
|
23
|
+
const ctx = await createContext();
|
|
24
|
+
try {
|
|
25
|
+
const result = await runTool(tools.normalize_audio, {
|
|
26
|
+
inputPath,
|
|
27
|
+
outputPath,
|
|
28
|
+
ffmpegCommand: ctx.config.ffmpegCommand,
|
|
29
|
+
});
|
|
30
|
+
return {
|
|
31
|
+
content: [{ type: 'text', text: result.normalizedAudioPath }],
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
36
|
+
return {
|
|
37
|
+
content: [{ type: 'text', text: `Error normalizing audio: ${message}` }],
|
|
38
|
+
isError: true,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
server.registerTool('transcribe_audio', {
|
|
43
|
+
title: 'Transcribe Audio',
|
|
44
|
+
description: 'Transcribe an audio file using whisper.cpp. Returns the transcript text.',
|
|
45
|
+
inputSchema: {
|
|
46
|
+
filePath: z.string().describe('Absolute path to the audio file'),
|
|
47
|
+
},
|
|
48
|
+
}, async ({ filePath }) => {
|
|
49
|
+
const ctx = await createContext();
|
|
50
|
+
try {
|
|
51
|
+
const result = await runTool(tools.transcribe_audio, {
|
|
52
|
+
audioPath: filePath,
|
|
53
|
+
outputDir: ctx.config.outputDir ?? 'output',
|
|
54
|
+
modelPath: ctx.config.whisperModelPath,
|
|
55
|
+
language: ctx.config.language,
|
|
56
|
+
whisperCommand: ctx.config.whisperCommand,
|
|
57
|
+
});
|
|
58
|
+
return {
|
|
59
|
+
content: [{ type: 'text', text: result.text }],
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
64
|
+
return {
|
|
65
|
+
content: [{ type: 'text', text: `Error transcribing audio: ${message}` }],
|
|
66
|
+
isError: true,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
server.registerTool('summarize_transcript', {
|
|
71
|
+
title: 'Summarize Transcript',
|
|
72
|
+
description: 'Generate a concise meeting summary from transcript text.',
|
|
73
|
+
inputSchema: {
|
|
74
|
+
transcriptText: z.string().describe('Full transcript text to summarize'),
|
|
75
|
+
model: z.string().optional().describe('Ollama model to use (optional)'),
|
|
76
|
+
},
|
|
77
|
+
}, async ({ transcriptText, model }) => {
|
|
78
|
+
const ctx = await createContext();
|
|
79
|
+
try {
|
|
80
|
+
const result = await runTool(tools.summarize_transcript, {
|
|
81
|
+
transcriptText,
|
|
82
|
+
model: model ?? ctx.config.model,
|
|
83
|
+
ollamaBaseUrl: ctx.config.ollamaBaseUrl,
|
|
84
|
+
});
|
|
85
|
+
return {
|
|
86
|
+
content: [{ type: 'text', text: result.summary }],
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
91
|
+
return {
|
|
92
|
+
content: [{ type: 'text', text: `Error summarizing transcript: ${message}` }],
|
|
93
|
+
isError: true,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
server.registerTool('extract_action_items', {
|
|
98
|
+
title: 'Extract Action Items',
|
|
99
|
+
description: 'Extract action items from a meeting transcript. Returns a JSON list of tasks with owner and due date.',
|
|
100
|
+
inputSchema: {
|
|
101
|
+
transcriptText: z.string().describe('Full transcript text'),
|
|
102
|
+
model: z.string().optional().describe('Ollama model to use (optional)'),
|
|
103
|
+
},
|
|
104
|
+
}, async ({ transcriptText, model }) => {
|
|
105
|
+
const ctx = await createContext();
|
|
106
|
+
try {
|
|
107
|
+
const result = await runTool(tools.extract_action_items, {
|
|
108
|
+
transcriptText,
|
|
109
|
+
model: model ?? ctx.config.model,
|
|
110
|
+
ollamaBaseUrl: ctx.config.ollamaBaseUrl,
|
|
111
|
+
});
|
|
112
|
+
return {
|
|
113
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
catch (err) {
|
|
117
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
118
|
+
return {
|
|
119
|
+
content: [{ type: 'text', text: `Error extracting action items: ${message}` }],
|
|
120
|
+
isError: true,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
server.registerTool('extract_decisions', {
|
|
125
|
+
title: 'Extract Decisions',
|
|
126
|
+
description: 'Extract confirmed decisions from a meeting transcript. Returns a JSON list of decisions.',
|
|
127
|
+
inputSchema: {
|
|
128
|
+
transcriptText: z.string().describe('Full transcript text'),
|
|
129
|
+
model: z.string().optional().describe('Ollama model to use (optional)'),
|
|
130
|
+
},
|
|
131
|
+
}, async ({ transcriptText, model }) => {
|
|
132
|
+
const ctx = await createContext();
|
|
133
|
+
try {
|
|
134
|
+
const result = await runTool(tools.extract_decisions, {
|
|
135
|
+
transcriptText,
|
|
136
|
+
model: model ?? ctx.config.model,
|
|
137
|
+
ollamaBaseUrl: ctx.config.ollamaBaseUrl,
|
|
138
|
+
});
|
|
139
|
+
return {
|
|
140
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
catch (err) {
|
|
144
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
145
|
+
return {
|
|
146
|
+
content: [{ type: 'text', text: `Error extracting decisions: ${message}` }],
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
server.registerTool('process_recording', {
|
|
151
|
+
title: 'Process Recording',
|
|
152
|
+
description: 'Run the full Samuraizer pipeline on an audio file. Returns summary, action items, decisions, and output file paths.',
|
|
153
|
+
inputSchema: {
|
|
154
|
+
filePath: z.string().describe('Absolute path to the audio file'),
|
|
155
|
+
model: z.string().optional().describe('Ollama model to use (optional)'),
|
|
156
|
+
},
|
|
157
|
+
}, async ({ filePath, model }) => {
|
|
158
|
+
const ctx = await createContext();
|
|
159
|
+
try {
|
|
160
|
+
const { processMeeting } = await import('../orchestrators/process-meeting.js');
|
|
161
|
+
const result = await processMeeting({
|
|
162
|
+
inputPath: filePath,
|
|
163
|
+
outputRootDir: ctx.config.outputDir,
|
|
164
|
+
model: model ?? ctx.config.model,
|
|
165
|
+
ollamaBaseUrl: ctx.config.ollamaBaseUrl,
|
|
166
|
+
whisperCommand: ctx.config.whisperCommand,
|
|
167
|
+
whisperModelPath: ctx.config.whisperModelPath,
|
|
168
|
+
language: ctx.config.language,
|
|
169
|
+
ffmpegCommand: ctx.config.ffmpegCommand,
|
|
170
|
+
ffprobeCommand: ctx.config.ffprobeCommand,
|
|
171
|
+
});
|
|
172
|
+
return {
|
|
173
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
catch (err) {
|
|
177
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
178
|
+
return {
|
|
179
|
+
content: [{ type: 'text', text: `Error processing recording: ${message}` }],
|
|
180
|
+
isError: true,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
export async function startMcpServer() {
|
|
185
|
+
const transport = new StdioServerTransport();
|
|
186
|
+
await server.connect(transport);
|
|
187
|
+
process.stderr.write('Samuraizer MCP server started and listening for requests...\n');
|
|
188
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "samuraizer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"samuraizer": "./dist/cli/index.js"
|
|
@@ -11,7 +11,9 @@
|
|
|
11
11
|
"build": "tsc",
|
|
12
12
|
"typecheck": "tsc --noEmit",
|
|
13
13
|
"prepack": "npm run build",
|
|
14
|
-
"prepublishOnly": "npm run typecheck && npm run build"
|
|
14
|
+
"prepublishOnly": "npm run typecheck && npm run build",
|
|
15
|
+
"mcp": "tsx src/cli/index.ts mcp",
|
|
16
|
+
"mcp:dist": "node dist/cli/index.js mcp"
|
|
15
17
|
},
|
|
16
18
|
"files": [
|
|
17
19
|
"dist",
|
|
@@ -46,6 +48,7 @@
|
|
|
46
48
|
"url": "https://github.com/UladzKha/samuraizer-cli/issues"
|
|
47
49
|
},
|
|
48
50
|
"dependencies": {
|
|
51
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
49
52
|
"commander": "^14.0.3",
|
|
50
53
|
"execa": "^9.6.1",
|
|
51
54
|
"zod": "^4.3.6"
|
|
@@ -55,4 +58,4 @@
|
|
|
55
58
|
"tsx": "^4.21.0",
|
|
56
59
|
"typescript": "^5.9.3"
|
|
57
60
|
}
|
|
58
|
-
}
|
|
61
|
+
}
|