Add MCP server registry and support server references in tasks#307
Add MCP server registry and support server references in tasks#307sroussey wants to merge 3 commits intorebuild-clifrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds an MCP server registry to let MCP-related tasks reference pre-configured servers by ID, and updates task execution to resolve format-annotated config/inputs/outputs via the schema input resolver system.
Changes:
- Introduces MCP server registry primitives (schema, repository, in-memory implementation, global registry + input resolver).
- Updates MCP tasks to accept a
serverreference and merge registry-based config with inline overrides. - Enhances
TaskRunnerto resolve schema-annotated config properties and output properties before/after execution.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 13 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/tasks/src/task/mcp/McpToolCallTask.ts | Adds server reference support and merges resolved server config for tool calls + schema discovery. |
| packages/tasks/src/task/mcp/McpPromptGetTask.ts | Adds server reference support and merges resolved server config for prompt retrieval + schema discovery. |
| packages/tasks/src/task/mcp/McpResourceReadTask.ts | Adds server reference support and merges resolved server config for resource reads. |
| packages/tasks/src/task/mcp/McpListTask.ts | Adds server reference support and merges resolved server config for listing tools/resources/prompts. |
| packages/tasks/src/mcp-server/McpServerSchema.ts | Defines MCP server config/record JSON schemas and related TS types. |
| packages/tasks/src/mcp-server/McpServerRepository.ts | Implements a tabular-storage-backed repository with event emission for server records. |
| packages/tasks/src/mcp-server/InMemoryMcpServerRepository.ts | Provides an in-memory repository implementation. |
| packages/tasks/src/mcp-server/McpServerRegistry.ts | Registers a global MCP server repository service + input resolver for mcp-server references. |
| packages/tasks/src/common.ts | Re-exports the new MCP server registry APIs from the tasks package. |
| packages/task-graph/src/task/TaskRunner.ts | Resolves schema-annotated config and output properties via resolveSchemaInputs() during task runs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| private getMcpServerConfig(input: McpListTaskInput): McpServerConfig { | ||
| const server = (input as Record<string, unknown>).server as | ||
| | Record<string, unknown> | ||
| | string | ||
| | undefined; | ||
| const base = typeof server === "object" && server !== null ? server : {}; | ||
| const merged = { ...base } as Record<string, unknown>; | ||
| for (const key of ["transport", "server_url", "command", "args", "env"] as const) { | ||
| const val = (input as Record<string, unknown>)[key]; | ||
| if (val !== undefined) { | ||
| merged[key] = val; | ||
| } | ||
| } | ||
| if (!merged.transport) { | ||
| throw new Error("MCP server transport is required (provide inline or via server registry)"); | ||
| } | ||
| return merged as unknown as McpServerConfig; | ||
| } |
There was a problem hiding this comment.
@copilot apply changes based on this feedback
| // Resolve schema-annotated config properties (e.g., mcp-server references) | ||
| const configSchemaResult = (this.task.constructor as typeof Task).configSchema(); | ||
| if (configSchemaResult) { | ||
| const resolvedConfig = await resolveSchemaInputs( | ||
| this.task.config as Record<string, unknown>, | ||
| configSchemaResult, | ||
| { registry: this.registry } | ||
| ); | ||
| Object.assign(this.task.config, resolvedConfig); | ||
| } |
| // Resolve schema-annotated config properties (e.g., mcp-server references) | ||
| const configSchemaResult = (this.task.constructor as typeof Task).configSchema(); | ||
| if (configSchemaResult) { | ||
| const resolvedConfig = await resolveSchemaInputs( | ||
| this.task.config as Record<string, unknown>, | ||
| configSchemaResult, | ||
| { registry: this.registry } | ||
| ); | ||
| Object.assign(this.task.config, resolvedConfig); | ||
| } |
| * A lightweight MCP server configuration suitable for task config/input schemas. | ||
| * | ||
| * When a string `server_id` is provided, the input resolver system resolves it | ||
| * to the full server record from the registry. | ||
| */ |
There was a problem hiding this comment.
@copilot apply changes based on this feedback
| required: ["tool_name"], | ||
| if: { properties: { transport: { const: "stdio" } }, required: ["transport"] }, | ||
| then: { required: ["command"] }, | ||
| else: { required: ["server_url"] }, | ||
| else: {}, |
There was a problem hiding this comment.
@copilot apply changes based on this feedback
| export type McpServerEventListeners = { | ||
| server_added: (server: McpServerRecord) => void; | ||
| server_removed: (server: McpServerRecord) => void; | ||
| server_updated: (server: McpServerRecord) => void; | ||
| }; |
| // Register the MCP server resolver for format: "mcp-server" | ||
| registerInputResolver("mcp-server", resolveMcpServerFromRegistry); |
| private getMcpServerConfig(): McpServerConfig | undefined { | ||
| const server = this.config.server as Record<string, unknown> | string | undefined; | ||
| const base = typeof server === "object" && server !== null ? server : {}; | ||
| const merged = { ...base } as Record<string, unknown>; | ||
| // Inline fields override registry values | ||
| for (const key of ["transport", "server_url", "command", "args", "env"] as const) { | ||
| if (this.config[key] !== undefined) { | ||
| merged[key] = this.config[key]; | ||
| } | ||
| } | ||
| if (!merged.transport) return undefined; | ||
| return merged as unknown as McpServerConfig; | ||
| } |
| required: ["prompt_name"], | ||
| if: { properties: { transport: { const: "stdio" } }, required: ["transport"] }, | ||
| then: { required: ["command"] }, | ||
| else: { required: ["server_url"] }, | ||
| else: {}, |
| required: ["resource_uri"], | ||
| if: { properties: { transport: { const: "stdio" } }, required: ["transport"] }, | ||
| then: { required: ["command"] }, | ||
| else: { required: ["server_url"] }, | ||
| else: {}, | ||
| allOf: mcpServerConfigSchema.allOf, |
…hemas - Create McpServerSchema, McpServerRepository, InMemoryMcpServerRepository, and McpServerRegistry following the ModelRegistry pattern - MCP servers can now be registered once and referenced by string ID (format: "mcp-server") in task configs and inputs - Extend TaskRunner to run resolveSchemaInputs on config and output schemas, not just input schemas, making the resolver system uniform - Add optional 'server' field to all MCP task configs (ToolCall, ResourceRead, PromptGet) and McpListTask input, with backward-compatible inline config - Inline config fields override registry values for per-task customization https://claude.ai/code/session_01X1uCtkgCkeH2GJq1QAzUp1
…us re-export The workglow meta-package re-exports both @workglow/util and @workglow/tasks, which both exported a type named McpServerConfig. Renamed the tasks version to McpServerTaskConfig to avoid the TS2308 ambiguity error. https://claude.ai/code/session_01X1uCtkgCkeH2GJq1QAzUp1
8e182c2 to
0699590
Compare
There was a problem hiding this comment.
Pull request overview
This PR introduces an MCP server registry/repository so MCP-related tasks can reference preconfigured servers by ID (via format: "mcp-server") instead of requiring full inline server configuration, and updates task execution to resolve schema-annotated config/outputs before running.
Changes:
- Added MCP server schema + repository + global registry integration (including an input resolver for
"mcp-server"). - Updated MCP tasks (tool call, prompt get, resource read, list) to accept a
serverreference and merge it with inline overrides. - Enhanced
TaskRunnerto resolve schema-annotated config properties and output properties viaresolveSchemaInputs().
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 14 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/tasks/src/task/mcp/McpToolCallTask.ts | Adds server reference support and merges registry + inline MCP config for tool calls. |
| packages/tasks/src/task/mcp/McpPromptGetTask.ts | Adds server reference support and merges registry + inline MCP config for prompt retrieval. |
| packages/tasks/src/task/mcp/McpResourceReadTask.ts | Adds server reference support and merges registry + inline MCP config for resource reads. |
| packages/tasks/src/task/mcp/McpListTask.ts | Adds server reference support and merges registry + inline MCP config for listing tools/resources/prompts. |
| packages/tasks/src/mcp-server/McpServerSchema.ts | Introduces schemas/types for MCP server configs and persisted records. |
| packages/tasks/src/mcp-server/McpServerRepository.ts | Adds a repository abstraction with event emission for MCP server records. |
| packages/tasks/src/mcp-server/InMemoryMcpServerRepository.ts | Provides an in-memory repository implementation. |
| packages/tasks/src/mcp-server/McpServerRegistry.ts | Registers a global MCP server repository + input resolver for "mcp-server". |
| packages/tasks/src/common.ts | Exports MCP server registry/repository/schema APIs from @workglow/tasks. |
| packages/task-graph/src/task/TaskRunner.ts | Resolves schema-annotated config and output properties before/after task execution. |
| examples/cli/src/storage.ts | Switches CLI MCP persistence to use McpServerRepository and shared schemas. |
| examples/cli/src/lib.ts | Renames exported CLI factory to createMcpServerRepository. |
| examples/cli/src/commands/mcp.ts | Updates CLI MCP commands to use the new repository + server_id schema. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const merged = { ...base } as Record<string, unknown>; | ||
| for (const key of ["transport", "server_url", "command", "args", "env"] as const) { | ||
| if (this.config[key] !== undefined) { | ||
| merged[key] = this.config[key]; | ||
| } | ||
| } | ||
| if (!merged.transport) return undefined; | ||
| return merged as unknown as McpServerConfig; |
| transport: cfg.transport, | ||
| server_url: cfg.server_url, | ||
| command: cfg.command, | ||
| args: cfg.args, | ||
| env: cfg.env, |
| required: ["resource_uri"], | ||
| if: { properties: { transport: { const: "stdio" } }, required: ["transport"] }, | ||
| then: { required: ["command"] }, | ||
| else: { required: ["server_url"] }, | ||
| else: {}, | ||
| allOf: mcpServerConfigSchema.allOf, |
| for (const key of ["transport", "server_url", "command", "args", "env"] as const) { | ||
| if (this.config[key] !== undefined) { | ||
| merged[key] = this.config[key]; | ||
| } | ||
| } | ||
| if (!merged.transport) { | ||
| throw new Error("MCP server transport is required (provide inline or via server registry)"); | ||
| } |
| * When a string `server_id` is provided, the input resolver system resolves it | ||
| * to the full server record from the registry. |
| // Inline fields override registry values | ||
| for (const key of ["transport", "server_url", "command", "args", "env"] as const) { | ||
| if (this.config[key] !== undefined) { | ||
| merged[key] = this.config[key]; | ||
| } | ||
| } | ||
| if (!merged.transport) return undefined; |
| then: { required: ["command"] }, | ||
| else: { required: ["server_url"] }, | ||
| else: {}, | ||
| allOf: mcpServerConfigSchema.allOf, |
| transport: cfg.transport, | ||
| server_url: cfg.server_url, | ||
| command: cfg.command, | ||
| args: cfg.args, | ||
| env: cfg.env, |
| }, | ||
| required: ["transport", "list_type"], | ||
| required: ["list_type"], | ||
| allOf: mcpServerConfigSchema.allOf, |
| const resolvedConfig = await resolveSchemaInputs( | ||
| this.task.config as Record<string, unknown>, |
|
@copilot open a new pull request to apply changes based on the comments in this thread |
Summary
This PR introduces a centralized MCP server registry system that allows MCP tasks to reference pre-configured servers by ID instead of requiring inline configuration. It includes a new repository pattern for managing MCP server configurations and updates all MCP-related tasks to support both registry-based and inline server configuration.
Key Changes
New MCP Server Registry System
McpServerRepositorybase class for managing MCP server configurations with event emission (server_added, server_removed, server_updated)InMemoryMcpServerRepositoryfor in-memory storage of server configurationsMcpServerRegistrywith global service registration and input resolver integrationMcpServerSchemadefiningMcpServerConfigandMcpServerRecordtypesTask Configuration Updates
McpToolCallTask,McpPromptGetTask,McpResourceReadTask, andMcpListTaskto support aserverproperty with format"mcp-server"transportno longer required in task configs (can be provided via registry reference)getMcpServerConfig()helper method to merge registry-based and inline configurations, with inline values taking precedenceTask Runner Enhancement
TaskRunnerto resolve format-annotated config properties (likemcp-serverreferences) before task executionInput Resolver Integration
resolveMcpServerFromRegistry()as the handler for"mcp-server"format, enabling automatic resolution of server IDs to full configurationsImplementation Details
https://claude.ai/code/session_01X1uCtkgCkeH2GJq1QAzUp1