日语学习代理服务

一个基于FastAPI/uvicorn的服务器,通过HTTP流暴露Bunpro MCP捆绑包,提供日语语法、词汇、阅读、用户统计和复习工具的代理服务。

  • by brimalval
  • Python
  • 本地部署
  • 1 星标
  • 更新于
在 GitHub 查看 brimalval/bunpro-mcp

使用说明

Bunpro MCP

概述

此FastAPI/uvicorn服务器通过HTTP流将Bunpro MCP包暴露给其他代理 /mcp.它注册代理Bunpro API端点的语法、词汇、阅读、用户统计和评论工具。

这是一个个人学习项目,旨在:

  • 探索MCP(模型上下文协议)服务器的工作原理
  • 通过Bunpro集成协助日语学习

与Bunpro无关。为教育目的而建。

需求

  • Python 3.14或更高版本(匹配Bunpro MCP依赖项)
  • 阿斯特拉 uv CLI。通过安装 pip install uv 或跟随https://github.com/astral-sh/uv,然后运行 uv sync 在启动服务器之前,从repo根目录开始。

设置

  1. 复制 .env.example 到 .env 并提供您的Bunpro前端API代币(BUNPRO_FRONTEND_API_TOKEN= 线)。像对待其他秘密一样对待令牌;不要承诺。
  2. 如果请求通过HTTP到达,服务器首先读取Bunpro的 frontend_api_token cookie,并将该令牌用于出站Bunpro API调用。
  3. 对于没有该cookie的本地运行,服务器将回退到 BUNPRO_FRONTEND_API_TOKEN,然后是遗产 BUNPRO_JWT.

环境变量

  • BUNPRO_FRONTEND_API_TOKEN (首选):当不存在请求cookie时,每个工具使用Bunpro前端API令牌。
  • BUNPRO_JWT (旧别名):向后兼容的回退 BUNPRO_FRONTEND_API_TOKEN.
  • BUNPRO_API_BASE_URL (可选):覆盖Bunpro API根(默认为内置客户端值)。
  • BUNPRO_HOST (可选):服务器监听的主机(默认为 127.0.0.1).
  • BUNPRO_PORT (可选):服务器监听的端口(默认为 8000).
  • BUNPRO_STREAM_PATH (可选):MCP流路径(默认为 /mcp).

运行服务器

在回购根目录中,在同一行运行带有前端API令牌的FastAPI应用程序,以便服务器立即获取该令牌。 uv run 重复利用环境 uv sync 为这次回购做准备。

BUNPRO_FRONTEND_API_TOKEN=your_frontend_api_token_here uv run server.py

使用OpenCode(MCP客户端)

开源代码 可以作为MCP客户端连接到此服务器。服务器运行在 http://127.0.0.1:8000/mcp 默认情况下。

设置

  1. 安装OpenCode(请参阅 OpenCode文档)
  2. 使用 .env.example 创建a .env 使用Bunpro API令牌文件
  3. 启动MCP服务器:
    uv --env-file .env run server.py
    
  4. 配置OpenCode以连接到服务器。添加到您的 opencode.jsonc:
    {
      "mcpServers": {
        "bunpro": {
          "url": "http://127.0.0.1:8000/mcp"
        }
      }
    }
    
  5. 跑 opencode 使用Bunpro工具开始聊天

可选:Bunpro导师代理

此仓库包括一个预配置的代理,位于 .opencode/agent/bunpro-tutor.md 提供日语辅导服务。要使用它:

  1. 确保 .opencode/ 目录在您的项目中
  2. 在OpenCode中,切换到 bunpro导师 代理
  3. 询问有关日语学习进展的问题

示例提示:

I'm currently reviewing. Why is 行きませんか used instead of 行きましょうか here?

使用MCP检查员检查工具

在服务器运行时,使用捆绑的MCP检查器列出和调用工具:

BUNPRO_FRONTEND_API_TOKEN=your_frontend_api_token_here uv run mcp dev server.py

运行检查器后,您可以浏览已注册的工具,查看它们的签名,并从UI以交互方式传递参数。

工具参考+示例

在MCP检查器和Python脚本之间进行选择以调用工具。每次运行时,MCP检查器都会显示已注册的工具列表 uv run mcp dev server.py;您选择一个工具名称,填写其参数,并以交互方式发送请求。自动化或调试可以直接调用异步工具函数 uv run python.

Python代码片段

替换 your_frontend_api_token_here 使用您导出的值(并将其置于版本控制之外):

  • search_grammar(query)
    uv run python - <<'PY'
    import asyncio
    from src.tools.grammar import search_grammar
    
    async def main():
        print(await search_grammar("particles"))
    
    asyncio.run(main())
    PY
    
  • get_grammar_point(slug)
    uv run python - <<'PY'
    import asyncio
    from src.tools.grammar import get_grammar_point
    
    async def main():
        print(await get_grammar_point("particles-1"))
    
    asyncio.run(main())
    PY
    
  • get_vocab_level()
    uv run python - <<'PY'
    import asyncio
    from src.tools.vocabulary import get_vocab_level
    
    async def main():
        print(await get_vocab_level())
    
    asyncio.run(main())
    PY
    
  • get_vocab_items(vocab_slug_or_id)
    uv run python - <<'PY'
    import asyncio
    from src.tools.vocabulary import get_vocab_items
    
    async def main():
        print(await get_vocab_items("genki-lesson-1"))
    
    asyncio.run(main())
    PY
    
  • search_vocab(query, result_limit=None)
    uv run python - <<'PY'
    import asyncio
    from src.tools.vocabulary import search_vocab
    
    async def main():
        print(await search_vocab("greetings", result_limit=10))
    
    asyncio.run(main())
    PY
    
  • get_reading_passages()
    uv run python - <<'PY'
    import asyncio
    from src.tools.reading import get_reading_passages
    
    async def main():
        print(await get_reading_passages())
    
    asyncio.run(main())
    PY
    
  • search_reading_passages(query)
    uv run python - <<'PY'
    import asyncio
    from src.tools.reading import search_reading_passages
    
    async def main():
        print(await search_reading_passages("vacation"))
    
    asyncio.run(main())
    PY
    
  • get_user_stats()
    uv run python - <<'PY'
    import asyncio
    from src.tools.user_stats import get_user_stats
    
    async def main():
        print(await get_user_stats())
    
    asyncio.run(main())
    PY
    
  • get_jlpt_progress()
    uv run python - <<'PY'
    import asyncio
    from src.tools.user_stats import get_jlpt_progress
    
    async def main():
        print(await get_jlpt_progress())
    
    asyncio.run(main())
    PY
    
  • get_srs_forecast(granularity="daily")
    uv run python - <<'PY'
    import asyncio
    from src.tools.user_stats import get_srs_forecast
    
    async def main():
        print(await get_srs_forecast(granularity="daily"))
    
    asyncio.run(main())
    PY
    
  • get_study_configuration()
    uv run python - <<'PY'
    import asyncio
    from src.tools.review import get_study_configuration
    
    async def main():
        print(await get_study_configuration())
    
    asyncio.run(main())
    PY
    
  • get_due_count()
    uv run python - <<'PY'
    import asyncio
    from src.tools.review import get_due_count
    
    async def main():
        print(await get_due_count())
    
    asyncio.run(main())
    PY
    
  • get_pending_reviews()
    uv run python - <<'PY'
    import asyncio
    from src.tools.review import get_pending_reviews
    
    async def main():
        print(await get_pending_reviews())
    
    asyncio.run(main())
    PY