mirror of
https://github.com/gravitational/teleport.git
synced 2026-10-12 02:15:01 +00:00
[mcp] report transport type and egress auth type to prehog (#61397)
* [mcp] report transport type and egress auth type to prehog * report unknown egress type and add more comments * make grpc * fix tests ( ̄▽ ̄;)
This commit is contained in:
@@ -8687,6 +8687,14 @@ message MCPSessionStart {
|
||||
string mcp_session_id = 7;
|
||||
// ClientInfo stores reported client agent information, e.g. "claude-ai/0.1.0".
|
||||
string client_info = 8;
|
||||
// ServerInfo stores reported MCP server information, e.g. "teleport-mcp-demo/18.3.0".
|
||||
string server_info = 9;
|
||||
// ProtocolVersion is the MCP protocol version like "2025-06-18".
|
||||
string protocol_version = 10;
|
||||
// IngressAuthType indicates how the MCP session is authorized between MCP client and Teleport.
|
||||
string ingress_auth_type = 11;
|
||||
// EgressAuthType indicates how the MCP session is authorized between Teleport and remote MCP server.
|
||||
string egress_auth_type = 12;
|
||||
}
|
||||
|
||||
// MCPSessionEnd is emitted when an MCP session ends.
|
||||
|
||||
+1485
-1301
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+145
-1
@@ -293,6 +293,13 @@ export interface SessionStartEvent {
|
||||
* @generated from protobuf field: prehog.v1alpha.SessionStartGitMetadata git = 7;
|
||||
*/
|
||||
git?: SessionStartGitMetadata;
|
||||
/**
|
||||
* if session_type == "app_mcp" the mcp struct contains additional information
|
||||
* about the MCP session.
|
||||
*
|
||||
* @generated from protobuf field: prehog.v1alpha.SessionStartMCPMetadata mcp = 8;
|
||||
*/
|
||||
mcp?: SessionStartMCPMetadata;
|
||||
}
|
||||
/**
|
||||
* SessionStartDatabaseMetadata contains additional information about database session.
|
||||
@@ -402,6 +409,49 @@ export interface SessionStartGitMetadata {
|
||||
*/
|
||||
gitService: string;
|
||||
}
|
||||
/**
|
||||
* SessionStartMCPMetadata contains additional information about an MCP session.
|
||||
*
|
||||
* @generated from protobuf message prehog.v1alpha.SessionStartMCPMetadata
|
||||
*/
|
||||
export interface SessionStartMCPMetadata {
|
||||
/**
|
||||
* Transport type of the MCP server. Value from types.GetMCPServerTransportType.
|
||||
*
|
||||
* @generated from protobuf field: string transport = 1;
|
||||
*/
|
||||
transport: string;
|
||||
/**
|
||||
* MCP protocol version like "2025-06-18".
|
||||
*
|
||||
* @generated from protobuf field: string protocol_version = 2;
|
||||
*/
|
||||
protocolVersion: string;
|
||||
/**
|
||||
* Indicates the client used on the session, e.g "claude-ai".
|
||||
*
|
||||
* @generated from protobuf field: string client_name = 3;
|
||||
*/
|
||||
clientName: string;
|
||||
/**
|
||||
* Indicates the server used on the session, e.g "example-servers/everything".
|
||||
*
|
||||
* @generated from protobuf field: string server_name = 4;
|
||||
*/
|
||||
serverName: string;
|
||||
/**
|
||||
* Indicates how the MCP session is authorized between MCP client and Teleport.
|
||||
*
|
||||
* @generated from protobuf field: string ingress_auth_type = 5;
|
||||
*/
|
||||
ingressAuthType: string;
|
||||
/**
|
||||
* Indicates how the MCP session is authorized between Teleport and remote MCP server.
|
||||
*
|
||||
* @generated from protobuf field: string egress_auth_type = 6;
|
||||
*/
|
||||
egressAuthType: string;
|
||||
}
|
||||
/**
|
||||
* the issuance of a user certificate from the user CA
|
||||
*
|
||||
@@ -4669,7 +4719,8 @@ class SessionStartEvent$Type extends MessageType<SessionStartEvent> {
|
||||
{ no: 4, name: "desktop", kind: "message", T: () => SessionStartDesktopMetadata },
|
||||
{ no: 5, name: "user_kind", kind: "enum", T: () => ["prehog.v1alpha.UserKind", UserKind, "USER_KIND_"] },
|
||||
{ no: 6, name: "app", kind: "message", T: () => SessionStartAppMetadata },
|
||||
{ no: 7, name: "git", kind: "message", T: () => SessionStartGitMetadata }
|
||||
{ no: 7, name: "git", kind: "message", T: () => SessionStartGitMetadata },
|
||||
{ no: 8, name: "mcp", kind: "message", T: () => SessionStartMCPMetadata }
|
||||
]);
|
||||
}
|
||||
create(value?: PartialMessage<SessionStartEvent>): SessionStartEvent {
|
||||
@@ -4707,6 +4758,9 @@ class SessionStartEvent$Type extends MessageType<SessionStartEvent> {
|
||||
case /* prehog.v1alpha.SessionStartGitMetadata git */ 7:
|
||||
message.git = SessionStartGitMetadata.internalBinaryRead(reader, reader.uint32(), options, message.git);
|
||||
break;
|
||||
case /* prehog.v1alpha.SessionStartMCPMetadata mcp */ 8:
|
||||
message.mcp = SessionStartMCPMetadata.internalBinaryRead(reader, reader.uint32(), options, message.mcp);
|
||||
break;
|
||||
default:
|
||||
let u = options.readUnknownField;
|
||||
if (u === "throw")
|
||||
@@ -4740,6 +4794,9 @@ class SessionStartEvent$Type extends MessageType<SessionStartEvent> {
|
||||
/* prehog.v1alpha.SessionStartGitMetadata git = 7; */
|
||||
if (message.git)
|
||||
SessionStartGitMetadata.internalBinaryWrite(message.git, writer.tag(7, WireType.LengthDelimited).fork(), options).join();
|
||||
/* prehog.v1alpha.SessionStartMCPMetadata mcp = 8; */
|
||||
if (message.mcp)
|
||||
SessionStartMCPMetadata.internalBinaryWrite(message.mcp, writer.tag(8, WireType.LengthDelimited).fork(), options).join();
|
||||
let u = options.writeUnknownFields;
|
||||
if (u !== false)
|
||||
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
|
||||
@@ -5003,6 +5060,93 @@ class SessionStartGitMetadata$Type extends MessageType<SessionStartGitMetadata>
|
||||
*/
|
||||
export const SessionStartGitMetadata = new SessionStartGitMetadata$Type();
|
||||
// @generated message type with reflection information, may provide speed optimized methods
|
||||
class SessionStartMCPMetadata$Type extends MessageType<SessionStartMCPMetadata> {
|
||||
constructor() {
|
||||
super("prehog.v1alpha.SessionStartMCPMetadata", [
|
||||
{ no: 1, name: "transport", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
|
||||
{ no: 2, name: "protocol_version", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
|
||||
{ no: 3, name: "client_name", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
|
||||
{ no: 4, name: "server_name", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
|
||||
{ no: 5, name: "ingress_auth_type", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
|
||||
{ no: 6, name: "egress_auth_type", kind: "scalar", T: 9 /*ScalarType.STRING*/ }
|
||||
]);
|
||||
}
|
||||
create(value?: PartialMessage<SessionStartMCPMetadata>): SessionStartMCPMetadata {
|
||||
const message = globalThis.Object.create((this.messagePrototype!));
|
||||
message.transport = "";
|
||||
message.protocolVersion = "";
|
||||
message.clientName = "";
|
||||
message.serverName = "";
|
||||
message.ingressAuthType = "";
|
||||
message.egressAuthType = "";
|
||||
if (value !== undefined)
|
||||
reflectionMergePartial<SessionStartMCPMetadata>(this, message, value);
|
||||
return message;
|
||||
}
|
||||
internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: SessionStartMCPMetadata): SessionStartMCPMetadata {
|
||||
let message = target ?? this.create(), end = reader.pos + length;
|
||||
while (reader.pos < end) {
|
||||
let [fieldNo, wireType] = reader.tag();
|
||||
switch (fieldNo) {
|
||||
case /* string transport */ 1:
|
||||
message.transport = reader.string();
|
||||
break;
|
||||
case /* string protocol_version */ 2:
|
||||
message.protocolVersion = reader.string();
|
||||
break;
|
||||
case /* string client_name */ 3:
|
||||
message.clientName = reader.string();
|
||||
break;
|
||||
case /* string server_name */ 4:
|
||||
message.serverName = reader.string();
|
||||
break;
|
||||
case /* string ingress_auth_type */ 5:
|
||||
message.ingressAuthType = reader.string();
|
||||
break;
|
||||
case /* string egress_auth_type */ 6:
|
||||
message.egressAuthType = reader.string();
|
||||
break;
|
||||
default:
|
||||
let u = options.readUnknownField;
|
||||
if (u === "throw")
|
||||
throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
|
||||
let d = reader.skip(wireType);
|
||||
if (u !== false)
|
||||
(u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
internalBinaryWrite(message: SessionStartMCPMetadata, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
|
||||
/* string transport = 1; */
|
||||
if (message.transport !== "")
|
||||
writer.tag(1, WireType.LengthDelimited).string(message.transport);
|
||||
/* string protocol_version = 2; */
|
||||
if (message.protocolVersion !== "")
|
||||
writer.tag(2, WireType.LengthDelimited).string(message.protocolVersion);
|
||||
/* string client_name = 3; */
|
||||
if (message.clientName !== "")
|
||||
writer.tag(3, WireType.LengthDelimited).string(message.clientName);
|
||||
/* string server_name = 4; */
|
||||
if (message.serverName !== "")
|
||||
writer.tag(4, WireType.LengthDelimited).string(message.serverName);
|
||||
/* string ingress_auth_type = 5; */
|
||||
if (message.ingressAuthType !== "")
|
||||
writer.tag(5, WireType.LengthDelimited).string(message.ingressAuthType);
|
||||
/* string egress_auth_type = 6; */
|
||||
if (message.egressAuthType !== "")
|
||||
writer.tag(6, WireType.LengthDelimited).string(message.egressAuthType);
|
||||
let u = options.writeUnknownFields;
|
||||
if (u !== false)
|
||||
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
|
||||
return writer;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @generated MessageType for protobuf message prehog.v1alpha.SessionStartMCPMetadata
|
||||
*/
|
||||
export const SessionStartMCPMetadata = new SessionStartMCPMetadata$Type();
|
||||
// @generated message type with reflection information, may provide speed optimized methods
|
||||
class UserCertificateIssuedEvent$Type extends MessageType<UserCertificateIssuedEvent> {
|
||||
constructor() {
|
||||
super("prehog.v1alpha.UserCertificateIssuedEvent", [
|
||||
|
||||
+51
-3
@@ -22,15 +22,18 @@ import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
|
||||
"github.com/gravitational/teleport"
|
||||
apidefaults "github.com/gravitational/teleport/api/defaults"
|
||||
"github.com/gravitational/teleport/api/types"
|
||||
apievents "github.com/gravitational/teleport/api/types/events"
|
||||
"github.com/gravitational/teleport/api/types/wrappers"
|
||||
"github.com/gravitational/teleport/lib/events"
|
||||
"github.com/gravitational/teleport/lib/services"
|
||||
appcommon "github.com/gravitational/teleport/lib/srv/app/common"
|
||||
"github.com/gravitational/teleport/lib/utils"
|
||||
"github.com/gravitational/teleport/lib/utils/mcputils"
|
||||
@@ -104,9 +107,9 @@ func eventWithHTTPResponseError(resp *http.Response, err error) eventOptionFunc
|
||||
return eventWithError(convertHTTPResponseErrorForAudit(resp, err))
|
||||
}
|
||||
|
||||
func eventWithHeader(r *http.Request) eventOptionFunc {
|
||||
func eventWithHeader(header http.Header) eventOptionFunc {
|
||||
return func(o *eventOptions) {
|
||||
o.header = headersForAudit(r.Header)
|
||||
o.header = headersForAudit(header)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +127,9 @@ func (a *sessionAuditor) shouldEmitEvent(method mcp.MCPMethod) bool {
|
||||
}
|
||||
}
|
||||
|
||||
func (a *sessionAuditor) emitStartEvent(ctx context.Context) {
|
||||
func (a *sessionAuditor) emitStartEvent(ctx context.Context, options ...eventOptionFunc) {
|
||||
opts := newEventOptions(options...)
|
||||
|
||||
a.emitEvent(ctx, &apievents.MCPSessionStart{
|
||||
Metadata: a.makeEventMetadata(
|
||||
events.MCPSessionStartEvent,
|
||||
@@ -136,6 +141,7 @@ func (a *sessionAuditor) emitStartEvent(ctx context.Context) {
|
||||
ConnectionMetadata: a.makeConnectionMetadata(),
|
||||
AppMetadata: a.makeAppMetadata(),
|
||||
McpSessionId: a.sessionCtx.mcpSessionID.String(),
|
||||
EgressAuthType: guessEgressAuthType(opts.header, a.sessionCtx.App.GetRewrite()),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -352,3 +358,45 @@ func headersForAudit(h http.Header) http.Header {
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// guessEgressAuthType makes an educated guess on what kind of auth is used to
|
||||
// for the remote MCP server.
|
||||
func guessEgressAuthType(headerWithoutRewrite http.Header, rewrite *types.Rewrite) string {
|
||||
if rewrite != nil {
|
||||
testJWTTraits := map[string][]string{
|
||||
"jwt": {"test", "jwt"},
|
||||
}
|
||||
|
||||
var rewriteAuth bool
|
||||
for _, rewrite := range rewrite.Headers {
|
||||
if strings.EqualFold(rewrite.Name, "Authorization") {
|
||||
rewriteAuth = true
|
||||
}
|
||||
|
||||
// Check if any header value includes "{{internal.jwt}}".
|
||||
if strings.Contains(rewrite.Value, "internal.jwt") {
|
||||
// Apply fake traits just to be sure. The fake traits will
|
||||
// result two values if applied successfully.
|
||||
if interpolated, _ := services.ApplyValueTraits(rewrite.Value, testJWTTraits); len(interpolated) > 1 {
|
||||
return "app-jwt"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Auth header has be defined in the app definition but not using
|
||||
// "{{internal.jwt}}".
|
||||
if rewriteAuth {
|
||||
return "app-defined"
|
||||
}
|
||||
}
|
||||
|
||||
// Reach here when app.Rewrite not overwriting auth. Check if Auth header is
|
||||
// defined by the user.
|
||||
if headerWithoutRewrite.Get("Authorization") != "" {
|
||||
return "user-defined"
|
||||
}
|
||||
|
||||
// No auth required for the remote MCP server or something we don't
|
||||
// understand yet.
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/gravitational/teleport/api/types"
|
||||
)
|
||||
|
||||
func Test_headersForAudit(t *testing.T) {
|
||||
@@ -68,3 +70,55 @@ func Test_headersForAudit(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_guessEgressAuthType(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
inputHeader http.Header
|
||||
inputRewrite *types.Rewrite
|
||||
expect string
|
||||
}{
|
||||
{
|
||||
name: "no header or rewrite",
|
||||
expect: "unknown",
|
||||
},
|
||||
{
|
||||
name: "user defined",
|
||||
inputHeader: http.Header{
|
||||
"Authorization": []string{"Bearer secret"},
|
||||
},
|
||||
inputRewrite: &types.Rewrite{
|
||||
Headers: []*types.Header{{
|
||||
Name: "Some-Other-Header",
|
||||
Value: "some-other-value",
|
||||
}},
|
||||
},
|
||||
expect: "user-defined",
|
||||
},
|
||||
{
|
||||
name: "app defined",
|
||||
inputRewrite: &types.Rewrite{
|
||||
Headers: []*types.Header{{
|
||||
Name: "Authorization",
|
||||
Value: "Bearer abcdef",
|
||||
}},
|
||||
},
|
||||
expect: "app-defined",
|
||||
},
|
||||
{
|
||||
name: "app defined with Teleport JWT",
|
||||
inputRewrite: &types.Rewrite{
|
||||
Headers: []*types.Header{{
|
||||
Name: "Authorization",
|
||||
Value: "Bearer {{internal.jwt}}",
|
||||
}},
|
||||
},
|
||||
expect: "app-jwt",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
require.Equal(t, tt.expect, guessEgressAuthType(tt.inputHeader, tt.inputRewrite))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+6
-6
@@ -193,13 +193,13 @@ func (t *streamableHTTPTransport) rewriteAndSendRequest(r *http.Request) (*http.
|
||||
|
||||
func (t *streamableHTTPTransport) handleSessionEndRequest(r *http.Request) (*http.Response, error) {
|
||||
resp, err := t.rewriteAndSendRequest(r)
|
||||
t.emitEndEvent(t.parentCtx, eventWithHTTPResponseError(resp, err), eventWithHeader(r))
|
||||
t.emitEndEvent(t.parentCtx, eventWithHTTPResponseError(resp, err), eventWithHeader(r.Header))
|
||||
return resp, trace.Wrap(err)
|
||||
}
|
||||
|
||||
func (t *streamableHTTPTransport) handleListenSSEStreamRequest(r *http.Request) (*http.Response, error) {
|
||||
resp, err := t.rewriteAndSendRequest(r)
|
||||
t.emitListenSSEStreamEvent(t.parentCtx, eventWithHTTPResponseError(resp, err), eventWithHeader(r))
|
||||
t.emitListenSSEStreamEvent(t.parentCtx, eventWithHTTPResponseError(resp, err), eventWithHeader(r.Header))
|
||||
return resp, trace.Wrap(err)
|
||||
}
|
||||
|
||||
@@ -242,17 +242,17 @@ func (t *streamableHTTPTransport) handleMCPMessage(r *http.Request) (*http.Respo
|
||||
mcpRequest := baseMessage.MakeRequest()
|
||||
// Only emit session start if "initialize" succeeded.
|
||||
if mcpRequest.Method == mcp.MethodInitialize && respErrForAudit == nil {
|
||||
t.emitStartEvent(t.parentCtx)
|
||||
t.emitStartEvent(t.parentCtx, eventWithHeader(r.Header))
|
||||
}
|
||||
t.emitRequestEvent(t.parentCtx, mcpRequest, eventWithError(respErrForAudit), eventWithHeader(r))
|
||||
t.emitRequestEvent(t.parentCtx, mcpRequest, eventWithError(respErrForAudit), eventWithHeader(r.Header))
|
||||
case baseMessage.IsNotification():
|
||||
t.emitNotificationEvent(t.parentCtx, baseMessage.MakeNotification(), eventWithError(respErrForAudit), eventWithHeader(r))
|
||||
t.emitNotificationEvent(t.parentCtx, baseMessage.MakeNotification(), eventWithError(respErrForAudit), eventWithHeader(r.Header))
|
||||
}
|
||||
return resp, trace.Wrap(err)
|
||||
}
|
||||
|
||||
func (t *streamableHTTPTransport) handleRequestAuthError(r *http.Request, mcpRequest *mcputils.JSONRPCRequest, errResp mcp.JSONRPCMessage, authErr error) (*http.Response, error) {
|
||||
t.emitRequestEvent(t.parentCtx, mcpRequest, eventWithError(authErr), eventWithHeader(r))
|
||||
t.emitRequestEvent(t.parentCtx, mcpRequest, eventWithError(authErr), eventWithHeader(r.Header))
|
||||
|
||||
errRespAsBody, err := json.Marshal(errResp)
|
||||
if err != nil {
|
||||
|
||||
@@ -140,6 +140,11 @@ func Test_handleStreamableHTTP(t *testing.T) {
|
||||
}, sliceutils.Map(emitter.Events(), getEventCode))
|
||||
}, 2*time.Second, time.Millisecond*100, "waiting for events")
|
||||
|
||||
// First event must be the start event.
|
||||
startEvent, ok := emitter.Events()[0].(*apievents.MCPSessionStart)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, "app-jwt", startEvent.EgressAuthType)
|
||||
|
||||
// Close client and wait for end event.
|
||||
require.NoError(t, client.Close())
|
||||
require.EventuallyWithT(t, func(t *assert.CollectT) {
|
||||
|
||||
@@ -379,6 +379,14 @@ func ConvertAuditEvent(event apievents.AuditEvent) Anonymizable {
|
||||
UserName: e.User,
|
||||
SessionType: MCPAppSessionType,
|
||||
UserKind: prehogUserKindFromEventKind(e.UserKind),
|
||||
Mcp: &prehogv1a.SessionStartMCPMetadata{
|
||||
Transport: types.GetMCPServerTransportType(e.AppURI),
|
||||
ProtocolVersion: e.ProtocolVersion,
|
||||
IngressAuthType: e.IngressAuthType,
|
||||
EgressAuthType: e.EgressAuthType,
|
||||
ClientName: e.ClientInfo,
|
||||
ServerName: e.ServerInfo,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -283,11 +283,25 @@ func TestConvertAuditEvent(t *testing.T) {
|
||||
UserMetadata: apievents.UserMetadata{User: "alice"},
|
||||
AppMetadata: apievents.AppMetadata{
|
||||
AppName: "mcp-everything",
|
||||
AppURI: "mcp+http://localhost:12345/mcp",
|
||||
},
|
||||
IngressAuthType: "ingress-auth",
|
||||
EgressAuthType: "egress-auth",
|
||||
ClientInfo: "client/1.0.0",
|
||||
ServerInfo: "server/1.0.0",
|
||||
ProtocolVersion: "protocol-version",
|
||||
},
|
||||
expected: &SessionStartEvent{
|
||||
SessionType: MCPAppSessionType,
|
||||
UserName: "alice",
|
||||
Mcp: &prehogv1a.SessionStartMCPMetadata{
|
||||
Transport: "Streamable HTTP",
|
||||
ProtocolVersion: "protocol-version",
|
||||
ClientName: "client/1.0.0",
|
||||
ServerName: "server/1.0.0",
|
||||
IngressAuthType: "ingress-auth",
|
||||
EgressAuthType: "egress-auth",
|
||||
},
|
||||
},
|
||||
expectedAnonymized: &prehogv1a.SubmitEventRequest{
|
||||
Event: &prehogv1a.SubmitEventRequest_SessionStartV2{
|
||||
|
||||
@@ -254,6 +254,10 @@ message SessionStartEvent {
|
||||
// if session_type == "git" the git struct contains additional information
|
||||
// about git session.
|
||||
SessionStartGitMetadata git = 7;
|
||||
|
||||
// if session_type == "app_mcp" the mcp struct contains additional information
|
||||
// about the MCP session.
|
||||
SessionStartMCPMetadata mcp = 8;
|
||||
}
|
||||
|
||||
// SessionStartDatabaseMetadata contains additional information about database session.
|
||||
@@ -308,6 +312,22 @@ message SessionStartGitMetadata {
|
||||
string git_service = 2;
|
||||
}
|
||||
|
||||
// SessionStartMCPMetadata contains additional information about an MCP session.
|
||||
message SessionStartMCPMetadata {
|
||||
// Transport type of the MCP server. Value from types.GetMCPServerTransportType.
|
||||
string transport = 1;
|
||||
// MCP protocol version like "2025-06-18".
|
||||
string protocol_version = 2;
|
||||
// Indicates the client used on the session, e.g "claude-ai".
|
||||
string client_name = 3;
|
||||
// Indicates the server used on the session, e.g "example-servers/everything".
|
||||
string server_name = 4;
|
||||
// Indicates how the MCP session is authorized between MCP client and Teleport.
|
||||
string ingress_auth_type = 5;
|
||||
// Indicates how the MCP session is authorized between Teleport and remote MCP server.
|
||||
string egress_auth_type = 6;
|
||||
}
|
||||
|
||||
// the issuance of a user certificate from the user CA
|
||||
//
|
||||
// PostHog event: tp.certificate.issued
|
||||
|
||||
Reference in New Issue
Block a user