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/mcp-directory-listing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"posthog-ruby": minor
---

`PostHog::MCP` tool definitions now match what directory reviews expect. The `get_more_tools` virtual tool declares `readOnlyHint`, `destructiveHint`, `idempotentHint` and `openWorldHint` as `false`, because a call records data. The injected `context` argument has a shorter description that asks for a few words by role. The injected `llm_model` argument is optional. This changes your published tool definitions; if your server is listed in the ChatGPT or Claude directories, those platforms may re-review the updated tools.
10 changes: 3 additions & 7 deletions lib/posthog/mcp/constants.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,9 @@ module MCP

# Description of the injected `context` argument.
DEFAULT_CONTEXT_PARAMETER_DESCRIPTION =
'Explain in 15-25 words, in third person, why this tool is called and how it supports ' \
"the user's goal. For analytics only. You MUST describe only the abstract purpose of the " \
'tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying ' \
'information from the user request or tool results, including names, emails, phone numbers, ' \
'IPs, IDs, or credentials. You MUST generalize specific entities into roles such as "a user", ' \
'"the customer", or "an account". Example: "Retrieving a customer\'s recent orders to ' \
'investigate a billing issue and help support determine the appropriate resolution."'
"A few words on why you are calling this tool, for the server developer's analytics. " \
'Describe people and things by role, such as "a customer". Do not quote the user or include ' \
'names, emails, or other personal details.'

# Description of the injected `llm_model` argument.
DEFAULT_MODEL_PARAMETER_DESCRIPTION =
Expand Down
2 changes: 1 addition & 1 deletion lib/posthog/mcp/schema_mutation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ def add_conversation_id_parameter(schema, tool_name:, options: nil)
tool_name: tool_name, required: false, options: options, label: 'conversation_id')
end

def add_model_parameter(schema, tool_name:, description: nil, required: true, options: nil)
def add_model_parameter(schema, tool_name:, description: nil, required: false, options: nil)
add_parameter(schema, ModelCapture::PARAM_NAME, description || DEFAULT_MODEL_PARAMETER_DESCRIPTION,
tool_name: tool_name, required: required, options: options, label: 'llm_model')
end
Expand Down
9 changes: 4 additions & 5 deletions lib/posthog/mcp/tools.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,10 @@ def descriptor(name = GET_MORE_TOOLS_NAME, options = nil)
required: ['context']
},
annotations: {
title: 'Get More Tools',
readOnlyHint: true,
openWorldHint: true,
idempotentHint: true,
destructiveHint: false
readOnlyHint: false,
destructiveHint: false,
idempotentHint: false,
openWorldHint: false
}
}
return spec unless options.respond_to?(:capture_model_enabled?) && options.capture_model_enabled?
Expand Down
2 changes: 1 addition & 1 deletion spec/posthog/mcp/client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@
expect(prepared[0][:inputSchema][:properties].keys).to eq(%i[context llm_model])
expect(prepared[1][:name]).to eq('get_more_tools')
expect(prepared[1][:inputSchema][:properties].keys).to eq(%i[context llm_model])
expect(prepared[1][:inputSchema][:required]).to contain_exactly('context', 'llm_model')
expect(prepared[1][:inputSchema][:required]).to eq(['context'])

without = client.prepare_tool_list(tools, capture_model: false, report_missing: true)
expect(without[0][:inputSchema][:properties].keys).to eq([:context])
Expand Down
4 changes: 2 additions & 2 deletions spec/posthog/mcp/instrument_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -623,7 +623,7 @@ def stringify_result_keys(target)
structured = list[:result][:tools].find { |t| t[:name] == 'structured' }
expect(structured[:inputSchema][:properties]).to have_key(:conversation_id)
expect(structured[:outputSchema][:properties]).to have_key(:_mcp_instructions)
expect(structured[:inputSchema][:required]).to contain_exactly('context', 'llm_model')
expect(structured[:inputSchema][:required]).to contain_exactly('context')

first = server.handle(rpc(2, 'tools/call', { name: 'echo', arguments: { message: 'hi', context: 'c' } }))
prompt_back = JSON.parse(first[:result][:content][1][:text])
Expand Down Expand Up @@ -754,7 +754,7 @@ def stringify_result_keys(target)
virtual = list[:result][:tools].find { |t| t[:name] == 'get_more_tools' }
expect(virtual[:inputSchema][:properties].keys).to contain_exactly(:context, :llm_model)
expect(virtual[:inputSchema][:properties].keys).not_to include(:conversation_id)
expect(virtual[:inputSchema][:required]).to contain_exactly('context', 'llm_model')
expect(virtual[:inputSchema][:required]).to contain_exactly('context')
result = server.handle(rpc(2, 'tools/call', { name: 'get_more_tools',
arguments: { context: 'need csv export',
llm_model: ' claude-opus-4-8 ' } }))
Expand Down
9 changes: 7 additions & 2 deletions spec/posthog/mcp/schema_mutation_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
out = described_class.add_context_parameter(schema, tool_name: 't')
expect(out[:properties].keys).to eq(%i[x context])
expect(out[:properties][:context][:description]).to eq(PostHog::MCP::DEFAULT_CONTEXT_PARAMETER_DESCRIPTION)
expect(PostHog::MCP::DEFAULT_CONTEXT_PARAMETER_DESCRIPTION).to eq(
"A few words on why you are calling this tool, for the server developer's analytics. " \
'Describe people and things by role, such as "a customer". Do not quote the user or include ' \
'names, emails, or other personal details.'
)
expect(out[:required]).to eq(%w[x context])
expect(schema[:properties].keys).to eq([:x])
expect(schema[:required]).to eq(['x'])
Expand All @@ -35,10 +40,10 @@
expect(out[:required]).to eq([])
end

it 'adds the model parameter with the JS default description' do
it 'adds the model parameter as optional with the JS default description' do
out = described_class.add_model_parameter(schema, tool_name: 't')
expect(out[:properties][:llm_model][:description]).to eq(PostHog::MCP::DEFAULT_MODEL_PARAMETER_DESCRIPTION)
expect(out[:required]).to include('llm_model')
expect(out[:required]).to eq(['x'])
end

describe '.injectable?' do
Expand Down
4 changes: 2 additions & 2 deletions spec/posthog/mcp/tools_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
expect(descriptor[:description]).to eq("Report that the user's request needs a capability this server does not have. " \
'Records the report for the server\'s developers; it does not return or add tools.')
expect(descriptor[:inputSchema][:required]).to eq(['context'])
expect(descriptor[:annotations]).to eq(title: 'Get More Tools', readOnlyHint: true, openWorldHint: true,
idempotentHint: true, destructiveHint: false)
expect(descriptor[:annotations]).to eq(readOnlyHint: false, destructiveHint: false,
idempotentHint: false, openWorldHint: false)
expect(PostHog::MCP.get_more_tools_result[:content][0][:text]).to eq(
'Unfortunately, we have shown you the full tool list. We have noted your feedback and will work to improve the ' \
'tool list in the future.'
Expand Down
Loading