tiny-stdio-mcp-test-server 0.1.0 → 0.1.1

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/README.md ADDED
@@ -0,0 +1,46 @@
1
+ # tiny-stdio-mcp-test-server
2
+
3
+ A deterministic MCP server for testing your MCP client integration end-to-end — no real APIs, no flaky responses. Every call returns the same predictable output, so you can assert with confidence.
4
+
5
+ Built on [tiny-stdio-mcp-server](https://www.npmjs.com/package/tiny-stdio-mcp-server).
6
+
7
+ ## Quick start
8
+
9
+ ```sh
10
+ npx tiny-stdio-mcp-test-server serve encrypt
11
+ npx tiny-stdio-mcp-test-server serve word-of-the-day
12
+ ```
13
+
14
+ ## Configure with an MCP client
15
+
16
+ Add to your agent's MCP config (e.g. Claude Code's `~/.claude.json`):
17
+
18
+ ```json
19
+ {
20
+ "mcpServers": {
21
+ "test": {
22
+ "command": "npx",
23
+ "args": ["tiny-stdio-mcp-test-server", "serve", "word-of-the-day"]
24
+ }
25
+ }
26
+ }
27
+ ```
28
+
29
+ ## Available tools
30
+
31
+ | Tool | Description | Expected output |
32
+ |------|-------------|-----------------|
33
+ | `caesar_cipher_encrypt` | Caesar cipher (`text`, optional `shift`) | `"hello"` → `"khoor"` |
34
+ | `word_of_the_day` | Fixed word of the day (no params) | `"Bumfuzzle - to confuse or fluster someone"` |
35
+
36
+ ## What to assert
37
+
38
+ - **Tool discovery:** your client can list tools with correct names, descriptions, and JSON Schema
39
+ - **Deterministic output:** `caesar_cipher_encrypt({ text: "hello" })` always returns `"khoor"`
40
+ - **Optional parameters:** `caesar_cipher_encrypt({ text: "abc", shift: 1 })` returns `"bcd"`
41
+ - **Zero-arg tools:** `word_of_the_day({})` always returns `"Bumfuzzle - to confuse or fluster someone"`
42
+ - **Content format:** results are always `[{ type: "text", text: "..." }]`
43
+
44
+ ## License
45
+
46
+ MIT
package/dist/cli.js CHANGED
@@ -1,20 +1,33 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command } from "commander";
3
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
4
+ import { access } from "node:fs/promises";
5
+ import packageJson from "../package.json" with { type: "json" };
3
6
  import { createEncryptServer, createWordOfTheDayServer, } from "./index.js";
4
7
  const program = new Command();
5
8
  program
6
9
  .name("tiny-stdio-mcp-test-server")
7
10
  .description("Test MCP server with example tools for integration testing")
8
- .version("0.0.1");
11
+ .version(packageJson.version);
9
12
  program
10
13
  .command("serve")
11
14
  .description("Start an MCP server on stdin/stdout")
12
15
  .argument("<tool>", "Tool to serve (encrypt, word-of-the-day)")
