feat(ccw): migrate backend to TypeScript

- Convert 40 JS files to TypeScript (CLI, tools, core, MCP server)
- Add Zod for runtime parameter validation
- Add type definitions in src/types/
- Keep src/templates/ as JavaScript (dashboard frontend)
- Update bin entries to use dist/
- Add tsconfig.json with strict mode
- Add backward-compatible exports for tests
- All 39 tests passing

Breaking changes: None (backward compatible)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
catlog22
2025-12-13 10:43:15 +08:00
parent d4e59770d0
commit 25ac862f46
93 changed files with 5531 additions and 9302 deletions

View File

@@ -11,17 +11,18 @@ import {
ListToolsRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
import { getAllToolSchemas, executeTool } from '../tools/index.js';
import type { ToolSchema, ToolResult } from '../types/tool.js';
const SERVER_NAME = 'ccw-tools';
const SERVER_VERSION = '6.1.4';
// Default enabled tools (core set)
const DEFAULT_TOOLS = ['write_file', 'edit_file', 'codex_lens', 'smart_search'];
const DEFAULT_TOOLS: string[] = ['write_file', 'edit_file', 'codex_lens', 'smart_search'];
/**
* Get list of enabled tools from environment or defaults
*/
function getEnabledTools() {
function getEnabledTools(): string[] | null {
const envTools = process.env.CCW_ENABLED_TOOLS;
if (envTools) {
// Support "all" to enable all tools
@@ -36,15 +37,35 @@ function getEnabledTools() {
/**
* Filter tools based on enabled list
*/
function filterTools(tools, enabledList) {
function filterTools(tools: ToolSchema[], enabledList: string[] | null): ToolSchema[] {
if (!enabledList) return tools; // null = all tools
return tools.filter(tool => enabledList.includes(tool.name));
}
/**
* Format tool result for display
*/
function formatToolResult(result: unknown): string {
if (result === null || result === undefined) {
return 'Tool completed successfully (no output)';
}
if (typeof result === 'string') {
return result;
}
if (typeof result === 'object') {
// Pretty print JSON with indentation
return JSON.stringify(result, null, 2);
}
return String(result);
}
/**
* Create and configure the MCP server
*/
function createServer() {
function createServer(): Server {
const enabledTools = getEnabledTools();
const server = new Server(
@@ -63,7 +84,7 @@ function createServer() {
* Handler for tools/list - Returns enabled CCW tools
*/
server.setRequestHandler(ListToolsRequestSchema, async () => {
const allTools = getAllToolSchemas();
const allTools = getAllToolSchemas().filter((tool): tool is ToolSchema => tool !== null);
const tools = filterTools(allTools, enabledTools);
return { tools };
});
@@ -77,27 +98,28 @@ function createServer() {
// Check if tool is enabled
if (enabledTools && !enabledTools.includes(name)) {
return {
content: [{ type: 'text', text: `Tool "${name}" is not enabled` }],
content: [{ type: 'text' as const, text: `Tool "${name}" is not enabled` }],
isError: true,
};
}
try {
const result = await executeTool(name, args || {});
const result: ToolResult = await executeTool(name, args || {});
if (!result.success) {
return {
content: [{ type: 'text', text: `Error: ${result.error}` }],
content: [{ type: 'text' as const, text: `Error: ${result.error}` }],
isError: true,
};
}
return {
content: [{ type: 'text', text: formatToolResult(result.result) }],
content: [{ type: 'text' as const, text: formatToolResult(result.result) }],
};
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
return {
content: [{ type: 'text', text: `Tool execution failed: ${error.message}` }],
content: [{ type: 'text' as const, text: `Tool execution failed: ${errorMessage}` }],
isError: true,
};
}
@@ -106,32 +128,10 @@ function createServer() {
return server;
}
/**
* Format tool result for display
* @param {*} result - Tool execution result
* @returns {string} - Formatted result string
*/
function formatToolResult(result) {
if (result === null || result === undefined) {
return 'Tool completed successfully (no output)';
}
if (typeof result === 'string') {
return result;
}
if (typeof result === 'object') {
// Pretty print JSON with indentation
return JSON.stringify(result, null, 2);
}
return String(result);
}
/**
* Main server execution
*/
async function main() {
async function main(): Promise<void> {
const server = createServer();
const transport = new StdioServerTransport();
@@ -154,7 +154,8 @@ async function main() {
}
// Run server
main().catch((error) => {
console.error('Server error:', error);
main().catch((error: unknown) => {
const errorMessage = error instanceof Error ? error.message : String(error);
console.error('Server error:', errorMessage);
process.exit(1);
});