Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/age-2395-xmcp-user-session-issuer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"server": minor
---

Integrate `/x/mcp` with `mcp_servers.user_session_issuer_id`. The `mcpServers.create` and `mcpServers.update` management endpoints now accept an optional `user_session_issuer_id`, and `McpServer` carries it on read. When set on an `mcp_server`, `/x/mcp` requests are issuer-gated: callers without a valid Authorization receive 401 + `WWW-Authenticate` pointing at `/.well-known/oauth-protected-resource/x/mcp/{slug}`, and the full OAuth surface — dynamic client registration, authorize, IDP callback, consent, token, revoke — is mounted under `/x/mcp/{slug}/...` against the same JWT machinery `/mcp` uses, with audience bound to `urn.NewUserSessionIssuer(...)` so tokens stay portable across toolset-backed and remote-backed servers under the same issuer. Both well-known metadata routes under `/x/mcp` now return the issuer-gated metadata shape for any addressed `mcp_server` with an issuer set, including remote-backed servers (previously 404). The `/oauth/proxy-register` DCR helper now also registers `<server>/x/mcp/remote_login_callback` so remote-OAuth `mcp_servers` reached via `/x/mcp/{slug}/connect` can complete the upstream callback against the same upstream client registration.
12 changes: 12 additions & 0 deletions .speakeasy/out.openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28086,6 +28086,10 @@ components:
type: string
description: The ID of the toolset to use as the backend
format: uuid
user_session_issuer_id:
type: string
description: The ID of the user session issuer that gates OAuth-based MCP client authentication. When set, MCP clients are required to authenticate against this issuer before connecting.
format: uuid
visibility:
type: string
description: The visibility of an MCP server
Expand Down Expand Up @@ -32088,6 +32092,10 @@ components:
type: string
description: When the MCP server was last updated
format: date-time
user_session_issuer_id:
type: string
description: The ID of the user session issuer that gates OAuth-based MCP client authentication for this server, if any.
format: uuid
visibility:
type: string
description: The visibility of an MCP server
Expand Down Expand Up @@ -36394,6 +36402,10 @@ components:
type: string
description: The ID of the toolset to use as the backend
format: uuid
user_session_issuer_id:
type: string
description: The ID of the user session issuer that gates OAuth-based MCP client authentication. Omit to disable issuer-gated OAuth.
format: uuid
visibility:
type: string
description: The visibility of an MCP server
Expand Down
14 changes: 7 additions & 7 deletions client/sdk/.speakeasy/gen.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions client/sdk/src/models/components/createmcpserverform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ export type CreateMcpServerForm = {
* The ID of the toolset to use as the backend
*/
toolsetId?: string | undefined;
/**
* The ID of the user session issuer that gates OAuth-based MCP client authentication. When set, MCP clients are required to authenticate against this issuer before connecting.
*/
userSessionIssuerId?: string | undefined;
/**
* The visibility of an MCP server
*/
Expand All @@ -58,6 +62,7 @@ export type CreateMcpServerForm$Outbound = {
name: string;
remote_mcp_server_id?: string | undefined;
toolset_id?: string | undefined;
user_session_issuer_id?: string | undefined;
visibility: string;
};

Expand All @@ -71,13 +76,15 @@ export const CreateMcpServerForm$outboundSchema: z.ZodMiniType<
name: z.string(),
remoteMcpServerId: z.optional(z.string()),
toolsetId: z.optional(z.string()),
userSessionIssuerId: z.optional(z.string()),
visibility: CreateMcpServerFormVisibility$outboundSchema,
}),
z.transform((v) => {
return remap$(v, {
environmentId: "environment_id",
remoteMcpServerId: "remote_mcp_server_id",
toolsetId: "toolset_id",
userSessionIssuerId: "user_session_issuer_id",
});
}),
);
Expand Down
6 changes: 6 additions & 0 deletions client/sdk/src/models/components/mcpserver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ export type McpServer = {
* When the MCP server was last updated
*/
updatedAt: Date;
/**
* The ID of the user session issuer that gates OAuth-based MCP client authentication for this server, if any.
*/
userSessionIssuerId?: string | undefined;
/**
* The visibility of an MCP server
*/
Expand Down Expand Up @@ -92,6 +96,7 @@ export const McpServer$inboundSchema: z.ZodMiniType<McpServer, unknown> = z
z.iso.datetime({ offset: true }),
z.transform(v => new Date(v)),
),
user_session_issuer_id: z.optional(z.string()),
visibility: McpServerVisibility$inboundSchema,
}),
z.transform((v) => {
Expand All @@ -102,6 +107,7 @@ export const McpServer$inboundSchema: z.ZodMiniType<McpServer, unknown> = z
"remote_mcp_server_id": "remoteMcpServerId",
"toolset_id": "toolsetId",
"updated_at": "updatedAt",
"user_session_issuer_id": "userSessionIssuerId",
});
}),
);
Expand Down
7 changes: 7 additions & 0 deletions client/sdk/src/models/components/updatemcpserverform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ export type UpdateMcpServerForm = {
* The ID of the toolset to use as the backend
*/
toolsetId?: string | undefined;
/**
* The ID of the user session issuer that gates OAuth-based MCP client authentication. Omit to disable issuer-gated OAuth.
*/
userSessionIssuerId?: string | undefined;
/**
* The visibility of an MCP server
*/
Expand All @@ -63,6 +67,7 @@ export type UpdateMcpServerForm$Outbound = {
name?: string | undefined;
remote_mcp_server_id?: string | undefined;
toolset_id?: string | undefined;
user_session_issuer_id?: string | undefined;
visibility: string;
};

Expand All @@ -77,13 +82,15 @@ export const UpdateMcpServerForm$outboundSchema: z.ZodMiniType<
name: z.optional(z.string()),
remoteMcpServerId: z.optional(z.string()),
toolsetId: z.optional(z.string()),
userSessionIssuerId: z.optional(z.string()),
visibility: UpdateMcpServerFormVisibility$outboundSchema,
}),
z.transform((v) => {
return remap$(v, {
environmentId: "environment_id",
remoteMcpServerId: "remote_mcp_server_id",
toolsetId: "toolset_id",
userSessionIssuerId: "user_session_issuer_id",
});
}),
);
Expand Down
9 changes: 9 additions & 0 deletions server/design/mcpservers/design.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,9 @@ var CreateMcpServerForm = Type("CreateMcpServerForm", func() {
Attribute("environment_id", String, "The ID of the environment to associate with the server", func() {
Format(FormatUUID)
})
Attribute("user_session_issuer_id", String, "The ID of the user session issuer that gates OAuth-based MCP client authentication. When set, MCP clients are required to authenticate against this issuer before connecting.", func() {
Format(FormatUUID)
})
Attribute("remote_mcp_server_id", String, "The ID of the remote MCP server to use as the backend", func() {
Format(FormatUUID)
})
Expand All @@ -189,6 +192,9 @@ var UpdateMcpServerForm = Type("UpdateMcpServerForm", func() {
Attribute("environment_id", String, "The ID of the environment to associate with the server", func() {
Format(FormatUUID)
})
Attribute("user_session_issuer_id", String, "The ID of the user session issuer that gates OAuth-based MCP client authentication. Omit to disable issuer-gated OAuth.", func() {
Format(FormatUUID)
})
Attribute("remote_mcp_server_id", String, "The ID of the remote MCP server to use as the backend", func() {
Format(FormatUUID)
})
Expand Down Expand Up @@ -216,6 +222,9 @@ var McpServer = Type("McpServer", func() {
Attribute("environment_id", String, "The ID of the environment associated with the server", func() {
Format(FormatUUID)
})
Attribute("user_session_issuer_id", String, "The ID of the user session issuer that gates OAuth-based MCP client authentication for this server, if any.", func() {
Format(FormatUUID)
})
Attribute("remote_mcp_server_id", String, "The ID of the remote MCP server used as the backend", func() {
Format(FormatUUID)
})
Expand Down
4 changes: 2 additions & 2 deletions server/gen/http/cli/gram/cli.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 21 additions & 13 deletions server/gen/http/mcp_servers/client/cli.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 11 additions & 10 deletions server/gen/http/mcp_servers/client/encode_decode.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading