# Directory Structure
```
├── .gitignore
├── mcp_instructions.md
├── notion_mcp_server.py
├── README.md
└── requirements.txt
```
# Files
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
```
1 | .env
2 | *.env
```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # Notion Knowledge Base MCP Server
2 |
3 | An MCP server that provides access to a Notion knowledge base through the Cline VSCode extension.
4 |
5 | ## Features
6 |
7 | - Query your Notion knowledge base directly from Cline
8 | - Get detailed answers with references to Notion pages
9 | - Built with FastMCP for reliable performance
10 | - Comprehensive error handling and logging
11 |
12 | ## Prerequisites
13 |
14 | - Python 3.10 or higher
15 | - [uv](https://github.com/astral-sh/uv) package manager
16 | - [Cline VSCode extension](https://marketplace.visualstudio.com/items?itemName=saoudrizwan.claude-dev)
17 | - A Dify API key for accessing the Notion knowledge base
18 |
19 | ## Installation
20 |
21 | 1. Clone this repository:
22 | ```bash
23 | git clone https://github.com/yourusername/notion-mcp-server.git
24 | cd notion-mcp-server
25 | ```
26 |
27 | 2. Create a `.env` file with your Dify API key:
28 | ```bash
29 | echo "DIFY_API_BACKEND_KEY=your-api-key-here" > .env
30 | ```
31 |
32 | 3. Install the server in Cline:
33 | ```bash
34 | fastmcp install notion_mcp_server.py
35 | ```
36 |
37 | This will automatically:
38 | - Install all required dependencies using uv
39 | - Configure the server in Cline's settings
40 | - Make the server available to use with Cline
41 |
42 | ## Usage
43 |
44 | Once installed, you can use the server in Cline by asking questions about your Notion knowledge base. For example:
45 |
46 | ```
47 | Tell me about internal tooling
48 | ```
49 |
50 | The server will respond with relevant information from your Notion knowledge base, including:
51 | - Detailed answers
52 | - Links to relevant Notion pages
53 | - Page IDs for reference
54 |
55 | ## Configuration
56 |
57 | The server is configured automatically during installation, but you can manually update the settings in Cline's configuration file if needed:
58 |
59 | - macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
60 | - Windows: `%APPDATA%\Claude\claude_desktop_config.json`
61 |
62 | Example configuration:
63 | ```json
64 | {
65 | "mcpServers": {
66 | "notion-kb": {
67 | "command": "uv",
68 | "args": [
69 | "run",
70 | "--with", "fastmcp",
71 | "--with", "python-dotenv",
72 | "--with", "requests",
73 | "fastmcp",
74 | "run",
75 | "/absolute/path/to/notion_mcp_server.py"
76 | ],
77 | "env": {
78 | "DIFY_API_BACKEND_KEY": "your-api-key"
79 | }
80 | }
81 | }
82 | }
83 | ```
84 |
85 | ## Development
86 |
87 | For development and testing:
88 |
89 | 1. Install dependencies:
90 | ```bash
91 | pip install -r requirements.txt
92 | ```
93 |
94 | 2. Run the development server:
95 | ```bash
96 | fastmcp dev notion_mcp_server.py
97 | ```
98 |
99 | This will start the MCP Inspector interface for testing the server.
100 |
101 | ## Troubleshooting
102 |
103 | 1. **Server not connecting**
104 | - Verify your API key in the `.env` file
105 | - Ensure the server path in Cline's config is absolute
106 | - Check that uv is installed and in your PATH
107 |
108 | 2. **Dependencies issues**
109 | - Try reinstalling with `fastmcp install notion_mcp_server.py --force`
110 | - Verify uv is installed correctly
111 |
112 | 3. **Server hangs**
113 | - Ensure you're using the uv run command as specified in the config
114 | - Check the server logs for errors
115 |
116 | ## Contributing
117 |
118 | See [mcp_instructions.md](mcp_instructions.md) for detailed information about the server's implementation and architecture.
119 |
120 | ## License
121 |
122 | MIT
123 |
```
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
```
1 | fastmcp==0.4.1
2 | python-dotenv
3 | requests
4 |
```
--------------------------------------------------------------------------------
/notion_mcp_server.py:
--------------------------------------------------------------------------------
```python
1 | #!/usr/bin/env python3
2 | import os
3 | import json
4 | import requests
5 | from dotenv import load_dotenv
6 | from fastmcp import FastMCP, Context
7 | from typing import Dict, Any
8 | from pydantic import BaseModel, Field
9 |
10 | # Initialize FastMCP server
11 | mcp = FastMCP(
12 | name="Notion Knowledge Base",
13 | description="MCP server for querying a Notion knowledge base",
14 | version="0.1.0",
15 | dependencies=["python-dotenv", "requests"]
16 | )
17 |
18 | # Load environment variables
19 | load_dotenv()
20 |
21 | # Get API key from environment
22 | API_KEY = os.getenv('DIFY_API_BACKEND_KEY')
23 | if not API_KEY:
24 | raise ValueError("DIFY_API_BACKEND_KEY environment variable not set")
25 |
26 | class NotionResponse(BaseModel):
27 | """Structure for Notion API responses."""
28 | answer: str = Field(default="")
29 | notion_page_url: str = Field(default="")
30 | notion_page_id: str = Field(default="")
31 |
32 | @classmethod
33 | def from_api_response(cls, data: Dict[str, Any]) -> "NotionResponse":
34 | """Create a NotionResponse from API response data."""
35 | outputs = data.get('data', {}).get('outputs', {})
36 | return cls(
37 | answer=outputs.get('answer', ''),
38 | notion_page_url=outputs.get('notion_page_url', ''),
39 | notion_page_id=outputs.get('notion_page_id', '')
40 | )
41 |
42 | @mcp.tool()
43 | def ask_notion_question(question: str, ctx: Context) -> Dict[str, Any]:
44 | """Ask a question about the Notion knowledge base.
45 |
46 | Args:
47 | question: The question to ask about the Notion knowledge base
48 | ctx: MCP context for logging and progress tracking
49 |
50 | Returns:
51 | Dictionary containing the answer and related Notion page information
52 |
53 | Raises:
54 | ValueError: If the API key is not set or if the request fails
55 | """
56 | ctx.info(f"Processing question: {question}")
57 |
58 | try:
59 | url = "https://dify.rickydata.com/v1/workflows/run"
60 | headers = {
61 | 'Authorization': f"Bearer {API_KEY}",
62 | 'Content-Type': 'application/json'
63 | }
64 | payload = {
65 | 'inputs': {'question': question},
66 | 'response_mode': 'blocking',
67 | 'user': 'curation_agent_python'
68 | }
69 |
70 | ctx.debug("Sending request to Notion API")
71 | response = requests.post(url, headers=headers, json=payload)
72 | response.raise_for_status()
73 |
74 | data = response.json()
75 | if not data.get('data', {}).get('outputs', {}):
76 | raise ValueError("Invalid response format from API")
77 |
78 | result = NotionResponse.from_api_response(data)
79 | ctx.debug("Successfully received response from Notion API")
80 |
81 | return result.model_dump()
82 |
83 | except requests.RequestException as e:
84 | error_msg = f"API request failed: {str(e)}"
85 | ctx.error(error_msg)
86 | raise ValueError(error_msg)
87 | except json.JSONDecodeError as e:
88 | error_msg = f"Invalid JSON response: {str(e)}"
89 | ctx.error(error_msg)
90 | raise ValueError(error_msg)
91 | except Exception as e:
92 | error_msg = f"Unexpected error: {str(e)}"
93 | ctx.error(error_msg)
94 | raise ValueError(error_msg)
95 |
96 | if __name__ == "__main__":
97 | mcp.run()
98 |
```
--------------------------------------------------------------------------------
/mcp_instructions.md:
--------------------------------------------------------------------------------
```markdown
1 | # Building a Notion Knowledge Base MCP Server
2 |
3 | This guide walks through the process of creating an MCP server that interfaces with a Notion knowledge base via the Dify API.
4 |
5 | ## Starting Point
6 |
7 | We began with a Python example that demonstrated how to query the Dify API:
8 |
9 | ```python
10 | def pull_dify_api_result_with_input(input_example="input_example"):
11 | load_dotenv()
12 | url = "https://dify.rickydata.com/v1/workflows/run"
13 | headers = {
14 | 'Authorization': f"Bearer {os.getenv('DIFY_API_BACKEND_KEY')}",
15 | 'Content-Type': 'application/json'
16 | }
17 | payload = {
18 | 'inputs': {
19 | 'input_example': input_example
20 | },
21 | 'response_mode': 'blocking',
22 | 'user': 'curation_agent_python'
23 | }
24 |
25 | response = requests.post(url, headers=headers, json=payload)
26 | response_data = response.json()
27 |
28 | if response_data.get('data', {}).get('outputs', {}):
29 | return response_data['data']['outputs']
30 | return response_data
31 | ```
32 |
33 | ## Implementation Steps
34 |
35 | ### 1. Project Setup
36 | 1. Create a new directory for the MCP server:
37 | ```bash
38 | mkdir notion_mcp_server
39 | cd notion_mcp_server
40 | ```
41 |
42 | 2. Create initial files:
43 | - `.env` for API key
44 | - `requirements.txt` for dependencies
45 | - `notion_mcp_server.py` for server implementation
46 |
47 | ### 2. Dependencies
48 | Set up required dependencies in `requirements.txt`:
49 | ```
50 | fastmcp==0.4.1
51 | python-dotenv
52 | requests
53 | ```
54 |
55 | ### 3. Server Implementation
56 | 1. Create a FastMCP server with proper configuration:
57 | ```python
58 | mcp = FastMCP(
59 | name="Notion Knowledge Base",
60 | description="MCP server for querying a Notion knowledge base",
61 | version="0.1.0",
62 | dependencies=["python-dotenv", "requests"]
63 | )
64 | ```
65 |
66 | 2. Define data models using Pydantic:
67 | ```python
68 | class NotionResponse(BaseModel):
69 | answer: str = Field(default="")
70 | notion_page_url: str = Field(default="")
71 | notion_page_id: str = Field(default="")
72 | ```
73 |
74 | 3. Implement the question-asking tool:
75 | ```python
76 | @mcp.tool()
77 | def ask_notion_question(question: str, ctx: Context) -> Dict[str, Any]:
78 | """Ask a question about the Notion knowledge base."""
79 | # Implementation details...
80 | ```
81 |
82 | ### 4. Error Handling and Logging
83 | Add comprehensive error handling and logging:
84 | ```python
85 | try:
86 | response = requests.post(url, headers=headers, json=payload)
87 | response.raise_for_status()
88 | except requests.RequestException as e:
89 | error_msg = f"API request failed: {str(e)}"
90 | ctx.error(error_msg)
91 | raise ValueError(error_msg)
92 | ```
93 |
94 | ### 5. Testing
95 | 1. Test the basic Python implementation:
96 | ```bash
97 | python3 notion_api.py
98 | ```
99 |
100 | 2. Test the MCP server:
101 | ```bash
102 | fastmcp dev notion_mcp_server.py
103 | ```
104 |
105 | ### 6. Integration with Cline
106 | 1. Install the server in Cline:
107 | ```bash
108 | fastmcp install notion_mcp_server.py
109 | ```
110 |
111 | 2. Configure the server in Cline's settings:
112 | ```json
113 | {
114 | "mcpServers": {
115 | "notion-kb": {
116 | "command": "uv",
117 | "args": [
118 | "run",
119 | "--with", "fastmcp",
120 | "--with", "python-dotenv",
121 | "--with", "requests",
122 | "fastmcp",
123 | "run",
124 | "/path/to/notion_mcp_server.py"
125 | ],
126 | "env": {
127 | "DIFY_API_BACKEND_KEY": "your-api-key"
128 | }
129 | }
130 | }
131 | }
132 | ```
133 |
134 | ## Key Learnings
135 |
136 | 1. **Synchronous vs Async**: Keep the implementation synchronous for simplicity and reliability.
137 | 2. **Proper Dependencies**: Use `uv run` with explicit dependencies for better isolation.
138 | 3. **Error Handling**: Implement comprehensive error handling and logging.
139 | 4. **Configuration**: Use environment variables for sensitive data.
140 | 5. **Testing**: Test thoroughly at each step of implementation.
141 |
142 | ## Troubleshooting
143 |
144 | 1. If the server hangs, ensure you're using `uv run` instead of `python3` directly.
145 | 2. If connection fails, verify the API key is properly set in environment variables.
146 | 3. For "Not connected" errors, ensure the server is properly configured in Cline settings.
147 |
```