@nahisaho/katashiro-mcp-server 0.1.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.
@@ -0,0 +1,474 @@
1
+ /**
2
+ * KatashiroMCPServer - MCP準拠サーバー実装
3
+ *
4
+ * Model Context Protocol (https://modelcontextprotocol.io) に準拠
5
+ * JSON-RPC 2.0ベースのプロトコルでTools, Resources, Promptsを提供
6
+ *
7
+ * @module @nahisaho/katashiro-mcp-server
8
+ * @task TSK-060
9
+ */
10
+
11
+ import { ok, err, type Result } from '@nahisaho/katashiro-core';
12
+
13
+ /**
14
+ * MCP Tool definition (JSON Schema based)
15
+ */
16
+ export interface MCPTool {
17
+ name: string;
18
+ description: string;
19
+ inputSchema: {
20
+ type: 'object';
21
+ properties: Record<string, unknown>;
22
+ required?: string[];
23
+ };
24
+ }
25
+
26
+ /**
27
+ * MCP Prompt definition
28
+ */
29
+ export interface MCPPrompt {
30
+ name: string;
31
+ description: string;
32
+ arguments?: Array<{
33
+ name: string;
34
+ description: string;
35
+ required?: boolean;
36
+ }>;
37
+ }
38
+
39
+ /**
40
+ * MCP Resource definition
41
+ */
42
+ export interface MCPResource {
43
+ uri: string;
44
+ name: string;
45
+ description?: string;
46
+ mimeType?: string;
47
+ }
48
+
49
+ /**
50
+ * Tool execution result (MCP content array format)
51
+ */
52
+ export interface ToolResult {
53
+ content: Array<{
54
+ type: 'text' | 'image' | 'resource';
55
+ text?: string;
56
+ data?: string;
57
+ mimeType?: string;
58
+ }>;
59
+ isError?: boolean;
60
+ }
61
+
62
+ /**
63
+ * Prompt result (MCP messages format)
64
+ */
65
+ export interface PromptResult {
66
+ messages: Array<{
67
+ role: 'user' | 'assistant';
68
+ content: {
69
+ type: 'text';
70
+ text: string;
71
+ };
72
+ }>;
73
+ }
74
+
75
+ /**
76
+ * Server capabilities (MCP capability negotiation)
77
+ */
78
+ export interface ServerCapabilities {
79
+ tools?: { listChanged?: boolean };
80
+ resources?: { subscribe?: boolean; listChanged?: boolean };
81
+ prompts?: { listChanged?: boolean };
82
+ }
83
+
84
+ /**
85
+ * Server info
86
+ */
87
+ export interface ServerInfo {
88
+ name: string;
89
+ version: string;
90
+ }
91
+
92
+ /**
93
+ * KatashiroMCPServer
94
+ *
95
+ * MCP server implementation for KATASHIRO
96
+ * Provides tools, resources, and prompts for AI-powered research
97
+ */
98
+ export class KatashiroMCPServer {
99
+ private tools: Map<string, MCPTool> = new Map();
100
+ private prompts: Map<string, MCPPrompt> = new Map();
101
+ private resources: Map<string, MCPResource> = new Map();
102
+
103
+ private readonly serverInfo: ServerInfo = {
104
+ name: 'katashiro',
105
+ version: '0.1.0',
106
+ };
107
+
108
+ private readonly capabilities: ServerCapabilities = {
109
+ tools: { listChanged: true },
110
+ resources: { subscribe: false, listChanged: true },
111
+ prompts: { listChanged: true },
112
+ };
113
+
114
+ constructor() {
115
+ this.registerTools();
116
+ this.registerPrompts();
117
+ this.registerResources();
118
+ }
119
+
120
+ /**
121
+ * Get server name
122
+ */
123
+ getName(): string {
124
+ return this.serverInfo.name;
125
+ }
126
+
127
+ /**
128
+ * Get server info
129
+ */
130
+ getServerInfo(): ServerInfo {
131
+ return this.serverInfo;
132
+ }
133
+
134
+ /**
135
+ * Get server capabilities
136
+ */
137
+ getCapabilities(): ServerCapabilities {
138
+ return this.capabilities;
139
+ }
140
+
141
+ /**
142
+ * Get all registered tools (tools/list)
143
+ */
144
+ getTools(): MCPTool[] {
145
+ return Array.from(this.tools.values());
146
+ }
147
+
148
+ /**
149
+ * Get all registered prompts (prompts/list)
150
+ */
151
+ getPrompts(): MCPPrompt[] {
152
+ return Array.from(this.prompts.values());
153
+ }
154
+
155
+ /**
156
+ * Execute a tool (tools/call)
157
+ */
158
+ async executeTool(
159
+ name: string,
160
+ args: Record<string, unknown>
161
+ ): Promise<Result<ToolResult, Error>> {
162
+ try {
163
+ const tool = this.tools.get(name);
164
+ if (!tool) {
165
+ return err(new Error(`Unknown tool: ${name}`));
166
+ }
167
+
168
+ const result = await this.handleToolExecution(name, args);
169
+ return result;
170
+ } catch (error) {
171
+ return err(error instanceof Error ? error : new Error(String(error)));
172
+ }
173
+ }
174
+
175
+ /**
176
+ * Execute a prompt (prompts/get)
177
+ */
178
+ async executePrompt(
179
+ name: string,
180
+ args: Record<string, unknown>
181
+ ): Promise<Result<PromptResult, Error>> {
182
+ try {
183
+ const prompt = this.prompts.get(name);
184
+ if (!prompt) {
185
+ return err(new Error(`Unknown prompt: ${name}`));
186
+ }
187
+
188
+ const result = await this.handlePromptExecution(name, args);
189
+ return result;
190
+ } catch (error) {
191
+ return err(error instanceof Error ? error : new Error(String(error)));
192
+ }
193
+ }
194
+
195
+ /**
196
+ * List all resources (resources/list)
197
+ */
198
+ async listResources(): Promise<Result<MCPResource[], Error>> {
199
+ try {
200
+ return ok(Array.from(this.resources.values()));
201
+ } catch (error) {
202
+ return err(error instanceof Error ? error : new Error(String(error)));
203
+ }
204
+ }
205
+
206
+ /**
207
+ * Read a resource (resources/read)
208
+ */
209
+ async readResource(uri: string): Promise<Result<string, Error>> {
210
+ try {
211
+ const resource = this.resources.get(uri);
212
+ if (!resource) {
213
+ return err(new Error(`Resource not found: ${uri}`));
214
+ }
215
+
216
+ return ok(`Resource content for: ${uri}`);
217
+ } catch (error) {
218
+ return err(error instanceof Error ? error : new Error(String(error)));
219
+ }
220
+ }
221
+
222
+ /**
223
+ * Register KATASHIRO tools
224
+ */
225
+ private registerTools(): void {
226
+ // Web Search Tool
227
+ this.tools.set('web_search', {
228
+ name: 'web_search',
229
+ description: 'Search the web for information on any topic',
230
+ inputSchema: {
231
+ type: 'object',
232
+ properties: {
233
+ query: { type: 'string', description: 'Search query' },
234
+ limit: { type: 'number', description: 'Maximum number of results' },
235
+ },
236
+ required: ['query'],
237
+ },
238
+ });
239
+
240
+ // Analyze Content Tool
241
+ this.tools.set('analyze_content', {
242
+ name: 'analyze_content',
243
+ description: 'Analyze content for entities, topics, and sentiment',
244
+ inputSchema: {
245
+ type: 'object',
246
+ properties: {
247
+ content: { type: 'string', description: 'Content to analyze' },
248
+ type: { type: 'string', description: 'Content type (text, code, html)' },
249
+ },
250
+ required: ['content'],
251
+ },
252
+ });
253
+
254
+ // Generate Summary Tool
255
+ this.tools.set('generate_summary', {
256
+ name: 'generate_summary',
257
+ description: 'Generate a summary of the provided content',
258
+ inputSchema: {
259
+ type: 'object',
260
+ properties: {
261
+ content: { type: 'string', description: 'Content to summarize' },
262
+ style: { type: 'string', description: 'Summary style (brief, detailed, bullet)' },
263
+ },
264
+ required: ['content'],
265
+ },
266
+ });
267
+
268
+ // Knowledge Query Tool
269
+ this.tools.set('query_knowledge', {
270
+ name: 'query_knowledge',
271
+ description: 'Query the knowledge graph for related information',
272
+ inputSchema: {
273
+ type: 'object',
274
+ properties: {
275
+ query: { type: 'string', description: 'Query string' },
276
+ type: { type: 'string', description: 'Node type filter' },
277
+ },
278
+ required: ['query'],
279
+ },
280
+ });
281
+
282
+ // Generate Report Tool
283
+ this.tools.set('generate_report', {
284
+ name: 'generate_report',
285
+ description: 'Generate a comprehensive research report',
286
+ inputSchema: {
287
+ type: 'object',
288
+ properties: {
289
+ topic: { type: 'string', description: 'Report topic' },
290
+ format: { type: 'string', description: 'Output format (markdown, html, pdf)' },
291
+ sections: { type: 'array', description: 'Report sections to include' },
292
+ },
293
+ required: ['topic'],
294
+ },
295
+ });
296
+ }
297
+
298
+ /**
299
+ * Register KATASHIRO prompts
300
+ */
301
+ private registerPrompts(): void {
302
+ this.prompts.set('research_topic', {
303
+ name: 'research_topic',
304
+ description: 'Research a topic comprehensively using web search and analysis',
305
+ arguments: [
306
+ { name: 'topic', description: 'Topic to research', required: true },
307
+ { name: 'depth', description: 'Research depth (shallow, moderate, deep)', required: false },
308
+ ],
309
+ });
310
+
311
+ this.prompts.set('analyze_document', {
312
+ name: 'analyze_document',
313
+ description: 'Analyze a document for key insights and structure',
314
+ arguments: [
315
+ { name: 'document', description: 'Document content to analyze', required: true },
316
+ ],
317
+ });
318
+
319
+ this.prompts.set('create_presentation', {
320
+ name: 'create_presentation',
321
+ description: 'Create a presentation outline from research',
322
+ arguments: [
323
+ { name: 'topic', description: 'Presentation topic', required: true },
324
+ { name: 'slides', description: 'Number of slides', required: false },
325
+ ],
326
+ });
327
+ }
328
+
329
+ /**
330
+ * Register KATASHIRO resources
331
+ */
332
+ private registerResources(): void {
333
+ this.resources.set('katashiro://knowledge/graph', {
334
+ uri: 'katashiro://knowledge/graph',
335
+ name: 'Knowledge Graph',
336
+ description: 'The knowledge graph containing research data',
337
+ mimeType: 'application/json',
338
+ });
339
+
340
+ this.resources.set('katashiro://patterns/library', {
341
+ uri: 'katashiro://patterns/library',
342
+ name: 'Pattern Library',
343
+ description: 'Learned patterns from user feedback',
344
+ mimeType: 'application/json',
345
+ });
346
+
347
+ this.resources.set('katashiro://feedback/stats', {
348
+ uri: 'katashiro://feedback/stats',
349
+ name: 'Feedback Statistics',
350
+ description: 'Statistics about user feedback and learning',
351
+ mimeType: 'application/json',
352
+ });
353
+ }
354
+
355
+ /**
356
+ * Handle tool execution
357
+ */
358
+ private async handleToolExecution(
359
+ name: string,
360
+ args: Record<string, unknown>
361
+ ): Promise<Result<ToolResult, Error>> {
362
+ switch (name) {
363
+ case 'web_search':
364
+ return this.executeWebSearch(args);
365
+ case 'analyze_content':
366
+ return this.executeAnalyzeContent(args);
367
+ case 'generate_summary':
368
+ return this.executeGenerateSummary(args);
369
+ case 'query_knowledge':
370
+ return this.executeQueryKnowledge(args);
371
+ case 'generate_report':
372
+ return this.executeGenerateReport(args);
373
+ default:
374
+ return err(new Error(`Unhandled tool: ${name}`));
375
+ }
376
+ }
377
+
378
+ /**
379
+ * Handle prompt execution
380
+ */
381
+ private async handlePromptExecution(
382
+ name: string,
383
+ args: Record<string, unknown>
384
+ ): Promise<Result<PromptResult, Error>> {
385
+ const topic = String(args['topic'] || args['document'] || 'Unknown');
386
+
387
+ return ok({
388
+ messages: [
389
+ {
390
+ role: 'user',
391
+ content: {
392
+ type: 'text',
393
+ text: `Execute prompt "${name}" with topic: ${topic}`,
394
+ },
395
+ },
396
+ ],
397
+ });
398
+ }
399
+
400
+ // Tool implementations
401
+
402
+ private async executeWebSearch(
403
+ args: Record<string, unknown>
404
+ ): Promise<Result<ToolResult, Error>> {
405
+ const query = String(args['query']);
406
+ return ok({
407
+ content: [
408
+ {
409
+ type: 'text',
410
+ text: `Search results for: ${query}\n\n[Simulated results]`,
411
+ },
412
+ ],
413
+ });
414
+ }
415
+
416
+ private async executeAnalyzeContent(
417
+ args: Record<string, unknown>
418
+ ): Promise<Result<ToolResult, Error>> {
419
+ const content = String(args['content']);
420
+ const type = String(args['type'] || 'text');
421
+ return ok({
422
+ content: [
423
+ {
424
+ type: 'text',
425
+ text: `Analysis of ${type} content:\n\nLength: ${content.length}\nType: ${type}`,
426
+ },
427
+ ],
428
+ });
429
+ }
430
+
431
+ private async executeGenerateSummary(
432
+ args: Record<string, unknown>
433
+ ): Promise<Result<ToolResult, Error>> {
434
+ const content = String(args['content']);
435
+ const style = String(args['style'] || 'brief');
436
+ return ok({
437
+ content: [
438
+ {
439
+ type: 'text',
440
+ text: `Summary (${style}):\n\n${content.substring(0, 100)}...`,
441
+ },
442
+ ],
443
+ });
444
+ }
445
+
446
+ private async executeQueryKnowledge(
447
+ args: Record<string, unknown>
448
+ ): Promise<Result<ToolResult, Error>> {
449
+ const query = String(args['query']);
450
+ return ok({
451
+ content: [
452
+ {
453
+ type: 'text',
454
+ text: `Knowledge query: ${query}\n\n[Query results]`,
455
+ },
456
+ ],
457
+ });
458
+ }
459
+
460
+ private async executeGenerateReport(
461
+ args: Record<string, unknown>
462
+ ): Promise<Result<ToolResult, Error>> {
463
+ const topic = String(args['topic']);
464
+ const format = String(args['format'] || 'markdown');
465
+ return ok({
466
+ content: [
467
+ {
468
+ type: 'text',
469
+ text: `# Report: ${topic}\n\nFormat: ${format}\n\n[Report content]`,
470
+ },
471
+ ],
472
+ });
473
+ }
474
+ }
@@ -0,0 +1,158 @@
1
+ /**
2
+ * ToolRegistry - ツール登録・管理
3
+ *
4
+ * MCPツールの登録と実行を管理
5
+ *
6
+ * @module @nahisaho/katashiro-mcp-server
7
+ * @task TSK-061
8
+ */
9
+
10
+ import { ok, err, type Result } from '@nahisaho/katashiro-core';
11
+
12
+ /**
13
+ * Tool content item
14
+ */
15
+ export interface ToolContent {
16
+ type: 'text' | 'image' | 'resource';
17
+ text?: string;
18
+ data?: string;
19
+ mimeType?: string;
20
+ }
21
+
22
+ /**
23
+ * Tool result
24
+ */
25
+ export interface ToolExecutionResult {
26
+ content: ToolContent[];
27
+ isError?: boolean;
28
+ }
29
+
30
+ /**
31
+ * Tool handler function
32
+ */
33
+ export type ToolHandler = (
34
+ args: Record<string, unknown>
35
+ ) => Promise<ToolExecutionResult>;
36
+
37
+ /**
38
+ * Registered tool
39
+ */
40
+ export interface RegisteredTool {
41
+ name: string;
42
+ description: string;
43
+ inputSchema: {
44
+ type: 'object';
45
+ properties: Record<string, unknown>;
46
+ required?: string[];
47
+ };
48
+ handler: ToolHandler;
49
+ }
50
+
51
+ /**
52
+ * ToolRegistry
53
+ *
54
+ * Manages MCP tool registration and execution
55
+ */
56
+ export class ToolRegistry {
57
+ private tools: Map<string, RegisteredTool> = new Map();
58
+
59
+ /**
60
+ * Register a tool
61
+ *
62
+ * @param tool - Tool to register
63
+ * @returns Result
64
+ */
65
+ register(tool: RegisteredTool): Result<void, Error> {
66
+ try {
67
+ if (this.tools.has(tool.name)) {
68
+ return err(new Error(`Tool already registered: ${tool.name}`));
69
+ }
70
+ this.tools.set(tool.name, tool);
71
+ return ok(undefined);
72
+ } catch (error) {
73
+ return err(error instanceof Error ? error : new Error(String(error)));
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Get a registered tool
79
+ *
80
+ * @param name - Tool name
81
+ * @returns Tool or null
82
+ */
83
+ get(name: string): Result<RegisteredTool | null, Error> {
84
+ try {
85
+ return ok(this.tools.get(name) ?? null);
86
+ } catch (error) {
87
+ return err(error instanceof Error ? error : new Error(String(error)));
88
+ }
89
+ }
90
+
91
+ /**
92
+ * List all registered tools
93
+ *
94
+ * @returns Array of tools
95
+ */
96
+ list(): Result<RegisteredTool[], Error> {
97
+ try {
98
+ return ok(Array.from(this.tools.values()));
99
+ } catch (error) {
100
+ return err(error instanceof Error ? error : new Error(String(error)));
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Execute a tool
106
+ *
107
+ * @param name - Tool name
108
+ * @param args - Tool arguments
109
+ * @returns Execution result
110
+ */
111
+ async execute(
112
+ name: string,
113
+ args: Record<string, unknown>
114
+ ): Promise<Result<ToolExecutionResult, Error>> {
115
+ try {
116
+ const tool = this.tools.get(name);
117
+ if (!tool) {
118
+ return err(new Error(`Unknown tool: ${name}`));
119
+ }
120
+
121
+ const result = await tool.handler(args);
122
+ return ok(result);
123
+ } catch (error) {
124
+ return err(error instanceof Error ? error : new Error(String(error)));
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Unregister a tool
130
+ *
131
+ * @param name - Tool name
132
+ * @returns Whether unregistered
133
+ */
134
+ unregister(name: string): Result<boolean, Error> {
135
+ try {
136
+ return ok(this.tools.delete(name));
137
+ } catch (error) {
138
+ return err(error instanceof Error ? error : new Error(String(error)));
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Check if tool exists
144
+ *
145
+ * @param name - Tool name
146
+ * @returns Whether exists
147
+ */
148
+ has(name: string): boolean {
149
+ return this.tools.has(name);
150
+ }
151
+
152
+ /**
153
+ * Clear all tools
154
+ */
155
+ clear(): void {
156
+ this.tools.clear();
157
+ }
158
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Transport module exports
3
+ */
4
+
5
+ export * from './stdio-transport.js';