Skip to main content
Glama
ParkJong-Hun

Get My Notion MCP Server

by ParkJong-Hun
github.rs8.97 kB
use crate::constants::github as constants; use crate::utils; use anyhow::{anyhow, Result}; use serde::{Deserialize, Serialize}; use base64::Engine; #[derive(Debug, Serialize, Deserialize)] pub struct GitHubFile { pub name: String, pub path: String, pub sha: String, #[serde(rename = "type")] pub file_type: String, pub size: Option<u64>, pub download_url: Option<String>, } #[derive(Debug, Serialize, Deserialize)] pub struct GitHubContent { pub name: String, pub path: String, pub sha: String, pub size: u64, pub content: String, pub encoding: String, } pub struct GitHubClient { pub client: reqwest::Client, owner: String, repo: String, } impl GitHubClient { pub fn new(owner: String, repo: String) -> Self { Self { client: reqwest::Client::new(), owner, repo, } } pub fn new_default() -> Self { Self::new(constants::DEFAULT_OWNER.to_string(), constants::DEFAULT_REPO.to_string()) } pub fn new_with_client(owner: String, repo: String, client: reqwest::Client) -> Self { Self { client, owner, repo, } } pub async fn list_files(&self, path: Option<&str>) -> Result<Vec<GitHubFile>> { let path = path.unwrap_or(""); let url = utils::build_github_contents_url(&self.owner, &self.repo, path); let (header_name, header_value) = utils::get_user_agent_header(); let response = self .client .get(&url) .header(header_name, header_value) .send() .await?; if !response.status().is_success() { return Err(anyhow!( "{}: {}", crate::constants::errors::GITHUB_API_FAILED, response.status() )); } let files: Vec<GitHubFile> = response.json().await?; Ok(files) } pub async fn get_file_content(&self, path: &str) -> Result<String> { let url = utils::build_github_contents_url(&self.owner, &self.repo, path); let (header_name, header_value) = utils::get_user_agent_header(); let response = self .client .get(&url) .header(header_name, header_value) .send() .await?; if !response.status().is_success() { return Err(anyhow!( "{}: {}", crate::constants::errors::GITHUB_API_FAILED, response.status() )); } let content: GitHubContent = response.json().await?; if content.encoding == constants::BASE64_ENCODING { let decoded = base64::engine::general_purpose::STANDARD .decode(&content.content.replace('\n', ""))?; Ok(String::from_utf8(decoded)?) } else { Ok(content.content) } } pub async fn get_latest_commit_sha(&self) -> Result<String> { let url = utils::build_github_commits_url(&self.owner, &self.repo, constants::DEFAULT_BRANCH); let (header_name, header_value) = utils::get_user_agent_header(); let response = self .client .get(&url) .header(header_name, header_value) .send() .await?; if !response.status().is_success() { return Err(anyhow!( "{}: {}", crate::constants::errors::GITHUB_API_FAILED, response.status() )); } let commit: serde_json::Value = response.json().await?; let sha = commit["sha"] .as_str() .ok_or_else(|| anyhow!(crate::constants::errors::COMMIT_SHA_EXTRACT_FAILED))?; Ok(sha.to_string()) } } #[cfg(test)] mod tests { use super::*; use wiremock::{MockServer, Mock, ResponseTemplate}; use wiremock::matchers::{method, path, header}; #[tokio::test] async fn test_list_files_success() { let mock_server = MockServer::start().await; let mock_response = serde_json::json!([ { "name": "README.md", "path": "README.md", "sha": "abc123", "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 client = reqwest::Client::builder() .build() .unwrap(); let github_client = GitHubClient::new_with_client( "test-owner".to_string(), "test-repo".to_string(), client, ); // Override the base URL to use mock server let url = format!("{}/repos/test-owner/test-repo/contents/", mock_server.uri()); let response = 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"); assert_eq!(files[0].file_type, "file"); } #[tokio::test] async fn test_get_file_content_base64() { let mock_server = MockServer::start().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 client = reqwest::Client::builder() .build() .unwrap(); let github_client = GitHubClient::new_with_client( "test-owner".to_string(), "test-repo".to_string(), client, ); // Test the decoding logic directly let url = format!("{}/repos/test-owner/test-repo/contents/test.txt", mock_server.uri()); let response = github_client.client .get(&url) .header("User-Agent", "get-my-notion-mcp") .send() .await .unwrap(); let file_content: GitHubContent = response.json().await.unwrap(); if file_content.encoding == "base64" { let decoded = base64::engine::general_purpose::STANDARD .decode(&file_content.content.replace('\n', "")) .unwrap(); let decoded_string = String::from_utf8(decoded).unwrap(); assert_eq!(decoded_string, "Hello, World!"); } } #[tokio::test] async fn test_get_latest_commit_sha() { let mock_server = MockServer::start().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 client = reqwest::Client::builder() .build() .unwrap(); let github_client = GitHubClient::new_with_client( "test-owner".to_string(), "test-repo".to_string(), client, ); // Test the commit SHA extraction let url = format!("{}/repos/test-owner/test-repo/commits/main", mock_server.uri()); let response = 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"); } #[test] fn test_github_client_creation() { let client = GitHubClient::new("owner".to_string(), "repo".to_string()); assert_eq!(client.owner, "owner"); assert_eq!(client.repo, "repo"); } }

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