#
tokens: 3699/50000 5/5 files
lines: on (toggle) GitHub
raw markdown copy reset
# Directory Structure

```
├── .gitignore
├── LICENSE
├── pyproject.toml
├── README.md
├── tau_bench.png
├── think_mcp
│   ├── __init__.py
│   └── __main__.py
└── uv.lock
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
  1 | # Byte-compiled / optimized / DLL files
  2 | __pycache__/
  3 | *.py[cod]
  4 | *$py.class
  5 | 
  6 | # C extensions
  7 | *.so
  8 | 
  9 | # Distribution / packaging
 10 | .Python
 11 | build/
 12 | develop-eggs/
 13 | dist/
 14 | downloads/
 15 | eggs/
 16 | .eggs/
 17 | lib/
 18 | lib64/
 19 | parts/
 20 | sdist/
 21 | var/
 22 | wheels/
 23 | share/python-wheels/
 24 | *.egg-info/
 25 | .installed.cfg
 26 | *.egg
 27 | MANIFEST
 28 | 
 29 | # PyInstaller
 30 | #  Usually these files are written by a python script from a template
 31 | #  before PyInstaller builds the exe, so as to inject date/other infos into it.
 32 | *.manifest
 33 | *.spec
 34 | 
 35 | # Installer logs
 36 | pip-log.txt
 37 | pip-delete-this-directory.txt
 38 | 
 39 | # Unit test / coverage reports
 40 | htmlcov/
 41 | .tox/
 42 | .nox/
 43 | .coverage
 44 | .coverage.*
 45 | .cache
 46 | nosetests.xml
 47 | coverage.xml
 48 | *.cover
 49 | *.py,cover
 50 | .hypothesis/
 51 | .pytest_cache/
 52 | cover/
 53 | 
 54 | # Translations
 55 | *.mo
 56 | *.pot
 57 | 
 58 | # Django stuff:
 59 | *.log
 60 | local_settings.py
 61 | db.sqlite3
 62 | db.sqlite3-journal
 63 | 
 64 | # Flask stuff:
 65 | instance/
 66 | .webassets-cache
 67 | 
 68 | # Scrapy stuff:
 69 | .scrapy
 70 | 
 71 | # Sphinx documentation
 72 | docs/_build/
 73 | 
 74 | # PyBuilder
 75 | .pybuilder/
 76 | target/
 77 | 
 78 | # Jupyter Notebook
 79 | .ipynb_checkpoints
 80 | 
 81 | # IPython
 82 | profile_default/
 83 | ipython_config.py
 84 | 
 85 | # pyenv
 86 | #   For a library or package, you might want to ignore these files since the code is
 87 | #   intended to run in multiple environments; otherwise, check them in:
 88 | # .python-version
 89 | 
 90 | # pipenv
 91 | #   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
 92 | #   However, in case of collaboration, if having platform-specific dependencies or dependencies
 93 | #   having no cross-platform support, pipenv may install dependencies that don't work, or not
 94 | #   install all needed dependencies.
 95 | #Pipfile.lock
 96 | 
 97 | # UV
 98 | #   Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
 99 | #   This is especially recommended for binary packages to ensure reproducibility, and is more
100 | #   commonly ignored for libraries.
101 | #uv.lock
102 | 
103 | # poetry
104 | #   Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
105 | #   This is especially recommended for binary packages to ensure reproducibility, and is more
106 | #   commonly ignored for libraries.
107 | #   https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
108 | #poetry.lock
109 | 
110 | # pdm
111 | #   Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
112 | #pdm.lock
113 | #   pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
114 | #   in version control.
115 | #   https://pdm.fming.dev/latest/usage/project/#working-with-version-control
116 | .pdm.toml
117 | .pdm-python
118 | .pdm-build/
119 | 
120 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
121 | __pypackages__/
122 | 
123 | # Celery stuff
124 | celerybeat-schedule
125 | celerybeat.pid
126 | 
127 | # SageMath parsed files
128 | *.sage.py
129 | 
130 | # Environments
131 | .env
132 | .venv
133 | env/
134 | venv/
135 | ENV/
136 | env.bak/
137 | venv.bak/
138 | 
139 | # Spyder project settings
140 | .spyderproject
141 | .spyproject
142 | 
143 | # Rope project settings
144 | .ropeproject
145 | 
146 | # mkdocs documentation
147 | /site
148 | 
149 | # mypy
150 | .mypy_cache/
151 | .dmypy.json
152 | dmypy.json
153 | 
154 | # Pyre type checker
155 | .pyre/
156 | 
157 | # pytype static type analyzer
158 | .pytype/
159 | 
160 | # Cython debug symbols
161 | cython_debug/
162 | 
163 | # PyCharm
164 | #  JetBrains specific template is maintained in a separate JetBrains.gitignore that can
165 | #  be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
166 | #  and can be added to the global gitignore or merged into this file.  For a more nuclear
167 | #  option (not recommended) you can uncomment the following to ignore the entire idea folder.
168 | #.idea/
169 | 
170 | # Ruff stuff:
171 | .ruff_cache/
172 | 
173 | # PyPI configuration file
174 | .pypirc
175 | 
176 | .vscode/
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
 1 | # Think MCP Tool
 2 | 
 3 | Think MCP is an implementation of an MCP (Model Context Protocol) server that provides a "think" tool for structured reasoning in agentic AI workflows. This project is inspired by the Anthropic engineering article: [The "think" tool: Enabling Claude to stop and think in complex tool use situations](https://www.anthropic.com/engineering/claude-think-tool).
 4 | 
 5 | According to the referenced article, adding the think tool can lead to improved evaluation metrics by enabling reasoning capabilities even in models that do not natively possess advanced reasoning skills.
 6 | 
 7 | ![alt text](tau_bench.png)
 8 | 
 9 | ## What is the "think" tool?
10 | The "think" tool allows an AI agent to pause and record an explicit thought during complex reasoning or multi-step tool use. It does not change the environment or database, but appends the thought to the log, helping the agent process information, backtrack, or comply with detailed policies.
11 | 
12 | This approach is especially useful for:
13 | - Tool output analysis (processing results of previous tool calls)
14 | - Policy-heavy environments (verifying compliance with guidelines)
15 | - Sequential decision making (where each step builds on previous ones)
16 | 
17 | ## Features
18 | - Implements the "think" tool as described in Anthropic's research
19 | - Minimal, standards-based MCP server using [mcp[cli]](https://pypi.org/project/mcp/)
20 | - Ready for integration with Claude or other agentic LLMs
21 | 
22 | ## Usage
23 | 
24 | ### MCP server configuration
25 | Add this MCP server to your facorite agent.
26 | ```
27 | "mcpServers": {
28 |     "think-mcp": {
29 |         "command": "uvx",
30 |         "args": ["think-mcp"],
31 |         "enabled": true
32 |     }
33 | }
34 | ```
35 | 
36 | ## Tool definition
37 | The "think" tool is defined as:
38 | - **Input:** `thought` (string) — A thought to think about.
39 | - **Behavior:** Appends the thought to the log for structured reasoning.
40 | 
41 | ## Advanced mode
42 | Adds aditional tools for your agent:
43 | - criticize
44 | - plan
45 | - search
46 | 
47 | ```
48 | "mcpServers": {
49 |     "think-mcp": {
50 |         "command": "uvx",
51 |         "args": ["think-mcp", "--advanced"],
52 |         "enabled": true,
53 |         "env": {
54 |             "TAVILY_API_KEY": ... YOUR TAVILY API KEY HERE ...
55 |         }
56 |     }
57 | }
58 | ```
59 | 
60 | ## Reference
61 | - Based on: [Anthropic Engineering Blog — The "think" tool](https://www.anthropic.com/engineering/claude-think-tool)
62 | 
63 | ## License
64 | MIT License — see [LICENSE](LICENSE)
```

