Module: PostHog::MCP::ConversationId Private

Defined in:
lib/posthog/mcp/conversation_id.rb

Overview

This module is part of a private API. You should avoid using this module if possible, as it may be removed or be changed in the future.

Optional conversation_id loop-back. When enabled, the SDK injects a conversation_id parameter into every tool, mints one when the agent does not supply it, hands it back on the response, and captures it as $mcp_conversation_id, stitching calls across reconnects and pods.

Constant Summary collapse

PARAM_NAME =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'conversation_id'
MINTED_CONVERSATION_ID =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

/\A[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\z/i
MCP_INSTRUCTIONS_KEY =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'_mcp_instructions'
INSTRUCTIONS_FIELD_DESCRIPTION =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'Server-issued metadata for this conversation.'
CONVERSATION_ID_FIELD_DESCRIPTION =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'The server-issued conversation identifier.'

Class Method Summary collapse

Class Method Details

.build_prompt_back(conversation_id) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Plain data, not an instruction: an instruction-shaped block is what a client's prompt-injection filter strips. Compact JSON.



56
57
58
# File 'lib/posthog/mcp/conversation_id.rb', line 56

def build_prompt_back(conversation_id)
  { type: 'text', text: JSON.generate({ conversation_id: conversation_id }) }
end

.extract(args) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



30
31
32
33
34
# File 'lib/posthog/mcp/conversation_id.rb', line 30

def extract(args)
  return nil unless args.is_a?(Hash)

  normalize(args[PARAM_NAME] || args[PARAM_NAME.to_sym])
end

.inject_prompt_back(result, conversation_id) ⇒ Hash

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns a new result with the prompt-back appended (or the input unchanged).

Returns:

  • (Hash) —

    a new result with the prompt-back appended (or the input unchanged)



61
62
63
64
65
66
# File 'lib/posthog/mcp/conversation_id.rb', line 61

def inject_prompt_back(result, conversation_id)
  return result unless prompt_back?(result)

  key = result.key?(:content) ? :content : 'content'
  result.merge(key => result[key] + [build_prompt_back(conversation_id)])
end

.mirror_instructions(result, conversation_id) ⇒ Array(Object, Boolean)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Mirror the handle into structuredContent for tools whose output schema declared _mcp_instructions. Customer data wins when the key exists.

Returns:

  • (Array(Object, Boolean)) —

    [result, delivered]



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/posthog/mcp/conversation_id.rb', line 72

def mirror_instructions(result, conversation_id)
  return [result, false] unless result.is_a?(Hash)

  key = %i[structuredContent structured_content].find { |k| result.key?(k) } ||
        %w[structuredContent structured_content].find { |k| result.key?(k) }
  return [result, false] if key.nil?

  structured = result[key]
  return [result, false] unless structured.is_a?(Hash)
  return [result, false] if structured.key?(MCP_INSTRUCTIONS_KEY) || structured.key?(MCP_INSTRUCTIONS_KEY.to_sym)

  payload = { 'conversation_id' => conversation_id }
  instructions_key = structured.keys.first.is_a?(Symbol) ? MCP_INSTRUCTIONS_KEY.to_sym : MCP_INSTRUCTIONS_KEY
  [result.merge(key => structured.merge(instructions_key => payload)), true]
end

.normalize(value) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



23
24
25
26
27
28
# File 'lib/posthog/mcp/conversation_id.rb', line 23

def normalize(value)
  return nil unless value.is_a?(String)

  trimmed = value.strip
  trimmed.empty? ? nil : trimmed
end

.prompt_back?(result) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


50
51
52
# File 'lib/posthog/mcp/conversation_id.rb', line 50

def prompt_back?(result)
  result.is_a?(Hash) && (result[:content] || result['content']).is_a?(Array)
end

.resolve(enabled, supplied, tool_name, missing_capability_tool_name) ⇒ Array(String, Boolean), Array(nil, false)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns [conversation_id, minted].

Parameters:

  • supplied (String, nil) —

    the conversation_id argument, and only when this layer owns it. A tool that declares conversation_id in its own schema is passed application data, which must not anchor analytics: two users sharing such a value would be stitched into one conversation.

Returns:

  • (Array(String, Boolean), Array(nil, false)) —

    [conversation_id, minted]



41
42
43
44
45
46
47
48
# File 'lib/posthog/mcp/conversation_id.rb', line 41

def resolve(enabled, supplied, tool_name, missing_capability_tool_name)
  return [nil, false] if !enabled || tool_name == missing_capability_tool_name

  value = normalize(supplied)
  return [value.downcase, false] if value && MINTED_CONVERSATION_ID.match?(value)

  [Ids.uuid_v7, true]
end