feat(mcp): expose resource APIs

This commit is contained in:
Aiden Cline 2026-07-06 23:42:29 -05:00
commit e1d1352d8f
18 changed files with 301 additions and 17 deletions

View file

@ -9,6 +9,7 @@ import { SessionInput as CoreSessionInput } from "@opencode-ai/core/session/inpu
import { SessionMessage as CoreSessionMessage } from "@opencode-ai/core/session/message"
import { Agent } from "@opencode-ai/schema/agent"
import { Location } from "@opencode-ai/schema/location"
import { Mcp } from "@opencode-ai/schema/mcp"
import { Model } from "@opencode-ai/schema/model"
import { Project } from "@opencode-ai/schema/project"
import { Provider } from "@opencode-ai/schema/provider"
@ -26,6 +27,7 @@ const Client = await import("../src/effect")
test("effect entrypoint exposes canonical Schema contracts", () => {
expect(Client.Agent).toBe(Agent)
expect(Client.Model).toBe(Model)
expect(Client.Mcp).toBe(Mcp)
expect(Client.Session).toBe(Session)
})

View file

@ -1,5 +1,5 @@
import { expect, test } from "bun:test"
import { isSessionNotFoundError, isUnauthorizedError, OpenCode } from "../src/promise/index"
import { isMcpServerNotFoundError, isSessionNotFoundError, isUnauthorizedError, OpenCode } from "../src/promise/index"
test("exposes every standard HTTP API group", () => {
const client = OpenCode.make({ baseUrl: "http://localhost:3000" })
@ -48,6 +48,74 @@ test("exposes every standard HTTP API group", () => {
expect(Object.keys(client.pty)).toEqual(["list", "create", "get", "update", "remove"])
expect(Object.keys(client.shell)).toEqual(["list", "create", "get", "timeout", "output", "remove"])
expect(Object.keys(client.project)).toEqual(["list", "current", "directories"])
expect(Object.keys(client["server.mcp"])).toEqual(["list", "resourceCatalog", "readResource"])
})
test("MCP resource methods use the public HTTP contract", async () => {
const requests: Array<{ method: string; url: string; body?: unknown }> = []
const client = OpenCode.make({
baseUrl: "http://localhost:3000",
fetch: async (input, init) => {
const request = input instanceof Request ? input : new Request(input, init)
requests.push({
method: request.method,
url: request.url,
body: request.method === "POST" ? await request.json() : undefined,
})
if (request.method === "POST")
return Response.json({
location: { directory: "/tmp/project", project: { id: "proj_test", directory: "/tmp/project" } },
data: {
server: "docs",
uri: "docs://readme",
contents: [{ type: "text", uri: "docs://readme", text: "hello", mimeType: "text/plain" }],
},
})
return Response.json({
location: { directory: "/tmp/project", project: { id: "proj_test", directory: "/tmp/project" } },
data: {
resources: [{ server: "docs", name: "Readme", uri: "docs://readme" }],
templates: [{ server: "docs", name: "File", uriTemplate: "docs://{path}" }],
},
})
},
})
const location = { directory: "/tmp/project" }
expect((await client["server.mcp"].resourceCatalog({ location })).data.resources[0]?.uri).toBe("docs://readme")
expect(
(await client["server.mcp"].readResource({ server: "docs", uri: "docs://readme", location })).data?.contents,
).toEqual([{ type: "text", uri: "docs://readme", text: "hello", mimeType: "text/plain" }])
expect(requests).toEqual([
{
method: "GET",
url: "http://localhost:3000/api/mcp/resource?location%5Bdirectory%5D=%2Ftmp%2Fproject",
body: undefined,
},
{
method: "POST",
url: "http://localhost:3000/api/mcp/resource/read?location%5Bdirectory%5D=%2Ftmp%2Fproject",
body: { server: "docs", uri: "docs://readme" },
},
])
})
test("MCP resource reads preserve unknown-server errors", async () => {
const client = OpenCode.make({
baseUrl: "http://localhost:3000",
fetch: async () =>
Response.json(
{ _tag: "McpServerNotFoundError", server: "missing", message: "MCP server not found: missing" },
{ status: 404 },
),
})
try {
await client["server.mcp"].readResource({ server: "missing", uri: "docs://readme" })
throw new Error("Expected request to fail")
} catch (error) {
expect(isMcpServerNotFoundError(error)).toBe(true)
}
})
test("file.read returns binary content from the public HTTP contract", async () => {