Skip to main content
Glama
ParkJong-Hun

Get My Notion MCP Server

by ParkJong-Hun
handlers.rs9.71 kB
use crate::constants::{github as github_constants, mcp as mcp_constants, errors}; use crate::github::GitHubClient; use crate::mcp::*; use crate::server::{ResourceHandler, ToolHandler}; use crate::utils; use anyhow::Result; use std::collections::HashMap; pub struct ListFilesHandler { github_client: GitHubClient, } impl ListFilesHandler { pub fn new() -> Self { Self { github_client: GitHubClient::new_default(), } } pub fn new_with_client(github_client: GitHubClient) -> Self { Self { github_client } } } #[async_trait::async_trait] impl ToolHandler for ListFilesHandler { async fn call(&self, arguments: Option<HashMap<String, serde_json::Value>>) -> Result<CallToolResult> { let path = arguments .as_ref() .and_then(|args| args.get(mcp_constants::PARAM_PATH)) .and_then(|v| v.as_str()); let files = self.github_client.list_files(path).await?; let content = utils::format_file_info(&files); Ok(CallToolResult { content: vec![ToolContent::Text { text: content }], }) } } pub struct GetFileContentHandler { github_client: GitHubClient, } impl GetFileContentHandler { pub fn new() -> Self { Self { github_client: GitHubClient::new_default(), } } pub fn new_with_client(github_client: GitHubClient) -> Self { Self { github_client } } } #[async_trait::async_trait] impl ToolHandler for GetFileContentHandler { async fn call(&self, arguments: Option<HashMap<String, serde_json::Value>>) -> Result<CallToolResult> { let path = arguments .as_ref() .and_then(|args| args.get(mcp_constants::PARAM_PATH)) .and_then(|v| v.as_str()) .ok_or_else(|| anyhow::anyhow!(errors::PATH_REQUIRED))?; let content = self.github_client.get_file_content(path).await?; let response_text = utils::format_file_content(path, &content); Ok(CallToolResult { content: vec![ToolContent::Text { text: response_text }], }) } } pub struct GetLatestCommitHandler { github_client: GitHubClient, } impl GetLatestCommitHandler { pub fn new() -> Self { Self { github_client: GitHubClient::new_default(), } } pub fn new_with_client(github_client: GitHubClient) -> Self { Self { github_client } } } #[async_trait::async_trait] impl ToolHandler for GetLatestCommitHandler { async fn call(&self, _arguments: Option<HashMap<String, serde_json::Value>>) -> Result<CallToolResult> { let sha = self.github_client.get_latest_commit_sha().await?; let response_text = format!("Latest commit SHA: {}", sha); Ok(CallToolResult { content: vec![ToolContent::Text { text: response_text }], }) } } pub struct NotionRepoResourceHandler { github_client: GitHubClient, } impl NotionRepoResourceHandler { pub fn new() -> Self { Self { github_client: GitHubClient::new_default(), } } pub fn new_with_client(github_client: GitHubClient) -> Self { Self { github_client } } } #[async_trait::async_trait] impl ResourceHandler for NotionRepoResourceHandler { async fn read(&self, uri: &str) -> Result<ReadResourceResult> { if uri == mcp_constants::RESOURCE_REPO_INFO { let sha = self.github_client.get_latest_commit_sha().await?; let info = utils::format_repository_info( github_constants::DEFAULT_OWNER, github_constants::DEFAULT_REPO, &sha ); Ok(ReadResourceResult { contents: vec![ResourceContent::Text { uri: uri.to_string(), text: info, }], }) } else { Err(anyhow::anyhow!("{}: {}", errors::UNKNOWN_RESOURCE_URI, uri)) } } } #[cfg(test)] mod tests { use super::*; use crate::github::{GitHubFile, GitHubContent}; use wiremock::{MockServer, Mock, ResponseTemplate}; use wiremock::matchers::{method, path, header}; use base64::Engine; async fn create_mock_github_client() -> (MockServer, GitHubClient) { let mock_server = MockServer::start().await; let client = reqwest::Client::builder() .build() .unwrap(); let github_client = GitHubClient::new_with_client( "test-owner".to_string(), "test-repo".to_string(), client, ); (mock_server, github_client) } #[tokio::test] async fn test_list_files_handler() { let (mock_server, github_client) = create_mock_github_client().await; let mock_response = serde_json::json!([ { "name": "README.md", "path": "README.md", "sha": "abc123def", "type": "file", "size": 100, "download_url": "https://example.com/file" } ]); Mock::given(method("GET")) .and(path("/repos/test-owner/test-repo/contents/")) .and(header("User-Agent", "get-my-notion-mcp")) .respond_with(ResponseTemplate::new(200).set_body_json(&mock_response)) .mount(&mock_server) .await; let handler = ListFilesHandler::new_with_client(github_client); // Simulate the API call directly let url = format!("{}/repos/test-owner/test-repo/contents/", mock_server.uri()); let response = handler.github_client.client .get(&url) .header("User-Agent", "get-my-notion-mcp") .send() .await .unwrap(); let files: Vec<GitHubFile> = response.json().await.unwrap(); assert_eq!(files.len(), 1); assert_eq!(files[0].name, "README.md"); } #[tokio::test] async fn test_get_file_content_handler() { let (mock_server, github_client) = create_mock_github_client().await; let content = "Hello, World!"; let encoded_content = base64::engine::general_purpose::STANDARD.encode(content); let mock_response = serde_json::json!({ "name": "test.txt", "path": "test.txt", "sha": "abc123", "size": 13, "content": encoded_content, "encoding": "base64" }); Mock::given(method("GET")) .and(path("/repos/test-owner/test-repo/contents/test.txt")) .and(header("User-Agent", "get-my-notion-mcp")) .respond_with(ResponseTemplate::new(200).set_body_json(&mock_response)) .mount(&mock_server) .await; let handler = GetFileContentHandler::new_with_client(github_client); let mut _args = HashMap::new(); _args.insert("path".to_string(), serde_json::Value::String("test.txt".to_string())); // Test that the handler structure exists (we can't easily test the full flow without real runtime) // let result_no_path = handler.call(None); // assert!(result_no_path.is_err()); // Test with valid arguments (simulate the API call directly) let url = format!("{}/repos/test-owner/test-repo/contents/test.txt", mock_server.uri()); let response = handler.github_client.client .get(&url) .header("User-Agent", "get-my-notion-mcp") .send() .await .unwrap(); let file_content: GitHubContent = response.json().await.unwrap(); assert_eq!(file_content.name, "test.txt"); assert_eq!(file_content.encoding, "base64"); } #[tokio::test] async fn test_get_latest_commit_handler() { let (mock_server, github_client) = create_mock_github_client().await; let mock_response = serde_json::json!({ "sha": "abc123def456", "commit": { "message": "Test commit" } }); Mock::given(method("GET")) .and(path("/repos/test-owner/test-repo/commits/main")) .and(header("User-Agent", "get-my-notion-mcp")) .respond_with(ResponseTemplate::new(200).set_body_json(&mock_response)) .mount(&mock_server) .await; let handler = GetLatestCommitHandler::new_with_client(github_client); let url = format!("{}/repos/test-owner/test-repo/commits/main", mock_server.uri()); let response = handler.github_client.client .get(&url) .header("User-Agent", "get-my-notion-mcp") .send() .await .unwrap(); let commit: serde_json::Value = response.json().await.unwrap(); let sha = commit["sha"].as_str().unwrap(); assert_eq!(sha, "abc123def456"); } #[tokio::test] async fn test_notion_repo_resource_handler_invalid_uri() { let handler = NotionRepoResourceHandler::new(); let result = handler.read("invalid://uri").await; assert!(result.is_err()); assert!(result.unwrap_err().to_string().contains("Unknown resource URI")); } #[test] fn test_handler_creation() { let _list_handler = ListFilesHandler::new(); let _content_handler = GetFileContentHandler::new(); let _commit_handler = GetLatestCommitHandler::new(); let _resource_handler = NotionRepoResourceHandler::new(); // Just test that they can be created without panicking assert!(true); } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ParkJong-Hun/get-my-notion-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server