Skip to content

Add Onedrive download MCP server#60

Open
baxkspace wants to merge 184 commits intomicrosoft:mainfrom
CDP1-Team4:onedrive-download
Open

Add Onedrive download MCP server#60
baxkspace wants to merge 184 commits intomicrosoft:mainfrom
CDP1-Team4:onedrive-download

Conversation

@baxkspace
Copy link

Purpose

Add an MCP server which download onedrive file from url

  • ...

Pull Request Type

What kind of change does this Pull Request introduce?

[ ] Bugfix
[ ] New feature to existing sample
[x ] New sample
[ ] Refactoring (no functional changes, no api changes)
[ ] Documentation content changes
[ ] Other... Please describe:

README updated?

The top-level readme for this repo contains a link to each sample in the repo. If you're adding a new sample did you update the readme?

[ ] Yes
[ x] No
[ ] N/A

baxkspace and others added 30 commits October 8, 2025 22:16
task template update
- mcp-api.bicep: API 경로를 /mcp에서 /로 변경, 작업 URL 템플릿을 /에서 /mcp로 변경
- Program.cs: Managed Identity 인증 구현, FUNCTIONS_CUSTOMHANDLER_PORT 환경변수 사용
- host.json: extensionBundle 제거로 순수 ASP.NET Core 앱으로 구성
- HostApplicationBuilderExtensions.cs: MapFallback 추가하여 404 응답 처리
- Constants.cs: 포트 설정 상수화
- resources.bicep: UserAssignedClientId 환경변수 설정
- VSCode MCP 클라이언트: 입력창을 통한 FQDN 설정 지원

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Azure Functions Custom Handler로 .NET 앱을 직접 실행하도록 구성

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- functionapp.bicep: customHandler 설정 추가하여 ASP.NET Core 앱 직접 실행
- host.json: 간단한 구성으로 정리
- local.settings.json: 로컬 개발 환경용 추가

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- linuxFxVersion: 'CUSTOM||'로 설정하여 Custom Handler 활성화
- WebSocket 및 HTTP/2 활성화

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
로컬 개발 환경에서 올바른 포트로 MCP 서버 연결

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- functionapp.bicep: customHandler를 bicep가 아닌 host.json에서 관리하도록 정리
- host.json: defaultExecutablePath로 ASP.NET Core 앱 직접 실행 설정
- worker.csx: Azure Functions 함수 진입점 파일 추가
- enableForwardingHttpRequest: true로 모든 HTTP 요청 전달

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- kind: 'functionapp,linux' -> 'app,linux' (일반 Web App)
- functionAppConfig 제거 (Web App에는 필요 없음)
- linuxFxVersion: 'DOTNETCORE|9.0' 추가
- alwaysOn: true로 설정

이렇게 하면 Azure Functions 런타임 없이 순수 ASP.NET Core 앱만 실행됨

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Web App(순수 ASP.NET Core)으로 배포할 때는 host.json에서
customHandler 설정을 제거해야 Azure Functions 런타임 대신
우리의 ASP.NET Core 앱이 직접 실행됨

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- host.json에 customHandler 설정 추가 (outlook-email 참고)
- extensions.http.routePrefix를 빈 문자열로 설정
- enableForwardingHttpRequest/enableHttpProxyingRequest 활성화
- functionapp.bicep kind를 'functionapp,linux'로 변경
- functionAppConfig를 bicep에 추가하여 올바른 runtime 설정
- siteConfig에서 불필요한 설정 제거 (linuxFxVersion, webSocketsEnabled 등)

이 변경으로 Azure Functions 런타임이 배포된 .NET 앱을 올바르게 실행할 수 있음
- mcp-handler/function.json 추가 (HTTP 트리거 바인딩)
- 모든 HTTP 메서드 (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS) 지원
- route를 '{*route}'로 설정하여 모든 경로를 처리
- outlook-email 구조와 동일하게 적용
- authLevel을 'function'에서 'anonymous'로 변경
- MCP 서버가 공개적으로 접근 가능하도록 설정
- mcp-entra-app.bicep에서 Files.Read.All과 Sites.Read.All 권한 부여
- resources.bicep에서 appScopes에 필요한 권한 추가
- Managed Identity가 OneDrive 파일에 접근할 수 있도록 설정

이 변경으로 download_file_from_onedrive_url 도구가 정상 작동함
- outlook-email의 방식을 따라 권한 ID를 하드코딩 대신 동적 조회
- map/filter를 사용하여 appScopes와 appRoles에서 실제 권한 ID 추출
- 모든 역할을 동적으로 할당하도록 변경

이로써 Files.Read.All, Sites.Read.All 등의 권한이 정상 부여됨
- mcp-entra-app.bicep에서 for 루프 제거 (Bicep 제한사항)
- resources.bicep에서 Microsoft Graph 권한 직접 할당
- Files.Read.All 역할을 MCP app과 user-assigned identity에 부여
- 올바른 권한 ID로 OneDrive 접근 권한 설정
baxkspace and others added 28 commits November 29, 2025 03:02
authSettingsV2 was referencing entraApp.outputs.mcpAppId before entraApp was defined.
Moved authSettingsV2 to appear after entraApp module and added explicit dependsOn.

