Deduplicate requests and include pubkey in notifications
This commit is contained in:
@@ -67,7 +67,7 @@ async function createSystemdService(repoName: string, repoDir: string, seedPath:
|
|||||||
return serviceName;
|
return serviceName;
|
||||||
}
|
}
|
||||||
|
|
||||||
function appendNotification(payload: { serviceName: string; description: string }) {
|
function appendNotification(payload: { serviceName: string; description: string; pubkey?: string }) {
|
||||||
const file = '/root/.meta-mcp/notifications.jsonl';
|
const file = '/root/.meta-mcp/notifications.jsonl';
|
||||||
const dir = path.dirname(file);
|
const dir = path.dirname(file);
|
||||||
fs.mkdirSync(dir, { recursive: true });
|
fs.mkdirSync(dir, { recursive: true });
|
||||||
@@ -201,7 +201,11 @@ export async function runPipeline(jobId: string, request: MetaRequest): Promise<
|
|||||||
const cvmiArgs = ['-y', 'cvmi', 'serve', '--', 'node', 'dist/server.js'];
|
const cvmiArgs = ['-y', 'cvmi', 'serve', '--', 'node', 'dist/server.js'];
|
||||||
const child = spawn('npx', cvmiArgs, { cwd: repoDir, env, stdio: 'pipe' });
|
const child = spawn('npx', cvmiArgs, { cwd: repoDir, env, stdio: 'pipe' });
|
||||||
|
|
||||||
|
const serviceName = toServiceName(repoName);
|
||||||
|
const desc = request.requirements[0]?.description ?? repoName;
|
||||||
let pubkey: string | undefined;
|
let pubkey: string | undefined;
|
||||||
|
let notified = false;
|
||||||
|
|
||||||
child.stdout.on('data', (d) => {
|
child.stdout.on('data', (d) => {
|
||||||
const text = d.toString();
|
const text = d.toString();
|
||||||
if (!pubkey) {
|
if (!pubkey) {
|
||||||
@@ -213,6 +217,10 @@ export async function runPipeline(jobId: string, request: MetaRequest): Promise<
|
|||||||
message: `Server running for ${repoName}`,
|
message: `Server running for ${repoName}`,
|
||||||
serverPubkey: pubkey,
|
serverPubkey: pubkey,
|
||||||
});
|
});
|
||||||
|
if (!notified) {
|
||||||
|
appendNotification({ serviceName, description: desc, pubkey });
|
||||||
|
notified = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -223,10 +231,11 @@ export async function runPipeline(jobId: string, request: MetaRequest): Promise<
|
|||||||
});
|
});
|
||||||
|
|
||||||
// create a systemd service to keep the MCP alive
|
// create a systemd service to keep the MCP alive
|
||||||
const serviceName = await createSystemdService(repoName, repoDir, repoSeedPath);
|
await createSystemdService(repoName, repoDir, repoSeedPath);
|
||||||
|
|
||||||
const desc = request.requirements[0]?.description ?? repoName;
|
if (!notified) {
|
||||||
appendNotification({ serviceName, description: desc });
|
appendNotification({ serviceName, description: desc });
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
status: 'running',
|
status: 'running',
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
||||||
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { createJob, getJob, listJobs, updateJob } from './storage.js';
|
import { createJob, findRecentJobBySignature, getJob, listJobs, updateJob } from './storage.js';
|
||||||
import type { MetaRequest, MetaResponse } from './types.js';
|
import type { MetaRequest, MetaResponse } from './types.js';
|
||||||
import { runPipeline } from './pipeline.js';
|
import { runPipeline } from './pipeline.js';
|
||||||
|
|
||||||
@@ -47,7 +47,21 @@ server.registerTool(
|
|||||||
},
|
},
|
||||||
async (args) => {
|
async (args) => {
|
||||||
const parsed = MetaRequestSchema.parse(args);
|
const parsed = MetaRequestSchema.parse(args);
|
||||||
const job = createJob(parsed as MetaRequest);
|
const signature = JSON.stringify(parsed.requirements ?? []);
|
||||||
|
|
||||||
|
const existing = findRecentJobBySignature(signature, 10 * 60 * 1000);
|
||||||
|
if (existing) {
|
||||||
|
return {
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
text: JSON.stringify(existing.response, null, 2),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const job = createJob(parsed as MetaRequest, signature);
|
||||||
|
|
||||||
updateJob(job.id, {
|
updateJob(job.id, {
|
||||||
status: 'building',
|
status: 'building',
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import type { MetaRequest, MetaResponse } from './types.js';
|
|||||||
export type Job = {
|
export type Job = {
|
||||||
id: string;
|
id: string;
|
||||||
request: MetaRequest;
|
request: MetaRequest;
|
||||||
|
signature?: string;
|
||||||
createdAt: number;
|
createdAt: number;
|
||||||
updatedAt: number;
|
updatedAt: number;
|
||||||
response: MetaResponse;
|
response: MetaResponse;
|
||||||
@@ -31,7 +32,7 @@ function hydrate() {
|
|||||||
|
|
||||||
hydrate();
|
hydrate();
|
||||||
|
|
||||||
export function createJob(request: MetaRequest): Job {
|
export function createJob(request: MetaRequest, signature?: string): Job {
|
||||||
const id = randomUUID();
|
const id = randomUUID();
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const response: MetaResponse = {
|
const response: MetaResponse = {
|
||||||
@@ -39,7 +40,7 @@ export function createJob(request: MetaRequest): Job {
|
|||||||
message: 'Request received. Preparing build pipeline.',
|
message: 'Request received. Preparing build pipeline.',
|
||||||
jobId: id,
|
jobId: id,
|
||||||
};
|
};
|
||||||
const job: Job = { id, request, createdAt: now, updatedAt: now, response };
|
const job: Job = { id, request, signature, createdAt: now, updatedAt: now, response };
|
||||||
jobs.set(id, job);
|
jobs.set(id, job);
|
||||||
persist();
|
persist();
|
||||||
return job;
|
return job;
|
||||||
@@ -65,3 +66,13 @@ export function getJob(id: string): Job | null {
|
|||||||
export function listJobs(): Job[] {
|
export function listJobs(): Job[] {
|
||||||
return [...jobs.values()].sort((a, b) => b.createdAt - a.createdAt);
|
return [...jobs.values()].sort((a, b) => b.createdAt - a.createdAt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function findRecentJobBySignature(signature: string, maxAgeMs: number): Job | null {
|
||||||
|
const now = Date.now();
|
||||||
|
for (const job of jobs.values()) {
|
||||||
|
if (job.signature === signature && now - job.createdAt <= maxAgeMs) {
|
||||||
|
return job;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user