midnight-mcp 0.0.9 → 0.1.1

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 CHANGED
@@ -26,7 +26,7 @@ Add to your `claude_desktop_config.json`:
26
26
  ```
27
27
 
28
28
  <details>
29
- <summary><strong>📍 Config file locations</strong></summary>
29
+ <summary><strong>Config file locations</strong></summary>
30
30
 
31
31
  - **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
32
32
  - **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
@@ -36,6 +36,13 @@ Add to your `claude_desktop_config.json`:
36
36
 
37
37
  ### Cursor
38
38
 
39
+ **Click the button to auto install the MCP for Cursor**
40
+
41
+ [![Install MCP Server](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en-US/install-mcp?name=midnight&config=eyJjb21tYW5kIjoibnB4IC15IG1pZG5pZ2h0LW1jcCJ9)
42
+
43
+ ---
44
+ **Manual setup instructions are below if you need them:**
45
+
39
46
  Add to Cursor's MCP settings (Settings → MCP → Add Server):
40
47
 
41
48
  ```json
@@ -62,6 +69,8 @@ Or add to `.cursor/mcp.json` in your project:
62
69
  }
63
70
  ```
64
71
 
72
+
73
+
65
74
  ### Windsurf
66
75
 
67
76
  Add to `~/.codeium/windsurf/mcp_config.json`:
@@ -87,11 +96,11 @@ Restart your editor after adding the config. All features work out of the box—
87
96
 
88
97
  By default, the MCP uses a **hosted API** for semantic search:
89
98
 
90
- - **Zero configuration** — just install and use
91
- - **Semantic search** works immediately
92
- - **No API keys** needed
99
+ - **Zero configuration** — just install and use
100
+ - **Semantic search** works immediately
101
+ - **No API keys** needed
93
102
 
94
- > **📊 Quality Metrics**: To ensure the MCP stays accurate as Midnight's codebase evolves rapidly, we collect anonymous usage metrics (query counts, relevance scores) to monitor search quality. No query content or personal data is stored. This helps us identify when re-indexing is needed and improve results over time.
103
+ > **Quality Metrics**: To ensure the MCP stays accurate as Midnight's codebase evolves rapidly, we collect anonymous usage metrics (query counts, relevance scores) to monitor search quality. No query content or personal data is stored. This helps us identify when re-indexing is needed and improve results over time.
95
104
 
96
105
  ### Local Mode (Optional)
97
106
 
@@ -123,26 +132,42 @@ Add `"GITHUB_TOKEN": "ghp_..."` for higher GitHub API rate limits (60 → 5000 r
123
132
 
124
133
  ## Features
125
134
 
126
- ### Tools (16)
127
-
128
- | Tool | Description |
129
- | --------------------------------- | --------------------------------------- |
130
- | `midnight-search-compact` | Search Compact contract code |
131
- | `midnight-search-typescript` | Search TypeScript SDK |
132
- | `midnight-search-docs` | Search documentation |
133
- | `midnight-analyze-contract` | Analyze contract structure and security |
134
- | `midnight-explain-circuit` | Explain circuits in plain language |
135
- | `midnight-get-file` | Get files from Midnight repos |
136
- | `midnight-list-examples` | List example contracts |
137
- | `midnight-get-latest-updates` | Recent repo changes |
138
- | `midnight-get-version-info` | Get version and release info |
139
- | `midnight-check-breaking-changes` | Check for breaking changes |
140
- | `midnight-get-migration-guide` | Migration guides between versions |
141
- | `midnight-get-file-at-version` | Get file at specific version |
142
- | `midnight-compare-syntax` | Compare files between versions |
143
- | `midnight-get-latest-syntax` | Latest syntax reference |
144
- | `midnight-health-check` | Check server health status |
145
- | `midnight-get-status` | Get rate limits and cache stats |
135
+ ### Tools (19)
136
+
137
+ | Tool | Description |
138
+ | --------------------------------- | ------------------------------------------- |
139
+ | `midnight-search-compact` | Search Compact contract code |
140
+ | `midnight-search-typescript` | Search TypeScript SDK |
141
+ | `midnight-search-docs` | Search documentation |
142
+ | `midnight-analyze-contract` | Analyze contract structure and security |
143
+ | `midnight-explain-circuit` | Explain circuits in plain language |
144
+ | `midnight-get-file` | Get files from Midnight repos |
145
+ | `midnight-list-examples` | List example contracts |
146
+ | `midnight-get-latest-updates` | Recent repo changes |
147
+ | `midnight-get-version-info` | Get version and release info |
148
+ | `midnight-check-breaking-changes` | Check for breaking changes |
149
+ | `midnight-get-migration-guide` | Migration guides between versions |
150
+ | `midnight-get-file-at-version` | Get file at specific version |
151
+ | `midnight-compare-syntax` | Compare files between versions |
152
+ | `midnight-get-latest-syntax` | Latest syntax reference |
153
+ | `midnight-health-check` | Check server health status |
154
+ | `midnight-get-status` | Get rate limits and cache stats |
155
+ | `midnight-generate-contract` | AI-generate contracts from natural language |
156
+ | `midnight-review-contract` | AI-powered security review of contracts |
157
+ | `midnight-document-contract` | AI-generate documentation for contracts |
158
+
159
+ > **Note:** The AI-powered tools require a client with [MCP Sampling](https://spec.modelcontextprotocol.io/specification/client/sampling/) support (e.g., Claude Desktop). Without sampling, these tools will return a helpful message instead.
160
+
161
+ ### Resource Templates (4)
162
+
163
+ Dynamic access to any resource using URI templates:
164
+
165
+ | Template | Description |
166
+ | --------------------------------------- | ------------------------------ |
167
+ | `midnight://code/{owner}/{repo}/{path}` | Any code file from any repo |
168
+ | `midnight://docs/{section}/{topic}` | Documentation by section/topic |
169
+ | `midnight://examples/{category}/{name}` | Example contracts by category |
170
+ | `midnight://schema/{type}` | JSON schemas (AST, tx, proofs) |
146
171
 
