mirror of
https://github.com/gravitational/teleport.git
synced 2026-09-24 16:17:11 +08:00
[mcp] update audit events for streamable HTTP transport (#59155)
* [mcp] update audit events for streamable HTTP transport * nolint for unused functions for now, they will be used in next PR
This commit is contained in:
@@ -4885,6 +4885,8 @@ message OneOf {
|
||||
events.BoundKeypairRecovery BoundKeypairRecovery = 220;
|
||||
events.BoundKeypairRotation BoundKeypairRotation = 221;
|
||||
events.BoundKeypairJoinStateVerificationFailed BoundKeypairJoinStateVerificationFailed = 222;
|
||||
events.MCPSessionListenSSEStream MCPSessionListenSSEStream = 223;
|
||||
events.MCPSessionInvalidHTTPRequest MCPSessionInvalidHTTPRequest = 224;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8714,6 +8716,16 @@ message MCPSessionEnd {
|
||||
(gogoproto.embed) = true,
|
||||
(gogoproto.jsontag) = ""
|
||||
];
|
||||
// Status contains common command or operation status fields.
|
||||
//
|
||||
// The protocol spec states that the MCP server may refuse the clients from
|
||||
// terminating the session.
|
||||
// https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#session-management
|
||||
Status Status = 7 [
|
||||
(gogoproto.nullable) = false,
|
||||
(gogoproto.embed) = true,
|
||||
(gogoproto.jsontag) = ""
|
||||
];
|
||||
}
|
||||
|
||||
// MCPJSONRPCMessage includes details of a MCP request or notification.
|
||||
@@ -8803,6 +8815,92 @@ message MCPSessionNotification {
|
||||
(gogoproto.nullable) = false,
|
||||
(gogoproto.jsontag) = "message,omitempty"
|
||||
];
|
||||
// Status contains information whether the request is successful or not.
|
||||
Status status = 6 [
|
||||
(gogoproto.nullable) = false,
|
||||
(gogoproto.embed) = true,
|
||||
(gogoproto.jsontag) = ""
|
||||
];
|
||||
}
|
||||
|
||||
// MCPSessionListenSSEStream is emitted when client sends a GET request to a
|
||||
// streamable HTTP MCP server for listening server notifications via a SSE
|
||||
// stream.
|
||||
message MCPSessionListenSSEStream {
|
||||
// Metadata is a common event metadata
|
||||
Metadata metadata = 1 [
|
||||
(gogoproto.nullable) = false,
|
||||
(gogoproto.embed) = true,
|
||||
(gogoproto.jsontag) = ""
|
||||
];
|
||||
// User is a common user event metadata
|
||||
UserMetadata user = 2 [
|
||||
(gogoproto.nullable) = false,
|
||||
(gogoproto.embed) = true,
|
||||
(gogoproto.jsontag) = ""
|
||||
];
|
||||
// SessionMetadata is a common event session metadata
|
||||
SessionMetadata session = 3 [
|
||||
(gogoproto.nullable) = false,
|
||||
(gogoproto.embed) = true,
|
||||
(gogoproto.jsontag) = ""
|
||||
];
|
||||
// App is a common application resource metadata.
|
||||
AppMetadata app = 4 [
|
||||
(gogoproto.nullable) = false,
|
||||
(gogoproto.embed) = true,
|
||||
(gogoproto.jsontag) = ""
|
||||
];
|
||||
// Status contains information whether the request is successful or not.
|
||||
Status status = 5 [
|
||||
(gogoproto.nullable) = false,
|
||||
(gogoproto.embed) = true,
|
||||
(gogoproto.jsontag) = ""
|
||||
];
|
||||
}
|
||||
|
||||
// MCPSessionInvalidHTTPRequest is a blanket event for all requests that we do
|
||||
// not understand (usually out of MCP spec).
|
||||
message MCPSessionInvalidHTTPRequest {
|
||||
// Metadata is a common event metadata
|
||||
Metadata metadata = 1 [
|
||||
(gogoproto.nullable) = false,
|
||||
(gogoproto.embed) = true,
|
||||
(gogoproto.jsontag) = ""
|
||||
];
|
||||
// User is a common user event metadata
|
||||
UserMetadata user = 2 [
|
||||
(gogoproto.nullable) = false,
|
||||
(gogoproto.embed) = true,
|
||||
(gogoproto.jsontag) = ""
|
||||
];
|
||||
// SessionMetadata is a common event session metadata
|
||||
SessionMetadata session = 3 [
|
||||
(gogoproto.nullable) = false,
|
||||
(gogoproto.embed) = true,
|
||||
(gogoproto.jsontag) = ""
|
||||
];
|
||||
// App is a common application resource metadata.
|
||||
AppMetadata app = 4 [
|
||||
(gogoproto.nullable) = false,
|
||||
(gogoproto.embed) = true,
|
||||
(gogoproto.jsontag) = ""
|
||||
];
|
||||
|
||||
// Path is relative path in the URL.
|
||||
string path = 5 [(gogoproto.jsontag) = "path"];
|
||||
// Method is the request HTTP method, like GET/POST/DELETE/etc.
|
||||
string method = 6 [(gogoproto.jsontag) = "method"];
|
||||
// RawQuery are the encoded query values.
|
||||
string raw_query = 7 [(gogoproto.jsontag) = "raw_query"];
|
||||
// Body is the request HTTP body.
|
||||
bytes body = 8 [(gogoproto.jsontag) = "body"];
|
||||
// Headers are the HTTP request headers.
|
||||
wrappers.LabelValues headers = 9 [
|
||||
(gogoproto.nullable) = false,
|
||||
(gogoproto.jsontag) = "headers,omitempty",
|
||||
(gogoproto.customtype) = "github.com/gravitational/teleport/api/types/wrappers.Traits"
|
||||
];
|
||||
}
|
||||
|
||||
// BoundKeypairRecovery is emitted when a client performs a self recovery using
|
||||
|
||||
@@ -2601,6 +2601,37 @@ func (m *MCPSessionNotification) TrimToMaxSize(maxSize int) AuditEvent {
|
||||
return out
|
||||
}
|
||||
|
||||
func (m *MCPSessionListenSSEStream) TrimToMaxSize(maxSize int) AuditEvent {
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *MCPSessionInvalidHTTPRequest) TrimToMaxSize(maxSize int) AuditEvent {
|
||||
size := m.Size()
|
||||
if size <= maxSize {
|
||||
return m
|
||||
}
|
||||
|
||||
out := utils.CloneProtoMsg(m)
|
||||
out.Path = ""
|
||||
out.RawQuery = ""
|
||||
out.Headers = nil
|
||||
out.Body = nil
|
||||
|
||||
maxSize = adjustedMaxSize(out, maxSize)
|
||||
|
||||
customFieldsCount := nonEmptyStrs(m.Path, m.RawQuery) + nonEmptyTraits(m.Headers)
|
||||
if len(m.Body) != 0 {
|
||||
customFieldsCount++
|
||||
}
|
||||
maxFieldsSize := maxSizePerField(maxSize, customFieldsCount)
|
||||
|
||||
out.Path = trimStr(m.Path, maxFieldsSize)
|
||||
out.RawQuery = trimStr(m.RawQuery, maxFieldsSize)
|
||||
out.Body = []byte(trimStr(string(m.Body), maxFieldsSize))
|
||||
out.Headers = trimTraits(m.Headers, maxFieldsSize)
|
||||
return out
|
||||
}
|
||||
|
||||
func (m *BoundKeypairRecovery) TrimToMaxSize(maxSize int) AuditEvent {
|
||||
size := m.Size()
|
||||
if size <= maxSize {
|
||||
|
||||
+2478
-1284
File diff suppressed because it is too large
Load Diff
@@ -902,6 +902,14 @@ func ToOneOf(in AuditEvent) (*OneOf, error) {
|
||||
out.Event = &OneOf_MCPSessionNotification{
|
||||
MCPSessionNotification: e,
|
||||
}
|
||||
case *MCPSessionListenSSEStream:
|
||||
out.Event = &OneOf_MCPSessionListenSSEStream{
|
||||
MCPSessionListenSSEStream: e,
|
||||
}
|
||||
case *MCPSessionInvalidHTTPRequest:
|
||||
out.Event = &OneOf_MCPSessionInvalidHTTPRequest{
|
||||
MCPSessionInvalidHTTPRequest: e,
|
||||
}
|
||||
case *BoundKeypairRecovery:
|
||||
out.Event = &OneOf_BoundKeypairRecovery{
|
||||
BoundKeypairRecovery: e,
|
||||
|
||||
@@ -927,6 +927,12 @@ const (
|
||||
// MCPSessionNotificationEvent is emitted when a notification is sent by
|
||||
// client during a MCP session.
|
||||
MCPSessionNotificationEvent = "mcp.session.notification"
|
||||
// MCPSessionListenSSEStream is emitted when the client sends a GET request for
|
||||
// listening server notifications via an SSE stream.
|
||||
MCPSessionListenSSEStream = "mcp.session.listen_sse_stream"
|
||||
// MCPSessionInvalidHTTPRequest is a blanket event for all requests that we
|
||||
// do not understand (usually out of MCP spec).
|
||||
MCPSessionInvalidHTTPRequest = "mcp.session.invalid_http_request"
|
||||
|
||||
// BoundKeypairRecovery is emitted when a bound keypair token is used to
|
||||
// perform a recovery.
|
||||
|
||||
+18
-1
@@ -743,14 +743,31 @@ const (
|
||||
MCPSessionStartCode = "TMCP001I"
|
||||
// MCPSessionEndCode is the event code for mcp.session.end.
|
||||
MCPSessionEndCode = "TMCP002I"
|
||||
// MCPSessionEndFailureCode is the event code for mcp.session.end when the
|
||||
// end request is denied by the MCP server.
|
||||
MCPSessionEndFailureCode = "TMCP002E"
|
||||
// MCPSessionRequestCode is the event code for mcp.session.request.
|
||||
MCPSessionRequestCode = "TMCP003I"
|
||||
// MCPSessionRequestFailureCode is the event code for mcp.session.request
|
||||
// when the request is denied by Teleport.
|
||||
// when the request is denied by Teleport or the MCP server.
|
||||
MCPSessionRequestFailureCode = "TMCP003E"
|
||||
// MCPSessionNotificationCode is the event code for
|
||||
// mcp.session.notification.
|
||||
MCPSessionNotificationCode = "TMCP004I"
|
||||
// MCPSessionNotificationFailureCode is the event code for
|
||||
// mcp.session.notification when the notification is denied by the MCP
|
||||
// server.
|
||||
MCPSessionNotificationFailureCode = "TMCP004E"
|
||||
// MCPSessionListenSSEStreamCode is the event code for
|
||||
// mcp.session.listen_sse_stream.
|
||||
MCPSessionListenSSEStreamCode = "TMCP005I"
|
||||
// MCPSessionListenSSEStreamFailureCode is the event code for
|
||||
// mcp.session.listen_sse_stream when the request is denied by the MCP
|
||||
// server.
|
||||
MCPSessionListenSSEStreamFailureCode = "TMCP005E"
|
||||
// MCPSessionInvalidHTTPRequestCode is the event code for
|
||||
// mcp.session.invalid_http_request.
|
||||
MCPSessionInvalidHTTPRequestCode = "TMCP006E"
|
||||
|
||||
// BoundKeypairRecoveryCode is the event code for
|
||||
// join_token.bound_keypair.recovery.
|
||||
|
||||
@@ -532,6 +532,10 @@ func FromEventFields(fields EventFields) (events.AuditEvent, error) {
|
||||
e = &events.MCPSessionRequest{}
|
||||
case MCPSessionNotificationEvent:
|
||||
e = &events.MCPSessionNotification{}
|
||||
case MCPSessionListenSSEStream:
|
||||
e = &events.MCPSessionListenSSEStream{}
|
||||
case MCPSessionInvalidHTTPRequest:
|
||||
e = &events.MCPSessionInvalidHTTPRequest{}
|
||||
|
||||
case BoundKeypairRecovery:
|
||||
e = &events.BoundKeypairRecovery{}
|
||||
|
||||
+68
-7
@@ -21,6 +21,7 @@ package mcp
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
@@ -29,6 +30,7 @@ import (
|
||||
apidefaults "github.com/gravitational/teleport/api/defaults"
|
||||
apievents "github.com/gravitational/teleport/api/types/events"
|
||||
"github.com/gravitational/teleport/lib/events"
|
||||
"github.com/gravitational/teleport/lib/utils"
|
||||
"github.com/gravitational/teleport/lib/utils/mcputils"
|
||||
)
|
||||
|
||||
@@ -103,8 +105,8 @@ func (a *sessionAuditor) emitStartEvent(ctx context.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
func (a *sessionAuditor) emitEndEvent(ctx context.Context) {
|
||||
a.emitEvent(ctx, &apievents.MCPSessionEnd{
|
||||
func (a *sessionAuditor) emitEndEvent(ctx context.Context, err error) {
|
||||
event := &apievents.MCPSessionEnd{
|
||||
Metadata: a.makeEventMetadata(
|
||||
events.MCPSessionEndEvent,
|
||||
events.MCPSessionEndCode,
|
||||
@@ -114,14 +116,23 @@ func (a *sessionAuditor) emitEndEvent(ctx context.Context) {
|
||||
UserMetadata: a.makeUserMetadata(),
|
||||
ConnectionMetadata: a.makeConnectionMetadata(),
|
||||
AppMetadata: a.makeAppMetadata(),
|
||||
})
|
||||
Status: apievents.Status{
|
||||
Success: true,
|
||||
},
|
||||
}
|
||||
if err != nil {
|
||||
event.Metadata.Code = events.MCPSessionEndFailureCode
|
||||
event.Status.Success = false
|
||||
event.Status.Error = err.Error()
|
||||
}
|
||||
a.emitEvent(ctx, event)
|
||||
}
|
||||
|
||||
func (a *sessionAuditor) emitNotificationEvent(ctx context.Context, msg *mcputils.JSONRPCNotification) {
|
||||
if !a.shouldEmitEvent(msg.Method) {
|
||||
func (a *sessionAuditor) emitNotificationEvent(ctx context.Context, msg *mcputils.JSONRPCNotification, err error) {
|
||||
if err == nil && !a.shouldEmitEvent(msg.Method) {
|
||||
return
|
||||
}
|
||||
a.emitEvent(ctx, &apievents.MCPSessionNotification{
|
||||
event := &apievents.MCPSessionNotification{
|
||||
Metadata: a.makeEventMetadata(
|
||||
events.MCPSessionNotificationEvent,
|
||||
events.MCPSessionNotificationCode,
|
||||
@@ -134,7 +145,16 @@ func (a *sessionAuditor) emitNotificationEvent(ctx context.Context, msg *mcputil
|
||||
Method: string(msg.Method),
|
||||
Params: msg.Params.GetEventParams(),
|
||||
},
|
||||
})
|
||||
Status: apievents.Status{
|
||||
Success: true,
|
||||
},
|
||||
}
|
||||
if err != nil {
|
||||
event.Metadata.Code = events.MCPSessionNotificationFailureCode
|
||||
event.Status.Success = false
|
||||
event.Status.Error = err.Error()
|
||||
}
|
||||
a.emitEvent(ctx, event)
|
||||
}
|
||||
|
||||
func (a *sessionAuditor) emitRequestEvent(ctx context.Context, msg *mcputils.JSONRPCRequest, err error) {
|
||||
@@ -168,6 +188,47 @@ func (a *sessionAuditor) emitRequestEvent(ctx context.Context, msg *mcputils.JSO
|
||||
a.emitEvent(ctx, event)
|
||||
}
|
||||
|
||||
//nolint:unused //TODO(greedy52) remove nolint
|
||||
func (a *sessionAuditor) emitListenSSEStreamEvent(ctx context.Context, err error) {
|
||||
event := &apievents.MCPSessionListenSSEStream{
|
||||
Metadata: a.makeEventMetadata(
|
||||
events.MCPSessionListenSSEStream,
|
||||
events.MCPSessionListenSSEStreamCode,
|
||||
),
|
||||
SessionMetadata: a.makeSessionMetadata(),
|
||||
UserMetadata: a.makeUserMetadata(),
|
||||
AppMetadata: a.makeAppMetadata(),
|
||||
Status: apievents.Status{
|
||||
Success: true,
|
||||
},
|
||||
}
|
||||
if err != nil {
|
||||
event.Metadata.Code = events.MCPSessionListenSSEStreamFailureCode
|
||||
event.Status.Success = false
|
||||
event.Status.Error = err.Error()
|
||||
}
|
||||
a.emitEvent(ctx, event)
|
||||
}
|
||||
|
||||
//nolint:unused //TODO(greedy52) remove nolint
|
||||
func (a *sessionAuditor) emitInvalidHTTPRequest(ctx context.Context, r *http.Request) {
|
||||
body, _ := utils.GetAndReplaceRequestBody(r)
|
||||
event := &apievents.MCPSessionInvalidHTTPRequest{
|
||||
Metadata: a.makeEventMetadata(
|
||||
events.MCPSessionInvalidHTTPRequest,
|
||||
events.MCPSessionInvalidHTTPRequestCode,
|
||||
),
|
||||
SessionMetadata: a.makeSessionMetadata(),
|
||||
UserMetadata: a.makeUserMetadata(),
|
||||
AppMetadata: a.makeAppMetadata(),
|
||||
Path: r.URL.Path,
|
||||
Method: r.Method,
|
||||
Body: body,
|
||||
RawQuery: r.URL.RawQuery,
|
||||
}
|
||||
a.emitEvent(ctx, event)
|
||||
}
|
||||
|
||||
func (a *sessionAuditor) emitEvent(ctx context.Context, event apievents.AuditEvent) {
|
||||
preparedEvent, err := a.preparer.PrepareSessionEvent(event)
|
||||
if err != nil {
|
||||
|
||||
@@ -176,7 +176,7 @@ func (s *sessionHandler) checkAccessToTool(ctx context.Context, toolName string)
|
||||
}
|
||||
|
||||
func (s *sessionHandler) processClientNotification(ctx context.Context, notification *mcputils.JSONRPCNotification) {
|
||||
s.emitNotificationEvent(ctx, notification)
|
||||
s.emitNotificationEvent(ctx, notification, nil)
|
||||
}
|
||||
|
||||
func (s *sessionHandler) onClientNotification(serverRequestWriter mcputils.MessageWriter) mcputils.HandleNotificationFunc {
|
||||
|
||||
+1
-1
@@ -102,7 +102,7 @@ func (s *Server) handleStdioToSSE(ctx context.Context, sessionCtx *SessionCtx) e
|
||||
// TODO(greedy52) capture client info then emit start event with client
|
||||
// information.
|
||||
session.emitStartEvent(s.cfg.ParentContext)
|
||||
defer session.emitEndEvent(s.cfg.ParentContext)
|
||||
defer session.emitEndEvent(s.cfg.ParentContext, nil)
|
||||
|
||||
// Wait until reader finishes.
|
||||
clientRequestReader.Run(ctx)
|
||||
|
||||
@@ -135,7 +135,7 @@ func (s *Server) handleStdio(ctx context.Context, sessionCtx *SessionCtx, makeSe
|
||||
// TODO(greedy52) capture client info then emit start event with client
|
||||
// information.
|
||||
session.emitStartEvent(s.cfg.ParentContext)
|
||||
defer session.emitEndEvent(s.cfg.ParentContext)
|
||||
defer session.emitEndEvent(s.cfg.ParentContext, nil)
|
||||
|
||||
go clientRequestReader.Run(ctx)
|
||||
go serverResponseReader.Run(ctx)
|
||||
|
||||
@@ -316,9 +316,14 @@ const EventIconMap: Record<EventCode, any> = {
|
||||
[eventCodes.AUTOUPDATE_AGENT_ROLLOUT_ROLLBACK]: Icons.Restore,
|
||||
[eventCodes.MCP_SESSION_START]: Icons.ModelContextProtocol,
|
||||
[eventCodes.MCP_SESSION_END]: Icons.ModelContextProtocol,
|
||||
[eventCodes.MCP_SESSION_END_FAILURE]: Icons.Warning,
|
||||
[eventCodes.MCP_SESSION_REQUEST]: Icons.ModelContextProtocol,
|
||||
[eventCodes.MCP_SESSION_REQUEST_FAILURE]: Icons.Warning,
|
||||
[eventCodes.MCP_SESSION_NOTIFICATION]: Icons.ModelContextProtocol,
|
||||
[eventCodes.MCP_SESSION_NOTIFICATION_FAILURE]: Icons.Warning,
|
||||
[eventCodes.MCP_SESSION_LISTEN_SSE_STREAM]: Icons.ModelContextProtocol,
|
||||
[eventCodes.MCP_SESSION_LISTEN_SSE_STREAM_FAILURE]: Icons.Warning,
|
||||
[eventCodes.MCP_SESSION_INVALID_HTTP_REQUEST]: Icons.Warning,
|
||||
[eventCodes.BOUND_KEYPAIR_RECOVERY]: Icons.Info,
|
||||
[eventCodes.BOUND_KEYPAIR_ROTATION]: Icons.Info,
|
||||
[eventCodes.BOUND_KEYPAIR_JOIN_STATE_VERIFICATION_FAILED]: Icons.Warning,
|
||||
|
||||
@@ -4094,6 +4094,24 @@ export const events = [
|
||||
jsonrpc: '2.0',
|
||||
},
|
||||
},
|
||||
{
|
||||
code: 'TMCP004E',
|
||||
ei: 0,
|
||||
event: 'mcp.session.notification',
|
||||
namespace: 'default',
|
||||
server_id: 'a0518380-0d53-4188-ac8b-8ddd8103e45b',
|
||||
sid: '6593cf87-9839-4f18-abf8-c54873aaeb4e',
|
||||
time: '2025-05-23T11:11:11.333Z',
|
||||
uid: '80400ed9-644e-4a6e-ab99-b264b34d0f55',
|
||||
user: 'ai-user',
|
||||
app_name: 'mcp-everything',
|
||||
success: false,
|
||||
message: {
|
||||
method: 'notifications/initialized',
|
||||
jsonrpc: '2.0',
|
||||
},
|
||||
error: 'HTTP 401 Unauthorized',
|
||||
},
|
||||
{
|
||||
code: 'TMCP003I',
|
||||
ei: 0,
|
||||
@@ -4154,6 +4172,62 @@ export const events = [
|
||||
uid: '80400ed9-644e-4a6e-ab99-b264b34d0f55',
|
||||
user: 'ai-user',
|
||||
app_name: 'mcp-everything',
|
||||
success: true,
|
||||
},
|
||||
{
|
||||
code: 'TMCP002E',
|
||||
ei: 0,
|
||||
event: 'mcp.session.end',
|
||||
namespace: 'default',
|
||||
server_id: 'a0518380-0d53-4188-ac8b-8ddd8103e45b',
|
||||
sid: '6593cf87-9839-4f18-abf8-c54873aaeb4e',
|
||||
time: '2025-05-23T12:22:22.222Z',
|
||||
uid: '80400ed9-644e-4a6e-ab99-b264b34d0f55',
|
||||
user: 'ai-user',
|
||||
app_name: 'mcp-everything',
|
||||
success: true,
|
||||
error: 'HTTP 405 Method Not Allowed',
|
||||
},
|
||||
{
|
||||
code: 'TMCP005I',
|
||||
ei: 0,
|
||||
event: 'mcp.session.listen_sse_stream',
|
||||
namespace: 'default',
|
||||
server_id: 'a0518380-0d53-4188-ac8b-8ddd8103e45b',
|
||||
sid: '6593cf87-9839-4f18-abf8-c54873aaeb4e',
|
||||
time: '2025-05-23T12:22:22.222Z',
|
||||
uid: '80400ed9-644e-4a6e-ab99-b264b34d0f55',
|
||||
user: 'ai-user',
|
||||
app_name: 'mcp-everything',
|
||||
success: true,
|
||||
},
|
||||
{
|
||||
code: 'TMCP005E',
|
||||
ei: 0,
|
||||
event: 'mcp.session.listen_sse_stream',
|
||||
namespace: 'default',
|
||||
server_id: 'a0518380-0d53-4188-ac8b-8ddd8103e45b',
|
||||
sid: '6593cf87-9839-4f18-abf8-c54873aaeb4e',
|
||||
time: '2025-05-23T12:22:22.222Z',
|
||||
uid: '80400ed9-644e-4a6e-ab99-b264b34d0f55',
|
||||
user: 'ai-user',
|
||||
app_name: 'mcp-everything',
|
||||
success: false,
|
||||
error: 'HTTP 405 Method Not Allowed',
|
||||
},
|
||||
{
|
||||
code: 'TMCP006E',
|
||||
ei: 0,
|
||||
event: 'mcp.session.invalid_http_request',
|
||||
namespace: 'default',
|
||||
server_id: 'a0518380-0d53-4188-ac8b-8ddd8103e45b',
|
||||
sid: '6593cf87-9839-4f18-abf8-c54873aaeb4e',
|
||||
time: '2025-05-23T12:22:22.222Z',
|
||||
uid: '80400ed9-644e-4a6e-ab99-b264b34d0f55',
|
||||
user: 'ai-user',
|
||||
app_name: 'mcp-everything',
|
||||
method: 'OPTIONS',
|
||||
path: '/',
|
||||
},
|
||||
].map(makeEvent);
|
||||
|
||||
|
||||
@@ -2253,6 +2253,14 @@ export const formatters: Formatters = {
|
||||
return `User [${user}] has disconnected from MCP server [${app_name}]`;
|
||||
},
|
||||
},
|
||||
[eventCodes.MCP_SESSION_END_FAILURE]: {
|
||||
type: 'mcp.session.end',
|
||||
desc: 'MCP Session End Failure',
|
||||
format: event => {
|
||||
const { user, app_name } = event;
|
||||
return `User [${user}] failed to disconnect from MCP server [${app_name}]`;
|
||||
},
|
||||
},
|
||||
[eventCodes.MCP_SESSION_REQUEST]: {
|
||||
type: 'mcp.session.request',
|
||||
desc: 'MCP Session Request',
|
||||
@@ -2268,9 +2276,9 @@ export const formatters: Formatters = {
|
||||
desc: 'MCP Session Request Failure',
|
||||
format: ({ user, app_name, message }) => {
|
||||
if (message.params?.name) {
|
||||
return `User [${user}] was denied access to an MCP request [${message.method}] for [${message.params.name}] to MCP server [${app_name}]`;
|
||||
return `User [${user}] failed to send an MCP request [${message.method}] for [${message.params.name}] to MCP server [${app_name}]`;
|
||||
}
|
||||
return `User [${user}] was denied access to an MCP request [${message.method}] to MCP server [${app_name}]`;
|
||||
return `User [${user}] failed to send an MCP request [${message.method}] to MCP server [${app_name}]`;
|
||||
},
|
||||
},
|
||||
[eventCodes.MCP_SESSION_NOTIFICATION]: {
|
||||
@@ -2280,6 +2288,34 @@ export const formatters: Formatters = {
|
||||
return `User [${user}] sent an MCP notification [${message.method}] to MCP server [${app_name}]`;
|
||||
},
|
||||
},
|
||||
[eventCodes.MCP_SESSION_NOTIFICATION_FAILURE]: {
|
||||
type: 'mcp.session.notification',
|
||||
desc: 'MCP Session Notification Failure',
|
||||
format: ({ user, app_name, message }) => {
|
||||
return `User [${user}] failed to send an MCP notification [${message.method}] to MCP server [${app_name}]`;
|
||||
},
|
||||
},
|
||||
[eventCodes.MCP_SESSION_LISTEN_SSE_STREAM]: {
|
||||
type: 'mcp.session.listen_sse_stream',
|
||||
desc: 'MCP Session Listen',
|
||||
format: ({ user, app_name }) => {
|
||||
return `User [${user}] has started listening events from MCP server [${app_name}]`;
|
||||
},
|
||||
},
|
||||
[eventCodes.MCP_SESSION_LISTEN_SSE_STREAM_FAILURE]: {
|
||||
type: 'mcp.session.listen_sse_stream',
|
||||
desc: 'MCP Session Listen Failure',
|
||||
format: ({ user, app_name }) => {
|
||||
return `User [${user}] failed to listen events from MCP server [${app_name}]`;
|
||||
},
|
||||
},
|
||||
[eventCodes.MCP_SESSION_INVALID_HTTP_REQUEST]: {
|
||||
type: 'mcp.session.invalid_http_request',
|
||||
desc: 'MCP Session Invalid Request',
|
||||
format: ({ user, app_name }) => {
|
||||
return `User [${user}] attempted to send an invalid request to MCP server [${app_name}]`;
|
||||
},
|
||||
},
|
||||
[eventCodes.BOUND_KEYPAIR_RECOVERY]: {
|
||||
type: 'join_token.bound_keypair.recovery',
|
||||
desc: 'Bound Keypair Recovery',
|
||||
|
||||
@@ -335,9 +335,14 @@ export const eventCodes = {
|
||||
AUTOUPDATE_AGENT_ROLLOUT_ROLLBACK: 'AUAR003I',
|
||||
MCP_SESSION_START: 'TMCP001I',
|
||||
MCP_SESSION_END: 'TMCP002I',
|
||||
MCP_SESSION_END_FAILURE: 'TMCP002E',
|
||||
MCP_SESSION_REQUEST: 'TMCP003I',
|
||||
MCP_SESSION_REQUEST_FAILURE: 'TMCP003E',
|
||||
MCP_SESSION_NOTIFICATION: 'TMCP004I',
|
||||
MCP_SESSION_NOTIFICATION_FAILURE: 'TMCP004E',
|
||||
MCP_SESSION_LISTEN_SSE_STREAM: 'TMCP005I',
|
||||
MCP_SESSION_LISTEN_SSE_STREAM_FAILURE: 'TMCP005E',
|
||||
MCP_SESSION_INVALID_HTTP_REQUEST: 'TMCP006E',
|
||||
BOUND_KEYPAIR_RECOVERY: 'TBK001I',
|
||||
BOUND_KEYPAIR_ROTATION: 'TBK002I',
|
||||
BOUND_KEYPAIR_JOIN_STATE_VERIFICATION_FAILED: 'TBK003W',
|
||||
@@ -1956,6 +1961,12 @@ export type RawEvents = {
|
||||
app_name: string;
|
||||
}
|
||||
>;
|
||||
[eventCodes.MCP_SESSION_END_FAILURE]: RawEvent<
|
||||
typeof eventCodes.MCP_SESSION_END_FAILURE,
|
||||
{
|
||||
app_name: string;
|
||||
}
|
||||
>;
|
||||
[eventCodes.MCP_SESSION_REQUEST]: RawEvent<
|
||||
typeof eventCodes.MCP_SESSION_REQUEST,
|
||||
{
|
||||
@@ -1989,6 +2000,33 @@ export type RawEvents = {
|
||||
};
|
||||
}
|
||||
>;
|
||||
[eventCodes.MCP_SESSION_NOTIFICATION_FAILURE]: RawEvent<
|
||||
typeof eventCodes.MCP_SESSION_NOTIFICATION_FAILURE,
|
||||
{
|
||||
app_name: string;
|
||||
message: {
|
||||
method: string;
|
||||
};
|
||||
}
|
||||
>;
|
||||
[eventCodes.MCP_SESSION_LISTEN_SSE_STREAM]: RawEvent<
|
||||
typeof eventCodes.MCP_SESSION_LISTEN_SSE_STREAM,
|
||||
{
|
||||
app_name: string;
|
||||
}
|
||||
>;
|
||||
[eventCodes.MCP_SESSION_LISTEN_SSE_STREAM_FAILURE]: RawEvent<
|
||||
typeof eventCodes.MCP_SESSION_LISTEN_SSE_STREAM_FAILURE,
|
||||
{
|
||||
app_name: string;
|
||||
}
|
||||
>;
|
||||
[eventCodes.MCP_SESSION_INVALID_HTTP_REQUEST]: RawEvent<
|
||||
typeof eventCodes.MCP_SESSION_INVALID_HTTP_REQUEST,
|
||||
{
|
||||
app_name: string;
|
||||
}
|
||||
>;
|
||||
[eventCodes.BOUND_KEYPAIR_RECOVERY]: RawEvent<
|
||||
typeof eventCodes.BOUND_KEYPAIR_RECOVERY,
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user