diff --git a/src/metadata/mod-config.ts b/src/metadata/mod-config.ts
new file mode 100644
index 0000000..9f385c3
--- /dev/null
+++ b/src/metadata/mod-config.ts
@@ -0,0 +1,30 @@
+import action from "../../package.json";
+import Dependency from "./dependency";
+import ModMetadata from "./mod-metadata";
+import PublisherTarget from "../publishing/publisher-target";
+
+export default abstract class ModConfig<TConfig = Record<string, unknown>> implements ModMetadata {
+    public abstract get id(): string;
+    public abstract get name(): string;
+    public abstract get version(): string;
+    public abstract get loaders(): string[];
+    public abstract get dependencies(): Dependency[];
+
+    protected readonly config: TConfig;
+
+    constructor(config: TConfig) {
+        this.config = config || <TConfig>{};
+    }
+
+    getProjectId(project: PublisherTarget): string | undefined {
+        const projectName = PublisherTarget.toString(project).toLowerCase();
+        const custom = this.config["custom"];
+        const projects = this.config["projects"];
+        const projectId = (
+            custom?.[action.name]?.[projectName]?.id ?? custom?.[action.name]?.[projectName] ??
+            projects?.[projectName]?.id ?? projects?.[projectName] ??
+            custom?.projects?.[projectName]?.id ?? custom?.projects?.[projectName]
+        );
+        return projectId === undefined ? projectId : String(projectId);
+    }
+}