midnight-mcp 0.1.29 → 0.1.30

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
@@ -105,6 +105,18 @@ npx clear-npx-cache
105
105
 
106
106
  All tools are prefixed with `midnight-` (e.g., `midnight-search-compact`).
107
107
 
108
+ ### MCP Capabilities
109
+
110
+ | Capability | Feature |
111
+ | --------------- | ----------------------------------------------- |
112
+ | **Tools** | 25 tools with `listChanged` notifications |
113
+ | **Resources** | 9 embedded resources with subscription support |
114
+ | **Prompts** | 5 workflow prompts |
115
+ | **Logging** | Client-controllable log level |
116
+ | **Completions** | Autocomplete for prompt arguments |
117
+ | **Progress** | Real-time progress for compound tools |
118
+ | **Sampling** | AI-powered generation (when client supports it) |
119
+
108
120
  ### 9 Embedded Resources
109
121
 
110
122
  Quick references available offline:
package/dist/server.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
+ import { LoggingLevel } from "@modelcontextprotocol/sdk/types.js";
2
3
  /**
3
4
  * Get update warning if outdated (to include in responses)
4
5
  */
@@ -8,8 +9,15 @@ export declare function getUpdateWarning(): string | null;
8
9
  */
9
10
  export declare function clearSubscriptions(): void;
10
11
  /**
11
- * Create and configure the MCP server
12
+ * Send a log message to the MCP client
13
+ * This allows clients to see server logs for debugging
12
14
  */
15
+ export declare function sendLogToClient(level: LoggingLevel, loggerName: string, data: unknown): void;
16
+ /**
17
+ * Send a progress notification to the MCP client
18
+ * Used for long-running operations like compound tools
19
+ */
20
+ export declare function sendProgressNotification(progressToken: string | number, progress: number, total?: number, message?: string): void;
13
21
  export declare function createServer(): Server;
14
22
  /**
15
23
  * Notify subscribers when a resource changes
package/dist/server.js CHANGED
@@ -1,14 +1,14 @@
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, ListResourceTemplatesRequestSchema, SubscribeRequestSchema, UnsubscribeRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
4
- import { logger, formatErrorResponse } from "./utils/index.js";
3
+ import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, ListResourceTemplatesRequestSchema, SubscribeRequestSchema, UnsubscribeRequestSchema, SetLevelRequestSchema, CompleteRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
4
+ import { logger, formatErrorResponse, setMCPLogCallback, } 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
9
  import { registerSamplingCallback } from "./services/index.js";
10
10
  // Server information - version should match package.json
11
- const CURRENT_VERSION = "0.1.29";
11
+ const CURRENT_VERSION = "0.1.30";
12
12
  const SERVER_INFO = {
13
13
  name: "midnight-mcp",
14
14
  version: CURRENT_VERSION,
@@ -97,6 +97,67 @@ const resourceTemplates = [
97
97
  /**
98
98
  * Create and configure the MCP server
99
99
  */