147
172
  ### Resources (20)
148
173
 
@@ -192,7 +217,9 @@ The hosted API runs on Cloudflare Workers + Vectorize. See [api/README.md](./api
192
217
 
193
218
  ### Search Quality
194
219
 
195
- The API uses several techniques to improve search relevance:
220
+ The API indexes **25 Midnight repositories** including core infrastructure, SDK, examples, ZK libraries, and developer tools.
221
+
222
+ Search quality techniques:
196
223
 
197
224
  - **Optimized chunking** — 1000-char chunks with 200-char overlap for precise, contextual results
198
225
  - **Hybrid search** — Combines vector similarity with keyword boosting (up to 20% boost for exact matches)
package/dist/server.d.ts CHANGED
@@ -1,8 +1,21 @@
1
1
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
+ /**
3
+ * Clear all subscriptions (useful for server restart/testing)
4
+ */
5
+ export declare function clearSubscriptions(): void;
2
6
  /**
3
7
  * Create and configure the MCP server
4
8
  */
5
9
  export declare function createServer(): Server;
10
+ /**
11
+ * Notify subscribers when a resource changes
12
+ * Call this when re-indexing or when docs are updated
13
+ */
14
+ export declare function notifyResourceUpdate(server: Server, uri: string): void;
15
+ /**
16
+ * Get the list of active subscriptions
17
+ */
18
+ export declare function getActiveSubscriptions(): string[];
6
19
  /**
7
20
  * Initialize the server and vector store
8
21
  */
package/dist/server.js CHANGED
@@ -1,24 +1,67 @@
1
1
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
2
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
- import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
3
+ import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, ListResourceTemplatesRequestSchema, SubscribeRequestSchema, UnsubscribeRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
4
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";
8
8
  import { promptDefinitions, generatePrompt } from "./prompts/index.js";
9
+ import { registerSamplingCallback } from "./services/index.js";
9
10
  // Server information
10
11
  const SERVER_INFO = {
11
12
  name: "midnight-mcp",
12
13
  version: "1.0.0",
13
14
  description: "MCP Server for Midnight Blockchain Development",
14
15
  };
16
+ // Resource subscriptions tracking
17
+ const resourceSubscriptions = new Set();
18
+ /**
19
+ * Clear all subscriptions (useful for server restart/testing)
20
+ */
21
+ export function clearSubscriptions() {
22
+ resourceSubscriptions.clear();
23
+ logger.debug("Subscriptions cleared");
24
+ }
25
+ // Resource templates for parameterized resources (RFC 6570 URI Templates)
26
+ const resourceTemplates = [
27
+ {
28
+ uriTemplate: "midnight://code/{owner}/{repo}/{path}",
29
+ name: "Repository Code",
30
+ title: "📄 Repository Code Files",
31
+ description: "Access code files from any Midnight repository by specifying owner, repo, and file path",
32
+ mimeType: "text/plain",
33
+ },
34
+ {
35
+ uriTemplate: "midnight://docs/{section}/{topic}",
36
+ name: "Documentation",
37
+ title: "📚 Documentation Sections",
38
+ description: "Access documentation by section (guides, api, concepts) and topic",
39
+ mimeType: "text/markdown",
40
+ },
41
+ {
42
+ uriTemplate: "midnight://examples/{category}/{name}",
43
+ name: "Example Contracts",
44
+ title: "📝 Example Contracts",
45
+ description: "Access example contracts by category (counter, bboard, token, voting) and name",
46
+ mimeType: "text/x-compact",
47
+ },
48
+ {
49
+ uriTemplate: "midnight://schema/{type}",
50
+ name: "Schema Definitions",
51
+ title: "🔧 Schema Definitions",
52
+ description: "Access JSON schemas for contract AST, transactions, and proofs",
53
+ mimeType: "application/json",
54
+ },
55
+ ];
15
56
  /**
16
57
  * Create and configure the MCP server
17
58
  */
18
59
  export function createServer() {
19
60
  const server = new Server(SERVER_INFO, {
20
61
  capabilities: {
21
- tools: {},
62
+ tools: {
63
+ listChanged: true,
64
+ },
22
65
  resources: {
23
66
  subscribe: true,
24
67
  listChanged: true,
@@ -34,13 +77,17 @@ export function createServer() {
34
77
  registerResourceHandlers(server);
35
78
  // Register prompt handlers
36
79
  registerPromptHandlers(server);
80
+ // Register subscription handlers
81
+ registerSubscriptionHandlers(server);
82
+ // Setup sampling callback if available
83
+ setupSampling(server);
37
84
  return server;
38
85
  }
39
86
  /**
40
87
  * Register tool handlers
41
88
  */
42
89
  function registerToolHandlers(server) {
43
- // List available tools
90
+ // List available tools with annotations and output schemas
44
91
  server.setRequestHandler(ListToolsRequestSchema, async () => {
45
92
  logger.debug("Listing tools");
46
93
  return {
@@ -48,6 +95,10 @@ function registerToolHandlers(server) {
48
95
  name: tool.name,
49
96
  description: tool.description,
50
97
  inputSchema: tool.inputSchema,
98
+ // Include output schema if defined
99
+ ...(tool.outputSchema && { outputSchema: tool.outputSchema }),
100
+ // Include annotations if defined
101
+ ...(tool.annotations && { annotations: tool.annotations }),
51
102
  })),
52
103
  };
53
104
  });
@@ -117,6 +168,19 @@ function registerResourceHandlers(server) {
117
168
  })),
118
169
  };
119
170
  });
