Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: CI

on:
push:
branches: [main]
pull_request:

jobs:
test:
name: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npx tsc --noEmit
- run: npm run build
4 changes: 3 additions & 1 deletion src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ function checkEnvironmentVariables() {

// Main function to run the MCP server
async function main() {
console.log('Starting WordPress MCP Server...');
// stderr only: the spawned server inherits this process's stdio, so stdout
// belongs to the MCP JSON-RPC channel and must stay clean.
console.error('Starting WordPress MCP Server...');

// Check for .env file in current directory
const envPath = path.join(process.cwd(), '.env');
Expand Down
47 changes: 22 additions & 25 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,17 @@ import { z } from 'zod';
import { zodToJsonSchema } from 'zod-to-json-schema';


// Create MCP server instance
// Create MCP server instance.
// capabilities.tools must be the capability flags object, NOT a map of tool
// definitions — server.tool() registration below handles tool advertisement.
// Stuffing tool objects here serialized their zod schemas into every
// initialize response (~65KB of internals per connection).
const server = new McpServer({
name: "wordpress",
version: "0.0.1"
}, {
capabilities: {
tools: allTools.reduce((acc, tool) => {
acc[tool.name] = tool;
return acc;
}, {} as Record<string, any>)
tools: {}
}
Comment on lines 22 to 24
});

Expand All @@ -40,22 +41,10 @@ for (const tool of allTools) {
};
};

// console.log(`Registering tool: ${tool.name}`);
// console.log(`Input schema: ${JSON.stringify(tool.inputSchema)}`);

// const zodSchema = z.any().optional();
// const jsonSchema = zodToJsonSchema(z.object(tool.inputSchema.properties as z.ZodRawShape));

// const schema = z.object(tool.inputSchema as z.ZodRawShape).catchall(z.unknown());

// The inputSchema is already in JSON Schema format with properties
// server.tool(tool.name, tool.inputSchema.shape, wrappedHandler);
// const zodSchema = z.any().optional();
// const jsonSchema = zodToJsonSchema(z.object(tool.inputSchema.properties as z.ZodRawShape));
// const parsedSchema = z.any().optional().parse(jsonSchema);

const zodSchema = z.object(tool.inputSchema.properties as z.ZodRawShape);
server.tool(tool.name, zodSchema.shape, wrappedHandler)
// Tool modules define inputSchema.properties as zod shapes (see CLAUDE.md);
// passing raw JSON Schema here collapses the published schema to {}.
const zodSchema = z.object(tool.inputSchema.properties as z.ZodRawShape);
server.tool(tool.name, tool.description ?? '', zodSchema.shape, wrappedHandler)
Comment on lines +46 to +47

}

Expand Down Expand Up @@ -100,12 +89,20 @@ process.on('SIGINT', () => {
process.exit(0);
});
process.on('uncaughtException', (error) => {
process.stderr.write(`[FATAL] Uncaught exception: ${error}\n`);
process.stderr.write(`[FATAL] Uncaught exception: ${error instanceof Error ? error.stack || error.message : error}\n`);
process.exit(1);
});
process.on('unhandledRejection', (error) => {
process.stderr.write(`[FATAL] Unhandled rejection: ${error}\n`);
process.exit(1);
// Do NOT exit on unhandled rejections: a stray promise rejection (e.g. a
// background axios call outside a handler's try/catch) is not worth killing
// the whole server over — that surfaces in clients as the server abruptly
// disconnecting. Log it loudly instead.
process.on('unhandledRejection', (reason) => {
process.stderr.write(`[ERROR] Unhandled rejection (server continuing): ${reason instanceof Error ? reason.stack || reason.message : reason}\n`);
});
// Always leave a trace of WHY the process is exiting, so client-side
// "transport closed unexpectedly" messages can be correlated with a cause.
process.on('exit', (code) => {
process.stderr.write(`[SHUTDOWN] Process exiting with code ${code}\n`);
Comment on lines +104 to +105
});

main().catch((error) => {
Expand Down
Loading