100
+ // Current MCP logging level (controlled by client)
101
+ let mcpLogLevel = "info";
102
+ // Server instance for sending notifications
103
+ let serverInstance = null;
104
+ /**
105
+ * Send a log message to the MCP client
106
+ * This allows clients to see server logs for debugging
107
+ */
108
+ export function sendLogToClient(level, loggerName, data) {
109
+ if (!serverInstance)
110
+ return;
111
+ // Map levels to numeric values for comparison
112
+ const levelValues = {
113
+ debug: 0,
114
+ info: 1,
115
+ notice: 2,
116
+ warning: 3,
117
+ error: 4,
118
+ critical: 5,
119
+ alert: 6,
120
+ emergency: 7,
121
+ };
122
+ // Only send if level meets threshold
123
+ if (levelValues[level] < levelValues[mcpLogLevel])
124
+ return;
125
+ try {
126
+ serverInstance.notification({
127
+ method: "notifications/message",
128
+ params: {
129
+ level,
130
+ logger: loggerName,
131
+ data,
132
+ },
133
+ });
134
+ }
135
+ catch {
136
+ // Ignore notification errors
137
+ }
138
+ }
139
+ /**
140
+ * Send a progress notification to the MCP client
141
+ * Used for long-running operations like compound tools
142
+ */
143
+ export function sendProgressNotification(progressToken, progress, total, message) {
144
+ if (!serverInstance)
145
+ return;
146
+ try {
147
+ serverInstance.notification({
148
+ method: "notifications/progress",
149
+ params: {
150
+ progressToken,
151
+ progress,
152
+ ...(total !== undefined && { total }),
153
+ ...(message && { message }),
154
+ },
155
+ });
156
+ }
157
+ catch {
158
+ // Ignore notification errors
159
+ }
160
+ }
100
161
  export function createServer() {
101
162
  const server = new Server(SERVER_INFO, {
102
163
  capabilities: {
@@ -110,8 +171,16 @@ export function createServer() {
110
171
  prompts: {
111
172
  listChanged: true,
112
173
  },
174
+ logging: {},
175
+ completions: {},
113
176
  },
114
177
  });
178
+ // Store server instance for logging notifications
179
+ serverInstance = server;
180
+ // Wire up MCP logging - send logger output to client
181
+ setMCPLogCallback((level, loggerName, data) => {
182
+ sendLogToClient(level, loggerName, data);
183
+ });
115
184
  // Register tool handlers
116
185
  registerToolHandlers(server);
117
186
  // Register resource handlers
@@ -120,10 +189,114 @@ export function createServer() {
120
189
  registerPromptHandlers(server);
121
190
  // Register subscription handlers
122
191
  registerSubscriptionHandlers(server);
192
+ // Register logging handler
193
+ registerLoggingHandler(server);
194
+ // Register completions handler
195
+ registerCompletionsHandler(server);
123
196
  // Setup sampling callback if available
124
197
  setupSampling(server);
125
198
  return server;
126
199
  }
200
+ /**
201
+ * Register logging handler for MCP logging capability
202
+ */
203
+ function registerLoggingHandler(server) {
204
+ server.setRequestHandler(SetLevelRequestSchema, async (request) => {
205
+ const { level } = request.params;
206
+ mcpLogLevel = level;
207
+ logger.info(`MCP log level set to: ${level}`);
208
+ sendLogToClient("info", "midnight-mcp", {
209
+ message: `Log level changed to ${level}`,
210
+ });
211
+ return {};
212
+ });
213
+ }
214
+ // Completion suggestions for prompt arguments
215
+ const COMPLETION_VALUES = {
216
+ "midnight:create-contract": {
217
+ contractType: [
218
+ "token",
219
+ "voting",
220
+ "credential",
221
+ "auction",
222
+ "escrow",
223
+ "custom",
224
+ ],
225
+ privacyLevel: ["full", "partial", "public"],
226
+ complexity: ["beginner", "intermediate", "advanced"],
227
+ },
228
+ "midnight:review-contract": {
229
+ focusAreas: [
230
+ "security",
231
+ "performance",
232
+ "privacy",
233
+ "readability",
234
+ "gas-optimization",
235
+ ],
236
+ },
237
+ "midnight:explain-concept": {
238
+ concept: [
239
+ "zk-proofs",
240
+ "circuits",
241
+ "witnesses",
242
+ "ledger",
243
+ "state-management",
244
+ "privacy-model",
245
+ "token-transfers",
246
+ "merkle-trees",
247
+ ],
248
+ level: ["beginner", "intermediate", "advanced"],
249
+ },
250
+ "midnight:compare-approaches": {
251
+ approaches: [
252
+ "token-standards",
253
+ "state-management",
254
+ "privacy-patterns",
255
+ "circuit-design",
256
+ ],
257
+ },
258
+ "midnight:debug-contract": {
259
+ errorType: [
260
+ "compilation",
261
+ "runtime",
262
+ "logic",
263
+ "privacy-leak",
264
+ "state-corruption",
265
+ ],
266
+ },
267
+ };
268
+ /**
269
+ * Register completions handler for argument autocompletion
270
+ */
271
+ function registerCompletionsHandler(server) {
272
+ server.setRequestHandler(CompleteRequestSchema, async (request) => {
273
+ const { ref, argument } = request.params;
274
+ if (ref.type !== "ref/prompt") {
275
+ return { completion: { values: [], hasMore: false } };
276
+ }
277
+ const promptName = ref.name;
278
+ const argName = argument.name;
279
+ const currentValue = argument.value?.toLowerCase() || "";
280
+ // Get completion values for this prompt/argument
281
+ const promptCompletions = COMPLETION_VALUES[promptName];
282
+ if (!promptCompletions) {
283
+ return { completion: { values: [], hasMore: false } };
284
+ }
285
+ const argValues = promptCompletions[argName];
286
+ if (!argValues) {
287
+ return { completion: { values: [], hasMore: false } };
288
+ }
289
+ // Filter by current input
290
+ const filtered = argValues.filter((v) => v.toLowerCase().includes(currentValue));
291
+ return {
292
+ completion: {
293
+ values: filtered.slice(0, 20),
294
+ total: filtered.length,
295
+ hasMore: filtered.length > 20,
296
+ },
297
+ };
298
+ });
299
+ }
127
300
  /**
128
301
  * Register tool handlers
129
302
  */
@@ -207,6 +380,9 @@ function registerToolHandlers(server) {
207
380
  text: JSON.stringify(result, null, 2),
208
381
  },
209
382
  ],
383
+ // Include structured content for machine-readable responses
384
+ // This allows clients to parse results without JSON.parse()
385
+ structuredContent: result,
210
386
  };