171
+ // List resource templates (RFC 6570 URI Templates)
172
+ server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => {
173
+ logger.debug("Listing resource templates");
174
+ return {
175
+ resourceTemplates: resourceTemplates.map((template) => ({
176
+ uriTemplate: template.uriTemplate,
177
+ name: template.name,
178
+ title: template.title,
179
+ description: template.description,
180
+ mimeType: template.mimeType,
181
+ })),
182
+ };
183
+ });
120
184
  // Read resource content
121
185
  server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
122
186
  const { uri } = request.params;
@@ -221,6 +285,107 @@ function registerPromptHandlers(server) {
221
285
  };
222
286
  });
223
287
  }
288
+ /**
289
+ * Register resource subscription handlers
290
+ */
291
+ function registerSubscriptionHandlers(server) {
292
+ // Handle subscribe requests
293
+ server.setRequestHandler(SubscribeRequestSchema, async (request) => {
294
+ const { uri } = request.params;
295
+ logger.info(`Subscribing to resource: ${uri}`);
296
+ // Validate that the URI is a valid resource
297
+ const validPrefixes = [
298
+ "midnight://docs/",
299
+ "midnight://code/",
300
+ "midnight://schema/",
301
+ ];
302
+ const isValid = validPrefixes.some((prefix) => uri.startsWith(prefix));
303
+ if (!isValid) {
304
+ logger.warn(`Invalid subscription URI: ${uri}`);
305
+ throw new Error(`Invalid subscription URI: ${uri}. Valid prefixes: ${validPrefixes.join(", ")}`);
306
+ }
307
+ resourceSubscriptions.add(uri);
308
+ logger.debug(`Active subscriptions: ${resourceSubscriptions.size}`);
309
+ return {};
310
+ });
311
+ // Handle unsubscribe requests
312
+ server.setRequestHandler(UnsubscribeRequestSchema, async (request) => {
313
+ const { uri } = request.params;
314
+ logger.info(`Unsubscribing from resource: ${uri}`);
315
+ resourceSubscriptions.delete(uri);
316
+ logger.debug(`Active subscriptions: ${resourceSubscriptions.size}`);
317
+ return {};
318
+ });
319
+ }
320
+ /**
321
+ * Notify subscribers when a resource changes
322
+ * Call this when re-indexing or when docs are updated
323
+ */
324
+ export function notifyResourceUpdate(server, uri) {
325
+ if (resourceSubscriptions.has(uri)) {
326
+ logger.info(`Notifying subscribers of update: ${uri}`);
327
+ // Send notification via the server
328
+ server.notification({
329
+ method: "notifications/resources/updated",
330
+ params: { uri },
331
+ });
332
+ }
333
+ }
334
+ /**
335
+ * Get the list of active subscriptions
336
+ */
337
+ export function getActiveSubscriptions() {
338
+ return Array.from(resourceSubscriptions);
339
+ }
340
+ /**
341
+ * Setup sampling capability
342
+ * Registers a callback that allows the server to request LLM completions
343
+ */
344
+ function setupSampling(server) {
345
+ // Create a sampling callback that uses the server's request method
346
+ const samplingCallback = async (request) => {
347
+ logger.debug("Requesting sampling from client", {
348
+ messageCount: request.messages.length,
349
+ maxTokens: request.maxTokens,
350
+ });
351
+ try {
352
+ // Request completion from the client
353
+ const response = await server.request({
354
+ method: "sampling/createMessage",
355
+ params: {
356
+ messages: request.messages,
357
+ systemPrompt: request.systemPrompt,
358
+ maxTokens: request.maxTokens || 2048,
359
+ temperature: request.temperature,
360
+ modelPreferences: request.modelPreferences,
361
+ },
362
+ },
363
+ // Use a schema that matches the expected response
364
+ {
365
+ parse: (data) => {
366
+ const response = data;
367
+ // Basic validation of expected response structure
368
+ if (!response || typeof response !== "object") {
369
+ throw new Error("Invalid sampling response: expected object");
370
+ }
371
+ if (!response.content || typeof response.content !== "object") {
372
+ throw new Error("Invalid sampling response: missing content");
373
+ }
374
+ return response;
375
+ },
376
+ _def: { typeName: "SamplingResponse" },
377
+ });
378
+ return response;
379
+ }
380
+ catch (error) {
381
+ logger.error("Sampling request failed", { error: String(error) });
382
+ throw error;
383
+ }
384
+ };
385
+ // Register the callback
386
+ registerSamplingCallback(samplingCallback);
387
+ logger.info("Sampling capability configured");
388
+ }
224
389
  /**
225
390
  * Initialize the server and vector store
226
391
  */
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Services index
3
+ * Export all service modules
4
+ */
5
+ export { isSamplingAvailable, registerSamplingCallback, requestCompletion, generateContract, reviewContract, generateDocumentation, } from "./sampling.js";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Services index
3
+ * Export all service modules
4
+ */
5
+ export { isSamplingAvailable, registerSamplingCallback, requestCompletion, generateContract, reviewContract, generateDocumentation, } from "./sampling.js";
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Sampling service for agentic workflows
3
+ *
4
+ * Enables the MCP server to request LLM completions from the client,
5
+ * allowing for sophisticated multi-step workflows like:
6
+ * - Auto-generating contracts from examples
7
+ * - Code review with suggested fixes
8
+ * - Documentation generation
9
+ */
10
+ import type { SamplingRequest, SamplingResponse, ModelPreferences } from "../types/index.js";
11
+ type SamplingCallback = (request: SamplingRequest) => Promise<SamplingResponse>;
12
+ /**
13
+ * Check if sampling is available
14
+ */
15
+ export declare function isSamplingAvailable(): boolean;
16
+ /**
17
+ * Register the sampling callback from the client
18
+ * This is called during server initialization when client supports sampling
19
+ */
20
+ export declare function registerSamplingCallback(callback: SamplingCallback): void;
21
+ /**
22
+ * Request a completion from the LLM via the client
23
+ */
24
+ export declare function requestCompletion(messages: {
25
+ role: "user" | "assistant";
26
+ content: string;
27
+ }[], options?: {
28
+ systemPrompt?: string;
29
+ maxTokens?: number;
30
+ temperature?: number;
31
+ modelPreferences?: ModelPreferences;
32
+ }): Promise<string>;
33
+ /**
34
+ * Generate a Compact contract based on requirements
35
+ */
36
+ export declare function generateContract(requirements: string, options?: {
37
+ baseExample?: string;
38
+ contractType?: "counter" | "token" | "voting" | "custom";
39
+ }): Promise<{
40
+ code: string;
41
+ explanation: string;
42
+ warnings: string[];
43
+ }>;
44
+ /**
45
+ * Review contract code and suggest improvements
46
+ */
47
+ export declare function reviewContract(code: string): Promise<{
48
+ summary: string;
49
+ issues: Array<{
50
+ severity: "error" | "warning" | "info";
51
+ line?: number;
52
+ message: string;
53
+ suggestion?: string;
54
+ }>;
55
+ improvedCode?: string;
56
+ }>;
57
+ /**
58
+ * Generate documentation for a contract
59
+ */
60
+ export declare function generateDocumentation(code: string, format?: "markdown" | "jsdoc"): Promise<string>;
61
+ export {};
62
+ //# sourceMappingURL=sampling.d.ts.map