--------------------------------------------------------------------------------
/think_mcp/__init__.py:
--------------------------------------------------------------------------------

```python
1 | 
```

--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------

```toml
 1 | [project]
 2 | name = "think_mcp"
 3 | version = "0.1.16"
 4 | description = "MCP server providing a 'think' tool for structured reasoning, inspired by Anthropic's Claude 'think' tool. Enables agentic LLMs to pause, log thoughts, and improve multi-step tool use."
 5 | readme = "README.md"
 6 | requires-python = ">=3.10"
 7 | dependencies = [
 8 |     "mcp[cli]>=1.6.0",
 9 |     "python-dotenv>=1.1.0",
10 |     "tavily-python>=0.5.4",
11 | ]
12 | authors = [
13 |     { name = "Konstantin Krestnikov", email = "[email protected]" }
14 | ]
15 | license = { text = "MIT" }
16 | keywords = [
17 |     "mcp",
18 |     "think",
19 |     "agentic",
20 |     "llm",
21 |     "structured reasoning",
22 |     "anthropic",
23 |     "claude",
24 | ]
25 | 
26 | [project.urls]
27 | Homepage = "https://github.com/Rai220/think-mcp"
28 | 
29 | [tool.setuptools.packages.find]
30 | where = ["."]
31 | 
32 | [project.scripts]
33 | think_mcp = "think_mcp.__main__:main"
34 | think-mcp = "think_mcp.__main__:main"
35 | 
```

