{"id":1027,"date":"2026-10-30T15:12:01","date_gmt":"2026-10-30T06:12:01","guid":{"rendered":"https:\/\/csharp.ihavenomoney.co.kr\/?p=1027"},"modified":"2026-10-30T16:34:30","modified_gmt":"2026-10-30T07:34:30","slug":"ubuntu-mysql-aspnetcore-mcp-readonly","status":"publish","type":"post","link":"https:\/\/csharp.ihavenomoney.co.kr\/?p=1027","title":{"rendered":"Ubuntu MySQL\uc744 ChatGPT\uc5d0 \uc5f0\uacb0\ud558\uae30: ASP.NET Core MCP \uc11c\ubc84 \uc870\ud68c \uc804\uc6a9 \uc124\uce58 \uac00\uc774\ub4dc"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><strong>\ub300\uc0c1:<\/strong> Ubuntu 24.04 LTS \/ .NET 10 \/ ASP.NET Core \/ MySQL 8.x \/ \uacf5\uc2dd C# MCP SDK. MySQL\uc5d0 \ub300\ud55c <strong>\uc870\ud68c \uc804\uc6a9 MCP \uc11c\ubc84<\/strong> \uad6c\ucd95 \uc608\uc81c\uc785\ub2c8\ub2e4. \uae30\uc874 MySQL \ub370\uc774\ud130\ubca0\uc774\uc2a4\ub97c \uc720\uc9c0\ud558\uba74\uc11c ChatGPT\uc5d0 \ud5c8\uc6a9\ub41c \ud14c\uc774\ube14\uacfc \uceec\ub7fc\ub9cc \ub178\ucd9c\ud558\ub3c4\ub85d \uc124\uacc4\ud569\ub2c8\ub2e4.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">1. \uc544\ud0a4\ud14d\ucc98<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">ChatGPT \u2192 OpenAI Secure MCP Tunnel(HTTPS 443 \uc544\uc6c3\ubc14\uc6b4\ub4dc) \u2192 ASP.NET Core MCP(127.0.0.1:5080\/mcp) \u2192 MySQL(127.0.0.1:3306). MySQL\uacfc MCP HTTP \ud3ec\ud2b8\ub294 \uc678\ubd80 \uacf5\uac1c\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">2. Ubuntu \uc124\uce58 \ubc0f C# \ud504\ub85c\uc81d\ud2b8 \uc0dd\uc131<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo apt update\nsudo apt install -y dotnet-sdk-10.0 default-mysql-client curl ca-certificates\ndotnet --info\n\nmkdir -p ~\/projects &amp;&amp; cd ~\/projects\ndotnet new web -n MySqlMcpServer -f net10.0\ncd MySqlMcpServer\ndotnet add package ModelContextProtocol.AspNetCore\ndotnet add package MySqlConnector\ndotnet restore<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">\uba85\ub839\uc740 Ubuntu 24.04 LTS \uae30\uc900\uc785\ub2c8\ub2e4. \uc9c0\uc6d0 \ub9ac\ud3ec\uc9c0\ud1a0\ub9ac\uac00 \uc5c6\ub294 \ud658\uacbd\uc5d0\uc11c\ub294 Microsoft\uc758 \ubc30\ud3ec\ubcc4 .NET \uc124\uce58 \uac00\uc774\ub4dc\ub97c \ud655\uc778\ud569\ub2c8\ub2e4. NuGet \ubc84\uc804\uc740 \uad6c\ucd95 \uc2dc\uc810\uc5d0 \uac80\uc99d\ub41c \ubc84\uc804\uc73c\ub85c \uace0\uc815\ud558\uc138\uc694.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">3. MySQL \ud14c\uc2a4\ud2b8 \ud14c\uc774\ube14 \ubc0f \uc77d\uae30 \uc804\uc6a9 \uc0ac\uc6a9\uc790<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE DATABASE IF NOT EXISTS sampledb CHARACTER SET utf8mb4;\nUSE sampledb;\nCREATE TABLE IF NOT EXISTS mcp_demo_items (\n id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,\n name VARCHAR(100) NOT NULL,\n price DECIMAL(12,2) NOT NULL,\n stock INT NOT NULL DEFAULT 0\n) ENGINE=InnoDB;\nINSERT INTO mcp_demo_items(name,price,stock) VALUES\n ('\ud0a4\ubcf4\ub4dc',35000,10),('\ub9c8\uc6b0\uc2a4',15000,20);\nCREATE USER 'chatgpt_ro'@'127.0.0.1'\n IDENTIFIED BY 'CHANGE_TO_STRONG_PASSWORD';\nGRANT SELECT ON sampledb.mcp_demo_items\n TO 'chatgpt_ro'@'127.0.0.1';\nSHOW GRANTS FOR 'chatgpt_ro'@'127.0.0.1';<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">\uc774 \uacc4\uc815\uc740 SELECT\ub9cc \ud5c8\uc6a9\ud569\ub2c8\ub2e4. CREATE\u00b7ALTER\u00b7DROP\u00b7UPDATE\u00b7INSERT\u00b7DELETE \uad8c\ud55c\uc744 \ubd80\uc5ec\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. \uc2e4\uc81c \uc6b4\uc601 \uc2dc\uc5d0\ub294 \uac1c\uc778\uc815\ubcf4\uac00 \ud3ec\ud568\ub41c \ud14c\uc774\ube14\uc744 \uacf5\uac1c\ud558\uc9c0 \ub9d0\uace0 \uac00\ub2a5\ud558\uba74 \uceec\ub7fc \ub2e8\uc704 SELECT \uad8c\ud55c\uc73c\ub85c \ub354 \uc81c\ud55c\ud558\uc138\uc694.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">4. Program.cs \uc804\uccb4 \uc608\uc81c<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\uae30\ubcf8 Program.cs\ub97c \uc544\ub798 \uc18c\uc2a4\ub85c \uad50\uccb4\ud569\ub2c8\ub2e4. \ud55c \uac1c \uc0d8\ud50c \ud14c\uc774\ube14(<code>mcp_demo_items<\/code>)\ub9cc \ud5c8\uc6a9\ud558\uace0, \ub3c4\uad6c 3\uac1c\ub97c \ub178\ucd9c\ud569\ub2c8\ub2e4. \ub2e4\ub978 \ud14c\uc774\ube14\uc744 \ucd94\uac00\ud560 \ub54c\ub294 \ud5c8\uc6a9 \ubaa9\ub85d\uacfc \uceec\ub7fc \uc815\ucc45\uc744 \ucf54\ub4dc \ub610\ub294 \ubcc4\ub3c4 \uc815\ucc45 \ud30c\uc77c\ub85c \uba85\uc2dc\uc801\uc73c\ub85c \ud655\uc7a5\ud574\uc57c \ud569\ub2c8\ub2e4.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>using System.ComponentModel;\nusing System.Text.Json;\nusing ModelContextProtocol.AspNetCore;\nusing ModelContextProtocol.Server;\nusing MySqlConnector;\n\nvar builder = WebApplication.CreateBuilder(args);\nbuilder.Services.AddSingleton&lt;ReadService&gt;();\nbuilder.Services.AddMcpServer()\n    .WithHttpTransport(options =&gt;\n    {\n        options.SessionMode = HttpServerSessionMode.Stateless;\n    })\n    .WithTools&lt;MySqlTools&gt;();\n\nvar app = builder.Build();\napp.MapGet(\"\/health\", () =&gt; Results.Ok(new { status = \"ok\" }));\napp.MapMcp(\"\/mcp\");\napp.Run(\"http:\/\/127.0.0.1:5080\");\n\npublic sealed class ReadService(IConfiguration config)\n{\n    private readonly string _connection =\n        config.GetConnectionString(\"MySql\")\n        ?? throw new InvalidOperationException(\"Connection missing\");\n\n    \/\/ This tutorial exposes exactly one demo table and four columns.\n    private const string Table = \"mcp_demo_items\";\n    private const string Columns = \"`id`,`name`,`price`,`stock`\";\n\n    public object ListTables() =&gt; new\n    {\n        tables = new&#91;] { Table },\n        read_only = true\n    };\n\n    public async Task&lt;string&gt; DescribeAsync(string table,\n        CancellationToken ct)\n    {\n        Check(table);\n        const string sql = \"\"\"\n            SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE\n            FROM INFORMATION_SCHEMA.COLUMNS\n            WHERE TABLE_SCHEMA = DATABASE()\n              AND TABLE_NAME = @table\n              AND COLUMN_NAME IN ('id','name','price','stock')\n            ORDER BY ORDINAL_POSITION\n            \"\"\";\n        await using var db = new MySqlConnection(_connection);\n        await db.OpenAsync(ct);\n        await using var cmd = new MySqlCommand(sql, db)\n        {\n            CommandTimeout = 5\n        };\n        cmd.Parameters.AddWithValue(\"@table\", Table);\n        await using var reader = await cmd.ExecuteReaderAsync(ct);\n        var rows = new List&lt;object&gt;();\n        while (await reader.ReadAsync(ct))\n            rows.Add(new {\n                column = reader.GetString(0),\n                type = reader.GetString(1),\n                nullable = reader.GetString(2)\n            });\n        return JsonSerializer.Serialize(new { table, columns = rows });\n    }\n\n    public async Task&lt;string&gt; SampleAsync(string table, int limit,\n        CancellationToken ct)\n    {\n        Check(table);\n        limit = Math.Clamp(limit, 1, 10);\n        string sql = $\"SELECT {Columns} FROM `{Table}` ORDER BY `id` DESC LIMIT @limit\";\n        await using var db = new MySqlConnection(_connection);\n        await db.OpenAsync(ct);\n        await using var cmd = new MySqlCommand(sql, db)\n        {\n            CommandTimeout = 5\n        };\n        cmd.Parameters.AddWithValue(\"@limit\", limit);\n        await using var reader = await cmd.ExecuteReaderAsync(ct);\n        var rows = new List&lt;Dictionary&lt;string, string?&gt;&gt;();\n        while (await reader.ReadAsync(ct))\n        {\n            var row = new Dictionary&lt;string, string?&gt;();\n            for (int i = 0; i &lt; reader.FieldCount; i++)\n            {\n                string? value = reader.IsDBNull(i)\n                    ? null : Convert.ToString(reader.GetValue(i),\n                        System.Globalization.CultureInfo.InvariantCulture);\n                row&#91;reader.GetName(i)] = value is { Length: &gt; 200 }\n                    ? value&#91;..200] + \"...\" : value;\n            }\n            rows.Add(row);\n        }\n        return JsonSerializer.Serialize(new { table, rows });\n    }\n\n    private static void Check(string table)\n    {\n        if (!string.Equals(table, Table, StringComparison.Ordinal))\n            throw new ArgumentException(\"Table is not allowed\");\n    }\n}\n\n&#91;McpServerToolType]\npublic static class MySqlTools\n{\n    &#91;McpServerTool(Name = \"list_tables\", ReadOnly = true, OpenWorld = false)]\n    &#91;Description(\"List approved MySQL tables; no writes.\")]\n    public static object ListTables(ReadService service)\n        =&gt; service.ListTables();\n\n    &#91;McpServerTool(Name = \"describe_table\", ReadOnly = true, OpenWorld = false)]\n    &#91;Description(\"Describe allowed columns in the approved table.\")]\n    public static Task&lt;string&gt; DescribeTable(ReadService service,\n        string table, CancellationToken cancellationToken)\n        =&gt; service.DescribeAsync(table, cancellationToken);\n\n    &#91;McpServerTool(Name = \"sample_rows\", ReadOnly = true, OpenWorld = false)]\n    &#91;Description(\"Read up to 10 rows of approved columns.\")]\n    public static Task&lt;string&gt; SampleRows(ReadService service,\n        string table, int limit, CancellationToken cancellationToken)\n        =&gt; service.SampleAsync(table, limit, cancellationToken);\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">5. \uc811\uc18d \ud658\uacbd \ud30c\uc77c<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\ucf54\ub4dc\uc640 \uc811\uc18d \ube44\ubc00\ubc88\ud638\ub97c \ubd84\ub9ac\ud569\ub2c8\ub2e4. \uc2e4\uc81c \ube44\ubc00\ubc88\ud638\ub85c \ubcc0\uacbd\ud569\ub2c8\ub2e4. DB\uc640 MCP \uc11c\ubc84\uac00 \uac19\uc740 Ubuntu \ud638\uc2a4\ud2b8\uc5d0\uc11c \ub8e8\ud504\ubc31\uc73c\ub85c \ud1b5\uc2e0\ud558\ub294 \uc608\uc81c\uc785\ub2c8\ub2e4.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo mkdir -p \/etc\/mysql-mcp\nsudo nano \/etc\/mysql-mcp\/read.env\n# \ub0b4\uc6a9:\n# ConnectionStrings__MySql=\"Server=127.0.0.1;Port=3306;Database=sampledb;User ID=chatgpt_ro;Password=CHANGE_TO_STRONG_PASSWORD;Connection Timeout=5;SslMode=None\"\nsudo chown root:root \/etc\/mysql-mcp\/read.env\nsudo chmod 600 \/etc\/mysql-mcp\/read.env<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">6. \ube4c\ub4dc\u00b7\ubc30\ud3ec\u00b7systemd<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>dotnet build -c Release\ndotnet publish -c Release -o .\/publish\n\nsudo useradd --system --home-dir \/nonexistent --shell \/usr\/sbin\/nologin mcpreader\nsudo install -d -m 755 \/opt\/mysql-mcp-dotnet\nsudo cp -a .\/publish\/. \/opt\/mysql-mcp-dotnet\/\nsudo chown -R root:root \/opt\/mysql-mcp-dotnet\n\nsudo nano \/etc\/systemd\/system\/mysql-mcp-dotnet.service<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;Unit]\nDescription=ASP.NET Core MySQL Read Only MCP\nAfter=network.target\n&#91;Service]\nType=simple\nUser=mcpreader\nGroup=mcpreader\nWorkingDirectory=\/opt\/mysql-mcp-dotnet\nEnvironmentFile=\/etc\/mysql-mcp\/read.env\nExecStart=\/usr\/bin\/dotnet \/opt\/mysql-mcp-dotnet\/MySqlMcpServer.dll\nRestart=on-failure\nRestartSec=5\nNoNewPrivileges=true\nProtectSystem=strict\nProtectHome=true\nPrivateTmp=true\n&#91;Install]\nWantedBy=multi-user.target<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo systemctl daemon-reload\nsudo systemctl enable --now mysql-mcp-dotnet\nsudo systemctl status mysql-mcp-dotnet\nsudo journalctl -u mysql-mcp-dotnet -n 50 --no-pager\ncurl -sS http:\/\/127.0.0.1:5080\/health<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">7. MCP \ub3c4\uad6c \uc870\ud68c \ubc0f \ub370\uc774\ud130 \ud14c\uc2a4\ud2b8<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>curl -sS http:\/\/127.0.0.1:5080\/mcp \\\n -H 'Content-Type: application\/json' \\\n -H 'Accept: application\/json, text\/event-stream' \\\n -H 'MCP-Protocol-Version: 2026-07-28' \\\n -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools\/list\",\"params\":{}}'\n\ncurl -sS http:\/\/127.0.0.1:5080\/mcp \\\n -H 'Content-Type: application\/json' \\\n -H 'Accept: application\/json, text\/event-stream' \\\n -H 'MCP-Protocol-Version: 2026-07-28' \\\n -d '{\"jsonrpc\":\"2.0\",\"id\":2,\"method\":\"tools\/call\",\"params\":{\"name\":\"sample_rows\",\"arguments\":{\"table\":\"mcp_demo_items\",\"limit\":5}}}'<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">\uc815\uc0c1\uc774\ub77c\uba74 <code>list_tables<\/code>, <code>describe_table<\/code>, <code>sample_rows<\/code>\uac00 \ud45c\uc2dc\ub429\ub2c8\ub2e4. \/health\ub294 \uc11c\ubc84 \uc0dd\uc874\ub9cc \uac80\uc0ac\ud558\ubbc0\ub85c \ubc18\ub4dc\uc2dc \uc2e4\uc81c \ub3c4\uad6c \ud638\ucd9c\uae4c\uc9c0 \ud655\uc778\ud574\uc57c \ud569\ub2c8\ub2e4.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">8. Secure MCP Tunnel \uc5f0\uacb0<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">OpenAI Platform Tunnels \uc124\uc815\uc5d0\uc11c <code>tunnel_id<\/code> \ubc0f \uc804\uc6a9 Runtime API Key\ub97c \ubc1c\uae09\ud558\uace0, \uacf5\uc2dd \ub9b4\ub9ac\uc2a4\uc758 <code>tunnel-client<\/code>\ub97c \uc124\uce58\ud569\ub2c8\ub2e4. TCP 5080 \ub610\ub294 MySQL 3306\uc744 \uc778\ud130\ub137\uc5d0 \uc5f4\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>export CONTROL_PLANE_API_KEY=\"YOUR_RUNTIME_API_KEY\"\ntunnel-client help quickstart\ntunnel-client init --profile local-dotnet --tunnel-id YOUR_TUNNEL_ID \\\n  --mcp-server-url http:\/\/127.0.0.1:5080\/mcp\ntunnel-client doctor --profile local-dotnet --explain\ntunnel-client run --profile local-dotnet<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">\uac80\uc99d \ud6c4 tunnel-client\ub3c4 systemd\ub85c \uc2e4\ud589\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4. \uc2e4\ud589 \uc2dc\uc5d0\ub294 \ud504\ub85c\ud544 \ud30c\uc77c \ubc0f API \ud0a4\uc758 \ud30c\uc77c \uad8c\ud55c\uc744 \uc81c\ud55c\ud558\uace0, \ud130\ub110\uc774 \ub85c\uceec MCP \uc11c\ube44\uc2a4\ubcf4\ub2e4 \uba3c\uc800 \uc2e4\ud589\ub418\uc9c0 \uc54a\ub3c4\ub85d \uc21c\uc11c\ub97c \uc124\uc815\ud558\uc138\uc694.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">9. ChatGPT \uc5f0\uacb0\uacfc \ud14c\uc2a4\ud2b8<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\uc9c0\uc6d0\ub418\ub294 ChatGPT \uc6cc\ud06c\uc2a4\ud398\uc774\uc2a4\uc5d0\uc11c Plugins \u2192 + \u2192 Add custom MCP server \u2192 Tunnel\uc744 \uc120\ud0dd\ud558\uace0 tunnel_id\ub97c \uc9c0\uc815\ud569\ub2c8\ub2e4. \uc5f0\uacb0 \ud6c4 \u201cMySQL\uc5d0\uc11c \uc870\ud68c \uac00\ub2a5\ud55c \ud14c\uc774\ube14 \ubaa9\ub85d\uacfc 5\uac1c \uc0d8\ud50c \ud589\uc744 \ubcf4\uc5ec\uc918\u201d\ucc98\ub7fc \uc694\uccad\ud569\ub2c8\ub2e4. \ud50c\ub79c\u00b7\uc6cc\ud06c\uc2a4\ud398\uc774\uc2a4 \uad8c\ud55c\uc5d0 \ub530\ub77c \uc0ac\uc6a9\uc790 \uc815\uc758 MCP \uc5f0\uacb0\uc774 \uc81c\ud55c\ub420 \uc218 \uc788\uc2b5\ub2c8\ub2e4.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">10. \uc6b4\uc601 \ubcf4\uc548\uacfc \ud655\uc7a5<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">MySQL SELECT \uc804\uc6a9 \uacc4\uc815, \uceec\ub7fc \ud5c8\uc6a9 \ubaa9\ub85d, \ucd5c\ub300 10\ud589, \uc9e7\uc740 CommandTimeout, \ubc29\ud654\ubcbd, \uc778\uc99d\/\uc811\uadfc \uad8c\ud55c, \ub85c\uadf8 \ubc0f \uc815\uae30 \ubc31\uc5c5\uc744 \uc801\uc6a9\ud558\uc138\uc694. \ucffc\ub9ac\uc758 ReadOnly \uba54\ud0c0\ub370\uc774\ud130 \uc790\uccb4\uac00 \ubcf4\uc548\uc744 \ubcf4\uc7a5\ud558\uc9c0\ub294 \uc54a\uc2b5\ub2c8\ub2e4. <strong>\uc774 \uc608\uc81c\ub294 \ud559\uc2b5\u00b7\ud14c\uc2a4\ud2b8\uc6a9\uc774\uba70 \uc2e4\uc81c \uc11c\ubc84\uc5d0\uc11c \ucef4\ud30c\uc77c\u00b7\uc2e4\ud589 \uac80\uc99d\ud55c \uacb0\uacfc\ub294 \uc544\ub2d9\ub2c8\ub2e4.<\/strong><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\ud568\uaed8 \uc77d\uae30<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/csharp.ihavenomoney.co.kr\/?p=1026\">Python MCP \uc11c\ubc84\ub85c MySQL \uc870\ud68c\u00b7\uc218\uc815\u00b7SSH \uc2b9\uc778 \uad6c\ud604\ud558\uae30<\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\uacf5\uc2dd \ubb38\uc11c<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/github.com\/modelcontextprotocol\/csharp-sdk\/blob\/main\/docs\/concepts\/getting-started.md\">C# MCP SDK Getting Started<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/developers.openai.com\/api\/docs\/guides\/secure-mcp-tunnels\">OpenAI Secure MCP Tunnel<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/help.openai.com\/en\/articles\/12584461-connecting-custom-mcp-servers-in-chatgpt\">ChatGPT MCP \uc5f0\uacb0<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Ubuntu 24.04\uc5d0 ASP.NET Core 10 \uae30\ubc18 MySQL \uc870\ud68c \uc804\uc6a9 MCP \uc11c\ubc84\ub97c \uad6c\uc131\ud558\uace0 \uacf5\uc2dd C# SDK, systemd, Secure MCP Tunnel\uacfc ChatGPT \uc5f0\uacb0\uae4c\uc9c0 \uc124\uba85\ud569\ub2c8\ub2e4.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-1027","post","type-post","status-publish","format-standard","hentry","category-1"],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/csharp.ihavenomoney.co.kr\/index.php?rest_route=\/wp\/v2\/posts\/1027","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/csharp.ihavenomoney.co.kr\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/csharp.ihavenomoney.co.kr\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/csharp.ihavenomoney.co.kr\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/csharp.ihavenomoney.co.kr\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1027"}],"version-history":[{"count":2,"href":"https:\/\/csharp.ihavenomoney.co.kr\/index.php?rest_route=\/wp\/v2\/posts\/1027\/revisions"}],"predecessor-version":[{"id":1030,"href":"https:\/\/csharp.ihavenomoney.co.kr\/index.php?rest_route=\/wp\/v2\/posts\/1027\/revisions\/1030"}],"wp:attachment":[{"href":"https:\/\/csharp.ihavenomoney.co.kr\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1027"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/csharp.ihavenomoney.co.kr\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1027"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/csharp.ihavenomoney.co.kr\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1027"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}