This comprehensive implementation includes: - FastAPI backend with MCP server integration - React/TypeScript frontend with Vite - PostgreSQL database with Redis caching - Grafana/Prometheus monitoring stack - Docker Compose orchestration - Full MCP protocol support for Claude Code integration Features: - Agent discovery and management across network - Visual workflow editor and execution engine - Real-time task coordination and monitoring - Multi-model support with specialized agents - Distributed development task allocation 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
295 lines
13 KiB
JavaScript
295 lines
13 KiB
JavaScript
"use strict";
|
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
};
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.Client = void 0;
|
|
const protocol_js_1 = require("../shared/protocol.js");
|
|
const types_js_1 = require("../types.js");
|
|
const ajv_1 = __importDefault(require("ajv"));
|
|
/**
|
|
* An MCP client on top of a pluggable transport.
|
|
*
|
|
* The client will automatically begin the initialization flow with the server when connect() is called.
|
|
*
|
|
* To use with custom types, extend the base Request/Notification/Result types and pass them as type parameters:
|
|
*
|
|
* ```typescript
|
|
* // Custom schemas
|
|
* const CustomRequestSchema = RequestSchema.extend({...})
|
|
* const CustomNotificationSchema = NotificationSchema.extend({...})
|
|
* const CustomResultSchema = ResultSchema.extend({...})
|
|
*
|
|
* // Type aliases
|
|
* type CustomRequest = z.infer<typeof CustomRequestSchema>
|
|
* type CustomNotification = z.infer<typeof CustomNotificationSchema>
|
|
* type CustomResult = z.infer<typeof CustomResultSchema>
|
|
*
|
|
* // Create typed client
|
|
* const client = new Client<CustomRequest, CustomNotification, CustomResult>({
|
|
* name: "CustomClient",
|
|
* version: "1.0.0"
|
|
* })
|
|
* ```
|
|
*/
|
|
class Client extends protocol_js_1.Protocol {
|
|
/**
|
|
* Initializes this client with the given name and version information.
|
|
*/
|
|
constructor(_clientInfo, options) {
|
|
var _a;
|
|
super(options);
|
|
this._clientInfo = _clientInfo;
|
|
this._cachedToolOutputValidators = new Map();
|
|
this._capabilities = (_a = options === null || options === void 0 ? void 0 : options.capabilities) !== null && _a !== void 0 ? _a : {};
|
|
this._ajv = new ajv_1.default();
|
|
}
|
|
/**
|
|
* Registers new capabilities. This can only be called before connecting to a transport.
|
|
*
|
|
* The new capabilities will be merged with any existing capabilities previously given (e.g., at initialization).
|
|
*/
|
|
registerCapabilities(capabilities) {
|
|
if (this.transport) {
|
|
throw new Error("Cannot register capabilities after connecting to transport");
|
|
}
|
|
this._capabilities = (0, protocol_js_1.mergeCapabilities)(this._capabilities, capabilities);
|
|
}
|
|
assertCapability(capability, method) {
|
|
var _a;
|
|
if (!((_a = this._serverCapabilities) === null || _a === void 0 ? void 0 : _a[capability])) {
|
|
throw new Error(`Server does not support ${capability} (required for ${method})`);
|
|
}
|
|
}
|
|
async connect(transport, options) {
|
|
await super.connect(transport);
|
|
// When transport sessionId is already set this means we are trying to reconnect.
|
|
// In this case we don't need to initialize again.
|
|
if (transport.sessionId !== undefined) {
|
|
return;
|
|
}
|
|
try {
|
|
const result = await this.request({
|
|
method: "initialize",
|
|
params: {
|
|
protocolVersion: types_js_1.LATEST_PROTOCOL_VERSION,
|
|
capabilities: this._capabilities,
|
|
clientInfo: this._clientInfo,
|
|
},
|
|
}, types_js_1.InitializeResultSchema, options);
|
|
if (result === undefined) {
|
|
throw new Error(`Server sent invalid initialize result: ${result}`);
|
|
}
|
|
if (!types_js_1.SUPPORTED_PROTOCOL_VERSIONS.includes(result.protocolVersion)) {
|
|
throw new Error(`Server's protocol version is not supported: ${result.protocolVersion}`);
|
|
}
|
|
this._serverCapabilities = result.capabilities;
|
|
this._serverVersion = result.serverInfo;
|
|
// HTTP transports must set the protocol version in each header after initialization.
|
|
if (transport.setProtocolVersion) {
|
|
transport.setProtocolVersion(result.protocolVersion);
|
|
}
|
|
this._instructions = result.instructions;
|
|
await this.notification({
|
|
method: "notifications/initialized",
|
|
});
|
|
}
|
|
catch (error) {
|
|
// Disconnect if initialization fails.
|
|
void this.close();
|
|
throw error;
|
|
}
|
|
}
|
|
/**
|
|
* After initialization has completed, this will be populated with the server's reported capabilities.
|
|
*/
|
|
getServerCapabilities() {
|
|
return this._serverCapabilities;
|
|
}
|
|
/**
|
|
* After initialization has completed, this will be populated with information about the server's name and version.
|
|
*/
|
|
getServerVersion() {
|
|
return this._serverVersion;
|
|
}
|
|
/**
|
|
* After initialization has completed, this may be populated with information about the server's instructions.
|
|
*/
|
|
getInstructions() {
|
|
return this._instructions;
|
|
}
|
|
assertCapabilityForMethod(method) {
|
|
var _a, _b, _c, _d, _e;
|
|
switch (method) {
|
|
case "logging/setLevel":
|
|
if (!((_a = this._serverCapabilities) === null || _a === void 0 ? void 0 : _a.logging)) {
|
|
throw new Error(`Server does not support logging (required for ${method})`);
|
|
}
|
|
break;
|
|
case "prompts/get":
|
|
case "prompts/list":
|
|
if (!((_b = this._serverCapabilities) === null || _b === void 0 ? void 0 : _b.prompts)) {
|
|
throw new Error(`Server does not support prompts (required for ${method})`);
|
|
}
|
|
break;
|
|
case "resources/list":
|
|
case "resources/templates/list":
|
|
case "resources/read":
|
|
case "resources/subscribe":
|
|
case "resources/unsubscribe":
|
|
if (!((_c = this._serverCapabilities) === null || _c === void 0 ? void 0 : _c.resources)) {
|
|
throw new Error(`Server does not support resources (required for ${method})`);
|
|
}
|
|
if (method === "resources/subscribe" &&
|
|
!this._serverCapabilities.resources.subscribe) {
|
|
throw new Error(`Server does not support resource subscriptions (required for ${method})`);
|
|
}
|
|
break;
|
|
case "tools/call":
|
|
case "tools/list":
|
|
if (!((_d = this._serverCapabilities) === null || _d === void 0 ? void 0 : _d.tools)) {
|
|
throw new Error(`Server does not support tools (required for ${method})`);
|
|
}
|
|
break;
|
|
case "completion/complete":
|
|
if (!((_e = this._serverCapabilities) === null || _e === void 0 ? void 0 : _e.completions)) {
|
|
throw new Error(`Server does not support completions (required for ${method})`);
|
|
}
|
|
break;
|
|
case "initialize":
|
|
// No specific capability required for initialize
|
|
break;
|
|
case "ping":
|
|
// No specific capability required for ping
|
|
break;
|
|
}
|
|
}
|
|
assertNotificationCapability(method) {
|
|
var _a;
|
|
switch (method) {
|
|
case "notifications/roots/list_changed":
|
|
if (!((_a = this._capabilities.roots) === null || _a === void 0 ? void 0 : _a.listChanged)) {
|
|
throw new Error(`Client does not support roots list changed notifications (required for ${method})`);
|
|
}
|
|
break;
|
|
case "notifications/initialized":
|
|
// No specific capability required for initialized
|
|
break;
|
|
case "notifications/cancelled":
|
|
// Cancellation notifications are always allowed
|
|
break;
|
|
case "notifications/progress":
|
|
// Progress notifications are always allowed
|
|
break;
|
|
}
|
|
}
|
|
assertRequestHandlerCapability(method) {
|
|
switch (method) {
|
|
case "sampling/createMessage":
|
|
if (!this._capabilities.sampling) {
|
|
throw new Error(`Client does not support sampling capability (required for ${method})`);
|
|
}
|
|
break;
|
|
case "elicitation/create":
|
|
if (!this._capabilities.elicitation) {
|
|
throw new Error(`Client does not support elicitation capability (required for ${method})`);
|
|
}
|
|
break;
|
|
case "roots/list":
|
|
if (!this._capabilities.roots) {
|
|
throw new Error(`Client does not support roots capability (required for ${method})`);
|
|
}
|
|
break;
|
|
case "ping":
|
|
// No specific capability required for ping
|
|
break;
|
|
}
|
|
}
|
|
async ping(options) {
|
|
return this.request({ method: "ping" }, types_js_1.EmptyResultSchema, options);
|
|
}
|
|
async complete(params, options) {
|
|
return this.request({ method: "completion/complete", params }, types_js_1.CompleteResultSchema, options);
|
|
}
|
|
async setLoggingLevel(level, options) {
|
|
return this.request({ method: "logging/setLevel", params: { level } }, types_js_1.EmptyResultSchema, options);
|
|
}
|
|
async getPrompt(params, options) {
|
|
return this.request({ method: "prompts/get", params }, types_js_1.GetPromptResultSchema, options);
|
|
}
|
|
async listPrompts(params, options) {
|
|
return this.request({ method: "prompts/list", params }, types_js_1.ListPromptsResultSchema, options);
|
|
}
|
|
async listResources(params, options) {
|
|
return this.request({ method: "resources/list", params }, types_js_1.ListResourcesResultSchema, options);
|
|
}
|
|
async listResourceTemplates(params, options) {
|
|
return this.request({ method: "resources/templates/list", params }, types_js_1.ListResourceTemplatesResultSchema, options);
|
|
}
|
|
async readResource(params, options) {
|
|
return this.request({ method: "resources/read", params }, types_js_1.ReadResourceResultSchema, options);
|
|
}
|
|
async subscribeResource(params, options) {
|
|
return this.request({ method: "resources/subscribe", params }, types_js_1.EmptyResultSchema, options);
|
|
}
|
|
async unsubscribeResource(params, options) {
|
|
return this.request({ method: "resources/unsubscribe", params }, types_js_1.EmptyResultSchema, options);
|
|
}
|
|
async callTool(params, resultSchema = types_js_1.CallToolResultSchema, options) {
|
|
const result = await this.request({ method: "tools/call", params }, resultSchema, options);
|
|
// Check if the tool has an outputSchema
|
|
const validator = this.getToolOutputValidator(params.name);
|
|
if (validator) {
|
|
// If tool has outputSchema, it MUST return structuredContent (unless it's an error)
|
|
if (!result.structuredContent && !result.isError) {
|
|
throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidRequest, `Tool ${params.name} has an output schema but did not return structured content`);
|
|
}
|
|
// Only validate structured content if present (not when there's an error)
|
|
if (result.structuredContent) {
|
|
try {
|
|
// Validate the structured content (which is already an object) against the schema
|
|
const isValid = validator(result.structuredContent);
|
|
if (!isValid) {
|
|
throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, `Structured content does not match the tool's output schema: ${this._ajv.errorsText(validator.errors)}`);
|
|
}
|
|
}
|
|
catch (error) {
|
|
if (error instanceof types_js_1.McpError) {
|
|
throw error;
|
|
}
|
|
throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, `Failed to validate structured content: ${error instanceof Error ? error.message : String(error)}`);
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
cacheToolOutputSchemas(tools) {
|
|
this._cachedToolOutputValidators.clear();
|
|
for (const tool of tools) {
|
|
// If the tool has an outputSchema, create and cache the Ajv validator
|
|
if (tool.outputSchema) {
|
|
try {
|
|
const validator = this._ajv.compile(tool.outputSchema);
|
|
this._cachedToolOutputValidators.set(tool.name, validator);
|
|
}
|
|
catch (_a) {
|
|
// Ignore schema compilation errors
|
|
}
|
|
}
|
|
}
|
|
}
|
|
getToolOutputValidator(toolName) {
|
|
return this._cachedToolOutputValidators.get(toolName);
|
|
}
|
|
async listTools(params, options) {
|
|
const result = await this.request({ method: "tools/list", params }, types_js_1.ListToolsResultSchema, options);
|
|
// Cache the tools and their output schemas for future validation
|
|
this.cacheToolOutputSchemas(result.tools);
|
|
return result;
|
|
}
|
|
async sendRootsListChanged() {
|
|
return this.notification({ method: "notifications/roots/list_changed" });
|
|
}
|
|
}
|
|
exports.Client = Client;
|
|
//# sourceMappingURL=index.js.map
|