@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.
Files changed (148) hide show
  1. package/.github/workflows/release.yml +62 -0
  2. package/.releaserc.json +38 -0
  3. package/CHANGELOG.md +161 -0
  4. package/README.md +114 -0
  5. package/README.zh-CN.md +119 -0
  6. package/apple-rag-mcp_process.log +8 -0
  7. package/biome.json +59 -0
  8. package/dist/src/auth/auth-middleware.d.ts +26 -0
  9. package/dist/src/auth/auth-middleware.d.ts.map +1 -0
  10. package/dist/src/auth/auth-middleware.js +77 -0
  11. package/dist/src/auth/auth-middleware.js.map +1 -0
  12. package/dist/src/auth/token-validator.d.ts +22 -0
  13. package/dist/src/auth/token-validator.d.ts.map +1 -0
  14. package/dist/src/auth/token-validator.js +64 -0
  15. package/dist/src/auth/token-validator.js.map +1 -0
  16. package/dist/src/mcp/formatters/response-formatter.d.ts +26 -0
  17. package/dist/src/mcp/formatters/response-formatter.d.ts.map +1 -0
  18. package/dist/src/mcp/formatters/response-formatter.js +119 -0
  19. package/dist/src/mcp/formatters/response-formatter.js.map +1 -0
  20. package/dist/src/mcp/manifest.d.ts +48 -0
  21. package/dist/src/mcp/manifest.d.ts.map +1 -0
  22. package/dist/src/mcp/manifest.js +46 -0
  23. package/dist/src/mcp/manifest.js.map +1 -0
  24. package/dist/src/mcp/middleware/request-validator.d.ts +48 -0
  25. package/dist/src/mcp/middleware/request-validator.d.ts.map +1 -0
  26. package/dist/src/mcp/middleware/request-validator.js +102 -0
  27. package/dist/src/mcp/middleware/request-validator.js.map +1 -0
  28. package/dist/src/mcp/protocol-handler.d.ts +70 -0
  29. package/dist/src/mcp/protocol-handler.d.ts.map +1 -0
  30. package/dist/src/mcp/protocol-handler.js +285 -0
  31. package/dist/src/mcp/protocol-handler.js.map +1 -0
  32. package/dist/src/mcp/tools/fetch-tool.d.ts +18 -0
  33. package/dist/src/mcp/tools/fetch-tool.d.ts.map +1 -0
  34. package/dist/src/mcp/tools/fetch-tool.js +76 -0
  35. package/dist/src/mcp/tools/fetch-tool.js.map +1 -0
  36. package/dist/src/mcp/tools/search-tool.d.ts +20 -0
  37. package/dist/src/mcp/tools/search-tool.d.ts.map +1 -0
  38. package/dist/src/mcp/tools/search-tool.js +86 -0
  39. package/dist/src/mcp/tools/search-tool.js.map +1 -0
  40. package/dist/src/services/database.d.ts +37 -0
  41. package/dist/src/services/database.d.ts.map +1 -0
  42. package/dist/src/services/database.js +166 -0
  43. package/dist/src/services/database.js.map +1 -0
  44. package/dist/src/services/deepinfra-base.d.ts +22 -0
  45. package/dist/src/services/deepinfra-base.d.ts.map +1 -0
  46. package/dist/src/services/deepinfra-base.js +55 -0
  47. package/dist/src/services/deepinfra-base.js.map +1 -0
  48. package/dist/src/services/embedding.d.ts +44 -0
  49. package/dist/src/services/embedding.d.ts.map +1 -0
  50. package/dist/src/services/embedding.js +61 -0
  51. package/dist/src/services/embedding.js.map +1 -0
  52. package/dist/src/services/index.d.ts +10 -0
  53. package/dist/src/services/index.d.ts.map +1 -0
  54. package/dist/src/services/index.js +52 -0
  55. package/dist/src/services/index.js.map +1 -0
  56. package/dist/src/services/ip-authentication.d.ts +12 -0
  57. package/dist/src/services/ip-authentication.d.ts.map +1 -0
  58. package/dist/src/services/ip-authentication.js +39 -0
  59. package/dist/src/services/ip-authentication.js.map +1 -0
  60. package/dist/src/services/rag.d.ts +35 -0
  61. package/dist/src/services/rag.d.ts.map +1 -0
  62. package/dist/src/services/rag.js +106 -0
  63. package/dist/src/services/rag.js.map +1 -0
  64. package/dist/src/services/rate-limit.d.ts +27 -0
  65. package/dist/src/services/rate-limit.d.ts.map +1 -0
  66. package/dist/src/services/rate-limit.js +91 -0
  67. package/dist/src/services/rate-limit.js.map +1 -0
  68. package/dist/src/services/reranker.d.ts +40 -0
  69. package/dist/src/services/reranker.d.ts.map +1 -0
  70. package/dist/src/services/reranker.js +97 -0
  71. package/dist/src/services/reranker.js.map +1 -0
  72. package/dist/src/services/search-engine.d.ts +89 -0
  73. package/dist/src/services/search-engine.d.ts.map +1 -0
  74. package/dist/src/services/search-engine.js +225 -0
  75. package/dist/src/services/search-engine.js.map +1 -0
  76. package/dist/src/services/tool-call-logger.d.ts +36 -0
  77. package/dist/src/services/tool-call-logger.d.ts.map +1 -0
  78. package/dist/src/services/tool-call-logger.js +34 -0
  79. package/dist/src/services/tool-call-logger.js.map +1 -0
  80. package/dist/src/types/env.d.ts +18 -0
  81. package/dist/src/types/env.d.ts.map +1 -0
  82. package/dist/src/types/env.js +2 -0
  83. package/dist/src/types/env.js.map +1 -0
  84. package/dist/src/types/index.d.ts +145 -0
  85. package/dist/src/types/index.d.ts.map +1 -0
  86. package/dist/src/types/index.js +6 -0
  87. package/dist/src/types/index.js.map +1 -0
  88. package/dist/src/utils/d1-utils.d.ts +6 -0
  89. package/dist/src/utils/d1-utils.d.ts.map +1 -0
  90. package/dist/src/utils/d1-utils.js +29 -0
  91. package/dist/src/utils/d1-utils.js.map +1 -0
  92. package/dist/src/utils/logger.d.ts +11 -0
  93. package/dist/src/utils/logger.d.ts.map +1 -0
  94. package/dist/src/utils/logger.js +26 -0
  95. package/dist/src/utils/logger.js.map +1 -0
  96. package/dist/src/utils/query-cleaner.d.ts +20 -0
  97. package/dist/src/utils/query-cleaner.d.ts.map +1 -0
  98. package/dist/src/utils/query-cleaner.js +117 -0
  99. package/dist/src/utils/query-cleaner.js.map +1 -0
  100. package/dist/src/utils/request-info.d.ts +18 -0
  101. package/dist/src/utils/request-info.d.ts.map +1 -0
  102. package/dist/src/utils/request-info.js +32 -0
  103. package/dist/src/utils/request-info.js.map +1 -0
  104. package/dist/src/utils/telegram-notifier.d.ts +4 -0
  105. package/dist/src/utils/telegram-notifier.d.ts.map +1 -0
  106. package/dist/src/utils/telegram-notifier.js +33 -0
  107. package/dist/src/utils/telegram-notifier.js.map +1 -0
  108. package/dist/src/utils/url-processor.d.ts +15 -0
  109. package/dist/src/utils/url-processor.d.ts.map +1 -0
  110. package/dist/src/utils/url-processor.js +54 -0
  111. package/dist/src/utils/url-processor.js.map +1 -0
  112. package/dist/src/worker.d.ts +15 -0
  113. package/dist/src/worker.d.ts.map +1 -0
  114. package/dist/src/worker.js +136 -0
  115. package/dist/src/worker.js.map +1 -0
  116. package/migrations/schema.sql +155 -0
  117. package/package.json +49 -0
  118. package/scripts/semantic-release-server-json.js +34 -0
  119. package/server.json +25 -0
  120. package/src/auth/auth-middleware.ts +104 -0
  121. package/src/auth/token-validator.ts +96 -0
  122. package/src/mcp/formatters/response-formatter.ts +157 -0
  123. package/src/mcp/manifest.ts +48 -0
  124. package/src/mcp/middleware/request-validator.ts +135 -0
  125. package/src/mcp/protocol-handler.ts +412 -0
  126. package/src/mcp/tools/fetch-tool.ts +146 -0
  127. package/src/mcp/tools/search-tool.ts +165 -0
  128. package/src/services/database.ts +202 -0
  129. package/src/services/deepinfra-base.ts +81 -0
  130. package/src/services/embedding.ts +96 -0
  131. package/src/services/index.ts +59 -0
  132. package/src/services/ip-authentication.ts +62 -0
  133. package/src/services/rag.ts +158 -0
  134. package/src/services/rate-limit.ts +141 -0
  135. package/src/services/reranker.ts +171 -0
  136. package/src/services/search-engine.ts +333 -0
  137. package/src/services/tool-call-logger.ts +98 -0
  138. package/src/types/env.ts +22 -0
  139. package/src/types/index.ts +189 -0
  140. package/src/utils/d1-utils.ts +45 -0
  141. package/src/utils/logger.ts +33 -0
  142. package/src/utils/query-cleaner.ts +151 -0
  143. package/src/utils/request-info.ts +47 -0
  144. package/src/utils/telegram-notifier.ts +47 -0
  145. package/src/utils/url-processor.ts +65 -0
  146. package/src/worker.ts +176 -0
  147. package/tsconfig.json +32 -0
  148. package/wrangler.toml.example +39 -0
