diff --git a/src/tap/metadata/TAPMetadata.java b/src/tap/metadata/TAPMetadata.java
index 2ea1aaf8134665e71ee74b3dced210433adac873..7181bc5301b48df8d3c24755251a0fcda46ca8fc 100644
--- a/src/tap/metadata/TAPMetadata.java
+++ b/src/tap/metadata/TAPMetadata.java
@@ -16,7 +16,7 @@ package tap.metadata;
  * You should have received a copy of the GNU Lesser General Public License
  * along with TAPLibrary.  If not, see <http://www.gnu.org/licenses/>.
  * 
- * Copyright 2012-2016 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ * Copyright 2012-2017 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
  *                       Astronomisches Rechen Institut (ARI)
  */
 
@@ -64,7 +64,7 @@ import uws.UWSToolBox;
  * </p>
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 2.1 (07/2016)
+ * @version 2.1 (03/2017)
  */
 public class TAPMetadata implements Iterable<TAPSchema>, VOSIResource, TAPResource {
 
@@ -386,6 +386,32 @@ public class TAPMetadata implements Iterable<TAPSchema>, VOSIResource, TAPResour
 		return tables;
 	}
 
+	/**
+	 * Get the description of the ObsCore table, if it is defined.
+	 * 
+	 * <p>
+	 * 	This function is case sensitive only on the schema name
+	 * 	(i.e. <code>ivoa</code>) which must be defined in full lower case.
+	 * 	The table name (i.e. <code>ObsCore</code>) will be found whatever
+	 * 	the case it is written in.
+	 * </p>
+	 * 
+	 * @return	Description of the ObsCore table,
+	 *        	or <code>NULL</code> if this table is not provided by this TAP service.
+	 * 
+	 * @since 2.1
+	 */
+	public TAPTable getObsCoreTable(){
+		TAPSchema ivoaSchema = getSchema("ivoa");
+		if (ivoaSchema != null){
+			for(TAPTable t : ivoaSchema){
+				if (t.getADQLName().equalsIgnoreCase("obscore"))
+					return t;
+			}
+		}
+		return null;
+	}
+
 	/**
 	 * Get the number of all tables contained in this TAP metadata set.
 	 * 
diff --git a/src/tap/resource/TAP.java b/src/tap/resource/TAP.java
index 8a528b61f750a6cec3d78f58b8bed7ff6bd116e7..e8fa13903ad6f9d88d473533f3cbf6b671c69a65 100644
--- a/src/tap/resource/TAP.java
+++ b/src/tap/resource/TAP.java
@@ -16,7 +16,7 @@ package tap.resource;
  * You should have received a copy of the GNU Lesser General Public License
  * along with TAPLibrary.  If not, see <http://www.gnu.org/licenses/>.
  * 
- * Copyright 2012-2016 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ * Copyright 2012-2017 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
  *                       Astronomisches Rechen Institut (ARI)
  */
 
@@ -30,6 +30,7 @@ import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import adql.db.DBColumn;
 import adql.db.FunctionDef;
 import tap.ServiceConnection;
 import tap.ServiceConnection.LimitUnit;
@@ -38,6 +39,7 @@ import tap.error.DefaultTAPErrorWriter;
 import tap.formatter.OutputFormat;
 import tap.log.TAPLog;
 import tap.metadata.TAPMetadata;
+import tap.metadata.TAPTable;
 import uk.ac.starlink.votable.VOSerializer;
 import uws.UWSException;
 import uws.UWSToolBox;
