LangChain / LangGraph / AutoGen — OpenAPI remote tool
ainote 의 OpenAPI 3.1 mirror 를 Python 에이전트 프레임워크에서 remote tool 로 등록한다. 별도 어댑터 코드 없이 openapi.json URL 만 있으면 된다.
LangChain
from langchain_community.tools.openapi.utils.openapi_utils import OpenAPISpec
from langchain.agents.agent_toolkits.openapi.toolkit import RequestsToolkit
from langchain.utilities.requests import RequestsWrapper
spec = OpenAPISpec.from_url("https://api.ainote.dev/api/mcp/openapi.json")
headers = {"Authorization": "McpKey <YOUR_MCP_KEY>"}
toolkit = RequestsToolkit(
requests_wrapper=RequestsWrapper(headers=headers),
allow_dangerous_requests=True, # destructive 도구 (delete_task 등) 허용 시
)
# agent 에서 사용
from langchain.agents import create_openai_tools_agent
agent = create_openai_tools_agent(llm, toolkit.get_tools(), prompt)destructive 도구
tool annotations.destructiveHint: true 인 도구 (delete_task, vault_sync, handoff_list 등) 는 allow_dangerous_requests=True 가 필수. production 환경에서는 explicit user consent 흐름을 추가 권장.
LangGraph (직접 tool wrapping)
LangGraph 에서 fine-grained control 이 필요하면 도구를 직접 wrap:
import requests
from langchain.tools import tool
AINOTE = "https://api.ainote.dev/api/mcp/tools"
HEADERS = {"Authorization": "McpKey <YOUR_MCP_KEY>"}
@tool
def handoff_save(project: str, topic: str, content: str, time: str = None) -> str:
"""Save a session handoff note. Use `time` (HHMM, KST) to disambiguate
multiple handoffs on the same day."""
body = {"project": project, "topic": topic, "content": content}
if time:
body["time"] = time
r = requests.post(f"{AINOTE}/handoff_save", json=body, headers=HEADERS)
return r.json()
@tool
def handoff_get(project: str, topic: str, date: str = None, time: str = None) -> str:
"""Retrieve a handoff. Omit date for latest matching."""
body = {"project": project, "topic": topic}
if date: body["date"] = date
if time: body["time"] = time
r = requests.post(f"{AINOTE}/handoff_get", json=body, headers=HEADERS)
return r.json()26 도구 전체를 미리 wrap 한 패키지를 contributing 환영 — langchain-ainote 또는 ainote-langchain 이름으로 PR 받습니다.
AutoGen
import autogen
ainote_tool_spec = {
"url": "https://api.ainote.dev/api/mcp/openapi.json",
"auth": {"type": "header", "name": "Authorization", "value": "McpKey <YOUR_KEY>"},
}
assistant = autogen.AssistantAgent(
name="ainote_assistant",
llm_config={"tools": [ainote_tool_spec]},
)(AutoGen 의 정확한 tool spec 형식은 라이브러리 버전에 따라 다름 — 최신 docs 참조)
CrewAI
CrewAI 는 LangChain 호환이므로 위 LangChain 패턴 그대로 사용:
from crewai import Agent
from langchain_community.tools.openapi.utils.openapi_utils import OpenAPISpec
agent = Agent(
role="Note keeper",
goal="Manage user's notes and tasks via ainote",
tools=[/* LangChain toolkit 결과 */],
)도구 발견 (tools/list 등가물)
OpenAPI spec 의 paths 를 enumerate:
import requests
spec = requests.get("https://api.ainote.dev/api/mcp/openapi.json").json()
for path, ops in spec["paths"].items():
op = ops["post"]
name = path.split("/")[-1]
annotations = op.get("x-mcp-annotations", {})
print(f"{name}: {op['summary']}")
print(f" annotations: {annotations}")26개 도구 + 각 annotations 출력.
안티패턴 — 피할 것
❌ annotations 무시하고 모든 도구 자동 호출 — destructive 도구 (delete_task, vault_sync 등) 가 user consent 없이 실행되면 데이터 손실 가능
❌ API key 하드코딩 — env var (AINOTE_API_KEY) 또는 secret manager 사용
❌ rate limit 무시 — 짧은 시간 안 다수 호출 → 429. response 의 Retry-After 헤더 존중 (Phase 3 후 정식 도입 예정)
✅ annotations 기반 게이팅 — destructive 도구는 user confirmation 후만 호출
✅ idempotent 도구는 retry 안전 — 네트워크 오류 시 재시도해도 안전
다른 진영
- Claude Code — MCP HTTP 직접
- Claude Desktop — stdio
- OpenAI Custom GPT — Actions