@@ -0,0 +1,54 @@
1
+ /**
2
+ * URL processing utility for Apple Developer documentation
3
+ * Handles URL validation, normalization, and malformed URL detection
4
+ */
5
+ /**
6
+ * Validates and normalizes a single URL using elegant malformed URL detection
7
+ * Integrates the sophisticated filtering logic for comprehensive validation
8
+ */
9
+ export function validateAndNormalizeUrl(url) {
10
+ // Basic validation
11
+ if (!url || typeof url !== "string" || url.trim().length === 0) {
12
+ return {
13
+ isValid: false,
14
+ normalizedUrl: url,
15
+ error: "URL is required",
16
+ };
17
+ }
18
+ // Apply malformed URL detection - global optimal solution
19
+ const isValidUrl = ![
20
+ url.split("https://").length > 2 || url.split("http://").length > 2, // Duplicate protocol
21
+ url.includes("%ef%bb%bf") || url.includes("\ufeff"), // BOM characters
22
+ url.split("/documentation/").length > 2, // Path duplication
23
+ url.includes("https:/") && !url.startsWith("https://"), // Protocol format error
24
+ url.length > 200, // Abnormal length
25
+ url.split("developer.apple.com").length > 2, // Duplicate domain
26
+ ].some(Boolean);
27
+ if (!isValidUrl) {
28
+ return {
29
+ isValid: false,
30
+ normalizedUrl: url,
31
+ error: "URL contains malformed patterns",
32
+ };
33
+ }
34
+ // Clean and normalize URL - elegant, modern, and concise
35
+ try {
36
+ const parsed = new URL(url);
37
+ // Preserve case sensitivity for Apple Developer paths
38
+ const normalizedPath = parsed.pathname === "/" ? "/" : parsed.pathname.replace(/\/+$/, ""); // Remove trailing slashes except root
39
+ // Remove query parameters and fragments to match pages table format
40
+ const normalizedUrl = `${parsed.protocol.toLowerCase()}//${parsed.hostname.toLowerCase()}${normalizedPath}`;
41
+ return {
42
+ isValid: true,
43
+ normalizedUrl,
44
+ };
45
+ }
46
+ catch {
47
+ return {
48
+ isValid: false,
49
+ normalizedUrl: url,
50
+ error: "Invalid URL format",
51
+ };
52
+ }
53
+ }
54
+ //# sourceMappingURL=url-processor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url-processor.js","sourceRoot":"","sources":["../../../src/utils/url-processor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CAAC,GAAW;IACjD,mBAAmB;IACnB,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/D,OAAO;YACL,OAAO,EAAE,KAAK;YACd,aAAa,EAAE,GAAG;YAClB,KAAK,EAAE,iBAAiB;SACzB,CAAC;IACJ,CAAC;IAED,0DAA0D;IAC1D,MAAM,UAAU,GAAG,CAAC;QAClB,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,qBAAqB;QAC1F,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,iBAAiB;QACtE,GAAG,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,mBAAmB;QAC5D,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,wBAAwB;QAChF,GAAG,CAAC,MAAM,GAAG,GAAG,EAAE,kBAAkB;QACpC,GAAG,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,mBAAmB;KACjE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAEhB,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO;YACL,OAAO,EAAE,KAAK;YACd,aAAa,EAAE,GAAG;YAClB,KAAK,EAAE,iCAAiC;SACzC,CAAC;IACJ,CAAC;IAED,yDAAyD;IACzD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QAC5B,sDAAsD;QACtD,MAAM,cAAc,GAClB,MAAM,CAAC,QAAQ,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,sCAAsC;QAE7G,oEAAoE;QACpE,MAAM,aAAa,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,GAAG,cAAc,EAAE,CAAC;QAE5G,OAAO;YACL,OAAO,EAAE,IAAI;YACb,aAAa;SACd,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;YACL,OAAO,EAAE,KAAK;YACd,aAAa,EAAE,GAAG;YAClB,KAAK,EAAE,oBAAoB;SAC5B,CAAC;IACJ,CAAC;AACH,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Apple RAG MCP Server - Cloudflare Worker Native
3
+ * Ultra-modern, zero-dependency MCP 2025-06-18 compliant server
4
+ * Global optimal solution with maximum performance
5
+ */
6
+ import type { WorkerEnv } from "./types/index.js";
7
+ /**
8
+ * Cloudflare Worker entry point - Global optimal implementation
9
+ * Handles all MCP protocol requests with edge-optimized performance
10
+ */
11
+ declare const _default: {
12
+ fetch(request: Request, env: WorkerEnv, ctx: ExecutionContext): Promise<Response>;
13
+ };
14
+ export default _default;
15
+ //# sourceMappingURL=worker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/worker.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAIlD;;;GAGG;;mBAGU,OAAO,OACX,SAAS,OACT,gBAAgB,GACpB,OAAO,CAAC,QAAQ,CAAC;;AALtB,wBA8JE"}
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Apple RAG MCP Server - Cloudflare Worker Native
3
+ * Ultra-modern, zero-dependency MCP 2025-06-18 compliant server
4
+ * Global optimal solution with maximum performance
5
+ */
6
+ import { HEALTH_STATUS, SERVER_MANIFEST } from "./mcp/manifest.js";
7
+ import { MCPProtocolHandler } from "./mcp/protocol-handler.js";
8
+ import { createServices } from "./services/index.js";
9
+ import { logger } from "./utils/logger.js";
10
+ import { configureTelegram } from "./utils/telegram-notifier.js";
11
+ /**
12
+ * Cloudflare Worker entry point - Global optimal implementation
13
+ * Handles all MCP protocol requests with edge-optimized performance
14
+ */
15
+ export default {
16
+ async fetch(request, env, ctx) {
17
+ const startTime = performance.now();
18
+ // Configure Telegram notification and set execution context for waitUntil
19
+ configureTelegram(env.TELEGRAM_BOT_URL);
20
+ logger.setContext(ctx);
21
+ try {
22
+ const url = new URL(request.url);
23
+ // Health check endpoint - ultra-fast response
24
+ if (request.method === "GET" && url.pathname === "/health") {
25
+ return new Response(JSON.stringify({
26
+ ...HEALTH_STATUS,
27
+ timestamp: new Date().toISOString(),
28
+ }), {
29
+ status: 200,
30
+ headers: {
31
+ "Content-Type": "application/json",
32
+ "Cache-Control": "no-cache",
33
+ },
34
+ });
35
+ }
36
+ // Manifest endpoint - server discovery
37
+ if (request.method === "GET" && url.pathname === "/manifest") {
38
+ return new Response(JSON.stringify(SERVER_MANIFEST), {
39
+ status: 200,
40
+ headers: {
41
+ "Content-Type": "application/json",
42
+ "Cache-Control": "public, max-age=3600",
43
+ },
44
+ });
45
+ }
46
+ // Reject other GET requests to the MCP endpoint
47
+ if (request.method === "GET" && url.pathname === "/") {
48
+ return new Response("Method Not Allowed", {
49
+ status: 405,
50
+ headers: {
51
+ "Access-Control-Allow-Origin": "*",
52
+ Allow: "POST, OPTIONS",
53
+ },
54
+ });
55
+ }
56
+ // Handle POST /manifest requests (VPS compatibility)
57
+ if (request.method === "POST" && url.pathname === "/manifest") {
58
+ try {
59
+ const body = await request.json();
60
+ // Empty body → return manifest (common client behavior)
61
+ if (!body || Object.keys(body).length === 0) {
62
+ return new Response(JSON.stringify(SERVER_MANIFEST), {
63
+ status: 200,
64
+ headers: { "Content-Type": "application/json" },
65
+ });
66
+ }
67
+ // MCP request to wrong endpoint → redirect to correct endpoint
68
+ if (body
69
+ .jsonrpc === "2.0" &&
70
+ body.method) {
71
+ return new Response(JSON.stringify({
72
+ error: "Endpoint redirect",
73
+ message: "MCP protocol requests should be sent to /",
74
+ redirect: "/",
75
+ }), {
76
+ status: 307,
77
+ headers: {
78
+ "Content-Type": "application/json",
79
+ Location: "/",
80
+ },
81
+ });
82
+ }
83
+ // Any other POST data → helpful error
84
+ return new Response(JSON.stringify({
85
+ error: "Invalid manifest request",
86
+ message: "Use GET /manifest for server discovery or POST / for MCP communication",
87
+ endpoints: {
88
+ manifest: "GET /manifest",
89
+ mcp: "POST /",
90
+ },
91
+ }), {
92
+ status: 400,
93
+ headers: { "Content-Type": "application/json" },
94
+ });
95
+ }
96
+ catch (_error) {
97
+ return new Response(JSON.stringify({
98
+ error: "Invalid JSON",
99
+ message: "Request body must be valid JSON",
100
+ }), {
101
+ status: 400,
102
+ headers: { "Content-Type": "application/json" },
103
+ });
104
+ }
105
+ }
106
+ // Initialize services with Worker environment
107
+ const services = await createServices(env);
108
+ // Authenticate request using auth service
109
+ const authContext = await services.auth.optionalAuth(request);
110
+ // Create MCP protocol handler
111
+ const handler = new MCPProtocolHandler(services);
112
+ // Handle MCP request
113
+ const response = await handler.handleRequest(request, authContext);
114
+ return response;
115
+ }
116
+ catch (error) {
117
+ const duration = performance.now() - startTime;
118
+ const errorUrl = new URL(request.url);
119
+ logger.error(`Worker error for ${request.method} ${errorUrl.pathname} (duration: ${Math.round(duration)}ms): ${error instanceof Error ? error.message : String(error)}`);
120
+ return new Response(JSON.stringify({
121
+ jsonrpc: "2.0",
122
+ error: {
123
+ code: -32603,
124
+ message: "Internal server error",
125
+ },
126
+ }), {
127
+ status: 500,
128
+ headers: {
129
+ "Content-Type": "application/json",
130
+ "Cache-Control": "no-cache",
131
+ },
132
+ });
133
+ }
134
+ },
135
+ };
136
+ //# sourceMappingURL=worker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker.js","sourceRoot":"","sources":["../../src/worker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,155 @@
1
+ -- Apple RAG Database Schema
2
+ -- Cloudflare D1 (SQLite)
3
+ -- Last updated: 2025-11-26
4
+
5
+ -- ============================================================
6
+ -- USERS TABLE
7
+ -- Primary user authentication and profile table
8
+ -- ============================================================
9
+ CREATE TABLE IF NOT EXISTS users (
10
+ id TEXT PRIMARY KEY,
11
+ email TEXT UNIQUE NOT NULL,
12
+ password_hash TEXT,
13
+ name TEXT,
14
+ avatar TEXT,
15
+ provider TEXT NOT NULL DEFAULT 'email',
16
+ provider_id TEXT,
17
+ oauth_provider TEXT,
18
+ oauth_id TEXT,
19
+ stripe_customer_id TEXT,
20
+ reset_token TEXT,
21
+ reset_token_expires_at TEXT,
22
+ last_login TEXT,
23
+ created_at TEXT NOT NULL,
24
+ updated_at TEXT NOT NULL
25
+ );
26
+
27
+ CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
28
+ CREATE INDEX IF NOT EXISTS idx_users_provider ON users(provider, provider_id);
29
+ CREATE INDEX IF NOT EXISTS idx_users_stripe ON users(stripe_customer_id);
30
+
31
+ -- ============================================================
32
+ -- MCP TOKENS TABLE
33
+ -- MCP token management for API access control
34
+ -- ============================================================
35
+ CREATE TABLE IF NOT EXISTS mcp_tokens (
36
+ id TEXT PRIMARY KEY,
37
+ user_id TEXT NOT NULL,
38
+ name TEXT NOT NULL,
39
+ mcp_token TEXT NOT NULL,
40
+ last_used_at TEXT,
41
+ created_at TEXT DEFAULT (datetime('now')),
42
+ updated_at TEXT DEFAULT (datetime('now')),
43
+ FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
44
+ );
45
+
46
+ CREATE INDEX IF NOT EXISTS idx_mcp_tokens_user_id ON mcp_tokens(user_id);
47
+ CREATE INDEX IF NOT EXISTS idx_mcp_tokens_token ON mcp_tokens(mcp_token);
48
+
49
+ -- ============================================================
50
+ -- SEARCH LOGS TABLE
51
+ -- Tracks search operations for usage monitoring and rate limiting
52
+ -- ============================================================
53
+ CREATE TABLE IF NOT EXISTS search_logs (
54
+ id TEXT DEFAULT (lower(hex(randomblob(16)))) PRIMARY KEY,
55
+ user_id TEXT NOT NULL,
56
+ mcp_token TEXT,
57
+ requested_query TEXT NOT NULL,
58
+ actual_query TEXT NOT NULL,
59
+ result_count INTEGER DEFAULT 0,
60
+ response_time_ms INTEGER,
61
+ status_code INTEGER,
62
+ error_code TEXT,
63
+ ip_address TEXT,
64
+ country_code TEXT,
65
+ created_at TEXT DEFAULT (datetime('now'))
66
+ );
67
+
68
+ CREATE INDEX IF NOT EXISTS idx_search_logs_user_id ON search_logs(user_id);
69
+ CREATE INDEX IF NOT EXISTS idx_search_logs_created_at ON search_logs(created_at);
70
+ CREATE INDEX IF NOT EXISTS idx_search_logs_country ON search_logs(country_code);
71
+
72
+ -- ============================================================
73
+ -- FETCH LOGS TABLE
74
+ -- Tracks fetch operations for usage monitoring and rate limiting
75
+ -- ============================================================
76
+ CREATE TABLE IF NOT EXISTS fetch_logs (
77
+ id TEXT DEFAULT (lower(hex(randomblob(16)))) PRIMARY KEY,
78
+ user_id TEXT NOT NULL,
79
+ mcp_token TEXT,
80
+ requested_url TEXT NOT NULL,
81
+ actual_url TEXT,
82
+ page_id TEXT,
83
+ response_time_ms INTEGER,
84
+ status_code INTEGER,
85
+ error_code TEXT,
86
+ ip_address TEXT,
87
+ country_code TEXT,
88
+ created_at TEXT DEFAULT (datetime('now'))
89
+ );
90
+
91
+ CREATE INDEX IF NOT EXISTS idx_fetch_logs_user_id ON fetch_logs(user_id);
92
+ CREATE INDEX IF NOT EXISTS idx_fetch_logs_created_at ON fetch_logs(created_at);
93
+ CREATE INDEX IF NOT EXISTS idx_fetch_logs_country ON fetch_logs(country_code);
94
+
95
+ -- ============================================================
96
+ -- USER SUBSCRIPTIONS TABLE
97
+ -- Stripe integration and subscription lifecycle tracking
98
+ -- ============================================================
99
+ CREATE TABLE IF NOT EXISTS user_subscriptions (
100
+ user_id TEXT PRIMARY KEY,
101
+ stripe_customer_id TEXT,
102
+ stripe_subscription_id TEXT,
103
+ plan_type TEXT DEFAULT 'hobby',
104
+ status TEXT DEFAULT 'active',
105
+ current_period_start TEXT,
106
+ current_period_end TEXT,
107
+ cancel_at_period_end BOOLEAN DEFAULT FALSE,
108
+ price REAL DEFAULT 0,
109
+ billing_interval TEXT DEFAULT 'month',
110
+ stripe_price_id TEXT,
111
+ updated_at TEXT DEFAULT (datetime('now')),
112
+ FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
113
+ );
114
+
115
+ CREATE INDEX IF NOT EXISTS idx_user_subscriptions_stripe_price_id ON user_subscriptions(stripe_price_id);
116
+
117
+ -- ============================================================
118
+ -- USER AUTHORIZED IPS TABLE
119
+ -- IP-based authentication and access control
120
+ -- ============================================================
121
+ CREATE TABLE IF NOT EXISTS user_authorized_ips (
122
+ id TEXT DEFAULT (lower(hex(randomblob(16)))) PRIMARY KEY,
123
+ user_id TEXT NOT NULL,
124
+ ip_address TEXT NOT NULL,
125
+ name TEXT NOT NULL,
126
+ last_used_at TEXT,
127
+ created_at TEXT DEFAULT (datetime('now')),
128
+ updated_at TEXT DEFAULT (datetime('now')),
129
+ FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
130
+ );
131
+
132
+ CREATE INDEX IF NOT EXISTS idx_user_authorized_ips_lookup ON user_authorized_ips(ip_address, user_id);
133
+ CREATE INDEX IF NOT EXISTS idx_user_authorized_ips_user_id ON user_authorized_ips(user_id);
134
+ CREATE INDEX IF NOT EXISTS idx_user_authorized_ips_last_used ON user_authorized_ips(last_used_at);
135
+
136
+ -- ============================================================
137
+ -- CONTACT MESSAGES TABLE
138
+ -- Contact form submissions with bidirectional messaging support
139
+ -- ============================================================
140
+ CREATE TABLE IF NOT EXISTS contact_messages (
141
+ id TEXT DEFAULT (lower(hex(randomblob(16)))) PRIMARY KEY,
142
+ user_id TEXT,
143
+ email TEXT,
144
+ message TEXT NOT NULL,
145
+ ip_address TEXT,
146
+ admin_reply TEXT,
147
+ replied_at TEXT,
148
+ user_read_at TEXT,
149
+ created_at TEXT DEFAULT (datetime('now')),
150
+ FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL
151
+ );
152
+
153
+ CREATE INDEX IF NOT EXISTS idx_contact_messages_created_at ON contact_messages(created_at DESC);
154
+ CREATE INDEX IF NOT EXISTS idx_contact_messages_replied_at ON contact_messages(replied_at DESC);
155
+
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@iflow-mcp/apple-rag-mcp",
3
+ "version": "4.6.2",
4
+ "type": "module",
5
+ "main": "dist/src/worker.js",
6
+ "bin": {
7
+ "apple-rag-mcp": "./dist/src/worker.js"
8
+ },
9
+ "mcpName": "io.github.BingoWon/apple-rag-mcp",
10
+ "scripts": {
11
+ "deploy:dev": "wrangler deploy --env development",
12
+ "deploy:prod": "wrangler deploy --env production",
13
+ "build": "tsc",
14
+ "format": "biome check --write --unsafe ."
15
+ },
16
+ "keywords": [
17
+ "mcp",
18
+ "mcp-2025-06-18",
19
+ "streamable-http",
20
+ "oauth-2.1",
21
+ "authorization",
22
+ "rag",
23
+ "apple",
24
+ "documentation",
25
+ "ai",
26
+ "vector-search",
27
+ "semantic-search"
28
+ ],
29
+ "author": "Apple RAG Team",
30
+ "license": "MIT",
31
+ "description": "Modern MCP 2025-06-18 compliant server with OAuth 2.1 Authorization for Apple Developer Documentation with Semantic Search for RAG, Keyword Search, and Hybrid Search - Production ready with vector similarity and semantic AI reranking",
32
+ "dependencies": {
33
+ "@types/node": "^20.19.11",
34
+ "postgres": "^3.4.7"
35
+ },
36
+ "devDependencies": {
37
+ "@biomejs/biome": "^2.2.2",
38
+ "@cloudflare/workers-types": "^4.20250831.0",
39
+ "@semantic-release/changelog": "^6.0.3",
40
+ "@semantic-release/git": "^10.0.1",
41
+ "semantic-release": "^24.2.0",
42
+ "typescript": "^5.9.2",
43
+ "wrangler": "^4.33.1"
44
+ },
45
+ "engines": {
46
+ "node": ">=18.0.0"
47
+ },
48
+ "packageManager": "pnpm@10.7.0+sha512.6b865ad4b62a1d9842b61d674a393903b871d9244954f652b8842c2b553c72176b278f64c463e52d40fff8aba385c235c8c9ecf5cc7de4fd78b8bb6d49633ab6"
49
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Custom Semantic-Release plugin to update server.json version
3
+ */
4
+
5
+ import fs from "node:fs";
6
+ import path from "node:path";
7
+
8
+ function updateServerJson(_pluginConfig, context) {
9
+ const { nextRelease, logger } = context;
10
+ const serverJsonPath = path.resolve(process.cwd(), "server.json");
11
+
12
+ if (!fs.existsSync(serverJsonPath)) {
13
+ logger.log("server.json not found, skipping version update");
14
+ return;
15
+ }
16
+
17
+ try {
18
+ const serverJson = JSON.parse(fs.readFileSync(serverJsonPath, "utf8"));
19
+ serverJson.version = nextRelease.version;
20
+
21
+ fs.writeFileSync(
22
+ serverJsonPath,
23
+ `${JSON.stringify(serverJson, null, 2)}\n`
24
+ );
25
+ logger.log(`Updated server.json version to ${nextRelease.version}`);
26
+ } catch (error) {
27
+ logger.error("Failed to update server.json:", error);
28
+ throw error;
29
+ }
30
+ }
31
+
32
+ export default {
33
+ prepare: updateServerJson,
34
+ };
package/server.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "$schema": "https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json",
3
+ "name": "com.apple-rag/mcp-server",
4
+ "description": "Apple Developer Documentation with Semantic Search, RAG, and AI reranking for MCP clients",
5
+ "status": "active",
6
+ "repository": {
7
+ "url": "https://github.com/BingoWon/apple-rag-mcp",
8
+ "source": "github"
9
+ },
10
+ "version": "4.6.2",
11
+ "remotes": [
12
+ {
13
+ "type": "streamable-http",
14
+ "url": "https://mcp.apple-rag.com",
15
+ "headers": [
16
+ {
17
+ "name": "Authorization",
18
+ "description": "MCP Token for authentication (optional - free tier available without token)",
19
+ "is_required": false,
20
+ "is_secret": true
21
+ }
22
+ ]
23
+ }
24
+ ]
25
+ }
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Simple MCP Authentication Middleware
3
+ */
4
+
5
+ import { IPAuthenticationService } from "../services/ip-authentication.js";
6
+ import type { AuthContext } from "../types/index.js";
7
+ import { logger } from "../utils/logger.js";
8
+ import { TokenValidator, type UserTokenData } from "./token-validator.js";
9
+
10
+ export class AuthMiddleware {
11
+ private readonly tokenValidator: TokenValidator;
12
+ private readonly ipAuthService: IPAuthenticationService;
13
+
14
+ constructor(d1: D1Database) {
15
+ this.tokenValidator = new TokenValidator(d1);
16
+ this.ipAuthService = new IPAuthenticationService(d1);
17
+ }
18
+
19
+ /**
20
+ * Extract Bearer token from Authorization header
21
+ * Handles multiple "Bearer" prefixes and validates token format
22
+ */
23
+ private extractBearerToken(authHeader?: string): string | null {
24
+ if (!authHeader) return null;
25
+
26
+ // Remove all "Bearer " prefixes (case-insensitive, supports multiple)
27
+ const token = authHeader.replace(/^(Bearer\s+)+/gi, "").trim();
28
+
29
+ // Only return if it matches valid token format
30
+ return /^at_[a-f0-9]{32}$/.test(token) ? token : null;
31
+ }
32
+
33
+ /**
34
+ * Optional authentication middleware
35
+ * Validates token if present, or checks IP-based authentication, allows access without either
36
+ */
37
+ async optionalAuth(request: Request): Promise<AuthContext> {
38
+ const authHeader = request.headers.get("authorization");
39
+ const token = this.extractBearerToken(authHeader || undefined);
40
+ const clientIP = this.getClientIP(request);
41
+
42
+ // Try token authentication first
43
+ if (token) {
44
+ const validation = await this.tokenValidator.validateToken(token);
45
+
46
+ if (validation.valid) {
47
+ logger.info(
48
+ `Token authentication successful for userId: ${validation.userData?.userId}`
49
+ );
50
+
51
+ return {
52
+ isAuthenticated: true,
53
+ userId: validation.userData?.userId,
54
+ email: validation.userData?.email,
55
+ token: token,
56
+ };
57
+ }
58
+
59
+ logger.error(
60
+ `Token validation failed. Raw header: "${authHeader}", Extracted token: "${token}", IP: ${clientIP}, Error: ${validation.error}`
61
+ );
62
+ }
63
+
64
+ // Try IP-based authentication
65
+ const ipAuthResult =
66
+ await this.ipAuthService.checkIPAuthentication(clientIP);
67
+ if (ipAuthResult) {
68
+ logger.info(
69
+ `IP-based authentication successful for userId: ${ipAuthResult.userId} from IP: ${clientIP}`
70
+ );
71
+
72
+ return {
73
+ isAuthenticated: true,
74
+ userId: ipAuthResult.userId,
75
+ email: ipAuthResult.email,
76
+ token: "ip-based",
77
+ };
78
+ }
79
+
80
+ // No authentication method succeeded
81
+ logger.info(
82
+ `No authentication provided - allowing unauthenticated access from IP: ${clientIP} (hasToken: ${!!token})`
83
+ );
84
+
85
+ return { isAuthenticated: false };
86
+ }
87
+
88
+ async getUserData(userId: string): Promise<UserTokenData> {
89
+ return this.tokenValidator.getUserDataById(userId);
90
+ }
91
+
92
+ /**
93
+ * Get client IP address from request (Worker optimized)
94
+ */
95
+ private getClientIP(request: Request): string {
96
+ // Cloudflare provides client IP in CF-Connecting-IP header
97
+ return (
98
+ request.headers.get("CF-Connecting-IP") ||
99
+ request.headers.get("X-Forwarded-For") ||
100
+ request.headers.get("X-Real-IP") ||
101
+ "unknown"
102
+ );
103
+ }
104
+ }