diff --git a/pkg/action-url/action_url.go b/pkg/action-url/action_url.go
new file mode 100644
index 0000000000000000000000000000000000000000..cb6760dfdd0349f4ca249c435504d5f4e260c7f4
--- /dev/null
+++ b/pkg/action-url/action_url.go
@@ -0,0 +1,82 @@
+package action_url
+
+import (
+	"fmt"
+	"net/url"
+	"strings"
+
+	"git.perx.ru/perxis/perxis-go/pkg/errors"
+)
+
+// ActionURL структура для хранения данных о переданном действии.
+type ActionURL struct {
+	id        string
+	extension string
+	*url.URL
+}
+
+// New возвращает структуру ActionURL
+func New(action string) (*ActionURL, error) {
+	return parse(action)
+}
+
+// ID возвращает сохраненный в ActionURL id действия
+func (p *ActionURL) ID() string {
+	return p.id
+}
+
+// SetID устанавливает в ActionURL id действия
+func (p *ActionURL) SetID(id string) {
+	p.id = id
+}
+
+// Extension возвращает сохраненный в ActionURL id расширения
+func (p *ActionURL) Extension() string {
+	return p.extension
+}
+
+// SetExtension устанавливает в ActionURL id расширения
+func (p *ActionURL) SetExtension(ext string) {
+	p.extension = ext
+}
+
+// SetURL устанавливает структуру URL
+func (p *ActionURL) SetURL(u string) (err error) {
+	if p.URL, err = url.Parse(u); err != nil {
+		return err
+	}
+	return nil
+}
+
+// Make возвращает Action URL из сохраненных данных
+func (p *ActionURL) Make() string {
+	if p.id != "" && p.extension != "" {
+		return fmt.Sprintf("grpc:///%s/%s", p.extension, p.id)
+	}
+	return p.URL.String()
+}
+
+// parse функция для заполнения структуры ActionURL из переданного действия
+func parse(action string) (*ActionURL, error) {
+	if action == "" {
+		return &ActionURL{URL: nil}, nil
+	}
+	actionURL := &ActionURL{}
+	err := actionURL.SetURL(action)
+	if err != nil {
+		return nil, err
+	}
+	if actionURL.URL.Scheme == "grpc" {
+		path := actionURL.Path
+		if strings.HasPrefix(actionURL.Path, "/") {
+			path = actionURL.Path[1:]
+		}
+		splitPath := strings.Split(path, "/")
+		if len(splitPath) < 2 {
+			return nil, errors.Errorf("incorrect action URL, no action id: '%s'", action)
+		}
+		actionURL.extension = splitPath[0]
+		actionURL.id = splitPath[1]
+	}
+	return actionURL, nil
+}
diff --git a/pkg/action-url/action_url_test.go b/pkg/action-url/action_url_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..30b4b32be112ee13250c8be929181d49a1301768
--- /dev/null
+++ b/pkg/action-url/action_url_test.go
@@ -0,0 +1,126 @@
+package action_url
+
+import (
+	"fmt"
+	"net/url"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestActionURL_New(t *testing.T) {
+	tests := []struct {
+		name    string
+		action  string
+		want    *ActionURL
+		url     string
+		wantErr assert.ErrorAssertionFunc
+	}{
+		{
+			name:    "Without action",
+			want:    &ActionURL{},
+			wantErr: assert.NoError,
+		},
+		{
+			name:    "Without deprecated action call",
+			action:  "build-site",
+			want:    &ActionURL{},
+			url:     "build-site",
+			wantErr: assert.NoError,
+		},
+		{
+			name:   "With grpc action",
+			action: "grpc:///perxisweb/build-site",
+			want: &ActionURL{
+				id:        "build-site",
+				extension: "perxisweb",
+			},
+			url:     "grpc:///perxisweb/build-site",
+			wantErr: assert.NoError,
+		},
+		{
+			name:   "With ui action",
+			action: "ui:///space/env/coll",
+			want: &ActionURL{
+				id:        "",
+				extension: "",
+			},
+			url:     "ui:///space/env/coll",
+			wantErr: assert.NoError,
+		},
+		{
+			name:    "With http action",
+			action:  "https://perx.ru",
+			want:    &ActionURL{},
+			url:     "https://perx.ru",
+			wantErr: assert.NoError,
+		},
+		{
+			name:    "With error in parse",
+			action:  "grpc://user:abc{DEf1=ghi@example.com:5432/db?sslmode=require",
+			want:    nil,
+			wantErr: assert.Error,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if tt.url != "" {
+				tt.want.URL, _ = url.Parse(tt.url)
+			}
+			got, err := New(tt.action)
+			if !tt.wantErr(t, err, fmt.Sprintf("New(%v)", tt.action)) {
+				return
+			}
+			assert.Equalf(t, tt.want, got, "New(%v)", tt.action)
+		})
+	}
+}
+
+func TestActionURL_Make(t *testing.T) {
+	tests := []struct {
+		name      string
+		id        string
+		extension string
+		url       string
+		want      string
+	}{
+		{
+			name: "Without action and extensions id's #1",
+			url:  "grpc:///extension-id/action-id",
+			want: "grpc:///extension-id/action-id",
+		},
+		{
+			name: "Without action and extensions id's #2",
+			url:  "ui:///space/env/coll",
+			want: "ui:///space/env/coll",
+		},
+		{
+			name: "Without action and extensions id's #3",
+			url:  "space/env/coll",
+			want: "space/env/coll",
+		},
+		{
+			name: "Without action and extensions id's #4",
+			url:  "https://perx.ru",
+			want: "https://perx.ru",
+		},
+		{
+			name:      "With action and extensions",
+			id:        "action-id",
+			extension: "extension-id",
+			want:      "grpc:///extension-id/action-id",
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			p := &ActionURL{
+				id:        tt.id,
+				extension: tt.extension,
+			}
+			if tt.url != "" {
+				p.URL, _ = url.Parse(tt.url)
+			}
+			assert.Equalf(t, tt.want, p.Make(), "MakeFromURL()")
+		})
+	}
+}
diff --git a/pkg/extension/action_url.go b/pkg/extension/action_url.go
deleted file mode 100644
index 5552b81a43ee237788a3ca217e9c6cbd49f7351c..0000000000000000000000000000000000000000
--- a/pkg/extension/action_url.go
+++ /dev/null
@@ -1,66 +0,0 @@
-package extension
-
-import (
-	"net/url"
-	"strings"
-
-	"git.perx.ru/perxis/perxis-go/pkg/errors"
-)
-
-// ActionURL структура для хранения данных о переданном действии
-type ActionURL struct {
-	actionURL string
-	actionID  string
-	extension string
-	scheme    string
-	url       *url.URL
-}
-
-// NewActionURL возвращает пустую структуру ActionURL, если передано пустое действие
-// при передаче в функцию действия - заполняет структуру
-func NewActionURL(action string) (*ActionURL, error) {
-	return parseActionURL(action)
-}
-
-// ID возвращает сохраненный в ActionURL id действия
-func (p *ActionURL) ID() string {
-	return p.actionID
-}
-
-// Extension возвращает сохраненный в ActionURL id расширения
-func (p *ActionURL) Extension() string {
-	return p.extension
-}
-
-// Scheme возвращает сохраненную в ActionURL схему
-func (p *ActionURL) Scheme() string {
-	return p.scheme
-}
-
-// parseActionURL функция для заполнения структуры ActionURL из переданного действия
-func parseActionURL(action string) (*ActionURL, error) {
-	if action == "" {
-		return &ActionURL{}, nil
-	}
-	u, err := url.Parse(action)
-	if err != nil {
-		return nil, err
-	}
-	parsed := &ActionURL{}
-	parsed.actionURL = action
-	parsed.url = u
-	parsed.scheme = u.Scheme
-	if parsed.Scheme() == "grpc" {
-		path := u.Path
-		if strings.HasPrefix(u.Path, "/") {
-			path = u.Path[1:]
-		}
-		splitPath := strings.Split(path, "/")
-		if len(splitPath) < 2 {
-			return nil, errors.Errorf("incorrect action URL, no action id: '%s'", action)
-		}
-		parsed.extension = splitPath[0]
-		parsed.actionID = splitPath[1]
-	}
-	return parsed, nil
-}
diff --git a/pkg/extension/action_url_test.go b/pkg/extension/action_url_test.go
deleted file mode 100644
index e71ef1b6acf54fd629904893551e08995f2fd2d2..0000000000000000000000000000000000000000
--- a/pkg/extension/action_url_test.go
+++ /dev/null
@@ -1,86 +0,0 @@
-package extension
-
-import (
-	"fmt"
-	"net/url"
-	"testing"
-
-	"github.com/stretchr/testify/assert"
-)
-
-func TestNewActionURL(t *testing.T) {
-	tests := []struct {
-		name    string
-		action  string
-		want    *ActionURL
-		url     string
-		wantErr assert.ErrorAssertionFunc
-	}{
-		{
-			name:    "Without action",
-			want:    &ActionURL{},
-			wantErr: assert.NoError,
-		},
-		{
-			name:   "Without deprecated action call",
-			action: "build-site",
-			want: &ActionURL{
-				actionURL: "build-site",
-			},
-			url:     "build-site",
-			wantErr: assert.NoError,
-		},
-		{
-			name:   "With grpc action",
-			action: "grpc:///perxisweb/build-site",
-			want: &ActionURL{
-				actionURL: "grpc:///perxisweb/build-site",
-				actionID:  "build-site",
-				extension: "perxisweb",
-				scheme:    "grpc",
-			},
-			url:     "grpc:///perxisweb/build-site",
-			wantErr: assert.NoError,
-		},
-		{
-			name:   "With ui action",
-			action: "ui:///space/env/coll",
-			want: &ActionURL{
-				actionURL: "ui:///space/env/coll",
-				actionID:  "",
-				extension: "",
-				scheme:    "ui",
-			},
-			url:     "ui:///space/env/coll",
-			wantErr: assert.NoError,
-		},
-		{
-			name:   "With http action",
-			action: "https://perx.ru",
-			want: &ActionURL{
-				actionURL: "https://perx.ru",
-				scheme:    "https",
-			},
-			url:     "https://perx.ru",
-			wantErr: assert.NoError,
-		},
-		{
-			name:    "With error in parse",
-			action:  "grpc://user:abc{DEf1=ghi@example.com:5432/db?sslmode=require",
-			want:    nil,
-			wantErr: assert.Error,
-		},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			if tt.url != "" {
-				tt.want.url, _ = url.Parse(tt.url)
-			}
-			got, err := NewActionURL(tt.action)
-			if !tt.wantErr(t, err, fmt.Sprintf("NewActionURL(%v)", tt.action)) {
-				return
-			}
-			assert.Equalf(t, tt.want, got, "NewActionURL(%v)", tt.action)
-		})
-	}
-}
diff --git a/pkg/extension/server.go b/pkg/extension/server.go
index aec70ff7a71128ed312030cc3bd28541f7f969f4..522c38b9492f0beb622b76708d6eab90c32dcd38 100644
--- a/pkg/extension/server.go
+++ b/pkg/extension/server.go
@@ -3,6 +3,7 @@ package extension
 import (
 	"context"
 
+	"git.perx.ru/perxis/perxis-go/pkg/action-url"
 	"git.perx.ru/perxis/perxis-go/pkg/errors"
 	pb "git.perx.ru/perxis/perxis-go/proto/extensions"
 )