13
16
  .action(async (tool) => {
14
- const servers = {
17
+ recordProcessStart();
18
+ const startupDelayMs = Number(process.env.TOOLCRAFT_TEST_STARTUP_DELAY_MS ?? "0");
19
+ if (startupDelayMs > 0) {
20
+ await new Promise((resolve) => setTimeout(resolve, startupDelayMs));
21
+ }
22
+ const startupGateFile = process.env.TOOLCRAFT_TEST_STARTUP_GATE_FILE;
23
+ if (startupGateFile !== undefined) {
24
+ await waitForFile(startupGateFile);
25
+ }
26
+ const servers = Object.create(null);
27
+ Object.assign(servers, {
15
28
  encrypt: () => createEncryptServer().listen(),
16
29
  "word-of-the-day": () => createWordOfTheDayServer().listen(),
17
- };
30
+ });
18
31
  const start = servers[tool];
19
32
  if (!start) {
20
33
  const available = Object.keys(servers).join(", ");
@@ -24,3 +37,27 @@ program
24
37
  await start();
25
38
  });
26
39
  program.parse();
40
+ function recordProcessStart() {
41
+ const countFile = process.env.TOOLCRAFT_TEST_SPAWN_COUNT_FILE;
42
+ if (countFile !== undefined) {
43
+ const previousCount = existsSync(countFile)
44
+ ? Number.parseInt(readFileSync(countFile, "utf8").trim() || "0", 10)
45
+ : 0;
46
+ writeFileSync(countFile, String(previousCount + 1));
47
+ }
48
+ const pidFile = process.env.TOOLCRAFT_TEST_WRAPPER_PID_FILE;
49
+ if (pidFile !== undefined) {
50
+ writeFileSync(pidFile, String(process.pid));
51
+ }
52
+ }
53
+ async function waitForFile(filePath) {
54
+ while (true) {
55
+ try {
56
+ await access(filePath);
57
+ return;
58
+ }
59
+ catch {
60
+ await new Promise((resolve) => setTimeout(resolve, 5));
61
+ }
62
+ }
63
+ }
package/dist/index.js CHANGED
@@ -1,4 +1,8 @@
1
1
  import { createServer, defineSchema } from "tiny-stdio-mcp-server";
2
+ import { appendFileSync } from "node:fs";
3
+ import packageJson from "../package.json" with { type: "json" };
4
+ const SERVER_NAME = "tiny-stdio-mcp-test-server";
5
+ const SERVER_VERSION = packageJson.version;
2
6
  const caesarCipherSchema = defineSchema({
3
7
  text: { type: "string", description: "The text to encrypt" },
4
8
  shift: {
@@ -8,14 +12,18 @@ const caesarCipherSchema = defineSchema({
8
12
  },
9
13
  });
10
14
  export function caesarEncrypt(text, shift) {
15
+ if (!Number.isInteger(shift)) {
16
+ throw new Error("Caesar cipher shift must be a finite integer");
17
+ }
18
+ const normalizedShift = ((shift % 26) + 26) % 26;
11
19
  return text
12
20
  .split("")
13
21
  .map((char) => {
14
22
  if (char >= "a" && char <= "z") {
15
- return String.fromCharCode(((char.charCodeAt(0) - 97 + shift) % 26) + 97);
23
+ return String.fromCharCode(((char.charCodeAt(0) - 97 + normalizedShift) % 26) + 97);
16
24
  }
17
25
  if (char >= "A" && char <= "Z") {
18
- return String.fromCharCode(((char.charCodeAt(0) - 65 + shift) % 26) + 65);
26
+ return String.fromCharCode(((char.charCodeAt(0) - 65 + normalizedShift) % 26) + 65);
19
27
  }
20
28
  return char;
21
29
  })
@@ -24,25 +32,32 @@ export function caesarEncrypt(text, shift) {
24
32
  const wordOfTheDaySchema = defineSchema({});
25
33
  export function createEncryptServer() {
26
34
  return createServer({
27
- name: "tiny-stdio-mcp-test-server",
28
- version: "0.0.1",
35
+ name: SERVER_NAME,
36
+ version: SERVER_VERSION,
29
37
  }).tool("caesar_cipher_encrypt", "Encrypts text using the Caesar cipher", caesarCipherSchema, ({ text, shift }) => {
38
+ recordToolCall("caesar_cipher_encrypt");
30
39
  const actualShift = shift ?? 3;
31
40
  return caesarEncrypt(text, actualShift);
32
41
  });
33
42
  }
43
+ function recordToolCall(name) {
44
+ const filePath = process.env.TOOLCRAFT_TEST_TOOL_CALL_FILE;
45
+ if (filePath !== undefined) {
46
+ appendFileSync(filePath, `${name}\n`);
47
+ }
48
+ }
34
49
  export function createWordOfTheDayServer() {
35
50
  return createServer({
36
- name: "tiny-stdio-mcp-test-server",
37
- version: "0.0.1",
51
+ name: SERVER_NAME,
52
+ version: SERVER_VERSION,
38
53
  }).tool("word_of_the_day", "Returns the word of the day", wordOfTheDaySchema, () => {
39
54
  return "Bumfuzzle - to confuse or fluster someone";
40
55
  });
41
56
  }
42
57
  export function createTestServer() {
43
58
  const server = createServer({
44
- name: "tiny-stdio-mcp-test-server",
45
- version: "0.0.1",
59
+ name: SERVER_NAME,
60
+ version: SERVER_VERSION,
46
61
  });
47
62
  server.tool("caesar_cipher_encrypt", "Encrypts text using the Caesar cipher", caesarCipherSchema, ({ text, shift }) => {
48
63
  const actualShift = shift ?? 3;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tiny-stdio-mcp-test-server",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Test MCP server with example tools for integration testing",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -15,14 +15,15 @@
15
15
  }
16
16
  },
17
17
  "scripts": {
18
- "build": "tsc",
18
+ "build": "node ../../scripts/guard-package-dist.mjs && tsc",
19
+ "prepack": "node ../../scripts/set-bin-executable.mjs",
19
20
  "prepublishOnly": "tsc"
20
21
  },
21
22
  "files": [
22
23
  "dist"
23
24
  ],
24
25
  "engines": {
25
- "node": ">=18"
26
+ "node": ">=20"
26
27
  },
27
28
  "repository": {
28
29
  "type": "git",
@@ -37,7 +38,7 @@
37
38
  "server"
38
39
  ],
39
40
  "dependencies": {
40
- "tiny-stdio-mcp-server": "*",
41
+ "tiny-stdio-mcp-server": "^0.1.0",
41
42
  "commander": "^14.0.3"
42
43
  },
43
44
  "devDependencies": {