Runtime MCP server with typed validation, capability discovery, and composition.
Source repo: effectorHQ/effector-server. CLI: effector-serve or effector-server (same binary).
Wraps any skill directory as an MCP server that validates tool I/O against declared types at runtime, lets agents discover capabilities by type signature, and suggests multi-step skill chains.
Three runtime layers β Typed, Composable, Verifiable β built on the effectorHQ toolchain.
npm install @effectorhq/serverOr run directly:
npx @effectorhq/server ./my-skills# Start a guarded MCP server (stdin/stdout)
effector-serve ./skills
# Strict mode β reject calls with type validation errors
effector-serve ./skills --strict
# Allow tools that require network access
effector-serve ./skills --allow-network
# Allow both network and subprocess
effector-serve ./skills --allow-network --allow-subprocessimport { createGuardedServer } from '@effectorhq/server';
const server = await createGuardedServer('./skills', {
strict: false, // warn on type errors (default)
allowNetwork: true, // permit network-requiring tools
allowSubprocess: false,
});
// Use handleRequest for testing or embedding
const response = server.handleRequest({
jsonrpc: '2.0',
id: 1,
method: 'tools/list',
});
console.log(response.result.tools);
// β [...your skills..., effector_discover, effector_compose, effector_inspect]ββββββββββββββββ ββββββββββββββββββββ ββββββββββββββββββ
β Your Skills ββββββΆβ effector-serve ββββββΆβ MCP Client β
β (SKILL.md + β β β β (Claude, etc) β
β effector.toml) β ββ Type Guard β ββββββββββββββββββ
ββββββββββββββββ β ββ Permissions β
β ββ Discovery β
β ββ Composition β
β ββ Telemetry β
ββββββββββββββββββββ
Your MCP server returns skill instructions. The LLM calls tools with whatever arguments it wants. If types don't match, you find out at runtime β or never.
Every tools/call is validated against the declared [effector.interface] types. Permission violations are caught before execution. Agents can query "what skills accept CodeDiff?" and "how do I get from CodeDiff to Notification?" at runtime.
effector-serve adds three MCP tools alongside your skills:
Find skills by type signature.
{
"name": "effector_discover",
"arguments": {
"input_type": "CodeDiff",
"output_type": "ReviewReport"
}
}Returns matching skills with their interfaces, permissions, and descriptions.
Suggest multi-step skill chains between types.
{
"name": "effector_compose",
"arguments": {
"from_type": "CodeDiff",
"to_type": "Notification",
"max_depth": 3
}
}Uses BFS to find the shortest type-compatible chains. If code-review outputs ReviewReport and notify accepts Markdown, and format-report bridges them, you get:
CodeDiff β [code-review] β ReviewReport β [format-report] β Markdown β [notify] β Notification
View a skill's full typed interface.
{
"name": "effector_inspect",
"arguments": { "tool_name": "code-review" }
}Returns interface, permissions, metadata, and inputSchema.
Every tools/call is checked against the skill's declared [effector.interface]:
# effector.toml
[effector.interface]
input = "CodeDiff"
output = "ReviewReport"If the input is missing required fields for the CodeDiff type (e.g., files), the server returns a structured error:
{
"error": {
"code": -32602,
"message": "Input validation failed: CodeDiff: missing required field: files",
"data": {
"code": "EFFECTOR_VALIDATION_ERROR",
"direction": "input",
"typeName": "CodeDiff",
"missingFields": ["files"]
}
}
}In permissive mode (default), validation warnings are recorded via telemetry but the call proceeds. In strict mode (--strict), validation errors reject the call.
Skills declare permissions in effector.toml:
[effector.permissions]
network = true
subprocess = falseBy default, the server blocks tools that require network or subprocess. Use --allow-network / --allow-subprocess to permit them.
{
"error": {
"code": -32600,
"message": "Permission denied: Tool \"deploy\" requires network access. Use --allow-network to permit.",
"data": { "code": "EFFECTOR_PERMISSION_DENIED" }
}
}All validation results, tool calls, and permission checks are tracked in an in-memory ring buffer:
const stats = server.telemetry.getStats();
// {
// totalEvents: 142,
// validationPass: 120,
// validationFail: 8,
// callCount: 130,
// permissionDenied: 2,
// byTool: {
// "code-review": { calls: 45, validationPass: 44, validationFail: 1 },
// ...
// }
// }| Export | Description |
|---|---|
createGuardedServer(dir, options?) |
Create a guarded MCP server |
startGuardedServer(dir, options?) |
Create and start on stdin/stdout |
createTelemetry(options?) |
Create a standalone telemetry instance |
createPermissionEnforcer(config?) |
Create a standalone permission enforcer |
handleDiscover(args, toolMap) |
Discovery tool handler (for embedding) |
handleCompose(args, toolMap) |
Composition tool handler (for embedding) |
handleInspect(args, toolMap) |
Inspection tool handler (for embedding) |
{
strict: false, // Reject on validation errors (default: warn)
allowNetwork: false, // Allow tools with network permission
allowSubprocess: false, // Allow tools with subprocess permission
telemetry: true, // Enable telemetry tracking
}effector-serve β Runtime MCP server with typed validation
Usage:
effector-serve <skills-dir> [options]
Options:
--strict Reject tools/call on type validation errors
--allow-network Allow tools that declare network permission
--allow-subprocess Allow tools that declare subprocess permission
--no-telemetry Disable telemetry event tracking
effector-serve wraps @effectorhq/skill-mcp (the existing MCP server) and intercepts handleRequest:
βββββββββββββββββββββββββββββββββββββββ
β effector-serve β
β β
stdin ββββββββββΆβ handleRequest() β
β β β
β ββ initialize β identity + caps β
β ββ tools/list β skills + 3 built-inβ
β ββ tools/call β
β β β
β ββ Permission check β
β ββ Type validation (guard) β
β ββ Telemetry recording β
β β β
β ββ Synthetic tool? β
β β ββ effector_discover β
β β ββ effector_compose β
β β ββ effector_inspect β
β β β
β ββ Delegate to inner server β
β (instruction passthrough)β
stdout ββββββββββ β
βββββββββββββββββββββββββββββββββββββββ
All @effectorhq/* packages, zero external dependencies:
| Package | Used For |
|---|---|
@effectorhq/core |
Type guards, type checker, error types |
@effectorhq/skill-mcp |
Inner MCP server (JSON-RPC 2.0) |
@effectorhq/compose |
BFS composition suggestion |
@effectorhq/types |
Standard type catalog |
This package uses only @effectorhq/* packages and Node.js built-ins. The @effectorhq/* packages themselves have zero external dependencies. The entire dependency tree is:
@effectorhq/server
βββ @effectorhq/core (0 deps)
βββ @effectorhq/skill-mcp (β @effectorhq/core)
βββ @effectorhq/compose (β @effectorhq/core, @effectorhq/types)
βββ @effectorhq/types (0 deps)
No supply chain risk. No version conflicts. Fast installs.
See CONTRIBUTING.md for guidelines.
This project is currently licensed under the Apache License, Version 2.0.