Skip to content

Commit fcd933d

Browse files
committed
got the page update for the tasks
1 parent a750eac commit fcd933d

7 files changed

Lines changed: 772 additions & 0 deletions

File tree

notion_brain_dump/.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Python-generated files
2+
__pycache__/
3+
*.py[oc]
4+
build/
5+
dist/
6+
wheels/
7+
*.egg-info
8+
9+
# Virtual environments
10+
.venv

notion_brain_dump/.python-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.11

notion_brain_dump/README.md

Whitespace-only changes.

notion_brain_dump/mcpclient.py

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
import asyncio
2+
from typing import Optional
3+
from contextlib import AsyncExitStack
4+
5+
# pyright: reportMissingImports=false
6+
# pyright: reportOptionalSubscript=false
7+
8+
from mcp import ClientSession, StdioServerParameters
9+
from mcp.client.stdio import stdio_client
10+
11+
from anthropic import Anthropic
12+
from dotenv import load_dotenv
13+
14+
# uncomment this when running in your local environment
15+
# ensure you have updated the .env file with the Anthropic API Key
16+
# load_dotenv() # load environment variables from .env
17+
18+
19+
class MCPClient:
20+
def __init__(self):
21+
# Initialize session and client objects
22+
self.session: Optional[ClientSession] = None
23+
self.exit_stack = AsyncExitStack()
24+
self.anthropic = Anthropic()
25+
26+
# methods will go here
27+
async def connect_to_server(self, server_script_path: str):
28+
"""Connect to notion MCP server
29+
30+
Args:
31+
server_script_path: Path to the server script (.py or .js)
32+
"""
33+
is_python = server_script_path.endswith(".py")
34+
is_js = server_script_path.endswith(".js")
35+
if not (is_python or is_js):
36+
raise ValueError("Server script must be a .py or .js file")
37+
38+
command = "python" if is_python else "node"
39+
server_params = StdioServerParameters(
40+
command=command, args=[server_script_path], env=None
41+
)
42+
43+
stdio_transport = await self.exit_stack.enter_async_context(
44+
stdio_client(server_params)
45+
)
46+
self.stdio, self.write = stdio_transport
47+
self.session = await self.exit_stack.enter_async_context(
48+
ClientSession(self.stdio, self.write)
49+
)
50+
51+
await self.session.initialize()
52+
53+
# List available tools
54+
response = await self.session.list_tools()
55+
tools = response.tools
56+
print(
57+
"\nConnected to Notion MCP server with tools:",
58+
[tool.name for tool in tools],
59+
)
60+
61+
async def process_query(self, query: str) -> str:
62+
"""Process a query using Claude and available tools"""
63+
messages = [{"role": "user", "content": query}]
64+
65+
response = await self.session.list_tools()
66+
available_tools = [
67+
{
68+
"name": tool.name,
69+
"description": tool.description,
70+
"input_schema": tool.inputSchema,
71+
}
72+
for tool in response.tools
73+
]
74+
75+
# Initial Claude API call
76+
response = self.anthropic.messages.create(
77+
model="claude-3-5-haiku-20241022",
78+
max_tokens=1000,
79+
messages=messages,
80+
tools=available_tools,
81+
)
82+
83+
# Process response and handle tool calls
84+
tool_results = []
85+
final_text = []
86+
87+
for content in response.content:
88+
if content.type == "text":
89+
final_text.append(content.text)
90+
elif content.type == "tool_use":
91+
tool_name = content.name
92+
tool_args = content.input
93+
94+
# Execute tool call
95+
result = await self.session.call_tool(tool_name, tool_args)
96+
tool_results.append({"call": tool_name, "result": result})
97+
final_text.append(f"[Calling tool {tool_name} with args {tool_args}]")
98+
99+
# Continue conversation with tool results
100+
if hasattr(content, "text") and content.text:
101+
messages.append({"role": "assistant", "content": content.text})
102+
messages.append({"role": "user", "content": result.content})
103+
104+
# Get next response from Claude
105+
response = self.anthropic.messages.create(
106+
model="claude-3-5-haiku-20241022",
107+
max_tokens=1000,
108+
messages=messages,
109+
)
110+
111+
final_text.append(response.content[0].text)
112+
113+
return "\n".join(final_text)
114+
115+
async def chat_loop(self):
116+
"""Run an interactive chat loop"""
117+
print("\nMCP Client Started!")
118+
print("Type your queries or 'quit' to exit.")
119+
120+
while True:
121+
try:
122+
query = input("\nQuery: ").strip()
123+
124+
if query.lower() == "quit":
125+
break
126+
127+
response = await self.process_query(query)
128+
print("\n" + response)
129+
130+
except Exception as e:
131+
print(f"\nError: {str(e)}")
132+
133+
async def cleanup(self):
134+
"""Clean up resources"""
135+
await self.exit_stack.aclose()
136+
137+
138+
async def main():
139+
if len(sys.argv) < 2:
140+
print("Usage: python mcpclient.py mcpserver.py")
141+
sys.exit(1)
142+
143+
client = MCPClient()
144+
try:
145+
await client.connect_to_server(sys.argv[1])
146+
await client.chat_loop()
147+
finally:
148+
await client.cleanup()
149+
150+
151+
if __name__ == "__main__":
152+
import sys
153+
154+
asyncio.run(main())

