migrate-mcp-template-to-effect
Skill3 starsChanged 35 days agoMITView on GitHub
Migrate a project created from the pre-Effect MCP TypeScript template to the Effect-based template while preserving MCP behavior and deployment contracts. Use for this template's Zod/Pino-to-Effect migration, not for generic TypeScript-to-Effect rewrites or native Effect MCP server migrations.
- Reads credentials
In this repository3 other files it gives its agents.
- AGENTS.mdAGENTS.md
- create-mcp-toolSkill
- beads-engineering-backlogSkill
The file
We split this skill into the rules it gives an agent
12 rules. About 0 are common, 4 are distinctive, and 8 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈4
Distinctive · estimate
This author's own choices. Worth a look.
8
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 11
Use this skill when an existing project was created from the earlier version of this repository and still uses Zod, Pino, direct …
- Distinctive: line 17
Keep these behaviors unless the user explicitly requests a separate breaking change:
- Distinctive: line 29
Check the working tree first and preserve unrelated user changes.
- Distinctive: line 37
Do not mechanically rewrite every Promise.
- Very specific: line 75
Keep business logic in an Effect.Effect and cross back to the MCP callback API only with runMcpEffect().
- Very specific: line 76
Use yield* to execute Effects in Effect.gen.
- Very specific: line 77
Use Effect.tryPromise around SDK, Express, Node, or other Promise APIs.
- Very specific: line 78
Use Effect.all with explicit concurrency for independent application effects, such as parallel shutdown cleanup.
- Very specific: line 79
Keep genuine tool failures in-band with createErrorResult({ error }).
- Very specific: line 80
Use yield* logger.info(...) and yield* logger.error(...) inside workflows.
- Very specific: line 89
Run npm run lint, npm run format:check, npm run build, and npm run test:ci.
- Very specific: line 94
Update README.md, AGENTS.md, and the create-mcp-tool skill so new tools follow the Effect patterns.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Migrate the MCP TypeScript Template to Effect
Use this skill when an existing project was created from the earlier version of this repository and still uses Zod, Pino, direct environment parsing, and Promise-based tool handlers.
The goal is to adopt Effect for application workflows without changing the MCP protocol or making users learn a different server boundary.
Preserve the external contract
Keep these behaviors unless the user explicitly requests a separate breaking change:
@modelcontextprotocol/serverremains the MCP implementation- Express remains the HTTP server and
/mcpremains the MCP endpoint /healthremains a plain{ "status": "ok" }liveness endpointPORT,NODE_ENV,SERVER_NAME,SERVER_VERSION, andLOG_LEVELkeep their names.SERVER_NAMEandSERVER_VERSIONdefault topackage.jsonnameandversion; the other defaults stay the same- tool names, MCP annotations, structured output, and elicitation behavior remain compatible
Effect should be introduced behind the MCP SDK boundary. Do not replace the official MCP server with Effect's separate native MCP implementation as part of this migration.
Inspect before editing
Check the working tree first and preserve unrelated user changes. Confirm the project has the expected pre-Effect patterns:
z.object(...)input/output schemas- Pino or
pino-prettylogging - direct
process.envconfiguration asyncMCP callbacks withtry/catchPromise.allfor independent application work
Do not mechanically rewrite every Promise. Identify which code is application workflow and which code is an MCP, Express, Node, or test boundary.
Migration map
| Earlier template | Effect-based template |
| Zod schemas | Effect Schema.Struct, adapted with toMcpSchema() |
.describe() | .annotate({ description: "..." }) |
Pino / pino-pretty | Effect JSON logging (Logger.formatStructured) behind the shared logger facade |
process.env parsing | Effect Config with ConfigProvider.fromEnv() |
async tool workflow | Effect.gen(function* () { ... }) |
await an Effect | yield* the Effect inside Effect.gen |
| Promise rejection handling | Effect.tryPromise, typed failures, and Effect.catch |
Promise.all for independent effects | Effect.all([...], { concurrency: "unbounded" }) |
| MCP callback returns Promise | runMcpEffect() at the callback boundary |
Schema boundary
Define tool contracts with Effect Schema and adapt them at registration time:
const InputSchema = toMcpSchema(
Schema.Struct({
message: Schema.String,
}),
);
server.registerTool(
"echo",
{ inputSchema: InputSchema },
(args, ctx) => runMcpEffect(echo(args, ctx)),
);toMcpSchema() supplies both Standard Schema validation and the JSON Schema converter required by the MCP SDK. It supports the MCP SDK's draft-07 and draft-2020-12 targets. Keep elicitation's restricted requestedSchema as hand-written JSON Schema; it is not interchangeable with a general tool schema.
Effect workflow rules
- Keep business logic in an
Effect.Effectand cross back to the MCP callback API only withrunMcpEffect(). - Use
yield*to execute Effects inEffect.gen; merely constructing an Effect does not run it. - Use
Effect.tryPromisearound SDK, Express, Node, or other Promise APIs. - Use
Effect.allwith explicit concurrency for independent application effects, such as parallel shutdown cleanup. - Keep genuine tool failures in-band with
createErrorResult({ error }); valid outcomes such as elicitation decline/cancel are normalcreateTextResultresults. - Use
yield* logger.info(...)andyield* logger.error(...)inside workflows. Never log raw tool arguments.
Tests and verification
Preserve the existing integration coverage and add focused tests for migration-sensitive behavior:
src/lib/mcp-schema.test.tsvalidates Effect schemas and both JSON Schema dialects.src/tools.test.tsuses an in-memory MCP client/server to test tool calls, structured output, elicitation outcomes, and modern-era behavior.- Assert
client.listTools()exposes the expected input/output JSON Schemas when schemas change. - Run
npm run lint,npm run format:check,npm run build, andnpm run test:ci. - Manually smoke-test
/healthand/mcpwhen the HTTP boundary changes.
Documentation and release
Update README.md, AGENTS.md, and the create-mcp-tool skill so new tools follow the Effect patterns. Explain that the template remains MCP-standard and uses the official MCP SDK.
This migration is breaking for existing template consumers. Use a conventional feat!: commit and let the release workflow calculate the major version; do not manually edit the package version solely for this migration.
Completion checklist
- Existing user changes were preserved.
- Zod, Pino, and direct configuration patterns were migrated.
- MCP registration still uses the official SDK and the existing HTTP boundary.
- Effect schema adapter and both JSON Schema targets are tested.
- Tool, elicitation, structured-output, and HTTP behavior are verified.
- README,
AGENTS.md, and tool-authoring guidance are consistent. - Required checks pass.
- The breaking change is recorded with
feat!:.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

