← 블로그 목록

MCP 전화 tool 설계 — create_call / control_call / send_message 패턴 정리

MCP 서버에서 전화 기능을 노출할 때 어떤 tool 을 어떻게 쪼개야 LLM 이 제대로 쓰는지. ClawOps MCP / SDK 기반 실전 patterns.

MCP 전화 tool 설계 — create_call / control_call / send_message 패턴 정리

MCP 서버에 전화 기능을 노출할 때 tool 을 어떻게 쪼개느냐로 LLM 사용성이 갈린다. 너무 잘게 쪼개면 LLM 이 순서 헷갈리고, 너무 묶으면 통제 못 함.

좋은 소식: ClawOps 는 이 tool 셋을 원격 HTTP MCP 서버로 이미 제공한다. 직접 MCP 서버를 짤 필요 없이 URL 한 줄(https://api.claw-ops.com/mcp, 인증은 OAuth)만 등록하면 아래 tool 들이 그대로 노출된다. 이 글은 (1) ClawOps MCP 가 실제로 어떤 tool 을 어떤 모델로 노출하는지, (2) 직접 MCP 서버를 짤 때 같은 패턴을 어떻게 따라야 하는지를 정리한다.

ClawOps MCP 가 노출하는 전화 관련 tool

tool역할호출 시점
create_call전화 발신 (To/From/Url/AI)발신이 필요한 모든 시나리오
control_call진행 중 통화 제어 (현재 hangup)통화 종료
send_messageSMS/LMS/MMS 발송문자 안내·링크 전달
get_call_summary끝난 통화 요약 조회결과 보고
get_transcript통화 전사 조회상세 확인

핵심: 발신은 항상 create_call 하나다. "알림성 단방향"과 "대화형"을 별도 tool 로 쪼개지 않는다. 차이는 tool 이 아니라 어떤 모드로 거느냐에서 갈린다.

  • Url 을 지정하면 → 그 URL 이 반환하는 VoiceML 시나리오대로 통화 진행
  • AgentId 를 지정하면 → 콘솔에서 만든 매니지드 에이전트가 통화를 처리. CallContext 로 이번 통화만의 지시를 덧붙일 수 있다
  • CallFlowId 를 지정하면 → 콜 플로우(결정적 ARS)가 통화를 진행. Variables 로 시작 변수를 넘긴다
  • 셋 다 생략하면 → From 번호에 연결된 Agent SDK(실시간 대화) 모드로 통화 진행

세 필드는 서로 배타적이다.

1. 직접 MCP 서버를 짤 때 — create_call 래핑

ClawOps MCP 를 그대로 쓰지 않고, 자기 도메인 로직을 끼워 직접 MCP 서버를 만들 수도 있다. 이때도 발신 tool 은 하나로 유지하고 내부에서 ClawOps SDK client.calls.create 를 호출한다.

DEFAULT_FROM = "07012345678"  # 발급받은 070 번호

@server.tool()
def create_call(to: str, scenario_url: str | None = None) -> dict:
    """전화 발신.
    - scenario_url 지정: 해당 VoiceML 시나리오대로 통화 진행
    - scenario_url 생략: From 번호에 연결된 Agent(실시간 대화) 모드
    """
    call = client.calls.create(
        to=to,
        from_=DEFAULT_FROM,
        url=scenario_url,  # None 이면 Agent 모드
    )
    return {"call_id": call.call_id, "status": call.status}

LLM 이 헷갈리지 않게: "대화형이냐 알림이냐"를 tool 이름이 아니라 scenario_url 유무로 표현한다고 description 에 명시. 시스템 프롬프트 작성을 LLM 한테 떠넘기지 말 것 — 시나리오는 scenario_url 이 가리키는 서버에서 고정한다.

매니지드 에이전트로 발신

콘솔에서 만든 AI 에이전트에게 통화를 맡길 수도 있다. agent_idurl 과 동시에 쓸 수 없다.

@server.tool()
def create_ai_call(to: str, instruction: str) -> dict:
    """AI 가 instruction 대로 응대하는 통화. url 과 동시 사용 불가."""
    call = client.calls.create(
        to=to,
        from_=DEFAULT_FROM,
        agent_id=DEFAULT_AGENT_ID,
        call_context={"instruction": instruction},
    )
    return {"call_id": call.call_id}

에이전트의 말투·지식·전환 대상은 콘솔에 설정해 두고, 이번 통화에서만 다르게 할 내용call_context 로 넘기는 구조다. 같은 에이전트로 동시에 거는 다른 통화에는 영향이 없다.

2. control_call — 통화 종료

진행 중 통화 제어는 control_call 이다. 현재 지원하는 동작은 Status="completed"(hangup). idempotent 하다.

@server.tool()
def control_call(call_id: str) -> dict:
    """진행 중 통화 종료 (Status='completed')."""
    client.calls.update(call_id, status="completed")
    return {"call_id": call_id, "status": "completed"}

참고: DTMF 전송이나 통화 전환(transfer) 같은 동작은 별도 MCP tool 로 제공되지 않는다. 통화 안에서의 흐름 제어(목적 달성 후 종료 등)는 Agent 모드의 system prompt / VoiceML 시나리오에서 처리하고, 외부에서의 강제 종료만 control_call 로 한다.

3. send_message — 문자 안내

통화 중/후에 링크·주소·기사 연락처 등을 문자로 보내는 패턴.

@server.tool()
def send_message(to: str, body: str) -> dict:
    """SMS/LMS/MMS 발송. 본문 길이에 따라 자동 분류."""
    msg = client.messages.create(to=to, from_=DEFAULT_FROM, body=body)
    return {"message_id": msg.message_id}

4. get_call_summary / get_transcript — 결과 조회

끝난 통화의 요약·전사는 별도 tool 로 조회한다. 둘 다 비동기 처리라 statuscompleted 가 될 때까지 폴링할 수 있다.

@server.tool()
def get_call_summary(call_id: str) -> dict:
    """통화 요약 조회. 아직 처리 중이면 status='pending'."""
    s = client.calls.get_summary(call_id)
    return {"status": s.status, "summary": getattr(s, "result_json", None)}

@server.tool()
def get_transcript(call_id: str) -> dict:
    """통화 전사 조회."""
    t = client.calls.get_transcript(call_id)
    return {
        "status": t.status,
        "segments": [
            {"speaker": seg.speaker, "text": seg.text}
            for seg in (t.segments or [])
        ],
    }

5. 안 권장 — 묶지 말 것

  • make_call_and_get_summary (발신+대기+결과 묶기) — 통화 시간 변동이 큰데 동기 호출이라 timeout 위험. 발신(create_call)과 결과 조회(get_call_summary)는 분리.
  • 알림용/대화용 발신 tool 을 따로 만들기 — 발신은 create_call 하나, 모드는 Url/AI 로 표현.
  • start_recording/stop_recording 별도 tool — ClawOps 는 기본 자동 녹음이라 불필요.

6. tool description 작성 팁 (LLM 한테 잘 쓰이게)

  • 한 문장 안에 언제 쓰는지 명시 ("발신용", "통화 종료용")
  • 파라미터 한국어 예시 ("010-1234-5678 형식")
  • 부작용 명시 ("통화 종료 즉시 호출")
  • 다른 tool 과의 관계 명시 ("결과는 get_call_summary 로 조회")

LLM 은 description 만 보고 어떤 tool 을 쓸지 결정. 모호하면 wrong tool 선택.

7. 작동 원리

ClawOps MCP 를 그대로 쓰는 경우:

LLM (Claude Code / Cursor 등)
  ↓ MCP tool call (HTTP / OAuth)
api.claw-ops.com/mcp (원격 MCP 서버)
  ↓
한국 070 → 대상 휴대폰

직접 MCP 서버를 짜는 경우:

LLM
  ↓ MCP tool call
당신의 MCP 서버 (create_call / control_call ...)
  ↓ ClawOps SDK (client.calls.create ...)
api.claw-ops.com
  ↓
한국 070 → 대상 휴대폰

다음 단계

이전 글

부동산 AI 매물 문의 응대 만들기 — 24시간 매물 안내 + 내방 예약

ClawOps AI 전화 API로 시작하기

070 번호 발급부터 AI 음성 통화까지, REST API 몇 줄이면 됩니다.