mcp-server-diff 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/dependabot.yml +21 -0
- package/.github/workflows/ci.yml +51 -0
- package/.github/workflows/publish.yml +36 -0
- package/.github/workflows/release.yml +51 -0
- package/.prettierignore +3 -0
- package/.prettierrc +8 -0
- package/CONTRIBUTING.md +81 -0
- package/LICENSE +21 -0
- package/README.md +526 -0
- package/action.yml +250 -0
- package/dist/__tests__/fixtures/http-server.d.ts +7 -0
- package/dist/__tests__/fixtures/stdio-server.d.ts +7 -0
- package/dist/cli/__tests__/fixtures/http-server.d.ts +7 -0
- package/dist/cli/__tests__/fixtures/stdio-server.d.ts +7 -0
- package/dist/cli/cli.d.ts +7 -0
- package/dist/cli/diff.d.ts +44 -0
- package/dist/cli/git.d.ts +37 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.js +57182 -0
- package/dist/cli/licenses.txt +466 -0
- package/dist/cli/logger.d.ts +46 -0
- package/dist/cli/package.json +3 -0
- package/dist/cli/probe.d.ts +35 -0
- package/dist/cli/reporter.d.ts +20 -0
- package/dist/cli/runner.d.ts +30 -0
- package/dist/cli/types.d.ts +134 -0
- package/dist/cli.d.ts +7 -0
- package/dist/diff.d.ts +44 -0
- package/dist/git.d.ts +37 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +58032 -0
- package/dist/licenses.txt +466 -0
- package/dist/logger.d.ts +46 -0
- package/dist/package.json +3 -0
- package/dist/probe.d.ts +35 -0
- package/dist/reporter.d.ts +20 -0
- package/dist/runner.d.ts +30 -0
- package/dist/types.d.ts +134 -0
- package/eslint.config.mjs +47 -0
- package/jest.config.mjs +26 -0
- package/package.json +64 -0
- package/src/__tests__/fixtures/http-server.ts +103 -0
- package/src/__tests__/fixtures/stdio-server.ts +158 -0
- package/src/__tests__/integration.test.ts +306 -0
- package/src/__tests__/runner.test.ts +430 -0
- package/src/cli.ts +421 -0
- package/src/diff.ts +252 -0
- package/src/git.ts +262 -0
- package/src/index.ts +284 -0
- package/src/logger.ts +93 -0
- package/src/probe.ts +327 -0
- package/src/reporter.ts +214 -0
- package/src/runner.ts +902 -0
- package/src/types.ts +155 -0
- package/tsconfig.json +30 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for MCP Conformance Action
|
|
3
|
+
*/
|
|
4
|
+
export interface TestConfiguration {
|
|
5
|
+
name: string;
|
|
6
|
+
transport: "stdio" | "streamable-http";
|
|
7
|
+
start_command?: string;
|
|
8
|
+
args?: string;
|
|
9
|
+
server_url?: string;
|
|
10
|
+
headers?: Record<string, string>;
|
|
11
|
+
env_vars?: string;
|
|
12
|
+
custom_messages?: CustomMessage[];
|
|
13
|
+
/** Command to run before starting the MCP server for this config */
|
|
14
|
+
pre_test_command?: string;
|
|
15
|
+
/** Milliseconds to wait after pre_test_command before starting the server */
|
|
16
|
+
pre_test_wait_ms?: number;
|
|
17
|
+
/** Milliseconds to wait for HTTP server to start (when using start_command with HTTP transport) */
|
|
18
|
+
startup_wait_ms?: number;
|
|
19
|
+
/** Command to run after stopping the MCP server for this config (cleanup) */
|
|
20
|
+
post_test_command?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface CustomMessage {
|
|
23
|
+
id: number;
|
|
24
|
+
name: string;
|
|
25
|
+
message: Record<string, unknown>;
|
|
26
|
+
}
|
|
27
|
+
export interface ActionInputs {
|
|
28
|
+
setupNode: boolean;
|
|
29
|
+
nodeVersion: string;
|
|
30
|
+
setupPython: boolean;
|
|
31
|
+
pythonVersion: string;
|
|
32
|
+
setupGo: boolean;
|
|
33
|
+
goVersion: string;
|
|
34
|
+
setupRust: boolean;
|
|
35
|
+
rustToolchain: string;
|
|
36
|
+
setupDotnet: boolean;
|
|
37
|
+
dotnetVersion: string;
|
|
38
|
+
installCommand: string;
|
|
39
|
+
buildCommand: string;
|
|
40
|
+
startCommand: string;
|
|
41
|
+
transport: "stdio" | "streamable-http";
|
|
42
|
+
serverUrl: string;
|
|
43
|
+
headers: Record<string, string>;
|
|
44
|
+
configurations: TestConfiguration[];
|
|
45
|
+
customMessages: CustomMessage[];
|
|
46
|
+
compareRef: string;
|
|
47
|
+
failOnError: boolean;
|
|
48
|
+
failOnDiff: boolean;
|
|
49
|
+
envVars: string;
|
|
50
|
+
serverTimeout: number;
|
|
51
|
+
httpStartCommand: string;
|
|
52
|
+
httpStartupWaitMs: number;
|
|
53
|
+
}
|
|
54
|
+
export interface ProbeResult {
|
|
55
|
+
initialize: InitializeInfo | null;
|
|
56
|
+
tools: ToolsResult | null;
|
|
57
|
+
prompts: PromptsResult | null;
|
|
58
|
+
resources: ResourcesResult | null;
|
|
59
|
+
resourceTemplates: ResourceTemplatesResult | null;
|
|
60
|
+
customResponses: Map<string, unknown>;
|
|
61
|
+
error?: string;
|
|
62
|
+
}
|
|
63
|
+
export interface InitializeInfo {
|
|
64
|
+
serverInfo?: {
|
|
65
|
+
name: string;
|
|
66
|
+
version: string;
|
|
67
|
+
};
|
|
68
|
+
capabilities?: Record<string, unknown>;
|
|
69
|
+
}
|
|
70
|
+
export interface ToolsResult {
|
|
71
|
+
tools: Array<{
|
|
72
|
+
name: string;
|
|
73
|
+
description?: string;
|
|
74
|
+
inputSchema?: Record<string, unknown>;
|
|
75
|
+
}>;
|
|
76
|
+
}
|
|
77
|
+
export interface PromptsResult {
|
|
78
|
+
prompts: Array<{
|
|
79
|
+
name: string;
|
|
80
|
+
description?: string;
|
|
81
|
+
arguments?: Array<{
|
|
82
|
+
name: string;
|
|
83
|
+
description?: string;
|
|
84
|
+
required?: boolean;
|
|
85
|
+
}>;
|
|
86
|
+
}>;
|
|
87
|
+
}
|
|
88
|
+
export interface ResourcesResult {
|
|
89
|
+
resources: Array<{
|
|
90
|
+
uri: string;
|
|
91
|
+
name: string;
|
|
92
|
+
description?: string;
|
|
93
|
+
mimeType?: string;
|
|
94
|
+
}>;
|
|
95
|
+
}
|
|
96
|
+
export interface ResourceTemplatesResult {
|
|
97
|
+
resourceTemplates: Array<{
|
|
98
|
+
uriTemplate: string;
|
|
99
|
+
name: string;
|
|
100
|
+
description?: string;
|
|
101
|
+
mimeType?: string;
|
|
102
|
+
}>;
|
|
103
|
+
}
|
|
104
|
+
/** Counts of MCP primitives discovered */
|
|
105
|
+
export interface PrimitiveCounts {
|
|
106
|
+
tools: number;
|
|
107
|
+
prompts: number;
|
|
108
|
+
resources: number;
|
|
109
|
+
resourceTemplates: number;
|
|
110
|
+
}
|
|
111
|
+
export interface TestResult {
|
|
112
|
+
configName: string;
|
|
113
|
+
transport: string;
|
|
114
|
+
branchTime: number;
|
|
115
|
+
baseTime: number;
|
|
116
|
+
hasDifferences: boolean;
|
|
117
|
+
diffs: Map<string, string>;
|
|
118
|
+
/** Primitive counts from current branch */
|
|
119
|
+
branchCounts?: PrimitiveCounts;
|
|
120
|
+
/** Primitive counts from base ref */
|
|
121
|
+
baseCounts?: PrimitiveCounts;
|
|
122
|
+
/** Error message if probing failed */
|
|
123
|
+
error?: string;
|
|
124
|
+
}
|
|
125
|
+
export interface ConformanceReport {
|
|
126
|
+
generatedAt: string;
|
|
127
|
+
currentBranch: string;
|
|
128
|
+
compareRef: string;
|
|
129
|
+
results: TestResult[];
|
|
130
|
+
totalBranchTime: number;
|
|
131
|
+
totalBaseTime: number;
|
|
132
|
+
passedCount: number;
|
|
133
|
+
diffCount: number;
|
|
134
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import eslint from "@eslint/js";
|
|
2
|
+
import tseslint from "typescript-eslint";
|
|
3
|
+
import prettier from "eslint-config-prettier";
|
|
4
|
+
|
|
5
|
+
export default tseslint.config(
|
|
6
|
+
eslint.configs.recommended,
|
|
7
|
+
...tseslint.configs.recommended,
|
|
8
|
+
prettier,
|
|
9
|
+
{
|
|
10
|
+
languageOptions: {
|
|
11
|
+
parserOptions: {
|
|
12
|
+
project: "./tsconfig.json",
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
rules: {
|
|
16
|
+
"@typescript-eslint/no-unused-vars": [
|
|
17
|
+
"error",
|
|
18
|
+
{ argsIgnorePattern: "^_", varsIgnorePattern: "^_" },
|
|
19
|
+
],
|
|
20
|
+
"@typescript-eslint/explicit-function-return-type": "off",
|
|
21
|
+
"@typescript-eslint/no-explicit-any": "warn",
|
|
22
|
+
"no-console": "warn",
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
// Test files don't need the project reference and need Jest globals
|
|
27
|
+
files: ["**/__tests__/**/*.ts", "**/*.test.ts"],
|
|
28
|
+
languageOptions: {
|
|
29
|
+
parserOptions: {
|
|
30
|
+
project: null,
|
|
31
|
+
},
|
|
32
|
+
globals: {
|
|
33
|
+
describe: "readonly",
|
|
34
|
+
it: "readonly",
|
|
35
|
+
expect: "readonly",
|
|
36
|
+
beforeEach: "readonly",
|
|
37
|
+
afterEach: "readonly",
|
|
38
|
+
beforeAll: "readonly",
|
|
39
|
+
afterAll: "readonly",
|
|
40
|
+
jest: "readonly",
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
ignores: ["dist/**", "node_modules/**", "*.js", "*.mjs"],
|
|
46
|
+
}
|
|
47
|
+
);
|
package/jest.config.mjs
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** @type {import('jest').Config} */
|
|
2
|
+
export default {
|
|
3
|
+
preset: "ts-jest/presets/default-esm",
|
|
4
|
+
testEnvironment: "node",
|
|
5
|
+
extensionsToTreatAsEsm: [".ts"],
|
|
6
|
+
// Force exit after tests complete - needed because npx tsx leaves handles open
|
|
7
|
+
forceExit: true,
|
|
8
|
+
moduleNameMapper: {
|
|
9
|
+
"^(\\.{1,2}/.*)\\.js$": "$1",
|
|
10
|
+
},
|
|
11
|
+
transform: {
|
|
12
|
+
"^.+\\.tsx?$": [
|
|
13
|
+
"ts-jest",
|
|
14
|
+
{
|
|
15
|
+
useESM: true,
|
|
16
|
+
diagnostics: {
|
|
17
|
+
ignoreCodes: [151002],
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
},
|
|
22
|
+
testMatch: ["**/__tests__/**/*.test.ts", "**/*.test.ts"],
|
|
23
|
+
collectCoverageFrom: ["src/**/*.ts", "!src/**/*.d.ts"],
|
|
24
|
+
coverageDirectory: "coverage",
|
|
25
|
+
coverageReporters: ["text", "lcov", "html"],
|
|
26
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mcp-server-diff",
|
|
3
|
+
"version": "2.1.0",
|
|
4
|
+
"description": "Diff MCP server public interfaces - CLI tool and GitHub Action",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"mcp-server-diff": "dist/cli/index.js"
|
|
8
|
+
},
|
|
9
|
+
"type": "module",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "npm run build:action && npm run build:cli",
|
|
12
|
+
"build:action": "ncc build src/index.ts -o dist --license licenses.txt --no-source-map-register",
|
|
13
|
+
"build:cli": "ncc build src/cli.ts -o dist/cli --license licenses.txt --no-source-map-register && node -e \"const fs=require('fs'); const p='dist/cli/index.js'; fs.writeFileSync(p, '#!/usr/bin/env node\\n' + fs.readFileSync(p))\"",
|
|
14
|
+
"lint": "eslint src/",
|
|
15
|
+
"lint:fix": "eslint src/ --fix",
|
|
16
|
+
"format": "prettier --write src/",
|
|
17
|
+
"format:check": "prettier --check src/",
|
|
18
|
+
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
|
|
19
|
+
"test:coverage": "npm test -- --coverage",
|
|
20
|
+
"typecheck": "tsc --noEmit",
|
|
21
|
+
"check": "npm run typecheck && npm run lint && npm run format:check && npm test",
|
|
22
|
+
"prepublishOnly": "npm run check && npm run build"
|
|
23
|
+
},
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "git+https://github.com/SamMorrowDrums/mcp-server-diff.git"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"cli",
|
|
30
|
+
"github-action",
|
|
31
|
+
"mcp",
|
|
32
|
+
"model-context-protocol",
|
|
33
|
+
"diff",
|
|
34
|
+
"api-changes"
|
|
35
|
+
],
|
|
36
|
+
"author": "Sam Morrow",
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@actions/core": "^1.11.1",
|
|
40
|
+
"@actions/exec": "^1.1.1",
|
|
41
|
+
"@actions/io": "^1.1.3",
|
|
42
|
+
"@modelcontextprotocol/sdk": "^1.13.2",
|
|
43
|
+
"diff": "^8.0.3",
|
|
44
|
+
"undici": "^6.23.0",
|
|
45
|
+
"zod": "^3.24.5"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@eslint/js": "^9.17.0",
|
|
49
|
+
"@types/diff": "^7.0.2",
|
|
50
|
+
"@types/jest": "^29.5.14",
|
|
51
|
+
"@types/node": "^22.0.0",
|
|
52
|
+
"@vercel/ncc": "^0.38.3",
|
|
53
|
+
"eslint": "^9.17.0",
|
|
54
|
+
"eslint-config-prettier": "^10.0.1",
|
|
55
|
+
"jest": "^29.7.0",
|
|
56
|
+
"prettier": "^3.4.2",
|
|
57
|
+
"ts-jest": "^29.2.5",
|
|
58
|
+
"typescript": "^5.6.0",
|
|
59
|
+
"typescript-eslint": "^8.19.1"
|
|
60
|
+
},
|
|
61
|
+
"overrides": {
|
|
62
|
+
"undici": "^6.23.0"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
|
+
/**
|
|
3
|
+
* Minimal MCP Server for integration testing (streamable-http transport)
|
|
4
|
+
*
|
|
5
|
+
* Run with: npx tsx http-server.ts <port>
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
9
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
10
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
11
|
+
import * as http from "http";
|
|
12
|
+
|
|
13
|
+
const server = new Server(
|
|
14
|
+
{
|
|
15
|
+
name: "test-http-server",
|
|
16
|
+
version: "1.0.0",
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
capabilities: {
|
|
20
|
+
tools: {},
|
|
21
|
+
},
|
|
22
|
+
}
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
// Define tools
|
|
26
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
27
|
+
return {
|
|
28
|
+
tools: [
|
|
29
|
+
{
|
|
30
|
+
name: "echo",
|
|
31
|
+
description: "Echoes back the input",
|
|
32
|
+
inputSchema: {
|
|
33
|
+
type: "object" as const,
|
|
34
|
+
properties: {
|
|
35
|
+
message: { type: "string", description: "Message to echo" },
|
|
36
|
+
},
|
|
37
|
+
required: ["message"],
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
};
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
45
|
+
const { name, arguments: args } = request.params;
|
|
46
|
+
|
|
47
|
+
if (name === "echo") {
|
|
48
|
+
return {
|
|
49
|
+
content: [{ type: "text", text: (args as { message: string }).message }],
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Create HTTP server with streamable transport
|
|
57
|
+
async function main() {
|
|
58
|
+
const httpServer = http.createServer();
|
|
59
|
+
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
|
|
60
|
+
|
|
61
|
+
httpServer.on("request", async (req, res) => {
|
|
62
|
+
// Simple CORS support
|
|
63
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
64
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
65
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
66
|
+
|
|
67
|
+
if (req.method === "OPTIONS") {
|
|
68
|
+
res.writeHead(200);
|
|
69
|
+
res.end();
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (req.url === "/mcp" || req.url?.startsWith("/mcp?")) {
|
|
74
|
+
await transport.handleRequest(req, res);
|
|
75
|
+
} else {
|
|
76
|
+
res.writeHead(404);
|
|
77
|
+
res.end("Not found");
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
await server.connect(transport);
|
|
82
|
+
|
|
83
|
+
// Use port 0 to let the OS assign a free port, unless specific port given
|
|
84
|
+
const requestedPort = parseInt(process.argv[2] || "0", 10);
|
|
85
|
+
httpServer.listen(requestedPort, () => {
|
|
86
|
+
const addr = httpServer.address();
|
|
87
|
+
const actualPort = typeof addr === "object" && addr ? addr.port : requestedPort;
|
|
88
|
+
// Output format that tests parse: "listening on port XXXXX"
|
|
89
|
+
console.log(`Test HTTP MCP server listening on port ${actualPort}`);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Handle shutdown
|
|
93
|
+
process.on("SIGTERM", () => {
|
|
94
|
+
httpServer.close();
|
|
95
|
+
process.exit(0);
|
|
96
|
+
});
|
|
97
|
+
process.on("SIGINT", () => {
|
|
98
|
+
httpServer.close();
|
|
99
|
+
process.exit(0);
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
|
+
/**
|
|
3
|
+
* Minimal MCP Server for integration testing (stdio transport)
|
|
4
|
+
*
|
|
5
|
+
* This server exposes tools, prompts, and resources for testing the probe functionality.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
9
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
10
|
+
import {
|
|
11
|
+
CallToolRequestSchema,
|
|
12
|
+
GetPromptRequestSchema,
|
|
13
|
+
ListPromptsRequestSchema,
|
|
14
|
+
ListResourcesRequestSchema,
|
|
15
|
+
ListToolsRequestSchema,
|
|
16
|
+
ReadResourceRequestSchema,
|
|
17
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
18
|
+
|
|
19
|
+
const server = new Server(
|
|
20
|
+
{
|
|
21
|
+
name: "test-stdio-server",
|
|
22
|
+
version: "1.0.0",
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
capabilities: {
|
|
26
|
+
tools: {},
|
|
27
|
+
prompts: {},
|
|
28
|
+
resources: {},
|
|
29
|
+
},
|
|
30
|
+
}
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
// Define tools
|
|
34
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
35
|
+
return {
|
|
36
|
+
tools: [
|
|
37
|
+
{
|
|
38
|
+
name: "greet",
|
|
39
|
+
description: "Greets a person by name",
|
|
40
|
+
inputSchema: {
|
|
41
|
+
type: "object" as const,
|
|
42
|
+
properties: {
|
|
43
|
+
name: { type: "string", description: "Name to greet" },
|
|
44
|
+
},
|
|
45
|
+
required: ["name"],
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: "add",
|
|
50
|
+
description: "Adds two numbers",
|
|
51
|
+
inputSchema: {
|
|
52
|
+
type: "object" as const,
|
|
53
|
+
properties: {
|
|
54
|
+
a: { type: "number", description: "First number" },
|
|
55
|
+
b: { type: "number", description: "Second number" },
|
|
56
|
+
},
|
|
57
|
+
required: ["a", "b"],
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
};
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
65
|
+
const { name, arguments: args } = request.params;
|
|
66
|
+
|
|
67
|
+
if (name === "greet") {
|
|
68
|
+
return {
|
|
69
|
+
content: [{ type: "text", text: `Hello, ${(args as { name: string }).name}!` }],
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (name === "add") {
|
|
74
|
+
const { a, b } = args as { a: number; b: number };
|
|
75
|
+
// Return as embedded JSON to test normalization
|
|
76
|
+
return {
|
|
77
|
+
content: [
|
|
78
|
+
{
|
|
79
|
+
type: "text",
|
|
80
|
+
text: JSON.stringify({ result: a + b, operation: "add", inputs: { b, a } }),
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Define prompts
|
|
90
|
+
server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
91
|
+
return {
|
|
92
|
+
prompts: [
|
|
93
|
+
{
|
|
94
|
+
name: "code-review",
|
|
95
|
+
description: "Review code for issues",
|
|
96
|
+
arguments: [
|
|
97
|
+
{ name: "code", description: "The code to review", required: true },
|
|
98
|
+
{ name: "language", description: "Programming language", required: false },
|
|
99
|
+
],
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
};
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
106
|
+
if (request.params.name === "code-review") {
|
|
107
|
+
const args = request.params.arguments || {};
|
|
108
|
+
return {
|
|
109
|
+
messages: [
|
|
110
|
+
{
|
|
111
|
+
role: "user" as const,
|
|
112
|
+
content: {
|
|
113
|
+
type: "text" as const,
|
|
114
|
+
text: `Please review this ${args.language || "code"}:\n\n${args.code}`,
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
throw new Error(`Unknown prompt: ${request.params.name}`);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Define resources
|
|
124
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
125
|
+
return {
|
|
126
|
+
resources: [
|
|
127
|
+
{
|
|
128
|
+
uri: "test://readme",
|
|
129
|
+
name: "README",
|
|
130
|
+
description: "Project readme file",
|
|
131
|
+
mimeType: "text/plain",
|
|
132
|
+
},
|
|
133
|
+
],
|
|
134
|
+
};
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
138
|
+
if (request.params.uri === "test://readme") {
|
|
139
|
+
return {
|
|
140
|
+
contents: [
|
|
141
|
+
{
|
|
142
|
+
uri: "test://readme",
|
|
143
|
+
mimeType: "text/plain",
|
|
144
|
+
text: "# Test Server\n\nThis is a test MCP server.",
|
|
145
|
+
},
|
|
146
|
+
],
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
throw new Error(`Unknown resource: ${request.params.uri}`);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// Start the server
|
|
153
|
+
async function main() {
|
|
154
|
+
const transport = new StdioServerTransport();
|
|
155
|
+
await server.connect(transport);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
main().catch(console.error);
|