diff --git a/.gitignore b/.gitignore index 1170717..e400f0c 100644 --- a/.gitignore +++ b/.gitignore @@ -126,7 +126,7 @@ dist .tern-port # Stores VSCode versions used for testing VSCode extensions -.vscode-test +.openmcp-test # yarn v2 .yarn/cache @@ -134,3 +134,5 @@ dist .yarn/build-state.yml .yarn/install-state.gz .pnp.* + +.pipeline \ No newline at end of file diff --git a/.vitepress/.DS_Store b/.vitepress/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/.vitepress/.DS_Store differ diff --git a/.vitepress/config.mts b/.vitepress/config.mts index 407bdd0..cc65c0e 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -10,6 +10,10 @@ import { contributors } from './contributors'; import { withMermaid } from "vitepress-plugin-mermaid"; import { customIcons } from './theme/hook/icons'; +import enConfig from './i18n/en'; +import zhConfig from './i18n/zh'; +import jaConfig from './i18n/ja'; + export const baseUrl = '/openmcp'; export default withMermaid({ @@ -30,7 +34,14 @@ export default withMermaid({ mapAuthors: contributors }), GitChangelogMarkdownSection({ - excludes: ['preview/contributors.md', 'index.md'] + excludes: [ + 'preview/contributors.md', + 'zh/preview/contributors.md', + 'ja/preview/contributors.md', + 'index.md', + 'zh/index.md', + 'ja/index.md' + ] }), ThumbnailHashImages(), ], @@ -65,186 +76,34 @@ export default withMermaid({ head: [ ['link', { rel: 'icon', href: baseUrl + '/images/favicon.svg' }] ], - themeConfig: { - // https://vitepress.dev/reference/default-theme-config - nav: [ - { text: '首页', link: '/' }, - { - text: '教程', - items: [ - { - component: 'KNavItem', - props: { - title: '简介', - description: '关于 mcp 和 openmcp,阁下需要知道的 ...', - icon: 'openmcp', - link: '/plugin-tutorial/' - } - }, - { - component: 'KNavItem', - props: { - title: '快速开始', - description: '通过一个例子快速了解 OpenMCP 的基本概念', - icon: 'quick-fill', - link: '/plugin-tutorial/quick-start/' - } - }, - { - component: 'KNavItem', - props: { - title: 'OpenMCP 使用手册', - description: 'OpenMCP Client 的基本使用', - icon: 'shiyongshouce', - link: '/plugin-tutorial/usage/connect-mcp' - } - }, - { - component: 'KNavItem', - props: { - title: 'MCP 服务器开发案例', - description: '使用不同语言开发的不同模式的 MCP 服务器', - icon: 'yibangonggongyusuan', - link: '/plugin-tutorial/examples/mcp-examples' - } - }, - { - component: 'KNavItem', - props: { - title: 'FAQ', - description: '为您答疑解惑,排忧解难', - icon: 'yijianchuli', - link: '/plugin-tutorial/faq/help' - } - }, - ] - }, - { text: 'SDK', link: '/sdk-tutorial' }, - { - text: '更多', - items: [ - { - component: 'KNavItem', - props: { - title: '更新日志', - description: '查看项目的更新历史记录', - icon: 'a-yusuan2', - link: '/preview/changelog' - } - }, - { - component: 'KNavItem', - props: { - title: '参与 OpenMCP', - description: '了解如何参与 OpenMCP 项目的开发和维护', - icon: 'shujuzhongxin', - link: '/preview/join' - } - }, - { - component: 'KNavItem', - props: { - title: 'OpenMCP 贡献者列表', - description: '关于参与 OpenMCP 的贡献者们', - icon: 'heike', - link: '/preview/contributors' - } - }, - { - component: 'KNavItem', - props: { - title: '资源频道', - description: '获取项目相关的资源和信息', - icon: 'xinxiang', - link: '/preview/channel' - } - } - ] - }, - ], - sidebar: { - '/plugin-tutorial/': [ - { - text: '简介', - items: [ - { text: 'OpenMCP 概述', link: '/plugin-tutorial/index' }, - { text: '什么是 MCP?', link: '/plugin-tutorial/what-is-mcp' }, - { text: 'MCP 基础概念', link: '/plugin-tutorial/concept' } - ] - }, - { - text: '快速开始', - items: [ - { text: '快速开始', link: '/plugin-tutorial/quick-start' }, - { text: '安装 OpenMCP', link: '/plugin-tutorial/quick-start/acquire-openmcp' }, - { text: '你的第一个 MCP', link: '/plugin-tutorial/quick-start/first-mcp' }, - { text: '快速调试 MCP', link: '/plugin-tutorial/quick-start/quick-debug' }, - { text: '扔进大模型里面测测好坏!', link: '/plugin-tutorial/quick-start/put-into-llm' }, - ] - }, - { - text: "OpenMCP 使用手册", - items: [ - { text: 'UI 配色', link: '/plugin-tutorial/usage/ui-color' }, - { text: '连接 mcp 服务器', link: '/plugin-tutorial/usage/connect-mcp' }, - { text: '调试 tools, resources 和 prompts', link: '/plugin-tutorial/usage/debug' }, - { text: '连接大模型', link: '/plugin-tutorial/usage/connect-llm' }, - { text: '用大模型测试您的 mcp', link: '/plugin-tutorial/usage/test-with-llm' }, - { text: '连接多个 MCP 服务器', link: '/plugin-tutorial/usage/multi-server' }, - { text: '分发您的实验结果', link: '/plugin-tutorial/usage/distribute-result' }, - { text: 'SSE 在线部署的鉴权器实现', link: '/plugin-tutorial/usage/sse-oauth2' }, - ] - }, - { - text: "MCP 服务器开发案例", - items: [ - { text: 'MCP 服务器开发案例', link: '/plugin-tutorial/examples/mcp-examples' }, - { text: '例子 1. python 实现天气信息 mcp 服务器 (STDIO)', link: '/plugin-tutorial/examples/python-simple-stdio' }, - { text: '例子 2. go 实现 neo4j 的只读 mcp 服务器 (SSE)', link: '/plugin-tutorial/examples/go-neo4j-sse' }, - { text: '例子 3. java 实现文档数据库的只读 mcp (HTTP)', link: '/plugin-tutorial/examples/java-es-http' }, - { text: '例子 4. typescript 实现基于 crawl4ai 的超级网页爬虫 mcp (STDIO)', link: '/plugin-tutorial/examples/typescript-crawl4ai-stdio' }, - { text: '例子 5. python 实现进行通用表单填充 的 mcp (STDIO)', link: '/plugin-tutorial/examples/python-form-stdio' }, - { text: '例子 6. python 实现基于 blender 的 mcp (STDIO)', link: '/plugin-tutorial/examples/python-blender-stdio' }, - { text: '例子 7. python 实现 cadence EDA 的 mcp (STDIO)', link: '/plugin-tutorial/examples/python-cadence-stdio' }, - ] - }, - { - text: 'FAQ', - items: [ - { text: '帮助', link: '/plugin-tutorial/faq/help' }, - ] - } - ], - - '/sdk-tutorial/': [ - { - text: '简介', - items: [ - { text: 'openmcp-sdk.js', link: '/sdk-tutorial/' }, - ] - }, - { - text: '基本使用', - items: [ - { text: '最简单的对话', link: '/sdk-tutorial/usage/greet' }, - { text: '任务循环', link: '/sdk-tutorial/usage/task-loop' }, - { text: '多服务器连接', link: '/sdk-tutorial/usage/multi-server' }, - ] - } - ] + locales: { + root: { + label: 'English', + lang: 'en', + themeConfig: enConfig }, + zh: { + label: '简体中文', + lang: 'zh', + link: '/zh/', + themeConfig: zhConfig + }, + ja: { + label: '日本語', + lang: 'ja', + link: '/ja/', + themeConfig: jaConfig + } + }, + + themeConfig: { socialLinks: [ { icon: 'github', link: 'https://github.com/LSTM-Kirigaya/openmcp-client' }, { icon: customIcons.share, link: 'https://kirigaya.cn/home' }, ], - footer: { - message: '缩短LLM到Agent的最后一公里', - copyright: 'OpenMCP All rights reserved' - }, - search: { provider: 'local' }, diff --git a/.vitepress/contributors.ts b/.vitepress/contributors.ts index a818f12..f3c6941 100644 --- a/.vitepress/contributors.ts +++ b/.vitepress/contributors.ts @@ -58,7 +58,7 @@ interface Contributor { export const contributors = [ { - name: '锦恢', + name: 'LSTM-Kirigaya (锦恢)', nameAliases: ['LSTM-Kirigaya', 'Kirigaya'], mapByEmailAliases: ['1193466151@qq.com'], links: [{ type: '', link: 'https://www.zhihu.com/people/can-meng-zhong-de-che-xian' }] diff --git a/.vitepress/i18n/en.ts b/.vitepress/i18n/en.ts new file mode 100644 index 0000000..cf0c06f --- /dev/null +++ b/.vitepress/i18n/en.ts @@ -0,0 +1,168 @@ +export default { + nav: [ + { text: 'Home', link: '/' }, + { + text: 'Tutorials', + items: [ + { + component: 'KNavItem', + props: { + title: 'Introduction', + description: 'What you need to know about MCP and OpenMCP...', + icon: 'openmcp', + link: '/plugin-tutorial/' + } + }, + { + component: 'KNavItem', + props: { + title: 'Quick Start', + description: 'Understand the basic concept of OpenMCP through an example', + icon: 'quick-fill', + link: '/plugin-tutorial/quick-start/' + } + }, + { + component: 'KNavItem', + props: { + title: 'OpenMCP User Guide', + description: 'Basic usage of the OpenMCP Client', + icon: 'shiyongshouce', + link: '/plugin-tutorial/usage/connect-mcp' + } + }, + { + component: 'KNavItem', + props: { + title: 'MCP Server Examples', + description: 'Examples of MCP servers developed in various languages and models', + icon: 'yibangonggongyusuan', + link: '/plugin-tutorial/examples/mcp-examples' + } + }, + { + component: 'KNavItem', + props: { + title: 'FAQ', + description: 'Answers to your questions and solutions to your problems', + icon: 'yijianchuli', + link: '/plugin-tutorial/faq/help' + } + }, + ] + + }, + { text: 'SDK', link: '/sdk-tutorial/' }, + { + text: 'More', + items: [ + { + component: 'KNavItem', + props: { + title: 'Changelog', + description: 'View the project update history', + icon: 'a-yusuan2', + link: '/preview/changelog' + } + }, + { + component: 'KNavItem', + props: { + title: 'Join OpenMCP', + description: 'Learn how to participate in the development and maintenance of OpenMCP', + icon: 'shujuzhongxin', + link: '/preview/join' + } + }, + { + component: 'KNavItem', + props: { + title: 'OpenMCP Contributors', + description: 'About the people who contributed to OpenMCP', + icon: 'heike', + link: '/preview/contributors' + } + }, + { + component: 'KNavItem', + props: { + title: 'Resource Channel', + description: 'Access resources and information related to the project', + icon: 'xinxiang', + link: '/preview/channel' + } + } + ] + } + ], + sidebar: { + '/plugin-tutorial/': [ + { + text: 'Overview', + items: [ + { text: 'Introduction to OpenMCP', link: '/plugin-tutorial/index' }, + { text: 'What is MCP?', link: '/plugin-tutorial/what-is-mcp' }, + { text: 'Basic Concepts of MCP', link: '/plugin-tutorial/concept' } + ] + }, + { + text: 'Quick Start', + items: [ + { text: 'Quick Start', link: '/plugin-tutorial/quick-start' }, + { text: 'Install OpenMCP', link: '/plugin-tutorial/quick-start/acquire-openmcp' }, + { text: 'Your First MCP', link: '/plugin-tutorial/quick-start/first-mcp' }, + { text: 'Quick Debugging of MCP', link: '/plugin-tutorial/quick-start/quick-debug' }, + { text: 'Throw it into an LLM and test it!', link: '/plugin-tutorial/quick-start/put-into-llm' } + ] + }, + { + text: 'User Guide', + items: [ + { text: 'UI Color Settings', link: '/plugin-tutorial/usage/ui-color' }, + { text: 'Connect to MCP Server', link: '/plugin-tutorial/usage/connect-mcp' }, + { text: 'Debug tools, resources, and prompts', link: '/plugin-tutorial/usage/debug' }, + { text: 'Connect to LLM', link: '/plugin-tutorial/usage/connect-llm' }, + { text: 'Test Your MCP with an LLM', link: '/plugin-tutorial/usage/test-with-llm' }, + { text: 'Connect to Multiple MCP Services', link: '/plugin-tutorial/usage/multi-server' }, + { text: 'Distribute Experiment Results', link: '/plugin-tutorial/usage/distribute-result' }, + { text: 'Implement SSE Authenticator', link: '/plugin-tutorial/usage/sse-oauth2' } + ] + }, + { + text: 'Development Examples', + items: [ + { text: 'MCP Server Development Examples', link: '/plugin-tutorial/examples/mcp-examples' }, + { text: 'Example 1: Weather Info MCP in Python (STDIO)', link: '/plugin-tutorial/examples/python-simple-stdio' }, + { text: 'Example 2: Read-only Neo4j MCP in Go (SSE)', link: '/plugin-tutorial/examples/go-neo4j-sse' }, + { text: 'Example 3: Read-only Document DB MCP in Java (HTTP)', link: '/plugin-tutorial/examples/java-es-http' }, + { text: 'Example 4: Super Web Crawler MCP in TypeScript using crawl4ai (STDIO)', link: '/plugin-tutorial/examples/typescript-crawl4ai-stdio' }, + { text: 'Example 5: Generic Form Filling MCP in Python (STDIO)', link: '/plugin-tutorial/examples/python-form-stdio' }, + { text: 'Example 6: Blender-based MCP in Python (STDIO)', link: '/plugin-tutorial/examples/python-blender-stdio' }, + { text: 'Example 7: Cadence EDA MCP in Python (STDIO)', link: '/plugin-tutorial/examples/python-cadence-stdio' } + ] + }, + { + text: 'FAQ', + items: [ + { text: 'Help', link: '/plugin-tutorial/faq/help' } + ] + } + ], + '/sdk-tutorial/': [ + { + text: 'Overview', + items: [ + { text: 'openmcp‑sdk.js', link: '/sdk-tutorial/' } + ] + }, + { + text: 'Basic Usage', + items: [ + { text: 'Simplest Conversation', link: '/sdk-tutorial/usage/greet' }, + { text: 'Task Loop', link: '/sdk-tutorial/usage/task-loop' }, + { text: 'Multiple Server Connections', link: '/sdk-tutorial/usage/multi-server' } + ] + } + ] + } +} diff --git a/.vitepress/i18n/ja.ts b/.vitepress/i18n/ja.ts new file mode 100644 index 0000000..b2ebae2 --- /dev/null +++ b/.vitepress/i18n/ja.ts @@ -0,0 +1,167 @@ +export default { + nav: [ + { text: 'ホーム', link: '/ja/' }, + { + text: 'チュートリアル', + items: [ + { + component: 'KNavItem', + props: { + title: '概要', + description: 'MCPとOpenMCPについて知っておくべきこと...', + icon: 'openmcp', + link: '/ja/plugin-tutorial/' + } + }, + { + component: 'KNavItem', + props: { + title: 'クイックスタート', + description: '例を通してOpenMCPの基本概念を素早く理解しましょう', + icon: 'quick-fill', + link: '/ja/plugin-tutorial/quick-start/' + } + }, + { + component: 'KNavItem', + props: { + title: 'OpenMCPユーザーガイド', + description: 'OpenMCPクライアントの基本的な使い方', + icon: 'shiyongshouce', + link: '/ja/plugin-tutorial/usage/connect-mcp' + } + }, + { + component: 'KNavItem', + props: { + title: 'MCPサーバー開発例', + description: 'さまざまな言語と方式で開発されたMCPサーバーの例', + icon: 'yibangonggongyusuan', + link: '/ja/plugin-tutorial/examples/mcp-examples' + } + }, + { + component: 'KNavItem', + props: { + title: 'FAQ(よくある質問)', + description: '質問への回答と問題の解決策をご提供します', + icon: 'yijianchuli', + link: '/ja/plugin-tutorial/faq/help' + } + }, + ] + }, + { text: 'SDK', link: '/ja/sdk-tutorial/' }, + { + text: 'もっと見る', + items: [ + { + component: 'KNavItem', + props: { + title: '更新履歴', + description: 'プロジェクトの更新履歴を見る', + icon: 'a-yusuan2', + link: '/ja/preview/changelog' + } + }, + { + component: 'KNavItem', + props: { + title: 'OpenMCP に参加', + description: 'OpenMCP プロジェクトの開発と保守への参加方法を知る', + icon: 'shujuzhongxin', + link: '/ja/preview/join' + } + }, + { + component: 'KNavItem', + props: { + title: 'OpenMCP 貢献者リスト', + description: 'OpenMCP に貢献したメンバーについて', + icon: 'heike', + link: '/ja/preview/contributors' + } + }, + { + component: 'KNavItem', + props: { + title: 'リソースチャンネル', + description: 'プロジェクトに関連するリソースや情報を取得する', + icon: 'xinxiang', + link: '/ja/preview/channel' + } + } + ] + } + ], + sidebar: { + '/ja/plugin-tutorial/': [ + { + text: '概要', + items: [ + { text: 'OpenMCP の概要', link: '/ja/plugin-tutorial/index' }, + { text: 'MCP とは?', link: '/ja/plugin-tutorial/what-is-mcp' }, + { text: 'MCP の基本概念', link: '/ja/plugin-tutorial/concept' } + ] + }, + { + text: 'クイックスタート', + items: [ + { text: 'クイックスタート', link: '/ja/plugin-tutorial/quick-start' }, + { text: 'OpenMCP のインストール', link: '/ja/plugin-tutorial/quick-start/acquire-openmcp' }, + { text: '最初の MCP', link: '/ja/plugin-tutorial/quick-start/first-mcp' }, + { text: 'MCP のクイックデバッグ', link: '/ja/plugin-tutorial/quick-start/quick-debug' }, + { text: '大規模言語モデルで試してみよう!', link: '/ja/plugin-tutorial/quick-start/put-into-llm' } + ] + }, + { + text: '使用ガイド', + items: [ + { text: 'UI カラー設定', link: '/ja/plugin-tutorial/usage/ui-color' }, + { text: 'MCP サーバーへの接続', link: '/ja/plugin-tutorial/usage/connect-mcp' }, + { text: 'tools・resources・prompts のデバッグ', link: '/ja/plugin-tutorial/usage/debug' }, + { text: '大規模言語モデルへの接続', link: '/ja/plugin-tutorial/usage/connect-llm' }, + { text: '大規模言語モデルで MCP をテストする', link: '/ja/plugin-tutorial/usage/test-with-llm' }, + { text: '複数 MCP サーバーの接続', link: '/ja/plugin-tutorial/usage/multi-server' }, + { text: '実験結果の配布', link: '/ja/plugin-tutorial/usage/distribute-result' }, + { text: 'SSE 認証の実装', link: '/ja/plugin-tutorial/usage/sse-oauth2' } + ] + }, + { + text: '開発事例', + items: [ + { text: 'MCP サーバー開発事例', link: '/ja/plugin-tutorial/examples/mcp-examples' }, + { text: '例 1. Python による天気情報 MCP サーバー (STDIO)', link: '/ja/plugin-tutorial/examples/python-simple-stdio' }, + { text: '例 2. Go による neo4j 読み取り専用 MCP サーバー (SSE)', link: '/ja/plugin-tutorial/examples/go-neo4j-sse' }, + { text: '例 3. Java によるドキュメントデータベース MCP (HTTP)', link: '/ja/plugin-tutorial/examples/java-es-http' }, + { text: '例 4. TypeScript による crawl4ai ベースのスーパーウェブクローラー MCP (STDIO)', link: '/ja/plugin-tutorial/examples/typescript-crawl4ai-stdio' }, + { text: '例 5. Python による汎用フォーム入力 MCP (STDIO)', link: '/ja/plugin-tutorial/examples/python-form-stdio' }, + { text: '例 6. Python による Blender ベース MCP (STDIO)', link: '/ja/plugin-tutorial/examples/python-blender-stdio' }, + { text: '例 7. Python による Cadence EDA MCP (STDIO)', link: '/ja/plugin-tutorial/examples/python-cadence-stdio' } + ] + }, + { + text: 'よくある質問', + items: [ + { text: 'ヘルプ', link: '/ja/plugin-tutorial/faq/help' } + ] + } + ], + '/ja/sdk-tutorial/': [ + { + text: '概要', + items: [ + { text: 'openmcp‑sdk.js', link: '/ja/sdk-tutorial/' } + ] + }, + { + text: '基本的な使い方', + items: [ + { text: '最もシンプルな会話', link: '/ja/sdk-tutorial/usage/greet' }, + { text: 'タスクループ', link: '/ja/sdk-tutorial/usage/task-loop' }, + { text: '複数サーバー接続', link: '/ja/sdk-tutorial/usage/multi-server' } + ] + } + ] + } +} diff --git a/.vitepress/i18n/zh.ts b/.vitepress/i18n/zh.ts new file mode 100644 index 0000000..577e1c6 --- /dev/null +++ b/.vitepress/i18n/zh.ts @@ -0,0 +1,167 @@ +export default { + nav: [ + { text: '首页', link: '/zh/' }, + { + text: '教程', + items: [ + { + component: 'KNavItem', + props: { + title: '简介', + description: '关于 mcp 和 openmcp,阁下需要知道的 ...', + icon: 'openmcp', + link: '/zh/plugin-tutorial/' + } + }, + { + component: 'KNavItem', + props: { + title: '快速开始', + description: '通过一个例子快速了解 OpenMCP 的基本概念', + icon: 'quick-fill', + link: '/zh/plugin-tutorial/quick-start/' + } + }, + { + component: 'KNavItem', + props: { + title: 'OpenMCP 使用手册', + description: 'OpenMCP Client 的基本使用', + icon: 'shiyongshouce', + link: '/zh/plugin-tutorial/usage/connect-mcp' + } + }, + { + component: 'KNavItem', + props: { + title: 'MCP 服务器开发案例', + description: '使用不同语言开发的不同模式的 MCP 服务器', + icon: 'yibangonggongyusuan', + link: '/zh/plugin-tutorial/examples/mcp-examples' + } + }, + { + component: 'KNavItem', + props: { + title: 'FAQ', + description: '为您答疑解惑,排忧解难', + icon: 'yijianchuli', + link: '/zh/plugin-tutorial/faq/help' + } + }, + ] + }, + { text: 'SDK', link: '/zh/sdk-tutorial/' }, + { + text: '更多', + items: [ + { + component: 'KNavItem', + props: { + title: '更新日志', + description: '查看项目的更新历史记录', + icon: 'a-yusuan2', + link: '/zh/preview/changelog' + } + }, + { + component: 'KNavItem', + props: { + title: '参与 OpenMCP', + description: '了解如何参与 OpenMCP 项目的开发和维护', + icon: 'shujuzhongxin', + link: '/zh/preview/join' + } + }, + { + component: 'KNavItem', + props: { + title: 'OpenMCP 贡献者列表', + description: '关于参与 OpenMCP 的贡献者们', + icon: 'heike', + link: '/zh/preview/contributors' + } + }, + { + component: 'KNavItem', + props: { + title: '资源频道', + description: '获取项目相关的资源和信息', + icon: 'xinxiang', + link: '/zh/preview/channel' + } + } + ] + }, + ], + sidebar: { + '/zh/plugin-tutorial/': [ + { + text: '简介', + items: [ + { text: 'OpenMCP 概述', link: '/zh/plugin-tutorial/index' }, + { text: '什么是 MCP?', link: '/zh/plugin-tutorial/what-is-mcp' }, + { text: 'MCP 基础概念', link: '/zh/plugin-tutorial/concept' }, + ] + }, + { + text: '快速开始', + items: [ + { text: '快速开始', link: '/zh/plugin-tutorial/quick-start' }, + { text: '安装 OpenMCP', link: '/zh/plugin-tutorial/quick-start/acquire-openmcp' }, + { text: '你的第一个 MCP', link: '/zh/plugin-tutorial/quick-start/first-mcp' }, + { text: '快速调试 MCP', link: '/zh/plugin-tutorial/quick-start/quick-debug' }, + { text: '扔进大模型里面测测好坏!', link: '/zh/plugin-tutorial/quick-start/put-into-llm' }, + ] + }, + { + text: '使用手册', + items: [ + { text: 'UI 配色', link: '/zh/plugin-tutorial/usage/ui-color' }, + { text: '连接 MCP 服务器', link: '/zh/plugin-tutorial/usage/connect-mcp' }, + { text: '调试 tools, resources 和 prompts', link: '/zh/plugin-tutorial/usage/debug' }, + { text: '连接大模型', link: '/zh/plugin-tutorial/usage/connect-llm' }, + { text: '用大模型测试你的 MCP', link: '/zh/plugin-tutorial/usage/test-with-llm' }, + { text: '连接多个 MCP 服务', link: '/zh/plugin-tutorial/usage/multi-server' }, + { text: '分发实验结果', link: '/zh/plugin-tutorial/usage/distribute-result' }, + { text: 'SSE 鉴权器实现', link: '/zh/plugin-tutorial/usage/sse-oauth2' }, + ] + }, + { + text: '开发案例', + items: [ + { text: 'MCP 服务器开发案例', link: '/zh/plugin-tutorial/examples/mcp-examples' }, + { text: '例子 1. python 实现天气信息 mcp 服务器 (STDIO)', link: '/zh/plugin-tutorial/examples/python-simple-stdio' }, + { text: '例子 2. go 实现 neo4j 的只读 mcp 服务器 (SSE)', link: '/zh/plugin-tutorial/examples/go-neo4j-sse' }, + { text: '例子 3. java 实现文档数据库的只读 mcp (HTTP)', link: '/zh/plugin-tutorial/examples/java-es-http' }, + { text: '例子 4. typescript 实现基于 crawl4ai 的超级网页爬虫 mcp (STDIO)', link: '/zh/plugin-tutorial/examples/typescript-crawl4ai-stdio' }, + { text: '例子 5. python 实现进行通用表单填充 的 mcp (STDIO)', link: '/zh/plugin-tutorial/examples/python-form-stdio' }, + { text: '例子 6. python 实现基于 blender 的 mcp (STDIO)', link: '/zh/plugin-tutorial/examples/python-blender-stdio' }, + { text: '例子 7. python 实现 cadence EDA 的 mcp (STDIO)', link: '/zh/plugin-tutorial/examples/python-cadence-stdio' }, + ] + }, + { + text: 'FAQ', + items: [ + { text: '帮助', link: '/zh/plugin-tutorial/faq/help' }, + ] + } + ], + '/zh/sdk-tutorial/': [ + { + text: '简介', + items: [ + { text: 'openmcp‑sdk.js', link: '/zh/sdk-tutorial/' }, + ] + }, + { + text: '基本使用', + items: [ + { text: '最简单的对话', link: '/zh/sdk-tutorial/usage/greet' }, + { text: '任务循环', link: '/zh/sdk-tutorial/usage/task-loop' }, + { text: '多服务器连接', link: '/zh/sdk-tutorial/usage/multi-server' }, + ] + } + ] + } +} \ No newline at end of file diff --git a/.vitepress/theme/.DS_Store b/.vitepress/theme/.DS_Store new file mode 100644 index 0000000..fdf857a Binary files /dev/null and b/.vitepress/theme/.DS_Store differ diff --git a/.vitepress/theme/Layout.vue b/.vitepress/theme/Layout.vue index 82606a0..4c8fd07 100644 --- a/.vitepress/theme/Layout.vue +++ b/.vitepress/theme/Layout.vue @@ -3,6 +3,15 @@ + + + + + @@ -10,7 +19,7 @@ + + + + \ No newline at end of file diff --git a/.vitepress/theme/components/Contributors/index.vue b/.vitepress/theme/components/Contributors/index.vue index 346f8e2..6d70f88 100644 --- a/.vitepress/theme/components/Contributors/index.vue +++ b/.vitepress/theme/components/Contributors/index.vue @@ -2,10 +2,10 @@ @@ -16,6 +16,27 @@ import { computed, PropType } from 'vue'; import { VPTeamPage, VPTeamPageTitle, VPTeamMembers } from 'vitepress/theme'; import { customIcons } from '../../hook/icons'; +import { useData } from 'vitepress' + +const { lang } = useData(); + +console.log(lang.value); + + +const messages = { + zh: { + title: 'OpenMCP 贡献者列表', + lead: 'OpenMCP 是一个非盈利的开源项目,它由对编程和AI技术热爱的开发者共同开发。我们欢迎任何有兴趣参与的开发者加入我们的项目中,一起努力提高AI技术的应用水平。' + }, + en: { + title: 'OpenMCP Contributors', + lead: 'OpenMCP is a non-profit open source project developed by developers passionate about programming and AI technology. We welcome any interested developers to join our project and work together to improve the application of AI technology.' + }, + ja: { + title: 'OpenMCP コントリビューター一覧', + lead: 'OpenMCPは、プログラミングとAI技術を愛する開発者によって共同開発された非営利のオープンソースプロジェクトです。興味のある開発者の参加を歓迎し、AI技術の応用向上に一緒に取り組みましょう。' + } +} interface Contributor { name: string; diff --git a/.vitepress/theme/components/KTab/index.vue b/.vitepress/theme/components/KTab/index.vue index c106926..7d4b88d 100644 --- a/.vitepress/theme/components/KTab/index.vue +++ b/.vitepress/theme/components/KTab/index.vue @@ -33,9 +33,9 @@ const tabsContainer = reactive({ paneInfos: [] as PaneInfo[], panes: [] as HTMLElement[], lastPaneId: 0, - panelContainer: undefined as HTMLElement | undefined, + panelContainer: undefined as any, - getPanes(el: HTMLElement | null, id: number) { + getPanes(el: any, id: number) { if (el) this.panes[id] = el; }, @@ -135,6 +135,12 @@ onMounted(() => { gap: 8px; } +@media screen and (max-width: 741px) { + .k-tabs-tags { + flex-direction: column; + } +} + .k-tabs-tag-item { background-color: var(--vp-button-alt-bg); border-radius: .5em; @@ -150,11 +156,40 @@ onMounted(() => { transform: scale(1.05); } -.k-tabs-content { +html[lang="zh"] .k-tabs-content { position: relative; - min-height: 200px; + min-height: 500px; } +html[lang="en"] .k-tabs-content { + position: relative; + min-height: 600px; +} + +html[lang="ja"] .k-tabs-content { + position: relative; + min-height: 600px; +} + + +@media screen and (max-width: 741px) { + html[lang="zh"] .k-tabs-content { + position: relative; + min-height: 600px; + } + + html[lang="en"] .k-tabs-content { + position: relative; + min-height: 900px; + } + + html[lang="ja"] .k-tabs-content { + position: relative; + min-height: 760px; + } +} + + .k-tabs-content>* { position: absolute; top: 0; diff --git a/.vitepress/theme/components/bilibli-player/index.vue b/.vitepress/theme/components/bilibli-player/index.vue index dfbac7c..3724207 100644 --- a/.vitepress/theme/components/bilibli-player/index.vue +++ b/.vitepress/theme/components/bilibli-player/index.vue @@ -99,6 +99,8 @@ onMounted(() => { min-height: 225px; width: 52.36vw; height: 28.26vw; + border: 2px solid var(--vp-c-brand-3); + border-radius: 8px; aspect-ratio: 16/9; border-radius: .5em; overflow: hidden; @@ -106,6 +108,28 @@ onMounted(() => { box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); } + +@media (max-width: 2700px) { + .bilibili-player-container { + width: 88%; + height: 100%; + } +} + +@media (max-width: 1200px) { + .bilibili-player-container { + width: 95%; + height: 100%; + } +} + +@media screen and (max-width: 741px) { + .bilibili-player-container iframe { + width: 95%; + height: 230px !important; + } +} + .bilibili-player-container:hover { border-color: var(--vp-c-brand-1); box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15); diff --git a/.vitepress/theme/css/giscus.css b/.vitepress/theme/css/giscus.css new file mode 100644 index 0000000..354c7ee --- /dev/null +++ b/.vitepress/theme/css/giscus.css @@ -0,0 +1,187 @@ +/* Giscus评论系统主题适配 */ +:root { + /* 浅色主题变量 */ + --giscus-light-bg: #ffffff; + --giscus-light-text: #22272e; + --giscus-light-border: #e1e4e8; + --giscus-light-btn-bg: #f6f8fa; + --giscus-light-btn-hover: #f3f4f6; + --giscus-light-comment-bg: #f6f8fa; + --giscus-light-link: var(--vp-c-brand-2); + --giscus-light-code-bg: rgba(175, 184, 193, 0.2); + + /* 深色主题变量 */ + --giscus-dark-bg: #15202b; + --giscus-dark-text: #adbac7; + --giscus-dark-border: #444c56; + --giscus-dark-btn-bg: #373e47; + --giscus-dark-btn-hover: #444c56; + --giscus-dark-comment-bg: #1e2d3b; + --giscus-dark-link: var(--vp-c-brand-1); + --giscus-dark-code-bg: rgba(99, 110, 123, 0.4); +} + +/* 基础样式 */ +main { + /* 使用您的品牌色重新定义语法高亮 */ + --color-prettylights-syntax-comment: var(--vp-c-text-2); + --color-prettylights-syntax-constant: var(--vp-c-brand-1); + --color-prettylights-syntax-entity: var(--vp-c-brand-2); + --color-prettylights-syntax-storage-modifier-import: var(--vp-c-text-1); + --color-prettylights-syntax-entity-tag: #8ddb8c; + --color-prettylights-syntax-keyword: #f47067; + --color-prettylights-syntax-string: #96d0ff; + --color-prettylights-syntax-variable: #f69d50; + --color-prettylights-syntax-brackethighlighter-unmatched: #e5534b; + --color-prettylights-syntax-invalid-illegal-text: var(--vp-c-text-1); + --color-prettylights-syntax-invalid-illegal-bg: #922323; + --color-prettylights-syntax-carriage-return-text: var(--vp-c-text-1); + --color-prettylights-syntax-carriage-return-bg: #ad2e2c; + --color-prettylights-syntax-string-regexp: #8ddb8c; + --color-prettylights-syntax-markup-list: #eac55f; + --color-prettylights-syntax-markup-heading: var(--vp-c-brand-3); + --color-prettylights-syntax-markup-italic: var(--vp-c-text-1); + --color-prettylights-syntax-markup-bold: var(--vp-c-text-1); + --color-prettylights-syntax-markup-deleted-text: #ffd8d3; + --color-prettylights-syntax-markup-deleted-bg: #78191b; + --color-prettylights-syntax-markup-inserted-text: #b4f1b4; + --color-prettylights-syntax-markup-inserted-bg: #1b4721; + --color-prettylights-syntax-markup-changed-text: #ffddb0; + --color-prettylights-syntax-markup-changed-bg: #682d0f; + --color-prettylights-syntax-markup-ignored-text: var(--vp-c-text-2); + --color-prettylights-syntax-markup-ignored-bg: #255ab2; + --color-prettylights-syntax-meta-diff-range: var(--vp-c-brand-2); + + /* 按钮样式 */ + --color-btn-text: var(--vp-c-text-1); + --color-btn-bg: var(--vp-c-default-soft); + --color-btn-border: var(--vp-c-border); + --color-btn-shadow: 0 0 transparent; + --color-btn-inset-shadow: 0 0 transparent; + --color-btn-hover-bg: var(--vp-c-default-2); + --color-btn-hover-border: var(--vp-c-border); + --color-btn-active-bg: var(--vp-c-default-3); + --color-btn-active-border: var(--vp-c-border); + --color-btn-selected-bg: var(--vp-c-default-3); + + /* 主按钮样式 - 使用您的品牌色 */ + --color-btn-primary-text: var(--vp-c-white); + --color-btn-primary-bg: var(--vp-c-brand-3); + --color-btn-primary-border: transparent; + --color-btn-primary-shadow: 0 0 transparent; + --color-btn-primary-inset-shadow: 0 0 transparent; + --color-btn-primary-hover-bg: var(--vp-c-brand-2); + --color-btn-primary-hover-border: transparent; + --color-btn-primary-selected-bg: var(--vp-c-brand-3); + --color-btn-primary-selected-shadow: 0 0 transparent; + --color-btn-primary-disabled-text: rgba(255, 255, 255, 0.5); + --color-btn-primary-disabled-bg: rgba(163, 104, 184, 0.6); + --color-btn-primary-disabled-border: transparent; + + /* 其他元素 */ + --color-fg-default: var(--vp-c-text-1); + --color-fg-muted: var(--vp-c-text-2); + --color-fg-subtle: var(--vp-c-text-3); + --color-canvas-default: var(--vp-c-bg); + --color-canvas-overlay: var(--vp-c-bg-soft); + --color-canvas-inset: var(--vp-c-bg-alt); + --color-canvas-subtle: var(--vp-c-bg-soft); + --color-border-default: var(--vp-c-border); + --color-border-muted: var(--vp-c-divider); + --color-accent-fg: var(--vp-c-brand-1); + --color-accent-emphasis: var(--vp-c-brand-3); + --color-accent-muted: var(--vp-c-brand-soft); + --color-accent-subtle: var(--vp-c-brand-soft); + + /* 加载动画 */ + --color-homepage-bg: var(--vp-c-bg); +} + +/* 浅色主题适配 */ +@media (prefers-color-scheme: light) { + main { + --color-canvas-default: var(--giscus-light-bg); + --color-canvas-overlay: var(--giscus-light-comment-bg); + --color-canvas-inset: var(--giscus-light-comment-bg); + --color-canvas-subtle: var(--giscus-light-btn-bg); + --color-fg-default: var(--giscus-light-text); + --color-fg-muted: #57606a; + --color-fg-subtle: #6e7781; + --color-border-default: var(--giscus-light-border); + --color-border-muted: #d0d7de; + --color-btn-bg: var(--giscus-light-btn-bg); + --color-btn-hover-bg: var(--giscus-light-btn-hover); + --color-accent-fg: var(--giscus-light-link); + --color-prettylights-syntax-comment: #6e7781; + } +} + +/* 深色主题适配 */ +@media (prefers-color-scheme: dark) { + main { + --color-canvas-default: var(--giscus-dark-bg); + --color-canvas-overlay: var(--giscus-dark-comment-bg); + --color-canvas-inset: var(--giscus-dark-comment-bg); + --color-canvas-subtle: var(--giscus-dark-btn-bg); + --color-fg-default: var(--giscus-dark-text); + --color-fg-muted: #768390; + --color-fg-subtle: #545d68; + --color-border-default: var(--giscus-dark-border); + --color-border-muted: #373e47; + --color-btn-bg: var(--giscus-dark-btn-bg); + --color-btn-hover-bg: var(--giscus-dark-btn-hover); + --color-accent-fg: var(--giscus-dark-link); + --color-prettylights-syntax-comment: #768390; + } +} + +/* 自定义样式 */ +.gsc-reactions-count { + display: none; +} + +.gsc-timeline { + flex-direction: column-reverse; +} + +.gsc-header { + padding-bottom: 1rem; +} + +.gsc-comments>.gsc-header { + order: 1; +} + +.gsc-comments>.gsc-comment-box { + margin-bottom: 1rem; + order: 2; +} + +.gsc-comments>.gsc-timeline { + order: 3; +} + +main .gsc-loading-image { + background-image: url(https://github.githubassets.com/images/mona-loading-dimmed.gif); +} + +/* 链接颜色 */ +.gsc-comment a { + color: var(--color-accent-fg) !important; +} + +/* 代码块背景 */ +.gsc-comment markdown-body pre { + background-color: var(--color-canvas-subtle) !important; +} + +/* 评论框样式 */ +.gsc-comment-box { + border: 1px solid var(--color-border-default) !important; + background-color: var(--color-canvas-default) !important; +} + +/* 按钮悬停效果 */ +.gsc-reaction-button:hover { + background-color: var(--color-btn-hover-bg) !important; +} \ No newline at end of file diff --git a/.vitepress/theme/css/style.css b/.vitepress/theme/css/style.css index ffc7521..ae0825d 100644 --- a/.vitepress/theme/css/style.css +++ b/.vitepress/theme/css/style.css @@ -217,6 +217,12 @@ iframe { transition: border-color 0.3s ease; } +@media screen and (max-width: 741px) { + iframe { + height: 210px; + width: 100%; + } +} .VPMenu { background-color: rgba(255, 255, 255, 0.55) !important; @@ -272,7 +278,7 @@ iframe { background-clip: text; -webkit-text-fill-color: var(--vp-home-hero-name-color); } - +/* .VPHero .heading .text { } @@ -281,7 +287,7 @@ iframe { .VPHero .actions { } - + */ #home-0 { font-size: 40px; @@ -314,4 +320,5 @@ iframe { font-size: 20px; opacity: 0.5; line-height: 0.8; -} \ No newline at end of file +} + diff --git a/.vitepress/theme/iconfont.css b/.vitepress/theme/iconfont.css new file mode 100644 index 0000000..b6d17ca --- /dev/null +++ b/.vitepress/theme/iconfont.css @@ -0,0 +1,95 @@ +@font-face { + font-family: "iconfont"; /* Project id 4933953 */ + src: url('iconfont.woff2?t=1748520354582') format('woff2'), + url('iconfont.woff?t=1748520354582') format('woff'), + url('iconfont.ttf?t=1748520354582') format('truetype'); +} + +.iconfont { + font-family: "iconfont" !important; + + font-style: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.icon-quick-fill:before { + content: "\e863"; +} + +.icon-heike:before { + content: "\e6c5"; +} + +.icon-bumendongtai:before { + content: "\e61f"; +} + +.icon-duzhengyishi:before { + content: "\e620"; +} + +.icon-lianwangzhongxin:before { + content: "\e621"; +} + +.icon-fenxitongji:before { + content: "\e622"; +} + +.icon-shujuzhongxin:before { + content: "\e623"; +} + +.icon-shuju:before { + content: "\e624"; +} + +.icon-shenji:before { + content: "\e625"; +} + +.icon-yusuan:before { + content: "\e626"; +} + +.icon-yibangonggongyusuan:before { + content: "\e627"; +} + +.icon-xinxiang:before { + content: "\e628"; +} + +.icon-yujing:before { + content: "\e629"; +} + +.icon-yijianchuli:before { + content: "\e62a"; +} + +.icon-zhuanti:before { + content: "\e62b"; +} + +.icon-a-yusuan2:before { + content: "\e62c"; +} + +.icon-yujuesuanshencha:before { + content: "\e62d"; +} + +.icon-zhengcefagui:before { + content: "\e62e"; +} + +.icon-ziliao:before { + content: "\e62f"; +} + +.icon-zixuntousu:before { + content: "\e630"; +} + diff --git a/.vitepress/theme/iconfont.woff2 b/.vitepress/theme/iconfont.woff2 new file mode 100644 index 0000000..37b6383 Binary files /dev/null and b/.vitepress/theme/iconfont.woff2 differ diff --git a/.vitepress/theme/index.mts b/.vitepress/theme/index.mts index 2bb7f7b..f70e93f 100644 --- a/.vitepress/theme/index.mts +++ b/.vitepress/theme/index.mts @@ -1,6 +1,8 @@ // https://vitepress.dev/guide/custom-theme import { h } from 'vue'; import type { Theme } from 'vitepress'; +import { inBrowser } from 'vitepress'; +import busuanzi from 'busuanzi.pure.js'; import DefaultTheme from 'vitepress/theme'; import CustomLayout from './Layout.vue'; @@ -14,7 +16,7 @@ import Contributors from './components/Contributors/index.vue'; import { NolebaseGitChangelogPlugin } from '@nolebase/vitepress-plugin-git-changelog/client'; import { NolebaseInlineLinkPreviewPlugin } from '@nolebase/vitepress-plugin-inline-link-preview/client'; import { NolebaseUnlazyImg } from '@nolebase/vitepress-plugin-thumbnail-hash/client'; - + import { ElCollapse, ElCollapseItem, ElTimeline, ElTimelineItem } from 'element-plus'; import './css/style.css'; @@ -46,5 +48,12 @@ export default { app.use(NolebaseGitChangelogPlugin); app.use(NolebaseInlineLinkPreviewPlugin); + + // 添加浏览量统计 + if (inBrowser) { + router.onAfterRouteChange = () => { + busuanzi.fetch() + } + } } } satisfies Theme diff --git a/.vscode/.DS_Store b/.vscode/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/.vscode/.DS_Store differ diff --git a/.vscode/openmcp_connection.json b/.vscode/openmcp_connection.json deleted file mode 100644 index 36fe563..0000000 --- a/.vscode/openmcp_connection.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "items": [ - [], - [], - [], - [ - { - "connectionType": "SSE", - "name": "SSE-1748530644202", - "version": "1.0", - "url": "http://localhost:8000/mcp", - "oauth": "" - } - ], - [], - [], - [], - [], - [], - [ - { - "connectionType": "SSE", - "name": "SSE-1748531219080", - "version": "1.0", - "url": "http://localhost:8000/sse", - "oauth": "" - } - ], - [] - ] -} \ No newline at end of file diff --git a/.vscode/tabs.my-browser.json b/.vscode/tabs.my-browser.json deleted file mode 100644 index 0c226c8..0000000 --- a/.vscode/tabs.my-browser.json +++ /dev/null @@ -1,215 +0,0 @@ -{ - "clientId": "81b3b1d6-b1d6-53b1d63c156-83b1d63c156a678-6a678e36", - "currentIndex": 0, - "tabs": [ - { - "name": "交互测试", - "icon": "icon-robot", - "type": "blank", - "componentIndex": 3, - "storage": { - "activeNames": [ - 0 - ], - "messages": [ - { - "role": "user", - "content": "使用 ffmpeg 把视频 (0, 65) 到 (1173, 769) 的位置截取出来,视频的格式是 mov,我希望导出为 mp4", - "extraInfo": { - "created": 1749111492698, - "state": "success", - "serverName": "deepseek" - } - }, - { - "role": "assistant", - "content": "你可以使用 `ffmpeg` 的 `crop` 滤镜来截取视频的指定区域,并将 `.mov` 转换为 `.mp4` 格式。以下是具体的命令:\n\n### 命令解析:\n1. **输入文件**:`input.mov`(替换为你的文件名)\n2. **裁剪参数**:\n - `crop=w:h:x:y`:从坐标 `(x,y)` 开始,截取宽度 `w`、高度 `h` 的区域。\n - 你的需求是从 `(0, 65)` 到 `(1173, 769)`,因此:\n - 宽度 `w = 1173 - 0 = 1173`\n - 高度 `h = 769 - 65 = 704`\n - 起始坐标 `(x,y) = (0, 65)`\n3. **输出格式**:`mp4`(默认使用 H.264 编码)\n\n### 完整命令:\n```bash\nffmpeg -i input.mov -filter:v \"crop=1173:704:0:65\" -c:a copy output.mp4\n```\n\n### 参数说明:\n- `-i input.mov`:指定输入文件。\n- `-filter:v \"crop=1173:704:0:65\"`:视频裁剪滤镜。\n- `-c:a copy`:直接复制音频流(无需重新编码,节省时间)。\n- `output.mp4`:输出文件名。\n\n### 其他选项:\n- 如果需要控制视频质量,可以添加 `-crf` 参数(默认 23,值越小质量越高):\n ```bash\n ffmpeg -i input.mov -filter:v \"crop=1173:704:0:65\" -crf 18 -c:a copy output.mp4\n ```\n- 如果需要硬件加速(如 NVIDIA GPU),可以添加 `-hwaccel cuda` 等参数。\n\n### 注意:\n1. 确保 `ffmpeg` 已安装并添加到系统路径。\n2. 如果视频有旋转/缩放等元数据,可能需要先处理(如用 `-vf \"transpose=1\"` 旋转)。\n\n运行命令后,`output.mp4` 就是你需要的裁剪后视频。", - "extraInfo": { - "created": 1749111520497, - "state": "success", - "serverName": "deepseek", - "usage": { - "prompt_tokens": 42, - "completion_tokens": 487, - "total_tokens": 529, - "prompt_tokens_details": { - "cached_tokens": 0 - }, - "prompt_cache_hit_tokens": 0, - "prompt_cache_miss_tokens": 42 - } - } - } - ], - "settings": { - "modelIndex": 0, - "enableTools": [ - { - "name": "k_navigate", - "description": "Navigate to a URL", - "inputSchema": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "URL to navigate to" - }, - "launchOptions": { - "type": "object", - "description": "PuppeteerJS LaunchOptions. Default null. If changed and not null, browser restarts. Example: { headless: true, args: ['--no-sandbox'] }" - }, - "allowDangerous": { - "type": "boolean", - "description": "Allow dangerous LaunchOptions that reduce security. When false, dangerous args like --no-sandbox will throw errors. Default false." - } - }, - "required": [ - "url" - ] - }, - "enabled": true - }, - { - "name": "k_screenshot", - "description": "Take a screenshot of the current page or a specific element", - "inputSchema": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Name for the screenshot" - }, - "selector": { - "type": "string", - "description": "CSS selector for element to screenshot" - }, - "width": { - "type": "number", - "description": "Width in pixels (default: 800)" - }, - "height": { - "type": "number", - "description": "Height in pixels (default: 600)" - } - }, - "required": [ - "name" - ] - }, - "enabled": true - }, - { - "name": "k_click", - "description": "Click an element on the page", - "inputSchema": { - "type": "object", - "properties": { - "selector": { - "type": "string", - "description": "CSS selector for element to click" - } - }, - "required": [ - "selector" - ] - }, - "enabled": true - }, - { - "name": "k_fill", - "description": "Fill out an input field", - "inputSchema": { - "type": "object", - "properties": { - "selector": { - "type": "string", - "description": "CSS selector for input field" - }, - "value": { - "type": "string", - "description": "Value to fill" - } - }, - "required": [ - "selector", - "value" - ] - }, - "enabled": true - }, - { - "name": "k_select", - "description": "Select an element on the page with Select tag", - "inputSchema": { - "type": "object", - "properties": { - "selector": { - "type": "string", - "description": "CSS selector for element to select" - }, - "value": { - "type": "string", - "description": "Value to select" - } - }, - "required": [ - "selector", - "value" - ] - }, - "enabled": true - }, - { - "name": "k_hover", - "description": "Hover an element on the page", - "inputSchema": { - "type": "object", - "properties": { - "selector": { - "type": "string", - "description": "CSS selector for element to hover" - } - }, - "required": [ - "selector" - ] - }, - "enabled": true - }, - { - "name": "k_evaluate", - "description": "Execute JavaScript in the browser console", - "inputSchema": { - "type": "object", - "properties": { - "script": { - "type": "string", - "description": "JavaScript code to execute" - } - }, - "required": [ - "script" - ] - }, - "enabled": true - }, - { - "name": "k_get_full_page_text", - "description": "获取页面所有文本内容", - "inputSchema": { - "type": "object", - "properties": {} - }, - "enabled": true - } - ], - "enableWebSearch": false, - "temperature": 0.6, - "contextLength": 20, - "systemPrompt": "猫娘", - "parallelToolCalls": true - } - } - } - ] -} \ No newline at end of file diff --git a/images/.DS_Store b/images/.DS_Store index d8d844a..633f1f9 100644 Binary files a/images/.DS_Store and b/images/.DS_Store differ diff --git a/images/favicon.png 16-02-07-980.png b/images/favicon.png 16-02-07-980.png new file mode 100644 index 0000000..a0647be Binary files /dev/null and b/images/favicon.png 16-02-07-980.png differ diff --git a/images/icons/.DS_Store b/images/icons/.DS_Store new file mode 100644 index 0000000..d82fade Binary files /dev/null and b/images/icons/.DS_Store differ diff --git a/images/icons/ai.svg b/images/icons/ai.svg new file mode 100644 index 0000000..5386a79 --- /dev/null +++ b/images/icons/ai.svg @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/images/icons/group.svg b/images/icons/group.svg new file mode 100644 index 0000000..ab4c8b4 --- /dev/null +++ b/images/icons/group.svg @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/images/icons/image.png b/images/icons/image.png new file mode 100644 index 0000000..232234b Binary files /dev/null and b/images/icons/image.png differ diff --git a/images/icons/monitor.svg b/images/icons/monitor.svg new file mode 100644 index 0000000..f605195 --- /dev/null +++ b/images/icons/monitor.svg @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/images/icons/openmcp-edge.svg 15-41-10-675.svg b/images/icons/openmcp-edge.svg 15-41-10-675.svg new file mode 100644 index 0000000..3ddc7d8 --- /dev/null +++ b/images/icons/openmcp-edge.svg 15-41-10-675.svg @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/index.md b/index.md index e61dcfe..28c2f6b 100644 --- a/index.md +++ b/index.md @@ -4,12 +4,12 @@ layout: home hero: name: "OpenMCP" - text: "面向优雅开发者的 MCP 调试器和 SDK" - tagline: 缩短从大语言模型到智能体的最后一公里 + text: "MCP Debugger and SDK for Elegant Developers" + tagline: Bridge the last mile from large language models to intelligent agents actions: - theme: brand - text: OpenMCP 插件 + text: OpenMCP Plugin link: ./plugin-tutorial - theme: alt text: openmcp-sdk @@ -21,43 +21,39 @@ features: - icon: src: /images/icons/vscode.svg height: 48px - alt: 集成调试环境 - title: 集成调试环境 - details: 将检查器与 MCP 客户端功能相结合,实现无缝开发和测试 + alt: Integrated Debugging Environment + title: Integrated Debugging Environment + details: Combine the inspector with MCP client functions to achieve seamless development and testing - icon: src: /images/icons/openmcp-edge.svg height: 48px - alt: 提供完整的项目级控制面板 - title: 全面的项目管理 - details: 提供完整的项目级控制面板,实现高效的 MCP 项目监督 + alt: Provide a complete project-level control panel + title: Comprehensive Project Management + details: Provide a complete project-level control panel for efficient MCP project supervision - icon: src: /images/icons/openmcp-sdk.svg height: 48px - alt: 提供完整的项目级控制面板 - title: 完整的部署方案 - details: 将测试完成的 agent 通过 openmcp-sdk 部署到您的应用或者服务器上 + alt: Provide a complete project-level control panel + title: Complete Deployment Solution + details: Deploy tested agents to your application or server via openmcp-sdk ---

- -

-为您的 MCP Agent 开发排忧解难 +Resolve Issues in Your MCP Agent Development
Providing Fun and Convenience for Your MCP Agent Development

- +
+ +

-

-OpenMCP 为谁准备? +Who is OpenMCP for?
The Development of OpenMCP is for ...

@@ -66,34 +62,34 @@ OpenMCP 为谁准备? @@ -102,30 +98,30 @@ OpenMCP 为谁准备?

-问题解答 +FAQ
Waiting for Your Questions

- - 正如它的名字一样,OpenMCP 是一个面向开发者的 MCP 调试器和 SDK,致力于降低 AI Agent 的全链路开发成本和开发人员的心智负担。通过 OpenMCP 制作出可以在真实生活场景中解决问题,缩短工作时间的 mcp 工具,或是让工程师与研发科学家更快地交付 demo,并将这份愿景让公众看到,是我们的任务和使命。 + + As its name suggests, OpenMCP is an MCP debugger and SDK for developers, committed to reducing the full - chain development cost of AI agents and the mental burden of developers. Our mission is to create MCP tools that can solve real - life problems and save working time through OpenMCP, or help engineers and research scientists deliver demos more quickly and make this vision visible to the public. - - 是的,OpenMCP 完全开源,您不仅可以免费使用此产品,也可以一起加入我们,实现你的关于 Agent 的奇思妙想。OpenMCP 的任务是建立起关于 MCP 的生态圈。因为我们认为,MCP 的开发在未来一段时间内会是一项高度定制化的工作,所以当前的重点并不是赶紧出做一个看起来什么都能做的 Agent,而是步步为营做出相关的生态和基础设施。 + + Yes, OpenMCP is completely open - source. You can not only use this product for free but also join us to realize your creative ideas about agents. The task of OpenMCP is to build an ecosystem around MCP. We believe that MCP development will be a highly customized task in the future, so our current focus is not to rush to create an all - purpose agent, but to steadily build the relevant ecosystem and infrastructure. - - 如果你试图通过 OpenMCP 开发一款什么都能做的,通用的 AI Agent,你应该做的是把钱全部投资到量子计算机的研发,而不是点开这个网站。记住一句话,这个时代做全领域通用AI Agent,依概率收敛到电信诈骗。 + + If you try to develop an all - purpose, general AI agent through OpenMCP, you should invest all your money in the research and development of quantum computers instead of visiting this website. Remember, in this era, developing a full - domain general AI agent is likely to be equivalent to telecom fraud. - -

OpenMCP 是由 LSTM-Kirigaya(锦恢) 最初主导开发的,用于构建 3D 相关工作的 mcp 测试工具。它的主要参与者都是大厂在职员工,高校计算机相关专业的学生、以及一些开源社区的活跃贡献者。

-

身份不重要,我非常喜欢的一句话,送给阁下:“不要回答我你会不会,回答我,你喜不喜欢”。

+ +

OpenMCP was initially led by LSTM - Kirigaya (Jinhui) for building MCP testing tools related to 3D work. Its main participants include employees from large companies, students majoring in computer - related fields at universities, and some active contributors from the open - source community.

+

Identity is not important. I'd like to share a quote with you: "Don't tell me if you can do it. Tell me if you like it."

- - 您可以通过 参与 OpenMCP 来了解如何参与 OpenMCP 的维护和开发。通过 资源频道 来获取我们的联系方式。目前主要的社区有三个,QQ群:782833642 、 OpenMCP Discord 频道知乎圈子【OpenMCP 博物馆】 + + You can learn how to participate in the maintenance and development of OpenMCP through Participate in OpenMCP. Obtain our contact information through Resource Channel. Currently, there are three main communities: QQ group: 782833642, OpenMCP Discord Channel, and Zhihu Circle [OpenMCP Museum] - - 合作请联系锦恢的个人邮箱:1193466151@qq.com + + For cooperation, please contact Jinhui's personal email: 1193466151@qq.com -
+ \ No newline at end of file diff --git a/ja/images/.DS_Store b/ja/images/.DS_Store new file mode 100644 index 0000000..d8d844a Binary files /dev/null and b/ja/images/.DS_Store differ diff --git a/ja/images/favicon.svg b/ja/images/favicon.svg new file mode 100644 index 0000000..3267d35 --- /dev/null +++ b/ja/images/favicon.svg @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ja/images/icons/openmcp-edge.free b/ja/images/icons/openmcp-edge.free new file mode 100644 index 0000000..1893973 Binary files /dev/null and b/ja/images/icons/openmcp-edge.free differ diff --git a/ja/images/icons/openmcp-edge.svg b/ja/images/icons/openmcp-edge.svg new file mode 100644 index 0000000..3267d35 --- /dev/null +++ b/ja/images/icons/openmcp-edge.svg @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ja/images/icons/openmcp-sdk.svg b/ja/images/icons/openmcp-sdk.svg new file mode 100644 index 0000000..60f824f --- /dev/null +++ b/ja/images/icons/openmcp-sdk.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ja/images/icons/openmcp.svg b/ja/images/icons/openmcp.svg new file mode 100644 index 0000000..8a81d2f --- /dev/null +++ b/ja/images/icons/openmcp.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ja/images/icons/vscode.svg b/ja/images/icons/vscode.svg new file mode 100644 index 0000000..c453e63 --- /dev/null +++ b/ja/images/icons/vscode.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ja/images/openmcp-default.png b/ja/images/openmcp-default.png new file mode 100644 index 0000000..76a21fe Binary files /dev/null and b/ja/images/openmcp-default.png differ diff --git a/ja/images/openmcp.chatbot.png b/ja/images/openmcp.chatbot.png new file mode 100644 index 0000000..6b50065 Binary files /dev/null and b/ja/images/openmcp.chatbot.png differ diff --git a/ja/images/openmcp.png b/ja/images/openmcp.png new file mode 100644 index 0000000..a0647be Binary files /dev/null and b/ja/images/openmcp.png differ diff --git a/ja/images/opensource.png b/ja/images/opensource.png new file mode 100644 index 0000000..f3d6402 Binary files /dev/null and b/ja/images/opensource.png differ diff --git a/ja/images/public-deploy.png b/ja/images/public-deploy.png new file mode 100644 index 0000000..e3b0bd4 Binary files /dev/null and b/ja/images/public-deploy.png differ diff --git a/ja/index.md b/ja/index.md new file mode 100644 index 0000000..6414022 --- /dev/null +++ b/ja/index.md @@ -0,0 +1,127 @@ +--- +# https://vitepress.dev/reference/default-theme-home-page +layout: home + +hero: + name: "OpenMCP" + text: "エレガントな開発者向けのMCPデバッガーとSDK" + tagline: 大規模言語モデルからエージェントまでの最後の一歩を埋める + + actions: + - theme: brand + text: OpenMCPプラグイン + link: ./plugin-tutorial + - theme: alt + text: openmcp-sdk + link: ./sdk-tutorial + - theme: alt + text: GitHub + link: https://github.com/LSTM-Kirigaya/openmcp-client +features: + - icon: + src: /images/icons/vscode.svg + height: 48px + alt: 統合デバッグ環境 + title: 統合デバッグ環境 + details: インスペクターとMCPクライアント機能を組み合わせて、シームレスな開発とテストを実現します。 + - icon: + src: /images/icons/openmcp-edge.svg + height: 48px + alt: 完全なプロジェクトレベルのコントロールパネルを提供する + title: 包括的なプロジェクト管理 + details: 完全なプロジェクトレベルのコントロールパネルを提供し、効率的なMCPプロジェクトの監視を実現します。 + - icon: + src: /images/icons/openmcp-sdk.svg + height: 48px + alt: 完全なプロジェクトレベルのコントロールパネルを提供する + title: 完全なデプロイメントソリューション + details: テストが完了したエージェントをopenmcp-sdkを通じてあなたのアプリケーションやサーバーにデプロイします。 +--- + +

+ +

+あなたのMCPエージェント開発のお手伝いをします +
+MCPエージェント開発に楽しさと利便性をもたらす +

+ +
+ +
+ +
+ +

+OpenMCPは誰のためのものですか? +
+OpenMCPの開発は... +

+ +
+ + + + + + + +
+ +

+よくある質問 +
+あなたの質問をお待ちしています +

+ + + + その名前の通り、OpenMCPは開発者向けのMCPデバッガーとSDKであり、AIエージェントの全工程の開発コストと開発者の負担を軽減することに取り組んでいます。OpenMCPを通じて、実生活のシーンで問題を解決し、作業時間を短縮できるMCPツールを作成したり、エンジニアや研究開発科学者がより早くデモを提供し、このビジョンを一般に知らしめることが私たちの使命です。 + + + はい、OpenMCPは完全にオープンソースです。あなたはこの製品を無料で使用できるだけでなく、私たちに参加してエージェントに関するあなたの独創的なアイデアを実現することもできます。OpenMCPのタスクは、MCPに関するエコシステムを構築することです。私たちは、MCPの開発は今後しばらくの間高度にカスタマイズされた作業になると考えているため、現在の重点はすぐに何でもできるようなエージェントを作ることではなく、着実に関連するエコシステムやインフラストラクチャを構築することです。 + + + OpenMCPを使って何でもできる汎用的なAIエージェントを開発しようとする場合は、このサイトを開くのではなく、すべての資金を量子コンピュータの研究開発に投資するべきです。この時代に全分野の汎用AIエージェントを作ることは、確率的に電話詐欺に収束することを忘れないでください。 + + +

OpenMCPは、LSTM-Kirigaya(錦恢)によって最初に主導され、3D関連の作業用のMCPテストツールを構築するために開発されました。その主な参加者は、大手企業の従業員、大学のコンピュータ関連学科の学生、およびいくつかのオープンソースコミュニティの積極的な貢献者です。

+

身元は重要ではありません。私がとても好きな一言をあなたに贈ります:「できるかどうかを答えるのではなく、好きかどうかを答えてください」。

+ +
+ + OpenMCPに参加する を通じて、OpenMCPのメンテナンスと開発に参加する方法を学ぶことができます。リソースチャンネル を通じて私たちの連絡先を取得できます。現在主なコミュニティは3つあります。QQグループ:782833642 、 OpenMCP Discordチャンネル知乎サークル【OpenMCP博物館】 + + + 協力に関しては、錦恢の個人メール:1193466151@qq.comまでご連絡ください。 + +
\ No newline at end of file diff --git a/ja/plugin-tutorial/concept.md b/ja/plugin-tutorial/concept.md new file mode 100644 index 0000000..ac9c31c --- /dev/null +++ b/ja/plugin-tutorial/concept.md @@ -0,0 +1,226 @@ +# MCP 基本概念 + +## はじめに + +[[what-is-mcp|前回の記事]]では、MCPの定義と基本的な組織構造について簡単に紹介しました。開発者として最も注目すべきは、自社の業務とシナリオに基づいて必要なMCPサーバーをカスタマイズ開発する方法です。これにより、任意のMCPクライアントに直接接続して、カスタマイズされたインタラクション能力を大規模モデルに提供できます。 + +MCPサーバーの開発方法を説明する前に、いくつかの基本概念を明確にしておく必要があると思います。 + +## Resources、Prompts、Tools + +[MCPクライアントプロトコル](https://modelcontextprotocol.io/clients)では、MCPプロトコルの3つの重要な能力カテゴリについて説明されています: + +- Resouces:ローカルリソースをカスタマイズしてリクエスト・アクセスする機能。ファイルシステム、データベース、現在のコードエディタ内のファイルなど、ウェブアプリでは通常アクセスできない**静的リソース**を指します。追加のresourcesは大規模モデルに送信されるコンテキストを豊富にし、AIからより正確な回答を得られます。 +- Prompts:特定のシナリオでAIが採用可能なプロンプトをカスタマイズします。例えば、AIに特定のフォーマットで内容を返させる必要がある場合、カスタムプロンプトを提供できます。 +- Tools:AIが使用できるツールで、関数である必要があります。ホテルの予約、ウェブページの開閉、照明のオンオフなどのカプセル化された関数がtoolとなります。大規模モデルはfunction callingの方法でこれらのtoolsを使用します。Toolsにより、AIが直接コンピュータを操作したり、現実世界とインタラクションしたりできるようになります。 + +フロントエンド・バックエンド開発経験のある方は、Resoucesを「大規模モデルに付与する追加の読み取り専用権限」、Toolsを「大規模モデルに付与する追加の読み書き権限」と考えることができます。 + +MCPクライアント(Claude Desktop、5ireなど)は既に上記のフロントエンドロジックを実装しています。具体的にどのようなリソースやツールを提供するかは、開発者の想像力次第です。つまり、多彩なMCP Serverを開発することで、大規模モデルにより興味深い作業を行わせることができます。 + +ただし、現在ほぼすべての大規模モデルはopenaiプロトコルをアクセスポイントとして採用している点に注意が必要です。openaiプロトコルとは何でしょうか? + +## openaiプロトコル + +PythonやTypeScriptでアプリを開発する際、通常はopenaiという名前のライブラリをインストールし、使用するモデルベンダー、モデルのベースURL、使用するモデルタイプを入力して大規模モデルに直接アクセスします。各モデルプロバイダーもこのライブラリとプロトコルをサポートする必要があります。 + +例えばPythonでdeepseekのサービスにアクセスする場合、次のようにできます: + +```python +from openai import OpenAI + +client = OpenAI(api_key="", base_url="https://api.deepseek.com") + +response = client.chat.completions.create( + model="deepseek-chat", + messages=[ + {"role": "system", "content": "You are a helpful assistant"}, + {"role": "user", "content": "Hello"}, + ], + stream=False +) + +print(response.choices[0].message.content) +``` + +このcreate関数の中身を見ると、openaiプロトコルが大規模モデルプロバイダーに要求する機能が非常に多いことがわかります: + +```python + @overload + def create( + self, + *, + messages: Iterable[ChatCompletionMessageParam], + model: Union[str, ChatModel], + audio: Optional[ChatCompletionAudioParam] | NotGiven = NOT_GIVEN, + frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, + function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, + functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, + logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, + logprobs: Optional[bool] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_tokens: Optional[int] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + modalities: Optional[List[Literal["text", "audio"]]] | NotGiven = NOT_GIVEN, + n: Optional[int] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, + prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, + presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, + response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, + seed: Optional[int] | NotGiven = NOT_GIVEN, + service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, + store: Optional[bool] | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, + stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, + tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + web_search_options: completion_create_params.WebSearchOptions | NotGiven = NOT_GIVEN, + # 以下の引数は、kwargsを介して利用できない追加のAPIパラメータを渡す必要がある場合に使用します。 + # ここで指定された追加の値は、クライアントで定義された値やこのメソッドに渡された値よりも優先されます。 + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ChatCompletion: +``` + +上記のシグネチャから、`temperature`や`top_p`など、いくつか見覚えのあるパラメータが確認できます。多くの大規模モデル使用ソフトウェアでは、このパラメータを調整できるようになっています。例えば5ireでは、コンテンツのランダム性は`temperature`パラメータのグラフィカル表示です。 + +
+ +
+ +実際、ご覧の通り、普通の呼び出しに関わる調整可能なパラメータは非常に多岐にわたります。すべてのパラメータの中で、`tools`というパラメータに注目してください: + +```python + @overload + def create( + self, + *, + messages: Iterable[ChatCompletionMessageParam], + model: Union[str, ChatModel], + audio: Optional[ChatCompletionAudioParam] | NotGiven = NOT_GIVEN, + + # ここを見て + tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + ) -> ChatCompletion: +``` + +## tool_callsフィールド + +上記のopenaiプロトコルには、toolsというパラメータがあります。toolsは、大規模モデルプロバイダーがfunction callingという特性をサポートする必要があることを意味します(MCPプロトコルと完全互換)。toolsが空でない場合、chat関数の戻り値には特別なフィールド`tool_calls`が含まれます。以下は、私が作成した天気を問い合わせるコードです: + +```python +from openai import OpenAI + +client = OpenAI( + api_key="Deepseek API", + base_url="https://api.deepseek.com" +) + +# tools(関数/ツールリスト)を定義 +tools = [ + { + "type": "function", + "function": { + "name": "get_current_weather", + "description": "指定された場所の天気を取得", + "parameters": { + "type": "object", + "properties": { + "location": { + "type": "string", + "description": "都市、例えば杭州、北京、上海", + } + }, + "required": ["location"], + }, + }, + } +] + +response = client.chat.completions.create( + model="deepseek-chat", + messages=[ + {"role": "system", "content": "あなたは役立つAIです"}, + {"role": "user", "content": "今日の杭州の天気は?"}, + ], + tools=tools, # toolsパラメータを渡す + tool_choice="auto", # オプション:特定のツールの呼び出しを強制するかどうかを制御 + stream=False, +) + +print(response.choices[0].message) +``` + +上記のコードを実行すると、次のような戻り値が得られます: + +```python +ChatCompletionMessage( + content='', + refusal=None, + role='assistant', + annotations=None, + audio=None, + function_call=None, + tool_calls=[ + ChatCompletionMessageToolCall( + id='call_0_baeaba2b-739d-40c2-aa6c-1e61c6d7e855', + function=Function( + arguments='{"location":"杭州"}', + name='get_current_weather' + ), + type='function', + index=0 + ) + ] +) +``` + +上記の`tool_calls`から、大規模モデルが提供されたツールをどのように使用したいかがわかります。注意点として、コンテキストの制限により、現在1つの質問で大規模モデルが呼び出せるツールの上限は通常100個を超えません。これは大規模モデルプロバイダーのコンテキストサイズに関係します。あ、そうそう、友情提示ですが、MCPクライアントで大規模モデルを使用して問題を解決する際、同時にアクティブなMCP Serverが多いほど、消費されるtokenも多くなりますよ :D + +現在のopenaiプロトコルでは、toolsは関数クラスの呼び出しのみをサポートしています。関数クラスの呼び出しは、Resourcesの効果をシミュレートできる場合があります。例えばリソースを取得する場合、それをtoolとして記述できます。したがって、通常の場合、MCP Serverを開発する際は、Toolsのみを開発するのが最善です。他の2つの機能はまだ広くサポートされていません。 + +## Inspectorを使用したデバッグ + +Claudeがネイティブで提供するMCPプロトコルは、公式が提供するInspectorでデバッグできます。[[first-mcp|最初のMCP]]の例では、次のようにデバッグできます。コマンドラインで次のコマンドを入力してInspectorを起動します: + +```bash +mcp dev main.py +``` + +これによりフロントエンドサーバーが起動し、`http://localhost:5173/`を開くとinspectorのデバッグインターフェースが表示されます。まず左側の`Connect`をクリックしてserver.pyを実行し、stdioを通信パイプとして使用してwebと通信を確立します。 + +さあ、楽しくデバッグを始められます。Inspectorは主に3つのセクションを提供し、それぞれResources、Prompts、Toolsに対応しています。 + +まずResourcesを見てみましょう。「Resource Templates」をクリックすると、登録されているすべてのResourceがリスト表示されます。例えば上記で定義した`get_greeting`は、パラメータを入力して実行することで、この関数が正常に動作するかどうかを確認できます。(通常、このリソースプロトコルはリモートデータベースまたはマイクロサービスにアクセスします) + +
+ +
+ +Prompts側は比較的シンプルで、定義済みパラメータを入力するだけで正常な戻り値を取得できます。 + +
+ +
+ +Tools側は、これからデバッグの中心となる部分です。前の章で説明したように、MCPプロトコルのPromptsとResourcesは現在openaiプロトコルや主要なMCPクライアントで広くサポートされていないため、サーバー側の主要なビジネスはtoolsの作成に集中すべきです。 + +ここで提供するtoolは、簡単な加算を実装するものです。非常にシンプルで、1と2を入力すると、結果が3であることが直接確認できます。今後、天気予報にアクセスできるtoolを開発する予定ですので、その際はこのようなウィンドウが天気情報の取得が正常かどうかをデバッグするのに非常に役立ちます。 + +
+ +
+ +## まとめ + +この記事では、MCP内部のいくつかの基本概念について簡単に理解しました。これらの概念は、MCPサーバーを開発する上で非常に有益だと思いますので、まず説明する必要があると考えました。 + +次の記事では、MCPの不思議な世界を探求し、AI Agentの時代がまもなく到来することをお伝えします。 \ No newline at end of file diff --git a/ja/plugin-tutorial/examples/go-neo4j-sse.md b/ja/plugin-tutorial/examples/go-neo4j-sse.md new file mode 100644 index 0000000..91ae39d --- /dev/null +++ b/ja/plugin-tutorial/examples/go-neo4j-sse.md @@ -0,0 +1,339 @@ +# Goで実装するNeo4jの読み取り専用MCPサーバー(SSE) + +[今回のチュートリアル動画](https://www.bilibili.com/video/BV1g8TozyEE7/) + +## はじめに + +このチュートリアルでは、Go言語を使ってNeo4jデータベースにアクセスできるMCPサーバーを書く方法をデモンストレーションします。実装が完了すると、クエリコードを一切書かずに、大規模言語モデルに問い合わせるだけでサーバーの状況を把握できるようになります。 + +従来の接続方法とは異なり、今回はSSE(Server-Sent Events)方式でサーバーの作成と接続を行います。 + +今回のチュートリアルのコード:https://github.com/LSTM-Kirigaya/openmcp-tutorial/tree/main/neo4j-go-server + +このコードをダウンロードすることをお勧めします。なぜなら、私が準備したデータベースファイルが含まれているからです。そうでないと、自分でデータをモックする必要があります。 + +## 1. 準備 + +プロジェクトの構造は以下の通りです: + +```bash +📦neo4j-go-server + ┣ 📂util + ┃ ┗ 📜util.go # ユーティリティ関数 + ┣ 📜main.go # メイン関数 + ┗ 📜neo4j.json # データベース接続のアカウント情報 +``` + +まずGoプロジェクトを作成します: + +```bash +mkdir neo4j-go-server +cd neo4j-go-server +go mod init neo4j-go-server +``` + +## 2. データベースの初期化を完了する + +### 2.1 Neo4jのインストール + +まず、私のチュートリアルに従ってローカルまたはサーバーにNeo4jデータベースを設定します。ここではチュートリアルですので、このチュートリアルの最初の2ステップだけを完了すれば十分です:[Neo4jデータベースのインストールと設定](https://kirigaya.cn/blog/article?seq=199)。binパスを環境変数に追加し、パスワードをopenmcpに設定します。 + +次に、main.goと同じ階層にneo4j.jsonを作成し、Neo4jデータベースの接続情報を記入します: + +```json +{ + "url" : "neo4j://localhost:7687", + "name" : "neo4j", + "password" : "openmcp" +} +``` + +### 2.2 事前に準備されたデータのインポート + +インストールが完了したら、私が事前に準備したデータをインポートできます。これらのデータは私の個人ウェブサイトの一部のデータを匿名化した要約です。自由に使用してください。ダウンロードリンク:[neo4j.db](https://github.com/LSTM-Kirigaya/openmcp-tutorial/releases/download/neo4j.db/neo4j.db)。ダウンロードが完了したら、以下のコマンドを実行します: + +```bash +neo4j stop +neo4j-admin load --database neo4j --from neo4j.db --force +neo4j start +``` + +その後、データベースにログインすると、私が準備したデータを見ることができます: + +```bash +cypher-shell -a localhost -u neo4j -p openmcp +``` + +
+ +
+ +### 2.3 Goからデータベースへの接続性を検証する + +データベースの接続性とGoのデータベースドライバーが正常に動作していることを確認するために、まずデータベースアクセスの最小システムを書く必要があります。 + +まず、Neo4jのv5バージョンのGoドライバーをインストールします: + +```bash +go get github.com/neo4j/neo4j-go-driver/v5 +``` + +`util.go`に以下のコードを追加します: + +```go +package util + +import ( + "context" + "encoding/json" + "fmt" + "os" + + "github.com/neo4j/neo4j-go-driver/v5/neo4j" +) + +var ( + Neo4jDriver neo4j.DriverWithContext +) + +// Neo4jサーバーへの接続を作成 +func CreateNeo4jDriver(configPath string) (neo4j.DriverWithContext, error) { + jsonString, _ := os.ReadFile(configPath) + config := make(map[string]string) + + json.Unmarshal(jsonString, &config) + // fmt.Printf("url: %s\nname: %s\npassword: %s\n", config["url"], config["name"], config["password"]) + + var err error + Neo4jDriver, err = neo4j.NewDriverWithContext( + config["url"], + neo4j.BasicAuth(config["name"], config["password"], ""), + ) + if err != nil { + return Neo4jDriver, err + } + return Neo4jDriver, nil +} + + +// 読み取り専用のCypherクエリを実行 +func ExecuteReadOnlyCypherQuery( + cypher string, +) ([]map[string]any, error) { + session := Neo4jDriver.NewSession(context.TODO(), neo4j.SessionConfig{ + AccessMode: neo4j.AccessModeRead, + }) + + defer session.Close(context.TODO()) + + result, err := session.Run(context.TODO(), cypher, nil) + if err != nil { + fmt.Println(err.Error()) + return nil, err + } + + var records []map[string]any + for result.Next(context.TODO()) { + records = append(records, result.Record().AsMap()) + } + + return records, nil +} +``` + +main.goに以下のコードを追加します: + +```go +package main + +import ( + "fmt" + "neo4j-go-server/util" +) + +var ( + neo4jPath string = "./neo4j.json" +) + +func main() { + _, err := util.CreateNeo4jDriver(neo4jPath) + if err != nil { + fmt.Println(err) + return + } + + fmt.Println("Neo4j driver created successfully") +} +``` + +メインプログラムを実行してデータベースの接続性を検証します: + +```bash +go run main.go +``` + +`Neo4j driver created successfully`と出力されれば、データベースの接続性検証は成功です。 + +## 3. MCPサーバーの実装 + +GoのMCP SDKで最も有名なのはmark3labs/mcp-goです。これを使用します。 + +> mark3labs/mcp-goのデモはhttps://github.com/mark3labs/mcp-goにあり、非常にシンプルです。ここでは直接使用します。 + +まずインストールします: + +```bash +go get github.com/mark3labs/mcp-go +``` + +次に`main.go`に以下のコードを追加します: + +```go +// ... 既存のコード ... + +var ( + addr string = "localhost:8083" +) + +func main() { + // ... 既存のコード ... + + s := server.NewMCPServer( + "読み取り専用Neo4jサーバー", + "0.0.1", + server.WithToolCapabilities(true), + ) + + srv := server.NewSSEServer(s) + + // executeReadOnlyCypherQueryツールのスキーマを定義 + executeReadOnlyCypherQuery := mcp.NewTool("executeReadOnlyCypherQuery", + mcp.WithDescription("読み取り専用のCypherクエリを実行"), + mcp.WithString("cypher", + mcp.Required(), + mcp.Description("Cypherクエリ文、読み取り専用でなければなりません"), + ), + ) + + // 実際の関数と宣言されたスキーマをバインド + s.AddTool(executeReadOnlyCypherQuery, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + args, ok := request.Params.Arguments.(map[string]interface{}) + if !ok { + return mcp.NewToolResultText(""), fmt.Errorf("無効な引数の型") + } + cypher, ok := args["cypher"].(string) + if !ok { + return mcp.NewToolResultText(""), fmt.Errorf("cypher引数が文字列ではありません") + } + result, err := util.ExecuteReadOnlyCypherQuery(cypher) + + fmt.Println(result) + + if err != nil { + return mcp.NewToolResultText(""), err + } + + return mcp.NewToolResultText(fmt.Sprintf("%v", result)), nil + }) + + // http://localhost:8083/sseでサービスを開始 + fmt.Printf("サーバーがhttp://%s/sseで起動しました\n", addr) + srv.Start(addr) +} +``` + +go run main.goを実行すると、以下の情報が表示されます: + +``` +Neo4j driver created successfully +サーバーがhttp://localhost:8083/sseで起動しました +``` + +これで、MCPサーバーがローカルの8083ポートで起動しました。 + +## 4. OpenMCPを使用したデバッグ + +### 4.1 ワークスペースにSSEデバッグプロジェクトを追加 + +次に、OpenMCPを使用してデバッグを行います。まず、VSコードの左側にあるOpenMCPアイコンをクリックしてコントロールパネルに入ります。https://github.com/LSTM-Kirigaya/openmcp-tutorial/tree/main/neo4j-go-server からこのプロジェクトをダウンロードした場合、【MCP接続(ワークスペース)】に既に作成済みのデバッグプロジェクト【読み取り専用Neo4jサーバー】が表示されます。完全に自分でこのプロジェクトを作成した場合は、以下のボタンから接続を追加し、SSEを選択してhttp://localhost:8083/sseを入力し、OAuthは空のままにします。 + +
+ +
+ +### 4.2 ツールのテスト + +MCPサーバーを初めてデバッグする際に最初に行うべきことは、MCPツールを動作させることです。新しいタブを作成し、ツールを選択し、以下の図のツールをクリックして、`CALL db.labels() YIELD label RETURN label`と入力します。この文はすべてのノードタイプをリストアップするために使用されます。以下の結果が出力されれば、現在のリンクは有効で問題ありません。 + +
+ +
+ +### 4.3 大規模言語モデルの機能範囲を理解し、プロンプトで知識をカプセル化する + +それでは、面白いことをしてみましょう!次に、大規模言語モデルの能力範囲をテストします。なぜなら、Neo4jは特殊なデータベースであり、一般的な大規模言語モデルはその使用方法を知らないかもしれないからです。新しいタブを作成し、「インタラクティブテスト」をクリックし、まず簡単な質問をします: + +``` +最新の10件のコメントを見つけてください +``` + +結果は以下の通りです: + +
+ +
+ +大規模言語モデルがクエリしたノードタイプが間違っていることがわかります。私が提供した例では、コメントを表すノードはBlogCommentであり、Commentではありません。つまり、大規模言語モデルはデータベースクエリの一般的な方法論を掌握していません。これが現在わかっているその能力範囲です。次に、私たちの経験と知識を段階的に注入します。うむ、システムプロンプトを通じて完了します。 + +### 4.4 大規模言語モデルにデータベースノードを見つける方法を教える + +よく考えてみてください。エンジニアとして、私たちはどのようにしてコメントのノードがBlogCommentであることを知るのでしょうか?一般的に、現在のデータベースのすべてのノードタイプをリストアップし、命名から推測します。たとえば、このデータベースの場合、私はまず以下のようなCypherクエリを入力します: + +```sql +CALL db.labels() YIELD label RETURN label +``` + +その出力は4.2の図にあります。英語が得意であれば、BlogCommentがおそらくブログコメントを表すノードであることがわかるでしょう。さて、この方法論をシステムプロンプトに注入し、この知識をカプセル化します。以下の図の下部のボタンをクリックして、【システムプロンプト】に入ります: + +
+ +
+ +新しいプロンプト【neo4j】を作成し、入力します: + +``` +あなたはneo4jクエリが得意なインテリジェントエージェントです。ユーザーからのクエリリクエストに対して、対応するデータベースノードが何であるか必ずしも知っているわけではありません。その場合、まずすべてのノードタイプをリストアップし、その中からユーザーの質問に最も一致すると考えるノードを見つける必要があります。たとえば、ユーザーが特定の条件に合致する「記事」を見たいと尋ねた場合、記事ノードのタイプが何であるか知らないので、まずすべてのノードをリストアップする必要があります。 +``` + +保存をクリックし、【インタラクティブテスト】で先ほどの質問を繰り返します: + +``` +最新の10件のコメントを見つけてください +``` + +大規模言語モデルの回答は以下の通りです: + +
+ +
+ +どうでしょう?ずっと良くなったのではないでしょうか?大規模言語モデルはBlogCommentノードを正しく見つけ、対応するデータを返しました。 + +しかし、まだ完全には正しくありません。なぜなら、私たちが要求したのは最新の10件のコメントですが、大規模言語モデルが返したのは実際には最も古い10件のコメントです。大規模言語モデルの呼び出しの詳細を開くと、大規模言語モデルは`ORDER BY comment.createdAt`を使用して実装していることがわかります。しかし、問題は、私たちのデータベースでは、コメントが作成された時間を記録するフィールドはcreatedAtではなく、createdTimeであることです。これは、大規模言語モデルがノードのフィールドを知らないことを知らず、「幻覚」を起こし、適当なフィールドを入力したことを意味します。 + +大規模言語モデルは自分が知らないことを明示的に言うことはありません。錦恢研究生のOODに関する研究は、このことの本質的な理由を説明できます:[EDL(Evidential Deep Learning)原理とコード実装](https://kirigaya.cn/blog/article?seq=154)。もしあなたの好奇心が数学の基礎力に匹敵するなら、この記事を試してみてください。とにかく、あなたが知っておくべきことは、大規模言語モデルが自分が知らないことに対して幻覚を起こすからこそ、私たちが経験を注入する操作の余地があるということです。 + +### 4.5 大規模言語モデルにデータベースノードのフィールドを見つける方法を教える + +上記の試みを通じて、私たちは終点まであと少しであることを知りました。それは、大規模言語モデルに、私たちのデータベースでは、コメントが作成された時間を記録するフィールドはcreatedAtではなく、createdTimeであることを教えることです。 + +フィールドを識別する知識について、先ほどのシステムプロンプトを改良します: + +``` +あなたはneo4jクエリが得意なインテリジェントエージェントです。ユーザーからのクエリリクエストに対して、対応するデータベースノードが何であるか必ずしも知っているわけではありません。その場合、まずすべてのノードタイプをリストアップし、その中からユーザーの質問に最も一致すると考えるノードを見つける必要があります。たとえば、ユーザーが特定の条件に合致する「記事」を見たいと尋ねた場合、記事ノードのタイプが何であるか知らないので、まずすべてのノードをリストアップする必要があります。 + +より具体的なクエリの場合、まず1つまたは2つの事例をクエリして、現在のタイプにどのようなフィールドがあるかを確認する必要があります。たとえば、ユーザーが最新の記事を尋ねた場合、記事ノードのどのフィールドが「作成時間」を表すか知らないので、まず1つまたは2つの記事ノードをリストアップし、その中にどのようなフィールドがあるかを確認し、その後、最新の10件の記事をクエリする必要があります。 +``` + +結果は以下の通りです: + diff --git a/ja/plugin-tutorial/examples/java-es-http.md b/ja/plugin-tutorial/examples/java-es-http.md new file mode 100644 index 0000000..e69de29 diff --git a/ja/plugin-tutorial/examples/mcp-examples.md b/ja/plugin-tutorial/examples/mcp-examples.md new file mode 100644 index 0000000..0e68d5d --- /dev/null +++ b/ja/plugin-tutorial/examples/mcp-examples.md @@ -0,0 +1,28 @@ +--- +next: + text: Pythonで実装した天気情報MCPサーバー(STDIO) + link: '/plugin-tutorial/examples/python-simple-stdio' +--- + +# MCPサーバー開発事例 + +## Python +- [Pythonで実装した天気情報MCPサーバー(STDIO)](./python-simple-stdio) +- [Pythonで実装した汎用フォーム入力MCP(STDIO)](./python-form-stdio) +- [Pythonで実装したBlenderベースのMCP(STDIO)](./python-blender-stdio) +- [Pythonで実装したCadence EDA用MCP(STDIO)](./python-cadence-stdio) +- FFmpegベースMCPによる対話型動画編集 +- RAGベースMCPによるナレッジベース注入 +- Stable Diffusion用MCPサーバーの実装 + +## Nodejs +- [TypeScriptで実装したcrawl4aiベースの超機能ウェブクローラーMCP(STDIO)](./typescript-crawl4ai-stdio) + +## Go +- [Goで実装したNeo4j用読み取り専用MCPサーバー(SSE)](./go-neo4j-sse) + +## Java +- [Javaで実装したドキュメントデータベース用読み取り専用MCP(HTTP)](./java-es-http) + +## 認証 +- [SSE方式OAuth2認証MCPサーバー事例](./sse-oauth2) \ No newline at end of file diff --git a/ja/plugin-tutorial/examples/python-blender-stdio.md b/ja/plugin-tutorial/examples/python-blender-stdio.md new file mode 100644 index 0000000..9b4d5b3 --- /dev/null +++ b/ja/plugin-tutorial/examples/python-blender-stdio.md @@ -0,0 +1,321 @@ +# Pythonで実装する天気情報MCPサーバー + +## フック + +待って、始める前に小さな例を見てみましょう。来週「アークナイツ」の「錆びた影、新生」コスプレイベントに行く予定なので、土曜日の杭州の天気を知りたいです。AIに土曜日の天気を聞くと、以下のような回答が返ってきました: + +
+ +
+ +これはダメですね。皆さんもよくあると思いますが、AIは「魚の釣り方を教える」ことが多く、時には単に結果が知りたいだけの退屈な日常の用事もあります。 + +実際、天気予報を実装するプログラムはたくさんあります。では、完成した天気予報プログラムをAIに接続し、実際の天気を教えてもらって、明日のコスプレイベントの服装を選ぶにはどうすればよいでしょうか? + +直接関数を書いてfunction callingを使うのは少し面倒です。AIプロバイダーのAPI呼び出しやタスクループの構築、テキストレンダリングなど、多くの技術的な詳細を検討する必要があり、貴重な時間を浪費します。MCPは私たちに救いの道を与えてくれます。今日のチュートリアルでは、簡単なMCPサーバーを書いて、AIに天気予報を知る能力を与える方法を教えます。 + +## はじめに + +👉 [前回のナビゲーション](https://zhuanlan.zhihu.com/p/32593727614) + +前回はMCPの基礎を簡単に説明しました。今回は、自分たちのMCPサーバーを開発し、既存のアプリケーション、サービス、ハードウェアなどをAIに接続します。これにより、AIからエンドアプリケーションへの最後の1キロメートルを完了します。 + +「工欲善其事、必先利其器」。よりエレガントで楽しくMCPサーバーを開発するために、開発プロセスでプログラムの変更を確認し、直接AIに接続してツールの有効性を検証できる優れたテストツールが必要です。 + +そこで、私は最近オールインワンのMCPテスト開発ツール「OpenMCP」をオープンソース化しました。[全网第一个 MCP 服务器一体化开发测试软件 OpenMCP 发布!](https://zhuanlan.zhihu.com/p/1894785817186121106) + +> OpenMCP QQグループ 782833642 + +OpenMCPオープンソースリンク:https://github.com/LSTM-Kirigaya/openmcp-client + +スターをお願いします :D + +### 最初のMCPプロジェクト + +前置きはこのくらいにして、コーディングを始めましょう :D + +vscodeやtraeを開く前に、基本的なuvツールをインストールします。uvはコミュニティで人気のバージョン管理ツールで、性能の良いcondaと考えてください。 + +まずuvをインストールします。anacondaを使用している場合は、必ずbase環境に切り替えてからインストールしてください: + +```bash +pip install uv +``` + +インストールが完了したら、uvを実行します: + +```bash +uv +``` + +エラーがなければ成功です。uvは再利用不可能な依存関係のみをローカルにインストールするので、anacondaユーザーは心配ありません。uvがインストールするライブラリがbaseを汚染することはありません。次にuvを使用して基本的なPythonプロジェクトを作成します: + +```bash +mkdir simple-mcp && cd simple-mcp +uv init +uv add mcp "mcp[cli]" +``` + +次にvscodeまたはtraeを開き、プラグインストアでOpenMCPプラグインを探してダウンロードします: + +
+ +
+ +まずMCPの最小プログラムを作成します: + +ファイル名:simple_mcp.py + +```python +from mcp.server.fastmcp import FastMCP + +mcp = FastMCP('錦恢の MCP Server', version="11.45.14") + +@mcp.tool( + name='add', + description='对两个数字进行实数域的加法' +) +def add(a: int, b: int) -> int: + return a + b + +@mcp.resource( + uri="greeting://{name}", + name='greeting', + description='用于演示的一个资源协议' +) +def get_greeting(name: str) -> str: + # 访问处理 greeting://{name} 资源访问协议,然后返回 + # 此处方便起见,直接返回一个 Hello,balabala 了 + return f"Hello, {name}!" + +@mcp.prompt( + name='translate', + description='进行翻译的prompt' +) +def translate(message: str) -> str: + return f'请将下面的话语翻译成中文:\n\n{message}' + +@mcp.tool( + name='weather', + description='获取指定城市的天气信息' +) +def get_weather(city: str) -> str: + """模拟天气查询协议,返回格式化字符串""" + return f"Weather in {city}: Sunny, 25°C" + +@mcp.resource( + uri="user://{user_id}", + name='user_profile', + description='获取用户基本信息' +) +def get_user_profile(user_id: str) -> dict: + """模拟用户协议,返回字典数据""" + return { + "id": user_id, + "name": "张三", + "role": "developer" + } + +@mcp.resource( + uri="book://{isbn}", + name='book_info', + description='通过ISBN查询书籍信息' +) +def get_book_info(isbn: str) -> dict: + """模拟书籍协议,返回结构化数据""" + return { + "isbn": isbn, + "title": "Python编程:从入门到实践", + "author": "Eric Matthes" + } + +@mcp.prompt( + name='summarize', + description='生成文本摘要的提示词模板' +) +def summarize(text: str) -> str: + """返回摘要生成提示词""" + return f"请用一句话总结以下内容:\n\n{text}" +``` + +実行してみます: + +```bash +uv run mcp run simple_mcp.py +``` + +エラーがなく、止まっている場合は、依存関係のインストールに問題がないことを意味します。Ctrl+CまたはCtrl+Zで終了します。 + +これらの関数は単純で意味がないように見えるかもしれませんが、最終的なシステムに至るまでには簡単な例が必要です。 + +### リンク、スタート! + +OpenMCPプラグインをダウンロードした場合、Pythonエディターの右上にOpenMCPの紫色のアイコンが表示されます。クリックするとOpenMCPが起動し、現在のMCPをデバッグできます。 + +
+ +
+ +デフォルトではSTDIOモードで起動し、以下のコマンドを実行します: + +```bash +uv run mcp run <現在開いているPythonファイルの相対パス> +``` + +したがって、mcpスキャフォールディング(`uv add mcp "mcp[cli]"`)がインストールされていることを確認する必要があります。 + +開いたらまず左下の接続状態を確認し、緑色であることを確認します。これはOpenMCPとMCPサーバーが正常にハンドシェイクしたことを意味します。 + +
+ +
+ +接続に成功すると、接続の上に現在のMCPサーバーの名前が表示され、カーソルを合わせるとバージョン番号が表示されます。この情報は以下のコードで定義されています: + +```python +mcp = FastMCP('錦恢の MCP Server', version="11.45.14") +``` + +バージョン管理時に非常に便利です。このシステムを活用してください。 + +接続に失敗した場合は、左側のツールバーの2番目のボタンをクリックして接続コンソールに入り、エラー情報を確認するか、手動で接続コマンドを調整します: + +
+ +
+ +### OpenMCPの紹介 + +次に、OpenMCPの基本機能モジュールを簡単に紹介します。最初に画面に何も表示されていない場合は、上のプラス記号をクリックして新しいタブを作成します。このページには以下の4つのボタンが表示されます: + +
+ +
+ +拡大: + +
+ +
+ +最初の3つ(リソース、プロンプト、ツール)は、MCPの3つの対応する項目(Resources、Prompts、Tools)をデバッグするために使用されます。これらの部分の使用法は基本的にMCP公式のInspectorツールと同じです。もちろん、私はこれらを参考にしました、えへ。 + +
+ +
+ +4番目のボタン「インタラクティブテスト」は、私が開発したMCPクライアントで、基本的にはチャットウィンドウです。現在のMCPサーバーの機能関数をAIでシームレスにテストできます。 + +
+ +
+ +現在はツールのサポートのみを一時的に提供しています。プロンプトとリソースについてはまだ考えがまとまっていません(リソースはツールとして扱えると思います)。グループで一緒に議論しましょう:QQグループ 782833642 + +## 天気関数のデバッグを開始 + +### ツールのデバッグ + +最初に示したMCPの例を覚えていますか?OpenMCPを使用してこれらの関数を迅速にデバッグできます。今回の目標は天気予報MCPを作成することです。天気予報関数がすでに完成していると仮定し、それをツールとしてカプセル化します: + +```python +@mcp.tool( + name='weather', + description='获取指定城市的天气信息' +) +def get_weather(city: str) -> str: + """模拟天气查询协议,返回格式化字符串""" + return f"Weather in {city}: Sunny, 25°C" +``` + +もちろん、今のところ意味はありません。黒龍江省の都市IDを入力しても25度と返しますが、これらの詳細は重要ではありません。まずはプロセス全体を体験し、トップダウンの理解を構築することがユーザーにとって学びやすいです。 + +この関数をデバッグする必要があります。OpenMCPを開き、新しい「ツール」デバッグプロジェクトを作成します: + +
+ +
+ +左側のリストにweatherツールが表示されます。選択し、右側の入力ボックスに何かを入力し、Enter(または「実行」をクリック)を押すと、以下の応答が表示されます: + +
+ +
+ +関数が返す文字列が表示され、問題なくリンクが機能していることがわかります。 + +### インタラクティブテスト + +プログラミングは得意かもしれませんが、天気予報クローラーを素早く作成する前に、作成したツールをAIチャットに注入する方法を見てみましょう。AIを使用するには、まずAIプロバイダーと対応するAPIを選択する必要があります。左側のツールバーの3番目のボタンをクリックしてAPIモジュールに入り、使用するAIプロバイダーとモデルを選択し、APIトークンを入力して「保存」をクリックします: + +
+ +
+ +新しいタブを作成し、「インタラクティブテスト」を選択します。これで直接AIとチャットできます。まずツールを注入しないAIが天気予報の質問にどのように応答するか見てみましょう。下部のツールバーの左から3番目のボタンをクリックしてツール選択インターフェースに入り、「すべてのツールを無効にする」を選択します: + +
+ +
+ +「閉じる」をクリックした後、AIに質問します: + +``` +杭州の気温は何度ですか? +``` + +
+ +
+ +冒頭と同じ回答が返ってきました。非常に形式的で、実際には知らないからです。 + +ここで、「weather」ツールを単独で有効にします: + +
+ +
+ +同じ質問をします: + +
+ +
+ +25度という回答と追加の推論情報が返ってきました。 + +いくつかの詳細に注目しましょう。まず、AIは直接質問に答えず、weatherツールを呼び出します。呼び出しパラメータは: + +```json +{ + "city": "杭州" +} +``` + +そして、MCPサーバーは以下の応答を返します: + +``` +Weather in 杭州: Sunny, 25°C +``` + +最終的にAIはこの情報に基づいて回答を生成します。つまり、このプロセスでは実際に2回AIサービスを呼び出しています。また、2回の呼び出しの入力トークン数が非常に多いことがわかります。これはOpenMCPが関数呼び出しをJSONスキーマとしてリクエストパラメータに注入するためです。weatherツールのJSONスキーマは以下の図の右側のjsonのようになります: + +
+ +
+ +OpenAIプロトコルをサポートするAIプロバイダーは、このような情報に対してfunction callingを行います。そのため、ツールを使用するAIリクエストの入力トークン数は多くなります。しかし、心配する必要はありません。ほとんどのプロバイダーはKVキャッシュを実装しており、同じプレフィックスの入力に対してキャッシュがあり、キャッシュヒット部分のコストは通常の入力出力トークン価格よりも大幅に低くなります。OpenMCPは各回答の下に、現在のリクエストの入力トークン、出力トークン、総トークン、キャッシュヒット率を示しています。 + +ここで: + +- 「総トークン」=「入力トークン」+「出力トークン」 + +- 「キャッシュヒット率」=「キャッシュヒットトークン」/「入力トークン」 + +> はい、キャッシュヒット率は入力トークンの概念で、出力トークンにはキャッシュヒット率という概念はありません。 + +今後の開発では、この情報に基づいてサービスやプロンプトを最適化できます。 + +### 実際の天気予報を完成させましょう! + +もちろん、このコードも非常に簡単で、直接AIに生成させることができます( \ No newline at end of file diff --git a/ja/plugin-tutorial/examples/python-cadence-stdio.md b/ja/plugin-tutorial/examples/python-cadence-stdio.md new file mode 100644 index 0000000..e69de29 diff --git a/ja/plugin-tutorial/examples/python-form-stdio.md b/ja/plugin-tutorial/examples/python-form-stdio.md new file mode 100644 index 0000000..e69de29 diff --git a/ja/plugin-tutorial/examples/python-simple-stdio.md b/ja/plugin-tutorial/examples/python-simple-stdio.md new file mode 100644 index 0000000..8d0b2b0 --- /dev/null +++ b/ja/plugin-tutorial/examples/python-simple-stdio.md @@ -0,0 +1,309 @@ +# Pythonで天気情報MCPサーバーを実装 + +[今回のチュートリアル動画](https://www.bilibili.com/video/BV1zYGozgEHc) + +## フック + +さて、始める前に小さな例を見てみましょう。来週「アークナイツ」の「錆影新生」コスプレイベントに行く予定なので、土曜日の杭州の天気を知りたいと思い、AIに土曜日の天気を聞きました。するとAIは以下のような返答をしました: + +
+ +
+ +これは困ります。皆さんもよくあると思いますが、AIは「魚の釣り方を教える」ことが多く、時には単に最終結果を知りたいだけのとき、特に些細な日常の質問に対してはそう感じます。 + +実際、天気予報を実装するプログラムはたくさんあります。では、作成した天気予報プログラムをAIに接続し、実際の天気を教えてもらい、明日のコスプレイベントの服装を決める方法はないでしょうか? + +直接関数を書いてfunction callingを使うのは少し面倒です。AIプロバイダーのAPI呼び出しやタスクループの構築、テキストレンダリングなど、多くの技術的な詳細を検討する必要があり、貴重な時間を浪費します。MCPは私たちに救いの道を与えてくれました。今回のチュートリアルでは、簡単なMCPサーバーを作成し、AIに天気予報を知る能力を与える方法を教えます。 + +## はじめに + +👉 [前回のナビゲーション](https://zhuanlan.zhihu.com/p/32593727614) + +前回はMCPの基礎を簡単に説明しました。今回は、独自のMCPサーバーの開発を開始し、既存のアプリケーション、サービス、ハードウェアなどをAIに接続します。これにより、AIからエンドユーザーアプリケーションへの最後の1キロメートルを完了します。 + +「工欲善其事、必先利其器」。よりエレガントで楽しいMCPサーバー開発のために、開発プロセスでプログラムの変更を確認し、直接AIに接続してツールの有効性を検証できる優れたテストツールが必要です。 + +そこで、私は最近、統合型MCPテスト開発ツール「OpenMCP」をオープンソース化しました。[初のMCPサーバー統合開発テストソフトウェアOpenMCPリリース!](https://zhuanlan.zhihu.com/p/1894785817186121106) + +> OpenMCP QQグループ 782833642 + +OpenMCPオープンソースリンク:https://github.com/LSTM-Kirigaya/openmcp-client + +スターをお願いします :D + +### 最初のMCPプロジェクト + +前置きはこのくらいにして、コーディングを始めましょう :D + +vscodeやtraeを開く前に、基本的なuvツールをインストールします。uvはコミュニティで人気のバージョン管理ツールで、性能の良いcondaと理解してください。 + +まずuvをインストールします。anacondaを使用している場合は、必ずbase環境に切り替えてからインストールしてください: + +```bash +pip install uv +``` + +インストールが完了したら、uvを実行します: + +```bash +uv +``` + +エラーがなければ成功です。uvは再利用不可能な依存関係のみをローカルにインストールするため、anacondaを使用している方は心配ありません。uvがインストールするライブラリがbaseを汚染することはありません。次に、uvを使用して基本的なpythonプロジェクトを作成します: + +```bash +mkdir simple-mcp && cd simple-mcp +uv init +uv add mcp "mcp[cli]" +``` + +次にvscodeまたはtraeを開き、プラグイン市場でOpenMCPプラグインを探してダウンロードします: + +
+ +
+ +まずMCPの最小プログラムを作成します: + +ファイル名:simple_mcp.py + +```python +from mcp.server.fastmcp import FastMCP + +mcp = FastMCP('錦恢の MCP Server', version="11.45.14") + +@mcp.tool( + name='add', + description='2つの数値の実数領域での加算' +) +def add(a: int, b: int) -> int: + return a + b + +@mcp.resource( + uri="greeting://{name}", + name='greeting', + description='デモンストレーション用のリソースプロトコル' +) +def get_greeting(name: str) -> str: + # greeting://{name}リソースアクセスプロトコルを処理し、返す + # ここでは簡単のため、直接Helloを返す + return f"Hello, {name}!" + +@mcp.prompt( + name='translate', + description='翻訳用のプロンプト' +) +def translate(message: str) -> str: + return f'以下の文章を中国語に翻訳してください:\n\n{message}' + +@mcp.tool( + name='weather', + description='指定都市の天気情報を取得' +) +def get_weather(city: str) -> str: + """天気予報プロトコルをシミュレートし、フォーマットされた文字列を返す""" + return f"Weather in {city}: Sunny, 25°C" + +@mcp.resource( + uri="user://{user_id}", + name='user_profile', + description='ユーザー基本情報を取得' +) +def get_user_profile(user_id: str) -> dict: + """ユーザープロトコルをシミュレートし、辞書データを返す""" + return { + "id": user_id, + "name": "張三", + "role": "developer" + } + +@mcp.resource( + uri="book://{isbn}", + name='book_info', + description='ISBNで書籍情報を検索' +) +def get_book_info(isbn: str) -> dict: + """書籍プロトコルをシミュレートし、構造化データを返す""" + return { + "isbn": isbn, + "title": "Pythonプログラミング:入門から実践まで", + "author": "Eric Matthes" + } + +@mcp.prompt( + name='summarize', + description='テキスト要約のプロンプトテンプレート' +) +def summarize(text: str) -> str: + """要約生成プロンプトを返す""" + return f"以下の内容を一言で要約してください:\n\n{text}" +``` + +実行してみます: + +```bash +uv run mcp run simple_mcp.py +``` + +エラーがなく、止まっている場合は、依存関係のインストールに問題がないことを意味します。ctrl cまたはctrl zで終了してください。 + +これらの関数は単純で意味がないように見えるかもしれませんが、最終的なシステムに到達するためには、簡単な例が必要です。 + +### リンク、スタート! + +OpenMCPプラグインをダウンロードした場合、pythonエディタの右上にOpenMCPの紫色のアイコンが表示されます。クリックするとOpenMCPが起動し、現在のMCPをデバッグできます。 + +
+ +
+ +デフォルトではSTDIOモードで起動し、以下のコマンドを実行します: + +```bash +uv run mcp run <現在開いているpythonファイルの相対パス> +``` + +したがって、mcpスキャフォールド(`uv add mcp "mcp[cli]"`)がインストールされていることを確認してください。 + +開いたらまず左下の接続状態を確認し、緑色であることを確認します。これはOpenMCPとMCPサーバーが正常にハンドシェイクしたことを示します。 + +
+ +
+ +接続に成功すると、接続の上に現在のMCPサーバーの名前が表示され、カーソルを合わせるとバージョン番号が表示されます。この情報は以下のコードで定義されます: + +```python +mcp = FastMCP('錦恢の MCP Server', version="11.45.14") +``` + +これはバージョン管理時に非常に便利です。このシステムを活用してください。 + +接続に失敗した場合は、左側のツールバーの2番目のボタンをクリックして接続コンソールに入り、エラーメッセージを確認するか、手動で接続コマンドを調整します: + +
+ +
+ +### OpenMCPの紹介 + +次に、OpenMCPの基本機能モジュールを簡単に紹介します。最初に画面に何も表示されていない場合は、上のプラス記号をクリックして新しいタブを作成します。このページには以下の4つのボタンが表示されます: + +
+ +
+ +拡大: + +
+ +
+ +最初の3つ、リソース、プロンプト、ツールは、MCP内の対応する項目(Resources、Prompts、Tools)をデバッグするために使用されます。これらの部分の使用法は基本的にMCP公式のInspectorツールと同じです。もちろん、私はこれらを参考にしました。 + +
+ +
+ +4番目のボタン「インタラクティブテスト」は、私が開発したMCPクライアントで、基本的にはダイアログウィンドウです。現在のMCPサーバーの機能関数をAIで直接テストできます。 + +
+ +
+ +現在はツールのサポートのみを提供しています。プロンプトとリソースについてはまだ考えがまとまっていません(リソースはツールとして扱えると思います)。グループで一緒に議論しましょう:QQグループ 782833642 + +## 天気関数のデバッグを開始 + +### ツールデバッグ + +最初に示したMCPの例を覚えていますか?OpenMCPを使用して、これらの関数を迅速にデバッグできます。今回の目標は、天気予報MCPを作成することです。天気予報関数がすでに作成されていると仮定し、それをツールとしてカプセル化します: + +```python +@mcp.tool( + name='weather', + description='指定都市の天気情報を取得' +) +def get_weather(city: str) -> str: + """天気予報プロトコルをシミュレートし、フォーマットされた文字列を返す""" + return f"Weather in {city}: Sunny, 25°C" +``` + +もちろん、今のところ意味はありません。黒龍江省の都市IDを入力しても25度を返しますが、これは重要ではありません。まず全体のプロセスを理解することが重要です。詳細にこだわるよりも、トップダウンで感覚的な理解を構築する方がユーザーにとって学びやすいです。 + +この関数をデバッグするには、OpenMCPを開き、新しい「ツール」デバッグプロジェクトを作成します: + +
+ +
+ +左側のリストにweatherツールが表示されます。それを選択し、右側の入力ボックスに何かを入力してEnter(または「実行」をクリック)を押すと、以下の応答が表示されます: + +
+ +
+ +関数が返す文字列が表示され、問題なくリンクが機能していることがわかります。 + +### インタラクティブテスト + +プログラミングは得意かもしれませんが、天気予報クローラーを素早く作成する前に、作成したツールをAIダイアログに注入する方法を見てみましょう。AIを使用するには、まずAIモデルと対応するAPIを選択する必要があります。左側のツールバーの3番目のボタンをクリックしてAPIモジュールに入り、使用するAIモデルプロバイダー、モデルを選択し、APIトークンを入力して「保存」をクリックします: + +
+ +
+ +新しいタブを作成し、「インタラクティブテスト」を選択します。これで直接AIと対話できます。まずツールを注入せずにAIが天気予報の質問にどのように応答するかを見てみましょう。下部のツールバーの左から3番目のボタンをクリックしてツール選択インターフェースに入り、「すべてのツールを無効にする」を選択します: + +
+ +
+ +「閉じる」をクリックした後、AIに質問します: + +``` +杭州の気温は何度ですか? +``` + +
+ +
+ +AIは記事の冒頭と同じ回答をしました。非常に形式的で、実際には知らないからです。 + +ここで、「weather」ツールを個別に有効にします: + +
+ +
+ +同じ質問をします: + +
+ +
+ +AIは25度と回答し、追加の推論情報も提供しました。 + +いくつかの詳細に注目しましょう。まず、AIは直接質問に答えず、weatherツールを呼び出します。呼び出しパラメータは: + +```json +{ + "city": "杭州" +} +``` + +そして、MCPサーバーは以下の応答を返します: + +``` +Weather in 杭州: Sunny, 25°C +``` + +これに基づいて、AIは最終的な回答を提供します。つまり、このプロセスでは実際に2回AIサービスを呼び出しています。また、2回の呼び出しの入力トークン数が非常に多いことがわかります。これはOpenMCPが関数呼び出しをJSONスキーマ形式でリクエストパラメータに注入するためです。weatherツールのJSONスキーマは以下の図の右側のjsonのようになります: + +
+ +
+ +openaiプロトコルをサポートするAIプロバイダーは、このような情報に対してfunction callingを実行します。そのため、ツールを使用するAIリクエストの入力トークン数は大きくなります。ただし、心配する必要はありません。ほとんどのプロバイダーはKVキャッシュを実装しており、同じプレフィックスの入力に対してキャッシュがあり、キャッシュヒット部分のコストは通常の入力出力トークン価格よりも大幅に低くなります。OpenMCPは各回答の下に、現在のリクエストの \ No newline at end of file diff --git a/ja/plugin-tutorial/examples/typescript-crawl4ai-stdio.md b/ja/plugin-tutorial/examples/typescript-crawl4ai-stdio.md new file mode 100644 index 0000000..e69de29 diff --git a/ja/plugin-tutorial/faq/help.md b/ja/plugin-tutorial/faq/help.md new file mode 100644 index 0000000..dd8fa78 --- /dev/null +++ b/ja/plugin-tutorial/faq/help.md @@ -0,0 +1,27 @@ +--- +layout: doc +--- + +# よくある質問 + +## エラーコード説明 + +### 32000 - MCP接続失敗 + +MCP接続失敗の原因は複数考えられます。以下に一般的なケースを挙げます: + +• **仮想環境パスの不一致** + +仮想環境(venv)とエントリーファイルのパスが一致していない場合、接続失敗の原因となることがあります。 + +詳細な解決方法はこちら:[設定説明](./venv-not-same-path/venv-not-same-path.md) + +--- + +• **その他の可能性** + +- ポートが使用中 +- 環境変数の設定誤り +- 依存ライブラリが正しくインストールされていない + +> 上記の問題が発生した場合、まずエラーログで詳細を確認してください。問題が解決しない場合は、[GitHub Issues](https://github.com/LSTM-Kirigaya/openmcp-client/issues)でサポートを求めてください。 \ No newline at end of file diff --git a/ja/plugin-tutorial/faq/venv-not-same-path/image-2.png b/ja/plugin-tutorial/faq/venv-not-same-path/image-2.png new file mode 100644 index 0000000..3b3953f Binary files /dev/null and b/ja/plugin-tutorial/faq/venv-not-same-path/image-2.png differ diff --git a/ja/plugin-tutorial/faq/venv-not-same-path/image.png b/ja/plugin-tutorial/faq/venv-not-same-path/image.png new file mode 100644 index 0000000..ac756aa Binary files /dev/null and b/ja/plugin-tutorial/faq/venv-not-same-path/image.png differ diff --git a/ja/plugin-tutorial/faq/venv-not-same-path/venv-not-same-path.md b/ja/plugin-tutorial/faq/venv-not-same-path/venv-not-same-path.md new file mode 100644 index 0000000..092385a --- /dev/null +++ b/ja/plugin-tutorial/faq/venv-not-same-path/venv-not-same-path.md @@ -0,0 +1,29 @@ +# 仮想環境とエントリーファイルが異なるディレクトリにある場合の設定方法 + +## 問題の説明 + +OpenMCPを使用する際、仮想環境(venv)とPythonファイルが同じディレクトリにない場合があり、仮想環境がプロジェクトフォルダの外にあることもあります。このような場合、右上の接続ボタンをクリックするとMCP接続失敗(エラーコード:32000)が発生する可能性があります。 + +## 解決策 + +### 1. 実行ディレクトリの調整 + +接続オプションで、実行ディレクトリを仮想環境がある場所に調整する必要があります: + +![MCP接続オプション画面](./image-2.png) + +### 2. 実行コマンドの変更 + +同時に、実行コマンドを適切に変更する必要があります: + +![実行コマンド変更例](./image.png) + +### 3. インタプリタパスの直接指定 + +特定の場合には、コマンドでPythonインタプリタのフルパスを直接指定できます。例: + +```bash +C:\code\ygo-chat\.venv\Scripts\python.exe example.py +``` + +> 注意:この方法はnodeやmcp指令の【コマンド】、その他のmcp clientのmcp設定ファイルにも適用できます。 \ No newline at end of file diff --git a/ja/plugin-tutorial/images/inspector.png b/ja/plugin-tutorial/images/inspector.png new file mode 100644 index 0000000..442aef4 Binary files /dev/null and b/ja/plugin-tutorial/images/inspector.png differ diff --git a/ja/plugin-tutorial/images/openmcp.png b/ja/plugin-tutorial/images/openmcp.png new file mode 100644 index 0000000..b732b99 Binary files /dev/null and b/ja/plugin-tutorial/images/openmcp.png differ diff --git a/ja/plugin-tutorial/index.md b/ja/plugin-tutorial/index.md new file mode 100644 index 0000000..be439c5 --- /dev/null +++ b/ja/plugin-tutorial/index.md @@ -0,0 +1,43 @@ +--- +next: + text: MCPとは? + link: '/plugin-tutorial/what-is-mcp' +--- + +# OpenMCP 概要 + +:::warning +OpenMCPの学習を開始する前に、MCPの基本概念を理解することを強くお勧めします:[エージェント時代のインフラストラクチャ | MCPプロトコル紹介](https://kirigaya.cn/blog/article?seq=299) +::: + +## OpenMCPとは + +OpenMCPは開発者向けのMCPデバッガーおよびSDKであり、AI Agentの全リンク開発コストと開発者の認知的負荷を軽減することを目的としています。 + +![](./images/openmcp.png) + +OpenMCPは2つの部分に分かれていますが、このセクションではOpenMCPデバッガーの部分の使用方法について説明します。この部分はOpenMCP Clientとも呼ばれています。OpenMCP Clientの本体は、vscodeのようなエディタ上で動作するプラグインです。現在のMCPプロトコルのすべての機能と互換性があり、開発者にとって便利な豊富な機能を提供しており、Claude Inspectorの上位互換として使用できます。 + +:::info vscodeのようなエディタ (VLE) +vscodeのようなエディタ(vscode-like editor、略称VLE)とは、Vscodiumカーネルをベースに開発された汎用コードエディタのことで、大部分のvscodeプラグインエコシステムと互換性があり、vscodeと同様の機能(LSP3.7プロトコルのサポート、remote sshによるリモート開発機能、エディター間で設定ファイルを共有する機能など)を備えています。 + +代表的なVLEには、vscode、trae、cursor、およびvscodiumの各種ディストリビューションがあります。 +::: + +## Claude Inspectorとは + +Claude Inspectorは、Claude公式(つまりMCPプロトコルの提案者)がリリースしたオープンソースのMCPデバッガーです。開発者はMCPサーバーの開発後に、このデバッガーを使用して機能の完全性をテストできます。 + +![](./images/inspector.png) + +ただし、Inspectorツールには以下のような欠点があります: + +- 使用が面倒:Inspectorを使用するたびに、mcp devでWebフロントエンドとバックエンドのアプリケーションを起動する必要があります。 +- 機能が少ない:Inspectorは最も基本的なMCPのtool属性などのデバッグのみを提供しています。ユーザーが開発したMCPサーバーが大規模モデルとの相互作用でどのように動作するかをテストしたい場合、Claude Desktopに接続してクライアントを再起動する必要があり、連続的なデバッグシナリオでは非常に不便です。 +- いくつかのバグが存在:SSEやstreamable httpなどのリモート接続シナリオでは、Inspectorに既知のバグがあり、実際の産業レベルの開発に大きな影響を与えています。 +- デバッグ内容を保存または記録できない:大規模なマイクロサービスMCP化プロジェクトでは、これは非常に重要です。 +- 複数のMCPサーバーを同時にデバッグできない:MCPの原子化水平展開シナリオでは、これは必要な機能です。 + +OpenMCP Clientが作成された理由の1つは、Inspectorの上記の課題を解決し、MCPサーバーの開発ハードルを下げ、ユーザーがビジネス自体に集中できるようにすることです。 + + \ No newline at end of file diff --git a/ja/plugin-tutorial/quick-start/acquire-openmcp.md b/ja/plugin-tutorial/quick-start/acquire-openmcp.md new file mode 100644 index 0000000..aa274a9 --- /dev/null +++ b/ja/plugin-tutorial/quick-start/acquire-openmcp.md @@ -0,0 +1,60 @@ +--- +layout: doc +--- + +# OpenMCPの入手方法 + +## プラグインストアからOpenMCPをインストール + +主要なVLEのプラグインストアで直接OpenMCPプラグインを入手できます。例えばVSCodeでは、左側のプラグインストアをクリックし、検索ボックスに`OpenMCP`と入力するとOpenMCPプラグインが見つかります。 + +![vscode プラグインストア](./images/vscode-plugin-market.png) + +## オフラインインストール + +VLEのプラグインは本質的にzip圧縮ファイルであり、拡張子はvsixで全プラットフォーム共通です。私たちのCI/CDボットは各バージョンリリース後に自動的にビルドし、vsixをGitHub Releaseにアップロードします。以下のリンクから対応バージョンのGitHub Releaseページにアクセスできます: + +``` +https://github.com/LSTM-Kirigaya/openmcp-client/releases/tag/v{バージョン番号} +``` + +例えばバージョン0.1.1の場合、リリースページのリンクはこちらです: [https://github.com/LSTM-Kirigaya/openmcp-client/releases/tag/v0.1.1](https://github.com/LSTM-Kirigaya/openmcp-client/releases/tag/v0.1.1) + +`Assets`の下に対応するvsix圧縮ファイルがあります + +![github release](./images/github-release.png) + +その他にも、以下のストアウェブサイトから最新のopenmcpのvsixを入手できます + +- https://open-vsx.org/extension/kirigaya/openmcp +- https://marketplace.visualstudio.com/items?itemName=kirigaya.openmcp + +vsix拡張子のファイルをクリックしてダウンロードし、ダウンロードが完了したら直接インストールできます。VLEで外部のvsixファイルをインストールする方法は2つあります。 + +### 方法1: VLE内でインストール + +VLEのプラグインストアページには3点リーダーボタンがあり、クリックすると以下のリスト内の赤くマークされたボタンが表示されます + +![vscode プラグインストア](./images/vscode-plugin-market-install-from.png) + +クリック後、先ほどダウンロードしたvsixファイルを見つけ、クリックするとインストールが完了します。 + +### 方法2: コマンドラインを使用 + +VLEがグローバルにインストールされている場合、自動的にコマンドラインツールが利用可能になります。コマンドは以下の通りです: + +::: code-group +```bash [vscode] +code --install-extension /path/to/openmcp-0.1.1.vsix +``` + +```bash [trae] +trae --install-extension /path/to/openmcp-0.1.1.vsix +``` + +```bash [cursor] +cursor --install-extension /path/to/openmcp-0.1.1.vsix +``` +::: + +`/path/to/openmcp-0.1.1.vsix`はダウンロードしたvsixファイルの絶対パスを表します。これでもプラグインをインストールできます。 \ No newline at end of file diff --git a/ja/plugin-tutorial/quick-start/first-mcp.md b/ja/plugin-tutorial/quick-start/first-mcp.md new file mode 100644 index 0000000..bd95ffb --- /dev/null +++ b/ja/plugin-tutorial/quick-start/first-mcp.md @@ -0,0 +1,136 @@ +# あなたの最初のMCP + +MCPを実装するプログラミング言語は多く、一般的なほぼすべてのプログラミング言語に公式・非公式のサポートがあります。「プログラミング言語 + MCP」で検索すれば対応するライブラリが見つかります。[[mcp-examples|MCPサーバー開発事例]]では、さまざまなプログラミング言語の異なる例も提供しています。 + +すべてのプログラミング言語の中で、PythonでのMCP開発は間違いなく最も簡単で、初心者にも取り組みやすいものです。そのため、最初のMCPはPythonで実装します。他のプログラミング言語での実装も大同小異です。 + +## uvのインストール + +Pythonでmcpサーバーを書く際には、パッケージマネージャーとしてuvを使用することを強く推奨します。uvについて知っておくべきことは、高性能なパッケージマネージャーであり、pipとcondaのすべての利点を備えていることです。uvがインストールされていない場合は、まずpipでuvをインストールしてください: + +```bash +pip install uv +``` + +:::warning anacondaまたはminicondaを使用している方へ! +非base環境にuvをインストールしないでください。base環境でuvをインストールしてください。uv自体が環境の分離を適切に行いますので、uvがbase環境を汚染する心配はありません。base環境以外にインストールしたり、グローバルなpipでインストールしたりすると、uvがどこにインストールされたのかわからなくなります!base環境でpipを使用してインストールしたスクリプトは`~/anaconda/bin/uv`にインストールされますので、`~/anaconda/bin/`が`$PATH`に含まれていることも確認してください。 +::: + +uvのバージョンを確認します: + +```bash +uv version +``` + +私の出力は次の通りです: +``` +uv 0.6.9 (3d9460278 2025-03-20) +``` + +実際に操作する際には、このバージョンよりも低くならないようにしてください。 + +## 最もシンプルなmcpサーバーの作成 + +プロジェクトディレクトリに移動し、最もシンプルなmcpサーバーを作成する準備をします。 + +```bash +mkdir -p ~/codes/my-first-mcp +cd ~/codes/my-first-mcp +uv init --no-workspace +``` + +この時点で、プロジェクト内には以下の3つのファイルがあるはずです: + +``` +README.md main.py pyproject.toml +``` + +次に、現在のフォルダでvscodeまたはtraeを開き、最もシンプルなmcpサーバーを作成します。その機能は次の通りです: +- 2つの数字を加算するための「add」というツールを提供 +- 挨拶メッセージを返す「greeting」というリソースを提供 + +まず、mcp関連のライブラリをインストールします: + +```bash +uv add mcp "mcp[cli]" +``` + +`main.py`の内容を以下のように変更します: + +```python +from mcp.server.fastmcp import FastMCP +mcp = FastMCP('錦恢の MCP Server', version="11.45.14") + +@mcp.tool( + name='add', + description='2つの数字を実数領域で加算する' +) +def add(a: int, b: int) -> int: + return a + b + +@mcp.resource( + uri="greeting://{name}", + name='greeting', + description='デモンストレーション用のリソースプロトコル' +) +def get_greeting(name: str) -> str: + return f"Hello, {name}!" + +@mcp.prompt( + name='translate', + description='翻訳を行うプロンプト' +) +def translate(message: str) -> str: + return f'以下の文章を中国語に翻訳してください:\n\n{message}' +``` + +## OpenMCPでワンクリック接続 + +上記のように、mcpのtool、resource、promptとして使用する3つの関数を宣言しました。OpenMCPでこれらを起動するのは非常に簡単で、右上のOpenMCPアイコンをクリックするだけで接続できます: + +![](./images/connect-simple.png) + +OpenMCPを初めて使用する場合、ガイド画面が表示されますので、ぜひ最後までご覧ください。 + +![](./images/guide.png) + +ログインが完了し、以下のように接続成功と表示されれば、mcpサーバーの起動と接続が成功したことを意味します。 + +![](./images/connect-success.png) + +おめでとうございます。最初の一歩が最も難しいですが、あなたはすでに最も難しいmcp接続を完了しました! + +OpenMCPを使用したmcpサーバー接続に関する詳細情報は、マニュアルのこの章[[connect-mcp|MCPサーバーへの接続]]を参照してください。 + +## 付録:uvでmcpを起動する際に知っておくべきこと + +OpenMCPはすでに多くのことを行ってくれていますが、uvでmcpサーバーを起動する方法は1つだけではありません。より基本的な原理を理解することで、あらゆる状況に対応できるようになります。OpenMCPはPythonプロジェクトに対してデフォルトで`uv run mcp run main.py`を実行してmcpサーバーを起動しますが、GitHub上の一部のプロジェクトはこの方法では起動できません。 + +まず、上記の例のPythonコードをコマンドラインからどのように起動するかを理解しましょう! + +### 方法1:mcp-cliを使用する + +mcp自体がスキャフォールディングを提供しており、宣言されたPythonコードを直接起動してmcpサーバーとして実行できます。以下のコードで実行します: + +```bash +uv run mcp run main.py +``` + +### 方法2:コード内で明示的に起動する + +コード内で明示的にmcpサーバーを起動することもできます。`main.py`の末尾に以下を追加します: + +```python +if __name__ == '__main__': + mcp.run() +``` + +その後、以下のコードを実行すればmcpサーバーが起動します: + +```bash +uv run main.py +``` + +:::warning +`python main.py`を実行しないでください。`uv run`は現在の仮想環境のライブラリを使用しますが、これらのライブラリは外部のPythonからは見えません。また、`mcp.run()`で起動コードを使用せずに`uv run main.py`を直接使用することも避けてください。私たちがこれまでに書いたコードは関数を宣言しただけで、実際には何の機能も実行していません。 +::: \ No newline at end of file diff --git a/ja/plugin-tutorial/quick-start/images/bing-image-common.png b/ja/plugin-tutorial/quick-start/images/bing-image-common.png new file mode 100644 index 0000000..35fb776 Binary files /dev/null and b/ja/plugin-tutorial/quick-start/images/bing-image-common.png differ diff --git a/ja/plugin-tutorial/quick-start/images/connect-simple.png b/ja/plugin-tutorial/quick-start/images/connect-simple.png new file mode 100644 index 0000000..20fd5f3 Binary files /dev/null and b/ja/plugin-tutorial/quick-start/images/connect-simple.png differ diff --git a/ja/plugin-tutorial/quick-start/images/connect-success.png b/ja/plugin-tutorial/quick-start/images/connect-success.png new file mode 100644 index 0000000..0100f62 Binary files /dev/null and b/ja/plugin-tutorial/quick-start/images/connect-success.png differ diff --git a/ja/plugin-tutorial/quick-start/images/github-release.png b/ja/plugin-tutorial/quick-start/images/github-release.png new file mode 100644 index 0000000..f34ed8e Binary files /dev/null and b/ja/plugin-tutorial/quick-start/images/github-release.png differ diff --git a/ja/plugin-tutorial/quick-start/images/guide.png b/ja/plugin-tutorial/quick-start/images/guide.png new file mode 100644 index 0000000..9dff225 Binary files /dev/null and b/ja/plugin-tutorial/quick-start/images/guide.png differ diff --git a/ja/plugin-tutorial/quick-start/images/image.png b/ja/plugin-tutorial/quick-start/images/image.png new file mode 100644 index 0000000..38f2071 Binary files /dev/null and b/ja/plugin-tutorial/quick-start/images/image.png differ diff --git a/ja/plugin-tutorial/quick-start/images/inspector.png b/ja/plugin-tutorial/quick-start/images/inspector.png new file mode 100644 index 0000000..442aef4 Binary files /dev/null and b/ja/plugin-tutorial/quick-start/images/inspector.png differ diff --git a/ja/plugin-tutorial/quick-start/images/llm-bing-image-render.png b/ja/plugin-tutorial/quick-start/images/llm-bing-image-render.png new file mode 100644 index 0000000..7fe1168 Binary files /dev/null and b/ja/plugin-tutorial/quick-start/images/llm-bing-image-render.png differ diff --git a/ja/plugin-tutorial/quick-start/images/llm-calc.png b/ja/plugin-tutorial/quick-start/images/llm-calc.png new file mode 100644 index 0000000..0fdfd1f Binary files /dev/null and b/ja/plugin-tutorial/quick-start/images/llm-calc.png differ diff --git a/ja/plugin-tutorial/quick-start/images/llm-intro.png b/ja/plugin-tutorial/quick-start/images/llm-intro.png new file mode 100644 index 0000000..3657701 Binary files /dev/null and b/ja/plugin-tutorial/quick-start/images/llm-intro.png differ diff --git a/ja/plugin-tutorial/quick-start/images/llm-tools.png b/ja/plugin-tutorial/quick-start/images/llm-tools.png new file mode 100644 index 0000000..47b351d Binary files /dev/null and b/ja/plugin-tutorial/quick-start/images/llm-tools.png differ diff --git a/ja/plugin-tutorial/quick-start/images/openmcp-home.png b/ja/plugin-tutorial/quick-start/images/openmcp-home.png new file mode 100644 index 0000000..26ac6cc Binary files /dev/null and b/ja/plugin-tutorial/quick-start/images/openmcp-home.png differ diff --git a/ja/plugin-tutorial/quick-start/images/openmcp.png b/ja/plugin-tutorial/quick-start/images/openmcp.png new file mode 100644 index 0000000..b732b99 Binary files /dev/null and b/ja/plugin-tutorial/quick-start/images/openmcp.png differ diff --git a/ja/plugin-tutorial/quick-start/images/rerun-bing-image.png b/ja/plugin-tutorial/quick-start/images/rerun-bing-image.png new file mode 100644 index 0000000..01b97cb Binary files /dev/null and b/ja/plugin-tutorial/quick-start/images/rerun-bing-image.png differ diff --git a/ja/plugin-tutorial/quick-start/images/resource-desc.png b/ja/plugin-tutorial/quick-start/images/resource-desc.png new file mode 100644 index 0000000..279f10e Binary files /dev/null and b/ja/plugin-tutorial/quick-start/images/resource-desc.png differ diff --git a/ja/plugin-tutorial/quick-start/images/resource-result.png b/ja/plugin-tutorial/quick-start/images/resource-result.png new file mode 100644 index 0000000..7cbbd76 Binary files /dev/null and b/ja/plugin-tutorial/quick-start/images/resource-result.png differ diff --git a/ja/plugin-tutorial/quick-start/images/system-prompt-add.png b/ja/plugin-tutorial/quick-start/images/system-prompt-add.png new file mode 100644 index 0000000..4e2106d Binary files /dev/null and b/ja/plugin-tutorial/quick-start/images/system-prompt-add.png differ diff --git a/ja/plugin-tutorial/quick-start/images/system-prompt-image.png b/ja/plugin-tutorial/quick-start/images/system-prompt-image.png new file mode 100644 index 0000000..d5620e8 Binary files /dev/null and b/ja/plugin-tutorial/quick-start/images/system-prompt-image.png differ diff --git a/ja/plugin-tutorial/quick-start/images/tool-add-test-project.png b/ja/plugin-tutorial/quick-start/images/tool-add-test-project.png new file mode 100644 index 0000000..e7d5f06 Binary files /dev/null and b/ja/plugin-tutorial/quick-start/images/tool-add-test-project.png differ diff --git a/ja/plugin-tutorial/quick-start/images/tool-desc.png b/ja/plugin-tutorial/quick-start/images/tool-desc.png new file mode 100644 index 0000000..2d134d7 Binary files /dev/null and b/ja/plugin-tutorial/quick-start/images/tool-desc.png differ diff --git a/ja/plugin-tutorial/quick-start/images/tool-result.png b/ja/plugin-tutorial/quick-start/images/tool-result.png new file mode 100644 index 0000000..d1bee87 Binary files /dev/null and b/ja/plugin-tutorial/quick-start/images/tool-result.png differ diff --git a/ja/plugin-tutorial/quick-start/images/vscode-plugin-market-install-from.png b/ja/plugin-tutorial/quick-start/images/vscode-plugin-market-install-from.png new file mode 100644 index 0000000..4b22fe2 Binary files /dev/null and b/ja/plugin-tutorial/quick-start/images/vscode-plugin-market-install-from.png differ diff --git a/ja/plugin-tutorial/quick-start/images/vscode-plugin-market.png b/ja/plugin-tutorial/quick-start/images/vscode-plugin-market.png new file mode 100644 index 0000000..0033ecd Binary files /dev/null and b/ja/plugin-tutorial/quick-start/images/vscode-plugin-market.png differ diff --git a/ja/plugin-tutorial/quick-start/index.md b/ja/plugin-tutorial/quick-start/index.md new file mode 100644 index 0000000..e39c482 --- /dev/null +++ b/ja/plugin-tutorial/quick-start/index.md @@ -0,0 +1,11 @@ +# クイックスタート + +1. [[acquire-openmcp|OpenMCPを入手する]] + +2. [[first-mcp|最初のMCPを作成]] +3. [[quick-debug|MCPを迅速にデバッグ]] +4. [[put-into-llm|大規模言語モデルでテストしてみよう!]] + + +
+
\ No newline at end of file diff --git a/ja/plugin-tutorial/quick-start/put-into-llm.md b/ja/plugin-tutorial/quick-start/put-into-llm.md new file mode 100644 index 0000000..7b47d33 --- /dev/null +++ b/ja/plugin-tutorial/quick-start/put-into-llm.md @@ -0,0 +1,63 @@ +# 大モデルで性能をテストしよう! + +[[quick-debug|前章]]では、mcpサーバーへの接続と各機能のデバッグを無事完了し、openmcpの基本的なデバッグ機能を紹介しました。次は、mcpを大規模モデル環境でテストする段階です。そもそもmcpが提案された目的は、誰もが自分で書いた機能を低コストで大規模モデルに統合できるようにするためでした。 + +本格的な対話を始める前に、[[connect-llm|大モデル接続]]を参照して大モデルAPIの設定を完了し、あなたの大モデルサービスが利用可能かどうかテストしてください。 + +## 大モデルとの対話 + +新しいデバッグプロジェクトを作成し、「インタラクティブテスト」を選択すると、大モデルとの対話ウィンドウが開きます。OpenMCPが提供する対話ウィンドウの基本構成は以下の通りです: + +![](./images/llm-intro.png) + +重要なボタンがいくつか表示されています。初めて使用する際は、デフォルト設定のまま進めても構いません。「使用するツール」をクリックすると、現在有効なツールが表示されます。OpenMCPはデフォルトで接続されているmcpサーバーが提供する全てのツールを有効にします。特定のツールを無効にしたい場合は、「使用するツール」から選択的に無効化できます: + +![](./images/llm-tools.png) + +それでは、mcpプロトコルに基づいて大モデルがどのようにツールを呼び出すか見てみましょう。デフォルト設定のまま、次の質問を入力します:123 + 1313 の計算結果を教えてください + +入力後、Enterキーを押して結果を待つと、以下のような出力が得られます: + +![](./images/llm-calc.png) + +大モデルが提供されたaddツールを使用して加算を実行したことがわかります。OpenMCPでは、大モデルが各ツールをどのように呼び出し、ツールからどのような結果が返されたかを確認できます。現在の質問とmcpが提供するツールは比較的単純ですが、複雑な問題の場合、大モデルは1回の応答で複数のツールを同時に呼び出して特定のタスクを完了することがあります。大モデルに毎回1つのツールのみを使用させたい場合は、デフォルトで点灯している「モデルが単一応答で複数ツールを呼び出すことを許可」をクリックしてこの機能を無効にできます。 + +## システムプロンプト + +[bing-images](/Users/bytedance/projects/openmcp-tutorial/bing-images)のような特殊なケースでは、キーワードに基づいてbingの画像を返すmcpサーバーです。 + +次の質問を直接投げかけてみましょう:アークナイツの画像をいくつか検索してください。デフォルト設定では、以下のような応答が得られる可能性があります: + +![](./images/bing-image-common.png) + +大モデルは取得した画像をリンク形式で返しましたが、時には画像形式で画面にレンダリングして表示してほしい場合もあります。大モデルの応答スタイルを制約・誘導したり、要求したテンプレートに従って応答させるためには、システムプロンプトを設定することで実現できます。 + +まず、下部の「システムプロンプト」をクリックします: + +![](./images/system-prompt-add.png) + +新しいシステムプロンプトを追加し、タイトルに「bing image」と入力し、本文に以下を記入します: + +``` +あなたはbing画像検索が得意なAIです。画像を見つけたら、markdown形式で画像を返す必要があります。例えば ![](https://xxxx.xx/xxxx) +``` + +保存をクリックします。 + +![](./images/system-prompt-image.png) + +次に、最初のユーザーダイアログにカーソルを移動させると、いくつかのボタンが表示されます。再実行ボタンを選択すると、openmcpはこの対話を再実行します。 + +![](./images/rerun-bing-image.png) + +すると、画像が正常にレンダリングされていることが確認できます: + +![](./images/llm-bing-image-render.png) + +system promptやその他のより精密な方法でagentを制御するテクニックについてさらに学びたい場合は、[[go-neo4j-sse|goで実装するneo4jの読み取り専用mcpサーバー (SSE)]]を参照してください。 + +## まとめ + +おめでとうございます!openmcpの基本チュートリアルを完了しました。次は、何か面白いことに取り組む時です![[mcp-examples|MCPサーバー開発事例]]では、openmcpを使用したmcpサーバー開発のさらなる例を見つけることができます。 + +様々な驚きがあなたをお待ちしています。どうぞご自由にお取りください。 \ No newline at end of file diff --git a/ja/plugin-tutorial/quick-start/quick-debug.md b/ja/plugin-tutorial/quick-start/quick-debug.md new file mode 100644 index 0000000..0bed3a7 --- /dev/null +++ b/ja/plugin-tutorial/quick-start/quick-debug.md @@ -0,0 +1,48 @@ +# MCPの迅速なデバッグ + +[[first-mcp|最初のMCP]]では、MCPサーバーの最小インスタンスを作成し、openmcpを使ってこのサーバーに接続することに成功しました。 + +次に、このサーバーの機能をデバッグできます。結局のところ、Jeaf Deanのように誰もが一度で全てのコードを正しく書けるわけではありません。私たちが作成するMCPサーバーも、最初から自信を持って公開できるものばかりではなく、常に私たちが気付かない問題が存在します。想像してみてください、後でmcpを大規模モデルに接続して全リンクのデバッグを行う際に問題が発生した場合、その時には非常に多くのエラー要因が考えられることに気付くでしょう:MCPサーバーの問題?大規模モデルベンダーの問題?OpenMCPの問題?可能性のあるエラーを分類し、一つずつ調査することが、エンジニアリングの直感(Engineering Instuition)に適った方法です。 + +## パネルの理解 + +初めてopenmcpに入ると、パネルが表示され、4つのボタンがあり、4種類のデバッグ項目を表しています: + +![](./images/openmcp-home.png) + +私たちが現在確認する必要があるのは、tool、resource、promptの3つの機能が正常に動作しているかどうかです。実際のプロジェクトでは、toolが最も頻繁に使用される項目であるため、まずtoolをデバッグします。 + +## Toolのデバッグ + +toolをデバッグするために、パネルの「ツール」ボタンをクリックし、toolデバッグインターフェースに入ります。toolパネルの基本説明は以下の通りです: + +![](./images/tool-desc.png) + +ツールをデバッグするには、まず「ツールリスト」からツールを選択する必要があります(展開されていない場合はまずツールリストを展開し、右側のボタンをクリックして更新できます)。次に、右側の「パラメータ入力と実行」でテストするパラメータを入力し、実行をクリックすると結果が表示されます: + +![](./images/tool-result.png) + +例えば、ここで最も簡単な2 + 2を計算すると、結果は4であることがわかります。これは、私たちのmcp接続が正常で、正しく結果を返すことができることを示しています。将来的には、簡単なテストを通じてmcpサーバーの可用性を検証できます。これは複雑なagentシステムのデバッグプロセスにおいて非常に重要です。自己診断プログラムの一部としてコード化することも可能です。 + +## テスト項目の追加 + +1つの項目のテストが完了したら、上部の+をクリックして追加のテスト項目を追加できます: + +![](./images/tool-add-test-project.png) + +ここでは、「リソース」を選択してリソース項目のデバッグ作業を行います。「リソース」は他の2つの項目とは少し異なり、MCPプロトコルにおけるリソースアクセスには2つのタイプがあります: + +- resources/templates/list: テンプレートリソース。アクセスパラメータを持ちます。例えば、ファイルシステムmcp内のファイルアクセスで、ファイルパスを入力し、リソースプロトコルに従ってファイル内容を返します。 +- resources/list:通常のリソース。アクセスパラメータを持ちません。例えば、ブラウザmcp内のconsoleで、直接コンソールのstdioを返します。この場合、パラメータは必要ありません。 + +![](./images/resource-desc.png) + +`resources/templates/list`の使用方法は以前のtoolと同じで、パラメータを入力して実行をクリックするとリソース結果が表示されます: + +![](./images/resource-result.png) + +一方、`resources/list`はパラメータがないため、左側のリソースを直接クリックするだけで内部データを確認できます。 + +## まとめ + +この章では、主にopenmcpを使用してMCPサーバーをデバッグする方法を紹介しました。toolとresourceのデバッグ方法を含み、promptの方法もこれらと似ていますので、各自で試してみてください。次の章では、最もエキサイティングな章を開始します。開発したmcpサーバーを大規模モデルに投入してテストを行い、作成したmcpが本当に面白く、価値があるかどうかを確認します。 \ No newline at end of file diff --git a/ja/plugin-tutorial/usage/connect-llm.md b/ja/plugin-tutorial/usage/connect-llm.md new file mode 100644 index 0000000..83cef9d --- /dev/null +++ b/ja/plugin-tutorial/usage/connect-llm.md @@ -0,0 +1,66 @@ +# 大モデルの接続 + +「インタラクションテスト」を使用して大モデルとの対話中にMCPツールの性能をテストする場合、まずOpenMCPで大モデルを設定する必要があります。 + +:::warning プロトコル互換性に関する警告 +現在OpenMCPはOpenAIインターフェース仕様に準拠した大モデルサービスのみをサポートしています。他の大モデルを呼び出す必要がある場合は、[newApi](https://github.com/QuantumNous/new-api)を介して転送するか、独自に実装してください。 + +現在市場で主流の以下のモデルはすべてサポートしています。大モデルの接続に問題が発生した場合は、いつでも[[channel|お問い合わせください]]。 +::: + +「設定」-「API」で大モデルの接続設定画面に入ることができます。 + +![](./images/setting-api.png) + +## デフォルトでサポートされているモデル + +OpenMCPはデフォルトで市場で一般的な大モデルを事前に設定しています。以下はサポートされているモデルです。 + +| 大モデル名 | プロバイダー | baseUrl | デフォルトモデル | +|----------------------|-----------------------------|---------------------------------------------|-----------------------| +| DeepSeek | DeepSeek | `https://api.deepseek.com/v1` | `deepseek-chat` | +| OpenAI | OpenAI | `https://api.openai.com/v1` | `gpt-4-turbo` | +| 通義千問 Qwen | Alibaba | `https://dashscope.aliyuncs.com/compatible-mode/v1` | `qwen-plus` | +| 豆包 Seed | ByteDance | `https://ark.cn-beijing.volces.com/api/v3` | `doubao-1.5-pro-32k` | +| Gemini | Google | `https://generativelanguage.googleapis.com/v1beta/openai/` | `gemini-2.0-flash` | +| Grok | xAI | `https://api.x.ai/v1` | `grok-3-mini` | +| Mistral | Mistral AI | `https://api.mistral.ai/v1` | `mistral-tiny` | +| Groq | Groq | `https://api.groq.com/openai/v1` | `mixtral-8x7b-32768` | +| Perplexity | Perplexity AI | `https://api.perplexity.ai/v1` | `pplx-7b-online` | +| Kimi Chat | 月の暗面 (Moonshot AI) | `https://api.moonshot.cn/v1` | `moonshot-v1-8k` | + +## 大モデルの設定 + +対応するサービスプロバイダーのapiTokenをopenmcpに入力するだけです。その後「テスト」をクリックし、以下の応答が表示されれば接続成功です。インタラクションテストで大モデルを使用できるようになります! + +![](./images/setting-api-test.png) + +:::warning +一部のユーザーはアクセスできない問題に遭遇する可能性があります。baseUrlが正しく入力されていることを確認してください。国内でGeminiやOpenAIなどの国外プロバイダーのサービスを使用する場合、ネットワーク環境がこれらのサービスにアクセスできることを確認してください。「設定」-「一般」でプロキシサーバーを設定できます。 +::: + +## モデルの追加 + +使用したい特定のプロバイダーのモデルがデフォルトでサポートされていない場合、2つの方法で追加できます。 + +### 方法1:モデルリストの更新 + +ここでは通義千問を例にします。apitokenが正しく入力されていることを確認した上で、「モデルリストを更新」をクリックします。プロバイダーがOpenAI標準を厳密に実装していれば、すべての更新されたモデルが表示されます。 + +![](./images/setting-update-models.png) + +### 方法2:手動でのモデル追加 + +サーバーがOpenAI標準をサポートしていない場合、「方法1」を使用できません。このように手動でモデルリストを追加できます。ここではGrokを例にします。プロバイダーリストからgrokを探し、図に示す編集をクリックします。 + +![](./images/setting-api-edit.png) + +モデルをクリックし、モデル名を入力してEnterキーを押し、確認をクリックします: + +![](./images/setting-api-edit-1.png) + +APIページに戻り、保存をクリックします。 + +## サービスの追加 + +リストにないプロバイダーサービス(クラウドプロバイダーのサービスや自分でデプロイしたサービス)を使用する場合、「サービスの追加」ボタンでカスタムモデルを追加できます。使用方法は「モデルの追加」「方法2:手動でのモデル追加」と同様なので、ここでは繰り返しません。 \ No newline at end of file diff --git a/ja/plugin-tutorial/usage/connect-mcp.md b/ja/plugin-tutorial/usage/connect-mcp.md new file mode 100644 index 0000000..617e1a6 --- /dev/null +++ b/ja/plugin-tutorial/usage/connect-mcp.md @@ -0,0 +1,73 @@ +# MCPサーバーへの接続 + +Claude Desktopや他のMCPクライアント製品とは異なり、OpenMCPによるMCPサーバー接続の手順は非常にスムーズです。 + +:::info MCPクライアント +MCPクライアントとは、MCPプロトコルを通じて通信可能な大規模言語モデル対話クライアントのことで、通常はローカルで動作するアプリケーション(ウェブページにはファイルIOの権限がないため)です。その製品形態は現在ほぼチャットボット形式で、chat.deepseek.comやchat.openai.comのようなウェブサイトで使用するものと類似しています。 +::: + +まず、VLEを開き、[[acquire-openmcp|OpenMCPの取得]]でOpenMCPのインストールを完了した後、pythonで最も簡単なmcpサーバーを作成し、mcpクライアントの接続をテストします。 + +## OpenMCPでワンクリック接続 + +[[first-mcp|最初のMCP]]の例では、mcpのtool、resource、promptとして3つの関数を宣言しました。OpenMCPでこれらを起動するのは非常に簡単で、右上のOpenMCPアイコンをクリックするだけで接続できます: + +![](./images/connect-simple.png) + +ログインが完了し、図のように接続成功が表示されれば、現在mcpサーバーが正常に起動・接続されたことを意味します。 + +![](./images/connect-success.png) + +## STDIO接続の起動 + +STDIOを接続オプションとする開発方案では、ワンクリックでの迅速な起動を提供しており、mcpプロセスを追加で起動する必要はありません。OpenMCPが自動的に接続と破棄を行います。 + +現在サポートされているプログラミング言語とそれに対応する起動パラメータは以下の通りです: + +|言語|接続パラメータ|起動ディレクトリ| +|:-|:-|:-| +|python|uv run mcp run $\{file\} | 遡って最初に見つかったpyproject.tomlのディレクトリ| +|nodejs|node $\{file\}| 遡って最初に見つかったpackage.jsonのディレクトリ| +|go|go run $\{file\}| 遡って最初に見つかったgo.modのディレクトリ| + +## SSE & Streamable HTTP接続の起動 + +SSEとStreamable HTTPという2つのリモート接続方式については、どのポートでサーバーが起動されているか分からない(起動hostとportが見えない設定ファイルや環境変数に書かれている可能性があるため)ため、リモート接続の場合、自動サーバー作成はサポートしておらず、手動で起動オプションを設定する必要があります。 + +VLE左側のプラグインメニューにあるOpenMCPをクリックし、「MCP接続(ワークスペース)」ビューで+をクリックすると、新しい接続を作成できます。 + +![](./images/add-connection.png) + +必要な通信方式を選択します。 + +![](./images/select-server-type.png) + +MCP Serverのアドレスを入力します。 + +![](./images/connect-sse.png) + +:::info +注意が必要なのは、異なる通信方式は一般的に異なるendpointを使用することです。現在のMCP serverの多くは以下の原則に従っています: + +SSEで起動する場合、デフォルトで/sseをendpointとして使用します。例:http://localhost:8001/sse + +Streamable Httpで起動する場合、デフォルトで/mcpをendpointとして使用します。例:http://localhost:8001/mcp + +もちろん、MCP Serverが2つの異なるendpointを使用して両接続方式を同時にサポートすることも可能で、Streamable Httpに移行したいが短期的にSSEを放棄できない状況に特に有効です +::: + +## openmcpプラグインのコントロールパネル + +VLEの左側にopenmcpのアイコンがあり、クリックするとopenmcpのコントロールパネルが表示されます。 + +![](./images/openmcp-control-panel.png) + +現在のワークスペースで以前接続したmcpサーバーはここに表示されます。これは、openmcpがデフォルトでワークスペース起動時のmcp接続情報を`.openmcp/tabs.{server-name}.json`に保存しているためで、`{server-name}`はmcpサーバー接続成功時のサーバー名です。 + +:::warning +注意:同じプロジェクト内で、名前が完全に同じmcpサーバーを2つ持つべきではありません。これにより`.openmcp/tabs.{server-name}.json`の接続情報保存が衝突し、未知のエラーが発生する可能性があります。 +::: + +任意のワークスペースで同じmcpサーバーを使用したい場合は、「インストール済みMCPサーバー」に成熟した耐久性のあるmcpサーバーを追加することを検討してください。この場所に追加されたmcpサーバーはグローバルに使用可能です。 + +「入門とヘルプ」では、入門用の参考資料をいくつか準備しています。ぜひご活用ください。 \ No newline at end of file diff --git a/ja/plugin-tutorial/usage/debug.md b/ja/plugin-tutorial/usage/debug.md new file mode 100644 index 0000000..5bd2a5e --- /dev/null +++ b/ja/plugin-tutorial/usage/debug.md @@ -0,0 +1,67 @@ +# デバッグ tools、resources、prompts + +## タブ + +openmcpはタブをデバッグ項目の最小単位としており、ナビゲーションバーの+をクリックすると新しいタブを作成できます。OpenMCPのtools、resources、promptsの基本的な使用方法はInspectorとほぼ同じですが、OpenMCPは自動的に左側のリソースリストの初期化を行います。Inspectorではこの手順を手動で行う必要があります。 + +## デバッグ内容の自動保存 + +openmcpにはテスト結果を自動保存する機能があります。以下の動作が発生すると、openmcpはタブとその内容を保存します: + +- タブを作成し、有効なデバッグ項目を選択した場合 +- デバッグページでデバッグ操作(ツールの選択、ツールの実行、大規模モデルへの質問など)を行った場合 + +現在のmcpプロジェクトのテストデータは`.openmcp/tabs.{server-name}.json`に保存されます。ここで`{server-name}`はmcpサーバーが正常に接続されたサーバー名です。 + +:::warning +注意:同じプロジェクト内で名前が完全に同じmcpサーバーを2つ持つべきではありません。これにより`.openmcp/tabs.{server-name}.json`の接続情報保存に競合が発生し、未知のエラーが発生する可能性があります。 +::: + +## クイックデバッグ + +デバッグプロセス中に、大規模モデルの回答が不十分で、これが特定のツールのエラーによるものである場合、問題がツールにあるかどうかを迅速に特定するために、下部の小さな飛行機アイコンをクリックできます。 + +![](./images/llm-fast-debug.png) + +クリックすると、OpenMCPは新しいテストツールプロジェクトを作成し、大規模モデルが使用したパラメータを自動的に右側のフォームに入力します: + +![](./images/llm-fast-debug-result.png) + +あなたがするべきことは、実行をクリックしてエラーオプションを確認または除外することだけです。 + +## pydanticサポート + +Pythonのfastmcpを使用してtoolを作成する際、インターフェースのタイプを宣言する方法は2つあります。1つはPythonのデフォルトのtypingライブラリを使用して複雑なデータ構造を宣言する方法、もう1つはpydanticを使用して複雑な変数を宣言する方法です。以下は例です: + +```python +from mcp.server.fastmcp import FastMCP +from pydantic import BaseModel, Field +from typing import Optional, Union, List, NamedTuple + +mcp = FastMCP('錦恢の MCP Server', version="11.45.14") + +class PathParams(BaseModel): + start: str + end: str + +@mcp.tool(name="test",description="用来测试") +def test( + params: PathParams, + test1: str, + test2: Union[str, List[str]] = Field("", description="测试参数2"), + test3: Optional[str] = Field(None, description="测试参数3") +): + return [test1, test2, test3, params] +``` + +これら2種類の宣言方法に対して内部変換を実装しているため、openmcpはどちらもサポートしています。特に、宣言した変数がオブジェクト(上記の`PathParams`など)の場合、openmcpのtoolデバッグウィンドウは「オブジェクト入力ボックス」を生成します。この入力ボックスは基本的な形式チェックとオートコンプリートをサポートします: + +![](./images/object-input.png) + +:::info オブジェクトとは? +ここでの「オブジェクト」はJavaScriptの概念で、シリアライズ可能なデータ型の中で基本データ型を除いた部分を指します。例えば{ "name": "helloworld" }はオブジェクトです。Pythonでは、オブジェクトはdictやnamedTupleに似ています。 +::: + +:::warning +openmcpは可能な限り多くのケースをサポートしていますが、生産環境ではmcp toolのパラメータをオブジェクトとして定義することは推奨しません。シンプルなデータ型として定義することで、大規模モデルがツールを呼び出す際の安定性を向上させることができます。 +::: \ No newline at end of file diff --git a/ja/plugin-tutorial/usage/distribute-result.md b/ja/plugin-tutorial/usage/distribute-result.md new file mode 100644 index 0000000..9c50b6d --- /dev/null +++ b/ja/plugin-tutorial/usage/distribute-result.md @@ -0,0 +1,43 @@ +# 実験結果の配布 + +## タブの復元 + +openmcpはデフォルトで実験結果をリアルタイムに保存します。ワークスペースで開かれた各サーバーは、結果を`.openmcp/tabs.{server-name}.json`に保存します。ここで`{server-name}`はmcpサーバー接続が成功したサーバー名です。 + +.gitignoreファイルに.openmcpフォルダに一致するルールが含まれていないことを確認してください。これにより、gitでコードをコミットしたり、agentのコードを管理したりする際に、他のコンピュータでcloneしたり、他の人があなたのプロジェクトをcloneした場合に、前回の実験内容を迅速に復元し、実験や開発デバッグを継続できます。 + +## 接続の復元 + +各mcpサーバーの接続情報は`.openmcp/connection.json`に保存されます。以下は例です: + +```json +{ + "items": [ + [ + { + "connectionType": "STDIO", + "command": "mcp", + "args": [ + "run", + "main.py" + ], + "url": "", + "cwd": "{workspace}/simple-mcp", + "oauth": "", + "clientName": "openmcp.connect.STDIO", + "clientVersion": "0.0.1", + "env": {}, + "serverInfo": { + "name": "錦恢の MCP Server", + "version": "1.9.2" + }, + "filePath": "{workspace}/simple-mcp/main.py", + "name": "錦恢の MCP Server", + "version": "1.9.2" + } + ] + ] +} +``` + +左側のコントロールパネルを開くか、過去に開いたmcpサーバーを開くと、mcpはデフォルトで上記の情報に基づいてワークスペースのサーバーリストを取得したり、自動接続を試みたりします。openmcpがmcpに接続する際に初期化エラーや保存エラーが発生した場合、openmcp公式に助けを求める以外に、`.openmcp/connection.json`ファイルを手動で管理することもできます。 \ No newline at end of file diff --git a/ja/plugin-tutorial/usage/images/add-connection.png b/ja/plugin-tutorial/usage/images/add-connection.png new file mode 100644 index 0000000..9bde43b Binary files /dev/null and b/ja/plugin-tutorial/usage/images/add-connection.png differ diff --git a/ja/plugin-tutorial/usage/images/add-new-mcp.png b/ja/plugin-tutorial/usage/images/add-new-mcp.png new file mode 100644 index 0000000..a05c9f0 Binary files /dev/null and b/ja/plugin-tutorial/usage/images/add-new-mcp.png differ diff --git a/ja/plugin-tutorial/usage/images/change-color.png b/ja/plugin-tutorial/usage/images/change-color.png new file mode 100644 index 0000000..e891ea4 Binary files /dev/null and b/ja/plugin-tutorial/usage/images/change-color.png differ diff --git a/ja/plugin-tutorial/usage/images/connect-simple.png b/ja/plugin-tutorial/usage/images/connect-simple.png new file mode 100644 index 0000000..20fd5f3 Binary files /dev/null and b/ja/plugin-tutorial/usage/images/connect-simple.png differ diff --git a/ja/plugin-tutorial/usage/images/connect-sse.png b/ja/plugin-tutorial/usage/images/connect-sse.png new file mode 100644 index 0000000..b178ef6 Binary files /dev/null and b/ja/plugin-tutorial/usage/images/connect-sse.png differ diff --git a/ja/plugin-tutorial/usage/images/connect-success.png b/ja/plugin-tutorial/usage/images/connect-success.png new file mode 100644 index 0000000..0100f62 Binary files /dev/null and b/ja/plugin-tutorial/usage/images/connect-success.png differ diff --git a/ja/plugin-tutorial/usage/images/drag-to-fill.png b/ja/plugin-tutorial/usage/images/drag-to-fill.png new file mode 100644 index 0000000..e2ce8e9 Binary files /dev/null and b/ja/plugin-tutorial/usage/images/drag-to-fill.png differ diff --git a/ja/plugin-tutorial/usage/images/guide.png b/ja/plugin-tutorial/usage/images/guide.png new file mode 100644 index 0000000..9dff225 Binary files /dev/null and b/ja/plugin-tutorial/usage/images/guide.png differ diff --git a/ja/plugin-tutorial/usage/images/llm-fast-debug-result.png b/ja/plugin-tutorial/usage/images/llm-fast-debug-result.png new file mode 100644 index 0000000..31706f4 Binary files /dev/null and b/ja/plugin-tutorial/usage/images/llm-fast-debug-result.png differ diff --git a/ja/plugin-tutorial/usage/images/llm-fast-debug.png b/ja/plugin-tutorial/usage/images/llm-fast-debug.png new file mode 100644 index 0000000..183dbad Binary files /dev/null and b/ja/plugin-tutorial/usage/images/llm-fast-debug.png differ diff --git a/ja/plugin-tutorial/usage/images/oauth-github-ak.png b/ja/plugin-tutorial/usage/images/oauth-github-ak.png new file mode 100644 index 0000000..c2b9813 Binary files /dev/null and b/ja/plugin-tutorial/usage/images/oauth-github-ak.png differ diff --git a/ja/plugin-tutorial/usage/images/oauth-github-new-application.png b/ja/plugin-tutorial/usage/images/oauth-github-new-application.png new file mode 100644 index 0000000..dc26ccd Binary files /dev/null and b/ja/plugin-tutorial/usage/images/oauth-github-new-application.png differ diff --git a/ja/plugin-tutorial/usage/images/oauth-github-success.png b/ja/plugin-tutorial/usage/images/oauth-github-success.png new file mode 100644 index 0000000..c84c737 Binary files /dev/null and b/ja/plugin-tutorial/usage/images/oauth-github-success.png differ diff --git a/ja/plugin-tutorial/usage/images/oauth-github-tool.png b/ja/plugin-tutorial/usage/images/oauth-github-tool.png new file mode 100644 index 0000000..f503075 Binary files /dev/null and b/ja/plugin-tutorial/usage/images/oauth-github-tool.png differ diff --git a/ja/plugin-tutorial/usage/images/object-input.png b/ja/plugin-tutorial/usage/images/object-input.png new file mode 100644 index 0000000..f8cb1b1 Binary files /dev/null and b/ja/plugin-tutorial/usage/images/object-input.png differ diff --git a/ja/plugin-tutorial/usage/images/one-dark-pro.png b/ja/plugin-tutorial/usage/images/one-dark-pro.png new file mode 100644 index 0000000..c8a30aa Binary files /dev/null and b/ja/plugin-tutorial/usage/images/one-dark-pro.png differ diff --git a/ja/plugin-tutorial/usage/images/openmcp-control-panel.png b/ja/plugin-tutorial/usage/images/openmcp-control-panel.png new file mode 100644 index 0000000..d8203bb Binary files /dev/null and b/ja/plugin-tutorial/usage/images/openmcp-control-panel.png differ diff --git a/ja/plugin-tutorial/usage/images/parallel-tool-call.png b/ja/plugin-tutorial/usage/images/parallel-tool-call.png new file mode 100644 index 0000000..da7e443 Binary files /dev/null and b/ja/plugin-tutorial/usage/images/parallel-tool-call.png differ diff --git a/ja/plugin-tutorial/usage/images/prompt.png b/ja/plugin-tutorial/usage/images/prompt.png new file mode 100644 index 0000000..7957e47 Binary files /dev/null and b/ja/plugin-tutorial/usage/images/prompt.png differ diff --git a/ja/plugin-tutorial/usage/images/resource.png b/ja/plugin-tutorial/usage/images/resource.png new file mode 100644 index 0000000..aec314d Binary files /dev/null and b/ja/plugin-tutorial/usage/images/resource.png differ diff --git a/ja/plugin-tutorial/usage/images/select-server-type.png b/ja/plugin-tutorial/usage/images/select-server-type.png new file mode 100644 index 0000000..ec7f182 Binary files /dev/null and b/ja/plugin-tutorial/usage/images/select-server-type.png differ diff --git a/ja/plugin-tutorial/usage/images/setting-api-edit-1.png b/ja/plugin-tutorial/usage/images/setting-api-edit-1.png new file mode 100644 index 0000000..00bd559 Binary files /dev/null and b/ja/plugin-tutorial/usage/images/setting-api-edit-1.png differ diff --git a/ja/plugin-tutorial/usage/images/setting-api-edit.png b/ja/plugin-tutorial/usage/images/setting-api-edit.png new file mode 100644 index 0000000..25264ba Binary files /dev/null and b/ja/plugin-tutorial/usage/images/setting-api-edit.png differ diff --git a/ja/plugin-tutorial/usage/images/setting-api-test.png b/ja/plugin-tutorial/usage/images/setting-api-test.png new file mode 100644 index 0000000..d0200a2 Binary files /dev/null and b/ja/plugin-tutorial/usage/images/setting-api-test.png differ diff --git a/ja/plugin-tutorial/usage/images/setting-api.png b/ja/plugin-tutorial/usage/images/setting-api.png new file mode 100644 index 0000000..89ea3eb Binary files /dev/null and b/ja/plugin-tutorial/usage/images/setting-api.png differ diff --git a/ja/plugin-tutorial/usage/images/setting-update-models.png b/ja/plugin-tutorial/usage/images/setting-update-models.png new file mode 100644 index 0000000..efe5462 Binary files /dev/null and b/ja/plugin-tutorial/usage/images/setting-update-models.png differ diff --git a/ja/plugin-tutorial/usage/images/system-prompt.png b/ja/plugin-tutorial/usage/images/system-prompt.png new file mode 100644 index 0000000..f7e52ba Binary files /dev/null and b/ja/plugin-tutorial/usage/images/system-prompt.png differ diff --git a/ja/plugin-tutorial/usage/images/trae-blue.png b/ja/plugin-tutorial/usage/images/trae-blue.png new file mode 100644 index 0000000..ea7f5f0 Binary files /dev/null and b/ja/plugin-tutorial/usage/images/trae-blue.png differ diff --git a/ja/plugin-tutorial/usage/multi-server.md b/ja/plugin-tutorial/usage/multi-server.md new file mode 100644 index 0000000..d17238c --- /dev/null +++ b/ja/plugin-tutorial/usage/multi-server.md @@ -0,0 +1,34 @@ +# 複数のMCPサーバーに接続する + +openmcpは複数のMCPサーバーへの接続をサポートしています。 + +例えば、資料を自動的に検索しWord文書にまとめるagentを実装したい場合、以下のようにできます: + +1. ウェブ検索可能なmcpを探す:[crawl4ai mcp](https://github.com/LSTM-Kirigaya/openmcp-tutorial/tree/main/crawl4ai-mcp) +2. Word操作可能なmcpを探す:[Office-Word-MCP-Server](https://github.com/GongRzhe/Office-Word-MCP-Server) +3. openmcpでこれらを組み合わせる +4. タスクを自動完了! + +最初のmcp(crawl4ai mcp)に既に接続している場合、追加のmcpサーバーを接続できます: + +![](./images/add-new-mcp.png) + +## 追加方法1:ドラッグ&ドロップ + +接続したいmcpサーバーファイルをShiftキーを押しながらopenmcpインターフェースにドラッグすると、パラメータが自動入力されます。 + +![](./images/drag-to-fill.png) + +:::warning +自動入力されたコマンドが常に正確とは限りません。[STDIO接続の起動](http://localhost:5173/openmcp/plugin-tutorial/usage/connect-mcp.html#stdio-%E8%BF%9E%E6%8E%A5%E7%9A%84%E5%90%AF%E5%8A%A8)で説明した通りです。具体的な接続方法は[付録:uvによるmcp起動の必須知識](http://localhost:5173/openmcp/plugin-tutorial/quick-start/first-mcp.html#%E9%99%84%E5%BD%95-%E5%85%B3%E4%BA%8E-uv-%E5%90%AF%E5%8A%A8-mcp-%E4%BD%A0%E5%BF%85%E9%A1%BB%E7%9F%A5%E9%81%93%E7%9A%84)を読んで判断してください。 +::: + +## 追加方法2:手動パラメータ入力 + +手動でパラメータを入力します。特に説明はありません。 + +## 複数サーバーの使用 + +複数サーバー接続後の使用方法は単一サーバーと大きく変わりません。openmcpが内部でツールのスケジューリングと選択を自動的に行います。唯一の注意点は、複数サーバー間でtool名が重複しないようにすることです。重複すると競合が発生します。 + +tool名の重複が必要なシナリオがある場合は、[issue](https://github.com/LSTM-Kirigaya/openmcp-client/issues)で使用ケースとアイデアを教えてください。議論を経て対応を検討します。 \ No newline at end of file diff --git a/ja/plugin-tutorial/usage/sse-oauth2.md b/ja/plugin-tutorial/usage/sse-oauth2.md new file mode 100644 index 0000000..9b91620 --- /dev/null +++ b/ja/plugin-tutorial/usage/sse-oauth2.md @@ -0,0 +1,65 @@ +# MCPサーバーのOAuth認証実装 + +**SSE**または**Streamable HTTP**を使用して接続する際、セキュリティを強化するためにインターフェースに認証メカニズムを設計できます。MCP公式ではOAuthプロトコルの採用を推奨しています。以下ではGitHubユーザー情報の取得を例に、openmcp-clientを使用してOAuth認証付きのインターフェースデバッグを完了する方法を説明します。 + +## 1. Github OAuth認証IDとsecretの取得 + +GitHubユーザー情報関連APIを使用するため、まずGitHub OAuthアプリのClient IDとClient secretを取得する必要があります。 + +[Github Developers](https://github.com/settings/developers)にアクセスし、`New OAuth App`をクリックして新しいOAuth APPを作成します。アプリケーション名は任意で入力し、`Homepage URL`には`http://localhost:8000`、`Authorization callback URL`には`http://localhost:8000/github/callback`を入力します。その後、`Register application`ボタンをクリックすると、アプリケーションが正常に登録されます。 + +![](images/oauth-github-new-application.png) + +登録が成功したら、`Client ID`を記録し、`Generate a new client secret`をクリックして`secret`を生成します。secretは生成時にのみ表示されるので注意してください。 + +## 2. 環境変数の設定 + +`Client ID`と`secret`を取得した後、それらを環境変数として設定する必要があります: + +::: code-group +```bash [bash] +export MCP_GITHUB_GITHUB_CLIENT_ID={{Client ID}} +export MCP_GITHUB_GITHUB_CLIENT_SECRET={{secret}} +``` + +```bash [PowerShell] +$env:MCP_GITHUB_CLIENT_ID = "your_id" +$env:MCP_GITHUB_CLIENT_SECRET = "your_secret" +``` + +```bash [CMD] +set MCP_GITHUB_GITHUB_CLIENT_ID={{Client ID}} +set MCP_GITHUB_GITHUB_CLIENT_SECRET={{secret}} +``` +::: + +注意:cmdで環境変数を設定する際は引用符を付けないでください。 + +## 3. ソースコードのクローン + +次に、OAuth認証付きのMCPサーバーをデプロイします。[公式python案例](https://github.com/modelcontextprotocol/python-sdk/tree/main/examples/servers/simple-auth)を参照してください。 + +まず公式python-sdkのソースコードをクローンします: + +```bash +git clone https://github.com/modelcontextprotocol/python-sdk/ # ソースコードをクローン +cd examples/servers/simple-auth # 対応するディレクトリに移動 +``` + +## 4. MCP Serverの起動 + +必要に応じて仮想環境を作成し依存関係をインストールした後、`uv`を使用して実行するか、直接`python main.py`を実行します。環境変数を設定していないと、起動時に`2 validation errors for ServerSettings`エラーが発生するので注意してください。 + +## 5. openmcp-clientの起動 + +これで、起動したばかりのserverにopenmcp-clientを使用して接続できます。ウェブ版でもVSCodeでも可能です。 + +プラス記号をクリックして接続を追加し、serverコードの`--transport`パラメータに基づいてSSEかStreamable HTTPかを決定します。SSEの場合、URLは`http://localhost:8000/sse`を入力します。Streamable HTTPの場合、URLは`http://localhost:8000/mcp`を入力します。認証署名は入力不要です。 + +次に現在のserverに接続すると、自動的にウェブページが開き認証が行われます。初回アクセス時は認証をクリックする必要があり、認証が成功するとウェブページは自動的に閉じます。 + +![](images/oauth-github-success.png) + +認証が成功すると、ツールページに移動し、`get_user_profile`ツールが表示されるはずです。クリックして使用すると、GitHubの個人情報を取得できます。 + +![](images/oauth-github-tool.png) \ No newline at end of file diff --git a/ja/plugin-tutorial/usage/test-with-llm.md b/ja/plugin-tutorial/usage/test-with-llm.md new file mode 100644 index 0000000..449f0af --- /dev/null +++ b/ja/plugin-tutorial/usage/test-with-llm.md @@ -0,0 +1,67 @@ +# 大モデルでMCPをテストする + +[[connect-llm|MCPサーバーへの接続]]が完了している場合、MCPのテストを開始できます。 + +[[put-into-llm|大モデルでテストしてみよう!]]では、MCPをテストする簡単な例を紹介しました。この記事では、「クイックスタート」では詳しく説明できなかった詳細について解説します。 + +大モデルと対話する際に調整可能なパラメータ(入力欄の下にあるボタン群)について簡単に説明します。 + +## モデル選択 + +名前の通り、使用するモデルを切り替えます。openmcpは会話ごとに使用されたモデルを記録します。この特性を活用して複数モデルの混合テストが可能です。 + +目的のモデルが見つからない場合や追加したい場合は、[[connect-llm|MCPサーバーへの接続]]を参照してください。 + +## システムプロンプト + +システムプロンプトを選択または追加できます。 + +![](./images/system-prompt.png) + +openmcpはデフォルトでシステムプロンプトを`~/.openmcp/nedb/systemPrompt.db`に保存します。nedbを使用してデシリアライズやコピーが可能です。 + +## プロンプト + +このモジュールでMCPサーバーが提供するprompt機能を呼び出せます。生成されたpromptフィールドはリッチテキストとして会話に挿入されます。 + +![](./images/prompt.png) + +## リソース + +このモジュールでMCPサーバーが提供するresource機能を呼び出せます。生成されたresourceフィールドはリッチテキストとして会話に挿入されます。 + +![](./images/resource.png) + +:::warning openmcpはresourceの永続化を管理しません! +注意!各会話終了後、resourceがディスクに保存されるかはMCPサーバー作者が決定します。openmcpを再起動後、resourceが空になってもopenmcpのバグではありません。MCPサーバー作者が永続化をサポートしていないためです! +::: + +## 単一応答での複数ツール呼び出しを許可 + +大モデルがツールを呼び出す際、1回の応答で複数ツールを呼び出す場合があります(例:3つのウェブページの翻訳を同時に取得する場合)。openmcpは並列ツール呼び出しを次のようにレンダリングします: + +![](./images/parallel-tool-call.png) + +「単一応答での複数ツール呼び出しを許可」オプションはデフォルトで有効です。 + +ツールを1つずつ実行したい場合は、このオプションを無効にします。 + +:::warning プロトコル互換性に関する注意 +Geminiなど、OpenAIプロトコルの「単一応答での複数ツール呼び出し」を完全にサポートしていないベンダーもあります。その場合、openmcpバックエンドは自動的にこのオプションを無効にします。 +::: + +## 温度パラメータ + +値が高いほど生成内容のランダム性が増します。汎用大モデルでは0.6~0.7が一般的なタスクに適しています。OpenMCPのデフォルト値は0.6です。 + +## コンテキスト長 + +大モデルが保持する最大対話数を指定します(デフォルト20)。合計40対話ある場合、openmcpは最後の20対話のみを送信します。 + +:::warning コンテキスト長を小さくしすぎないで! +20未満に設定するのは強く非推奨です。ツール呼び出し結果は以前のリクエストと正確に対応する必要があります。対応しない場合、400エラーが発生する可能性があります。この場合、新しい「対話テスト」を開始してください。 +::: + +## MCPサーバータイムアウト + +デフォルトのタイムアウトは30秒です。「設定」>「一般」で単位秒でグローバルに設定可能です。 \ No newline at end of file diff --git a/ja/plugin-tutorial/usage/ui-color.md b/ja/plugin-tutorial/usage/ui-color.md new file mode 100644 index 0000000..52164a6 --- /dev/null +++ b/ja/plugin-tutorial/usage/ui-color.md @@ -0,0 +1,23 @@ +# UI 配色 + +## openmcp のテーマカラーは vscode に連動 + +openmcp のテーマカラーは完全に vscode に連動しています。openmcp のテーマカラーを変更したい場合、vscode のテーマカラーを変更するだけでOKです。 + +例えば、コミュニティで有名なテーマ「One Dark Pro」に切り替えた時の openmcp の表示: + +![](./images/one-dark-pro.png) + +## テーマカラーの切り替え + +ここで openmcp のテーマカラーを切り替えられます(デフォルトはピンク) + +![](./images/change-color.png) + +## Trae への特別サポート + +openmcp は trae のデフォルトテーマカラーに対して追加サポートを提供しています。私たちはユーザーに、最適な操作性を得るために vscode、cursor、trae などの様々な VLE を試すことを推奨しています。 + +openmcp 公式ドキュメントのデモ例の多くは、trae の「深藍」デフォルトテーマをベースにしています。 + +![](./images/trae-blue.png) \ No newline at end of file diff --git a/ja/plugin-tutorial/what-is-mcp.md b/ja/plugin-tutorial/what-is-mcp.md new file mode 100644 index 0000000..775a040 --- /dev/null +++ b/ja/plugin-tutorial/what-is-mcp.md @@ -0,0 +1,34 @@ +# MCPとは? + +![](https://picx.zhimg.com/70/v2-1a2df8a081a76f4e90431d8a2445f495_1440w.avis) + +MCP(Model Context Protocol)は、アプリケーションが大規模言語モデル(LLMs)にコンテキストを提供する方法を標準化するためのオープンプロトコルです。MCPをAIアプリケーションのType-Cインターフェースと想像してください。Type-Cがデバイスを様々な周辺機器やアクセサリに接続する標準化された方法を提供するように、MCPはAIモデルを異なるデータソースやツールに接続する標準化された方法を提供します。 + +MCPプロトコルはAnthropicによって2024年11月末にリリースされました: + +- 公式ドキュメント:[Introduction](https://modelcontextprotocol.io/introduction) +- GitHubリポジトリ:[github.com/modelcontextprotocol](https://github.com/modelcontextprotocol) + + +## なぜMCPが必要なのか? + +私たちは皆、最初のchatgptから、後のcursor、copilot chatroom、そして現在よく知られているagentまで、実際にはユーザーインタラクションの観点から観察すると、現在の大規模モデル製品は以下のような変化を経てきたことがわかります: + +``` mermaid +graph LR + +a(chatbot > deepseek, chatgpt) --> b(composer > cursor, copilot) --> c(agent > AutoGPT, Manus, Open Manus) +``` + +- chatbot + - ただチャットするだけのプログラム。 + - ワークフロー:あなたが質問を入力し、それがその質問の解決策を提供しますが、具体的な実行は自分で行う必要があります。 + - 代表的な仕事:deepseek、chatgpt +- composer + - 少し手伝ってくれるインターン、コードを書くことに限られます。 + - ワークフロー:あなたが質問を入力し、それが問題を解決するコードを生成し、自動的にコードエディタのコンパイル領域に入力します、あなたは確認するだけです。 + - 代表的な仕事:cursor、copilot +- agent + - プライベート秘書。 + - ワークフロー:あなたが質問を入力し、それがその問題の解決策を生成し、あなたの同意を得た後、完全に自動的に実行します。 + - 代表的な仕事:AutoGPT、Manus、Open Manus \ No newline at end of file diff --git a/ja/preview/changelog.md b/ja/preview/changelog.md new file mode 100644 index 0000000..06ac7f5 --- /dev/null +++ b/ja/preview/changelog.md @@ -0,0 +1,79 @@ +# 変更履歴 + +## [main] 0.1.1 +- UbuntuへのSSH接続時のバグを修正 +- Pythonプロジェクトでopenmcpをクリックして接続する際の初期化パラメータエラーを修正 +- service層のmcp接続再利用技術を廃止し、リフレッシュ不能問題を防止 +- 接続後、ウェルカム画面でデバッグオプションが選択できないバグを修正 + +## [main] 0.1.0 +- 新機能: 複数mcpサーバーへの同時接続をサポート +- 新機能: プロトコル内容を更新し、streamable httpプロトコルをサポート(今後SSE接続方式を段階的に置換) +- issue#16実装: uv作成のpyプロジェクト向け特別サポート(自動初期化、mcpを.venv/bin/mcpに自動設定) +- npm作成のjs/tsプロジェクト向け特別サポート: 自動プロジェクト初期化 +- websearch設定を削除、parallel_tool_calls設定を追加(デフォルトtrue: 単一応答内での複数ツール呼び出し許可) +- openmcp接続モジュールインフラをリファクタリング、詳細なログシステムを実装 +- issue#15実装: コピー不能問題 +- issue#14実装: ログ消去ボタン追加 + +## [main] 0.0.9 +- 0.0.8で導入されたバグ修正: system promptがインデックスではなく実際の内容を返すように +- 新しいリリースパイプラインをテスト + +## [main] 0.0.8 +- 大規模モデルAPIテスト時のエラーレポートを強化 +- 0.0.7で導入されたバグ修正: 会話編集が送信できない問題 +- リッチテキストエディタの貼り付けスタイル問題を修正 +- リッチテキストエディタの空文字送信問題を修正 +- ストリーミング中のfunction calling時、マルチツールインデックスによるJSON Schemaデシリアライズ失敗を修正 +- 大規模モデルの重複エラーメッセージ問題を修正 +- 新機能: 単一会話での複数ツール同時呼び出しをサポート +- UI: コードハイライトのスクロールバーを最適化 +- 新機能: resources/listプロトコルコンテンツのクリック直接レンダリングを実装 +- 新機能: resources/prompts/toolsのJSON結果表示にハイライトをサポート + +## [main] 0.0.7 +- ページレイアウトを最適化し、デバッグウィンドウの表示領域を拡大 +- デフォルトコンテキスト長を10→20に拡張 +- 「一般オプション」に「MCPツール最大呼び出し時間(秒)」を追加 +- リッチテキスト入力欄をサポート、大規模prompt engineeringデバッグ作業が可能に + +## [main] 0.0.6 +- サーバー名特殊文字による保存不具合を修正 +- プラグインモードで左パネルの「MCP接続(ワークスペース)」ビューのCRUD操作を実現 +- 「インストール済みMCPサーバー」を追加(グローバル範囲mcp server管理) +- ガイドページを追加 +- オフラインOCR不能問題を修正 +- グローバルインストールmcpサーバーのname更新問題を修正 + +## [main] 0.0.5 +- 開いたことのあるファイルプロジェクトの管理をサポート +- ユーザー別サーバーデバッグ作業内容の保存をサポート +- 連続ツール呼び出しとエラー警告表示を実装 +- 小型ローカルオブジェクトデータベースを実装(会話生成マルチメディアの永続化) +- 呼び出し結果のワンクリック再現をサポート +- 中間結果の編集をサポート +- system promptの保存・編集をサポート + +## [main] 0.0.4 +- モデル選択後確認クリックでdeepseekに戻るバグを修正 +- mcpプロジェクト初期化時ツールが空になるバグを修正 +- 再接続不能問題を修正 +- サードパーティOpenAI互換モデルサービスのカスタマイズをサポート + +## [main] 0.0.3 +- メッセージごとのコスト統計情報を追加 +- 初期化ページルートがdebugでない場合の白画面バグを修正 + +## [main] 0.0.2 +- ページレイアウトを最適化 +- タブ更新後の表示不能バグを解決 +- 入力コンポーネントでEnter押下時の黒画面バグを解決 +- より完全で便利な開発スクリプトを実装 + +## [main] 0.0.1 +- openmcp基本inspector機能を完成 +- 設定読み込み/保存、大規模モデル設定を実装 +- タブ自動保存機能を実装 +- 大規模モデル会話ウィンドウとツール呼び出しを実装 +- vscodeとtraeのサポートを実装 \ No newline at end of file diff --git a/ja/preview/channel.md b/ja/preview/channel.md new file mode 100644 index 0000000..67064da --- /dev/null +++ b/ja/preview/channel.md @@ -0,0 +1,23 @@ +# リソースチャンネル + +## リソース + +[MCP シリーズビデオチュートリアル(制作中)](https://www.bilibili.com/video/BV1zYGozgEHc) + +[錦恢の mcp シリーズブログ](https://kirigaya.cn/blog/search?q=mcp) + +[OpenMCP 公式ドキュメント](https://kirigaya.cn/openmcp/plugin-tutorial) + +[openmcp-sdk 公式ドキュメント](https://kirigaya.cn/openmcp/sdk-tutorial) + +## チャンネル + +[Zhihuサークル - OpenMCP 博物館](https://www.zhihu.com/ring/host/1911121615279849840) + +[QQグループ - OpenMCP 正式技術チーム](https://qm.qq.com/cgi-bin/qm/qr?k=C6ZUTZvfqWoI12lWe7L93cWa1hUsuVT0&jump_from=webapi&authKey=McW6B1ogTPjPDrCyGttS890tMZGQ1KB3QLuG4aqVNRaYp4vlTSgf2c6dMcNjMuBD) + +[Discordチャンネル - OpenMCP 交流グループ](https://discord.gg/SKTZRf6NzU) + +[OpenMCP ソースコード](https://github.com/LSTM-Kirigaya/openmcp-client) + +[OpenMCP ドキュメントリポジトリ](https://github.com/LSTM-Kirigaya/openmcp-document) \ No newline at end of file diff --git a/ja/preview/contributors.md b/ja/preview/contributors.md new file mode 100644 index 0000000..04837bd --- /dev/null +++ b/ja/preview/contributors.md @@ -0,0 +1,83 @@ +--- +layout: page +--- + + + + \ No newline at end of file diff --git a/ja/preview/join.md b/ja/preview/join.md new file mode 100644 index 0000000..93fb70b --- /dev/null +++ b/ja/preview/join.md @@ -0,0 +1,34 @@ +# OpenMCP 開発に参加する + + +## 開発に参加したい場合、どうやって連絡すればいいですか? + +OpenMCPの開発に参加したい場合は、以下の方法で私たちに連絡できます: + +- OpenMCP正式技術グループに参加して直接議論する +- 錦恢のメールアドレス 1193466151@qq.com に連絡する +- [「Feature Request」タイプのissue](https://github.com/LSTM-Kirigaya/openmcp-client/issues)を提出するか、コードをforkして[PRを提出](https://github.com/LSTM-Kirigaya/openmcp-client/pulls)する + +## OpenMCPのために何ができますか? + +OpenMCP本体は技術的に複雑に見えるかもしれませんが、実際には想像以上に多くのことができます。 + +- PRを提出してコードを貢献したりバグを修正したりできます(もし希望すれば) +- 私たちのプロジェクトのために新しい機能を設計できます。必ずしもコードを書く必要はなく、[MVP要件計画](https://github.com/LSTM-Kirigaya/openmcp-client?tab=readme-ov-file#%E9%9C%80%E8%A6%81%E8%A6%8F%E5%88%92)に有意義な機能を提案するのも素晴らしい貢献です +- OpenMCPを使って様々なagent開発の例を作成したり、新しいAI Agent開発方法論を磨いたりできます。あなたの同意を得た後、私たちはあなたのチュートリアルをこのサイトに統合します + +上記の内容やその他をOpenMCPに貢献することで、あなたはOpenMCPの貢献者になることができます。 + +## openmcpドキュメントサイトに貢献する + +openmcpドキュメントサイトの専門的な誤りを修正したり、あなたの例を貢献したい場合は、[openmcp-document](https://github.com/LSTM-Kirigaya/openmcp-document)をforkしてPRを提出してください。 + +githubの操作に慣れていない場合は、[OpenMCP開発グループに参加](https://qm.qq.com/cgi-bin/qm/qr?k=C6ZUTZvfqWoI12lWe7L93cWa1hUsuVT0&jump_from=webapi&authKey=McW6B1ogTPjPDrCyGttS890tMZGQ1KB3QLuG4aqVNRaYp4vlTSgf2c6dMcNjMuBD)して管理者の錦恢に連絡してください。以下の情報を提供してください: + +- 貢献したい内容 +- あなたのgithubアカウント(ホームページリンク) +- メールアドレス +- サイトに表示したいIDとアバター +- 関連付けたいウェブサイトリンク(例:bilibili、Zhihu、個人サイトなど) + +追加が完了したら、[openmcp-document](https://github.com/LSTM-Kirigaya/openmcp-document)にPRを提出できます。 \ No newline at end of file diff --git a/ja/sdk-tutorial/index.md b/ja/sdk-tutorial/index.md new file mode 100644 index 0000000..221416a --- /dev/null +++ b/ja/sdk-tutorial/index.md @@ -0,0 +1,147 @@ +
+ + + +

openmcp-sdk : openmcp 向けデプロイメントフレームワーク

+

実験室から本番環境まで、エージェントを瞬時に展開

+ +
+ +# 導入 & インストール + +## openmcp-sdk.jsとは + +OpenMCP Clientは統合されたMCPデバッグソリューションを提供しますが、それだけでは物足りません。 + +作成したmcpを配布可能なアプリにしたり、サーバー上で関数サービスやマイクロサービスとして展開したい場合、OpenMCP Clientがフロントエンドに大規模モデルとの連携やツール使用ロジックを実装しているため、困難が生じます。 + +openmcp-sdk.jsはこの問題に対する軽量ソリューションです。Node.jsライブラリとして、完成したmcpをシームレスにエージェントとして展開可能にします。 + +## インストール + +::: code-group +```bash [npm] +npm install openmcp-sdk +``` + +```bash [yarn] +yarn add openmcp-sdk +``` + +```bash [pnpm] +pnpm add openmcp-sdk +``` +::: + +:::warning +現在openmcp-sdkはesmモードのインポートのみサポートしています +::: + +## 使用方法 + +ファイル名:main.ts + +```typescript +import { TaskLoop } from 'openmcp-sdk/task-loop'; +import { TaskLoopAdapter } from 'openmcp-sdk/service'; +async function main() { + // 通信とmcp接続を担当するアダプター作成 + const adapter = new TaskLoopAdapter(); + + // mcpサーバー追加 + adapter.addMcp({ + connectionType: 'STDIO', + commandString: 'node index.js', + cwd: '~/projects/openmcp-tutorial/my-browser/dist' + }); + + // イベントループドライバー作成(verbose値が高いほど詳細なログを出力) + const taskLoop = new TaskLoop({ adapter, verbose: 1 }); + + // 全ツール取得 + const tools = await taskLoop.getTools(); + + // 使用する大規模モデル設定 + taskLoop.setLlmConfig({ + id: 'deepseek', + baseUrl: 'https://api.deepseek.com/v1', + userToken: process.env['DEEPSEEK_API_TOKEN'], + userModel: 'deepseek-chat' + }); + + // コンテキスト作成と設定 + const storage = { + messages: [], + settings: { + temperature: 0.7, + // 全てのツールを有効化 + enableTools: tools, + // システムプロンプト + systemPrompt: 'you are a clever bot', + // 会話コンテキストのターン数 + contextLength: 20 + } + }; + + // 質問内容 + const message = 'hello world'; + + // イベントループ開始 + await taskLoop.start(storage, message); + + // 最終回答を表示(messages.at(-1)に格納) + const content = storage.messages.at(-1).content; + console.log('最終回答:', content); +} + +main(); +``` + +esmモードで実行するには、まずTypeScriptのesmランチャーをインストール: + +```bash +npm install tsx --save-dev +``` + +ファイルを実行: + +```bash +npx tsx main.ts +``` + +出力例: + +``` +[6/5/2025, 8:16:15 PM] 🚀 [my-browser] 0.1.0 接続済み +[6/5/2025, 8:16:15 PM] タスクループが新規エポックを開始 +[6/5/2025, 8:16:23 PM] タスクループがエポックを終了 +[6/5/2025, 8:16:23 PM] 🤖 LLMがこれらのツールを呼び出し要求: k_navigate +[6/5/2025, 8:16:23 PM] 🔧 ツールk_navigateを呼び出し中 +[6/5/2025, 8:16:34 PM] × ツール呼び出し失敗 McpError: MCP error -32603: net::ERR_CONNECTION_RESET at https://towardsdatascience.com/tag/editors-pick/ +[6/5/2025, 8:16:34 PM] タスクループが新規エポックを開始 +[6/5/2025, 8:16:40 PM] タスクループがエポックを終了 +[6/5/2025, 8:16:40 PM] 🤖 LLMがこれらのツールを呼び出し要求: k_navigate +[6/5/2025, 8:16:40 PM] 🔧 ツールk_navigateを呼び出し中 +[6/5/2025, 8:16:44 PM] ✓ ツール呼び出し成功 +[6/5/2025, 8:16:44 PM] タスクループが新規エポックを開始 +[6/5/2025, 8:16:57 PM] タスクループがエポックを終了 +[6/5/2025, 8:16:57 PM] 🤖 LLMがこれらのツールを呼び出し要求: k_evaluate +[6/5/2025, 8:16:57 PM] 🔧 ツールk_evaluateを呼び出し中 +[6/5/2025, 8:16:57 PM] ✓ ツール呼び出し成功 +[6/5/2025, 8:16:57 PM] タスクループが新規エポックを開始 +[6/5/2025, 8:17:06 PM] タスクループがエポックを終了 +[6/5/2025, 8:17:06 PM] 🤖 LLMがこれらのツールを呼び出し要求: k_navigate, k_navigate +[6/5/2025, 8:17:06 PM] 🔧 ツールk_navigateを呼び出し中 +[6/5/2025, 8:17:09 PM] ✓ ツール呼び出し成功 +[6/5/2025, 8:17:09 PM] 🔧 ツールk_navigateを呼び出し中 +[6/5/2025, 8:17:12 PM] ✓ ツール呼び出し成功 +[6/5/2025, 8:17:12 PM] タスクループが新規エポックを開始 +[6/5/2025, 8:17:19 PM] タスクループがエポックを終了 +[6/5/2025, 8:17:19 PM] 🤖 LLMがこれらのツールを呼び出し要求: k_evaluate, k_evaluate +[6/5/2025, 8:17:19 PM] 🔧 ツールk_evaluateを呼び出し中 +[6/5/2025, 8:17:19 PM] ✓ ツール呼び出し成功 +[6/5/2025, 8:17:19 PM] 🔧 ツールk_evaluateを呼び出し中 +[6/5/2025, 8:17:19 PM] ✓ ツール呼び出し成功 +[6/5/2025, 8:17:19 PM] タスクループが新規エポックを開始 +[6/5/2025, 8:17:45 PM] タスクループがエポックを終了 +"以下は人気記事の整理済み情報(簡体中文訳):\n\n---\n\n### K1 タイトル\n**『データドリフトは真の問題ではない:監視戦略こそが鍵』**\n\n**概要**\n機械学習分野でデータドリフトはモデル性能低下の原因とされますが、本記事はこの常識を覆します。監視戦略の不備こそが核心的問題だと指摘。eコマース推薦システムや金融リスクモデルなどの実例を通じ、従来の統計的監視の限界を明らかにし、3層監視フレームワークを提案:\n1. **統計監視**: データ分布変化を迅速検知(初期信号として)\n2. **コンテキスト監視**: ビジネスロジックと連動、重要指標への影響を判定\n3. **行動監視**: モデル予測の実効果を追跡、「無音ドリフト」を防止\n\n技術指標依存ではなく、ビジネス目標と密接に連携した監視システムの重要性を強調。\n\n**原文リンク**\n[記事を読む](https://towardsdatascience.com/data-drift-is-not-the-actual-problem-your-monitoring-strategy-is/)\n\n---\n\n### K2 タイトル\n**『Jupyterからプログラマーへの速習ガイド』**\n\n**概要**\nデータサイエンティストや初心者向けに、Jupyter Notebookから専門的プログラミングへ移行する道筋を提示。VS Code、Git、Dockerなどのツール推奨と実践コード例で、Notebookの限界を超え、コードの保守性と拡張性を向上させる方法を解説。\n\n主なハイライト:\n- Notebookコードを再利用可能なPythonスクリプトへモジュール化\n- バージョン管理とコンテナ技術で開発フローを最適化\n- 実験的コードから本番級アプリケーションへの変換実例\n\n**原文リンク**\n[記事を読む](https://towardsdatascience.com/the-journey-from-jupyter-to-programmer-a-quick-start-guide/)\n\n---\n\n追加調整やコンテンツ補充が必要な場合はお知らせください!" \ No newline at end of file diff --git a/ja/sdk-tutorial/usage/greet.md b/ja/sdk-tutorial/usage/greet.md new file mode 100644 index 0000000..e69de29 diff --git a/ja/sdk-tutorial/usage/multi-server.md b/ja/sdk-tutorial/usage/multi-server.md new file mode 100644 index 0000000..e69de29 diff --git a/ja/sdk-tutorial/usage/task-loop.md b/ja/sdk-tutorial/usage/task-loop.md new file mode 100644 index 0000000..e69de29 diff --git a/package-lock.json b/package-lock.json index 507b916..99eb754 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,12 @@ "@nolebase/vitepress-plugin-inline-link-preview": "^2.17.1", "@nolebase/vitepress-plugin-page-properties": "^2.17.1", "@nolebase/vitepress-plugin-thumbnail-hash": "^2.17.1", + "busuanzi.pure.js": "^1.0.3", + "chalk": "^5.4.1", "mermaid": "^11.6.0", + "ompipe": "^1.0.2", + "openai": "^5.3.0", + "simple-git": "^3.28.0", "vitepress-plugin-lightbox": "^1.0.2", "vitepress-plugin-mermaid": "^2.0.17" } @@ -879,6 +884,23 @@ "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" }, + "node_modules/@kwsites/file-exists": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", + "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1" + } + }, + "node_modules/@kwsites/promise-deferred": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", + "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", + "dev": true, + "license": "MIT" + }, "node_modules/@mermaid-js/mermaid-mindmap": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/@mermaid-js/mermaid-mindmap/-/mermaid-mindmap-9.3.0.tgz", @@ -2653,6 +2675,22 @@ "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -2690,6 +2728,13 @@ "node": ">=8" } }, + "node_modules/busuanzi.pure.js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/busuanzi.pure.js/-/busuanzi.pure.js-1.0.3.tgz", + "integrity": "sha512-tPULdyx+8NNb9geZYDpktKmHAYx9f2LEOBaHyWCaM7iPEMIa/43KgYAUluuU7YG9UFSOFpj4dO7Nj2Vlu3V9Rg==", + "dev": true, + "license": "MIT" + }, "node_modules/canvaskit-wasm": { "version": "0.40.0", "resolved": "https://registry.npmjs.org/canvaskit-wasm/-/canvaskit-wasm-0.40.0.tgz", @@ -2797,6 +2842,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, "node_modules/colorette": { "version": "2.0.20", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", @@ -3975,6 +4040,16 @@ "dev": true, "license": "MIT" }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/hast-util-to-html": { "version": "9.0.5", "resolved": "https://registry.npmmirror.com/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", @@ -4816,6 +4891,33 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/ompipe": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/ompipe/-/ompipe-1.0.2.tgz", + "integrity": "sha512-D9SbKT2fqSkVxQtp0AffMdSYNZiScA9YOrS0iymyt6Q4wLWyUzvxhmFcJO8AxlwYLZY/IddwirgM46EfeNdq4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "chalk": "4.1.2" + } + }, + "node_modules/ompipe/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/onetime": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", @@ -4842,6 +4944,28 @@ "regex-recursion": "^6.0.2" } }, + "node_modules/openai": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-5.3.0.tgz", + "integrity": "sha512-VIKmoF7y4oJCDOwP/oHXGzM69+x0dpGFmN9QmYO+uPbLFOmmnwO+x1GbsgUtI+6oraxomGZ566Y421oYVu191w==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.23.8" + }, + "peerDependenciesMeta": { + "ws": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, "node_modules/ora": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", @@ -5360,6 +5484,22 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/simple-git": { + "version": "3.28.0", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.28.0.tgz", + "integrity": "sha512-Rs/vQRwsn1ILH1oBUy8NucJlXmnnLeLCfcvbSehkPzbv3wwoFWIdtfd6Ndo6ZPhlPsCZ60CPI4rxurnwAa+a2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@kwsites/file-exists": "^1.1.1", + "@kwsites/promise-deferred": "^1.1.1", + "debug": "^4.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/steveukx/git-js?sponsor=1" + } + }, "node_modules/slash": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", @@ -5516,6 +5656,19 @@ "node": ">=16" } }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/tabbable": { "version": "6.2.0", "resolved": "https://registry.npmmirror.com/tabbable/-/tabbable-6.2.0.tgz", diff --git a/package.json b/package.json index 1591a7a..7614243 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,9 @@ { + "type": "module", "scripts": { "dev": "vitepress dev", "build": "vitepress build", - "postbuild": "node scripts/move-image.js", + "postbuild": "node scripts/move-image.mjs", "preview": "vitepress preview" }, "dependencies": { @@ -18,8 +19,13 @@ "@nolebase/vitepress-plugin-inline-link-preview": "^2.17.1", "@nolebase/vitepress-plugin-page-properties": "^2.17.1", "@nolebase/vitepress-plugin-thumbnail-hash": "^2.17.1", + "busuanzi.pure.js": "^1.0.3", + "chalk": "^5.4.1", "mermaid": "^11.6.0", + "ompipe": "^1.0.2", + "openai": "^5.3.0", + "simple-git": "^3.28.0", "vitepress-plugin-lightbox": "^1.0.2", "vitepress-plugin-mermaid": "^2.0.17" } -} \ No newline at end of file +} diff --git a/plugin-tutorial/concept.md b/plugin-tutorial/concept.md index 894a056..f779f0d 100644 --- a/plugin-tutorial/concept.md +++ b/plugin-tutorial/concept.md @@ -1,32 +1,30 @@ -# MCP 基础概念 +# MCP Basic Concepts -## 前言 +## Foreword +In the [[what-is-mcp|previous article]], we briefly introduced the definition of MCP and its basic organizational structure. As developers, what we need to focus on is how to customize the development of the MCP server based on our own business and scenario needs. This way, after directly connecting to any MCP client, we can provide the large model with the customized interaction capabilities we have developed. +Before we officially start teaching you how to develop your own MCP server, I think it might be necessary to clarify a few basic concepts. -在 [[what-is-mcp|之前的文章]] 中,我们简单介绍了 MCP 的定义和它的基本组织结构。作为开发者,我们最需要关注的其实是如何根据我们自己的业务和场景定制化地开发我们需要的 MCP 服务器,这样直接接入任何一个 MCP 客户端后,我们都可以给大模型以我们定制出的交互能力。 +## Resources, Prompts, and Tools -在正式开始教大家如何开发自己的 MCP 服务器之前,我想,或许有必要讲清楚几个基本概念。 +In the [MCP Client Protocol](https://modelcontextprotocol.io/clients), three very important capability categories in the MCP protocol are mentioned: -## Resources, Prompts 和 Tools +* Resources: Customized requests and access to local resources, which can be file systems, databases, files in the current code editor, etc., essentially **static resources** that web apps cannot access. Additional resources will enrich the context sent to the large model, allowing AI to give us more accurate responses. +* Prompts: Customized prompts that AI can adopt in certain scenarios. For example, if AI needs to return certain formatted content, custom prompts can be provided. +* Tools: Tools available for AI use. These must be functions, such as booking a hotel, opening a webpage, or turning off a lamp—these encapsulated functions can be a tool. The large model will use these tools via function calling. Tools will allow AI to directly operate our computer and even interact with the real world. -在 [MCP 客户端协议](https://modelcontextprotocol.io/clients) 中,讲到了 MCP 协议中三个非常重要的能力类别: +For those with front-end and back-end development experience, you can think of Resources as "read-only permissions granted to the large model" and Tools as "read-write permissions granted to the large model." -- Resouces :定制化地请求和访问本地的资源,可以是文件系统、数据库、当前代码编辑器中的文件等等原本网页端的app 无法访问到的 **静态资源**。额外的 resources 会丰富发送给大模型的上下文,使得 AI 给我们更加精准的回答。 -- Prompts :定制化一些场景下可供 AI 进行采纳的 prompt,比如如果需要 AI 定制化地返回某些格式化内容时,可以提供自定义的 prompts。 -- Tools :可供 AI 使用的工具,它必须是一个函数,比如预定酒店、打开网页、关闭台灯这些封装好的函数就可以是一个 tool,大模型会通过 function calling 的方式来使用这些 tools。 Tools 将会允许 AI 直接操作我们的电脑,甚至和现实世界发生交互。 +MCP clients (such as Claude Desktop, 5ire, etc.) have already implemented the front-end logic for the above. However, what resources and tools to provide requires each developer’s imagination. In other words, we need to develop rich and diverse MCP Servers to enable the large model to perform more interesting tasks. -各位拥有前后端开发经验的朋友们,可以将 Resouces 看成是「额外给予大模型的只读权限」,把 Tools 看成是「额外给予大模型的读写权限」。 +However, one thing to note is that almost all large models currently use the OpenAI protocol as the access point for connecting to the large model. What does the OpenAI protocol mean? -MCP 客户端(比如 Claude Desktop,5ire 等)已经实现好了上述的前端部分逻辑。而具体提供什么资源,具体提供什么工具,则需要各位玩家充分想象了,也就是我们需要开发丰富多彩的 MCP Server 来允许大模型做出更多有意思的工作。 +## OpenAI Protocol -不过需要说明的一点是,目前几乎所有大模型采用了 openai 协议作为我们访问大模型的接入点。什么叫 openai 协议呢? +When developing an app using Python or TypeScript, we often install a library named OpenAI, where you fill in the model vendor, base URL of the model, and the type of model to directly access the large model. The various model providers must also support this library and protocol. -## openai 协议 - -当我们使用 python 或者 typescript 开发 app 时,往往会安装一个名为 openai 的库,里面填入你需要使用的模型厂商、模型的基础 url、使用的模型类别来直接访问大模型。而各个大模型提供商也必须支持这个库,这套协议。 - -比如我们在 python 中访问 deepseek 的服务就可以这么做: +For example, to access the Deepseek service in Python, we can do it like this: ```python from openai import OpenAI @@ -45,7 +43,7 @@ response = client.chat.completions.create( print(response.choices[0].message.content) ``` -如果你点进这个 create 函数去看,你会发现 openai 协议需要大模型厂家支持的 feature 是非常非常多的: +If you go into the `create` function, you will see that the OpenAI protocol requires a lot of features to be supported by the model provider: ```python @overload @@ -92,13 +90,13 @@ print(response.choices[0].message.content) ) -> ChatCompletion: ``` -从上面的签名中,你应该可以看到几个很熟悉的参数,比如 `temperature`, `top_p`,很多的大模型使用软件中,有的会给你暴露这个参数进行调节。比如在 5ire 中,内容随机度就是 `temperature` 这个参数的图形化显示。 +From the above signature, you should see several familiar parameters like `temperature` and `top_p`. Many large model software expose these parameters for adjustment. For example, in 5ire, the content randomness is displayed graphically as the `temperature` parameter.
-其实如你所见,一次普普通通调用涉及到的可调控参数是非常之多的。而在所有参数中,你可以注意到一个参数叫做 `tools`: +As you can see, a simple invocation involves many adjustable parameters. Among all these parameters, you can notice one called `tools`: ```python @overload @@ -109,14 +107,14 @@ print(response.choices[0].message.content) model: Union[str, ChatModel], audio: Optional[ChatCompletionAudioParam] | NotGiven = NOT_GIVEN, - # 看这里 + # Look here tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, ) -> ChatCompletion: ``` -## tool_calls 字段 +## tool\_calls Field -在上面的 openai 协议中,有一个名为 tools 的参数。 tools 就是要求大模型厂商必须支持 function calling 这个特性,也就是我们提供一部分工具的描述(和 MCP 协议完全兼容的),在 tools 不为空的情况下,chat 函数返回的值中会包含一个特殊的字段 `tool_calls`,我们可以运行下面的我写好的让大模型调用可以查询天气的代码: +In the OpenAI protocol above, there is a parameter called `tools`. This requires the large model provider to support the function calling feature, i.e., we provide a description of a set of tools (which is fully compatible with the MCP protocol). When `tools` is not empty, the value returned by the chat function will include a special field `tool_calls`. We can run the code I wrote to allow the large model to query the weather: ```python from openai import OpenAI @@ -126,19 +124,19 @@ client = OpenAI( base_url="https://api.deepseek.com" ) -# 定义 tools(函数/工具列表) +# Define tools (functions/tool list) tools = [ { "type": "function", "function": { "name": "get_current_weather", - "description": "获取给定地点的天气", + "description": "Get the weather for a given location", "parameters": { "type": "object", "properties": { "location": { "type": "string", - "description": "城市,比如杭州,北京,上海", + "description": "City, e.g., Hangzhou, Beijing, Shanghai", } }, "required": ["location"], @@ -150,18 +148,18 @@ tools = [ response = client.chat.completions.create( model="deepseek-chat", messages=[ - {"role": "system", "content": "你是一个很有用的 AI"}, - {"role": "user", "content": "今天杭州的天气是什么?"}, + {"role": "system", "content": "You are a helpful AI"}, + {"role": "user", "content": "What’s the weather in Hangzhou today?"}, ], - tools=tools, # 传入 tools 参数 - tool_choice="auto", # 可选:控制是否强制调用某个工具 + tools=tools, # Pass the tools parameter + tool_choice="auto", # Optional: control whether to force a specific tool call stream=False, ) print(response.choices[0].message) ``` -运行上述代码,它的返回如下: +Running the above code, the return will be as follows: ```python ChatCompletionMessage( @@ -175,7 +173,7 @@ ChatCompletionMessage( ChatCompletionMessageToolCall( id='call_0_baeaba2b-739d-40c2-aa6c-1e61c6d7e855', function=Function( - arguments='{"location":"杭州"}', + arguments='{"location":"Hangzhou"}', name='get_current_weather' ), type='function', @@ -185,48 +183,4 @@ ChatCompletionMessage( ) ``` -可以看到上面的 `tool_calls` 给出了大模型想要如何去使用我们给出的工具。需要说明的一点是,收到上下文的限制,目前一个问题能够让大模型调取的工具上限一般不会超过 100 个,这个和大模型厂商的上下文大小有关系。奥,对了,友情提示,当你使用 MCP 客户端在使用大模型解决问题时,同一时间激活的 MCP Server 越多,消耗的 token 越多哦 :D - -而目前 openai 的协议中,tools 是只支持函数类的调用。而函数类的调用往往是可以模拟出 Resources 的效果的。比如取资源,你可以描述为一个 tool。因此在正常情况下,如果大家要开发 MCP Server,最好只开发 Tools,另外两个 feature 还暂时没有得到广泛支持。 - - - -## 使用 Inspector 进行调试 - -Claude 原生提供的 MCP 协议可以通过官方提供的 Inspector 进行调试,对于 [[first-mcp|你的第一个 MCP]] 中的例子,可以如下进行调试,在命令行输入如下命令启动 Inspector: - -```bash -mcp dev main.py -``` - -这会启动一个前端服务器,并打开 `http://localhost:5173/` 后我们可以看到 inspector 的调试界面,先点击左侧的 `Connect` 来运行我们的 server.py 并通过 stdio 为通信管道和 web 建立通信。 - -Fine,可以开始愉快地进行调试了,Inspector 主要给了我们三个板块,分别对应 Resources,Prompts 和 Tools。 - -先来看 Resources,点击「Resource Templates」可以罗列所有注册的 Resource,比如我们上文定义的 `get_greeting`,你可以通过输入参数运行来查看这个函数是否正常工作。(因为一般情况下的这个资源协议是会访问远程数据库或者微服务的) - -
- -
- -Prompts 端就比较简单了,直接输入预定义参数就能获取正常的返回结果。 - -
- -
- -Tools 端将会是我们后面调试的核心。在之前的章节我们讲过了,MCP 协议中的 Prompts 和 Resources 目前还没有被 openai 协议和各大 MCP 客户端广泛支持,因此,我们主要的服务端业务都应该是在写 tools。 - -我们此处提供的 tool 是实现一个简单的加法,它非常简单,我们输入 1 和 2 就可以直接看到结果是 3。我们后续会开发一个可以访问天气预报的 tool,那么到时候就非常需要一个这样的窗口来调试我们的天气信息获取是否正常了。 - -
- -
- - - -## 结语 - -这篇文章,我们简单了解了 MCP 内部的一些基本概念,我认为这些概念对于诸位开发一个 MCP 服务器是大有裨益的,所以我认为有必要先讲一讲。 - -下面的文章中,我将带领大家探索 MCP 的奇境,一个属于 AI Agent 的时代快要到来了。 +As you can see, the `tool_calls` shows how the large model wants to use the tools we provided. One thing to note is that due to context limitations, the maximum number of tools a large model diff --git a/plugin-tutorial/examples/go-neo4j-sse.md b/plugin-tutorial/examples/go-neo4j-sse.md index 5e70ebe..d5bea4e 100644 --- a/plugin-tutorial/examples/go-neo4j-sse.md +++ b/plugin-tutorial/examples/go-neo4j-sse.md @@ -1,473 +1,491 @@ -# go 实现 neo4j 的只读 mcp 服务器 (SSE) -## 前言 +# Implementing a Read-Only MCP Server for Neo4j in Go (SSE) -本篇教程,演示一下如何使用 go 语言写一个可以访问 neo4j 数据库的 mcp 服务器。实现完成后,我们不需要写任何 查询代码 就能通过询问大模型了解服务器近况。 +[Video Tutorial](https://www.bilibili.com/video/BV1g8TozyEE7/) -不同于之前的连接方式,这次,我们将采用 SSE 的方式来完成服务器的创建和连接。 +## Introduction -本期教程的代码:https://github.com/LSTM-Kirigaya/openmcp-tutorial/tree/main/neo4j-go-server +This tutorial demonstrates how to use Go to create an MCP server that can access a Neo4j database. Once implemented, you can query the server's status via a large language model (LLM) without writing any additional query code. -建议下载本期的代码,因为里面有我为大家准备好的数据库文件。要不然,你们得自己 mock 数据了。 +Unlike previous connection methods, this time we’ll use Server-Sent Events (SSE) to create and connect the server. +The code for this tutorial: [https://github.com/LSTM-Kirigaya/openmcp-tutorial/tree/main/neo4j-go-server](https://github.com/LSTM-Kirigaya/openmcp-tutorial/tree/main/neo4j-go-server) +It’s recommended to download the code for this tutorial, as it includes a pre-prepared database file. Otherwise, you’ll need to mock the data yourself. -## 1. 准备 +--- -项目结构如下: +## 1. Preparation + +The project structure is as follows: ```bash -📦neo4j-go-server - ┣ 📂util - ┃ ┗ 📜util.go # 工具函数 - ┣ 📜main.go # 主函数 - ┗ 📜neo4j.json # 数据库连接的账号密码 -``` +📦neo4j-go-server + ┣ 📂util + ┃ ┗ 📜util.go # Utility functions + ┣ 📜main.go # Main function + ┗ 📜neo4j.json # Database connection credentials +``` -我们先创建一个 go 项目: +First, create a Go project: ```bash -mkdir neo4j-go-server -cd neo4j-go-server -go mod init neo4j-go-server -``` +mkdir neo4j-go-server +cd neo4j-go-server +go mod init neo4j-go-server +``` +--- +## 2. Database Initialization -## 2. 完成数据库初始化 +### 2.1 Install Neo4j -### 2.1 安装 neo4j +First, set up a Neo4j database locally or on a server following [this tutorial](https://kirigaya.cn/blog/article?seq=199). You only need to complete the first two steps. Add the `bin` path to your environment variables and set the password to `openmcp`. -首先,根据我的教程在本地或者服务器配置一个 neo4j 数据库,这里是是教程,你只需要完成该教程的前两步即可: [neo4j 数据库安装与配置](https://kirigaya.cn/blog/article?seq=199)。将 bin 路径加入环境变量,并且设置的密码设置为 openmcp。 - -然后在 main.go 同级下创建 neo4j.json,填写 neo4j 数据库的连接信息: +Next, create `neo4j.json` in the same directory as `main.go` and fill in the connection details: ```json { - "url" : "neo4j://localhost:7687", - "name" : "neo4j", - "password" : "openmcp" + "url": "neo4j://localhost:7687", + "name": "neo4j", + "password": "openmcp" } -``` +``` -### 2.2 导入事先准备好的数据 +### 2.2 Import Pre-Prepared Data -安装完成后,大家可以导入我实现准备好的数据,这些数据是我的个人网站上部分数据脱敏后的摘要,大家可以随便使用,下载链接:[neo4j.db](https://github.com/LSTM-Kirigaya/openmcp-tutorial/releases/download/neo4j.db/neo4j.db)。下载完成后,运行下面的命令: +After installation, import the pre-prepared data. This data is an anonymized excerpt from my personal website and can be freely used. Download link: [neo4j.db](https://github.com/LSTM-Kirigaya/openmcp-tutorial/releases/download/neo4j.db/neo4j.db). After downloading, run the following commands: ```bash -neo4j stop -neo4j-admin load --database neo4j --from neo4j.db --force -neo4j start -``` +neo4j stop +neo4j-admin load --database neo4j --from neo4j.db --force +neo4j start +``` -然后,我们登录数据库就能看到我准备好的数据啦: +Then, log in to the database to see the imported data: ```bash -cypher-shell -a localhost -u neo4j -p openmcp -``` +cypher-shell -a localhost -u neo4j -p openmcp +``` -
- -
+
+ +
-### 2.3 验证 go -> 数据库连通性 +### 2.3 Verify Go-to-Database Connectivity -为了验证数据库的连通性和 go 的数据库驱动是否正常工作,我们需要先写一段数据库访问的最小系统。 +To verify connectivity and the Go driver’s functionality, we’ll first implement a minimal database access system. -先安装 neo4j 的 v5 版本的 go 驱动: +Install the Neo4j Go driver (v5): ```bash -go get github.com/neo4j/neo4j-go-driver/v5 -``` +go get github.com/neo4j/neo4j-go-driver/v5 +``` -在 `util.go` 中添加以下代码: +Add the following code to `util.go`: ```go -package util +package util -import ( - "context" - "encoding/json" - "fmt" - "os" +import ( + "context" + "encoding/json" + "fmt" + "os" - "github.com/neo4j/neo4j-go-driver/v5/neo4j" -) + "github.com/neo4j/neo4j-go-driver/v5/neo4j" +) -var ( - Neo4jDriver neo4j.DriverWithContext -) +var ( + Neo4jDriver neo4j.DriverWithContext +) -// 创建 neo4j 服务器的连接 -func CreateNeo4jDriver(configPath string) (neo4j.DriverWithContext, error) { - jsonString, _ := os.ReadFile(configPath) - config := make(map[string]string) +// Create a connection to the Neo4j server +func CreateNeo4jDriver(configPath string) (neo4j.DriverWithContext, error) { + jsonString, _ := os.ReadFile(configPath) + config := make(map[string]string) - json.Unmarshal(jsonString, &config) - // fmt.Printf("url: %s\nname: %s\npassword: %s\n", config["url"], config["name"], config["password"]) + json.Unmarshal(jsonString, &config) - var err error - Neo4jDriver, err = neo4j.NewDriverWithContext( - config["url"], - neo4j.BasicAuth(config["name"], config["password"], ""), - ) - if err != nil { - return Neo4jDriver, err - } - return Neo4jDriver, nil -} + var err error + Neo4jDriver, err = neo4j.NewDriverWithContext( + config["url"], + neo4j.BasicAuth(config["name"], config["password"], ""), + ) + if err != nil { + return Neo4jDriver, err + } + return Neo4jDriver, nil +} +// Execute a read-only Cypher query +func ExecuteReadOnlyCypherQuery( + cypher string, +) ([]map[string]any, error) { + session := Neo4jDriver.NewSession(context.TODO(), neo4j.SessionConfig{ + AccessMode: neo4j.AccessModeRead, + }) -// 执行只读的 cypher 查询 -func ExecuteReadOnlyCypherQuery( - cypher string, -) ([]map[string]any, error) { - session := Neo4jDriver.NewSession(context.TODO(), neo4j.SessionConfig{ - AccessMode: neo4j.AccessModeRead, - }) + defer session.Close(context.TODO()) - defer session.Close(context.TODO()) + result, err := session.Run(context.TODO(), cypher, nil) + if err != nil { + fmt.Println(err.Error()) + return nil, err + } - result, err := session.Run(context.TODO(), cypher, nil) - if err != nil { - fmt.Println(err.Error()) - return nil, err - } + var records []map[string]any + for result.Next(context.TODO()) { + records = append(records, result.Record().AsMap()) + } - var records []map[string]any - for result.Next(context.TODO()) { - records = append(records, result.Record().AsMap()) - } + return records, nil +} +``` - return records, nil -} -``` - -main.go 中添加以下代码: +Add the following code to `main.go`: ```go -package main +package main -import ( - "fmt" - "github.com/neo4j/neo4j-go-driver/v5/neo4j" - "neo4j-go-server/util" -) +import ( + "fmt" + "neo4j-go-server/util" +) -var ( - neo4jPath string = "./neo4j.json" -) +var ( + neo4jPath string = "./neo4j.json" +) -func main() { - _, err := util.CreateNeo4jDriver(neo4jPath) - if err != nil { - fmt.Println(err) - return - } +func main() { + _, err := util.CreateNeo4jDriver(neo4jPath) + if err != nil { + fmt.Println(err) + return + } - fmt.Println("Neo4j driver created successfully") -} -``` + fmt.Println("Neo4j driver created successfully") +} +``` -运行主程序来验证数据库的连通性: +Run the program to verify database connectivity: ```bash -go run main.go -``` +go run main.go +``` -如果输出了 `Neo4j driver created successfully`,则说明数据库的连通性验证通过。 +If the output is `Neo4j driver created successfully`, the connection is successful. +--- +## 3. Implement the MCP Server -## 3. 实现 mcp 服务器 +The most popular Go MCP SDK is `mark3labs/mcp-go`. We’ll use this. -go 的 mcp 的 sdk 最为有名的是 mark3labs/mcp-go 了,我们就用这个。 +> The demo for `mark3labs/mcp-go` is at [https://github.com/mark3labs/mcp-go](https://github.com/mark3labs/mcp-go). It’s very simple, so we’ll use it directly. -> mark3labs/mcp-go 的 demo 在 https://github.com/mark3labs/mcp-go,非常简单,此处直接使用即可。 - -先安装 +Install it first: ```bash -go get github.com/mark3labs/mcp-go -``` +go get github.com/mark3labs/mcp-go +``` -然后在 `main.go` 中添加以下代码: +Then, add the following code to `main.go`: ```go -// ... existing code ... +// ... existing code ... -var ( - addr string = "localhost:8083" -) +var ( + addr string = "localhost:8083" +) -func main() { - // ... existing code ... +func main() { + // ... existing code ... - s := server.NewMCPServer( - "只读 Neo4j 服务器", - "0.0.1", - server.WithToolCapabilities(true), - ) + s := server.NewMCPServer( + "Read-Only Neo4j Server", + "0.0.1", + server.WithToolCapabilities(true), + ) - srv := server.NewSSEServer(s) - - // 定义 executeReadOnlyCypherQuery 这个工具的 schema - executeReadOnlyCypherQuery := mcp.NewTool("executeReadOnlyCypherQuery", - mcp.WithDescription("执行只读的 Cypher 查询"), - mcp.WithString("cypher", - mcp.Required(), - mcp.Description("Cypher 查询语句,必须是只读的"), - ), - ) - - // 将真实函数和申明的 schema 绑定 - s.AddTool(executeReadOnlyCypherQuery, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { - cypher := request.Params.Arguments["cypher"].(string) - result, err := util.ExecuteReadOnlyCypherQuery(cypher) + srv := server.NewSSEServer(s) - fmt.Println(result) + // Define the schema for the `executeReadOnlyCypherQuery` tool + executeReadOnlyCypherQuery := mcp.NewTool("executeReadOnlyCypherQuery", + mcp.WithDescription("Execute a read-only Cypher query"), + mcp.WithString("cypher", + mcp.Required(), + mcp.Description("Cypher query statement (must be read-only)"), + ), + ) - if err != nil { - return mcp.NewToolResultText(""), err - } + // Bind the actual function to the declared schema + s.AddTool(executeReadOnlyCypherQuery, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + args, ok := request.Params.Arguments.(map[string]interface{}) + if !ok { + return mcp.NewToolResultText(""), fmt.Errorf("invalid arguments type") + } + cypher, ok := args["cypher"].(string) + if !ok { + return mcp.NewToolResultText(""), fmt.Errorf("cypher argument is not a string") + } + result, err := util.ExecuteReadOnlyCypherQuery(cypher) - return mcp.NewToolResultText(fmt.Sprintf("%v", result)), nil - }) - - // 在 http://localhost:8083/sse 开启服务 - fmt.Printf("Server started at http://%s/sse\n", addr) - srv.Start(addr) -} -``` + fmt.Println(result) -go run main.go 运行上面的代码,你就能看到如下信息: + if err != nil { + return mcp.NewToolResultText(""), err + } + + return mcp.NewToolResultText(fmt.Sprintf("%v", result)), nil + }) + + // Start the server at http://localhost:8083/sse + fmt.Printf("Server started at http://%s/sse\n", addr) + srv.Start(addr) +} +``` + +Run the server: + +```bash +go run main.go +``` + +You should see the following output: ``` -Neo4j driver created successfully -Server started at http://localhost:8083/sse -``` +Neo4j driver created successfully +Server started at http://localhost:8083/sse +``` -说明我们的 mcp 服务器在本地的 8083 上启动了。 +This means the MCP server is running locally on port 8083. +--- +## 4. Debugging with OpenMCP -## 4. 通过 openmcp 来进行调试 +### 4.1 Add the Workspace SSE Debugging Project -### 4.1 添加工作区 sse 调试项目 +Next, we’ll debug using OpenMCP. Click the OpenMCP icon on the left side of VSCode to enter the control panel. If you downloaded the project from [https://github.com/LSTM-Kirigaya/openmcp-tutorial/tree/main/neo4j-go-server](https://github.com/LSTM-Kirigaya/openmcp-tutorial/tree/main/neo4j-go-server), you’ll see a pre-configured debugging project named "Read-Only Neo4j Server" under "MCP Connections (Workspace)". If you built this project from scratch, you can add the connection manually by selecting SSE and entering `http://localhost:8083/sse` (leave OAuth blank). -接下来,我们来通过 openmcp 进行调试,先点击 vscode 左侧的 openmcp 图标进入控制面板,如果你是下载的 https://github.com/LSTM-Kirigaya/openmcp-tutorial/tree/main/neo4j-go-server 这个项目,那么你能看到【MCP 连接(工作区)】里面已经有一个创建好的调试项目【只读 Neo4j 服务器】了。如果你是完全自己做的这个项目,可以通过下面的按钮添加连接,选择 sse 后填入 http://localhost:8083/sse,oauth 空着不填即可。 +
+ +
-
- -
+### 4.2 Test the Tool -### 4.2 测试工具 +The first step in debugging an MCP server is always testing the MCP tool. Create a new tab, select "Tool," click the tool shown below, and enter `CALL db.labels() YIELD label RETURN label`. This query lists all node types. If the output matches the expected result, the connection is working correctly. -第一次调试 mcp 服务器要做的事情一定是先调通 mcp tool,新建标签页,选择 tool,点击下图的工具,输入 `CALL db.labels() YIELD label RETURN label`,这个语句是用来列出所有节点类型的。如果输出下面的结果,说明当前的链路生效,没有问题。 +
+ +
-
- -
+### 4.3 Explore the LLM’s Capabilities and Use Prompts to Encapsulate Knowledge - -### 4.3 摸清大模型功能边界,用提示词来封装我们的知识 - -然后,让我们做点有趣的事情吧!我们接下来要测试一下大模型的能力边界,因为 neo4j 属于特种数据库,通用大模型不一定知道怎么用它。新建标签页,点击「交互测试」,我们先问一个简单的问题: +Now, let’s do something fun! We’ll test the LLM’s boundaries because Neo4j is a specialized database, and general-purpose LLMs may not know how to use it. Create a new tab, click "Interactive Test," and ask a simple question: ``` -帮我找出最新的 10 条评论 -``` +Find the latest 10 comments for me. +``` -结果如下: +The result is as follows: -
- -
+
+ +
-可以看到,大模型查询的节点类型就是错误的,在我提供的例子中,代表评论的节点是 BlogComment,而不是 Comment。也就是说,大模型并不掌握进行数据库查询的通用方法论。这就是我们目前知道的它的能力边界。我们接下来要一步一步地注入我们的经验和知识,唔姆,通过 system prompt 来完成。 +You can see that the LLM queried the wrong node type. In my example, the node representing comments is `BlogComment`, not `Comment`. This means the LLM doesn’t have a general methodology for querying databases. This is its current boundary. We’ll now inject our experience and knowledge step by step through the system prompt. -### 4.4 教大模型找数据库节点 +### 4.4 Teach the LLM to Find Database Nodes -好好想一下,作为工程师的我们是怎么知道评论的节点是 BlogComment?我们一般是通过罗列当前数据库的所有节点的类型来从命名中猜测的,比如,对于这个数据库,我一般会先输入如下的 cypher 查询: +Think about it: How do we engineers know that the comment node is `BlogComment`? We usually list all node types in the database and guess from the naming. For this database, I’d first run the following Cypher query: ```sql -CALL db.labels() YIELD label RETURN label -``` +CALL db.labels() YIELD label RETURN label +``` -它的输出就在 4.2 的图中,如果你的英文不错,也能看出来 BlogComment 大概率是代表博客评论的节点。好了,那么我们将这段方法论注入到 system prompt 中,从而封装我们的这层知识,点击下图的下方的按钮,进入到【系统提示词】: +The output is shown in Figure 4.2. If your English is decent, you can guess that `BlogComment` is likely the node for blog comments. Now, let’s inject this methodology into the system prompt to encapsulate this knowledge. Click the button at the bottom of the image below to enter "System Prompts": -
- -
+
+ +
- -新建提示词【neo4j】,输入: +Create a new prompt named "neo4j" and enter: ``` -你是一个善于进行neo4j查询的智能体,对于用户要求的查询请求,你并不一定知道对应的数据库节点是什么,这个时候,你需要先列出所有的节点类型,然后从中找到你认为最有可能是匹配用户询问的节点。比如用户问你要看符合特定条件的「文章」,你并不知道文章的节点类型是什么,这个时候,你就需要先列出所有的节点。 -``` +You are an agent skilled in Neo4j queries. For user requests, you may not know the corresponding database nodes. In such cases, you should first list all node types and identify the most likely match for the user’s query. For example, if the user asks for "articles" matching certain criteria, you don’t know the node type for articles, so you need to list all nodes first. +``` -点击保存,然后在【交互测试】中,重复刚才的问题: +Click "Save," then repeat the earlier question in "Interactive Test": ``` -帮我找出最新的 10 条评论 -``` +Find the latest 10 comments for me. +``` -大模型的回答如下: +The LLM’s response is now: -
- -
+
+ +
-诶?怎么说,是不是好了很多了?大模型成功找到了 BlogComment 这个节点,然后返回了对应的数据。 +Much better, right? The LLM successfully found the `BlogComment` node and returned the corresponding data. -但是其实还是不太对,因为我们要求的说最新的 10 条评论,但是大模型返回的其实是最早的 10 条评论,我们点开大模型的调用细节就能看到,大模型是通过 `ORDER BY comment.createdAt` 来实现的,但是问题是,在我们的数据库中,记录一条评论何时创建的字段并不是 createdAt,而是 createdTime,这意味着大模型并不知道自己不知道节点的字段,从而产生了「幻觉」,瞎输入了一个字段。 +However, it’s still not quite right. We asked for the "latest" 10 comments, but the LLM returned the "earliest" 10. Looking at the LLM’s call details, we can see it used `ORDER BY comment.createdAt`. The problem is that in our database, the field recording when a comment was created is not `createdAt` but `createdTime`. This means the LLM doesn’t know it doesn’t know the node’s fields, leading to "hallucinations" where it makes up a field. -大模型是不会显式说自己不知道的,锦恢研究生关于 OOD 的一项研究可以说明这件事的本质原因:[EDL(Evidential Deep Learning) 原理与代码实现](https://kirigaya.cn/blog/article?seq=154),如果阁下的好奇心能够配得上您的数学功底,可以一试这篇文章。总之,阁下只需要知道,正因为大模型对自己不知道的东西会产生幻觉,所以才有我们得以注入经验的操作空间。 +LLMs won’t explicitly admit ignorance. Research on OOD (Out-of-Distribution) by Jinhui during his graduate studies explains the fundamental reason: [EDL (Evidential Deep Learning) Principles and Code Implementation](https://kirigaya.cn/blog/article?seq=154). If your curiosity matches your mathematical skills, give this article a try. In short, because LLMs hallucinate about things they don’t know, we have room to inject our experience. -### 4.5 教大模型找数据库节点的字段 +### 4.5 Teach the LLM to Find Database Node Fields -通过上面的尝试,我们知道我们距离终点只剩一点了,那就是告诉大模型,我们的数据库中,记录一条评论何时创建的字段并不是 createdAt,而是 createdTime。 +From the above attempt, we know we’re close to the finish line. We just need to tell the LLM that in our database, the field recording when a comment was created is `createdTime`, not `createdAt`. -对于识别字段的知识,我们改良一下刚刚的系统提示词下: +To teach the LLM about fields, let’s refine the system prompt: ``` -你是一个善于进行neo4j查询的智能体,对于用户要求的查询请求,你并不一定知道对应的数据库节点是什么,这个时候,你需要先列出所有的节点类型,然后从中找到你认为最有可能是匹配用户询问的节点。比如用户问你要看符合特定条件的「文章」,你并不知道文章的节点类型是什么,这个时候,你就需要先列出所有的节点。 +You are an agent skilled in Neo4j queries. For user requests, you may not know the corresponding database nodes. In such cases, you should first list all node types and identify the most likely match for the user’s query. For example, if the user asks for "articles" matching certain criteria, you don’t know the node type for articles, so you need to list all nodes first. -对于比较具体的查询,你需要先查询单个事例来看一下当前类型有哪些字段。比如用户问你最新的文章,你是不知道文章节点的哪一个字段代表 「创建时间」的,因此,你需要先列出一到两个文章节点,看一下里面有什么字段,然后再创建查询查看最新的10篇文章。 -``` +For specific queries, you should first query a few examples to see what fields the node type has. For example, if the user asks for the "latest articles," you don’t know which field represents the "creation time," so you need to list one or two article nodes to see the available fields before querying the latest 10 articles. +``` -结果如下: +The result is now: -
- -
+
+ +
+Perfect, right? -是不是很完美? +By using OpenMCP for debugging, we can uniquely determine an agent’s behavior through the system prompt + MCP server. -通过使用 openmcp 调试,我们可以通过 system prompt + mcp server 来唯一确定一个 agent 的表现行为。 +--- +## 5. Expand the MCP Server’s Atomic Skills +In the above example, although we injected our experience and knowledge through the system prompt, you’ll notice that behaviors like "query all node types" and "get all fields of a node" are very fixed processes. However, system prompts are written in natural language, which is inherently ambiguous. We can’t guarantee they’ll always be extensible. Besides system prompts, is there another way to inject our experience and knowledge? Yes, there is. -## 5. 扩充 mcp 服务器的原子技能 +For processes that are fixed and easily conceivable by "somewhat experienced people," besides using system prompts, we can also standardize knowledge injection by writing these processes as additional MCP tools. This method is called "Atomization Supplement." -在上面的例子中,虽然我们通过 system prompt 注入了我们的经验和知识,但是其实你会发现这些我们注入的行为,比如「查询所有节点类型」和「获取一个节点的所有字段」,是不是流程很固定?但是 system prompt 是通过自然语言编写的,它具有语言特有的模糊性,我们无法保证它一定是可以拓展的。那么除了 system prompt,还有什么方法可以注入我们的经验与知识呢?有的,兄弟,有的。 +Atomization Supplement involves adding extra MCP tools that are "atomic" in functionality. -在这种流程固定,而且这个操作也非常地容易让「稍微有点经验的人」也能想到的情况下,除了使用 system prompt 外,我们还有一个方法可以做到更加标准化地注入知识,也就是把上面的这些个流程写成额外的 mcp tool。这个方法被我称为「原子化扩充」(Atomization Supplement)。 +> A tool is called an "Atomic Tool" if it meets one of the following conditions: +> - The tool cannot be obtained by combining finer-grained functionalities in a limited way. +> - The finer-grained functionalities used to compose the tool are not fully or reliably used by the LLM (e.g., assembly language, DOM queries). -所谓原子化扩充,也就是增加额外的 mcp tool,这些 tool 在功能层面是「原子化」的。 +Adding extra atomic tools lets the LLM know, "Ah! I have other tricks up my sleeve!" As long as the descriptions are appropriate, the LLM can use them to gather additional information instead of hallucinating and failing the task. -> 满足如下条件之一的 tool,被称为 原子 tool (Atomic Tool): -> tool 无法由更加细粒度的功能通过有限组合得到 -> 组成得到 tool 的更加细粒度的功能,大模型并不会完全使用,或者使用不可靠 (比如汇编语言,比如 DOM 查询) +For the above process, we’ve identified two areas where the LLM hallucinates: -扩充额外的原子 tool,能够让大模型知道 “啊!我还有别的手段可以耍!” ,那么只要 description 比较恰当,大模型就能够使用它们来获得额外的信息,而不是产生「幻觉」让任务失败。 +1. Getting the label of a node type (e.g., asking for "comments," the LLM doesn’t admit it doesn’t know the label and directly uses `Comment`, but the actual label is `BlogComment`). +2. Getting the fields of a node type (e.g., asking for the "latest comments," the LLM sorts by `createdAt`, but the field recording creation time is `createdTime`). -对于上面的一整套流程,我们目前知道了如下两个技能大模型是会产生「幻觉」的: - -1. 获取一个节点类别的标签(询问评论,大模型没说自己不知道什么是评论标签,而是直接使用了 Comment,但是实际的评论标签是 BlogComment) -2. 获取一个节点类别的字段(询问最新评论,大模型选择通过 createAt 排序,但是记录 BlogComment 创建时间的字段是 createTime) - -在之前,我们通过了 system prompt 来完成了信息的注入,现在,丢弃你的 system prompt 吧!我们来玩点更加有趣的游戏。在刚刚的 util.go 中,我们针对上面的两个幻觉,实现两个额外的函数 (经过测试,cursor或者trae能完美生成下面的代码,可以不用自己写): +Earlier, we used the system prompt to inject this knowledge. Now, discard your system prompt! Let’s play a more interesting game. In `util.go`, we’ll implement two additional functions for the above hallucinations (tested, cursor or trae can generate this code perfectly, so you don’t need to write it yourself): ```go -// 获取所有的节点类型 -func GetAllNodeTypes() ([]string, error) { - cypher := "MATCH (n) RETURN DISTINCT labels(n) AS labels" - result, err := ExecuteReadOnlyCypherQuery(cypher) - if err!= nil { - return nil, err - } - var nodeTypes []string - for _, record := range result { - labels := record["labels"].([]any) - for _, label := range labels { - nodeTypes = append(nodeTypes, label.(string)) - } - } - return nodeTypes, nil -} +// Get all node types +func GetAllNodeTypes() ([]string, error) { + cypher := "MATCH (n) RETURN DISTINCT labels(n) AS labels" + result, err := ExecuteReadOnlyCypherQuery(cypher) + if err != nil { + return nil, err + } + var nodeTypes []string + for _, record := range result { + labels := record["labels"].([]any) + for _, label := range labels { + nodeTypes = append(nodeTypes, label.(string)) + } + } + return nodeTypes, nil +} -// 获取一个节点的字段示范 -func GetNodeFields(nodeType string) ([]string, error) { - cypher := fmt.Sprintf("MATCH (n:%s) RETURN keys(n) AS keys LIMIT 1", nodeType) - result, err := ExecuteReadOnlyCypherQuery(cypher) - if err!= nil { - return nil, err - } - var fields []string - for _, record := range result { - keys := record["keys"].([]any) - for _, key := range keys { - fields = append(fields, key.(string)) - } - } - return fields, nil -} -``` +// Get an example of a node's fields +func GetNodeFields(nodeType string) ([]string, error) { + cypher := fmt.Sprintf("MATCH (n:%s) RETURN keys(n) AS keys LIMIT 1", nodeType) + result, err := ExecuteReadOnlyCypherQuery(cypher) + if err != nil { + return nil, err + } + var fields []string + for _, record := range result { + keys := record["keys"].([]any) + for _, key := range keys { + fields = append(fields, key.(string)) + } + } + return fields, nil +} +``` -在 main.go 中完成它们的 schema 的申明和 tool 的注册: +In `main.go`, declare their schemas and register the tools: ```go -// ... existing code ... +// ... existing code ... - getAllNodeTypes := mcp.NewTool("getAllNodeTypes", - mcp.WithDescription("获取所有的节点类型"), - ) + getAllNodeTypes := mcp.NewTool("getAllNodeTypes", + mcp.WithDescription("Get all node types"), + ) - getNodeField := mcp.NewTool("getNodeField", - mcp.WithDescription("获取节点的字段"), - mcp.WithString("nodeLabel", - mcp.Required(), - mcp.Description("节点的标签"), - ), - ) + getNodeField := mcp.NewTool("getNodeField", + mcp.WithDescription("Get the fields of a node"), + mcp.WithString("nodeLabel", + mcp.Required(), + mcp.Description("The label of the node"), + ), + ) - // 注册对应的工具到 schema 上 - s.AddTool(getAllNodeTypes, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { - result, err := util.GetAllNodeTypes() + // Register the tools with the schema + s.AddTool(getAllNodeTypes, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + result, err := util.GetAllNodeTypes() - fmt.Println(result) - - if err != nil { - return mcp.NewToolResultText(""), err - } - - return mcp.NewToolResultText(fmt.Sprintf("%v", result)), nil - }) + fmt.Println(result) - s.AddTool(getNodeField, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { - nodeLabel := request.Params.Arguments["nodeLabel"].(string) - result, err := util.GetNodeFields(nodeLabel) - - fmt.Println(result) - - if err!= nil { - return mcp.NewToolResultText(""), err - } - - return mcp.NewToolResultText(fmt.Sprintf("%v", result)), nil - }) + if err != nil { + return mcp.NewToolResultText(""), err + } -// ... existing code ... -``` + return mcp.NewToolResultText(fmt.Sprintf("%v", result)), nil + }) -重新运行 sse 服务器,然后直接询问大模型,此时,我们取消使用 system prompt(创建一个空的,或者直接把当前的 prompt 删除),询问结果如下: + s.AddTool(getNodeField, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + args, ok := request.Params.Arguments.(map[string]interface{}) + if !ok { + return mcp.NewToolResultText(""), fmt.Errorf("invalid arguments type") + } + nodeLabel, ok := args["nodeLabel"].(string) + if !ok { + return mcp.NewToolResultText(""), fmt.Errorf("nodeLabel argument is not a string") + } + result, err := util.GetNodeFields(nodeLabel) -
- -
+ fmt.Println(result) + if err != nil { + return mcp.NewToolResultText(""), err + } -可以看到,在没有 system prompt 的情况下,大模型成功执行了这个过程,非常完美。 + return mcp.NewToolResultText(fmt.Sprintf("%v", result)), nil + }) -## 总结 +// ... existing code ... +``` -这期教程,带大家使用 go 走完了 mcp sse 的连接方式,并且做出了一个「只读 neo4j 数据库」的 mcp,通过这个 mcp,我们可以非常方便地用自然语言查询数据库的结果,而不需要手动输入 cypher。 +Restart the SSE server and directly ask the LLM. This time, we’ll disable the system prompt (create an empty one or delete the current prompt). The result is as follows: -对于部分情况下,大模型因为「幻觉」问题而导致的任务失败,我们通过一步步有逻辑可遵循的方法论,完成了 system prompt 的调优和知识的封装。最终,通过范式化的原子化扩充的方式,将这些知识包装成了更加完善的 mcp 服务器。这样,任何人都可以直接使用你的 mcp 服务器来完成 neo4j 数据库的自然语言查询了。 +
+ +
-最后,觉得 openmcp 好用的米娜桑,别忘了给我们的项目点个 star:https://github.com/LSTM-Kirigaya/openmcp-client +As you can see, without the system prompt, the LLM successfully executed the process. Perfect. -想要和我进一步交流 OpenMCP 的朋友,可以进入我们的交流群(github 项目里面有) \ No newline at end of file +--- + +## Summary + +This tutorial walked you through implementing an MCP server for a read-only Neo4j database using Go and SSE. With this MCP, you can easily query the database using natural language without manually writing Cypher queries. + +For cases where the LLM fails due to hallucinations, we methodically refined the system prompt to encapsulate knowledge. Finally, through the paradigm of Atomization Supplement, we packaged this knowledge into a more robust MCP server. Now, anyone can use your MCP server to query the Neo4j database with natural language. + +Finally, if you find OpenMCP useful, don’t forget to star our project: [https://github.com/LSTM-Kirigaya/openmcp-client](https://github.com/LSTM-Kirigaya/openmcp-client) + +To further discuss OpenMCP, join our communication group (link in the GitHub repo). \ No newline at end of file diff --git a/plugin-tutorial/examples/mcp-examples.md b/plugin-tutorial/examples/mcp-examples.md index 56f4714..2c3246d 100644 --- a/plugin-tutorial/examples/mcp-examples.md +++ b/plugin-tutorial/examples/mcp-examples.md @@ -1,28 +1,40 @@ --- next: - text: python 实现天气信息 mcp 服务器 (STDIO) + text: Python Weather MCP Server (STDIO) link: '/plugin-tutorial/examples/python-simple-stdio' --- -# MCP 服务器开发案例 +# MCP Server Development Examples ## Python -- [python 实现天气信息 mcp 服务器 (STDIO)](./python-simple-stdio) -- [python 实现进行通用表单填充 的 mcp (STDIO)](./python-form-stdio) -- [python 实现基于 blender 的 mcp (STDIO)](./python-blender-stdio) -- [python 实现 cadence EDA 的 mcp (STDIO)](./python-cadence-stdio) -- 基于 ffmpeg mcp 实现通过对话的视频剪辑 -- 基于 rag mcp 实现知识库的注入 -- 实现 Stable Diffusion 的 MCP 服务器 +- [Weather Information MCP Server (STDIO)](./python-simple-stdio) + Python implementation for weather data queries via STDIO +- [Universal Form Filling MCP (STDIO)](./python-form-stdio) + Automated form population solution using Python +- [Blender Integration MCP (STDIO)](./python-blender-stdio) + 3D modeling automation through Blender commands +- [Cadence EDA MCP (STDIO)](./python-cadence-stdio) + Electronic design automation interface +- Video Editing via FFmpeg MCP + AI-driven video editing workflows +- Knowledge Base Injection with RAG MCP + Dynamic document retrieval system +- Stable Diffusion MCP Server + Text-to-image generation service -## Nodejs -- [typescript 实现基于 crawl4ai 的超级网页爬虫 mcp (STDIO)](./typescript-crawl4ai-stdio) +## Node.js +- [Crawl4AI Web Crawler MCP (STDIO)](./typescript-crawl4ai-stdio) + TypeScript implementation for advanced web scraping ## Go -- [go 实现 neo4j 的只读 mcp 服务器 (SSE)](./go-neo4j-sse) +- [Neo4j Read-Only MCP (SSE)](./go-neo4j-sse) + Graph database query interface using Server-Sent Events ## Java -- [java 实现文档数据库的只读 mcp (HTTP)](./java-es-http) +- [Document Database MCP (HTTP)](./java-es-http) + Elasticsearch integration with HTTP transport + +## Authentication +- [OAuth2 MCP Server (SSE)](./sse-oauth2) + Secure authentication implementation example -## 认证 -- [SSE 方式的 OAuth2 认证 mcp 服务器示例](./sse-oauth2) \ No newline at end of file diff --git a/plugin-tutorial/examples/python-simple-stdio.md b/plugin-tutorial/examples/python-simple-stdio.md index ec11cc4..7162b4e 100644 --- a/plugin-tutorial/examples/python-simple-stdio.md +++ b/plugin-tutorial/examples/python-simple-stdio.md @@ -1,342 +1,131 @@ -# python 实现天气信息 mcp 服务器 +# Implementing a Weather Information MCP Server in Python -## hook +[Video Tutorial](https://www.bilibili.com/video/BV1zYGozgEHc) -等等,开始前,先让我们看一个小例子,假设我下周要去明日方舟锈影新生的漫展,所以我想要知道周六杭州的天气,于是我问大模型周六的天气,结果大模型给了我如下的回复: +## The Problem Scenario + +Before we begin, consider this common situation: I'm planning to attend the Arknights "Rusty Shadows" convention next Saturday in Hangzhou and want to check the weather forecast. When I ask an LLM about Saturday's weather, I get this response:
-这可不行,相信朋友们也经常遇到过这样的情况,大模型总是会“授人以渔”,但是有的时候,我们往往就是想要直接知道最终结果,特别是一些无聊的生活琐事。 +This "teach you to fish" approach isn't helpful for simple everyday queries. While there are many weather apps available, how can we integrate weather data directly into LLMs to get actionable answers? -其实实现天气预报的程序也很多啦,那么有什么方法可以把写好的天气预报的程序接入大模型,让大模型告诉我们真实的天气情况,从而选择明天漫展的穿搭选择呢? +## Introduction -如果直接写函数用 function calling 显得有点麻烦,这里面涉及到很多麻烦的技术细节需要我们商榷,比如大模型提供商的API调用呀,任务循环的搭建呀,文本渲染等等,从而浪费我们宝贵的时间。而 MCP 给了我们救赎之道,今天这期教程,就教大家写一个简单的 MCP 服务器,可以让大模型拥有得知天气预报的能力。 +👉 [Previous Guide](https://zhuanlan.zhihu.com/p/32593727614) +In our last tutorial, we covered MCP fundamentals. Now, we'll develop our first MCP server to bridge existing applications/services with LLMs. +For efficient development, we'll use OpenMCP - an integrated MCP testing tool I recently open-sourced: -## 前言 +[OpenMCP Announcement](https://zhuanlan.zhihu.com/p/1894785817186121106) +OpenMCP GitHub: https://github.com/LSTM-Kirigaya/openmcp-client +(Stars appreciated! :D) -👉 [上篇导航](https://zhuanlan.zhihu.com/p/32593727614) +### Initial Setup -在上篇,我们简单讲解了 MCP 的基础,在这一篇,我们将正式开始着手开发我们自己的 MCP 服务器,从而将现成的应用,服务,硬件等等接入大模型。从而走完大模型到赋能终端应用的最后一公里。 - -工欲善其事,必先利其器。为了更加优雅快乐地开发 MCP 服务器,我们需要一个比较好的测试工具,允许我们在开发的过程看到程序的变化,并且可以直接接入大模型验证工具的有效性。 - -于是,我在前不久开源了一款一体化的 MCP 测试开发工具 —— OpenMCP,[全网第一个 MCP 服务器一体化开发测试软件 OpenMCP 发布!](https://zhuanlan.zhihu.com/p/1894785817186121106) - -> OpenMCP QQ 群 782833642 - -OpenMCP 开源链接:https://github.com/LSTM-Kirigaya/openmcp-client - -求个 star :D - -### 第一个 MCP 项目 - -事已至此,先 coding 吧 :D - -在打开 vscode 或者 trae 之前,先安装基本的 uv 工具,uv 是一款社区流行的版本管理工具,你只需要把它理解为性能更好的 conda 就行了。 - -我们先安装 uv,如果您正在使用 anaconda,一定要切换到 base 环境,再安装: +First, install the UV tool (a Conda alternative): ```bash pip install uv +uv # Verify installation ``` -安装完成后,运行 uv - -```bash -uv -``` - -没有报错就说明成功。uv 只会将不可以复用的依赖安装在本地,所以使用 anaconda 的朋友不用担心,uv 安装的依赖库会污染你的 base,我们接下来使用 uv 来创建一个基础的 python 项目 - +Create a new project: ```bash mkdir simple-mcp && cd simple-mcp uv init uv add mcp "mcp[cli]" ``` -然后我们打开 vscode 或者 trae,在插件商城找到并下载 OpenMCP 插件 +Install the OpenMCP plugin in VSCode:
-先制作一个 MCP 的最小程序: +### Basic MCP Server -文件名:simple_mcp.py +Create `simple_mcp.py`: ```python from mcp.server.fastmcp import FastMCP -mcp = FastMCP('锦恢的 MCP Server', version="11.45.14") - -@mcp.tool( - name='add', - description='对两个数字进行实数域的加法' -) -def add(a: int, b: int) -> int: - return a + b - -@mcp.resource( - uri="greeting://{name}", - name='greeting', - description='用于演示的一个资源协议' -) -def get_greeting(name: str) -> str: - # 访问处理 greeting://{name} 资源访问协议,然后返回 - # 此处方便起见,直接返回一个 Hello,balabala 了 - return f"Hello, {name}!" - -@mcp.prompt( - name='translate', - description='进行翻译的prompt' -) -def translate(message: str) -> str: - return f'请将下面的话语翻译成中文:\n\n{message}' +mcp = FastMCP('Weather MCP Server', version="1.0.0") @mcp.tool( name='weather', - description='获取指定城市的天气信息' + description='Get weather information for specified city' ) def get_weather(city: str) -> str: - """模拟天气查询协议,返回格式化字符串""" + """Weather query protocol - returns formatted string""" return f"Weather in {city}: Sunny, 25°C" -@mcp.resource( - uri="user://{user_id}", - name='user_profile', - description='获取用户基本信息' -) -def get_user_profile(user_id: str) -> dict: - """模拟用户协议,返回字典数据""" - return { - "id": user_id, - "name": "张三", - "role": "developer" - } - -@mcp.resource( - uri="book://{isbn}", - name='book_info', - description='通过ISBN查询书籍信息' -) -def get_book_info(isbn: str) -> dict: - """模拟书籍协议,返回结构化数据""" - return { - "isbn": isbn, - "title": "Python编程:从入门到实践", - "author": "Eric Matthes" - } - -@mcp.prompt( - name='summarize', - description='生成文本摘要的提示词模板' -) -def summarize(text: str) -> str: - """返回摘要生成提示词""" - return f"请用一句话总结以下内容:\n\n{text}" +# Additional example tools/resources omitted for brevity ``` -我们试着运行它: - - +Test the server: ```bash uv run mcp run simple_mcp.py ``` -如果没有报错,但是卡住了,那么说明我们的依赖安装没有问题,按下 ctrl c 或者 ctrl z 退出即可。 +### Connecting with OpenMCP -在阁下看起来,这些函数都简单到毫无意义,但是请相信我,我们总需要一些简单的例子来通往最终的系统。 - -### Link, Start! - -如果你下载了 OpenMCP 插件,那么此时你就能在打开的 python 编辑器的右上角看到 OpenMCP 的紫色图标,点击它就能启动 OpenMCP,调试当前的 MCP 了。 - -
- -
- -默认是以 STDIO 的方式启动,默认运行如下的命令: - -```bash -uv run mcp run <当前打开的 python 文件的相对路径> -``` - -所以你需要保证已经安装了 mcp 脚手架,也就是 `uv add mcp "mcp[cli]"`。 - -打开后第一件事就是先看左下角连接状态,确保是绿色的,代表当前 OpenMCP 和你的 MCP 服务器已经握手成功。 +Click the purple OpenMCP icon in VSCode to launch the debugger. Verify connection status (green indicator):
-如果连接成功,此时连接上方还会显示你当前的 MCP 服务器的名字,光标移动上去还能看到版本号。这些信息由我们如下的代码定义: +## Developing the Weather Function -```python -mcp = FastMCP('锦恢的 MCP Server', version="11.45.14") -``` +### Tool Debugging -这在我们进行版本管理的时候会非常有用。请善用这套系统。 - - -如果连接失败,可以点击左侧工具栏的第二个按钮,进入连接控制台,查看错误信息,或是手动调整连接命令: - -
- -
- -### 初识 OpenMCP - -接下来,我来简单介绍一下 OpenMCP 的基本功能模块,如果一开始,你的屏幕里什么也没有,先点击上面的加号创建一个新的标签页,此处页面中会出现下图屏幕中的四个按钮 - -
- -
- -放大一点 - -
- -
- -前三个,资源、提词和工具,分别用于调试 MCP 中的三个对应项目,也就是 Resources,Prompts 和 Tools,这三个部分的使用,基本和 MCP 官方的 Inspector 工具是一样的,那是自然,我就照着这几个抄的,诶嘿。 - -
- -
- -然后第四个按钮「交互测试」,它是一个我开发的 MCP 客户端,其实就是一个对话窗口,你可以无缝衔接地直接在大模型中测试你当前的 MCP 服务器的功能函数。 - -
- -
- - -目前我暂时只支持 tools 的支持,因为 prompts 和 resources 的我还没有想好,(resource 感觉就是可以当成一个 tool),欢迎大家进群和我一起讨论:QQ群 782833642 - - - -## 开始调试天气函数 - -### 工具调试 - -还记得我们一开始给的 mcp 的例子吗?我们可以通过 OpenMCP 来快速调试这里面写的函数,比如我们本期的目标,写一个天气预报的 MCP,那么假设我们已经写好了一个天气预报的函数了,我们把它封装成一个 tool: - -```python -@mcp.tool( - name='weather', - description='获取指定城市的天气信息' -) -def get_weather(city: str) -> str: - """模拟天气查询协议,返回格式化字符串""" - return f"Weather in {city}: Sunny, 25°C" -``` - -当然,它现在是没有意义的,因为就算把黑龙江的城市ID输入,它也返回 25 度,但是这些都不重要,我想要带阁下先走完整套流程。建立自上而下的感性认知比死抠细节更加容易让用户学懂。 - -那么我们现在需要调试这个函数,打开 OpenMCP,新建一个「工具」调试项目 - -
- -
- -然后此时,你在左侧的列表可以看到 weather 这个工具,选择它,然后在右侧的输入框中随便输入一些东西,按下回车(或者点击「运行」),你能看到如下的响应: +Our initial weather tool just returns static data. Let's test it in OpenMCP:
-看到我们函数 return 的字符串传过来了,说明没问题,链路通了。 +### Interactive Testing -### 交互测试 - -诶?我知道你编程很厉害,但是,在噼里啪啦快速写完天气预报爬虫前,我们现在看看我们要如何把已经写好的工具注入大模型对话中。为了使用大模型,我们需要先选择大模型和对应的 API,点击左侧工具栏的第三个按钮,进入 API 模块,选择你想要使用的大模型运营商、模型,填写 API token,然后点击下面的「保存」 +Configure your LLM API in OpenMCP:
-再新建一个标签页,选择「交互测试」,此时,我们就可以直接和大模型对话了,我们先看看没有任何工具注入的大模型会如何回应天气预报的问题,点击最下侧工具栏从左往右第三个按钮,进入工具选择界面,选择「禁用所有工具」 - -
- -
- -点击「关闭」后,我们问大模型一个问题: - +Test without tools: ``` -请问杭州的温度是多少? +What's the temperature in Hangzhou? ``` -
- -
- -可以看到,大模型给出了和文章开头一样的回答。非常敷衍,因为它确实无法知道。 - -此处,我们再单独打开「weather」工具: - -
- -
- -问出相同的问题: +Then with our weather tool enabled:
-可以看到,大模型给出了回答是 25 度,还有一些额外的推导信息。 +Notice the two-step process: +1. LLM calls our weather tool with `{"city": "Hangzhou"}` +2. Our server responds with formatted weather data +3. LLM generates final answer -我们不妨关注一些细节,首先,大模型并不会直接回答问题,而是会先去调用 weather 这个工具,调用参数为: +## Production-Ready Implementation -```json -{ - "city": "杭州" -} -``` - -然后,我们的 MCP 服务器给出了响应: - -``` -Weather in 杭州: Sunny, 25°C -``` - -从而,最终大模型才根据这些信息给出了最终的回答。也就是,这个过程我们实际调用了两次大模型的服务。而且可以看到两次调用的输入 token 数量都非常大,这是因为 OpenMCP 会将函数调用以 JSON Schema 的形式注入到请求参数中,weather 这个工具的 JSON Schema 如下图的右侧的 json 所示: - -
- -
- -然后支持 openai 协议的大模型厂商都会针对这样的信息进行 function calling,所以使用了工具的大模型请求的输入 token 数量都会比较大。但是不需要担心,大部分厂商都实现了 KV Cache,对相同前缀的输入存在缓存,缓存命中部分的费用开销是显著低于普通的 输入输出 token 价格的。OpenMCP 在每个回答的下面都表明了当次请求的 输入 token,输出 token,总 token 和 缓存命中率。 - -其中 - -- 「总 token」 = 「输入 token」 + 「输出 token」 - -- 「缓存命中率」 = 「缓存命令的 token」 / 「输入 token」 - -> 没错,缓存命中率 是对于输入 token 的概念,输出 token 是没有 缓存命中率这个说法的。 - -在后续的开发中,你可以根据这些信息来针对性地对你的服务或者 prompt 进行调优。 - -### 完成一个真正的天气预报吧! - -当然,这些代码也非常简单,直接让大模型生成就行了(其实大模型是无法生成免 API 的 python 获取天气的代码的,我是直接让大模型把我个人网站上天气预报的 go 函数翻译了一下) - -我直接把函数贴上来了: +Here's a complete weather implementation using a real API: ```python import requests import json from typing import NamedTuple, Optional +from mcp.server.fastmcp import FastMCP class CityWeather(NamedTuple): city_name_en: str - city_name_cn: str + city_name_cn: str city_code: str temp: str wd: str @@ -345,37 +134,23 @@ class CityWeather(NamedTuple): aqi: str weather: str -def get_city_weather_by_city_name(city_code: str) -> Optional[CityWeather]: - """根据城市名获取天气信息""" - - if not city_code: - print(f"找不到{city_code}对应的城市") - return None - +def get_city_weather(city_code: str) -> Optional[CityWeather]: + """Get weather by city code""" try: - # 构造请求URL url = f"http://d1.weather.com.cn/sk_2d/{city_code}.html" - - # 设置请求头 headers = { - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.0.0", + "User-Agent": "Mozilla/5.0...", "Host": "d1.weather.com.cn", "Referer": "http://www.weather.com.cn/" } - # 发送HTTP请求 response = requests.get(url, headers=headers) response.raise_for_status() - # 解析JSON数据 - # 解析JSON数据前先处理编码问题 content = response.text.encode('latin1').decode('unicode_escape') - json_start = content.find("{") - json_str = content[json_start:] - + json_str = content[content.find("{"):] weather_data = json.loads(json_str) - # 构造返回对象 return CityWeather( city_name_en=weather_data.get("nameen", ""), city_name_cn=weather_data.get("cityname", "").encode('latin1').decode('utf-8'), @@ -387,50 +162,31 @@ def get_city_weather_by_city_name(city_code: str) -> Optional[CityWeather]: aqi=weather_data.get("aqi", ""), weather=weather_data.get("weather", "").encode('latin1').decode('utf-8') ) - except Exception as e: - print(f"获取天气信息失败: {str(e)}") + print(f"Weather query failed: {str(e)}") return None -from mcp.server.fastmcp import FastMCP - -mcp = FastMCP('weather', version="0.0.1") +mcp = FastMCP('Weather MCP', version="1.0.0") @mcp.tool( name='get_weather_by_city_code', - description='根据城市天气预报的城市编码 (int),获取指定城市的天气信息' + description='Get weather by city code (integer)' ) def get_weather_by_code(city_code: int) -> str: - """模拟天气查询协议,返回格式化字符串""" - city_weather = get_city_weather_by_city_name(city_code) - return str(city_weather) + weather_data = get_city_weather(str(city_code)) + return str(weather_data) ``` +Key Notes: +1. Use `int` type for numeric IDs to ensure proper JSON serialization +2. Follow Python naming conventions for tool names -这里有几个点一定要注意: - -1. 如果你的输入参数是数字,就算是城市编码这种比较长的数字,请一定定义成 int,因为 mcp 底层的是要走 JSON 正反序列化的,而 "114514" 这样的字符串会被 JSON 反序列化成 114514,而不是 "114514" 这个字符串。你实在要用 str 来表示一个很长的数字,那么就在前面加一个前缀,比如 "code-114514",避免被反序列化成数字,从而触发 mcp 内部的 type check error -2. tool 的 name 请按照 python 的变量命名要求进行命名,否则部分大模型厂商会给你报错。 - -好,我们先测试一下: - -
- -
- -可以看到,我们的天气查询工具已经可以正常工作了。 - -那么接下来,我们就可以把这个工具注入到大模型中了。点击 「交互测试」,只激活当前这个工具,然后询问大模型: -``` -请问杭州的天气是多少? -``` +### Final Test
-完美! +Success! We've built a fully functional weather MCP service. For production deployments, consider using SSE connections for better scalability (covered in future tutorials). -如此,我们便完成了一个天气查询工具的开发。并且轻松地注入到了我们的大模型中。在实际提供商业级部署方案的时候,虽然 mcp 目前的 stdio 冷启动速度足够快,但是考虑到拓展性等多方面因素,SSE 还是我们首选的连接方案,关于 SSE 的使用,我们下期再聊。 - -OpenMCP 开源链接:https://github.com/LSTM-Kirigaya/openmcp-client \ No newline at end of file +OpenMCP GitHub: https://github.com/LSTM-Kirigaya/openmcp-client \ No newline at end of file diff --git a/plugin-tutorial/faq/help.md b/plugin-tutorial/faq/help.md index a7de7c4..6878a2c 100644 --- a/plugin-tutorial/faq/help.md +++ b/plugin-tutorial/faq/help.md @@ -1,27 +1,26 @@ ---- -layout: doc ---- -# 常见问题解答 +## layout: doc -## 错误代码说明 +# FAQ -### 32000 - MCP 连接失败 +## Error Code Explanation -MCP 连接失败可能有多种原因,以下是一些常见情况: +### 32000 - MCP Connection Failed -• **虚拟环境路径不匹配** +MCP connection failure can have multiple causes. Here are some common situations: -虚拟环境(venv)与入口文件路径不匹配是导致连接失败的常见原因之一。 +• **Virtual Environment Path Mismatch** -详细的解决方案请参考:[配置说明](./venv-not-same-path/venv-not-same-path.md) +A mismatch between the virtual environment (venv) and the entry file path is one of the common reasons for connection failure. + +For detailed solutions, please refer to: [Configuration Guide](./venv-not-same-path/venv-not-same-path.md) --- -• **其他可能的原因** +• **Other Possible Causes** -- 端口被占用 -- 环境变量配置错误 -- 依赖库未正确安装 +* Port is occupied +* Environment variables are misconfigured +* Dependencies are not properly installed -> 如果您遇到以上问题,请先查看错误日志获取详细信息。如果问题仍然存在,可以在 [GitHub Issues](https://github.com/LSTM-Kirigaya/openmcp-client/issues) 中寻求帮助。 +> If you encounter the above issues, please first check the error logs for more details. If the problem persists, you can seek help on [GitHub Issues](https://github.com/LSTM-Kirigaya/openmcp-client/issues). diff --git a/plugin-tutorial/faq/venv-not-same-path/venv-not-same-path.md b/plugin-tutorial/faq/venv-not-same-path/venv-not-same-path.md index 53cc616..7ab3bb5 100644 --- a/plugin-tutorial/faq/venv-not-same-path/venv-not-same-path.md +++ b/plugin-tutorial/faq/venv-not-same-path/venv-not-same-path.md @@ -1,29 +1,29 @@ -# 虚拟环境与入口文件不在同一目录时的配置方式 +# Configuration When Virtual Environment and Entry File Are in Different Directories -## 问题描述 +## Problem Description -在使用 OpenMCP 时,有时会遇到虚拟环境(venv)与 Python 文件不在同一目录的情况,甚至虚拟环境可能位于项目文件夹之外。这种情况下,点击右上角连接按钮可能会出现 MCP 连接失败(错误代码:32000)的问题。 +When using OpenMCP, you may sometimes encounter situations where the virtual environment (venv) and the Python file are not located in the same directory, or the virtual environment might even be outside the project folder. In such cases, clicking the connect button on the top right may result in an MCP connection failure (Error Code: 32000). -## 解决方案 +## Solution -### 1. 调整执行目录 +### 1. Adjust the Execution Directory -在连接选项中,您需要调整执行目录到虚拟环境所在的位置: +In the connection options, you need to set the execution directory to where the virtual environment is located: -![MCP 连接选项界面](./image-2.png) +![MCP Connection Options Interface](./image-2.png) -### 2. 修改执行命令 +### 2. Modify the Execution Command -同时,需要相应地修改执行命令: +At the same time, adjust the execution command accordingly: -![修改执行命令示例](./image.png) +![Example of Modified Execution Command](./image.png) -### 3. 直接指定解释器路径 +### 3. Directly Specify the Interpreter Path -对于特定情况,您可以直接在命令中指定 Python 解释器的完整路径,例如: +For certain cases, you can directly specify the full path of the Python interpreter in the command, for example: ```bash C:\code\ygo-chat\.venv\Scripts\python.exe example.py ``` -> 注意:此方法同样适用于 node或者mcp指令的【命令】以及其它mcp client的mcp配置文件。 \ No newline at end of file +> Note: This method also applies to the command field for node or mcp instructions, as well as other MCP client configuration files. diff --git a/plugin-tutorial/images/.DS_Store b/plugin-tutorial/images/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/plugin-tutorial/images/.DS_Store differ diff --git a/plugin-tutorial/index.md b/plugin-tutorial/index.md index ad9ae1b..1fc889f 100644 --- a/plugin-tutorial/index.md +++ b/plugin-tutorial/index.md @@ -1,43 +1,41 @@ --- next: - text: 什么是 MCP? + text: What is MCP? link: '/plugin-tutorial/what-is-mcp' --- -# OpenMCP 概述 +# OpenMCP Overview :::warning -在正式开始 OpenMCP 的学习之前,我们强烈推荐您先了解一下 MCP 的基本概念:[Agent 时代基础设施 | MCP 协议介绍](https://kirigaya.cn/blog/article?seq=299) +Before starting with OpenMCP, we strongly recommend understanding the basic concepts of MCP: [Agent Era Infrastructure | MCP Protocol Introduction](https://kirigaya.cn/blog/article?seq=299) ::: -## 什么是 OpenMCP +## What is OpenMCP -OpenMCP 是一个面向开发者的 MCP 调试器和 SDK,致力于降低 AI Agent 的全链路开发成本和开发人员的心智负担。 +OpenMCP is a developer-oriented MCP debugger and SDK, dedicated to reducing the full-chain development costs of AI Agents and developers' cognitive load. ![](./images/openmcp.png) -OpenMCP 分为两个部分,但是本板块讲解的是 OpenMCP 调试器的部分的使用,这部分也被我们称为 OpenMCP Client。OpenMCP Client 的本体是一个可在类 vscode 编辑器上运行的插件。它兼容了目前 MCP 协议的全部特性,且提供了丰富的利于开发者使用的功能,可以作为 Claude Inspector 的上位进行使用。 +OpenMCP consists of two parts, but this section focuses on using the OpenMCP debugger portion, which we call OpenMCP Client. The OpenMCP Client is a plugin that runs on vscode-like editors. It's compatible with all current MCP protocol features and provides rich developer-friendly functionality, serving as an enhanced replacement for Claude Inspector. -:::info 类 vscode 编辑器 (VLE) -类 vscode 编辑器 (vscode-like editor,简称 VLE) 是指基于 Vscodium 内核开发的通用型代码编辑器,它们都能够兼容的大部分的vscode插件生态,并且具有类似 vscode 的功能(比如支持 LSP3.7 协议、拥有 remote ssh 进行远程开发的能力、拥有跨编辑器的配置文件)。 +:::info Vscode-like Editors (VLE) +Vscode-like editors (VLE) refer to general-purpose code editors developed based on the Vscodium core. They can mostly support the vscode plugin ecosystem and have similar functionality to vscode (such as supporting LSP3.7 protocol, remote SSH for development, and cross-editor configuration files). -比较典型的 VLE 有:vscode, trae, cursor 和 vscodium 各类发行版本。 +Typical VLEs include: vscode, trae, cursor, and various Vscodium distributions. ::: -## 什么是 Claude Inspector +## What is Claude Inspector -Claude Inspector 是一款 Claude 官方(也就是 MCP 协议的提出者)发布的开源 MCP 调试器,开发者在开发完 MCP 服务器后,可以通过这款调试器来测试功能完整性。 +Claude Inspector is an open-source MCP debugger released by Claude (the proposer of the MCP protocol). Developers can use this debugger to test functionality after developing an MCP server. ![](./images/inspector.png) -但是 Inspector 工具存在如下几个缺点: +However, Inspector has several drawbacks: -- 使用麻烦:使用 Inspector 每次都需要通过 mcp dev 启动一个 web 前后端应用 -- 功能少:Inspector 只提供了最为基础的 MCP 的 tool 等属性的调试。如果用户想要测试自己开发的 MCP 服务器在大模型的交互下如何,还需要连接进入 Claude Desktop 并重启客户端,对于连续调试场景,非常不方便。 -- 存在部分 bug:对于 SSE 和 streamable http 等远程连接的场景,Inspector 存在已知 bug,这对真实工业级开发造成了极大的影响。 -- 无法对调试内容进行保存和留痕:在大规模微服务 mcp 化的项目中,这非常重要。 -- 无法同时调试多个 mcp 服务器:在进行 mcp 原子化横向拓展的场景中,这是一项必要的功能。 +- Cumbersome to use: Requires starting a web application via `mcp dev` each time +- Limited features: Only provides basic debugging of MCP tool properties. Testing MCP server interactions with large models requires connecting to Claude Desktop and restarting the client, which is inconvenient for continuous debugging scenarios. +- Contains bugs: Known issues with SSE and streamable HTTP remote connections significantly impact real-world industrial development. +- No debugging content saving/tracing: Critical for large-scale microservice MCP projects. +- Cannot debug multiple MCP servers simultaneously: Essential for MCP atomic horizontal scaling scenarios. -而 OpenMCP Client 被我们制作出来的一个原因就是为了解决 Inspector 上述的痛点,从而让 mcp 服务器的开发门槛更低,用户能够更加专注于业务本身。 - - \ No newline at end of file +The OpenMCP Client was developed to address these Inspector pain points, lowering MCP server development barriers and allowing users to focus more on business logic. \ No newline at end of file diff --git a/plugin-tutorial/quick-start/acquire-openmcp.md b/plugin-tutorial/quick-start/acquire-openmcp.md index e87da63..7193ed0 100644 --- a/plugin-tutorial/quick-start/acquire-openmcp.md +++ b/plugin-tutorial/quick-start/acquire-openmcp.md @@ -1,50 +1,46 @@ ---- -layout: doc ---- +# Getting OpenMCP +## Installing OpenMCP from the Plugin Marketplace -# 获取 OpenMCP +You can directly obtain the OpenMCP plugin from the plugin marketplace of mainstream VLEs. For example, in vscode, click on the left side's plugin marketplace, and then search for `OpenMCP` in the search bar to find the OpenMCP plugin. -## 在插件商城中安装 OpenMCP +![vscode Plugin Marketplace](./images/vscode-plugin-market.png) -你可以在主流 VLE 的插件商城直接获取 OpenMCP 插件。比如在 vscode 中,点击左侧的插件商城,然后在搜索框中输入 `OpenMCP` 即可找到 OpenMCP 插件。 +## Offline Installation -![vscode 插件商城](./images/vscode-plugin-market.png) - -## 离线安装 - -VLE 的插件本质是一个 zip 压缩包,后缀名为 vsix,全平台通用。我们的 CI/CD 机器人在每次版本发布后,会自动构建并上传 vsix 到 github release,你可以通过如下的链接访问到对应版本的 github release 页面: +VLE plugins are essentially zip files with the `.vsix` extension, which are cross-platform. Our CI/CD bot automatically builds and uploads the `.vsix` file to GitHub Releases after every version release. You can access the corresponding GitHub release page for each version through the following link: ``` -https://github.com/LSTM-Kirigaya/openmcp-client/releases/tag/v{版本号} +https://github.com/LSTM-Kirigaya/openmcp-client/releases/tag/v{version_number} ``` -比如对于 0.1.1 这个版本,它的 release 页面链接为:[https://github.com/LSTM-Kirigaya/openmcp-client/releases/tag/v0.1.1](https://github.com/LSTM-Kirigaya/openmcp-client/releases/tag/v0.1.1) +For example, for version 0.1.1, its release page link is: [https://github.com/LSTM-Kirigaya/openmcp-client/releases/tag/v0.1.1](https://github.com/LSTM-Kirigaya/openmcp-client/releases/tag/v0.1.1) -在 `Assets` 下面,你可以找到对应的 vsix 压缩包 +Under the `Assets` section, you can find the corresponding `.vsix` file. -![github release](./images/github-release.png) +![GitHub Release](./images/github-release.png) -除此之外,您还可以通过如下的商城网页来获取最新的 openmcp 的 vsix +In addition, you can also get the latest OpenMCP `.vsix` from the following marketplace web pages: -- https://open-vsx.org/extension/kirigaya/openmcp -- https://marketplace.visualstudio.com/items?itemName=kirigaya.openmcp +* [https://open-vsx.org/extension/kirigaya/openmcp](https://open-vsx.org/extension/kirigaya/openmcp) +* [https://marketplace.visualstudio.com/items?itemName=kirigaya.openmcp](https://marketplace.visualstudio.com/items?itemName=kirigaya.openmcp) -点击 vsix 后缀名的文件下载,下载完成后,您就可以直接安装它了。在 VLE 中安装外部的 vsix 文件有两种方法。 +Click on the `.vsix` file to download it. After the download is complete, you can directly install it. There are two ways to install an external `.vsix` file in VLE. -### 方法一:在 VLE 中安装 +### Method 1: Install in VLE -VLE 的插件商城页面有一个三个点的按钮,点击它后,你能看到下面这个列表中被我标红的按钮 +In the VLE plugin marketplace page, there is a button with three dots. After clicking it, you will see a list with a button I have highlighted in red. -![vscode 插件商城](./images/vscode-plugin-market-install-from.png) +![vscode Plugin Marketplace](./images/vscode-plugin-market-install-from.png) -点击它后,找到刚刚下载的 vsix 文件,点击即可完成安装。 +Click on it, find the `.vsix` file you just downloaded, and click to complete the installation. -### 方法二:通过命令行 +### Method 2: Install via Command Line -如果您的 VLE 是全局安装的,会自动存在一个命令行工具,命令如下: +If your VLE is globally installed, a command-line tool will be available automatically. The command is as follows: ::: code-group + ```bash [vscode] code --install-extension /path/to/openmcp-0.1.1.vsix ``` @@ -56,6 +52,7 @@ trae --install-extension /path/to/openmcp-0.1.1.vsix ```bash [cursor] cursor --install-extension /path/to/openmcp-0.1.1.vsix ``` + ::: -`/path/to/openmcp-0.1.1.vsix` 代表你刚刚下载的 vsix 文件的绝对路径。这样也可以安装插件。 \ No newline at end of file +`/path/to/openmcp-0.1.1.vsix` represents the absolute path of the `.vsix` file you just downloaded. This will also install the plugin. diff --git a/plugin-tutorial/quick-start/first-mcp.md b/plugin-tutorial/quick-start/first-mcp.md index 9510068..b0e1d16 100644 --- a/plugin-tutorial/quick-start/first-mcp.md +++ b/plugin-tutorial/quick-start/first-mcp.md @@ -1,41 +1,38 @@ +# Your First MCP -# 你的第一个 MCP +There are many programming languages that can implement MCP. The major ones have both official and community support, and you can easily find corresponding libraries by searching for "programming language + MCP". In the [[mcp-examples|MCP Server Development Examples]] section, we also provide examples in different programming languages. -实现 MCP 的编程语言很多,常见的几户所有编程语言都有官方和民间的支持,以 编程语言 + MCP 就能搜到对应的库,在 [[mcp-examples|MCP 服务器开发案例]] 中,我们也提供了不同编程语言的不同例子。 +Among all the programming languages, Python is undoubtedly the easiest and simplest for developing MCP, especially for beginners. So for our first MCP, we will use Python. The results in other programming languages are quite similar. -在所有编程语言中,Python 的 MCP 的开发无疑是最为简单,最容易让新手上手的,所以第一个 MCP我们先用 python 来实现。其他的编程语言实现效果也大同小异。 - -## 安装 uv - -Python 写 mcp 服务器强烈推荐使用 uv 作为包管理器,关于 uv,你只需要知道它是一个高性能包管理器,拥有 pip 和 conda 的所有优点。没有的朋友请先通过 pip 安装 uv: +## Installing `uv` +When writing an MCP server in Python, it's highly recommended to use `uv` as the package manager. All you need to know about `uv` is that it is a high-performance package manager, combining the best features of pip and conda. If you don't have it, please install `uv` using pip: ```bash pip install uv ``` +\:::warning Attention Anaconda or Miniconda users! +Please do not install `uv` in any environment other than the base environment. Install it in the base environment, as `uv` will handle environment isolation. Do not worry about it contaminating your base environment. If you don't install it in the base environment or use the global pip, we won't know where you installed `uv`. When installed via pip in the base environment, the script will be installed in `~/anaconda/bin/uv`. Please make sure that `~/anaconda/bin/` is included in your `$PATH`. +\::: -:::warning 使用 anaconda 或者 miniconda 的朋友注意了! -请不要在非 base 环境下安装 uv,请在 base 环境下安装 uv,uv 本身会做好环境隔离的工作,请不要担心 uv 会污染你的 base 环境。你不安装在 base 下或者使用全局默认的 pip 安装,我们根本不知道你安装的 uv 在哪里!base 环境下使用 pip 安装的脚本会安装在 `~/anaconda/bin/uv` 中,也请确保 `~/anaconda/bin/` 在你的 `$PATH` 中。 -::: - - -查看 uv 的版本: +Check the version of `uv`: ```bash uv version ``` -我的输出是: +My output is: + ``` uv 0.6.9 (3d9460278 2025-03-20) ``` -实操时,请保证版本不要低于我的。 +Make sure your version is not lower than mine when operating. -## 创建一个最简单的 mcp 服务器 +## Creating the Simplest MCP Server -我们进入工程目录,准备创建一个最简单的 mcp 服务器。 +Let's enter the project directory and create the simplest MCP server. ```bash mkdir -p ~/codes/my-first-mcp @@ -43,31 +40,32 @@ cd ~/codes/my-first-mcp uv init --no-workspace ``` -此时,你的项目里面应该有这三个文件: +At this point, your project should have the following three files: ``` README.md main.py pyproject.toml ``` -然后,我们在当前文件夹打开 vscode 或者 trae,我们创建一个最简单的 mcp 服务器,它的功能是: -- 提供一个名为 add 的工具,用于对两个数字进行加法 -- 提供一个名为 greeting 的资源,用于返回一个 greeting 消息 +Then, open the current folder in vscode or trae. We’ll create the simplest MCP server, which will: -先安装 mcp 相关的库: +* Provide a tool named `add` for adding two numbers. +* Provide a resource named `greeting` to return a greeting message. + +First, install the necessary MCP libraries: ```bash uv add mcp "mcp[cli]" ``` -修改 `main.py` 内容如下: +Edit the content of `main.py` as follows: ```python from mcp.server.fastmcp import FastMCP -mcp = FastMCP('锦恢的 MCP Server', version="11.45.14") +mcp = FastMCP('JinHui’s MCP Server', version="11.45.14") @mcp.tool( name='add', - description='对两个数字进行实数域的加法' + description='Addition of two numbers in the real domain' ) def add(a: int, b: int) -> int: return a + b @@ -75,66 +73,62 @@ def add(a: int, b: int) -> int: @mcp.resource( uri="greeting://{name}", name='greeting', - description='用于演示的一个资源协议' + description='A resource protocol for demonstration purposes' ) def get_greeting(name: str) -> str: return f"Hello, {name}!" @mcp.prompt( name='translate', - description='进行翻译的prompt' + description='A prompt for translation' ) def translate(message: str) -> str: - return f'请将下面的话语翻译成中文:\n\n{message}' + return f'Please translate the following sentence into Chinese:\n\n{message}' ``` -## 使用 OpenMCP 一键连接 +## One-Click Connection Using OpenMCP -如上,我们申明了三个函数,用作 mcp 的 tool,resource 和 prompt。在 OpenMCP 中启动它们非常简单,点击右上角的 OpenMCP 图标即可连接: +As mentioned above, we declared three functions as MCP tools, resources, and prompts. It’s very easy to start them in OpenMCP. Simply click the OpenMCP icon at the top right to connect: ![](./images/connect-simple.png) -初次使用 OpenMCP,会出现引导界面,还希望阁下可以耐心看完。 +The first time you use OpenMCP, a guide will appear. We hope you can take a moment to read it carefully. ![](./images/guide.png) -如果登录完成后,如图显示连接成功,则代表当前已经成功启动并连接 mcp 服务器。 +Once you log in and see the "Connection Successful" message as shown in the image, this indicates that the MCP server has been successfully started and connected. ![](./images/connect-success.png) -恭喜您,万事开头难,您已经完成了最难的 mcp 连接! +Congratulations, the hardest part is over — you've successfully made the MCP connection! -有关 openmcp 进行 mcp 服务器连接的更多信息,可以参考手册里面的这一章 [[connect-mcp|连接到 MCP 服务器]]。 +For more information about connecting to an MCP server using OpenMCP, you can refer to the chapter [[connect-mcp|Connecting to an MCP Server]] in the manual. -## 附录:关于 uv 启动 mcp 你必须知道的 +## Appendix: What You Need to Know About Starting MCP with `uv` -OpenMCP 已经帮你做好了很多事情,但是使用 uv 启动 mcp 服务器其实是不只一种方法的,了解更加底层的原理有助于您以不变应万变。因为 OpenMCP 对于 python 项目默认运行 `uv run mcp run main.py` 来启动 mcp 服务器,但是 GitHub 上的部分项目无法这么启动。 +OpenMCP has already done a lot of work for you, but there are actually multiple ways to start an MCP server with `uv`. Understanding the underlying principles will help you adapt to various situations. While OpenMCP uses `uv run mcp run main.py` to start the MCP server for Python projects, some GitHub projects may not work in this way. -先了解一下对于上面那个例子的 python 代码,应该如何通过命令行启动 mcp 吧! +Let’s first learn how to start the MCP server from the command line using the Python code in the above example! -### 方法一:使用 mcp-cli +### Method 1: Using `mcp-cli` -mcp 本身提供了脚手架,可以直接启动一段被申明的 python 代码,作为一个 mcp 服务器。使用如下代码运行它: +MCP itself provides a scaffolding tool that can directly start the declared Python code as an MCP server. Use the following command to run it: ```bash uv run mcp run main.py ``` -### 方法二:在代码中显式启动 +### Method 2: Explicitly Start in the Code -你也可以在代码中显式启动 mcp 服务器,在 `main.py` 的结尾添加: +You can also explicitly start the MCP server within the code by adding the following to the end of `main.py`: ```python if __name__ == '__main__': mcp.run() ``` -然后运行如下代码即可启动 mcp 服务器: +Then run the following command to start the MCP server: ```bash uv run main.py ``` - -:::warning -请不要运行 python main.py,因为 uv run 会使用当前虚拟环境的库,这些库在外部 python 看来是不可见的。也不要在没有使用 `mcp.run()` 启动代码的情况下就直接使用 uv run main.py,我们之前的代码只是申明了函数,并没有实际上执行任何功能。 -::: diff --git a/plugin-tutorial/quick-start/images/.DS_Store b/plugin-tutorial/quick-start/images/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/plugin-tutorial/quick-start/images/.DS_Store differ diff --git a/plugin-tutorial/quick-start/index.md b/plugin-tutorial/quick-start/index.md index c09f987..eb52ca2 100644 --- a/plugin-tutorial/quick-start/index.md +++ b/plugin-tutorial/quick-start/index.md @@ -1,11 +1,9 @@ -# 快速开始 - -1. [[acquire-openmcp|获取 OpenMCP]] - -2. [[first-mcp|你的第一个 MCP]] -3. [[quick-debug|快速调试 MCP]] -4. [[put-into-llm|扔进大模型里面测测好坏!]] +# Quick Start +1. [[acquire-openmcp|Get OpenMCP]] +2. [[first-mcp|Your First MCP]] +3. [[quick-debug|Quick Debugging of MCP]] +4. [[put-into-llm|Throw it into a Large Model to Test Its Effectiveness!]]

diff --git a/plugin-tutorial/quick-start/put-into-llm.md b/plugin-tutorial/quick-start/put-into-llm.md index d544523..3a8d2fa 100644 --- a/plugin-tutorial/quick-start/put-into-llm.md +++ b/plugin-tutorial/quick-start/put-into-llm.md @@ -1,65 +1,67 @@ -# 扔进大模型里面测测好坏! -在 [[quick-debug|之前的章节]] 中,我们成功完成了 mcp 服务器的连接和各个功能的调试,也算是带大家认识了一下 openmcp 的基本调试功能。接下来,我们需要把 mcp 放到大模型环境中来测试,毕竟,mcp 提出的初衷就是为了让大家可以低成本把自己写的功能接入大模型中。 -在正式进行对话前,还请大家先参照 [[connect-llm|连接大模型]] 来完成大模型 API 的配置,并测试完成你的大模型服务是否可用。 +# Throw It into a Large Model to Test Its Effectiveness! -## 和大模型进行对话 +In the [[quick-debug|previous chapter]], we successfully connected to the MCP server and debugged its various features—giving us a good overview of OpenMCP’s basic debugging capabilities. Now, it’s time to place the MCP into a large language model (LLM) environment to test it. After all, the primary goal of MCP is to make it easy and low-cost to integrate your own functionalities into an LLM. -我们先创建一个新的调试项目,选择「交互测试」,就可以进入一个和大模型对话的窗口。OpenMCP 提供的对话窗口的基本介绍如下: +Before we begin the actual interaction, please follow the instructions in [[connect-llm|Connecting to a Large Model]] to configure your LLM API and ensure your LLM service is working correctly. + +## Talking to a Large Model + +Let’s start by creating a new debug project and choosing **“Interactive Test”**, which opens a chat window for interacting with the LLM. Here’s a basic overview of the chat window provided by OpenMCP: ![](./images/llm-intro.png) -上面标定了几个比较重要的按钮,初次使用,您可以直接使用默认设置。点击「使用的工具」,可以看到当前激活的工具,OpenMCP 默认激活当前连接的 mcp 服务器的所有提供的工具,如果您希望禁用某些工具,可以点击下方的「使用的工具」来选择性地禁用工具: +Several important buttons are marked in the image above. When using it for the first time, you can proceed with the default settings. By clicking “**Tools in Use**,” you’ll see the currently active tools. By default, OpenMCP activates all tools provided by the connected MCP server. If you'd like to disable any tools, you can selectively do so via the same button: ![](./images/llm-tools.png) -好啦,让我们先来看看基于 mcp 协议,大模型会如何调用我们的工具吧,保持默认设置,然后询问如下问题:请帮我计算一下 123 + 1313 等于多少 +Alright, let’s test how the large model calls our tools via the MCP protocol. Keep the default settings, and ask this question: Can you help me calculate 123 + 1313? -输入后回车等待结果,可以看到如下的输出: +Press enter and wait for the result. You should see something like this: ![](./images/llm-calc.png) -可以看到大模型选择使用了我们提供的工具 add 完成了上述的加法,OpenMCP 中你能看到大模型是如何调用每一个工具的和工具的返回结果。目前我们问的问题和 mcp 提供的工具都比较简单,对于复杂问题,大模型有可能会在一轮内同时调用多个工具来完成特定的任务,如果你希望大模型每次都只使用一个工具,可以点击下方的默认亮着的「允许模型在单轮回复中调用多个工具」 来禁用这个特性。 +As you can see, the LLM chose to use the `add` tool we provided to perform the addition. OpenMCP also shows exactly how the LLM invoked the tool and the result returned by the tool. While this example is simple, for more complex queries, the LLM may call multiple tools in a single round to complete a task. If you want the model to only use one tool per response, you can disable the default setting **“Allow multiple tools per turn”** by clicking the toggle button below. +## System Prompts -## 系统提示词 +For special cases—such as [bing-images](/Users/bytedance/projects/openmcp-tutorial/bing-images), an MCP server that returns Bing images based on keywords—you may need to guide the model on how to format its output. -对于一些特殊的情况,比如 [bing-images](/Users/bytedance/projects/openmcp-tutorial/bing-images),这是一个根据关键词来返回 bing 图片的 mcp 服务器。 +Try asking: Can you help me search for some Arknights images? -我们直接询问如下的问题:请帮我搜索几张明日方舟的图片,默认情况下,你有可能会得到如下的回复: +By default, you might get a response like this: ![](./images/bing-image-common.png) -大模型将得到的图片以链接的形式返回了,但是有的时候其实我希望是返回成图片的形式渲染在屏幕中让我看到的,为了约束和引导大模型返回文本的风格、或是按照我们要求的模板进行返回,我们可以通过设置系统提示词的方式来实现。 +Here, the LLM returns image links. However, what we really want is for the images to be displayed directly on the screen. To instruct the LLM to return Markdown-style image outputs, we can use a **system prompt**. -我们先点击下方的「系统提示词」 +Click on the “**System Prompt**” button below: ![](./images/system-prompt-add.png) -我们添加一个新的系统提示词,在标题输入「bing image」,然后主体部分填入: +Add a new system prompt with the title **"bing image"** and the content: ``` -你是一个擅长找 bing 图片的 AI,当你找到图片时,你应该返回图片形式的 markdown,比如 ![](https://xxxx.xx/xxxx) +You are an AI skilled at finding Bing images. When you find images, you should return them in Markdown image format, e.g., ![](https://xxxx.xx/xxxx) ``` -点击保存。 +Click save: ![](./images/system-prompt-image.png) -然后将光标移动到第一个用户对话框上,此时会显示几个按钮,选择重新运行的按钮,openmcp 便会重新执行此处的对话。 +Next, move your cursor to the first user message box. Several buttons will appear—click the **Re-run** button to re-execute that conversation turn: ![](./images/rerun-bing-image.png) -可以看到此时,图片就被正常渲染出来了: +Now you should see the images rendered correctly: ![](./images/llm-bing-image-render.png) +For more tips on using system prompts or other advanced techniques to control the behavior of agents, see [[go-neo4j-sse|Building a Read-only Neo4j MCP Server with Go (SSE)]]. -关于更多使用 system prompt 或者其他更加精准的方法来控制 agent 的技巧,可以移步 [[go-neo4j-sse|go 实现 neo4j 的只读 mcp 服务器 (SSE)]] +## Conclusion -## 结语 +Great job! You've completed the basic OpenMCP tutorial. Now it’s time to build something fun and meaningful. Check out the [[mcp-examples|MCP Server Development Examples]] for more use cases and inspiration. -很好!你完成了 openmcp 的基础教程,接下来,该去做点有趣的事情了!在 [[mcp-examples|MCP 服务器开发案例]] 中,你能找到更多的使用 openmcp 进行 mcp 服务器开发的例子。 - -遍地惊喜,任君自取。 \ No newline at end of file +The world is full of surprises—take your pick! diff --git a/plugin-tutorial/quick-start/quick-debug.md b/plugin-tutorial/quick-start/quick-debug.md index 5d4cf20..d609e7b 100644 --- a/plugin-tutorial/quick-start/quick-debug.md +++ b/plugin-tutorial/quick-start/quick-debug.md @@ -1,49 +1,51 @@ -# 快速调试 MCP +# Quick Debugging of MCP -在 [[first-mcp|你的第一个 MCP]] 中,我们成功创建了一个 MCP 服务器的最小实例,并且成功使用 openmcp 连接了这个服务器。 +In [[first-mcp|Your First MCP]], we successfully created a minimal MCP server instance and connected to it using OpenMCP. -接下来,我们可以来调试这个服务器的功能了,毕竟,不是所有人都是 Jeaf Dean,都能一次就写对所有代码。我们写的 MCP 服务器也不总是一开始就自信满满可以上线的,它总是存在着一些我们无法发现的问题。试想一下,如果后面我们把 mcp 连接到大模型进行全链路调试时出了问题,这个时候你就会发现,可能出错的环节非常多:MCP 服务器的问题?大模型厂商的问题?OpenMCP 的问题?把可能的错误进行分类,然后逐一排查,才是符合工程直觉 (Engineering Instuition) 的做法。 +Now, it’s time to debug the server's functions. After all, not everyone is Jeff Dean—we don’t always get everything right on the first try. MCP servers are rarely production-ready the moment they're written; there are often hidden issues. Imagine debugging a full-chain setup where your MCP is connected to a large model—if something goes wrong, there are many possible failure points: is it the MCP server? The LLM vendor? OpenMCP itself? Categorizing potential errors and inspecting them one by one is the right way to go—this is **engineering intuition**. -## 认识面板 +## Getting to Know the Panel -首次进入 openmcp 时,会进入一个面板,上面一共四个按钮,代表四种调试项目: +When you first open OpenMCP, you’ll land on a dashboard with four buttons, each representing a different type of debugging project: ![](./images/openmcp-home.png) -我们现在需要确认的是 tool,resource 和 prompt 这三个功能是否运行正常,因为在实际项目中,tool 是使用得最多的项目,因此,我们先调试 tool。 +Right now, we need to verify that the **tool**, **resource**, and **prompt** functions are working properly. Since **tool** is the most frequently used in real projects, we’ll start by debugging it. -## 调试 Tool +## Debugging Tools -为了调试 tool,我们点击面板上的 「工具」 按钮,进入 tool 调试界面。tool 面板的基本介绍如下所示 +To debug a tool, click the “Tool” button on the dashboard to enter the tool debugging interface. Here's a basic overview of the tool panel: ![](./images/tool-desc.png) -调试工具,我们需要先在「工具列表」中选择一个工具(如果没有展开需要先展开工具列表,点击右侧的按钮可以刷新),然后在右侧的「参数填写和执行」中,填写需要测试的参数,点击运行,就能看到结果了: +To debug, first select a tool from the “Tool List” (click the right-side button to expand or refresh the list if needed). Then, on the right side under “Parameter Input & Execution,” enter test parameters and click **Run** to see the result: ![](./images/tool-result.png) -比如我们这边运算最简单的 2 + 2,可以看到结果是 4,这说明我们的 mcp 连接正常还可以正常返回结果。大家未来可以通过简单测试来验证 mcp 服务器的可用性,这在复杂 agent 系统的调试过程中非常重要。可以编码成自检程序的一部分。 +For example, testing `2 + 2` yields the result `4`, confirming that our MCP is connected properly and functioning correctly. You can use simple tests like this to verify your server’s availability—this becomes especially important when debugging complex agent systems, and can even be scripted as part of an automated self-check. -## 添加测试项目 +## Adding a Test Project -测试完成一个项目后,我们可以通过点击上方的 + 来添加额外的测试项目: +After testing one feature, you can add more test cases by clicking the **+** icon at the top: ![](./images/tool-add-test-project.png) -这里,我们选择「资源」来进行资源项目的调试工作,「资源」和另外两个项目有点不一样,MCP 协议中的资源访问有两种类型 +Here, select **Resource** to start debugging resources. Note that MCP protocol has two types of resource access: -- resources/templates/list: 模板资源,带有访问参数,比如文件系统 mcp 中的文件访问,输入文件路径,根据资源协议返回文件内容。 -- resources/list:普通资源,不带访问参数,比如浏览器 mcp 中的 console,直接返回控制台的 stdio,这种就不需要参数。 +* `resources/templates/list`: **Template resources**, which accept parameters (e.g., file path) and return resource content accordingly—like a file system MCP. +* `resources/list`: **Basic resources**, which do not accept parameters and simply return static or live data—like the browser console MCP that returns current stdout. ![](./images/resource-desc.png) - -`resources/templates/list` 的使用方法和之前的 tool 一样,填入参数点击运行就能看到资源结果 +Using `resources/templates/list` is similar to debugging tools—just enter the parameter and click **Run** to view the output: ![](./images/resource-result.png) -而 `resources/list` 由于没有参数,直接点击左侧的资源就能直接看到内部的数据。 +For `resources/list`, since no parameters are required, just click on the resource name in the list to view its data. -## 总结 +## Summary + +In this chapter, we covered how to use OpenMCP to debug an MCP server, including how to test **tools** and **resources**. The process for debugging **prompts** is similar, so feel free to try it on your own. + +In the next chapter, we’ll move on to the most exciting part—testing your MCP server with a large model to see whether what you've built is truly fun and valuable. -在这一章节中,我们主要介绍了如何使用 openmcp 来调试 MCP 服务器,包括如何调试 tool 和 resource,prompt 的方法和这两个类似,大家可以自行尝试。下一章中,我们将开启最激动人心的一章,我们将把开发的 mcp 服务器扔到大模型中进行测试,这样你才知道你写的 mcp 是不是真的好玩,是不是有价值。 diff --git a/plugin-tutorial/usage/connect-llm.md b/plugin-tutorial/usage/connect-llm.md index 8647399..70b2bb0 100644 --- a/plugin-tutorial/usage/connect-llm.md +++ b/plugin-tutorial/usage/connect-llm.md @@ -1,69 +1,65 @@ -# 连接大模型 +# Connecting to LLMs (Large Language Models) -如果需要使用「交互测试」来在和大模型的交互中测试 MCP 工具的性能,你需要首先需要在 OpenMCP 配置大模型。 +To use the **"Interactive Testing"** feature for evaluating your MCP tools in conversations with an LLM, you’ll first need to configure an LLM in OpenMCP. -:::warning 协议兼容性警告 -目前 OpenMCP 只支持符合 OpenAI 接口规范的 大模型服务,其他大模型的调用需要请通过 [newApi](https://github.com/QuantumNous/new-api) 进行转发或者自行实现。 +:::warning Compatibility Notice +Currently, OpenMCP only supports LLM services that are **OpenAI API-compatible**. To use other types of LLMs, you can use a proxy like [newApi](https://github.com/QuantumNous/new-api) or implement your own adapter. -目前市面上主流的如下模型我们都是支持的,如果遇到大模型连接的问题,请随时 [[channel|联系我们]]。 +We support most mainstream LLMs out of the box. If you encounter issues connecting to an LLM, feel free to [[channel|contact us]]. ::: -在 「设置」-「API」 可以进入大模型的连接配置界面。 +To configure an LLM, go to **Settings → API**: ![](./images/setting-api.png) -## 默认支持的模型 +## Supported Models (by default) -OpenMCP 默认填充了市面上常见的大模型,下面是支持的模型 +OpenMCP comes pre-configured with commonly used LLM services. Here is a list of supported models: -| 大模型 Name | 提供商 | baseUrl | 默认模型 | -|----------------------|---------------------------|---------------------------------------------|-----------------------| -| DeepSeek | DeepSeek | `https://api.deepseek.com/v1` | `deepseek-chat` | -| OpenAI | OpenAI | `https://api.openai.com/v1` | `gpt-4-turbo` | -| 通义千问 Qwen | Alibaba | `https://dashscope.aliyuncs.com/compatible-mode/v1` | `qwen-plus` | -| 豆包 Seed | ByteDance | `https://ark.cn-beijing.volces.com/api/v3` | `doubao-1.5-pro-32k` | -| Gemini | Google | `https://generativelanguage.googleapis.com/v1beta/openai/` | `gemini-2.0-flash` | -| Grok | xAI | `https://api.x.ai/v1` | `grok-3-mini` | -| Mistral | Mistral AI | `https://api.mistral.ai/v1` | `mistral-tiny` | -| Groq | Groq | `https://api.groq.com/openai/v1` | `mixtral-8x7b-32768` | -| Perplexity | Perplexity AI | `https://api.perplexity.ai/v1` | `pplx-7b-online` | -| Kimi Chat | 月之暗面 (Moonshot AI) | `https://api.moonshot.cn/v1` | `moonshot-v1-8k` | +| LLM Name | Provider | baseUrl | Default Model | +| --------------------- | ------------- | ---------------------------------------------------------- | -------------------- | +| DeepSeek | DeepSeek | `https://api.deepseek.com/v1` | `deepseek-chat` | +| OpenAI | OpenAI | `https://api.openai.com/v1` | `gpt-4-turbo` | +| Qwen (Tongyi Qianwen) | Alibaba | `https://dashscope.aliyuncs.com/compatible-mode/v1` | `qwen-plus` | +| Doubao Seed | ByteDance | `https://ark.cn-beijing.volces.com/api/v3` | `doubao-1.5-pro-32k` | +| Gemini | Google | `https://generativelanguage.googleapis.com/v1beta/openai/` | `gemini-2.0-flash` | +| Grok | xAI | `https://api.x.ai/v1` | `grok-3-mini` | +| Mistral | Mistral AI | `https://api.mistral.ai/v1` | `mistral-tiny` | +| Groq | Groq | `https://api.groq.com/openai/v1` | `mixtral-8x7b-32768` | +| Perplexity | Perplexity AI | `https://api.perplexity.ai/v1` | `pplx-7b-online` | +| Kimi Chat | Moonshot AI | `https://api.moonshot.cn/v1` | `moonshot-v1-8k` | +## Configuring the LLM -## 配置大模型 - -你需要做的只是把对应服务商的 apiToken 填入 openmcp 中即可。然后点击「测试」,看到下面的响应说明连接成功。您就可以在交互测试里面使用大模型了! +All you need to do is enter the corresponding `apiToken` for your provider. Then click the **Test** button. If the connection is successful, you’ll see a response like this: ![](./images/setting-api-test.png) -:::warning -有些用户会遇到无法访问的问题,请确保你的 baseUrl 填写正确。如果在国内使用某些国外厂商的服务,比如 gemini,openai,请确保你的网络环境可以访问到这些服务。在 「设置」-「通用」中你可以设置代理服务器。 -::: +## Adding More Models +If the model you want to use is not in the default list, you can add it in two ways: -## 添加模型 +### Method 1: Update Model List Automatically -如果你想使用的指定服务商的模型不在默认支持的模型中,有两种方法可以添加它们。 - -### 方法一:更新模型列表 - -此处以通义千问为例子,确保在 apitoken 填写正确的情况下,点击「更新模型列表」,如果服务商严格实现了 openai 标准,那么就能看到所有更新的模型了。 +Using Qwen as an example, after entering a valid `apiToken`, click **Update Model List**. If the provider strictly follows the OpenAI API standard, all available models will be fetched automatically. ![](./images/setting-update-models.png) -### 方法二:手动添加模型 +### Method 2: Manually Add Model -如果你的服务器没有支持 openai 标准,你将无法使用「方法一」,你可以如此手动添加模型列表。此处以 Grok 为例,在服务商中找到 grok,点击图中所示的编辑 +If your server doesn’t follow the OpenAI standard, you won’t be able to use method 1. You can instead manually add models. For example, to add a Grok model: + +1. Locate **Grok** under providers and click **Edit**: ![](./images/setting-api-edit.png) -点击模型,输入模型名称,回车,点击确认: +2. Click on the **Models** section, type the model name, press Enter, then click **Confirm**: ![](./images/setting-api-edit-1.png) +3. Return to the API page and click **Save**. -回到 api 页面,再点击保存。 +## Adding Custom Services -## 添加服务 +If the provider you want to use is not listed (e.g., a self-hosted model or a new cloud service), you can add it using the **Add Service** button. The process is similar to **Manual Model Addition** and won’t be repeated here. -如果你要的服务商没有出现我们的列表中(云服务商的服务,或者自己部署的服务),可以通过「添加服务」按钮来添加自定义模型,使用方法和「添加模型」「方法二:手动添加模型」类似,就不赘述了。 diff --git a/plugin-tutorial/usage/connect-mcp.md b/plugin-tutorial/usage/connect-mcp.md index c74bec9..ee91635 100644 --- a/plugin-tutorial/usage/connect-mcp.md +++ b/plugin-tutorial/usage/connect-mcp.md @@ -1,77 +1,66 @@ -# 连接 mcp 服务器 +# Connecting to an MCP Server -不同于 Claude Desktop 和其他的 MCP 客户端类产品,OpenMCP 进行 MCP 服务器连接的步骤是相当丝滑的。 +Unlike Claude Desktop or other MCP client products, OpenMCP offers a **much smoother process** for connecting to MCP servers. -:::info MCP客户端 -MCP 客户端是指能够通过 MCP 协议进行通信的大模型对话客户端,通常是一个运行在本地的应用程序(因为网页没有文件IO的权限)。它的产品形式目前几乎都是聊天机器人的形式,类似于你在网页使用的 chat.deepseek.com 或者 chat.openai.com +:::info What is an MCP Client? +An **MCP client** is a local application that communicates using the MCP protocol, typically in the form of a chatbot interface (similar to chat.deepseek.com or chat.openai.com). It's usually run as a local application because web pages lack direct file I/O permissions. ::: -首先,打开你的 VLE,在 [[acquire-openmcp|获取 OpenMCP]] 中完成 OpenMCP 的安装后,我们先用 python 创建一个最简单的 mcp 服务器,来测试 mcp 客户端的连接。 +To get started, open your VLE (Visual Language Environment). After installing OpenMCP following the steps in [[acquire-openmcp|Getting OpenMCP]], let's use Python to create a simple MCP server and test the connection. +## One-Click Connection with OpenMCP -## 使用 OpenMCP 一键连接 - -在 [[first-mcp|你的第一个 MCP]] 这个例子中,我们申明了三个函数,用作 mcp 的 tool,resource 和 prompt。在 OpenMCP 中启动它们非常简单,点击右上角的 OpenMCP 图标即可连接: +In the [[first-mcp|Your First MCP]] example, we defined three functions as MCP `tool`, `resource`, and `prompt`. Launching them in OpenMCP is super easy—click the OpenMCP icon in the top-right corner: ![](./images/connect-simple.png) - -如果登录完成后,如图显示连接成功,则代表当前已经成功启动并连接 mcp 服务器。 +Once logged in, if you see a success message like below, your MCP server is now running and connected properly: ![](./images/connect-success.png) -## STDIO 连接的启动 +## STDIO Connection (Auto-Start) -对于 STDIO 为连接选项的开发方案,我们提供了一键式的快速启动,您不需要额外启动 mcp 的进程。OpenMCP 会自动连接和销毁。 +If you're using **STDIO** as the connection method, OpenMCP offers one-click auto-start support. You don't need to manually run the MCP process—OpenMCP will handle launching and shutting it down. -目前支持的编程语言和它们对应的启动参数为: +Currently supported languages and their launch configurations: -|语言|连接参数|启动目录| -|:-|:-|:-| -|python|uv run mcp run $\{file\} | 往上追溯,第一个找到的 pyproject.toml 的目录| -|nodejs|node $\{file\}| 往上追溯,第一个找到的 package.json 的目录| -|go|go run $\{file\}| 往上追溯,第一个找到的 go.mod 的目录| +| Language | Launch Command | Root Directory Determination | +| -------- | ------------------------ | ----------------------------------------- | +| Python | `uv run mcp run ${file}` | First parent folder with `pyproject.toml` | +| Node.js | `node ${file}` | First parent folder with `package.json` | +| Go | `go run ${file}` | First parent folder with `go.mod` | -## SSE & Streamable HTTP 连接的启动 +## SSE & Streamable HTTP Connection -对于 SSE 和 Streamable HTTP 这两种远程连接的方式,由于我们并不知道您到底在哪个端口启动的服务器(因为你有可能把启动的 host 和 port 写在不可见的配置文件里或者写在环境变量里),因此,对于远程连接的情况,我们不支持自动创建服务器,您需要手动配置启动选项。 +For **SSE (Server-Sent Events)** and **Streamable HTTP** remote connections, OpenMCP can’t auto-start your server since the port and host may be defined in hidden config files or environment variables. In these cases, **you must manually configure the connection.** -点击 VLE 左侧插件栏目的 OpenMCP,在 「MCP 连接(工作区)」 视图中,点击 + ,就可以创建一个新的连接。 +To do this, open the **OpenMCP** plugin from the left sidebar in your VLE and go to **"MCP Connections (Workspace)"**. Click the "+" to create a new connection: ![](./images/add-connection.png) - - -选择你需要的通信方式。 +Select the type of communication method: ![](./images/select-server-type.png) -输入MCP Server的地址。 +Then input your MCP Server's endpoint address: ![](./images/connect-sse.png) -:::info -需要注意的是,不同的通信方式一般使用不同endpoint,目前的MCP server大多遵循下面的原则: +## OpenMCP Plugin Control Panel -如果是以 SSE 启动,那么默认使用 /sse 作为endpoint,比如 http://localhost:8001/sse - -如果是以 Streamable Http 启动,那么默认使用 /mcp 作为endpoint,比如 http://localhost:8001/mcp - -当然,允许MCP Server使用两个不同的endpoint同时支持两种连接方式,这对于想要迁移到Streamable Http但短时间又不能放弃SSE的情况特别有效 -::: - -## openmcp 插件的控制面板 - -在 VLE 的左侧可以找到 openmcp 的图标,点击后就是 openmcp 的控制面板。 +On the left sidebar of your VLE, you’ll find the OpenMCP icon. Clicking it opens the control panel: ![](./images/openmcp-control-panel.png) -当前工作区曾经连接过的 mcp 服务器会出现在这里,这是因为 openmcp 默认将工作区启动的 mcp 的连接信息存储在了 `.vscode/tabs.{server-name}.json` 中,其中 `{server-name}` 就是 mcp 服务器连接成功的服务器名称。 +Previously connected MCP servers for the current workspace will be listed here. That’s because OpenMCP stores connection details in files like: -:::warning -注意,同一个项目中,你不应该有两个名字完全相同的 mcp 服务器,这会导致 `.vscode/tabs.{server-name}.json` 连接信息存储冲突,发生未知错误。 -::: +``` +.openmcp/tabs.{server-name}.json +``` -如果你想要在任意工作区都能使用同一个 mcp 服务器,可以考虑在「安装的 MCP 服务器」中添加成熟耐用的 mcp 服务器,这个位置添加的 mcp 服务器全局可用。 +Where `{server-name}` is the name of the connected MCP server. + +If you'd like to use an MCP server **across all workspaces**, consider adding it to the **"Installed MCP Servers"** section—those servers are globally accessible. + +Lastly, check out the **"Getting Started & Help"** section for reference materials to guide your journey. -在「入门与帮助」中,我们准备了一些可供入门的参考资料,还请阁下善加利用。 diff --git a/plugin-tutorial/usage/debug.md b/plugin-tutorial/usage/debug.md index 50359db..7e78bae 100644 --- a/plugin-tutorial/usage/debug.md +++ b/plugin-tutorial/usage/debug.md @@ -1,68 +1,70 @@ -# 调试 tools, resources 和 prompts +# Debugging Tools, Resources, and Prompts -## 标签页 +## Tabs -openmcp 以标签页作为调试项目的最小单元,点击栏目中的 + 可以创建新的标签页。OpenMCP 的 tools, resources 和 prompts 的基本使用与 Inspector 差不多,但是 OpenMCP 会自动帮您完成左侧资源列表的初始化,Inspector 中这一步需要手动完成。 +OpenMCP uses tabs as the smallest unit for debugging items. Click the "+" button in the navigation bar to create a new tab. The basic usage of Tools, Resources, and Prompts in OpenMCP is similar to the Inspector, but OpenMCP automatically initializes the resource list on the left—a step that requires manual effort in the Inspector. -## 调试内容的自动保存 +## Auto-Saving Debug Content -openmcp 具备自动保存测试结果的功能。如下的行为会触发 openmcp 对标签页及其内容进行保存: +OpenMCP features automatic saving of test results. The following actions will trigger OpenMCP to save the tab and its contents: -- 创建标签页,并选择一个有效的调试项目 -- 在调试页进行调试行为(选择工具,执行工具,询问大模型等) +- Creating a tab and selecting a valid debugging item. +- Performing debugging actions on the debug page (selecting tools, executing tools, querying the large model, etc.). -当前 mcp 项目的测试数据会被保存在 `.vscode/tabs.{server-name}.json` 中,其中 `{server-name}` 就是 mcp 服务器连接成功的服务器名称。 +The test data for the current MCP project is saved in `.openmcp/tabs.{server-name}.json`, where `{server-name}` corresponds to the successfully connected server's name. :::warning -注意,同一个项目中,你不应该有两个名字完全相同的 mcp 服务器,这会导致 `.vscode/tabs.{server-name}.json` 连接信息存储冲突,发生未知错误。 +Note: Within the same project, you should not have two MCP servers with identical names. This will cause conflicts in the `.openmcp/tabs.{server-name}.json` connection information storage, leading to undefined errors. ::: -## 快速调试 +## Quick Debugging -在我们调试的过程中,难免会出现大模型回答得不好,而且这是因为某个工具出错导致的,为了快速定位是不是工具的问题,可以点击下方的小飞机图标 +During debugging, it's common for the large model to provide unsatisfactory responses due to tool errors. To quickly identify whether a tool is the culprit, click the small airplane icon at the bottom: ![](./images/llm-fast-debug.png) -点击后,OpenMCP 会一个新的测试 tool 的项目,并自动把当时大模型使用的参数自动填充到右侧的表单中: +After clicking, OpenMCP will create a new test tool project and automatically populate the form on the right with the parameters the large model used: ![](./images/llm-fast-debug-result.png) -你要做的,只是点击运行来确定或者排除一个错误选项。 +All you need to do is click "Run" to confirm or rule out a potential error. -## pydantic 支持 +## Pydantic Support -使用 python 的 fastmcp 进行 tool 的创建时,你有两种方法来申明接口的类型,一种是通过 python 默认的 typing 库来申明复杂数据结构,或者通过 pydantic 来申明一个复杂的变量,下面是一个例子: +When creating tools using Python's `fastmcp`, you have two ways to declare interface types: + +1. Using Python's built-in `typing` library for complex data structures. +2. Using `pydantic` to define complex variables. Here's an example: ```python from mcp.server.fastmcp import FastMCP from pydantic import BaseModel, Field from typing import Optional, Union, List, NamedTuple -mcp = FastMCP('锦恢的 MCP Server', version="11.45.14") +mcp = FastMCP('Jinhui's MCP Server', version="11.45.14") class PathParams(BaseModel): start: str end: str -@mcp.tool(name="test",description="用来测试") +@mcp.tool(name="test", description="Test tool") def test( params: PathParams, test1: str, - test2: Union[str, List[str]] = Field("", description="测试参数2"), - test3: Optional[str] = Field(None, description="测试参数3") + test2: Union[str, List[str]] = Field("", description="Test parameter 2"), + test3: Optional[str] = Field(None, description="Test parameter 3") ): return [test1, test2, test3, params] ``` -由于我们对这两种类型的申明方式实现了内部的转换,所以 openmcp 都是支持的。值得一提的是,如果你申明的变量是一个对象,比如上面的 `PathParams`,那么 openmcp 的 tool 调试窗口会生成一个「对象输入框」,这个输入框支持基本的格式检查和自动补全: +Since we've implemented internal conversion for both declaration methods, OpenMCP supports both. Notably, if you declare a variable as an object (e.g., `PathParams` above), OpenMCP's tool debug window will generate an "Object Input Field" with basic format validation and auto-completion: ![](./images/object-input.png) -:::info 什么是对象? -这里的「对象」是 javascript 中的概念,它指的是可被序列化的数据类型中,除去基本数据类型后,剩下的那部分。比如 { "name": "helloworld" } 就是一个对象。对象在 python 中更加类似于一个 dict 或者 namedTuple。 +:::info What is an "object"? +Here, an "object" refers to the JavaScript concept—serializable data types excluding primitives. For example, `{ "name": "helloworld" }` is an object. In Python, this is more akin to a `dict` or `namedTuple`. ::: :::warning -虽然 openmcp 已经做到了尽可能多情况的支持,但是生产场景中,我们仍然不建议您将 mcp tool 的参数定义成对象,尽可能定义成简单数据类型也能更好提高大模型进行工具调用时的稳定性。 -::: - +Although OpenMCP supports most use cases, we still recommend avoiding object-type parameters for MCP tools in production. Using simple data types improves the stability of tool calls by the large model. +::: \ No newline at end of file diff --git a/plugin-tutorial/usage/distribute-result.md b/plugin-tutorial/usage/distribute-result.md index 1c33557..6ece1c4 100644 --- a/plugin-tutorial/usage/distribute-result.md +++ b/plugin-tutorial/usage/distribute-result.md @@ -1 +1,59 @@ -# 分发您的实验结果 \ No newline at end of file +# Distributing Your Experiment Results + +## Tab Restoration + +OpenMCP automatically saves your experiment results in real-time. For each active server in the workspace, results are stored in `.openmcp/tabs.{server-name}.json`, where `{server-name}` matches the successfully connected MCP server's name. + +Ensure your `.gitignore` file doesn't contain rules excluding the `.openmcp` folder. This way, when you: +- Commit code via Git +- Manage agent code +- Clone the project on another machine +- Share the project with others + +...you can instantly restore your previous experiment context to continue development or debugging. + +## Connection Recovery + +Each MCP server's connection details are saved in `.openmcp/connection.json`. Example: + +```json +{ + "items": [ + [ + { + "connectionType": "STDIO", + "command": "mcp", + "args": [ + "run", + "main.py" + ], + "url": "", + "cwd": "{workspace}/simple-mcp", + "oauth": "", + "clientName": "openmcp.connect.STDIO", + "clientVersion": "0.0.1", + "env": {}, + "serverInfo": { + "name": "Jinhui's MCP Server", + "version": "1.9.2" + }, + "filePath": "{workspace}/simple-mcp/main.py", + "name": "Jinhui's MCP Server", + "version": "1.9.2" + } + ] + ] +} +``` + +When you: +- Open the left control panel, or +- Access a previously connected MCP server + +OpenMCP automatically uses this information to: +1. Retrieve the workspace's server list +2. Attempt automatic reconnection + +If OpenMCP encounters initialization or save errors during connection, you can: +1. Contact OpenMCP official support, or +2. Manually manage the `.openmcp/connection.json` file \ No newline at end of file diff --git a/plugin-tutorial/usage/images/drag-to-fill.png b/plugin-tutorial/usage/images/drag-to-fill.png new file mode 100644 index 0000000..e2ce8e9 Binary files /dev/null and b/plugin-tutorial/usage/images/drag-to-fill.png differ diff --git a/plugin-tutorial/usage/multi-server.md b/plugin-tutorial/usage/multi-server.md index d3f0290..b2aa14e 100644 --- a/plugin-tutorial/usage/multi-server.md +++ b/plugin-tutorial/usage/multi-server.md @@ -1,14 +1,34 @@ -# 连接多个 MCP 服务器 +# Connecting Multiple MCP Servers -openmcp 支持连接多个 MCP 服务器。 +OpenMCP supports connecting to multiple MCP servers simultaneously. -假设你现在想要实现一个可以自动查阅资料并且整理成 word 文档的 agent,你可以这样做: +For example, if you want to create an agent that can automatically research topics and compile Word documents, you could: -1. 找到能进行网络搜索的 mcp:[crawl4ai mcp](https://github.com/LSTM-Kirigaya/openmcp-tutorial/tree/main/crawl4ai-mcp) -2. 找到能进行 word 操作的 mcp:[Office-Word-MCP-Server](https://github.com/GongRzhe/Office-Word-MCP-Server) -3. 在 openmcp 中把它们组合起来。 -4. 自动完成你的任务! +1. Find an MCP for web searching: [crawl4ai MCP](https://github.com/LSTM-Kirigaya/openmcp-tutorial/tree/main/crawl4ai-mcp) +2. Find an MCP for Word operations: [Office-Word-MCP-Server](https://github.com/GongRzhe/Office-Word-MCP-Server) +3. Combine them in OpenMCP +4. Automate your task! -假设,我们已经连接了第一个 mcp,也就是 crawl4ai mcp,我们可以添加额外的 mcp 服务器: +Assuming we've already connected the first MCP (crawl4ai), here's how to add additional servers: -![](./images/add-new-mcp.png) \ No newline at end of file +![](./images/add-new-mcp.png) + +## Method 1: Drag and Drop + +Simply shift-click and drag the MCP server file into the OpenMCP interface to auto-fill connection parameters: + +![](./images/drag-to-fill.png) + +:::warning +Auto-filled commands may not always be accurate. As mentioned in [STDIO Connection Setup](http://localhost:5173/openmcp/plugin-tutorial/usage/connect-mcp.html#stdio-%E8%BF%9E%E6%8E%A5%E7%9A%84%E5%90%AF%E5%8A%A8), please review [Appendix: What You Must Know About UV MCP Startup](http://localhost:5173/openmcp/plugin-tutorial/quick-start/first-mcp.html#%E9%99%84%E5%BD%95-%E5%85%B3%E4%BA%8E-uv-%E5%90%AF%E5%8A%A8-mcp-%E4%BD%A0%E5%BF%85%E9%A1%BB%E7%9F%A5%E9%81%93%E7%9A%84) to verify connection methods. +::: + +## Method 2: Manual Parameter Entry + +Manually enter connection parameters (self-explanatory). + +## Using Multiple Servers + +Using multiple servers works similarly to single-server operation - OpenMCP automatically handles tool routing and selection. The key requirement is that tool names must be unique across servers to prevent conflicts. + +If you have a use case requiring duplicate tool names, please describe your scenario via [GitHub issue](https://github.com/LSTM-Kirigaya/openmcp-client/issues). We'll evaluate supporting this based on community discussion. \ No newline at end of file diff --git a/plugin-tutorial/usage/sse-oauth2.md b/plugin-tutorial/usage/sse-oauth2.md index b717357..378097c 100644 --- a/plugin-tutorial/usage/sse-oauth2.md +++ b/plugin-tutorial/usage/sse-oauth2.md @@ -1,77 +1,77 @@ -# MCP Server的OAuth鉴权实现 +# Implementing OAuth Authentication for MCP Servers -在使用 **SSE** 或 **Streamable HTTP** 进行连接时,为增强安全性可为接口设计鉴权机制,MCP 官方推荐采用 OAuth 协议。下面以获取 GitHub 用户信息为例,演示如何通过 openmcp-client 完成带 OAuth 认证的接口调试。 +When using **SSE** or **Streamable HTTP** connections, implementing authentication mechanisms is recommended for enhanced security. MCP officially recommends using the OAuth protocol. This guide demonstrates how to debug OAuth-authenticated APIs using openmcp-client, using GitHub user information as an example. +## 1. Obtain GitHub OAuth Credentials +To access GitHub user APIs, you'll need to create an OAuth application: -## 1. 获取Github OAuth认证ID和secret - -由于我们使用了Github用户信息相关API,需要先获取Github OAuth应用的Client ID和Client secret。 - -先进入[Github Developers](https://github.com/settings/developers),点击`New OAuth App`新建一个OAuth APP,应用名称随便填,`Homepage URL`填写`http://localhost:8000`,`Authorization callback URL`填写`http://localhost:8000/github/callback`。然后点击`Register application`按钮,即可成功注册一个应用。 - +1. Visit [GitHub Developers](https://github.com/settings/developers) +2. Click `New OAuth App` +3. Enter any application name +4. Set `Homepage URL` to `http://localhost:8000` +5. Set `Authorization callback URL` to `http://localhost:8000/github/callback` +6. Click `Register application` ![](images/oauth-github-new-application.png) +After registration: +- Save your `Client ID` +- Click `Generate a new client secret` (note: secrets are only visible once at generation) -注册成功后,请记录`Client ID`,然后点击`Generate a new client secret`生成一个`secret`,注意secret仅在生成的时候可见。 +## 2. Configure Environment Variables -## 2. 设置环境变量 +Set your credentials as environment variables: -在获取`Client ID`和`secret`之后,需要将其设置为环境变量: - - - -::: code-group +::: code-group ```bash [bash] -export MCP_GITHUB_GITHUB_CLIENT_ID={{Client ID}} -export MCP_GITHUB_GITHUB_CLIENT_SECRET={{secret}} +export MCP_GITHUB_CLIENT_ID={{Client ID}} +export MCP_GITHUB_CLIENT_SECRET={{secret}} ``` -```bash [PowerShell] +```powershell [PowerShell] $env:MCP_GITHUB_CLIENT_ID = "your_id" $env:MCP_GITHUB_CLIENT_SECRET = "your_secret" ``` -```bash [CMD] -set MCP_GITHUB_GITHUB_CLIENT_ID={{Client ID}} -set MCP_GITHUB_GITHUB_CLIENT_SECRET={{secret}} -``` +```cmd [CMD] +set MCP_GITHUB_CLIENT_ID={{Client ID}} +set MCP_GITHUB_CLIENT_SECRET={{secret}} ::: -注意:cmd里面设置环境变量请不要加引号。 +> Note: Avoid quotes when setting variables in CMD -## 3. 克隆源码 +## 3. Clone Reference Implementation -接下来,我们需要部署带有OAuth认证的MCP服务器。可以参照[官方python案例](https://github.com/modelcontextprotocol/python-sdk/tree/main/examples/servers/simple-auth)进行。 - -需要先克隆官方python-sdk源码: +Deploy an OAuth-enabled MCP server using the [official Python example](https://github.com/modelcontextprotocol/python-sdk/tree/main/examples/servers/simple-auth): ```bash -git clone https://github.com/modelcontextprotocol/python-sdk/ # 克隆源码 -cd examples/servers/simple-auth # 进入对应的目录 +git clone https://github.com/modelcontextprotocol/python-sdk/ +cd examples/servers/simple-auth ``` -## 4. 启动MCP Server +## 4. Launch MCP Server -先根据需要创建虚拟环境安装依赖,然后可以使用`uv`运行或者直接运行`python main.py`即可,注意需要先设置环境变量,不然启动会报错`2 validation errors for ServerSettings`。 +1. Create a virtual environment +2. Install dependencies +3. Run with `uv` or `python main.py` -## 5. 启动openmcp-client +> Important: Environment variables must be set first to avoid `2 validation errors for ServerSettings` -接下来,你就可以使用openmcp-client连接刚刚启动的server了,不管是使用网页端还是VSCode均可。 +## 5. Connect with openmcp-client -点击加号添加连接,根据server代码中的`--transport`参数决定是SSE还是Streamable HTTP。如果是SSE,则URL填写`http://localhost:8000/sse`;如果是Streamable HTTP,则URL填写`http://localhost:8000/mcp`。认证签名无需填写。 +Connect to your server via web or VSCode: -接下来连接到当前server,此时会自动打开一个网页进行认证,首次打开需要点击认证,认证成功后该网页会自动关闭。 +1. Click "+" to add connection +2. Set URL based on server's transport: + - SSE: `http://localhost:8000/sse` + - Streamable HTTP: `http://localhost:8000/mcp` +3. Leave auth signature blank + +On first connection, a browser window will open for OAuth authentication. After successful auth, the window will automatically close. ![](images/oauth-github-success.png) -认证成功后,进入工具页面,应该能看到一个`get_user_profile`工具,点击使用就可以获取到你的Github个人信息了。 - -![](images/oauth-github-tool.png) - - - - - +Once authenticated, access the `get_user_profile` tool to retrieve your GitHub profile: +![](images/oauth-github-tool.png) \ No newline at end of file diff --git a/plugin-tutorial/usage/test-with-llm.md b/plugin-tutorial/usage/test-with-llm.md index 2108ec3..1d10671 100644 --- a/plugin-tutorial/usage/test-with-llm.md +++ b/plugin-tutorial/usage/test-with-llm.md @@ -1,72 +1,64 @@ -# 用大模型测试您的 mcp +# Testing Your MCP with Large Language Models -如果您完成了 [[connect-llm|连接 mcp 服务器]] 这一步,那么您就可以开始测试您的 mcp 了。 +After completing [[connect-llm|connecting your MCP server]], you're ready to begin testing. While [[put-into-llm|quick start]] covered basic testing, this article details advanced configuration options visible below the input box. -在 [[put-into-llm|扔进大模型里面测测好坏!]] 中,我们已经通过一个简单的例子来展示来如何使用大模型测试您的 mcp。因此,这篇文章更多是讲解不便在「快速开始」中赘述的细节。 +## Model Selection +Switch between different LLMs here. OpenMCP tracks models at the message level, enabling mixed-model testing scenarios. -和大模型交互时,有一些参数可以选择,也就是输入框下面那一排按钮,我来简单介绍一下。 +> Can't find your model? See [[connect-llm|connecting MCP servers]] to add new models. -## 选择模型 +## System Prompts -顾名思义,你可以在这里切换你的模型。值得一提的是,openmcp 会以单条对话为粒度来记录每一条对话使用的模型。您可以利用这一特性来进行混合模型测试。 - -如果你没有找到你想要的模型,或是想要添加额外的模型,请移步 [[connect-llm|连接 mcp 服务器]] 来了解如何添加模型。 - -## 系统提示词 - -您可以在这里选择和添加系统提示词。 +Manage and create system prompts through this module: ![](./images/system-prompt.png) -openmcp 默认将您的系统提示词保存在 `~/.openmcp/nedb/systemPrompt.db` 中。您可以通过 nedb 来进行反序列化和拷贝。 +Prompts are stored in `~/.openmcp/nedb/systemPrompt.db` (NeDB format for easy inspection/backup). +## Prompt Generation -## 提词 - -您可以利用该模块来调用 mcp 服务器提供的 prompt 功能,生成的 prompt 字段会作为富文本被插入您的对话中。 +Invoke server-provided prompt functions, with results inserted as rich text: ![](./images/prompt.png) -## 资源 +## Resource Access -您可以利用该模块来调用 mcp 服务器提供的 resource 功能,生成的 resource 字段会作为富文本被插入您的对话中。 +Call server resource functions - outputs are inserted as formatted content: ![](./images/resource.png) -:::warning openmcp 不负责 resource 的数据持久化! -请注意!每次对话完成后 resource 是否会被保存到磁盘完全由 mcp server 作者决定,openmcp 不负责 resource 的数据持久化!如果您发现关闭 openmcp 再打开,resource 彩蛋为空,这不是 openmcp 的 bug,而是 mcp server 作者没有支持数据持久化! +:::warning Data Persistence +OpenMCP doesn't manage resource persistence! Empty resources after restart indicate the MCP server lacks storage implementation - this is expected behavior. ::: +## Parallel Tool Execution -## 允许模型在单轮回复中调用多个工具 - -大模型在进行工具调用时,有时候会将在一次回复中要求调用多次工具,比如你想要同时获取三个网页的内容翻译,大模型可能会同时调用三次「网络搜索」工具(如果你提供了的话)。多次工具使用时,openmcp 会如此渲染调用执行过程: +When enabled (default), models may call multiple tools in one response (e.g., three parallel web searches): ![](./images/parallel-tool-call.png) -openmcp 输入框的按钮中的「允许模型在单轮回复中调用多个工具」默认是点亮的。也就是允许大模型可以在一次回复中调用多次工具。 +Disable for sequential execution. -有的时候,我们希望命令一条条执行,就可以选择把这个按钮熄灭。 +> Note: Some providers (like Gemini) may force-disable this if they don't fully support OpenAI's parallel calling spec. -:::warning 协议兼容性警告 -有的厂商(比如 gemini)不一定严格支持了 openai 协议,对于不支持 「允许模型在单轮回复中调用多个工具」的厂商,openmcp 的后端会自动将该选项关闭。 +## Temperature Control + +Controls output randomness: +- Recommended range: 0.6-0.7 for general tasks +- Default: 0.6 (balanced creativity/consistency) + +## Context Window + +Determines how many prior messages (default: 20) are sent with each request. Count includes: +- User queries +- Model responses +- Tool calls/results + +:::danger Minimum Threshold +Values below 20 often break tool call sequencing, causing 400 errors. Start fresh conversations if this occurs. ::: +## Server Timeout -## 温度参数 - -温度参数越高,生成内容的随机性越强,对于通用大模型,个人建议参数为 0.6 ~ 0.7,会是一个适用于通用型任务的数值。OpenMCP 提供的默认值为 0.6。 - -## 上下文长度 - -上下文长度代表了大模型的最大上下文轮数,默认值为 20。举个例子,如果你和大模型对话产生了 40 轮数据(工具调用条数+你的问题数量+大模型回答的次数总共 40 个),那么下一次对话 openmcp 只会发送最后 20 条数据给大模型。 - -:::warning 上下文长度不要太小! -我们强烈不推荐你将这个数值设置低于 20,因为大模型接受工具调用的结果需要和之前的调用请求一一对应。如果不对应,大模型会返回注入 400 这样的错误。遇到这样的错误,请从最初一句话重启或者新开一个「交互测试」。 -::: - -## MCP 服务器超时 - -MCP 服务器默认超时时间为 30 秒,如果您的 mcp 服务器需要更长的时间才能完成任务,您可以通过「设置」「通用」来设置超时时间,单位为秒,该设置全局生效。 - +Default 30-second timeout adjustable via Settings → General (global configuration, in seconds). Increase for long-running operations. \ No newline at end of file diff --git a/plugin-tutorial/usage/ui-color.md b/plugin-tutorial/usage/ui-color.md index cf53207..64ce28d 100644 --- a/plugin-tutorial/usage/ui-color.md +++ b/plugin-tutorial/usage/ui-color.md @@ -1,25 +1,23 @@ -# UI 配色 +# UI Theme Colors -## openmcp 的主题色随跟 vscode +## OpenMCP Theme Follows VS Code -openmcp 的主题颜色完全跟随 vscode,如果你想要更换 openmcp 的主题颜色,你只需要更换 vscode 的主题颜色即可。 +OpenMCP’s theme color automatically follows your current VS Code theme. If you want to change the appearance of OpenMCP, simply switch the theme in VS Code. -比如当你把颜色切换为社区知名主题 One Dark Pro 时,openmcp 的表现: +For example, when switching to the popular community theme **One Dark Pro**, OpenMCP will adapt accordingly: ![](./images/one-dark-pro.png) +## Switching Theme Colors -## 切换主题色 - -这里可以切换 openmcp 的主题色(默认是粉色) +You can change OpenMCP’s theme color here (default is pink): ![](./images/change-color.png) +## Special Support for Trae -## Trae 的特殊支持 +OpenMCP provides enhanced theme support for **Trae**'s default color schemes. We encourage users to try different visual language environments (VLEs) like VS Code, Cursor, and Trae for the best developer experience. -openmcp 对 trae 的默认主题色都有额外的支持。我们也鼓励我们的用户尝试 vscode,cursor,trae 等不同的 VLE 来获得最佳手感。 +Most of the examples in the official OpenMCP documentation use **Trae’s default "Deep Blue" theme**: -openmcp 官方文档大部分演示的例子都是基于 trae 的「深蓝」默认主题。 - -![](./images/trae-blue.png) \ No newline at end of file +![](./images/trae-blue.png) diff --git a/plugin-tutorial/what-is-mcp.md b/plugin-tutorial/what-is-mcp.md index 8afe97d..0aecbd4 100644 --- a/plugin-tutorial/what-is-mcp.md +++ b/plugin-tutorial/what-is-mcp.md @@ -1,18 +1,17 @@ -# 什么是 MCP? +# What is MCP? ![](https://picx.zhimg.com/70/v2-1a2df8a081a76f4e90431d8a2445f495_1440w.avis) -MCP (Model Context Protocol)是一种开放协议,用于标准化应用程序如何向大型语言模型(LLMs)提供上下文。可以将 MCP 想象为 AI 应用的 typec 接口。正如 typec 提供了一种标准化的方式将您的设备连接到各种外设和配件,MCP 也提供了一种标准化的方式,将 AI 模型连接到不同的数据源和工具。 +MCP (Model Context Protocol) is an open protocol designed to standardize how applications provide context to large language models (LLMs). You can think of MCP as a Type-C interface for AI applications. Just as Type-C provides a standardized way to connect your devices to various peripherals and accessories, MCP provides a standardized way to connect AI models to different data sources and tools. -MCP 协议由 Anthropic 在 2024 年 11 月底推出: +The MCP protocol was introduced by Anthropic in late November 2024: -- 官方文档:[Introduction](https://modelcontextprotocol.io/introduction) -- GitHub 仓库:[github.com/modelcontextprotocol](https://github.com/modelcontextprotocol) +- Official documentation: [Introduction](https://modelcontextprotocol.io/introduction) +- GitHub repository: [github.com/modelcontextprotocol](https://github.com/modelcontextprotocol) +## Why do we need MCP? -## 为什么需要 MCP? - -我们都知道,从最初的 chatgpt,到后来的 cursor,copilot chatroom,再到现在耳熟能详的 agent,实际上,从用户交互的角度去观察,你会发现目前的大模型产品经历了如下的变化: +We all know that from the initial ChatGPT, to later Cursor, Copilot Chatroom, and now the well-known agents, if we observe from the user interaction perspective, we can see the following evolution in current LLM products: ``` mermaid graph LR @@ -21,31 +20,30 @@ a(chatbot > deepseek, chatgpt) --> b(composer > cursor, copilot) --> c(agent > A ``` - chatbot - - 只会聊天的程序。 - - 工作流程:你输入问题,它给你这个问题的解决方案,但是具体执行还需要你自己去。 - - 代表工作:deepseek,chatgpt + - Programs that only chat. + - Workflow: You input a question, it provides a solution, but you need to execute it yourself. + - Representative works: deepseek, chatgpt - composer - - 稍微会帮你干活的实习生,仅限于写代码。 - - 工作流程:你输入问题,它会给你帮你生成解决问题的代码,并且自动填入代码编辑器的编译区,你只需要审核确认即可。 - - 代表工作:cursor,copilot + - Interns that can help with work, but limited to coding. + - Workflow: You input a question, it generates code to solve the problem and automatically fills it in the editor's compilation area, you just need to review and confirm. + - Representative works: cursor, copilot - agent - - 私人秘书。 - - 工作流程:你输入问题,它生成这个问题的解决方案,并在征询了你的同意后全自动执行。 - - 代表工作:AutoGPT,Manus,Open Manus + - Personal assistants. + - Workflow: You input a question, it generates a solution, and after obtaining your consent, executes it automatically. + - Representative works: AutoGPT, Manus, Open Manus - - -为了实现 agent,也就需要让 LLM 可以自如灵活地操作所有软件甚至物理世界的机器人,于是需要定义统一的上下文协议与之上统一工作流。MCP(model context protocol) 就是解决这套方案的应运而生的基础协议。一个感性认识如下: +To implement agents, we need to enable LLMs to flexibly operate all software and even physical world robots, which requires defining a unified context protocol and workflow. MCP (Model Context Protocol) is the fundamental protocol born to solve this problem. An intuitive understanding is as follows: ```mermaid graph TB -user(用户) -ai(AI软件) -llm(大模型) -computer(本地环境) +user(User) +ai(AI Software) +llm(Large Language Model) +computer(Local Environment) -user --帮我整理两会报告中有\n关AI的咨询到word文件--> agent +user --Help me organize AI-related consultations +from the Two Sessions report into a Word file--> agent subgraph agent @@ -54,115 +52,113 @@ computer <--MCP--> ai end -agent --> word(D:/会议总结/两会报告AI专题.docx) - +agent --> word(D:/Meeting Summary/Two Sessions Report AI Topic.docx) ``` - :::info -Anthropic 对于 MCP 的必要性给出的解释:MCP 帮助您在 LLMs 之上构建 agent 和复杂的工作流程。LLMs 经常需要与数据和工具集成,而 MCP 提供了以下支持: +Anthropic's explanation for the necessity of MCP: MCP helps you build agents and complex workflows on top of LLMs. LLMs often need to integrate with data and tools, and MCP provides the following support: -- 一系列不断增长的预构建集成,您的 LLM 可以直接接入这些集成。 -- 在 LLM 提供商和供应商之间灵活切换。 -- 在基础设施内保护数据的最佳实践。 +- A growing collection of pre-built integrations that your LLM can directly connect to. +- Flexibility to switch between LLM providers and vendors. +- Best practices for protecting data within infrastructure. ::: +## Overall Architecture - - - -## 总体架构 - -MCP 的核心采用客户端-服务器架构,其中 host 可以连接到多个服务器: +The core of MCP adopts a client-server architecture where a host can connect to multiple servers: ```mermaid graph LR -host[Host MCP 客户端\n 浏览器, 代码编辑器, 其他工具] +host[Host MCP Client + Browser, Code Editor, Other Tools] -server_a[MCP 服务器 A] -server_b[MCP 服务器 B] -server_c[MCP 服务器 C] +server_a[MCP Server A] +server_b[MCP Server B] +server_c[MCP Server C] -db_a[(本地\n数据源 A)] -db_b[(本地\n数据源 B)] +db_a[(Local +Data Source A)] +db_b[(Local +Data Source B)] -remote[(远程服务 C)] +remote[(Remote Service C)] -subgraph 你的电脑 +subgraph Your Computer direction LR -host <--MCP 协议--> server_a -host <--MCP 协议--> server_b -host <--MCP 协议--> server_c +host <--MCP Protocol--> server_a +host <--MCP Protocol--> server_b +host <--MCP Protocol--> server_c server_a <--> db_a server_b <--> db_b end -subgraph 互联网 +subgraph Internet server_c <--Web APIs--> remote end ``` +- MCP Hosts: Programs that want to access data through MCP, such as Claude Desktop, Integrated Development Environments (IDEs), or other AI tools. +- MCP Clients: Protocol clients that maintain a 1:1 connection with servers, responsible for communicating with MCP servers. +- MCP Servers: Lightweight programs, each exposing specific functionality through the standardized Model Context Protocol. +- Local Data Sources: Computer files, databases, and services that MCP servers can securely access. +- Remote Services: External systems that MCP servers can connect to via the internet (e.g., services accessed through APIs). +## MCP Workflow -- MCP 主机(MCP Hosts) :MCP 主机是指希望通过 MCP 访问数据的程序,例如 Claude Desktop、集成开发环境(IDEs)或其他 AI 工具。 -- MCP 客户端(MCP Clients):MCP 客户端是与服务器保持 1:1 连接的协议客户端,负责与 MCP 服务器通信。 -- MCP 服务器(MCP Servers):MCP 服务器是轻量级程序,每个服务器通过标准化的 Model Context Protocol 暴露特定的功能。 -- 本地数据源(Local Data Sources):本地数据源是指 MCP 服务器可以安全访问的计算机文件、数据库和服务。 -- 远程服务(Remote Services):远程服务是指 MCP 服务器可以通过互联网连接的外部系统(例如通过 API 访问的服务)。 +In terms of workflow, MCP is very similar to LSP. In fact, like LSP, current MCP also transmits data based on [JSON-RPC 2.0](https://link.zhihu.com/?target=https%3A//www.jsonrpc.org/specification) (via Stdio or SSE). Those who have developed LSP should find MCP very intuitive. I'll use several simple and clear sequence diagrams to help everyone understand how this works. +### Initialization - -## MCP 的工作流程 - -从工作流程上,MCP 和 LSP 非常非常像,事实上,目前的 MCP 和 LSP 一样,也是基于 [JSON-RPC 2.0](https://link.zhihu.com/?target=https%3A//www.jsonrpc.org/specification) 进行数据传输的(基于Stdio 或者 基于SSE)。如果开发过 LSP 的朋友对于 MCP 应该会感到非常的理所当然。我将用若干个简单明了的泳道图尽可能让大家看懂这玩意儿是如何执行的。 - -### 初始化 - -假设我们的软件已经支持了 MCP 客户端,那么当我们的软件启动时,它会经历如下的步骤: +Assuming our software already supports MCP clients, when the software starts, it goes through the following steps: ```mermaid graph TB - subgraph MCP 客户端 - A1[初始化] - A2[获取 MCP 服务端提供的工具集合 \n1. 创建文件 -> createFile\n2. 删除文件 -> deleteFile\n3. 使用搜索引擎 -> useBrowser\n 4. ...] + subgraph MCP Client + A1[Initialization] + A2[Get tool collection provided by MCP server +1. Create file -> createFile +2. Delete file -> deleteFile +3. Use search engine -> useBrowser + 4. ...] end - subgraph MCP 服务端 - B1[初始化] + subgraph MCP Server + B1[Initialization] end A1 --startMCPServer--> B1 B1 --ListToolsRequestSchema--> A2 ``` +### Workflow - -### 工作流程 - - 假设,你是一位 C语言工程师,你现在想要让 agent 自动完成一个项目的编译,那么执行流程如下: +Suppose you are a C language engineer and want an agent to automatically compile a project. The execution process would be as follows: ```mermaid graph TB - subgraph MCP 客户端 - A1[用户询问 请帮我删除 build\n下的所有编译的中间结果] --> A2[将用户询问, 资源, MCP服务端的工具集合发送给大模型] + subgraph MCP Client + A1[User asks: Please help me delete all intermediate compilation results under build] --> A2[Send user query, resources, and MCP server tool collection to LLM] - A3[大模型返回操作流\n1. deleteFile build/a.o\n2. deleteFile build/b.o] - A4[整理操作结果给大模型] - A5[最终响应展示给用户] + A3[LLM returns operation flow +1. deleteFile build/a.o +2. deleteFile build/b.o] + A4[Organize operation results for LLM] + A5[Display final response to user] end - subgraph MCP 服务端 - B1[解析大模型操作流] --征询用户同意--> B2[执行操作流] + subgraph MCP Server + B1[Parse LLM operation flow] --Request user consent--> B2[Execute operation flow] end - subgraph 大模型 - C1[大模型基于上下文生成操作方案] - - C2[大模型基于所有信息生\n成自然语言响应] + subgraph LLM + C1[LLM generates operation plan based on context] + + C2[LLM generates natural language response +based on all information] end A3 --> B1 @@ -173,9 +169,8 @@ A4 --> C2 C2 --> A5 ``` +## Open Source Ecosystem -## 开源生态 +Like LSP, which has many client and server frameworks in the open source community, MCP is the same. Currently, Anthropic has open-sourced an MCP server framework: https://github.com/modelcontextprotocol/servers. Friends who want to explore LLM applications can freely use this framework. This repository also includes many officially recognized MCP servers that can serve as learning references. -和 LSP 一样,LSP 在开源社区有非常多的客户端和服务端框架,MCP 也是一样的,目前 Anthropic 开源了一套 MCP 的服务端框架:https://github.com/modelcontextprotocol/servers ,想要探索大模型效用的朋友可以尽情去使用这个框架。这个仓库还收录了很多的官方认可的 MCP 服务器,可以作为学习的参考。 - -除此之外,pulsemcp 上也有很多开源社区开发的 MCP 客户端和服务端:https://www.pulsemcp.com/clients \ No newline at end of file +In addition, there are many MCP clients and servers developed by the open source community on pulsemcp: https://www.pulsemcp.com/clients \ No newline at end of file diff --git a/preview/changelog.md b/preview/changelog.md index 1d664bf..0c9ab90 100644 --- a/preview/changelog.md +++ b/preview/changelog.md @@ -1,86 +1,91 @@ ---- - ---- # Change Log ## [main] 0.1.1 -- 修复 SSH 连接 Ubuntu 的情况下的部分 bug -- 修复 python 项目点击 openmcp 进行连接时,初始化参数错误的问题 -- 取消 service 底层的 mcp 连接复用技术,防止无法刷新 -- 修复连接后,可能无法在欢迎界面选择调试选项的 bug + +* Fixed some bugs when connecting to Ubuntu via SSH +* Fixed the issue where the initialization parameters were incorrect when clicking openmcp to connect to a Python project +* Removed the service-level MCP connection reuse technique to prevent connection refresh issues +* Fixed a bug where debug options could not be selected on the welcome screen after connection ## [main] 0.1.0 -- 新特性:支持同时连入多个 mcp server -- 新特性:更新协议内容,支持 streamable http 协议,未来将逐步取代 SSE 的连接方式 -- impl issue#16:对于 uv 创建的 py 项目进行特殊支持,自动初始化项目,并将 mcp 定向到 .venv/bin/mcp 中,不再需要用户全局安装 mcp -- 对于 npm 创建的 js/ts 项目进行特殊支持:自动初始化项目 -- 去除了 websearch 的设置,增加了 parallel_tool_calls 的设置,parallel_tool_calls 默认为 true,代表 允许模型在单轮回复中调用多个工具 -- 重构了 openmcp 连接模块的基础设施,基于新的技术设施实现了更加详细的连接模块的日志系统. -- impl issue#15:无法复制 -- impl issue#14:增加日志清除按钮 + +* New Feature: Support for connecting to multiple MCP servers simultaneously +* New Feature: Updated protocol to support Streamable HTTP, which will gradually replace SSE connection method +* Implemented issue #16: Special support for Python projects created by uv, automatically initializing the project and directing MCP to `.venv/bin/mcp` so that global MCP installation is no longer required +* Special support for npm-created js/ts projects: Automatically initializes the project +* Removed the "websearch" setting, added the "parallel\_tool\_calls" setting, with the default set to true, allowing the model to call multiple tools in a single round of response +* Refactored the OpenMCP connection module infrastructure, creating a more detailed logging system for connection modules +* Implemented issue #15: Unable to copy +* Implemented issue #14: Added a log clearing button ## [main] 0.0.9 -- 修复 0.0.8 引入的bug:system prompt 返回的是索引而非真实内容 -- 测试新的发布管线 + +* Fixed the bug introduced in version 0.0.8 where the system prompt returned an index instead of the real content +* Tested the new release pipeline ## [main] 0.0.8 -- 大模型 API 测试时更加完整的报错 -- 修复 0.0.7 引入的bug:修改对话无法发出 -- 修复 bug:富文本编辑器粘贴文本会带样式 -- 修复 bug:富文本编辑器发送前缀为空的字符会全部为空 -- 修复 bug:流式传输进行 function calling 时,多工具的索引串流导致的 JSON Schema 反序列化失败 -- 修复 bug:大模型返回大量重复错误信息 -- 新特性:支持一次对话同时调用多个工具 -- UI:优化代码高亮的滚动条 -- 新特性:resources/list 协议的内容点击就会直接渲染,无需二次发送 -- 新特性:resources prompts tools 的结果的 json 模式支持高亮 + +* More complete error reporting when testing large model APIs +* Fixed the bug introduced in version 0.0.7 where conversation messages could not be sent +* Fixed the bug where rich-text editor pasted content retained styles +* Fixed the bug where sending a character with an empty prefix in the rich-text editor resulted in the content being empty +* Fixed the bug where function calling in streamable transfers caused JSON schema deserialization failure due to multi-tool index streaming +* Fixed the bug where large models returned a lot of repeated error messages +* New Feature: Support calling multiple tools in a single conversation +* UI: Optimized the code highlighting scrollbar +* New Feature: Resources/list protocol content now renders directly upon click without needing to be resent +* New Feature: JSON format support for highlighting results from resources, prompts, and tools ## [main] 0.0.7 -- 优化页面布局,使得调试窗口可以显示更多内容 -- 扩大默认的上下文长度 10 -> 20 -- 增加「通用选项」 -> 「MCP工具最长调用时间 (sec)」 -- 支持富文本输入框,现在可以将 prompt 和 resource 嵌入到输入框中 进行 大规模 prompt engineering 调试工作了 + +* Optimized page layout to display more content in the debug window +* Increased default context length from 10 to 20 +* Added "General Options" -> "MCP Tool Max Call Duration (sec)" +* Added support for rich-text input, allowing prompts and resources to be embedded for large-scale prompt engineering debugging ## [main] 0.0.6 -- 修复部分因为服务器名称特殊字符而导致的保存实效的错误 -- 插件模式下,左侧管理面板中的「MCP连接(工作区)」视图可以进行增删改查了 -- 新增「安装的 MCP 服务器」,用于安装全局范围的 mcp server -- 增加引导页面 -- 修复无法进行离线 OCR 的问题 -- 修复全局安装的 mcp 服务器 name 更新的问题 + +* Fixed the issue of saving failure caused by special characters in the server name +* In plugin mode, the "MCP Connections (Workspace)" view in the left management panel now supports CRUD operations +* Added "Installed MCP Servers" for installing global MCP servers +* Added a guide page +* Fixed the issue of offline OCR not working +* Fixed the issue of global MCP server name not updating ## [main] 0.0.5 -- 支持对已经打开过的文件项目进行管理 -- 支持对用户对应服务器的调试工作内容进行保存 -- 支持连续工具调用和错误警告的显示 -- 实现小型本地对象数据库,用于对对话产生的多媒体进行数据持久化 -- 支持对于调用结果进行一键复现 -- 支持对中间结果进行修改 -- 支持 system prompt 的保存和修改 + +* Supported managing already opened file projects +* Supported saving debugging work related to user servers +* Supported continuous tool calls and error warnings display +* Implemented a small local object database for persisting multimedia data generated during conversations +* Supported one-click reproduction of call results +* Supported modifying intermediate results +* Supported saving and modifying system prompts ## [main] 0.0.4 -- 修复选择模型后点击确认跳转回 deepseek 的 bug -- 修复 mcp 项目初始化点击工具全部都是空的 bug -- 修复无法重新连接的 bug -- 支持自定义第三方 openai 兼容的模型服务 + +* Fixed the bug where selecting a model and clicking confirm would revert back to DeepSeek +* Fixed the bug where MCP project initialization clicked tools were all empty +* Fixed the bug where reconnection was not possible +* Supported custom third-party OpenAI-compatible model services ## [main] 0.0.3 -- 增加每一条信息的成本统计信息 -- 修复初始化页面路由不为 debug 导致页面空白的 bug +* Added cost statistics for each message +* Fixed the bug where the initial page route not being set to "debug" led to a blank page ## [main] 0.0.2 -- 优化页面布局 -- 解决更新标签页后打开无法显示的 bug -- 解决不如输入组件按下回车直接黑屏的 bug -- 更加完整方便的开发脚本 +* Optimized page layout +* Fixed the bug where tabs would not display after update +* Fixed the bug where pressing Enter in input components led to a black screen +* Made development scripts more complete and convenient ## [main] 0.0.1 -- 完成 openmcp 的基础 inspector 功能 -- 完成配置加载,保存,大模型设置 -- 完成标签页自动保存 -- 完成大模型对话窗口和工具调用 -- 完成对 vscode 和 trae 的支持 +* Completed basic OpenMCP inspector functionality +* Completed configuration loading, saving, and large model settings +* Completed automatic tab saving +* Completed large model conversation window and tool calls +* Completed support for VSCode and Trae diff --git a/preview/channel.md b/preview/channel.md index 0f223f9..a1a530f 100644 --- a/preview/channel.md +++ b/preview/channel.md @@ -1,26 +1,16 @@ ---- ---- +# Resource Channel -# 资源频道 +## Resources -## 资源 +* [MCP Series Video Tutorials (Under Construction)](https://www.bilibili.com/video/BV1zYGozgEHc) +* [Jin Hui's MCP Series Blog](https://kirigaya.cn/blog/search?q=mcp) +* [OpenMCP Official Documentation](https://kirigaya.cn/openmcp/plugin-tutorial) +* [openmcp-sdk Official Documentation](https://kirigaya.cn/openmcp/sdk-tutorial) -[MCP 系列视频教程(正在施工中)](https://www.bilibili.com/video/BV1zYGozgEHc) +## Channels -[锦恢的 mcp 系列博客](https://kirigaya.cn/blog/search?q=mcp) - -[OpenMCP 官方文档](https://kirigaya.cn/openmcp/plugin-tutorial) - -[openmcp-sdk 官方文档](https://kirigaya.cn/openmcp/sdk-tutorial) - -## 频道 - -[知乎圈子 - OpenMCP 博物馆](https://www.zhihu.com/ring/host/1911121615279849840) - -[QQ群 - OpenMCP 正式级技术组](https://qm.qq.com/cgi-bin/qm/qr?k=C6ZUTZvfqWoI12lWe7L93cWa1hUsuVT0&jump_from=webapi&authKey=McW6B1ogTPjPDrCyGttS890tMZGQ1KB3QLuG4aqVNRaYp4vlTSgf2c6dMcNjMuBD) - -[Discord 频道 - OpenMCP 交流群](https://discord.gg/SKTZRf6NzU) - -[OpenMCP 源代码](https://github.com/LSTM-Kirigaya/openmcp-client) - -[OpenMCP 文档仓库](https://github.com/LSTM-Kirigaya/openmcp-document) \ No newline at end of file +* [Zhihu Circle - OpenMCP Museum](https://www.zhihu.com/ring/host/1911121615279849840) +* [QQ Group - OpenMCP Official Technical Group](https://qm.qq.com/cgi-bin/qm/qr?k=C6ZUTZvfqWoI12lWe7L93cWa1hUsuVT0&jump_from=webapi&authKey=McW6B1ogTPjPDrCyGttS890tMZGQ1KB3QLuG4aqVNRaYp4vlTSgf2c6dMcNjMuBD) +* [Discord Channel - OpenMCP Discussion Group](https://discord.gg/SKTZRf6NzU) +* [OpenMCP Source Code](https://github.com/LSTM-Kirigaya/openmcp-client) +* [OpenMCP Documentation Repository](https://github.com/LSTM-Kirigaya/openmcp-document) diff --git a/preview/contributors.md b/preview/contributors.md index 5689839..04837bd 100644 --- a/preview/contributors.md +++ b/preview/contributors.md @@ -34,7 +34,7 @@ const contributors = [ { icon: 'github', link: 'https://github.com/appli456' }, ] }, - { + { avatar: 'https://avatars.githubusercontent.com/u/115577936?v=4', name: 'AmeSoraQwQ (AmeZora)', title: 'Creator & Operation', @@ -43,6 +43,38 @@ const contributors = [ { icon: 'github', link: 'https://github.com/ArcStellar2025' }, ] }, + { + avatar: 'https://avatars.githubusercontent.com/u/129645384?v=4', + name: 'SFXJX (社奉行佳婿)', + title: 'Developer', + links: [ + { icon: 'github', link: 'https://github.com/STUzhy' }, + ] + }, + { + avatar: 'https://avatars.githubusercontent.com/u/37235140?v=4', + name: 'hao (cybermanhao)', + title: 'Developer', + links: [ + { icon: 'github', link: 'https://github.com/cybermanhao' }, + ] + }, + { + avatar: 'https://avatars.githubusercontent.com/u/213030338?v=4', + name: 'reliedejuedouzhe2', + title: 'Developer', + links: [ + { icon: 'github', link: 'https://github.com/reliedejuedouzhe2' }, + ] + }, + { + avatar: 'https://avatars.githubusercontent.com/u/206822796?v=4', + name: 'ZYD045692', + title: 'Developer', + links: [ + { icon: 'github', link: 'https://github.com/ZYD045692' }, + ] + } ] diff --git a/preview/join.md b/preview/join.md index d5c44af..478ed00 100644 --- a/preview/join.md +++ b/preview/join.md @@ -1,34 +1,33 @@ -# 参与 OpenMCP 开发 +# Contributing to OpenMCP Development +## How to Contact Us if You Want to Contribute? -## 想要参与开发,如果联系我们? +If you'd like to get involved in the development of OpenMCP, you can contact us through the following channels: -如果你也想要参与 OpenMCP 的开发,你可以通过如下的方式联系到我们: +* Join the OpenMCP Technical Group to discuss with us directly. +* Contact Jin Hui via email at [1193466151@qq.com](mailto:1193466151@qq.com). +* Submit a [Feature Request](https://github.com/LSTM-Kirigaya/openmcp-client/issues) or fork the code and [submit a PR](https://github.com/LSTM-Kirigaya/openmcp-client/pulls). -- 加入 OpenMCP正式级技术组 来和我们直接讨论。 -- 联系锦恢的邮箱 1193466151@qq.com -- 提交 [「Feature Request」类型的 issue](https://github.com/LSTM-Kirigaya/openmcp-client/issues) 或者 fork 代码后 [提交 PR](https://github.com/LSTM-Kirigaya/openmcp-client/pulls) +## What Can I Do for OpenMCP? -## 我能为 OpenMCP 做些什么? +Although OpenMCP might seem technically complicated, there are many things you can contribute to the project: -虽然 OpenMCP 本体看起来技术乱七八糟,但是实际上,阁下可以为 OpenMCP 做的事情远比您想象的多。 +* Contribute code or fix bugs by submitting a PR, if you're willing. +* Design new features for the project. You don’t necessarily need to write code — just proposing meaningful features in the [MVP Requirement Plan](https://github.com/LSTM-Kirigaya/openmcp-client?tab=readme-ov-file#%E9%9C%80%E6%B1%82%E8%A7%84%E5%88%92) is already a great contribution. +* Develop examples of different agent applications using OpenMCP, or refine new methodologies for developing AI Agents. With your permission, we will integrate your tutorials into this site. -- 通过提交 PR 贡献代码或者修复 bug,如果您愿意的话。 -- 为我们的项目设计新的功能,这未必一定需要阁下写代码,只是在 [MVP 需求规划](https://github.com/LSTM-Kirigaya/openmcp-client?tab=readme-ov-file#%E9%9C%80%E6%B1%82%E8%A7%84%E5%88%92) 中提出有意义的功能也是很不错的贡献。 -- 通过 OpenMCP 来完成不同的 agent 开发的例子或者打磨新的开发 AI Agent 的方法论。在征得阁下本人同意后,我们将会将你的教程整合到这个网站中。 +By contributing in any of the above ways, you’ll become an official contributor to OpenMCP. -通过向 OpenMCP 贡献以上内容或是其他,阁下将能成为 OpenMCP 的贡献者。 +## Contribute to OpenMCP Documentation Site -## 为 openmcp 文档站点添砖加瓦 +If you'd like to fix professional errors or contribute examples to the OpenMCP documentation site, please fork [openmcp-document](https://github.com/LSTM-Kirigaya/openmcp-document) and submit a PR. -如果您想要为 openmcp 文档站点修复专业错误或者贡献您的例子,请 fork [openmcp-document](https://github.com/LSTM-Kirigaya/openmcp-document) 提交 PR 。 +If you're unfamiliar with GitHub operations, you can [join the OpenMCP development group](https://qm.qq.com/cgi-bin/qm/qr?k=C6ZUTZvfqWoI12lWe7L93cWa1hUsuVT0&jump_from=webapi&authKey=McW6B1ogTPjPDrCyGttS890tMZGQ1KB3QLuG4aqVNRaYp4vlTSgf2c6dMcNjMuBD) and contact the admin Jin Hui. Please provide the following details: -如果您对 github 的操作不熟悉,请 [进入 OpenMCP 开发群](https://qm.qq.com/cgi-bin/qm/qr?k=C6ZUTZvfqWoI12lWe7L93cWa1hUsuVT0&jump_from=webapi&authKey=McW6B1ogTPjPDrCyGttS890tMZGQ1KB3QLuG4aqVNRaYp4vlTSgf2c6dMcNjMuBD) 并联系管理员锦恢,请提供如下几件东西: +* The content you want to contribute. +* Your GitHub account (profile link). +* Your email address. +* The ID and avatar you'd like to display on the site. +* Any links you'd like associated with your profile (e.g., Bilibili, Zhihu, personal website). -- 您希望贡献的内容 -- 您的 github 账号(主页链接) -- 您的邮箱 -- 您希望展现在网站上的 ID 和 头像 -- 您希望关联的网站链接(比如b站,知乎,个人网站什么的) - -完成添加后,您就可以向 [openmcp-document](https://github.com/LSTM-Kirigaya/openmcp-document) 提交 PR 了。 \ No newline at end of file +After adding your information, you’ll be able to submit PRs to [openmcp-document](https://github.com/LSTM-Kirigaya/openmcp-document). diff --git a/scripts/move-image.js b/scripts/move-image.js deleted file mode 100644 index ebee575..0000000 --- a/scripts/move-image.js +++ /dev/null @@ -1,68 +0,0 @@ -const fs = require('fs'); -const path = require('path'); - -// 要排除的文件夹 -const excludedDirs = ['node_modules', '.vitepress', '.vscode', 'scripts']; - -// 递归搜索图片文件夹 -function findImageDirs(rootDir, currentDir = '', results = []) { - const fullPath = path.join(rootDir, currentDir); - const items = fs.readdirSync(fullPath); - - for (const item of items) { - const itemPath = path.join(currentDir, item); - const fullItemPath = path.join(rootDir, itemPath); - const stat = fs.statSync(fullItemPath); - - if (stat.isDirectory()) { - if (item === 'image' || item === 'images') { - results.push(itemPath); - } else if (!excludedDirs.includes(item)) { - findImageDirs(rootDir, itemPath, results); - } - } - } - - return results; -} - -// 复制文件夹 -function copyDir(src, dest) { - if (!fs.existsSync(dest)) { - fs.mkdirSync(dest, { recursive: true }); - } - - const items = fs.readdirSync(src); - for (const item of items) { - const srcPath = path.join(src, item); - const destPath = path.join(dest, item); - const stat = fs.statSync(srcPath); - - if (stat.isDirectory()) { - copyDir(srcPath, destPath); - } else { - fs.copyFileSync(srcPath, destPath); - } - } -} - -// 主函数 -function main() { - const workspaceDir = path.join(__dirname, '..'); - const distDir = path.join(workspaceDir, '.vitepress', 'dist'); - - // 查找所有图片文件夹 - const imageDirs = findImageDirs(workspaceDir); - - // 复制到目标目录 - for (const dir of imageDirs) { - const src = path.join(workspaceDir, dir); - const dest = path.join(distDir, dir); - console.log(`Copying ${dir} to ${dest}`); - copyDir(src, dest); - } - - console.log('Done!'); -} - -main(); \ No newline at end of file diff --git a/scripts/move-image.mjs b/scripts/move-image.mjs new file mode 100644 index 0000000..12b958a --- /dev/null +++ b/scripts/move-image.mjs @@ -0,0 +1,80 @@ +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; +import chalk from 'chalk'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const excludedDirs = ['node_modules', '.vitepress', '.openmcp', 'scripts']; + +// 递归搜索图片文件夹 +function findImageDirs(rootDir, currentDir = '', results = []) { + const fullPath = path.join(rootDir, currentDir); + const items = fs.readdirSync(fullPath); + + for (const item of items) { + const itemPath = path.join(currentDir, item); + const fullItemPath = path.join(rootDir, itemPath); + const stat = fs.statSync(fullItemPath); + + if (stat.isDirectory()) { + if (item === 'image' || item === 'images') { + results.push(itemPath); + } else if (!excludedDirs.includes(item)) { + findImageDirs(rootDir, itemPath, results); + } + } + } + + return results; +} + +// 复制文件夹 +function copyDir(src, dest) { + if (!fs.existsSync(dest)) { + fs.mkdirSync(dest, { recursive: true }); + } + + const items = fs.readdirSync(src); + for (const item of items) { + const srcPath = path.join(src, item); + const destPath = path.join(dest, item); + const stat = fs.statSync(srcPath); + + if (stat.isDirectory()) { + copyDir(srcPath, destPath); + } else { + fs.copyFileSync(srcPath, destPath); + } + } +} + +// 修改后的主函数 +function main() { + const workspaceDir = path.join(__dirname, '..'); + const distDir = path.join(workspaceDir, '.vitepress', 'dist'); + + console.log(chalk.yellow.bold('Searching for image directories...')); + + // 查找所有图片文件夹 + const imageDirs = findImageDirs(workspaceDir); + + if(imageDirs.length === 0) { + console.log(chalk.red('No image directories found!')); + return; + } + + console.log(chalk.green(`Found ${imageDirs.length} image directories:`)); + + // 复制到目标目录 + for (const dir of imageDirs) { + const src = path.join(workspaceDir, dir); + const dest = path.join(distDir, dir); + console.log(chalk.blue(`Copying ${dir} to ${dest}`)); + copyDir(src, dest); + } + + console.log(chalk.green.bold('Done!')); +} + +main(); \ No newline at end of file diff --git a/scripts/simple-git.mjs b/scripts/simple-git.mjs new file mode 100644 index 0000000..fd6a684 --- /dev/null +++ b/scripts/simple-git.mjs @@ -0,0 +1,11 @@ +import simpleGit from 'simple-git'; +const git = simpleGit(); + +export async function getLatestCommitId() { + // 方法1:使用log获取最新commit + const log = await git.log({n: 1}); + console.log('Latest commit ID:', log.latest.hash); +} + +// 如果需要在模块外调用 +await getLatestCommitId(); \ No newline at end of file diff --git a/scripts/translate.mjs b/scripts/translate.mjs new file mode 100644 index 0000000..dd40968 --- /dev/null +++ b/scripts/translate.mjs @@ -0,0 +1,103 @@ +import { OmPipe } from 'ompipe'; +import { OpenAI } from 'openai'; +import chalk from 'chalk'; +import fs from 'fs'; +import path from 'path'; +import simpleGit from 'simple-git'; +import { fileURLToPath } from 'url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const project = path.join(__dirname, '..'); +const git = simpleGit(); + +if (!process.env.DEEPSEEK_API_TOKEN) { + console.error(chalk.red('请设置环境变量 DEEPSEEK_API_TOKEN')); + process.exit(1); +} + +// 初始化DeepSeek客户端 +const client = new OpenAI({ + apiKey: process.env.DEEPSEEK_API_TOKEN, + baseURL: 'https://api.deepseek.com/v1' +}); + +const log = await git.log({n: 1}); +const lastCommitMessage = log.latest.hash; +const pipe = new OmPipe('translate-docs:' + lastCommitMessage.toString()); + +async function translateText(text, targetLangID) { + const prompt = `将以下内容准确翻译为ISO编码为 ${targetLangID} 的语言。请严格遵循以下要求翻译内容: +1. 不要添加任何解释、说明或额外文本 +2. 直接返回翻译后的内容,不要包含任何前缀或后缀 + +需要翻译的内容: +${text}`; + + const response = await client.chat.completions.create({ + model: 'deepseek-chat', + messages: [ + { role: 'system', content: '你是一名专业的翻译助手' }, + { role: 'user', content: prompt } + ], + temperature: 0.6 + }); + + return response.choices[0].message.content; +} + +// 使用示例 +// const translated = await translateText('你好', 'ja'); +// console.log(translated); // "こんにちは" + +async function processFile(filePath, targetPath, targetLangID) { + const content = fs.readFileSync(filePath, 'utf8'); + + if (content.trim() === '') { + console.log(chalk.yellow(`跳过空文件: ${filePath}`)); + return; + } + + const translated = await translateText(content, targetLangID); + if (translated.trim() === '') { + throw new Error(`翻译失败: ${filePath}`); + } + fs.writeFileSync(targetPath, translated); +} + +async function traverseAndTranslate(srcDir, destDir, targetLangID) { + const files = fs.readdirSync(srcDir); + + for (const file of files) { + const fullPath = path.join(srcDir, file); + const stat = fs.statSync(fullPath); + + if (stat.isDirectory()) { + const newDestDir = path.join(destDir, file); + if (!fs.existsSync(newDestDir)) { + fs.mkdirSync(newDestDir, { recursive: true }); + console.log(chalk.cyan(`创建目录: ${newDestDir}`)); + } + await traverseAndTranslate(fullPath, newDestDir, targetLangID); + } else if (file.endsWith('.md')) { + const destPath = path.join(destDir, file); + // 添加任务 + pipe.add('translate:' + destPath, async () => { + await processFile(fullPath, destPath, targetLangID); + }, { critical: false, maxRetryCount: 2 }); + } + } +} + + +const zhDir = path.join(__dirname, '../zh'); +const jaDir = path.join(__dirname, '../ja'); +const enDir = path.join(__dirname, '..'); + +console.log(chalk.bold('开始翻译任务')); + +await traverseAndTranslate(zhDir, jaDir, 'ja'); +// await traverseAndTranslate(zhDir, enDir); + +await pipe.start(); + +console.log(chalk.bold.green('✓ 所有文件翻译完成')); \ No newline at end of file diff --git a/zh/images/.DS_Store b/zh/images/.DS_Store new file mode 100644 index 0000000..d8d844a Binary files /dev/null and b/zh/images/.DS_Store differ diff --git a/zh/images/favicon.svg b/zh/images/favicon.svg new file mode 100644 index 0000000..3267d35 --- /dev/null +++ b/zh/images/favicon.svg @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/images/icons/openmcp-edge.free b/zh/images/icons/openmcp-edge.free new file mode 100644 index 0000000..1893973 Binary files /dev/null and b/zh/images/icons/openmcp-edge.free differ diff --git a/zh/images/icons/openmcp-edge.svg b/zh/images/icons/openmcp-edge.svg new file mode 100644 index 0000000..3267d35 --- /dev/null +++ b/zh/images/icons/openmcp-edge.svg @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/images/icons/openmcp-sdk.svg b/zh/images/icons/openmcp-sdk.svg new file mode 100644 index 0000000..60f824f --- /dev/null +++ b/zh/images/icons/openmcp-sdk.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/images/icons/openmcp.svg b/zh/images/icons/openmcp.svg new file mode 100644 index 0000000..8a81d2f --- /dev/null +++ b/zh/images/icons/openmcp.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/images/icons/vscode.svg b/zh/images/icons/vscode.svg new file mode 100644 index 0000000..c453e63 --- /dev/null +++ b/zh/images/icons/vscode.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/zh/images/openmcp-default.png b/zh/images/openmcp-default.png new file mode 100644 index 0000000..76a21fe Binary files /dev/null and b/zh/images/openmcp-default.png differ diff --git a/zh/images/openmcp.chatbot.png b/zh/images/openmcp.chatbot.png new file mode 100644 index 0000000..6b50065 Binary files /dev/null and b/zh/images/openmcp.chatbot.png differ diff --git a/zh/images/openmcp.png b/zh/images/openmcp.png new file mode 100644 index 0000000..a0647be Binary files /dev/null and b/zh/images/openmcp.png differ diff --git a/zh/images/opensource.png b/zh/images/opensource.png new file mode 100644 index 0000000..f3d6402 Binary files /dev/null and b/zh/images/opensource.png differ diff --git a/zh/images/public-deploy.png b/zh/images/public-deploy.png new file mode 100644 index 0000000..e3b0bd4 Binary files /dev/null and b/zh/images/public-deploy.png differ diff --git a/zh/index.md b/zh/index.md new file mode 100644 index 0000000..3eaa807 --- /dev/null +++ b/zh/index.md @@ -0,0 +1,131 @@ +--- +# https://vitepress.dev/reference/default-theme-home-page +layout: home + +hero: + name: "OpenMCP" + text: "面向优雅开发者的 MCP 调试器和 SDK" + tagline: 缩短从大语言模型到智能体的最后一公里 + + actions: + - theme: brand + text: OpenMCP 插件 + link: ./plugin-tutorial + - theme: alt + text: openmcp-sdk + link: ./sdk-tutorial + - theme: alt + text: GitHub + link: https://github.com/LSTM-Kirigaya/openmcp-client +features: + - icon: + src: /images/icons/vscode.svg + height: 48px + alt: 集成调试环境 + title: 集成调试环境 + details: 将检查器与 MCP 客户端功能相结合,实现无缝开发和测试 + - icon: + src: /images/icons/openmcp-edge.svg + height: 48px + alt: 提供完整的项目级控制面板 + title: 全面的项目管理 + details: 提供完整的项目级控制面板,实现高效的 MCP 项目监督 + - icon: + src: /images/icons/openmcp-sdk.svg + height: 48px + alt: 提供完整的项目级控制面板 + title: 完整的部署方案 + details: 将测试完成的 agent 通过 openmcp-sdk 部署到您的应用或者服务器上 +--- + +

+ + + +

+为您的 MCP Agent 开发排忧解难 +
+Providing Fun and Convenience for Your MCP Agent Development +

+ + + +
+ + +

+OpenMCP 为谁准备? +
+The Development of OpenMCP is for ... +

+ +
+ + + + + + + +
+ +

+问题解答 +
+Waiting for Your Questions +

+ + + + 正如它的名字一样,OpenMCP 是一个面向开发者的 MCP 调试器和 SDK,致力于降低 AI Agent 的全链路开发成本和开发人员的心智负担。通过 OpenMCP 制作出可以在真实生活场景中解决问题,缩短工作时间的 mcp 工具,或是让工程师与研发科学家更快地交付 demo,并将这份愿景让公众看到,是我们的任务和使命。 + + + 是的,OpenMCP 完全开源,您不仅可以免费使用此产品,也可以一起加入我们,实现你的关于 Agent 的奇思妙想。OpenMCP 的任务是建立起关于 MCP 的生态圈。因为我们认为,MCP 的开发在未来一段时间内会是一项高度定制化的工作,所以当前的重点并不是赶紧出做一个看起来什么都能做的 Agent,而是步步为营做出相关的生态和基础设施。 + + + 如果你试图通过 OpenMCP 开发一款什么都能做的,通用的 AI Agent,你应该做的是把钱全部投资到量子计算机的研发,而不是点开这个网站。记住一句话,这个时代做全领域通用AI Agent,依概率收敛到电信诈骗。 + + +

OpenMCP 是由 LSTM-Kirigaya(锦恢) 最初主导开发的,用于构建 3D 相关工作的 mcp 测试工具。它的主要参与者都是大厂在职员工,高校计算机相关专业的学生、以及一些开源社区的活跃贡献者。

+

身份不重要,我非常喜欢的一句话,送给阁下:“不要回答我你会不会,回答我,你喜不喜欢”。

+ +
+ + 您可以通过 参与 OpenMCP 来了解如何参与 OpenMCP 的维护和开发。通过 资源频道 来获取我们的联系方式。目前主要的社区有三个,QQ群:782833642 、 OpenMCP Discord 频道知乎圈子【OpenMCP 博物馆】 + + + 合作请联系锦恢的个人邮箱:1193466151@qq.com + +
diff --git a/zh/plugin-tutorial/concept.md b/zh/plugin-tutorial/concept.md new file mode 100644 index 0000000..894a056 --- /dev/null +++ b/zh/plugin-tutorial/concept.md @@ -0,0 +1,232 @@ +# MCP 基础概念 + +## 前言 + + + +在 [[what-is-mcp|之前的文章]] 中,我们简单介绍了 MCP 的定义和它的基本组织结构。作为开发者,我们最需要关注的其实是如何根据我们自己的业务和场景定制化地开发我们需要的 MCP 服务器,这样直接接入任何一个 MCP 客户端后,我们都可以给大模型以我们定制出的交互能力。 + +在正式开始教大家如何开发自己的 MCP 服务器之前,我想,或许有必要讲清楚几个基本概念。 + +## Resources, Prompts 和 Tools + +在 [MCP 客户端协议](https://modelcontextprotocol.io/clients) 中,讲到了 MCP 协议中三个非常重要的能力类别: + +- Resouces :定制化地请求和访问本地的资源,可以是文件系统、数据库、当前代码编辑器中的文件等等原本网页端的app 无法访问到的 **静态资源**。额外的 resources 会丰富发送给大模型的上下文,使得 AI 给我们更加精准的回答。 +- Prompts :定制化一些场景下可供 AI 进行采纳的 prompt,比如如果需要 AI 定制化地返回某些格式化内容时,可以提供自定义的 prompts。 +- Tools :可供 AI 使用的工具,它必须是一个函数,比如预定酒店、打开网页、关闭台灯这些封装好的函数就可以是一个 tool,大模型会通过 function calling 的方式来使用这些 tools。 Tools 将会允许 AI 直接操作我们的电脑,甚至和现实世界发生交互。 + +各位拥有前后端开发经验的朋友们,可以将 Resouces 看成是「额外给予大模型的只读权限」,把 Tools 看成是「额外给予大模型的读写权限」。 + +MCP 客户端(比如 Claude Desktop,5ire 等)已经实现好了上述的前端部分逻辑。而具体提供什么资源,具体提供什么工具,则需要各位玩家充分想象了,也就是我们需要开发丰富多彩的 MCP Server 来允许大模型做出更多有意思的工作。 + +不过需要说明的一点是,目前几乎所有大模型采用了 openai 协议作为我们访问大模型的接入点。什么叫 openai 协议呢? + +## openai 协议 + +当我们使用 python 或者 typescript 开发 app 时,往往会安装一个名为 openai 的库,里面填入你需要使用的模型厂商、模型的基础 url、使用的模型类别来直接访问大模型。而各个大模型提供商也必须支持这个库,这套协议。 + +比如我们在 python 中访问 deepseek 的服务就可以这么做: + +```python +from openai import OpenAI + +client = OpenAI(api_key="", base_url="https://api.deepseek.com") + +response = client.chat.completions.create( + model="deepseek-chat", + messages=[ + {"role": "system", "content": "You are a helpful assistant"}, + {"role": "user", "content": "Hello"}, + ], + stream=False +) + +print(response.choices[0].message.content) +``` + +如果你点进这个 create 函数去看,你会发现 openai 协议需要大模型厂家支持的 feature 是非常非常多的: + +```python + @overload + def create( + self, + *, + messages: Iterable[ChatCompletionMessageParam], + model: Union[str, ChatModel], + audio: Optional[ChatCompletionAudioParam] | NotGiven = NOT_GIVEN, + frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, + function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, + functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, + logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, + logprobs: Optional[bool] | NotGiven = NOT_GIVEN, + max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_tokens: Optional[int] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + modalities: Optional[List[Literal["text", "audio"]]] | NotGiven = NOT_GIVEN, + n: Optional[int] | NotGiven = NOT_GIVEN, + parallel_tool_calls: bool | NotGiven = NOT_GIVEN, + prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, + presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, + response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, + seed: Optional[int] | NotGiven = NOT_GIVEN, + service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, + store: Optional[bool] | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, + stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, + temperature: Optional[float] | NotGiven = NOT_GIVEN, + tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, + tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, + top_p: Optional[float] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + web_search_options: completion_create_params.WebSearchOptions | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ChatCompletion: +``` + +从上面的签名中,你应该可以看到几个很熟悉的参数,比如 `temperature`, `top_p`,很多的大模型使用软件中,有的会给你暴露这个参数进行调节。比如在 5ire 中,内容随机度就是 `temperature` 这个参数的图形化显示。 + +
+ +
+ +其实如你所见,一次普普通通调用涉及到的可调控参数是非常之多的。而在所有参数中,你可以注意到一个参数叫做 `tools`: + +```python + @overload + def create( + self, + *, + messages: Iterable[ChatCompletionMessageParam], + model: Union[str, ChatModel], + audio: Optional[ChatCompletionAudioParam] | NotGiven = NOT_GIVEN, + + # 看这里 + tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + ) -> ChatCompletion: +``` + +## tool_calls 字段 + +在上面的 openai 协议中,有一个名为 tools 的参数。 tools 就是要求大模型厂商必须支持 function calling 这个特性,也就是我们提供一部分工具的描述(和 MCP 协议完全兼容的),在 tools 不为空的情况下,chat 函数返回的值中会包含一个特殊的字段 `tool_calls`,我们可以运行下面的我写好的让大模型调用可以查询天气的代码: + +```python +from openai import OpenAI + +client = OpenAI( + api_key="Deepseek API", + base_url="https://api.deepseek.com" +) + +# 定义 tools(函数/工具列表) +tools = [ + { + "type": "function", + "function": { + "name": "get_current_weather", + "description": "获取给定地点的天气", + "parameters": { + "type": "object", + "properties": { + "location": { + "type": "string", + "description": "城市,比如杭州,北京,上海", + } + }, + "required": ["location"], + }, + }, + } +] + +response = client.chat.completions.create( + model="deepseek-chat", + messages=[ + {"role": "system", "content": "你是一个很有用的 AI"}, + {"role": "user", "content": "今天杭州的天气是什么?"}, + ], + tools=tools, # 传入 tools 参数 + tool_choice="auto", # 可选:控制是否强制调用某个工具 + stream=False, +) + +print(response.choices[0].message) +``` + +运行上述代码,它的返回如下: + +```python +ChatCompletionMessage( + content='', + refusal=None, + role='assistant', + annotations=None, + audio=None, + function_call=None, + tool_calls=[ + ChatCompletionMessageToolCall( + id='call_0_baeaba2b-739d-40c2-aa6c-1e61c6d7e855', + function=Function( + arguments='{"location":"杭州"}', + name='get_current_weather' + ), + type='function', + index=0 + ) + ] +) +``` + +可以看到上面的 `tool_calls` 给出了大模型想要如何去使用我们给出的工具。需要说明的一点是,收到上下文的限制,目前一个问题能够让大模型调取的工具上限一般不会超过 100 个,这个和大模型厂商的上下文大小有关系。奥,对了,友情提示,当你使用 MCP 客户端在使用大模型解决问题时,同一时间激活的 MCP Server 越多,消耗的 token 越多哦 :D + +而目前 openai 的协议中,tools 是只支持函数类的调用。而函数类的调用往往是可以模拟出 Resources 的效果的。比如取资源,你可以描述为一个 tool。因此在正常情况下,如果大家要开发 MCP Server,最好只开发 Tools,另外两个 feature 还暂时没有得到广泛支持。 + + + +## 使用 Inspector 进行调试 + +Claude 原生提供的 MCP 协议可以通过官方提供的 Inspector 进行调试,对于 [[first-mcp|你的第一个 MCP]] 中的例子,可以如下进行调试,在命令行输入如下命令启动 Inspector: + +```bash +mcp dev main.py +``` + +这会启动一个前端服务器,并打开 `http://localhost:5173/` 后我们可以看到 inspector 的调试界面,先点击左侧的 `Connect` 来运行我们的 server.py 并通过 stdio 为通信管道和 web 建立通信。 + +Fine,可以开始愉快地进行调试了,Inspector 主要给了我们三个板块,分别对应 Resources,Prompts 和 Tools。 + +先来看 Resources,点击「Resource Templates」可以罗列所有注册的 Resource,比如我们上文定义的 `get_greeting`,你可以通过输入参数运行来查看这个函数是否正常工作。(因为一般情况下的这个资源协议是会访问远程数据库或者微服务的) + +
+ +
+ +Prompts 端就比较简单了,直接输入预定义参数就能获取正常的返回结果。 + +
+ +
+ +Tools 端将会是我们后面调试的核心。在之前的章节我们讲过了,MCP 协议中的 Prompts 和 Resources 目前还没有被 openai 协议和各大 MCP 客户端广泛支持,因此,我们主要的服务端业务都应该是在写 tools。 + +我们此处提供的 tool 是实现一个简单的加法,它非常简单,我们输入 1 和 2 就可以直接看到结果是 3。我们后续会开发一个可以访问天气预报的 tool,那么到时候就非常需要一个这样的窗口来调试我们的天气信息获取是否正常了。 + +
+ +
+ + + +## 结语 + +这篇文章,我们简单了解了 MCP 内部的一些基本概念,我认为这些概念对于诸位开发一个 MCP 服务器是大有裨益的,所以我认为有必要先讲一讲。 + +下面的文章中,我将带领大家探索 MCP 的奇境,一个属于 AI Agent 的时代快要到来了。 diff --git a/zh/plugin-tutorial/examples/go-neo4j-sse.md b/zh/plugin-tutorial/examples/go-neo4j-sse.md new file mode 100644 index 0000000..24a11db --- /dev/null +++ b/zh/plugin-tutorial/examples/go-neo4j-sse.md @@ -0,0 +1,488 @@ +# go 实现 neo4j 的只读 mcp 服务器 (SSE) + +[本期教程视频](https://www.bilibili.com/video/BV1g8TozyEE7/) + +## 前言 + +本篇教程,演示一下如何使用 go 语言写一个可以访问 neo4j 数据库的 mcp 服务器。实现完成后,我们不需要写任何 查询代码 就能通过询问大模型了解服务器近况。 + +不同于之前的连接方式,这次,我们将采用 SSE 的方式来完成服务器的创建和连接。 + +本期教程的代码:https://github.com/LSTM-Kirigaya/openmcp-tutorial/tree/main/neo4j-go-server + +建议下载本期的代码,因为里面有我为大家准备好的数据库文件。要不然,你们得自己 mock 数据了。 + + + +## 1. 准备 + +项目结构如下: + +```bash +📦neo4j-go-server + ┣ 📂util + ┃ ┗ 📜util.go # 工具函数 + ┣ 📜main.go # 主函数 + ┗ 📜neo4j.json # 数据库连接的账号密码 +``` + +我们先创建一个 go 项目: + +```bash +mkdir neo4j-go-server +cd neo4j-go-server +go mod init neo4j-go-server +``` + + + +## 2. 完成数据库初始化 + +### 2.1 安装 neo4j + +首先,根据我的教程在本地或者服务器配置一个 neo4j 数据库,这里是是教程,你只需要完成该教程的前两步即可: [neo4j 数据库安装与配置](https://kirigaya.cn/blog/article?seq=199)。将 bin 路径加入环境变量,并且设置的密码设置为 openmcp。 + +然后在 main.go 同级下创建 neo4j.json,填写 neo4j 数据库的连接信息: + +```json +{ + "url" : "neo4j://localhost:7687", + "name" : "neo4j", + "password" : "openmcp" +} +``` + +### 2.2 导入事先准备好的数据 + +安装完成后,大家可以导入我实现准备好的数据,这些数据是我的个人网站上部分数据脱敏后的摘要,大家可以随便使用,下载链接:[neo4j.db](https://github.com/LSTM-Kirigaya/openmcp-tutorial/releases/download/neo4j.db/neo4j.db)。下载完成后,运行下面的命令: + +```bash +neo4j stop +neo4j-admin load --database neo4j --from neo4j.db --force +neo4j start +``` + +然后,我们登录数据库就能看到我准备好的数据啦: + +```bash +cypher-shell -a localhost -u neo4j -p openmcp +``` + +
+ +
+ +### 2.3 验证 go -> 数据库连通性 + +为了验证数据库的连通性和 go 的数据库驱动是否正常工作,我们需要先写一段数据库访问的最小系统。 + +先安装 neo4j 的 v5 版本的 go 驱动: + +```bash +go get github.com/neo4j/neo4j-go-driver/v5 +``` + +在 `util.go` 中添加以下代码: + +```go +package util + +import ( + "context" + "encoding/json" + "fmt" + "os" + + "github.com/neo4j/neo4j-go-driver/v5/neo4j" +) + +var ( + Neo4jDriver neo4j.DriverWithContext +) + +// 创建 neo4j 服务器的连接 +func CreateNeo4jDriver(configPath string) (neo4j.DriverWithContext, error) { + jsonString, _ := os.ReadFile(configPath) + config := make(map[string]string) + + json.Unmarshal(jsonString, &config) + // fmt.Printf("url: %s\nname: %s\npassword: %s\n", config["url"], config["name"], config["password"]) + + var err error + Neo4jDriver, err = neo4j.NewDriverWithContext( + config["url"], + neo4j.BasicAuth(config["name"], config["password"], ""), + ) + if err != nil { + return Neo4jDriver, err + } + return Neo4jDriver, nil +} + + +// 执行只读的 cypher 查询 +func ExecuteReadOnlyCypherQuery( + cypher string, +) ([]map[string]any, error) { + session := Neo4jDriver.NewSession(context.TODO(), neo4j.SessionConfig{ + AccessMode: neo4j.AccessModeRead, + }) + + defer session.Close(context.TODO()) + + result, err := session.Run(context.TODO(), cypher, nil) + if err != nil { + fmt.Println(err.Error()) + return nil, err + } + + var records []map[string]any + for result.Next(context.TODO()) { + records = append(records, result.Record().AsMap()) + } + + return records, nil +} +``` + +main.go 中添加以下代码: + +```go +package main + +import ( + "fmt" + "neo4j-go-server/util" +) + +var ( + neo4jPath string = "./neo4j.json" +) + +func main() { + _, err := util.CreateNeo4jDriver(neo4jPath) + if err != nil { + fmt.Println(err) + return + } + + fmt.Println("Neo4j driver created successfully") +} +``` + +运行主程序来验证数据库的连通性: + +```bash +go run main.go +``` + +如果输出了 `Neo4j driver created successfully`,则说明数据库的连通性验证通过。 + + + +## 3. 实现 mcp 服务器 + +go 的 mcp 的 sdk 最为有名的是 mark3labs/mcp-go 了,我们就用这个。 + +> mark3labs/mcp-go 的 demo 在 https://github.com/mark3labs/mcp-go,非常简单,此处直接使用即可。 + +先安装 + +```bash +go get github.com/mark3labs/mcp-go +``` + +然后在 `main.go` 中添加以下代码: + +```go +// ... existing code ... + +var ( + addr string = "localhost:8083" +) + +func main() { + // ... existing code ... + + s := server.NewMCPServer( + "只读 Neo4j 服务器", + "0.0.1", + server.WithToolCapabilities(true), + ) + + srv := server.NewSSEServer(s) + + // 定义 executeReadOnlyCypherQuery 这个工具的 schema + executeReadOnlyCypherQuery := mcp.NewTool("executeReadOnlyCypherQuery", + mcp.WithDescription("执行只读的 Cypher 查询"), + mcp.WithString("cypher", + mcp.Required(), + mcp.Description("Cypher 查询语句,必须是只读的"), + ), + ) + + // 将真实函数和申明的 schema 绑定 + s.AddTool(executeReadOnlyCypherQuery, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + args, ok := request.Params.Arguments.(map[string]interface{}) + if !ok { + return mcp.NewToolResultText(""), fmt.Errorf("invalid arguments type") + } + cypher, ok := args["cypher"].(string) + if !ok { + return mcp.NewToolResultText(""), fmt.Errorf("cypher argument is not a string") + } + result, err := util.ExecuteReadOnlyCypherQuery(cypher) + + fmt.Println(result) + + if err != nil { + return mcp.NewToolResultText(""), err + } + + return mcp.NewToolResultText(fmt.Sprintf("%v", result)), nil + }) + + // 在 http://localhost:8083/sse 开启服务 + fmt.Printf("Server started at http://%s/sse\n", addr) + srv.Start(addr) +} +``` + +go run main.go 运行上面的代码,你就能看到如下信息: + +``` +Neo4j driver created successfully +Server started at http://localhost:8083/sse +``` + +说明我们的 mcp 服务器在本地的 8083 上启动了。 + + + +## 4. 通过 openmcp 来进行调试 + +### 4.1 添加工作区 sse 调试项目 + +接下来,我们来通过 openmcp 进行调试,先点击 vscode 左侧的 openmcp 图标进入控制面板,如果你是下载的 https://github.com/LSTM-Kirigaya/openmcp-tutorial/tree/main/neo4j-go-server 这个项目,那么你能看到【MCP 连接(工作区)】里面已经有一个创建好的调试项目【只读 Neo4j 服务器】了。如果你是完全自己做的这个项目,可以通过下面的按钮添加连接,选择 sse 后填入 http://localhost:8083/sse,oauth 空着不填即可。 + +
+ +
+ +### 4.2 测试工具 + +第一次调试 mcp 服务器要做的事情一定是先调通 mcp tool,新建标签页,选择 tool,点击下图的工具,输入 `CALL db.labels() YIELD label RETURN label`,这个语句是用来列出所有节点类型的。如果输出下面的结果,说明当前的链路生效,没有问题。 + +
+ +
+ + +### 4.3 摸清大模型功能边界,用提示词来封装我们的知识 + +然后,让我们做点有趣的事情吧!我们接下来要测试一下大模型的能力边界,因为 neo4j 属于特种数据库,通用大模型不一定知道怎么用它。新建标签页,点击「交互测试」,我们先问一个简单的问题: + +``` +帮我找出最新的 10 条评论 +``` + +结果如下: + +
+ +
+ +可以看到,大模型查询的节点类型就是错误的,在我提供的例子中,代表评论的节点是 BlogComment,而不是 Comment。也就是说,大模型并不掌握进行数据库查询的通用方法论。这就是我们目前知道的它的能力边界。我们接下来要一步一步地注入我们的经验和知识,唔姆,通过 system prompt 来完成。 + +### 4.4 教大模型找数据库节点 + +好好想一下,作为工程师的我们是怎么知道评论的节点是 BlogComment?我们一般是通过罗列当前数据库的所有节点的类型来从命名中猜测的,比如,对于这个数据库,我一般会先输入如下的 cypher 查询: + +```sql +CALL db.labels() YIELD label RETURN label +``` + +它的输出就在 4.2 的图中,如果你的英文不错,也能看出来 BlogComment 大概率是代表博客评论的节点。好了,那么我们将这段方法论注入到 system prompt 中,从而封装我们的这层知识,点击下图的下方的按钮,进入到【系统提示词】: + +
+ +
+ + +新建提示词【neo4j】,输入: + +``` +你是一个善于进行neo4j查询的智能体,对于用户要求的查询请求,你并不一定知道对应的数据库节点是什么,这个时候,你需要先列出所有的节点类型,然后从中找到你认为最有可能是匹配用户询问的节点。比如用户问你要看符合特定条件的「文章」,你并不知道文章的节点类型是什么,这个时候,你就需要先列出所有的节点。 +``` + +点击保存,然后在【交互测试】中,重复刚才的问题: + +``` +帮我找出最新的 10 条评论 +``` + +大模型的回答如下: + +
+ +
+ +诶?怎么说,是不是好了很多了?大模型成功找到了 BlogComment 这个节点,然后返回了对应的数据。 + +但是其实还是不太对,因为我们要求的说最新的 10 条评论,但是大模型返回的其实是最早的 10 条评论,我们点开大模型的调用细节就能看到,大模型是通过 `ORDER BY comment.createdAt` 来实现的,但是问题是,在我们的数据库中,记录一条评论何时创建的字段并不是 createdAt,而是 createdTime,这意味着大模型并不知道自己不知道节点的字段,从而产生了「幻觉」,瞎输入了一个字段。 + +大模型是不会显式说自己不知道的,锦恢研究生关于 OOD 的一项研究可以说明这件事的本质原因:[EDL(Evidential Deep Learning) 原理与代码实现](https://kirigaya.cn/blog/article?seq=154),如果阁下的好奇心能够配得上您的数学功底,可以一试这篇文章。总之,阁下只需要知道,正因为大模型对自己不知道的东西会产生幻觉,所以才有我们得以注入经验的操作空间。 + +### 4.5 教大模型找数据库节点的字段 + +通过上面的尝试,我们知道我们距离终点只剩一点了,那就是告诉大模型,我们的数据库中,记录一条评论何时创建的字段并不是 createdAt,而是 createdTime。 + +对于识别字段的知识,我们改良一下刚刚的系统提示词下: + +``` +你是一个善于进行neo4j查询的智能体,对于用户要求的查询请求,你并不一定知道对应的数据库节点是什么,这个时候,你需要先列出所有的节点类型,然后从中找到你认为最有可能是匹配用户询问的节点。比如用户问你要看符合特定条件的「文章」,你并不知道文章的节点类型是什么,这个时候,你就需要先列出所有的节点。 + +对于比较具体的查询,你需要先查询单个事例来看一下当前类型有哪些字段。比如用户问你最新的文章,你是不知道文章节点的哪一个字段代表 「创建时间」的,因此,你需要先列出一到两个文章节点,看一下里面有什么字段,然后再创建查询查看最新的10篇文章。 +``` + +结果如下: + +
+ +
+ + +是不是很完美? + +通过使用 openmcp 调试,我们可以通过 system prompt + mcp server 来唯一确定一个 agent 的表现行为。 + + + +## 5. 扩充 mcp 服务器的原子技能 + +在上面的例子中,虽然我们通过 system prompt 注入了我们的经验和知识,但是其实你会发现这些我们注入的行为,比如「查询所有节点类型」和「获取一个节点的所有字段」,是不是流程很固定?但是 system prompt 是通过自然语言编写的,它具有语言特有的模糊性,我们无法保证它一定是可以拓展的。那么除了 system prompt,还有什么方法可以注入我们的经验与知识呢?有的,兄弟,有的。 + +在这种流程固定,而且这个操作也非常地容易让「稍微有点经验的人」也能想到的情况下,除了使用 system prompt 外,我们还有一个方法可以做到更加标准化地注入知识,也就是把上面的这些个流程写成额外的 mcp tool。这个方法被我称为「原子化扩充」(Atomization Supplement)。 + +所谓原子化扩充,也就是增加额外的 mcp tool,这些 tool 在功能层面是「原子化」的。 + +> 满足如下条件之一的 tool,被称为 原子 tool (Atomic Tool): +> tool 无法由更加细粒度的功能通过有限组合得到 +> 组成得到 tool 的更加细粒度的功能,大模型并不会完全使用,或者使用不可靠 (比如汇编语言,比如 DOM 查询) + +扩充额外的原子 tool,能够让大模型知道 “啊!我还有别的手段可以耍!” ,那么只要 description 比较恰当,大模型就能够使用它们来获得额外的信息,而不是产生「幻觉」让任务失败。 + +对于上面的一整套流程,我们目前知道了如下两个技能大模型是会产生「幻觉」的: + +1. 获取一个节点类别的标签(询问评论,大模型没说自己不知道什么是评论标签,而是直接使用了 Comment,但是实际的评论标签是 BlogComment) +2. 获取一个节点类别的字段(询问最新评论,大模型选择通过 createAt 排序,但是记录 BlogComment 创建时间的字段是 createTime) + +在之前,我们通过了 system prompt 来完成了信息的注入,现在,丢弃你的 system prompt 吧!我们来玩点更加有趣的游戏。在刚刚的 util.go 中,我们针对上面的两个幻觉,实现两个额外的函数 (经过测试,cursor或者trae能完美生成下面的代码,可以不用自己写): + +```go +// 获取所有的节点类型 +func GetAllNodeTypes() ([]string, error) { + cypher := "MATCH (n) RETURN DISTINCT labels(n) AS labels" + result, err := ExecuteReadOnlyCypherQuery(cypher) + if err!= nil { + return nil, err + } + var nodeTypes []string + for _, record := range result { + labels := record["labels"].([]any) + for _, label := range labels { + nodeTypes = append(nodeTypes, label.(string)) + } + } + return nodeTypes, nil +} + +// 获取一个节点的字段示范 +func GetNodeFields(nodeType string) ([]string, error) { + cypher := fmt.Sprintf("MATCH (n:%s) RETURN keys(n) AS keys LIMIT 1", nodeType) + result, err := ExecuteReadOnlyCypherQuery(cypher) + if err!= nil { + return nil, err + } + var fields []string + for _, record := range result { + keys := record["keys"].([]any) + for _, key := range keys { + fields = append(fields, key.(string)) + } + } + return fields, nil +} +``` + +在 main.go 中完成它们的 schema 的申明和 tool 的注册: + +```go +// ... existing code ... + + getAllNodeTypes := mcp.NewTool("getAllNodeTypes", + mcp.WithDescription("获取所有的节点类型"), + ) + + getNodeField := mcp.NewTool("getNodeField", + mcp.WithDescription("获取节点的字段"), + mcp.WithString("nodeLabel", + mcp.Required(), + mcp.Description("节点的标签"), + ), + ) + + // 注册对应的工具到 schema 上 + s.AddTool(getAllNodeTypes, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + result, err := util.GetAllNodeTypes() + + fmt.Println(result) + + if err != nil { + return mcp.NewToolResultText(""), err + } + + return mcp.NewToolResultText(fmt.Sprintf("%v", result)), nil + }) + + s.AddTool(getNodeField, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + args, ok := request.Params.Arguments.(map[string]interface{}) + if !ok { + return mcp.NewToolResultText(""), fmt.Errorf("invalid arguments type") + } + nodeLabel, ok := args["nodeLabel"].(string) + if !ok { + return mcp.NewToolResultText(""), fmt.Errorf("nodeLabel argument is not a string") + } + result, err := util.GetNodeFields(nodeLabel) + + fmt.Println(result) + + if err!= nil { + return mcp.NewToolResultText(""), err + } + + return mcp.NewToolResultText(fmt.Sprintf("%v", result)), nil + }) + +// ... existing code ... +``` + +重新运行 sse 服务器,然后直接询问大模型,此时,我们取消使用 system prompt(创建一个空的,或者直接把当前的 prompt 删除),询问结果如下: + +
+ +
+ + +可以看到,在没有 system prompt 的情况下,大模型成功执行了这个过程,非常完美。 + +## 总结 + +这期教程,带大家使用 go 走完了 mcp sse 的连接方式,并且做出了一个「只读 neo4j 数据库」的 mcp,通过这个 mcp,我们可以非常方便地用自然语言查询数据库的结果,而不需要手动输入 cypher。 + +对于部分情况下,大模型因为「幻觉」问题而导致的任务失败,我们通过一步步有逻辑可遵循的方法论,完成了 system prompt 的调优和知识的封装。最终,通过范式化的原子化扩充的方式,将这些知识包装成了更加完善的 mcp 服务器。这样,任何人都可以直接使用你的 mcp 服务器来完成 neo4j 数据库的自然语言查询了。 + +最后,觉得 openmcp 好用的米娜桑,别忘了给我们的项目点个 star:https://github.com/LSTM-Kirigaya/openmcp-client + +想要和我进一步交流 OpenMCP 的朋友,可以进入我们的交流群(github 项目里面有) \ No newline at end of file diff --git a/zh/plugin-tutorial/examples/java-es-http.md b/zh/plugin-tutorial/examples/java-es-http.md new file mode 100644 index 0000000..e69de29 diff --git a/zh/plugin-tutorial/examples/mcp-examples.md b/zh/plugin-tutorial/examples/mcp-examples.md new file mode 100644 index 0000000..56f4714 --- /dev/null +++ b/zh/plugin-tutorial/examples/mcp-examples.md @@ -0,0 +1,28 @@ +--- +next: + text: python 实现天气信息 mcp 服务器 (STDIO) + link: '/plugin-tutorial/examples/python-simple-stdio' +--- + +# MCP 服务器开发案例 + +## Python +- [python 实现天气信息 mcp 服务器 (STDIO)](./python-simple-stdio) +- [python 实现进行通用表单填充 的 mcp (STDIO)](./python-form-stdio) +- [python 实现基于 blender 的 mcp (STDIO)](./python-blender-stdio) +- [python 实现 cadence EDA 的 mcp (STDIO)](./python-cadence-stdio) +- 基于 ffmpeg mcp 实现通过对话的视频剪辑 +- 基于 rag mcp 实现知识库的注入 +- 实现 Stable Diffusion 的 MCP 服务器 + +## Nodejs +- [typescript 实现基于 crawl4ai 的超级网页爬虫 mcp (STDIO)](./typescript-crawl4ai-stdio) + +## Go +- [go 实现 neo4j 的只读 mcp 服务器 (SSE)](./go-neo4j-sse) + +## Java +- [java 实现文档数据库的只读 mcp (HTTP)](./java-es-http) + +## 认证 +- [SSE 方式的 OAuth2 认证 mcp 服务器示例](./sse-oauth2) \ No newline at end of file diff --git a/zh/plugin-tutorial/examples/python-blender-stdio.md b/zh/plugin-tutorial/examples/python-blender-stdio.md new file mode 100644 index 0000000..ec11cc4 --- /dev/null +++ b/zh/plugin-tutorial/examples/python-blender-stdio.md @@ -0,0 +1,436 @@ +# python 实现天气信息 mcp 服务器 + +## hook + +等等,开始前,先让我们看一个小例子,假设我下周要去明日方舟锈影新生的漫展,所以我想要知道周六杭州的天气,于是我问大模型周六的天气,结果大模型给了我如下的回复: + +
+ +
+ +这可不行,相信朋友们也经常遇到过这样的情况,大模型总是会“授人以渔”,但是有的时候,我们往往就是想要直接知道最终结果,特别是一些无聊的生活琐事。 + +其实实现天气预报的程序也很多啦,那么有什么方法可以把写好的天气预报的程序接入大模型,让大模型告诉我们真实的天气情况,从而选择明天漫展的穿搭选择呢? + +如果直接写函数用 function calling 显得有点麻烦,这里面涉及到很多麻烦的技术细节需要我们商榷,比如大模型提供商的API调用呀,任务循环的搭建呀,文本渲染等等,从而浪费我们宝贵的时间。而 MCP 给了我们救赎之道,今天这期教程,就教大家写一个简单的 MCP 服务器,可以让大模型拥有得知天气预报的能力。 + + + +## 前言 + +👉 [上篇导航](https://zhuanlan.zhihu.com/p/32593727614) + +在上篇,我们简单讲解了 MCP 的基础,在这一篇,我们将正式开始着手开发我们自己的 MCP 服务器,从而将现成的应用,服务,硬件等等接入大模型。从而走完大模型到赋能终端应用的最后一公里。 + +工欲善其事,必先利其器。为了更加优雅快乐地开发 MCP 服务器,我们需要一个比较好的测试工具,允许我们在开发的过程看到程序的变化,并且可以直接接入大模型验证工具的有效性。 + +于是,我在前不久开源了一款一体化的 MCP 测试开发工具 —— OpenMCP,[全网第一个 MCP 服务器一体化开发测试软件 OpenMCP 发布!](https://zhuanlan.zhihu.com/p/1894785817186121106) + +> OpenMCP QQ 群 782833642 + +OpenMCP 开源链接:https://github.com/LSTM-Kirigaya/openmcp-client + +求个 star :D + +### 第一个 MCP 项目 + +事已至此,先 coding 吧 :D + +在打开 vscode 或者 trae 之前,先安装基本的 uv 工具,uv 是一款社区流行的版本管理工具,你只需要把它理解为性能更好的 conda 就行了。 + +我们先安装 uv,如果您正在使用 anaconda,一定要切换到 base 环境,再安装: + +```bash +pip install uv +``` + +安装完成后,运行 uv + +```bash +uv +``` + +没有报错就说明成功。uv 只会将不可以复用的依赖安装在本地,所以使用 anaconda 的朋友不用担心,uv 安装的依赖库会污染你的 base,我们接下来使用 uv 来创建一个基础的 python 项目 + +```bash +mkdir simple-mcp && cd simple-mcp +uv init +uv add mcp "mcp[cli]" +``` + +然后我们打开 vscode 或者 trae,在插件商城找到并下载 OpenMCP 插件 + +
+ +
+ +先制作一个 MCP 的最小程序: + +文件名:simple_mcp.py + +```python +from mcp.server.fastmcp import FastMCP + +mcp = FastMCP('锦恢的 MCP Server', version="11.45.14") + +@mcp.tool( + name='add', + description='对两个数字进行实数域的加法' +) +def add(a: int, b: int) -> int: + return a + b + +@mcp.resource( + uri="greeting://{name}", + name='greeting', + description='用于演示的一个资源协议' +) +def get_greeting(name: str) -> str: + # 访问处理 greeting://{name} 资源访问协议,然后返回 + # 此处方便起见,直接返回一个 Hello,balabala 了 + return f"Hello, {name}!" + +@mcp.prompt( + name='translate', + description='进行翻译的prompt' +) +def translate(message: str) -> str: + return f'请将下面的话语翻译成中文:\n\n{message}' + +@mcp.tool( + name='weather', + description='获取指定城市的天气信息' +) +def get_weather(city: str) -> str: + """模拟天气查询协议,返回格式化字符串""" + return f"Weather in {city}: Sunny, 25°C" + +@mcp.resource( + uri="user://{user_id}", + name='user_profile', + description='获取用户基本信息' +) +def get_user_profile(user_id: str) -> dict: + """模拟用户协议,返回字典数据""" + return { + "id": user_id, + "name": "张三", + "role": "developer" + } + +@mcp.resource( + uri="book://{isbn}", + name='book_info', + description='通过ISBN查询书籍信息' +) +def get_book_info(isbn: str) -> dict: + """模拟书籍协议,返回结构化数据""" + return { + "isbn": isbn, + "title": "Python编程:从入门到实践", + "author": "Eric Matthes" + } + +@mcp.prompt( + name='summarize', + description='生成文本摘要的提示词模板' +) +def summarize(text: str) -> str: + """返回摘要生成提示词""" + return f"请用一句话总结以下内容:\n\n{text}" +``` + +我们试着运行它: + + +```bash +uv run mcp run simple_mcp.py +``` + +如果没有报错,但是卡住了,那么说明我们的依赖安装没有问题,按下 ctrl c 或者 ctrl z 退出即可。 + +在阁下看起来,这些函数都简单到毫无意义,但是请相信我,我们总需要一些简单的例子来通往最终的系统。 + +### Link, Start! + +如果你下载了 OpenMCP 插件,那么此时你就能在打开的 python 编辑器的右上角看到 OpenMCP 的紫色图标,点击它就能启动 OpenMCP,调试当前的 MCP 了。 + +
+ +
+ +默认是以 STDIO 的方式启动,默认运行如下的命令: + +```bash +uv run mcp run <当前打开的 python 文件的相对路径> +``` + +所以你需要保证已经安装了 mcp 脚手架,也就是 `uv add mcp "mcp[cli]"`。 + +打开后第一件事就是先看左下角连接状态,确保是绿色的,代表当前 OpenMCP 和你的 MCP 服务器已经握手成功。 + +
+ +
+ +如果连接成功,此时连接上方还会显示你当前的 MCP 服务器的名字,光标移动上去还能看到版本号。这些信息由我们如下的代码定义: + +```python +mcp = FastMCP('锦恢的 MCP Server', version="11.45.14") +``` + +这在我们进行版本管理的时候会非常有用。请善用这套系统。 + + +如果连接失败,可以点击左侧工具栏的第二个按钮,进入连接控制台,查看错误信息,或是手动调整连接命令: + +
+ +
+ +### 初识 OpenMCP + +接下来,我来简单介绍一下 OpenMCP 的基本功能模块,如果一开始,你的屏幕里什么也没有,先点击上面的加号创建一个新的标签页,此处页面中会出现下图屏幕中的四个按钮 + +
+ +
+ +放大一点 + +
+ +
+ +前三个,资源、提词和工具,分别用于调试 MCP 中的三个对应项目,也就是 Resources,Prompts 和 Tools,这三个部分的使用,基本和 MCP 官方的 Inspector 工具是一样的,那是自然,我就照着这几个抄的,诶嘿。 + +
+ +
+ +然后第四个按钮「交互测试」,它是一个我开发的 MCP 客户端,其实就是一个对话窗口,你可以无缝衔接地直接在大模型中测试你当前的 MCP 服务器的功能函数。 + +
+ +
+ + +目前我暂时只支持 tools 的支持,因为 prompts 和 resources 的我还没有想好,(resource 感觉就是可以当成一个 tool),欢迎大家进群和我一起讨论:QQ群 782833642 + + + +## 开始调试天气函数 + +### 工具调试 + +还记得我们一开始给的 mcp 的例子吗?我们可以通过 OpenMCP 来快速调试这里面写的函数,比如我们本期的目标,写一个天气预报的 MCP,那么假设我们已经写好了一个天气预报的函数了,我们把它封装成一个 tool: + +```python +@mcp.tool( + name='weather', + description='获取指定城市的天气信息' +) +def get_weather(city: str) -> str: + """模拟天气查询协议,返回格式化字符串""" + return f"Weather in {city}: Sunny, 25°C" +``` + +当然,它现在是没有意义的,因为就算把黑龙江的城市ID输入,它也返回 25 度,但是这些都不重要,我想要带阁下先走完整套流程。建立自上而下的感性认知比死抠细节更加容易让用户学懂。 + +那么我们现在需要调试这个函数,打开 OpenMCP,新建一个「工具」调试项目 + +
+ +
+ +然后此时,你在左侧的列表可以看到 weather 这个工具,选择它,然后在右侧的输入框中随便输入一些东西,按下回车(或者点击「运行」),你能看到如下的响应: + +
+ +
+ +看到我们函数 return 的字符串传过来了,说明没问题,链路通了。 + +### 交互测试 + +诶?我知道你编程很厉害,但是,在噼里啪啦快速写完天气预报爬虫前,我们现在看看我们要如何把已经写好的工具注入大模型对话中。为了使用大模型,我们需要先选择大模型和对应的 API,点击左侧工具栏的第三个按钮,进入 API 模块,选择你想要使用的大模型运营商、模型,填写 API token,然后点击下面的「保存」 + +
+ +
+ +再新建一个标签页,选择「交互测试」,此时,我们就可以直接和大模型对话了,我们先看看没有任何工具注入的大模型会如何回应天气预报的问题,点击最下侧工具栏从左往右第三个按钮,进入工具选择界面,选择「禁用所有工具」 + +
+ +
+ +点击「关闭」后,我们问大模型一个问题: + +``` +请问杭州的温度是多少? +``` + +
+ +
+ +可以看到,大模型给出了和文章开头一样的回答。非常敷衍,因为它确实无法知道。 + +此处,我们再单独打开「weather」工具: + +
+ +
+ +问出相同的问题: + +
+ +
+ +可以看到,大模型给出了回答是 25 度,还有一些额外的推导信息。 + +我们不妨关注一些细节,首先,大模型并不会直接回答问题,而是会先去调用 weather 这个工具,调用参数为: + +```json +{ + "city": "杭州" +} +``` + +然后,我们的 MCP 服务器给出了响应: + +``` +Weather in 杭州: Sunny, 25°C +``` + +从而,最终大模型才根据这些信息给出了最终的回答。也就是,这个过程我们实际调用了两次大模型的服务。而且可以看到两次调用的输入 token 数量都非常大,这是因为 OpenMCP 会将函数调用以 JSON Schema 的形式注入到请求参数中,weather 这个工具的 JSON Schema 如下图的右侧的 json 所示: + +
+ +
+ +然后支持 openai 协议的大模型厂商都会针对这样的信息进行 function calling,所以使用了工具的大模型请求的输入 token 数量都会比较大。但是不需要担心,大部分厂商都实现了 KV Cache,对相同前缀的输入存在缓存,缓存命中部分的费用开销是显著低于普通的 输入输出 token 价格的。OpenMCP 在每个回答的下面都表明了当次请求的 输入 token,输出 token,总 token 和 缓存命中率。 + +其中 + +- 「总 token」 = 「输入 token」 + 「输出 token」 + +- 「缓存命中率」 = 「缓存命令的 token」 / 「输入 token」 + +> 没错,缓存命中率 是对于输入 token 的概念,输出 token 是没有 缓存命中率这个说法的。 + +在后续的开发中,你可以根据这些信息来针对性地对你的服务或者 prompt 进行调优。 + +### 完成一个真正的天气预报吧! + +当然,这些代码也非常简单,直接让大模型生成就行了(其实大模型是无法生成免 API 的 python 获取天气的代码的,我是直接让大模型把我个人网站上天气预报的 go 函数翻译了一下) + +我直接把函数贴上来了: + +```python +import requests +import json +from typing import NamedTuple, Optional + +class CityWeather(NamedTuple): + city_name_en: str + city_name_cn: str + city_code: str + temp: str + wd: str + ws: str + sd: str + aqi: str + weather: str + +def get_city_weather_by_city_name(city_code: str) -> Optional[CityWeather]: + """根据城市名获取天气信息""" + + if not city_code: + print(f"找不到{city_code}对应的城市") + return None + + try: + # 构造请求URL + url = f"http://d1.weather.com.cn/sk_2d/{city_code}.html" + + # 设置请求头 + headers = { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.0.0", + "Host": "d1.weather.com.cn", + "Referer": "http://www.weather.com.cn/" + } + + # 发送HTTP请求 + response = requests.get(url, headers=headers) + response.raise_for_status() + + # 解析JSON数据 + # 解析JSON数据前先处理编码问题 + content = response.text.encode('latin1').decode('unicode_escape') + json_start = content.find("{") + json_str = content[json_start:] + + weather_data = json.loads(json_str) + + # 构造返回对象 + return CityWeather( + city_name_en=weather_data.get("nameen", ""), + city_name_cn=weather_data.get("cityname", "").encode('latin1').decode('utf-8'), + city_code=weather_data.get("city", ""), + temp=weather_data.get("temp", ""), + wd=weather_data.get("wd", "").encode('latin1').decode('utf-8'), + ws=weather_data.get("ws", "").encode('latin1').decode('utf-8'), + sd=weather_data.get("sd", ""), + aqi=weather_data.get("aqi", ""), + weather=weather_data.get("weather", "").encode('latin1').decode('utf-8') + ) + + except Exception as e: + print(f"获取天气信息失败: {str(e)}") + return None + +from mcp.server.fastmcp import FastMCP + +mcp = FastMCP('weather', version="0.0.1") + +@mcp.tool( + name='get_weather_by_city_code', + description='根据城市天气预报的城市编码 (int),获取指定城市的天气信息' +) +def get_weather_by_code(city_code: int) -> str: + """模拟天气查询协议,返回格式化字符串""" + city_weather = get_city_weather_by_city_name(city_code) + return str(city_weather) +``` + + +这里有几个点一定要注意: + +1. 如果你的输入参数是数字,就算是城市编码这种比较长的数字,请一定定义成 int,因为 mcp 底层的是要走 JSON 正反序列化的,而 "114514" 这样的字符串会被 JSON 反序列化成 114514,而不是 "114514" 这个字符串。你实在要用 str 来表示一个很长的数字,那么就在前面加一个前缀,比如 "code-114514",避免被反序列化成数字,从而触发 mcp 内部的 type check error +2. tool 的 name 请按照 python 的变量命名要求进行命名,否则部分大模型厂商会给你报错。 + +好,我们先测试一下: + +
+ +
+ +可以看到,我们的天气查询工具已经可以正常工作了。 + +那么接下来,我们就可以把这个工具注入到大模型中了。点击 「交互测试」,只激活当前这个工具,然后询问大模型: +``` +请问杭州的天气是多少? +``` + +
+ +
+ +完美! + +如此,我们便完成了一个天气查询工具的开发。并且轻松地注入到了我们的大模型中。在实际提供商业级部署方案的时候,虽然 mcp 目前的 stdio 冷启动速度足够快,但是考虑到拓展性等多方面因素,SSE 还是我们首选的连接方案,关于 SSE 的使用,我们下期再聊。 + +OpenMCP 开源链接:https://github.com/LSTM-Kirigaya/openmcp-client \ No newline at end of file diff --git a/zh/plugin-tutorial/examples/python-cadence-stdio.md b/zh/plugin-tutorial/examples/python-cadence-stdio.md new file mode 100644 index 0000000..e69de29 diff --git a/zh/plugin-tutorial/examples/python-form-stdio.md b/zh/plugin-tutorial/examples/python-form-stdio.md new file mode 100644 index 0000000..e69de29 diff --git a/zh/plugin-tutorial/examples/python-simple-stdio.md b/zh/plugin-tutorial/examples/python-simple-stdio.md new file mode 100644 index 0000000..02ddf47 --- /dev/null +++ b/zh/plugin-tutorial/examples/python-simple-stdio.md @@ -0,0 +1,438 @@ +# python 实现天气信息 mcp 服务器 + +[本期教程视频](https://www.bilibili.com/video/BV1zYGozgEHc) + +## hook + +等等,开始前,先让我们看一个小例子,假设我下周要去明日方舟锈影新生的漫展,所以我想要知道周六杭州的天气,于是我问大模型周六的天气,结果大模型给了我如下的回复: + +
+ +
+ +这可不行,相信朋友们也经常遇到过这样的情况,大模型总是会“授人以渔”,但是有的时候,我们往往就是想要直接知道最终结果,特别是一些无聊的生活琐事。 + +其实实现天气预报的程序也很多啦,那么有什么方法可以把写好的天气预报的程序接入大模型,让大模型告诉我们真实的天气情况,从而选择明天漫展的穿搭选择呢? + +如果直接写函数用 function calling 显得有点麻烦,这里面涉及到很多麻烦的技术细节需要我们商榷,比如大模型提供商的API调用呀,任务循环的搭建呀,文本渲染等等,从而浪费我们宝贵的时间。而 MCP 给了我们救赎之道,今天这期教程,就教大家写一个简单的 MCP 服务器,可以让大模型拥有得知天气预报的能力。 + + + +## 前言 + +👉 [上篇导航](https://zhuanlan.zhihu.com/p/32593727614) + +在上篇,我们简单讲解了 MCP 的基础,在这一篇,我们将正式开始着手开发我们自己的 MCP 服务器,从而将现成的应用,服务,硬件等等接入大模型。从而走完大模型到赋能终端应用的最后一公里。 + +工欲善其事,必先利其器。为了更加优雅快乐地开发 MCP 服务器,我们需要一个比较好的测试工具,允许我们在开发的过程看到程序的变化,并且可以直接接入大模型验证工具的有效性。 + +于是,我在前不久开源了一款一体化的 MCP 测试开发工具 —— OpenMCP,[全网第一个 MCP 服务器一体化开发测试软件 OpenMCP 发布!](https://zhuanlan.zhihu.com/p/1894785817186121106) + +> OpenMCP QQ 群 782833642 + +OpenMCP 开源链接:https://github.com/LSTM-Kirigaya/openmcp-client + +求个 star :D + +### 第一个 MCP 项目 + +事已至此,先 coding 吧 :D + +在打开 vscode 或者 trae 之前,先安装基本的 uv 工具,uv 是一款社区流行的版本管理工具,你只需要把它理解为性能更好的 conda 就行了。 + +我们先安装 uv,如果您正在使用 anaconda,一定要切换到 base 环境,再安装: + +```bash +pip install uv +``` + +安装完成后,运行 uv + +```bash +uv +``` + +没有报错就说明成功。uv 只会将不可以复用的依赖安装在本地,所以使用 anaconda 的朋友不用担心,uv 安装的依赖库会污染你的 base,我们接下来使用 uv 来创建一个基础的 python 项目 + +```bash +mkdir simple-mcp && cd simple-mcp +uv init +uv add mcp "mcp[cli]" +``` + +然后我们打开 vscode 或者 trae,在插件商城找到并下载 OpenMCP 插件 + +
+ +
+ +先制作一个 MCP 的最小程序: + +文件名:simple_mcp.py + +```python +from mcp.server.fastmcp import FastMCP + +mcp = FastMCP('锦恢的 MCP Server', version="11.45.14") + +@mcp.tool( + name='add', + description='对两个数字进行实数域的加法' +) +def add(a: int, b: int) -> int: + return a + b + +@mcp.resource( + uri="greeting://{name}", + name='greeting', + description='用于演示的一个资源协议' +) +def get_greeting(name: str) -> str: + # 访问处理 greeting://{name} 资源访问协议,然后返回 + # 此处方便起见,直接返回一个 Hello,balabala 了 + return f"Hello, {name}!" + +@mcp.prompt( + name='translate', + description='进行翻译的prompt' +) +def translate(message: str) -> str: + return f'请将下面的话语翻译成中文:\n\n{message}' + +@mcp.tool( + name='weather', + description='获取指定城市的天气信息' +) +def get_weather(city: str) -> str: + """模拟天气查询协议,返回格式化字符串""" + return f"Weather in {city}: Sunny, 25°C" + +@mcp.resource( + uri="user://{user_id}", + name='user_profile', + description='获取用户基本信息' +) +def get_user_profile(user_id: str) -> dict: + """模拟用户协议,返回字典数据""" + return { + "id": user_id, + "name": "张三", + "role": "developer" + } + +@mcp.resource( + uri="book://{isbn}", + name='book_info', + description='通过ISBN查询书籍信息' +) +def get_book_info(isbn: str) -> dict: + """模拟书籍协议,返回结构化数据""" + return { + "isbn": isbn, + "title": "Python编程:从入门到实践", + "author": "Eric Matthes" + } + +@mcp.prompt( + name='summarize', + description='生成文本摘要的提示词模板' +) +def summarize(text: str) -> str: + """返回摘要生成提示词""" + return f"请用一句话总结以下内容:\n\n{text}" +``` + +我们试着运行它: + + +```bash +uv run mcp run simple_mcp.py +``` + +如果没有报错,但是卡住了,那么说明我们的依赖安装没有问题,按下 ctrl c 或者 ctrl z 退出即可。 + +在阁下看起来,这些函数都简单到毫无意义,但是请相信我,我们总需要一些简单的例子来通往最终的系统。 + +### Link, Start! + +如果你下载了 OpenMCP 插件,那么此时你就能在打开的 python 编辑器的右上角看到 OpenMCP 的紫色图标,点击它就能启动 OpenMCP,调试当前的 MCP 了。 + +
+ +
+ +默认是以 STDIO 的方式启动,默认运行如下的命令: + +```bash +uv run mcp run <当前打开的 python 文件的相对路径> +``` + +所以你需要保证已经安装了 mcp 脚手架,也就是 `uv add mcp "mcp[cli]"`。 + +打开后第一件事就是先看左下角连接状态,确保是绿色的,代表当前 OpenMCP 和你的 MCP 服务器已经握手成功。 + +
+ +
+ +如果连接成功,此时连接上方还会显示你当前的 MCP 服务器的名字,光标移动上去还能看到版本号。这些信息由我们如下的代码定义: + +```python +mcp = FastMCP('锦恢的 MCP Server', version="11.45.14") +``` + +这在我们进行版本管理的时候会非常有用。请善用这套系统。 + + +如果连接失败,可以点击左侧工具栏的第二个按钮,进入连接控制台,查看错误信息,或是手动调整连接命令: + +
+ +
+ +### 初识 OpenMCP + +接下来,我来简单介绍一下 OpenMCP 的基本功能模块,如果一开始,你的屏幕里什么也没有,先点击上面的加号创建一个新的标签页,此处页面中会出现下图屏幕中的四个按钮 + +
+ +
+ +放大一点 + +
+ +
+ +前三个,资源、提词和工具,分别用于调试 MCP 中的三个对应项目,也就是 Resources,Prompts 和 Tools,这三个部分的使用,基本和 MCP 官方的 Inspector 工具是一样的,那是自然,我就照着这几个抄的,诶嘿。 + +
+ +
+ +然后第四个按钮「交互测试」,它是一个我开发的 MCP 客户端,其实就是一个对话窗口,你可以无缝衔接地直接在大模型中测试你当前的 MCP 服务器的功能函数。 + +
+ +
+ + +目前我暂时只支持 tools 的支持,因为 prompts 和 resources 的我还没有想好,(resource 感觉就是可以当成一个 tool),欢迎大家进群和我一起讨论:QQ群 782833642 + + + +## 开始调试天气函数 + +### 工具调试 + +还记得我们一开始给的 mcp 的例子吗?我们可以通过 OpenMCP 来快速调试这里面写的函数,比如我们本期的目标,写一个天气预报的 MCP,那么假设我们已经写好了一个天气预报的函数了,我们把它封装成一个 tool: + +```python +@mcp.tool( + name='weather', + description='获取指定城市的天气信息' +) +def get_weather(city: str) -> str: + """模拟天气查询协议,返回格式化字符串""" + return f"Weather in {city}: Sunny, 25°C" +``` + +当然,它现在是没有意义的,因为就算把黑龙江的城市ID输入,它也返回 25 度,但是这些都不重要,我想要带阁下先走完整套流程。建立自上而下的感性认知比死抠细节更加容易让用户学懂。 + +那么我们现在需要调试这个函数,打开 OpenMCP,新建一个「工具」调试项目 + +
+ +
+ +然后此时,你在左侧的列表可以看到 weather 这个工具,选择它,然后在右侧的输入框中随便输入一些东西,按下回车(或者点击「运行」),你能看到如下的响应: + +
+ +
+ +看到我们函数 return 的字符串传过来了,说明没问题,链路通了。 + +### 交互测试 + +诶?我知道你编程很厉害,但是,在噼里啪啦快速写完天气预报爬虫前,我们现在看看我们要如何把已经写好的工具注入大模型对话中。为了使用大模型,我们需要先选择大模型和对应的 API,点击左侧工具栏的第三个按钮,进入 API 模块,选择你想要使用的大模型运营商、模型,填写 API token,然后点击下面的「保存」 + +
+ +
+ +再新建一个标签页,选择「交互测试」,此时,我们就可以直接和大模型对话了,我们先看看没有任何工具注入的大模型会如何回应天气预报的问题,点击最下侧工具栏从左往右第三个按钮,进入工具选择界面,选择「禁用所有工具」 + +
+ +
+ +点击「关闭」后,我们问大模型一个问题: + +``` +请问杭州的温度是多少? +``` + +
+ +
+ +可以看到,大模型给出了和文章开头一样的回答。非常敷衍,因为它确实无法知道。 + +此处,我们再单独打开「weather」工具: + +
+ +
+ +问出相同的问题: + +
+ +
+ +可以看到,大模型给出了回答是 25 度,还有一些额外的推导信息。 + +我们不妨关注一些细节,首先,大模型并不会直接回答问题,而是会先去调用 weather 这个工具,调用参数为: + +```json +{ + "city": "杭州" +} +``` + +然后,我们的 MCP 服务器给出了响应: + +``` +Weather in 杭州: Sunny, 25°C +``` + +从而,最终大模型才根据这些信息给出了最终的回答。也就是,这个过程我们实际调用了两次大模型的服务。而且可以看到两次调用的输入 token 数量都非常大,这是因为 OpenMCP 会将函数调用以 JSON Schema 的形式注入到请求参数中,weather 这个工具的 JSON Schema 如下图的右侧的 json 所示: + +
+ +
+ +然后支持 openai 协议的大模型厂商都会针对这样的信息进行 function calling,所以使用了工具的大模型请求的输入 token 数量都会比较大。但是不需要担心,大部分厂商都实现了 KV Cache,对相同前缀的输入存在缓存,缓存命中部分的费用开销是显著低于普通的 输入输出 token 价格的。OpenMCP 在每个回答的下面都表明了当次请求的 输入 token,输出 token,总 token 和 缓存命中率。 + +其中 + +- 「总 token」 = 「输入 token」 + 「输出 token」 + +- 「缓存命中率」 = 「缓存命令的 token」 / 「输入 token」 + +> 没错,缓存命中率 是对于输入 token 的概念,输出 token 是没有 缓存命中率这个说法的。 + +在后续的开发中,你可以根据这些信息来针对性地对你的服务或者 prompt 进行调优。 + +### 完成一个真正的天气预报吧! + +当然,这些代码也非常简单,直接让大模型生成就行了(其实大模型是无法生成免 API 的 python 获取天气的代码的,我是直接让大模型把我个人网站上天气预报的 go 函数翻译了一下) + +我直接把函数贴上来了: + +```python +import requests +import json +from typing import NamedTuple, Optional + +class CityWeather(NamedTuple): + city_name_en: str + city_name_cn: str + city_code: str + temp: str + wd: str + ws: str + sd: str + aqi: str + weather: str + +def get_city_weather_by_city_name(city_code: str) -> Optional[CityWeather]: + """根据城市名获取天气信息""" + + if not city_code: + print(f"找不到{city_code}对应的城市") + return None + + try: + # 构造请求URL + url = f"http://d1.weather.com.cn/sk_2d/{city_code}.html" + + # 设置请求头 + headers = { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.0.0", + "Host": "d1.weather.com.cn", + "Referer": "http://www.weather.com.cn/" + } + + # 发送HTTP请求 + response = requests.get(url, headers=headers) + response.raise_for_status() + + # 解析JSON数据 + # 解析JSON数据前先处理编码问题 + content = response.text.encode('latin1').decode('unicode_escape') + json_start = content.find("{") + json_str = content[json_start:] + + weather_data = json.loads(json_str) + + # 构造返回对象 + return CityWeather( + city_name_en=weather_data.get("nameen", ""), + city_name_cn=weather_data.get("cityname", "").encode('latin1').decode('utf-8'), + city_code=weather_data.get("city", ""), + temp=weather_data.get("temp", ""), + wd=weather_data.get("wd", "").encode('latin1').decode('utf-8'), + ws=weather_data.get("ws", "").encode('latin1').decode('utf-8'), + sd=weather_data.get("sd", ""), + aqi=weather_data.get("aqi", ""), + weather=weather_data.get("weather", "").encode('latin1').decode('utf-8') + ) + + except Exception as e: + print(f"获取天气信息失败: {str(e)}") + return None + +from mcp.server.fastmcp import FastMCP + +mcp = FastMCP('weather', version="0.0.1") + +@mcp.tool( + name='get_weather_by_city_code', + description='根据城市天气预报的城市编码 (int),获取指定城市的天气信息' +) +def get_weather_by_code(city_code: int) -> str: + """模拟天气查询协议,返回格式化字符串""" + city_weather = get_city_weather_by_city_name(city_code) + return str(city_weather) +``` + + +这里有几个点一定要注意: + +1. 如果你的输入参数是数字,就算是城市编码这种比较长的数字,请一定定义成 int,因为 mcp 底层的是要走 JSON 正反序列化的,而 "114514" 这样的字符串会被 JSON 反序列化成 114514,而不是 "114514" 这个字符串。你实在要用 str 来表示一个很长的数字,那么就在前面加一个前缀,比如 "code-114514",避免被反序列化成数字,从而触发 mcp 内部的 type check error +2. tool 的 name 请按照 python 的变量命名要求进行命名,否则部分大模型厂商会给你报错。 + +好,我们先测试一下: + +
+ +
+ +可以看到,我们的天气查询工具已经可以正常工作了。 + +那么接下来,我们就可以把这个工具注入到大模型中了。点击 「交互测试」,只激活当前这个工具,然后询问大模型: +``` +请问杭州的天气是多少? +``` + +
+ +
+ +完美! + +如此,我们便完成了一个天气查询工具的开发。并且轻松地注入到了我们的大模型中。在实际提供商业级部署方案的时候,虽然 mcp 目前的 stdio 冷启动速度足够快,但是考虑到拓展性等多方面因素,SSE 还是我们首选的连接方案,关于 SSE 的使用,我们下期再聊。 + +OpenMCP 开源链接:https://github.com/LSTM-Kirigaya/openmcp-client \ No newline at end of file diff --git a/zh/plugin-tutorial/examples/typescript-crawl4ai-stdio.md b/zh/plugin-tutorial/examples/typescript-crawl4ai-stdio.md new file mode 100644 index 0000000..e69de29 diff --git a/zh/plugin-tutorial/faq/help.md b/zh/plugin-tutorial/faq/help.md new file mode 100644 index 0000000..a7de7c4 --- /dev/null +++ b/zh/plugin-tutorial/faq/help.md @@ -0,0 +1,27 @@ +--- +layout: doc +--- + +# 常见问题解答 + +## 错误代码说明 + +### 32000 - MCP 连接失败 + +MCP 连接失败可能有多种原因,以下是一些常见情况: + +• **虚拟环境路径不匹配** + +虚拟环境(venv)与入口文件路径不匹配是导致连接失败的常见原因之一。 + +详细的解决方案请参考:[配置说明](./venv-not-same-path/venv-not-same-path.md) + +--- + +• **其他可能的原因** + +- 端口被占用 +- 环境变量配置错误 +- 依赖库未正确安装 + +> 如果您遇到以上问题,请先查看错误日志获取详细信息。如果问题仍然存在,可以在 [GitHub Issues](https://github.com/LSTM-Kirigaya/openmcp-client/issues) 中寻求帮助。 diff --git a/zh/plugin-tutorial/faq/venv-not-same-path/image-2.png b/zh/plugin-tutorial/faq/venv-not-same-path/image-2.png new file mode 100644 index 0000000..3b3953f Binary files /dev/null and b/zh/plugin-tutorial/faq/venv-not-same-path/image-2.png differ diff --git a/zh/plugin-tutorial/faq/venv-not-same-path/image.png b/zh/plugin-tutorial/faq/venv-not-same-path/image.png new file mode 100644 index 0000000..ac756aa Binary files /dev/null and b/zh/plugin-tutorial/faq/venv-not-same-path/image.png differ diff --git a/zh/plugin-tutorial/faq/venv-not-same-path/venv-not-same-path.md b/zh/plugin-tutorial/faq/venv-not-same-path/venv-not-same-path.md new file mode 100644 index 0000000..53cc616 --- /dev/null +++ b/zh/plugin-tutorial/faq/venv-not-same-path/venv-not-same-path.md @@ -0,0 +1,29 @@ +# 虚拟环境与入口文件不在同一目录时的配置方式 + +## 问题描述 + +在使用 OpenMCP 时,有时会遇到虚拟环境(venv)与 Python 文件不在同一目录的情况,甚至虚拟环境可能位于项目文件夹之外。这种情况下,点击右上角连接按钮可能会出现 MCP 连接失败(错误代码:32000)的问题。 + +## 解决方案 + +### 1. 调整执行目录 + +在连接选项中,您需要调整执行目录到虚拟环境所在的位置: + +![MCP 连接选项界面](./image-2.png) + +### 2. 修改执行命令 + +同时,需要相应地修改执行命令: + +![修改执行命令示例](./image.png) + +### 3. 直接指定解释器路径 + +对于特定情况,您可以直接在命令中指定 Python 解释器的完整路径,例如: + +```bash +C:\code\ygo-chat\.venv\Scripts\python.exe example.py +``` + +> 注意:此方法同样适用于 node或者mcp指令的【命令】以及其它mcp client的mcp配置文件。 \ No newline at end of file diff --git a/zh/plugin-tutorial/images/inspector.png b/zh/plugin-tutorial/images/inspector.png new file mode 100644 index 0000000..442aef4 Binary files /dev/null and b/zh/plugin-tutorial/images/inspector.png differ diff --git a/zh/plugin-tutorial/images/openmcp.png b/zh/plugin-tutorial/images/openmcp.png new file mode 100644 index 0000000..b732b99 Binary files /dev/null and b/zh/plugin-tutorial/images/openmcp.png differ diff --git a/zh/plugin-tutorial/index.md b/zh/plugin-tutorial/index.md new file mode 100644 index 0000000..ad9ae1b --- /dev/null +++ b/zh/plugin-tutorial/index.md @@ -0,0 +1,43 @@ +--- +next: + text: 什么是 MCP? + link: '/plugin-tutorial/what-is-mcp' +--- + +# OpenMCP 概述 + +:::warning +在正式开始 OpenMCP 的学习之前,我们强烈推荐您先了解一下 MCP 的基本概念:[Agent 时代基础设施 | MCP 协议介绍](https://kirigaya.cn/blog/article?seq=299) +::: + +## 什么是 OpenMCP + +OpenMCP 是一个面向开发者的 MCP 调试器和 SDK,致力于降低 AI Agent 的全链路开发成本和开发人员的心智负担。 + +![](./images/openmcp.png) + +OpenMCP 分为两个部分,但是本板块讲解的是 OpenMCP 调试器的部分的使用,这部分也被我们称为 OpenMCP Client。OpenMCP Client 的本体是一个可在类 vscode 编辑器上运行的插件。它兼容了目前 MCP 协议的全部特性,且提供了丰富的利于开发者使用的功能,可以作为 Claude Inspector 的上位进行使用。 + +:::info 类 vscode 编辑器 (VLE) +类 vscode 编辑器 (vscode-like editor,简称 VLE) 是指基于 Vscodium 内核开发的通用型代码编辑器,它们都能够兼容的大部分的vscode插件生态,并且具有类似 vscode 的功能(比如支持 LSP3.7 协议、拥有 remote ssh 进行远程开发的能力、拥有跨编辑器的配置文件)。 + +比较典型的 VLE 有:vscode, trae, cursor 和 vscodium 各类发行版本。 +::: + +## 什么是 Claude Inspector + +Claude Inspector 是一款 Claude 官方(也就是 MCP 协议的提出者)发布的开源 MCP 调试器,开发者在开发完 MCP 服务器后,可以通过这款调试器来测试功能完整性。 + +![](./images/inspector.png) + +但是 Inspector 工具存在如下几个缺点: + +- 使用麻烦:使用 Inspector 每次都需要通过 mcp dev 启动一个 web 前后端应用 +- 功能少:Inspector 只提供了最为基础的 MCP 的 tool 等属性的调试。如果用户想要测试自己开发的 MCP 服务器在大模型的交互下如何,还需要连接进入 Claude Desktop 并重启客户端,对于连续调试场景,非常不方便。 +- 存在部分 bug:对于 SSE 和 streamable http 等远程连接的场景,Inspector 存在已知 bug,这对真实工业级开发造成了极大的影响。 +- 无法对调试内容进行保存和留痕:在大规模微服务 mcp 化的项目中,这非常重要。 +- 无法同时调试多个 mcp 服务器:在进行 mcp 原子化横向拓展的场景中,这是一项必要的功能。 + +而 OpenMCP Client 被我们制作出来的一个原因就是为了解决 Inspector 上述的痛点,从而让 mcp 服务器的开发门槛更低,用户能够更加专注于业务本身。 + + \ No newline at end of file diff --git a/zh/plugin-tutorial/quick-start/acquire-openmcp.md b/zh/plugin-tutorial/quick-start/acquire-openmcp.md new file mode 100644 index 0000000..e87da63 --- /dev/null +++ b/zh/plugin-tutorial/quick-start/acquire-openmcp.md @@ -0,0 +1,61 @@ +--- +layout: doc +--- + + +# 获取 OpenMCP + +## 在插件商城中安装 OpenMCP + +你可以在主流 VLE 的插件商城直接获取 OpenMCP 插件。比如在 vscode 中,点击左侧的插件商城,然后在搜索框中输入 `OpenMCP` 即可找到 OpenMCP 插件。 + +![vscode 插件商城](./images/vscode-plugin-market.png) + +## 离线安装 + +VLE 的插件本质是一个 zip 压缩包,后缀名为 vsix,全平台通用。我们的 CI/CD 机器人在每次版本发布后,会自动构建并上传 vsix 到 github release,你可以通过如下的链接访问到对应版本的 github release 页面: + +``` +https://github.com/LSTM-Kirigaya/openmcp-client/releases/tag/v{版本号} +``` + +比如对于 0.1.1 这个版本,它的 release 页面链接为:[https://github.com/LSTM-Kirigaya/openmcp-client/releases/tag/v0.1.1](https://github.com/LSTM-Kirigaya/openmcp-client/releases/tag/v0.1.1) + +在 `Assets` 下面,你可以找到对应的 vsix 压缩包 + +![github release](./images/github-release.png) + +除此之外,您还可以通过如下的商城网页来获取最新的 openmcp 的 vsix + +- https://open-vsx.org/extension/kirigaya/openmcp +- https://marketplace.visualstudio.com/items?itemName=kirigaya.openmcp + +点击 vsix 后缀名的文件下载,下载完成后,您就可以直接安装它了。在 VLE 中安装外部的 vsix 文件有两种方法。 + +### 方法一:在 VLE 中安装 + +VLE 的插件商城页面有一个三个点的按钮,点击它后,你能看到下面这个列表中被我标红的按钮 + +![vscode 插件商城](./images/vscode-plugin-market-install-from.png) + +点击它后,找到刚刚下载的 vsix 文件,点击即可完成安装。 + +### 方法二:通过命令行 + +如果您的 VLE 是全局安装的,会自动存在一个命令行工具,命令如下: + +::: code-group +```bash [vscode] +code --install-extension /path/to/openmcp-0.1.1.vsix +``` + +```bash [trae] +trae --install-extension /path/to/openmcp-0.1.1.vsix +``` + +```bash [cursor] +cursor --install-extension /path/to/openmcp-0.1.1.vsix +``` +::: + +`/path/to/openmcp-0.1.1.vsix` 代表你刚刚下载的 vsix 文件的绝对路径。这样也可以安装插件。 \ No newline at end of file diff --git a/zh/plugin-tutorial/quick-start/first-mcp.md b/zh/plugin-tutorial/quick-start/first-mcp.md new file mode 100644 index 0000000..9510068 --- /dev/null +++ b/zh/plugin-tutorial/quick-start/first-mcp.md @@ -0,0 +1,140 @@ + +# 你的第一个 MCP + +实现 MCP 的编程语言很多,常见的几户所有编程语言都有官方和民间的支持,以 编程语言 + MCP 就能搜到对应的库,在 [[mcp-examples|MCP 服务器开发案例]] 中,我们也提供了不同编程语言的不同例子。 + +在所有编程语言中,Python 的 MCP 的开发无疑是最为简单,最容易让新手上手的,所以第一个 MCP我们先用 python 来实现。其他的编程语言实现效果也大同小异。 + +## 安装 uv + +Python 写 mcp 服务器强烈推荐使用 uv 作为包管理器,关于 uv,你只需要知道它是一个高性能包管理器,拥有 pip 和 conda 的所有优点。没有的朋友请先通过 pip 安装 uv: + + +```bash +pip install uv +``` + + +:::warning 使用 anaconda 或者 miniconda 的朋友注意了! +请不要在非 base 环境下安装 uv,请在 base 环境下安装 uv,uv 本身会做好环境隔离的工作,请不要担心 uv 会污染你的 base 环境。你不安装在 base 下或者使用全局默认的 pip 安装,我们根本不知道你安装的 uv 在哪里!base 环境下使用 pip 安装的脚本会安装在 `~/anaconda/bin/uv` 中,也请确保 `~/anaconda/bin/` 在你的 `$PATH` 中。 +::: + + +查看 uv 的版本: + +```bash +uv version +``` + +我的输出是: +``` +uv 0.6.9 (3d9460278 2025-03-20) +``` + +实操时,请保证版本不要低于我的。 + +## 创建一个最简单的 mcp 服务器 + +我们进入工程目录,准备创建一个最简单的 mcp 服务器。 + +```bash +mkdir -p ~/codes/my-first-mcp +cd ~/codes/my-first-mcp +uv init --no-workspace +``` + +此时,你的项目里面应该有这三个文件: + +``` +README.md main.py pyproject.toml +``` + +然后,我们在当前文件夹打开 vscode 或者 trae,我们创建一个最简单的 mcp 服务器,它的功能是: +- 提供一个名为 add 的工具,用于对两个数字进行加法 +- 提供一个名为 greeting 的资源,用于返回一个 greeting 消息 + +先安装 mcp 相关的库: + +```bash +uv add mcp "mcp[cli]" +``` + +修改 `main.py` 内容如下: + +```python +from mcp.server.fastmcp import FastMCP +mcp = FastMCP('锦恢的 MCP Server', version="11.45.14") + +@mcp.tool( + name='add', + description='对两个数字进行实数域的加法' +) +def add(a: int, b: int) -> int: + return a + b + +@mcp.resource( + uri="greeting://{name}", + name='greeting', + description='用于演示的一个资源协议' +) +def get_greeting(name: str) -> str: + return f"Hello, {name}!" + +@mcp.prompt( + name='translate', + description='进行翻译的prompt' +) +def translate(message: str) -> str: + return f'请将下面的话语翻译成中文:\n\n{message}' +``` + +## 使用 OpenMCP 一键连接 + +如上,我们申明了三个函数,用作 mcp 的 tool,resource 和 prompt。在 OpenMCP 中启动它们非常简单,点击右上角的 OpenMCP 图标即可连接: + +![](./images/connect-simple.png) + +初次使用 OpenMCP,会出现引导界面,还希望阁下可以耐心看完。 + +![](./images/guide.png) + +如果登录完成后,如图显示连接成功,则代表当前已经成功启动并连接 mcp 服务器。 + +![](./images/connect-success.png) + +恭喜您,万事开头难,您已经完成了最难的 mcp 连接! + +有关 openmcp 进行 mcp 服务器连接的更多信息,可以参考手册里面的这一章 [[connect-mcp|连接到 MCP 服务器]]。 + +## 附录:关于 uv 启动 mcp 你必须知道的 + +OpenMCP 已经帮你做好了很多事情,但是使用 uv 启动 mcp 服务器其实是不只一种方法的,了解更加底层的原理有助于您以不变应万变。因为 OpenMCP 对于 python 项目默认运行 `uv run mcp run main.py` 来启动 mcp 服务器,但是 GitHub 上的部分项目无法这么启动。 + +先了解一下对于上面那个例子的 python 代码,应该如何通过命令行启动 mcp 吧! + +### 方法一:使用 mcp-cli + +mcp 本身提供了脚手架,可以直接启动一段被申明的 python 代码,作为一个 mcp 服务器。使用如下代码运行它: + +```bash +uv run mcp run main.py +``` + +### 方法二:在代码中显式启动 + +你也可以在代码中显式启动 mcp 服务器,在 `main.py` 的结尾添加: + +```python +if __name__ == '__main__': + mcp.run() +``` + +然后运行如下代码即可启动 mcp 服务器: + +```bash +uv run main.py +``` + +:::warning +请不要运行 python main.py,因为 uv run 会使用当前虚拟环境的库,这些库在外部 python 看来是不可见的。也不要在没有使用 `mcp.run()` 启动代码的情况下就直接使用 uv run main.py,我们之前的代码只是申明了函数,并没有实际上执行任何功能。 +::: diff --git a/zh/plugin-tutorial/quick-start/images/bing-image-common.png b/zh/plugin-tutorial/quick-start/images/bing-image-common.png new file mode 100644 index 0000000..35fb776 Binary files /dev/null and b/zh/plugin-tutorial/quick-start/images/bing-image-common.png differ diff --git a/zh/plugin-tutorial/quick-start/images/connect-simple.png b/zh/plugin-tutorial/quick-start/images/connect-simple.png new file mode 100644 index 0000000..20fd5f3 Binary files /dev/null and b/zh/plugin-tutorial/quick-start/images/connect-simple.png differ diff --git a/zh/plugin-tutorial/quick-start/images/connect-success.png b/zh/plugin-tutorial/quick-start/images/connect-success.png new file mode 100644 index 0000000..0100f62 Binary files /dev/null and b/zh/plugin-tutorial/quick-start/images/connect-success.png differ diff --git a/zh/plugin-tutorial/quick-start/images/github-release.png b/zh/plugin-tutorial/quick-start/images/github-release.png new file mode 100644 index 0000000..f34ed8e Binary files /dev/null and b/zh/plugin-tutorial/quick-start/images/github-release.png differ diff --git a/zh/plugin-tutorial/quick-start/images/guide.png b/zh/plugin-tutorial/quick-start/images/guide.png new file mode 100644 index 0000000..9dff225 Binary files /dev/null and b/zh/plugin-tutorial/quick-start/images/guide.png differ diff --git a/zh/plugin-tutorial/quick-start/images/image.png b/zh/plugin-tutorial/quick-start/images/image.png new file mode 100644 index 0000000..38f2071 Binary files /dev/null and b/zh/plugin-tutorial/quick-start/images/image.png differ diff --git a/zh/plugin-tutorial/quick-start/images/inspector.png b/zh/plugin-tutorial/quick-start/images/inspector.png new file mode 100644 index 0000000..442aef4 Binary files /dev/null and b/zh/plugin-tutorial/quick-start/images/inspector.png differ diff --git a/zh/plugin-tutorial/quick-start/images/llm-bing-image-render.png b/zh/plugin-tutorial/quick-start/images/llm-bing-image-render.png new file mode 100644 index 0000000..7fe1168 Binary files /dev/null and b/zh/plugin-tutorial/quick-start/images/llm-bing-image-render.png differ diff --git a/zh/plugin-tutorial/quick-start/images/llm-calc.png b/zh/plugin-tutorial/quick-start/images/llm-calc.png new file mode 100644 index 0000000..0fdfd1f Binary files /dev/null and b/zh/plugin-tutorial/quick-start/images/llm-calc.png differ diff --git a/zh/plugin-tutorial/quick-start/images/llm-intro.png b/zh/plugin-tutorial/quick-start/images/llm-intro.png new file mode 100644 index 0000000..3657701 Binary files /dev/null and b/zh/plugin-tutorial/quick-start/images/llm-intro.png differ diff --git a/zh/plugin-tutorial/quick-start/images/llm-tools.png b/zh/plugin-tutorial/quick-start/images/llm-tools.png new file mode 100644 index 0000000..47b351d Binary files /dev/null and b/zh/plugin-tutorial/quick-start/images/llm-tools.png differ diff --git a/zh/plugin-tutorial/quick-start/images/openmcp-home.png b/zh/plugin-tutorial/quick-start/images/openmcp-home.png new file mode 100644 index 0000000..26ac6cc Binary files /dev/null and b/zh/plugin-tutorial/quick-start/images/openmcp-home.png differ diff --git a/zh/plugin-tutorial/quick-start/images/openmcp.png b/zh/plugin-tutorial/quick-start/images/openmcp.png new file mode 100644 index 0000000..b732b99 Binary files /dev/null and b/zh/plugin-tutorial/quick-start/images/openmcp.png differ diff --git a/zh/plugin-tutorial/quick-start/images/rerun-bing-image.png b/zh/plugin-tutorial/quick-start/images/rerun-bing-image.png new file mode 100644 index 0000000..01b97cb Binary files /dev/null and b/zh/plugin-tutorial/quick-start/images/rerun-bing-image.png differ diff --git a/zh/plugin-tutorial/quick-start/images/resource-desc.png b/zh/plugin-tutorial/quick-start/images/resource-desc.png new file mode 100644 index 0000000..279f10e Binary files /dev/null and b/zh/plugin-tutorial/quick-start/images/resource-desc.png differ diff --git a/zh/plugin-tutorial/quick-start/images/resource-result.png b/zh/plugin-tutorial/quick-start/images/resource-result.png new file mode 100644 index 0000000..7cbbd76 Binary files /dev/null and b/zh/plugin-tutorial/quick-start/images/resource-result.png differ diff --git a/zh/plugin-tutorial/quick-start/images/system-prompt-add.png b/zh/plugin-tutorial/quick-start/images/system-prompt-add.png new file mode 100644 index 0000000..4e2106d Binary files /dev/null and b/zh/plugin-tutorial/quick-start/images/system-prompt-add.png differ diff --git a/zh/plugin-tutorial/quick-start/images/system-prompt-image.png b/zh/plugin-tutorial/quick-start/images/system-prompt-image.png new file mode 100644 index 0000000..d5620e8 Binary files /dev/null and b/zh/plugin-tutorial/quick-start/images/system-prompt-image.png differ diff --git a/zh/plugin-tutorial/quick-start/images/tool-add-test-project.png b/zh/plugin-tutorial/quick-start/images/tool-add-test-project.png new file mode 100644 index 0000000..e7d5f06 Binary files /dev/null and b/zh/plugin-tutorial/quick-start/images/tool-add-test-project.png differ diff --git a/zh/plugin-tutorial/quick-start/images/tool-desc.png b/zh/plugin-tutorial/quick-start/images/tool-desc.png new file mode 100644 index 0000000..2d134d7 Binary files /dev/null and b/zh/plugin-tutorial/quick-start/images/tool-desc.png differ diff --git a/zh/plugin-tutorial/quick-start/images/tool-result.png b/zh/plugin-tutorial/quick-start/images/tool-result.png new file mode 100644 index 0000000..d1bee87 Binary files /dev/null and b/zh/plugin-tutorial/quick-start/images/tool-result.png differ diff --git a/zh/plugin-tutorial/quick-start/images/vscode-plugin-market-install-from.png b/zh/plugin-tutorial/quick-start/images/vscode-plugin-market-install-from.png new file mode 100644 index 0000000..4b22fe2 Binary files /dev/null and b/zh/plugin-tutorial/quick-start/images/vscode-plugin-market-install-from.png differ diff --git a/zh/plugin-tutorial/quick-start/images/vscode-plugin-market.png b/zh/plugin-tutorial/quick-start/images/vscode-plugin-market.png new file mode 100644 index 0000000..0033ecd Binary files /dev/null and b/zh/plugin-tutorial/quick-start/images/vscode-plugin-market.png differ diff --git a/zh/plugin-tutorial/quick-start/index.md b/zh/plugin-tutorial/quick-start/index.md new file mode 100644 index 0000000..c09f987 --- /dev/null +++ b/zh/plugin-tutorial/quick-start/index.md @@ -0,0 +1,11 @@ +# 快速开始 + +1. [[acquire-openmcp|获取 OpenMCP]] + +2. [[first-mcp|你的第一个 MCP]] +3. [[quick-debug|快速调试 MCP]] +4. [[put-into-llm|扔进大模型里面测测好坏!]] + + +
+
diff --git a/zh/plugin-tutorial/quick-start/put-into-llm.md b/zh/plugin-tutorial/quick-start/put-into-llm.md new file mode 100644 index 0000000..d544523 --- /dev/null +++ b/zh/plugin-tutorial/quick-start/put-into-llm.md @@ -0,0 +1,65 @@ +# 扔进大模型里面测测好坏! + +在 [[quick-debug|之前的章节]] 中,我们成功完成了 mcp 服务器的连接和各个功能的调试,也算是带大家认识了一下 openmcp 的基本调试功能。接下来,我们需要把 mcp 放到大模型环境中来测试,毕竟,mcp 提出的初衷就是为了让大家可以低成本把自己写的功能接入大模型中。 + +在正式进行对话前,还请大家先参照 [[connect-llm|连接大模型]] 来完成大模型 API 的配置,并测试完成你的大模型服务是否可用。 + +## 和大模型进行对话 + +我们先创建一个新的调试项目,选择「交互测试」,就可以进入一个和大模型对话的窗口。OpenMCP 提供的对话窗口的基本介绍如下: + +![](./images/llm-intro.png) + +上面标定了几个比较重要的按钮,初次使用,您可以直接使用默认设置。点击「使用的工具」,可以看到当前激活的工具,OpenMCP 默认激活当前连接的 mcp 服务器的所有提供的工具,如果您希望禁用某些工具,可以点击下方的「使用的工具」来选择性地禁用工具: + +![](./images/llm-tools.png) + +好啦,让我们先来看看基于 mcp 协议,大模型会如何调用我们的工具吧,保持默认设置,然后询问如下问题:请帮我计算一下 123 + 1313 等于多少 + +输入后回车等待结果,可以看到如下的输出: + +![](./images/llm-calc.png) + +可以看到大模型选择使用了我们提供的工具 add 完成了上述的加法,OpenMCP 中你能看到大模型是如何调用每一个工具的和工具的返回结果。目前我们问的问题和 mcp 提供的工具都比较简单,对于复杂问题,大模型有可能会在一轮内同时调用多个工具来完成特定的任务,如果你希望大模型每次都只使用一个工具,可以点击下方的默认亮着的「允许模型在单轮回复中调用多个工具」 来禁用这个特性。 + + +## 系统提示词 + +对于一些特殊的情况,比如 [bing-images](/Users/bytedance/projects/openmcp-tutorial/bing-images),这是一个根据关键词来返回 bing 图片的 mcp 服务器。 + +我们直接询问如下的问题:请帮我搜索几张明日方舟的图片,默认情况下,你有可能会得到如下的回复: + +![](./images/bing-image-common.png) + +大模型将得到的图片以链接的形式返回了,但是有的时候其实我希望是返回成图片的形式渲染在屏幕中让我看到的,为了约束和引导大模型返回文本的风格、或是按照我们要求的模板进行返回,我们可以通过设置系统提示词的方式来实现。 + +我们先点击下方的「系统提示词」 + +![](./images/system-prompt-add.png) + +我们添加一个新的系统提示词,在标题输入「bing image」,然后主体部分填入: + +``` +你是一个擅长找 bing 图片的 AI,当你找到图片时,你应该返回图片形式的 markdown,比如 ![](https://xxxx.xx/xxxx) +``` + +点击保存。 + +![](./images/system-prompt-image.png) + +然后将光标移动到第一个用户对话框上,此时会显示几个按钮,选择重新运行的按钮,openmcp 便会重新执行此处的对话。 + +![](./images/rerun-bing-image.png) + +可以看到此时,图片就被正常渲染出来了: + +![](./images/llm-bing-image-render.png) + + +关于更多使用 system prompt 或者其他更加精准的方法来控制 agent 的技巧,可以移步 [[go-neo4j-sse|go 实现 neo4j 的只读 mcp 服务器 (SSE)]] + +## 结语 + +很好!你完成了 openmcp 的基础教程,接下来,该去做点有趣的事情了!在 [[mcp-examples|MCP 服务器开发案例]] 中,你能找到更多的使用 openmcp 进行 mcp 服务器开发的例子。 + +遍地惊喜,任君自取。 \ No newline at end of file diff --git a/zh/plugin-tutorial/quick-start/quick-debug.md b/zh/plugin-tutorial/quick-start/quick-debug.md new file mode 100644 index 0000000..5d4cf20 --- /dev/null +++ b/zh/plugin-tutorial/quick-start/quick-debug.md @@ -0,0 +1,49 @@ +# 快速调试 MCP + +在 [[first-mcp|你的第一个 MCP]] 中,我们成功创建了一个 MCP 服务器的最小实例,并且成功使用 openmcp 连接了这个服务器。 + +接下来,我们可以来调试这个服务器的功能了,毕竟,不是所有人都是 Jeaf Dean,都能一次就写对所有代码。我们写的 MCP 服务器也不总是一开始就自信满满可以上线的,它总是存在着一些我们无法发现的问题。试想一下,如果后面我们把 mcp 连接到大模型进行全链路调试时出了问题,这个时候你就会发现,可能出错的环节非常多:MCP 服务器的问题?大模型厂商的问题?OpenMCP 的问题?把可能的错误进行分类,然后逐一排查,才是符合工程直觉 (Engineering Instuition) 的做法。 + +## 认识面板 + +首次进入 openmcp 时,会进入一个面板,上面一共四个按钮,代表四种调试项目: + +![](./images/openmcp-home.png) + +我们现在需要确认的是 tool,resource 和 prompt 这三个功能是否运行正常,因为在实际项目中,tool 是使用得最多的项目,因此,我们先调试 tool。 + +## 调试 Tool + +为了调试 tool,我们点击面板上的 「工具」 按钮,进入 tool 调试界面。tool 面板的基本介绍如下所示 + +![](./images/tool-desc.png) + +调试工具,我们需要先在「工具列表」中选择一个工具(如果没有展开需要先展开工具列表,点击右侧的按钮可以刷新),然后在右侧的「参数填写和执行」中,填写需要测试的参数,点击运行,就能看到结果了: + +![](./images/tool-result.png) + +比如我们这边运算最简单的 2 + 2,可以看到结果是 4,这说明我们的 mcp 连接正常还可以正常返回结果。大家未来可以通过简单测试来验证 mcp 服务器的可用性,这在复杂 agent 系统的调试过程中非常重要。可以编码成自检程序的一部分。 + +## 添加测试项目 + +测试完成一个项目后,我们可以通过点击上方的 + 来添加额外的测试项目: + +![](./images/tool-add-test-project.png) + +这里,我们选择「资源」来进行资源项目的调试工作,「资源」和另外两个项目有点不一样,MCP 协议中的资源访问有两种类型 + +- resources/templates/list: 模板资源,带有访问参数,比如文件系统 mcp 中的文件访问,输入文件路径,根据资源协议返回文件内容。 +- resources/list:普通资源,不带访问参数,比如浏览器 mcp 中的 console,直接返回控制台的 stdio,这种就不需要参数。 + +![](./images/resource-desc.png) + + +`resources/templates/list` 的使用方法和之前的 tool 一样,填入参数点击运行就能看到资源结果 + +![](./images/resource-result.png) + +而 `resources/list` 由于没有参数,直接点击左侧的资源就能直接看到内部的数据。 + +## 总结 + +在这一章节中,我们主要介绍了如何使用 openmcp 来调试 MCP 服务器,包括如何调试 tool 和 resource,prompt 的方法和这两个类似,大家可以自行尝试。下一章中,我们将开启最激动人心的一章,我们将把开发的 mcp 服务器扔到大模型中进行测试,这样你才知道你写的 mcp 是不是真的好玩,是不是有价值。 diff --git a/zh/plugin-tutorial/usage/connect-llm.md b/zh/plugin-tutorial/usage/connect-llm.md new file mode 100644 index 0000000..8647399 --- /dev/null +++ b/zh/plugin-tutorial/usage/connect-llm.md @@ -0,0 +1,69 @@ +# 连接大模型 + +如果需要使用「交互测试」来在和大模型的交互中测试 MCP 工具的性能,你需要首先需要在 OpenMCP 配置大模型。 + +:::warning 协议兼容性警告 +目前 OpenMCP 只支持符合 OpenAI 接口规范的 大模型服务,其他大模型的调用需要请通过 [newApi](https://github.com/QuantumNous/new-api) 进行转发或者自行实现。 + +目前市面上主流的如下模型我们都是支持的,如果遇到大模型连接的问题,请随时 [[channel|联系我们]]。 +::: + +在 「设置」-「API」 可以进入大模型的连接配置界面。 + +![](./images/setting-api.png) + +## 默认支持的模型 + +OpenMCP 默认填充了市面上常见的大模型,下面是支持的模型 + +| 大模型 Name | 提供商 | baseUrl | 默认模型 | +|----------------------|---------------------------|---------------------------------------------|-----------------------| +| DeepSeek | DeepSeek | `https://api.deepseek.com/v1` | `deepseek-chat` | +| OpenAI | OpenAI | `https://api.openai.com/v1` | `gpt-4-turbo` | +| 通义千问 Qwen | Alibaba | `https://dashscope.aliyuncs.com/compatible-mode/v1` | `qwen-plus` | +| 豆包 Seed | ByteDance | `https://ark.cn-beijing.volces.com/api/v3` | `doubao-1.5-pro-32k` | +| Gemini | Google | `https://generativelanguage.googleapis.com/v1beta/openai/` | `gemini-2.0-flash` | +| Grok | xAI | `https://api.x.ai/v1` | `grok-3-mini` | +| Mistral | Mistral AI | `https://api.mistral.ai/v1` | `mistral-tiny` | +| Groq | Groq | `https://api.groq.com/openai/v1` | `mixtral-8x7b-32768` | +| Perplexity | Perplexity AI | `https://api.perplexity.ai/v1` | `pplx-7b-online` | +| Kimi Chat | 月之暗面 (Moonshot AI) | `https://api.moonshot.cn/v1` | `moonshot-v1-8k` | + + +## 配置大模型 + +你需要做的只是把对应服务商的 apiToken 填入 openmcp 中即可。然后点击「测试」,看到下面的响应说明连接成功。您就可以在交互测试里面使用大模型了! + +![](./images/setting-api-test.png) + +:::warning +有些用户会遇到无法访问的问题,请确保你的 baseUrl 填写正确。如果在国内使用某些国外厂商的服务,比如 gemini,openai,请确保你的网络环境可以访问到这些服务。在 「设置」-「通用」中你可以设置代理服务器。 +::: + + +## 添加模型 + +如果你想使用的指定服务商的模型不在默认支持的模型中,有两种方法可以添加它们。 + +### 方法一:更新模型列表 + +此处以通义千问为例子,确保在 apitoken 填写正确的情况下,点击「更新模型列表」,如果服务商严格实现了 openai 标准,那么就能看到所有更新的模型了。 + +![](./images/setting-update-models.png) + +### 方法二:手动添加模型 + +如果你的服务器没有支持 openai 标准,你将无法使用「方法一」,你可以如此手动添加模型列表。此处以 Grok 为例,在服务商中找到 grok,点击图中所示的编辑 + +![](./images/setting-api-edit.png) + +点击模型,输入模型名称,回车,点击确认: + +![](./images/setting-api-edit-1.png) + + +回到 api 页面,再点击保存。 + +## 添加服务 + +如果你要的服务商没有出现我们的列表中(云服务商的服务,或者自己部署的服务),可以通过「添加服务」按钮来添加自定义模型,使用方法和「添加模型」「方法二:手动添加模型」类似,就不赘述了。 diff --git a/zh/plugin-tutorial/usage/connect-mcp.md b/zh/plugin-tutorial/usage/connect-mcp.md new file mode 100644 index 0000000..267d008 --- /dev/null +++ b/zh/plugin-tutorial/usage/connect-mcp.md @@ -0,0 +1,77 @@ +# 连接 mcp 服务器 + +不同于 Claude Desktop 和其他的 MCP 客户端类产品,OpenMCP 进行 MCP 服务器连接的步骤是相当丝滑的。 + +:::info MCP客户端 +MCP 客户端是指能够通过 MCP 协议进行通信的大模型对话客户端,通常是一个运行在本地的应用程序(因为网页没有文件IO的权限)。它的产品形式目前几乎都是聊天机器人的形式,类似于你在网页使用的 chat.deepseek.com 或者 chat.openai.com +::: + +首先,打开你的 VLE,在 [[acquire-openmcp|获取 OpenMCP]] 中完成 OpenMCP 的安装后,我们先用 python 创建一个最简单的 mcp 服务器,来测试 mcp 客户端的连接。 + + +## 使用 OpenMCP 一键连接 + +在 [[first-mcp|你的第一个 MCP]] 这个例子中,我们申明了三个函数,用作 mcp 的 tool,resource 和 prompt。在 OpenMCP 中启动它们非常简单,点击右上角的 OpenMCP 图标即可连接: + +![](./images/connect-simple.png) + + +如果登录完成后,如图显示连接成功,则代表当前已经成功启动并连接 mcp 服务器。 + +![](./images/connect-success.png) + +## STDIO 连接的启动 + +对于 STDIO 为连接选项的开发方案,我们提供了一键式的快速启动,您不需要额外启动 mcp 的进程。OpenMCP 会自动连接和销毁。 + +目前支持的编程语言和它们对应的启动参数为: + +|语言|连接参数|启动目录| +|:-|:-|:-| +|python|uv run mcp run $\{file\} | 往上追溯,第一个找到的 pyproject.toml 的目录| +|nodejs|node $\{file\}| 往上追溯,第一个找到的 package.json 的目录| +|go|go run $\{file\}| 往上追溯,第一个找到的 go.mod 的目录| + +## SSE & Streamable HTTP 连接的启动 + +对于 SSE 和 Streamable HTTP 这两种远程连接的方式,由于我们并不知道您到底在哪个端口启动的服务器(因为你有可能把启动的 host 和 port 写在不可见的配置文件里或者写在环境变量里),因此,对于远程连接的情况,我们不支持自动创建服务器,您需要手动配置启动选项。 + +点击 VLE 左侧插件栏目的 OpenMCP,在 「MCP 连接(工作区)」 视图中,点击 + ,就可以创建一个新的连接。 + +![](./images/add-connection.png) + + + +选择你需要的通信方式。 + +![](./images/select-server-type.png) + +输入MCP Server的地址。 + +![](./images/connect-sse.png) + +:::info +需要注意的是,不同的通信方式一般使用不同endpoint,目前的MCP server大多遵循下面的原则: + +如果是以 SSE 启动,那么默认使用 /sse 作为endpoint,比如 http://localhost:8001/sse + +如果是以 Streamable Http 启动,那么默认使用 /mcp 作为endpoint,比如 http://localhost:8001/mcp + +当然,允许MCP Server使用两个不同的endpoint同时支持两种连接方式,这对于想要迁移到Streamable Http但短时间又不能放弃SSE的情况特别有效 +::: + +## openmcp 插件的控制面板 + +在 VLE 的左侧可以找到 openmcp 的图标,点击后就是 openmcp 的控制面板。 + +![](./images/openmcp-control-panel.png) + +当前工作区曾经连接过的 mcp 服务器会出现在这里,这是因为 openmcp 默认将工作区启动的 mcp 的连接信息存储在了 `.openmcp/tabs.{server-name}.json` 中,其中 `{server-name}` 就是 mcp 服务器连接成功的服务器名称。 + +:::warning +注意,同一个项目中,你不应该有两个名字完全相同的 mcp 服务器,这会导致 `.openmcp/tabs.{server-name}.json` 连接信息存储冲突,发生未知错误。 +::: + +如果你想要在任意工作区都能使用同一个 mcp 服务器,可以考虑在「安装的 MCP 服务器」中添加成熟耐用的 mcp 服务器,这个位置添加的 mcp 服务器全局可用。 + +在「入门与帮助」中,我们准备了一些可供入门的参考资料,还请阁下善加利用。 diff --git a/zh/plugin-tutorial/usage/debug.md b/zh/plugin-tutorial/usage/debug.md new file mode 100644 index 0000000..c38fd98 --- /dev/null +++ b/zh/plugin-tutorial/usage/debug.md @@ -0,0 +1,68 @@ +# 调试 tools, resources 和 prompts + +## 标签页 + +openmcp 以标签页作为调试项目的最小单元,点击栏目中的 + 可以创建新的标签页。OpenMCP 的 tools, resources 和 prompts 的基本使用与 Inspector 差不多,但是 OpenMCP 会自动帮您完成左侧资源列表的初始化,Inspector 中这一步需要手动完成。 + +## 调试内容的自动保存 + +openmcp 具备自动保存测试结果的功能。如下的行为会触发 openmcp 对标签页及其内容进行保存: + +- 创建标签页,并选择一个有效的调试项目 +- 在调试页进行调试行为(选择工具,执行工具,询问大模型等) + +当前 mcp 项目的测试数据会被保存在 `.openmcp/tabs.{server-name}.json` 中,其中 `{server-name}` 就是 mcp 服务器连接成功的服务器名称。 + +:::warning +注意,同一个项目中,你不应该有两个名字完全相同的 mcp 服务器,这会导致 `.openmcp/tabs.{server-name}.json` 连接信息存储冲突,发生未知错误。 +::: + +## 快速调试 + +在我们调试的过程中,难免会出现大模型回答得不好,而且这是因为某个工具出错导致的,为了快速定位是不是工具的问题,可以点击下方的小飞机图标 + +![](./images/llm-fast-debug.png) + +点击后,OpenMCP 会一个新的测试 tool 的项目,并自动把当时大模型使用的参数自动填充到右侧的表单中: + +![](./images/llm-fast-debug-result.png) + +你要做的,只是点击运行来确定或者排除一个错误选项。 + +## pydantic 支持 + +使用 python 的 fastmcp 进行 tool 的创建时,你有两种方法来申明接口的类型,一种是通过 python 默认的 typing 库来申明复杂数据结构,或者通过 pydantic 来申明一个复杂的变量,下面是一个例子: + +```python +from mcp.server.fastmcp import FastMCP +from pydantic import BaseModel, Field +from typing import Optional, Union, List, NamedTuple + +mcp = FastMCP('锦恢的 MCP Server', version="11.45.14") + +class PathParams(BaseModel): + start: str + end: str + +@mcp.tool(name="test",description="用来测试") +def test( + params: PathParams, + test1: str, + test2: Union[str, List[str]] = Field("", description="测试参数2"), + test3: Optional[str] = Field(None, description="测试参数3") +): + return [test1, test2, test3, params] +``` + +由于我们对这两种类型的申明方式实现了内部的转换,所以 openmcp 都是支持的。值得一提的是,如果你申明的变量是一个对象,比如上面的 `PathParams`,那么 openmcp 的 tool 调试窗口会生成一个「对象输入框」,这个输入框支持基本的格式检查和自动补全: + +![](./images/object-input.png) + +:::info 什么是对象? +这里的「对象」是 javascript 中的概念,它指的是可被序列化的数据类型中,除去基本数据类型后,剩下的那部分。比如 { "name": "helloworld" } 就是一个对象。对象在 python 中更加类似于一个 dict 或者 namedTuple。 +::: + +:::warning +虽然 openmcp 已经做到了尽可能多情况的支持,但是生产场景中,我们仍然不建议您将 mcp tool 的参数定义成对象,尽可能定义成简单数据类型也能更好提高大模型进行工具调用时的稳定性。 +::: + diff --git a/zh/plugin-tutorial/usage/distribute-result.md b/zh/plugin-tutorial/usage/distribute-result.md new file mode 100644 index 0000000..2ea20c1 --- /dev/null +++ b/zh/plugin-tutorial/usage/distribute-result.md @@ -0,0 +1,44 @@ +# 分发您的实验结果 + +## 标签页恢复 + +openmcp 默认会实时保存您的实验结果,每一个在工作区开启的服务器默认会将结果存储在 `.openmcp/tabs.{server-name}.json` 中,其中 `{server-name}` 就是 mcp 服务器连接成功的服务器名称。 + +请确保您的 `.gitignore` 文件中没有包含匹配到 .openmcp 文件夹的规则。这样,当您通过 git 提交你的代码,对 agent 的代码进行管理时,当你在别的电脑上 clone 或者他人 clone 你的项目时,就能快速恢复你上一次的实验内容,继续进行实验或者开发调试。 + +## 连接恢复 + +每一个 mcp 服务器的连接信息会被保存在 `.openmcp/connection.json` 中,下面是一个例子: + +```json +{ + "items": [ + [ + { + "connectionType": "STDIO", + "command": "mcp", + "args": [ + "run", + "main.py" + ], + "url": "", + "cwd": "{workspace}/simple-mcp", + "oauth": "", + "clientName": "openmcp.connect.STDIO", + "clientVersion": "0.0.1", + "env": {}, + "serverInfo": { + "name": "锦恢的 MCP Server", + "version": "1.9.2" + }, + "filePath": "{workspace}/simple-mcp/main.py", + "name": "锦恢的 MCP Server", + "version": "1.9.2" + } + ] + ] +} +``` + + +当您打开左侧的控制面板或者打开一个过去打开过的 mcp 服务器时, mcp 默认会根据上面的信息来获取工作区的服务器列表或者尝试进行自动连接。如果 openmcp 在连接 mcp 时发生了初始化错误或者保存错误,除了向 openmcp 官方求助外,您还可以尝试手动管理 `.openmcp/connection.json` 文件。 \ No newline at end of file diff --git a/zh/plugin-tutorial/usage/images/add-connection.png b/zh/plugin-tutorial/usage/images/add-connection.png new file mode 100644 index 0000000..9bde43b Binary files /dev/null and b/zh/plugin-tutorial/usage/images/add-connection.png differ diff --git a/zh/plugin-tutorial/usage/images/add-new-mcp.png b/zh/plugin-tutorial/usage/images/add-new-mcp.png new file mode 100644 index 0000000..a05c9f0 Binary files /dev/null and b/zh/plugin-tutorial/usage/images/add-new-mcp.png differ diff --git a/zh/plugin-tutorial/usage/images/change-color.png b/zh/plugin-tutorial/usage/images/change-color.png new file mode 100644 index 0000000..e891ea4 Binary files /dev/null and b/zh/plugin-tutorial/usage/images/change-color.png differ diff --git a/zh/plugin-tutorial/usage/images/connect-simple.png b/zh/plugin-tutorial/usage/images/connect-simple.png new file mode 100644 index 0000000..20fd5f3 Binary files /dev/null and b/zh/plugin-tutorial/usage/images/connect-simple.png differ diff --git a/zh/plugin-tutorial/usage/images/connect-sse.png b/zh/plugin-tutorial/usage/images/connect-sse.png new file mode 100644 index 0000000..b178ef6 Binary files /dev/null and b/zh/plugin-tutorial/usage/images/connect-sse.png differ diff --git a/zh/plugin-tutorial/usage/images/connect-success.png b/zh/plugin-tutorial/usage/images/connect-success.png new file mode 100644 index 0000000..0100f62 Binary files /dev/null and b/zh/plugin-tutorial/usage/images/connect-success.png differ diff --git a/zh/plugin-tutorial/usage/images/drag-to-fill.png b/zh/plugin-tutorial/usage/images/drag-to-fill.png new file mode 100644 index 0000000..e2ce8e9 Binary files /dev/null and b/zh/plugin-tutorial/usage/images/drag-to-fill.png differ diff --git a/zh/plugin-tutorial/usage/images/guide.png b/zh/plugin-tutorial/usage/images/guide.png new file mode 100644 index 0000000..9dff225 Binary files /dev/null and b/zh/plugin-tutorial/usage/images/guide.png differ diff --git a/zh/plugin-tutorial/usage/images/llm-fast-debug-result.png b/zh/plugin-tutorial/usage/images/llm-fast-debug-result.png new file mode 100644 index 0000000..31706f4 Binary files /dev/null and b/zh/plugin-tutorial/usage/images/llm-fast-debug-result.png differ diff --git a/zh/plugin-tutorial/usage/images/llm-fast-debug.png b/zh/plugin-tutorial/usage/images/llm-fast-debug.png new file mode 100644 index 0000000..183dbad Binary files /dev/null and b/zh/plugin-tutorial/usage/images/llm-fast-debug.png differ diff --git a/zh/plugin-tutorial/usage/images/oauth-github-ak.png b/zh/plugin-tutorial/usage/images/oauth-github-ak.png new file mode 100644 index 0000000..c2b9813 Binary files /dev/null and b/zh/plugin-tutorial/usage/images/oauth-github-ak.png differ diff --git a/zh/plugin-tutorial/usage/images/oauth-github-new-application.png b/zh/plugin-tutorial/usage/images/oauth-github-new-application.png new file mode 100644 index 0000000..dc26ccd Binary files /dev/null and b/zh/plugin-tutorial/usage/images/oauth-github-new-application.png differ diff --git a/zh/plugin-tutorial/usage/images/oauth-github-success.png b/zh/plugin-tutorial/usage/images/oauth-github-success.png new file mode 100644 index 0000000..c84c737 Binary files /dev/null and b/zh/plugin-tutorial/usage/images/oauth-github-success.png differ diff --git a/zh/plugin-tutorial/usage/images/oauth-github-tool.png b/zh/plugin-tutorial/usage/images/oauth-github-tool.png new file mode 100644 index 0000000..f503075 Binary files /dev/null and b/zh/plugin-tutorial/usage/images/oauth-github-tool.png differ diff --git a/zh/plugin-tutorial/usage/images/object-input.png b/zh/plugin-tutorial/usage/images/object-input.png new file mode 100644 index 0000000..f8cb1b1 Binary files /dev/null and b/zh/plugin-tutorial/usage/images/object-input.png differ diff --git a/zh/plugin-tutorial/usage/images/one-dark-pro.png b/zh/plugin-tutorial/usage/images/one-dark-pro.png new file mode 100644 index 0000000..c8a30aa Binary files /dev/null and b/zh/plugin-tutorial/usage/images/one-dark-pro.png differ diff --git a/zh/plugin-tutorial/usage/images/openmcp-control-panel.png b/zh/plugin-tutorial/usage/images/openmcp-control-panel.png new file mode 100644 index 0000000..d8203bb Binary files /dev/null and b/zh/plugin-tutorial/usage/images/openmcp-control-panel.png differ diff --git a/zh/plugin-tutorial/usage/images/parallel-tool-call.png b/zh/plugin-tutorial/usage/images/parallel-tool-call.png new file mode 100644 index 0000000..da7e443 Binary files /dev/null and b/zh/plugin-tutorial/usage/images/parallel-tool-call.png differ diff --git a/zh/plugin-tutorial/usage/images/prompt.png b/zh/plugin-tutorial/usage/images/prompt.png new file mode 100644 index 0000000..7957e47 Binary files /dev/null and b/zh/plugin-tutorial/usage/images/prompt.png differ diff --git a/zh/plugin-tutorial/usage/images/resource.png b/zh/plugin-tutorial/usage/images/resource.png new file mode 100644 index 0000000..aec314d Binary files /dev/null and b/zh/plugin-tutorial/usage/images/resource.png differ diff --git a/zh/plugin-tutorial/usage/images/select-server-type.png b/zh/plugin-tutorial/usage/images/select-server-type.png new file mode 100644 index 0000000..ec7f182 Binary files /dev/null and b/zh/plugin-tutorial/usage/images/select-server-type.png differ diff --git a/zh/plugin-tutorial/usage/images/setting-api-edit-1.png b/zh/plugin-tutorial/usage/images/setting-api-edit-1.png new file mode 100644 index 0000000..00bd559 Binary files /dev/null and b/zh/plugin-tutorial/usage/images/setting-api-edit-1.png differ diff --git a/zh/plugin-tutorial/usage/images/setting-api-edit.png b/zh/plugin-tutorial/usage/images/setting-api-edit.png new file mode 100644 index 0000000..25264ba Binary files /dev/null and b/zh/plugin-tutorial/usage/images/setting-api-edit.png differ diff --git a/zh/plugin-tutorial/usage/images/setting-api-test.png b/zh/plugin-tutorial/usage/images/setting-api-test.png new file mode 100644 index 0000000..d0200a2 Binary files /dev/null and b/zh/plugin-tutorial/usage/images/setting-api-test.png differ diff --git a/zh/plugin-tutorial/usage/images/setting-api.png b/zh/plugin-tutorial/usage/images/setting-api.png new file mode 100644 index 0000000..89ea3eb Binary files /dev/null and b/zh/plugin-tutorial/usage/images/setting-api.png differ diff --git a/zh/plugin-tutorial/usage/images/setting-update-models.png b/zh/plugin-tutorial/usage/images/setting-update-models.png new file mode 100644 index 0000000..efe5462 Binary files /dev/null and b/zh/plugin-tutorial/usage/images/setting-update-models.png differ diff --git a/zh/plugin-tutorial/usage/images/system-prompt.png b/zh/plugin-tutorial/usage/images/system-prompt.png new file mode 100644 index 0000000..f7e52ba Binary files /dev/null and b/zh/plugin-tutorial/usage/images/system-prompt.png differ diff --git a/zh/plugin-tutorial/usage/images/trae-blue.png b/zh/plugin-tutorial/usage/images/trae-blue.png new file mode 100644 index 0000000..ea7f5f0 Binary files /dev/null and b/zh/plugin-tutorial/usage/images/trae-blue.png differ diff --git a/zh/plugin-tutorial/usage/multi-server.md b/zh/plugin-tutorial/usage/multi-server.md new file mode 100644 index 0000000..d12bb56 --- /dev/null +++ b/zh/plugin-tutorial/usage/multi-server.md @@ -0,0 +1,35 @@ +# 连接多个 MCP 服务器 + +openmcp 支持连接多个 MCP 服务器。 + +假设你现在想要实现一个可以自动查阅资料并且整理成 word 文档的 agent,你可以这样做: + +1. 找到能进行网络搜索的 mcp:[crawl4ai mcp](https://github.com/LSTM-Kirigaya/openmcp-tutorial/tree/main/crawl4ai-mcp) +2. 找到能进行 word 操作的 mcp:[Office-Word-MCP-Server](https://github.com/GongRzhe/Office-Word-MCP-Server) +3. 在 openmcp 中把它们组合起来。 +4. 自动完成你的任务! + +假设,我们已经连接了第一个 mcp,也就是 crawl4ai mcp,我们可以添加额外的 mcp 服务器: + +![](./images/add-new-mcp.png) + +## 添加方法一:拖拽 + +直接把需要加入的 mcp 服务器的文件,按住 shift 点击拖拽进入 openmcp 界面就能自动完成参数的填充。 + +![](./images/drag-to-fill.png) + +:::warning +自动填充的命令不一定总是准确的,在 [STDIO 连接的启动](http://localhost:5173/openmcp/plugin-tutorial/usage/connect-mcp.html#stdio-%E8%BF%9E%E6%8E%A5%E7%9A%84%E5%90%AF%E5%8A%A8) 中我们说过这一点。具体的连接方法请阅读 [附录:关于 uv 启动 mcp 你必须知道的](http://localhost:5173/openmcp/plugin-tutorial/quick-start/first-mcp.html#%E9%99%84%E5%BD%95-%E5%85%B3%E4%BA%8E-uv-%E5%90%AF%E5%8A%A8-mcp-%E4%BD%A0%E5%BF%85%E9%A1%BB%E7%9F%A5%E9%81%93%E7%9A%84) 后自行判断。 +::: + +## 添加方法二:手动填写参数 + +手动填写参数,没啥好说的。 + + +## 使用多服务器 + +多服务器连接后的使用和单服务器没有太大的区别,openmcp 内部会自动完成工具的调度和选择。唯一需要注意的是,多服务器的 tool name 一定不要重名,否则会出现冲突。 + +如果您认为 tool 重名有存在的必要性,请通过 [issue](https://github.com/LSTM-Kirigaya/openmcp-client/issues) 让我们知道您的场景和想法,根据讨论,我们会支持。 \ No newline at end of file diff --git a/zh/plugin-tutorial/usage/sse-oauth2.md b/zh/plugin-tutorial/usage/sse-oauth2.md new file mode 100644 index 0000000..b717357 --- /dev/null +++ b/zh/plugin-tutorial/usage/sse-oauth2.md @@ -0,0 +1,77 @@ +# MCP Server的OAuth鉴权实现 + +在使用 **SSE** 或 **Streamable HTTP** 进行连接时,为增强安全性可为接口设计鉴权机制,MCP 官方推荐采用 OAuth 协议。下面以获取 GitHub 用户信息为例,演示如何通过 openmcp-client 完成带 OAuth 认证的接口调试。 + + + +## 1. 获取Github OAuth认证ID和secret + +由于我们使用了Github用户信息相关API,需要先获取Github OAuth应用的Client ID和Client secret。 + +先进入[Github Developers](https://github.com/settings/developers),点击`New OAuth App`新建一个OAuth APP,应用名称随便填,`Homepage URL`填写`http://localhost:8000`,`Authorization callback URL`填写`http://localhost:8000/github/callback`。然后点击`Register application`按钮,即可成功注册一个应用。 + + +![](images/oauth-github-new-application.png) + + +注册成功后,请记录`Client ID`,然后点击`Generate a new client secret`生成一个`secret`,注意secret仅在生成的时候可见。 + +## 2. 设置环境变量 + +在获取`Client ID`和`secret`之后,需要将其设置为环境变量: + + + +::: code-group +```bash [bash] +export MCP_GITHUB_GITHUB_CLIENT_ID={{Client ID}} +export MCP_GITHUB_GITHUB_CLIENT_SECRET={{secret}} +``` + +```bash [PowerShell] +$env:MCP_GITHUB_CLIENT_ID = "your_id" +$env:MCP_GITHUB_CLIENT_SECRET = "your_secret" +``` + +```bash [CMD] +set MCP_GITHUB_GITHUB_CLIENT_ID={{Client ID}} +set MCP_GITHUB_GITHUB_CLIENT_SECRET={{secret}} +``` +::: + +注意:cmd里面设置环境变量请不要加引号。 + +## 3. 克隆源码 + +接下来,我们需要部署带有OAuth认证的MCP服务器。可以参照[官方python案例](https://github.com/modelcontextprotocol/python-sdk/tree/main/examples/servers/simple-auth)进行。 + +需要先克隆官方python-sdk源码: + +```bash +git clone https://github.com/modelcontextprotocol/python-sdk/ # 克隆源码 +cd examples/servers/simple-auth # 进入对应的目录 +``` + +## 4. 启动MCP Server + +先根据需要创建虚拟环境安装依赖,然后可以使用`uv`运行或者直接运行`python main.py`即可,注意需要先设置环境变量,不然启动会报错`2 validation errors for ServerSettings`。 + +## 5. 启动openmcp-client + +接下来,你就可以使用openmcp-client连接刚刚启动的server了,不管是使用网页端还是VSCode均可。 + +点击加号添加连接,根据server代码中的`--transport`参数决定是SSE还是Streamable HTTP。如果是SSE,则URL填写`http://localhost:8000/sse`;如果是Streamable HTTP,则URL填写`http://localhost:8000/mcp`。认证签名无需填写。 + +接下来连接到当前server,此时会自动打开一个网页进行认证,首次打开需要点击认证,认证成功后该网页会自动关闭。 + +![](images/oauth-github-success.png) + +认证成功后,进入工具页面,应该能看到一个`get_user_profile`工具,点击使用就可以获取到你的Github个人信息了。 + +![](images/oauth-github-tool.png) + + + + + + diff --git a/zh/plugin-tutorial/usage/test-with-llm.md b/zh/plugin-tutorial/usage/test-with-llm.md new file mode 100644 index 0000000..2108ec3 --- /dev/null +++ b/zh/plugin-tutorial/usage/test-with-llm.md @@ -0,0 +1,72 @@ +# 用大模型测试您的 mcp + +如果您完成了 [[connect-llm|连接 mcp 服务器]] 这一步,那么您就可以开始测试您的 mcp 了。 + +在 [[put-into-llm|扔进大模型里面测测好坏!]] 中,我们已经通过一个简单的例子来展示来如何使用大模型测试您的 mcp。因此,这篇文章更多是讲解不便在「快速开始」中赘述的细节。 + + +和大模型交互时,有一些参数可以选择,也就是输入框下面那一排按钮,我来简单介绍一下。 + +## 选择模型 + +顾名思义,你可以在这里切换你的模型。值得一提的是,openmcp 会以单条对话为粒度来记录每一条对话使用的模型。您可以利用这一特性来进行混合模型测试。 + +如果你没有找到你想要的模型,或是想要添加额外的模型,请移步 [[connect-llm|连接 mcp 服务器]] 来了解如何添加模型。 + +## 系统提示词 + +您可以在这里选择和添加系统提示词。 + +![](./images/system-prompt.png) + +openmcp 默认将您的系统提示词保存在 `~/.openmcp/nedb/systemPrompt.db` 中。您可以通过 nedb 来进行反序列化和拷贝。 + + +## 提词 + +您可以利用该模块来调用 mcp 服务器提供的 prompt 功能,生成的 prompt 字段会作为富文本被插入您的对话中。 + +![](./images/prompt.png) + +## 资源 + +您可以利用该模块来调用 mcp 服务器提供的 resource 功能,生成的 resource 字段会作为富文本被插入您的对话中。 + +![](./images/resource.png) + +:::warning openmcp 不负责 resource 的数据持久化! +请注意!每次对话完成后 resource 是否会被保存到磁盘完全由 mcp server 作者决定,openmcp 不负责 resource 的数据持久化!如果您发现关闭 openmcp 再打开,resource 彩蛋为空,这不是 openmcp 的 bug,而是 mcp server 作者没有支持数据持久化! +::: + + +## 允许模型在单轮回复中调用多个工具 + +大模型在进行工具调用时,有时候会将在一次回复中要求调用多次工具,比如你想要同时获取三个网页的内容翻译,大模型可能会同时调用三次「网络搜索」工具(如果你提供了的话)。多次工具使用时,openmcp 会如此渲染调用执行过程: + +![](./images/parallel-tool-call.png) + +openmcp 输入框的按钮中的「允许模型在单轮回复中调用多个工具」默认是点亮的。也就是允许大模型可以在一次回复中调用多次工具。 + +有的时候,我们希望命令一条条执行,就可以选择把这个按钮熄灭。 + +:::warning 协议兼容性警告 +有的厂商(比如 gemini)不一定严格支持了 openai 协议,对于不支持 「允许模型在单轮回复中调用多个工具」的厂商,openmcp 的后端会自动将该选项关闭。 +::: + + +## 温度参数 + +温度参数越高,生成内容的随机性越强,对于通用大模型,个人建议参数为 0.6 ~ 0.7,会是一个适用于通用型任务的数值。OpenMCP 提供的默认值为 0.6。 + +## 上下文长度 + +上下文长度代表了大模型的最大上下文轮数,默认值为 20。举个例子,如果你和大模型对话产生了 40 轮数据(工具调用条数+你的问题数量+大模型回答的次数总共 40 个),那么下一次对话 openmcp 只会发送最后 20 条数据给大模型。 + +:::warning 上下文长度不要太小! +我们强烈不推荐你将这个数值设置低于 20,因为大模型接受工具调用的结果需要和之前的调用请求一一对应。如果不对应,大模型会返回注入 400 这样的错误。遇到这样的错误,请从最初一句话重启或者新开一个「交互测试」。 +::: + +## MCP 服务器超时 + +MCP 服务器默认超时时间为 30 秒,如果您的 mcp 服务器需要更长的时间才能完成任务,您可以通过「设置」「通用」来设置超时时间,单位为秒,该设置全局生效。 + diff --git a/zh/plugin-tutorial/usage/ui-color.md b/zh/plugin-tutorial/usage/ui-color.md new file mode 100644 index 0000000..cf53207 --- /dev/null +++ b/zh/plugin-tutorial/usage/ui-color.md @@ -0,0 +1,25 @@ +# UI 配色 + +## openmcp 的主题色随跟 vscode + +openmcp 的主题颜色完全跟随 vscode,如果你想要更换 openmcp 的主题颜色,你只需要更换 vscode 的主题颜色即可。 + +比如当你把颜色切换为社区知名主题 One Dark Pro 时,openmcp 的表现: + +![](./images/one-dark-pro.png) + + +## 切换主题色 + +这里可以切换 openmcp 的主题色(默认是粉色) + +![](./images/change-color.png) + + +## Trae 的特殊支持 + +openmcp 对 trae 的默认主题色都有额外的支持。我们也鼓励我们的用户尝试 vscode,cursor,trae 等不同的 VLE 来获得最佳手感。 + +openmcp 官方文档大部分演示的例子都是基于 trae 的「深蓝」默认主题。 + +![](./images/trae-blue.png) \ No newline at end of file diff --git a/zh/plugin-tutorial/what-is-mcp.md b/zh/plugin-tutorial/what-is-mcp.md new file mode 100644 index 0000000..8afe97d --- /dev/null +++ b/zh/plugin-tutorial/what-is-mcp.md @@ -0,0 +1,181 @@ +# 什么是 MCP? + +![](https://picx.zhimg.com/70/v2-1a2df8a081a76f4e90431d8a2445f495_1440w.avis) + +MCP (Model Context Protocol)是一种开放协议,用于标准化应用程序如何向大型语言模型(LLMs)提供上下文。可以将 MCP 想象为 AI 应用的 typec 接口。正如 typec 提供了一种标准化的方式将您的设备连接到各种外设和配件,MCP 也提供了一种标准化的方式,将 AI 模型连接到不同的数据源和工具。 + +MCP 协议由 Anthropic 在 2024 年 11 月底推出: + +- 官方文档:[Introduction](https://modelcontextprotocol.io/introduction) +- GitHub 仓库:[github.com/modelcontextprotocol](https://github.com/modelcontextprotocol) + + +## 为什么需要 MCP? + +我们都知道,从最初的 chatgpt,到后来的 cursor,copilot chatroom,再到现在耳熟能详的 agent,实际上,从用户交互的角度去观察,你会发现目前的大模型产品经历了如下的变化: + +``` mermaid +graph LR + +a(chatbot > deepseek, chatgpt) --> b(composer > cursor, copilot) --> c(agent > AutoGPT, Manus, Open Manus) +``` + +- chatbot + - 只会聊天的程序。 + - 工作流程:你输入问题,它给你这个问题的解决方案,但是具体执行还需要你自己去。 + - 代表工作:deepseek,chatgpt +- composer + - 稍微会帮你干活的实习生,仅限于写代码。 + - 工作流程:你输入问题,它会给你帮你生成解决问题的代码,并且自动填入代码编辑器的编译区,你只需要审核确认即可。 + - 代表工作:cursor,copilot +- agent + - 私人秘书。 + - 工作流程:你输入问题,它生成这个问题的解决方案,并在征询了你的同意后全自动执行。 + - 代表工作:AutoGPT,Manus,Open Manus + + + +为了实现 agent,也就需要让 LLM 可以自如灵活地操作所有软件甚至物理世界的机器人,于是需要定义统一的上下文协议与之上统一工作流。MCP(model context protocol) 就是解决这套方案的应运而生的基础协议。一个感性认识如下: + +```mermaid +graph TB + +user(用户) +ai(AI软件) +llm(大模型) +computer(本地环境) + +user --帮我整理两会报告中有\n关AI的咨询到word文件--> agent + +subgraph agent + +ai <--MCP--> llm +computer <--MCP--> ai + +end + +agent --> word(D:/会议总结/两会报告AI专题.docx) + +``` + + +:::info + +Anthropic 对于 MCP 的必要性给出的解释:MCP 帮助您在 LLMs 之上构建 agent 和复杂的工作流程。LLMs 经常需要与数据和工具集成,而 MCP 提供了以下支持: + +- 一系列不断增长的预构建集成,您的 LLM 可以直接接入这些集成。 +- 在 LLM 提供商和供应商之间灵活切换。 +- 在基础设施内保护数据的最佳实践。 + +::: + + + + + +## 总体架构 + +MCP 的核心采用客户端-服务器架构,其中 host 可以连接到多个服务器: + +```mermaid +graph LR + +host[Host MCP 客户端\n 浏览器, 代码编辑器, 其他工具] + +server_a[MCP 服务器 A] +server_b[MCP 服务器 B] +server_c[MCP 服务器 C] + +db_a[(本地\n数据源 A)] +db_b[(本地\n数据源 B)] + +remote[(远程服务 C)] + +subgraph 你的电脑 +direction LR +host <--MCP 协议--> server_a +host <--MCP 协议--> server_b +host <--MCP 协议--> server_c +server_a <--> db_a +server_b <--> db_b +end + + +subgraph 互联网 +server_c <--Web APIs--> remote +end +``` + + + +- MCP 主机(MCP Hosts) :MCP 主机是指希望通过 MCP 访问数据的程序,例如 Claude Desktop、集成开发环境(IDEs)或其他 AI 工具。 +- MCP 客户端(MCP Clients):MCP 客户端是与服务器保持 1:1 连接的协议客户端,负责与 MCP 服务器通信。 +- MCP 服务器(MCP Servers):MCP 服务器是轻量级程序,每个服务器通过标准化的 Model Context Protocol 暴露特定的功能。 +- 本地数据源(Local Data Sources):本地数据源是指 MCP 服务器可以安全访问的计算机文件、数据库和服务。 +- 远程服务(Remote Services):远程服务是指 MCP 服务器可以通过互联网连接的外部系统(例如通过 API 访问的服务)。 + + + +## MCP 的工作流程 + +从工作流程上,MCP 和 LSP 非常非常像,事实上,目前的 MCP 和 LSP 一样,也是基于 [JSON-RPC 2.0](https://link.zhihu.com/?target=https%3A//www.jsonrpc.org/specification) 进行数据传输的(基于Stdio 或者 基于SSE)。如果开发过 LSP 的朋友对于 MCP 应该会感到非常的理所当然。我将用若干个简单明了的泳道图尽可能让大家看懂这玩意儿是如何执行的。 + +### 初始化 + +假设我们的软件已经支持了 MCP 客户端,那么当我们的软件启动时,它会经历如下的步骤: + +```mermaid +graph TB + subgraph MCP 客户端 + A1[初始化] + A2[获取 MCP 服务端提供的工具集合 \n1. 创建文件 -> createFile\n2. 删除文件 -> deleteFile\n3. 使用搜索引擎 -> useBrowser\n 4. ...] + end + + subgraph MCP 服务端 + B1[初始化] + end + +A1 --startMCPServer--> B1 +B1 --ListToolsRequestSchema--> A2 +``` + + + +### 工作流程 + + 假设,你是一位 C语言工程师,你现在想要让 agent 自动完成一个项目的编译,那么执行流程如下: + +```mermaid +graph TB + subgraph MCP 客户端 + A1[用户询问 请帮我删除 build\n下的所有编译的中间结果] --> A2[将用户询问, 资源, MCP服务端的工具集合发送给大模型] + + A3[大模型返回操作流\n1. deleteFile build/a.o\n2. deleteFile build/b.o] + A4[整理操作结果给大模型] + A5[最终响应展示给用户] + end + + subgraph MCP 服务端 + B1[解析大模型操作流] --征询用户同意--> B2[执行操作流] + end + + subgraph 大模型 + C1[大模型基于上下文生成操作方案] + + C2[大模型基于所有信息生\n成自然语言响应] + end + +A3 --> B1 +B2 --> A4 +A2 --> C1 +C1 --> A3 +A4 --> C2 +C2 --> A5 +``` + + +## 开源生态 + +和 LSP 一样,LSP 在开源社区有非常多的客户端和服务端框架,MCP 也是一样的,目前 Anthropic 开源了一套 MCP 的服务端框架:https://github.com/modelcontextprotocol/servers ,想要探索大模型效用的朋友可以尽情去使用这个框架。这个仓库还收录了很多的官方认可的 MCP 服务器,可以作为学习的参考。 + +除此之外,pulsemcp 上也有很多开源社区开发的 MCP 客户端和服务端:https://www.pulsemcp.com/clients \ No newline at end of file diff --git a/zh/preview/changelog.md b/zh/preview/changelog.md new file mode 100644 index 0000000..1d3d061 --- /dev/null +++ b/zh/preview/changelog.md @@ -0,0 +1,82 @@ +# Change Log + +## [main] 0.1.1 +- 修复 SSH 连接 Ubuntu 的情况下的部分 bug +- 修复 python 项目点击 openmcp 进行连接时,初始化参数错误的问题 +- 取消 service 底层的 mcp 连接复用技术,防止无法刷新 +- 修复连接后,可能无法在欢迎界面选择调试选项的 bug + +## [main] 0.1.0 +- 新特性:支持同时连入多个 mcp server +- 新特性:更新协议内容,支持 streamable http 协议,未来将逐步取代 SSE 的连接方式 +- impl issue#16:对于 uv 创建的 py 项目进行特殊支持,自动初始化项目,并将 mcp 定向到 .venv/bin/mcp 中,不再需要用户全局安装 mcp +- 对于 npm 创建的 js/ts 项目进行特殊支持:自动初始化项目 +- 去除了 websearch 的设置,增加了 parallel_tool_calls 的设置,parallel_tool_calls 默认为 true,代表 允许模型在单轮回复中调用多个工具 +- 重构了 openmcp 连接模块的基础设施,基于新的技术设施实现了更加详细的连接模块的日志系统. +- impl issue#15:无法复制 +- impl issue#14:增加日志清除按钮 + +## [main] 0.0.9 +- 修复 0.0.8 引入的bug:system prompt 返回的是索引而非真实内容 +- 测试新的发布管线 + +## [main] 0.0.8 +- 大模型 API 测试时更加完整的报错 +- 修复 0.0.7 引入的bug:修改对话无法发出 +- 修复 bug:富文本编辑器粘贴文本会带样式 +- 修复 bug:富文本编辑器发送前缀为空的字符会全部为空 +- 修复 bug:流式传输进行 function calling 时,多工具的索引串流导致的 JSON Schema 反序列化失败 +- 修复 bug:大模型返回大量重复错误信息 +- 新特性:支持一次对话同时调用多个工具 +- UI:优化代码高亮的滚动条 +- 新特性:resources/list 协议的内容点击就会直接渲染,无需二次发送 +- 新特性:resources prompts tools 的结果的 json 模式支持高亮 + +## [main] 0.0.7 +- 优化页面布局,使得调试窗口可以显示更多内容 +- 扩大默认的上下文长度 10 -> 20 +- 增加「通用选项」 -> 「MCP工具最长调用时间 (sec)」 +- 支持富文本输入框,现在可以将 prompt 和 resource 嵌入到输入框中 进行 大规模 prompt engineering 调试工作了 + +## [main] 0.0.6 +- 修复部分因为服务器名称特殊字符而导致的保存实效的错误 +- 插件模式下,左侧管理面板中的「MCP连接(工作区)」视图可以进行增删改查了 +- 新增「安装的 MCP 服务器」,用于安装全局范围的 mcp server +- 增加引导页面 +- 修复无法进行离线 OCR 的问题 +- 修复全局安装的 mcp 服务器 name 更新的问题 + +## [main] 0.0.5 +- 支持对已经打开过的文件项目进行管理 +- 支持对用户对应服务器的调试工作内容进行保存 +- 支持连续工具调用和错误警告的显示 +- 实现小型本地对象数据库,用于对对话产生的多媒体进行数据持久化 +- 支持对于调用结果进行一键复现 +- 支持对中间结果进行修改 +- 支持 system prompt 的保存和修改 + +## [main] 0.0.4 +- 修复选择模型后点击确认跳转回 deepseek 的 bug +- 修复 mcp 项目初始化点击工具全部都是空的 bug +- 修复无法重新连接的 bug +- 支持自定义第三方 openai 兼容的模型服务 + +## [main] 0.0.3 + +- 增加每一条信息的成本统计信息 +- 修复初始化页面路由不为 debug 导致页面空白的 bug + +## [main] 0.0.2 + +- 优化页面布局 +- 解决更新标签页后打开无法显示的 bug +- 解决不如输入组件按下回车直接黑屏的 bug +- 更加完整方便的开发脚本 + +## [main] 0.0.1 + +- 完成 openmcp 的基础 inspector 功能 +- 完成配置加载,保存,大模型设置 +- 完成标签页自动保存 +- 完成大模型对话窗口和工具调用 +- 完成对 vscode 和 trae 的支持 diff --git a/zh/preview/channel.md b/zh/preview/channel.md new file mode 100644 index 0000000..27ec0c3 --- /dev/null +++ b/zh/preview/channel.md @@ -0,0 +1,23 @@ +# 资源频道 + +## 资源 + +[MCP 系列视频教程(正在施工中)](https://www.bilibili.com/video/BV1zYGozgEHc) + +[锦恢的 mcp 系列博客](https://kirigaya.cn/blog/search?q=mcp) + +[OpenMCP 官方文档](https://kirigaya.cn/openmcp/plugin-tutorial) + +[openmcp-sdk 官方文档](https://kirigaya.cn/openmcp/sdk-tutorial) + +## 频道 + +[知乎圈子 - OpenMCP 博物馆](https://www.zhihu.com/ring/host/1911121615279849840) + +[QQ群 - OpenMCP 正式级技术组](https://qm.qq.com/cgi-bin/qm/qr?k=C6ZUTZvfqWoI12lWe7L93cWa1hUsuVT0&jump_from=webapi&authKey=McW6B1ogTPjPDrCyGttS890tMZGQ1KB3QLuG4aqVNRaYp4vlTSgf2c6dMcNjMuBD) + +[Discord 频道 - OpenMCP 交流群](https://discord.gg/SKTZRf6NzU) + +[OpenMCP 源代码](https://github.com/LSTM-Kirigaya/openmcp-client) + +[OpenMCP 文档仓库](https://github.com/LSTM-Kirigaya/openmcp-document) \ No newline at end of file diff --git a/zh/preview/contributors.md b/zh/preview/contributors.md new file mode 100644 index 0000000..04837bd --- /dev/null +++ b/zh/preview/contributors.md @@ -0,0 +1,83 @@ +--- +layout: page +--- + + + + \ No newline at end of file diff --git a/zh/preview/join.md b/zh/preview/join.md new file mode 100644 index 0000000..d5c44af --- /dev/null +++ b/zh/preview/join.md @@ -0,0 +1,34 @@ +# 参与 OpenMCP 开发 + + +## 想要参与开发,如果联系我们? + +如果你也想要参与 OpenMCP 的开发,你可以通过如下的方式联系到我们: + +- 加入 OpenMCP正式级技术组 来和我们直接讨论。 +- 联系锦恢的邮箱 1193466151@qq.com +- 提交 [「Feature Request」类型的 issue](https://github.com/LSTM-Kirigaya/openmcp-client/issues) 或者 fork 代码后 [提交 PR](https://github.com/LSTM-Kirigaya/openmcp-client/pulls) + +## 我能为 OpenMCP 做些什么? + +虽然 OpenMCP 本体看起来技术乱七八糟,但是实际上,阁下可以为 OpenMCP 做的事情远比您想象的多。 + +- 通过提交 PR 贡献代码或者修复 bug,如果您愿意的话。 +- 为我们的项目设计新的功能,这未必一定需要阁下写代码,只是在 [MVP 需求规划](https://github.com/LSTM-Kirigaya/openmcp-client?tab=readme-ov-file#%E9%9C%80%E6%B1%82%E8%A7%84%E5%88%92) 中提出有意义的功能也是很不错的贡献。 +- 通过 OpenMCP 来完成不同的 agent 开发的例子或者打磨新的开发 AI Agent 的方法论。在征得阁下本人同意后,我们将会将你的教程整合到这个网站中。 + +通过向 OpenMCP 贡献以上内容或是其他,阁下将能成为 OpenMCP 的贡献者。 + +## 为 openmcp 文档站点添砖加瓦 + +如果您想要为 openmcp 文档站点修复专业错误或者贡献您的例子,请 fork [openmcp-document](https://github.com/LSTM-Kirigaya/openmcp-document) 提交 PR 。 + +如果您对 github 的操作不熟悉,请 [进入 OpenMCP 开发群](https://qm.qq.com/cgi-bin/qm/qr?k=C6ZUTZvfqWoI12lWe7L93cWa1hUsuVT0&jump_from=webapi&authKey=McW6B1ogTPjPDrCyGttS890tMZGQ1KB3QLuG4aqVNRaYp4vlTSgf2c6dMcNjMuBD) 并联系管理员锦恢,请提供如下几件东西: + +- 您希望贡献的内容 +- 您的 github 账号(主页链接) +- 您的邮箱 +- 您希望展现在网站上的 ID 和 头像 +- 您希望关联的网站链接(比如b站,知乎,个人网站什么的) + +完成添加后,您就可以向 [openmcp-document](https://github.com/LSTM-Kirigaya/openmcp-document) 提交 PR 了。 \ No newline at end of file diff --git a/zh/sdk-tutorial/index.md b/zh/sdk-tutorial/index.md new file mode 100644 index 0000000..6ff7d9a --- /dev/null +++ b/zh/sdk-tutorial/index.md @@ -0,0 +1,153 @@ + + +
+ + + +

openmcp-sdk : 适用于 openmcp 的部署框架

+

闪电般将您的 agent 从实验室部署到生产环境

+ +
+ +# 介绍 & 安装 + +## 什么是 openmcp-sdk.js + +OpenMCP Client 提供了一体化的 MCP 调试解决方案,这很好,但是,还是不够有趣。 + +因为,我们总是希望可以把做好的 mcp 搞一个可以直接分发的 app 或者扔到服务器上做成一个函数服务或者微服务。而 OpenMCP Client 把和大模型交互,使用工具的这套逻辑全部放到了前端,导致我们如果想要把 mcp 做成一个和大模型绑定的独立应用或者服务,困难重重。 + +这个时候,openmcp-sdk.js 就提供了一种轻量级解决方案。它是一个 nodejs 的库,可以让您通过 nodejs 将写好的 mcp 和调试好的流程无缝部署成一个 agent。 + + +## 安装 + +::: code-group +```bash [npm] +npm install openmcp-sdk +``` + +```bash [yarn] +yarn add openmcp-sdk +``` + +```bash [pnpm] +pnpm add openmcp-sdk +``` +::: + +:::warning +目前 openmcp-sdk 只支持 esm 模式的导入 +::: + + +## 使用 + +文件名:main.ts + +```typescript +import { TaskLoop } from 'openmcp-sdk/task-loop'; +import { TaskLoopAdapter } from 'openmcp-sdk/service'; +async function main() { + // 创建适配器,负责通信和 mcp 连接 + const adapter = new TaskLoopAdapter(); + + // 添加 mcp 服务器 + adapter.addMcp({ + connectionType: 'STDIO', + commandString: 'node index.js', + cwd: '~/projects/openmcp-tutorial/my-browser/dist' + }); + + // 创建事件循环驱动器, verbose 数值越高,输出的日志越详细 + const taskLoop = new TaskLoop({ adapter, verbose: 1 }); + + // 获取所有工具 + const tools = await taskLoop.getTools(); + + // 配置改次事件循环使用的大模型 + taskLoop.setLlmConfig({ + id: 'deepseek', + baseUrl: 'https://api.deepseek.com/v1', + userToken: process.env['DEEPSEEK_API_TOKEN'], + userModel: 'deepseek-chat' + }); + + // 创建当前事件循环对应的上下文,并且配置当前上下文的设置 + const storage = { + messages: [], + settings: { + temperature: 0.7, + // 在本次对话使用所有工具 + enableTools: tools, + // 系统提示词 + systemPrompt: 'you are a clever bot', + // 对话上下文的轮数 + contextLength: 20 + } + }; + + // 本次发出的问题 + const message = 'hello world'; + + // 开启事件循环 + await taskLoop.start(storage, message); + + // 打印上下文,最终的回答在 messages.at(-1) 中 + const content = storage.messages.at(-1).content; + console.log('最终回答:', content); +} + +main(); +``` + +以 esm 模块来运行它,先安装 typescript 的 esm 启动器: + +```bash +npm install tsx --save-dev +``` + +运行上面的文件 + +```bash +npx tsx main.ts +``` + + +下面是可能的输出: + +``` +[6/5/2025, 8:16:15 PM] 🚀 [my-browser] 0.1.0 connected +[6/5/2025, 8:16:15 PM] task loop enters a new epoch +[6/5/2025, 8:16:23 PM] task loop finish a epoch +[6/5/2025, 8:16:23 PM] 🤖 llm wants to call these tools k_navigate +[6/5/2025, 8:16:23 PM] 🔧 calling tool k_navigate +[6/5/2025, 8:16:34 PM] × fail to call tools McpError: MCP error -32603: net::ERR_CONNECTION_RESET at https://towardsdatascience.com/tag/editors-pick/ +[6/5/2025, 8:16:34 PM] task loop enters a new epoch +[6/5/2025, 8:16:40 PM] task loop finish a epoch +[6/5/2025, 8:16:40 PM] 🤖 llm wants to call these tools k_navigate +[6/5/2025, 8:16:40 PM] 🔧 calling tool k_navigate +[6/5/2025, 8:16:44 PM] ✓ call tools okey dockey success +[6/5/2025, 8:16:44 PM] task loop enters a new epoch +[6/5/2025, 8:16:57 PM] task loop finish a epoch +[6/5/2025, 8:16:57 PM] 🤖 llm wants to call these tools k_evaluate +[6/5/2025, 8:16:57 PM] 🔧 calling tool k_evaluate +[6/5/2025, 8:16:57 PM] ✓ call tools okey dockey success +[6/5/2025, 8:16:57 PM] task loop enters a new epoch +[6/5/2025, 8:17:06 PM] task loop finish a epoch +[6/5/2025, 8:17:06 PM] 🤖 llm wants to call these tools k_navigate, k_navigate +[6/5/2025, 8:17:06 PM] 🔧 calling tool k_navigate +[6/5/2025, 8:17:09 PM] ✓ call tools okey dockey success +[6/5/2025, 8:17:09 PM] 🔧 calling tool k_navigate +[6/5/2025, 8:17:12 PM] ✓ call tools okey dockey success +[6/5/2025, 8:17:12 PM] task loop enters a new epoch +[6/5/2025, 8:17:19 PM] task loop finish a epoch +[6/5/2025, 8:17:19 PM] 🤖 llm wants to call these tools k_evaluate, k_evaluate +[6/5/2025, 8:17:19 PM] 🔧 calling tool k_evaluate +[6/5/2025, 8:17:19 PM] ✓ call tools okey dockey success +[6/5/2025, 8:17:19 PM] 🔧 calling tool k_evaluate +[6/5/2025, 8:17:19 PM] ✓ call tools okey dockey success +[6/5/2025, 8:17:19 PM] task loop enters a new epoch +[6/5/2025, 8:17:45 PM] task loop finish a epoch +"以下是整理好的热门文章信息,并已翻译为简体中文:\n\n---\n\n### K1 标题 \n**《数据漂移并非真正问题:你的监控策略才是》** \n\n**简介** \n在机器学习领域,数据漂移常被视为模型性能下降的罪魁祸首,但本文作者提出了一种颠覆性的观点:数据漂移只是一个信号,真正的核心问题在于监控策略的不足。文章通过实际案例(如电商推荐系统和金融风控模型)揭示了传统统计监控的局限性,并提出了一个三层监控框架: \n1. **统计监控**:快速检测数据分布变化,但仅作为初步信号。 \n2. **上下文监控**:结合业务逻辑,判断漂移是否对关键指标产生影响。 \n3. **行为监控**:追踪模型预测的实际效果,避免“无声漂移”。 \n\n亮点在于作者强调了监控系统需要与业务目标紧密结合,而非单纯依赖技术指标。 \n\n**原文链接** \n[点击阅读原文](https://towardsdatascience.com/data-drift-is-not-the-actual-problem-your-monitoring-strategy-is/) \n\n---\n\n### K2 标题 \n**《从 Jupyter 到程序员的快速入门指南》** \n\n**简介** \n本文为数据科学家和初学者提供了一条从 Jupyter Notebook 过渡到专业编程的清晰路径。作者通过实际代码示例和工具推荐(如 VS Code、Git 和 Docker),帮助读者摆脱 Notebook 的局限性,提升代码的可维护性和可扩展性。 \n\n亮点包括: \n- 如何将 Notebook 代码模块化为可复用的 Python 脚本。 \n- 使用版本控制和容器化技术优化开发流程。 \n- 实战案例展示如何将实验性代码转化为生产级应用。 \n\n**原文链接** \n[点击阅读原文](https://towardsdatascience.com/the-journey-from-jupyter-to-programmer-a-quick-start-guide/) \n\n---\n\n如果需要进一步优化或补充其他内容,请随时告诉我!" +``` diff --git a/zh/sdk-tutorial/usage/greet.md b/zh/sdk-tutorial/usage/greet.md new file mode 100644 index 0000000..e69de29 diff --git a/zh/sdk-tutorial/usage/multi-server.md b/zh/sdk-tutorial/usage/multi-server.md new file mode 100644 index 0000000..e69de29 diff --git a/zh/sdk-tutorial/usage/task-loop.md b/zh/sdk-tutorial/usage/task-loop.md new file mode 100644 index 0000000..e69de29