{"id":204,"date":"2026-11-08T07:22:36","date_gmt":"2026-11-08T03:22:36","guid":{"rendered":"https:\/\/demensdeum.com\/blog\/2026\/11\/08\/opencode-mcp-deepseek\/"},"modified":"2026-11-08T07:22:36","modified_gmt":"2026-11-08T03:22:36","slug":"opencode-mcp-deepseek","status":"publish","type":"post","link":"https:\/\/demensdeum.com\/blog\/2026\/11\/08\/opencode-mcp-deepseek\/","title":{"rendered":"MCP in OpenCode on DeepSeek: how to connect external tools"},"content":{"rendered":"<p>MCP (Model Context Protocol) is an open protocol that describes how to connect external tools and data sources to the language model: a file system, documentation, task tracker, bug tracker or your own service. Instead of embedding each tool into the agent, you set up a separate MCP server, and the agent communicates with it according to the standard scheme.<\/p>\n<p>OpenCode supports MCP out of the box: both local servers, which are launched by a child process, and remote ones, accessible via HTTP. An important point is that the entire MCP layer is implemented on the side of OpenCode itself, and not on the side of the model provider. Therefore, DeepSeek receives MCP tools as normal functions in the request, and does not need to know anything about the protocol itself.<\/p>\n<p>In this post, I will look at how to add a local and remote server, how to get authorized, how not to drown in tokens, and what to consider when working with DeepSeek models.<\/p>\n<h2>What you need<\/h2>\n<p>To work you need three things:<\/p>\n<ul>\n<li><b>OpenCode installed.<\/b> If the agent is not yet configured, I discussed the procedure for installing and connecting DeepSeek in a separate note.<\/li>\n<li><b>Node.js and npx.<\/b> Necessary for local MCP servers, which are distributed as npm packages.<\/li>\n<li><b>Access keys.<\/b> Only for those remote servers that require authorization. Public servers like Context7 work without a key, but with lower limits.<\/li>\n<\/ul>\n<h2>How it works<\/h2>\n<p>Servers are described in the OpenCode configuration in the <b>mcp<\/b> block. Each server has a unique name by which it can be mentioned in a request. Server tools are registered with this name prefix, so there are no conflicts between different servers.<\/p>\n<p>After connecting, MCP tools become available to models along with the built-in ones: reading files, editing code and running commands. From DeepSeek&#8217;s point of view, these are ordinary functions that an agent can call &#8211; which is why the bundle works without any MCP support on the model API side.<\/p>\n<h2>Local server<\/h2>\n<p>The easiest way to get acquainted with the mechanism is the test server <b>@modelcontextprotocol\/server-everything<\/b>. Let&#8217;s add it to the <b>opencode.json<\/b> file in the project root:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism undefined-numbers lang-unknown\" data-lang=\"unknown\"><code>{\n  \"$schema\": \"https:\/\/opencode.ai\/config.json\",\n  \"mcp\": {\n    \"mcp_everything\": {\n      \"type\": \"local\",\n      \"command\": [\"npx\", \"-y\", \"@modelcontextprotocol\/server-everything\"]\n    }\n  }\n}\n<\/code><\/pre>\n<\/div>\n<p>The <b>command<\/b> field is the server startup command and its arguments in the form of an array. After restarting OpenCode, the tools will appear in the session and can be asked for explicitly:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism undefined-numbers lang-unknown\" data-lang=\"unknown\"><code>\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 mcp_everything, \u0447\u0442\u043e\u0431\u044b \u0441\u043b\u043e\u0436\u0438\u0442\u044c 3 \u0438 4\n<\/code><\/pre>\n<\/div>\n<p>The local server has several more useful fields. <b>environment<\/b> sets the process environment variables, <b>cwd<\/b> &#8211; the working directory, <b>timeout<\/b> &#8211; request timeout in milliseconds, and <b>enabled<\/b> allows you to temporarily disable the server without removing it from the configuration.<\/p>\n<h2>Remote server<\/h2>\n<p>Remote servers are more convenient: you don\u2019t need to run anything locally, and they can be accessed via URL. A good example is Context7, which looks for up-to-date library documentation. It connects without a key:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism undefined-numbers lang-unknown\" data-lang=\"unknown\"><code>{\n  \"$schema\": \"https:\/\/opencode.ai\/config.json\",\n  \"mcp\": {\n    \"context7\": {\n      \"type\": \"remote\",\n      \"url\": \"https:\/\/mcp.context7.com\/mcp\"\n    }\n  }\n}\n<\/code><\/pre>\n<\/div>\n<p>If you have a free Context7 account, the key is raising the limits. Headers are transmitted through the <b>headers<\/b> block, and it is convenient to keep the key itself in an environment variable:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism undefined-numbers lang-unknown\" data-lang=\"unknown\"><code>{\n  \"$schema\": \"https:\/\/opencode.ai\/config.json\",\n  \"mcp\": {\n    \"context7\": {\n      \"type\": \"remote\",\n      \"url\": \"https:\/\/mcp.context7.com\/mcp\",\n      \"headers\": {\n        \"CONTEXT7_API_KEY\": \"{env:CONTEXT7_API_KEY}\"\n      }\n    }\n  }\n}\n<\/code><\/pre>\n<\/div>\n<p>The <b>{env:NAME}<\/b> construction substitutes the value of the environment variable when loading the configuration, so the key does not end up in the repository. Before starting the agent, the variable must be exported in the terminal.<\/p>\n<p>Another useful public server is Grep by Vercel, search for code examples on GitHub:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism undefined-numbers lang-unknown\" data-lang=\"unknown\"><code>{\n  \"$schema\": \"https:\/\/opencode.ai\/config.json\",\n  \"mcp\": {\n    \"gh_grep\": {\n      \"type\": \"remote\",\n      \"url\": \"https:\/\/mcp.grep.app\"\n    }\n  }\n}\n<\/code><\/pre>\n<\/div>\n<h2>Authorization via OAuth<\/h2>\n<p>Some remote servers require OAuth. OpenCode can go through this scenario itself: it notices the 401 response, triggers the authorization and saves the tokens. For Sentry, just specify the server:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism undefined-numbers lang-unknown\" data-lang=\"unknown\"><code>{\n  \"$schema\": \"https:\/\/opencode.ai\/config.json\",\n  \"mcp\": {\n    \"sentry\": {\n      \"type\": \"remote\",\n      \"url\": \"https:\/\/mcp.sentry.dev\/mcp\",\n      \"oauth\": {}\n    }\n  }\n}\n<\/code><\/pre>\n<\/div>\n<p>After this, authorization can be started manually &#8211; the browser will open:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism undefined-numbers lang-unknown\" data-lang=\"unknown\"><code>opencode mcp auth sentry\n<\/code><\/pre>\n<\/div>\n<p>Service commands are also useful in everyday work. <b>opencode mcp list<\/b> shows servers and authorization status, <b>opencode mcp logout<\/b> deletes stored tokens, and <b>opencode mcp debug<\/b> checks network connectivity and OAuth discovery for a specific server. Tokens are stored locally in the file <b>~\/.local\/share\/opencode\/mcp-auth.json<\/b>.<\/p>\n<p>If the server does not use OAuth, but a regular API key, automatic authorization should be disabled, otherwise OpenCode will try to pass OAuth where it is not needed:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism undefined-numbers lang-unknown\" data-lang=\"unknown\"><code>{\n  \"$schema\": \"https:\/\/opencode.ai\/config.json\",\n  \"mcp\": {\n    \"my-api-key-server\": {\n      \"type\": \"remote\",\n      \"url\": \"https:\/\/mcp.example.com\/mcp\",\n      \"oauth\": false,\n      \"headers\": {\n        \"Authorization\": \"Bearer {env:MY_API_KEY}\"\n      }\n    }\n  }\n}\n<\/code><\/pre>\n<\/div>\n<h2>Tool management<\/h2>\n<p>MCP tools consume context: each server adds descriptions of its functions to the request. Therefore, it is better to keep servers that are rarely needed turned off. This can be done globally through the <b>tools<\/b> block:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism undefined-numbers lang-unknown\" data-lang=\"unknown\"><code>{\n  \"$schema\": \"https:\/\/opencode.ai\/config.json\",\n  \"mcp\": {\n    \"my-mcp-foo\": {\n      \"type\": \"local\",\n      \"command\": [\"npx\", \"-y\", \"my-mcp-command-foo\"]\n    },\n    \"my-mcp-bar\": {\n      \"type\": \"local\",\n      \"command\": [\"npx\", \"-y\", \"my-mcp-command-bar\"]\n    }\n  },\n  \"tools\": {\n    \"my-mcp*\": false\n  }\n}\n<\/code><\/pre>\n<\/div>\n<p>The <b>my-mcp*<\/b> template disables all tools whose name begins with this prefix. If there are many servers, it makes more sense to disable them globally and enable them locally for a specific agent:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism undefined-numbers lang-unknown\" data-lang=\"unknown\"><code>{\n  \"$schema\": \"https:\/\/opencode.ai\/config.json\",\n  \"mcp\": {\n    \"my-mcp\": {\n      \"type\": \"local\",\n      \"command\": [\"npx\", \"-y\", \"my-mcp-command\"],\n      \"enabled\": true\n    }\n  },\n  \"tools\": {\n    \"my-mcp*\": false\n  },\n  \"agent\": {\n    \"my-agent\": {\n      \"tools\": {\n        \"my-mcp*\": true\n      }\n    }\n  }\n}\n<\/code><\/pre>\n<\/div>\n<h2>DeepSeek Features<\/h2>\n<p>The connection with DeepSeek works because the models support calling functions, and the MCP tools for them look like ordinary functions. DeepSeek itself does not connect MCP servers and should not: the connection is handled by OpenCode.<\/p>\n<p>What you should pay attention to:<\/p>\n<ul>\n<li><b>Selecting a model.<\/b> For routine and small edits, <b>deepseek-flash<\/b> is sufficient. For tasks where you need to keep a lot of context in your head and carefully select a tool, it is better to give <b>deepseek-v4-pro<\/b>.<\/li>\n<li><b>Context size.<\/b> Heavy servers like GitHub MCP add a lot of tokens and, together with a large project, easily hit the limit. Turn them on only for the duration of a specific task.<\/li>\n<li><b>Cost<\/b> Tooling schemas are sent with each request, so the more servers included, the more expensive each iteration is. You pay the provider directly, so the extra servers are noticeable on your bill.<\/li>\n<li><b>Hints for the agent.<\/b> It is safer not to rely on the ingenuity of the model, but to directly name the server in the request or write a rule in <b>AGENTS.md<\/b>.<\/li>\n<\/ul>\n<p>An example of a rule for <b>AGENTS.md<\/b>, so that the agent itself accesses the required source:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism undefined-numbers lang-unknown\" data-lang=\"unknown\"><code>\u041a\u043e\u0433\u0434\u0430 \u043d\u0443\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b context7.\n<\/code><\/pre>\n<\/div>\n<h2>Practical recipe<\/h2>\n<p>For everyday work, two servers are usually enough: Context7 for documentation and one narrow server for a specific task &#8211; for example, Sentry for analyzing errors or Grep for searching for code examples. It is better to keep the rest turned off and turn on as needed.<\/p>\n<p>My procedure is as follows: I add the server to <b>opencode.json<\/b>, restart OpenCode, check the list through <b>opencode mcp list<\/b>, then ask a test question that explicitly mentions the server. If the tool is not called, I look towards <b>opencode mcp debug<\/b> and the timeout &#8211; a slow server may not have time to respond in the allotted 30 seconds.<\/p>\n<h2>What to pay attention to<\/h2>\n<p>A couple of practical points. MCP servers are trusted code: the local server runs on your machine with your rights, and you send request data to the remote one. Therefore, installing random servers from the Internet should be done with the same caution as any third-party CLI.<\/p>\n<p>Keep keys and tokens in environment variables and do not commit to the repository. OpenCode stores OAuth tokens locally, but on a shared machine this is also worth considering: <b>opencode mcp logout<\/b> deletes them when access is no longer needed.<\/p>\n<p>And one more thing: don\u2019t turn on everything at once. MCP adds tools to context, and context is money and quality of response. Two or three really necessary servers work much better than ten connected just in case.<\/p>\n<h2>Links<\/h2>\n<p><a href=\"https:\/\/opencode.ai\/docs\/mcp-servers\/\" rel=\"noopener\" target=\"_blank\">https:\/\/opencode.ai\/docs\/mcp-servers\/<\/a><br \/>\n<a href=\"https:\/\/opencode.ai\/docs\/config\/\" rel=\"noopener\" target=\"_blank\">https:\/\/opencode.ai\/docs\/config\/<\/a><br \/>\n<a href=\"https:\/\/modelcontextprotocol.io\/\" rel=\"noopener\" target=\"_blank\">https:\/\/modelcontextprotocol.io\/<\/a><br \/>\n<a href=\"https:\/\/github.com\/upstash\/context7\" rel=\"noopener\" target=\"_blank\">https:\/\/github.com\/upstash\/context7<\/a><br \/>\n<a href=\"https:\/\/mcp.sentry.dev\" rel=\"noopener\" target=\"_blank\">https:\/\/mcp.sentry.dev<\/a><br \/>\n<a href=\"https:\/\/mcp.grep.app\" rel=\"noopener\" target=\"_blank\">https:\/\/mcp.grep.app<\/a><br \/>\n<a href=\"https:\/\/github.com\/anomalyco\/opencode\" rel=\"noopener\" target=\"_blank\">https:\/\/github.com\/anomalyco\/opencode<\/a><\/p>\n<h2>Sources<\/h2>\n<p><a href=\"https:\/\/opencode.ai\/docs\/mcp-servers\/\" rel=\"noopener\" target=\"_blank\">https:\/\/opencode.ai\/docs\/mcp-servers\/<\/a><br \/>\n<a href=\"https:\/\/modelcontextprotocol.io\/\" rel=\"noopener\" target=\"_blank\">https:\/\/modelcontextprotocol.io\/<\/a><br \/>\n<a href=\"https:\/\/api-docs.deepseek.com\/\" rel=\"noopener\" target=\"_blank\">https:\/\/api-docs.deepseek.com\/<\/a><br \/>\n<a href=\"https:\/\/api-docs.deepseek.com\/quick_start\/agent_integrations\/opencode\" rel=\"noopener\" target=\"_blank\">https:\/\/api-docs.deepseek.com\/quick_start\/agent_integrations\/opencode<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>MCP (Model Context Protocol) is an open protocol that describes how to connect external tools and data sources to the language model: a file system, documentation, task tracker, bug tracker or your own service. Instead of embedding each tool into the agent, you set up a separate MCP server, and the agent communicates with it [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[8],"tags":[],"class_list":["post-204","post","type-post","status-publish","format-standard","hentry","category-notes"],"translation":{"provider":"WPGlobus","version":"3.0.6","language":"en","enabled_languages":["en","ru","zh","de","ja","fr","es","pt","hi"],"languages":{"en":{"title":true,"content":true,"excerpt":false},"ru":{"title":true,"content":true,"excerpt":false},"zh":{"title":true,"content":true,"excerpt":false},"de":{"title":true,"content":true,"excerpt":false},"ja":{"title":true,"content":true,"excerpt":false},"fr":{"title":true,"content":true,"excerpt":false},"es":{"title":true,"content":true,"excerpt":false},"pt":{"title":true,"content":true,"excerpt":false},"hi":{"title":true,"content":true,"excerpt":false}}},"_links":{"self":[{"href":"https:\/\/demensdeum.com\/blog\/wp-json\/wp\/v2\/posts\/204","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/demensdeum.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/demensdeum.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/wp-json\/wp\/v2\/comments?post=204"}],"version-history":[{"count":0,"href":"https:\/\/demensdeum.com\/blog\/wp-json\/wp\/v2\/posts\/204\/revisions"}],"wp:attachment":[{"href":"https:\/\/demensdeum.com\/blog\/wp-json\/wp\/v2\/media?parent=204"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/wp-json\/wp\/v2\/categories?post=204"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/wp-json\/wp\/v2\/tags?post=204"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}