원클릭으로
원클릭으로
| type | skill |
| name | MCP Tool Design |
| description | Design MCP tools and gateway interfaces for the dotcontext server |
| skillSlug | api-design |
| phases | ["P","R"] |
| generated | "2026-03-18T00:00:00.000Z" |
| status | filled |
| scaffoldVersion | 2.0.0 |
Guidance for designing and extending MCP (Model Context Protocol) tools exposed by AIContextMCPServer in src/services/mcp/mcpServer.ts.
context://, file://, workflow://)src/services/mcp/gateway/The MCP server follows a consolidated gateway pattern to minimize cognitive load for AI agents:
AIContextMCPServer (mcpServer.ts)
registerGatewayTools()
-> explore (read, list, analyze, search, getStructure)
-> context (check, init, fill, fillSingle, listToFill, getMap, buildSemantic, scaffoldPlan)
-> sync (exportRules, exportDocs, exportAgents, exportContext, exportSkills, reverseSync, importDocs, importAgents, importSkills)
-> plan (link, getLinked, getDetails, getForPhase, updatePhase, recordDecision, updateStep, getStatus, syncMarkdown, commitPhase)
-> agent (discover, getInfo, orchestrate, getSequence, getDocs, getPhaseDocs, listTypes)
-> skill (list, getContent, getForPhase, scaffold, export, fill)
-> workflow-init / workflow-status / workflow-advance / workflow-manage
Each gateway dispatches to a handler in src/services/mcp/gateway/<name>.ts. The handler receives typed params and an options object with repoPath.
z.enum([...]) and extend the handler switch.workflow-* was split out for clarity).All MCP tool inputs use Zod v4 schemas (imported from zod). Follow the existing pattern:
inputSchema: {
action: z.enum(['existingAction', 'newAction'])
.describe('Action to perform'),
newParam: z.string().optional()
.describe('(newAction) What this param does'),
}
Key conventions:
.describe() with the action prefix in parentheses: (actionName)z.enum() for constrained values, z.array(z.string()) for lists.optional() since different actions use different params../../workflow (e.g., PREVC_ROLES, AGENT_TYPES)Add a new file in src/services/mcp/gateway/ or extend an existing one:
// src/services/mcp/gateway/myGateway.ts
import { createJsonResponse, createErrorResponse, type MCPToolResponse } from './response';
export type MyAction = 'doThing' | 'doOther';
export interface MyParams {
action: MyAction;
repoPath?: string;
// action-specific params
}
export interface MyOptions {
repoPath: string;
}
export async function handleMy(params: MyParams, options: MyOptions): Promise<MCPToolResponse> {
switch (params.action) {
case 'doThing':
return createJsonResponse({ success: true, message: 'Done' });
default:
return createErrorResponse(`Unknown action: ${params.action}`);
}
}
Use the three helpers from src/services/mcp/gateway/response.ts:
createJsonResponse(data) -- structured JSON for programmatic consumptioncreateErrorResponse(message) -- sets isError: truecreateTextResponse(text) -- plain text for human-readable outputAll responses conform to MCPToolResponse with content: [{ type: 'text', text }].
Register the tool in registerGatewayTools() using the wrap() helper for automatic action logging:
this.server.registerTool('my-tool', {
description: `Description with action list...`,
inputSchema: { /* Zod schemas */ }
}, wrap('my-tool', async (params): Promise<MCPToolResponse> => {
return handleMy(params as MyParams, { repoPath: this.getRepoPath() });
}));
Add your handler, types, and params to the re-export barrel in src/services/mcp/gatewayTools.ts and src/services/mcp/gateway/index.ts.
Write tests in src/services/mcp/ following the pattern in mcpServer.test.ts. Since MCP tools require stdio transport, test the handler functions directly:
import { handleMy } from './gateway/myGateway';
it('should handle doThing action', async () => {
const result = await handleMy(
{ action: 'doThing' },
{ repoPath: tempDir }
);
const payload = JSON.parse(result.content[0].text);
expect(payload.success).toBe(true);
});
getStatus, buildSemantic, exportRules).describe() annotations with action prefixMCPToolResponse via response helpersrepoPath resolution uses this.getRepoPath(params.repoPath) for cachingwrap() helperprocess.cwd() directly in handlers; always use the repoPath from options.optional() and validate inside the handlercreateErrorResponse() for consistent error shapeprocess.stderr.write() to avoid corrupting the MCP protocolSystematic bug investigation and root cause analysis
Review code quality, patterns, and best practices
Generate commit messages following conventional commits with scope detection
Generate and update technical documentation
Break down features into implementable tasks
Review pull requests against team standards and best practices