@@ -53,7 +55,7 @@ import uws.service.log.UWSLog.LogLevel;
  * <p>At its creation it is creating and configuring the other resources in function of the given description of the TAP service.</p>
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 2.1 (08/2016)
+ * @version 2.1 (03/2017)
  */
 public class TAP implements VOSIResource {
 
@@ -489,6 +491,9 @@ public class TAP implements VOSIResource {
 		xml.append("\t\t<accessURL use=\"base\">").append((getAccessURL() == null) ? "" : VOSerializer.formatText(getAccessURL())).append("</accessURL>\n");
 		xml.append("\t</interface>\n");
 
+		// Data models:
+		appendDataModels(xml, "\t");
+
 		// Language description:
 		xml.append("\t<language>\n");
 		xml.append("\t\t<name>ADQL</name>\n");
@@ -623,6 +628,92 @@ public class TAP implements VOSIResource {
 		return xml.toString();
 	}
 
+	/**
+	 * List and declare all IVOA Data Models supported by this TAP service.
+	 * 
+	 * <p>Currently, only the following DMs are natively supported:</p>
+	 * <ul>
+	 * 	<li>Obscore (1.0 and PR-1.1)</li>
+	 * </ul>
+	 * 
+	 * <p>
+	 * 	More can be supported by extending this function
+	 * 	(but not overwriting it completely otherwise the above
+	 * 	supported DMs won't be anymore).
+	 * </p>
+	 * 
+	 * <p>A DM declaration should follow this XML syntax:</p>
+	 * <pre>&lt;dataModel ivo-id="{DM-IVO_ID}"&gt;{DM-NAME}&lt;/dataModel&gt;</pre>
+	 * 
+	 * @param xml			The <code>/capabilities</code> in-progress content in which
+	 *           			implemented DMs can be declared.
+	 * @param linePrefix	Tabulations/Spaces that should prefix all lines
+	 *                  	(for human readability).
+	 * 
+	 * @since 2.1
+	 */
+	protected void appendDataModels(final StringBuffer xml, final String linePrefix){
+		appendObsCoreDM(xml, linePrefix);
+	}
+
+	/**
+	 * <p>Append the ObsCore DM declaration in the given {@link StringBuffer}
+	 * if an <code>ivoa.Obscore</code> table can be found in <code>TAP_SCHEMA</code>.</p>
+	 * 
+	 * <p>
+	 * 	This function has no effect if <code>ivoa.Obscore</code> can not
+	 * 	be found. The <code>ivoa</code> schema is searched case sensitively,
+	 * 	but not the table name <code>Obscore</code> which can be written
+	 * 	in any possible case.
+	 * </p>
+	 * 
+	 * <p>
+	 * 	If an <code>ivoa.Obscore</code> table is found, this function
+	 * 	detects automatically which version of Obscore is implemented.
+	 * 	It will be declared as Obscore 1.1 if ALL the following columns
+	 * 	are found (case INsensitively): <code>s_xel1</code>, <code>x_xel2</code>,
+	 * 	<code>t_xel</code>, <code>em_xel</code> and <code>pol_xel</code>.
+	 * 	If not, the Obscore table will be declared as Obscore 1.0.
+	 * </p>
+	 * 
+	 * @param xml	The <code>/capabilities</code> in-progress content in which
+	 *           	Obscore-DM should be declared if found.
+	 * @param linePrefix	Tabulations/Spaces that should prefix all lines
+	 *                  	(for human readability).
+	 * 
+	 * @see TAPMetadata#getObsCoreTable()
+	 * 
+	 * @since 2.1
+	 */
+	protected void appendObsCoreDM(final StringBuffer xml, final String linePrefix){
+		// Try to get the ObsCore table definition:
+		TAPTable obscore = service.getTAPMetadata().getObsCoreTable();
+
+		// If there is one, determine the supported DM version and declare it:
+		if (obscore != null){
+			/* ObsCore 1.1 MUST have s_xel1, s_xel2, t_xel, em_xel and pol_xel
+			 * These columns do not exist in ObsCore 1.0. */
+			byte hasAllXel = 0x0;
+			for(DBColumn col : obscore){
+				if (col.getADQLName().equalsIgnoreCase("s_xel1"))
+					hasAllXel |= 1;  // 2^0 = 0000 0001
+				else if (col.getADQLName().equalsIgnoreCase("s_xel2"))
+					hasAllXel |= 2;  // 2^1 = 0000 0010
+				else if (col.getADQLName().equalsIgnoreCase("t_xel"))
+					hasAllXel |= 4;  // 2^2 = 0000 0100
+				else if (col.getADQLName().equalsIgnoreCase("em_xel"))
+					hasAllXel |= 8;  // 2^3 = 0000 1000
+				else if (col.getADQLName().equalsIgnoreCase("pol_xel"))
+					hasAllXel |= 16; // 2^4 = 0001 0000
+			}
+			// Finally add the appropriate DM declaration:
+			if (hasAllXel == 31) // 2^5 - 1 =  0001 1111
+				xml.append(linePrefix + "<dataModel ivo-id=\"ivo://ivoa.net/std/ObsCore#core-1.1\">ObsCore-1.1</dataModel>\n");
+			else
+				xml.append(linePrefix + "<dataModel ivo-id=\"ivo://ivoa.net/std/ObsCore/v1.0\">ObsCore-1.0</dataModel>\n");
+		}
+	}
+
 	/* ************************************* */
 	/* MANAGEMENT OF THIS RESOURCE'S CONTENT */
 	/* ************************************* */
diff --git a/test/tap/formatter/ServiceConnection4Test.java b/test/tap/formatter/ServiceConnection4Test.java
index 074448adc7051023bd40451f441a52e3094d0e17..0b55f7851233c44e686f1498b2eb9d25fa82df5c 100644
--- a/test/tap/formatter/ServiceConnection4Test.java
+++ b/test/tap/formatter/ServiceConnection4Test.java
@@ -3,16 +3,27 @@ package tap.formatter;
 import java.util.Collection;
 import java.util.Iterator;
 
+import adql.db.FunctionDef;
 import tap.ServiceConnection;
 import tap.TAPFactory;
 import tap.log.TAPLog;
 import tap.metadata.TAPMetadata;
 import uws.service.UserIdentifier;
 import uws.service.file.UWSFileManager;
-import adql.db.FunctionDef;
 
 public class ServiceConnection4Test implements ServiceConnection {
 
+	private TAPMetadata metadata = null;
+	private TAPFactory factory = null;
+	private UWSFileManager fileManager = null;
+
+	public ServiceConnection4Test(){}
+
+	public ServiceConnection4Test(final TAPMetadata metadata, final UWSFileManager fileManager){
+		this.metadata = metadata;
+		this.fileManager = fileManager;
+	}
+
 	@Override
 	public int[] getOutputLimit(){
 		return new int[]{1000000,1000000};
@@ -80,7 +91,7 @@ public class ServiceConnection4Test implements ServiceConnection {
 
 	@Override
 	public TAPMetadata getTAPMetadata(){
-		return null;
+		return metadata;
 	}
 
 	@Override
@@ -105,12 +116,16 @@ public class ServiceConnection4Test implements ServiceConnection {
 
 	@Override
 	public TAPFactory getFactory(){
-		return null;
+		return factory;
+	}
+
+	public void setFactory(TAPFactory factory){
+		this.factory = factory;
 	}
 
 	@Override
 	public UWSFileManager getFileManager(){
-		return null;
+		return fileManager;
 	}
 
 	@Override
diff --git a/test/tap/metadata/TestTAPMetadata.java b/test/tap/metadata/TestTAPMetadata.java
new file mode 100644
index 0000000000000000000000000000000000000000..8979e878d09d5086ba34a90ba21a66161bae86a9
--- /dev/null
+++ b/test/tap/metadata/TestTAPMetadata.java
@@ -0,0 +1,46 @@
+package tap.metadata;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import org.junit.Test;
+
+import tap.metadata.TAPTable.TableType;
+
+public class TestTAPMetadata {
+
+	@Test
+	public void testGetObsCoreTable(){
+		TAPMetadata metadata = new TAPMetadata();
+
+		// CASE: no IVOA schema:
+		assertNull(metadata.getObsCoreTable());
+
+		// CASE: empty IVOA schema:
+		TAPSchema ivoaSchema = new TAPSchema("ivoa");
+		metadata.addSchema(ivoaSchema);
+		assertNull(metadata.getObsCoreTable());
+
+		// CASE: with ObsCore table as defined in the ObsCore's IVOA standard:
+		TAPTable obscore = new TAPTable("ObsCore");
+		ivoaSchema.addTable(obscore);
+		assertNotNull(metadata.getObsCoreTable());
+		assertEquals("ivoa.ObsCore", metadata.getObsCoreTable().getFullName());
+
+		// CASE: with "obscore" (all lower-case):
+		obscore = new TAPTable("obscore", TableType.view);
+		ivoaSchema.removeAllTables();
+		ivoaSchema.addTable(obscore);
+		assertNotNull(metadata.getObsCoreTable());
+		assertEquals("ivoa.obscore", metadata.getObsCoreTable().getFullName());
+
+		// CASE: ObsCore table BUT in a different schema:
+		metadata.removeAllSchemas();
+		TAPSchema differentSchema = new TAPSchema("different");
+		metadata.addSchema(differentSchema);
+		differentSchema.addTable("ObsCore");
+		assertNull(metadata.getObsCoreTable());
+	}
+
+}
diff --git a/test/tap/resource/TestTAP.java b/test/tap/resource/TestTAP.java
new file mode 100644
index 0000000000000000000000000000000000000000..b8b291542917577ef80c413fe9ccea3cb1afe4f7
--- /dev/null
+++ b/test/tap/resource/TestTAP.java
@@ -0,0 +1,91 @@
+package tap.resource;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+
+import org.junit.Test;
+
+import adql.db.DBType;
+import adql.db.DBType.DBDatatype;
+import tap.AbstractTAPFactory;
+import tap.ServiceConnection;
+import tap.TAPException;
+import tap.db.DBConnection;
+import tap.formatter.ServiceConnection4Test;
+import tap.metadata.TAPMetadata;
+import tap.metadata.TAPSchema;
+import tap.metadata.TAPTable;
+import uws.service.file.LocalUWSFileManager;
+
+public class TestTAP {
+
+	@Test
+	public void testAppendObsCoreDM(){
+		// Create the TAPMetadata:
+		TAPMetadata metadata = new TAPMetadata();
+		TAPSchema ivoaSchema = new TAPSchema("ivoa");
+		TAPTable obscore = new TAPTable("ObsCore");
+
+		// Create a TAP instance:
+		try{
+			ServiceConnection4Test serviceConn = new ServiceConnection4Test(metadata, new LocalUWSFileManager(new File(System.getProperty("java.io.tmpdir"))));
+			serviceConn.setFactory(new TAPFactory4Test(serviceConn));
+			TAP tap = new TAP(serviceConn);
+			StringBuffer xml = new StringBuffer();
+
+			// CASE: no Obscore table:
+			tap.appendObsCoreDM(xml, "");
+			assertEquals(0, xml.length());
+
+			// CASE: with an IVOA schema:
+			metadata.addSchema(ivoaSchema);
+			tap.appendObsCoreDM(xml, "");
+			assertEquals(0, xml.length());
+
+			// CASE: with an Obscore table (with no *_xel columns) - ObsCore 1.0:
+			ivoaSchema.addTable(obscore);
+			tap.appendObsCoreDM(xml, "\t");
+			assertEquals("\t<dataModel ivo-id=\"ivo://ivoa.net/std/ObsCore/v1.0\">ObsCore-1.0</dataModel>\n", xml.toString());
+
+			// CASE: with an Obscore 1.1 table but not with all *_xel columns:
+			obscore.addColumn("s_xel1", new DBType(DBDatatype.BIGINT), null, null, null, null);
+			obscore.addColumn("s_xel2", new DBType(DBDatatype.BIGINT), null, null, null, null);
+			obscore.addColumn("t_xel", new DBType(DBDatatype.BIGINT), null, null, null, null);
+			obscore.addColumn("em_xel", new DBType(DBDatatype.BIGINT), null, null, null, null);
+			xml.delete(0, xml.length());
+			tap.appendObsCoreDM(xml, "\t");
+			assertEquals("\t<dataModel ivo-id=\"ivo://ivoa.net/std/ObsCore/v1.0\">ObsCore-1.0</dataModel>\n", xml.toString());
+
+			// CASE: correct Obscore 1.1 table:
+			obscore.addColumn("pol_xel", new DBType(DBDatatype.BIGINT), null, null, null, null);
+			xml.delete(0, xml.length());
+			tap.appendObsCoreDM(xml, "\t");
+			assertEquals("\t<dataModel ivo-id=\"ivo://ivoa.net/std/ObsCore#core-1.1\">ObsCore-1.1</dataModel>\n", xml.toString());
+
+		}catch(Exception e){
+			e.printStackTrace();
+			fail("Unexpected error while creating a TAP instance! (see console for more details)");
+		}
+	}
+
+	private static class TAPFactory4Test extends AbstractTAPFactory {
+
+		protected TAPFactory4Test(ServiceConnection service) throws NullPointerException{
+			super(service);
+		}
+
+		@Override
+		public DBConnection getConnection(String jobID) throws TAPException{
+			return null;
+		}
+
+		@Override
+		public void freeConnection(DBConnection conn){}
+
+		@Override
+		public void destroy(){}
+	}
+
+}