@iflow-mcp/apple-rag-mcp 4.6.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/.github/workflows/release.yml +62 -0
- package/.releaserc.json +38 -0
- package/CHANGELOG.md +161 -0
- package/README.md +114 -0
- package/README.zh-CN.md +119 -0
- package/apple-rag-mcp_process.log +8 -0
- package/biome.json +59 -0
- package/dist/src/auth/auth-middleware.d.ts +26 -0
- package/dist/src/auth/auth-middleware.d.ts.map +1 -0
- package/dist/src/auth/auth-middleware.js +77 -0
- package/dist/src/auth/auth-middleware.js.map +1 -0
- package/dist/src/auth/token-validator.d.ts +22 -0
- package/dist/src/auth/token-validator.d.ts.map +1 -0
- package/dist/src/auth/token-validator.js +64 -0
- package/dist/src/auth/token-validator.js.map +1 -0
- package/dist/src/mcp/formatters/response-formatter.d.ts +26 -0
- package/dist/src/mcp/formatters/response-formatter.d.ts.map +1 -0
- package/dist/src/mcp/formatters/response-formatter.js +119 -0
- package/dist/src/mcp/formatters/response-formatter.js.map +1 -0
- package/dist/src/mcp/manifest.d.ts +48 -0
- package/dist/src/mcp/manifest.d.ts.map +1 -0
- package/dist/src/mcp/manifest.js +46 -0
- package/dist/src/mcp/manifest.js.map +1 -0
- package/dist/src/mcp/middleware/request-validator.d.ts +48 -0
- package/dist/src/mcp/middleware/request-validator.d.ts.map +1 -0
- package/dist/src/mcp/middleware/request-validator.js +102 -0
- package/dist/src/mcp/middleware/request-validator.js.map +1 -0
- package/dist/src/mcp/protocol-handler.d.ts +70 -0
- package/dist/src/mcp/protocol-handler.d.ts.map +1 -0
- package/dist/src/mcp/protocol-handler.js +285 -0
- package/dist/src/mcp/protocol-handler.js.map +1 -0
- package/dist/src/mcp/tools/fetch-tool.d.ts +18 -0
- package/dist/src/mcp/tools/fetch-tool.d.ts.map +1 -0
- package/dist/src/mcp/tools/fetch-tool.js +76 -0
- package/dist/src/mcp/tools/fetch-tool.js.map +1 -0
- package/dist/src/mcp/tools/search-tool.d.ts +20 -0
- package/dist/src/mcp/tools/search-tool.d.ts.map +1 -0
- package/dist/src/mcp/tools/search-tool.js +86 -0
- package/dist/src/mcp/tools/search-tool.js.map +1 -0
- package/dist/src/services/database.d.ts +37 -0
- package/dist/src/services/database.d.ts.map +1 -0
- package/dist/src/services/database.js +166 -0
- package/dist/src/services/database.js.map +1 -0
- package/dist/src/services/deepinfra-base.d.ts +22 -0
- package/dist/src/services/deepinfra-base.d.ts.map +1 -0
- package/dist/src/services/deepinfra-base.js +55 -0
- package/dist/src/services/deepinfra-base.js.map +1 -0
- package/dist/src/services/embedding.d.ts +44 -0
- package/dist/src/services/embedding.d.ts.map +1 -0
- package/dist/src/services/embedding.js +61 -0
- package/dist/src/services/embedding.js.map +1 -0
- package/dist/src/services/index.d.ts +10 -0
- package/dist/src/services/index.d.ts.map +1 -0
- package/dist/src/services/index.js +52 -0
- package/dist/src/services/index.js.map +1 -0
- package/dist/src/services/ip-authentication.d.ts +12 -0
- package/dist/src/services/ip-authentication.d.ts.map +1 -0
- package/dist/src/services/ip-authentication.js +39 -0
- package/dist/src/services/ip-authentication.js.map +1 -0
- package/dist/src/services/rag.d.ts +35 -0
- package/dist/src/services/rag.d.ts.map +1 -0
- package/dist/src/services/rag.js +106 -0
- package/dist/src/services/rag.js.map +1 -0
- package/dist/src/services/rate-limit.d.ts +27 -0
- package/dist/src/services/rate-limit.d.ts.map +1 -0
- package/dist/src/services/rate-limit.js +91 -0
- package/dist/src/services/rate-limit.js.map +1 -0
- package/dist/src/services/reranker.d.ts +40 -0
- package/dist/src/services/reranker.d.ts.map +1 -0
- package/dist/src/services/reranker.js +97 -0
- package/dist/src/services/reranker.js.map +1 -0
- package/dist/src/services/search-engine.d.ts +89 -0
- package/dist/src/services/search-engine.d.ts.map +1 -0
- package/dist/src/services/search-engine.js +225 -0
- package/dist/src/services/search-engine.js.map +1 -0
- package/dist/src/services/tool-call-logger.d.ts +36 -0
- package/dist/src/services/tool-call-logger.d.ts.map +1 -0
- package/dist/src/services/tool-call-logger.js +34 -0
- package/dist/src/services/tool-call-logger.js.map +1 -0
- package/dist/src/types/env.d.ts +18 -0
- package/dist/src/types/env.d.ts.map +1 -0
- package/dist/src/types/env.js +2 -0
- package/dist/src/types/env.js.map +1 -0
- package/dist/src/types/index.d.ts +145 -0
- package/dist/src/types/index.d.ts.map +1 -0
- package/dist/src/types/index.js +6 -0
- package/dist/src/types/index.js.map +1 -0
- package/dist/src/utils/d1-utils.d.ts +6 -0
- package/dist/src/utils/d1-utils.d.ts.map +1 -0
- package/dist/src/utils/d1-utils.js +29 -0
- package/dist/src/utils/d1-utils.js.map +1 -0
- package/dist/src/utils/logger.d.ts +11 -0
- package/dist/src/utils/logger.d.ts.map +1 -0
- package/dist/src/utils/logger.js +26 -0
- package/dist/src/utils/logger.js.map +1 -0
- package/dist/src/utils/query-cleaner.d.ts +20 -0
- package/dist/src/utils/query-cleaner.d.ts.map +1 -0
- package/dist/src/utils/query-cleaner.js +117 -0
- package/dist/src/utils/query-cleaner.js.map +1 -0
- package/dist/src/utils/request-info.d.ts +18 -0
- package/dist/src/utils/request-info.d.ts.map +1 -0
- package/dist/src/utils/request-info.js +32 -0
- package/dist/src/utils/request-info.js.map +1 -0
- package/dist/src/utils/telegram-notifier.d.ts +4 -0
- package/dist/src/utils/telegram-notifier.d.ts.map +1 -0
- package/dist/src/utils/telegram-notifier.js +33 -0
- package/dist/src/utils/telegram-notifier.js.map +1 -0
- package/dist/src/utils/url-processor.d.ts +15 -0
- package/dist/src/utils/url-processor.d.ts.map +1 -0
- package/dist/src/utils/url-processor.js +54 -0
- package/dist/src/utils/url-processor.js.map +1 -0
- package/dist/src/worker.d.ts +15 -0
- package/dist/src/worker.d.ts.map +1 -0
- package/dist/src/worker.js +136 -0
- package/dist/src/worker.js.map +1 -0
- package/migrations/schema.sql +155 -0
- package/package.json +49 -0
- package/scripts/semantic-release-server-json.js +34 -0
- package/server.json +25 -0
- package/src/auth/auth-middleware.ts +104 -0
- package/src/auth/token-validator.ts +96 -0
- package/src/mcp/formatters/response-formatter.ts +157 -0
- package/src/mcp/manifest.ts +48 -0
- package/src/mcp/middleware/request-validator.ts +135 -0
- package/src/mcp/protocol-handler.ts +412 -0
- package/src/mcp/tools/fetch-tool.ts +146 -0
- package/src/mcp/tools/search-tool.ts +165 -0
- package/src/services/database.ts +202 -0
- package/src/services/deepinfra-base.ts +81 -0
- package/src/services/embedding.ts +96 -0
- package/src/services/index.ts +59 -0
- package/src/services/ip-authentication.ts +62 -0
- package/src/services/rag.ts +158 -0
- package/src/services/rate-limit.ts +141 -0
- package/src/services/reranker.ts +171 -0
- package/src/services/search-engine.ts +333 -0
- package/src/services/tool-call-logger.ts +98 -0
- package/src/types/env.ts +22 -0
- package/src/types/index.ts +189 -0
- package/src/utils/d1-utils.ts +45 -0
- package/src/utils/logger.ts +33 -0
- package/src/utils/query-cleaner.ts +151 -0
- package/src/utils/request-info.ts +47 -0
- package/src/utils/telegram-notifier.ts +47 -0
- package/src/utils/url-processor.ts +65 -0
- package/src/worker.ts +176 -0
- package/tsconfig.json +32 -0
- package/wrangler.toml.example +39 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Token Validator with D1 timeout protection
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { backgroundD1Write, withD1Timeout } from "../utils/d1-utils.js";
|
|
6
|
+
import { logger } from "../utils/logger.js";
|
|
7
|
+
|
|
8
|
+
export interface TokenValidationResult {
|
|
9
|
+
valid: boolean;
|
|
10
|
+
error?: string;
|
|
11
|
+
userData?: UserTokenData;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface UserTokenData {
|
|
15
|
+
userId: string;
|
|
16
|
+
email: string;
|
|
17
|
+
name: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class TokenValidator {
|
|
21
|
+
constructor(private d1: D1Database) {}
|
|
22
|
+
|
|
23
|
+
async validateToken(token: string): Promise<TokenValidationResult> {
|
|
24
|
+
if (!/^at_[a-f0-9]{32}$/.test(token)) {
|
|
25
|
+
return { valid: false, error: "Invalid token format" };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const userData = await withD1Timeout(
|
|
29
|
+
() => this.queryUserByToken(token),
|
|
30
|
+
null,
|
|
31
|
+
"token_lookup"
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
if (!userData) {
|
|
35
|
+
return { valid: false, error: "Token not found" };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
backgroundD1Write(
|
|
39
|
+
logger.getContext(),
|
|
40
|
+
() => this.updateLastUsed(token),
|
|
41
|
+
"token_last_used"
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
return { valid: true, userData };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private async queryUserByToken(token: string): Promise<UserTokenData | null> {
|
|
48
|
+
const result = await this.d1
|
|
49
|
+
.prepare(
|
|
50
|
+
`SELECT u.id as user_id, u.email, u.name
|
|
51
|
+
FROM mcp_tokens t JOIN users u ON t.user_id = u.id
|
|
52
|
+
WHERE t.mcp_token = ?`
|
|
53
|
+
)
|
|
54
|
+
.bind(token)
|
|
55
|
+
.all();
|
|
56
|
+
|
|
57
|
+
if (!result.success || !result.results?.length) return null;
|
|
58
|
+
|
|
59
|
+
const row = result.results[0] as Record<string, unknown>;
|
|
60
|
+
return {
|
|
61
|
+
userId: row.user_id as string,
|
|
62
|
+
email: (row.email as string) || "unknown",
|
|
63
|
+
name: (row.name as string) || "unknown",
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private async updateLastUsed(token: string): Promise<void> {
|
|
68
|
+
await this.d1
|
|
69
|
+
.prepare("UPDATE mcp_tokens SET last_used_at = ? WHERE mcp_token = ?")
|
|
70
|
+
.bind(new Date().toISOString(), token)
|
|
71
|
+
.run();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async getUserDataById(userId: string): Promise<UserTokenData> {
|
|
75
|
+
const result = await withD1Timeout(
|
|
76
|
+
async () => {
|
|
77
|
+
const res = await this.d1
|
|
78
|
+
.prepare("SELECT id, email, name FROM users WHERE id = ?")
|
|
79
|
+
.bind(userId)
|
|
80
|
+
.all();
|
|
81
|
+
if (!res.success || !res.results?.length) throw new Error("User not found");
|
|
82
|
+
const user = res.results[0] as Record<string, unknown>;
|
|
83
|
+
return {
|
|
84
|
+
userId: user.id as string,
|
|
85
|
+
email: user.email as string,
|
|
86
|
+
name: (user.name as string) || (user.email as string).split("@")[0],
|
|
87
|
+
};
|
|
88
|
+
},
|
|
89
|
+
null,
|
|
90
|
+
"user_lookup"
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
if (!result) throw new Error("User not found");
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Response Formatting Utilities
|
|
3
|
+
* Professional response formatting for MCP protocol
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { MCPResponse, RAGResult } from "../../types/index.js";
|
|
7
|
+
import { APP_CONSTANTS } from "../protocol-handler.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Format RAG response with professional layout
|
|
11
|
+
*/
|
|
12
|
+
export function formatRAGResponse(
|
|
13
|
+
ragResult: RAGResult,
|
|
14
|
+
isAuthenticated: boolean,
|
|
15
|
+
wasAdjusted: boolean = false
|
|
16
|
+
): string {
|
|
17
|
+
if (
|
|
18
|
+
!ragResult ||
|
|
19
|
+
!ragResult.success ||
|
|
20
|
+
!ragResult.results ||
|
|
21
|
+
ragResult.results.length === 0
|
|
22
|
+
) {
|
|
23
|
+
return APP_CONSTANTS.NO_RESULTS_MESSAGE;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const results = ragResult.results;
|
|
27
|
+
let response = "";
|
|
28
|
+
|
|
29
|
+
results.forEach((result, index) => {
|
|
30
|
+
let title = `[${index + 1}] ${result.title || "Untitled"}`;
|
|
31
|
+
|
|
32
|
+
// Add completeness indicator based on chunk information and merge status
|
|
33
|
+
const isMerged =
|
|
34
|
+
result.mergedChunkIndices && result.mergedChunkIndices.length > 1;
|
|
35
|
+
|
|
36
|
+
if (result.total_chunks === 1) {
|
|
37
|
+
title += ` ✅ Complete Document`;
|
|
38
|
+
response += `${title}\n\n`;
|
|
39
|
+
} else if (isMerged) {
|
|
40
|
+
// For merged content, show which specific parts are included
|
|
41
|
+
const mergedParts = result
|
|
42
|
+
.mergedChunkIndices!.map((idx) => idx + 1)
|
|
43
|
+
.join(", ");
|
|
44
|
+
title += ` 📄 Parts ${mergedParts} merged (${result.total_chunks} total)`;
|
|
45
|
+
response += `${title}\n\n`;
|
|
46
|
+
response += `This shows merged content from multiple parts. For the complete document, use Apple RAG MCP fetch tool: \`fetch(url: "${result.url}")\`\n\n`;
|
|
47
|
+
} else {
|
|
48
|
+
title += ` 📄 Part ${result.chunk_index + 1} of ${result.total_chunks}`;
|
|
49
|
+
response += `${title}\n\n`;
|
|
50
|
+
response += `This is a partial document. For the complete content, use Apple RAG MCP fetch tool: \`fetch(url: "${result.url}")\`\n\n`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
response += `${result.content}\n`;
|
|
54
|
+
|
|
55
|
+
if (index < results.length - 1) {
|
|
56
|
+
response += `\n${"─".repeat(80)}\n\n`;
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Additional URLs section
|
|
61
|
+
if (ragResult.additionalUrls && ragResult.additionalUrls.length > 0) {
|
|
62
|
+
response += `\n\n${"─".repeat(60)}\n\n`;
|
|
63
|
+
response += `Additional Related Documentation:\n`;
|
|
64
|
+
response += `The following ${ragResult.additionalUrls.length} URLs contain supplementary information that may provide additional context or related topics. This includes Apple developer documentation and WWDC video transcripts. Use the \`fetch\` tool to retrieve their complete, cleaned content:\n\n`;
|
|
65
|
+
|
|
66
|
+
ragResult.additionalUrls.forEach((item) => {
|
|
67
|
+
response += `${item.url}\n`;
|
|
68
|
+
|
|
69
|
+
// Show title for Apple Developer video URLs
|
|
70
|
+
if (item.title && item.url.includes("developer.apple.com/videos/play/")) {
|
|
71
|
+
response += ` └─ ${item.title}\n`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
response += ` └─ ${item.characterCount} characters\n\n`;
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Footer message for anonymous users
|
|
79
|
+
if (!isAuthenticated) {
|
|
80
|
+
response += `\n\n${APP_CONSTANTS.ANONYMOUS_ACCESS_MESSAGE}`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Parameter range reminder for AI agents (only when parameter was adjusted)
|
|
84
|
+
if (wasAdjusted) {
|
|
85
|
+
response += `\n\nNote: The result_count parameter accepts values between 1 and 10. Values outside this range are automatically adjusted to the nearest valid limit.`;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return response;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Format fetch response with professional styling
|
|
93
|
+
*/
|
|
94
|
+
export function formatFetchResponse(
|
|
95
|
+
result: { success?: boolean; title?: string; content?: string },
|
|
96
|
+
isAuthenticated: boolean
|
|
97
|
+
): string {
|
|
98
|
+
if (!result || !result.success) {
|
|
99
|
+
return "Failed to retrieve content from the specified URL.";
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
let response = "";
|
|
103
|
+
|
|
104
|
+
if (result.title) {
|
|
105
|
+
response += `${result.title}\n\n`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (result.content) {
|
|
109
|
+
response += result.content;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Footer message for anonymous users
|
|
113
|
+
if (!isAuthenticated) {
|
|
114
|
+
response += `\n\n${APP_CONSTANTS.ANONYMOUS_ACCESS_MESSAGE}`;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return response;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Create success response
|
|
122
|
+
*/
|
|
123
|
+
export function createSuccessResponse(
|
|
124
|
+
requestId: string | number,
|
|
125
|
+
content: string
|
|
126
|
+
): MCPResponse {
|
|
127
|
+
return {
|
|
128
|
+
jsonrpc: "2.0",
|
|
129
|
+
id: requestId,
|
|
130
|
+
result: {
|
|
131
|
+
content: [
|
|
132
|
+
{
|
|
133
|
+
type: "text",
|
|
134
|
+
text: content,
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Create error response
|
|
143
|
+
*/
|
|
144
|
+
export function createErrorResponse(
|
|
145
|
+
requestId: string | number,
|
|
146
|
+
code: number,
|
|
147
|
+
message: string
|
|
148
|
+
): MCPResponse {
|
|
149
|
+
return {
|
|
150
|
+
jsonrpc: "2.0",
|
|
151
|
+
id: requestId,
|
|
152
|
+
error: {
|
|
153
|
+
code,
|
|
154
|
+
message,
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Server Manifest
|
|
3
|
+
* Centralized server discovery and capability information
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export const SERVER_MANIFEST = {
|
|
7
|
+
name: "Apple RAG MCP Server",
|
|
8
|
+
title: "Apple Developer Documentation Search",
|
|
9
|
+
version: "2.0.0",
|
|
10
|
+
description:
|
|
11
|
+
"Ultra-modern MCP server providing AI agents with comprehensive access to Apple's complete developer documentation using advanced RAG technology.",
|
|
12
|
+
protocolVersion: "2025-06-18",
|
|
13
|
+
supportedVersions: ["2025-06-18", "2025-03-26"],
|
|
14
|
+
capabilities: {
|
|
15
|
+
tools: { listChanged: true },
|
|
16
|
+
logging: {},
|
|
17
|
+
experimental: {},
|
|
18
|
+
},
|
|
19
|
+
serverInfo: {
|
|
20
|
+
name: "Apple RAG MCP Server",
|
|
21
|
+
version: "2.0.0",
|
|
22
|
+
},
|
|
23
|
+
endpoints: {
|
|
24
|
+
mcp: "/",
|
|
25
|
+
manifest: "/manifest",
|
|
26
|
+
health: "/health",
|
|
27
|
+
},
|
|
28
|
+
transport: {
|
|
29
|
+
type: "http",
|
|
30
|
+
methods: ["POST"],
|
|
31
|
+
headers: {
|
|
32
|
+
required: ["Content-Type"],
|
|
33
|
+
optional: ["Authorization", "MCP-Protocol-Version"],
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
authorization: {
|
|
37
|
+
enabled: true,
|
|
38
|
+
type: "bearer",
|
|
39
|
+
optional: true,
|
|
40
|
+
},
|
|
41
|
+
} as const;
|
|
42
|
+
|
|
43
|
+
export const HEALTH_STATUS = {
|
|
44
|
+
status: "healthy",
|
|
45
|
+
version: "2.0.0",
|
|
46
|
+
protocol: "2025-06-18",
|
|
47
|
+
supportedVersions: ["2025-06-18", "2025-03-26"],
|
|
48
|
+
} as const;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request Validation Middleware
|
|
3
|
+
* Validates MCP protocol requests and parameters
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { MCPNotification, MCPRequest } from "../../types/index.js";
|
|
7
|
+
import {
|
|
8
|
+
MCP_ERROR_CODES,
|
|
9
|
+
SUPPORTED_MCP_VERSIONS,
|
|
10
|
+
} from "../protocol-handler.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Validate MCP request structure
|
|
14
|
+
*/
|
|
15
|
+
export function isValidMCPRequest(body: unknown): body is MCPRequest {
|
|
16
|
+
return (
|
|
17
|
+
body != null &&
|
|
18
|
+
typeof body === "object" &&
|
|
19
|
+
"jsonrpc" in body &&
|
|
20
|
+
(body as Record<string, unknown>).jsonrpc === "2.0" &&
|
|
21
|
+
"id" in body &&
|
|
22
|
+
"method" in body &&
|
|
23
|
+
typeof (body as Record<string, unknown>).method === "string"
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Validate MCP notification structure
|
|
29
|
+
*/
|
|
30
|
+
export function isValidMCPNotification(body: unknown): body is MCPNotification {
|
|
31
|
+
return (
|
|
32
|
+
body != null &&
|
|
33
|
+
typeof body === "object" &&
|
|
34
|
+
"jsonrpc" in body &&
|
|
35
|
+
(body as Record<string, unknown>).jsonrpc === "2.0" &&
|
|
36
|
+
"method" in body &&
|
|
37
|
+
typeof (body as Record<string, unknown>).method === "string" &&
|
|
38
|
+
!("id" in body)
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Validate protocol version
|
|
44
|
+
*/
|
|
45
|
+
export function validateProtocolVersion(version?: string): {
|
|
46
|
+
isValid: boolean;
|
|
47
|
+
error?: { code: number; message: string };
|
|
48
|
+
} {
|
|
49
|
+
if (
|
|
50
|
+
version &&
|
|
51
|
+
!SUPPORTED_MCP_VERSIONS.includes(
|
|
52
|
+
version as (typeof SUPPORTED_MCP_VERSIONS)[number]
|
|
53
|
+
)
|
|
54
|
+
) {
|
|
55
|
+
return {
|
|
56
|
+
isValid: false,
|
|
57
|
+
error: {
|
|
58
|
+
code: MCP_ERROR_CODES.INVALID_PARAMS,
|
|
59
|
+
message: `Unsupported protocol version: ${version}. Supported versions: ${SUPPORTED_MCP_VERSIONS.join(", ")}`,
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return { isValid: true };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Validate initialize parameters
|
|
69
|
+
*/
|
|
70
|
+
export function validateInitializeParams(params: unknown): {
|
|
71
|
+
isValid: boolean;
|
|
72
|
+
error?: { code: number; message: string };
|
|
73
|
+
} {
|
|
74
|
+
// Basic validation - can be extended
|
|
75
|
+
if (params && typeof params !== "object") {
|
|
76
|
+
return {
|
|
77
|
+
isValid: false,
|
|
78
|
+
error: {
|
|
79
|
+
code: MCP_ERROR_CODES.INVALID_PARAMS,
|
|
80
|
+
message: "Initialize parameters must be an object",
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return { isValid: true };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Validate tool call parameters
|
|
90
|
+
*/
|
|
91
|
+
export function validateToolCallParams(params: unknown): {
|
|
92
|
+
isValid: boolean;
|
|
93
|
+
toolCall?: { name: string; arguments?: Record<string, unknown> };
|
|
94
|
+
error?: { code: number; message: string };
|
|
95
|
+
} {
|
|
96
|
+
if (!params || typeof params !== "object") {
|
|
97
|
+
return {
|
|
98
|
+
isValid: false,
|
|
99
|
+
error: {
|
|
100
|
+
code: MCP_ERROR_CODES.INVALID_PARAMS,
|
|
101
|
+
message: "Tool call parameters are required",
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const p = params as Record<string, unknown>;
|
|
107
|
+
|
|
108
|
+
if (!p.name || typeof p.name !== "string") {
|
|
109
|
+
return {
|
|
110
|
+
isValid: false,
|
|
111
|
+
error: {
|
|
112
|
+
code: MCP_ERROR_CODES.INVALID_PARAMS,
|
|
113
|
+
message: "Tool name is required and must be a string",
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (!p.arguments || typeof p.arguments !== "object") {
|
|
119
|
+
return {
|
|
120
|
+
isValid: false,
|
|
121
|
+
error: {
|
|
122
|
+
code: MCP_ERROR_CODES.INVALID_PARAMS,
|
|
123
|
+
message: "Tool arguments are required and must be an object",
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
isValid: true,
|
|
130
|
+
toolCall: {
|
|
131
|
+
name: p.name,
|
|
132
|
+
arguments: p.arguments as Record<string, unknown>,
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
}
|