Skip to main content
Glama

DynamoDB Scanner

by katpyeon
app.py10.5 kB
from fastmcp import FastMCP import boto3 from boto3.dynamodb.conditions import Attr from typing import Optional, Dict import os # MCP 서버 인스턴스 생성 mcp = FastMCP( name="DynamoDB Scanner", description=( "This MCP server allows scanning a fixed DynamoDB table. " "It supports filtering and pagination, but only supports scan operations.\n" "The table name, AWS access key, and secret key must be provided at runtime via environment variables:\n" "- DYNAMO_TABLE_NAME\n" "- AWS_ACCESS_KEY_ID\n" "- AWS_SECRET_ACCESS_KEY\n" "- AWS_REGION (default: ap-northeast-2)" ) ) # 환경 변수에서 구성 정보 로드 table_name = os.environ["DYNAMO_TABLE_NAME"] region_name = os.environ.get("AWS_REGION", "ap-northeast-2") # boto3 세션 및 테이블 객체 생성 session = boto3.Session() dynamodb = session.resource("dynamodb", region_name=region_name) table = dynamodb.Table(table_name) @mcp.tool() def describe_table_schema() -> dict: """ Return the list of available columns in the DynamoDB table with descriptions. Useful for building filter conditions or understanding the data structure. """ return { "columns": { "PK": "기본 파티션 키 (Primary Key)", "SK": "정렬 키 (Sort Key)", "accountChannelCode": "가입 채널 코드", "accountLoginId": "계정 로그인 ID", "accountLoginPassword": "계정 로그인 비밀번호", "accountNewsSubscription": "뉴스레터 수신 동의 여부", "accountRegistrationSource": "회원가입 출처", "accountRegistrationType": "회원가입 방식 (이메일, 간편로그인)", "activatedAt": "계정 활성화 일시", "agreeTerm": "약관 동의 여부", "authProvider": "외부 인증 제공자 (예: Google, Facebook)", "companyCode": "회사 코드", "companyDepartment": "회사 부서", "companyEmployeeNumber": "사번", "companyName": "회사명", "companySK": "회사 세부 식별자 (Sort Key)", "createdAt": "생성 일시", "dataType": "데이터 유형", "email": "이메일 주소", "engName": "영문 이름", "engNation": "영문 국적", "estimate": "견적 금액", "estimateFile": "견적서 파일 참조", "finishAt": "서비스 종료 일시", "gender": "성별", "groupKey": "그룹 식별 키", "groupName": "그룹 이름", "idNumber": "신분증 번호 또는 주민등록번호", "insuranceCertificateUrl": "보험 증명서 URL", "insuranceEndDate": "보험 종료일", "insuranceEvacuationPlan": "보험 포함 대피 계획", "insurancePlan": "보험 플랜 종류", "insuranceStartDate": "보험 시작일", "linkPaperGuide": "종이 신청서 가이드 링크", "linkPaperJoin": "종이 신청서 접수 링크", "linkPaperJoinEng": "종이 신청서 접수 링크 (영문)", "managerEmail": "담당자 이메일", "managerName": "담당자 이름", "managerTel": "담당자 전화번호", "membershipCertificateUrl": "기업멤버십 가입증명서 URL", "membershipCorporateType": "기업멤버십 유형", "membershipEndDate": "기업멤버십 종료일", "membershipPersonalType": "기업멤버십 개인 유형", "membershipStartDate": "기업멤버십 시작일", "name": "이름", "nation": "국가", "paidAt": "결제 일시", "paidPrice": "결제 금액", "period": "이용 기간", "policyNumber": "보험 증권 번호", "price": "기본 금액", "productName": "상품명", "productSk": "상품 세부 식별자 (SK)", "receipts": "영수증 목록 또는 파일 참조", "registrationCount": "등록 횟수", "registrationNumber": "등록 번호", "residenceBusinessTripType": "체류 유형 (거주/출장)", "residenceCityCode": "거주 도시 코드", "residenceCountryCode": "거주 국가 코드", "residenceCountryName": "거주 국가명", "residenceEndDate": "거주 종료일", "residenceStartDate": "거주 시작일", "residenceStayType": "거주 형태 (단기/장기 등)", "startAt": "시작 일시", "status": "상태 (예: 활성, 만료)", "tel": "전화번호", "timestamp": "타임스탬프 (Unix epoch)", "totalPrice": "총 금액", "trainingInfo": "교육 정보", "updatedAt": "수정 일시", "userBirthDate": "사용자 생년월일", "userEmail": "사용자 이메일 주소", "userEmergencyContact": "긴급 연락처", "userEnglishName": "사용자 영문 이름", "userGender": "사용자 성별", "userName": "사용자 실명", "userNote": "사용자 메모", "userPhone": "사용자 전화번호", "userRegistrationNumber": "사용자 등록번호 또는 신분증 번호", "userRelation": "신청자와의 관계 (본인, 배우자 등)", "userType": "사용자 유형 (관리자, 일반, 게스트 등)" } } @mcp.tool() def scan_table(filters: Optional[Dict[str, Dict[str, str]]] = None, start_key: Optional[dict] = None, limit: Optional[int] = 10) -> dict: """ Scan the DynamoDB table with optional filters and start_key for pagination. Returns up to the specified limit of items per call after filtering. Parameters: - filters: Dictionary of attribute conditions using the format: { "attributeName": {"operator": "value"} } Supported operators: - eq: Equal to - ne: Not equal to - lt: Less than - lte: Less than or equal to - gt: Greater than - gte: Greater than or equal to - begins_with: Starts with the specified value - contains: Contains the specified value - between: Between two values - {"between": ["value1", "value2"]} - in: In a list of values - {"in": ["value1", "value2", "value3"]} - exists: Attribute exists - {"exists": true} - not_exists: Attribute does not exist - {"not_exists": true} - start_key: Start key for pagination (from lastEvaluatedKey). - limit: Maximum number of items to return (default 10). Max value is 100. Returns: - items: Array of matching items from the table. - lastEvaluatedKey: Pagination token for getting the next set of results. - count: Number of items returned in this response. - scannedCount: Total number of items scanned before filtering. Note: - If lastEvaluatedKey is present in the response, more results are available. You can retrieve these by passing this value as the start_key in a subsequent call. - Results are formatted as a table for better readability. Examples: # Equal condition - find items where name is "John" filters = {"name": {"eq": "John"}} # Multiple conditions - find items where age >= 30 AND job is "Developer" filters = { "age": {"gte": 30}, "job": {"eq": "Developer"} } # Range search - find items where age is between 20 and 30 filters = {"age": {"between": [20, 30]}} # Contains search - find items where hobbies list contains "Reading" filters = {"hobbies": {"contains": "Reading"}} """ scan_kwargs = {} # 필터 조건 구성 if filters: condition = None for key, condition_dict in filters.items(): for operator, value in condition_dict.items(): expr = None # 지원하는 연산자별 조건식 생성 if operator == 'eq': expr = Attr(key).eq(value) elif operator == 'ne': expr = Attr(key).ne(value) elif operator == 'lt': expr = Attr(key).lt(value) elif operator == 'lte': expr = Attr(key).lte(value) elif operator == 'gt': expr = Attr(key).gt(value) elif operator == 'gte': expr = Attr(key).gte(value) elif operator == 'begins_with': expr = Attr(key).begins_with(value) elif operator == 'contains': expr = Attr(key).contains(value) elif operator == 'between' and isinstance(value, list) and len(value) == 2: expr = Attr(key).between(value[0], value[1]) elif operator == 'in' and isinstance(value, list): expr = Attr(key).is_in(value) elif operator == 'exists' and value: expr = Attr(key).exists() elif operator == 'not_exists' and value: expr = Attr(key).not_exists() # 생성된 표현식을 기존 조건과 AND로 결합 if expr: condition = expr if condition is None else condition & expr # 필터 표현식 설정 if condition: scan_kwargs["FilterExpression"] = condition # 페이지네이션 키 적용 if start_key: scan_kwargs["ExclusiveStartKey"] = start_key # DynamoDB 스캔 - AWS 콘솔과 같이 모든 데이터를 스캔하고 필터링 response = table.scan(**scan_kwargs) # 사용자가 지정한 limit 값 사용 (최대 100개로 제한) actual_limit = min(limit, 100) if limit is not None else 10 # 필터링 후 결과에서 지정된 개수만 반환 items = response.get("Items", [])[:actual_limit] return { "items": items, "lastEvaluatedKey": response.get("LastEvaluatedKey"), "count": len(items), "scannedCount": response.get("ScannedCount", 0) } if __name__ == "__main__": mcp.run()

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/katpyeon/mcp_dynamodb_scan'

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