midnight-mcp 0.0.2 → 0.0.4
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/README.md +40 -20
- package/dist/pipeline/github.d.ts +18 -1
- package/dist/pipeline/github.js +132 -20
- package/dist/server.js +29 -7
- package/dist/tools/health.d.ts +91 -0
- package/dist/tools/health.js +91 -0
- package/dist/tools/index.d.ts +30 -4
- package/dist/tools/index.js +10 -3
- package/dist/tools/search.d.ts +3 -45
- package/dist/tools/search.js +101 -13
- package/dist/utils/cache.d.ts +77 -0
- package/dist/utils/cache.js +172 -0
- package/dist/utils/errors.d.ts +45 -0
- package/dist/utils/errors.js +95 -0
- package/dist/utils/health.d.ts +29 -0
- package/dist/utils/health.js +132 -0
- package/dist/utils/index.d.ts +9 -0
- package/dist/utils/index.js +9 -0
- package/dist/utils/logger.d.ts +30 -1
- package/dist/utils/logger.js +68 -3
- package/dist/utils/rate-limit.d.ts +61 -0
- package/dist/utils/rate-limit.js +148 -0
- package/dist/utils/validation.d.ts +52 -0
- package/dist/utils/validation.js +255 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -64,31 +64,51 @@ Add `"GITHUB_TOKEN": "ghp_..."` to increase API rate limits from 60 to 5000 requ
|
|
|
64
64
|
|
|
65
65
|
## What's Included
|
|
66
66
|
|
|
67
|
-
### Tools
|
|
68
|
-
|
|
69
|
-
| Tool
|
|
70
|
-
|
|
|
71
|
-
| `midnight
|
|
72
|
-
| `midnight
|
|
73
|
-
| `midnight
|
|
74
|
-
| `midnight
|
|
75
|
-
| `midnight
|
|
76
|
-
| `midnight
|
|
77
|
-
| `midnight
|
|
78
|
-
| `midnight
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
67
|
+
### Tools (14 total)
|
|
68
|
+
|
|
69
|
+
| Tool | Description |
|
|
70
|
+
| --------------------------------- | --------------------------------------- |
|
|
71
|
+
| `midnight-search-compact` | Search Compact contract code |
|
|
72
|
+
| `midnight-search-typescript` | Search TypeScript SDK |
|
|
73
|
+
| `midnight-search-docs` | Search documentation |
|
|
74
|
+
| `midnight-analyze-contract` | Analyze contract structure and security |
|
|
75
|
+
| `midnight-explain-circuit` | Explain circuits in plain language |
|
|
76
|
+
| `midnight-get-file` | Get files from Midnight repos |
|
|
77
|
+
| `midnight-list-examples` | List example contracts |
|
|
78
|
+
| `midnight-get-latest-updates` | Recent repo changes |
|
|
79
|
+
| `midnight-get-version-info` | Get version and release info |
|
|
80
|
+
| `midnight-check-breaking-changes` | Check for breaking changes |
|
|
81
|
+
| `midnight-get-migration-guide` | Migration guides between versions |
|
|
82
|
+
| `midnight-get-file-at-version` | Get file at specific version |
|
|
83
|
+
| `midnight-compare-syntax` | Compare files between versions |
|
|
84
|
+
| `midnight-get-latest-syntax` | Latest syntax reference |
|
|
85
|
+
|
|
86
|
+
### Resources (20 total)
|
|
87
|
+
|
|
88
|
+
- `midnight://docs/*` — Documentation (Compact reference, SDK API, ZK concepts, OpenZeppelin patterns)
|
|
83
89
|
- `midnight://code/*` — Examples, patterns, and templates
|
|
84
90
|
- `midnight://schema/*` — AST, transaction, and proof schemas
|
|
85
91
|
|
|
86
92
|
### Prompts
|
|
87
93
|
|
|
88
|
-
- `midnight
|
|
89
|
-
- `midnight
|
|
90
|
-
- `midnight
|
|
91
|
-
- `midnight
|
|
94
|
+
- `midnight-create-contract` — Create new contracts
|
|
95
|
+
- `midnight-review-contract` — Security review
|
|
96
|
+
- `midnight-explain-concept` — Learn Midnight concepts
|
|
97
|
+
- `midnight-debug-contract` — Debug issues
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## How Environment Variables Work
|
|
102
|
+
|
|
103
|
+
The npm package contains no secrets. **You provide your own credentials** via the `env` block in your config:
|
|
104
|
+
|
|
105
|
+
| Variable | Required | Without It | With It |
|
|
106
|
+
| ---------------- | -------- | -------------------------------------- | -------------------- |
|
|
107
|
+
| `GITHUB_TOKEN` | No | 60 API calls/hour, may hit rate limits | 5,000 calls/hour |
|
|
108
|
+
| `OPENAI_API_KEY` | No | Keyword search only (no embeddings) | Semantic search |
|
|
109
|
+
| `CHROMA_URL` | No | In-memory search, no persistence | Persistent vector DB |
|
|
110
|
+
|
|
111
|
+
Your tokens stay on your machine and are only used to access services on your behalf.
|
|
92
112
|
|
|
93
113
|
---
|
|
94
114
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RepositoryConfig } from "../utils/index.js";
|
|
1
|
+
import { RepositoryConfig, getRateLimitStatus } from "../utils/index.js";
|
|
2
2
|
export interface GitHubFile {
|
|
3
3
|
path: string;
|
|
4
4
|
content: string;
|
|
@@ -62,6 +62,23 @@ export declare class GitHubClient {
|
|
|
62
62
|
repository: string;
|
|
63
63
|
url: string;
|
|
64
64
|
}>>;
|
|
65
|
+
/**
|
|
66
|
+
* Get current rate limit status from GitHub API
|
|
67
|
+
*/
|
|
68
|
+
getRateLimit(): Promise<{
|
|
69
|
+
limit: number;
|
|
70
|
+
remaining: number;
|
|
71
|
+
reset: Date;
|
|
72
|
+
used: number;
|
|
73
|
+
}>;
|
|
74
|
+
/**
|
|
75
|
+
* Check if it's safe to make API requests
|
|
76
|
+
*/
|
|
77
|
+
checkRateLimit(): {
|
|
78
|
+
proceed: boolean;
|
|
79
|
+
reason?: string;
|
|
80
|
+
status: ReturnType<typeof getRateLimitStatus>;
|
|
81
|
+
};
|
|
65
82
|
}
|
|
66
83
|
export declare const githubClient: GitHubClient;
|
|
67
84
|
//# sourceMappingURL=github.d.ts.map
|
package/dist/pipeline/github.js
CHANGED
|
@@ -1,5 +1,87 @@
|
|
|
1
1
|
import { Octokit } from "octokit";
|
|
2
|
-
import { config, logger } from "../utils/index.js";
|
|
2
|
+
import { config, logger, updateRateLimit, shouldProceedWithRequest, getRateLimitStatus, } from "../utils/index.js";
|
|
3
|
+
// Retry configuration
|
|
4
|
+
const RETRY_CONFIG = {
|
|
5
|
+
maxRetries: 3,
|
|
6
|
+
baseDelayMs: 1000,
|
|
7
|
+
maxDelayMs: 10000,
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Retry wrapper with exponential backoff
|
|
11
|
+
*/
|
|
12
|
+
async function withRetry(operation, operationName) {
|
|
13
|
+
let lastError = null;
|
|
14
|
+
for (let attempt = 1; attempt <= RETRY_CONFIG.maxRetries; attempt++) {
|
|
15
|
+
try {
|
|
16
|
+
return await operation();
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
lastError = error;
|
|
20
|
+
const isRetryable = isRetryableError(error);
|
|
21
|
+
if (!isRetryable || attempt === RETRY_CONFIG.maxRetries) {
|
|
22
|
+
logger.error(`${operationName} failed after ${attempt} attempt(s)`, {
|
|
23
|
+
error: String(error),
|
|
24
|
+
attempt,
|
|
25
|
+
});
|
|
26
|
+
throw enhanceError(error, operationName);
|
|
27
|
+
}
|
|
28
|
+
const delay = Math.min(RETRY_CONFIG.baseDelayMs * Math.pow(2, attempt - 1), RETRY_CONFIG.maxDelayMs);
|
|
29
|
+
logger.warn(`${operationName} failed, retrying in ${delay}ms...`, {
|
|
30
|
+
attempt,
|
|
31
|
+
error: String(error),
|
|
32
|
+
});
|
|
33
|
+
await sleep(delay);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
throw lastError;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Check if an error is retryable
|
|
40
|
+
*/
|
|
41
|
+
function isRetryableError(error) {
|
|
42
|
+
if (error instanceof Error) {
|
|
43
|
+
const message = error.message.toLowerCase();
|
|
44
|
+
// Retry on network errors, rate limits, and server errors
|
|
45
|
+
return (message.includes("network") ||
|
|
46
|
+
message.includes("timeout") ||
|
|
47
|
+
message.includes("econnreset") ||
|
|
48
|
+
message.includes("rate limit") ||
|
|
49
|
+
message.includes("403") ||
|
|
50
|
+
message.includes("500") ||
|
|
51
|
+
message.includes("502") ||
|
|
52
|
+
message.includes("503") ||
|
|
53
|
+
message.includes("504"));
|
|
54
|
+
}
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Enhance error with more context
|
|
59
|
+
*/
|
|
60
|
+
function enhanceError(error, operation) {
|
|
61
|
+
const originalMessage = error instanceof Error ? error.message : String(error);
|
|
62
|
+
// Provide user-friendly error messages
|
|
63
|
+
if (originalMessage.includes("rate limit") ||
|
|
64
|
+
originalMessage.includes("403")) {
|
|
65
|
+
return new Error(`GitHub API rate limit exceeded during ${operation}. ` +
|
|
66
|
+
`Add a GITHUB_TOKEN to your config to increase limits from 60 to 5000 requests/hour.`);
|
|
67
|
+
}
|
|
68
|
+
if (originalMessage.includes("404")) {
|
|
69
|
+
return new Error(`Resource not found during ${operation}. ` +
|
|
70
|
+
`Check that the repository/file exists and is accessible.`);
|
|
71
|
+
}
|
|
72
|
+
if (originalMessage.includes("timeout") ||
|
|
73
|
+
originalMessage.includes("network")) {
|
|
74
|
+
return new Error(`Network error during ${operation}. ` +
|
|
75
|
+
`Check your internet connection and try again.`);
|
|
76
|
+
}
|
|
77
|
+
return new Error(`${operation} failed: ${originalMessage}`);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Sleep for a given number of milliseconds
|
|
81
|
+
*/
|
|
82
|
+
function sleep(ms) {
|
|
83
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
84
|
+
}
|
|
3
85
|
class SimpleCache {
|
|
4
86
|
cache = new Map();
|
|
5
87
|
ttlMs;
|
|
@@ -47,15 +129,8 @@ export class GitHubClient {
|
|
|
47
129
|
return cached;
|
|
48
130
|
}
|
|
49
131
|
try {
|
|
50
|
-
const { data: repoData } = await this.octokit.rest.repos.get({
|
|
51
|
-
|
|
52
|
-
repo,
|
|
53
|
-
});
|
|
54
|
-
const { data: commits } = await this.octokit.rest.repos.listCommits({
|
|
55
|
-
owner,
|
|
56
|
-
repo,
|
|
57
|
-
per_page: 1,
|
|
58
|
-
});
|
|
132
|
+
const { data: repoData } = await withRetry(() => this.octokit.rest.repos.get({ owner, repo }), `getRepositoryInfo(${owner}/${repo})`);
|
|
133
|
+
const { data: commits } = await withRetry(() => this.octokit.rest.repos.listCommits({ owner, repo, per_page: 1 }), `getCommits(${owner}/${repo})`);
|
|
59
134
|
const lastCommit = commits[0]
|
|
60
135
|
? {
|
|
61
136
|
sha: commits[0].sha,
|
|
@@ -93,12 +168,7 @@ export class GitHubClient {
|
|
|
93
168
|
return cached;
|
|
94
169
|
}
|
|
95
170
|
try {
|
|
96
|
-
const { data } = await this.octokit.rest.repos.getContent({
|
|
97
|
-
owner,
|
|
98
|
-
repo,
|
|
99
|
-
path,
|
|
100
|
-
ref,
|
|
101
|
-
});
|
|
171
|
+
const { data } = await withRetry(() => this.octokit.rest.repos.getContent({ owner, repo, path, ref }), `getFileContent(${owner}/${repo}/${path})`);
|
|
102
172
|
if (Array.isArray(data) || data.type !== "file") {
|
|
103
173
|
return null;
|
|
104
174
|
}
|
|
@@ -133,17 +203,17 @@ export class GitHubClient {
|
|
|
133
203
|
return cached;
|
|
134
204
|
}
|
|
135
205
|
try {
|
|
136
|
-
const { data: refData } = await this.octokit.rest.git.getRef({
|
|
206
|
+
const { data: refData } = await withRetry(() => this.octokit.rest.git.getRef({
|
|
137
207
|
owner,
|
|
138
208
|
repo,
|
|
139
209
|
ref: `heads/${ref || "main"}`,
|
|
140
|
-
});
|
|
141
|
-
const { data: treeData } = await this.octokit.rest.git.getTree({
|
|
210
|
+
}), `getRef(${owner}/${repo})`);
|
|
211
|
+
const { data: treeData } = await withRetry(() => this.octokit.rest.git.getTree({
|
|
142
212
|
owner,
|
|
143
213
|
repo,
|
|
144
214
|
tree_sha: refData.object.sha,
|
|
145
215
|
recursive: "true",
|
|
146
|
-
});
|
|
216
|
+
}), `getTree(${owner}/${repo})`);
|
|
147
217
|
const result = treeData.tree
|
|
148
218
|
.filter((item) => item.type === "blob" && item.path)
|
|
149
219
|
.map((item) => item.path);
|
|
@@ -282,6 +352,48 @@ export class GitHubClient {
|
|
|
282
352
|
return [];
|
|
283
353
|
}
|
|
284
354
|
}
|
|
355
|
+
/**
|
|
356
|
+
* Get current rate limit status from GitHub API
|
|
357
|
+
*/
|
|
358
|
+
async getRateLimit() {
|
|
359
|
+
try {
|
|
360
|
+
const { data } = await this.octokit.rest.rateLimit.get();
|
|
361
|
+
const rateLimit = {
|
|
362
|
+
limit: data.rate.limit,
|
|
363
|
+
remaining: data.rate.remaining,
|
|
364
|
+
reset: new Date(data.rate.reset * 1000),
|
|
365
|
+
used: data.rate.used,
|
|
366
|
+
};
|
|
367
|
+
// Update the global rate limit tracker
|
|
368
|
+
updateRateLimit(rateLimit);
|
|
369
|
+
return rateLimit;
|
|
370
|
+
}
|
|
371
|
+
catch (error) {
|
|
372
|
+
logger.warn("Failed to get rate limit", { error: String(error) });
|
|
373
|
+
// Return defaults if we can't get rate limit
|
|
374
|
+
return {
|
|
375
|
+
limit: 60,
|
|
376
|
+
remaining: 60,
|
|
377
|
+
reset: new Date(Date.now() + 3600000),
|
|
378
|
+
used: 0,
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Check if it's safe to make API requests
|
|
384
|
+
*/
|
|
385
|
+
checkRateLimit() {
|
|
386
|
+
const check = shouldProceedWithRequest();
|
|
387
|
+
const status = getRateLimitStatus();
|
|
388
|
+
if (!check.proceed) {
|
|
389
|
+
logger.warn("Rate limit check failed", { reason: check.reason });
|
|
390
|
+
}
|
|
391
|
+
return {
|
|
392
|
+
proceed: check.proceed,
|
|
393
|
+
reason: check.reason,
|
|
394
|
+
status,
|
|
395
|
+
};
|
|
396
|
+
}
|
|
285
397
|
}
|
|
286
398
|
export const githubClient = new GitHubClient();
|
|
287
399
|
//# sourceMappingURL=github.js.map
|
package/dist/server.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
2
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
3
|
import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
-
import { logger } from "./utils/index.js";
|
|
4
|
+
import { logger, formatErrorResponse } from "./utils/index.js";
|
|
5
5
|
import { vectorStore } from "./db/index.js";
|
|
6
6
|
import { allTools } from "./tools/index.js";
|
|
7
7
|
import { allResources, getDocumentation, getCode, getSchema, } from "./resources/index.js";
|
|
@@ -57,11 +57,19 @@ function registerToolHandlers(server) {
|
|
|
57
57
|
logger.info(`Tool called: ${name}`, { args });
|
|
58
58
|
const tool = allTools.find((t) => t.name === name);
|
|
59
59
|
if (!tool) {
|
|
60
|
+
const availableTools = allTools
|
|
61
|
+
.map((t) => t.name)
|
|
62
|
+
.slice(0, 5)
|
|
63
|
+
.join(", ");
|
|
60
64
|
return {
|
|
61
65
|
content: [
|
|
62
66
|
{
|
|
63
67
|
type: "text",
|
|
64
|
-
text:
|
|
68
|
+
text: JSON.stringify({
|
|
69
|
+
error: `Unknown tool: ${name}`,
|
|
70
|
+
suggestion: `Available tools include: ${availableTools}...`,
|
|
71
|
+
hint: "Use ListTools to see all available tools",
|
|
72
|
+
}, null, 2),
|
|
65
73
|
},
|
|
66
74
|
],
|
|
67
75
|
isError: true,
|
|
@@ -80,11 +88,12 @@ function registerToolHandlers(server) {
|
|
|
80
88
|
}
|
|
81
89
|
catch (error) {
|
|
82
90
|
logger.error(`Tool error: ${name}`, { error: String(error) });
|
|
91
|
+
const errorResponse = formatErrorResponse(error, `tool:${name}`);
|
|
83
92
|
return {
|
|
84
93
|
content: [
|
|
85
94
|
{
|
|
86
95
|
type: "text",
|
|
87
|
-
text:
|
|
96
|
+
text: JSON.stringify(errorResponse, null, 2),
|
|
88
97
|
},
|
|
89
98
|
],
|
|
90
99
|
isError: true,
|
|
@@ -129,12 +138,24 @@ function registerResourceHandlers(server) {
|
|
|
129
138
|
mimeType = "application/json";
|
|
130
139
|
}
|
|
131
140
|
if (!content) {
|
|
141
|
+
const resourceTypes = [
|
|
142
|
+
"midnight://docs/",
|
|
143
|
+
"midnight://code/",
|
|
144
|
+
"midnight://schema/",
|
|
145
|
+
];
|
|
146
|
+
const validPrefix = resourceTypes.find((p) => uri.startsWith(p));
|
|
132
147
|
return {
|
|
133
148
|
contents: [
|
|
134
149
|
{
|
|
135
150
|
uri,
|
|
136
|
-
mimeType: "
|
|
137
|
-
text:
|
|
151
|
+
mimeType: "application/json",
|
|
152
|
+
text: JSON.stringify({
|
|
153
|
+
error: `Resource not found: ${uri}`,
|
|
154
|
+
suggestion: validPrefix
|
|
155
|
+
? `Check the resource path after '${validPrefix}'`
|
|
156
|
+
: `Valid resource prefixes: ${resourceTypes.join(", ")}`,
|
|
157
|
+
hint: "Use ListResources to see all available resources",
|
|
158
|
+
}, null, 2),
|
|
138
159
|
},
|
|
139
160
|
],
|
|
140
161
|
};
|
|
@@ -151,12 +172,13 @@ function registerResourceHandlers(server) {
|
|
|
151
172
|
}
|
|
152
173
|
catch (error) {
|
|
153
174
|
logger.error(`Resource error: ${uri}`, { error: String(error) });
|
|
175
|
+
const errorResponse = formatErrorResponse(error, `resource:${uri}`);
|
|
154
176
|
return {
|
|
155
177
|
contents: [
|
|
156
178
|
{
|
|
157
179
|
uri,
|
|
158
|
-
mimeType: "
|
|
159
|
-
text:
|
|
180
|
+
mimeType: "application/json",
|
|
181
|
+
text: JSON.stringify(errorResponse, null, 2),
|
|
160
182
|
},
|
|
161
183
|
],
|
|
162
184
|
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Health check and diagnostic tools for MCP server
|
|
3
|
+
*/
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
export declare const HealthCheckInputSchema: z.ZodObject<{
|
|
6
|
+
detailed: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
7
|
+
}, "strip", z.ZodTypeAny, {
|
|
8
|
+
detailed: boolean;
|
|
9
|
+
}, {
|
|
10
|
+
detailed?: boolean | undefined;
|
|
11
|
+
}>;
|
|
12
|
+
export declare const GetStatusInputSchema: z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>;
|
|
13
|
+
export type HealthCheckInput = z.infer<typeof HealthCheckInputSchema>;
|
|
14
|
+
export type GetStatusInput = z.infer<typeof GetStatusInputSchema>;
|
|
15
|
+
/**
|
|
16
|
+
* Perform health check on the MCP server
|
|
17
|
+
*/
|
|
18
|
+
export declare function healthCheck(input: HealthCheckInput): Promise<{
|
|
19
|
+
rateLimit: string;
|
|
20
|
+
cacheStats: {
|
|
21
|
+
search: import("../utils/cache.js").CacheStats;
|
|
22
|
+
file: import("../utils/cache.js").CacheStats;
|
|
23
|
+
metadata: import("../utils/cache.js").CacheStats;
|
|
24
|
+
};
|
|
25
|
+
status: "healthy" | "degraded" | "unhealthy";
|
|
26
|
+
timestamp: string;
|
|
27
|
+
version: string;
|
|
28
|
+
uptime: number;
|
|
29
|
+
checks: {
|
|
30
|
+
name: string;
|
|
31
|
+
status: "pass" | "warn" | "fail";
|
|
32
|
+
message?: string;
|
|
33
|
+
latency?: number;
|
|
34
|
+
}[];
|
|
35
|
+
} | {
|
|
36
|
+
rateLimit: string;
|
|
37
|
+
status: "healthy" | "degraded" | "unhealthy";
|
|
38
|
+
version: string;
|
|
39
|
+
timestamp: string;
|
|
40
|
+
uptime: number;
|
|
41
|
+
checks: {
|
|
42
|
+
name: string;
|
|
43
|
+
status: "pass";
|
|
44
|
+
}[];
|
|
45
|
+
}>;
|
|
46
|
+
/**
|
|
47
|
+
* Get current server status and statistics
|
|
48
|
+
*/
|
|
49
|
+
export declare function getStatus(_input: GetStatusInput): Promise<{
|
|
50
|
+
server: string;
|
|
51
|
+
status: string;
|
|
52
|
+
timestamp: string;
|
|
53
|
+
rateLimit: {
|
|
54
|
+
remaining: number;
|
|
55
|
+
limit: number;
|
|
56
|
+
percentUsed: number;
|
|
57
|
+
status: string;
|
|
58
|
+
message: string;
|
|
59
|
+
};
|
|
60
|
+
cache: {
|
|
61
|
+
search: import("../utils/cache.js").CacheStats;
|
|
62
|
+
file: import("../utils/cache.js").CacheStats;
|
|
63
|
+
metadata: import("../utils/cache.js").CacheStats;
|
|
64
|
+
};
|
|
65
|
+
}>;
|
|
66
|
+
export declare const healthTools: ({
|
|
67
|
+
name: string;
|
|
68
|
+
description: string;
|
|
69
|
+
inputSchema: {
|
|
70
|
+
type: "object";
|
|
71
|
+
properties: {
|
|
72
|
+
detailed: {
|
|
73
|
+
type: string;
|
|
74
|
+
description: string;
|
|
75
|
+
default: boolean;
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
handler: typeof healthCheck;
|
|
80
|
+
} | {
|
|
81
|
+
name: string;
|
|
82
|
+
description: string;
|
|
83
|
+
inputSchema: {
|
|
84
|
+
type: "object";
|
|
85
|
+
properties: {
|
|
86
|
+
detailed?: undefined;
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
handler: typeof getStatus;
|
|
90
|
+
})[];
|
|
91
|
+
//# sourceMappingURL=health.d.ts.map
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Health check and diagnostic tools for MCP server
|
|
3
|
+
*/
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { getHealthStatus, getQuickHealthStatus, getRateLimitStatus, formatRateLimitStatus, } from "../utils/index.js";
|
|
6
|
+
import { searchCache, fileCache, metadataCache } from "../utils/cache.js";
|
|
7
|
+
// Schema definitions
|
|
8
|
+
export const HealthCheckInputSchema = z.object({
|
|
9
|
+
detailed: z
|
|
10
|
+
.boolean()
|
|
11
|
+
.optional()
|
|
12
|
+
.default(false)
|
|
13
|
+
.describe("Include detailed checks (slower but more comprehensive)"),
|
|
14
|
+
});
|
|
15
|
+
export const GetStatusInputSchema = z.object({});
|
|
16
|
+
/**
|
|
17
|
+
* Perform health check on the MCP server
|
|
18
|
+
*/
|
|
19
|
+
export async function healthCheck(input) {
|
|
20
|
+
if (input.detailed) {
|
|
21
|
+
const status = await getHealthStatus();
|
|
22
|
+
return {
|
|
23
|
+
...status,
|
|
24
|
+
rateLimit: formatRateLimitStatus(),
|
|
25
|
+
cacheStats: {
|
|
26
|
+
search: searchCache.getStats(),
|
|
27
|
+
file: fileCache.getStats(),
|
|
28
|
+
metadata: metadataCache.getStats(),
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
...getQuickHealthStatus(),
|
|
34
|
+
rateLimit: formatRateLimitStatus(),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Get current server status and statistics
|
|
39
|
+
*/
|
|
40
|
+
export async function getStatus(_input) {
|
|
41
|
+
const rateLimitStatus = getRateLimitStatus();
|
|
42
|
+
return {
|
|
43
|
+
server: "midnight-mcp",
|
|
44
|
+
status: "running",
|
|
45
|
+
timestamp: new Date().toISOString(),
|
|
46
|
+
rateLimit: {
|
|
47
|
+
remaining: rateLimitStatus.remaining,
|
|
48
|
+
limit: rateLimitStatus.limit,
|
|
49
|
+
percentUsed: rateLimitStatus.percentUsed,
|
|
50
|
+
status: rateLimitStatus.isLimited
|
|
51
|
+
? "limited"
|
|
52
|
+
: rateLimitStatus.isWarning
|
|
53
|
+
? "warning"
|
|
54
|
+
: "ok",
|
|
55
|
+
message: rateLimitStatus.message,
|
|
56
|
+
},
|
|
57
|
+
cache: {
|
|
58
|
+
search: searchCache.getStats(),
|
|
59
|
+
file: fileCache.getStats(),
|
|
60
|
+
metadata: metadataCache.getStats(),
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
// Tool definitions for MCP server
|
|
65
|
+
export const healthTools = [
|
|
66
|
+
{
|
|
67
|
+
name: "midnight-health-check",
|
|
68
|
+
description: "Check the health status of the Midnight MCP server. Returns server status, API connectivity, and resource availability.",
|
|
69
|
+
inputSchema: {
|
|
70
|
+
type: "object",
|
|
71
|
+
properties: {
|
|
72
|
+
detailed: {
|
|
73
|
+
type: "boolean",
|
|
74
|
+
description: "Include detailed checks including GitHub API and vector store status (slower)",
|
|
75
|
+
default: false,
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
handler: healthCheck,
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: "midnight-get-status",
|
|
83
|
+
description: "Get current server status including rate limits and cache statistics. Quick status check without external API calls.",
|
|
84
|
+
inputSchema: {
|
|
85
|
+
type: "object",
|
|
86
|
+
properties: {},
|
|
87
|
+
},
|
|
88
|
+
handler: getStatus,
|
|
89
|
+
},
|
|
90
|
+
];
|
|
91
|
+
//# sourceMappingURL=health.js.map
|
package/dist/tools/index.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
export { searchTools, searchCompact, searchTypeScript, searchDocs } from "./search.js";
|
|
2
|
-
export type { SearchCompactInput, SearchTypeScriptInput, SearchDocsInput } from "./search.js";
|
|
1
|
+
export { searchTools, searchCompact, searchTypeScript, searchDocs, } from "./search.js";
|
|
2
|
+
export type { SearchCompactInput, SearchTypeScriptInput, SearchDocsInput, } from "./search.js";
|
|
3
3
|
export { analyzeTools, analyzeContract, explainCircuit } from "./analyze.js";
|
|
4
4
|
export type { AnalyzeContractInput, ExplainCircuitInput } from "./analyze.js";
|
|
5
|
-
export { repositoryTools, getFile, listExamples, getLatestUpdates } from "./repository.js";
|
|
6
|
-
export type { GetFileInput, ListExamplesInput, GetLatestUpdatesInput } from "./repository.js";
|
|
5
|
+
export { repositoryTools, getFile, listExamples, getLatestUpdates, } from "./repository.js";
|
|
6
|
+
export type { GetFileInput, ListExamplesInput, GetLatestUpdatesInput, } from "./repository.js";
|
|
7
|
+
export { healthTools, healthCheck, getStatus } from "./health.js";
|
|
8
|
+
export type { HealthCheckInput, GetStatusInput } from "./health.js";
|
|
7
9
|
export declare const allTools: ({
|
|
8
10
|
name: string;
|
|
9
11
|
description: string;
|
|
@@ -388,5 +390,29 @@ export declare const allTools: ({
|
|
|
388
390
|
required: never[];
|
|
389
391
|
};
|
|
390
392
|
handler: typeof import("./repository.js").getLatestSyntax;
|
|
393
|
+
} | {
|
|
394
|
+
name: string;
|
|
395
|
+
description: string;
|
|
396
|
+
inputSchema: {
|
|
397
|
+
type: "object";
|
|
398
|
+
properties: {
|
|
399
|
+
detailed: {
|
|
400
|
+
type: string;
|
|
401
|
+
description: string;
|
|
402
|
+
default: boolean;
|
|
403
|
+
};
|
|
404
|
+
};
|
|
405
|
+
};
|
|
406
|
+
handler: typeof import("./health.js").healthCheck;
|
|
407
|
+
} | {
|
|
408
|
+
name: string;
|
|
409
|
+
description: string;
|
|
410
|
+
inputSchema: {
|
|
411
|
+
type: "object";
|
|
412
|
+
properties: {
|
|
413
|
+
detailed?: undefined;
|
|
414
|
+
};
|
|
415
|
+
};
|
|
416
|
+
handler: typeof import("./health.js").getStatus;
|
|
391
417
|
})[];
|
|
392
418
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/tools/index.js
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
|
-
export { searchTools, searchCompact, searchTypeScript, searchDocs } from "./search.js";
|
|
1
|
+
export { searchTools, searchCompact, searchTypeScript, searchDocs, } from "./search.js";
|
|
2
2
|
export { analyzeTools, analyzeContract, explainCircuit } from "./analyze.js";
|
|
3
|
-
export { repositoryTools, getFile, listExamples, getLatestUpdates } from "./repository.js";
|
|
3
|
+
export { repositoryTools, getFile, listExamples, getLatestUpdates, } from "./repository.js";
|
|
4
|
+
export { healthTools, healthCheck, getStatus } from "./health.js";
|
|
4
5
|
// Combined tool list for MCP server
|
|
5
6
|
import { searchTools } from "./search.js";
|
|
6
7
|
import { analyzeTools } from "./analyze.js";
|
|
7
8
|
import { repositoryTools } from "./repository.js";
|
|
8
|
-
|
|
9
|
+
import { healthTools } from "./health.js";
|
|
10
|
+
export const allTools = [
|
|
11
|
+
...searchTools,
|
|
12
|
+
...analyzeTools,
|
|
13
|
+
...repositoryTools,
|
|
14
|
+
...healthTools,
|
|
15
|
+
];
|
|
9
16
|
//# sourceMappingURL=index.js.map
|