Skip to main content
Glama
czangyeob
by czangyeob

mcp_anonymize_text

Anonymize text by removing or encrypting personally identifiable information (PII) using GPT-4o detection and cryptographic methods to protect sensitive data.

Instructions

MCP Tool: 텍스트 익명화 Args: text (str): 원본 텍스트 pii_items (List[Dict[str, Any]]): PII 항목들 Returns: str: 익명화된 텍스트

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
pii_itemsYes
textYes

Implementation Reference

  • The handler function for the 'mcp_anonymize_text' MCP tool. It is decorated with @mcp.tool() for automatic registration and executes the anonymization logic by converting pii_items to PIIItem objects and calling the detector's anonymize_text method.
    @mcp.tool() def mcp_anonymize_text(text: str, pii_items: List[Dict[str, Any]]) -> str: """ MCP Tool: 텍스트 익명화 Args: text (str): 원본 텍스트 pii_items (List[Dict[str, Any]]): PII 항목들 Returns: str: 익명화된 텍스트 """ detector = get_detector() pii_objects = [PIIItem(**item) for item in pii_items] return detector.anonymize_text(text, pii_objects)
  • The input schema definition for the anonymize_text tool (corresponding to mcp_anonymize_text handler), specifying parameters 'text' and 'pii_items' with types and descriptions.
    "anonymize_text": { "name": "anonymize_text", "description": "PII(개인 정보) 항목들을 사용하여 텍스트를 익명화합니다.", "parameters": { "type": "object", "properties": { "text": { "type": "string", "description": "원본 텍스트" }, "pii_items": { "type": "array", "items": { "type": "object", "properties": { "type": {"type": "string"}, "value": {"type": "string"}, "confidence": {"type": "number"}, "start_pos": {"type": "number"}, "end_pos": {"type": "number"} } }, "description": "PII 항목들" } }, "required": ["text", "pii_items"] } },
  • The core helper method in MCPPIIDetector class that performs the actual text anonymization by replacing detected PII items with placeholders like [이름], [전화번호], etc., with special logic for addresses.
    def anonymize_text(self, text: str, pii_items: List[PIIItem]) -> str: """텍스트에서 PII를 익명화 처리""" if not pii_items: return text logger.info(f"익명화 시작: 원본 텍스트 길이={len(text)}") logger.info(f"익명화할 PII 항목 수: {len(pii_items)}") # PII 항목들을 위치별로 정렬하고 중복 제거 valid_items = [] for item in pii_items: if item.start_pos != -1 and item.end_pos != -1 and item.start_pos < len(text): # 실제 텍스트에서 해당 위치의 내용이 일치하는지 확인 actual_text = text[item.start_pos:item.end_pos] if actual_text == item.value: valid_items.append(item) logger.info(f"유효한 PII: '{item.value}' ({item.type}) at {item.start_pos}-{item.end_pos}") else: # 주소의 경우 위치 불일치를 무시하고 유효한 것으로 처리 if item.type == "주소": valid_items.append(item) logger.info(f"주소 위치 불일치 무시: '{item.value}' ({item.type}) at {item.start_pos}-{item.end_pos}") else: logger.warning(f"위치 불일치: 예상='{item.value}', 실제='{actual_text}' at {item.start_pos}-{item.end_pos}") else: # 주소의 경우 위치가 유효하지 않아도 처리 if item.type == "주소": valid_items.append(item) logger.info(f"주소 위치 무효 무시: '{item.value}' ({item.type}) at {item.start_pos}-{item.end_pos}") else: logger.warning(f"유효하지 않은 위치: '{item.value}' ({item.type}) at {item.start_pos}-{item.end_pos}") # 뒤에서부터 치환 (인덱스 변화 방지) sorted_items = sorted(valid_items, key=lambda x: x.start_pos, reverse=True) anonymized_text = text # 주소의 경우 특별 처리: langextract 위치 정보 무시하고 직접 찾기 address_items = [item for item in sorted_items if item.type == "주소"] other_items = [item for item in sorted_items if item.type != "주소"] # 주소 먼저 처리 for item in address_items: logger.info(f"주소 특별 처리: '{item.value}' ({item.type})") # PII 유형에 따른 익명화 anonymized_value = "[주소]" # 직접 텍스트에서 찾기 if item.value in anonymized_text: anonymized_text = anonymized_text.replace(item.value, anonymized_value, 1) logger.info(f"주소 직접 매치 익명화 완료: '{item.value}' -> '{anonymized_value}'") else: # 부분 매치 시도 keywords = [word for word in item.value.split() if len(word) > 1] for keyword in reversed(keywords): # 뒤에서부터 시도 if keyword in anonymized_text: anonymized_text = anonymized_text.replace(keyword, anonymized_value, 1) logger.info(f"주소 키워드 매치 익명화 완료: '{keyword}' -> '{anonymized_value}' (원본: '{item.value}')") break else: logger.warning(f"주소 '{item.value}'를 찾을 수 없음") # 나머지 PII 처리 for item in other_items: logger.info(f"익명화 처리 중: '{item.value}' ({item.type}) at {item.start_pos}-{item.end_pos}") # PII 유형에 따른 익명화 if item.type == "이름": anonymized_value = "[이름]" elif item.type == "전화번호": anonymized_value = "[전화번호]" elif item.type == "이메일": anonymized_value = "[이메일]" elif item.type == "주소": anonymized_value = "[주소]" elif item.type == "여권번호": anonymized_value = "[여권번호]" else: anonymized_value = f"[{item.type}]" # 텍스트에서 치환 (강화된 문자열 치환 사용) try: # 현재 텍스트에서 해당 값이 있는지 확인 if item.value in anonymized_text: # 문자열 치환 수행 anonymized_text = anonymized_text.replace(item.value, anonymized_value, 1) # 첫 번째 매치만 치환 logger.info(f"익명화 완료: '{item.value}' -> '{anonymized_value}'") else: # 대소문자 무시하고 찾기 import re pattern = re.escape(item.value) match = re.search(pattern, anonymized_text, re.IGNORECASE) if match: start, end = match.span() anonymized_text = anonymized_text[:start] + anonymized_value + anonymized_text[end:] logger.info(f"대소문자 무시 익명화 완료: '{item.value}' -> '{anonymized_value}'") else: # 부분 매치 시도 (주소의 경우) if item.type == "주소" and len(item.value) > 3: # 주소의 마지막 부분으로 찾기 last_part = item.value.split()[-1] if ' ' in item.value else item.value[-3:] if last_part in anonymized_text: anonymized_text = anonymized_text.replace(last_part, anonymized_value, 1) logger.info(f"부분 매치 익명화 완료: '{last_part}' -> '{anonymized_value}' (원본: '{item.value}')") else: # 더 강력한 주소 매칭: 정규식으로 찾기 import re # 주소 패턴을 정규식으로 변환 address_pattern = re.escape(item.value).replace(r'\ ', r'\s+') match = re.search(address_pattern, anonymized_text, re.IGNORECASE) if match: start, end = match.span() anonymized_text = anonymized_text[:start] + anonymized_value + anonymized_text[end:] logger.info(f"정규식 매치 익명화 완료: '{item.value}' -> '{anonymized_value}'") else: # 마지막 시도: 주소의 핵심 키워드로 찾기 keywords = [word for word in item.value.split() if len(word) > 1] for keyword in reversed(keywords): # 뒤에서부터 시도 if keyword in anonymized_text: anonymized_text = anonymized_text.replace(keyword, anonymized_value, 1) logger.info(f"키워드 매치 익명화 완료: '{keyword}' -> '{anonymized_value}' (원본: '{item.value}')") break else: logger.warning(f"텍스트에서 '{item.value}'를 찾을 수 없음") else: logger.warning(f"텍스트에서 '{item.value}'를 찾을 수 없음") except Exception as e: logger.error(f"익명화 실패: {e}, item: {item}") logger.info(f"익명화 완료: 결과 텍스트 길이={len(anonymized_text)}") return anonymized_text
  • The @mcp.tool() decorator on the handler function registers 'mcp_anonymize_text' as an MCP tool.
    @mcp.tool()

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/czangyeob/mcp-pii-tools'

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