This ensures:
1. entraApp is created first
2. authSettingsV2 can reference its outputs (mcpAppId)
3. Proper authentication configuration is applied to Function App

This should fix the missing authentication popup issue.

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
App Service의 Easy Auth 401 응답으로 VSCode 내장 인증이 자동 트리거되므로,
mcp.json의 auth 블록은 불필요. 이전에 getAzureAuthToken 명령이 없어서
정상 작동하지 않았음.

인증 흐름:
1. VSCode가 토큰 없이 요청
2. App Service Easy Auth가 401 반환
3. VSCode 내장 Azure 확장이 로그인 팝업 표시
4. 사용자 로그인 후 자동으로 토큰을 헤더에 포함시켜 재요청
5. 서버의 Token Passthrough 로직이 토큰 추출 및 사용

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
AADSTS901002 에러 해결: resource 파라미터를 지원하지 않는 문제
- 이전: https://sts.windows.net/common/v2.0
- 변경: https://login.microsoftonline.com/{tenantId}/v2.0

이 변경으로 Azure AD 인증이 제대로 작동합니다.

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
AADSTS700016 에러 해결: VSCode가 사용하는 리다이렉트 URI가 Entra 앱에 등록되지 않은 문제

추가된 리다이렉트 URI:
- http://127.0.0.1:33418 (VSCode 로컬 인증)
- https://vscode.dev/redirect (VSCode Web)

이제 VSCode 인증이 정상 작동합니다.

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
signInAudience: 'AzureADandPersonalMicrosoftAccount'로 설정하여
- 조직 사용자 (Azure AD)
- 개인 Microsoft 계정 (hotmail, outlook.com 등)
모두 로그인할 수 있도록 변경합니다.

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
SPA 뿐만 아니라 모바일/데스크톱 앱도 지원하도록
publicClient 블록을 추가합니다.

이제 더 많은 클라이언트 유형에서 정상 작동합니다.

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
- Add postprovision hook in azure.yaml
- Create infra/hooks/postprovision.ps1 script
- Add AZURE_CLIENT_ID output to resources.bicep

The postprovision hook automatically injects the Client ID from azd env
into mcp.http.remote-func.json and mcp.http.remote-apim.json after deployment.

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
Change hook format from list to key-value pair:
- From: - pwsh ./infra/hooks/postprovision.ps1
- To: - pwsh: ./infra/hooks/postprovision.ps1

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
Add 'run' field to hook configuration as required by azd CLI

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
Use Windows PowerShell with ExecutionPolicy Bypass for better compatibility

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
응용 프로그램 계층에서 인증을 강제하도록 변경:
- resources.bicep authSettingsV2에서 unauthenticatedClientAction을 'AllowAnonymous'로 변경
- Program.cs에서 토큰 누락 시 UnauthorizedAccessException 발생
- mcp.json 파일들에서 oauth2 auth 블록 제거 (env만 유지)
- postprovision 훅을 새로운 구조에 맞게 간소화

각 배포마다 자동으로 올바른 Client ID가 env에 주입됩니다.

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
토큰이 없을 때 httpContext.Response.StatusCode = 401을 설정하여
VSCode에서 인증 팝업을 트리거하도록 수정했습니다.

이전에는 예외만 throw했으나 상태코드가 설정되지 않아 500 에러로 응답되었습니다.

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
DI 컨테이너에서 예외를 throw하는 방식 대신
미들웨어에서 Authorization 헤더를 직접 검증하여 401을 응답하도록 변경했습니다.

이제 토큰이 없으면 미들웨어가 먼저 처리하여 VSCode에 401을 반환하고
팝업을 트리거합니다.

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
VSCode는 매번 다른 동적 포트를 사용하므로
http://127.0.0.1:33418 같은 고정 포트는 작동하지 않습니다.

대신 http://localhost와 https://vscode.dev/redirect만 유지하여
VSCode의 동적 포트를 지원하도록 수정했습니다.

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
VSCode가 /authorize로 요청할 때 Microsoft 로그인 페이지로 자동 리다이렉트합니다.

변경사항:
1. GraphServiceClient DI: /authorize 요청 시 토큰 검사 건너뛰기
2. 미들웨어: /authorize 요청을 Microsoft OAuth2 인증 URL로 리다이렉트

이제 VSCode가 서버를 "로그인 서버"로 착각해도
자동으로 Microsoft 로그인 페이지로 이동합니다.

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
Microsoft에서 인증 코드를 받으면 localhost로 리다이렉트되는데,
이를 처리하는 엔드포인트를 추가했습니다.

