midnight-mcp 0.0.8 → 0.1.0
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 +50 -20
- package/dist/server.d.ts +9 -0
- package/dist/server.js +151 -3
- package/dist/services/index.d.ts +6 -0
- package/dist/services/index.js +6 -0
- package/dist/services/sampling.d.ts +62 -0
- package/dist/services/sampling.js +256 -0
- package/dist/tools/analyze.d.ts +2 -36
- package/dist/tools/analyze.js +117 -0
- package/dist/tools/generation.d.ts +9 -0
- package/dist/tools/generation.js +282 -0
- package/dist/tools/health.d.ts +2 -25
- package/dist/tools/health.js +71 -0
- package/dist/tools/index.d.ts +4 -409
- package/dist/tools/index.js +3 -0
- package/dist/tools/repository.d.ts +2 -263
- package/dist/tools/repository.js +47 -0
- package/dist/tools/search.d.ts +2 -88
- package/dist/tools/search.js +72 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.js +2 -0
- package/dist/types/mcp.d.ts +167 -0
- package/dist/types/mcp.js +6 -0
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -91,6 +91,8 @@ By default, the MCP uses a **hosted API** for semantic search:
|
|
|
91
91
|
- ✅ **Semantic search** works immediately
|
|
92
92
|
- ✅ **No API keys** needed
|
|
93
93
|
|
|
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.
|
|
95
|
+
|
|
94
96
|
### Local Mode (Optional)
|
|
95
97
|
|
|
96
98
|
Run everything locally for privacy or offline use:
|
|
@@ -121,26 +123,42 @@ Add `"GITHUB_TOKEN": "ghp_..."` for higher GitHub API rate limits (60 → 5000 r
|
|
|
121
123
|
|
|
122
124
|
## Features
|
|
123
125
|
|
|
124
|
-
### Tools (
|
|
125
|
-
|
|
126
|
-
| Tool | Description
|
|
127
|
-
| --------------------------------- |
|
|
128
|
-
| `midnight-search-compact` | Search Compact contract code
|
|
129
|
-
| `midnight-search-typescript` | Search TypeScript SDK
|
|
130
|
-
| `midnight-search-docs` | Search documentation
|
|
131
|
-
| `midnight-analyze-contract` | Analyze contract structure and security
|
|
132
|
-
| `midnight-explain-circuit` | Explain circuits in plain language
|
|
133
|
-
| `midnight-get-file` | Get files from Midnight repos
|
|
134
|
-
| `midnight-list-examples` | List example contracts
|
|
135
|
-
| `midnight-get-latest-updates` | Recent repo changes
|
|
136
|
-
| `midnight-get-version-info` | Get version and release info
|
|
137
|
-
| `midnight-check-breaking-changes` | Check for breaking changes
|
|
138
|
-
| `midnight-get-migration-guide` | Migration guides between versions
|
|
139
|
-
| `midnight-get-file-at-version` | Get file at specific version
|
|
140
|
-
| `midnight-compare-syntax` | Compare files between versions
|
|
141
|
-
| `midnight-get-latest-syntax` | Latest syntax reference
|
|
142
|
-
| `midnight-health-check` | Check server health status
|
|
143
|
-
| `midnight-get-status` | Get rate limits and cache stats
|
|
126
|
+
### Tools (19)
|
|
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 |
|
|
146
|
+
| `midnight-generate-contract` ✨ | AI-generate contracts from natural language |
|
|
147
|
+
| `midnight-review-contract` ✨ | AI-powered security review of contracts |
|
|
148
|
+
| `midnight-document-contract` ✨ | AI-generate documentation for contracts |
|
|
149
|
+
|
|
150
|
+
> **✨ New 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.
|
|
151
|
+
|
|
152
|
+
### Resource Templates (4)
|
|
153
|
+
|
|
154
|
+
Dynamic access to any resource using URI templates:
|
|
155
|
+
|
|
156
|
+
| Template | Description |
|
|
157
|
+
| --------------------------------------- | ------------------------------ |
|
|
158
|
+
| `midnight://code/{owner}/{repo}/{path}` | Any code file from any repo |
|
|
159
|
+
| `midnight://docs/{section}/{topic}` | Documentation by section/topic |
|
|
160
|
+
| `midnight://examples/{category}/{name}` | Example contracts by category |
|
|
161
|
+
| `midnight://schema/{type}` | JSON schemas (AST, tx, proofs) |
|
|
144
162
|
|
|
145
163
|
### Resources (20)
|
|
146
164
|
|
|
@@ -188,6 +206,18 @@ MIDNIGHT_API_URL=http://localhost:8787 npm start
|
|
|
188
206
|
|
|
189
207
|
The hosted API runs on Cloudflare Workers + Vectorize. See [api/README.md](./api/README.md) for deployment and development instructions.
|
|
190
208
|
|
|
209
|
+
### Search Quality
|
|
210
|
+
|
|
211
|
+
The API indexes **25 Midnight repositories** including core infrastructure, SDK, examples, ZK libraries, and developer tools.
|
|
212
|
+
|
|
213
|
+
Search quality techniques:
|
|
214
|
+
|
|
215
|
+
- **Optimized chunking** — 1000-char chunks with 200-char overlap for precise, contextual results
|
|
216
|
+
- **Hybrid search** — Combines vector similarity with keyword boosting (up to 20% boost for exact matches)
|
|
217
|
+
- **Incremental indexing** — Daily updates via tarball download + batch embeddings (~5 min)
|
|
218
|
+
|
|
219
|
+
View live metrics at the [Dashboard](https://midnight-mcp-api.midnightmcp.workers.dev/dashboard).
|
|
220
|
+
|
|
191
221
|
## License
|
|
192
222
|
|
|
193
223
|
MIT
|
package/dist/server.d.ts
CHANGED
|
@@ -3,6 +3,15 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
|
3
3
|
* Create and configure the MCP server
|
|
4
4
|
*/
|
|
5
5
|
export declare function createServer(): Server;
|
|
6
|
+
/**
|
|
7
|
+
* Notify subscribers when a resource changes
|
|
8
|
+
* Call this when re-indexing or when docs are updated
|
|
9
|
+
*/
|
|
10
|
+
export declare function notifyResourceUpdate(server: Server, uri: string): void;
|
|
11
|
+
/**
|
|
12
|
+
* Get the list of active subscriptions
|
|
13
|
+
*/
|
|
14
|
+
export declare function getActiveSubscriptions(): string[];
|
|
6
15
|
/**
|
|
7
16
|
* Initialize the server and vector store
|
|
8
17
|
*/
|
package/dist/server.js
CHANGED
|
@@ -1,24 +1,60 @@
|
|
|
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
|
+
// Resource templates for parameterized resources (RFC 6570 URI Templates)
|
|
19
|
+
const resourceTemplates = [
|
|
20
|
+
{
|
|
21
|
+
uriTemplate: "midnight://code/{owner}/{repo}/{path}",
|
|
22
|
+
name: "Repository Code",
|
|
23
|
+
title: "📄 Repository Code Files",
|
|
24
|
+
description: "Access code files from any Midnight repository by specifying owner, repo, and file path",
|
|
25
|
+
mimeType: "text/plain",
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
uriTemplate: "midnight://docs/{section}/{topic}",
|
|
29
|
+
name: "Documentation",
|
|
30
|
+
title: "📚 Documentation Sections",
|
|
31
|
+
description: "Access documentation by section (guides, api, concepts) and topic",
|
|
32
|
+
mimeType: "text/markdown",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
uriTemplate: "midnight://examples/{category}/{name}",
|
|
36
|
+
name: "Example Contracts",
|
|
37
|
+
title: "📝 Example Contracts",
|
|
38
|
+
description: "Access example contracts by category (counter, bboard, token, voting) and name",
|
|
39
|
+
mimeType: "text/x-compact",
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
uriTemplate: "midnight://schema/{type}",
|
|
43
|
+
name: "Schema Definitions",
|
|
44
|
+
title: "🔧 Schema Definitions",
|
|
45
|
+
description: "Access JSON schemas for contract AST, transactions, and proofs",
|
|
46
|
+
mimeType: "application/json",
|
|
47
|
+
},
|
|
48
|
+
];
|
|
15
49
|
/**
|
|
16
50
|
* Create and configure the MCP server
|
|
17
51
|
*/
|
|
18
52
|
export function createServer() {
|
|
19
53
|
const server = new Server(SERVER_INFO, {
|
|
20
54
|
capabilities: {
|
|
21
|
-
tools: {
|
|
55
|
+
tools: {
|
|
56
|
+
listChanged: true,
|
|
57
|
+
},
|
|
22
58
|
resources: {
|
|
23
59
|
subscribe: true,
|
|
24
60
|
listChanged: true,
|
|
@@ -34,13 +70,17 @@ export function createServer() {
|
|
|
34
70
|
registerResourceHandlers(server);
|
|
35
71
|
// Register prompt handlers
|
|
36
72
|
registerPromptHandlers(server);
|
|
73
|
+
// Register subscription handlers
|
|
74
|
+
registerSubscriptionHandlers(server);
|
|
75
|
+
// Setup sampling callback if available
|
|
76
|
+
setupSampling(server);
|
|
37
77
|
return server;
|
|
38
78
|
}
|
|
39
79
|
/**
|
|
40
80
|
* Register tool handlers
|
|
41
81
|
*/
|
|
42
82
|
function registerToolHandlers(server) {
|
|
43
|
-
// List available tools
|
|
83
|
+
// List available tools with annotations and output schemas
|
|
44
84
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
45
85
|
logger.debug("Listing tools");
|
|
46
86
|
return {
|
|
@@ -48,6 +88,10 @@ function registerToolHandlers(server) {
|
|
|
48
88
|
name: tool.name,
|
|
49
89
|
description: tool.description,
|
|
50
90
|
inputSchema: tool.inputSchema,
|
|
91
|
+
// Include output schema if defined
|
|
92
|
+
...(tool.outputSchema && { outputSchema: tool.outputSchema }),
|
|
93
|
+
// Include annotations if defined
|
|
94
|
+
...(tool.annotations && { annotations: tool.annotations }),
|
|
51
95
|
})),
|
|
52
96
|
};
|
|
53
97
|
});
|
|
@@ -117,6 +161,19 @@ function registerResourceHandlers(server) {
|
|
|
117
161
|
})),
|
|
118
162
|
};
|
|
119
163
|
});
|
|
164
|
+
// List resource templates (RFC 6570 URI Templates)
|
|
165
|
+
server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => {
|
|
166
|
+
logger.debug("Listing resource templates");
|
|
167
|
+
return {
|
|
168
|
+
resourceTemplates: resourceTemplates.map((template) => ({
|
|
169
|
+
uriTemplate: template.uriTemplate,
|
|
170
|
+
name: template.name,
|
|
171
|
+
title: template.title,
|
|
172
|
+
description: template.description,
|
|
173
|
+
mimeType: template.mimeType,
|
|
174
|
+
})),
|
|
175
|
+
};
|
|
176
|
+
});
|
|
120
177
|
// Read resource content
|
|
121
178
|
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
122
179
|
const { uri } = request.params;
|
|
@@ -221,6 +278,97 @@ function registerPromptHandlers(server) {
|
|
|
221
278
|
};
|
|
222
279
|
});
|
|
223
280
|
}
|
|
281
|
+
/**
|
|
282
|
+
* Register resource subscription handlers
|
|
283
|
+
*/
|
|
284
|
+
function registerSubscriptionHandlers(server) {
|
|
285
|
+
// Handle subscribe requests
|
|
286
|
+
server.setRequestHandler(SubscribeRequestSchema, async (request) => {
|
|
287
|
+
const { uri } = request.params;
|
|
288
|
+
logger.info(`Subscribing to resource: ${uri}`);
|
|
289
|
+
// Validate that the URI is a valid resource
|
|
290
|
+
const validPrefixes = [
|
|
291
|
+
"midnight://docs/",
|
|
292
|
+
"midnight://code/",
|
|
293
|
+
"midnight://schema/",
|
|
294
|
+
];
|
|
295
|
+
const isValid = validPrefixes.some((prefix) => uri.startsWith(prefix));
|
|
296
|
+
if (!isValid) {
|
|
297
|
+
logger.warn(`Invalid subscription URI: ${uri}`);
|
|
298
|
+
return {};
|
|
299
|
+
}
|
|
300
|
+
resourceSubscriptions.add(uri);
|
|
301
|
+
logger.debug(`Active subscriptions: ${resourceSubscriptions.size}`);
|
|
302
|
+
return {};
|
|
303
|
+
});
|
|
304
|
+
// Handle unsubscribe requests
|
|
305
|
+
server.setRequestHandler(UnsubscribeRequestSchema, async (request) => {
|
|
306
|
+
const { uri } = request.params;
|
|
307
|
+
logger.info(`Unsubscribing from resource: ${uri}`);
|
|
308
|
+
resourceSubscriptions.delete(uri);
|
|
309
|
+
logger.debug(`Active subscriptions: ${resourceSubscriptions.size}`);
|
|
310
|
+
return {};
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Notify subscribers when a resource changes
|
|
315
|
+
* Call this when re-indexing or when docs are updated
|
|
316
|
+
*/
|
|
317
|
+
export function notifyResourceUpdate(server, uri) {
|
|
318
|
+
if (resourceSubscriptions.has(uri)) {
|
|
319
|
+
logger.info(`Notifying subscribers of update: ${uri}`);
|
|
320
|
+
// Send notification via the server
|
|
321
|
+
server.notification({
|
|
322
|
+
method: "notifications/resources/updated",
|
|
323
|
+
params: { uri },
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Get the list of active subscriptions
|
|
329
|
+
*/
|
|
330
|
+
export function getActiveSubscriptions() {
|
|
331
|
+
return Array.from(resourceSubscriptions);
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Setup sampling capability
|
|
335
|
+
* Registers a callback that allows the server to request LLM completions
|
|
336
|
+
*/
|
|
337
|
+
function setupSampling(server) {
|
|
338
|
+
// Create a sampling callback that uses the server's request method
|
|
339
|
+
const samplingCallback = async (request) => {
|
|
340
|
+
logger.debug("Requesting sampling from client", {
|
|
341
|
+
messageCount: request.messages.length,
|
|
342
|
+
maxTokens: request.maxTokens,
|
|
343
|
+
});
|
|
344
|
+
try {
|
|
345
|
+
// Request completion from the client
|
|
346
|
+
const response = await server.request({
|
|
347
|
+
method: "sampling/createMessage",
|
|
348
|
+
params: {
|
|
349
|
+
messages: request.messages,
|
|
350
|
+
systemPrompt: request.systemPrompt,
|
|
351
|
+
maxTokens: request.maxTokens || 2048,
|
|
352
|
+
temperature: request.temperature,
|
|
353
|
+
modelPreferences: request.modelPreferences,
|
|
354
|
+
},
|
|
355
|
+
},
|
|
356
|
+
// Use a schema that matches the expected response
|
|
357
|
+
{
|
|
358
|
+
parse: (data) => data,
|
|
359
|
+
_def: { typeName: "SamplingResponse" },
|
|
360
|
+
});
|
|
361
|
+
return response;
|
|
362
|
+
}
|
|
363
|
+
catch (error) {
|
|
364
|
+
logger.error("Sampling request failed", { error: String(error) });
|
|
365
|
+
throw error;
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
// Register the callback
|
|
369
|
+
registerSamplingCallback(samplingCallback);
|
|
370
|
+
logger.info("Sampling capability configured");
|
|
371
|
+
}
|
|
224
372
|
/**
|
|
225
373
|
* Initialize the server and vector store
|
|
226
374
|
*/
|
|
@@ -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
|
|
@@ -0,0 +1,256 @@
|
|
|
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 { logger } from "../utils/index.js";
|
|
11
|
+
// Store for the sampling callback
|
|
12
|
+
let samplingCallback = null;
|
|
13
|
+
/**
|
|
14
|
+
* Check if sampling is available
|
|
15
|
+
*/
|
|
16
|
+
export function isSamplingAvailable() {
|
|
17
|
+
return samplingCallback !== null;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Register the sampling callback from the client
|
|
21
|
+
* This is called during server initialization when client supports sampling
|
|
22
|
+
*/
|
|
23
|
+
export function registerSamplingCallback(callback) {
|
|
24
|
+
samplingCallback = callback;
|
|
25
|
+
logger.info("Sampling capability registered");
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Request a completion from the LLM via the client
|
|
29
|
+
*/
|
|
30
|
+
export async function requestCompletion(messages, options = {}) {
|
|
31
|
+
if (!samplingCallback) {
|
|
32
|
+
throw new Error("Sampling not available - client does not support this capability");
|
|
33
|
+
}
|
|
34
|
+
const request = {
|
|
35
|
+
messages: messages.map((m) => ({
|
|
36
|
+
role: m.role,
|
|
37
|
+
content: { type: "text", text: m.content },
|
|
38
|
+
})),
|
|
39
|
+
systemPrompt: options.systemPrompt,
|
|
40
|
+
maxTokens: options.maxTokens ?? 2048,
|
|
41
|
+
temperature: options.temperature ?? 0.7,
|
|
42
|
+
modelPreferences: options.modelPreferences ?? {
|
|
43
|
+
hints: [{ name: "claude-3-sonnet" }, { name: "gpt-4" }],
|
|
44
|
+
intelligencePriority: 0.8,
|
|
45
|
+
speedPriority: 0.5,
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
logger.debug("Requesting LLM completion", {
|
|
49
|
+
messageCount: messages.length,
|
|
50
|
+
maxTokens: request.maxTokens,
|
|
51
|
+
});
|
|
52
|
+
const response = await samplingCallback(request);
|
|
53
|
+
if (response.content.type !== "text") {
|
|
54
|
+
throw new Error("Unexpected response content type");
|
|
55
|
+
}
|
|
56
|
+
return response.content.text;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Generate a Compact contract based on requirements
|
|
60
|
+
*/
|
|
61
|
+
export async function generateContract(requirements, options = {}) {
|
|
62
|
+
if (!isSamplingAvailable()) {
|
|
63
|
+
return {
|
|
64
|
+
code: "",
|
|
65
|
+
explanation: "Sampling not available - this feature requires a client that supports the sampling capability (like Claude Desktop)",
|
|
66
|
+
warnings: ["Feature unavailable without sampling support"],
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
const systemPrompt = `You are an expert Compact smart contract developer for the Midnight blockchain.
|
|
70
|
+
Your task is to generate secure, well-documented Compact contracts based on user requirements.
|
|
71
|
+
|
|
72
|
+
Key Compact concepts:
|
|
73
|
+
- \`ledger { }\` - Defines on-chain state (public and private)
|
|
74
|
+
- \`@private\` - Marks state as private/shielded
|
|
75
|
+
- \`export circuit\` - Public functions that generate ZK proofs
|
|
76
|
+
- \`witness\` - Off-chain computation functions
|
|
77
|
+
- \`assert()\` - Creates ZK constraints
|
|
78
|
+
- \`Counter\`, \`Map<K,V>\`, \`Set<T>\` - Built-in collection types
|
|
79
|
+
- \`Field\`, \`Boolean\`, \`Uint<N>\`, \`Bytes<N>\` - Primitive types
|
|
80
|
+
|
|
81
|
+
Always include:
|
|
82
|
+
1. Proper imports (include "std")
|
|
83
|
+
2. Clear ledger state definitions
|
|
84
|
+
3. Access control where appropriate
|
|
85
|
+
4. Comprehensive inline comments
|
|
86
|
+
|
|
87
|
+
Return ONLY the Compact code, no explanations.`;
|
|
88
|
+
const userPrompt = options.baseExample
|
|
89
|
+
? `Based on this example contract:
|
|
90
|
+
\`\`\`compact
|
|
91
|
+
${options.baseExample}
|
|
92
|
+
\`\`\`
|
|
93
|
+
|
|
94
|
+
Generate a new contract with these requirements:
|
|
95
|
+
${requirements}`
|
|
96
|
+
: `Generate a Compact smart contract with these requirements:
|
|
97
|
+
${requirements}
|
|
98
|
+
|
|
99
|
+
Contract type: ${options.contractType || "custom"}`;
|
|
100
|
+
try {
|
|
101
|
+
const code = await requestCompletion([{ role: "user", content: userPrompt }], {
|
|
102
|
+
systemPrompt,
|
|
103
|
+
maxTokens: 4096,
|
|
104
|
+
temperature: 0.3, // Lower temperature for code generation
|
|
105
|
+
modelPreferences: {
|
|
106
|
+
hints: [{ name: "claude-3-sonnet" }],
|
|
107
|
+
intelligencePriority: 0.9,
|
|
108
|
+
speedPriority: 0.3,
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
// Extract code from markdown if wrapped
|
|
112
|
+
const extractedCode = code.includes("```")
|
|
113
|
+
? code
|
|
114
|
+
.replace(/```compact?\n?/g, "")
|
|
115
|
+
.replace(/```/g, "")
|
|
116
|
+
.trim()
|
|
117
|
+
: code.trim();
|
|
118
|
+
// Generate explanation
|
|
119
|
+
const explanation = await requestCompletion([
|
|
120
|
+
{
|
|
121
|
+
role: "user",
|
|
122
|
+
content: `Briefly explain what this Compact contract does (2-3 sentences):
|
|
123
|
+
\`\`\`compact
|
|
124
|
+
${extractedCode}
|
|
125
|
+
\`\`\``,
|
|
126
|
+
},
|
|
127
|
+
], {
|
|
128
|
+
systemPrompt: "You are a Compact contract documentation expert. Be concise.",
|
|
129
|
+
maxTokens: 256,
|
|
130
|
+
temperature: 0.5,
|
|
131
|
+
});
|
|
132
|
+
return {
|
|
133
|
+
code: extractedCode,
|
|
134
|
+
explanation: explanation.trim(),
|
|
135
|
+
warnings: [],
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
logger.error("Contract generation failed", { error: String(error) });
|
|
140
|
+
return {
|
|
141
|
+
code: "",
|
|
142
|
+
explanation: `Contract generation failed: ${String(error)}`,
|
|
143
|
+
warnings: ["Generation failed - check logs for details"],
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Review contract code and suggest improvements
|
|
149
|
+
*/
|
|
150
|
+
export async function reviewContract(code) {
|
|
151
|
+
if (!isSamplingAvailable()) {
|
|
152
|
+
return {
|
|
153
|
+
summary: "Code review requires sampling capability",
|
|
154
|
+
issues: [
|
|
155
|
+
{
|
|
156
|
+
severity: "info",
|
|
157
|
+
message: "This feature requires a client that supports the sampling capability",
|
|
158
|
+
},
|
|
159
|
+
],
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
const systemPrompt = `You are a Compact smart contract security auditor.
|
|
163
|
+
Review the provided contract for:
|
|
164
|
+
1. Security vulnerabilities
|
|
165
|
+
2. Privacy concerns (improper handling of shielded state)
|
|
166
|
+
3. Logic errors
|
|
167
|
+
4. Best practice violations
|
|
168
|
+
5. Gas/performance issues
|
|
169
|
+
|
|
170
|
+
Respond in JSON format:
|
|
171
|
+
{
|
|
172
|
+
"summary": "Brief summary of the contract and overall quality",
|
|
173
|
+
"issues": [
|
|
174
|
+
{
|
|
175
|
+
"severity": "error|warning|info",
|
|
176
|
+
"line": optional_line_number,
|
|
177
|
+
"message": "Description of the issue",
|
|
178
|
+
"suggestion": "How to fix it"
|
|
179
|
+
}
|
|
180
|
+
],
|
|
181
|
+
"improvedCode": "Full improved contract code if changes are needed"
|
|
182
|
+
}`;
|
|
183
|
+
try {
|
|
184
|
+
const response = await requestCompletion([
|
|
185
|
+
{
|
|
186
|
+
role: "user",
|
|
187
|
+
content: `Review this Compact contract:\n\`\`\`compact\n${code}\n\`\`\``,
|
|
188
|
+
},
|
|
189
|
+
], {
|
|
190
|
+
systemPrompt,
|
|
191
|
+
maxTokens: 4096,
|
|
192
|
+
temperature: 0.2,
|
|
193
|
+
});
|
|
194
|
+
// Parse JSON response
|
|
195
|
+
const jsonMatch = response.match(/\{[\s\S]*\}/);
|
|
196
|
+
if (jsonMatch) {
|
|
197
|
+
return JSON.parse(jsonMatch[0]);
|
|
198
|
+
}
|
|
199
|
+
return {
|
|
200
|
+
summary: response,
|
|
201
|
+
issues: [],
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
logger.error("Contract review failed", { error: String(error) });
|
|
206
|
+
return {
|
|
207
|
+
summary: `Review failed: ${String(error)}`,
|
|
208
|
+
issues: [
|
|
209
|
+
{
|
|
210
|
+
severity: "error",
|
|
211
|
+
message: "Review failed - check logs for details",
|
|
212
|
+
},
|
|
213
|
+
],
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Generate documentation for a contract
|
|
219
|
+
*/
|
|
220
|
+
export async function generateDocumentation(code, format = "markdown") {
|
|
221
|
+
if (!isSamplingAvailable()) {
|
|
222
|
+
return "Documentation generation requires sampling capability";
|
|
223
|
+
}
|
|
224
|
+
const systemPrompt = format === "markdown"
|
|
225
|
+
? `Generate comprehensive Markdown documentation for this Compact contract.
|
|
226
|
+
Include:
|
|
227
|
+
- Overview and purpose
|
|
228
|
+
- State variables (with privacy annotations)
|
|
229
|
+
- Circuit functions with parameters and effects
|
|
230
|
+
- Witness functions
|
|
231
|
+
- Usage examples
|
|
232
|
+
- Security considerations`
|
|
233
|
+
: `Generate JSDoc-style documentation comments for this Compact contract.
|
|
234
|
+
Add documentation comments above each:
|
|
235
|
+
- Ledger field
|
|
236
|
+
- Circuit function
|
|
237
|
+
- Witness function
|
|
238
|
+
- Type definition`;
|
|
239
|
+
try {
|
|
240
|
+
return await requestCompletion([
|
|
241
|
+
{
|
|
242
|
+
role: "user",
|
|
243
|
+
content: `Generate ${format} documentation for:\n\`\`\`compact\n${code}\n\`\`\``,
|
|
244
|
+
},
|
|
245
|
+
], {
|
|
246
|
+
systemPrompt,
|
|
247
|
+
maxTokens: 4096,
|
|
248
|
+
temperature: 0.5,
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
catch (error) {
|
|
252
|
+
logger.error("Documentation generation failed", { error: String(error) });
|
|
253
|
+
return `Documentation generation failed: ${String(error)}`;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
//# sourceMappingURL=sampling.js.map
|