notion_brain_dump/pyproject.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[project]
2+
name = "notion-brain-dump"
3+
version = "0.1.0"
4+
description = "Add your description here"
5+
readme = "README.md"
6+
requires-python = ">=3.11"
7+
dependencies = [
8+
"anthropic>=0.49.0",
9+
"mcp[cli]>=1.6.0",
10+
"notion-client>=2.3.0",
11+
"python-dotenv>=1.1.0",
12+
]

notion_brain_dump/server.py

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
from mcp.server.fastmcp import FastMCP
2+
from notion_client import AsyncClient
3+
import os
4+
from typing import List
5+
6+
token = "ntn_token"
7+
mcp = FastMCP("notion_bd_server")
8+
brain = AsyncClient(auth=token)
9+
10+
11+
@mcp.tool()
12+
async def add_task(task: str):
13+
"""Add a task to the Notion database and returns the new page ID"""
14+
dump_db = "1d784ade96ac80a3b7ecf54f3eae5f49"
15+
newpage = {
16+
"TaskTitle": {"title": [{"text": {"content": task}}]},
17+
}
18+
pg_create = await brain.pages.create(
19+
parent={"database_id": dump_db},
20+
properties=newpage,
21+
)
22+
return f"Task has been added {pg_create['id']}"
23+
24+
25+
@mcp.prompt()
26+
def task_steps(task_text: str) -> str:
27+
"""Prompt to get the steps to complete the tasks"""
28+
return f"Provide the step by approach required to complete the {task_text}"
29+
30+
31+
@mcp.prompt()
32+
def task_analysis(tasks: str) -> str:
33+
"""Prompt to analyse the tasks"""
34+
return f"Analyse the {tasks} and provide the steps to complete the task"
35+
36+
37+
@mcp.resource("notion://dumpdb/{task_text}")
38+
async def search_db(task_text: str) -> str:
39+
"""Get the filtered task from the Notion database and returns the data as text"""
40+
dump_db = "1d784ade96ac80a3b7ecf54f3eae5f49"
41+
search_results = await brain.databases.query(
42+
database_id=dump_db,
43+
filter={
44+
"property": "TaskTitle",
45+
"title": {
46+
"contains": task_text,
47+
},
48+
},
49+
)
50+
results = search_results.get("results", [])
51+
text_conv = ""
52+
for result in results:
53+
page_id = result["id"]
54+
created_time = result["created_time"]
55+
last_edited_time = result["last_edited_time"]
56+
# getting the properties now
57+
task_title = result["properties"]["TaskTitle"]["title"][0]["text"]["content"]
58+
task_status = result["properties"]["TaskStatus"]["select"]
59+
area = result["properties"]["Area"]["rich_text"]
60+
resource = result["properties"]["Resource"]["rich_text"]
61+
due_date = result["properties"]["DueDate"]["date"]
62+
url = result["url"]
63+
text_conv += f"Task: {task_title}\t Status: {task_status}\t Area: {area}\t Resource: {resource}\t Due Date: {due_date}\t URL: {url}\t Page ID: {page_id}\t Last Edited Time: {last_edited_time}\t "
64+
# return results[0]["text"]
65+
return text_conv
66+
67+
68+
# @mcp.tool()
69+
async def analyse_tasks(task_kw: str) -> str:
70+
"""Searches the database for the task, and gets the steps to complete the task"""
71+
task_info = await mcp.read_resource(f"notion://dumpdb/{task_kw}")
72+
assembled_prompt = await mcp.get_prompt(
73+
"task_analysis",
74+
arguments={
75+
"tasks": task_info[0].content,
76+
},
77+
)
78+
return assembled_prompt.messages[0].content.text
79+
80+
81+
@mcp.tool()
82+
async def update_task(task_title: str, task_data: str):
83+
"""Update the task in the database with given task title"""
84+
# First find the task and get its page id
85+
dump_db = "1d784ade96ac80a3b7ecf54f3eae5f49"
86+
87+
search_results = await brain.databases.query(
88+
database_id=dump_db,
89+
filter={
90+
"property": "TaskTitle",
91+
"title": {
92+
"equals": task_title,
93+
},
94+
},
95+
)
96+
results = search_results.get("results", [])
97+
page_id = results[0]["id"]
98+
99+
# Then update the data into the page using append method
100+
page_data = {
101+
"object": "block",
102+
"type": "paragraph",
103+
"paragraph": {"rich_text": [{"text": {"content": task_data}}]},
104+
}
105+
106+
await brain.blocks.children.append(
107+
block_id=page_id,
108+
children=[page_data],
109+
)
110+
# Inform the LLM that the work has been done
111+
return f"The page with {page_id} has been updated"
112+
113+
114+
# "TaskTitle": {"id": "title", "type": "title", "title": [{"type": "text", "text": {"content": "test", "link": null}, "annotations": {"bold": false, "italic": false, "strikethrough": false, "underline": false, "code": false, "color": "default"}, "plain_text": "test", "href": null}] [ "properties": {"TaskStatus": {"id": "IJax", "type": "select", "select": null}, "Area": {"id": "c%3C%60%7C", "type": "rich_text", "rich_text": []}, "DueDate": {"id": "ce%3DU", "type": "date", "date": null}, "Resource": {"id": "kUxZ", "type": "rich_text", "rich_text": []}, }}, "url": "https://www.notion.so/test-1d784ade96ac81a38f7bdd1eccaa13c4", "public_url": null}]',]
115+
if __name__ == "__main__":
116+
print("Starting MCP server...")
117+
mcp.run()

0 commit comments

Comments
 (0)