Files
meta-mcp/src/server.ts
2026-03-12 10:19:11 +00:00

139 lines
3.2 KiB
TypeScript

import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';
import { createJob, getJob, listJobs, updateJob } from './storage.js';
import type { MetaRequest, MetaResponse } from './types.js';
import { runPipeline } from './pipeline.js';
const server = new McpServer(
{
name: 'meta-mcp',
version: '0.2.0',
},
{
capabilities: {
tools: {},
},
},
);
const ToolRequirementSchema = z.object({
name: z.string(),
description: z.string(),
inputSchema: z.record(z.unknown()),
outputSchema: z.record(z.unknown()).optional(),
notes: z.string().optional(),
});
const MetaRequestSchema = z.object({
requirements: z.array(ToolRequirementSchema).min(1),
timeoutSeconds: z.number().int().positive().optional(),
requester: z
.object({
name: z.string().optional(),
pubkey: z.string().optional(),
sessionId: z.string().optional(),
})
.optional(),
context: z.record(z.unknown()).optional(),
});
server.registerTool(
'meta.request_tool',
{
description:
'Request creation of a new MCP tool/server. Returns a job id and later the server pubkey when available.',
inputSchema: MetaRequestSchema,
},
async (args) => {
const parsed = MetaRequestSchema.parse(args);
const job = createJob(parsed as MetaRequest);
updateJob(job.id, {
status: 'building',
message: 'Accepted. Starting build pipeline.',
});
// Run pipeline asynchronously
runPipeline(job.id, parsed as MetaRequest).catch((err) => {
updateJob(job.id, {
status: 'failed',
message: err instanceof Error ? err.message : String(err),
});
});
const response: MetaResponse = {
status: 'building',
message: 'Accepted. Starting build pipeline.',
jobId: job.id,
};
return {
content: [
{
type: 'text',
text: JSON.stringify(response, null, 2),
},
],
};
},
);
server.registerTool(
'meta.job_status',
{
description: 'Check status of a previously requested MCP tool build.',
inputSchema: z.object({ jobId: z.string() }),
},
async (args) => {
const job = getJob(args.jobId);
if (!job) {
return {
content: [
{
type: 'text',
text: JSON.stringify({ error: 'job_not_found', jobId: args.jobId }),
},
],
};
}
return {
content: [
{
type: 'text',
text: JSON.stringify(job.response, null, 2),
},
],
};
},
);
server.registerTool(
'meta.jobs_recent',
{
description: 'List recent build jobs for monitoring.',
inputSchema: z.object({ limit: z.number().int().positive().optional() }),
},
async (args) => {
const jobs = listJobs().slice(0, args.limit ?? 10).map((job) => job.response);
return {
content: [
{
type: 'text',
text: JSON.stringify(jobs, null, 2),
},
],
};
},
);
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
}
main().catch((err) => {
console.error('Meta MCP server failed to start', err);
process.exit(1);
});