@stabgan/steelmind-mcp 2.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 stabgan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,189 @@
1
+ # Steelmind MCP — Structured Thinking & Verification for AI Agents
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@stabgan/steelmind-mcp)](https://www.npmjs.com/package/@stabgan/steelmind-mcp)
4
+ [![Docker](https://img.shields.io/docker/v/stabgan/steelmind-mcp?label=docker)](https://hub.docker.com/r/stabgan/steelmind-mcp)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
6
+
7
+ **The research-grounded reasoning MCP server for AI agents.** Combines step-by-step sequential thinking with steel-manning verification — backed by 43+ cognitive science and AI research papers.
8
+
9
+ Steelmind gives your AI agent two tools:
10
+
11
+ - **`think`** — Record structured reasoning steps with sequential decomposition. Embeds Socratic self-questioning and Polya's problem-solving method.
12
+ - **`verify`** — Challenge conclusions with steel-manning before committing. Embeds dialectical evaluation from MetaCrit and SIEV research.
13
+
14
+ The code is minimal. The descriptions do the heavy lifting — tool descriptions account for ~80% of reasoning improvement per [Anthropic τ-bench research](https://www.anthropic.com/engineering/claude-think-tool).
15
+
16
+ ## Why Steelmind?
17
+
18
+ | Feature | Think MCP | Sequential Thinking | **Steelmind** |
19
+ | ------------------------------ | --------- | ------------------- | ------------- |
20
+ | Step tracking | ✗ | ✓ | ✓ |
21
+ | Adjustable step count | ✗ | ✓ | ✓ |
22
+ | Cognitive mode separation | ✗ | ✗ | ✓ |
23
+ | Steel-manning verification | ✗ | ✗ | ✓ |
24
+ | Socratic self-questioning | ✗ | ✗ | ✓ |
25
+ | Research-grounded descriptions | ✗ | ✗ | ✓ |
26
+ | Verify nudge on completion | ✗ | ✗ | ✓ |
27
+ | Tool count | 1 | 1 | 2 |
28
+
29
+ **Key research insight:** MetaCrit (arxiv 2507.15015) proved that separating reasoning generation from reasoning evaluation prevents self-bias and improves accuracy by up to 76%. Sequential-thinking uses one tool for both. Steelmind separates them.
30
+
31
+ ## Quick Start
32
+
33
+ ### npx (no install)
34
+
35
+ ```json
36
+ {
37
+ "mcpServers": {
38
+ "steelmind": {
39
+ "command": "npx",
40
+ "args": ["-y", "@stabgan/steelmind-mcp"]
41
+ }
42
+ }
43
+ }
44
+ ```
45
+
46
+ ### Docker
47
+
48
+ ```json
49
+ {
50
+ "mcpServers": {
51
+ "steelmind": {
52
+ "command": "docker",
53
+ "args": ["run", "--rm", "-i", "stabgan/steelmind-mcp"]
54
+ }
55
+ }
56
+ }
57
+ ```
58
+
59
+ ### npm global install
60
+
61
+ ```bash
62
+ npm install -g @stabgan/steelmind-mcp
63
+ ```
64
+
65
+ ```json
66
+ {
67
+ "mcpServers": {
68
+ "steelmind": {
69
+ "command": "steelmind-mcp"
70
+ }
71
+ }
72
+ }
73
+ ```
74
+
75
+ ## How It Works
76
+
77
+ ### The `think` tool
78
+
79
+ Records a structured reasoning step with sequential tracking.
80
+
81
+ **Input:**
82
+
83
+ ```json
84
+ {
85
+ "thought": "What are the dependencies? Need to check imports before refactoring.",
86
+ "thoughtNumber": 1,
87
+ "totalThoughts": 3,
88
+ "nextThoughtNeeded": true
89
+ }
90
+ ```
91
+
92
+ **Output (mid-sequence):**
93
+
94
+ ```
95
+ [Thinking 1/3]
96
+
97
+ What are the dependencies? Need to check imports before refactoring.
98
+ ```
99
+
100
+ **Output (final step — includes verify nudge):**
101
+
102
+ ```
103
+ [Thinking 3/3]
104
+
105
+ My conclusion: use the adapter pattern for backward compatibility.
106
+
107
+ ---
108
+ Thinking complete. Before acting on this conclusion, use the verify tool to challenge it.
109
+ ```
110
+
111
+ The verify nudge appears in the tool result (not just the description), making it far more likely the model will actually call `verify`. Tool results get different attention treatment than descriptions — they're processed as fresh context.
112
+
113
+ ### The `verify` tool
114
+
115
+ Challenges your reasoning with steel-manning before you commit.
116
+
117
+ **Input:**
118
+
119
+ ```json
120
+ {
121
+ "concern": "The adapter pattern adds complexity. Is the simpler approach actually better?"
122
+ }
123
+ ```
124
+
125
+ **Output:**
126
+
127
+ ```
128
+ The adapter pattern adds complexity. Is the simpler approach actually better?
129
+ ```
130
+
131
+ Pure identity function — returns your concern unchanged. The value is in the description, which prompts: _"Steel-man the opposition: What is the strongest argument that your conclusion is wrong?"_
132
+
133
+ ### The workflow
134
+
135
+ ```
136
+ think(step 1/3) → think(step 2/3) → think(step 3/3) → [verify nudge] → verify → act
137
+
138
+ adjust totalThoughts if needed
139
+ ```
140
+
141
+ ## Research Foundation
142
+
143
+ Steelmind's design is grounded in 43+ research papers. Key findings:
144
+
145
+ | Paper | Finding | How Steelmind Uses It |
146
+ | -------------------------------------------- | -------------------------------------------------------- | -------------------------------------------------------- |
147
+ | **MetaCrit** (arxiv 2507.15015) | Separating generation from evaluation prevents self-bias | Two separate tools: think (generate) + verify (evaluate) |
148
+ | **Anthropic τ-bench** | Optimized tool descriptions yield 54% improvement | Descriptions are the primary scaffold, not code |
149
+ | **Think2** (arxiv 2602.18806) | Structured metacognition yields 3x self-correction | Sequential step tracking + Socratic questioning |
150
+ | **SIEV** (ICML) | Models lose 40+ points under dialectical evaluation | Steel-manning prompt in verify description |
151
+ | **Scaling TTC** (arxiv 2408.03314) | Difficulty-adaptive compute improves efficiency 4x | Adjustable totalThoughts |
152
+ | **EasyTool** (NAACL 2025) | Concise descriptions outperform verbose ones | ~100 word descriptions |
153
+ | **ToolACE** | "When NOT to use" improves irrelevance detection 6→84% | Negative guidance in both descriptions |
154
+ | **Cognitive Foundations** (arxiv 2511.16660) | External scaffolding improves performance up to 72% | Research-grounded cognitive frameworks |
155
+
156
+ ## Compatible Clients
157
+
158
+ Works with any MCP-compatible client:
159
+
160
+ - Claude Desktop / Claude Code
161
+ - Cursor
162
+ - Windsurf
163
+ - Kiro
164
+ - Cline
165
+ - Any client supporting MCP stdio transport
166
+
167
+ ## Compatible Models
168
+
169
+ Designed for frontier models but works across families:
170
+
171
+ - Claude (Opus, Sonnet) — native MCP
172
+ - GPT-5 / GPT-4o / o-series — via MCP adapters
173
+ - Gemini — via MCP adapters
174
+ - DeepSeek — via MCP adapters
175
+
176
+ ## Development
177
+
178
+ ```bash
179
+ npm install # Install dependencies
180
+ npm run build # Compile TypeScript
181
+ npm test # Run 90 tests
182
+ npm run lint # ESLint
183
+ npm run format # Prettier
184
+ npm start # Run the server
185
+ ```
186
+
187
+ ## License
188
+
189
+ MIT
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,98 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { THINK_DESCRIPTION, VERIFY_DESCRIPTION, SYSTEM_PROMPT } from '../descriptions.js';
3
+ function wordCount(text) {
4
+ return text.split(/\s+/).filter(Boolean).length;
5
+ }
6
+ describe('THINK_DESCRIPTION', () => {
7
+ it('is between 80-130 words', () => {
8
+ const count = wordCount(THINK_DESCRIPTION);
9
+ expect(count).toBeGreaterThanOrEqual(80);
10
+ expect(count).toBeLessThanOrEqual(130);
11
+ });
12
+ it('leads with purpose (primacy effect)', () => {
13
+ expect(THINK_DESCRIPTION).toMatch(/^Use this tool to/);
14
+ });
15
+ it('includes "when NOT to use" guidance (ToolACE)', () => {
16
+ expect(THINK_DESCRIPTION).toMatch(/Do NOT use/i);
17
+ });
18
+ it('embeds Socratic self-questioning', () => {
19
+ expect(THINK_DESCRIPTION).toMatch(/What am I assuming/);
20
+ expect(THINK_DESCRIPTION).toMatch(/What evidence supports/);
21
+ });
22
+ it('does NOT instruct to "think harder" (DC-4)', () => {
23
+ expect(THINK_DESCRIPTION.toLowerCase()).not.toMatch(/think harder/);
24
+ });
25
+ it('frames as recording, not commanding', () => {
26
+ expect(THINK_DESCRIPTION).toMatch(/record/i);
27
+ });
28
+ it('mentions no state change', () => {
29
+ expect(THINK_DESCRIPTION).toMatch(/not.*change any state/i);
30
+ });
31
+ it('references adjustable totalThoughts', () => {
32
+ expect(THINK_DESCRIPTION).toMatch(/totalThoughts/);
33
+ });
34
+ it('cross-references verify tool', () => {
35
+ expect(THINK_DESCRIPTION).toMatch(/verify tool/i);
36
+ });
37
+ it('mentions nextThoughtNeeded transition', () => {
38
+ expect(THINK_DESCRIPTION).toMatch(/nextThoughtNeeded/);
39
+ });
40
+ });
41
+ describe('VERIFY_DESCRIPTION', () => {
42
+ it('is between 80-130 words', () => {
43
+ const count = wordCount(VERIFY_DESCRIPTION);
44
+ expect(count).toBeGreaterThanOrEqual(80);
45
+ expect(count).toBeLessThanOrEqual(130);
46
+ });
47
+ it('leads with purpose (primacy effect)', () => {
48
+ expect(VERIFY_DESCRIPTION).toMatch(/^Use this tool to/);
49
+ });
50
+ it('includes "when NOT to use" guidance (ToolACE)', () => {
51
+ expect(VERIFY_DESCRIPTION).toMatch(/Do NOT use/i);
52
+ });
53
+ it('embeds steel-manning prompt (SIEV, MetaCrit)', () => {
54
+ expect(VERIFY_DESCRIPTION).toMatch(/steel-man/i);
55
+ expect(VERIFY_DESCRIPTION).toMatch(/strongest argument/i);
56
+ });
57
+ it('does NOT instruct to "think harder" (DC-4)', () => {
58
+ expect(VERIFY_DESCRIPTION.toLowerCase()).not.toMatch(/think harder/);
59
+ });
60
+ it('mentions no state change', () => {
61
+ expect(VERIFY_DESCRIPTION).toMatch(/not.*change any state/i);
62
+ });
63
+ it('cross-references think tool', () => {
64
+ expect(VERIFY_DESCRIPTION).toMatch(/think tool/i);
65
+ });
66
+ });
67
+ describe('SYSTEM_PROMPT', () => {
68
+ it('is under 500 tokens (~375 words)', () => {
69
+ expect(wordCount(SYSTEM_PROMPT)).toBeLessThanOrEqual(375);
70
+ });
71
+ it('contains workflow guidance for both tools', () => {
72
+ expect(SYSTEM_PROMPT).toMatch(/think tool/i);
73
+ expect(SYSTEM_PROMPT).toMatch(/verify tool/i);
74
+ });
75
+ it('contains think, verify, and bad examples', () => {
76
+ expect(SYSTEM_PROMPT).toMatch(/think_example/);
77
+ expect(SYSTEM_PROMPT).toMatch(/verify_example/);
78
+ expect(SYSTEM_PROMPT).toMatch(/bad_example/);
79
+ });
80
+ it('bad example explains WHY it is bad', () => {
81
+ expect(SYSTEM_PROMPT).toMatch(/adds tokens without insight/i);
82
+ });
83
+ it('mentions totalThoughts adjustment', () => {
84
+ expect(SYSTEM_PROMPT).toMatch(/totalThoughts/i);
85
+ });
86
+ it('mentions nextThoughtNeeded transition to verify', () => {
87
+ expect(SYSTEM_PROMPT).toMatch(/nextThoughtNeeded.*false/i);
88
+ });
89
+ });
90
+ describe('description separation (DC-3)', () => {
91
+ it('think and verify descriptions are different strings', () => {
92
+ expect(THINK_DESCRIPTION).not.toBe(VERIFY_DESCRIPTION);
93
+ });
94
+ it('think uses "thought" terminology, verify uses "concern"', () => {
95
+ expect(THINK_DESCRIPTION).toMatch(/thought/i);
96
+ expect(VERIFY_DESCRIPTION).toMatch(/concern|self-assessment/i);
97
+ });
98
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,200 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
3
+ import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js';
4
+ import { createServer } from '../index.js';
5
+ import { SYSTEM_PROMPT } from '../descriptions.js';
6
+ describe('Output Quality', () => {
7
+ let client;
8
+ let cleanup;
9
+ async function connect() {
10
+ const server = createServer();
11
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
12
+ await server.connect(serverTransport);
13
+ client = new Client({ name: 'test-client', version: '1.0.0' });
14
+ await client.connect(clientTransport);
15
+ cleanup = async () => {
16
+ await client.close();
17
+ await server.close();
18
+ };
19
+ }
20
+ beforeEach(async () => {
21
+ await connect();
22
+ });
23
+ afterEach(async () => {
24
+ await cleanup();
25
+ });
26
+ function extractText(result) {
27
+ return result.content[0].text;
28
+ }
29
+ function thinkArgs(thought, num = 1, total = 1, next = true) {
30
+ return { thought, thoughtNumber: num, totalThoughts: total, nextThoughtNeeded: next };
31
+ }
32
+ // ─── 1. Think Output Structure ─────────────────────────────────────
33
+ describe('think output structure', () => {
34
+ it('includes step prefix [Thinking N/M]', async () => {
35
+ const text = extractText(await client.callTool({ name: 'think', arguments: thinkArgs('test', 2, 5) }));
36
+ expect(text).toMatch(/^\[Thinking 2\/5\]/);
37
+ });
38
+ it('includes thought content after prefix', async () => {
39
+ const text = extractText(await client.callTool({ name: 'think', arguments: thinkArgs('my analysis') }));
40
+ expect(text).toContain('my analysis');
41
+ });
42
+ it('includes verify nudge when nextThoughtNeeded=false', async () => {
43
+ const text = extractText(await client.callTool({
44
+ name: 'think',
45
+ arguments: thinkArgs('final conclusion', 3, 3, false),
46
+ }));
47
+ expect(text).toMatch(/verify tool/i);
48
+ expect(text).toContain('final conclusion');
49
+ });
50
+ it('does NOT include verify nudge when nextThoughtNeeded=true', async () => {
51
+ const text = extractText(await client.callTool({ name: 'think', arguments: thinkArgs('still working', 1, 3) }));
52
+ expect(text).not.toMatch(/verify tool/i);
53
+ });
54
+ it('returns exactly one content item', async () => {
55
+ const result = await client.callTool({
56
+ name: 'think',
57
+ arguments: thinkArgs('test'),
58
+ });
59
+ expect(result.content).toHaveLength(1);
60
+ });
61
+ it('content item has type "text"', async () => {
62
+ const result = await client.callTool({
63
+ name: 'think',
64
+ arguments: thinkArgs('test'),
65
+ });
66
+ expect(result.content[0].type).toBe('text');
67
+ });
68
+ });
69
+ // ─── 2. Think Identity Preservation ────────────────────────────────
70
+ describe('think identity preservation', () => {
71
+ const cases = [
72
+ ['plain ASCII', 'The quick brown fox jumps over the lazy dog.'],
73
+ ['JSON string', '{"key": "value", "nested": {"arr": [1, 2, 3]}}'],
74
+ ['markdown', '# Heading\n\n- bullet\n- **bold**\n\n```code```'],
75
+ ['HTML/XML tags', '<div class="test">content &amp; more</div>'],
76
+ ['backslashes', 'path\\to\\file and regex \\d+\\.\\d+'],
77
+ ['quotes', 'she said "hello" and \'goodbye\''],
78
+ ['newlines and tabs', 'line1\nline2\n\ttabbed\r\nwindows-style'],
79
+ ['emoji sequences', '👨‍👩‍👧‍👦 family, 🏳️‍🌈 flag, 👋🏽 skin tone'],
80
+ ['CJK characters', '思考する verify 検証 확인하다 验证'],
81
+ ['Arabic RTL', 'التحقق من الاستدلال'],
82
+ ['combining diacritics', 'e\u0301 vs é — n\u0303 vs ñ'],
83
+ ['zero-width chars', 'zero\u200Bwidth\u200Cjoiner\u200Dhere\uFEFFbom'],
84
+ ['surrogate pairs', '𝕳𝖊𝖑𝖑𝖔 𝕎𝕠𝕣𝕝𝕕 — 𝄞 music'],
85
+ ['very long', 'a'.repeat(50_000)],
86
+ ['only whitespace', ' \t\t\n\n '],
87
+ ['single character', 'x'],
88
+ ];
89
+ for (const [label, input] of cases) {
90
+ it(`preserves: ${label}`, async () => {
91
+ const text = extractText(await client.callTool({ name: 'think', arguments: thinkArgs(input) }));
92
+ expect(text).toContain(input);
93
+ });
94
+ }
95
+ });
96
+ // ─── 3. Verify Identity Preservation ───────────────────────────────
97
+ describe('verify identity preservation', () => {
98
+ const cases = [
99
+ ['plain ASCII', 'Is my assumption about thread safety correct?'],
100
+ ['JSON payload', '{"error": null, "data": [true, false]}'],
101
+ ['code snippet', 'if (x === null) throw new Error("unexpected null");'],
102
+ ['multiline', 'Line 1: assumption\nLine 2: evidence\nLine 3: conclusion'],
103
+ ['emoji + unicode', '⚠️ Edge case: 空の入力 → crash?'],
104
+ ['SQL injection', "'; DROP TABLE users; --"],
105
+ ['regex special chars', '^(?:foo|bar)\\b.*?\\d{3,}$'],
106
+ ['URL with params', 'https://example.com/api?key=val&other=123#fragment'],
107
+ ];
108
+ for (const [label, input] of cases) {
109
+ it(`preserves: ${label}`, async () => {
110
+ const text = extractText(await client.callTool({ name: 'verify', arguments: { concern: input } }));
111
+ expect(text).toBe(input);
112
+ });
113
+ }
114
+ });
115
+ // ─── 4. Verify Output Minimality ───────────────────────────────────
116
+ describe('verify output minimality', () => {
117
+ it('verify output is exactly the input string', async () => {
118
+ const input = 'My critical self-assessment';
119
+ const text = extractText(await client.callTool({ name: 'verify', arguments: { concern: input } }));
120
+ expect(text).toBe(input);
121
+ expect(text.length).toBe(input.length);
122
+ });
123
+ it('verify does not add timestamps or metadata', async () => {
124
+ const input = 'no metadata please';
125
+ const text = extractText(await client.callTool({ name: 'verify', arguments: { concern: input } }));
126
+ expect(text).toBe(input);
127
+ });
128
+ });
129
+ // ─── 5. Prompt Output Quality ──────────────────────────────────────
130
+ describe('prompt output quality', () => {
131
+ it('returns exactly one message with role "user"', async () => {
132
+ const result = await client.getPrompt({ name: 'steelmind' });
133
+ expect(result.messages).toHaveLength(1);
134
+ expect(result.messages[0].role).toBe('user');
135
+ });
136
+ it('message text matches SYSTEM_PROMPT exactly', async () => {
137
+ const result = await client.getPrompt({ name: 'steelmind' });
138
+ expect(result.messages[0].content.text).toBe(SYSTEM_PROMPT);
139
+ });
140
+ it('contains structured XML sections for compaction survivability', async () => {
141
+ const result = await client.getPrompt({ name: 'steelmind' });
142
+ const text = result.messages[0].content.text;
143
+ expect(text).toContain('<think_example>');
144
+ expect(text).toContain('</think_example>');
145
+ expect(text).toContain('<verify_example>');
146
+ expect(text).toContain('</verify_example>');
147
+ expect(text).toContain('<bad_example>');
148
+ expect(text).toContain('</bad_example>');
149
+ });
150
+ it('uses bullet points for compaction survivability', async () => {
151
+ const result = await client.getPrompt({ name: 'steelmind' });
152
+ const text = result.messages[0].content.text;
153
+ const bulletCount = (text.match(/^- /gm) || []).length;
154
+ expect(bulletCount).toBeGreaterThanOrEqual(4);
155
+ });
156
+ });
157
+ // ─── 6. Concurrent Correctness ─────────────────────────────────────
158
+ describe('concurrent correctness', () => {
159
+ it('parallel think calls return their own input', async () => {
160
+ const inputs = Array.from({ length: 20 }, (_, i) => `thought-${i}-${Math.random()}`);
161
+ const results = await Promise.all(inputs.map((thought, i) => client.callTool({
162
+ name: 'think',
163
+ arguments: thinkArgs(thought, i + 1, 20),
164
+ })));
165
+ for (let i = 0; i < inputs.length; i++) {
166
+ expect(extractText(results[i])).toContain(inputs[i]);
167
+ }
168
+ });
169
+ it('parallel verify calls return their own input', async () => {
170
+ const inputs = Array.from({ length: 20 }, (_, i) => `concern-${i}-${Math.random()}`);
171
+ const results = await Promise.all(inputs.map((concern) => client.callTool({ name: 'verify', arguments: { concern } })));
172
+ for (let i = 0; i < inputs.length; i++) {
173
+ expect(extractText(results[i])).toBe(inputs[i]);
174
+ }
175
+ });
176
+ });
177
+ // ─── 7. Tool Listing Output Quality ────────────────────────────────
178
+ describe('tool listing output quality', () => {
179
+ it('each tool has a non-empty description', async () => {
180
+ const { tools } = await client.listTools();
181
+ for (const tool of tools) {
182
+ expect(tool.description).toBeTruthy();
183
+ expect(tool.description.length).toBeGreaterThan(50);
184
+ }
185
+ });
186
+ it('think requires 4 fields, verify requires 1 (different cognitive modes)', async () => {
187
+ const { tools } = await client.listTools();
188
+ const think = tools.find((t) => t.name === 'think');
189
+ const verify = tools.find((t) => t.name === 'verify');
190
+ expect(think.inputSchema.required).toHaveLength(4);
191
+ expect(verify.inputSchema.required).toHaveLength(1);
192
+ });
193
+ it('no tool has outputSchema', async () => {
194
+ const { tools } = await client.listTools();
195
+ for (const tool of tools) {
196
+ expect(tool.outputSchema).toBeUndefined();
197
+ }
198
+ });
199
+ });
200
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,279 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
3
+ import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js';
4
+ import { createServer } from '../index.js';
5
+ import { THINK_DESCRIPTION, VERIFY_DESCRIPTION, SYSTEM_PROMPT } from '../descriptions.js';
6
+ describe('Steelmind MCP Server', () => {
7
+ let client;
8
+ let cleanup;
9
+ async function connect() {
10
+ const server = createServer();
11
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
12
+ await server.connect(serverTransport);
13
+ client = new Client({ name: 'test-client', version: '1.0.0' });
14
+ await client.connect(clientTransport);
15
+ cleanup = async () => {
16
+ await client.close();
17
+ await server.close();
18
+ };
19
+ }
20
+ beforeEach(async () => {
21
+ await connect();
22
+ });
23
+ afterEach(async () => {
24
+ await cleanup();
25
+ });
26
+ // ─── Server Identity ───────────────────────────────────────────────
27
+ describe('server identity', () => {
28
+ it('reports correct name and version', () => {
29
+ const info = client.getServerVersion();
30
+ expect(info?.name).toBe('steelmind-mcp');
31
+ expect(info?.version).toBe('2.0.0');
32
+ });
33
+ it('advertises tools and prompts capabilities', () => {
34
+ const caps = client.getServerCapabilities();
35
+ expect(caps?.tools).toBeDefined();
36
+ expect(caps?.prompts).toBeDefined();
37
+ });
38
+ });
39
+ // ─── Tool Listing ──────────────────────────────────────────────────
40
+ describe('listTools', () => {
41
+ it('exposes exactly 2 tools', async () => {
42
+ const { tools } = await client.listTools();
43
+ expect(tools).toHaveLength(2);
44
+ });
45
+ it('exposes think and verify by name', async () => {
46
+ const { tools } = await client.listTools();
47
+ const names = tools.map((t) => t.name);
48
+ expect(names).toContain('think');
49
+ expect(names).toContain('verify');
50
+ });
51
+ it('think tool has correct input schema with step tracking', async () => {
52
+ const { tools } = await client.listTools();
53
+ const think = tools.find((t) => t.name === 'think');
54
+ expect(think.inputSchema.type).toBe('object');
55
+ expect(think.inputSchema.required).toEqual([
56
+ 'thought',
57
+ 'thoughtNumber',
58
+ 'totalThoughts',
59
+ 'nextThoughtNeeded',
60
+ ]);
61
+ expect(think.inputSchema.properties).toHaveProperty('thought');
62
+ expect(think.inputSchema.properties).toHaveProperty('thoughtNumber');
63
+ expect(think.inputSchema.properties).toHaveProperty('totalThoughts');
64
+ expect(think.inputSchema.properties).toHaveProperty('nextThoughtNeeded');
65
+ });
66
+ it('verify tool has concern field (not thought)', async () => {
67
+ const { tools } = await client.listTools();
68
+ const verify = tools.find((t) => t.name === 'verify');
69
+ expect(verify.inputSchema.required).toEqual(['concern']);
70
+ expect(verify.inputSchema.properties).toHaveProperty('concern');
71
+ expect(verify.inputSchema.properties).not.toHaveProperty('thought');
72
+ });
73
+ it('think description matches descriptions.ts export', async () => {
74
+ const { tools } = await client.listTools();
75
+ const think = tools.find((t) => t.name === 'think');
76
+ expect(think.description).toBe(THINK_DESCRIPTION);
77
+ });
78
+ it('verify description matches descriptions.ts export', async () => {
79
+ const { tools } = await client.listTools();
80
+ const verify = tools.find((t) => t.name === 'verify');
81
+ expect(verify.description).toBe(VERIFY_DESCRIPTION);
82
+ });
83
+ });
84
+ // ─── Think Tool ────────────────────────────────────────────────────
85
+ describe('think tool (structured output)', () => {
86
+ it('returns thought with step prefix when nextThoughtNeeded=true', async () => {
87
+ const result = await client.callTool({
88
+ name: 'think',
89
+ arguments: {
90
+ thought: 'What assumptions am I making here?',
91
+ thoughtNumber: 1,
92
+ totalThoughts: 3,
93
+ nextThoughtNeeded: true,
94
+ },
95
+ });
96
+ const text = result.content[0].text;
97
+ expect(text).toContain('[Thinking 1/3]');
98
+ expect(text).toContain('What assumptions am I making here?');
99
+ });
100
+ it('includes verify nudge when nextThoughtNeeded=false', async () => {
101
+ const result = await client.callTool({
102
+ name: 'think',
103
+ arguments: {
104
+ thought: 'My conclusion is X.',
105
+ thoughtNumber: 3,
106
+ totalThoughts: 3,
107
+ nextThoughtNeeded: false,
108
+ },
109
+ });
110
+ const text = result.content[0].text;
111
+ expect(text).toContain('[Thinking 3/3]');
112
+ expect(text).toContain('My conclusion is X.');
113
+ expect(text).toMatch(/verify tool/i);
114
+ });
115
+ it('does NOT include verify nudge when nextThoughtNeeded=true', async () => {
116
+ const result = await client.callTool({
117
+ name: 'think',
118
+ arguments: {
119
+ thought: 'Still thinking...',
120
+ thoughtNumber: 1,
121
+ totalThoughts: 5,
122
+ nextThoughtNeeded: true,
123
+ },
124
+ });
125
+ const text = result.content[0].text;
126
+ expect(text).not.toMatch(/verify tool/i);
127
+ });
128
+ it('preserves unicode and special characters in thought', async () => {
129
+ const input = '思考: émojis 🧠 & "quotes" <tags> \n\tnewlines';
130
+ const result = await client.callTool({
131
+ name: 'think',
132
+ arguments: {
133
+ thought: input,
134
+ thoughtNumber: 1,
135
+ totalThoughts: 1,
136
+ nextThoughtNeeded: true,
137
+ },
138
+ });
139
+ const text = result.content[0].text;
140
+ expect(text).toContain(input);
141
+ });
142
+ it('handles very long input', async () => {
143
+ const longThought = 'x'.repeat(100_000);
144
+ const result = await client.callTool({
145
+ name: 'think',
146
+ arguments: {
147
+ thought: longThought,
148
+ thoughtNumber: 1,
149
+ totalThoughts: 1,
150
+ nextThoughtNeeded: true,
151
+ },
152
+ });
153
+ const text = result.content[0].text;
154
+ expect(text).toContain(longThought);
155
+ });
156
+ it('is not marked as an error', async () => {
157
+ const result = await client.callTool({
158
+ name: 'think',
159
+ arguments: {
160
+ thought: 'test',
161
+ thoughtNumber: 1,
162
+ totalThoughts: 1,
163
+ nextThoughtNeeded: false,
164
+ },
165
+ });
166
+ expect(result.isError).toBeFalsy();
167
+ });
168
+ it('supports adjustable totalThoughts', async () => {
169
+ const r1 = await client.callTool({
170
+ name: 'think',
171
+ arguments: {
172
+ thought: 'initial plan',
173
+ thoughtNumber: 1,
174
+ totalThoughts: 3,
175
+ nextThoughtNeeded: true,
176
+ },
177
+ });
178
+ expect(r1.content[0].text).toContain('[Thinking 1/3]');
179
+ const r2 = await client.callTool({
180
+ name: 'think',
181
+ arguments: {
182
+ thought: 'this is more complex than expected',
183
+ thoughtNumber: 2,
184
+ totalThoughts: 7,
185
+ nextThoughtNeeded: true,
186
+ },
187
+ });
188
+ expect(r2.content[0].text).toContain('[Thinking 2/7]');
189
+ });
190
+ });
191
+ // ─── Verify Tool ───────────────────────────────────────────────────
192
+ describe('verify tool (identity function)', () => {
193
+ it('returns the concern unchanged', async () => {
194
+ const result = await client.callTool({
195
+ name: 'verify',
196
+ arguments: { concern: 'Am I sure the recursive approach is safe?' },
197
+ });
198
+ expect(result.content).toEqual([
199
+ { type: 'text', text: 'Am I sure the recursive approach is safe?' },
200
+ ]);
201
+ });
202
+ it('handles empty string', async () => {
203
+ const result = await client.callTool({
204
+ name: 'verify',
205
+ arguments: { concern: '' },
206
+ });
207
+ expect(result.content).toEqual([{ type: 'text', text: '' }]);
208
+ });
209
+ it('preserves unicode and special characters', async () => {
210
+ const input = 'Vérification: 検証 🔍 & "edge cases" <xml/>';
211
+ const result = await client.callTool({
212
+ name: 'verify',
213
+ arguments: { concern: input },
214
+ });
215
+ expect(result.content).toEqual([{ type: 'text', text: input }]);
216
+ });
217
+ it('is not marked as an error', async () => {
218
+ const result = await client.callTool({
219
+ name: 'verify',
220
+ arguments: { concern: 'test' },
221
+ });
222
+ expect(result.isError).toBeFalsy();
223
+ });
224
+ });
225
+ // ─── Unknown Tool ──────────────────────────────────────────────────
226
+ describe('unknown tool', () => {
227
+ it('throws for unknown tool name', async () => {
228
+ await expect(client.callTool({ name: 'nonexistent', arguments: {} })).rejects.toThrow();
229
+ });
230
+ });
231
+ // ─── Prompts ───────────────────────────────────────────────────────
232
+ describe('prompts', () => {
233
+ it('exposes exactly 1 prompt named steelmind', async () => {
234
+ const { prompts } = await client.listPrompts();
235
+ expect(prompts).toHaveLength(1);
236
+ expect(prompts[0].name).toBe('steelmind');
237
+ });
238
+ it('returns the system prompt content', async () => {
239
+ const result = await client.getPrompt({ name: 'steelmind' });
240
+ expect(result.messages).toHaveLength(1);
241
+ expect(result.messages[0].role).toBe('user');
242
+ expect(result.messages[0].content).toEqual({ type: 'text', text: SYSTEM_PROMPT });
243
+ });
244
+ it('throws for unknown prompt name', async () => {
245
+ await expect(client.getPrompt({ name: 'nonexistent' })).rejects.toThrow();
246
+ });
247
+ });
248
+ // ─── Statelessness ─────────────────────────────────────────────────
249
+ describe('statelessness', () => {
250
+ it('interleaved think and verify calls are independent', async () => {
251
+ const t1 = await client.callTool({
252
+ name: 'think',
253
+ arguments: {
254
+ thought: 'plan approach',
255
+ thoughtNumber: 1,
256
+ totalThoughts: 2,
257
+ nextThoughtNeeded: true,
258
+ },
259
+ });
260
+ const v1 = await client.callTool({
261
+ name: 'verify',
262
+ arguments: { concern: 'is this safe?' },
263
+ });
264
+ const t2 = await client.callTool({
265
+ name: 'think',
266
+ arguments: {
267
+ thought: 'revised plan',
268
+ thoughtNumber: 2,
269
+ totalThoughts: 2,
270
+ nextThoughtNeeded: false,
271
+ },
272
+ });
273
+ expect(t1.content[0].text).toContain('plan approach');
274
+ expect(v1.content).toEqual([{ type: 'text', text: 'is this safe?' }]);
275
+ expect(t2.content[0].text).toContain('revised plan');
276
+ expect(t2.content[0].text).toMatch(/verify tool/i);
277
+ });
278
+ });
279
+ });
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env node
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import { createServer } from './index.js';
4
+ process.on('uncaughtException', (err) => {
5
+ console.error('[Fatal]', err);
6
+ process.exit(1);
7
+ });
8
+ process.on('unhandledRejection', (err) => {
9
+ console.error('[Fatal]', err);
10
+ process.exit(1);
11
+ });
12
+ const server = createServer();
13
+ process.on('SIGINT', async () => {
14
+ await server.close();
15
+ process.exit(0);
16
+ });
17
+ const transport = new StdioServerTransport();
18
+ server
19
+ .connect(transport)
20
+ .then(() => {
21
+ console.error('Steelmind MCP server running — think + verify');
22
+ })
23
+ .catch((err) => {
24
+ console.error('[Fatal] Server failed to start:', err);
25
+ process.exit(1);
26
+ });
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Steelmind MCP: Tool Descriptions and System Prompt
3
+ *
4
+ * These descriptions are the primary cognitive scaffold — they account for
5
+ * ~80% of the performance improvement (per Anthropic τ-bench research).
6
+ *
7
+ * Design principles applied:
8
+ * 1. Purpose first (primacy effect — ACL 2025)
9
+ * 2. When NOT to use (ToolACE: 6.99% → 83.81% irrelevance detection)
10
+ * 3. Socratic self-questioning in think (Chang 2023, Princeton SocraticAI)
11
+ * 4. Steel-manning in verify (SIEV ICML, MetaCrit)
12
+ * 5. Conciseness ~100 words (EasyTool: 70-97% token reduction → better perf)
13
+ * 6. No "think harder" instructions (OpenAI: degrades reasoning models)
14
+ * 7. Contrastive example in system prompt (Contrastive CoT, ACL 2024)
15
+ * 8. Sequential decomposition (Scaling TTC: 4x efficiency with adaptive compute)
16
+ * 9. Cognitive mode separation (MetaCrit: separating generation from evaluation)
17
+ *
18
+ * Key references:
19
+ * - Anthropic τ-bench: 54% improvement from optimized prompting
20
+ * - MetaCrit (2507.15015v3): separating generation from evaluation
21
+ * - Think2 (2602.18806v1): 3x self-correction with structured phases
22
+ * - SIEV (2510.18134, ICML): models lose 40+ pts under dialectical eval
23
+ * - Cognitive Foundations (2511.16660): scaffolding improves perf up to 72%
24
+ * - Scaling TTC (2408.03314): difficulty-adaptive compute improves efficiency >4x
25
+ */
26
+ export declare const THINK_DESCRIPTION: string;
27
+ export declare const VERIFY_DESCRIPTION: string;
28
+ export declare const SYSTEM_PROMPT = "## Using the think and verify tools\n\nBefore taking any action or responding after receiving tool results, use the think tool as a scratchpad to:\n- List the specific rules or constraints that apply\n- Check if all required information is collected\n- Verify your planned action makes sense\n- Identify what could go wrong\n- Adjust totalThoughts as your understanding deepens\n\nAfter your final thinking step (nextThoughtNeeded: false), use the verify tool to:\n- Steel-man the opposing view: build the strongest case against your conclusion\n- Check assumptions you haven't validated\n- Look for edge cases you may have missed\n\n<think_example>\nUser wants to cancel order #1234\n- thoughtNumber: 1, totalThoughts: 3\n- Need to verify: order status, cancellation policy, refund eligibility\n- Check: is order already shipped? If so, different policy applies\n- Missing info: haven't confirmed the user's identity yet\n- Risk: cancelling a shipped order requires return logistics\n\u2192 Plan: verify identity first, then check order status, then apply correct policy\n</think_example>\n\n<verify_example>\nI concluded we should use recursive approach over iterative\n- Steel-man for iterative: simpler to debug, no stack overflow risk, same time complexity\n- My assumption that recursion is \"cleaner\" is subjective, not evidence-based\n- Edge case: what if input size exceeds stack depth? Recursion fails silently\n\u2192 Reconsider: iterative is safer for unknown input sizes\n</verify_example>\n\n<bad_example>\n\"Let me think about this. The user wants X. I should do Y. Yes, Y seems right.\"\nThis adds tokens without insight. Be specific: what rules apply? What could go wrong? What are you assuming?\n</bad_example>";
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Steelmind MCP: Tool Descriptions and System Prompt
3
+ *
4
+ * These descriptions are the primary cognitive scaffold — they account for
5
+ * ~80% of the performance improvement (per Anthropic τ-bench research).
6
+ *
7
+ * Design principles applied:
8
+ * 1. Purpose first (primacy effect — ACL 2025)
9
+ * 2. When NOT to use (ToolACE: 6.99% → 83.81% irrelevance detection)
10
+ * 3. Socratic self-questioning in think (Chang 2023, Princeton SocraticAI)
11
+ * 4. Steel-manning in verify (SIEV ICML, MetaCrit)
12
+ * 5. Conciseness ~100 words (EasyTool: 70-97% token reduction → better perf)
13
+ * 6. No "think harder" instructions (OpenAI: degrades reasoning models)
14
+ * 7. Contrastive example in system prompt (Contrastive CoT, ACL 2024)
15
+ * 8. Sequential decomposition (Scaling TTC: 4x efficiency with adaptive compute)
16
+ * 9. Cognitive mode separation (MetaCrit: separating generation from evaluation)
17
+ *
18
+ * Key references:
19
+ * - Anthropic τ-bench: 54% improvement from optimized prompting
20
+ * - MetaCrit (2507.15015v3): separating generation from evaluation
21
+ * - Think2 (2602.18806v1): 3x self-correction with structured phases
22
+ * - SIEV (2510.18134, ICML): models lose 40+ pts under dialectical eval
23
+ * - Cognitive Foundations (2511.16660): scaffolding improves perf up to 72%
24
+ * - Scaling TTC (2408.03314): difficulty-adaptive compute improves efficiency >4x
25
+ */
26
+ export const THINK_DESCRIPTION = 'Use this tool to record a structured reasoning step. It will not obtain ' +
27
+ 'new information or change any state — it appends your thought to the log. ' +
28
+ 'Use it when you need to: process results from previous tool calls before ' +
29
+ 'acting, plan your approach to a multi-step task, analyze a complex ' +
30
+ 'situation before deciding, or navigate environments with detailed policies. ' +
31
+ 'Do NOT use for simple single-step tasks or restating without analysis. ' +
32
+ 'When thinking, ask yourself: What am I assuming? What evidence supports ' +
33
+ "this? What's my plan, and what could go wrong? " +
34
+ 'You can adjust totalThoughts up or down as your understanding deepens. ' +
35
+ 'When you set nextThoughtNeeded to false, use the verify tool to challenge ' +
36
+ 'your conclusion before acting.';
37
+ export const VERIFY_DESCRIPTION = 'Use this tool to challenge and evaluate your reasoning before committing ' +
38
+ 'to an action. It will not obtain new information or change any state — it ' +
39
+ 'logs your critical self-assessment. Use it when you need to: check if your ' +
40
+ 'planned action complies with all requirements, validate reasoning before ' +
41
+ 'committing, assess edge cases, or evaluate tool results for correctness. ' +
42
+ 'Do NOT use to confirm what you are already confident about. ' +
43
+ 'When verifying, steel-man the opposition: What is the strongest argument ' +
44
+ "that your conclusion is wrong? If you can't defeat it, reconsider. " +
45
+ 'If your verification reveals a flaw, use the think tool to revise your approach.';
46
+ export const SYSTEM_PROMPT = `## Using the think and verify tools
47
+
48
+ Before taking any action or responding after receiving tool results, use the think tool as a scratchpad to:
49
+ - List the specific rules or constraints that apply
50
+ - Check if all required information is collected
51
+ - Verify your planned action makes sense
52
+ - Identify what could go wrong
53
+ - Adjust totalThoughts as your understanding deepens
54
+
55
+ After your final thinking step (nextThoughtNeeded: false), use the verify tool to:
56
+ - Steel-man the opposing view: build the strongest case against your conclusion
57
+ - Check assumptions you haven't validated
58
+ - Look for edge cases you may have missed
59
+
60
+ <think_example>
61
+ User wants to cancel order #1234
62
+ - thoughtNumber: 1, totalThoughts: 3
63
+ - Need to verify: order status, cancellation policy, refund eligibility
64
+ - Check: is order already shipped? If so, different policy applies
65
+ - Missing info: haven't confirmed the user's identity yet
66
+ - Risk: cancelling a shipped order requires return logistics
67
+ → Plan: verify identity first, then check order status, then apply correct policy
68
+ </think_example>
69
+
70
+ <verify_example>
71
+ I concluded we should use recursive approach over iterative
72
+ - Steel-man for iterative: simpler to debug, no stack overflow risk, same time complexity
73
+ - My assumption that recursion is "cleaner" is subjective, not evidence-based
74
+ - Edge case: what if input size exceeds stack depth? Recursion fails silently
75
+ → Reconsider: iterative is safer for unknown input sizes
76
+ </verify_example>
77
+
78
+ <bad_example>
79
+ "Let me think about this. The user wants X. I should do Y. Yes, Y seems right."
80
+ This adds tokens without insight. Be specific: what rules apply? What could go wrong? What are you assuming?
81
+ </bad_example>`;
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
+ /**
4
+ * Creates and configures the Steelmind MCP server with all tool and prompt handlers.
5
+ * Exported for testing — the server is fully functional but not yet connected to a transport.
6
+ */
7
+ export declare function createServer(): Server;
package/dist/index.js ADDED
@@ -0,0 +1,106 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
+ import { CallToolRequestSchema, ListToolsRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
4
+ import { THINK_DESCRIPTION, VERIFY_DESCRIPTION, SYSTEM_PROMPT } from './descriptions.js';
5
+ /**
6
+ * Creates and configures the Steelmind MCP server with all tool and prompt handlers.
7
+ * Exported for testing — the server is fully functional but not yet connected to a transport.
8
+ */
9
+ export function createServer() {
10
+ const server = new Server({ name: 'steelmind-mcp', version: '2.0.0' }, { capabilities: { tools: {}, prompts: {} } });
11
+ server.onerror = (error) => console.error('[MCP Error]', error);
12
+ // --- Tools ---
13
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
14
+ tools: [
15
+ {
16
+ name: 'think',
17
+ description: THINK_DESCRIPTION,
18
+ inputSchema: {
19
+ type: 'object',
20
+ properties: {
21
+ thought: {
22
+ type: 'string',
23
+ description: 'Your current thinking step.',
24
+ },
25
+ thoughtNumber: {
26
+ type: 'integer',
27
+ description: 'Current thought number in the sequence.',
28
+ },
29
+ totalThoughts: {
30
+ type: 'integer',
31
+ description: 'Estimated total thoughts needed. Can be adjusted up or down as you progress.',
32
+ },
33
+ nextThoughtNeeded: {
34
+ type: 'boolean',
35
+ description: 'Whether another thinking step is needed after this one.',
36
+ },
37
+ },
38
+ required: ['thought', 'thoughtNumber', 'totalThoughts', 'nextThoughtNeeded'],
39
+ },
40
+ },
41
+ {
42
+ name: 'verify',
43
+ description: VERIFY_DESCRIPTION,
44
+ inputSchema: {
45
+ type: 'object',
46
+ properties: {
47
+ concern: {
48
+ type: 'string',
49
+ description: 'Your critical assessment or concern to verify.',
50
+ },
51
+ },
52
+ required: ['concern'],
53
+ },
54
+ },
55
+ ],
56
+ }));
57
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
58
+ const { name, arguments: args } = request.params;
59
+ switch (name) {
60
+ case 'think': {
61
+ const { thought, thoughtNumber, totalThoughts, nextThoughtNeeded } = args;
62
+ const prefix = `[Thinking ${thoughtNumber}/${totalThoughts}]`;
63
+ if (!nextThoughtNeeded) {
64
+ return {
65
+ content: [
66
+ {
67
+ type: 'text',
68
+ text: `${prefix}\n\n${thought}\n\n` +
69
+ '---\nThinking complete. Before acting on this conclusion, ' +
70
+ 'use the verify tool to challenge it.',
71
+ },
72
+ ],
73
+ };
74
+ }
75
+ return {
76
+ content: [{ type: 'text', text: `${prefix}\n\n${thought}` }],
77
+ };
78
+ }
79
+ case 'verify':
80
+ return {
81
+ content: [{ type: 'text', text: args.concern }],
82
+ };
83
+ default:
84
+ throw new Error(`Unknown tool: ${name}`);
85
+ }
86
+ });
87
+ // --- Prompts ---
88
+ server.setRequestHandler(ListPromptsRequestSchema, async () => ({
89
+ prompts: [
90
+ {
91
+ name: 'steelmind',
92
+ description: 'Metacognitive system prompt for structured thinking and steel-manning verification',
93
+ },
94
+ ],
95
+ }));
96
+ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
97
+ if (request.params.name !== 'steelmind') {
98
+ throw new Error(`Unknown prompt: ${request.params.name}`);
99
+ }
100
+ return {
101
+ description: 'Metacognitive system prompt for structured thinking and steel-manning verification',
102
+ messages: [{ role: 'user', content: { type: 'text', text: SYSTEM_PROMPT } }],
103
+ };
104
+ });
105
+ return server;
106
+ }
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "@stabgan/steelmind-mcp",
3
+ "version": "2.0.0",
4
+ "description": "Research-grounded metacognitive reasoning tools for AI agents. Structured step-by-step thinking with steel-manning verification via MCP. Combines sequential decomposition with cognitive science frameworks. Backed by 43+ papers.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "steelmind-mcp": "dist/cli.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "README.md"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc && shx chmod +x dist/*.js",
16
+ "prepare": "npm run build",
17
+ "start": "node dist/cli.js",
18
+ "lint": "eslint src",
19
+ "format": "prettier --write \"src/**/*.ts\" *.json \"*.md\"",
20
+ "format:check": "prettier --check \"src/**/*.ts\"",
21
+ "test": "node --experimental-vm-modules node_modules/.bin/vitest run"
22
+ },
23
+ "keywords": [
24
+ "mcp",
25
+ "steelmind",
26
+ "metacognition",
27
+ "reasoning",
28
+ "think",
29
+ "verify",
30
+ "steel-manning",
31
+ "dialectical",
32
+ "agentic",
33
+ "llm",
34
+ "claude",
35
+ "gpt",
36
+ "anthropic",
37
+ "sequential-thinking",
38
+ "chain-of-thought",
39
+ "model-context-protocol",
40
+ "ai-agent",
41
+ "cognitive-science",
42
+ "structured-reasoning"
43
+ ],
44
+ "author": "stabgan",
45
+ "repository": {
46
+ "type": "git",
47
+ "url": "https://github.com/stabgan/steelmind-mcp"
48
+ },
49
+ "bugs": {
50
+ "url": "https://github.com/stabgan/steelmind-mcp/issues"
51
+ },
52
+ "homepage": "https://github.com/stabgan/steelmind-mcp#readme",
53
+ "license": "MIT",
54
+ "engines": {
55
+ "node": ">=18.0.0"
56
+ },
57
+ "dependencies": {
58
+ "@modelcontextprotocol/sdk": "^1.27.1"
59
+ },
60
+ "devDependencies": {
61
+ "@eslint/js": "^9.39.2",
62
+ "@types/node": "^22.13.14",
63
+ "eslint": "^9.39.2",
64
+ "eslint-config-prettier": "^10.1.8",
65
+ "prettier": "^3.7.4",
66
+ "shx": "^0.3.4",
67
+ "typescript": "^5.8.2",
68
+ "typescript-eslint": "^8.53.0",
69
+ "vitest": "^3.1.1"
70
+ }
71
+ }