@@ -82,11 +83,11 @@ func (srv *Server) Update(ctx context.Context, request *UpdateRequest) (*UpdateR
 func (srv *Server) Action(ctx context.Context, in *pb.ActionRequest) (*pb.ActionResponse, error) {
 	ext := in.Extension
 	if ext == "" {
-		actionURL, err := NewActionURL(in.Action)
+		actionURL, err := action_url.New(in.Action)
 		if err != nil {
 			return nil, err
 		}
-		ext = actionURL.extension
+		ext = actionURL.Extension()
 	}
 
 	svc, ok := srv.services[ext]
diff --git a/pkg/extension/service/extension.go b/pkg/extension/service/extension.go
index 2fd57ee7c36d681e8d9768fa36a0961a953508c3..27ca5ca363ece282b18e49158d005693a6854c69 100644
--- a/pkg/extension/service/extension.go
+++ b/pkg/extension/service/extension.go
@@ -4,6 +4,7 @@ import (
 	"context"
 	"fmt"
 
+	"git.perx.ru/perxis/perxis-go/pkg/action-url"
 	"git.perx.ru/perxis/perxis-go/pkg/clients"
 	"git.perx.ru/perxis/perxis-go/pkg/content"
 	"git.perx.ru/perxis/perxis-go/pkg/errors"
@@ -157,7 +158,7 @@ func (s *Extension) Uninstall(ctx context.Context, in *extension.UninstallReques
 func (s *Extension) Action(ctx context.Context, in *extension.ActionRequest) (*extension.ActionResponse, error) {
 	ext := in.Extension
 	if ext == "" {
-		actionURL, err := extension.NewActionURL(in.Action)
+		actionURL, err := action_url.New(in.Action)
 		if err != nil {
 			return nil, err
 		}