agent-hub-mcp
Version:
Universal AI agent coordination platform based on Model Context Protocol (MCP)
1,485 lines (1,467 loc) • 78.2 kB
JavaScript
#!/usr/bin/env node
// src/index.ts
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
// src/agents/service.ts
var AgentService = class {
constructor(storage2, featuresService2, messageService2) {
this.storage = storage2;
this.featuresService = featuresService2;
this.messageService = messageService2;
}
async getAllAgents() {
return this.storage.getAgents();
}
async getHubStatus() {
const allAgents = await this.getAllAgents();
const fiveMinutesAgo = Date.now() - 5 * 60 * 1e3;
const activeAgents = allAgents.filter((agent) => agent.lastSeen > fiveMinutesAgo);
const inactiveAgents = allAgents.filter((agent) => agent.lastSeen <= fiveMinutesAgo);
const allFeatures = await this.featuresService.getFeatures();
const activeFeatures = allFeatures.filter((feature) => feature.status === "active");
const priorityCounts = {
critical: allFeatures.filter((f) => f.priority === "critical").length,
high: allFeatures.filter((f) => f.priority === "high").length,
normal: allFeatures.filter((f) => f.priority === "normal").length,
low: allFeatures.filter((f) => f.priority === "low").length
};
const allMessages = await this.messageService.getAllMessages();
const unreadMessages = allMessages.filter((message) => !message.read);
const oneHourAgo = Date.now() - 60 * 60 * 1e3;
const recentMessages = allMessages.filter((message) => message.timestamp > oneHourAgo);
return {
agents: {
total: allAgents.length,
active: activeAgents,
inactive: inactiveAgents
},
features: {
total: allFeatures.length,
active: activeFeatures,
byPriority: priorityCounts
},
messages: {
totalUnread: unreadMessages.length,
recentActivity: recentMessages.length
}
};
}
};
// src/features/service.ts
import { createId } from "@paralleldrive/cuid2";
// src/types/features.types.ts
var FeaturePriority = /* @__PURE__ */ ((FeaturePriority2) => {
FeaturePriority2["CRITICAL"] = "critical";
FeaturePriority2["HIGH"] = "high";
FeaturePriority2["NORMAL"] = "normal";
FeaturePriority2["LOW"] = "low";
return FeaturePriority2;
})(FeaturePriority || {});
var FeatureStatus = /* @__PURE__ */ ((FeatureStatus2) => {
FeatureStatus2["PLANNING"] = "planning";
FeatureStatus2["ACTIVE"] = "active";
FeatureStatus2["COMPLETED"] = "completed";
FeatureStatus2["ON_HOLD"] = "on-hold";
FeatureStatus2["CANCELLED"] = "cancelled";
return FeatureStatus2;
})(FeatureStatus || {});
var PRIORITY_ORDER = {
["critical" /* CRITICAL */]: 0,
["high" /* HIGH */]: 1,
["normal" /* NORMAL */]: 2,
["low" /* LOW */]: 3
};
// src/types/messages.types.ts
var MessagePriority = /* @__PURE__ */ ((MessagePriority2) => {
MessagePriority2["URGENT"] = "urgent";
MessagePriority2["NORMAL"] = "normal";
MessagePriority2["LOW"] = "low";
return MessagePriority2;
})(MessagePriority || {});
var MessageType = /* @__PURE__ */ ((MessageType2) => {
MessageType2["CONTEXT"] = "context";
MessageType2["TASK"] = "task";
MessageType2["QUESTION"] = "question";
MessageType2["COMPLETION"] = "completion";
MessageType2["ERROR"] = "error";
return MessageType2;
})(MessageType || {});
// src/features/service.ts
var FeaturesService = class {
constructor(storage2) {
this.storage = storage2;
}
async createFeature(input, createdBy) {
const now = Date.now();
const feature = {
id: input.name.toLowerCase().replace(/[^\da-z]+/g, "-"),
name: input.name.toLowerCase().replace(/[^\da-z]+/g, "-"),
title: input.title,
description: input.description,
status: "planning" /* PLANNING */,
createdBy,
priority: input.priority ?? "normal" /* NORMAL */,
estimatedAgents: input.estimatedAgents,
assignedAgents: [],
createdAt: now,
updatedAt: now
};
await this.storage.createFeature(feature);
return feature;
}
async getFeatures(filters) {
return this.storage.getFeatures(filters);
}
async getFeature(featureId) {
return this.storage.getFeature(featureId);
}
async updateFeature(featureId, updates) {
await this.storage.updateFeature(featureId, { ...updates, updatedAt: Date.now() });
}
async approveFeature(featureId) {
await this.updateFeature(featureId, { status: "active" /* ACTIVE */ });
}
async createTask(featureId, input, createdBy) {
const feature = await this.storage.getFeature(featureId);
if (!feature) {
throw new Error(`Feature not found: ${featureId}`);
}
const now = Date.now();
const taskId = createId();
const task = {
id: taskId,
title: input.title,
description: input.description,
status: "planning" /* PLANNING */,
createdBy,
createdAt: now,
updatedAt: now
};
const delegations = input.delegations.map((del, index) => ({
id: `${taskId}-del-${index + 1}`,
parentTaskId: taskId,
agent: del.agent,
scope: del.scope,
status: "pending" /* PENDING */,
subtaskIds: [],
createdBy,
createdAt: now,
updatedAt: now
}));
await this.storage.createTask(featureId, task);
for (const delegation of delegations) {
await this.storage.createDelegation(featureId, delegation);
}
const allAgents = [...feature.assignedAgents || [], ...delegations.map((d) => d.agent)];
const uniqueAgents = [...new Set(allAgents)];
await this.updateFeature(featureId, { assignedAgents: uniqueAgents });
return { task, delegations };
}
async acceptDelegation(featureId, delegationId, agentId) {
const delegation = await this.storage.getDelegation(featureId, delegationId);
if (!delegation) {
throw new Error(`Delegation not found: ${delegationId} in feature ${featureId}`);
}
if (delegation.agent !== agentId) {
throw new Error(`Delegation ${delegationId} is not assigned to agent ${agentId}`);
}
if (delegation.status !== "pending" /* PENDING */) {
throw new Error(`Delegation ${delegationId} has already been ${delegation.status}`);
}
await this.storage.updateDelegation(featureId, delegationId, {
status: "accepted" /* ACCEPTED */,
acceptedAt: Date.now()
});
}
async createSubtask(featureId, delegationId, input, createdBy) {
const delegation = await this.storage.getDelegation(featureId, delegationId);
if (!delegation) {
throw new Error(`Delegation not found: ${delegationId} in feature ${featureId}`);
}
if (delegation.agent !== createdBy) {
throw new Error(
`Only assigned agent ${delegation.agent} can create subtasks for delegation ${delegationId}`
);
}
const now = Date.now();
const subtaskId = createId();
const subtask = {
id: subtaskId,
delegationId,
parentTaskId: delegation.parentTaskId,
title: input.title,
description: input.description,
status: "todo" /* TODO */,
createdBy,
dependsOn: input.dependsOn || [],
createdAt: now,
updatedAt: now
};
await this.storage.createSubtask(featureId, subtask);
const updatedSubtaskIds = [...delegation.subtaskIds, subtaskId];
await this.storage.updateDelegation(featureId, delegationId, {
subtaskIds: updatedSubtaskIds
});
if (delegation.status === "accepted" /* ACCEPTED */ || delegation.status === "pending" /* PENDING */) {
await this.storage.updateDelegation(featureId, delegationId, {
status: "in-progress" /* IN_PROGRESS */
});
}
return subtask;
}
async updateSubtask(featureId, subtaskId, updates, updatedBy) {
const subtask = await this.storage.getSubtask(featureId, subtaskId);
if (!subtask) {
throw new Error(`Subtask not found: ${subtaskId} in feature ${featureId}`);
}
if (subtask.createdBy !== updatedBy) {
throw new Error(`Only the creator ${subtask.createdBy} can update subtask ${subtaskId}`);
}
await this.storage.updateSubtask(featureId, subtaskId, {
...updates,
updatedAt: Date.now()
});
if (updates.status === "completed" /* COMPLETED */) {
await this.checkDelegationCompletion(featureId, subtask.delegationId);
}
}
async checkDelegationCompletion(featureId, delegationId) {
const delegation = await this.storage.getDelegation(featureId, delegationId);
if (!delegation) {
return;
}
const subtasks = await this.storage.getSubtasks(featureId, delegationId);
const allCompleted = subtasks.length > 0 && subtasks.every((s) => s.status === "completed" /* COMPLETED */);
if (allCompleted && delegation.status !== "completed" /* COMPLETED */) {
await this.storage.updateDelegation(featureId, delegationId, {
status: "completed" /* COMPLETED */,
completedAt: Date.now()
});
await this.checkTaskCompletion(featureId, delegation.parentTaskId);
}
}
async checkTaskCompletion(featureId, taskId) {
const task = await this.storage.getTask(featureId, taskId);
if (!task) {
return;
}
const delegations = await this.storage.getDelegations(featureId);
const taskDelegations = delegations.filter((d) => d.parentTaskId === taskId);
const allCompleted = taskDelegations.length > 0 && taskDelegations.every((d) => d.status === "completed" /* COMPLETED */);
if (allCompleted && task.status !== "completed" /* COMPLETED */) {
await this.storage.updateTask(featureId, taskId, {
status: "completed" /* COMPLETED */
});
await this.checkFeatureCompletion(featureId);
}
}
async checkFeatureCompletion(featureId) {
const tasks = await this.storage.getTasksInFeature(featureId);
const allCompleted = tasks.length > 0 && tasks.every((t) => t.status === "completed" /* COMPLETED */);
if (allCompleted) {
await this.updateFeature(featureId, { status: "completed" /* COMPLETED */ });
}
}
async getAgentWorkload(agentId) {
return this.storage.getAgentWorkload(agentId);
}
async getFeatureData(featureId) {
return this.storage.getFeatureData(featureId);
}
async getFeatureOverview(featureId) {
if (featureId) {
const data = await this.getFeatureData(featureId);
if (!data) {
throw new Error(`Feature not found: ${featureId}`);
}
return data;
}
return this.getFeatures();
}
// Utility methods for common queries
async getActiveFeatures() {
return this.getFeatures({ status: "active" /* ACTIVE */ });
}
async getAgentFeatures(agentId) {
return this.getFeatures({ agent: agentId });
}
async getFeaturesByPriority(priority) {
return this.getFeatures({ priority });
}
async getAgentDelegations(agentId, featureId) {
return this.storage.getDelegations(featureId, agentId);
}
async getAgentSubtasks(agentId, featureId) {
const subtasks = await this.storage.getSubtasks(featureId);
return subtasks.filter((s) => s.createdBy === agentId);
}
// Feature lifecycle management
async pauseFeature(featureId) {
await this.updateFeature(featureId, { status: "on-hold" /* ON_HOLD */ });
}
async resumeFeature(featureId) {
await this.updateFeature(featureId, { status: "active" /* ACTIVE */ });
}
async cancelFeature(featureId) {
await this.updateFeature(featureId, { status: "cancelled" /* CANCELLED */ });
}
// Dependency management
async getSubtaskDependencies(featureId, subtaskId) {
const subtask = await this.storage.getSubtask(featureId, subtaskId);
if (!subtask || !subtask.dependsOn.length) {
return [];
}
const dependencies = [];
for (const depId of subtask.dependsOn) {
const dep = await this.storage.getSubtask(featureId, depId);
if (dep) {
dependencies.push(dep);
}
}
return dependencies;
}
async canStartSubtask(featureId, subtaskId) {
const dependencies = await this.getSubtaskDependencies(featureId, subtaskId);
return dependencies.every((dep) => dep.status === "completed" /* COMPLETED */);
}
// Statistics and monitoring
async getFeatureStats(featureId) {
if (featureId) {
const data = await this.getFeatureData(featureId);
if (!data) {
return null;
}
return {
feature: data.feature,
tasksTotal: data.tasks.length,
tasksCompleted: data.tasks.filter((t) => t.status === "completed" /* COMPLETED */).length,
delegationsTotal: data.delegations.length,
delegationsCompleted: data.delegations.filter((d) => d.status === "completed" /* COMPLETED */).length,
subtasksTotal: data.subtasks.length,
subtasksCompleted: data.subtasks.filter((s) => s.status === "completed" /* COMPLETED */).length,
agents: [...new Set(data.delegations.map((d) => d.agent))]
};
}
const features = await this.getFeatures();
const stats = {
active: features.filter((f) => f.status === "active" /* ACTIVE */).length,
completed: features.filter((f) => f.status === "completed" /* COMPLETED */).length,
planning: features.filter((f) => f.status === "planning" /* PLANNING */).length,
onHold: features.filter((f) => f.status === "on-hold" /* ON_HOLD */).length,
cancelled: features.filter((f) => f.status === "cancelled" /* CANCELLED */).length,
byPriority: {},
byAgent: {}
};
for (const feature of features) {
stats.byPriority[feature.priority] = (stats.byPriority[feature.priority] || 0) + 1;
for (const agent of feature.assignedAgents || []) {
stats.byAgent[agent] = (stats.byAgent[agent] || 0) + 1;
}
}
return stats;
}
};
// src/messaging/service.ts
import { createId as createId2 } from "@paralleldrive/cuid2";
var MessageService = class {
constructor(storage2) {
this.storage = storage2;
}
async sendMessage(from, to, type, content, options = {}) {
const message = {
id: createId2(),
from,
to,
type,
content,
metadata: options.metadata,
timestamp: Date.now(),
read: false,
threadId: options.threadId,
priority: options.priority ?? "normal" /* NORMAL */
};
await this.storage.saveMessage(message);
return message.id;
}
async getAllMessages(options = {}) {
return this.storage.getMessages({
type: options.type,
since: options.since
});
}
async getMessages(agentId, options = {}) {
const messages = await this.storage.getMessages({
agent: agentId,
type: options.type,
since: options.since
});
const unreadMessages = messages.filter(
(m) => !m.read && (m.to === agentId || m.to === "all" && m.from !== agentId)
);
if (options.markAsRead !== false) {
const markAsReadPromises = unreadMessages.map(async (message) => {
try {
await this.storage.markMessageAsRead(message.id);
} catch (error) {
console.error(`Failed to mark message ${message.id} as read:`, error);
}
});
await Promise.allSettled(markAsReadPromises);
}
return {
count: unreadMessages.length,
messages: unreadMessages
};
}
async getMessageById(messageId) {
return this.storage.getMessage(messageId);
}
};
// src/servers/mcp.ts
import { Server } from "@modelcontextprotocol/sdk/server";
import {
CallToolRequestSchema,
InitializeRequestSchema,
ListResourcesRequestSchema,
ListToolsRequestSchema,
ReadResourceRequestSchema
} from "@modelcontextprotocol/sdk/types.js";
// src/tools/definitions.ts
var TOOLS = [
{
name: "register_agent",
description: "Register an agent with the hub",
inputSchema: {
type: "object",
properties: {
id: {
type: "string",
description: "Agent identifier (optional - will be generated from project path if not provided)"
},
projectPath: { type: "string", description: "Agent working directory" },
role: { type: "string", description: "Agent role description" },
capabilities: {
type: "array",
items: { type: "string" },
description: "Agent capabilities",
default: []
},
collaboratesWith: {
type: "array",
items: { type: "string" },
description: "Expected collaborators",
default: []
}
},
required: ["projectPath", "role"]
}
},
{
name: "send_message",
description: "Send a message to another agent or broadcast to all agents",
inputSchema: {
type: "object",
properties: {
from: { type: "string", description: "Source agent identifier" },
to: { type: "string", description: 'Target agent identifier or "all" for broadcast' },
type: {
type: "string",
enum: Object.values(MessageType),
description: "Message type"
},
content: { type: "string", description: "Message content" },
metadata: { type: "object", description: "Additional structured data" },
priority: {
type: "string",
enum: Object.values(MessagePriority),
description: "Message priority",
default: "normal" /* NORMAL */
},
threadId: { type: "string", description: "Optional conversation thread ID" }
},
required: ["from", "to", "type", "content"]
}
},
{
name: "get_messages",
description: "Retrieve messages for an agent",
inputSchema: {
type: "object",
properties: {
agent: { type: "string", description: "Agent identifier to get messages for" },
markAsRead: {
type: "boolean",
description: "Mark retrieved messages as read",
default: true
},
type: {
type: "string",
enum: Object.values(MessageType),
description: "Filter by message type"
},
since: { type: "number", description: "Get messages since timestamp" }
},
required: ["agent"]
}
},
{
name: "get_hub_status",
description: "Get overview of hub activity, agents, and collaboration opportunities",
inputSchema: {
type: "object",
properties: {}
}
},
{
name: "create_feature",
description: "Create a new feature for multi-agent collaboration",
inputSchema: {
type: "object",
properties: {
name: { type: "string", description: "Feature name (will be converted to kebab-case ID)" },
title: { type: "string", description: "Human-readable feature title" },
description: { type: "string", description: "Detailed feature requirements and context" },
priority: {
type: "string",
enum: Object.values(FeaturePriority),
description: "Feature priority level",
default: "normal" /* NORMAL */
},
estimatedAgents: {
type: "array",
items: { type: "string" },
description: "Agents expected to be needed for this feature",
default: []
},
createdBy: { type: "string", description: "Agent creating this feature" }
},
required: ["name", "title", "description", "createdBy"]
}
},
{
name: "create_task",
description: "Create a task within a feature with agent delegations",
inputSchema: {
type: "object",
properties: {
featureId: { type: "string", description: "Feature ID to create task in" },
title: { type: "string", description: "Task title" },
description: { type: "string", description: "Detailed task requirements" },
delegations: {
type: "array",
items: {
type: "object",
properties: {
agent: { type: "string", description: "Agent ID to delegate to" },
scope: { type: "string", description: "What this agent should accomplish" }
},
required: ["agent", "scope"]
},
description: "Agent delegations for this task"
},
createdBy: { type: "string", description: "Agent creating this task" }
},
required: ["featureId", "title", "description", "delegations", "createdBy"]
}
},
{
name: "create_subtask",
description: "Create implementation subtasks within a delegation",
inputSchema: {
type: "object",
properties: {
featureId: { type: "string", description: "Feature ID" },
delegationId: { type: "string", description: "Delegation ID to create subtasks for" },
subtasks: {
type: "array",
items: {
type: "object",
properties: {
title: { type: "string", description: "Subtask title" },
description: { type: "string", description: "Subtask description" },
dependsOn: {
type: "array",
items: { type: "string" },
description: "Subtask IDs this depends on",
default: []
}
},
required: ["title"]
},
description: "Subtasks to create"
},
createdBy: { type: "string", description: "Agent creating these subtasks" }
},
required: ["featureId", "delegationId", "subtasks", "createdBy"]
}
},
{
name: "get_features",
description: "Get list of features with optional filtering",
inputSchema: {
type: "object",
properties: {
status: {
type: "string",
enum: Object.values(FeatureStatus),
description: "Filter by feature status"
},
priority: {
type: "string",
enum: Object.values(FeaturePriority),
description: "Filter by feature priority"
},
agent: { type: "string", description: "Filter features assigned to this agent" },
createdBy: { type: "string", description: "Filter features created by this agent" }
}
}
},
{
name: "get_feature",
description: "Get complete feature data including tasks, delegations, and subtasks",
inputSchema: {
type: "object",
properties: {
featureId: { type: "string", description: "Feature ID to retrieve" }
},
required: ["featureId"]
}
},
{
name: "accept_delegation",
description: "Accept a delegation assigned to an agent",
inputSchema: {
type: "object",
properties: {
featureId: { type: "string", description: "Feature ID" },
delegationId: { type: "string", description: "Delegation ID to accept" },
agentId: { type: "string", description: "Agent accepting the delegation" }
},
required: ["featureId", "delegationId", "agentId"]
}
},
{
name: "update_subtask",
description: "Update subtask status and provide output/context",
inputSchema: {
type: "object",
properties: {
featureId: { type: "string", description: "Feature ID" },
subtaskId: { type: "string", description: "Subtask ID to update" },
status: {
type: "string",
enum: ["todo", "in-progress", "completed", "blocked"],
description: "New subtask status"
},
output: { type: "string", description: "Output or context for other agents" },
blockedReason: { type: "string", description: "Reason if status is blocked" },
updatedBy: { type: "string", description: "Agent updating this subtask" }
},
required: ["featureId", "subtaskId", "updatedBy"]
}
},
{
name: "sync",
description: "Comprehensive sync with the hub - get messages, workload, and status in one call",
inputSchema: {
type: "object",
properties: {
agentId: { type: "string", description: "Agent ID to sync for" },
markAsRead: {
type: "boolean",
description: "Mark retrieved messages as read",
default: true
}
},
required: ["agentId"]
}
}
];
// src/tools/handlers.ts
import path from "path";
// src/validation/schema.ts
import Ajv from "ajv";
var ajv = new Ajv({
allErrors: true,
removeAdditional: false,
useDefaults: true,
coerceTypes: false
});
var validators = /* @__PURE__ */ new Map();
for (const tool of TOOLS) {
const validator = ajv.compile(tool.inputSchema);
validators.set(tool.name, validator);
}
function validateToolInput(toolName, arguments_) {
const validator = validators.get(toolName);
if (!validator) {
throw new Error(`No validator found for tool: ${toolName}`);
}
const valid = validator(arguments_);
if (!valid) {
const errors = validator.errors ?? [];
const errorMessages = errors.map((error) => {
const instancePath = error.instancePath ?? "root";
return `${instancePath}: ${error.message}`;
});
throw new Error(`Validation failed for tool '${toolName}': ${errorMessages.join(", ")}`);
}
return arguments_;
}
// src/validation/security.ts
import { realpathSync } from "fs";
import { resolve } from "path";
function validateIdentifier(value, fieldName) {
return validateString(value, fieldName, {
required: true,
maxLength: 100,
pattern: /^[\w-]+$/
});
}
function validateMessagePriority(value) {
if (!value) {
return void 0;
}
if (typeof value !== "string") {
throw new TypeError("Priority must be a string");
}
const validPriorities = Object.values(MessagePriority);
if (!validPriorities.includes(value)) {
throw new Error(`Invalid priority: ${value}`);
}
return value;
}
function validateMessageType(value) {
if (!value || typeof value !== "string") {
throw new Error("Invalid message type");
}
const validTypes = Object.values(MessageType);
if (!validTypes.includes(value)) {
throw new Error(`Invalid message type: ${value}`);
}
return value;
}
function validateMetadata(value) {
if (!value) {
return void 0;
}
if (typeof value !== "object" || Array.isArray(value)) {
throw new TypeError("Metadata must be an object");
}
const metadata = value;
const keys = Object.keys(metadata);
if (keys.length > 20) {
throw new Error("Metadata cannot have more than 20 properties");
}
const sanitized = {};
for (const [key, value_] of Object.entries(metadata)) {
if (key === "__proto__" || key === "constructor" || key === "prototype") {
continue;
}
if (!/^[\w-]+$/.test(key)) {
throw new Error(`Invalid metadata key: ${key}`);
}
sanitized[key] = JSON.parse(JSON.stringify(value_));
}
return sanitized;
}
function validateProjectPath(path3) {
if (!path3 || typeof path3 !== "string") {
throw new Error("Invalid project path");
}
if (path3.includes("..") || path3.includes("~")) {
throw new Error("Invalid project path: directory traversal detected");
}
const allowedPrefixes = [
"/Users/",
"/home/",
"/var/www/",
"/opt/",
"/workspace/",
"/tmp/",
process.cwd()
// Current working directory
];
const resolvedPath = resolve(path3);
let realPath;
try {
realPath = realpathSync(resolvedPath);
} catch {
realPath = resolvedPath;
}
const pathsToValidate = [resolvedPath, realPath];
for (const pathToCheck of pathsToValidate) {
const isAllowed = allowedPrefixes.some((prefix) => pathToCheck.startsWith(prefix));
if (!isAllowed) {
throw new Error(`Project path must be in an allowed directory. Resolved to: ${pathToCheck}`);
}
}
return realPath;
}
function validateString(value, fieldName, options = {}) {
const { maxLength = 1e3, minLength = 1, pattern, required = true } = options;
if (value === void 0 || value === null) {
if (required) {
throw new Error(`${fieldName} is required`);
}
return "";
}
if (typeof value !== "string") {
throw new TypeError(`${fieldName} must be a string`);
}
const trimmed = value.trim();
if (required && trimmed.length < minLength) {
throw new Error(`${fieldName} must be at least ${minLength} characters`);
}
if (trimmed.length > maxLength) {
throw new Error(`${fieldName} must not exceed ${maxLength} characters`);
}
if (pattern && !pattern.test(trimmed)) {
throw new Error(`${fieldName} contains invalid characters`);
}
const dangerousPatterns = [
/<script/i,
/javascript:/i,
/on\w+\s*=/i,
// onclick, onload, etc.
/__proto__/,
/constructor\[/,
/prototype\[/
];
for (const dangerous of dangerousPatterns) {
if (dangerous.test(trimmed)) {
throw new Error(`${fieldName} contains potentially malicious content`);
}
}
return trimmed;
}
// src/agents/detection.ts
async function createAgentFromProjectPath(agentId, projectPath) {
const validatedPath = validateProjectPath(projectPath);
const { capabilities, role } = await detectProjectCapabilities(validatedPath);
return {
id: agentId,
projectPath: validatedPath,
role,
capabilities,
status: "active",
lastSeen: Date.now(),
collaboratesWith: []
};
}
async function detectProjectCapabilities(projectPath) {
let role = "Development agent";
const capabilities = [];
try {
const fs2 = await import("fs/promises");
const path3 = await import("path");
try {
const packageJsonPath = path3.resolve(path3.join(projectPath, "package.json"));
if (!packageJsonPath.startsWith(path3.resolve(projectPath))) {
throw new Error("Invalid package.json path");
}
const packageJson = JSON.parse(await fs2.readFile(packageJsonPath, "utf-8"));
const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
if (deps.react) {
capabilities.push("react", "frontend");
}
if (deps.vue) {
capabilities.push("vue", "frontend");
}
if (deps.express || deps.fastify) {
capabilities.push("api", "backend");
}
if (deps.typescript) {
capabilities.push("typescript");
}
if (deps.jest || deps.vitest) {
capabilities.push("testing");
}
} catch {
}
try {
const files = await fs2.readdir(projectPath);
if (files.includes("src") && files.includes("public")) {
role = "Frontend development agent";
} else if (files.includes("src") && capabilities.includes("api")) {
role = "Backend development agent";
} else if (files.some((f) => f.endsWith(".py"))) {
role = "Python development agent";
capabilities.push("python");
} else if (files.some((f) => f.endsWith(".go"))) {
role = "Go development agent";
capabilities.push("go");
} else if (files.some((f) => f.endsWith(".rs"))) {
role = "Rust development agent";
capabilities.push("rust");
}
} catch {
}
} catch {
}
return { role, capabilities };
}
// src/features/handlers.ts
var FeaturesHandler = class {
service;
constructor(storage2) {
this.service = new FeaturesService(storage2);
}
async handleFeatureTool(name, arguments_) {
switch (name) {
case "create_feature":
return this.createFeature(validateToolInput("create_feature", arguments_));
case "create_task":
return this.createTask(validateToolInput("create_task", arguments_));
case "create_subtask":
return this.createSubtask(validateToolInput("create_subtask", arguments_));
case "get_features":
return this.getFeatures(validateToolInput("get_features", arguments_));
case "get_feature":
return this.getFeature(validateToolInput("get_feature", arguments_));
case "accept_delegation":
return this.acceptDelegation(validateToolInput("accept_delegation", arguments_));
case "update_subtask":
return this.updateSubtask(validateToolInput("update_subtask", arguments_));
default:
throw new Error(`Unknown feature tool: ${name}`);
}
}
async createFeature(arguments_) {
try {
const feature = await this.service.createFeature(
{
...arguments_,
priority: arguments_.priority ?? "normal" /* NORMAL */,
estimatedAgents: arguments_.estimatedAgents || []
},
arguments_.createdBy
);
return {
success: true,
feature,
message: `Feature "${feature.title}" created successfully with ID: ${feature.id}`
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error"
};
}
}
async createTask(arguments_) {
try {
const result = await this.service.createTask(
arguments_.featureId,
arguments_,
arguments_.createdBy
);
return {
success: true,
task: result.task,
delegations: result.delegations,
message: `Task "${result.task.title}" created with ${result.delegations.length} delegations`
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error"
};
}
}
async createSubtask(arguments_) {
try {
const subtasks = [];
for (const subtaskData of arguments_.subtasks) {
const subtask = await this.service.createSubtask(
arguments_.featureId,
arguments_.delegationId,
subtaskData,
arguments_.createdBy
);
subtasks.push(subtask);
}
return {
success: true,
subtasks,
message: `Created ${subtasks.length} subtask(s) for delegation ${arguments_.delegationId}`
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error"
};
}
}
async getFeatures(arguments_) {
try {
const filters = {
status: arguments_.status,
priority: arguments_.priority,
agent: arguments_.agent,
createdBy: arguments_.createdBy
};
Object.keys(filters).forEach((key) => {
if (filters[key] === void 0) {
delete filters[key];
}
});
const features = await this.service.getFeatures(
Object.keys(filters).length > 0 ? filters : void 0
);
return {
success: true,
features,
count: features.length,
summary: {
byStatus: features.reduce(
(acc, f) => {
acc[f.status] = (acc[f.status] || 0) + 1;
return acc;
},
{}
),
byPriority: features.reduce(
(acc, f) => {
acc[f.priority] = (acc[f.priority] || 0) + 1;
return acc;
},
{}
)
}
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error"
};
}
}
async getFeature(arguments_) {
try {
const featureData = await this.service.getFeatureData(arguments_.featureId);
if (!featureData) {
return {
success: false,
error: `Feature not found: ${arguments_.featureId}`
};
}
return {
success: true,
...featureData,
summary: {
tasksTotal: featureData.tasks.length,
tasksCompleted: featureData.tasks.filter((t) => t.status === "completed").length,
delegationsTotal: featureData.delegations.length,
delegationsCompleted: featureData.delegations.filter((d) => d.status === "completed").length,
subtasksTotal: featureData.subtasks.length,
subtasksCompleted: featureData.subtasks.filter((s) => s.status === "completed").length,
uniqueAgents: [...new Set(featureData.delegations.map((d) => d.agent))]
}
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error"
};
}
}
async acceptDelegation(arguments_) {
try {
await this.service.acceptDelegation(
arguments_.featureId,
arguments_.delegationId,
arguments_.agentId
);
return {
success: true,
message: `Delegation ${arguments_.delegationId} accepted by agent ${arguments_.agentId}`
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error"
};
}
}
async updateSubtask(arguments_) {
try {
const updates = {
status: arguments_.status,
output: arguments_.output,
blockedReason: arguments_.blockedReason
};
Object.keys(updates).forEach((key) => {
if (updates[key] === void 0) {
delete updates[key];
}
});
await this.service.updateSubtask(
arguments_.featureId,
arguments_.subtaskId,
updates,
arguments_.updatedBy
);
return {
success: true,
message: `Subtask ${arguments_.subtaskId} updated successfully`
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error"
};
}
}
// Utility methods for common operations
async getFeatureStats() {
try {
const stats = await this.service.getFeatureStats();
return {
success: true,
stats
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error"
};
}
}
async approveFeature(featureId) {
try {
await this.service.approveFeature(featureId);
return {
success: true,
message: `Feature ${featureId} approved and activated`
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error"
};
}
}
};
// src/messaging/handlers.ts
function createMessageHandlers(messageService2, storage2, sendNotificationToAgent, sendResourceNotification) {
return {
async send_message(arguments_) {
const from = validateIdentifier(arguments_.from, "from");
const to = validateIdentifier(arguments_.to, "to");
const type = validateMessageType(arguments_.type);
const content = validateString(arguments_.content, "content", { maxLength: 1e4 });
const metadata = validateMetadata(arguments_.metadata);
const priority = validateMessagePriority(arguments_.priority);
const threadId = arguments_.threadId ? validateIdentifier(arguments_.threadId, "threadId") : void 0;
const messageId = await messageService2.sendMessage(from, to, type, content, {
metadata,
priority,
threadId
});
if (to !== "all") {
const message = await messageService2.getMessageById(messageId);
if (message) {
await sendNotificationToAgent(to, "new_message", { message });
if (sendResourceNotification) {
await sendResourceNotification(to, `agent-hub://messages/${to}`);
}
}
} else {
const agents = await storage2.getAgents();
const message = await messageService2.getMessageById(messageId);
if (message) {
for (const agent of agents) {
if (agent.id !== arguments_.from) {
await sendNotificationToAgent(agent.id, "new_message", { message });
if (sendResourceNotification) {
await sendResourceNotification(agent.id, `agent-hub://messages/${agent.id}`);
}
}
}
}
}
return { success: true, messageId };
},
async get_messages(arguments_) {
const agent = validateIdentifier(arguments_.agent, "agent");
const type = arguments_.type ? validateMessageType(arguments_.type) : void 0;
const since = arguments_.since;
const { markAsRead } = arguments_;
const result = await messageService2.getMessages(agent, {
type,
since,
markAsRead
});
return result;
}
};
}
// src/tools/handlers.ts
function createToolHandlers(services) {
const messageHandlers = createMessageHandlers(
services.messageService,
services.storage,
services.sendNotificationToAgent,
services.sendResourceNotification
);
const featuresHandler = new FeaturesHandler(services.storage);
return {
async send_message(arguments_) {
const validatedArguments = validateToolInput("send_message", arguments_);
return messageHandlers.send_message(validatedArguments);
},
async get_messages(arguments_) {
const validatedArguments = validateToolInput("get_messages", arguments_);
return messageHandlers.get_messages(validatedArguments);
},
async register_agent(arguments_) {
const validatedArguments = validateToolInput("register_agent", arguments_);
const currentSession = services.getCurrentSession();
let agent;
let isExistingAgent = false;
const { projectPath } = validatedArguments;
const proposedAgentId = validatedArguments.id ? validatedArguments.id : path.basename(projectPath);
if (validatedArguments.id) {
const existingAgentById = await services.storage.findAgentById(proposedAgentId);
if (existingAgentById && existingAgentById.projectPath !== projectPath) {
return {
success: false,
error: "AGENT_ID_CONFLICT",
message: `\u274C Agent ID '${proposedAgentId}' is already registered with a different project path (${existingAgentById.projectPath}). Cannot register with ${projectPath}.`,
existingAgent: {
id: existingAgentById.id,
projectPath: existingAgentById.projectPath,
role: existingAgentById.role
}
};
}
}
const existingAgent = await services.storage.findAgentByProjectPath(projectPath);
if (existingAgent) {
isExistingAgent = true;
agent = existingAgent;
agent.lastSeen = Date.now();
agent.status = "active";
if (validatedArguments.role) {
agent.role = validatedArguments.role;
}
if (validatedArguments.capabilities) {
agent.capabilities = [
.../* @__PURE__ */ new Set([...agent.capabilities, ...validatedArguments.capabilities])
];
}
if (validatedArguments.collaboratesWith) {
agent.collaboratesWith = validatedArguments.collaboratesWith;
}
} else {
const agentId = validatedArguments.id ? validatedArguments.id : path.basename(projectPath);
if (projectPath && projectPath !== "unknown") {
agent = await createAgentFromProjectPath(agentId, projectPath);
if (validatedArguments.capabilities) {
agent.capabilities = [
.../* @__PURE__ */ new Set([...agent.capabilities, ...validatedArguments.capabilities])
];
}
if (validatedArguments.role) {
agent.role = validatedArguments.role;
}
if (validatedArguments.collaboratesWith) {
agent.collaboratesWith = validatedArguments.collaboratesWith;
}
} else {
agent = {
id: agentId,
projectPath,
role: validatedArguments.role,
capabilities: validatedArguments.capabilities ?? [],
status: "active",
lastSeen: Date.now(),
collaboratesWith: validatedArguments.collaboratesWith ?? []
};
}
}
agent.status = "active";
if (currentSession) {
currentSession.agent = agent;
}
await services.storage.saveAgent(agent);
if (isExistingAgent) {
await services.broadcastNotification("agent_rejoined", { agent });
} else {
await services.broadcastNotification("agent_joined", { agent });
}
const actionVerb = isExistingAgent ? "reconnected" : "registered";
return {
success: true,
agent,
message: `\u2705 Agent ${actionVerb} successfully! ${isExistingAgent ? "Welcome back" : "Welcome"} ${agent.id} (${agent.role}).`,
detectedCapabilities: agent.capabilities,
collaborationReady: true,
reconnected: isExistingAgent
};
},
async get_hub_status(arguments_) {
validateToolInput("get_hub_status", arguments_);
return services.agentService.getHubStatus();
},
// Features system tools
async create_feature(arguments_) {
const result = await featuresHandler.handleFeatureTool("create_feature", arguments_);
if (result.success) {
await services.broadcastNotification("feature_created", {
feature: result.feature
});
}
return result;
},
async create_task(arguments_) {
const result = await featuresHandler.handleFeatureTool("create_task", arguments_);
if (result.success) {
await services.broadcastNotification("task_created", {
featureId: arguments_.featureId,
task: result.task,
delegations: result.delegations
});
}
return result;
},
async create_subtask(arguments_) {
return featuresHandler.handleFeatureTool("create_subtask", arguments_);
},
async get_features(arguments_) {
return featuresHandler.handleFeatureTool("get_features", arguments_);
},
async get_feature(arguments_) {
return featuresHandler.handleFeatureTool("get_feature", arguments_);
},
async accept_delegation(arguments_) {
const result = await featuresHandler.handleFeatureTool("accept_delegation", arguments_);
if (result.success) {
await services.broadcastNotification("delegation_accepted", {
featureId: arguments_.featureId,
delegationId: arguments_.delegationId,
agentId: arguments_.agentId
});
}
return result;
},
async update_subtask(arguments_) {
const result = await featuresHandler.handleFeatureTool("update_subtask", arguments_);
if (result.success) {
await services.broadcastNotification("subtask_updated", {
featureId: arguments_.featureId,
subtaskId: arguments_.subtaskId,
status: arguments_.status
});
}
return result;
},
async sync(arguments_) {
const validatedArguments = validateToolInput("sync", arguments_);
const { agentId } = validatedArguments;
const markAsRead = validatedArguments.markAsRead !== false;
try {
const [messagesResult, workloadRaw, hubStatusResult] = await Promise.all([
messageHandlers.get_messages({ agent: agentId, markAsRead }),
services.storage.getAgentWorkload(agentId),
services.agentService.getHubStatus()
]);
const workloadResult = {
success: true,
activeFeatures: workloadRaw.activeFeatures,
summary: {
totalFeatures: workloadRaw.activeFeatures.length,
totalDelegations: workloadRaw.activeFeatures.reduce(
(sum, f) => sum + f.myDelegations.length,
0
),
featuresByPriority: workloadRaw.activeFeatures.reduce(
(acc, f) => {
acc[f.feature.priority] = (acc[f.feature.priority] || 0) + 1;
return acc;
},
{}
)
}
};
return {
success: true,
timestamp: Date.now(),
messages: messagesResult,
workload: workloadResult,
hubStatus: hubStatusResult
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error",
timestamp: Date.now()
};
}
}
};
}
// src/servers/mcp.ts
function createMcpServer(deps) {
const server = new Server(
{
name: "agent-hub-mcp",
version: "0.2.0"
},
{
capabilities: {
tools: {},
resources: {
subscribe: true,
listChanged: true
}
}
}
);
async function updateAgentLastSeen() {
const currentSession = deps.getCurrentSession();
if (currentSession?.agent) {
await deps.storage.updateAgent(currentSession.agent.id, {
lastSeen: Date.now(),
status: "active"
// Ensure they're marked active when making requests
});
}
}
const toolHandlerServices = {
storage: deps.storage,
messageService: deps.messageService,
agentService: deps.agentService,
getCurrentSession: deps.getCurrentSession,
broadcastNotification: deps.broadcastNotification,
sendNotificationToAgent: deps.sendNotificationToAgent,
sendResourceNotification: deps.sendResourceNotification
};
const toolHandlers = createToolHandlers(toolHandlerServices);
server.setRequestHandler(InitializeRequestSchema, async (request) => {
const agents = await deps.storage.getAgents();
const activeAgents = agents.filter((a) => Date.now() - a.lastSeen < 5 * 60 * 1e3);
const totalMessages = await deps.storage.getMessages({});
const unreadCount = totalMessages.filter((m) => !m.read).length;
return {
protocolVersion: request.params.protocolVersion,
capabilities: {
tools: {},
resources: {
subscribe: true,
listChanged: true
}
},
serverInfo: {
name: "agent-hub-mcp",
version: "0.2.0",
activeAgents: activeAgents.length,
totalMessages: unreadCount,
collaborationHints: activeAgents.map((a) => ({
id: a.id,
role: a.role,
capabilities: a.capabilities
}))
},
instructions: `\u{1F7E2} CONNECTED TO AGENT-HUB | Registration Required
\u26A0\uFE0F REGIST