--------------------------------------------------------------------------------
/think_mcp/__main__.py:
--------------------------------------------------------------------------------

```python
 1 | from mcp.server.fastmcp import FastMCP
 2 | from pydantic import Field
 3 | from dotenv import load_dotenv, find_dotenv
 4 | import argparse
 5 | import json
 6 | 
 7 | load_dotenv(find_dotenv())
 8 | 
 9 | mcp = FastMCP("think_mcp")
10 | tavily_client = None
11 | 
12 | advanced_mode = False  # default, may be set in main()
13 | 
14 | @mcp.prompt()
15 | def system() -> str:
16 |     return """## Using the think tool
17 | 
18 | Before taking any action or responding to the user after receiving tool results, use the think tool as a scratchpad to:
19 | - List the specific rules that apply to the current request
20 | - Check if all required information is collected
21 | - Verify that the planned action complies with all policies
22 | - Iterate over tool results for correctness 
23 | 
24 | Here are some examples of what to iterate over inside the think tool:
25 | <think_tool_example_1>
26 | User wants to cancel flight ABC123
27 | - Need to verify: user ID, reservation ID, reason
28 | - Check cancellation rules:
29 |   * Is it within 24h of booking?
30 |   * If not, check ticket class and insurance
31 | - Verify no segments flown or are in the past
32 | - Plan: collect missing info, verify rules, get confirmation
33 | </think_tool_example_1>
34 | 
35 | <think_tool_example_2>
36 | User wants to book 3 tickets to NYC with 2 checked bags each
37 | - Need user ID to check:
38 |   * Membership tier for baggage allowance
39 |   * Which payments methods exist in profile
40 | - Baggage calculation:
41 |   * Economy class × 3 passengers
42 |   * If regular member: 1 free bag each → 3 extra bags = $150
43 |   * If silver member: 2 free bags each → 0 extra bags = $0
44 |   * If gold member: 3 free bags each → 0 extra bags = $0
45 | - Payment rules to verify:
46 |   * Max 1 travel certificate, 1 credit card, 3 gift cards
47 |   * All payment methods must be in profile
48 |   * Travel certificate remainder goes to waste
49 | - Plan:
50 | 1. Get user ID
51 | 2. Verify membership level for bag fees
52 | 3. Check which payment methods in profile and if their combination is allowed
53 | 4. Calculate total: ticket price + any bag fees
54 | 5. Get explicit confirmation for booking
55 | </think_tool_example_2>"""
56 | 
57 | @mcp.tool()
58 | async def think(thought: str = Field(..., description="A thought to think about.")) -> str:
59 |     """Use the tool to think about something. 
60 | It will not obtain new information or change the database, but just append the thought to the log. 
61 | Use it when complex reasoning or some cache memory is needed."""
62 |     return thought
63 | 
64 | parser = argparse.ArgumentParser(description="Think MCP server")
65 | parser.add_argument('--advanced', action='store_true', help='Enable advanced mode (plan, search, criticize tools)')
66 | args = parser.parse_args()
67 | 
68 | if args.advanced:
69 |     print("Advanced mode enabled. Loading advanced tools...")
70 |     
71 |     @mcp.tool()
72 |     async def criticize(criticism: str = Field(..., description="Сonstructive criticism")) -> str:
73 |         """Use the tool to critic your steps. 
74 |         It will not obtain new information or change the database, but just append the thought to the log. 
75 |         Use it when complex reasoning or some cache memory is needed."""
76 |         return criticism
77 | 
78 |     @mcp.tool()
79 |     async def plan(plan: str = Field(..., description="A plan of next steps")) -> str:
80 |         """Use the tool to plan your steps. 
81 |         It will not obtain new information or change the database, but just append the thought to the log. 
82 |         Use it when complex reasoning or some cache memory is needed."""
83 |         return plan
84 | 
85 |     @mcp.tool()
86 |     async def search(query: str = Field(..., description="Search query")) -> str:
87 |         """Search the web for a given query."""
88 |         from tavily import TavilyClient
89 |         tavily_client = TavilyClient()
90 |         context = tavily_client.get_search_context(query=query)
91 |         return json.dumps(context, ensure_ascii=False)
92 | 
93 | def main():
94 |     mcp.run(transport='stdio')
95 | 
96 | if __name__ == "__main__":
97 |     main()
98 | 
```