mirror of
https://github.com/openai/codex.git
synced 2026-11-02 16:20:35 +00:00
[mcp] Support MCP Apps part 1. (#16082)
- [x] Add `mcpResource/read` method to read mcp resource.
This commit is contained in:
1 parent
ee12772e80
commit
5fe9ef06ce
20 files changed
+754
-2
No files matched your search
@@ -1219,6 +1219,25 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"McpResourceReadParams": {
|
||||||
|
"properties": {
|
||||||
|
"server": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"threadId": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"uri": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"server",
|
||||||
|
"threadId",
|
||||||
|
"uri"
|
||||||
|
],
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
"McpServerOauthLoginParams": {
|
"McpServerOauthLoginParams": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": {
|
"name": {
|
||||||
@@ -4438,6 +4457,30 @@
|
|||||||
"title": "McpServerStatus/listRequest",
|
"title": "McpServerStatus/listRequest",
|
||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"$ref": "#/definitions/RequestId"
|
||||||
|
},
|
||||||
|
"method": {
|
||||||
|
"enum": [
|
||||||
|
"mcpServer/resource/read"
|
||||||
|
],
|
||||||
|
"title": "McpServer/resource/readRequestMethod",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"params": {
|
||||||
|
"$ref": "#/definitions/McpResourceReadParams"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"id",
|
||||||
|
"method",
|
||||||
|
"params"
|
||||||
|
],
|
||||||
|
"title": "McpServer/resource/readRequest",
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"properties": {
|
"properties": {
|
||||||
"id": {
|
"id": {
|
||||||
|
|||||||
@@ -1201,6 +1201,30 @@
|
|||||||
"title": "McpServerStatus/listRequest",
|
"title": "McpServerStatus/listRequest",
|
||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"$ref": "#/definitions/v2/RequestId"
|
||||||
|
},
|
||||||
|
"method": {
|
||||||
|
"enum": [
|
||||||
|
"mcpServer/resource/read"
|
||||||
|
],
|
||||||
|
"title": "McpServer/resource/readRequestMethod",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"params": {
|
||||||
|
"$ref": "#/definitions/v2/McpResourceReadParams"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"id",
|
||||||
|
"method",
|
||||||
|
"params"
|
||||||
|
],
|
||||||
|
"title": "McpServer/resource/readRequest",
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"properties": {
|
"properties": {
|
||||||
"id": {
|
"id": {
|
||||||
@@ -8940,6 +8964,43 @@
|
|||||||
],
|
],
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"McpResourceReadParams": {
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"properties": {
|
||||||
|
"server": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"threadId": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"uri": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"server",
|
||||||
|
"threadId",
|
||||||
|
"uri"
|
||||||
|
],
|
||||||
|
"title": "McpResourceReadParams",
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"McpResourceReadResponse": {
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"properties": {
|
||||||
|
"contents": {
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/v2/ResourceContent"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"contents"
|
||||||
|
],
|
||||||
|
"title": "McpResourceReadResponse",
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
"McpServerOauthLoginCompletedNotification": {
|
"McpServerOauthLoginCompletedNotification": {
|
||||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -10661,6 +10722,57 @@
|
|||||||
],
|
],
|
||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
|
"ResourceContent": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"_meta": true,
|
||||||
|
"mimeType": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"text": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"uri": {
|
||||||
|
"description": "The URI of this resource.",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"text",
|
||||||
|
"uri"
|
||||||
|
],
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"_meta": true,
|
||||||
|
"blob": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"mimeType": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"uri": {
|
||||||
|
"description": "The URI of this resource.",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"blob",
|
||||||
|
"uri"
|
||||||
|
],
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Contents returned when reading a resource from an MCP server."
|
||||||
|
},
|
||||||
"ResourceTemplate": {
|
"ResourceTemplate": {
|
||||||
"description": "A template description for resources available on the server.",
|
"description": "A template description for resources available on the server.",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|||||||
@@ -1776,6 +1776,30 @@
|
|||||||
"title": "McpServerStatus/listRequest",
|
"title": "McpServerStatus/listRequest",
|
||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"$ref": "#/definitions/RequestId"
|
||||||
|
},
|
||||||
|
"method": {
|
||||||
|
"enum": [
|
||||||
|
"mcpServer/resource/read"
|
||||||
|
],
|
||||||
|
"title": "McpServer/resource/readRequestMethod",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"params": {
|
||||||
|
"$ref": "#/definitions/McpResourceReadParams"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"id",
|
||||||
|
"method",
|
||||||
|
"params"
|
||||||
|
],
|
||||||
|
"title": "McpServer/resource/readRequest",
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"properties": {
|
"properties": {
|
||||||
"id": {
|
"id": {
|
||||||
@@ -5763,6 +5787,43 @@
|
|||||||
],
|
],
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"McpResourceReadParams": {
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"properties": {
|
||||||
|
"server": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"threadId": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"uri": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"server",
|
||||||
|
"threadId",
|
||||||
|
"uri"
|
||||||
|
],
|
||||||
|
"title": "McpResourceReadParams",
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"McpResourceReadResponse": {
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"properties": {
|
||||||
|
"contents": {
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/ResourceContent"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"contents"
|
||||||
|
],
|
||||||
|
"title": "McpResourceReadResponse",
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
"McpServerOauthLoginCompletedNotification": {
|
"McpServerOauthLoginCompletedNotification": {
|
||||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -7484,6 +7545,57 @@
|
|||||||
],
|
],
|
||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
|
"ResourceContent": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"_meta": true,
|
||||||
|
"mimeType": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"text": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"uri": {
|
||||||
|
"description": "The URI of this resource.",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"text",
|
||||||
|
"uri"
|
||||||
|
],
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"_meta": true,
|
||||||
|
"blob": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"mimeType": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"uri": {
|
||||||
|
"description": "The URI of this resource.",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"blob",
|
||||||
|
"uri"
|
||||||
|
],
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Contents returned when reading a resource from an MCP server."
|
||||||
|
},
|
||||||
"ResourceTemplate": {
|
"ResourceTemplate": {
|
||||||
"description": "A template description for resources available on the server.",
|
"description": "A template description for resources available on the server.",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"properties": {
|
||||||
|
"server": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"threadId": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"uri": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"server",
|
||||||
|
"threadId",
|
||||||
|
"uri"
|
||||||
|
],
|
||||||
|
"title": "McpResourceReadParams",
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"definitions": {
|
||||||
|
"ResourceContent": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"_meta": true,
|
||||||
|
"mimeType": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"text": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"uri": {
|
||||||
|
"description": "The URI of this resource.",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"text",
|
||||||
|
"uri"
|
||||||
|
],
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"_meta": true,
|
||||||
|
"blob": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"mimeType": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"uri": {
|
||||||
|
"description": "The URI of this resource.",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"blob",
|
||||||
|
"uri"
|
||||||
|
],
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Contents returned when reading a resource from an MCP server."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"contents": {
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/ResourceContent"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"contents"
|
||||||
|
],
|
||||||
|
"title": "McpResourceReadResponse",
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
@@ -33,6 +33,7 @@ import type { FsWriteFileParams } from "./v2/FsWriteFileParams";
|
|||||||
import type { GetAccountParams } from "./v2/GetAccountParams";
|
import type { GetAccountParams } from "./v2/GetAccountParams";
|
||||||
import type { ListMcpServerStatusParams } from "./v2/ListMcpServerStatusParams";
|
import type { ListMcpServerStatusParams } from "./v2/ListMcpServerStatusParams";
|
||||||
import type { LoginAccountParams } from "./v2/LoginAccountParams";
|
import type { LoginAccountParams } from "./v2/LoginAccountParams";
|
||||||
|
import type { McpResourceReadParams } from "./v2/McpResourceReadParams";
|
||||||
import type { McpServerOauthLoginParams } from "./v2/McpServerOauthLoginParams";
|
import type { McpServerOauthLoginParams } from "./v2/McpServerOauthLoginParams";
|
||||||
import type { ModelListParams } from "./v2/ModelListParams";
|
import type { ModelListParams } from "./v2/ModelListParams";
|
||||||
import type { PluginInstallParams } from "./v2/PluginInstallParams";
|
import type { PluginInstallParams } from "./v2/PluginInstallParams";
|
||||||
@@ -64,4 +65,4 @@ import type { WindowsSandboxSetupStartParams } from "./v2/WindowsSandboxSetupSta
|
|||||||
/**
|
/**
|
||||||
* Request from the client to the server.
|
* Request from the client to the server.
|
||||||
*/
|
*/
|
||||||
export type ClientRequest ={ "method": "initialize", id: RequestId, params: InitializeParams, } | { "method": "thread/start", id: RequestId, params: ThreadStartParams, } | { "method": "thread/resume", id: RequestId, params: ThreadResumeParams, } | { "method": "thread/fork", id: RequestId, params: ThreadForkParams, } | { "method": "thread/archive", id: RequestId, params: ThreadArchiveParams, } | { "method": "thread/unsubscribe", id: RequestId, params: ThreadUnsubscribeParams, } | { "method": "thread/name/set", id: RequestId, params: ThreadSetNameParams, } | { "method": "thread/metadata/update", id: RequestId, params: ThreadMetadataUpdateParams, } | { "method": "thread/unarchive", id: RequestId, params: ThreadUnarchiveParams, } | { "method": "thread/compact/start", id: RequestId, params: ThreadCompactStartParams, } | { "method": "thread/shellCommand", id: RequestId, params: ThreadShellCommandParams, } | { "method": "thread/rollback", id: RequestId, params: ThreadRollbackParams, } | { "method": "thread/list", id: RequestId, params: ThreadListParams, } | { "method": "thread/loaded/list", id: RequestId, params: ThreadLoadedListParams, } | { "method": "thread/read", id: RequestId, params: ThreadReadParams, } | { "method": "skills/list", id: RequestId, params: SkillsListParams, } | { "method": "plugin/list", id: RequestId, params: PluginListParams, } | { "method": "plugin/read", id: RequestId, params: PluginReadParams, } | { "method": "app/list", id: RequestId, params: AppsListParams, } | { "method": "fs/readFile", id: RequestId, params: FsReadFileParams, } | { "method": "fs/writeFile", id: RequestId, params: FsWriteFileParams, } | { "method": "fs/createDirectory", id: RequestId, params: FsCreateDirectoryParams, } | { "method": "fs/getMetadata", id: RequestId, params: FsGetMetadataParams, } | { "method": "fs/readDirectory", id: RequestId, params: FsReadDirectoryParams, } | { "method": "fs/remove", id: RequestId, params: FsRemoveParams, } | { "method": "fs/copy", id: RequestId, params: FsCopyParams, } | { "method": "fs/watch", id: RequestId, params: FsWatchParams, } | { "method": "fs/unwatch", id: RequestId, params: FsUnwatchParams, } | { "method": "skills/config/write", id: RequestId, params: SkillsConfigWriteParams, } | { "method": "plugin/install", id: RequestId, params: PluginInstallParams, } | { "method": "plugin/uninstall", id: RequestId, params: PluginUninstallParams, } | { "method": "turn/start", id: RequestId, params: TurnStartParams, } | { "method": "turn/steer", id: RequestId, params: TurnSteerParams, } | { "method": "turn/interrupt", id: RequestId, params: TurnInterruptParams, } | { "method": "review/start", id: RequestId, params: ReviewStartParams, } | { "method": "model/list", id: RequestId, params: ModelListParams, } | { "method": "experimentalFeature/list", id: RequestId, params: ExperimentalFeatureListParams, } | { "method": "experimentalFeature/enablement/set", id: RequestId, params: ExperimentalFeatureEnablementSetParams, } | { "method": "mcpServer/oauth/login", id: RequestId, params: McpServerOauthLoginParams, } | { "method": "config/mcpServer/reload", id: RequestId, params: undefined, } | { "method": "mcpServerStatus/list", id: RequestId, params: ListMcpServerStatusParams, } | { "method": "windowsSandbox/setupStart", id: RequestId, params: WindowsSandboxSetupStartParams, } | { "method": "account/login/start", id: RequestId, params: LoginAccountParams, } | { "method": "account/login/cancel", id: RequestId, params: CancelLoginAccountParams, } | { "method": "account/logout", id: RequestId, params: undefined, } | { "method": "account/rateLimits/read", id: RequestId, params: undefined, } | { "method": "feedback/upload", id: RequestId, params: FeedbackUploadParams, } | { "method": "command/exec", id: RequestId, params: CommandExecParams, } | { "method": "command/exec/write", id: RequestId, params: CommandExecWriteParams, } | { "method": "command/exec/terminate", id: RequestId, params: CommandExecTerminateParams, } | { "method": "command/exec/resize", id: RequestId, params: CommandExecResizeParams, } | { "method": "config/read", id: RequestId, params: ConfigReadParams, } | { "method": "externalAgentConfig/detect", id: RequestId, params: ExternalAgentConfigDetectParams, } | { "method": "externalAgentConfig/import", id: RequestId, params: ExternalAgentConfigImportParams, } | { "method": "config/value/write", id: RequestId, params: ConfigValueWriteParams, } | { "method": "config/batchWrite", id: RequestId, params: ConfigBatchWriteParams, } | { "method": "configRequirements/read", id: RequestId, params: undefined, } | { "method": "account/read", id: RequestId, params: GetAccountParams, } | { "method": "getConversationSummary", id: RequestId, params: GetConversationSummaryParams, } | { "method": "gitDiffToRemote", id: RequestId, params: GitDiffToRemoteParams, } | { "method": "getAuthStatus", id: RequestId, params: GetAuthStatusParams, } | { "method": "fuzzyFileSearch", id: RequestId, params: FuzzyFileSearLine truncated
|
export type ClientRequest ={ "method": "initialize", id: RequestId, params: InitializeParams, } | { "method": "thread/start", id: RequestId, params: ThreadStartParams, } | { "method": "thread/resume", id: RequestId, params: ThreadResumeParams, } | { "method": "thread/fork", id: RequestId, params: ThreadForkParams, } | { "method": "thread/archive", id: RequestId, params: ThreadArchiveParams, } | { "method": "thread/unsubscribe", id: RequestId, params: ThreadUnsubscribeParams, } | { "method": "thread/name/set", id: RequestId, params: ThreadSetNameParams, } | { "method": "thread/metadata/update", id: RequestId, params: ThreadMetadataUpdateParams, } | { "method": "thread/unarchive", id: RequestId, params: ThreadUnarchiveParams, } | { "method": "thread/compact/start", id: RequestId, params: ThreadCompactStartParams, } | { "method": "thread/shellCommand", id: RequestId, params: ThreadShellCommandParams, } | { "method": "thread/rollback", id: RequestId, params: ThreadRollbackParams, } | { "method": "thread/list", id: RequestId, params: ThreadListParams, } | { "method": "thread/loaded/list", id: RequestId, params: ThreadLoadedListParams, } | { "method": "thread/read", id: RequestId, params: ThreadReadParams, } | { "method": "skills/list", id: RequestId, params: SkillsListParams, } | { "method": "plugin/list", id: RequestId, params: PluginListParams, } | { "method": "plugin/read", id: RequestId, params: PluginReadParams, } | { "method": "app/list", id: RequestId, params: AppsListParams, } | { "method": "fs/readFile", id: RequestId, params: FsReadFileParams, } | { "method": "fs/writeFile", id: RequestId, params: FsWriteFileParams, } | { "method": "fs/createDirectory", id: RequestId, params: FsCreateDirectoryParams, } | { "method": "fs/getMetadata", id: RequestId, params: FsGetMetadataParams, } | { "method": "fs/readDirectory", id: RequestId, params: FsReadDirectoryParams, } | { "method": "fs/remove", id: RequestId, params: FsRemoveParams, } | { "method": "fs/copy", id: RequestId, params: FsCopyParams, } | { "method": "fs/watch", id: RequestId, params: FsWatchParams, } | { "method": "fs/unwatch", id: RequestId, params: FsUnwatchParams, } | { "method": "skills/config/write", id: RequestId, params: SkillsConfigWriteParams, } | { "method": "plugin/install", id: RequestId, params: PluginInstallParams, } | { "method": "plugin/uninstall", id: RequestId, params: PluginUninstallParams, } | { "method": "turn/start", id: RequestId, params: TurnStartParams, } | { "method": "turn/steer", id: RequestId, params: TurnSteerParams, } | { "method": "turn/interrupt", id: RequestId, params: TurnInterruptParams, } | { "method": "review/start", id: RequestId, params: ReviewStartParams, } | { "method": "model/list", id: RequestId, params: ModelListParams, } | { "method": "experimentalFeature/list", id: RequestId, params: ExperimentalFeatureListParams, } | { "method": "experimentalFeature/enablement/set", id: RequestId, params: ExperimentalFeatureEnablementSetParams, } | { "method": "mcpServer/oauth/login", id: RequestId, params: McpServerOauthLoginParams, } | { "method": "config/mcpServer/reload", id: RequestId, params: undefined, } | { "method": "mcpServerStatus/list", id: RequestId, params: ListMcpServerStatusParams, } | { "method": "mcpServer/resource/read", id: RequestId, params: McpResourceReadParams, } | { "method": "windowsSandbox/setupStart", id: RequestId, params: WindowsSandboxSetupStartParams, } | { "method": "account/login/start", id: RequestId, params: LoginAccountParams, } | { "method": "account/login/cancel", id: RequestId, params: CancelLoginAccountParams, } | { "method": "account/logout", id: RequestId, params: undefined, } | { "method": "account/rateLimits/read", id: RequestId, params: undefined, } | { "method": "feedback/upload", id: RequestId, params: FeedbackUploadParams, } | { "method": "command/exec", id: RequestId, params: CommandExecParams, } | { "method": "command/exec/write", id: RequestId, params: CommandExecWriteParams, } | { "method": "command/exec/terminate", id: RequestId, params: CommandExecTerminateParams, } | { "method": "command/exec/resize", id: RequestId, params: CommandExecResizeParams, } | { "method": "config/read", id: RequestId, params: ConfigReadParams, } | { "method": "externalAgentConfig/detect", id: RequestId, params: ExternalAgentConfigDetectParams, } | { "method": "externalAgentConfig/import", id: RequestId, params: ExternalAgentConfigImportParams, } | { "method": "config/value/write", id: RequestId, params: ConfigValueWriteParams, } | { "method": "config/batchWrite", id: RequestId, params: ConfigBatchWriteParams, } | { "method": "configRequirements/read", id: RequestId, params: undefined, } | { "method": "account/read", id: RequestId, params: GetAccountParams, } | { "method": "getConversationSummary", id: RequestId, params: GetConversationSummaryParams, } | { "method": "gitDiffToRemote", id: RequestId, params: GitDiffToRemoteParams, } | { "method": "getAuthStatus", id: RequestId, params: GetLine truncated
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||||
|
|
||||||
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
import type { JsonValue } from "./serde_json/JsonValue";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contents returned when reading a resource from an MCP server.
|
||||||
|
*/
|
||||||
|
export type ResourceContent = {
|
||||||
|
/**
|
||||||
|
* The URI of this resource.
|
||||||
|
*/
|
||||||
|
uri: string, mimeType?: string, text: string, _meta?: JsonValue, } | {
|
||||||
|
/**
|
||||||
|
* The URI of this resource.
|
||||||
|
*/
|
||||||
|
uri: string, mimeType?: string, blob: string, _meta?: JsonValue, };
|
||||||
@@ -55,6 +55,7 @@ export type { ReasoningItemReasoningSummary } from "./ReasoningItemReasoningSumm
|
|||||||
export type { ReasoningSummary } from "./ReasoningSummary";
|
export type { ReasoningSummary } from "./ReasoningSummary";
|
||||||
export type { RequestId } from "./RequestId";
|
export type { RequestId } from "./RequestId";
|
||||||
export type { Resource } from "./Resource";
|
export type { Resource } from "./Resource";
|
||||||
|
export type { ResourceContent } from "./ResourceContent";
|
||||||
export type { ResourceTemplate } from "./ResourceTemplate";
|
export type { ResourceTemplate } from "./ResourceTemplate";
|
||||||
export type { ResponseItem } from "./ResponseItem";
|
export type { ResponseItem } from "./ResponseItem";
|
||||||
export type { ReviewDecision } from "./ReviewDecision";
|
export type { ReviewDecision } from "./ReviewDecision";
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||||
|
|
||||||
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
|
||||||
|
export type McpResourceReadParams = { threadId: string, server: string, uri: string, };
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||||
|
|
||||||
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
import type { ResourceContent } from "../ResourceContent";
|
||||||
|
|
||||||
|
export type McpResourceReadResponse = { contents: Array<ResourceContent>, };
|
||||||
@@ -172,6 +172,8 @@ export type { McpElicitationTitledSingleSelectEnumSchema } from "./McpElicitatio
|
|||||||
export type { McpElicitationUntitledEnumItems } from "./McpElicitationUntitledEnumItems";
|
export type { McpElicitationUntitledEnumItems } from "./McpElicitationUntitledEnumItems";
|
||||||
export type { McpElicitationUntitledMultiSelectEnumSchema } from "./McpElicitationUntitledMultiSelectEnumSchema";
|
export type { McpElicitationUntitledMultiSelectEnumSchema } from "./McpElicitationUntitledMultiSelectEnumSchema";
|
||||||
export type { McpElicitationUntitledSingleSelectEnumSchema } from "./McpElicitationUntitledSingleSelectEnumSchema";
|
export type { McpElicitationUntitledSingleSelectEnumSchema } from "./McpElicitationUntitledSingleSelectEnumSchema";
|
||||||
|
export type { McpResourceReadParams } from "./McpResourceReadParams";
|
||||||
|
export type { McpResourceReadResponse } from "./McpResourceReadResponse";
|
||||||
export type { McpServerElicitationAction } from "./McpServerElicitationAction";
|
export type { McpServerElicitationAction } from "./McpServerElicitationAction";
|
||||||
export type { McpServerElicitationRequestParams } from "./McpServerElicitationRequestParams";
|
export type { McpServerElicitationRequestParams } from "./McpServerElicitationRequestParams";
|
||||||
export type { McpServerElicitationRequestResponse } from "./McpServerElicitationRequestResponse";
|
export type { McpServerElicitationRequestResponse } from "./McpServerElicitationRequestResponse";
|
||||||
|
|||||||
@@ -459,6 +459,11 @@ client_request_definitions! {
|
|||||||
response: v2::ListMcpServerStatusResponse,
|
response: v2::ListMcpServerStatusResponse,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
McpResourceRead => "mcpServer/resource/read" {
|
||||||
|
params: v2::McpResourceReadParams,
|
||||||
|
response: v2::McpResourceReadResponse,
|
||||||
|
},
|
||||||
|
|
||||||
WindowsSandboxSetupStart => "windowsSandbox/setupStart" {
|
WindowsSandboxSetupStart => "windowsSandbox/setupStart" {
|
||||||
params: v2::WindowsSandboxSetupStartParams,
|
params: v2::WindowsSandboxSetupStartParams,
|
||||||
response: v2::WindowsSandboxSetupStartResponse,
|
response: v2::WindowsSandboxSetupStartResponse,
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ use codex_protocol::config_types::WebSearchToolConfig;
|
|||||||
use codex_protocol::items::AgentMessageContent as CoreAgentMessageContent;
|
use codex_protocol::items::AgentMessageContent as CoreAgentMessageContent;
|
||||||
use codex_protocol::items::TurnItem as CoreTurnItem;
|
use codex_protocol::items::TurnItem as CoreTurnItem;
|
||||||
use codex_protocol::mcp::Resource as McpResource;
|
use codex_protocol::mcp::Resource as McpResource;
|
||||||
|
pub use codex_protocol::mcp::ResourceContent as McpResourceContent;
|
||||||
use codex_protocol::mcp::ResourceTemplate as McpResourceTemplate;
|
use codex_protocol::mcp::ResourceTemplate as McpResourceTemplate;
|
||||||
use codex_protocol::mcp::Tool as McpTool;
|
use codex_protocol::mcp::Tool as McpTool;
|
||||||
use codex_protocol::memory_citation::MemoryCitation as CoreMemoryCitation;
|
use codex_protocol::memory_citation::MemoryCitation as CoreMemoryCitation;
|
||||||
@@ -1984,6 +1985,22 @@ pub struct ListMcpServerStatusResponse {
|
|||||||
pub next_cursor: Option<String>,
|
pub next_cursor: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
#[ts(export_to = "v2/")]
|
||||||
|
pub struct McpResourceReadParams {
|
||||||
|
pub thread_id: String,
|
||||||
|
pub server: String,
|
||||||
|
pub uri: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
#[ts(export_to = "v2/")]
|
||||||
|
pub struct McpResourceReadResponse {
|
||||||
|
pub contents: Vec<McpResourceContent>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema, TS)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema, TS)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
#[ts(export_to = "v2/")]
|
#[ts(export_to = "v2/")]
|
||||||
|
|||||||
@@ -187,7 +187,8 @@ Example with notification opt-out:
|
|||||||
- `mcpServer/oauth/login` — start an OAuth login for a configured MCP server; returns an `authorization_url` and later emits `mcpServer/oauthLogin/completed` once the browser flow finishes.
|
- `mcpServer/oauth/login` — start an OAuth login for a configured MCP server; returns an `authorization_url` and later emits `mcpServer/oauthLogin/completed` once the browser flow finishes.
|
||||||
- `tool/requestUserInput` — prompt the user with 1–3 short questions for a tool call and return their answers (experimental).
|
- `tool/requestUserInput` — prompt the user with 1–3 short questions for a tool call and return their answers (experimental).
|
||||||
- `config/mcpServer/reload` — reload MCP server config from disk and queue a refresh for loaded threads (applied on each thread's next active turn); returns `{}`. Use this after editing `config.toml` without restarting the server.
|
- `config/mcpServer/reload` — reload MCP server config from disk and queue a refresh for loaded threads (applied on each thread's next active turn); returns `{}`. Use this after editing `config.toml` without restarting the server.
|
||||||
- `mcpServerStatus/list` — enumerate configured MCP servers with their tools and auth status, plus optional resources/resource templates when requested; supports cursor+limit pagination. If `detail` is omitted, the server defaults to `full`.
|
- `mcpServerStatus/list` — enumerate configured MCP servers with their tools and auth status, plus resources/resource templates for `full` detail; supports cursor+limit pagination. If `detail` is omitted, the server defaults to `full`.
|
||||||
|
- `mcpServer/resource/read` — read a resource from a thread's configured MCP server by `threadId`, `server`, and `uri`, returning text/blob resource `contents`.
|
||||||
- `windowsSandbox/setupStart` — start Windows sandbox setup for the selected mode (`elevated` or `unelevated`); accepts an optional absolute `cwd` to target setup for a specific workspace, returns `{ started: true }` immediately, and later emits `windowsSandbox/setupCompleted`.
|
- `windowsSandbox/setupStart` — start Windows sandbox setup for the selected mode (`elevated` or `unelevated`); accepts an optional absolute `cwd` to target setup for a specific workspace, returns `{ started: true }` immediately, and later emits `windowsSandbox/setupCompleted`.
|
||||||
- `feedback/upload` — submit a feedback report (classification + optional reason/logs, conversation_id, and optional `extraLogFiles` attachments array); returns the tracking thread id.
|
- `feedback/upload` — submit a feedback report (classification + optional reason/logs, conversation_id, and optional `extraLogFiles` attachments array); returns the tracking thread id.
|
||||||
- `config/read` — fetch the effective config on disk after resolving config layering.
|
- `config/read` — fetch the effective config on disk after resolving config layering.
|
||||||
|
|||||||
@@ -76,6 +76,8 @@ use codex_app_server_protocol::LoginAccountResponse;
|
|||||||
use codex_app_server_protocol::LoginApiKeyParams;
|
use codex_app_server_protocol::LoginApiKeyParams;
|
||||||
use codex_app_server_protocol::LogoutAccountResponse;
|
use codex_app_server_protocol::LogoutAccountResponse;
|
||||||
use codex_app_server_protocol::MarketplaceInterface;
|
use codex_app_server_protocol::MarketplaceInterface;
|
||||||
|
use codex_app_server_protocol::McpResourceReadParams;
|
||||||
|
use codex_app_server_protocol::McpResourceReadResponse;
|
||||||
use codex_app_server_protocol::McpServerOauthLoginCompletedNotification;
|
use codex_app_server_protocol::McpServerOauthLoginCompletedNotification;
|
||||||
use codex_app_server_protocol::McpServerOauthLoginParams;
|
use codex_app_server_protocol::McpServerOauthLoginParams;
|
||||||
use codex_app_server_protocol::McpServerOauthLoginResponse;
|
use codex_app_server_protocol::McpServerOauthLoginResponse;
|
||||||
@@ -882,6 +884,10 @@ impl CodexMessageProcessor {
|
|||||||
self.list_mcp_server_status(to_connection_request_id(request_id), params)
|
self.list_mcp_server_status(to_connection_request_id(request_id), params)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
ClientRequest::McpResourceRead { request_id, params } => {
|
||||||
|
self.read_mcp_resource(to_connection_request_id(request_id), params)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
ClientRequest::WindowsSandboxSetupStart { request_id, params } => {
|
ClientRequest::WindowsSandboxSetupStart { request_id, params } => {
|
||||||
self.windows_sandbox_setup_start(to_connection_request_id(request_id), params)
|
self.windows_sandbox_setup_start(to_connection_request_id(request_id), params)
|
||||||
.await;
|
.await;
|
||||||
@@ -5297,6 +5303,58 @@ impl CodexMessageProcessor {
|
|||||||
outgoing.send_response(request_id, response).await;
|
outgoing.send_response(request_id, response).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn read_mcp_resource(
|
||||||
|
&self,
|
||||||
|
request_id: ConnectionRequestId,
|
||||||
|
params: McpResourceReadParams,
|
||||||
|
) {
|
||||||
|
let outgoing = Arc::clone(&self.outgoing);
|
||||||
|
let (_, thread) = match self.load_thread(¶ms.thread_id).await {
|
||||||
|
Ok(thread) => thread,
|
||||||
|
Err(error) => {
|
||||||
|
self.outgoing.send_error(request_id, error).await;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
tokio::spawn(async move {
|
||||||
|
let result = thread.read_mcp_resource(¶ms.server, ¶ms.uri).await;
|
||||||
|
match result {
|
||||||
|
Ok(result) => match serde_json::from_value::<McpResourceReadResponse>(result) {
|
||||||
|
Ok(response) => {
|
||||||
|
outgoing.send_response(request_id, response).await;
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
outgoing
|
||||||
|
.send_error(
|
||||||
|
request_id,
|
||||||
|
JSONRPCErrorError {
|
||||||
|
code: INTERNAL_ERROR_CODE,
|
||||||
|
message: format!(
|
||||||
|
"failed to deserialize MCP resource read response: {error}"
|
||||||
|
),
|
||||||
|
data: None,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(error) => {
|
||||||
|
outgoing
|
||||||
|
.send_error(
|
||||||
|
request_id,
|
||||||
|
JSONRPCErrorError {
|
||||||
|
code: INTERNAL_ERROR_CODE,
|
||||||
|
message: format!("{error:#}"),
|
||||||
|
data: None,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async fn send_invalid_request_error(&self, request_id: ConnectionRequestId, message: String) {
|
async fn send_invalid_request_error(&self, request_id: ConnectionRequestId, message: String) {
|
||||||
let error = JSONRPCErrorError {
|
let error = JSONRPCErrorError {
|
||||||
code: INVALID_REQUEST_ERROR_CODE,
|
code: INVALID_REQUEST_ERROR_CODE,
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ use codex_app_server_protocol::JSONRPCRequest;
|
|||||||
use codex_app_server_protocol::JSONRPCResponse;
|
use codex_app_server_protocol::JSONRPCResponse;
|
||||||
use codex_app_server_protocol::ListMcpServerStatusParams;
|
use codex_app_server_protocol::ListMcpServerStatusParams;
|
||||||
use codex_app_server_protocol::LoginAccountParams;
|
use codex_app_server_protocol::LoginAccountParams;
|
||||||
|
use codex_app_server_protocol::McpResourceReadParams;
|
||||||
use codex_app_server_protocol::MockExperimentalMethodParams;
|
use codex_app_server_protocol::MockExperimentalMethodParams;
|
||||||
use codex_app_server_protocol::ModelListParams;
|
use codex_app_server_protocol::ModelListParams;
|
||||||
use codex_app_server_protocol::PluginInstallParams;
|
use codex_app_server_protocol::PluginInstallParams;
|
||||||
@@ -482,6 +483,15 @@ impl McpProcess {
|
|||||||
self.send_request("app/list", params).await
|
self.send_request("app/list", params).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Send an `mcpServer/resource/read` JSON-RPC request.
|
||||||
|
pub async fn send_mcp_resource_read_request(
|
||||||
|
&mut self,
|
||||||
|
params: McpResourceReadParams,
|
||||||
|
) -> anyhow::Result<i64> {
|
||||||
|
let params = Some(serde_json::to_value(params)?);
|
||||||
|
self.send_request("mcpServer/resource/read", params).await
|
||||||
|
}
|
||||||
|
|
||||||
/// Send a `skills/list` JSON-RPC request.
|
/// Send a `skills/list` JSON-RPC request.
|
||||||
pub async fn send_skills_list_request(
|
pub async fn send_skills_list_request(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|||||||
@@ -0,0 +1,218 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use app_test_support::ChatGptAuthFixture;
|
||||||
|
use app_test_support::McpProcess;
|
||||||
|
use app_test_support::to_response;
|
||||||
|
use app_test_support::write_chatgpt_auth;
|
||||||
|
use axum::Router;
|
||||||
|
use codex_app_server_protocol::JSONRPCError;
|
||||||
|
use codex_app_server_protocol::JSONRPCResponse;
|
||||||
|
use codex_app_server_protocol::McpResourceContent;
|
||||||
|
use codex_app_server_protocol::McpResourceReadParams;
|
||||||
|
use codex_app_server_protocol::McpResourceReadResponse;
|
||||||
|
use codex_app_server_protocol::RequestId;
|
||||||
|
use codex_app_server_protocol::ThreadStartParams;
|
||||||
|
use codex_app_server_protocol::ThreadStartResponse;
|
||||||
|
use codex_login::AuthCredentialsStoreMode;
|
||||||
|
use core_test_support::responses;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
use rmcp::handler::server::ServerHandler;
|
||||||
|
use rmcp::model::ProtocolVersion;
|
||||||
|
use rmcp::model::ReadResourceRequestParams;
|
||||||
|
use rmcp::model::ReadResourceResult;
|
||||||
|
use rmcp::model::ResourceContents;
|
||||||
|
use rmcp::model::ServerCapabilities;
|
||||||
|
use rmcp::model::ServerInfo;
|
||||||
|
use rmcp::service::RequestContext;
|
||||||
|
use rmcp::service::RoleServer;
|
||||||
|
use rmcp::transport::StreamableHttpServerConfig;
|
||||||
|
use rmcp::transport::StreamableHttpService;
|
||||||
|
use rmcp::transport::streamable_http_server::session::local::LocalSessionManager;
|
||||||
|
use tempfile::TempDir;
|
||||||
|
use tokio::net::TcpListener;
|
||||||
|
use tokio::time::timeout;
|
||||||
|
|
||||||
|
const DEFAULT_READ_TIMEOUT: Duration = Duration::from_secs(10);
|
||||||
|
const TEST_RESOURCE_URI: &str = "test://codex/resource";
|
||||||
|
const TEST_BLOB_RESOURCE_URI: &str = "test://codex/resource.bin";
|
||||||
|
const TEST_RESOURCE_BLOB: &str = "YmluYXJ5LXJlc291cmNl";
|
||||||
|
const TEST_RESOURCE_TEXT: &str = "Resource body from the MCP server.";
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
|
async fn mcp_resource_read_returns_resource_contents() -> Result<()> {
|
||||||
|
let responses_server = responses::start_mock_server().await;
|
||||||
|
let listener = TcpListener::bind("127.0.0.1:0").await?;
|
||||||
|
let addr = listener.local_addr()?;
|
||||||
|
let apps_server_url = format!("http://{addr}");
|
||||||
|
|
||||||
|
let mcp_service = StreamableHttpService::new(
|
||||||
|
move || Ok(ResourceAppsMcpServer),
|
||||||
|
Arc::new(LocalSessionManager::default()),
|
||||||
|
StreamableHttpServerConfig::default(),
|
||||||
|
);
|
||||||
|
let router = Router::new().nest_service("/api/codex/apps", mcp_service);
|
||||||
|
let apps_server_handle = tokio::spawn(async move {
|
||||||
|
let _ = axum::serve(listener, router).await;
|
||||||
|
});
|
||||||
|
|
||||||
|
let codex_home = TempDir::new()?;
|
||||||
|
let responses_server_uri = responses_server.uri();
|
||||||
|
std::fs::write(
|
||||||
|
codex_home.path().join("config.toml"),
|
||||||
|
format!(
|
||||||
|
r#"
|
||||||
|
model = "mock-model"
|
||||||
|
approval_policy = "untrusted"
|
||||||
|
sandbox_mode = "read-only"
|
||||||
|
|
||||||
|
model_provider = "mock_provider"
|
||||||
|
chatgpt_base_url = "{apps_server_url}"
|
||||||
|
mcp_oauth_credentials_store = "file"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
apps = true
|
||||||
|
|
||||||
|
[model_providers.mock_provider]
|
||||||
|
name = "Mock provider for test"
|
||||||
|
base_url = "{responses_server_uri}/v1"
|
||||||
|
wire_api = "responses"
|
||||||
|
request_max_retries = 0
|
||||||
|
stream_max_retries = 0
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)?;
|
||||||
|
write_chatgpt_auth(
|
||||||
|
codex_home.path(),
|
||||||
|
ChatGptAuthFixture::new("chatgpt-token")
|
||||||
|
.account_id("account-123")
|
||||||
|
.chatgpt_user_id("user-123")
|
||||||
|
.chatgpt_account_id("account-123"),
|
||||||
|
AuthCredentialsStoreMode::File,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let mut mcp = McpProcess::new(codex_home.path()).await?;
|
||||||
|
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
|
||||||
|
|
||||||
|
let thread_start_id = mcp
|
||||||
|
.send_thread_start_request(ThreadStartParams {
|
||||||
|
model: Some("mock-model".to_string()),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
let thread_start_resp: JSONRPCResponse = timeout(
|
||||||
|
DEFAULT_READ_TIMEOUT,
|
||||||
|
mcp.read_stream_until_response_message(RequestId::Integer(thread_start_id)),
|
||||||
|
)
|
||||||
|
.await??;
|
||||||
|
let ThreadStartResponse { thread, .. } = to_response(thread_start_resp)?;
|
||||||
|
|
||||||
|
let read_request_id = mcp
|
||||||
|
.send_mcp_resource_read_request(McpResourceReadParams {
|
||||||
|
thread_id: thread.id,
|
||||||
|
server: "codex_apps".to_string(),
|
||||||
|
uri: TEST_RESOURCE_URI.to_string(),
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
let read_response: JSONRPCResponse = timeout(
|
||||||
|
DEFAULT_READ_TIMEOUT,
|
||||||
|
mcp.read_stream_until_response_message(RequestId::Integer(read_request_id)),
|
||||||
|
)
|
||||||
|
.await??;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
to_response::<McpResourceReadResponse>(read_response)?,
|
||||||
|
McpResourceReadResponse {
|
||||||
|
contents: vec![
|
||||||
|
McpResourceContent::Text {
|
||||||
|
uri: TEST_RESOURCE_URI.to_string(),
|
||||||
|
mime_type: Some("text/markdown".to_string()),
|
||||||
|
text: TEST_RESOURCE_TEXT.to_string(),
|
||||||
|
meta: None,
|
||||||
|
},
|
||||||
|
McpResourceContent::Blob {
|
||||||
|
uri: TEST_BLOB_RESOURCE_URI.to_string(),
|
||||||
|
mime_type: Some("application/octet-stream".to_string()),
|
||||||
|
blob: TEST_RESOURCE_BLOB.to_string(),
|
||||||
|
meta: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
apps_server_handle.abort();
|
||||||
|
let _ = apps_server_handle.await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn mcp_resource_read_returns_error_for_unknown_thread() -> Result<()> {
|
||||||
|
let codex_home = TempDir::new()?;
|
||||||
|
let mut mcp = McpProcess::new(codex_home.path()).await?;
|
||||||
|
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
|
||||||
|
|
||||||
|
let request_id = mcp
|
||||||
|
.send_mcp_resource_read_request(McpResourceReadParams {
|
||||||
|
thread_id: "00000000-0000-4000-8000-000000000000".to_string(),
|
||||||
|
server: "codex_apps".to_string(),
|
||||||
|
uri: TEST_RESOURCE_URI.to_string(),
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
let error: JSONRPCError = timeout(
|
||||||
|
DEFAULT_READ_TIMEOUT,
|
||||||
|
mcp.read_stream_until_error_message(RequestId::Integer(request_id)),
|
||||||
|
)
|
||||||
|
.await??;
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
error.error.message.contains("thread not found"),
|
||||||
|
"expected thread-not-found error, got: {error:?}"
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Default)]
|
||||||
|
struct ResourceAppsMcpServer;
|
||||||
|
|
||||||
|
impl ServerHandler for ResourceAppsMcpServer {
|
||||||
|
fn get_info(&self) -> ServerInfo {
|
||||||
|
ServerInfo {
|
||||||
|
protocol_version: ProtocolVersion::V_2025_06_18,
|
||||||
|
capabilities: ServerCapabilities::builder().enable_resources().build(),
|
||||||
|
..ServerInfo::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn read_resource(
|
||||||
|
&self,
|
||||||
|
request: ReadResourceRequestParams,
|
||||||
|
_context: RequestContext<RoleServer>,
|
||||||
|
) -> Result<ReadResourceResult, rmcp::ErrorData> {
|
||||||
|
let uri = request.uri;
|
||||||
|
if uri != TEST_RESOURCE_URI {
|
||||||
|
return Err(rmcp::ErrorData::resource_not_found(
|
||||||
|
format!("resource not found: {uri}"),
|
||||||
|
None,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ReadResourceResult {
|
||||||
|
contents: vec![
|
||||||
|
ResourceContents::TextResourceContents {
|
||||||
|
uri: TEST_RESOURCE_URI.to_string(),
|
||||||
|
mime_type: Some("text/markdown".to_string()),
|
||||||
|
text: TEST_RESOURCE_TEXT.to_string(),
|
||||||
|
meta: None,
|
||||||
|
},
|
||||||
|
ResourceContents::BlobResourceContents {
|
||||||
|
uri: TEST_BLOB_RESOURCE_URI.to_string(),
|
||||||
|
mime_type: Some("application/octet-stream".to_string()),
|
||||||
|
blob: TEST_RESOURCE_BLOB.to_string(),
|
||||||
|
meta: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@ mod experimental_api;
|
|||||||
mod experimental_feature_list;
|
mod experimental_feature_list;
|
||||||
mod fs;
|
mod fs;
|
||||||
mod initialize;
|
mod initialize;
|
||||||
|
mod mcp_resource;
|
||||||
mod mcp_server_elicitation;
|
mod mcp_server_elicitation;
|
||||||
mod mcp_server_status;
|
mod mcp_server_status;
|
||||||
mod model_list;
|
mod model_list;
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ use codex_protocol::protocol::Submission;
|
|||||||
use codex_protocol::protocol::TokenUsage;
|
use codex_protocol::protocol::TokenUsage;
|
||||||
use codex_protocol::protocol::W3cTraceContext;
|
use codex_protocol::protocol::W3cTraceContext;
|
||||||
use codex_protocol::user_input::UserInput;
|
use codex_protocol::user_input::UserInput;
|
||||||
|
use rmcp::model::ReadResourceRequestParams;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use tokio::sync::watch;
|
use tokio::sync::watch;
|
||||||
@@ -199,6 +200,26 @@ impl CodexThread {
|
|||||||
self.codex.thread_config_snapshot().await
|
self.codex.thread_config_snapshot().await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn read_mcp_resource(
|
||||||
|
&self,
|
||||||
|
server: &str,
|
||||||
|
uri: &str,
|
||||||
|
) -> anyhow::Result<serde_json::Value> {
|
||||||
|
let result = self
|
||||||
|
.codex
|
||||||
|
.session
|
||||||
|
.read_resource(
|
||||||
|
server,
|
||||||
|
ReadResourceRequestParams {
|
||||||
|
meta: None,
|
||||||
|
uri: uri.to_string(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(serde_json::to_value(result)?)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn enabled(&self, feature: Feature) -> bool {
|
pub fn enabled(&self, feature: Feature) -> bool {
|
||||||
self.codex.enabled(feature)
|
self.codex.enabled(feature)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,6 +82,38 @@ pub struct Resource {
|
|||||||
pub meta: Option<serde_json::Value>,
|
pub meta: Option<serde_json::Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Contents returned when reading a resource from an MCP server.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, TS)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum ResourceContent {
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
#[ts(rename_all = "camelCase")]
|
||||||
|
Text {
|
||||||
|
/// The URI of this resource.
|
||||||
|
uri: String,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
#[ts(optional)]
|
||||||
|
mime_type: Option<String>,
|
||||||
|
text: String,
|
||||||
|
#[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
|
||||||
|
#[ts(optional)]
|
||||||
|
meta: Option<serde_json::Value>,
|
||||||
|
},
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
#[ts(rename_all = "camelCase")]
|
||||||
|
Blob {
|
||||||
|
/// The URI of this resource.
|
||||||
|
uri: String,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
#[ts(optional)]
|
||||||
|
mime_type: Option<String>,
|
||||||
|
blob: String,
|
||||||
|
#[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
|
||||||
|
#[ts(optional)]
|
||||||
|
meta: Option<serde_json::Value>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
/// A template description for resources available on the server.
|
/// A template description for resources available on the server.
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, TS)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, TS)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
|
|||||||
Reference in new issue
Block a user