Support metadata retrieval
diff --git a/tools/metadata_test.go b/tools/metadata_test.go
new file mode 100644
index 0000000..88c7e6d
--- /dev/null
+++ b/tools/metadata_test.go
@@ -0,0 +1,285 @@
+package tools
+
+import (
+	"context"
+	"testing"
+
+	"github.com/korap/korap-mcp/service"
+	"github.com/mark3labs/mcp-go/mcp"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestMetadataTool_Name(t *testing.T) {
+	client := &service.Client{}
+	tool := NewMetadataTool(client)
+
+	assert.Equal(t, "korap_metadata", tool.Name())
+}
+
+func TestMetadataTool_Description(t *testing.T) {
+	client := &service.Client{}
+	tool := NewMetadataTool(client)
+
+	expected := "Retrieve metadata and statistics for KorAP corpora"
+	assert.Equal(t, expected, tool.Description())
+}
+
+func TestMetadataTool_InputSchema(t *testing.T) {
+	client := &service.Client{}
+	tool := NewMetadataTool(client)
+
+	schema := tool.InputSchema()
+
+	// Verify it's an object type
+	assert.Equal(t, "object", schema["type"])
+
+	// Verify properties exist
+	properties, ok := schema["properties"].(map[string]interface{})
+	assert.True(t, ok)
+	assert.Contains(t, properties, "action")
+	assert.Contains(t, properties, "corpus")
+
+	// Verify action property details
+	action, ok := properties["action"].(map[string]interface{})
+	assert.True(t, ok)
+	assert.Equal(t, "string", action["type"])
+
+	enum, ok := action["enum"].([]string)
+	assert.True(t, ok)
+	assert.Contains(t, enum, "list")
+	assert.Contains(t, enum, "statistics")
+	assert.Equal(t, "list", action["default"])
+
+	// Verify required fields
+	required, ok := schema["required"].([]string)
+	assert.True(t, ok)
+	assert.Contains(t, required, "action")
+}
+
+func TestNewMetadataTool(t *testing.T) {
+	client := &service.Client{}
+	tool := NewMetadataTool(client)
+
+	assert.NotNil(t, tool)
+	assert.Equal(t, client, tool.client)
+}
+
+func TestMetadataTool_Execute_MissingAction(t *testing.T) {
+	client := &service.Client{}
+	tool := NewMetadataTool(client)
+
+	// Create request without action parameter
+	request := mcp.CallToolRequest{
+		Params: mcp.CallToolParams{
+			Arguments: map[string]interface{}{},
+		},
+	}
+
+	_, err := tool.Execute(context.Background(), request)
+	assert.Error(t, err)
+	assert.Contains(t, err.Error(), "action parameter is required")
+}
+
+func TestMetadataTool_Execute_NilClient(t *testing.T) {
+	tool := NewMetadataTool(nil)
+
+	request := mcp.CallToolRequest{
+		Params: mcp.CallToolParams{
+			Arguments: map[string]interface{}{
+				"action": "list",
+			},
+		},
+	}
+
+	_, err := tool.Execute(context.Background(), request)
+	assert.Error(t, err)
+	assert.Contains(t, err.Error(), "KorAP client not configured")
+}
+
+func TestMetadataTool_Execute_UnknownAction(t *testing.T) {
+	client := &service.Client{}
+	tool := NewMetadataTool(client)
+
+	request := mcp.CallToolRequest{
+		Params: mcp.CallToolParams{
+			Arguments: map[string]interface{}{
+				"action": "unknown",
+			},
+		},
+	}
+
+	_, err := tool.Execute(context.Background(), request)
+	assert.Error(t, err)
+	// The unknown action error should come before authentication
+	assert.Contains(t, err.Error(), "unknown action: unknown")
+}
+
+func TestMetadataTool_Execute_StatisticsWithoutCorpus(t *testing.T) {
+	tool := NewMetadataTool(nil)
+
+	request := mcp.CallToolRequest{
+		Params: mcp.CallToolParams{
+			Arguments: map[string]interface{}{
+				"action": "statistics",
+			},
+		},
+	}
+
+	_, err := tool.Execute(context.Background(), request)
+	assert.Error(t, err)
+	// Should fail at client check since corpus is now optional
+	assert.Contains(t, err.Error(), "KorAP client not configured")
+}
+
+func TestMetadataTool_Execute_ParameterExtraction(t *testing.T) {
+	// This test verifies that parameters are extracted correctly
+	// It should fail with authentication error since we don't have a mock server
+	// but we can verify the parameters were parsed correctly by checking the log messages
+
+	client := &service.Client{}
+	tool := NewMetadataTool(client)
+
+	tests := []struct {
+		name      string
+		arguments map[string]interface{}
+		expectErr bool
+	}{
+		{
+			name: "list_action",
+			arguments: map[string]interface{}{
+				"action": "list",
+			},
+			expectErr: true, // Will fail at authentication
+		},
+		{
+			name: "statistics_action",
+			arguments: map[string]interface{}{
+				"action": "statistics",
+				"corpus": "test-corpus",
+			},
+			expectErr: true, // Will fail at authentication
+		},
+		{
+			name: "statistics_with_empty_corpus",
+			arguments: map[string]interface{}{
+				"action": "statistics",
+				"corpus": "",
+			},
+			expectErr: true, // Will fail at authentication (corpus is optional)
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			request := mcp.CallToolRequest{
+				Params: mcp.CallToolParams{
+					Arguments: tt.arguments,
+				},
+			}
+
+			_, err := tool.Execute(context.Background(), request)
+			if tt.expectErr {
+				assert.Error(t, err)
+			}
+		})
+	}
+}
+
+func TestMetadataTool_formatCorpusList(t *testing.T) {
+	client := &service.Client{}
+	tool := NewMetadataTool(client)
+
+	// Test empty response
+	emptyResponse := &service.CorpusListResponse{
+		Corpora: []service.CorpusInfo{},
+	}
+
+	result := tool.formatCorpusList(emptyResponse)
+	assert.Contains(t, result, "KorAP Available Corpora")
+	assert.Contains(t, result, "No corpora available")
+
+	// Test response with corpora
+	responseWithCorpora := &service.CorpusListResponse{
+		Corpora: []service.CorpusInfo{
+			{
+				ID:          "corpus1",
+				Name:        "Test Corpus 1",
+				Description: "A test corpus",
+				Documents:   100,
+				Tokens:      50000,
+				Sentences:   2500,
+				Paragraphs:  500,
+			},
+			{
+				ID:        "corpus2",
+				Name:      "Test Corpus 2",
+				Documents: 200,
+				Tokens:    75000,
+			},
+		},
+	}
+
+	result = tool.formatCorpusList(responseWithCorpora)
+	assert.Contains(t, result, "KorAP Available Corpora")
+	assert.Contains(t, result, "Total Corpora: 2")
+	assert.Contains(t, result, "1. Test Corpus 1")
+	assert.Contains(t, result, "ID: corpus1")
+	assert.Contains(t, result, "Description: A test corpus")
+	assert.Contains(t, result, "Documents: 100")
+	assert.Contains(t, result, "Tokens: 50000")
+	assert.Contains(t, result, "Sentences: 2500")
+	assert.Contains(t, result, "Paragraphs: 500")
+	assert.Contains(t, result, "2. Test Corpus 2")
+	assert.Contains(t, result, "ID: corpus2")
+	assert.Contains(t, result, "Documents: 200")
+	assert.Contains(t, result, "Tokens: 75000")
+}
+
+func TestMetadataTool_formatCorpusStatistics(t *testing.T) {
+	client := &service.Client{}
+	tool := NewMetadataTool(client)
+
+	// Test minimal statistics
+	minimalStats := &service.StatisticsResponse{
+		Documents: 100,
+		Tokens:    50000,
+	}
+
+	result := tool.formatCorpusStatistics("test-corpus", minimalStats)
+	assert.Contains(t, result, "KorAP Corpus Statistics")
+	assert.Contains(t, result, "Corpus Query: test-corpus")
+	assert.Contains(t, result, "Documents: 100")
+	assert.Contains(t, result, "Tokens: 50000")
+
+	// Test complete statistics with additional fields
+	completeStats := &service.StatisticsResponse{
+		Documents:  200,
+		Tokens:     100000,
+		Sentences:  5000,
+		Paragraphs: 1000,
+		Fields: map[string]interface{}{
+			"genre":    "literature",
+			"language": "German",
+			"year":     2023,
+		},
+	}
+
+	result = tool.formatCorpusStatistics("complete-corpus", completeStats)
+	assert.Contains(t, result, "KorAP Corpus Statistics")
+	assert.Contains(t, result, "Corpus Query: complete-corpus")
+	assert.Contains(t, result, "Documents: 200")
+	assert.Contains(t, result, "Tokens: 100000")
+	assert.Contains(t, result, "Sentences: 5000")
+	assert.Contains(t, result, "Paragraphs: 1000")
+	assert.Contains(t, result, "Additional Fields:")
+	assert.Contains(t, result, "genre: literature")
+	assert.Contains(t, result, "language: German")
+	assert.Contains(t, result, "year: 2023")
+
+	// Test empty corpus query (all available data)
+	result = tool.formatCorpusStatistics("", minimalStats)
+	assert.Contains(t, result, "KorAP Corpus Statistics")
+	assert.Contains(t, result, "Corpus Query: (all available data)")
+	assert.Contains(t, result, "Documents: 100")
+	assert.Contains(t, result, "Tokens: 50000")
+}