동작:
1. Microsoft 로그인 후 code를 받음
2. localhost/?code=... 로 리다이렉트
3. 서버가 code를 access_token으로 교환
4. localStorage에 토큰 저장
5. VSCode가 이후 요청에서 토큰 사용

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
- Move BearerTokenCredential.cs from Authentication/ to Services/
- Remove Authentication/ directory (consolidate into Services)
- Update mcp.json files: use ${env:AZURE_CLIENT_ID} for dynamic clientId
- Update postprovision hook to inject both clientId and env vars
- Add appsettings.Development.json.example template
- Remove hardcoded sensitive data from config files
- Standardize OAuth2 auth format (authorization_url, token_url, redirectUri)

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
- BearerTokenCredential moved to Services namespace
- Remove unused using statement for Authentication

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
- Required by Azure Functions deployment

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
… App settings

- Disable Azure Easy Auth at server level (enabled: false)
- Move authentication logic to Program.cs middleware for token passthrough
- Add CORS support (allowedOrigins: ['*']) for VS Code connectivity
- Add localhost and 127.0.0.1 to Entra App redirect URIs
- Enable fallback public client flow (isFallbackPublicClient: true)
- Add appsettings.*.json to .gitignore for sensitive configurations

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Delete BearerTokenCredential.cs (authentication logic moved to Program.cs)
- Delete test-auth.ps1 (no longer needed)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Delete the following unused modules that were never referenced:
- functionapp.bicep (logic already inline in resources.bicep)
- rbac.bicep (logic already inline in resources.bicep)
- vnet.bicep (not used)
- storage-privateendpoint.bicep (not used)
- fetch-container-image.bicep (not used)

These files have no impact on azd up deployment as they were never called from resources.bicep.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Create /downloads APIM operation with custom policy (downloads.policy.xml)
- Bypass token validation for /downloads (SAS URL provides access control)
- Modify OneDriveTool to generate APIM proxy URLs
- Add APIM_FQDN environment variable to Function App settings
- Downloads now use APIM gateway instead of direct storage URLs

Architecture:
- Client receives: https://apim-fqdn.com/mcp/downloads?file=...&sv=...&sig=...
- APIM forwards to: Function App (which can log/audit)
- Or APIM can directly proxy to Azure File Share

This provides:
✓ Abstraction of storage account details
✓ Opportunity for request logging/auditing
✓ Single gateway for all API traffic

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Change config key from 'OnedriveDownload:Storage:ConnectionString' to
'AZURE_STORAGE_CONNECTION_STRING' to match resources.bicep setting.

This fixes the 'Storage connection string not configured' error in the
/download handler.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Added comprehensive README with prerequisites and Azure deployment instructions
- Included VS Code integration steps for both Function App and APIM configurations
- Documented MCP server connection setup with remote deployment options

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Major changes:
- Added /.well-known/oauth-protected-resource endpoint in Program.cs
- Server dynamically returns OAuth2 configuration to MCP clients
- Simplified mcp.json files: removed hardcoded auth blocks
- Removed postprovision hook from azure.yaml
- Deleted infra/hooks/postprovision.ps1: no longer needed

Why this is better:
✅ OAuth config is dynamic - no build-time file modifications needed
✅ Simpler deployment process - no postprovision hooks
✅ Works with any RFC 9728-compliant MCP client (VS Code, Claude Code, etc.)
✅ Server-side configuration management (secure, maintainable)
✅ No hardcoded ClientId in mcp.json files

RFC 9728 Compliance:
✅ authorization_endpoint: Microsoft OAuth endpoint
✅ token_endpoint: Microsoft token endpoint
✅ revocation_endpoint: Token revocation support
✅ client_id: Read from OnedriveDownload__Auth__ClientId (Bicep-injected)
✅ scopes: Graph API default scope + offline_access
✅ response_types_supported: ["code"]
✅ code_challenge_methods_supported: ["S256"] (PKCE)
✅ token_endpoint_auth_methods_supported: ["none"] (public client)
✅ grant_types_supported: ["authorization_code", "refresh_token"]

Updated/Deleted files:
- Program.cs: Implemented RFC 9728 endpoint
- mcp.http.remote-func.json: Removed auth block
- mcp.http.remote-apim.json: Removed auth block
- resources.bicep: Updated auth variable names
- azure.yaml: Removed postprovision hook
- infra/hooks/postprovision.ps1: DELETED

Flow:
1. Client connects to /mcp endpoint (no token)
2. Server returns 401 + WWW-Authenticate
3. Client fetches /.well-known/oauth-protected-resource
4. Server provides OAuth config (client_id, endpoints, scopes)
5. Client initiates OAuth flow with Microsoft
6. User logs in and grants permissions
7. Client receives access_token
8. Client retries /mcp with Authorization header
9. Server validates token and processes MCP request

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Remove manual OAuth-Protected-Resource endpoint from Program.cs
- Update mcp-prm.policy.xml to return dynamic OAuth configuration
- PRM endpoint now handled entirely by APIM mcp-prm policy
- Includes authorization/token/revocation endpoints and supported OAuth scopes

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
- Use set-variable to store McpClientId and McpTenantId
- Use liquid template for direct variable interpolation
- Simplify JSON response structure
- Remove unnecessary C# code serialization

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
@justinyoo justinyoo added the feature Issue related to a new feature request label Jan 25, 2026
@baxkspace
Copy link
Author

@microsoft-github-policy-service agree

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature Issue related to a new feature request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants