Gemini와 옵시디언 연결하기: 내 노트를 검색하고 정리하는 AI 비서 만들기
옵시디언에 독서 기록, 수업 아이디어, 업무 메모를 모아 두었다고 해봅시다. Gemini에게 이렇게 부탁할 수 있다면 편리하겠지요.
“내 노트에서 목적격 관계대명사 수업 아이디어를 찾아줘. 내용을 정리해서 새 노트로 저장해줘.”
이 글에서는 내 PC의 옵시디언과 Gemini를 연결하는 과정을 살펴봅니다. 옵시디언에 접근하는 플러그인, PC까지 요청을 전달하는 Cloudflare Tunnel, 로그인과 도구 실행을 담당하는 Cloudflare Worker를 차례로 준비합니다. 처음에는 연습용 노트로 검색과 읽기를 확인하고, 마지막에 정리한 내용을 저장합니다.
확인 기준일: 2026년 9월 13일. 공식 문서와 공개 소스에 근거해 작성한 실습 예제이며, 이 글의 수정 코드를 실제 Gemini 계정에 배포해 전체 연결을 검증한 것은 아닙니다.
시작 전에: 내 계정과 PC에서 가능한지 확인하세요
먼저 Gemini 웹사이트의 설정에서 Connected Apps → Custom apps for Spark 항목을 확인합니다. 현재 Google 공식 안내에는 커스텀 앱 연결 조건으로 Spark 접근 권한, 미국 지역, 만 18세 이상, 개인 Google 계정, 활동 기록 활성화가 명시되어 있습니다. 한국에서 모든 계정에 제공되는 기능으로 생각하면 안 됩니다. 커스텀 앱 등록은 웹에서 진행합니다. (Google 커스텀 앱 공식 안내)
해당 메뉴가 없다면 설치 명령어를 반복해도 해결되지 않습니다. 계정의 기능 제공 여부를 먼저 확인하세요. 아래 옵시디언 로컬 API 실습은 Gemini 연결과 별개로 진행할 수 있습니다.
PC 환경도 확인합니다. 최신 Wrangler의 공식 Windows 지원 기준은 Windows 11입니다. Windows 10에서는 같은 결과를 보장하기 어렵습니다. Node.js는 현재 지원되는 LTS 버전을 준비합니다. (Wrangler 설치 및 지원 환경)
이 글의 준비물은 다음과 같습니다.
| 준비물 | 사용하는 이유 |
|---|---|
| Windows 11 PC | 옵시디언과 연결 프로그램 실행 |
| 옵시디언 | 노트 보관 |
| Gemini Spark 커스텀 앱 사용 가능 계정 | 대화에서 노트 도구 호출 |
| Cloudflare 계정 | 중계 프로그램 배포 |
| GitHub 계정 | 중계 프로그램 이용자 로그인 |
| Node.js와 npm | 프로젝트 설치와 배포 |
| PowerShell | 명령어 입력 |
GitHub는 여기서 로그인 수단으로 사용합니다. 옵시디언 노트를 GitHub 저장소에 올리는 과정은 없습니다.
먼저 알아둘 용어 다섯 가지
설정을 시작하기 전에 아래 표현만 알아두면 설명이 훨씬 쉬워집니다.
| 용어 | 쉬운 설명 |
|---|---|
| 볼트 (Vault) | 옵시디언 노트가 들어 있는 폴더 |
| API | 다른 프로그램이 노트를 읽거나 저장하도록 만든 통로 |
| API 키 | 그 통로를 사용할 때 제시하는 비밀 열쇠 |
| MCP | AI가 외부 도구를 찾아 실행할 때 사용하는 통신 규칙 |
| OAuth | 로그인 화면을 통해 연결 권한을 부여하는 방식 |
이 글에서 만드는 Worker는 Gemini의 요청을 받아 옵시디언 API 호출로 바꾸는 중계 프로그램입니다. Cloudflare Tunnel은 이 중계 프로그램이 내 PC에 도달할 수 있도록 연결합니다. 따라서 Worker가 인터넷에 배포되어 있어도, PC의 옵시디언과 터널 프로그램이 꺼져 있으면 노트를 읽을 수 없습니다.
또한 원본 파일이 PC에 남아 있다는 사실과, 내용이 외부로 전송되지 않는다는 사실은 다릅니다. AI가 읽는 노트 내용은 이 연결을 거쳐 외부 서비스로 전달됩니다.
1단계. 연습용 옵시디언 볼트 만들기
처음부터 실제 업무 자료가 들어 있는 볼트를 연결하지 말고, AI-Practice라는 연습용 볼트를 만드세요.
그 안에 AI-Inbox 폴더를 만들고, hello.md라는 노트를 추가합니다.
노트에는 다음 내용을 입력합니다.
# Gemini 연결 연습
목적격 관계대명사 수업 아이디어를 정리하는 노트입니다.
- 두 문장에서 같은 목적어 찾기
- who, which, that으로 문장 연결하기
- 관계대명사를 생략할 수 있는지 확인하기
이번 실습에서는 이 노트를 읽고, 정리 결과를 AI-Inbox/result.md에 저장합니다.
경로 앞의 AI-Inbox는 볼트 안의 폴더 이름입니다. C:\Users\...로 시작하는 PC 전체 경로를 입력하는 것이 아닙니다.
2단계. Local REST API 플러그인 설치하기
옵시디언에서 다음 순서로 이동합니다.
- 왼쪽 아래 톱니바퀴를 눌러 설정을 엽니다.
- Community plugins를 선택합니다.
- 필요하면 제한 모드를 해제합니다.
- Browse에서 Local REST API를 검색합니다.
- 작성자가 Adam Coddington인 플러그인을 설치하고 활성화합니다.
공식 저장소 이름은 obsidian-local-rest-api입니다. 현재 문서는 REST API와 내장 MCP 연결을 함께 안내합니다. (플러그인 공식 저장소)
플러그인 설정에서 다음 두 가지를 확인하세요.
| 항목 | 확인할 내용 |
|---|---|
| API Key | 나중에 Worker에 등록할 비밀 키 |
| HTTPS 주소 | 기본값은 https://127.0.0.1:27124 |
- 127.0.0.1은 지금 사용하는 내 PC 자신을 뜻합니다.
- API 키는 블로그, 공개 저장소, 질문용 스크린샷에 넣지 마세요. 이 실습에서는 코드에 직접 적지 않고 Cloudflare의 Secret으로 등록합니다.
3단계. 내 PC에서 옵시디언에 접속해 보기
Windows 시작 메뉴에서 PowerShell을 실행합니다. 아래 명령어를 복사해 붙여넣고 Enter를 누릅니다.
curl.exe -k -i https://127.0.0.1:27124/
명령어의 뜻은 다음과 같습니다.
curl.exe: 주소로 요청을 보내는 프로그램-i: 응답 상태도 표시-k: 이번 요청에서 인증서 검증 생략
플러그인은 로컬 인증서를 사용하므로, 첫 연결 확인에서는 -k를 사용합니다. 장기 사용 시에는 플러그인이 제공하는 인증서를 신뢰하도록 설정하는 방법이 있습니다. (인증서와 연결 예제)
정상이라면 200 OK와 상태를 나타내는 JSON이 보입니다. 응답 모양은 버전에 따라 다를 수 있습니다.
여기서 주의할 점이 하나 있습니다. 상태 확인 성공은 노트 읽기 권한 확인과 다릅니다. 기본 상태 주소는 인증 없이도 응답할 수 있습니다. 실제 노트에 접근하려면 API 키가 필요합니다.
연결 실패가 나오면 아래 순서로 확인하세요.
- 옵시디언이 켜져 있는가?
- 연습용 볼트가 열려 있는가?
- 플러그인이 활성화되어 있는가?
- 설정의 HTTPS 포트가 27124인가?
이 단계가 성공한 뒤 다음으로 넘어갑니다.
4단계. Cloudflare Tunnel 설치하기
PowerShell에서 실행합니다.
winget install --id Cloudflare.cloudflared -e
설치가 끝나면 PowerShell을 닫고 다시 엽니다.
cloudflared --version
버전 번호가 출력되면 설치가 완료된 것입니다. 이제 첫 번째 PowerShell 창에서 다음 명령을 실행합니다.
cloudflared tunnel --url https://127.0.0.1:27124 --no-tls-verify
화면에 다음과 비슷한 주소가 나타납니다.
https://임의로-생성된-이름.trycloudflare.com
실제로 출력된 본인의 주소를 복사하세요. 다른 사람의 실습 화면에 나온 주소는 사용할 수 없습니다.
이 창은 계속 열어 둡니다. 창을 닫거나 Ctrl+C를 누르면 터널이 종료됩니다.
--no-tls-verify는 터널 프로그램이 로컬 옵시디언 인증서를 검사하지 않게 하는 옵션입니다. 이 글에서는 같은 PC의 127.0.0.1에 연결하는 연습에 한정해 사용합니다. 상시 운영에서는 caPool 등으로 인증서를 신뢰하도록 설정하는 편이 적절합니다. (Cloudflare 인증서 설정)
이번에 만든 Quick Tunnel은 임시 테스트용입니다. 다시 실행하면 주소가 달라질 수 있고, SSE도 지원하지 않습니다. 이 글에서는 터널을 통해 일반 REST 요청만 전달합니다. (Quick Tunnel 공식 안내)
5단계. 로그인 기능이 있는 중계 프로젝트 만들기
이제 두 번째 PowerShell 창을 엽니다. 첫 번째 창의 터널은 그대로 실행해 둡니다. Node.js 설치 여부부터 확인합니다.
node --version
npm.cmd --version
없다면 Node.js 공식 사이트에서 현재 지원되는 LTS 버전을 설치하고 PowerShell을 다시 여세요.
이 글에서는 PowerShell의 스크립트 실행 정책과 불필요하게 충돌하지 않도록 npm.cmd, npx.cmd를 사용합니다.
작업 폴더를 준비합니다.
New-Item -ItemType Directory -Force "$env:USERPROFILE\Projects"
Set-Location "$env:USERPROFILE\Projects"
이어서 Cloudflare의 GitHub OAuth 예제를 가져옵니다.
npm.cmd create cloudflare@latest -- obsidian-gemini-bridge --template=cloudflare/ai/demos/remote-mcp-github-oauth
설치 중 배포 여부를 물으면 아직은 No를 선택합니다. 설치가 끝나면 이동합니다.
Set-Location .\obsidian-gemini-bridge
npx.cmd wrangler login
브라우저가 열리면 본인의 Cloudflare 계정으로 로그인합니다. 이 템플릿에는 GitHub 로그인과 MCP 연결 권한 처리가 들어 있습니다. 공개 서버에 옵시디언 API 키만 붙이는 방식은 호출자 확인이 빠지므로 사용하지 않습니다. (Cloudflare OAuth 예제)
6단계. OAuth 정보를 보관할 공간 만들기
두 번째 PowerShell에서 실행합니다.
npx.cmd wrangler kv namespace create OAUTH_KV
결과에 표시되는 id 값을 복사합니다.
프로젝트 폴더의 wrangler.jsonc를 편집기로 엽니다. 메모장으로 열려면 다음 명령을 사용해도 됩니다.
notepad .\wrangler.jsonc
파일에서 name을 다음과 같이 바꿉니다.
"name": "obsidian-gemini-bridge"
OAUTH_KV 항목의 id에는 방금 생성한 값을 넣습니다.
"kv_namespaces": [
{
"binding": "OAUTH_KV",
"id": "방금 생성한 실제 ID"
}
]
여기서 보여 준 코드는 파일 전체가 아니라 수정할 부분입니다.
나머지 durable_objects, migrations, compatibility_flags 같은 항목은 유지하세요. 특히 이 템플릿의 MyMCP 클래스와 MCP_OBJECT 연결 설정이 필요합니다. (공식 템플릿 설정 파일)
7단계. 옵시디언 검색·읽기·저장 도구 넣기
프로젝트의 src/index.ts를 엽니다.
notepad .\src\index.ts
기존 내용을 아래 코드로 교체합니다.
your-github-username에는 본인의 GitHub 사용자 이름을 소문자로 입력하세요. 프로필 표시 이름이나 이메일 주소가 아닙니다.
이 코드는 세 가지 도구를 제공합니다.
| 도구 | 기능 |
|---|---|
obsidian_search |
연습용 볼트에서 검색 |
obsidian_read_note |
지정한 마크다운 노트 읽기 |
obsidian_write_result |
AI-Inbox/result.md에 결과 저장 |
저장은 경로를 고정했습니다. 같은 이름의 파일이 이미 있으면 내용을 덮어씁니다.
import OAuthProvider from "@cloudflare/workers-oauth-provider";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { McpAgent } from "agents/mcp";
import { z } from "zod";
import { GitHubHandler } from "./github-handler";
import type { Props } from "./utils";
declare global {
interface Env {
OBSIDIAN_TUNNEL_URL: string;
OBSIDIAN_API_KEY: string;
}
}
const ALLOWED_USERS = new Set([
"your-github-username",
]);
function textResult(text: string) {
return {
content: [{ type: "text" as const, text }],
};
}
export class MyMCP extends McpAgent<
Env,
Record<string, never>,
Props
> {
server = new McpServer({
name: "Obsidian Practice Bridge",
version: "1.0.0",
});
private checkUser() {
const login = this.props?.login?.toLowerCase();
if (!login || !ALLOWED_USERS.has(login)) {
throw new Error("이 계정에는 노트 접근 권한이 없습니다.");
}
}
private async api(
path: string,
options: RequestInit = {},
) {
this.checkUser();
if (
!this.env.OBSIDIAN_TUNNEL_URL ||
!this.env.OBSIDIAN_API_KEY
) {
throw new Error("옵시디언 연결 Secret을 확인하세요.");
}
const base = new URL(this.env.OBSIDIAN_TUNNEL_URL);
if (base.protocol !== "https:") {
throw new Error("터널 주소는 HTTPS여야 합니다.");
}
const headers = new Headers(options.headers);
headers.set(
"Authorization",
`Bearer ${this.env.OBSIDIAN_API_KEY}`,
);
return fetch(new URL(path, base.origin), {
...options,
headers,
redirect: "error",
signal: AbortSignal.timeout(15000),
});
}
private async run(action: () => Promise<string>) {
try {
this.checkUser();
return textResult(await action());
} catch (error) {
return {
...textResult(
error instanceof Error
? error.message
: "요청을 처리하지 못했습니다.",
),
isError: true,
};
}
}
async init() {
this.checkUser();
this.server.tool(
"obsidian_search",
"연습용 옵시디언 볼트에서 노트를 검색합니다.",
{ query: z.string().min(1).max(200) },
async ({ query }) =>
this.run(async () => {
const response = await this.api(
`/search/simple/?query=${encodeURIComponent(query)}`,
{ method: "POST" },
);
if (!response.ok) {
throw new Error(`검색 실패: HTTP ${response.status}`);
}
const results = await response.json();
return JSON.stringify(results).slice(0, 20000);
}),
);
this.server.tool(
"obsidian_read_note",
"볼트 내부 상대 경로로 마크다운 노트를 읽습니다.",
{ path: z.string().min(1).max(300) },
async ({ path }) =>
this.run(async () => {
const parts = path.split("/");
if (
!path.endsWith(".md") ||
path.includes("\\") ||
parts.some(
(part) => !part || part.startsWith("."),
)
) {
throw new Error(
"AI-Inbox/hello.md처럼 상대 경로를 입력하세요.",
);
}
const encoded = parts.map(encodeURIComponent).join("/");
const response = await this.api(`/vault/${encoded}`, {
headers: { Accept: "text/markdown" },
});
if (!response.ok) {
throw new Error(`읽기 실패: HTTP ${response.status}`);
}
const note = await response.text();
return note.length > 20000
? note.slice(0, 20000) + "\n\n[긴 노트: 이후 내용 생략]"
: note;
}),
);
this.server.tool(
"obsidian_write_result",
"AI-Inbox/result.md에 저장합니다. 기존 내용은 덮어씁니다.",
{ content: z.string().min(1).max(20000) },
async ({ content }) =>
this.run(async () => {
const response = await this.api(
"/vault/AI-Inbox/result.md",
{
method: "PUT",
headers: { "Content-Type": "text/markdown" },
body: content,
},
);
if (!response.ok) {
throw new Error(`저장 실패: HTTP ${response.status}`);
}
return "AI-Inbox/result.md에 저장했습니다.";
}),
);
}
}
export default new OAuthProvider({
apiHandler: MyMCP.serve("/mcp"),
apiRoute: "/mcp",
authorizeEndpoint: "/authorize",
clientRegistrationEndpoint: "/register",
defaultHandler: GitHubHandler as any,
tokenEndpoint: "/token",
});
로그인 처리는 템플릿의 파일들을 사용하므로 github-handler.ts, utils.ts, workers-oauth-utils.ts는 삭제하지 않습니다.
또한 GitHub 로그인 성공과 내 노트 이용 권한은 별개입니다. 위 코드의 ALLOWED_USERS가 실제로 노트에 접근할 수 있는 사용자를 제한합니다. 도구 실행 때도 권한을 다시 확인합니다.
이 예제는 확인한 공식 템플릿의 McpAgent 구조를 따릅니다. 설치 후 만들어지는 package-lock.json을 보관하고, 오류가 난다고 관련 패키지를 각각 임의의 최신 버전으로 바꾸지 마세요.
8단계. Worker 주소와 GitHub 로그인 설정하기
코드 저장 후 먼저 배포합니다.
npx.cmd wrangler deploy
이 시점에는 Secret이 없으므로 노트 도구를 아직 사용할 수 없습니다. 먼저 배포 결과에서 Worker 주소를 확인합니다. 주소는 다음과 같은 형태입니다.
https://obsidian-gemini-bridge.본인계정.workers.dev
이제 GitHub에서 다음 순서로 이동합니다: Settings → Developer settings → OAuth Apps → New OAuth App
입력값은 다음과 같습니다.
| GitHub 입력란 | 입력 내용 |
|---|---|
| Application name | Obsidian Practice Bridge |
| Homepage URL | 방금 배포된 Worker 기본 주소 |
| Authorization callback URL | Worker 기본 주소 뒤에 /callback 추가 |
앱을 등록한 뒤 Client ID와 Client secret을 확인합니다. 이 값은 Worker가 GitHub 로그인을 처리할 때 사용합니다. Gemini의 Advanced Settings에 그대로 넣는 값이 아닙니다. (GitHub OAuth를 사용하는 공식 템플릿 설명)
9단계. 비밀 키를 코드 밖에 등록하기
프로젝트 폴더의 PowerShell에서 명령을 하나씩 실행합니다. 입력을 요구하면 해당 값을 붙여넣고 Enter를 누르세요.
npx.cmd wrangler secret put GITHUB_CLIENT_ID
- GitHub OAuth App의 Client ID를 입력합니다.
npx.cmd wrangler secret put GITHUB_CLIENT_SECRET
- GitHub OAuth App의 Client secret을 입력합니다.
다음으로 로그인 처리에 사용할 임의의 키를 생성합니다.
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
- 출력된 문자열을 복사한 뒤 다음 명령의 입력값으로 사용합니다.
npx.cmd wrangler secret put COOKIE_ENCRYPTION_KEY
이어서 옵시디언 연결 정보를 등록합니다.
npx.cmd wrangler secret put OBSIDIAN_TUNNEL_URL
- 첫 번째 PowerShell 창에서 생성된 본인의
trycloudflare.com기본 주소를 입력합니다. 끝에/vault/나/mcp를 붙이지 않습니다.
npx.cmd wrangler secret put OBSIDIAN_API_KEY
- 옵시디언 플러그인 설정에 있는 API 키를 입력합니다.
마지막으로 다시 배포합니다.
npx.cmd wrangler deploy
Secret 등록은 값을 코드와 분리해서 관리하는 과정입니다. Cloudflare는 민감한 값에 Secret 사용을 안내합니다. (Cloudflare OAuth 및 설정 안내)
10단계. Gemini보다 먼저 연결을 검사하기
여기까지 왔다면 다음 세 가지가 준비되어 있어야 합니다.
- 옵시디언의 연습용 볼트가 열려 있습니다.
- 첫 번째 PowerShell에서 터널이 실행 중입니다.
- Worker 배포와 Secret 등록이 끝났습니다.
새 PowerShell 창에서 검사 도구를 실행합니다.
npx.cmd @modelcontextprotocol/inspector@latest
실행 결과에 표시되는 로컬 검사 화면 주소를 브라우저에서 엽니다. 포트 번호를 외워서 입력하지 말고 출력된 주소를 사용하세요.
검사 화면에서는 다음과 같이 설정합니다.
| 항목 | 값 |
|---|---|
| 연결 방식 | Streamable HTTP |
| 서버 주소 | 본인의 Worker 주소 + /mcp |
OAuth 로그인 절차에서 GitHub로 로그인합니다.
그다음 도구 목록에서 obsidian_read_note를 선택하고 다음 값을 입력합니다.
{
"path": "AI-Inbox/hello.md"
}
옵시디언에 적어 둔 연습 노트가 반환되면 연결이 동작한 것입니다. 로그인 창이 뜬 것만으로 성공을 판단하지 말고, 실제 노트 본문까지 확인하세요. Inspector를 이용한 원격 MCP 테스트는 Cloudflare 공식 예제에서도 안내합니다. (원격 MCP 서버 테스트 안내)
11단계. Gemini에 커스텀 앱 등록하기
Gemini 웹사이트에서 설정을 엽니다: Settings & help → Connected Apps → Custom apps for Spark
커스텀 앱 주소에 다음 형태로 입력합니다.
https://본인의-Worker-주소/mcp
여기에 넣는 것은 Worker의 MCP 주소입니다.
| 주소 | 사용하는 곳 |
|---|---|
https://127.0.0.1:27124 |
PC 내부 연결 확인 |
https://…trycloudflare.com |
Worker의 OBSIDIAN_TUNNEL_URL |
https://…workers.dev/mcp |
Gemini와 Inspector |
https://…workers.dev/callback |
GitHub OAuth App 설정 |
이 예제에는 /register를 통한 동적 클라이언트 등록이 설정되어 있습니다. 따라서 Gemini의 Advanced Settings에서 Client ID와 Client secret을 임의로 채우지 말고 기본 연결부터 진행합니다.
Google 안내에서도 고급 자격 증명 입력은 서버가 동적 클라이언트 등록을 지원하지 않는 경우에 설명합니다. 옵시디언 API 키를 Client secret 칸에 넣어 해결하려고 하면 안 됩니다. (Gemini 커스텀 앱 등록 절차)
이후 표시되는 로그인과 연결 안내를 따릅니다. 앱을 사용할 때는 Spark 작업에서 @를 입력하고 등록한 앱을 선택합니다.
12단계. 검색 → 읽기 → 저장 순서로 사용하기
한 번에 여러 일을 시키기보다 세 단계로 나누면 어디에서 문제가 생겼는지 쉽게 알 수 있습니다.
먼저 검색합니다.
“연결한 옵시디언 앱을 사용해서 ‘관계대명사’가 들어 있는 노트를 찾아줘. 파일 이름과 찾은 내용을 알려줘.”
다음으로 정확한 노트를 읽습니다.
“AI-Inbox/hello.md의 본문을 읽어줘. 실제 도구로 읽은 내용만 보여줘.”
마지막으로 정리 결과를 저장합니다.
“방금 읽은 노트를 바탕으로 중학생용 수업 활동 세 가지를 정리해줘. 먼저 저장할 내용을 보여줘. 내가 확인하면 obsidian_write_result로 저장해줘.”
내용을 확인한 다음 저장을 요청합니다.
“확인했어. AI-Inbox/result.md에 저장해줘. 그 파일의 기존 내용은 덮어써도 돼.”
옵시디언에서 result.md를 열어 실제 저장 결과를 확인합니다.
Google은 현재 커스텀 앱의 쓰기 작업에 수동 확인을 요구한다고 안내합니다. 다만 화면의 확인 절차가 서버의 인증을 대신하지는 않습니다. (커스텀 앱의 쓰기 작업과 주의사항)
문제가 생기면 이 표부터 확인하세요
| 증상 | 확인할 곳 | 조치 |
|---|---|---|
| Gemini에 커스텀 앱 메뉴가 없음 | 계정·지역·Spark 제공 조건 | 기능 제공 여부부터 확인 |
cloudflared를 찾을 수 없음 |
설치와 PATH | 설치 후 PowerShell 다시 열기 |
npm.ps1 실행 정책 오류 |
실행한 명령 | npm.cmd, npx.cmd 사용 |
127.0.0.1:27124 연결 실패 |
옵시디언 | 앱·볼트·플러그인·포트 확인 |
| 터널에서 인증서 오류 | 로컬 HTTPS 연결 | 연습용 옵션 또는 CA 설정 확인 |
| HTTP 401 | 오류가 난 연결 구간 | MCP 로그인 또는 옵시디언 API 키 확인 |
| 노트 접근 권한이 없다는 메시지 | ALLOWED_USERS |
GitHub 사용자 이름 확인 후 재배포 |
| HTTP 404 | 노트 경로 | 폴더·파일 이름·.md 확장자 확인 |
| GitHub callback 오류 | OAuth App 설정 | 실제 Worker 주소의 /callback인지 확인 |
| HTTP 502 또는 시간 초과 | 터널과 PC | 터널 실행 상태·최신 주소·절전 상태 확인 |
| Inspector는 성공하고 Gemini만 실패 | Gemini 연결 | MCP 주소·OAuth 절차·계정 지원 조건 확인 |
HTTP 401이라고 해서 항상 옵시디언 키가 틀린 것은 아닙니다. Gemini에서 Worker로 들어오는 인증과 Worker에서 옵시디언으로 들어가는 인증을 구분해야 합니다. 반대로 HTTP 502는 터널이 로컬 서비스에 도달하지 못하는 상황 등을 먼저 점검합니다. (Cloudflare Tunnel 오류 안내)
다음 날 다시 사용할 때
Quick Tunnel을 종료했다가 다시 켜면 주소가 바뀔 수 있습니다.
- 옵시디언에서 연습용 볼트를 엽니다.
- PowerShell에서 터널을 실행합니다.
- 새로 나온 주소를 확인합니다.
- 주소가 달라졌다면 프로젝트 폴더에서 Secret을 갱신합니다:
npx.cmd wrangler secret put OBSIDIAN_TUNNEL_URL - 입력란에 새 터널 주소를 붙여넣습니다.
Worker 주소가 그대로라면 Gemini에 등록한 주소까지 바꿀 필요는 없습니다.
실습을 마쳤을 때는 터널 창에서 Ctrl+C를 눌러 종료하면 됩니다.
실제 업무용 볼트로 확장하기 전에
연습용 연결이 성공하면 다음에는 접근 범위를 정할 차례입니다.
예를 들어 수업자료 볼트라면 AI가 읽을 폴더를 Teaching/으로 제한하고, 새 결과물은 AI-Inbox/에만 저장하도록 만들 수 있습니다. 학생 개인정보가 들어 있는 상담 기록은 별도로 관리하는 방식입니다.
이번 예제는 검색과 읽기에 대해 연습용 볼트 전체를 대상으로 합니다. 실제 볼트로 옮기려면 검색 결과뿐 아니라 읽기 경로에도 폴더 제한을 적용해야 합니다.
저장 역시 실습에서는 result.md 한 파일을 덮어쓰지만, 업무용으로는 날짜와 고유 번호가 붙은 새 파일 생성, 변경 전 백업, 충돌 확인 등을 추가할 수 있습니다.
상시 운영에는 고정 주소를 쓰는 Named Tunnel과 인증서 신뢰 설정도 필요합니다. 터널을 Windows 서비스로 실행하더라도 옵시디언과 볼트가 열려 있어야 한다는 점은 같습니다.
옵시디언에 MCP가 있는데도 Worker가 필요한가요?
플러그인 자체에도 MCP 서버가 있습니다. 공식 문서는 다음 주소와 Bearer 인증을 안내합니다.
https://127.0.0.1:27124/mcp/
같은 PC에서 실행되는 MCP 클라이언트가 해당 주소와 인증 헤더를 지원한다면 직접 연결을 검토할 수 있습니다. (옵시디언 플러그인의 MCP 연결 안내) 이번 구성에서는 Gemini가 접근할 수 있는 원격 주소와 OAuth 로그인을 제공하고, 사용할 도구를 세 가지로 정하기 위해 Worker를 사용했습니다.
처음 실습의 목표는 작게 잡으면 좋습니다.
Gemini가 hello.md를 실제로 읽고, 내가 확인한 내용을 result.md에 저장하는 것.
이 과정이 확인되면 독서 메모 요약, 수업안 정리, 블로그 초안 수집으로 하나씩 확장할 수 있습니다.
- 제목 대안: 「내 옵시디언 노트를 AI가 읽게 하려다 막힌 곳들」, 「Gemini에서 내 PC의 옵시디언을 사용할 수 있을까?」
- 검색 설명문: Gemini와 옵시디언을 연결하는 초보자용 안내. 계정 조건부터 Local REST API, Cloudflare Tunnel, OAuth 인증, 노트 검색·저장과 오류 해결까지 설명합니다.
- 검수 상태: 초안 작성 완료 · 공식 기능 및 템플릿 확인 · 수정 코드의 실제 배포와 전체 연결 테스트는 미실시
[상태 요약]
- 지금까지 한 일: 첨부 자료를 실습형 블로그로 재작성하고 인증 누락·이용 조건·오류 설명을 보완했습니다.
- 아직 남은 일: 수정 코드의 실제 환경 테스트와 블로그 게시입니다.
- 내가 결정해야 할 것: 원고 작성에는 추가 결정이 없습니다. 실습 시 Gemini 기능 제공 여부부터 확인하면 됩니다.
- 다음에 이어서 할 때 붙여넣을 프롬프트: “위 블로그의 실습을 내 환경에서 진행하자. Gemini 커스텀 앱 제공 여부와 Windows 버전부터 확인해줘.”
- 산출물/파일/링크: 위 블로그 원고 전체와 본문에 연결한 공식 문서입니다.