mirror of
https://github.com/Sea-Haven-Industries/sh-mcp.git
synced 2026-10-05 04:52:05 +00:00
123 lines
3.7 KiB
TypeScript
123 lines
3.7 KiB
TypeScript
|
|
/**
|
|||
|
|
* MCP tool definitions for the knowledge-base package.
|
|||
|
|
*
|
|||
|
|
* Exports a factory so callers can inject the KnowledgeBaseClient
|
|||
|
|
* (production: BedrockKnowledgeBaseClient; tests: mock).
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
import { defineTool, requireScope } from '@sh-mcp/shared';
|
|||
|
|
import type { AuthContext, ToolDef } from '@sh-mcp/shared';
|
|||
|
|
import type { KnowledgeBaseClient } from './client.js';
|
|||
|
|
|
|||
|
|
// ---------------------------------------------------------------------------
|
|||
|
|
// Input / output types
|
|||
|
|
// ---------------------------------------------------------------------------
|
|||
|
|
|
|||
|
|
export interface SearchKnowledgeBaseInput {
|
|||
|
|
/** Natural-language question or keyword query. */
|
|||
|
|
query: string;
|
|||
|
|
/**
|
|||
|
|
* Maximum number of passages to return (1–20).
|
|||
|
|
* Defaults to 5 when omitted.
|
|||
|
|
*/
|
|||
|
|
maxResults?: number;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface SearchKnowledgeBaseResultItem {
|
|||
|
|
/** Source document reference (S3 URI, Notion page title, etc.). */
|
|||
|
|
source: string;
|
|||
|
|
/** Relevance score in [0, 1]. */
|
|||
|
|
score: number;
|
|||
|
|
/** Relevant text passage. */
|
|||
|
|
passage: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface SearchKnowledgeBaseOutput {
|
|||
|
|
/** Ordered list of matching passages, most relevant first. */
|
|||
|
|
results: SearchKnowledgeBaseResultItem[];
|
|||
|
|
/** Total number of results returned. */
|
|||
|
|
count: number;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ---------------------------------------------------------------------------
|
|||
|
|
// Tool factory
|
|||
|
|
// ---------------------------------------------------------------------------
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Build the knowledge-base tools array with the supplied client injected.
|
|||
|
|
*
|
|||
|
|
* Production usage:
|
|||
|
|
* import { createBedrockClientFromEnv } from './client.js';
|
|||
|
|
* const tools = createKnowledgeBaseTools(createBedrockClientFromEnv());
|
|||
|
|
*
|
|||
|
|
* Test usage:
|
|||
|
|
* const tools = createKnowledgeBaseTools(mockClient);
|
|||
|
|
*/
|
|||
|
|
export function createKnowledgeBaseTools(
|
|||
|
|
client: KnowledgeBaseClient
|
|||
|
|
): ToolDef<SearchKnowledgeBaseInput, SearchKnowledgeBaseOutput>[] {
|
|||
|
|
const searchKnowledgeBase = defineTool<
|
|||
|
|
SearchKnowledgeBaseInput,
|
|||
|
|
SearchKnowledgeBaseOutput
|
|||
|
|
>({
|
|||
|
|
name: 'search_knowledge_base',
|
|||
|
|
description:
|
|||
|
|
'Search the Sea Haven internal knowledge base for relevant information. ' +
|
|||
|
|
'The knowledge base is populated from Notion pages, purchase-order records, ' +
|
|||
|
|
'and work-order records. Use this tool to answer questions about company ' +
|
|||
|
|
'procedures, vendor details, site information, or historical work orders.',
|
|||
|
|
tier: 'ops',
|
|||
|
|
requiredScope: 'ops:read',
|
|||
|
|
inputSchema: {
|
|||
|
|
type: 'object',
|
|||
|
|
properties: {
|
|||
|
|
query: {
|
|||
|
|
type: 'string',
|
|||
|
|
description:
|
|||
|
|
'Natural-language question or keyword search query.',
|
|||
|
|
minLength: 1,
|
|||
|
|
maxLength: 1000,
|
|||
|
|
},
|
|||
|
|
maxResults: {
|
|||
|
|
type: 'integer',
|
|||
|
|
description:
|
|||
|
|
'Maximum number of passages to return (1–20). Defaults to 5.',
|
|||
|
|
minimum: 1,
|
|||
|
|
maximum: 20,
|
|||
|
|
default: 5,
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
required: ['query'],
|
|||
|
|
additionalProperties: false,
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
async handler(
|
|||
|
|
input: SearchKnowledgeBaseInput,
|
|||
|
|
ctx: AuthContext
|
|||
|
|
): Promise<SearchKnowledgeBaseOutput> {
|
|||
|
|
// Server-side scope enforcement — never rely solely on UI tool-hiding.
|
|||
|
|
requireScope(ctx, 'ops:read');
|
|||
|
|
|
|||
|
|
// NOTE: This is an ops-tier tool. The output contains knowledge-base
|
|||
|
|
// passages (not finance data), so redact() is not called here.
|
|||
|
|
// Finance-tier tools in other packages MUST call redact() on sensitive fields.
|
|||
|
|
|
|||
|
|
const results = await client.retrieve({
|
|||
|
|
query: input.query,
|
|||
|
|
maxResults: input.maxResults ?? 5,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
results: results.map((r) => ({
|
|||
|
|
source: r.source,
|
|||
|
|
score: r.score,
|
|||
|
|
passage: r.passage,
|
|||
|
|
})),
|
|||
|
|
count: results.length,
|
|||
|
|
};
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
return [searchKnowledgeBase];
|
|||
|
|
}
|