class SaveRemoteAddressTool: name = 'save_remote_address' route_path = '/mcp/tools/saveRemoteAddress' backend = 'base' def __init__(self, api_client=None): self.api_client = api_client def metadata(self): return { 'name': self.name, 'description': ( '使用场景:新增地址库,或修改已有地址库的名称和启用状态。' '新增时不要传编号,必须带名称、状态,以及至少一条地址明细。' '编辑时必须传query_remote_address_list返回的编号,只修改名称和状态,不能提交明细。' '没有地址明细的地址库不能保存。' '状态用1表示启用,用0表示禁用。' '国家可以填国家二字码或中文名称。发件人国家和收件人国家至少填一个。' '结束邮编不能单独填写,必须同时有开始邮编。' '禁止使用:手填不存在的编号、导入文件、删除明细、修改已有明细。' '参数只用于工具内部调用;最终回答只能展示中文业务名称和地址库编号。' ), 'input_schema': { 'type': 'object', 'properties': { 'address_no': { 'type': 'string', 'minLength': 1, 'maxLength': 20, 'description': '要编辑的地址库编号,格式为PY加数字。新增时不要传。', }, 'remote_name': { 'type': 'string', 'minLength': 1, 'maxLength': 100, 'description': '地址库名称。', }, 'status': { 'type': 'integer', 'enum': [0, 1], 'description': '1启用,0禁用。', }, 'details': { 'type': 'array', 'maxItems': 200, 'description': '新增时的地址明细。编辑时不要传。', 'items': { 'type': 'object', 'properties': { 'shipper_country': {'type': 'string', 'maxLength': 50}, 'shipper_province': {'type': 'string', 'maxLength': 20}, 'shipper_city': {'type': 'string', 'maxLength': 20}, 'shipper_start_postcode': {'type': 'string', 'maxLength': 10}, 'shipper_end_postcode': {'type': 'string', 'maxLength': 10}, 'country': {'type': 'string', 'maxLength': 50}, 'province': {'type': 'string', 'maxLength': 20}, 'city': {'type': 'string', 'maxLength': 20}, 'start_postcode': {'type': 'string', 'maxLength': 10}, 'end_postcode': {'type': 'string', 'maxLength': 10}, }, 'additionalProperties': False, }, }, }, 'required': ['remote_name', 'status'], 'additionalProperties': False, }, } def call( self, remote_name, status, address_no=None, details=None, request_id='rq_save_remote_address', ): if self.api_client is None: raise RuntimeError('api client is required for save_remote_address') if not isinstance(remote_name, str) or not remote_name.strip() or len(remote_name.strip()) > 100: raise ValueError('remote_name is invalid') if isinstance(status, bool) or status not in (0, 1): raise ValueError('status is invalid') payload = { 'remote_name': remote_name.strip(), 'status': status, } if address_no is not None: if not isinstance(address_no, str): raise ValueError('address_no is invalid') address_no = address_no.strip() if address_no: if len(address_no) > 20: raise ValueError('address_no is invalid') payload['address_no'] = address_no if details is not None: payload['details'] = self._details(details) if payload.get('address_no') and payload.get('details'): raise ValueError('details is invalid') if not payload.get('address_no') and not payload.get('details'): raise ValueError('details is invalid') return self.api_client.call_tool( self.name, self.route_path, payload, request_id, ) @staticmethod def _details(details): if not isinstance(details, list) or not details or len(details) > 200: raise ValueError('details is invalid') fields = ( 'shipper_country', 'shipper_province', 'shipper_city', 'shipper_start_postcode', 'shipper_end_postcode', 'country', 'province', 'city', 'start_postcode', 'end_postcode', ) postcode_fields = { 'shipper_start_postcode', 'shipper_end_postcode', 'start_postcode', 'end_postcode', } rows = [] for detail in details: if not isinstance(detail, dict) or set(detail) - set(fields): raise ValueError('details is invalid') row = {} for field in fields: if field not in detail: continue value = detail[field] if not isinstance(value, str): raise ValueError('details is invalid') value = value.strip() limit = 10 if field in postcode_fields else (50 if field in ('shipper_country', 'country') else 20) if len(value) > limit: raise ValueError('details is invalid') if field in postcode_fields and value and not _postcode(value): raise ValueError('details is invalid') if value: row[field] = value if not row.get('shipper_country') and not row.get('country'): raise ValueError('details is invalid') if row.get('shipper_end_postcode') and not row.get('shipper_start_postcode'): raise ValueError('details is invalid') if row.get('end_postcode') and not row.get('start_postcode'): raise ValueError('details is invalid') rows.append(row) return rows def _postcode(value): for character in value: if ('0' <= character <= '9') or ('A' <= character <= 'Z') or ('a' <= character <= 'z') or character in ' -': continue return False return True