@@ -1215,6 +1215,155 @@ func TestMCPHTTP_E2E_OAuth2_EndToEnd(t *testing.T) {
12151215})
12161216}
12171217
1218+ func TestMCPHTTP_E2E_ChatGPTEndpoint (t * testing.T ) {
1219+ t .Parallel ()
1220+
1221+ // Setup Coder server with authentication
1222+ coderClient ,closer ,api := coderdtest .NewWithAPI (t ,& coderdtest.Options {
1223+ IncludeProvisionerDaemon :true ,
1224+ })
1225+ defer closer .Close ()
1226+
1227+ user := coderdtest .CreateFirstUser (t ,coderClient )
1228+
1229+ // Create template and workspace for testing search functionality
1230+ version := coderdtest .CreateTemplateVersion (t ,coderClient ,user .OrganizationID ,nil )
1231+ coderdtest .AwaitTemplateVersionJobCompleted (t ,coderClient ,version .ID )
1232+ template := coderdtest .CreateTemplate (t ,coderClient ,user .OrganizationID ,version .ID )
1233+
1234+ // Create MCP client pointing to the ChatGPT endpoint
1235+ mcpURL := api .AccessURL .String ()+ "/api/experimental/mcp/chatgpt"
1236+
1237+ // Configure client with authentication headers using RFC 6750 Bearer token
1238+ mcpClient ,err := mcpclient .NewStreamableHttpClient (mcpURL ,
1239+ transport .WithHTTPHeaders (map [string ]string {
1240+ "Authorization" :"Bearer " + coderClient .SessionToken (),
1241+ }))
1242+ require .NoError (t ,err )
1243+ defer func () {
1244+ if closeErr := mcpClient .Close ();closeErr != nil {
1245+ t .Logf ("Failed to close MCP client: %v" ,closeErr )
1246+ }
1247+ }()
1248+
1249+ ctx ,cancel := context .WithTimeout (context .Background (),testutil .WaitLong )
1250+ defer cancel ()
1251+
1252+ // Start client
1253+ err = mcpClient .Start (ctx )
1254+ require .NoError (t ,err )
1255+
1256+ // Initialize connection
1257+ initReq := mcp.InitializeRequest {
1258+ Params : mcp.InitializeParams {
1259+ ProtocolVersion :mcp .LATEST_PROTOCOL_VERSION ,
1260+ ClientInfo : mcp.Implementation {
1261+ Name :"test-chatgpt-client" ,
1262+ Version :"1.0.0" ,
1263+ },
1264+ },
1265+ }
1266+
1267+ result ,err := mcpClient .Initialize (ctx ,initReq )
1268+ require .NoError (t ,err )
1269+ require .Equal (t ,mcpserver .MCPServerName ,result .ServerInfo .Name )
1270+ require .Equal (t ,mcp .LATEST_PROTOCOL_VERSION ,result .ProtocolVersion )
1271+ require .NotNil (t ,result .Capabilities )
1272+
1273+ // Test tool listing - should only have search and fetch tools for ChatGPT
1274+ tools ,err := mcpClient .ListTools (ctx , mcp.ListToolsRequest {})
1275+ require .NoError (t ,err )
1276+ require .NotEmpty (t ,tools .Tools )
1277+
1278+ // Verify we have exactly the ChatGPT tools and no others
1279+ var foundTools []string
1280+ for _ ,tool := range tools .Tools {
1281+ foundTools = append (foundTools ,tool .Name )
1282+ }
1283+
1284+ // ChatGPT endpoint should only expose search and fetch tools
1285+ assert .Contains (t ,foundTools ,toolsdk .ToolNameChatGPTSearch ,"Should have ChatGPT search tool" )
1286+ assert .Contains (t ,foundTools ,toolsdk .ToolNameChatGPTFetch ,"Should have ChatGPT fetch tool" )
1287+ assert .Len (t ,foundTools ,2 ,"ChatGPT endpoint should only expose search and fetch tools" )
1288+
1289+ // Should NOT have other tools that are available in the standard endpoint
1290+ assert .NotContains (t ,foundTools ,toolsdk .ToolNameGetAuthenticatedUser ,"Should not have authenticated user tool" )
1291+ assert .NotContains (t ,foundTools ,toolsdk .ToolNameListWorkspaces ,"Should not have list workspaces tool" )
1292+
1293+ t .Logf ("ChatGPT endpoint tools: %v" ,foundTools )
1294+
1295+ // Test search tool - search for templates
1296+ var searchTool * mcp.Tool
1297+ for _ ,tool := range tools .Tools {
1298+ if tool .Name == toolsdk .ToolNameChatGPTSearch {
1299+ searchTool = & tool
1300+ break
1301+ }
1302+ }
1303+ require .NotNil (t ,searchTool ,"Expected to find search tool" )
1304+
1305+ // Execute search for templates
1306+ searchReq := mcp.CallToolRequest {
1307+ Params : mcp.CallToolParams {
1308+ Name :searchTool .Name ,
1309+ Arguments :map [string ]any {
1310+ "query" :"templates" ,
1311+ },
1312+ },
1313+ }
1314+
1315+ searchResult ,err := mcpClient .CallTool (ctx ,searchReq )
1316+ require .NoError (t ,err )
1317+ require .NotEmpty (t ,searchResult .Content )
1318+
1319+ // Verify the search result contains our template
1320+ assert .Len (t ,searchResult .Content ,1 )
1321+ if textContent ,ok := searchResult .Content [0 ].(mcp.TextContent );ok {
1322+ assert .Equal (t ,"text" ,textContent .Type )
1323+ assert .Contains (t ,textContent .Text ,template .ID .String (),"Search result should contain our test template" )
1324+ t .Logf ("Search result: %s" ,textContent .Text )
1325+ }else {
1326+ t .Errorf ("Expected TextContent type, got %T" ,searchResult .Content [0 ])
1327+ }
1328+
1329+ // Test fetch tool
1330+ var fetchTool * mcp.Tool
1331+ for _ ,tool := range tools .Tools {
1332+ if tool .Name == toolsdk .ToolNameChatGPTFetch {
1333+ fetchTool = & tool
1334+ break
1335+ }
1336+ }
1337+ require .NotNil (t ,fetchTool ,"Expected to find fetch tool" )
1338+
1339+ // Execute fetch for the template
1340+ fetchReq := mcp.CallToolRequest {
1341+ Params : mcp.CallToolParams {
1342+ Name :fetchTool .Name ,
1343+ Arguments :map [string ]any {
1344+ "id" :fmt .Sprintf ("template:%s" ,template .ID .String ()),
1345+ },
1346+ },
1347+ }
1348+
1349+ fetchResult ,err := mcpClient .CallTool (ctx ,fetchReq )
1350+ require .NoError (t ,err )
1351+ require .NotEmpty (t ,fetchResult .Content )
1352+
1353+ // Verify the fetch result contains template details
1354+ assert .Len (t ,fetchResult .Content ,1 )
1355+ if textContent ,ok := fetchResult .Content [0 ].(mcp.TextContent );ok {
1356+ assert .Equal (t ,"text" ,textContent .Type )
1357+ assert .Contains (t ,textContent .Text ,template .Name ,"Fetch result should contain template name" )
1358+ assert .Contains (t ,textContent .Text ,template .ID .String (),"Fetch result should contain template ID" )
1359+ t .Logf ("Fetch result contains template data" )
1360+ }else {
1361+ t .Errorf ("Expected TextContent type, got %T" ,fetchResult .Content [0 ])
1362+ }
1363+
1364+ t .Logf ("ChatGPT endpoint E2E test successful: search and fetch tools working correctly" )
1365+ }
1366+
12181367// Helper function to parse URL safely in tests
12191368func mustParseURL (t * testing.T ,rawURL string )* url.URL {
12201369u ,err := url .Parse (rawURL )