The first 4 bytes will be "MDL7"

The next 44 bytes make up the header, that corresponds to the MD7_HEADER struct definition

struct MD7_HEADER
{
    char    ident[4];        // "MDL7"
    long    version;        // '0', 'sub'- version number;
    long    bones_num;
    long    groups_num;
    long    mdl7data_size;    // this file size (head included), except entlump and medlump.
    long    entlump_size;    // entity info size (not yet used, always 0)
    long    medlump_size;    // size of data for MED in bytes

    unsigned short md7_bone_stc_size;
    unsigned short md7_skin_stc_size;
    unsigned short md7_colorvalue_stc_size;
    unsigned short md7_material_stc_size;
    unsigned short md7_skinpoint_stc_size;
    unsigned short md7_triangle_stc_size;
    unsigned short md7_mainvertex_stc_size;
    unsigned short md7_framevertex_stc_size;
    unsigned short md7_bonetrans_stc_size;
    unsigned short md7_frame_stc_size;
};

Bones data follows, the following struct is repeated "bones_num" times:

struct MD7_BONE
{
    unsigned short parent_index;
    float x,y,z;
    //ws, 12.05.2003: bone name mitspeichern...
    char name[MAX_BONENAMESIZE];
};

Following the bones data is the groups data, which is really a lot of stuff. This is repated "groups_num" times.

struct MD7_GROUP
{
    unsigned char    typ;        // = '1' -> triangle based Mesh
    long    groupdata_size; // size of data for this group in bytes ( MD7_GROUP stc. included).
    char    name[16];
    long    numskins;
    long    num_stpts;    // number of 2D skin vertices
    long    numtris;
    long    numverts;
    long    numframes;
};

Then follows skins, the number of which is defined by the "numskins" field above.

struct MD7_SKIN
{
    unsigned char    typ; // 0,2,3,10,11,13  (wie vorher) oder 0x80 (1000 0000) oder 0xC0 (1100 0000)
long    width;
long    height;
char    texture_name[16];    //Skin bzw. Texture oder Material Name
};

Once you read the type, you need to decode it because the structure is followed by the raw skin data and mipmaps if they are included. I don't know all the types, but they basically define the number of bpp (bits per pixel) and whether mipmaps are included. Here are the types I've ran into so far, and their bpp/mipmap setting.

Type
BPP
Mipmaps
2
2
No
3
2
No
4
3
No
5
4
No
10
2
Yes
11
2
Yes
12
3
Yes
13
4
Yes

To calculate the size of the data to read, simply multiply bpp * width * height. If mipmaps are present, then their are 3 mipmaps that follow, where the height and witdth are 1/2 the size of the previous image.

For example, if the original image is 256x256 and 3 bpp, the size would be 196608 bytes.
The first mipmap would be 128x128, for a size of  49152 bytes.
The second mipmap would be 64x64, for a size of  12288 bytes.
The third mipmap would be 32x32, for a size of  3072 bytes.

There are material definitions that might be related to skins, but I havent ran into any yet.

After the skins are the Skin Vertices, which use the following struct, and there are "num_stpts" of them.

struct MD7_SKINPOINT
{
    float s,t;    // 0.0f - 1.0f
};

Then comes the Triangles, which use the following struct and there are "numtris" of them.

struct MD7_TRIANGLE
{
     unsigned short   v_index[3];     // vertex index  ( MD7_MAINVERTEX list)
     unsigned short   st_index[3];    // st index ( MD7_SKINPOINT list)
};

Then comes the Vertices, which use the following struct and there are "numverts" of them.

struct MD7_MAINVERTEX
{
    float x,y,z;
    unsigned short bone_index;
    //ws, 12.05.2003: vertex normal abspeichern
    unsigned short normals162index; //float xn,yn,zn;
};

Then comes the Frames, which use the following struct, and there are "numframes" of them.

struct MD7_FRAME
{
    char    frame_name[16];
    long    vertices_count;            //    length of vertices list
    long    transmatrix_count;        //    length of vertices list
};


Before the next frame, there are "vertices_count" MD7_FRAMEVERTEX structs
followed by "transmatrix_count" MD7_BONETRANS structs.

struct MD7_FRAMEVERTEX
{
    float x,y,z;
    unsigned short vertindex;        //    the index of this vertex, 0.. MD7_GROUP::numverts - 1
//ws, 12.05.2003: vertex normal abspeichern
    unsigned short normals162index; //float xn,yn,zn;
};

struct MD7_BONETRANS
{
    float    m [4*4];                // [4*3]
    unsigned short bone_index;        //    the index of this vertex, 0.. MD7_HEADER::bones_num - 1
};

After the frames, is the "medlump" which has a size in bytes of medlump_size from the header. Its data is opaque, to the best of my knowledege.