211
387
  }
212
388
  catch (error) {
@@ -191,7 +191,11 @@ export declare function getLatestSyntax(input: GetLatestSyntaxInput): Promise<{
191
191
  * Combines: getVersionInfo + checkBreakingChanges + getMigrationGuide
192
192
  * Reduces 3 tool calls to 1, saving ~60% tokens
193
193
  */
194
- export declare function upgradeCheck(input: UpgradeCheckInput): Promise<{
194
+ export declare function upgradeCheck(input: UpgradeCheckInput & {
195
+ _meta?: {
196
+ progressToken?: string | number;
197
+ };
198
+ }): Promise<{
195
199
  repository: string;
196
200
  currentVersion: string;
197
201
  version: {
@@ -219,7 +223,11 @@ export declare function upgradeCheck(input: UpgradeCheckInput): Promise<{
219
223
  * Combines: getVersionInfo + getLatestSyntax + listExamples (filtered)
220
224
  * Provides everything needed to start working with a repo
221
225
  */
222
- export declare function getFullRepoContext(input: FullRepoContextInput): Promise<{
226
+ export declare function getFullRepoContext(input: FullRepoContextInput & {
227
+ _meta?: {
228
+ progressToken?: string | number;
229
+ };
230
+ }): Promise<{
223
231
  repository: string;
224
232
  quickStart: {
225
233
  version: string;
@@ -5,6 +5,7 @@
5
5
  import { githubClient } from "../../pipeline/index.js";
6
6
  import { releaseTracker } from "../../pipeline/releases.js";
7
7
  import { logger, DEFAULT_REPOSITORIES, SelfCorrectionHints, } from "../../utils/index.js";
8
+ import { sendProgressNotification } from "../../server.js";
8
9
  import { REPO_ALIASES, EXAMPLES } from "./constants.js";
9
10
  import { EMBEDDED_DOCS } from "../../resources/content/docs-content.js";
10
11
  // Re-export validation handlers from validation.ts
@@ -399,6 +400,7 @@ export async function getLatestSyntax(input) {
399
400
  export async function upgradeCheck(input) {
400
401
  const repoName = input?.repo || "compact";
401
402
  const currentVersion = input.currentVersion;
403
+ const progressToken = input._meta?.progressToken;
402
404
  logger.debug("Running compound upgrade check", {
403
405
  repo: repoName,
404
406
  currentVersion,
@@ -407,18 +409,34 @@ export async function upgradeCheck(input) {
407
409
  if (!resolved) {
408
410
  throw new Error(`Unknown repository: ${repoName}. Available: ${Object.keys(REPO_ALIASES).join(", ")}`);
409
411
  }
412
+ // Send progress: Starting
413
+ if (progressToken) {
414
+ sendProgressNotification(progressToken, 1, 4, "Fetching version info...");
415
+ }
410
416
  // Fetch all data in parallel
411
417
  const [versionInfo, outdatedInfo, breakingChanges] = await Promise.all([
412
418
  releaseTracker.getVersionInfo(resolved.owner, resolved.repo),
413
419
  releaseTracker.isOutdated(resolved.owner, resolved.repo, currentVersion),
414
420
  releaseTracker.getBreakingChangesSince(resolved.owner, resolved.repo, currentVersion),
415
421
  ]);
422
+ // Send progress: Fetched version data
423
+ if (progressToken) {
424
+ sendProgressNotification(progressToken, 2, 4, "Checking breaking changes...");
425
+ }
416
426
  const latestVersion = versionInfo.latestStableRelease?.tag || versionInfo.latestRelease?.tag;
417
427
  // Only fetch migration guide if there are breaking changes
418
428
  let migrationGuide = null;
419
429
  if (breakingChanges.length > 0 && latestVersion) {
430
+ // Send progress: Fetching migration guide
431
+ if (progressToken) {
432
+ sendProgressNotification(progressToken, 3, 4, "Generating migration guide...");
433
+ }
420
434
  migrationGuide = await releaseTracker.getMigrationGuide(resolved.owner, resolved.repo, currentVersion, latestVersion);
421
435
  }
436
+ // Send progress: Complete
437
+ if (progressToken) {
438
+ sendProgressNotification(progressToken, 4, 4, "Analysis complete");
439
+ }
422
440
  // Compute upgrade urgency
423
441
  const urgency = computeUpgradeUrgency(outdatedInfo, breakingChanges.length);
424
442
  return {
@@ -458,21 +476,34 @@ export async function upgradeCheck(input) {
458
476
  */
459
477
  export async function getFullRepoContext(input) {
460
478
  const repoName = input?.repo || "compact";
479
+ const progressToken = input._meta?.progressToken;
461
480
  logger.debug("Getting full repo context", { repo: repoName });
462
481
  const resolved = resolveRepo(repoName);
463
482
  if (!resolved) {
464
483
  throw new Error(`Unknown repository: ${repoName}. Available: ${Object.keys(REPO_ALIASES).join(", ")}`);
465
484
  }
485
+ // Send progress: Starting
486
+ if (progressToken) {
487
+ sendProgressNotification(progressToken, 1, 4, "Fetching version info...");
488
+ }
466
489
  // Fetch version info
467
490
  const versionInfo = await releaseTracker.getVersionInfo(resolved.owner, resolved.repo);
468
491
  const version = versionInfo.latestStableRelease?.tag ||
469
492
  versionInfo.latestRelease?.tag ||
470
493
  "main";
494
+ // Send progress: Fetched version
495
+ if (progressToken) {
496
+ sendProgressNotification(progressToken, 2, 4, "Loading syntax reference...");
497
+ }
471
498
  // Conditionally fetch syntax reference
472
499
  let syntaxRef = null;
473
500
  if (input.includeSyntax !== false) {
474
501
  syntaxRef = await releaseTracker.getLatestSyntaxReference(resolved.owner, resolved.repo);
475
502
  }
503
+ // Send progress: Loading examples
504
+ if (progressToken) {
505
+ sendProgressNotification(progressToken, 3, 4, "Gathering examples...");
506
+ }
476
507
  // Get relevant examples for this repo
477
508
  let examples = [];
478
509
  if (input.includeExamples !== false) {
@@ -486,6 +517,10 @@ export async function getFullRepoContext(input) {
486
517
  complexity: ex.complexity,
487
518
  }));
488
519
  }
520
+ // Send progress: Complete
521
+ if (progressToken) {
522
+ sendProgressNotification(progressToken, 4, 4, "Context ready");
523
+ }
489
524
  return {
490
525
  repository: `${resolved.owner}/${resolved.repo}`,
491
526
  // Quick start info
@@ -1,7 +1,7 @@
1
1
  export { config, isHostedMode, isLocalMode } from "./config.js";
2
2
  export type { Config, RepositoryConfig } from "./config.js";
3
3
  export { DEFAULT_REPOSITORIES } from "./config.js";
4
- export { logger } from "./logger.js";
4
+ export { logger, setMCPLogCallback } from "./logger.js";
5
5
  export { MCPError, ErrorCodes, createUserError, formatErrorResponse, withErrorHandling, SelfCorrectionHints, } from "./errors.js";
6
6
  export { validateQuery, validateRepository, validatePath, validateRef, validateNumber, validateToolArgs, sanitizeString, } from "./validation.js";
7
7
  export type { ValidationResult } from "./validation.js";
@@ -1,6 +1,6 @@
1
1
  export { config, isHostedMode, isLocalMode } from "./config.js";
2
2
  export { DEFAULT_REPOSITORIES } from "./config.js";
3
- export { logger } from "./logger.js";
3
+ export { logger, setMCPLogCallback } from "./logger.js";
4
4
  export { MCPError, ErrorCodes, createUserError, formatErrorResponse, withErrorHandling, SelfCorrectionHints, } from "./errors.js";
5
5
  // Validation utilities
6
6
  export { validateQuery, validateRepository, validatePath, validateRef, validateNumber, validateToolArgs, sanitizeString, } from "./validation.js";
@@ -1,5 +1,10 @@
1
1
  type LogLevel = "debug" | "info" | "warn" | "error";
2
2
  type LogFormat = "text" | "json";
3
+ type MCPLogCallback = (level: "debug" | "info" | "notice" | "warning" | "error", logger: string, data: unknown) => void;
4
+ /**
5
+ * Set the MCP log callback to send logs to the client
6
+ */
7
+ export declare function setMCPLogCallback(callback: MCPLogCallback | null): void;
3
8
  declare class Logger {
4
9
  private level;
5
10
  private format;
@@ -1,4 +1,12 @@
1
1
  import { config } from "./config.js";
2
+ // Global MCP log callback (set by server)
3
+ let mcpLogCallback = null;
4
+ /**
5
+ * Set the MCP log callback to send logs to the client
6
+ */
7
+ export function setMCPLogCallback(callback) {
8
+ mcpLogCallback = callback;
9
+ }
2
10
  const LOG_LEVELS = {
3
11
  debug: 0,
4
12
  info: 1,
@@ -56,21 +64,29 @@ class Logger {
56
64
  if (this.shouldLog("debug")) {
57
65
  console.error(this.formatMessage("debug", message, meta));
58
66
  }
67
+ // Also send to MCP client
68
+ mcpLogCallback?.("debug", this.service, { message, ...meta });
59
69
  }
60
70
  info(message, meta) {
61
71
  if (this.shouldLog("info")) {
62
72
  console.error(this.formatMessage("info", message, meta));
63
73
  }
74
+ // Also send to MCP client
75
+ mcpLogCallback?.("info", this.service, { message, ...meta });
64
76
  }
65
77
  warn(message, meta) {
66
78
  if (this.shouldLog("warn")) {
67
79
  console.error(this.formatMessage("warn", message, meta));
68
80
  }
81
+ // Also send to MCP client (MCP uses "warning" not "warn")
82
+ mcpLogCallback?.("warning", this.service, { message, ...meta });
69
83
  }
70
84
  error(message, meta) {
71
85
  if (this.shouldLog("error")) {
72
86
  console.error(this.formatMessage("error", message, meta));
73
87
  }
88
+ // Also send to MCP client
89
+ mcpLogCallback?.("error", this.service, { message, ...meta });
74
90
  }
75
91
  /**
76
92
  * Create a child logger with additional context
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "midnight-mcp",
3
- "version": "0.1.29",
3
+ "version": "0.1.30",
4
4
  "description": "Model Context Protocol Server for Midnight Blockchain Development",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",